diff --git a/net/redsocks/Makefile b/net/redsocks/Makefile new file mode 100644 index 000000000..91f047736 --- /dev/null +++ b/net/redsocks/Makefile @@ -0,0 +1,56 @@ +# +# Copyright (C) 2014 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=redsocks +PKG_VERSION:=0.4 +PKG_RELEASE:=1 + +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-release-$(PKG_VERSION) +PKG_SOURCE:=release-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://github.com/darkk/redsocks/archive/ +PKG_MD5SUM:=810102ef06a9ea796f310ae811afc6a8 +PKG_MAINTAINER:=Johannes Morgenroth +PKG_LICENSE:=Apache-2.0 + +include $(INCLUDE_DIR)/package.mk + +define Package/redsocks + SECTION:=net + CATEGORY:=Network + DEPENDS:=+libevent2 + TITLE:=Redirect any TCP connection to a SOCKS or HTTPS proxy server +endef + +define Package/redsocks/conffiles +/etc/redsocks.conf +endef + +define Package/redsocks/description + Redsocks is a daemon running on the local system, that will transparently + tunnel any TCP connection via a remote SOCKS4, SOCKS5 or HTTP proxy server. It + uses the system firewall's redirection facility to intercept TCP connections, + thus the redirection is system-wide, with fine-grained control, and does + not depend on LD_PRELOAD libraries. + + Redsocks supports tunneling TCP connections and UDP packets. It has + authentication support for both, SOCKS and HTTP proxies. + + Also included is a small DNS server returning answers with the "truncated" flag + set for any UDP query, forcing the resolver to use TCP. +endef + +define Package/redsocks/install + $(INSTALL_DIR) $(1)/usr/sbin/ + $(INSTALL_BIN) $(PKG_BUILD_DIR)/redsocks $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/init.d/ + $(INSTALL_BIN) files/redsocks.init $(1)/etc/init.d/redsocks + $(INSTALL_CONF) files/redsocks.conf $(1)/etc/ +endef + +$(eval $(call BuildPackage,redsocks)) diff --git a/net/redsocks/files/redsocks.conf b/net/redsocks/files/redsocks.conf new file mode 100644 index 000000000..64307c715 --- /dev/null +++ b/net/redsocks/files/redsocks.conf @@ -0,0 +1,108 @@ +base { + // debug: connection progress & client list on SIGUSR1 + log_debug = off; + + // info: start and end of client session + log_info = on; + + /* possible `log' values are: + * stderr + * "file:/path/to/file" + * syslog:FACILITY facility is any of "daemon", "local0"..."local7" + */ + // log = stderr; + // log = "file:/path/to/file"; + log = "syslog:local7"; + + // detach from console + daemon = on; + + /* Change uid, gid and root directory, these options require root + * privilegies on startup. + * Note, your chroot may requre /etc/localtime if you write log to syslog. + * Log is opened before chroot & uid changing. + */ + // user = nobody; + // group = nobody; + // chroot = "/var/chroot"; + + /* possible `redirector' values are: + * iptables - for Linux + * ipf - for FreeBSD + * pf - for OpenBSD + * generic - some generic redirector that MAY work + */ + redirector = iptables; +} + +redsocks { + /* `local_ip' defaults to 127.0.0.1 for security reasons, + * use 0.0.0.0 if you want to listen on every interface. + * `local_*' are used as port to redirect to. + */ + local_ip = 127.0.0.1; + local_port = 12345; + + // listen() queue length. Default value is SOMAXCONN and it should be + // good enough for most of us. + // listenq = 128; // SOMAXCONN equals 128 on my Linux box. + + // `max_accept_backoff` is a delay to retry `accept()` after accept + // failure (e.g. due to lack of file descriptors). It's measured in + // milliseconds and maximal value is 65535. `min_accept_backoff` is + // used as initial backoff value and as a damper for `accept() after + // close()` logic. + // min_accept_backoff = 100; + // max_accept_backoff = 60000; + + // `ip' and `port' are IP and tcp-port of proxy-server + // You can also use hostname instead of IP, only one (random) + // address of multihomed host will be used. + ip = example.org; + port = 1080; + + + // known types: socks4, socks5, http-connect, http-relay + type = socks5; + + // login = "foobar"; + // password = "baz"; +} + +redudp { + // `local_ip' should not be 0.0.0.0 as it's also used for outgoing + // packets that are sent as replies - and it should be fixed + // if we want NAT to work properly. + local_ip = 127.0.0.1; + local_port = 10053; + + // `ip' and `port' of socks5 proxy server. + ip = 10.0.0.1; + port = 1080; + login = username; + password = pazzw0rd; + + // redsocks knows about two options while redirecting UDP packets at + // linux: TPROXY and REDIRECT. TPROXY requires more complex routing + // configuration and fresh kernel (>= 2.6.37 according to squid + // developers[1]) but has hack-free way to get original destination + // address, REDIRECT is easier to configure, but requires `dest_ip` and + // `dest_port` to be set, limiting packet redirection to single + // destination. + // [1] http://wiki.squid-cache.org/Features/Tproxy4 + dest_ip = 8.8.8.8; + dest_port = 53; + + udp_timeout = 30; + udp_timeout_stream = 180; +} + +dnstc { + // fake and really dumb DNS server that returns "truncated answer" to + // every query via UDP, RFC-compliant resolver should repeat same query + // via TCP in this case. + local_ip = 127.0.0.1; + local_port = 5300; +} + +// you can add more `redsocks' and `redudp' sections if you need. diff --git a/net/redsocks/files/redsocks.init b/net/redsocks/files/redsocks.init new file mode 100644 index 000000000..56c9de327 --- /dev/null +++ b/net/redsocks/files/redsocks.init @@ -0,0 +1,36 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2007 OpenWrt.org + +START=90 + +# check if configuration exists +[ -e "/etc/redsocks.conf" ] || exit 0 + +start() { + if [ -e "/var/run/redsocks.pid" ]; then + echo "redsocks is already running" + exit 0 + fi + + /bin/echo -n "running redsocks ..." + + # startup the safety-wrapper for the daemon + /usr/sbin/redsocks -p /var/run/redsocks.pid + + /bin/echo " done" +} + +stop() { + if [ ! -e "/var/run/redsocks.pid" ]; then + echo "redsocks is not running" + exit 0 + fi + + /bin/echo -n "stopping redsocks ..." + + # kill the process + /bin/kill $(cat /var/run/redsocks.pid) + rm /var/run/redsocks.pid + + echo " done" +} diff --git a/net/redsocks/patches/0001-Fix-bug-in-DNS-resolution-results-were-ignored-since.patch b/net/redsocks/patches/0001-Fix-bug-in-DNS-resolution-results-were-ignored-since.patch new file mode 100644 index 000000000..595be191d --- /dev/null +++ b/net/redsocks/patches/0001-Fix-bug-in-DNS-resolution-results-were-ignored-since.patch @@ -0,0 +1,53 @@ +From 290f19972e9f7b74f818ae211cb535e32f1f314f Mon Sep 17 00:00:00 2001 +From: Leonid Evdokimov +Date: Tue, 10 Apr 2012 00:57:26 +0400 +Subject: [PATCH 01/12] Fix bug in DNS resolution - results were ignored (since + 8179a1ff). + +--- + parser.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/parser.c b/parser.c +index 85d3533..6198828 100644 +--- a/parser.c ++++ b/parser.c +@@ -295,22 +295,22 @@ static int vp_in_addr(parser_context *context, void *addr, const char *token) + memcpy(addr, &ia, sizeof(ia)); + } + else { +- struct addrinfo *addr, hints; ++ struct addrinfo *ainfo, hints; + int err; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; /* IPv4-only */ + hints.ai_socktype = SOCK_STREAM; /* I want to have one address once and ONLY once, that's why I specify socktype and protocol */ + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_ADDRCONFIG; /* I don't need IPv4 addrs without IPv4 connectivity */ +- err = getaddrinfo(token, NULL, &hints, &addr); ++ err = getaddrinfo(token, NULL, &hints, &ainfo); + if (err == 0) { + int count, taken; + struct addrinfo *iter; + struct sockaddr_in *resolved_addr; +- for (iter = addr, count = 0; iter; iter = iter->ai_next, ++count) ++ for (iter = ainfo, count = 0; iter; iter = iter->ai_next, ++count) + ; + taken = rand() % count; +- for (iter = addr; taken > 0; iter = iter->ai_next, --taken) ++ for (iter = ainfo; taken > 0; iter = iter->ai_next, --taken) + ; + resolved_addr = (struct sockaddr_in*)iter->ai_addr; + assert(resolved_addr->sin_family == iter->ai_family && iter->ai_family == AF_INET); +@@ -318,7 +318,7 @@ static int vp_in_addr(parser_context *context, void *addr, const char *token) + log_error(LOG_WARNING, "%s resolves to %d addresses, using %s", + token, count, inet_ntoa(resolved_addr->sin_addr)); + memcpy(addr, &resolved_addr->sin_addr, sizeof(ia)); +- freeaddrinfo(addr); ++ freeaddrinfo(ainfo); + } + else { + if (err == EAI_SYSTEM) +-- +1.9.1 + diff --git a/net/redsocks/patches/0002-inet_ntop-red_inet_ntop.patch b/net/redsocks/patches/0002-inet_ntop-red_inet_ntop.patch new file mode 100644 index 000000000..058556e79 --- /dev/null +++ b/net/redsocks/patches/0002-inet_ntop-red_inet_ntop.patch @@ -0,0 +1,161 @@ +From 6015b3a6f26e04dd5d78cd6c1320886fc9035612 Mon Sep 17 00:00:00 2001 +From: Leonid Evdokimov +Date: Tue, 10 Apr 2012 01:37:34 +0400 +Subject: [PATCH 02/12] inet_ntop -> red_inet_ntop + +--- + redsocks.c | 13 ++++--------- + redudp.c | 19 +++++++++++-------- + utils.c | 37 +++++++++++++++++++++++++++++++++---- + utils.h | 7 +++++++ + 4 files changed, 55 insertions(+), 21 deletions(-) + +diff --git a/redsocks.c b/redsocks.c +index d085e10..ba5eab2 100644 +--- a/redsocks.c ++++ b/redsocks.c +@@ -207,22 +207,17 @@ void redsocks_log_write_plain( + int saved_errno = errno; + struct evbuffer *fmt = evbuffer_new(); + va_list ap; +- char clientaddr_str[INET6_ADDRSTRLEN], destaddr_str[INET6_ADDRSTRLEN]; ++ char clientaddr_str[RED_INET_ADDRSTRLEN], destaddr_str[RED_INET_ADDRSTRLEN]; + + if (!fmt) { + log_errno(LOG_ERR, "evbuffer_new()"); + // no return, as I have to call va_start/va_end + } + +- if (!inet_ntop(clientaddr->sin_family, &clientaddr->sin_addr, clientaddr_str, sizeof(clientaddr_str))) +- strncpy(clientaddr_str, "???", sizeof(clientaddr_str)); +- if (!inet_ntop(destaddr->sin_family, &destaddr->sin_addr, destaddr_str, sizeof(destaddr_str))) +- strncpy(destaddr_str, "???", sizeof(destaddr_str)); +- + if (fmt) { +- evbuffer_add_printf(fmt, "[%s:%i->%s:%i]: %s", +- clientaddr_str, ntohs(clientaddr->sin_port), +- destaddr_str, ntohs(destaddr->sin_port), ++ evbuffer_add_printf(fmt, "[%s->%s]: %s", ++ red_inet_ntop(clientaddr, clientaddr_str, sizeof(clientaddr_str)), ++ red_inet_ntop(destaddr, destaddr_str, sizeof(destaddr_str)), + orig_fmt); + } + +diff --git a/redudp.c b/redudp.c +index 0a97852..9516a50 100644 +--- a/redudp.c ++++ b/redudp.c +@@ -436,10 +436,9 @@ static void redudp_pkt_from_socks(int fd, short what, void *_arg) + return; + + if (memcmp(&udprelayaddr, &client->udprelayaddr, sizeof(udprelayaddr)) != 0) { +- char buf[INET6_ADDRSTRLEN]; +- const char *addr = inet_ntop(udprelayaddr.sin_family, &udprelayaddr.sin_addr, buf, sizeof(buf)); +- redudp_log_error(client, LOG_NOTICE, "Got packet from unexpected address %s:%u.", +- addr ? addr : "?", ntohs(udprelayaddr.sin_port)); ++ char buf[RED_INET_ADDRSTRLEN]; ++ redudp_log_error(client, LOG_NOTICE, "Got packet from unexpected address %s.", ++ red_inet_ntop(&udprelayaddr, buf, sizeof(buf))); + return; + } + +@@ -459,10 +458,14 @@ static void redudp_pkt_from_socks(int fd, short what, void *_arg) + if (pkt.header.ip.port != client->instance->config.destaddr.sin_port || + pkt.header.ip.addr != client->instance->config.destaddr.sin_addr.s_addr) + { +- char buf[INET6_ADDRSTRLEN]; +- const char *addr = inet_ntop(AF_INET, &pkt.header.ip.addr, buf, sizeof(buf)); +- redudp_log_error(client, LOG_NOTICE, "Socks5 server relayed packet from unexpected address %s:%u.", +- addr ? addr : "?", ntohs(pkt.header.ip.port)); ++ char buf[RED_INET_ADDRSTRLEN]; ++ struct sockaddr_in pktaddr = { ++ .sin_family = AF_INET, ++ .sin_addr = { pkt.header.ip.addr }, ++ .sin_port = pkt.header.ip.port, ++ }; ++ redudp_log_error(client, LOG_NOTICE, "Socks5 server relayed packet from unexpected address %s.", ++ red_inet_ntop(&pktaddr, buf, sizeof(buf))); + return; + } + +diff --git a/utils.c b/utils.c +index c6ced51..6e1f3af 100644 +--- a/utils.c ++++ b/utils.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -42,10 +43,9 @@ int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inadd + } + + if (pktlen >= buflen) { +- char buf[INET6_ADDRSTRLEN]; +- const char *addr = inet_ntop(inaddr->sin_family, &inaddr->sin_addr, buf, sizeof(buf)); +- log_error(LOG_WARNING, "wow! Truncated udp packet of size %zd from %s:%u! impossible! dropping it...", +- pktlen, addr ? addr : "?", ntohs(inaddr->sin_port)); ++ char buf[RED_INET_ADDRSTRLEN]; ++ log_error(LOG_WARNING, "wow! Truncated udp packet of size %zd from %s! impossible! dropping it...", ++ pktlen, red_inet_ntop(inaddr, buf, sizeof(buf))); + return -1; + } + +@@ -176,4 +176,33 @@ int red_is_socket_connected_ok(struct bufferevent *buffev) + } + } + ++char *red_inet_ntop(const struct sockaddr_in* sa, char* buffer, size_t buffer_size) ++{ ++ const char *retval = 0; ++ size_t len = 0; ++ uint16_t port; ++ const char placeholder[] = "???:???"; ++ ++ assert(buffer_size >= sizeof(placeholder)); ++ ++ memset(buffer, buffer_size, 0); ++ if (sa->sin_family == AF_INET) { ++ retval = inet_ntop(AF_INET, &sa->sin_addr, buffer, buffer_size); ++ port = ((struct sockaddr_in*)sa)->sin_port; ++ } ++ else if (sa->sin_family == AF_INET6) { ++ retval = inet_ntop(AF_INET6, &((const struct sockaddr_in6*)sa)->sin6_addr, buffer, buffer_size); ++ port = ((struct sockaddr_in6*)sa)->sin6_port; ++ } ++ if (retval) { ++ assert(retval == buffer); ++ len = strlen(retval); ++ snprintf(buffer + len, buffer_size - len, ":%d", ntohs(port)); ++ } ++ else { ++ strcpy(buffer, placeholder); ++ } ++ return buffer; ++} ++ + /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ +diff --git a/utils.h b/utils.h +index f691b77..d3af00f 100644 +--- a/utils.h ++++ b/utils.h +@@ -57,6 +57,13 @@ int fcntl_nonblock(int fd); + (what) & EVBUFFER_TIMEOUT ? "EVBUFFER_TIMEOUT" : "0", \ + (what) & ~(EVBUFFER_READ|EVBUFFER_WRITE|EVBUFFER_EOF|EVBUFFER_ERROR|EVBUFFER_TIMEOUT) + ++#if INET6_ADDRSTRLEN < INET_ADDRSTRLEN ++# error Impossible happens: INET6_ADDRSTRLEN < INET_ADDRSTRLEN ++#else ++# define RED_INET_ADDRSTRLEN (INET6_ADDRSTRLEN + 1 + 5 + 1) // addr + : + port + \0 ++#endif ++char *red_inet_ntop(const struct sockaddr_in* sa, char* buffer, size_t buffer_size); ++ + /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */ + /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */ + #endif /* UTILS_H_SAT_FEB__2_02_24_05_2008 */ +-- +1.9.1 + diff --git a/net/redsocks/patches/0003-Initial-support-for-UDP-TPROXY-redirection.-No-more-.patch b/net/redsocks/patches/0003-Initial-support-for-UDP-TPROXY-redirection.-No-more-.patch new file mode 100644 index 000000000..b89f4be90 --- /dev/null +++ b/net/redsocks/patches/0003-Initial-support-for-UDP-TPROXY-redirection.-No-more-.patch @@ -0,0 +1,449 @@ +From 709646d59d96cb73a7e70347f37de9823e4e5f14 Mon Sep 17 00:00:00 2001 +From: Leonid Evdokimov +Date: Fri, 13 Apr 2012 01:57:23 +0400 +Subject: [PATCH 03/12] Initial support for UDP + TPROXY redirection. No more + dest_ip in redudp. + + * TPROXY requires Linux 2.6.29+ (see man 7 ip[1]). + * all redsocks code is running as root to bind to arbitrary port. + * Non-Linux and old-Linux builds are broken at the moment. + +[1] http://www.kernel.org/doc/man-pages/online/pages/man7/ip.7.html +--- + dnstc.c | 2 +- + redudp.c | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- + redudp.h | 2 + + utils.c | 43 +++++++++++++- + utils.h | 2 +- + 5 files changed, 227 insertions(+), 19 deletions(-) + +diff --git a/dnstc.c b/dnstc.c +index 43881d8..5f9fedd 100644 +--- a/dnstc.c ++++ b/dnstc.c +@@ -68,7 +68,7 @@ static void dnstc_pkt_from_client(int fd, short what, void *_arg) + ssize_t pktlen, outgoing; + + assert(fd == EVENT_FD(&self->listener)); +- pktlen = red_recv_udp_pkt(fd, buf.raw, sizeof(buf), &clientaddr); ++ pktlen = red_recv_udp_pkt(fd, buf.raw, sizeof(buf), &clientaddr, NULL); + if (pktlen == -1) + return; + +diff --git a/redudp.c b/redudp.c +index 9516a50..262af3e 100644 +--- a/redudp.c ++++ b/redudp.c +@@ -15,6 +15,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -33,30 +34,157 @@ + #include "redudp.h" + + #define redudp_log_error(client, prio, msg...) \ +- redsocks_log_write_plain(__FILE__, __LINE__, __func__, 0, &(client)->clientaddr, &(client)->instance->config.destaddr, prio, ## msg) ++ redsocks_log_write_plain(__FILE__, __LINE__, __func__, 0, &(client)->clientaddr, get_destaddr(client), prio, ## msg) + #define redudp_log_errno(client, prio, msg...) \ +- redsocks_log_write_plain(__FILE__, __LINE__, __func__, 1, &(client)->clientaddr, &(client)->instance->config.destaddr, prio, ## msg) ++ redsocks_log_write_plain(__FILE__, __LINE__, __func__, 1, &(client)->clientaddr, get_destaddr(client), prio, ## msg) + + static void redudp_pkt_from_socks(int fd, short what, void *_arg); + static void redudp_drop_client(redudp_client *client); + static void redudp_fini_instance(redudp_instance *instance); + static int redudp_fini(); ++static int redudp_transparent(int fd); + + typedef struct redudp_expected_assoc_reply_t { + socks5_reply h; + socks5_addr_ipv4 ip; + } PACKED redudp_expected_assoc_reply; + ++struct bound_udp4_key { ++ struct in_addr sin_addr; ++ uint16_t sin_port; ++}; ++ ++struct bound_udp4 { ++ struct bound_udp4_key key; ++ int ref; ++ int fd; ++}; ++ + /*********************************************************************** + * Helpers + */ ++// TODO: separate binding to privileged process (this operation requires uid-0) ++static void* root_bound_udp4 = NULL; // to avoid two binds to same IP:port ++ ++static int bound_udp4_cmp(const void *a, const void *b) ++{ ++ return memcmp(a, b, sizeof(struct bound_udp4_key)); ++} ++ ++static void bound_udp4_mkkey(struct bound_udp4_key *key, const struct sockaddr_in *addr) ++{ ++ memset(key, 0, sizeof(*key)); ++ key->sin_addr = addr->sin_addr; ++ key->sin_port = addr->sin_port; ++} ++ ++static int bound_udp4_get(const struct sockaddr_in *addr) ++{ ++ struct bound_udp4_key key; ++ struct bound_udp4 *node, **pnode; ++ ++ bound_udp4_mkkey(&key, addr); ++ // I assume, that memory allocation for lookup is awful, so I use ++ // tfind/tsearch pair instead of tsearch/check-result. ++ pnode = tfind(&key, &root_bound_udp4, bound_udp4_cmp); ++ if (pnode) { ++ assert((*pnode)->ref > 0); ++ (*pnode)->ref++; ++ return (*pnode)->fd; ++ } ++ ++ node = calloc(1, sizeof(*node)); ++ if (!node) { ++ log_errno(LOG_ERR, "calloc"); ++ goto fail; ++ } ++ ++ node->key = key; ++ node->ref = 1; ++ node->fd = socket(AF_INET, SOCK_DGRAM, 0); ++ if (node->fd == -1) { ++ log_errno(LOG_ERR, "socket"); ++ goto fail; ++ } ++ ++ if (0 != redudp_transparent(node->fd)) ++ goto fail; ++ ++ if (0 != bind(node->fd, (struct sockaddr*)addr, sizeof(*addr))) { ++ log_errno(LOG_ERR, "bind"); ++ goto fail; ++ } ++ ++ pnode = tsearch(node, &root_bound_udp4, bound_udp4_cmp); ++ if (!pnode) { ++ log_errno(LOG_ERR, "tsearch(%p) == %p", node, pnode); ++ goto fail; ++ } ++ assert(node == *pnode); ++ ++ return node->fd; ++ ++fail: ++ if (node) { ++ if (node->fd != -1) ++ redsocks_close(node->fd); ++ free(node); ++ } ++ return -1; ++} ++ ++static void bound_udp4_put(const struct sockaddr_in *addr) ++{ ++ struct bound_udp4_key key; ++ struct bound_udp4 **pnode, *node; ++ void *parent; ++ ++ bound_udp4_mkkey(&key, addr); ++ pnode = tfind(&key, &root_bound_udp4, bound_udp4_cmp); ++ assert(pnode && (*pnode)->ref > 0); ++ ++ node = *pnode; ++ ++ node->ref--; ++ if (node->ref) ++ return; ++ ++ parent = tdelete(node, &root_bound_udp4, bound_udp4_cmp); ++ assert(parent); ++ ++ redsocks_close(node->fd); // expanding `pnode` to avoid use after free ++ free(node); ++} ++ ++static int redudp_transparent(int fd) ++{ ++ int on = 1; ++ int error = setsockopt(fd, SOL_IP, IP_TRANSPARENT, &on, sizeof(on)); ++ if (error) ++ log_errno(LOG_ERR, "setsockopt(..., SOL_IP, IP_TRANSPARENT)"); ++ return error; ++} ++ ++static int do_tproxy(redudp_instance* instance) ++{ ++ return instance->config.destaddr.sin_addr.s_addr == 0; ++} ++ ++static struct sockaddr_in* get_destaddr(redudp_client *client) ++{ ++ if (do_tproxy(client->instance)) ++ return &client->destaddr; ++ else ++ return &client->instance->config.destaddr; ++} ++ + static void redudp_fill_preamble(socks5_udp_preabmle *preamble, redudp_client *client) + { + preamble->reserved = 0; + preamble->frag_no = 0; /* fragmentation is not supported */ + preamble->addrtype = socks5_addrtype_ipv4; +- preamble->ip.addr = client->instance->config.destaddr.sin_addr.s_addr; +- preamble->ip.port = client->instance->config.destaddr.sin_port; ++ preamble->ip.addr = get_destaddr(client)->sin_addr.s_addr; ++ preamble->ip.port = get_destaddr(client)->sin_port; + } + + static struct evbuffer* socks5_mkmethods_plain_wrapper(void *p) +@@ -104,6 +232,8 @@ static void redudp_drop_client(redudp_client *client) + redudp_log_errno(client, LOG_ERR, "event_del"); + redsocks_close(fd); + } ++ if (client->sender_fd != -1) ++ bound_udp4_put(&client->destaddr); + list_for_each_entry_safe(q, tmp, &client->queue, list) { + list_del(&q->list); + free(q); +@@ -344,7 +474,8 @@ static void redudp_relay_connected(struct bufferevent *buffev, void *_arg) + redudp_client *client = _arg; + int do_password = socks5_is_valid_cred(client->instance->config.login, client->instance->config.password); + int error; +- redudp_log_error(client, LOG_DEBUG, ""); ++ char relayaddr_str[RED_INET_ADDRSTRLEN]; ++ redudp_log_error(client, LOG_DEBUG, "via %s", red_inet_ntop(&client->instance->config.relayaddr, relayaddr_str, sizeof(relayaddr_str))); + + if (!red_is_socket_connected_ok(buffev)) { + redudp_log_errno(client, LOG_NOTICE, "red_is_socket_connected_ok"); +@@ -382,7 +513,7 @@ static void redudp_timeout(int fd, short what, void *_arg) + redudp_drop_client(client); + } + +-static void redudp_first_pkt_from_client(redudp_instance *self, struct sockaddr_in *clientaddr, char *buf, size_t pktlen) ++static void redudp_first_pkt_from_client(redudp_instance *self, struct sockaddr_in *clientaddr, struct sockaddr_in *destaddr, char *buf, size_t pktlen) + { + redudp_client *client = calloc(1, sizeof(*client)); + +@@ -395,9 +526,13 @@ static void redudp_first_pkt_from_client(redudp_instance *self, struct sockaddr_ + INIT_LIST_HEAD(&client->queue); + client->instance = self; + memcpy(&client->clientaddr, clientaddr, sizeof(*clientaddr)); ++ if (destaddr) ++ memcpy(&client->destaddr, destaddr, sizeof(client->destaddr)); + evtimer_set(&client->timeout, redudp_timeout, client); + // XXX: self->relay_ss->init(client); + ++ client->sender_fd = -1; // it's postponed until socks-server replies to avoid trivial DoS ++ + client->relay = red_connect_relay(&client->instance->config.relayaddr, + redudp_relay_connected, redudp_relay_error, client); + if (!client->relay) +@@ -431,7 +566,7 @@ static void redudp_pkt_from_socks(int fd, short what, void *_arg) + + assert(fd == EVENT_FD(&client->udprelay)); + +- pktlen = red_recv_udp_pkt(fd, pkt.buf, sizeof(pkt.buf), &udprelayaddr); ++ pktlen = red_recv_udp_pkt(fd, pkt.buf, sizeof(pkt.buf), &udprelayaddr, NULL); + if (pktlen == -1) + return; + +@@ -455,8 +590,8 @@ static void redudp_pkt_from_socks(int fd, short what, void *_arg) + return; + } + +- if (pkt.header.ip.port != client->instance->config.destaddr.sin_port || +- pkt.header.ip.addr != client->instance->config.destaddr.sin_addr.s_addr) ++ if (pkt.header.ip.port != get_destaddr(client)->sin_port || ++ pkt.header.ip.addr != get_destaddr(client)->sin_addr.s_addr) + { + char buf[RED_INET_ADDRSTRLEN]; + struct sockaddr_in pktaddr = { +@@ -472,8 +607,18 @@ static void redudp_pkt_from_socks(int fd, short what, void *_arg) + redsocks_time(&client->last_relay_event); + redudp_bump_timeout(client); + ++ if (do_tproxy(client->instance) && client->sender_fd == -1) { ++ client->sender_fd = bound_udp4_get(&client->destaddr); ++ if (client->sender_fd == -1) { ++ redudp_log_error(client, LOG_WARNING, "bound_udp4_get failure"); ++ return; ++ } ++ } ++ + fwdlen = pktlen - sizeof(pkt.header); +- outgoing = sendto(EVENT_FD(&client->instance->listener), ++ outgoing = sendto(do_tproxy(client->instance) ++ ? client->sender_fd ++ : EVENT_FD(&client->instance->listener), + pkt.buf + sizeof(pkt.header), fwdlen, 0, + (struct sockaddr*)&client->clientaddr, sizeof(client->clientaddr)); + if (outgoing != fwdlen) { +@@ -486,18 +631,21 @@ static void redudp_pkt_from_socks(int fd, short what, void *_arg) + static void redudp_pkt_from_client(int fd, short what, void *_arg) + { + redudp_instance *self = _arg; +- struct sockaddr_in clientaddr; ++ struct sockaddr_in clientaddr, destaddr, *pdestaddr; + char buf[0xFFFF]; // UDP packet can't be larger then that + ssize_t pktlen; + redudp_client *tmp, *client = NULL; + ++ pdestaddr = do_tproxy(self) ? &destaddr : NULL; ++ + assert(fd == EVENT_FD(&self->listener)); +- pktlen = red_recv_udp_pkt(fd, buf, sizeof(buf), &clientaddr); ++ pktlen = red_recv_udp_pkt(fd, buf, sizeof(buf), &clientaddr, pdestaddr); + if (pktlen == -1) + return; + + // TODO: this lookup may be SLOOOOOW. + list_for_each_entry(tmp, &self->clients, list) { ++ // TODO: check destaddr + if (0 == memcmp(&clientaddr, &tmp->clientaddr, sizeof(clientaddr))) { + client = tmp; + break; +@@ -515,7 +663,7 @@ static void redudp_pkt_from_client(int fd, short what, void *_arg) + } + } + else { +- redudp_first_pkt_from_client(self, &clientaddr, buf, pktlen); ++ redudp_first_pkt_from_client(self, &clientaddr, pdestaddr, buf, pktlen); + } + } + +@@ -554,7 +702,6 @@ static int redudp_onenter(parser_section *section) + instance->config.relayaddr.sin_family = AF_INET; + instance->config.relayaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + instance->config.destaddr.sin_family = AF_INET; +- instance->config.destaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + instance->config.max_pktqueue = 5; + instance->config.udp_timeout = 30; + instance->config.udp_timeout_stream = 180; +@@ -614,6 +761,28 @@ static int redudp_init_instance(redudp_instance *instance) + goto fail; + } + ++ if (do_tproxy(instance)) { ++ int on = 1; ++ char buf[RED_INET_ADDRSTRLEN]; ++ // iptables TPROXY target does not send packets to non-transparent sockets ++ if (0 != redudp_transparent(fd)) ++ goto fail; ++ ++ error = setsockopt(fd, SOL_IP, IP_RECVORIGDSTADDR, &on, sizeof(on)); ++ if (error) { ++ log_errno(LOG_ERR, "setsockopt(listener, SOL_IP, IP_RECVORIGDSTADDR)"); ++ goto fail; ++ } ++ ++ log_error(LOG_DEBUG, "redudp @ %s: TPROXY", red_inet_ntop(&instance->config.bindaddr, buf, sizeof(buf))); ++ } ++ else { ++ char buf1[RED_INET_ADDRSTRLEN], buf2[RED_INET_ADDRSTRLEN]; ++ log_error(LOG_DEBUG, "redudp @ %s: destaddr=%s", ++ red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1)), ++ red_inet_ntop(&instance->config.destaddr, buf2, sizeof(buf2))); ++ } ++ + error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr)); + if (error) { + log_errno(LOG_ERR, "bind"); +diff --git a/redudp.h b/redudp.h +index 308bd33..3f1d9d1 100644 +--- a/redudp.h ++++ b/redudp.h +@@ -24,6 +24,8 @@ typedef struct redudp_client_t { + list_head list; + redudp_instance *instance; + struct sockaddr_in clientaddr; ++ struct sockaddr_in destaddr; ++ int sender_fd; // shared between several clients socket (bound to `destaddr`) + struct event timeout; + struct bufferevent *relay; + struct event udprelay; +diff --git a/utils.c b/utils.c +index 6e1f3af..afdeea8 100644 +--- a/utils.c ++++ b/utils.c +@@ -26,17 +26,54 @@ + #include "utils.h" + #include "redsocks.h" // for redsocks_close + +-int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inaddr) ++int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inaddr, struct sockaddr_in *toaddr) + { + socklen_t addrlen = sizeof(*inaddr); + ssize_t pktlen; +- +- pktlen = recvfrom(fd, buf, buflen, 0, (struct sockaddr*)inaddr, &addrlen); ++ struct msghdr msg; ++ struct iovec io; ++ char control[1024]; ++ ++ memset(&msg, 0, sizeof(msg)); ++ msg.msg_name = inaddr; ++ msg.msg_namelen = sizeof(*inaddr); ++ msg.msg_iov = &io; ++ msg.msg_iovlen = 1; ++ msg.msg_control = control; ++ msg.msg_controllen = sizeof(control); ++ io.iov_base = buf; ++ io.iov_len = buflen; ++ ++ pktlen = recvmsg(fd, &msg, 0); + if (pktlen == -1) { + log_errno(LOG_WARNING, "recvfrom"); + return -1; + } + ++ if (toaddr) { ++ memset(toaddr, 0, sizeof(*toaddr)); ++ for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { ++ if ( ++ cmsg->cmsg_level == SOL_IP && ++ cmsg->cmsg_type == IP_ORIGDSTADDR && ++ cmsg->cmsg_len >= CMSG_LEN(sizeof(*toaddr)) ++ ) { ++ struct sockaddr_in* cmsgaddr = (struct sockaddr_in*)CMSG_DATA(cmsg); ++ char buf[RED_INET_ADDRSTRLEN]; ++ log_error(LOG_DEBUG, "IP_ORIGDSTADDR: %s", red_inet_ntop(cmsgaddr, buf, sizeof(buf))); ++ memcpy(toaddr, cmsgaddr, sizeof(*toaddr)); ++ } ++ else { ++ log_error(LOG_WARNING, "unexepcted cmsg (level,type) = (%d,%d)", ++ cmsg->cmsg_level, cmsg->cmsg_type); ++ } ++ } ++ if (toaddr->sin_family != AF_INET) { ++ log_error(LOG_WARNING, "(SOL_IP, IP_ORIGDSTADDR) not found"); ++ return -1; ++ } ++ } ++ + if (addrlen != sizeof(*inaddr)) { + log_error(LOG_WARNING, "unexpected address length %u instead of %zu", addrlen, sizeof(*inaddr)); + return -1; +diff --git a/utils.h b/utils.h +index d3af00f..c2277e9 100644 +--- a/utils.h ++++ b/utils.h +@@ -44,7 +44,7 @@ char *redsocks_evbuffer_readline(struct evbuffer *buf); + struct bufferevent* red_connect_relay(struct sockaddr_in *addr, evbuffercb writecb, everrorcb errorcb, void *cbarg); + int red_socket_geterrno(struct bufferevent *buffev); + int red_is_socket_connected_ok(struct bufferevent *buffev); +-int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inaddr); ++int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *fromaddr, struct sockaddr_in *toaddr); + + int fcntl_nonblock(int fd); + +-- +1.9.1 + diff --git a/net/redsocks/patches/0004-Fix-transposition-of-memset-parameters.patch b/net/redsocks/patches/0004-Fix-transposition-of-memset-parameters.patch new file mode 100644 index 000000000..cb6fa1e4c --- /dev/null +++ b/net/redsocks/patches/0004-Fix-transposition-of-memset-parameters.patch @@ -0,0 +1,25 @@ +From b60b492602448b59aea194afd4991910d3613e5c Mon Sep 17 00:00:00 2001 +From: Cody Schafer +Date: Tue, 24 Apr 2012 04:33:13 -0500 +Subject: [PATCH 04/12] Fix transposition of memset parameters. + +--- + utils.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/utils.c b/utils.c +index afdeea8..31c6894 100644 +--- a/utils.c ++++ b/utils.c +@@ -222,7 +222,7 @@ char *red_inet_ntop(const struct sockaddr_in* sa, char* buffer, size_t buffer_si + + assert(buffer_size >= sizeof(placeholder)); + +- memset(buffer, buffer_size, 0); ++ memset(buffer, 0, buffer_size); + if (sa->sin_family == AF_INET) { + retval = inet_ntop(AF_INET, &sa->sin_addr, buffer, buffer_size); + port = ((struct sockaddr_in*)sa)->sin_port; +-- +1.9.1 + diff --git a/net/redsocks/patches/0005-Fix-compilation-on-Ubuntu-10.04-LTS-and-hopefully-De.patch b/net/redsocks/patches/0005-Fix-compilation-on-Ubuntu-10.04-LTS-and-hopefully-De.patch new file mode 100644 index 000000000..f0f09aa54 --- /dev/null +++ b/net/redsocks/patches/0005-Fix-compilation-on-Ubuntu-10.04-LTS-and-hopefully-De.patch @@ -0,0 +1,105 @@ +From 18e2b5ed1ffb3e7c5dfec8ff41b3027163f680ed Mon Sep 17 00:00:00 2001 +From: Leonid Evdokimov +Date: Wed, 12 Sep 2012 02:05:39 +0400 +Subject: [PATCH 09/12] Fix compilation on Ubuntu 10.04 LTS and (hopefully) + Debian squeeze[1] + +fixes #28, fixes #22, fixes #24 +[1] current "stable" release +--- + libc-compat.h | 25 +++++++++++++++++++++++++ + libevent-compat.h | 11 +++++++++++ + redsocks.c | 1 + + redudp.c | 1 + + utils.c | 1 + + 5 files changed, 39 insertions(+) + create mode 100644 libc-compat.h + create mode 100644 libevent-compat.h + +diff --git a/libc-compat.h b/libc-compat.h +new file mode 100644 +index 0000000..adcf63b +--- /dev/null ++++ b/libc-compat.h +@@ -0,0 +1,25 @@ ++#ifndef UUID_67C91670_FCCB_4855_BDF7_609F1EECB8B4 ++#define UUID_67C91670_FCCB_4855_BDF7_609F1EECB8B4 ++ ++/* all these definitions, are included into bits/in.h from libc6-dev 2.15-0ubuntu10 ++ * from Ubuntu 12.04 and is not included into libc6-dev 2.11.1-0ubuntu7.10 from ++ * Ubuntu 10.04. ++ * linux/in.h is not included directly because of lots of redefinitions, ++ * extracting single value from linux/in.h is not done because it looks like ++ * autotools reinvention */ ++#ifndef IP_ORIGDSTADDR ++# warning Using hardcoded value for IP_ORIGDSTADDR as libc headers do not define it. ++# define IP_ORIGDSTADDR 20 ++#endif ++ ++#ifndef IP_RECVORIGDSTADDR ++# warning Using hardcoded value for IP_RECVORIGDSTADDR as libc headers do not define it. ++# define IP_RECVORIGDSTADDR IP_ORIGDSTADDR ++#endif ++ ++#ifndef IP_TRANSPARENT ++# warning Using hardcoded value for IP_TRANSPARENT as libc headers do not define it. ++# define IP_TRANSPARENT 19 ++#endif ++ ++#endif // 67C91670_FCCB_4855_BDF7_609F1EECB8B4 +diff --git a/libevent-compat.h b/libevent-compat.h +new file mode 100644 +index 0000000..a7f1ca1 +--- /dev/null ++++ b/libevent-compat.h +@@ -0,0 +1,11 @@ ++#ifndef UUID_FC148CFA_5ECC_488E_8A62_CD39406C9F1E ++#define UUID_FC148CFA_5ECC_488E_8A62_CD39406C9F1E ++ ++/* evutil_socket_t is macros in libevent-2.0, not typedef, libevent-1.4 is ++ * still supported because of Ubuntu 10.04 LTS */ ++#ifndef evutil_socket_t ++# warning Using hardcoded value for evutil_socket_t as libevent headers do not define it. ++# define evutil_socket_t int ++#endif ++ ++#endif // FC148CFA_5ECC_488E_8A62_CD39406C9F1E +diff --git a/redsocks.c b/redsocks.c +index ba5eab2..878576f 100644 +--- a/redsocks.c ++++ b/redsocks.c +@@ -33,6 +33,7 @@ + #include "base.h" + #include "redsocks.h" + #include "utils.h" ++#include "libevent-compat.h" + + + #define REDSOCKS_RELAY_HALFBUFF 4096 +diff --git a/redudp.c b/redudp.c +index 262af3e..05460dc 100644 +--- a/redudp.c ++++ b/redudp.c +@@ -32,6 +32,7 @@ + #include "main.h" + #include "redsocks.h" + #include "redudp.h" ++#include "libc-compat.h" + + #define redudp_log_error(client, prio, msg...) \ + redsocks_log_write_plain(__FILE__, __LINE__, __func__, 0, &(client)->clientaddr, get_destaddr(client), prio, ## msg) +diff --git a/utils.c b/utils.c +index 31c6894..7de3969 100644 +--- a/utils.c ++++ b/utils.c +@@ -25,6 +25,7 @@ + #include "log.h" + #include "utils.h" + #include "redsocks.h" // for redsocks_close ++#include "libc-compat.h" + + int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inaddr, struct sockaddr_in *toaddr) + { +-- +1.9.1 + diff --git a/net/redsocks/patches/0006-fix_default_config_location.patch b/net/redsocks/patches/0006-fix_default_config_location.patch new file mode 100644 index 000000000..1994befa6 --- /dev/null +++ b/net/redsocks/patches/0006-fix_default_config_location.patch @@ -0,0 +1,17 @@ +Description: change the default config file location + redsocks by default looks for ./redsocks.conf. Change this to + /etc/redsocks.conf for a more deterministic behaviour. +Author: Apollon Oikonomopoulos +Forwared: no +Last-Update: 2013-04-23 +--- a/main.c ++++ b/main.c +@@ -39,7 +39,7 @@ + &dnstc_subsys, + }; + +-static const char *confname = "redsocks.conf"; ++static const char *confname = "/etc/redsocks.conf"; + static const char *pidfile = NULL; + + static void terminate(int sig, short what, void *_arg)