tunnel.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. /*
  2. Copyright (c) 2004-2006 by Juliusz Chroboczek
  3. Permission is hereby granted, free of charge, to any person obtaining a copy
  4. of this software and associated documentation files (the "Software"), to deal
  5. in the Software without restriction, including without limitation the rights
  6. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. copies of the Software, and to permit persons to whom the Software is
  8. furnished to do so, subject to the following conditions:
  9. The above copyright notice and this permission notice shall be included in
  10. all copies or substantial portions of the Software.
  11. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  17. THE SOFTWARE.
  18. */
  19. #include "polipo.h"
  20. #ifdef NO_TUNNEL
  21. void
  22. do_tunnel(int fd, char *buf, int offset, int len, AtomPtr url)
  23. {
  24. int n;
  25. assert(buf);
  26. n = httpWriteErrorHeaders(buf, CHUNK_SIZE, 0, 1,
  27. 501, internAtom("CONNECT not available "
  28. "in this version."),
  29. 1, NULL, url->string, url->length, NULL);
  30. releaseAtom(url);
  31. if(n >= 0) {
  32. /* This is completely wrong. The write is non-blocking, and we
  33. don't reschedule it if it fails. But then, if the write
  34. blocks, we'll simply drop the connection with no error message. */
  35. write(fd, buf, n);
  36. }
  37. dispose_chunk(buf);
  38. lingeringClose(fd);
  39. return;
  40. }
  41. #else
  42. static void tunnelDispatch(TunnelPtr);
  43. static int tunnelRead1Handler(int, FdEventHandlerPtr, StreamRequestPtr);
  44. static int tunnelRead2Handler(int, FdEventHandlerPtr, StreamRequestPtr);
  45. static int tunnelWrite1Handler(int, FdEventHandlerPtr, StreamRequestPtr);
  46. static int tunnelWrite2Handler(int, FdEventHandlerPtr, StreamRequestPtr);
  47. static int tunnelDnsHandler(int, GethostbynameRequestPtr);
  48. static int tunnelConnectionHandler(int, FdEventHandlerPtr, ConnectRequestPtr);
  49. static int tunnelSocksHandler(int, SocksRequestPtr);
  50. static int tunnelHandlerCommon(int, TunnelPtr);
  51. static int tunnelError(TunnelPtr, int, AtomPtr);
  52. static int
  53. circularBufferFull(CircularBufferPtr buf)
  54. {
  55. if(buf->head == buf->tail - 1)
  56. return 1;
  57. if(buf->head == CHUNK_SIZE - 1 && buf->tail == 0)
  58. return 1;
  59. return 0;
  60. }
  61. static int
  62. circularBufferEmpty(CircularBufferPtr buf)
  63. {
  64. return buf->head == buf->tail;
  65. }
  66. static void
  67. logTunnel(TunnelPtr tunnel, int blocked)
  68. {
  69. do_log(L_TUNNEL,"tunnel %s:%d %s\n", tunnel->hostname->string, tunnel->port,
  70. blocked ? "blocked" : "allowed");
  71. }
  72. static TunnelPtr
  73. makeTunnel(int fd, char *buf, int offset, int len)
  74. {
  75. TunnelPtr tunnel;
  76. assert(offset < CHUNK_SIZE);
  77. tunnel = malloc(sizeof(TunnelRec));
  78. if(tunnel == NULL)
  79. return NULL;
  80. tunnel->hostname = NULL;
  81. tunnel->port = -1;
  82. tunnel->flags = 0;
  83. tunnel->fd1 = fd;
  84. tunnel->fd2 = -1;
  85. tunnel->buf1.buf = buf;
  86. if(offset == len) {
  87. tunnel->buf1.tail = 0;
  88. tunnel->buf1.head = 0;
  89. } else {
  90. tunnel->buf1.tail = offset;
  91. tunnel->buf1.head = len;
  92. }
  93. tunnel->buf2.buf = NULL;
  94. tunnel->buf2.tail = 0;
  95. tunnel->buf2.head = 0;
  96. return tunnel;
  97. }
  98. static void
  99. destroyTunnel(TunnelPtr tunnel)
  100. {
  101. assert(tunnel->fd1 < 0 && tunnel->fd2 < 0);
  102. releaseAtom(tunnel->hostname);
  103. if(tunnel->buf1.buf)
  104. dispose_chunk(tunnel->buf1.buf);
  105. if(tunnel->buf2.buf)
  106. dispose_chunk(tunnel->buf2.buf);
  107. free(tunnel);
  108. }
  109. void
  110. do_tunnel(int fd, char *buf, int offset, int len, AtomPtr url)
  111. {
  112. TunnelPtr tunnel;
  113. int port;
  114. char *p, *q;
  115. tunnel = makeTunnel(fd, buf, offset, len);
  116. if(tunnel == NULL) {
  117. do_log(L_ERROR, "Couldn't allocate tunnel.\n");
  118. releaseAtom(url);
  119. dispose_chunk(buf);
  120. CLOSE(fd);
  121. return;
  122. }
  123. if(proxyOffline) {
  124. do_log(L_INFO, "Attemted CONNECT when disconnected.\n");
  125. releaseAtom(url);
  126. tunnelError(tunnel, 502,
  127. internAtom("Cannot CONNECT when disconnected."));
  128. return;
  129. }
  130. p = memrchr(url->string, ':', url->length);
  131. q = NULL;
  132. if(p)
  133. port = strtol(p + 1, &q, 10);
  134. if(!p || q != url->string + url->length) {
  135. do_log(L_ERROR, "Couldn't parse CONNECT.\n");
  136. releaseAtom(url);
  137. tunnelError(tunnel, 400, internAtom("Couldn't parse CONNECT"));
  138. return;
  139. }
  140. tunnel->hostname = internAtomLowerN(url->string, p - url->string);
  141. if(tunnel->hostname == NULL) {
  142. releaseAtom(url);
  143. tunnelError(tunnel, 501, internAtom("Couldn't allocate hostname"));
  144. return;
  145. }
  146. if(!intListMember(port, tunnelAllowedPorts)) {
  147. releaseAtom(url);
  148. tunnelError(tunnel, 403, internAtom("Forbidden port"));
  149. return;
  150. }
  151. tunnel->port = port;
  152. if (tunnelIsMatched(url->string, url->length,
  153. tunnel->hostname->string, tunnel->hostname->length)) {
  154. releaseAtom(url);
  155. tunnelError(tunnel, 403, internAtom("Forbidden tunnel"));
  156. logTunnel(tunnel,1);
  157. return;
  158. }
  159. logTunnel(tunnel,0);
  160. releaseAtom(url);
  161. if(socksParentProxy)
  162. do_socks_connect(parentHost ?
  163. parentHost->string : tunnel->hostname->string,
  164. parentHost ? parentPort : tunnel->port,
  165. tunnelSocksHandler, tunnel);
  166. else
  167. do_gethostbyname(parentHost ?
  168. parentHost->string : tunnel->hostname->string, 0,
  169. tunnelDnsHandler, tunnel);
  170. }
  171. static int
  172. tunnelDnsHandler(int status, GethostbynameRequestPtr request)
  173. {
  174. TunnelPtr tunnel = request->data;
  175. if(status <= 0) {
  176. tunnelError(tunnel, 504,
  177. internAtomError(-status,
  178. "Host %s lookup failed",
  179. atomString(tunnel->hostname)));
  180. return 1;
  181. }
  182. if(request->addr->string[0] == DNS_CNAME) {
  183. if(request->count > 10)
  184. tunnelError(tunnel, 504, internAtom("CNAME loop"));
  185. do_gethostbyname(request->addr->string + 1, request->count + 1,
  186. tunnelDnsHandler, tunnel);
  187. return 1;
  188. }
  189. do_connect(retainAtom(request->addr), 0,
  190. parentHost ? parentPort : tunnel->port,
  191. tunnelConnectionHandler, tunnel);
  192. return 1;
  193. }
  194. static int
  195. tunnelConnectionHandler(int status,
  196. FdEventHandlerPtr event,
  197. ConnectRequestPtr request)
  198. {
  199. TunnelPtr tunnel = request->data;
  200. int rc;
  201. if(status < 0) {
  202. tunnelError(tunnel, 504, internAtomError(-status, "Couldn't connect"));
  203. return 1;
  204. }
  205. rc = setNodelay(request->fd, 1);
  206. if(rc < 0)
  207. do_log_error(L_WARN, errno, "Couldn't disable Nagle's algorithm");
  208. return tunnelHandlerCommon(request->fd, tunnel);
  209. }
  210. static int
  211. tunnelSocksHandler(int status, SocksRequestPtr request)
  212. {
  213. TunnelPtr tunnel = request->data;
  214. if(status < 0) {
  215. tunnelError(tunnel, 504, internAtomError(-status, "Couldn't connect"));
  216. return 1;
  217. }
  218. return tunnelHandlerCommon(request->fd, tunnel);
  219. }
  220. static int
  221. tunnelHandlerParent(int fd, TunnelPtr tunnel)
  222. {
  223. char *message;
  224. int n;
  225. if(tunnel->buf1.buf == NULL)
  226. tunnel->buf1.buf = get_chunk();
  227. if(tunnel->buf1.buf == NULL) {
  228. message = "Couldn't allocate buffer";
  229. goto fail;
  230. }
  231. if(tunnel->buf1.tail != tunnel->buf1.head) {
  232. message = "Pipelined connect to parent proxy not implemented";
  233. goto fail;
  234. }
  235. n = snnprintf(tunnel->buf1.buf, tunnel->buf1.tail, CHUNK_SIZE,
  236. "CONNECT %s:%d HTTP/1.1\r\nHost: %s",
  237. tunnel->hostname->string, tunnel->port, tunnel->hostname->string);
  238. if (tunnel->port != 443)
  239. n = snnprintf(tunnel->buf1.buf, n, CHUNK_SIZE, ":%d", tunnel->port);
  240. if (parentAuthCredentials)
  241. n = buildServerAuthHeaders(tunnel->buf1.buf, n, CHUNK_SIZE,
  242. parentAuthCredentials);
  243. n = snnprintf(tunnel->buf1.buf, n, CHUNK_SIZE, "\r\n\r\n");
  244. if(n < 0) {
  245. message = "Buffer overflow";
  246. goto fail;
  247. }
  248. tunnel->buf1.head = n;
  249. tunnelDispatch(tunnel);
  250. return 1;
  251. fail:
  252. CLOSE(fd);
  253. tunnel->fd2 = -1;
  254. tunnelError(tunnel, 501, internAtom(message));
  255. return 1;
  256. }
  257. static int
  258. tunnelHandlerCommon(int fd, TunnelPtr tunnel)
  259. {
  260. const char *message = "HTTP/1.1 200 Tunnel established\r\n\r\n";
  261. tunnel->fd2 = fd;
  262. if(parentHost)
  263. return tunnelHandlerParent(fd, tunnel);
  264. if(tunnel->buf2.buf == NULL)
  265. tunnel->buf2.buf = get_chunk();
  266. if(tunnel->buf2.buf == NULL) {
  267. CLOSE(fd);
  268. tunnelError(tunnel, 501, internAtom("Couldn't allocate buffer"));
  269. return 1;
  270. }
  271. memcpy(tunnel->buf2.buf, message, MIN(CHUNK_SIZE - 1, strlen(message)));
  272. tunnel->buf2.head = MIN(CHUNK_SIZE - 1, strlen(message));
  273. tunnelDispatch(tunnel);
  274. return 1;
  275. }
  276. static void
  277. bufRead(int fd, CircularBufferPtr buf,
  278. int (*handler)(int, FdEventHandlerPtr, StreamRequestPtr),
  279. void *data)
  280. {
  281. int tail;
  282. if(buf->tail == 0)
  283. tail = CHUNK_SIZE - 1;
  284. else
  285. tail = buf->tail - 1;
  286. if(buf->head == 0)
  287. do_stream_buf(IO_READ | IO_NOTNOW,
  288. fd, 0,
  289. &buf->buf, tail,
  290. handler, data);
  291. else if(buf->tail > buf->head)
  292. do_stream(IO_READ | IO_NOTNOW,
  293. fd, buf->head,
  294. buf->buf, tail,
  295. handler, data);
  296. else
  297. do_stream_2(IO_READ | IO_NOTNOW,
  298. fd, buf->head,
  299. buf->buf, CHUNK_SIZE,
  300. buf->buf, tail,
  301. handler, data);
  302. }
  303. static void
  304. bufWrite(int fd, CircularBufferPtr buf,
  305. int (*handler)(int, FdEventHandlerPtr, StreamRequestPtr),
  306. void *data)
  307. {
  308. if(buf->head > buf->tail)
  309. do_stream(IO_WRITE,
  310. fd, buf->tail,
  311. buf->buf, buf->head,
  312. handler, data);
  313. else
  314. do_stream_2(IO_WRITE,
  315. fd, buf->tail,
  316. buf->buf, CHUNK_SIZE,
  317. buf->buf, buf->head,
  318. handler, data);
  319. }
  320. static void
  321. tunnelDispatch(TunnelPtr tunnel)
  322. {
  323. if(circularBufferEmpty(&tunnel->buf1)) {
  324. if(tunnel->buf1.buf &&
  325. !(tunnel->flags & (TUNNEL_READER1 | TUNNEL_WRITER2))) {
  326. dispose_chunk(tunnel->buf1.buf);
  327. tunnel->buf1.buf = NULL;
  328. tunnel->buf1.head = tunnel->buf1.tail = 0;
  329. }
  330. }
  331. if(circularBufferEmpty(&tunnel->buf2)) {
  332. if(tunnel->buf2.buf &&
  333. !(tunnel->flags & (TUNNEL_READER2 | TUNNEL_WRITER1))) {
  334. dispose_chunk(tunnel->buf2.buf);
  335. tunnel->buf2.buf = NULL;
  336. tunnel->buf2.head = tunnel->buf2.tail = 0;
  337. }
  338. }
  339. if(tunnel->fd1 >= 0) {
  340. if(!(tunnel->flags & (TUNNEL_READER1 | TUNNEL_EOF1)) &&
  341. !circularBufferFull(&tunnel->buf1)) {
  342. tunnel->flags |= TUNNEL_READER1;
  343. bufRead(tunnel->fd1, &tunnel->buf1, tunnelRead1Handler, tunnel);
  344. }
  345. if(!(tunnel->flags & (TUNNEL_WRITER1 | TUNNEL_EPIPE1)) &&
  346. !circularBufferEmpty(&tunnel->buf2)) {
  347. tunnel->flags |= TUNNEL_WRITER1;
  348. /* There's no IO_NOTNOW in bufWrite, so it might close the
  349. file descriptor straight away. Wait until we're
  350. rescheduled. */
  351. bufWrite(tunnel->fd1, &tunnel->buf2, tunnelWrite1Handler, tunnel);
  352. return;
  353. }
  354. if(tunnel->fd2 < 0 || (tunnel->flags & TUNNEL_EOF2)) {
  355. if(!(tunnel->flags & TUNNEL_EPIPE1))
  356. shutdown(tunnel->fd1, 1);
  357. tunnel->flags |= TUNNEL_EPIPE1;
  358. } else if(tunnel->fd1 < 0 || (tunnel->flags & TUNNEL_EPIPE2)) {
  359. if(!(tunnel->flags & TUNNEL_EOF1))
  360. shutdown(tunnel->fd1, 0);
  361. tunnel->flags |= TUNNEL_EOF1;
  362. }
  363. if((tunnel->flags & TUNNEL_EOF1) && (tunnel->flags & TUNNEL_EPIPE1)) {
  364. if(!(tunnel->flags & (TUNNEL_READER1 | TUNNEL_WRITER1))) {
  365. CLOSE(tunnel->fd1);
  366. tunnel->fd1 = -1;
  367. }
  368. }
  369. }
  370. if(tunnel->fd2 >= 0) {
  371. if(!(tunnel->flags & (TUNNEL_READER2 | TUNNEL_EOF2)) &&
  372. !circularBufferFull(&tunnel->buf2)) {
  373. tunnel->flags |= TUNNEL_READER2;
  374. bufRead(tunnel->fd2, &tunnel->buf2, tunnelRead2Handler, tunnel);
  375. }
  376. if(!(tunnel->flags & (TUNNEL_WRITER2 | TUNNEL_EPIPE2)) &&
  377. !circularBufferEmpty(&tunnel->buf1)) {
  378. tunnel->flags |= TUNNEL_WRITER2;
  379. bufWrite(tunnel->fd2, &tunnel->buf1, tunnelWrite2Handler, tunnel);
  380. return;
  381. }
  382. if(tunnel->fd1 < 0 || (tunnel->flags & TUNNEL_EOF1)) {
  383. if(!(tunnel->flags & TUNNEL_EPIPE2))
  384. shutdown(tunnel->fd2, 1);
  385. tunnel->flags |= TUNNEL_EPIPE2;
  386. } else if(tunnel->fd1 < 0 || (tunnel->flags & TUNNEL_EPIPE1)) {
  387. if(!(tunnel->flags & TUNNEL_EOF2))
  388. shutdown(tunnel->fd2, 0);
  389. tunnel->flags |= TUNNEL_EOF2;
  390. }
  391. if((tunnel->flags & TUNNEL_EOF2) && (tunnel->flags & TUNNEL_EPIPE2)) {
  392. if(!(tunnel->flags & (TUNNEL_READER2 | TUNNEL_WRITER2))) {
  393. CLOSE(tunnel->fd2);
  394. tunnel->fd2 = -1;
  395. }
  396. }
  397. }
  398. if(tunnel->fd1 < 0 && tunnel->fd2 < 0)
  399. destroyTunnel(tunnel);
  400. else
  401. assert(tunnel->flags & (TUNNEL_READER1 | TUNNEL_WRITER1 |
  402. TUNNEL_READER2 | TUNNEL_WRITER2));
  403. }
  404. static int
  405. tunnelRead1Handler(int status,
  406. FdEventHandlerPtr event, StreamRequestPtr request)
  407. {
  408. TunnelPtr tunnel = request->data;
  409. if(status) {
  410. if(status < 0 && status != -EPIPE && status != -ECONNRESET)
  411. do_log_error(L_ERROR, -status, "Couldn't read from client");
  412. tunnel->flags |= TUNNEL_EOF1;
  413. goto done;
  414. }
  415. tunnel->buf1.head = request->offset % CHUNK_SIZE;
  416. done:
  417. /* Keep buffer empty to avoid a deadlock */
  418. if((tunnel->flags & TUNNEL_EPIPE2))
  419. tunnel->buf1.tail = tunnel->buf1.head;
  420. tunnel->flags &= ~TUNNEL_READER1;
  421. tunnelDispatch(tunnel);
  422. return 1;
  423. }
  424. static int
  425. tunnelRead2Handler(int status,
  426. FdEventHandlerPtr event, StreamRequestPtr request)
  427. {
  428. TunnelPtr tunnel = request->data;
  429. if(status) {
  430. if(status < 0 && status != -EPIPE && status != -ECONNRESET)
  431. do_log_error(L_ERROR, -status, "Couldn't read from server");
  432. tunnel->flags |= TUNNEL_EOF2;
  433. goto done;
  434. }
  435. tunnel->buf2.head = request->offset % CHUNK_SIZE;
  436. done:
  437. /* Keep buffer empty to avoid a deadlock */
  438. if((tunnel->flags & TUNNEL_EPIPE1))
  439. tunnel->buf2.tail = tunnel->buf2.head;
  440. tunnel->flags &= ~TUNNEL_READER2;
  441. tunnelDispatch(tunnel);
  442. return 1;
  443. }
  444. static int
  445. tunnelWrite1Handler(int status,
  446. FdEventHandlerPtr event, StreamRequestPtr request)
  447. {
  448. TunnelPtr tunnel = request->data;
  449. if(status || (tunnel->flags & TUNNEL_EPIPE1)) {
  450. tunnel->flags |= TUNNEL_EPIPE1;
  451. if(status < 0 && status != -EPIPE)
  452. do_log_error(L_ERROR, -status, "Couldn't write to client");
  453. /* Empty the buffer to avoid a deadlock */
  454. tunnel->buf2.tail = tunnel->buf2.head;
  455. goto done;
  456. }
  457. tunnel->buf2.tail = request->offset % CHUNK_SIZE;
  458. done:
  459. tunnel->flags &= ~TUNNEL_WRITER1;
  460. tunnelDispatch(tunnel);
  461. return 1;
  462. }
  463. static int
  464. tunnelWrite2Handler(int status,
  465. FdEventHandlerPtr event, StreamRequestPtr request)
  466. {
  467. TunnelPtr tunnel = request->data;
  468. if(status || (tunnel->flags & TUNNEL_EPIPE2)) {
  469. tunnel->flags |= TUNNEL_EPIPE2;
  470. if(status < 0 && status != -EPIPE)
  471. do_log_error(L_ERROR, -status, "Couldn't write to server");
  472. /* Empty the buffer to avoid a deadlock */
  473. tunnel->buf1.tail = tunnel->buf1.head;
  474. goto done;
  475. }
  476. tunnel->buf1.tail = request->offset % CHUNK_SIZE;
  477. done:
  478. tunnel->flags &= ~TUNNEL_WRITER2;
  479. tunnelDispatch(tunnel);
  480. return 1;
  481. }
  482. static int
  483. tunnelError(TunnelPtr tunnel, int code, AtomPtr message)
  484. {
  485. int n;
  486. if(tunnel->fd2 > 0) {
  487. CLOSE(tunnel->fd2);
  488. tunnel->fd2 = -1;
  489. }
  490. if(tunnel->buf2.buf == NULL)
  491. tunnel->buf2.buf = get_chunk();
  492. if(tunnel->buf2.buf == NULL)
  493. goto fail;
  494. n = httpWriteErrorHeaders(tunnel->buf2.buf, CHUNK_SIZE - 1, 0,
  495. 1, code, message, 1, NULL,
  496. NULL, 0, NULL);
  497. if(n <= 0) goto fail;
  498. tunnel->buf2.head = n;
  499. tunnelDispatch(tunnel);
  500. return 1;
  501. fail:
  502. CLOSE(tunnel->fd1);
  503. tunnel->fd1 = -1;
  504. tunnelDispatch(tunnel);
  505. return 1;
  506. }
  507. #endif