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