2 Copyright (c) 2003-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
27 AtomPtr socksParentProxy
= NULL
;
42 do_socks_connect(char *name
, int port
,
43 int (*handler
)(int, SocksRequestPtr
),
46 SocksRequestRec request
;
47 request
.name
= internAtomLowerN(name
, strlen(name
));
49 request
.handler
= handler
;
53 handler(-ENOSYS
, &request
);
54 releaseAtom(request
.name
);
60 AtomPtr socksParentProxy
= NULL
;
61 AtomPtr socksProxyHost
= NULL
;
62 int socksProxyPort
= -1;
63 AtomPtr socksProxyAddress
= NULL
;
64 int socksProxyAddressIndex
= -1;
65 AtomPtr socksUserName
= NULL
;
66 AtomPtr socksProxyType
= NULL
;
67 AtomPtr aSocks4a
, aSocks5
;
69 static int socksParentProxySetter(ConfigVariablePtr
, void*);
70 static int socksProxyTypeSetter(ConfigVariablePtr
, void*);
71 static int do_socks_connect_common(SocksRequestPtr
);
72 static int socksDnsHandler(int, GethostbynameRequestPtr
);
73 static int socksConnectHandler(int, FdEventHandlerPtr
, ConnectRequestPtr
);
74 static int socksWriteHandler(int, FdEventHandlerPtr
, StreamRequestPtr
);
75 static int socksReadHandler(int, FdEventHandlerPtr
, StreamRequestPtr
);
76 static int socks5ReadHandler(int, FdEventHandlerPtr
, StreamRequestPtr
);
77 static int socks5WriteHandler(int, FdEventHandlerPtr
, StreamRequestPtr
);
78 static int socks5ReadHandler2(int, FdEventHandlerPtr
, StreamRequestPtr
);
83 aSocks4a
= internAtom("socks4a");
84 aSocks5
= internAtom("socks5");
85 socksProxyType
= retainAtom(aSocks5
);
86 socksUserName
= internAtom("");
87 CONFIG_VARIABLE_SETTABLE(socksParentProxy
, CONFIG_ATOM_LOWER
,
88 socksParentProxySetter
,
89 "SOCKS parent proxy (host:port)");
90 CONFIG_VARIABLE_SETTABLE(socksUserName
, CONFIG_ATOM
,
93 CONFIG_VARIABLE_SETTABLE(socksProxyType
, CONFIG_ATOM_LOWER
,
95 "One of socks4a or socks5");
99 socksParentProxySetter(ConfigVariablePtr var
, void *value
)
101 configAtomSetter(var
, value
);
107 socksProxyTypeSetter(ConfigVariablePtr var
, void *value
)
109 if(*var
->value
.a
!= aSocks4a
&& *var
->value
.a
!= aSocks5
) {
110 do_log(L_ERROR
, "Unknown socksProxyType %s\n", (*var
->value
.a
)->string
);
114 return configAtomSetter(var
, value
);
121 AtomPtr host
= NULL
, port_atom
;
124 if(socksParentProxy
) {
125 rc
= atomSplit(socksParentProxy
, ':', &host
, &port_atom
);
127 do_log(L_ERROR
, "Couldn't parse socksParentProxy");
130 port
= atoi(port_atom
->string
);
131 releaseAtom(port_atom
);
135 releaseAtom(socksProxyHost
);
136 socksProxyHost
= host
;
137 socksProxyPort
= port
;
138 if(socksProxyAddress
)
139 releaseAtom(socksProxyAddress
);
140 socksProxyAddress
= NULL
;
141 socksProxyAddressIndex
= -1;
143 if(socksProxyType
!= aSocks4a
&& socksProxyType
!= aSocks5
) {
144 do_log(L_ERROR
, "Unknown socksProxyType %s\n", socksProxyType
->string
);
150 destroySocksRequest(SocksRequestPtr request
)
152 releaseAtom(request
->name
);
159 do_socks_connect(char *name
, int port
,
160 int (*handler
)(int, SocksRequestPtr
),
163 SocksRequestPtr request
= malloc(sizeof(SocksRequestRec
));
164 SocksRequestRec request_nomem
;
168 request
->name
= internAtomLowerN(name
, strlen(name
));
169 if(request
->name
== NULL
) {
174 request
->port
= port
;
176 request
->handler
= handler
;
178 request
->data
= data
;
180 if(socksProxyAddress
== NULL
) {
181 do_gethostbyname(socksProxyHost
->string
, 0,
187 return do_socks_connect_common(request
);
190 request_nomem
.name
= internAtomLowerN(name
, strlen(name
));
191 request_nomem
.port
= port
;
192 request_nomem
.handler
= handler
;
193 request_nomem
.buf
= NULL
;
194 request_nomem
.data
= data
;
196 handler(-ENOMEM
, &request_nomem
);
197 releaseAtom(request_nomem
.name
);
202 do_socks_connect_common(SocksRequestPtr request
)
204 assert(socksProxyAddressIndex
>= 0);
206 do_connect(retainAtom(socksProxyAddress
),
207 socksProxyAddressIndex
,
209 socksConnectHandler
, request
);
214 socksDnsHandler(int status
, GethostbynameRequestPtr grequest
)
216 SocksRequestPtr request
= grequest
->data
;
218 request
->handler(status
, request
);
219 destroySocksRequest(request
);
223 if(grequest
->addr
->string
[0] == DNS_CNAME
) {
224 if(grequest
->count
> 10) {
225 do_log(L_ERROR
, "DNS CNAME loop.\n");
226 request
->handler(-EDNS_CNAME_LOOP
, request
);
227 destroySocksRequest(request
);
230 do_gethostbyname(grequest
->addr
->string
+ 1, grequest
->count
+ 1,
231 httpServerConnectionDnsHandler
, request
);
236 socksProxyAddress
= retainAtom(grequest
->addr
);
237 socksProxyAddressIndex
= 0;
239 do_socks_connect_common(request
);
244 socksConnectHandler(int status
,
245 FdEventHandlerPtr event
,
246 ConnectRequestPtr crequest
)
248 SocksRequestPtr request
= crequest
->data
;
252 request
->handler(status
, request
);
253 destroySocksRequest(request
);
257 assert(request
->fd
< 0);
258 request
->fd
= crequest
->fd
;
259 socksProxyAddressIndex
= crequest
->index
;
261 rc
= setNodelay(request
->fd
, 1);
263 do_log_error(L_WARN
, errno
, "Couldn't disable Nagle's algorithm");
265 if(socksProxyType
== aSocks4a
) {
266 request
->buf
= malloc(8 +
267 socksUserName
->length
+ 1 +
268 request
->name
->length
+ 1);
269 if(request
->buf
== NULL
) {
272 request
->handler(-ENOMEM
, request
);
273 destroySocksRequest(request
);
277 request
->buf
[0] = 4; /* VN */
278 request
->buf
[1] = 1; /* CD = REQUEST */
279 request
->buf
[2] = (request
->port
>> 8) & 0xFF;
280 request
->buf
[3] = request
->port
& 0xFF;
281 request
->buf
[4] = request
->buf
[5] = request
->buf
[6] = 0;
284 memcpy(request
->buf
+ 8, socksUserName
->string
, socksUserName
->length
);
285 request
->buf
[8 + socksUserName
->length
] = '\0';
287 memcpy(request
->buf
+ 8 + socksUserName
->length
+ 1,
288 request
->name
->string
, request
->name
->length
);
289 request
->buf
[8 + socksUserName
->length
+ 1 + request
->name
->length
] =
292 do_stream(IO_WRITE
, request
->fd
, 0, request
->buf
,
293 8 + socksUserName
->length
+ 1 + request
->name
->length
+ 1,
294 socksWriteHandler
, request
);
295 } else if(socksProxyType
== aSocks5
) {
296 request
->buf
= malloc(8); /* 8 needed for the subsequent read */
297 if(request
->buf
== NULL
) {
300 request
->handler(-ENOMEM
, request
);
301 destroySocksRequest(request
);
305 request
->buf
[0] = 5; /* ver */
306 request
->buf
[1] = 1; /* nmethods */
307 request
->buf
[2] = 0; /* no authentication required */
308 do_stream(IO_WRITE
, request
->fd
, 0, request
->buf
, 3,
309 socksWriteHandler
, request
);
311 request
->handler(-EUNKNOWN
, request
);
317 socksWriteHandler(int status
,
318 FdEventHandlerPtr event
,
319 StreamRequestPtr srequest
)
321 SocksRequestPtr request
= srequest
->data
;
326 if(!streamRequestDone(srequest
)) {
328 status
= -ESOCKS_PROTOCOL
;
334 do_stream(IO_READ
| IO_NOTNOW
, request
->fd
, 0, request
->buf
, 8,
335 socksProxyType
== aSocks5
?
336 socks5ReadHandler
: socksReadHandler
,
343 request
->handler(status
, request
);
344 destroySocksRequest(request
);
349 socksReadHandler(int status
,
350 FdEventHandlerPtr event
,
351 StreamRequestPtr srequest
)
353 SocksRequestPtr request
= srequest
->data
;
358 if(srequest
->offset
< 8) {
360 status
= -ESOCKS_PROTOCOL
;
366 if(request
->buf
[0] != 0 || request
->buf
[1] != 90) {
367 if(request
->buf
[1] >= 91 && request
->buf
[1] <= 93)
368 status
= -(ESOCKS_PROTOCOL
+ request
->buf
[1] - 90);
370 status
= -ESOCKS_PROTOCOL
;
374 request
->handler(1, request
);
375 destroySocksRequest(request
);
381 request
->handler(status
, request
);
382 destroySocksRequest(request
);
387 socks5ReadHandler(int status
,
388 FdEventHandlerPtr event
,
389 StreamRequestPtr srequest
)
391 SocksRequestPtr request
= srequest
->data
;
396 if(srequest
->offset
< 2) {
398 status
= -ESOCKS_PROTOCOL
;
404 if(request
->buf
[0] != 5 || request
->buf
[1] != 0) {
405 status
= -ESOCKS_PROTOCOL
;
410 request
->buf
= malloc(5 + request
->name
->length
+ 2);
411 if(request
->buf
== NULL
) {
416 request
->buf
[0] = 5; /* ver */
417 request
->buf
[1] = 1; /* cmd */
418 request
->buf
[2] = 0; /* rsv */
419 request
->buf
[3] = 3; /* atyp */
420 request
->buf
[4] = request
->name
->length
;
421 memcpy(request
->buf
+ 5, request
->name
->string
, request
->name
->length
);
422 request
->buf
[5 + request
->name
->length
] = (request
->port
>> 8) & 0xFF;
423 request
->buf
[5 + request
->name
->length
+ 1] = request
->port
& 0xFF;
425 do_stream(IO_WRITE
, request
->fd
, 0,
426 request
->buf
, 5 + request
->name
->length
+ 2,
427 socks5WriteHandler
, request
);
433 request
->handler(status
, request
);
434 destroySocksRequest(request
);
439 socks5WriteHandler(int status
,
440 FdEventHandlerPtr event
,
441 StreamRequestPtr srequest
)
443 SocksRequestPtr request
= srequest
->data
;
448 if(!streamRequestDone(srequest
)) {
450 status
= -ESOCKS_PROTOCOL
;
456 do_stream(IO_READ
| IO_NOTNOW
, request
->fd
, 0, request
->buf
, 10,
457 socks5ReadHandler2
, request
);
461 request
->handler(status
, request
);
462 destroySocksRequest(request
);
467 socks5ReadHandler2(int status
,
468 FdEventHandlerPtr event
,
469 StreamRequestPtr srequest
)
471 SocksRequestPtr request
= srequest
->data
;
476 if(srequest
->offset
< 4) {
478 status
= -ESOCKS_PROTOCOL
;
484 if(request
->buf
[0] != 5) {
485 status
= -ESOCKS_PROTOCOL
;
489 if(request
->buf
[1] != 0) {
490 status
= -(ESOCKS5_BASE
+ request
->buf
[1]);
494 if(request
->buf
[3] != 1) {
495 status
= -ESOCKS_PROTOCOL
;
499 if(srequest
->offset
< 10)
502 request
->handler(1, request
);
503 destroySocksRequest(request
);
509 request
->handler(status
, request
);
510 destroySocksRequest(request
);