diff options
-rw-r--r-- | Makefile | 25 | ||||
-rwxr-xr-x | configure | 3 | ||||
-rw-r--r-- | src/allocator_thread.h | 2 | ||||
-rw-r--r-- | src/core.c | 15 | ||||
-rw-r--r-- | src/daemon/daemon.c | 231 | ||||
-rw-r--r-- | src/daemon/hsearch.c | 188 | ||||
-rw-r--r-- | src/daemon/hsearch.h | 21 | ||||
-rw-r--r-- | src/daemon/sblist.c | 73 | ||||
-rw-r--r-- | src/daemon/sblist.h | 92 | ||||
-rw-r--r-- | src/daemon/sblist_delete.c | 9 | ||||
-rw-r--r-- | src/daemon/udpclient.c | 41 | ||||
-rw-r--r-- | src/daemon/udpserver.c | 65 | ||||
-rw-r--r-- | src/daemon/udpserver.h | 48 | ||||
-rw-r--r-- | src/libproxychains.c | 58 | ||||
-rw-r--r-- | src/proxychains.conf | 24 | ||||
-rw-r--r-- | src/rdns.c | 99 | ||||
-rw-r--r-- | src/rdns.h | 28 | ||||
-rw-r--r-- | src/remotedns.h | 2 |
18 files changed, 979 insertions, 45 deletions
@@ -13,13 +13,18 @@ includedir = $(prefix)/include libdir = $(prefix)/lib sysconfdir=$(prefix)/etc -SRCS = $(sort $(wildcard src/*.c)) -OBJS = $(SRCS:.c=.o) +OBJS = src/common.o src/main.o + +DOBJS = src/daemon/hsearch.o \ + src/daemon/sblist.o src/daemon/sblist_delete.o \ + src/daemon/daemon.o src/daemon/udpserver.o + LOBJS = src/nameinfo.o src/version.o \ src/core.o src/common.o src/libproxychains.o \ - src/allocator_thread.o \ + src/allocator_thread.o src/rdns.o \ src/hostsreader.o src/hash.o src/debug.o + GENH = src/version.h CFLAGS += -Wall -O0 -g -std=c99 -D_GNU_SOURCE -pipe @@ -41,7 +46,8 @@ LDSO_PATHNAME = libproxychains4.$(LDSO_SUFFIX) SHARED_LIBS = $(LDSO_PATHNAME) ALL_LIBS = $(SHARED_LIBS) PXCHAINS = proxychains4 -ALL_TOOLS = $(PXCHAINS) +PXCHAINS_D = proxychains4-daemon +ALL_TOOLS = $(PXCHAINS) $(PXCHAINS_D) ALL_CONFIGS = src/proxychains.conf -include config.mak @@ -70,7 +76,7 @@ install-config: $(ALL_CONFIGS:src/%=$(DESTDIR)$(sysconfdir)/%) clean: rm -f $(ALL_LIBS) rm -f $(ALL_TOOLS) - rm -f $(OBJS) + rm -f $(OBJS) $(LOBJS) $(DOBJS) rm -f $(GENH) src/version.h: $(wildcard VERSION .git) @@ -83,10 +89,13 @@ src/version.o: src/version.h $(LDSO_PATHNAME): $(LOBJS) $(CC) $(LDFLAGS) $(LD_SET_SONAME)$(LDSO_PATHNAME) $(USER_LDFLAGS) \ - -shared -o $@ $(LOBJS) $(SOCKET_LIBS) + -shared -o $@ $^ $(SOCKET_LIBS) + +$(PXCHAINS): $(OBJS) + $(CC) $^ $(USER_LDFLAGS) $(LIBDL) -o $@ -$(ALL_TOOLS): $(OBJS) - $(CC) src/main.o src/common.o $(USER_LDFLAGS) $(LIBDL) -o $(PXCHAINS) +$(PXCHAINS_D): $(DOBJS) + $(CC) $^ $(USER_LDFLAGS) -o $@ .PHONY: all clean install install-config install-libs install-tools @@ -159,6 +159,9 @@ check_compile 'whether we have GNU-style getservbyname_r()' "-DHAVE_GNU_GETSERVB check_compile 'whether we have pipe2() and O_CLOEXEC' "-DHAVE_PIPE2" \ '#define _GNU_SOURCE\n#include <fcntl.h>\n#include <unistd.h>\nint main() {\nint pipefd[2];\npipe2(pipefd, O_CLOEXEC);\nreturn 0;}' +check_compile 'whether we have SOCK_CLOEXEC' "-DHAVE_SOCK_CLOEXEC" \ +'#define _GNU_SOURCE\n#include <sys/socket.h>\nint main() {\nreturn socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0);}' + check_define __APPLE__ && { mac_detected=true check_define __x86_64__ && mac_64=true diff --git a/src/allocator_thread.h b/src/allocator_thread.h index 734ed89..1e8fae3 100644 --- a/src/allocator_thread.h +++ b/src/allocator_thread.h @@ -4,8 +4,6 @@ #include <unistd.h> #include "ip_type.h" -#define MSG_LEN_MAX 256 - extern int req_pipefd[2]; extern int resp_pipefd[2]; @@ -37,13 +37,12 @@ #include "core.h" #include "common.h" -#include "allocator_thread.h" +#include "rdns.h" #include "mutex.h" extern int tcp_read_time_out; extern int tcp_connect_time_out; extern int proxychains_quiet_mode; -extern int proxychains_resolver; extern unsigned int proxychains_proxy_offset; extern unsigned int remote_dns_subnet; @@ -200,8 +199,8 @@ static int tunnel_to(int sock, ip_type ip, unsigned short port, proxy_type pt, c // the range 224-255.* is reserved, and it won't go outside (unless the app does some other stuff with // the results returned from gethostbyname et al.) // the hardcoded number 224 can now be changed using the config option remote_dns_subnet to i.e. 127 - if(!ip.is_v6 && proxychains_resolver && ip.addr.v4.octet[0] == remote_dns_subnet) { - dns_len = at_get_host_for_ip(ip.addr.v4, hostnamebuf); + if(!ip.is_v6 && proxychains_resolver >= DNSLF_RDNS_START && ip.addr.v4.octet[0] == remote_dns_subnet) { + dns_len = rdns_get_host_for_ip(ip.addr.v4, hostnamebuf); if(!dns_len) goto err; else dns_name = hostnamebuf; } @@ -525,8 +524,8 @@ static int chain_step(int ns, proxy_data * pfrom, proxy_data * pto) { PFUNC(); - if(!v6 && proxychains_resolver && pto->ip.addr.v4.octet[0] == remote_dns_subnet) { - if(!at_get_host_for_ip(pto->ip.addr.v4, hostname_buf)) goto usenumericip; + if(!v6 && proxychains_resolver >= DNSLF_RDNS_START && pto->ip.addr.v4.octet[0] == remote_dns_subnet) { + if(!rdns_get_host_for_ip(pto->ip.addr.v4, hostname_buf)) goto usenumericip; else hostname = hostname_buf; } else { usenumericip: @@ -865,7 +864,7 @@ struct hostent *proxy_gethostbyname(const char *name, struct gethostbyname_data* goto retname; } - data->resolved_addr = at_get_ip_for_host((char*) name, strlen(name)).as_int; + data->resolved_addr = rdns_get_ip_for_host((char*) name, strlen(name)).as_int; if(data->resolved_addr == (in_addr_t) IPT4_INVALID.as_int) return NULL; retname: @@ -961,7 +960,7 @@ int proxy_getaddrinfo(const char *node, const char *service, const struct addrin free(space); return EAI_NONAME; } - if(proxychains_resolver == 2) + if(proxychains_resolver == DNSLF_FORKEXEC) hp = proxy_gethostbyname_old(node); else hp = proxy_gethostbyname(node, &ghdata); diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c new file mode 100644 index 000000000..a0e8f2a --- /dev/null +++ b/src/daemon/daemon.c @@ -0,0 +1,231 @@ +/* + proxychains-ng DNS daemon + + Copyright (C) 2020 rofl0r. + +*/ + +#define _GNU_SOURCE +#include <unistd.h> +#define _POSIX_C_SOURCE 200809L +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <pthread.h> +#include <signal.h> +#include <sys/select.h> +#include <arpa/inet.h> +#include <errno.h> +#include <limits.h> +#include "udpserver.h" +#include "sblist.h" +#include "hsearch.h" +#include "../remotedns.h" +#include "../ip_type.h" + +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif + +static struct htab *ip_lookup_table; +static sblist *hostnames; +static unsigned remote_subnet; +static const struct server* server; + +#ifndef CONFIG_LOG +#define CONFIG_LOG 1 +#endif +#if CONFIG_LOG +/* we log to stderr because it's not using line buffering, i.e. malloc which would need + locking when called from different threads. for the same reason we use dprintf, + which writes directly to an fd. */ +#define dolog(...) dprintf(2, __VA_ARGS__) +#else +static void dolog(const char* fmt, ...) { } +#endif + +static char* my_inet_ntoa(unsigned char *ip_buf_4_bytes, char *outbuf_16_bytes) { + unsigned char *p; + char *o = outbuf_16_bytes; + unsigned char n; + for(p = ip_buf_4_bytes; p < ip_buf_4_bytes + 4; p++) { + n = *p; + if(*p >= 100) { + if(*p >= 200) + *(o++) = '2'; + else + *(o++) = '1'; + n %= 100; + } + if(*p >= 10) { + *(o++) = (n / 10) + '0'; + n %= 10; + } + *(o++) = n + '0'; + *(o++) = '.'; + } + o[-1] = 0; + return outbuf_16_bytes; +} + + +/* buf needs to be long enough for an ipv6 addr, i.e. INET6_ADDRSTRLEN + 1 */ +static char* ipstr(union sockaddr_union *su, char* buf) { + int af = SOCKADDR_UNION_AF(su); + void *ipdata = SOCKADDR_UNION_ADDRESS(su); + inet_ntop(af, ipdata, buf, INET6_ADDRSTRLEN+1); + char portbuf[7]; + snprintf(portbuf, sizeof portbuf, ":%u", (unsigned) ntohs(SOCKADDR_UNION_PORT(su))); + strcat(buf, portbuf); + return buf; +} + +static int usage(char *a0) { + dprintf(2, + "Proxychains-NG remote dns daemon\n" + "--------------------------------\n" + "usage: %s -i listenip -p port -r remotesubnet\n" + "all arguments are optional.\n" + "by default listenip is 127.0.0.1, port 1053 and remotesubnet 224.\n\n", a0 + ); + return 1; +} + +unsigned index_from_ip(ip_type4 internalip) { + ip_type4 tmp = internalip; + uint32_t ret; + ret = tmp.octet[3] + (tmp.octet[2] << 8) + (tmp.octet[1] << 16); + ret -= 1; + return ret; +} + +char *host_from_ip(ip_type4 internalip) { + char *res = NULL; + unsigned index = index_from_ip(internalip); + if(index < sblist_getsize(hostnames)) { + char **tmp = sblist_get(hostnames, index); + if(tmp && *tmp) res = *tmp; + } + return res; +} + +ip_type4 get_ip_from_index(unsigned index) { + ip_type4 ret; + index++; // so we can start at .0.0.1 + if(index > 0xFFFFFF) + return IPT4_INVALID; + ret.octet[0] = remote_subnet & 0xFF; + ret.octet[1] = (index & 0xFF0000) >> 16; + ret.octet[2] = (index & 0xFF00) >> 8; + ret.octet[3] = index & 0xFF; + return ret; +} + +ip_type4 get_ip(char* hn) { + htab_value *v = htab_find(ip_lookup_table, hn); + if(v) return get_ip_from_index(v->n); + char *n = strdup(hn); + if(!n) return IPT4_INVALID; + if(!sblist_add(hostnames, &n)) { + o_out:; + free(n); + return IPT4_INVALID; + } + if(!htab_insert(ip_lookup_table, n, HTV_N(sblist_getsize(hostnames)-1))) { + sblist_delete(hostnames, sblist_getsize(hostnames)-1); + goto o_out; + } + return get_ip_from_index(sblist_getsize(hostnames)-1); +} + +int main(int argc, char** argv) { + int ch; + const char *listenip = "127.0.0.1"; + unsigned port = 1053; + remote_subnet = 224; + while((ch = getopt(argc, argv, ":r:i:p:")) != -1) { + switch(ch) { + case 'r': + remote_subnet = atoi(optarg); + break; + case 'i': + listenip = optarg; + break; + case 'p': + port = atoi(optarg); + break; + case ':': + dprintf(2, "error: option -%c requires an operand\n", optopt); + /* fall through */ + case '?': + return usage(argv[0]); + } + } + signal(SIGPIPE, SIG_IGN); + struct server s; + if(server_setup(&s, listenip, port)) { + perror("server_setup"); + return 1; + } + server = &s; + + ip_lookup_table = htab_create(64); + hostnames = sblist_new(sizeof(char*), 64); + + while(1) { + struct client c; + char ipstr_buf[INET6_ADDRSTRLEN+6+1]; + char ip4str_buf[16]; + struct at_msg msg, out; + size_t msgl = sizeof(msg); + int failed = 0; + +#define FAIL() do { failed=1; goto sendresp; } while(0) + + if(server_waitclient(&s, &c, &msg, &msgl)) continue; + msg.h.datalen = ntohs(msg.h.datalen); + if(msgl != sizeof(msg.h)+msg.h.datalen) { + dolog("%s: invalid datalen\n", ipstr(&c.addr, ipstr_buf)); + FAIL(); + } + + out.h.msgtype = msg.h.msgtype; + if(msg.h.msgtype == ATM_GETIP) { + if(!memchr(msg.m.host, 0, msg.h.datalen)) { + dolog("%s: nul terminator missing\n", ipstr(&c.addr, ipstr_buf)); + FAIL(); + } + out.h.datalen = sizeof(ip_type4); + out.m.ip = get_ip(msg.m.host); + failed = !memcmp(&out.m.ip, &IPT4_INVALID, 4); + dolog("%s requested ip for %s (%s)\n", ipstr(&c.addr, ipstr_buf), + msg.m.host, failed?"FAIL":my_inet_ntoa((void*)&out.m.ip, ip4str_buf)); + if(failed) FAIL(); + } else if (msg.h.msgtype == ATM_GETNAME) { + if(msg.h.datalen != 4) { + dolog("%s: invalid len for getname request\n", ipstr(&c.addr, ipstr_buf)); + FAIL(); + } + char *hn = host_from_ip(msg.m.ip); + if(hn) { + size_t l = strlen(hn); + memcpy(out.m.host, hn, l+1); + out.h.datalen = l+1; + } + dolog("%s requested name for %s (%s)\n", ipstr(&c.addr, ipstr_buf), + my_inet_ntoa((void*) &msg.m.ip, ip4str_buf), hn?hn:"FAIL"); + if(!hn) FAIL(); + } else { + dolog("%s: unknown request %u\n", ipstr(&c.addr, ipstr_buf), + (unsigned) msg.h.msgtype); + } + sendresp:; + if(failed) { + out.h.msgtype = ATM_FAIL; + out.h.datalen = 0; + } + unsigned short dlen = out.h.datalen; + out.h.datalen = htons(dlen); + sendto(server->fd, &out, sizeof(out.h)+dlen, 0, (void*) &c.addr, SOCKADDR_UNION_LENGTH(&c.addr)); + } +} diff --git a/src/daemon/hsearch.c b/src/daemon/hsearch.c new file mode 100644 index 000000000..a00e87b --- /dev/null +++ b/src/daemon/hsearch.c @@ -0,0 +1,188 @@ +/* +musl license, hsearch.c originally written by Szabolcs Nagy + +Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include <stdlib.h> +#include <string.h> +#include "hsearch.h" + +/* +open addressing hash table with 2^n table size +quadratic probing is used in case of hash collision +tab indices and hash are size_t +after resize fails with ENOMEM the state of tab is still usable +*/ + +typedef struct htab_entry { + char *key; + htab_value data; +} htab_entry; + +struct elem { + htab_entry item; + size_t hash; +}; + +struct htab { + struct elem *elems; + size_t mask; + size_t used; +}; + +#define MINSIZE 8 +#define MAXSIZE ((size_t)-1/2 + 1) + +static size_t keyhash(char *k) +{ + unsigned char *p = (void *)k; + size_t h = 0; + + while (*p) + h = 31*h + *p++; + return h; +} + +static int resize(struct htab *htab, size_t nel) +{ + size_t newsize; + size_t i, j; + struct elem *e, *newe; + struct elem *oldtab = htab->elems; + struct elem *oldend = htab->elems + htab->mask + 1; + + if (nel > MAXSIZE) + nel = MAXSIZE; + for (newsize = MINSIZE; newsize < nel; newsize *= 2); + htab->elems = calloc(newsize, sizeof *htab->elems); + if (!htab->elems) { + htab->elems = oldtab; + return 0; + } + htab->mask = newsize - 1; + if (!oldtab) + return 1; + for (e = oldtab; e < oldend; e++) + if (e->item.key) { + for (i=e->hash,j=1; ; i+=j++) { + newe = htab->elems + (i & htab->mask); + if (!newe->item.key) + break; + } + *newe = *e; + } + free(oldtab); + return 1; +} + +static struct elem *lookup(struct htab *htab, char *key, size_t hash) +{ + size_t i, j; + struct elem *e; + + for (i=hash,j=1; ; i+=j++) { + e = htab->elems + (i & htab->mask); + if (!e->item.key || + (e->hash==hash && strcmp(e->item.key, key)==0)) + break; + } + return e; +} + +struct htab *htab_create(size_t nel) +{ + struct htab *r = calloc(1, sizeof *r); + if(r && !resize(r, nel)) { + free(r); + r = 0; + } + return r; +} + +void htab_destroy(struct htab *htab) +{ + free(htab->elems); + free(htab); +} + +static htab_entry *htab_find_item(struct htab *htab, char* key) +{ + size_t hash = keyhash(key); + struct elem *e = lookup(htab, key, hash); + + if (e->item.key) { + return &e->item; + } + return 0; +} + +htab_value* htab_find(struct htab *htab, char* key) +{ + htab_entry *i = htab_find_item(htab, key); + if(i) return &i->data; + return 0; +} + +int htab_delete(struct htab *htab, char* key) +{ + htab_entry *i = htab_find_item(htab, key); + if(!i) return 0; + i->key = 0; + return 1; +} + +int htab_insert(struct htab *htab, char* key, htab_value value) +{ + size_t hash = keyhash(key); + struct elem *e = lookup(htab, key, hash); + if(e->item.key) { + /* it's not allowed to overwrite existing data */ + return 0; + } + + e->item.key = key; + e->item.data = value; + e->hash = hash; + if (++htab->used > htab->mask - htab->mask/4) { + if (!resize(htab, 2*htab->used)) { + htab->used--; + e->item.key = 0; + return 0; + } + } + return 1; +} + +size_t htab_next(struct htab *htab, size_t iterator, char** key, htab_value **v) +{ + size_t i; + for(i=iterator;i<htab->mask+1;++i) { + struct elem *e = htab->elems + i; + if(e->item.key) { + *key = e->item.key; + *v = &e->item.data; + return i+1; + } + } + return 0; +} diff --git a/src/daemon/hsearch.h b/src/daemon/hsearch.h new file mode 100644 index 000000000..98da86d --- /dev/null +++ b/src/daemon/hsearch.h @@ -0,0 +1,21 @@ +#ifndef HSEARCH_H +#define HSEARCH_H + +#include <stdlib.h> + +typedef union htab_value { + void *p; + size_t n; +} htab_value; + +#define HTV_N(N) (htab_value) {.n = N} +#define HTV_P(P) (htab_value) {.p = P} + +struct htab * htab_create(size_t); +void htab_destroy(struct htab *); +htab_value* htab_find(struct htab *, char* key); +int htab_insert(struct htab *, char*, htab_value); +int htab_delete(struct htab *htab, char* key); +size_t htab_next(struct htab *, size_t iterator, char** key, htab_value **v); + +#endif diff --git a/src/daemon/sblist.c b/src/daemon/sblist.c new file mode 100644 index 000000000..96bbebc --- /dev/null +++ b/src/daemon/sblist.c @@ -0,0 +1,73 @@ +#undef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#include "sblist.h" +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#define MY_PAGE_SIZE 4096 + +sblist* sblist_new(size_t itemsize, size_t blockitems) { + sblist* ret = (sblist*) malloc(sizeof(sblist)); + sblist_init(ret, itemsize, blockitems); + return ret; +} + +static void sblist_clear(sblist* l) { + l->items = NULL; + l->capa = 0; + l->count = 0; +} + +void sblist_init(sblist* l, size_t itemsize, size_t blockitems) { + if(l) { + l->blockitems = blockitems ? blockitems : MY_PAGE_SIZE / itemsize; + l->itemsize = itemsize; + sblist_clear(l); + } +} + +void sblist_free_items(sblist* l) { + if(l) { + if(l->items) free(l->items); + sblist_clear(l); + } +} + +void sblist_free(sblist* l) { + if(l) { + sblist_free_items(l); + free(l); + } +} + +char* sblist_item_from_index(sblist* l, size_t idx) { + return l->items + (idx * l->itemsize); +} + +void* sblist_get(sblist* l, size_t item) { + if(item < l->count) return (void*) sblist_item_from_index(l, item); + return NULL; +} + +int sblist_set(sblist* l, void* item, size_t pos) { + if(pos >= l->count) return 0; + memcpy(sblist_item_from_index(l, pos), item, l->itemsize); + return 1; +} + +int sblist_grow_if_needed(sblist* l) { + char* temp; + if(l->count == l->capa) { + temp = realloc(l->items, (l->capa + l->blockitems) * l->itemsize); + if(!temp) return 0; + l->capa += l->blockitems; + l->items = temp; + } + return 1; +} + +int sblist_add(sblist* l, void* item) { + if(!sblist_grow_if_needed(l)) return 0; + l->count++; + return sblist_set(l, item, l->count - 1); +} diff --git a/src/daemon/sblist.h b/src/daemon/sblist.h new file mode 100644 index 000000000..f0d1846 --- /dev/null +++ b/src/daemon/sblist.h @@ -0,0 +1,92 @@ +#ifndef SBLIST_H +#define SBLIST_H + +/* this file is part of libulz, as of commit 8ab361a27743aaf025323ee43b8b8876dc054fdd + modified for direct inclusion in microsocks. */ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stddef.h> +/* + * simple buffer list. + * + * this thing here is basically a generic dynamic array + * will realloc after every blockitems inserts + * can store items of any size. + * + * so think of it as a by-value list, as opposed to a typical by-ref list. + * you typically use it by having some struct on the stack, and pass a pointer + * to sblist_add, which will copy the contents into its internal memory. + * + */ + +typedef struct { + size_t itemsize; + size_t blockitems; + size_t count; + size_t capa; + char* items; +} sblist; + +#define sblist_getsize(X) ((X)->count) +#define sblist_get_count(X) ((X)->count) +#define sblist_empty(X) ((X)->count == 0) + +// for dynamic style +sblist* sblist_new(size_t itemsize, size_t blockitems); +void sblist_free(sblist* l); + +//for static style +void sblist_init(sblist* l, size_t itemsize, size_t blockitems); +void sblist_free_items(sblist* l); + +// accessors +void* sblist_get(sblist* l, size_t item); +// returns 1 on success, 0 on OOM +int sblist_add(sblist* l, void* item); +int sblist_set(sblist* l, void* item, size_t pos); +void sblist_delete(sblist* l, size_t item); +char* sblist_item_from_index(sblist* l, size_t idx); +int sblist_grow_if_needed(sblist* l); +int sblist_insert(sblist* l, void* item, size_t pos); +/* same as sblist_add, but returns list index of new item, or -1 */ +size_t sblist_addi(sblist* l, void* item); +void sblist_sort(sblist *l, int (*compar)(const void *, const void *)); +/* insert element into presorted list, returns listindex of new entry or -1*/ +size_t sblist_insert_sorted(sblist* l, void* o, int (*compar)(const void *, const void *)); + +#ifndef __COUNTER__ +#define __COUNTER__ __LINE__ +#endif + +#define __sblist_concat_impl( x, y ) x##y +#define __sblist_macro_concat( x, y ) __sblist_concat_impl( x, y ) +#define __sblist_iterator_name __sblist_macro_concat(sblist_iterator, __COUNTER__) + +// use with custom iterator variable +#define sblist_iter_counter(LIST, ITER, PTR) \ + for(size_t ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < sblist_getsize(LIST); ITER++) + +// use with custom iterator variable, which is predeclared +#define sblist_iter_counter2(LIST, ITER, PTR) \ + for(ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < sblist_getsize(LIST); ITER++) + +// use with custom iterator variable, which is predeclared and signed +// useful for a loop which can delete items from the list, and then decrease the iterator var. +#define sblist_iter_counter2s(LIST, ITER, PTR) \ + for(ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < (ssize_t) sblist_getsize(LIST); ITER++) + + +// uses "magic" iterator variable +#define sblist_iter(LIST, PTR) sblist_iter_counter(LIST, __sblist_iterator_name, PTR) + +#ifdef __cplusplus +} +#endif + +#pragma RcB2 DEP "sblist.c" "sblist_delete.c" + +#endif diff --git a/src/daemon/sblist_delete.c b/src/daemon/sblist_delete.c new file mode 100644 index 000000000..a18209a --- /dev/null +++ b/src/daemon/sblist_delete.c @@ -0,0 +1,9 @@ +#include "sblist.h" +#include <string.h> + +void sblist_delete(sblist* l, size_t item) { + if (l->count && item < l->count) { + memmove(sblist_item_from_index(l, item), sblist_item_from_index(l, item + 1), (sblist_getsize(l) - (item + 1)) * l->itemsize); + l->count--; + } +} diff --git a/src/daemon/udpclient.c b/src/daemon/udpclient.c new file mode 100644 index 000000000..1cb5825 --- /dev/null +++ b/src/daemon/udpclient.c @@ -0,0 +1,41 @@ +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include "../remotedns.h" +#include "../ip_type.h" + +int main() { + int fd; + int port = 1053; + char srvn[] = "127.0.0.1"; + struct sockaddr_in srva = {.sin_family = AF_INET, .sin_port = htons(port)}; + inet_pton(AF_INET, srvn, &srva.sin_addr); + fd = socket(AF_INET, SOCK_DGRAM, 0); + char namebuf[260]; + while(fgets(namebuf, sizeof namebuf, stdin)) { + size_t l = strlen(namebuf); + if(namebuf[l-1] == '\n') { + l--; + namebuf[l] = 0; + } + struct at_msg msg = {0}; + unsigned msglen; + if(isdigit(namebuf[0])) { + msglen = 4; + msg.h.msgtype = ATM_GETNAME; + inet_aton(namebuf, (void*) &msg.m.ip); + } else { + msglen = l+1; + msg.h.msgtype = ATM_GETIP; + memcpy(msg.m.host, namebuf, msglen); + } + msg.h.datalen = htons(msglen); + sendto(fd, &msg, sizeof(msg.h)+msglen, 0, (void*)&srva, sizeof(srva)); + char rcvbuf[512]; + recvfrom(fd, rcvbuf, sizeof rcvbuf, 0, (void*)0, (void*)0); + } +} diff --git a/src/daemon/udpserver.c b/src/daemon/udpserver.c new file mode 100644 index 000000000..62b85d0 --- /dev/null +++ b/src/daemon/udpserver.c @@ -0,0 +1,65 @@ +#include "udpserver.h" +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +int resolve(const char *host, unsigned short port, struct addrinfo** addr) { + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_PASSIVE, + }; + char port_buf[8]; + snprintf(port_buf, sizeof port_buf, "%u", port); + return getaddrinfo(host, port_buf, &hints, addr); +} + +int resolve_sa(const char *host, unsigned short port, union sockaddr_union *res) { + struct addrinfo *ainfo = 0; + int ret; + SOCKADDR_UNION_AF(res) = AF_UNSPEC; + if((ret = resolve(host, port, &ainfo))) return ret; + memcpy(res, ainfo->ai_addr, ainfo->ai_addrlen); + freeaddrinfo(ainfo); + return 0; +} + +int bindtoip(int fd, union sockaddr_union *bindaddr) { + socklen_t sz = SOCKADDR_UNION_LENGTH(bindaddr); + if(sz) + return bind(fd, (struct sockaddr*) bindaddr, sz); + return 0; +} + +int server_waitclient(struct server *server, struct client* client, void* buf, size_t *buflen) { + socklen_t clen = sizeof client->addr; + ssize_t ret = recvfrom(server->fd, buf, *buflen, 0, (void*)&client->addr, &clen); + if(ret >= 0) { + *buflen = ret; + return 0; + } + return ret; +} + +int server_setup(struct server *server, const char* listenip, unsigned short port) { + struct addrinfo *ainfo = 0; + if(resolve(listenip, port, &ainfo)) return -1; + struct addrinfo* p; + int listenfd = -1; + for(p = ainfo; p; p = p->ai_next) { + if((listenfd = socket(p->ai_family, SOCK_DGRAM, 0)) < 0) + continue; + int yes = 1; + setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); + if(bind(listenfd, p->ai_addr, p->ai_addrlen) < 0) { + close(listenfd); + listenfd = -1; + continue; + } + break; + } + freeaddrinfo(ainfo); + if(listenfd < 0) return -2; + server->fd = listenfd; + return 0; +} diff --git a/src/daemon/udpserver.h b/src/daemon/udpserver.h new file mode 100644 index 000000000..88e16d2 --- /dev/null +++ b/src/daemon/udpserver.h @@ -0,0 +1,48 @@ +#ifndef SERVER_H +#define SERVER_H + +#undef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L + +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> + +#pragma RcB2 DEP "udpserver.c" + +union sockaddr_union { + struct sockaddr_in v4; + struct sockaddr_in6 v6; +}; + +#define SOCKADDR_UNION_AF(PTR) (PTR)->v4.sin_family + +#define SOCKADDR_UNION_LENGTH(PTR) ( \ + ( SOCKADDR_UNION_AF(PTR) == AF_INET ) ? sizeof((PTR)->v4) : ( \ + ( SOCKADDR_UNION_AF(PTR) == AF_INET6 ) ? sizeof((PTR)->v6) : 0 ) ) + +#define SOCKADDR_UNION_ADDRESS(PTR) ( \ + ( SOCKADDR_UNION_AF(PTR) == AF_INET ) ? (void*) &(PTR)->v4.sin_addr : ( \ + ( SOCKADDR_UNION_AF(PTR) == AF_INET6 ) ? (void*) &(PTR)->v6.sin6_addr : (void*) 0 ) ) + +#define SOCKADDR_UNION_PORT(PTR) ( \ + ( SOCKADDR_UNION_AF(PTR) == AF_INET ) ? (PTR)->v4.sin_port : ( \ + ( SOCKADDR_UNION_AF(PTR) == AF_INET6 ) ? (PTR)->v6.sin6_port : 0 ) ) + +struct client { + union sockaddr_union addr; +}; + +struct server { + int fd; +}; + +int resolve(const char *host, unsigned short port, struct addrinfo** addr); +int resolve_sa(const char *host, unsigned short port, union sockaddr_union *res); +int bindtoip(int fd, union sockaddr_union *bindaddr); + +int server_waitclient(struct server *server, struct client* client, void* buf, size_t *buflen); +int server_setup(struct server *server, const char* listenip, unsigned short port); + +#endif + diff --git a/src/libproxychains.c b/src/libproxychains.c index ca10cda..fc6880f 100644 --- a/src/libproxychains.c +++ b/src/libproxychains.c @@ -38,6 +38,7 @@ #include "core.h" #include "common.h" +#include "rdns.h" #undef satosin #define satosin(x) ((struct sockaddr_in *) &(x)) @@ -71,7 +72,7 @@ unsigned int proxychains_proxy_offset = 0; int proxychains_got_chain_data = 0; unsigned int proxychains_max_chain = 1; int proxychains_quiet_mode = 0; -int proxychains_resolver = 0; +enum dns_lookup_flavor proxychains_resolver = DNSLF_LIBC; localaddr_arg localnet_addr[MAX_LOCALNET]; size_t num_localnet_addr = 0; dnat_arg dnats[MAX_DNAT]; @@ -126,12 +127,6 @@ static void setup_hooks(void) { static int close_fds[16]; static int close_fds_cnt = 0; -static void rdns_init(void) { - static int init_done = 0; - if(!init_done) at_init(); - init_done = 1; -} - static void do_init(void) { srand(time(NULL)); core_initialize(); @@ -147,7 +142,7 @@ static void do_init(void) { while(close_fds_cnt) true_close(close_fds[--close_fds_cnt]); init_l = 1; - if(proxychains_resolver == 1) rdns_init(); + rdns_init(proxychains_resolver); } static void init_lib_wrapper(const char* caller) { @@ -281,6 +276,7 @@ static void get_chain_data(proxy_data * pd, unsigned int *proxy_count, chain_typ char local_in_addr[32], local_in_port[32], local_netmask[32]; char dnat_orig_addr_port[32], dnat_new_addr_port[32]; char dnat_orig_addr[32], dnat_orig_port[32], dnat_new_addr[32], dnat_new_port[32]; + char rdnsd_addr[32], rdnsd_port[8]; FILE *file = NULL; if(proxychains_got_chain_data) @@ -339,9 +335,9 @@ static void get_chain_data(proxy_data * pd, unsigned int *proxy_count, chain_typ pd[count].port = htons((unsigned short) port_n); ip_type* host_ip = &pd[count].ip; if(1 != inet_pton(host_ip->is_v6 ? AF_INET6 : AF_INET, host, host_ip->addr.v6)) { - if(*ct == STRICT_TYPE && proxychains_resolver == 1 && count > 0) { + if(*ct == STRICT_TYPE && proxychains_resolver >= DNSLF_RDNS_START && count > 0) { /* we can allow dns hostnames for all but the first proxy in the list if chaintype is strict, as remote lookup can be done */ - rdns_init(); + rdns_init(proxychains_resolver); ip_type4 internal_ip = at_get_ip_for_host(host, strlen(host)); pd[count].ip.is_v6 = 0; host_ip->addr.v4 = internal_ip; @@ -351,7 +347,7 @@ static void get_chain_data(proxy_data * pd, unsigned int *proxy_count, chain_typ inv_host: fprintf(stderr, "proxy %s has invalid value or is not numeric\n", host); fprintf(stderr, "non-numeric ips are only allowed under the following circumstances:\n"); - 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), bool_str(proxychains_resolver)); + 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)); exit(1); } } @@ -443,9 +439,25 @@ inv_host: } else if(!strcmp(buff, "quiet_mode")) { proxychains_quiet_mode = 1; } else if(!strcmp(buff, "proxy_dns_old")) { - proxychains_resolver = 2; + proxychains_resolver = DNSLF_FORKEXEC; } else if(!strcmp(buff, "proxy_dns")) { - proxychains_resolver = 1; + proxychains_resolver = DNSLF_RDNS_THREAD; + } else if(STR_STARTSWITH(buff, "proxy_dns_daemon")) { + struct sockaddr_in rdns_server_buffer; + + if(sscanf(buff, "%s %15[^:]:%5s", user, rdnsd_addr, rdnsd_port) < 3) { + fprintf(stderr, "proxy_dns_daemon format error\n"); + exit(1); + } + rdns_server_buffer.sin_family = AF_INET; + int error = inet_pton(AF_INET, rdnsd_addr, &rdns_server_buffer.sin_addr); + if(error <= 0) { + fprintf(stderr, "bogus proxy_dns_daemon address\n"); + exit(1); + } + rdns_server_buffer.sin_port = htons(atoi(rdnsd_port)); + proxychains_resolver = DNSLF_RDNS_DAEMON; + rdns_set_daemon(&rdns_server_buffer); } else if(STR_STARTSWITH(buff, "dnat")) { if(sscanf(buff, "%s %21[^ ] %21s\n", user, dnat_orig_addr_port, dnat_new_addr_port) < 3) { fprintf(stderr, "dnat format error"); @@ -508,7 +520,7 @@ inv_host: } *proxy_count = count; proxychains_got_chain_data = 1; - PDEBUG("proxy_dns: %s\n", proxychains_resolver ? (proxychains_resolver == 2 ? "OLD" : "ON") : "OFF"); + PDEBUG("proxy_dns: %s\n", rdns_resolver_string(proxychains_resolver)); } /******* HOOK FUNCTIONS *******/ @@ -520,7 +532,7 @@ int close(int fd) { errno = 0; return 0; } - if(proxychains_resolver != 1) return true_close(fd); + if(proxychains_resolver != DNSLF_RDNS_THREAD) return true_close(fd); /* prevent rude programs (like ssh) from closing our pipes */ if(fd != req_pipefd[0] && fd != req_pipefd[1] && @@ -635,12 +647,12 @@ struct hostent *gethostbyname(const char *name) { INIT(); PDEBUG("gethostbyname: %s\n", name); - if(proxychains_resolver == 1) - return proxy_gethostbyname(name, &ghbndata); - else if(proxychains_resolver == 2) + if(proxychains_resolver == DNSLF_FORKEXEC) return proxy_gethostbyname_old(name); - else + else if(proxychains_resolver == DNSLF_LIBC) return true_gethostbyname(name); + else + return proxy_gethostbyname(name, &ghbndata); return NULL; } @@ -649,7 +661,7 @@ int getaddrinfo(const char *node, const char *service, const struct addrinfo *hi INIT(); PDEBUG("getaddrinfo: %s %s\n", node ? node : "null", service ? service : "null"); - if(proxychains_resolver) + if(proxychains_resolver != DNSLF_LIBC) return proxy_getaddrinfo(node, service, hints, res); else return true_getaddrinfo(node, service, hints, res); @@ -659,7 +671,7 @@ void freeaddrinfo(struct addrinfo *res) { INIT(); PDEBUG("freeaddrinfo %p \n", (void *) res); - if(!proxychains_resolver) + if(proxychains_resolver == DNSLF_LIBC) true_freeaddrinfo(res); else proxy_freeaddrinfo(res); @@ -672,7 +684,7 @@ int pc_getnameinfo(const struct sockaddr *sa, socklen_t salen, INIT(); PFUNC(); - if(!proxychains_resolver) { + if(proxychains_resolver == DNSLF_LIBC) { return true_getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); } else { if(!salen || !(SOCKFAMILY(*sa) == AF_INET || SOCKFAMILY(*sa) == AF_INET6)) @@ -719,7 +731,7 @@ struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type) { static char *aliases[1]; static struct hostent he; - if(!proxychains_resolver) + if(proxychains_resolver == DNSLF_LIBC) return true_gethostbyaddr(addr, len, type); else { diff --git a/src/proxychains.conf b/src/proxychains.conf index fb08ae1..ecb9164 100644 --- a/src/proxychains.conf +++ b/src/proxychains.conf @@ -48,17 +48,33 @@ strict_chain # Quiet mode (no output from library) #quiet_mode -# Proxy DNS requests - no leak for DNS data -# this uses the proxychains4 style method to do remote dns +## Proxy DNS requests - no leak for DNS data +# (disable all of the 3 items below to not proxy your DNS requests) + +# method 1. this uses the proxychains4 style method to do remote dns: +# a thread is spawned that serves DNS requests and hands down an ip +# assigned from an internal list (via remote_dns_subset). +# this is the easiest (setup-wise) and fastest method, however on +# systems with buggy libcs and very complex software like webbrosers +# this might not work and/or cause crashes. proxy_dns -# use the old proxyresolv script to proxy DNS requests -# in proxychains 3.1 style. requires proxyresolv in $PATH +# method 2. use the old proxyresolv script to proxy DNS requests +# in proxychains 3.1 style. requires `proxyresolv` in $PATH # plus a dynamically linked `dig` binary. # this is a lot slower than `proxy_dns`, doesn't support .onion URLs, # but might be more compatible with complex software like webbrowsers. #proxy_dns_old +# method 3. use proxychains4-daemon process to serve remote DNS requests. +# this is similar to the threaded `proxy_dns` method, however it requires +# that proxychains4-daemon is already running on the specified address. +# on the plus side it doesn't do malloc/threads so it should be quite +# compatible with complex, async-unsafe software. +# note that if you don't start proxychains4-daemon before using this, +# the process will simply hang. +#proxy_dns_daemon 127.0.0.1:1053 + # set the class A subnet number to use for the internal remote DNS mapping # we use the reserved 224.x.x.x range by default, # if the proxified app does a DNS request, we will return an IP from that range. diff --git a/src/rdns.c b/src/rdns.c new file mode 100644 index 000000000..d9496b1 --- /dev/null +++ b/src/rdns.c @@ -0,0 +1,99 @@ +#include <sys/socket.h> +#include <stdlib.h> +#include <string.h> + +#include "rdns.h" +#include "allocator_thread.h" +#include "remotedns.h" + +#ifndef HAVE_SOCK_CLOEXEC +#define SOCK_CLOEXEC 0 +#endif + +//static enum dns_lookup_flavor dns_flavor; +#define dns_flavor rdns_get_flavor() + +static struct sockaddr_in rdns_server; + +size_t rdns_daemon_get_host_for_ip(ip_type4 ip, char* readbuf) { + struct at_msg msg = { + .h.msgtype = ATM_GETNAME, + .h.datalen = htons(4), + .m.ip = ip, + }; + int fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0); + sendto(fd, &msg, sizeof(msg.h)+4, 0, (void*)&rdns_server, sizeof(rdns_server)); + recvfrom(fd, &msg, sizeof msg, 0, (void*)0, (void*)0); + close(fd); + msg.h.datalen = ntohs(msg.h.datalen); + if(!msg.h.datalen || msg.h.datalen > 256) return 0; + memcpy(readbuf, msg.m.host, msg.h.datalen); + return msg.h.datalen - 1; +} + +static ip_type4 rdns_daemon_get_ip_for_host(char* host, size_t len) { + struct at_msg msg = { + .h.msgtype = ATM_GETIP, + }; + if(len >= 256) return IPT4_INT(-1); + memcpy(msg.m.host, host, len+1); + msg.h.datalen = htons(len+1); + int fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0); + sendto(fd, &msg, sizeof(msg.h)+len+1, 0, (void*)&rdns_server, sizeof(rdns_server)); + recvfrom(fd, &msg, sizeof msg, 0, (void*)0, (void*)0); + close(fd); + if(ntohs(msg.h.datalen) != 4) return IPT4_INT(-1); + return msg.m.ip; +} + +const char *rdns_resolver_string(enum dns_lookup_flavor flavor) { + static const char tab[][7] = { + [DNSLF_LIBC] = "off", + [DNSLF_FORKEXEC] = "old", + [DNSLF_RDNS_THREAD] = "thread", + [DNSLF_RDNS_DAEMON] = "daemon", + }; + return tab[flavor]; +} + +void rdns_init(enum dns_lookup_flavor flavor) { + static int init_done = 0; + if(!init_done) switch(flavor) { + case DNSLF_RDNS_THREAD: + at_init(); + break; + case DNSLF_RDNS_DAEMON: + default: + break; + } + init_done = 1; +} + +void rdns_set_daemon(struct sockaddr_in* addr) { + rdns_server = *addr; +} + +#if 0 +enum dns_lookup_flavor rdns_get_flavor(void) { + return dns_flavor; +} +#endif + +size_t rdns_get_host_for_ip(ip_type4 ip, char* readbuf) { + switch(dns_flavor) { + case DNSLF_RDNS_THREAD: return at_get_host_for_ip(ip, readbuf); + case DNSLF_RDNS_DAEMON: return rdns_daemon_get_host_for_ip(ip, readbuf); + default: + abort(); + } +} + +ip_type4 rdns_get_ip_for_host(char* host, size_t len) { + switch(dns_flavor) { + case DNSLF_RDNS_THREAD: return at_get_ip_for_host(host, len); + case DNSLF_RDNS_DAEMON: return rdns_daemon_get_ip_for_host(host, len); + default: + abort(); + } +} + diff --git a/src/rdns.h b/src/rdns.h new file mode 100644 index 000000000..8f63ab8 --- /dev/null +++ b/src/rdns.h @@ -0,0 +1,28 @@ +#ifndef RDNS_H +#define RDNS_H + +#include <unistd.h> +#include <netinet/in.h> +#include "ip_type.h" +#include "remotedns.h" + +enum dns_lookup_flavor { + DNSLF_LIBC = 0, + DNSLF_FORKEXEC, + + DNSLF_RDNS_START, + DNSLF_RDNS_THREAD = DNSLF_RDNS_START, + DNSLF_RDNS_DAEMON, +}; + +void rdns_init(enum dns_lookup_flavor flavor); +void rdns_set_daemon(struct sockaddr_in* addr); +const char *rdns_resolver_string(enum dns_lookup_flavor flavor); +size_t rdns_get_host_for_ip(ip_type4 ip, char* readbuf); +ip_type4 rdns_get_ip_for_host(char* host, size_t len); + +//enum dns_lookup_flavor rdns_get_flavor(void); +#define rdns_get_flavor() proxychains_resolver +extern enum dns_lookup_flavor proxychains_resolver; + +#endif diff --git a/src/remotedns.h b/src/remotedns.h index f4be974..588e09a 100644 --- a/src/remotedns.h +++ b/src/remotedns.h @@ -4,6 +4,8 @@ #include <unistd.h> #include "ip_type.h" +#define MSG_LEN_MAX 256 + enum at_msgtype { ATM_GETIP = 0, ATM_GETNAME, |