1 /***************************************************************************
2 libproxychains.c - description
4 begin : Tue May 14 2002
5 copyright : netcreature (C) 2002
6 email : netcreature@users.sourceforge.net
7 ***************************************************************************/
9 /***************************************************************************
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. *
16 ***************************************************************************/
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
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)
54 int __xnet_connect(int sock
, const struct sockaddr
*addr
, unsigned int len
);
55 connect_t true___xnet_connect
;
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
;
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
];
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());
96 } else if (!funcptr
) {
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");
108 #include "allocator_thread.h"
110 const char *proxychains_get_version(void);
112 static void setup_hooks(void);
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
128 clock_gettime(CLOCK_REALTIME
, &now
);
129 return now
.tv_sec
^ now
.tv_nsec
;
135 static void do_init(void) {
138 srand(get_rand_seed());
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());
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
);
160 rdns_init(proxychains_resolver
);
163 static void init_lib_wrapper(const char* caller
) {
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 */
177 __attribute__((constructor
))
178 static void gcc_init(void) {
179 init_lib_wrapper(__FUNCTION__
);
181 #define INIT() do {} while(0)
183 #define INIT() init_lib_wrapper(__FUNCTION__)
195 proxy_from_string() taken from rocksock network I/O library (C) rofl0r
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
,
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
;
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
, '@');
236 if(proxytype
== RS_PT_SOCKS4
)
238 p
= strchr(proxystring
+next_token
, ':');
239 if(!p
|| p
>= at
) goto inv_string
;
240 const char *u
= proxystring
+next_token
;
244 if(proxytype
== RS_PT_SOCKS5
&& (ul
> 255 || pl
> 255))
246 memcpy(user_buf
, u
, ul
);
248 memcpy(pass_buf
, p
, pl
);
250 next_token
+= 2+ul
+pl
;
255 const char* h
= proxystring
+next_token
;
257 if(!p
) goto inv_string
;
261 memcpy(host_buf
, h
, hl
);
266 strcpy(type_buf
, "socks4");
269 strcpy(type_buf
, "socks5");
272 strcpy(type_buf
, "http");
282 static const char* bool_str(int bool_val
) {
283 if(bool_val
) return "true";
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];
299 if(proxychains_got_chain_data
)
305 tcp_read_time_out
= 4 * 1000;
306 tcp_connect_time_out
= 10 * 1000;
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");
316 while(fgets(buf
, sizeof(buf
), file
)) {
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 */
327 /* proxylist has to come last */
329 if(count
>= MAX_CHAIN
)
332 memset(&pd
[count
], 0, sizeof(proxy_data
));
334 pd
[count
].ps
= PLAY_STATE
;
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
)) {
341 fprintf(stderr
, "error: invalid item in proxylist section: %s", buff
);
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
)
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
));
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
;
382 if(!strcmp(buff
, "[ProxyList]")) {
384 } else if(!strcmp(buff
, "random_chain")) {
386 } else if(!strcmp(buff
, "strict_chain")) {
388 } else if(!strcmp(buff
, "dynamic_chain")) {
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) {
400 "remote_dns_subnet: invalid value. requires a number between 0 and 255.\n");
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");
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
== ':');
421 local_family
= AF_INET6
;
422 valid
= sscanf(local_addr_port
, "%45[^][]%c", local_addr
, &extra
) == 1;
425 fprintf(stderr
, "localnet address or port error\n");
429 PDEBUG("added localnet: netaddr=%s, port=%u, netmask=%s\n",
430 local_addr
, local_port
, local_netmask
);
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
;
439 if (local_family
== AF_INET
) {
441 inet_pton(local_family
, local_addr
,
442 &localnet_addr
[num_localnet_addr
].in_addr
) > 0;
443 } else if(local_family
== AF_INET6
) {
445 inet_pton(local_family
, local_addr
,
446 &localnet_addr
[num_localnet_addr
].in6_addr
) > 0;
449 fprintf(stderr
, "localnet address error\n");
452 if(local_family
== AF_INET
&& strchr(local_netmask
, '.')) {
454 inet_pton(local_family
, local_netmask
,
455 &localnet_addr
[num_localnet_addr
].in_mask
) > 0;
457 valid
= sscanf(local_netmask
, "%hu%c", &local_prefix
, &extra
) == 1;
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
=
471 fprintf(stderr
, "localnet netmask error\n");
476 fprintf(stderr
, "# of localnet exceed %d.\n", MAX_LOCALNET
);
478 } else if(STR_STARTSWITH(buff
, "chain_len")) {
481 pc
= strchr(buff
, '=');
483 fprintf(stderr
, "error: missing equals sign '=' in chain_len directive.\n");
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");
501 rdns_server_buffer
.sin_family
= AF_INET
;
502 int error
= inet_pton(AF_INET
, rdnsd_addr
, &rdns_server_buffer
.sin_addr
);
504 fprintf(stderr
, "bogus proxy_dns_daemon address\n");
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");
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
) {
525 inet_pton(AF_INET
, dnat_orig_addr
,
526 &dnats
[num_dnats
].orig_dst
);
528 fprintf(stderr
, "dnat original destination address error\n");
533 inet_pton(AF_INET
, dnat_new_addr
,
534 &dnats
[num_dnats
].new_dst
);
536 fprintf(stderr
, "dnat effective destination address error\n");
540 if(dnat_orig_port
[0]) {
541 dnats
[num_dnats
].orig_port
=
542 (short) atoi(dnat_orig_port
);
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
);
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
);
557 fprintf(stderr
, "# of dnat exceed %d.\n", MAX_DNAT
);
563 #ifndef BROKEN_FCLOSE
567 fprintf(stderr
, "error: no valid proxy found in config\n");
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) )
581 #define HOOKFUNC(R, N, args...) R N ( EXPAND(args) )
584 HOOKFUNC(int, close
, int fd
) {
586 if(close_fds_cnt
>=(sizeof close_fds
/sizeof close_fds
[0])) goto err
;
587 close_fds
[close_fds_cnt
++] = fd
;
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
);
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
) {
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. ");
624 /* push back to cache, and delay the execution. */
625 if(close_range_buffer_cnt
>= (sizeof close_range_buffer
/ sizeof close_range_buffer
[0])) {
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
;
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
)
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
)) {
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
)) {
669 HOOKFUNC(int, connect
, int sock
, const struct sockaddr
*addr
, unsigned int len
) {
673 int socktype
= 0, flags
= 0, ret
= 0;
674 socklen_t optlen
= 0;
676 DEBUGDECL(char str
[256]);
678 struct in_addr
*p_addr_in
;
679 struct in6_addr
*p_addr_in6
;
680 dnat_arg
*dnat
= NULL
;
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;
702 if(!v6
&& !memcmp(p_addr_in
, "\0\0\0\0", 4)) {
703 errno
= ECONNREFUSED
;
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
))
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
)
727 p_addr_in
= &dnat
->new_dst
;
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
)
735 if (localnet_addr
[i
].family
!= (v6
? AF_INET6
: AF_INET
))
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)
742 if (prefix_bits
&& (p_addr_in6
->s6_addr
[prefix_bytes
] ^ localnet_addr
[i
].in6_addr
.s6_addr
[prefix_bytes
]) >> (CHAR_BIT
- prefix_bits
))
745 if((p_addr_in
->s_addr
^ localnet_addr
[i
].in_addr
.s_addr
) & localnet_addr
[i
].in_mask
.s_addr
)
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
,
761 proxychains_pd
, proxychains_proxy_count
, proxychains_ct
, proxychains_max_chain
);
763 fcntl(sock
, F_SETFL
, flags
);
765 errno
= ECONNREFUSED
;
770 HOOKFUNC(int, __xnet_connect
, int sock
, const struct sockaddr
*addr
, unsigned int len
)
771 return connect(sock
, addr
, len
);
775 static struct gethostbyname_data ghbndata
;
776 HOOKFUNC(struct hostent
*, gethostbyname
, const char *name
) {
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
);
785 return proxy_gethostbyname(name
, &ghbndata
);
790 HOOKFUNC(int, getaddrinfo
, const char *node
, const char *service
, const struct addrinfo
*hints
, struct addrinfo
**res
) {
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
);
797 return true_getaddrinfo(node
, service
, hints
, res
);
800 HOOKFUNC(void, freeaddrinfo
, struct addrinfo
*res
) {
802 PDEBUG("freeaddrinfo %p \n", (void *) res
);
804 if(proxychains_resolver
== DNSLF_LIBC
)
805 true_freeaddrinfo(res
);
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
)
817 if(proxychains_resolver
== DNSLF_LIBC
) {
818 return true_getnameinfo(sa
, salen
, host
, hostlen
, serv
, servlen
, flags
);
820 if(!salen
|| !(SOCKFAMILY(*sa
) == AF_INET
|| SOCKFAMILY(*sa
) == AF_INET6
))
822 int v6
= SOCKFAMILY(*sa
) == AF_INET6
;
823 if(salen
< (v6
?sizeof(struct sockaddr_in6
):sizeof(struct sockaddr_in
)))
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;
831 if(is_v4inv6(&((struct sockaddr_in6
*)sa
)->sin6_addr
)) {
832 memcpy(v4inv6buf
, &((struct sockaddr_in6
*)sa
)->sin6_addr
.s6_addr
[12], 4);
836 scopeid
= ((struct sockaddr_in6
*)sa
)->sin6_scope_id
;
838 if(!inet_ntop(v6
?AF_INET6
:AF_INET
,ip
,host
,hostlen
))
841 size_t l
= strlen(host
);
842 if(snprintf(host
+l
, hostlen
-l
, "%%%u", scopeid
) >= hostlen
-l
)
847 if(snprintf(serv
, servlen
, "%d", ntohs(SOCKPORT(*sa
))) >= servlen
)
854 HOOKFUNC(struct hostent
*, gethostbyaddr
, const void *addr
, socklen_t len
, int type
) {
856 PDEBUG("TODO: proper gethostbyaddr hook\n");
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
);
868 PDEBUG("len %u\n", len
);
872 memcpy(ipv4
, addr
, 4);
875 he
.h_addr_list
= list
;
876 he
.h_addrtype
= AF_INET
;
878 he
.h_aliases
= aliases
;
880 pc_stringfromipv4((unsigned char *) addr
, buf
);
887 # define MSG_FASTOPEN 0x20000000
890 HOOKFUNC(ssize_t
, sendto
, int sockfd
, const void *buf
, size_t len
, int flags
,
891 const struct sockaddr
*dest_addr
, socklen_t addrlen
) {
894 if (flags
& MSG_FASTOPEN
) {
895 if (!connect(sockfd
, dest_addr
, addrlen
) && errno
!= EINPROGRESS
) {
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)
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)
914 static void setup_hooks(void) {
917 SETUP_SYM(gethostbyname
);
918 SETUP_SYM(getaddrinfo
);
919 SETUP_SYM(freeaddrinfo
);
920 SETUP_SYM(gethostbyaddr
);
921 SETUP_SYM(getnameinfo
);
923 SETUP_SYM(__xnet_connect
);
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)
938 DYLD_HOOK(gethostbyname
);
939 DYLD_HOOK(getaddrinfo
);
940 DYLD_HOOK(freeaddrinfo
);
941 DYLD_HOOK(gethostbyaddr
);
942 DYLD_HOOK(getnameinfo
);