Implement CACHE_MISMATCH.
[polipo.git] / socks.c
blobd76baa0d9c0ab44e9342c2d5718744850f7c1910
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 scksProxyType %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 request->handler(-ENOMEM, request);
271 destroySocksRequest(request);
272 return 1;
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;
280 request->buf[7] = 3;
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] =
288 '\0';
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);
298 return 1;
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);
306 } else {
307 request->handler(-EUNKNOWN, request);
309 return 1;
312 static int
313 socksWriteHandler(int status,
314 FdEventHandlerPtr event,
315 StreamRequestPtr srequest)
317 SocksRequestPtr request = srequest->data;
319 if(status < 0)
320 goto error;
322 if(!streamRequestDone(srequest)) {
323 if(status) {
324 status = -ESOCKS_PROTOCOL;
325 goto error;
327 return 0;
330 do_stream(IO_READ | IO_NOTNOW, request->fd, 0, request->buf, 8,
331 socksProxyType == aSocks5 ?
332 socks5ReadHandler : socksReadHandler,
333 request);
334 return 1;
336 error:
337 request->handler(status, request);
338 destroySocksRequest(request);
339 return 1;
342 static int
343 socksReadHandler(int status,
344 FdEventHandlerPtr event,
345 StreamRequestPtr srequest)
347 SocksRequestPtr request = srequest->data;
349 if(status < 0)
350 goto error;
352 if(srequest->offset < 8) {
353 if(status) {
354 status = -ESOCKS_PROTOCOL;
355 goto error;
357 return 0;
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);
363 else
364 status = -ESOCKS_PROTOCOL;
365 goto error;
368 request->handler(1, request);
369 destroySocksRequest(request);
370 return 1;
372 error:
373 request->handler(status, request);
374 destroySocksRequest(request);
375 return 1;
378 static int
379 socks5ReadHandler(int status,
380 FdEventHandlerPtr event,
381 StreamRequestPtr srequest)
383 SocksRequestPtr request = srequest->data;
385 if(status < 0)
386 goto error;
388 if(srequest->offset < 2) {
389 if(status) {
390 status = -ESOCKS_PROTOCOL;
391 goto error;
393 return 0;
396 if(request->buf[0] != 5 || request->buf[1] != 0) {
397 status = -ESOCKS_PROTOCOL;
398 goto error;
401 free(request->buf);
402 request->buf = malloc(5 + request->name->length + 2);
403 if(request->buf == NULL) {
404 status = -ENOMEM;
405 goto error;
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);
420 return 1;
422 error:
423 request->handler(status, request);
424 destroySocksRequest(request);
425 return 1;
428 static int
429 socks5WriteHandler(int status,
430 FdEventHandlerPtr event,
431 StreamRequestPtr srequest)
433 SocksRequestPtr request = srequest->data;
435 if(status < 0)
436 goto error;
438 if(!streamRequestDone(srequest)) {
439 if(status) {
440 status = -ESOCKS_PROTOCOL;
441 goto error;
443 return 0;
446 do_stream(IO_READ | IO_NOTNOW, request->fd, 0, request->buf, 10,
447 socks5ReadHandler2, request);
448 return 1;
450 error:
451 request->handler(status, request);
452 destroySocksRequest(request);
453 return 1;
456 static int
457 socks5ReadHandler2(int status,
458 FdEventHandlerPtr event,
459 StreamRequestPtr srequest)
461 SocksRequestPtr request = srequest->data;
463 if(status < 0)
464 goto error;
466 if(srequest->offset < 4) {
467 if(status) {
468 status = -ESOCKS_PROTOCOL;
469 goto error;
471 return 0;
474 if(request->buf[0] != 5) {
475 status = -ESOCKS_PROTOCOL;
476 goto error;
479 if(request->buf[1] != 0) {
480 status = -(ESOCKS5_BASE + request->buf[1]);
481 goto error;
484 if(request->buf[3] != 1) {
485 status = -ESOCKS_PROTOCOL;
486 goto error;
489 if(srequest->offset < 10)
490 return 0;
492 request->handler(1, request);
493 destroySocksRequest(request);
494 return 1;
496 error:
497 request->handler(status, request);
498 destroySocksRequest(request);
499 return 1;
502 #endif