6

I have a program that listens on port 443 and then redirects to either an SSH or HTTPS local server depending on the detected protocol.

The program does this by connecting to the local server and proxying all data back and forth through its own process.

However, this causes the originating host on the local servers to be logged as localhost.

Is there any way to pass the socket directly to the local server process (rather than just making a new TCP connection) so that the parameters of sockaddr_in (or sockaddr_in6) will be retained?

Platform for this is Linux.

Luca Farber
  • 96
  • 1
  • 3

3 Answers3

7

Here is a code snippet taken from stunnel (from client.c in the local_bind function if you want to look at all the code).

#ifdef IP_TRANSPARENT
int on=1;
if(c->opt->option.transparent) {
    if(setsockopt(c->fd, SOL_IP, IP_TRANSPARENT, &on, sizeof on))
        sockerror("setsockopt IP_TRANSPARENT");
    /* ignore the error to retain Linux 2.2 compatibility */
    /* the error will be handled by bind(), anyway */
}
#endif /* IP_TRANSPARENT */

memcpy(&addr, &c->bind_addr.addr[0], sizeof addr);
if(ntohs(addr.in.sin_port)>=1024) { /* security check */
    if(!bind(c->fd, &addr.sa, addr_len(addr))) {
        s_log(LOG_INFO, "local_bind succeeded on the original port");
        return; /* success */
    }
    if(get_last_socket_error()!=EADDRINUSE
#ifndef USE_WIN32
            || !c->opt->option.transparent
#endif /* USE_WIN32 */
            ) {
        sockerror("local_bind (original port)");
        longjmp(c->err, 1);
    }
}

Earlier, c->bind_addr was set to the address of the connecting peer with this code:

    else if(c->opt->option.transparent)
    memcpy(&c->bind_addr, &c->peer_addr, sizeof(SOCKADDR_LIST));

The stunnel documentation contains this advice for recent Linux kernels:

Remote mode (either 2.2.x and >=2.6.28) requires stunnel to be executed as root. setuid option will also break this functionality.

Linux >=2.6.28 requires the following setup for iptables and routing (possibly in /etc/rc.local or equivalent file):

iptables -t mangle -N DIVERT
iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
iptables -t mangle -A DIVERT -j MARK --set-mark 1
iptables -t mangle -A DIVERT -j ACCEPT
ip rule add fwmark 1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
Community
  • 1
  • 1
Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
  • Thanks for the advice! but unfortunately that technique does not appear to work when the destination host is localhost – Luca Farber Apr 30 '10 at 00:34
  • @Luca Farber: I added some more information taken from the stunnel FAQ. Try that. – Zan Lynx Apr 30 '10 at 01:20
  • Hmm, despite fiddling around with it a lot, I can't seem to get it any further than connect(), where it hangs. "netstat -a -n" shows the connection in state of SYN_SENT (with remote host/port as source and 127.0.0.1:localport as destination). – Luca Farber Apr 30 '10 at 03:38
  • See if you can get something like stunnel working. Maybe there's a trick your code is missing. – Zan Lynx Apr 30 '10 at 05:35
  • 1
    From stunnel manpage: -T transparent proxy mode Re-write address to appear as if wrapped daemon is connecting from the SSL client machine instead of the machine running stunnel. Available only on some operating systems (Linux only, we believe) and then only in server mode. Note that this option will not combine with proxy mode (-r) unless the client's default route to the target machine lies through the host running stunnel, *which cannot be localhost*. – Giel Jan 08 '12 at 17:10
6

If you have control over the local server processes, then you can do this. Create a persistent UNIX-domain socket connection between the proxy process and the server process, then use sendmsg() on that UNIX-domain socket to pass a SCM_RIGHTS message containing the TCP socket's file descriptor. The proxy process can then close its handle to the TCP socket.

When the server process gets passed a file descriptor from the proxy in a SCM_RIGHTS message, it just has to add it to its normal set of client sockets, as if it had come from accept().

caf
  • 233,326
  • 40
  • 323
  • 462
  • Thanks that is very interesting, though unfortunately I do not have control over the local servers; they are essentially black boxes – Luca Farber Apr 30 '10 at 00:35
1

Using https://stackoverflow.com/a/2741535/388191, it is possible to do this with traffic to localhost by using connmark to keep track of the incoming local transparent connection and mark the outgoing packets to reroute them back to the local host:

iptables -t mangle -N DIVERT
iptables -t mangle -A PREROUTING -i lo -p tcp -m socket -j DIVERT
iptables -t mangle -A DIVERT -j CONNMARK --set-mark 2
iptables -t mangle -A DIVERT -j ACCEPT
iptables -t mangle -A OUTPUT -m connmark --mark 2 -j MARK --set-mark 1
ip rule add fwmark 1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
Simon
  • 11
  • 3