--- /dev/null
+#
+# 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 <jm@m-network.de>
+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))
--- /dev/null
+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.
--- /dev/null
+#!/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"
+}
--- /dev/null
+From 290f19972e9f7b74f818ae211cb535e32f1f314f Mon Sep 17 00:00:00 2001
+From: Leonid Evdokimov <leon@darkk.net.ru>
+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
+
--- /dev/null
+From 6015b3a6f26e04dd5d78cd6c1320886fc9035612 Mon Sep 17 00:00:00 2001
+From: Leonid Evdokimov <leon@darkk.net.ru>
+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 <errno.h>
+ #include <assert.h>
+ #include <fcntl.h>
++#include <string.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+@@ -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
+
--- /dev/null
+From 709646d59d96cb73a7e70347f37de9823e4e5f14 Mon Sep 17 00:00:00 2001
+From: Leonid Evdokimov <leon@darkk.net.ru>
+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 <stdlib.h>
++#include <search.h>
+ #include <string.h>
+ #include <sys/types.h>
+ #include <sys/uio.h>
+@@ -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, "<trace>");
++ 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
+
--- /dev/null
+From b60b492602448b59aea194afd4991910d3613e5c Mon Sep 17 00:00:00 2001
+From: Cody Schafer <jmesmon@gmail.com>
+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
+
--- /dev/null
+From 18e2b5ed1ffb3e7c5dfec8ff41b3027163f680ed Mon Sep 17 00:00:00 2001
+From: Leonid Evdokimov <leon@darkk.net.ru>
+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
+
--- /dev/null
+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 <apoikos@gmail.com>
+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)