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(tunnel
->hostname
->string
, tunnel
->port
,
158 tunnelSocksHandler
, tunnel
);
160 do_gethostbyname(tunnel
->hostname
->string
, 0,
161 tunnelDnsHandler
, tunnel
);
165 tunnelDnsHandler(int status
, GethostbynameRequestPtr request
)
167 TunnelPtr tunnel
= request
->data
;
170 tunnelError(tunnel
, 504,
171 internAtomError(-status
,
172 "Host %s lookup failed",
173 atomString(tunnel
->hostname
)));
177 if(request
->addr
->string
[0] == DNS_CNAME
) {
178 if(request
->count
> 10)
179 tunnelError(tunnel
, 504, internAtom("CNAME loop"));
180 do_gethostbyname(request
->addr
->string
+ 1, request
->count
+ 1,
181 tunnelDnsHandler
, tunnel
);
185 do_connect(retainAtom(request
->addr
), 0, tunnel
->port
,
186 tunnelConnectionHandler
, tunnel
);
191 tunnelConnectionHandler(int status
,
192 FdEventHandlerPtr event
,
193 ConnectRequestPtr request
)
195 TunnelPtr tunnel
= request
->data
;
199 tunnelError(tunnel
, 504, internAtomError(-status
, "Couldn't connect"));
203 rc
= setNodelay(request
->fd
, 1);
205 do_log_error(L_WARN
, errno
, "Couldn't disable Nagle's algorithm");
207 return tunnelHandlerCommon(request
->fd
, tunnel
);
211 tunnelSocksHandler(int status
, SocksRequestPtr request
)
213 TunnelPtr tunnel
= request
->data
;
216 tunnelError(tunnel
, 504, internAtomError(-status
, "Couldn't connect"));
220 return tunnelHandlerCommon(request
->fd
, tunnel
);
224 tunnelHandlerCommon(int fd
, TunnelPtr tunnel
)
226 const char *message
= "HTTP/1.1 200 Tunnel established\r\n\r\n";
227 assert(tunnel
->buf1
.buf
== NULL
);
228 tunnel
->buf1
.buf
= get_chunk();
229 if(tunnel
->buf1
.buf
== NULL
) {
231 tunnelError(tunnel
, 501, internAtom("Couldn't allocate buffer"));
237 memcpy(tunnel
->buf2
.buf
, message
, MIN(CHUNK_SIZE
- 1, strlen(message
)));
238 tunnel
->buf2
.head
= MIN(CHUNK_SIZE
- 1, strlen(message
));
240 tunnelDispatch(tunnel
);
245 bufRead(int fd
, CircularBufferPtr buf
,
246 int (*handler
)(int, FdEventHandlerPtr
, StreamRequestPtr
),
252 tail
= CHUNK_SIZE
- 1;
254 tail
= buf
->tail
- 1;
257 do_stream_buf(IO_READ
| IO_NOTNOW
,
261 else if(buf
->tail
> buf
->head
)
262 do_stream(IO_READ
| IO_NOTNOW
,
267 do_stream_2(IO_READ
| IO_NOTNOW
,
269 buf
->buf
, CHUNK_SIZE
,
275 bufWrite(int fd
, CircularBufferPtr buf
,
276 int (*handler
)(int, FdEventHandlerPtr
, StreamRequestPtr
),
279 if(buf
->head
> buf
->tail
)
285 do_stream_2(IO_WRITE
,
287 buf
->buf
, CHUNK_SIZE
,
293 tunnelDispatch(TunnelPtr tunnel
)
295 if(circularBufferEmpty(&tunnel
->buf1
)) {
296 if(tunnel
->buf1
.buf
&&
297 !(tunnel
->flags
& (TUNNEL_READER1
| TUNNEL_WRITER2
))) {
298 dispose_chunk(tunnel
->buf1
.buf
);
299 tunnel
->buf1
.buf
= NULL
;
300 tunnel
->buf1
.head
= tunnel
->buf1
.tail
= 0;
304 if(circularBufferEmpty(&tunnel
->buf2
)) {
305 if(tunnel
->buf2
.buf
&&
306 !(tunnel
->flags
& (TUNNEL_READER2
| TUNNEL_WRITER1
))) {
307 dispose_chunk(tunnel
->buf2
.buf
);
308 tunnel
->buf2
.buf
= NULL
;
309 tunnel
->buf2
.head
= tunnel
->buf2
.tail
= 0;
313 if(tunnel
->fd1
>= 0) {
314 if(!(tunnel
->flags
& (TUNNEL_READER1
| TUNNEL_EOF1
)) &&
315 !circularBufferFull(&tunnel
->buf1
)) {
316 tunnel
->flags
|= TUNNEL_READER1
;
317 bufRead(tunnel
->fd1
, &tunnel
->buf1
, tunnelRead1Handler
, tunnel
);
319 if(!(tunnel
->flags
& (TUNNEL_WRITER1
| TUNNEL_EPIPE1
)) &&
320 !circularBufferEmpty(&tunnel
->buf2
)) {
321 tunnel
->flags
|= TUNNEL_WRITER1
;
322 /* There's no IO_NOTNOW in bufWrite, so it might close the
323 file descriptor straight away. Wait until we're
325 bufWrite(tunnel
->fd1
, &tunnel
->buf2
, tunnelWrite1Handler
, tunnel
);
328 if(tunnel
->fd2
< 0 || (tunnel
->flags
& TUNNEL_EOF2
)) {
329 if(!(tunnel
->flags
& TUNNEL_EPIPE1
))
330 shutdown(tunnel
->fd1
, 1);
331 tunnel
->flags
|= TUNNEL_EPIPE1
;
332 } else if(tunnel
->fd1
< 0 || (tunnel
->flags
& TUNNEL_EPIPE2
)) {
333 if(!(tunnel
->flags
& TUNNEL_EOF1
))
334 shutdown(tunnel
->fd1
, 0);
335 tunnel
->flags
|= TUNNEL_EOF1
;
337 if((tunnel
->flags
& TUNNEL_EOF1
) && (tunnel
->flags
& TUNNEL_EPIPE1
)) {
338 if(!(tunnel
->flags
& (TUNNEL_READER1
| TUNNEL_WRITER1
))) {
345 if(tunnel
->fd2
>= 0) {
346 if(!(tunnel
->flags
& (TUNNEL_READER2
| TUNNEL_EOF2
)) &&
347 !circularBufferFull(&tunnel
->buf2
)) {
348 tunnel
->flags
|= TUNNEL_READER2
;
349 bufRead(tunnel
->fd2
, &tunnel
->buf2
, tunnelRead2Handler
, tunnel
);
351 if(!(tunnel
->flags
& (TUNNEL_WRITER2
| TUNNEL_EPIPE2
)) &&
352 !circularBufferEmpty(&tunnel
->buf1
)) {
353 tunnel
->flags
|= TUNNEL_WRITER2
;
354 bufWrite(tunnel
->fd2
, &tunnel
->buf1
, tunnelWrite2Handler
, tunnel
);
357 if(tunnel
->fd1
< 0 || (tunnel
->flags
& TUNNEL_EOF1
)) {
358 if(!(tunnel
->flags
& TUNNEL_EPIPE2
))
359 shutdown(tunnel
->fd2
, 1);
360 tunnel
->flags
|= TUNNEL_EPIPE2
;
361 } else if(tunnel
->fd1
< 0 || (tunnel
->flags
& TUNNEL_EPIPE1
)) {
362 if(!(tunnel
->flags
& TUNNEL_EOF2
))
363 shutdown(tunnel
->fd2
, 0);
364 tunnel
->flags
|= TUNNEL_EOF2
;
366 if((tunnel
->flags
& TUNNEL_EOF2
) && (tunnel
->flags
& TUNNEL_EPIPE2
)) {
367 if(!(tunnel
->flags
& (TUNNEL_READER2
| TUNNEL_WRITER2
))) {
374 if(tunnel
->fd1
< 0 && tunnel
->fd2
< 0)
375 destroyTunnel(tunnel
);
377 assert(tunnel
->flags
& (TUNNEL_READER1
| TUNNEL_WRITER1
|
378 TUNNEL_READER2
| TUNNEL_WRITER2
));
382 tunnelRead1Handler(int status
,
383 FdEventHandlerPtr event
, StreamRequestPtr request
)
385 TunnelPtr tunnel
= request
->data
;
388 do_log_error(L_ERROR
, -status
, "Couldn't read from client");
389 tunnel
->flags
|= TUNNEL_EOF1
;
392 tunnel
->buf1
.head
= request
->offset
% CHUNK_SIZE
;
394 tunnel
->flags
&= ~TUNNEL_READER1
;
395 tunnelDispatch(tunnel
);
400 tunnelRead2Handler(int status
,
401 FdEventHandlerPtr event
, StreamRequestPtr request
)
403 TunnelPtr tunnel
= request
->data
;
406 do_log_error(L_ERROR
, -status
, "Couldn't read from server");
407 tunnel
->flags
|= TUNNEL_EOF2
;
410 tunnel
->buf2
.head
= request
->offset
% CHUNK_SIZE
;
412 tunnel
->flags
&= ~TUNNEL_READER2
;
413 tunnelDispatch(tunnel
);
418 tunnelWrite1Handler(int status
,
419 FdEventHandlerPtr event
, StreamRequestPtr request
)
421 TunnelPtr tunnel
= request
->data
;
422 if(status
|| (tunnel
->flags
& TUNNEL_EPIPE1
)) {
423 tunnel
->flags
|= TUNNEL_EPIPE1
;
424 if(status
< 0 && status
!= -EPIPE
)
425 do_log_error(L_ERROR
, -status
, "Couldn't write to client");
428 tunnel
->buf2
.tail
= request
->offset
% CHUNK_SIZE
;
430 tunnel
->flags
&= ~TUNNEL_WRITER1
;
431 tunnelDispatch(tunnel
);
436 tunnelWrite2Handler(int status
,
437 FdEventHandlerPtr event
, StreamRequestPtr request
)
439 TunnelPtr tunnel
= request
->data
;
440 if(status
|| (tunnel
->flags
& TUNNEL_EPIPE2
)) {
441 tunnel
->flags
|= TUNNEL_EPIPE2
;
442 if(status
< 0 && status
!= -EPIPE
)
443 do_log_error(L_ERROR
, -status
, "Couldn't write to server");
446 tunnel
->buf1
.tail
= request
->offset
% CHUNK_SIZE
;
448 tunnel
->flags
&= ~TUNNEL_WRITER2
;
449 tunnelDispatch(tunnel
);
454 tunnelError(TunnelPtr tunnel
, int code
, AtomPtr message
)
457 if(tunnel
->fd2
> 0) {
462 if(tunnel
->buf2
.buf
== NULL
)
463 tunnel
->buf2
.buf
= get_chunk();
464 if(tunnel
->buf2
.buf
== NULL
)
467 n
= httpWriteErrorHeaders(tunnel
->buf2
.buf
, CHUNK_SIZE
- 1, 0,
468 1, code
, message
, 1, NULL
,
471 if(n
<= 0) goto fail
;
473 tunnel
->buf2
.head
= n
;
475 tunnelDispatch(tunnel
);
481 tunnelDispatch(tunnel
);