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