2 Copyright (c) 2004-2006 by Juliusz Chroboczek
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:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
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
28 do_tunnel(int fd
, char *buf
, int offset
, int len
, AtomPtr url
)
33 n
= httpWriteErrorHeaders(buf
, CHUNK_SIZE
, 0, 1,
34 501, internAtom("CONNECT not available "
36 1, NULL
, url
->string
, url
->length
, NULL
);
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. */
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
);
63 circularBufferFull(CircularBufferPtr buf
)
65 if(buf
->head
== buf
->tail
- 1)
67 if(buf
->head
== CHUNK_SIZE
- 1 && buf
->tail
== 0)
73 circularBufferEmpty(CircularBufferPtr buf
)
75 return buf
->head
== buf
->tail
;
79 makeTunnel(int fd
, char *buf
, int offset
, int len
)
82 assert(offset
< CHUNK_SIZE
);
84 tunnel
= malloc(sizeof(TunnelRec
));
88 tunnel
->hostname
= NULL
;
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
;
103 destroyTunnel(TunnelPtr tunnel
)
105 assert(tunnel
->fd1
< 0 && tunnel
->fd2
< 0);
106 releaseAtom(tunnel
->hostname
);
108 dispose_chunk(tunnel
->buf1
.buf
);
110 dispose_chunk(tunnel
->buf2
.buf
);
115 do_tunnel(int fd
, char *buf
, int offset
, int len
, AtomPtr url
)
121 tunnel
= makeTunnel(fd
, buf
, offset
, len
);
123 do_log(L_ERROR
, "Couldn't allocate tunnel.\n");
130 p
= memrchr(url
->string
, ':', url
->length
);
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");
137 tunnelError(tunnel
, 400, internAtom("Couldn't parse CONNECT"));
140 tunnel
->hostname
= internAtomLowerN(url
->string
, p
- url
->string
);
141 if(tunnel
->hostname
== NULL
) {
143 tunnelError(tunnel
, 501, internAtom("Couldn't allocate hostname"));
147 if(!intListMember(port
, tunnelAllowedPorts
)) {
149 tunnelError(tunnel
, 403, internAtom("Forbidden port"));
157 do_socks_connect(parentHost
?
158 parentHost
->string
: tunnel
->hostname
->string
,
159 parentHost
? parentPort
: tunnel
->port
,
160 tunnelSocksHandler
, tunnel
);
162 do_gethostbyname(parentHost
?
163 parentHost
->string
: tunnel
->hostname
->string
, 0,
164 tunnelDnsHandler
, tunnel
);
168 tunnelDnsHandler(int status
, GethostbynameRequestPtr request
)
170 TunnelPtr tunnel
= request
->data
;
173 tunnelError(tunnel
, 504,
174 internAtomError(-status
,
175 "Host %s lookup failed",
176 atomString(tunnel
->hostname
)));
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
);
188 do_connect(retainAtom(request
->addr
), 0,
189 parentHost
? parentPort
: tunnel
->port
,
190 tunnelConnectionHandler
, tunnel
);
195 tunnelConnectionHandler(int status
,
196 FdEventHandlerPtr event
,
197 ConnectRequestPtr request
)
199 TunnelPtr tunnel
= request
->data
;
203 tunnelError(tunnel
, 504, internAtomError(-status
, "Couldn't connect"));
207 rc
= setNodelay(request
->fd
, 1);
209 do_log_error(L_WARN
, errno
, "Couldn't disable Nagle's algorithm");
211 return tunnelHandlerCommon(request
->fd
, tunnel
);
215 tunnelSocksHandler(int status
, SocksRequestPtr request
)
217 TunnelPtr tunnel
= request
->data
;
220 tunnelError(tunnel
, 504, internAtomError(-status
, "Couldn't connect"));
224 return tunnelHandlerCommon(request
->fd
, tunnel
);
228 tunnelHandlerParent(int fd
, TunnelPtr tunnel
)
235 tunnel
->buf2
.buf
= get_chunk();
236 if(tunnel
->buf2
.buf
== NULL
) {
237 message
= "Couldn't allocate buffer";
241 n
= snnprintf(tunnel
->buf2
.buf
, 0, CHUNK_SIZE
,
242 "CONNECT %s:%d HTTP/1.1"
244 tunnel
->hostname
->string
, tunnel
->port
);
246 message
= "Buffer overflow";
249 tunnel
->buf2
.head
= n
;
250 tunnelDispatch(tunnel
);
256 tunnelError(tunnel
, 501, internAtom(message
));
261 tunnelHandlerCommon(int fd
, TunnelPtr tunnel
)
263 const char *message
= "HTTP/1.1 200 Tunnel established\r\n\r\n";
264 assert(tunnel
->buf1
.buf
== NULL
);
267 return tunnelHandlerParent(fd
, tunnel
);
269 tunnel
->buf1
.buf
= get_chunk();
270 if(tunnel
->buf1
.buf
== NULL
) {
272 tunnelError(tunnel
, 501, internAtom("Couldn't allocate buffer"));
278 memcpy(tunnel
->buf2
.buf
, message
, MIN(CHUNK_SIZE
- 1, strlen(message
)));
279 tunnel
->buf2
.head
= MIN(CHUNK_SIZE
- 1, strlen(message
));
281 tunnelDispatch(tunnel
);
286 bufRead(int fd
, CircularBufferPtr buf
,
287 int (*handler
)(int, FdEventHandlerPtr
, StreamRequestPtr
),
293 tail
= CHUNK_SIZE
- 1;
295 tail
= buf
->tail
- 1;
298 do_stream_buf(IO_READ
| IO_NOTNOW
,
302 else if(buf
->tail
> buf
->head
)
303 do_stream(IO_READ
| IO_NOTNOW
,
308 do_stream_2(IO_READ
| IO_NOTNOW
,
310 buf
->buf
, CHUNK_SIZE
,
316 bufWrite(int fd
, CircularBufferPtr buf
,
317 int (*handler
)(int, FdEventHandlerPtr
, StreamRequestPtr
),
320 if(buf
->head
> buf
->tail
)
326 do_stream_2(IO_WRITE
,
328 buf
->buf
, CHUNK_SIZE
,
334 tunnelDispatch(TunnelPtr tunnel
)
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;
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;
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
);
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
366 bufWrite(tunnel
->fd1
, &tunnel
->buf2
, tunnelWrite1Handler
, tunnel
);
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
;
378 if((tunnel
->flags
& TUNNEL_EOF1
) && (tunnel
->flags
& TUNNEL_EPIPE1
)) {
379 if(!(tunnel
->flags
& (TUNNEL_READER1
| TUNNEL_WRITER1
))) {
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
);
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
);
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
;
407 if((tunnel
->flags
& TUNNEL_EOF2
) && (tunnel
->flags
& TUNNEL_EPIPE2
)) {
408 if(!(tunnel
->flags
& (TUNNEL_READER2
| TUNNEL_WRITER2
))) {
415 if(tunnel
->fd1
< 0 && tunnel
->fd2
< 0)
416 destroyTunnel(tunnel
);
418 assert(tunnel
->flags
& (TUNNEL_READER1
| TUNNEL_WRITER1
|
419 TUNNEL_READER2
| TUNNEL_WRITER2
));
423 tunnelRead1Handler(int status
,
424 FdEventHandlerPtr event
, StreamRequestPtr request
)
426 TunnelPtr tunnel
= request
->data
;
429 do_log_error(L_ERROR
, -status
, "Couldn't read from client");
430 tunnel
->flags
|= TUNNEL_EOF1
;
433 tunnel
->buf1
.head
= request
->offset
% CHUNK_SIZE
;
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
);
444 tunnelRead2Handler(int status
,
445 FdEventHandlerPtr event
, StreamRequestPtr request
)
447 TunnelPtr tunnel
= request
->data
;
450 do_log_error(L_ERROR
, -status
, "Couldn't read from server");
451 tunnel
->flags
|= TUNNEL_EOF2
;
454 tunnel
->buf2
.head
= request
->offset
% CHUNK_SIZE
;
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
);
465 tunnelWrite1Handler(int status
,
466 FdEventHandlerPtr event
, StreamRequestPtr request
)
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
;
477 tunnel
->buf2
.tail
= request
->offset
% CHUNK_SIZE
;
479 tunnel
->flags
&= ~TUNNEL_WRITER1
;
480 tunnelDispatch(tunnel
);
485 tunnelWrite2Handler(int status
,
486 FdEventHandlerPtr event
, StreamRequestPtr request
)
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
;
497 tunnel
->buf1
.tail
= request
->offset
% CHUNK_SIZE
;
499 tunnel
->flags
&= ~TUNNEL_WRITER2
;
500 tunnelDispatch(tunnel
);
505 tunnelError(TunnelPtr tunnel
, int code
, AtomPtr message
)
508 if(tunnel
->fd2
> 0) {
513 if(tunnel
->buf2
.buf
== NULL
)
514 tunnel
->buf2
.buf
= get_chunk();
515 if(tunnel
->buf2
.buf
== NULL
)
518 n
= httpWriteErrorHeaders(tunnel
->buf2
.buf
, CHUNK_SIZE
- 1, 0,
519 1, code
, message
, 1, NULL
,
522 if(n
<= 0) goto fail
;
524 tunnel
->buf2
.head
= n
;
526 tunnelDispatch(tunnel
);
532 tunnelDispatch(tunnel
);