diff --git a/AUTHORS b/AUTHORS index a1027b8..368f57e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,5 +1,16 @@ +original code N3E7CR34TUR3. +http://proxychains.sourceforge.net +netcreature@users.sourceforge.net -web site: proxychains.sourceforge.net -email: netcreature@users.sourceforge.net +main.c, remote-dns, thread safety, bugfixes, build system, cleanups +rofl0r. +https://github.com/rofl0r/proxychains +localnet, bugfixes +adam hamsik. +https://github.com/haad/proxychains + +localnet-port, bugfixes +jianing yang. +https://github.com/jianingy/proxychains diff --git a/INSTALL b/INSTALL deleted file mode 100644 index f118af8..0000000 --- a/INSTALL +++ /dev/null @@ -1,13 +0,0 @@ -ProxyChains ver 3.1 Installation -======================= - -If you have installed other ver of proxychains "make uninstall" before installing this distribution - -unpack the .tar.gz , 'cd' to distribution directory and -run following commands (as root) -------------------------------------- -./configure -make -make install -------------------------------------- -that's all diff --git a/Makefile b/Makefile index b4466de..f96e2bc 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ bindir = $(exec_prefix)/bin SRCS = $(sort $(wildcard src/*.c)) OBJS = $(SRCS:.c=.o) -LOBJS = src/core.o src/libproxychains.o +LOBJS = src/core.o src/common.o src/libproxychains.o CCFLAGS = -Wall -O0 -g -std=c99 -D_GNU_SOURCE -pipe -DTHREAD_SAFE -Werror LDFLAGS = -shared -fPIC -ldl -lpthread @@ -39,16 +39,19 @@ ALL_TOOLS = $(PXCHAINS) CFLAGS+=$(USER_CFLAGS) $(MAC_CFLAGS) -CFLAGS_MAIN=-DLIB_DIR=\"$(libdir)\" -DINSTALL_PREFIX=\"$(prefix)\" -DDLL_NAME=\"$(LDSO_PATHNAME)\" +CFLAGS_MAIN=-DLIB_DIR=\"$(libdir)\" -DSYSCONFDIR=\"$(confdir)\" -DDLL_NAME=\"$(LDSO_PATHNAME)\" all: $(ALL_LIBS) $(ALL_TOOLS) -#install: $(ALL_LIBS:lib/%=$(DESTDIR)$(libdir)/%) $(DESTDIR)$(LDSO_PATHNAME) +install-config: + install -d $(DESTDIR)/$(confdir) + install $(INSTALL_FLAGS) 644 src/proxychains.conf $(DESTDIR)/$(confdir)/ + install: - install $(INSTALL_FLAGS) 755 $(ALL_TOOLS) $(bindir)/ - install $(INSTALL_FLAGS) 644 $(ALL_LIBS) $(libdir)/ - install $(INSTALL_FLAGS) 644 src/proxychains.conf $(prefix)/etc/ + install -d $(DESTDIR)/$(bindir)/ $(DESTDIR)/$(libdir)/ + install $(INSTALL_FLAGS) 755 $(ALL_TOOLS) $(DESTDIR)/$(bindir)/ + install $(INSTALL_FLAGS) 644 $(ALL_LIBS) $(DESTDIR)/$(libdir)/ clean: rm -f $(ALL_LIBS) @@ -62,7 +65,7 @@ $(LDSO_PATHNAME): $(LOBJS) $(CC) $(LDFLAGS) $(LD_SET_SONAME)$(LDSO_PATHNAME) -o $@ $(LOBJS) $(ALL_TOOLS): $(OBJS) - $(CC) src/main.o -o $(PXCHAINS) + $(CC) src/main.o src/common.o -o $(PXCHAINS) .PHONY: all clean install diff --git a/README b/README index c07f104..6ee1b8e 100644 --- a/README +++ b/README @@ -1,23 +1,43 @@ ProxyChains ver 4.0 README ========================== + ProxyChains is a UNIX program, that hooks network-related libc functions + in dynamically linked programs via a preloaded DLL and redirects the + connections through SOCKS4a/5 or HTTP proxies. + *********** ATTENTION *********** -this program works only on dynamically linked programs. -also both proxychains and the program to call must use -the same dynamic linker (i.e. same libc) + this program works only on dynamically linked programs. + also both proxychains and the program to call must use + the same dynamic linker (i.e. same libc) ********************************* +*** Known limitations of the current version: *** -This is Unix version only. + when a process forks, does a DNS lookup in the child, and then uses + the ip in the parent, the corresponding ip mapping will not be found. + this is because the fork can't write back into the parents mapping table. + IRSSI shows this behaviour, so you have to pass the resolved ip address + to it. (you can use the proxyresolv script (requires "dig") to do so) -How to mess with sources - How to Install : read INSTALL !!!!!! + this means that you can't currently use tor onion urls for irssi. + to solve this issue, an external data store (file, pipe, ...) has to + manage the dns <-> ip mapping. of course there has to be proper locking. + shm_open, mkstemp, are possible candidates for a file based approach, + the other option is to spawn some kind of server process that manages the + map lookups. since connect() etc are hooked, this must not be a TCP server. -This program forces any tcp connection made by any given tcp client -to follow through proxy (or proxy chain). It is a kind of proxifier. -It acts like sockscap / permeo / eborder driver ( intercepts TCP calls ) -It is FREE. + I am reluctant on doing this change, because the described behaviour + seems pretty idiotic (doing a fork only for a DNS lookup), and irssi + is currently the only known affected program. + +*** Installation *** + + # needs a working C compiler, preferably gcc + ./configure + make + sudo make install Changelog: ---------- @@ -74,21 +94,21 @@ proxychains looks for config file in following order: Usage Example: - bash$ proxychains telnet targethost.com + $ proxychains telnet targethost.com in this example it will run telnet through proxy(or chained proxies) specified by proxychains.conf Usage Example: - bash$ proxychains -f /etc/proxychains-other.conf targethost2.com + $ proxychains -f /etc/proxychains-other.conf targethost2.com in this example it will use different configuration file then proxychains.conf to connect to targethost2.com host. Usage Example: - bash$ proxyresolv targethost.com + $ proxyresolv targethost.com in this example it will resolve targethost.com through proxy(or chained proxies) specified by proxychains.conf diff --git a/configure b/configure index 6ce9341..0c51ece 100755 --- a/configure +++ b/configure @@ -2,6 +2,18 @@ prefix=/usr/local +usage() { + echo "supported arguments" + echo "--prefix=/path default: $prefix" + echo "--exec_prefix=/path default: $prefix/bin" + echo "--bindir=/path default: $prefix/bin" + echo "--libdir=/path default: $prefix/lib" + echo "--includedir=/path default: $prefix/include" + echo "--sysconfdir=/path default: $prefix/etc" + echo "--help : show this text" + exit 1 +} + spliteq() { arg=$1 echo "${arg#*=}" @@ -11,11 +23,13 @@ spliteq() { parsearg() { case "$1" in - --prefix=*) prefix=$(spliteq "$1");; + --prefix=*) prefix=`spliteq $1`;; --exec_prefix=*) exec_prefix=`spliteq $1`;; --bindir=*) bindir=`spliteq $1`;; --libdir=*) libdir=`spliteq $1`;; --includedir=*) includedir=`spliteq $1`;; + --sysconfdir=*) sysconfdir=`spliteq $1`;; + --help) usage;; esac } @@ -30,23 +44,27 @@ while true ; do esac done -if [ ! $exec_prefix ] ; then +if [ -z "$exec_prefix" ] ; then exec_prefix=$prefix fi -if [ ! $libdir ] ; then +if [ -z "$libdir" ] ; then libdir=$prefix/lib fi -if [ ! $includedir ] ; then +if [ -z "$includedir" ] ; then includedir=$prefix/include fi -if [ ! $bindir ] ; then +if [ -z "$sysconfdir" ] ; then + sysconfdir=$prefix/etc +fi + +if [ -z "$bindir" ] ; then bindir=$exec_prefix/bin fi -if [ ! $CC ] ; then +if [ -z "$CC" ] ; then CC=cc fi @@ -58,6 +76,7 @@ echo exec_prefix=$exec_prefix>>config.mak echo bindir=$bindir>>config.mak echo libdir=$libdir>>config.mak echo includedir=$includedir>>config.mak +echo sysconfdir=$sysconfdir>>config.mak if ismac ; then echo LDSO_SUFFIX=dylib>>config.mak echo MAC_CFLAGS+=-DIS_MAC=1>>config.mak diff --git a/proxychains.lsm b/proxychains.lsm deleted file mode 100644 index d10f15b..0000000 --- a/proxychains.lsm +++ /dev/null @@ -1,14 +0,0 @@ -Begin3 -Title: ProxyChains -Version: 3.0 -Entered-date: -Description: -Keywords: -Author: -Maintained-by: -Primary-site: -Home-page: http://proxychains.sourceforge.net -Original-site: -Platforms: Linux and other Unices -Copying-policy: GNU Public License -End diff --git a/proxychains4 b/proxychains4 new file mode 100755 index 0000000..eda181f Binary files /dev/null and b/proxychains4 differ diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..5f9b7e1 --- /dev/null +++ b/src/common.c @@ -0,0 +1,54 @@ +#include "common.h" +#include +#include +#include + +static int check_path(char *path) { + if(!path) + return 0; + return access(path, R_OK) != -1; +} + +char *get_config_path(char* default_path, char* pbuf, size_t bufsize) { + char buf[512]; + // top priority: user defined path + char *path = default_path; + if(check_path(path)) + goto have; + + // priority 1: env var PROXYCHAINS_CONF_FILE + path = getenv(PROXYCHAINS_CONF_FILE_ENV_VAR); + if(check_path(path)) + goto have; + + // priority 2; proxychains conf in actual dir + path = getcwd(buf, sizeof(buf)); + snprintf(pbuf, bufsize, "%s/%s", path, PROXYCHAINS_CONF_FILE); + path = pbuf; + if(check_path(path)) + goto have; + + // priority 3; $HOME/.proxychains/proxychains.conf + path = getenv("HOME"); + snprintf(pbuf, bufsize, "%s/.proxychains/%s", path, PROXYCHAINS_CONF_FILE); + path = pbuf; + if(check_path(path)) + goto have; + + // priority 4: $SYSCONFDIR/proxychains.conf + path = SYSCONFDIR "/" PROXYCHAINS_CONF_FILE; + if(check_path(path)) + goto have; + + // priority 5: /etc/proxychains.conf + path = "/etc/" PROXYCHAINS_CONF_FILE; + if(check_path(path)) + goto have; + + perror("couldnt find configuration file"); + exit(1); + + return NULL; + have: + return path; +} diff --git a/src/common.h b/src/common.h index ecfc33c..156ef45 100644 --- a/src/common.h +++ b/src/common.h @@ -2,3 +2,7 @@ #define PROXYCHAINS_QUIET_MODE_ENV_VAR "PROXYCHAINS_QUIET_MODE" #define PROXYCHAINS_CONF_FILE "proxychains.conf" #define LOG_PREFIX "[proxychains] " + +#include + +char *get_config_path(char* default_path, char* pbuf, size_t bufsize); \ No newline at end of file diff --git a/src/core.c b/src/core.c index 801a8e3..ee719cd 100644 --- a/src/core.c +++ b/src/core.c @@ -33,6 +33,11 @@ #include #include #include +#include +#ifdef THREAD_SAFE +#include +pthread_mutex_t internal_ips_lock; +#endif #include "core.h" #include "common.h" @@ -147,21 +152,16 @@ static void encode_base_64(char *src, char *dest, int max_len) { *dest++ = 0; } -#define LOG_BUFF 1024*20 - -int proxychains_write_log(char *str, ...) { - char buff[LOG_BUFF]; +void proxychains_write_log(char *str, ...) { + char buff[1024*20]; va_list arglist; - FILE *log_file; - log_file = stderr; if(!proxychains_quiet_mode) { va_start(arglist, str); - vsprintf(buff, str, arglist); + vsnprintf(buff, sizeof(buff), str, arglist); va_end(arglist); - fprintf(log_file, "%s", buff); - fflush(log_file); + fprintf(stderr, "%s", buff); + fflush(stderr); } - return EXIT_SUCCESS; } static int write_n_bytes(int fd, char *buff, size_t size) { @@ -202,26 +202,15 @@ static int timed_connect(int sock, const struct sockaddr *addr, socklen_t len) { pfd[0].events = POLLOUT; fcntl(sock, F_SETFL, O_NONBLOCK); ret = true_connect(sock, addr, len); -#ifdef DEBUG - if(ret == -1) - perror("true_connect"); - printf("\nconnect ret=%d\n", ret); - - fflush(stdout); -#endif + PDEBUG("\nconnect ret=%d\n", ret); + if(ret == -1 && errno == EINPROGRESS) { ret = poll_retry(pfd, 1, tcp_connect_time_out); -#ifdef DEBUG - printf("\npoll ret=%d\n", ret); - fflush(stdout); -#endif + PDEBUG("\npoll ret=%d\n", ret); if(ret == 1) { value_len = sizeof(socklen_t); getsockopt(sock, SOL_SOCKET, SO_ERROR, &value, &value_len); -#ifdef DEBUG - printf("\nvalue=%d\n", value); - fflush(stdout); -#endif + PDEBUG("\nvalue=%d\n", value); if(!value) ret = 0; else @@ -230,6 +219,10 @@ static int timed_connect(int sock, const struct sockaddr *addr, socklen_t len) { ret = -1; } } else { +#ifdef DEBUG + if(ret == -1) + perror("true_connect"); +#endif if(ret != 0) ret = -1; } @@ -260,6 +253,8 @@ static int tunnel_to(int sock, ip_type ip, unsigned short port, proxy_type pt, c if(!dns_len) goto err; } + + PDEBUG("host dns %s\n", dns_name ? dns_name : ""); size_t ulen = strlen(user); size_t passlen = strlen(pass); @@ -597,7 +592,7 @@ static int chain_step(int ns, proxy_data * pfrom, proxy_data * pto) { break; case SOCKET_ERROR: pto->ps = DOWN_STATE; - proxychains_write_log("<--timeout\n"); + proxychains_write_log("<--socket error or timeout!\n"); close(ns); break; } @@ -719,14 +714,9 @@ int connect_proxy_chain(int sock, ip_type target_ip, return -1; } -// TODO: all those buffers aren't threadsafe, but since no memory allocation happens there shouldnt be any segfaults -static struct hostent hostent_space; -static in_addr_t resolved_addr; -static char *resolved_addr_p[2]; -static char addr_name[1024 * 8]; static const ip_type local_host = { {127, 0, 0, 1} }; -struct hostent *proxy_gethostbyname(const char *name) { +struct hostent *proxy_gethostbyname(const char *name, struct gethostbyname_data* data) { char buff[256]; uint32_t i, hash; // yep, new_mem never gets freed. once you passed a fake ip to the client, you can't "retreat" it @@ -734,18 +724,20 @@ struct hostent *proxy_gethostbyname(const char *name) { size_t l; struct hostent *hp; - resolved_addr_p[0] = (char *) &resolved_addr; - resolved_addr_p[1] = NULL; - hostent_space.h_addr_list = resolved_addr_p; - resolved_addr = 0; + data->resolved_addr_p[0] = (char *) &data->resolved_addr; + data->resolved_addr_p[1] = NULL; + + data->hostent_space.h_addr_list = data->resolved_addr_p; + + data->resolved_addr = 0; gethostname(buff, sizeof(buff)); if(!strcmp(buff, name)) { - resolved_addr = inet_addr(buff); - if(resolved_addr == (in_addr_t) (-1)) - resolved_addr = (in_addr_t) (local_host.as_int); - return &hostent_space; + data->resolved_addr = inet_addr(buff); + if(data->resolved_addr == (in_addr_t) (-1)) + data->resolved_addr = (in_addr_t) (local_host.as_int); + return &data->hostent_space; } memset(buff, 0, sizeof(buff)); @@ -762,7 +754,7 @@ struct hostent *proxy_gethostbyname(const char *name) { if(internal_ips.counter) { for(i = 0; i < internal_ips.counter; i++) { if(internal_ips.list[i]->hash == hash && !strcmp(name, internal_ips.list[i]->string)) { - resolved_addr = make_internal_ip(i); + data->resolved_addr = make_internal_ip(i); PDEBUG("got cached ip for %s\n", name); goto have_ip; } @@ -783,8 +775,8 @@ struct hostent *proxy_gethostbyname(const char *name) { } } - resolved_addr = make_internal_ip(internal_ips.counter); - if(resolved_addr == (in_addr_t) - 1) + data->resolved_addr = make_internal_ip(internal_ips.counter); + if(data->resolved_addr == (in_addr_t) - 1) goto err_plus_unlock; l = strlen(name); @@ -807,73 +799,77 @@ struct hostent *proxy_gethostbyname(const char *name) { MUTEX_UNLOCK(&internal_ips_lock); - strncpy(addr_name, name, sizeof(addr_name)); + strncpy(data->addr_name, name, sizeof(data->addr_name)); - hostent_space.h_name = addr_name; - hostent_space.h_length = sizeof(in_addr_t); - return &hostent_space; + data->hostent_space.h_name = data->addr_name; + data->hostent_space.h_length = sizeof(in_addr_t); + return &data->hostent_space; err_plus_unlock: MUTEX_UNLOCK(&internal_ips_lock); return NULL; } + +struct addrinfo_data { + struct addrinfo addrinfo_space; + struct sockaddr sockaddr_space; + char addr_name[256]; +}; + +void proxy_freeaddrinfo(struct addrinfo *res) { + free(res); +} + + int proxy_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { + struct gethostbyname_data ghdata; + struct addrinfo_data *space; struct servent *se = NULL; struct hostent *hp = NULL; - - struct sockaddr *sockaddr_space = NULL; - struct addrinfo *addrinfo_space = NULL; + struct servent se_buf; + struct addrinfo *p; + char buf[1024]; + int port; // printf("proxy_getaddrinfo node %s service %s\n",node,service); - addrinfo_space = malloc(sizeof(struct addrinfo)); - if(!addrinfo_space) - goto err1; - sockaddr_space = malloc(sizeof(struct sockaddr)); - if(!sockaddr_space) - goto err2; - memset(sockaddr_space, 0, sizeof(*sockaddr_space)); - memset(addrinfo_space, 0, sizeof(*addrinfo_space)); - if(node && !inet_aton(node, &((struct sockaddr_in *) sockaddr_space)->sin_addr)) { - hp = proxy_gethostbyname(node); - + space = calloc(1, sizeof(struct addrinfo_data)); + if(!space) goto err1; + + if(node && !inet_aton(node, &((struct sockaddr_in *) &space->sockaddr_space)->sin_addr)) { + hp = proxy_gethostbyname(node, &ghdata); if(hp) - memcpy(&((struct sockaddr_in *) sockaddr_space)->sin_addr, + memcpy(&((struct sockaddr_in *) &space->sockaddr_space)->sin_addr, *(hp->h_addr_list), sizeof(in_addr_t)); else - goto err3; + goto err2; } - if(service) - se = getservbyname(service, NULL); + if(service) getservbyname_r(service, NULL, &se_buf, buf, sizeof(buf), &se); - if(!se) { - ((struct sockaddr_in *) sockaddr_space)->sin_port = htons(atoi(service ? : "0")); - } else - ((struct sockaddr_in *) sockaddr_space)->sin_port = se->s_port; + port = se ? se->s_port : htons(atoi(service ? service : "0")); + ((struct sockaddr_in *) &space->sockaddr_space)->sin_port = port; - *res = addrinfo_space; - (*res)->ai_addr = sockaddr_space; + *res = p = &space->addrinfo_space; + assert((size_t)p == (size_t) space); + + p->ai_addr = &space->sockaddr_space; if(node) - strcpy(addr_name, node); - (*res)->ai_canonname = addr_name; - (*res)->ai_next = NULL; - (*res)->ai_family = sockaddr_space->sa_family = AF_INET; - (*res)->ai_addrlen = sizeof(*sockaddr_space); + strncpy(space->addr_name, node, sizeof(space->addr_name)); + p->ai_canonname = space->addr_name; + p->ai_next = NULL; + p->ai_family = space->sockaddr_space.sa_family = AF_INET; + p->ai_addrlen = sizeof(space->sockaddr_space); - if (hints != NULL) { - (*res)->ai_socktype = hints->ai_socktype; - (*res)->ai_flags = hints->ai_flags; - (*res)->ai_protocol = hints->ai_protocol; + if(hints) { + p->ai_socktype = hints->ai_socktype; + p->ai_flags = hints->ai_flags; + p->ai_protocol = hints->ai_protocol; } else { - (*res)->ai_socktype = 0; - (*res)->ai_flags = 0; - (*res)->ai_protocol = 0; + p->ai_flags = (AI_V4MAPPED | AI_ADDRCONFIG); } - + goto out; - err3: - free(sockaddr_space); err2: - free(addrinfo_space); + free(space); err1: return 1; out: diff --git a/src/core.h b/src/core.h index 3ca8cdd..7fcf597 100644 --- a/src/core.h +++ b/src/core.h @@ -4,10 +4,8 @@ begin : Tue May 14 2002 copyright : netcreature (C) 2002 email : netcreature@users.sourceforge.net - ***************************************************************************/ -#include - /* GPL */ -/*************************************************************************** + *************************************************************************** + *************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -15,10 +13,13 @@ * (at your option) any later version. * * * ***************************************************************************/ + +#include + #ifndef __CORE_HEADER #define __CORE_HEADER #define BUFF_SIZE 8*1024 // used to read responses from proxies. -#define MAX_LOCALNET 1024 +#define MAX_LOCALNET 64 typedef union { unsigned char octet[4]; @@ -60,7 +61,6 @@ typedef enum { BLOCKED // target's port blocked on last proxy in the chain } ERR_CODE; - typedef enum { HTTP_TYPE, SOCKS4_TYPE, @@ -99,60 +99,48 @@ typedef struct { char pass[256]; } proxy_data; -typedef struct { - proxy_data *pd; - chain_type ct; - unsigned int proxy_count; - int sock; - struct sockaddr addr; - int flags; -} thread_arg; +int connect_proxy_chain (int sock, ip_type target_ip, unsigned short target_port, + proxy_data * pd, unsigned int proxy_count, chain_type ct, + unsigned int max_chain ); -int connect_proxy_chain ( - int sock, - ip_type target_ip, - unsigned short target_port, - proxy_data * pd, - unsigned int proxy_count, - chain_type ct, - unsigned int max_chain ); - -int proxychains_write_log(char *str,...); -struct hostent* proxy_gethostbyname(const char *name); +void proxychains_write_log(char *str, ...); typedef int (*connect_t)(int, const struct sockaddr *, socklen_t); -connect_t true_connect; - typedef struct hostent* (*gethostbyname_t)(const char *); -gethostbyname_t true_gethostbyname; - -typedef int (*getaddrinfo_t)(const char *, const char *, - const struct addrinfo *, - struct addrinfo **); -getaddrinfo_t true_getaddrinfo; - typedef int (*freeaddrinfo_t)(struct addrinfo *); -freeaddrinfo_t true_freeaddrinfo; - -typedef int (*getnameinfo_t) (const struct sockaddr *, - socklen_t, char *, - socklen_t, char *, - socklen_t, unsigned int); -getnameinfo_t true_getnameinfo; - typedef struct hostent *(*gethostbyaddr_t) (const void *, socklen_t, int); -gethostbyaddr_t true_gethostbyaddr; -int proxy_getaddrinfo(const char *node, const char *service, - const struct addrinfo *hints, - struct addrinfo **res); +typedef int (*getaddrinfo_t)(const char *, const char *, const struct addrinfo *, + struct addrinfo **); -struct hostent* proxy_gethostbyname(const char *name); +typedef int (*getnameinfo_t) (const struct sockaddr *, socklen_t, char *, + socklen_t, char *, socklen_t, int); + + +extern connect_t true_connect; +extern gethostbyname_t true_gethostbyname; +extern getaddrinfo_t true_getaddrinfo; +extern freeaddrinfo_t true_freeaddrinfo; +extern getnameinfo_t true_getnameinfo; +extern gethostbyaddr_t true_gethostbyaddr; + +struct gethostbyname_data { + struct hostent hostent_space; + in_addr_t resolved_addr; + char *resolved_addr_p[2]; + char addr_name[1024 * 8]; +}; + +struct hostent* proxy_gethostbyname(const char *name, struct gethostbyname_data *data); + +int proxy_getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, struct addrinfo **res); +void proxy_freeaddrinfo(struct addrinfo *res); #ifdef DEBUG -# define PDEBUG(fmt, args...) fprintf(stderr,"DEBUG:"fmt, ## args) +# define PDEBUG(fmt, args...) do { fprintf(stderr,"DEBUG:"fmt, ## args); fflush(stderr); } while(0) #else -# define PDEBUG(fmt, args...) +# define PDEBUG(fmt, args...) do {} while (0) #endif #endif diff --git a/src/libproxychains.c b/src/libproxychains.c index 0353b4a..2ada0b5 100644 --- a/src/libproxychains.c +++ b/src/libproxychains.c @@ -41,106 +41,105 @@ #define SOCKADDR_2(x) (satosin(x)->sin_addr) #define SOCKPORT(x) (satosin(x)->sin_port) #define SOCKFAMILY(x) (satosin(x)->sin_family) -#define MAX_CHAIN 30*1024 +#define MAX_CHAIN 512 + +connect_t true_connect; +gethostbyname_t true_gethostbyname; +getaddrinfo_t true_getaddrinfo; +freeaddrinfo_t true_freeaddrinfo; +getnameinfo_t true_getnameinfo; +gethostbyaddr_t true_gethostbyaddr; int tcp_read_time_out; int tcp_connect_time_out; int proxychains_got_chain_data = 0; int proxychains_quiet_mode = 0; int proxychains_resolver = 0; -static int init_l = 0; - -unsigned int proxychains_proxy_count = 0; -unsigned int proxychains_max_chain = 1; -unsigned int remote_dns_subnet = 224; - localaddr_arg localnet_addr[MAX_LOCALNET]; chain_type proxychains_ct; proxy_data proxychains_pd[MAX_CHAIN]; size_t num_localnet_addr = 0; -static inline void get_chain_data(proxy_data * pd, unsigned int *proxy_count, chain_type * ct); -static void init_lib(void); +#ifdef THREAD_SAFE +pthread_once_t init_once = PTHREAD_ONCE_INIT; +#endif +static int init_l = 0; -static void init_lib(void) { +static inline void get_chain_data(proxy_data * pd, unsigned int *proxy_count, chain_type * ct); + +static void* load_sym(char* symname, void* proxyfunc) { + + void *funcptr = dlsym(RTLD_NEXT, symname); + + if(!funcptr) { + fprintf(stderr, "Cannot load symbol '%s' %s\n", symname, dlerror()); + exit(1); + } else { + PDEBUG("loaded symbol '%s'" " real addr %p wrapped addr %p\n", symname, funcptr, proxyfunc); + } + if(funcptr == proxyfunc) { + PDEBUG("circular reference detected, aborting!\n"); + abort(); + } + return funcptr; +} + +#define INIT() init_lib_wrapper(__FUNCTION__) + +#define SETUP_SYM(X) do { true_ ## X = load_sym( # X, X ); } while(0) + +static void do_init(void) { MUTEX_INIT(&internal_ips_lock, NULL); /* read the config file */ get_chain_data(proxychains_pd, &proxychains_proxy_count, &proxychains_ct); proxychains_write_log(LOG_PREFIX "DLL init\n"); - - true_connect = (connect_t) dlsym(RTLD_NEXT, "connect"); - - if(!true_connect) { - fprintf(stderr, "Cannot load symbol 'connect' %s\n", dlerror()); - exit(1); - } else { - PDEBUG("loaded symbol 'connect'" " real addr %p wrapped addr %p\n", true_connect, connect); - } - if(connect == true_connect) { - PDEBUG("circular reference detected, aborting!\n"); - abort(); - } - - true_gethostbyname = (gethostbyname_t) - dlsym(RTLD_NEXT, "gethostbyname"); - - if(!true_gethostbyname) { - fprintf(stderr, "Cannot load symbol 'gethostbyname' %s\n", dlerror()); - exit(1); - } else { - PDEBUG("loaded symbol 'gethostbyname'" - " real addr %p wrapped addr %p\n", true_gethostbyname, gethostbyname); - } - true_getaddrinfo = (getaddrinfo_t) - dlsym(RTLD_NEXT, "getaddrinfo"); - - if(!true_getaddrinfo) { - fprintf(stderr, "Cannot load symbol 'getaddrinfo' %s\n", dlerror()); - exit(1); - } else { - PDEBUG("loaded symbol 'getaddrinfo'" " real addr %p wrapped addr %p\n", true_getaddrinfo, getaddrinfo); - } - true_freeaddrinfo = (freeaddrinfo_t) - dlsym(RTLD_NEXT, "freeaddrinfo"); - - if(!true_freeaddrinfo) { - fprintf(stderr, "Cannot load symbol 'freeaddrinfo' %s\n", dlerror()); - exit(1); - } else { - PDEBUG("loaded symbol 'freeaddrinfo'" - " real addr %p wrapped addr %p\n", true_freeaddrinfo, freeaddrinfo); - } - true_gethostbyaddr = (gethostbyaddr_t) - dlsym(RTLD_NEXT, "gethostbyaddr"); - - if(!true_gethostbyaddr) { - fprintf(stderr, "Cannot load symbol 'gethostbyaddr' %s\n", dlerror()); - exit(1); - } else { - PDEBUG("loaded symbol 'gethostbyaddr'" - " real addr %p wrapped addr %p\n", true_gethostbyaddr, gethostbyaddr); - } - true_getnameinfo = (getnameinfo_t) - dlsym(RTLD_NEXT, "getnameinfo"); - - if(!true_getnameinfo) { - fprintf(stderr, "Cannot load symbol 'getnameinfo' %s\n", dlerror()); - exit(1); - } else { - PDEBUG("loaded symbol 'getnameinfo'" " real addr %p wrapped addr %p\n", true_getnameinfo, getnameinfo); - } + + SETUP_SYM(connect); + SETUP_SYM(gethostbyname); + SETUP_SYM(getaddrinfo); + SETUP_SYM(freeaddrinfo); + SETUP_SYM(gethostbyaddr); + SETUP_SYM(getnameinfo); + init_l = 1; } -static inline void get_chain_data(proxy_data * pd, unsigned int *proxy_count, chain_type * ct) { +static void init_lib_wrapper(const char* caller) { +#ifndef DEBUG + (void) caller; +#endif +#ifndef THREAD_SAFE + if(init_l) return; + PDEBUG("%s called from %s\n", __FUNCTION__, caller); + do_init(); +#else + if(!init_l) PDEBUG("%s called from %s\n", __FUNCTION__, caller); + pthread_once(&init_once, do_init); +#endif +} + +/* if we use gcc >= 3, we can instruct the dynamic loader + * to call init_lib at link time. otherwise it gets loaded + * lazily, which has the disadvantage that there's a potential + * race condition if 2 threads call it before init_l is set + * and PTHREAD support was disabled */ +#if __GNUC__ > 2 +__attribute__((constructor)) +static void gcc_init(void) { + INIT(); +} +#endif + +/* get configuration from config file */ +static void get_chain_data(proxy_data * pd, unsigned int *proxy_count, chain_type * ct) { int count = 0, port_n = 0, list = 0; char buff[1024], type[1024], host[1024], user[1024]; char *env; char local_in_addr_port[32]; char local_in_addr[32], local_in_port[32], local_netmask[32]; - FILE *file; + FILE *file = NULL; if(proxychains_got_chain_data) return; @@ -149,22 +148,9 @@ static inline void get_chain_data(proxy_data * pd, unsigned int *proxy_count, ch tcp_read_time_out = 4 * 1000; tcp_connect_time_out = 10 * 1000; *ct = DYNAMIC_TYPE; - - /* - * Get path to configuration file from env this file has priority - * if it's defined. - */ - env = getenv(PROXYCHAINS_CONF_FILE_ENV_VAR); - - snprintf(buff, 256, "%s/.proxychains/proxychains.conf", getenv("HOME")); - - if(!env || (!(file = fopen(env, "r")))) - if(!(file = fopen("./proxychains.conf", "r"))) - if(!(file = fopen(buff, "r"))) - if(!(file = fopen("/etc/proxychains.conf", "r"))) { - perror("Can't locate proxychains.conf"); - exit(1); - } + + env = get_config_path(getenv(PROXYCHAINS_CONF_FILE_ENV_VAR), buff, sizeof(buff)); + file = fopen(env, "r"); env = getenv(PROXYCHAINS_QUIET_MODE_ENV_VAR); if(env && *env == '1') @@ -172,7 +158,11 @@ static inline void get_chain_data(proxy_data * pd, unsigned int *proxy_count, ch while(fgets(buff, sizeof(buff), file)) { if(buff[0] != '\n' && buff[strspn(buff, " ")] != '#') { + /* proxylist has to come last */ if(list) { + if(count >= MAX_CHAIN) + break; + memset(&pd[count], 0, sizeof(proxy_data)); pd[count].ps = PLAY_STATE; @@ -193,8 +183,7 @@ static inline void get_chain_data(proxy_data * pd, unsigned int *proxy_count, ch continue; if(pd[count].ip.as_int && port_n && pd[count].ip.as_int != (uint32_t) - 1) - if(++count == MAX_CHAIN) - break; + count++; } else { if(strstr(buff, "[ProxyList]")) { list = 1; @@ -224,7 +213,7 @@ static inline void get_chain_data(proxy_data * pd, unsigned int *proxy_count, ch memset(local_in_port, 0, sizeof(local_in_port) / sizeof(local_in_port[0])); if(sscanf(local_in_addr_port, "%15[^:]:%5s", local_in_addr, local_in_port) < 2) { - PDEBUG("added localnet: netaddr=%s, port=%s\n", + PDEBUG("added localnet: netaddr=%s, netmask=%s\n", local_in_addr, local_netmask); } else { PDEBUG("added localnet: netaddr=%s, port=%s, netmask=%s\n", @@ -275,6 +264,8 @@ static inline void get_chain_data(proxy_data * pd, unsigned int *proxy_count, ch proxychains_got_chain_data = 1; } +/******* HOOK FUNCTIONS *******/ + int connect(int sock, const struct sockaddr *addr, unsigned int len) { int socktype = 0, flags = 0, ret = 0; socklen_t optlen = 0; @@ -285,9 +276,8 @@ int connect(int sock, const struct sockaddr *addr, unsigned int len) { struct in_addr *p_addr_in; unsigned short port; size_t i; - - if(!init_l) - init_lib(); + int remote_dns_connect = 0; + INIT(); optlen = sizeof(socktype); getsockopt(sock, SOL_SOCKET, SO_TYPE, &socktype, &optlen); if(!(SOCKFAMILY(*addr) == AF_INET && socktype == SOCK_STREAM)) @@ -302,10 +292,14 @@ int connect(int sock, const struct sockaddr *addr, unsigned int len) { PDEBUG("target: %s\n", inet_ntop(AF_INET, p_addr_in, str, sizeof(str))); PDEBUG("port: %d\n", port); #endif - for(i = 0; i < num_localnet_addr; i++) { + + // check if connect called from proxydns + remote_dns_connect = (ntohl(p_addr_in->s_addr) >> 24 == remote_dns_subnet); + + for(i = 0; i < num_localnet_addr && !remote_dns_connect; i++) { if((localnet_addr[i].in_addr.s_addr & localnet_addr[i].netmask.s_addr) == (p_addr_in->s_addr & localnet_addr[i].netmask.s_addr)) { - if(localnet_addr[i].port || localnet_addr[i].port == port) { + if(!localnet_addr[i].port || localnet_addr[i].port == port) { PDEBUG("accessing localnet using true_connect\n"); return true_connect(sock, addr, len); } @@ -329,14 +323,14 @@ int connect(int sock, const struct sockaddr *addr, unsigned int len) { return ret; } +static struct gethostbyname_data ghbndata; struct hostent *gethostbyname(const char *name) { - if(!init_l) - init_lib(); + INIT(); PDEBUG("gethostbyname: %s\n", name); if(proxychains_resolver) - return proxy_gethostbyname(name); + return proxy_gethostbyname(name, &ghbndata); else return true_gethostbyname(name); @@ -346,8 +340,7 @@ struct hostent *gethostbyname(const char *name) { int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { int ret = 0; - if(!init_l) - init_lib(); + INIT(); PDEBUG("getaddrinfo: %s %s\n", node, service); @@ -360,19 +353,14 @@ int getaddrinfo(const char *node, const char *service, const struct addrinfo *hi } void freeaddrinfo(struct addrinfo *res) { - if(!init_l) - init_lib(); + INIT(); PDEBUG("freeaddrinfo %p \n", res); if(!proxychains_resolver) true_freeaddrinfo(res); - else { - if (res != NULL) { - free(res->ai_addr); - free(res); - } - } + else + proxy_freeaddrinfo(res); return; } @@ -389,9 +377,8 @@ int getnameinfo(const struct sockaddr *sa, char ip_buf[16]; int ret = 0; - if(!init_l) - init_lib(); - + INIT(); + PDEBUG("getnameinfo: %s %s\n", host, serv); if(!proxychains_resolver) { @@ -413,8 +400,7 @@ struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type) { static char *list[2]; static struct hostent he; - if(!init_l) - init_lib(); + INIT(); PDEBUG("TODO: proper gethostbyaddr hook\n"); diff --git a/src/main.c b/src/main.c index 122f92f..4fd4d23 100644 --- a/src/main.c +++ b/src/main.c @@ -1,12 +1,4 @@ -/*************************************************************************** - main.c - description - - begin : Tue May 14 2002 - copyright : netcreature (C) 2002 - email : netcreature@users.sourceforge.net - ***************************************************************************/ - /* GPL */ -/*************************************************************************** +/* (C) 2011, 2012 rofl0r * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -14,6 +6,7 @@ * (at your option) any later version. * * * ***************************************************************************/ + #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #undef _XOPEN_SOURCE @@ -40,12 +33,6 @@ static int usage(char **argv) { return EXIT_FAILURE; } -int check_path(char *path) { - if(!path) - return 0; - return access(path, R_OK) != -1; -} - static const char *dll_name = DLL_NAME; static char own_dir[256]; @@ -106,44 +93,8 @@ int main(int argc, char *argv[]) { return usage(argv); /* check if path of config file has not been passed via command line */ - if(!path) { - // priority 1: env var PROXYCHAINS_CONF_FILE - path = getenv(PROXYCHAINS_CONF_FILE_ENV_VAR); - - if(check_path(path)) - goto have; - - // priority 2; proxychains conf in actual dir - path = getcwd(buf, sizeof(buf)); - snprintf(pbuf, sizeof(pbuf), "%s/%s", path, PROXYCHAINS_CONF_FILE); - path = pbuf; - - if(check_path(path)) - goto have; - - // priority 3; $HOME/.proxychains/proxychains.conf - path = getenv("HOME"); - snprintf(pbuf, sizeof(pbuf), "%s/.proxychains/%s", path, PROXYCHAINS_CONF_FILE); - path = pbuf; - if(check_path(path)) - goto have; - - // priority 4: $INSTALL_PREFIX/etc/proxychains.conf - snprintf(pbuf, sizeof(pbuf), "%s/%s", INSTALL_PREFIX, PROXYCHAINS_CONF_FILE); - path = pbuf; - if(check_path(path)) - goto have; - - // priority 5: /etc/proxychains.conf - path = "/etc/proxychains.conf"; - if(check_path(path)) - goto have; - perror("couldnt find configuration file"); - return 1; - } - - have: - + path = get_config_path(path, pbuf, sizeof(pbuf)); + if(!quiet) fprintf(stderr, LOG_PREFIX "config file found: %s\n", path); diff --git a/tests/test_getaddrinfo.c b/tests/test_getaddrinfo.c new file mode 100644 index 0000000..7581428 --- /dev/null +++ b/tests/test_getaddrinfo.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include + +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif + +int main(void) { + struct addrinfo *result; + struct addrinfo *res; + int error; + + /* resolve the domain name into a list of addresses */ + error = getaddrinfo("www.example.com", NULL, NULL, &result); + if (error != 0) + { + fprintf(stderr, "error in getaddrinfo: %s\n", gai_strerror(error)); + return EXIT_FAILURE; + } + + /* loop over all returned results and do inverse lookup */ + for (res = result; res != NULL; res = res->ai_next) + { + char hostname[NI_MAXHOST] = ""; + + error = getnameinfo(res->ai_addr, res->ai_addrlen, hostname, NI_MAXHOST, NULL, 0, 0); + if (error != 0) + { + fprintf(stderr, "error in getnameinfo: %s\n", gai_strerror(error)); + continue; + } + if (*hostname != '\0') + printf("hostname: %s\n", hostname); + } + + freeaddrinfo(result); + return EXIT_SUCCESS; +}