root / trunk / src / dot / proxy / tunnel.c

Revision 7618, 15.6 kB (checked in by BradNeuberg, 22 months ago)

Local and remote SVN repositories somehow became out of sync and corrupted -- re-adding these in

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