Don't use all of physical memory -- just one quarter. Oops.
[polipo.git] / socks.c
blob78e2f9b4882d5c9572f9b169fdef04e71e4a1975
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) {
125 rc = atomSplit(socksParentProxy, ':', &host, &port_atom);
126 if(rc <= 0) {
127 do_log(L_ERROR, "Couldn't parse socksParentProxy");
128 exit(1);
130 port = atoi(port_atom->string);
131 releaseAtom(port_atom);
134 if(socksProxyHost)
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);
145 exit(1);
149 static void
150 destroySocksRequest(SocksRequestPtr request)
152 releaseAtom(request->name);
153 if(request->buf)
154 free(request->buf);
155 free(request);
159 do_socks_connect(char *name, int port,
160 int (*handler)(int, SocksRequestPtr),
161 void *data)
163 SocksRequestPtr request = malloc(sizeof(SocksRequestRec));
164 SocksRequestRec request_nomem;
165 if(request == NULL)
166 goto nomem;
168 request->name = internAtomLowerN(name, strlen(name));
169 if(request->name == NULL) {
170 free(request);
171 goto nomem;
174 request->port = port;
175 request->fd = -1;
176 request->handler = handler;
177 request->buf = NULL;
178 request->data = data;
180 if(socksProxyAddress == NULL) {
181 do_gethostbyname(socksProxyHost->string, 0,
182 socksDnsHandler,
183 request);
184 return 1;
187 return do_socks_connect_common(request);
189 nomem:
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);
198 return 1;
201 static int
202 do_socks_connect_common(SocksRequestPtr request)
204 assert(socksProxyAddressIndex >= 0);
206 do_connect(retainAtom(socksProxyAddress),
207 socksProxyAddressIndex,
208 socksProxyPort,
209 socksConnectHandler, request);
210 return 1;
213 static int
214 socksDnsHandler(int status, GethostbynameRequestPtr grequest)
216 SocksRequestPtr request = grequest->data;
217 if(status <= 0) {
218 request->handler(status, request);
219 destroySocksRequest(request);
220 return 1;
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);
228 return 1;
230 do_gethostbyname(grequest->addr->string + 1, grequest->count + 1,
231 httpServerConnectionDnsHandler, request);
232 return 1;
236 socksProxyAddress = retainAtom(grequest->addr);
237 socksProxyAddressIndex = 0;
239 do_socks_connect_common(request);
240 return 1;
243 static int
244 socksConnectHandler(int status,
245 FdEventHandlerPtr event,
246 ConnectRequestPtr crequest)
248 SocksRequestPtr request = crequest->data;
249 int rc;
251 if(status < 0) {
252 request->handler(status, request);
253 destroySocksRequest(request);
254 return 1;
257 assert(request->fd < 0);
258 request->fd = crequest->fd;
259 socksProxyAddressIndex = crequest->index;
261 rc = setNodelay(request->fd, 1);
262 if(rc < 0)
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 CLOSE(request->fd);
271 request->fd = -1;
272 request->handler(-ENOMEM, request);
273 destroySocksRequest(request);
274 return 1;
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;
282 request->buf[7] = 3;
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] =
290 '\0';
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) {
298 CLOSE(request->fd);
299 request->fd = -1;
300 request->handler(-ENOMEM, request);
301 destroySocksRequest(request);
302 return 1;
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);
310 } else {
311 request->handler(-EUNKNOWN, request);
313 return 1;
316 static int
317 socksWriteHandler(int status,
318 FdEventHandlerPtr event,
319 StreamRequestPtr srequest)
321 SocksRequestPtr request = srequest->data;
323 if(status < 0)
324 goto error;
326 if(!streamRequestDone(srequest)) {
327 if(status) {
328 status = -ESOCKS_PROTOCOL;
329 goto error;
331 return 0;
334 do_stream(IO_READ | IO_NOTNOW, request->fd, 0, request->buf, 8,
335 socksProxyType == aSocks5 ?
336 socks5ReadHandler : socksReadHandler,
337 request);
338 return 1;
340 error:
341 CLOSE(request->fd);
342 request->fd = -1;
343 request->handler(status, request);
344 destroySocksRequest(request);
345 return 1;
348 static int
349 socksReadHandler(int status,
350 FdEventHandlerPtr event,
351 StreamRequestPtr srequest)
353 SocksRequestPtr request = srequest->data;
355 if(status < 0)
356 goto error;
358 if(srequest->offset < 8) {
359 if(status) {
360 status = -ESOCKS_PROTOCOL;
361 goto error;
363 return 0;
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);
369 else
370 status = -ESOCKS_PROTOCOL;
371 goto error;
374 request->handler(1, request);
375 destroySocksRequest(request);
376 return 1;
378 error:
379 CLOSE(request->fd);
380 request->fd = -1;
381 request->handler(status, request);
382 destroySocksRequest(request);
383 return 1;
386 static int
387 socks5ReadHandler(int status,
388 FdEventHandlerPtr event,
389 StreamRequestPtr srequest)
391 SocksRequestPtr request = srequest->data;
393 if(status < 0)
394 goto error;
396 if(srequest->offset < 2) {
397 if(status) {
398 status = -ESOCKS_PROTOCOL;
399 goto error;
401 return 0;
404 if(request->buf[0] != 5 || request->buf[1] != 0) {
405 status = -ESOCKS_PROTOCOL;
406 goto error;
409 free(request->buf);
410 request->buf = malloc(5 + request->name->length + 2);
411 if(request->buf == NULL) {
412 status = -ENOMEM;
413 goto error;
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);
428 return 1;
430 error:
431 CLOSE(request->fd);
432 request->fd = -1;
433 request->handler(status, request);
434 destroySocksRequest(request);
435 return 1;
438 static int
439 socks5WriteHandler(int status,
440 FdEventHandlerPtr event,
441 StreamRequestPtr srequest)
443 SocksRequestPtr request = srequest->data;
445 if(status < 0)
446 goto error;
448 if(!streamRequestDone(srequest)) {
449 if(status) {
450 status = -ESOCKS_PROTOCOL;
451 goto error;
453 return 0;
456 do_stream(IO_READ | IO_NOTNOW, request->fd, 0, request->buf, 10,
457 socks5ReadHandler2, request);
458 return 1;
460 error:
461 request->handler(status, request);
462 destroySocksRequest(request);
463 return 1;
466 static int
467 socks5ReadHandler2(int status,
468 FdEventHandlerPtr event,
469 StreamRequestPtr srequest)
471 SocksRequestPtr request = srequest->data;
473 if(status < 0)
474 goto error;
476 if(srequest->offset < 4) {
477 if(status) {
478 status = -ESOCKS_PROTOCOL;
479 goto error;
481 return 0;
484 if(request->buf[0] != 5) {
485 status = -ESOCKS_PROTOCOL;
486 goto error;
489 if(request->buf[1] != 0) {
490 status = -(ESOCKS5_BASE + request->buf[1]);
491 goto error;
494 if(request->buf[3] != 1) {
495 status = -ESOCKS_PROTOCOL;
496 goto error;
499 if(srequest->offset < 10)
500 return 0;
502 request->handler(1, request);
503 destroySocksRequest(request);
504 return 1;
506 error:
507 CLOSE(request->fd);
508 request->fd = -1;
509 request->handler(status, request);
510 destroySocksRequest(request);
511 return 1;
514 #endif