core: use string instead of compound literal
[rofl0r-proxychains-ng.git] / src / libproxychains.c
blob84a8f004cb5e8955e8c8c3954d4c3c990a7b18af
1 /***************************************************************************
2 libproxychains.c - description
3 -------------------
4 begin : Tue May 14 2002
5 copyright : netcreature (C) 2002
6 email : netcreature@users.sourceforge.net
7 ***************************************************************************/
8 /* GPL */
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
18 #undef _GNU_SOURCE
19 #define _GNU_SOURCE
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <limits.h>
26 #include <ctype.h>
27 #include <errno.h>
28 #include <assert.h>
29 #include <netdb.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <fcntl.h>
36 #include <dlfcn.h>
37 #include <pthread.h>
40 #include "core.h"
41 #include "common.h"
42 #include "rdns.h"
44 #undef satosin
45 #define satosin(x) ((struct sockaddr_in *) &(x))
46 #define SOCKADDR(x) (satosin(x)->sin_addr.s_addr)
47 #define SOCKADDR_2(x) (satosin(x)->sin_addr)
48 #define SOCKPORT(x) (satosin(x)->sin_port)
49 #define SOCKFAMILY(x) (satosin(x)->sin_family)
50 #define MAX_CHAIN 512
52 #ifdef IS_SOLARIS
53 #undef connect
54 int __xnet_connect(int sock, const struct sockaddr *addr, unsigned int len);
55 connect_t true___xnet_connect;
56 #endif
58 close_t true_close;
59 close_range_t true_close_range;
60 connect_t true_connect;
61 gethostbyname_t true_gethostbyname;
62 getaddrinfo_t true_getaddrinfo;
63 freeaddrinfo_t true_freeaddrinfo;
64 getnameinfo_t true_getnameinfo;
65 gethostbyaddr_t true_gethostbyaddr;
66 sendto_t true_sendto;
68 int tcp_read_time_out;
69 int tcp_connect_time_out;
70 chain_type proxychains_ct;
71 proxy_data proxychains_pd[MAX_CHAIN];
72 unsigned int proxychains_proxy_count = 0;
73 unsigned int proxychains_proxy_offset = 0;
74 int proxychains_got_chain_data = 0;
75 unsigned int proxychains_max_chain = 1;
76 int proxychains_quiet_mode = 0;
77 enum dns_lookup_flavor proxychains_resolver = DNSLF_LIBC;
78 localaddr_arg localnet_addr[MAX_LOCALNET];
79 size_t num_localnet_addr = 0;
80 dnat_arg dnats[MAX_DNAT];
81 size_t num_dnats = 0;
82 unsigned int remote_dns_subnet = 224;
84 pthread_once_t init_once = PTHREAD_ONCE_INIT;
86 static int init_l = 0;
88 static void get_chain_data(proxy_data * pd, unsigned int *proxy_count, chain_type * ct);
90 static void* load_sym(char* symname, void* proxyfunc, int is_mandatory) {
91 void *funcptr = dlsym(RTLD_NEXT, symname);
93 if(is_mandatory && !funcptr) {
94 fprintf(stderr, "Cannot load symbol '%s' %s\n", symname, dlerror());
95 exit(1);
96 } else if (!funcptr) {
97 return funcptr;
98 } else {
99 PDEBUG("loaded symbol '%s'" " real addr %p wrapped addr %p\n", symname, funcptr, proxyfunc);
101 if(funcptr == proxyfunc) {
102 PDEBUG("circular reference detected, aborting!\n");
103 abort();
105 return funcptr;
108 #include "allocator_thread.h"
110 const char *proxychains_get_version(void);
112 static void setup_hooks(void);
114 typedef struct {
115 unsigned int first, last, flags;
116 } close_range_args_t;
118 /* If there is some `close` or `close_range` system call before do_init,
119 we buffer it, and actually execute them in do_init. */
120 static int close_fds[16];
121 static int close_fds_cnt = 0;
122 static close_range_args_t close_range_buffer[16];
123 static int close_range_buffer_cnt = 0;
125 static unsigned get_rand_seed(void) {
126 #ifdef HAVE_CLOCK_GETTIME
127 struct timespec now;
128 clock_gettime(CLOCK_REALTIME, &now);
129 return now.tv_sec ^ now.tv_nsec;
130 #else
131 return time(NULL);
132 #endif
135 static void do_init(void) {
136 char *env;
138 srand(get_rand_seed());
139 core_initialize();
141 env = getenv(PROXYCHAINS_QUIET_MODE_ENV_VAR);
142 if(env && *env == '1')
143 proxychains_quiet_mode = 1;
145 proxychains_write_log(LOG_PREFIX "DLL init: proxychains-ng %s\n", proxychains_get_version());
147 setup_hooks();
149 /* read the config file */
150 get_chain_data(proxychains_pd, &proxychains_proxy_count, &proxychains_ct);
151 DUMP_PROXY_CHAIN(proxychains_pd, proxychains_proxy_count);
153 while(close_fds_cnt) true_close(close_fds[--close_fds_cnt]);
154 while(close_range_buffer_cnt) {
155 int i = --close_range_buffer_cnt;
156 true_close_range(close_range_buffer[i].first, close_range_buffer[i].last, close_range_buffer[i].flags);
158 init_l = 1;
160 rdns_init(proxychains_resolver);
163 static void init_lib_wrapper(const char* caller) {
164 #ifndef DEBUG
165 (void) caller;
166 #endif
167 if(!init_l) PDEBUG("%s called from %s\n", __FUNCTION__, caller);
168 pthread_once(&init_once, do_init);
171 /* if we use gcc >= 3, we can instruct the dynamic loader
172 * to call init_lib at link time. otherwise it gets loaded
173 * lazily, which has the disadvantage that there's a potential
174 * race condition if 2 threads call it before init_l is set
175 * and PTHREAD support was disabled */
176 #if __GNUC__+0 > 2
177 __attribute__((constructor))
178 static void gcc_init(void) {
179 init_lib_wrapper(__FUNCTION__);
181 #define INIT() do {} while(0)
182 #else
183 #define INIT() init_lib_wrapper(__FUNCTION__)
184 #endif
187 typedef enum {
188 RS_PT_NONE = 0,
189 RS_PT_SOCKS4,
190 RS_PT_SOCKS5,
191 RS_PT_HTTP
192 } rs_proxyType;
195 proxy_from_string() taken from rocksock network I/O library (C) rofl0r
196 valid inputs:
197 socks5://user:password@proxy.domain.com:port
198 socks5://proxy.domain.com:port
199 socks4://proxy.domain.com:port
200 http://user:password@proxy.domain.com:port
201 http://proxy.domain.com:port
203 supplying port number is obligatory.
204 user:pass@ part is optional for http and socks5.
205 however, user:pass authentication is currently not implemented for http proxies.
206 return 1 on success, 0 on error.
208 static int proxy_from_string(const char *proxystring,
209 char *type_buf,
210 char* host_buf,
211 int *port_n,
212 char *user_buf,
213 char* pass_buf)
215 const char* p;
216 rs_proxyType proxytype;
218 size_t next_token = 6, ul = 0, pl = 0, hl;
219 if(!proxystring[0] || !proxystring[1] || !proxystring[2] || !proxystring[3] || !proxystring[4] || !proxystring[5]) goto inv_string;
220 if(*proxystring == 's') {
221 switch(proxystring[5]) {
222 case '5': proxytype = RS_PT_SOCKS5; break;
223 case '4': proxytype = RS_PT_SOCKS4; break;
224 default: goto inv_string;
226 } else if(*proxystring == 'h') {
227 proxytype = RS_PT_HTTP;
228 next_token = 4;
229 } else goto inv_string;
231 proxystring[next_token++] != ':' ||
232 proxystring[next_token++] != '/' ||
233 proxystring[next_token++] != '/') goto inv_string;
234 const char *at = strrchr(proxystring+next_token, '@');
235 if(at) {
236 if(proxytype == RS_PT_SOCKS4)
237 return 0;
238 p = strchr(proxystring+next_token, ':');
239 if(!p || p >= at) goto inv_string;
240 const char *u = proxystring+next_token;
241 ul = p-u;
242 p++;
243 pl = at-p;
244 if(proxytype == RS_PT_SOCKS5 && (ul > 255 || pl > 255))
245 return 0;
246 memcpy(user_buf, u, ul);
247 user_buf[ul]=0;
248 memcpy(pass_buf, p, pl);
249 pass_buf[pl]=0;
250 next_token += 2+ul+pl;
251 } else {
252 user_buf[0]=0;
253 pass_buf[0]=0;
255 const char* h = proxystring+next_token;
256 p = strchr(h, ':');
257 if(!p) goto inv_string;
258 hl = p-h;
259 if(hl > 255)
260 return 0;
261 memcpy(host_buf, h, hl);
262 host_buf[hl]=0;
263 *port_n = atoi(p+1);
264 switch(proxytype) {
265 case RS_PT_SOCKS4:
266 strcpy(type_buf, "socks4");
267 break;
268 case RS_PT_SOCKS5:
269 strcpy(type_buf, "socks5");
270 break;
271 case RS_PT_HTTP:
272 strcpy(type_buf, "http");
273 break;
274 default:
275 return 0;
277 return 1;
278 inv_string:
279 return 0;
282 static const char* bool_str(int bool_val) {
283 if(bool_val) return "true";
284 return "false";
287 #define STR_STARTSWITH(P, LIT) (!strncmp(P, LIT, sizeof(LIT)-1))
288 /* get configuration from config file */
289 static void get_chain_data(proxy_data * pd, unsigned int *proxy_count, chain_type * ct) {
290 int count = 0, port_n = 0, list = 0;
291 char buf[1024], type[1024], host[1024], user[1024];
292 char *buff, *env, *p;
293 char local_addr_port[64], local_addr[64], local_netmask[32];
294 char dnat_orig_addr_port[32], dnat_new_addr_port[32];
295 char dnat_orig_addr[32], dnat_orig_port[32], dnat_new_addr[32], dnat_new_port[32];
296 char rdnsd_addr[32], rdnsd_port[8];
297 FILE *file = NULL;
299 if(proxychains_got_chain_data)
300 return;
302 PFUNC();
304 //Some defaults
305 tcp_read_time_out = 4 * 1000;
306 tcp_connect_time_out = 10 * 1000;
307 *ct = DYNAMIC_TYPE;
309 env = get_config_path(getenv(PROXYCHAINS_CONF_FILE_ENV_VAR), buf, sizeof(buf));
310 if( ( file = fopen(env, "r") ) == NULL )
312 perror("couldnt read configuration file");
313 exit(1);
316 while(fgets(buf, sizeof(buf), file)) {
317 buff = buf;
318 /* remove leading whitespace */
319 while(isspace(*buff)) buff++;
320 /* remove trailing '\n' */
321 if((p = strrchr(buff, '\n'))) *p = 0;
322 p = buff + strlen(buff)-1;
323 /* remove trailing whitespace */
324 while(p >= buff && isspace(*p)) *(p--) = 0;
325 if(!*buff || *buff == '#') continue; /* skip empty lines and comments */
326 if(1) {
327 /* proxylist has to come last */
328 if(list) {
329 if(count >= MAX_CHAIN)
330 break;
332 memset(&pd[count], 0, sizeof(proxy_data));
334 pd[count].ps = PLAY_STATE;
335 port_n = 0;
337 int ret = sscanf(buff, "%s %s %d %s %s", type, host, &port_n, pd[count].user, pd[count].pass);
338 if(ret < 3 || ret == EOF) {
339 if(!proxy_from_string(buff, type, host, &port_n, pd[count].user, pd[count].pass)) {
340 inv:
341 fprintf(stderr, "error: invalid item in proxylist section: %s", buff);
342 exit(1);
346 memset(&pd[count].ip, 0, sizeof(pd[count].ip));
347 pd[count].ip.is_v6 = !!strchr(host, ':');
348 pd[count].port = htons((unsigned short) port_n);
349 ip_type* host_ip = &pd[count].ip;
350 if(1 != inet_pton(host_ip->is_v6 ? AF_INET6 : AF_INET, host, host_ip->addr.v6)) {
351 if(*ct == STRICT_TYPE && proxychains_resolver >= DNSLF_RDNS_START && count > 0) {
352 /* we can allow dns hostnames for all but the first proxy in the list if chaintype is strict, as remote lookup can be done */
353 rdns_init(proxychains_resolver);
354 ip_type4 internal_ip = rdns_get_ip_for_host(host, strlen(host));
355 pd[count].ip.is_v6 = 0;
356 host_ip->addr.v4 = internal_ip;
357 if(internal_ip.as_int == IPT4_INVALID.as_int)
358 goto inv_host;
359 } else {
360 inv_host:
361 fprintf(stderr, "proxy %s has invalid value or is not numeric\n", host);
362 fprintf(stderr, "non-numeric ips are only allowed under the following circumstances:\n");
363 fprintf(stderr, "chaintype == strict (%s), proxy is not first in list (%s), proxy_dns active (%s)\n\n", bool_str(*ct == STRICT_TYPE), bool_str(count > 0), rdns_resolver_string(proxychains_resolver));
364 exit(1);
368 if(!strcmp(type, "http")) {
369 pd[count].pt = HTTP_TYPE;
370 } else if(!strcmp(type, "raw")) {
371 pd[count].pt = RAW_TYPE;
372 } else if(!strcmp(type, "socks4")) {
373 pd[count].pt = SOCKS4_TYPE;
374 } else if(!strcmp(type, "socks5")) {
375 pd[count].pt = SOCKS5_TYPE;
376 } else
377 goto inv;
379 if(port_n)
380 count++;
381 } else {
382 if(!strcmp(buff, "[ProxyList]")) {
383 list = 1;
384 } else if(!strcmp(buff, "random_chain")) {
385 *ct = RANDOM_TYPE;
386 } else if(!strcmp(buff, "strict_chain")) {
387 *ct = STRICT_TYPE;
388 } else if(!strcmp(buff, "dynamic_chain")) {
389 *ct = DYNAMIC_TYPE;
390 } else if(!strcmp(buff, "round_robin_chain")) {
391 *ct = ROUND_ROBIN_TYPE;
392 } else if(STR_STARTSWITH(buff, "tcp_read_time_out")) {
393 sscanf(buff, "%s %d", user, &tcp_read_time_out);
394 } else if(STR_STARTSWITH(buff, "tcp_connect_time_out")) {
395 sscanf(buff, "%s %d", user, &tcp_connect_time_out);
396 } else if(STR_STARTSWITH(buff, "remote_dns_subnet")) {
397 sscanf(buff, "%s %u", user, &remote_dns_subnet);
398 if(remote_dns_subnet >= 256) {
399 fprintf(stderr,
400 "remote_dns_subnet: invalid value. requires a number between 0 and 255.\n");
401 exit(1);
403 } else if(STR_STARTSWITH(buff, "localnet")) {
404 char colon, extra, right_bracket[2];
405 unsigned short local_port = 0, local_prefix;
406 int local_family, n, valid;
407 if(sscanf(buff, "%s %53[^/]/%15s%c", user, local_addr_port, local_netmask, &extra) != 3) {
408 fprintf(stderr, "localnet format error");
409 exit(1);
411 p = strchr(local_addr_port, ':');
412 if(!p || p == strrchr(local_addr_port, ':')) {
413 local_family = AF_INET;
414 n = sscanf(local_addr_port, "%15[^:]%c%5hu%c", local_addr, &colon, &local_port, &extra);
415 valid = n == 1 || (n == 3 && colon == ':');
416 } else if(local_addr_port[0] == '[') {
417 local_family = AF_INET6;
418 n = sscanf(local_addr_port, "[%45[^][]%1[]]%c%5hu%c", local_addr, right_bracket, &colon, &local_port, &extra);
419 valid = n == 2 || (n == 4 && colon == ':');
420 } else {
421 local_family = AF_INET6;
422 valid = sscanf(local_addr_port, "%45[^][]%c", local_addr, &extra) == 1;
424 if(!valid) {
425 fprintf(stderr, "localnet address or port error\n");
426 exit(1);
428 if(local_port) {
429 PDEBUG("added localnet: netaddr=%s, port=%u, netmask=%s\n",
430 local_addr, local_port, local_netmask);
431 } else {
432 PDEBUG("added localnet: netaddr=%s, netmask=%s\n",
433 local_addr, local_netmask);
435 if(num_localnet_addr < MAX_LOCALNET) {
436 localnet_addr[num_localnet_addr].family = local_family;
437 localnet_addr[num_localnet_addr].port = local_port;
438 valid = 0;
439 if (local_family == AF_INET) {
440 valid =
441 inet_pton(local_family, local_addr,
442 &localnet_addr[num_localnet_addr].in_addr) > 0;
443 } else if(local_family == AF_INET6) {
444 valid =
445 inet_pton(local_family, local_addr,
446 &localnet_addr[num_localnet_addr].in6_addr) > 0;
448 if(!valid) {
449 fprintf(stderr, "localnet address error\n");
450 exit(1);
452 if(local_family == AF_INET && strchr(local_netmask, '.')) {
453 valid =
454 inet_pton(local_family, local_netmask,
455 &localnet_addr[num_localnet_addr].in_mask) > 0;
456 } else {
457 valid = sscanf(local_netmask, "%hu%c", &local_prefix, &extra) == 1;
458 if (valid) {
459 if(local_family == AF_INET && local_prefix <= 32) {
460 localnet_addr[num_localnet_addr].in_mask.s_addr =
461 htonl(0xFFFFFFFFu << (32u - local_prefix));
462 } else if(local_family == AF_INET6 && local_prefix <= 128) {
463 localnet_addr[num_localnet_addr].in6_prefix =
464 local_prefix;
465 } else {
466 valid = 0;
470 if(!valid) {
471 fprintf(stderr, "localnet netmask error\n");
472 exit(1);
474 ++num_localnet_addr;
475 } else {
476 fprintf(stderr, "# of localnet exceed %d.\n", MAX_LOCALNET);
478 } else if(STR_STARTSWITH(buff, "chain_len")) {
479 char *pc;
480 int len;
481 pc = strchr(buff, '=');
482 if(!pc) {
483 fprintf(stderr, "error: missing equals sign '=' in chain_len directive.\n");
484 exit(1);
486 len = atoi(++pc);
487 proxychains_max_chain = (len ? len : 1);
488 } else if(!strcmp(buff, "quiet_mode")) {
489 proxychains_quiet_mode = 1;
490 } else if(!strcmp(buff, "proxy_dns_old")) {
491 proxychains_resolver = DNSLF_FORKEXEC;
492 } else if(!strcmp(buff, "proxy_dns")) {
493 proxychains_resolver = DNSLF_RDNS_THREAD;
494 } else if(STR_STARTSWITH(buff, "proxy_dns_daemon")) {
495 struct sockaddr_in rdns_server_buffer;
497 if(sscanf(buff, "%s %15[^:]:%5s", user, rdnsd_addr, rdnsd_port) < 3) {
498 fprintf(stderr, "proxy_dns_daemon format error\n");
499 exit(1);
501 rdns_server_buffer.sin_family = AF_INET;
502 int error = inet_pton(AF_INET, rdnsd_addr, &rdns_server_buffer.sin_addr);
503 if(error <= 0) {
504 fprintf(stderr, "bogus proxy_dns_daemon address\n");
505 exit(1);
507 rdns_server_buffer.sin_port = htons(atoi(rdnsd_port));
508 proxychains_resolver = DNSLF_RDNS_DAEMON;
509 rdns_set_daemon(&rdns_server_buffer);
510 } else if(STR_STARTSWITH(buff, "dnat")) {
511 if(sscanf(buff, "%s %21[^ ] %21s\n", user, dnat_orig_addr_port, dnat_new_addr_port) < 3) {
512 fprintf(stderr, "dnat format error");
513 exit(1);
515 /* clean previously used buffer */
516 memset(dnat_orig_port, 0, sizeof(dnat_orig_port) / sizeof(dnat_orig_port[0]));
517 memset(dnat_new_port, 0, sizeof(dnat_new_port) / sizeof(dnat_new_port[0]));
519 (void)sscanf(dnat_orig_addr_port, "%15[^:]:%5s", dnat_orig_addr, dnat_orig_port);
520 (void)sscanf(dnat_new_addr_port, "%15[^:]:%5s", dnat_new_addr, dnat_new_port);
522 if(num_dnats < MAX_DNAT) {
523 int error;
524 error =
525 inet_pton(AF_INET, dnat_orig_addr,
526 &dnats[num_dnats].orig_dst);
527 if(error <= 0) {
528 fprintf(stderr, "dnat original destination address error\n");
529 exit(1);
532 error =
533 inet_pton(AF_INET, dnat_new_addr,
534 &dnats[num_dnats].new_dst);
535 if(error <= 0) {
536 fprintf(stderr, "dnat effective destination address error\n");
537 exit(1);
540 if(dnat_orig_port[0]) {
541 dnats[num_dnats].orig_port =
542 (short) atoi(dnat_orig_port);
543 } else {
544 dnats[num_dnats].orig_port = 0;
547 if(dnat_new_port[0]) {
548 dnats[num_dnats].new_port =
549 (short) atoi(dnat_new_port);
550 } else {
551 dnats[num_dnats].new_port = 0;
554 PDEBUG("added dnat: orig-dst=%s orig-port=%d new-dst=%s new-port=%d\n", dnat_orig_addr, dnats[num_dnats].orig_port, dnat_new_addr, dnats[num_dnats].new_port);
555 ++num_dnats;
556 } else {
557 fprintf(stderr, "# of dnat exceed %d.\n", MAX_DNAT);
563 #ifndef BROKEN_FCLOSE
564 fclose(file);
565 #endif
566 if(!count) {
567 fprintf(stderr, "error: no valid proxy found in config\n");
568 exit(1);
570 *proxy_count = count;
571 proxychains_got_chain_data = 1;
572 PDEBUG("proxy_dns: %s\n", rdns_resolver_string(proxychains_resolver));
575 /******* HOOK FUNCTIONS *******/
577 #define EXPAND( args...) args
578 #ifdef MONTEREY_HOOKING
579 #define HOOKFUNC(R, N, args...) R pxcng_ ## N ( EXPAND(args) )
580 #else
581 #define HOOKFUNC(R, N, args...) R N ( EXPAND(args) )
582 #endif
584 HOOKFUNC(int, close, int fd) {
585 if(!init_l) {
586 if(close_fds_cnt>=(sizeof close_fds/sizeof close_fds[0])) goto err;
587 close_fds[close_fds_cnt++] = fd;
588 errno = 0;
589 return 0;
591 if(proxychains_resolver != DNSLF_RDNS_THREAD) return true_close(fd);
593 /* prevent rude programs (like ssh) from closing our pipes */
594 if(fd != req_pipefd[0] && fd != req_pipefd[1] &&
595 fd != resp_pipefd[0] && fd != resp_pipefd[1]) {
596 return true_close(fd);
598 err:
599 errno = EBADF;
600 return -1;
602 static int is_v4inv6(const struct in6_addr *a) {
603 return !memcmp(a->s6_addr, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
606 static void intsort(int *a, int n) {
607 int i, j, s;
608 for(i=0; i<n; ++i)
609 for(j=i+1; j<n; ++j)
610 if(a[j] < a[i]) {
611 s = a[i];
612 a[i] = a[j];
613 a[j] = s;
617 /* Warning: Linux manual says the third arg is `unsigned int`, but unistd.h says `int`. */
618 HOOKFUNC(int, close_range, unsigned first, unsigned last, int flags) {
619 if(true_close_range == NULL) {
620 fprintf(stderr, "Calling close_range, but this platform does not provide this system call. ");
621 return -1;
623 if(!init_l) {
624 /* push back to cache, and delay the execution. */
625 if(close_range_buffer_cnt >= (sizeof close_range_buffer / sizeof close_range_buffer[0])) {
626 errno = ENOMEM;
627 return -1;
629 int i = close_range_buffer_cnt++;
630 close_range_buffer[i].first = first;
631 close_range_buffer[i].last = last;
632 close_range_buffer[i].flags = flags;
633 return errno = 0;
635 if(proxychains_resolver != DNSLF_RDNS_THREAD) return true_close_range(first, last, flags);
637 /* prevent rude programs (like ssh) from closing our pipes */
638 int res = 0, uerrno = 0, i;
639 int protected_fds[] = {req_pipefd[0], req_pipefd[1], resp_pipefd[0], resp_pipefd[1]};
640 intsort(protected_fds, 4);
641 /* We are skipping protected_fds while calling true_close_range()
642 * If protected_fds cut the range into some sub-ranges, we close sub-ranges BEFORE cut point in the loop.
643 * [first, cut1-1] , [cut1+1, cut2-1] , [cut2+1, cut3-1]
644 * Finally, we delete the remaining sub-range, outside the loop. [cut3+1, tail]
646 int next_fd_to_close = first;
647 for(i = 0; i < 4; ++i) {
648 if(protected_fds[i] < first || protected_fds[i] > last)
649 continue;
650 int prev = (i == 0 || protected_fds[i-1] < first) ? first : protected_fds[i-1]+1;
651 if(prev != protected_fds[i]) {
652 if(-1 == true_close_range(prev, protected_fds[i]-1, flags)) {
653 res = -1;
654 uerrno = errno;
657 next_fd_to_close = protected_fds[i]+1;
659 if(next_fd_to_close <= last) {
660 if(-1 == true_close_range(next_fd_to_close, last, flags)) {
661 res = -1;
662 uerrno = errno;
665 errno = uerrno;
666 return res;
669 HOOKFUNC(int, connect, int sock, const struct sockaddr *addr, unsigned int len) {
670 INIT();
671 PFUNC();
673 int socktype = 0, flags = 0, ret = 0;
674 socklen_t optlen = 0;
675 ip_type dest_ip;
676 DEBUGDECL(char str[256]);
678 struct in_addr *p_addr_in;
679 struct in6_addr *p_addr_in6;
680 dnat_arg *dnat = NULL;
681 unsigned short port;
682 size_t i;
683 int remote_dns_connect = 0;
684 optlen = sizeof(socktype);
685 sa_family_t fam = SOCKFAMILY(*addr);
686 getsockopt(sock, SOL_SOCKET, SO_TYPE, &socktype, &optlen);
687 if(!((fam == AF_INET || fam == AF_INET6) && socktype == SOCK_STREAM))
688 return true_connect(sock, addr, len);
690 int v6 = dest_ip.is_v6 = fam == AF_INET6;
692 p_addr_in = &((struct sockaddr_in *) addr)->sin_addr;
693 p_addr_in6 = &((struct sockaddr_in6 *) addr)->sin6_addr;
694 port = !v6 ? ntohs(((struct sockaddr_in *) addr)->sin_port)
695 : ntohs(((struct sockaddr_in6 *) addr)->sin6_port);
696 struct in_addr v4inv6;
697 if(v6 && is_v4inv6(p_addr_in6)) {
698 memcpy(&v4inv6.s_addr, &p_addr_in6->s6_addr[12], 4);
699 v6 = dest_ip.is_v6 = 0;
700 p_addr_in = &v4inv6;
702 if(!v6 && !memcmp(p_addr_in, "\0\0\0\0", 4)) {
703 errno = ECONNREFUSED;
704 return -1;
707 // PDEBUG("localnet: %s; ", inet_ntop(AF_INET,&in_addr_localnet, str, sizeof(str)));
708 // PDEBUG("netmask: %s; " , inet_ntop(AF_INET, &in_addr_netmask, str, sizeof(str)));
709 PDEBUG("target: %s\n", inet_ntop(v6 ? AF_INET6 : AF_INET, v6 ? (void*)p_addr_in6 : (void*)p_addr_in, str, sizeof(str)));
710 PDEBUG("port: %d\n", port);
712 // check if connect called from proxydns
713 remote_dns_connect = !v6 && (ntohl(p_addr_in->s_addr) >> 24 == remote_dns_subnet);
715 // more specific first
716 if (!v6) for(i = 0; i < num_dnats && !remote_dns_connect && !dnat; i++)
717 if(dnats[i].orig_dst.s_addr == p_addr_in->s_addr)
718 if(dnats[i].orig_port && (dnats[i].orig_port == port))
719 dnat = &dnats[i];
721 if (!v6) for(i = 0; i < num_dnats && !remote_dns_connect && !dnat; i++)
722 if(dnats[i].orig_dst.s_addr == p_addr_in->s_addr)
723 if(!dnats[i].orig_port)
724 dnat = &dnats[i];
726 if (dnat) {
727 p_addr_in = &dnat->new_dst;
728 if (dnat->new_port)
729 port = dnat->new_port;
732 for(i = 0; i < num_localnet_addr && !remote_dns_connect; i++) {
733 if (localnet_addr[i].port && localnet_addr[i].port != port)
734 continue;
735 if (localnet_addr[i].family != (v6 ? AF_INET6 : AF_INET))
736 continue;
737 if (v6) {
738 size_t prefix_bytes = localnet_addr[i].in6_prefix / CHAR_BIT;
739 size_t prefix_bits = localnet_addr[i].in6_prefix % CHAR_BIT;
740 if (prefix_bytes && memcmp(p_addr_in6->s6_addr, localnet_addr[i].in6_addr.s6_addr, prefix_bytes) != 0)
741 continue;
742 if (prefix_bits && (p_addr_in6->s6_addr[prefix_bytes] ^ localnet_addr[i].in6_addr.s6_addr[prefix_bytes]) >> (CHAR_BIT - prefix_bits))
743 continue;
744 } else {
745 if((p_addr_in->s_addr ^ localnet_addr[i].in_addr.s_addr) & localnet_addr[i].in_mask.s_addr)
746 continue;
748 PDEBUG("accessing localnet using true_connect\n");
749 return true_connect(sock, addr, len);
752 flags = fcntl(sock, F_GETFL, 0);
753 if(flags & O_NONBLOCK)
754 fcntl(sock, F_SETFL, !O_NONBLOCK);
756 memcpy(dest_ip.addr.v6, v6 ? (void*)p_addr_in6 : (void*)p_addr_in, v6?16:4);
758 ret = connect_proxy_chain(sock,
759 dest_ip,
760 htons(port),
761 proxychains_pd, proxychains_proxy_count, proxychains_ct, proxychains_max_chain);
763 fcntl(sock, F_SETFL, flags);
764 if(ret != SUCCESS)
765 errno = ECONNREFUSED;
766 return ret;
769 #ifdef IS_SOLARIS
770 HOOKFUNC(int, __xnet_connect, int sock, const struct sockaddr *addr, unsigned int len)
771 return connect(sock, addr, len);
773 #endif
775 static struct gethostbyname_data ghbndata;
776 HOOKFUNC(struct hostent*, gethostbyname, const char *name) {
777 INIT();
778 PDEBUG("gethostbyname: %s\n", name);
780 if(proxychains_resolver == DNSLF_FORKEXEC)
781 return proxy_gethostbyname_old(name);
782 else if(proxychains_resolver == DNSLF_LIBC)
783 return true_gethostbyname(name);
784 else
785 return proxy_gethostbyname(name, &ghbndata);
787 return NULL;
790 HOOKFUNC(int, getaddrinfo, const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) {
791 INIT();
792 PDEBUG("getaddrinfo: %s %s\n", node ? node : "null", service ? service : "null");
794 if(proxychains_resolver != DNSLF_LIBC)
795 return proxy_getaddrinfo(node, service, hints, res);
796 else
797 return true_getaddrinfo(node, service, hints, res);
800 HOOKFUNC(void, freeaddrinfo, struct addrinfo *res) {
801 INIT();
802 PDEBUG("freeaddrinfo %p \n", (void *) res);
804 if(proxychains_resolver == DNSLF_LIBC)
805 true_freeaddrinfo(res);
806 else
807 proxy_freeaddrinfo(res);
810 HOOKFUNC(int, getnameinfo, const struct sockaddr *sa, socklen_t salen,
811 char *host, GN_NODELEN_T hostlen, char *serv,
812 GN_SERVLEN_T servlen, GN_FLAGS_T flags)
814 INIT();
815 PFUNC();
817 if(proxychains_resolver == DNSLF_LIBC) {
818 return true_getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
819 } else {
820 if(!salen || !(SOCKFAMILY(*sa) == AF_INET || SOCKFAMILY(*sa) == AF_INET6))
821 return EAI_FAMILY;
822 int v6 = SOCKFAMILY(*sa) == AF_INET6;
823 if(salen < (v6?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in)))
824 return EAI_FAMILY;
825 if(hostlen) {
826 unsigned char v4inv6buf[4];
827 const void *ip = v6 ? (void*)&((struct sockaddr_in6*)sa)->sin6_addr
828 : (void*)&((struct sockaddr_in*)sa)->sin_addr;
829 unsigned scopeid = 0;
830 if(v6) {
831 if(is_v4inv6(&((struct sockaddr_in6*)sa)->sin6_addr)) {
832 memcpy(v4inv6buf, &((struct sockaddr_in6*)sa)->sin6_addr.s6_addr[12], 4);
833 ip = v4inv6buf;
834 v6 = 0;
835 } else
836 scopeid = ((struct sockaddr_in6 *)sa)->sin6_scope_id;
838 if(!inet_ntop(v6?AF_INET6:AF_INET,ip,host,hostlen))
839 return EAI_OVERFLOW;
840 if(scopeid) {
841 size_t l = strlen(host);
842 if(snprintf(host+l, hostlen-l, "%%%u", scopeid) >= hostlen-l)
843 return EAI_OVERFLOW;
846 if(servlen) {
847 if(snprintf(serv, servlen, "%d", ntohs(SOCKPORT(*sa))) >= servlen)
848 return EAI_OVERFLOW;
851 return 0;
854 HOOKFUNC(struct hostent*, gethostbyaddr, const void *addr, socklen_t len, int type) {
855 INIT();
856 PDEBUG("TODO: proper gethostbyaddr hook\n");
858 static char buf[16];
859 static char ipv4[4];
860 static char *list[2];
861 static char *aliases[1];
862 static struct hostent he;
864 if(proxychains_resolver == DNSLF_LIBC)
865 return true_gethostbyaddr(addr, len, type);
866 else {
868 PDEBUG("len %u\n", len);
869 if(len != 4)
870 return NULL;
871 he.h_name = buf;
872 memcpy(ipv4, addr, 4);
873 list[0] = ipv4;
874 list[1] = NULL;
875 he.h_addr_list = list;
876 he.h_addrtype = AF_INET;
877 aliases[0] = NULL;
878 he.h_aliases = aliases;
879 he.h_length = 4;
880 pc_stringfromipv4((unsigned char *) addr, buf);
881 return &he;
883 return NULL;
886 #ifndef MSG_FASTOPEN
887 # define MSG_FASTOPEN 0x20000000
888 #endif
890 HOOKFUNC(ssize_t, sendto, int sockfd, const void *buf, size_t len, int flags,
891 const struct sockaddr *dest_addr, socklen_t addrlen) {
892 INIT();
893 PFUNC();
894 if (flags & MSG_FASTOPEN) {
895 if (!connect(sockfd, dest_addr, addrlen) && errno != EINPROGRESS) {
896 return -1;
898 dest_addr = NULL;
899 addrlen = 0;
900 flags &= ~MSG_FASTOPEN;
902 return true_sendto(sockfd, buf, len, flags, dest_addr, addrlen);
905 #ifdef MONTEREY_HOOKING
906 #define SETUP_SYM(X) do { if (! true_ ## X ) true_ ## X = &X; } while(0)
907 #define SETUP_SYM_OPTIONAL(X)
908 #else
909 #define SETUP_SYM_IMPL(X, IS_MANDATORY) do { if (! true_ ## X ) true_ ## X = load_sym( # X, X, IS_MANDATORY ); } while(0)
910 #define SETUP_SYM(X) SETUP_SYM_IMPL(X, 1)
911 #define SETUP_SYM_OPTIONAL(X) SETUP_SYM_IMPL(X, 0)
912 #endif
914 static void setup_hooks(void) {
915 SETUP_SYM(connect);
916 SETUP_SYM(sendto);
917 SETUP_SYM(gethostbyname);
918 SETUP_SYM(getaddrinfo);
919 SETUP_SYM(freeaddrinfo);
920 SETUP_SYM(gethostbyaddr);
921 SETUP_SYM(getnameinfo);
922 #ifdef IS_SOLARIS
923 SETUP_SYM(__xnet_connect);
924 #endif
925 SETUP_SYM(close);
926 SETUP_SYM_OPTIONAL(close_range);
929 #ifdef MONTEREY_HOOKING
931 #define DYLD_INTERPOSE(_replacement,_replacee) \
932 __attribute__((used)) static struct{ const void* replacement; const void* replacee; } _interpose_##_replacee \
933 __attribute__((section ("__DATA,__interpose"))) = { (const void*)(unsigned long)&_replacement, (const void*)(unsigned long)&_replacee };
934 #define DYLD_HOOK(F) DYLD_INTERPOSE(pxcng_ ## F, F)
936 DYLD_HOOK(connect);
937 DYLD_HOOK(sendto);
938 DYLD_HOOK(gethostbyname);
939 DYLD_HOOK(getaddrinfo);
940 DYLD_HOOK(freeaddrinfo);
941 DYLD_HOOK(gethostbyaddr);
942 DYLD_HOOK(getnameinfo);
943 DYLD_HOOK(close);
945 #endif