Add -fno-strict-aliasing to prevent compile warnings on some systems.
[polipo.git] / socks.c
blobce8f600e54a128466b1726ee59ccde83507aa371
1 /*
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
20 THE SOFTWARE.
23 #include "polipo.h"
25 #ifdef NO_SOCKS
27 AtomPtr socksParentProxy = NULL;
29 void
30 preinitSocks()
32 return;
35 void
36 initSocks()
38 return;
41 int
42 do_socks_connect(char *name, int port,
43 int (*handler)(int, SocksRequestPtr),
44 void *data)
46 SocksRequestRec request;
47 request.name = internAtomLowerN(name, strlen(name));
48 request.port = port;
49 request.handler = handler;
50 request.buf = NULL;
51 request.data = data;
53 handler(-ENOSYS, &request);
54 releaseAtom(request.name);
55 return 1;
58 #else
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);
80 void
81 preinitSocks()
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,
91 configAtomSetter,
92 "SOCKS4a user name");
93 CONFIG_VARIABLE_SETTABLE(socksProxyType, CONFIG_ATOM_LOWER,
94 socksProxyTypeSetter,
95 "One of socks4a or socks5");
98 static int
99 socksParentProxySetter(ConfigVariablePtr var, void *value)
101 configAtomSetter(var, value);
102 initSocks();
103 return 1;
106 static int
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);
111 return -1;
114 return configAtomSetter(var, value);
117 void
118 initSocks()
120 int port = -1;
121 AtomPtr host = NULL, port_atom;
122 int rc;
124 if(socksParentProxy != NULL && socksParentProxy->length == 0) {
125 releaseAtom(socksParentProxy);
126 socksParentProxy = NULL;
129 if(socksParentProxy) {
130 rc = atomSplit(socksParentProxy, ':', &host, &port_atom);
131 if(rc <= 0) {
132 do_log(L_ERROR, "Couldn't parse socksParentProxy");
133 exit(1);
135 port = atoi(port_atom->string);
136 releaseAtom(port_atom);
139 if(socksProxyHost)
140 releaseAtom(socksProxyHost);
141 socksProxyHost = host;
142 socksProxyPort = port;
143 if(socksProxyAddress)
144 releaseAtom(socksProxyAddress);
145 socksProxyAddress = NULL;
146 socksProxyAddressIndex = -1;
148 if(socksProxyType != aSocks4a && socksProxyType != aSocks5) {
149 do_log(L_ERROR, "Unknown socksProxyType %s\n", socksProxyType->string);
150 exit(1);
154 static void
155 destroySocksRequest(SocksRequestPtr request)
157 releaseAtom(request->name);
158 if(request->buf)
159 free(request->buf);
160 free(request);
164 do_socks_connect(char *name, int port,
165 int (*handler)(int, SocksRequestPtr),
166 void *data)
168 SocksRequestPtr request = malloc(sizeof(SocksRequestRec));
169 SocksRequestRec request_nomem;
170 if(request == NULL)
171 goto nomem;
173 request->name = internAtomLowerN(name, strlen(name));
174 if(request->name == NULL) {
175 free(request);
176 goto nomem;
179 request->port = port;
180 request->fd = -1;
181 request->handler = handler;
182 request->buf = NULL;
183 request->data = data;
185 if(socksProxyAddress == NULL) {
186 do_gethostbyname(socksProxyHost->string, 0,
187 socksDnsHandler,
188 request);
189 return 1;
192 return do_socks_connect_common(request);
194 nomem:
195 request_nomem.name = internAtomLowerN(name, strlen(name));
196 request_nomem.port = port;
197 request_nomem.handler = handler;
198 request_nomem.buf = NULL;
199 request_nomem.data = data;
201 handler(-ENOMEM, &request_nomem);
202 releaseAtom(request_nomem.name);
203 return 1;
206 static int
207 do_socks_connect_common(SocksRequestPtr request)
209 assert(socksProxyAddressIndex >= 0);
211 do_connect(retainAtom(socksProxyAddress),
212 socksProxyAddressIndex,
213 socksProxyPort,
214 socksConnectHandler, request);
215 return 1;
218 static int
219 socksDnsHandler(int status, GethostbynameRequestPtr grequest)
221 SocksRequestPtr request = grequest->data;
222 if(status <= 0) {
223 request->handler(status, request);
224 destroySocksRequest(request);
225 return 1;
228 if(grequest->addr->string[0] == DNS_CNAME) {
229 if(grequest->count > 10) {
230 do_log(L_ERROR, "DNS CNAME loop.\n");
231 request->handler(-EDNS_CNAME_LOOP, request);
232 destroySocksRequest(request);
233 return 1;
235 do_gethostbyname(grequest->addr->string + 1, grequest->count + 1,
236 httpServerConnectionDnsHandler, request);
237 return 1;
241 socksProxyAddress = retainAtom(grequest->addr);
242 socksProxyAddressIndex = 0;
244 do_socks_connect_common(request);
245 return 1;
248 static int
249 socksConnectHandler(int status,
250 FdEventHandlerPtr event,
251 ConnectRequestPtr crequest)
253 SocksRequestPtr request = crequest->data;
254 int rc;
256 if(status < 0) {
257 request->handler(status, request);
258 destroySocksRequest(request);
259 return 1;
262 assert(request->fd < 0);
263 request->fd = crequest->fd;
264 socksProxyAddressIndex = crequest->index;
266 rc = setNodelay(request->fd, 1);
267 if(rc < 0)
268 do_log_error(L_WARN, errno, "Couldn't disable Nagle's algorithm");
270 if(socksProxyType == aSocks4a) {
271 request->buf = malloc(8 +
272 socksUserName->length + 1 +
273 request->name->length + 1);
274 if(request->buf == NULL) {
275 CLOSE(request->fd);
276 request->fd = -1;
277 request->handler(-ENOMEM, request);
278 destroySocksRequest(request);
279 return 1;
282 request->buf[0] = 4; /* VN */
283 request->buf[1] = 1; /* CD = REQUEST */
284 request->buf[2] = (request->port >> 8) & 0xFF;
285 request->buf[3] = request->port & 0xFF;
286 request->buf[4] = request->buf[5] = request->buf[6] = 0;
287 request->buf[7] = 3;
289 memcpy(request->buf + 8, socksUserName->string, socksUserName->length);
290 request->buf[8 + socksUserName->length] = '\0';
292 memcpy(request->buf + 8 + socksUserName->length + 1,
293 request->name->string, request->name->length);
294 request->buf[8 + socksUserName->length + 1 + request->name->length] =
295 '\0';
297 do_stream(IO_WRITE, request->fd, 0, request->buf,
298 8 + socksUserName->length + 1 + request->name->length + 1,
299 socksWriteHandler, request);
300 } else if(socksProxyType == aSocks5) {
301 request->buf = malloc(8); /* 8 needed for the subsequent read */
302 if(request->buf == NULL) {
303 CLOSE(request->fd);
304 request->fd = -1;
305 request->handler(-ENOMEM, request);
306 destroySocksRequest(request);
307 return 1;
310 request->buf[0] = 5; /* ver */
311 request->buf[1] = 1; /* nmethods */
312 request->buf[2] = 0; /* no authentication required */
313 do_stream(IO_WRITE, request->fd, 0, request->buf, 3,
314 socksWriteHandler, request);
315 } else {
316 request->handler(-EUNKNOWN, request);
318 return 1;
321 static int
322 socksWriteHandler(int status,
323 FdEventHandlerPtr event,
324 StreamRequestPtr srequest)
326 SocksRequestPtr request = srequest->data;
328 if(status < 0)
329 goto error;
331 if(!streamRequestDone(srequest)) {
332 if(status) {
333 status = -ESOCKS_PROTOCOL;
334 goto error;
336 return 0;
339 do_stream(IO_READ | IO_NOTNOW, request->fd, 0, request->buf, 8,
340 socksProxyType == aSocks5 ?
341 socks5ReadHandler : socksReadHandler,
342 request);
343 return 1;
345 error:
346 CLOSE(request->fd);
347 request->fd = -1;
348 request->handler(status, request);
349 destroySocksRequest(request);
350 return 1;
353 static int
354 socksReadHandler(int status,
355 FdEventHandlerPtr event,
356 StreamRequestPtr srequest)
358 SocksRequestPtr request = srequest->data;
360 if(status < 0)
361 goto error;
363 if(srequest->offset < 8) {
364 if(status) {
365 status = -ESOCKS_PROTOCOL;
366 goto error;
368 return 0;
371 if(request->buf[0] != 0 || request->buf[1] != 90) {
372 if(request->buf[1] >= 91 && request->buf[1] <= 93)
373 status = -(ESOCKS_PROTOCOL + request->buf[1] - 90);
374 else
375 status = -ESOCKS_PROTOCOL;
376 goto error;
379 request->handler(1, request);
380 destroySocksRequest(request);
381 return 1;
383 error:
384 CLOSE(request->fd);
385 request->fd = -1;
386 request->handler(status, request);
387 destroySocksRequest(request);
388 return 1;
391 static int
392 socks5ReadHandler(int status,
393 FdEventHandlerPtr event,
394 StreamRequestPtr srequest)
396 SocksRequestPtr request = srequest->data;
398 if(status < 0)
399 goto error;
401 if(srequest->offset < 2) {
402 if(status) {
403 status = -ESOCKS_PROTOCOL;
404 goto error;
406 return 0;
409 if(request->buf[0] != 5 || request->buf[1] != 0) {
410 status = -ESOCKS_PROTOCOL;
411 goto error;
414 free(request->buf);
415 request->buf = malloc(5 + request->name->length + 2);
416 if(request->buf == NULL) {
417 status = -ENOMEM;
418 goto error;
421 request->buf[0] = 5; /* ver */
422 request->buf[1] = 1; /* cmd */
423 request->buf[2] = 0; /* rsv */
424 request->buf[3] = 3; /* atyp */
425 request->buf[4] = request->name->length;
426 memcpy(request->buf + 5, request->name->string, request->name->length);
427 request->buf[5 + request->name->length] = (request->port >> 8) & 0xFF;
428 request->buf[5 + request->name->length + 1] = request->port & 0xFF;
430 do_stream(IO_WRITE, request->fd, 0,
431 request->buf, 5 + request->name->length + 2,
432 socks5WriteHandler, request);
433 return 1;
435 error:
436 CLOSE(request->fd);
437 request->fd = -1;
438 request->handler(status, request);
439 destroySocksRequest(request);
440 return 1;
443 static int
444 socks5WriteHandler(int status,
445 FdEventHandlerPtr event,
446 StreamRequestPtr srequest)
448 SocksRequestPtr request = srequest->data;
450 if(status < 0)
451 goto error;
453 if(!streamRequestDone(srequest)) {
454 if(status) {
455 status = -ESOCKS_PROTOCOL;
456 goto error;
458 return 0;
461 do_stream(IO_READ | IO_NOTNOW, request->fd, 0, request->buf, 10,
462 socks5ReadHandler2, request);
463 return 1;
465 error:
466 request->handler(status, request);
467 destroySocksRequest(request);
468 return 1;
471 static int
472 socks5ReadHandler2(int status,
473 FdEventHandlerPtr event,
474 StreamRequestPtr srequest)
476 SocksRequestPtr request = srequest->data;
478 if(status < 0)
479 goto error;
481 if(srequest->offset < 4) {
482 if(status) {
483 status = -ESOCKS_PROTOCOL;
484 goto error;
486 return 0;
489 if(request->buf[0] != 5) {
490 status = -ESOCKS_PROTOCOL;
491 goto error;
494 if(request->buf[1] != 0) {
495 status = -(ESOCKS5_BASE + request->buf[1]);
496 goto error;
499 if(request->buf[3] != 1) {
500 status = -ESOCKS_PROTOCOL;
501 goto error;
504 if(srequest->offset < 10)
505 return 0;
507 request->handler(1, request);
508 destroySocksRequest(request);
509 return 1;
511 error:
512 CLOSE(request->fd);
513 request->fd = -1;
514 request->handler(status, request);
515 destroySocksRequest(request);
516 return 1;
519 #endif