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 scksProxyType %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
) {
270 request
->handler(-ENOMEM
, request
);
271 destroySocksRequest(request
);
275 request
->buf
[0] = 4; /* VN */
276 request
->buf
[1] = 1; /* CD = REQUEST */
277 request
->buf
[2] = (request
->port
>> 8) & 0xFF;
278 request
->buf
[3] = request
->port
& 0xFF;
279 request
->buf
[4] = request
->buf
[5] = request
->buf
[6] = 0;
282 memcpy(request
->buf
+ 8, socksUserName
->string
, socksUserName
->length
);
283 request
->buf
[8 + socksUserName
->length
] = '\0';
285 memcpy(request
->buf
+ 8 + socksUserName
->length
+ 1,
286 request
->name
->string
, request
->name
->length
);
287 request
->buf
[8 + socksUserName
->length
+ 1 + request
->name
->length
] =
290 do_stream(IO_WRITE
, request
->fd
, 0, request
->buf
,
291 8 + socksUserName
->length
+ 1 + request
->name
->length
+ 1,
292 socksWriteHandler
, request
);
293 } else if(socksProxyType
== aSocks5
) {
294 request
->buf
= malloc(8); /* 8 needed for the subsequent read */
295 if(request
->buf
== NULL
) {
296 request
->handler(-ENOMEM
, request
);
297 destroySocksRequest(request
);
301 request
->buf
[0] = 5; /* ver */
302 request
->buf
[1] = 1; /* nmethods */
303 request
->buf
[2] = 0; /* no authentication required */
304 do_stream(IO_WRITE
, request
->fd
, 0, request
->buf
, 3,
305 socksWriteHandler
, request
);
307 request
->handler(-EUNKNOWN
, request
);
313 socksWriteHandler(int status
,
314 FdEventHandlerPtr event
,
315 StreamRequestPtr srequest
)
317 SocksRequestPtr request
= srequest
->data
;
322 if(!streamRequestDone(srequest
)) {
324 status
= -ESOCKS_PROTOCOL
;
330 do_stream(IO_READ
| IO_NOTNOW
, request
->fd
, 0, request
->buf
, 8,
331 socksProxyType
== aSocks5
?
332 socks5ReadHandler
: socksReadHandler
,
337 request
->handler(status
, request
);
338 destroySocksRequest(request
);
343 socksReadHandler(int status
,
344 FdEventHandlerPtr event
,
345 StreamRequestPtr srequest
)
347 SocksRequestPtr request
= srequest
->data
;
352 if(srequest
->offset
< 8) {
354 status
= -ESOCKS_PROTOCOL
;
360 if(request
->buf
[0] != 0 || request
->buf
[1] != 90) {
361 if(request
->buf
[1] >= 91 && request
->buf
[1] <= 93)
362 status
= -(ESOCKS_PROTOCOL
+ request
->buf
[1] - 90);
364 status
= -ESOCKS_PROTOCOL
;
368 request
->handler(1, request
);
369 destroySocksRequest(request
);
373 request
->handler(status
, request
);
374 destroySocksRequest(request
);
379 socks5ReadHandler(int status
,
380 FdEventHandlerPtr event
,
381 StreamRequestPtr srequest
)
383 SocksRequestPtr request
= srequest
->data
;
388 if(srequest
->offset
< 2) {
390 status
= -ESOCKS_PROTOCOL
;
396 if(request
->buf
[0] != 5 || request
->buf
[1] != 0) {
397 status
= -ESOCKS_PROTOCOL
;
402 request
->buf
= malloc(5 + request
->name
->length
+ 2);
403 if(request
->buf
== NULL
) {
408 request
->buf
[0] = 5; /* ver */
409 request
->buf
[1] = 1; /* cmd */
410 request
->buf
[2] = 0; /* rsv */
411 request
->buf
[3] = 3; /* atyp */
412 request
->buf
[4] = request
->name
->length
;
413 memcpy(request
->buf
+ 5, request
->name
->string
, request
->name
->length
);
414 request
->buf
[5 + request
->name
->length
] = (request
->port
>> 8) & 0xFF;
415 request
->buf
[5 + request
->name
->length
+ 1] = request
->port
& 0xFF;
417 do_stream(IO_WRITE
, request
->fd
, 0,
418 request
->buf
, 5 + request
->name
->length
+ 2,
419 socks5WriteHandler
, request
);
423 request
->handler(status
, request
);
424 destroySocksRequest(request
);
429 socks5WriteHandler(int status
,
430 FdEventHandlerPtr event
,
431 StreamRequestPtr srequest
)
433 SocksRequestPtr request
= srequest
->data
;
438 if(!streamRequestDone(srequest
)) {
440 status
= -ESOCKS_PROTOCOL
;
446 do_stream(IO_READ
| IO_NOTNOW
, request
->fd
, 0, request
->buf
, 10,
447 socks5ReadHandler2
, request
);
451 request
->handler(status
, request
);
452 destroySocksRequest(request
);
457 socks5ReadHandler2(int status
,
458 FdEventHandlerPtr event
,
459 StreamRequestPtr srequest
)
461 SocksRequestPtr request
= srequest
->data
;
466 if(srequest
->offset
< 4) {
468 status
= -ESOCKS_PROTOCOL
;
474 if(request
->buf
[0] != 5) {
475 status
= -ESOCKS_PROTOCOL
;
479 if(request
->buf
[1] != 0) {
480 status
= -(ESOCKS5_BASE
+ request
->buf
[1]);
484 if(request
->buf
[3] != 1) {
485 status
= -ESOCKS_PROTOCOL
;
489 if(srequest
->offset
< 10)
492 request
->handler(1, request
);
493 destroySocksRequest(request
);
497 request
->handler(status
, request
);
498 destroySocksRequest(request
);