1 /***************************************************************************
2 * nsock_proxy.c -- This contains the functions relating to proxies. *
3 * *
4 ***********************IMPORTANT NSOCK LICENSE TERMS***********************
5 * *
6 * The nsock parallel socket event library is (C) 1999-2020 Insecure.Com *
7 * LLC This library is free software; you may redistribute and/or *
8 * modify it under the terms of the GNU General Public License as *
9 * published by the Free Software Foundation; Version 2. This guarantees *
10 * your right to use, modify, and redistribute this software under certain *
11 * conditions. If this license is unacceptable to you, Insecure.Com LLC *
12 * may be willing to sell alternative licenses (contact *
13 * sales@insecure.com ). *
14 * *
15 * As a special exception to the GPL terms, Insecure.Com LLC grants *
16 * permission to link the code of this program with any version of the *
17 * OpenSSL library which is distributed under a license identical to that *
18 * listed in the included docs/licenses/OpenSSL.txt file, and distribute *
19 * linked combinations including the two. You must obey the GNU GPL in all *
20 * respects for all of the code used other than OpenSSL. If you modify *
21 * this file, you may extend this exception to your version of the file, *
22 * but you are not obligated to do so. *
23 * *
24 * If you received these files with a written license agreement stating *
25 * terms other than the (GPL) terms above, then that alternative license *
26 * agreement takes precedence over this comment. *
27 * *
28 * Source is provided to this software because we believe users have a *
29 * right to know exactly what a program is going to do before they run it. *
30 * This also allows you to audit the software for security holes. *
31 * *
32 * Source code also allows you to port Nmap to new platforms, fix bugs, *
33 * and add new features. You are highly encouraged to send your changes *
34 * to the dev@nmap.org mailing list for possible incorporation into the *
35 * main distribution. By sending these changes to Fyodor or one of the *
36 * Insecure.Org development mailing lists, or checking them into the Nmap *
37 * source code repository, it is understood (unless you specify otherwise) *
38 * that you are offering the Nmap Project (Insecure.Com LLC) the *
39 * unlimited, non-exclusive right to reuse, modify, and relicense the *
40 * code. Nmap will always be available Open Source, but this is important *
41 * because the inability to relicense code has caused devastating problems *
42 * for other Free Software projects (such as KDE and NASM). We also *
43 * occasionally relicense the code to third parties as discussed above. *
44 * If you wish to specify special license conditions of your *
45 * contributions, just say so when you send them. *
46 * *
47 * This program is distributed in the hope that it will be useful, but *
48 * WITHOUT ANY WARRANTY; without even the implied warranty of *
49 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
50 * General Public License v2.0 for more details *
51 * (http://www.gnu.org/licenses/gpl-2.0.html). *
52 * *
53 ***************************************************************************/
54
55 /* $Id$ */
56
57 #include "nsock.h"
58 #include "nsock_internal.h"
59 #include "nsock_log.h"
60 #include <string.h>
61
62 #define IN_RANGE(x, min, max) ((x) >= (min) && (x) <= (max))
63
64
65 struct proxy_parser {
66 int done;
67 struct proxy_node *value;
68 char *str;
69 char *tokens;
70 };
71
72 static struct proxy_parser *proxy_parser_new(const char *proxychainstr);
73 static void proxy_parser_next(struct proxy_parser *parser);
74 static void proxy_parser_delete(struct proxy_parser *parser);
75
76
77 /* --- Implemented proxy backends --- */
78 extern const struct proxy_spec ProxySpecHttp;
79 extern const struct proxy_spec ProxySpecSocks4;
80
81
82 static const struct proxy_spec *ProxyBackends[] = {
83 &ProxySpecHttp,
84 &ProxySpecSocks4,
85 NULL
86 };
87
88
89 /* A proxy chain is a comma-separated list of proxy specification strings:
90 * proto://[user:pass@]host[:port] */
nsock_proxychain_new(const char * proxystr,nsock_proxychain * chain,nsock_pool nspool)91 int nsock_proxychain_new(const char *proxystr, nsock_proxychain *chain, nsock_pool nspool) {
92 struct npool *nsp = (struct npool *)nspool;
93 struct proxy_chain *pxc, **pchain = (struct proxy_chain **)chain;
94
95 *pchain = NULL;
96
97 pxc = (struct proxy_chain *)safe_malloc(sizeof(struct proxy_chain));
98 gh_list_init(&pxc->nodes);
99
100 if (proxystr) {
101 struct proxy_parser *parser;
102
103 parser = proxy_parser_new(proxystr);
104 while (!parser->done) {
105 gh_list_append(&pxc->nodes, &parser->value->nodeq);
106 proxy_parser_next(parser);
107 }
108 proxy_parser_delete(parser);
109 }
110
111 if (nsp) {
112 if (nsock_pool_set_proxychain(nspool, pxc) < 0) {
113 nsock_proxychain_delete(pxc);
114 return -1;
115 }
116 }
117
118 *pchain = pxc;
119 return 1;
120 }
121
nsock_proxychain_delete(nsock_proxychain chain)122 void nsock_proxychain_delete(nsock_proxychain chain) {
123 struct proxy_chain *pchain = (struct proxy_chain *)chain;
124 gh_lnode_t *lnode;
125
126 if (!pchain)
127 return;
128
129 while ((lnode = gh_list_pop(&pchain->nodes)) != NULL) {
130 struct proxy_node *node;
131
132 node = container_of(lnode, struct proxy_node, nodeq);
133 node->spec->ops->node_delete(node);
134 }
135
136 gh_list_free(&pchain->nodes);
137 free(pchain);
138 }
139
nsock_pool_set_proxychain(nsock_pool nspool,nsock_proxychain chain)140 int nsock_pool_set_proxychain(nsock_pool nspool, nsock_proxychain chain) {
141 struct npool *nsp = (struct npool *)nspool;
142 assert(nsp != NULL);
143
144 if (nsp && nsp->px_chain) {
145 nsock_log_error("Invalid call. Existing proxychain on this nsock_pool");
146 return -1;
147 }
148
149 nsp->px_chain = (struct proxy_chain *)chain;
150 return 1;
151 }
152
proxy_chain_context_new(nsock_pool nspool)153 struct proxy_chain_context *proxy_chain_context_new(nsock_pool nspool) {
154 struct npool *nsp = (struct npool *)nspool;
155 struct proxy_chain_context *ctx;
156
157 ctx = (struct proxy_chain_context *)safe_malloc(sizeof(struct proxy_chain_context));
158 ctx->px_chain = nsp->px_chain;
159 ctx->px_state = PROXY_STATE_INITIAL;
160 ctx->px_current = container_of(gh_list_first_elem(&nsp->px_chain->nodes),
161 struct proxy_node,
162 nodeq);
163 return ctx;
164 }
165
proxy_chain_context_delete(struct proxy_chain_context * ctx)166 void proxy_chain_context_delete(struct proxy_chain_context *ctx) {
167 free(ctx);
168 }
169
uri_free(struct uri * uri)170 static void uri_free(struct uri *uri) {
171 free(uri->scheme);
172 free(uri->user);
173 free(uri->pass);
174 free(uri->host);
175 free(uri->path);
176 }
177
lowercase(char * s)178 static int lowercase(char *s) {
179 char *p;
180
181 for (p = s; *p != '\0'; p++)
182 *p = tolower((int) (unsigned char) *p);
183
184 return p - s;
185 }
186
hex_digit_value(char digit)187 static int hex_digit_value(char digit) {
188 static const char DIGITS[] = "0123456789abcdef";
189 const char *p;
190
191 if ((unsigned char)digit == '\0')
192 return -1;
193
194 p = strchr(DIGITS, tolower((int)(unsigned char)digit));
195 if (p == NULL)
196 return -1;
197
198 return p - DIGITS;
199 }
200
percent_decode(char * s)201 static int percent_decode(char *s) {
202 char *p, *q;
203
204 /* Skip to the first '%'. If there are no percent escapes, this lets us
205 * return without doing any copying. */
206 q = s;
207 while (*q != '\0' && *q != '%')
208 q++;
209
210 p = q;
211 while (*q != '\0') {
212 if (*q == '%') {
213 int c, d;
214
215 q++;
216 c = hex_digit_value(*q);
217 if (c == -1)
218 return -1;
219 q++;
220 d = hex_digit_value(*q);
221 if (d == -1)
222 return -1;
223
224 *p++ = c * 16 + d;
225 q++;
226 } else {
227 *p++ = *q++;
228 }
229 }
230 *p = '\0';
231
232 return p - s;
233 }
234
uri_parse_authority(const char * authority,struct uri * uri)235 static int uri_parse_authority(const char *authority, struct uri *uri) {
236 const char *portsep;
237 const char *host_start, *host_end;
238 char *tail;
239
240 /* We do not support "user:pass@" userinfo. The proxy has no use for it. */
241 if (strchr(authority, '@') != NULL)
242 return -1;
243
244 /* Find the beginning and end of the host. */
245 host_start = authority;
246
247 if (*host_start == '[') {
248 /* IPv6 address in brackets. */
249 host_start++;
250 host_end = strchr(host_start, ']');
251
252 if (host_end == NULL)
253 return -1;
254
255 portsep = host_end + 1;
256
257 if (!(*portsep == ':' || *portsep == '\0'))
258 return -1;
259
260 } else {
261 portsep = strrchr(authority, ':');
262
263 if (portsep == NULL)
264 portsep = strchr(authority, '\0');
265 host_end = portsep;
266 }
267
268 /* Get the port number. */
269 if (*portsep == ':' && *(portsep + 1) != '\0') {
270 long n;
271
272 errno = 0;
273 n = parse_long(portsep + 1, &tail);
274 if (errno || *tail || (tail == (portsep + 1)) || !IN_RANGE(n, 1, 65535))
275 return -1;
276 uri->port = n;
277 } else {
278 uri->port = -1;
279 }
280
281 /* Get the host. */
282 uri->host = mkstr(host_start, host_end);
283 if (percent_decode(uri->host) < 0) {
284 free(uri->host);
285 uri->host = NULL;
286 return -1;
287 }
288
289 return 1;
290 }
291
parse_uri(const char * proxystr,struct uri * uri)292 static int parse_uri(const char *proxystr, struct uri *uri) {
293 const char *p, *q;
294
295 /* Scheme, section 3.1. */
296 p = proxystr;
297 if (!isalpha(*p))
298 goto fail;
299
300 q = p;
301 while (isalpha(*q) || isdigit(*q) || *q == '+' || *q == '-' || *q == '.')
302 q++;
303
304 if (*q != ':')
305 goto fail;
306
307 uri->scheme = mkstr(p, q);
308
309 /* "An implementation should accept uppercase letters as equivalent to
310 * lowercase in scheme names (e.g., allow "HTTP" as well as "http") for the
311 * sake of robustness..." */
312 lowercase(uri->scheme);
313
314 /* Authority, section 3.2. */
315 p = q + 1;
316 if (*p == '/' && *(p + 1) == '/') {
317 char *authority = NULL;
318
319 p += 2;
320 q = p;
321 while (!(*q == '/' || *q == '?' || *q == '#' || *q == '\0'))
322 q++;
323 ;
324 authority = mkstr(p, q);
325 if (uri_parse_authority(authority, uri) < 0) {
326 free(authority);
327 goto fail;
328 }
329 free(authority);
330
331 p = q;
332 }
333
334 /* Path, section 3.3. We include the query and fragment in the path. The
335 * path is also not percent-decoded because we just pass it on to the origin
336 * server. */
337
338 q = strchr(p, '\0');
339 uri->path = mkstr(p, q);
340
341 return 1;
342
343 fail:
344 uri_free(uri);
345 return -1;
346 }
347
proxy_node_new(char * proxystr)348 static struct proxy_node *proxy_node_new(char *proxystr) {
349 int i;
350
351 for (i = 0; ProxyBackends[i] != NULL; i++) {
352 const struct proxy_spec *pspec;
353
354 pspec = ProxyBackends[i];
355 if (strncasecmp(proxystr, pspec->prefix, strlen(pspec->prefix)) == 0) {
356 struct proxy_node *proxy = NULL;
357 struct uri uri;
358
359 memset(&uri, 0x00, sizeof(struct uri));
360
361 if (parse_uri(proxystr, &uri) < 0)
362 break;
363
364 if (pspec->ops->node_new(&proxy, &uri) < 0)
365 fatal("Cannot initialize proxy node %s", proxystr);
366
367 uri_free(&uri);
368
369 return proxy;
370 }
371 }
372 fatal("Invalid protocol in proxy specification string: %s", proxystr);
373 return NULL;
374 }
375
proxy_parser_new(const char * proxychainstr)376 struct proxy_parser *proxy_parser_new(const char *proxychainstr) {
377 struct proxy_parser *parser;
378
379 parser = (struct proxy_parser *)safe_malloc(sizeof(struct proxy_parser));
380 parser->done = 0;
381 parser->value = NULL;
382
383 parser->str = strdup(proxychainstr);
384
385 parser->tokens = strtok(parser->str, ",");
386 if (parser->tokens)
387 parser->value = proxy_node_new(parser->tokens);
388 else
389 parser->done = 1;
390
391 return parser;
392 }
393
proxy_parser_next(struct proxy_parser * parser)394 void proxy_parser_next(struct proxy_parser *parser) {
395
396 parser->tokens = strtok(NULL, ",");
397 if (parser->tokens)
398 parser->value = proxy_node_new(parser->tokens);
399 else
400 parser->done = 1;
401 }
402
proxy_parser_delete(struct proxy_parser * parser)403 void proxy_parser_delete(struct proxy_parser *parser) {
404 if (parser) {
405 free(parser->str);
406 free(parser);
407 }
408 }
409
forward_event(nsock_pool nspool,nsock_event nsevent,void * udata)410 void forward_event(nsock_pool nspool, nsock_event nsevent, void *udata) {
411 struct npool *nsp = (struct npool *)nspool;
412 struct nevent *nse = (struct nevent *)nsevent;
413 enum nse_type cached_type;
414 enum nse_status cached_status;
415
416 cached_type = nse->type;
417 cached_status = nse->status;
418
419 nse->type = nse->iod->px_ctx->target_ev_type;
420
421 if (nse->status != NSE_STATUS_SUCCESS)
422 nse->status = NSE_STATUS_PROXYERROR;
423
424 nsock_log_info("Forwarding event upstream: TCP connect %s (IOD #%li) EID %li",
425 nse_status2str(nse->status), nse->iod->id, nse->id);
426
427 nse->iod->px_ctx->target_handler(nsp, nse, udata);
428
429 nse->type = cached_type;
430 nse->status = cached_status;
431 }
432
nsock_proxy_ev_dispatch(nsock_pool nspool,nsock_event nsevent,void * udata)433 void nsock_proxy_ev_dispatch(nsock_pool nspool, nsock_event nsevent, void *udata) {
434 struct nevent *nse = (struct nevent *)nsevent;
435
436 if (nse->status == NSE_STATUS_SUCCESS) {
437 struct proxy_node *current;
438
439 current = nse->iod->px_ctx->px_current;
440 assert(current);
441 current->spec->ops->handler(nspool, nsevent, udata);
442 } else {
443 forward_event(nspool, nsevent, udata);
444 }
445 }
446
proxy_resolve(const char * host,struct sockaddr * addr,size_t * addrlen)447 int proxy_resolve(const char *host, struct sockaddr *addr, size_t *addrlen) {
448 struct addrinfo *res;
449 int rc;
450
451 rc = getaddrinfo(host, NULL, NULL, &res);
452 if (rc)
453 return -abs(rc);
454
455 *addr = *res->ai_addr;
456 *addrlen = res->ai_addrlen;
457 freeaddrinfo(res);
458 return 1;
459 }
460
461