Iptables – How a Transparent SOCKS Proxy Determines Destination IP

iptablesPROXYsockstor

There are two SOCKS proxies that I know about that support transparent proxying for any outgoing TCP connection: Tor and redsocks. Unlike HTTP proxies, these SOCKS proxies can transparently proxy any outgoing TCP connection, including encrypted protocols and protocols without metadata or headers.

Both of these proxies require the use of NAT to redirect any outgoing TCP traffic to the proxy's local port. For instance, if I am running Tor with TransPort 9040 on my local machine, I would need to add an iptables rule like this:

iptables -t nat -A OUTPUT -p tcp -j REDIRECT --to-port 9040

To my knowledge, this would replace the original destination IP and port with 127.0.0.1 and 9040, so given that this is an encrypted stream (like SSH) or one without headers (like whois), how does the proxy know the original destination IP and port?

Best Answer

Here is how it does it:

static int getdestaddr_iptables(int fd, const struct sockaddr_in *client, const struct sockaddr_in *bindaddr, struct sockaddr_in *destaddr)
{
        socklen_t socklen = sizeof(*destaddr);
        int error;

        error = getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, destaddr, &socklen);
        if (error) {
                log_errno(LOG_WARNING, "getsockopt");
                return -1;
        }
        return 0;
}

iptables overrites the original destination address but it remembers the old one. The application code can then fetch it by asking for a special socket option, SO_ORIGINAL_DST.

Related Question