我有一个程序侦听端口443,然后根据检测到的协议redirect到SSH或HTTPS本地服务器。
程序通过连接到本地服务器并通过自己的进程来回传递所有数据。
但是,这会导致本地服务器上的始发主机被logging为本地主机。
有没有办法将套接字直接传递给本地服务器进程(而不是仅仅build立一个新的TCP连接),以便保留sockaddr_in
(或sockaddr_in6
)的参数?
这个平台是Linux。
这里是一个来自stunnel的代码片段(如果你想查看所有的代码,可以从local_bind函数中的client.c获得)。
#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); } }
早些时候,c-> bind_addr被设置为连接对等体的地址,这个代码如下:
else if(c->opt->option.transparent) memcpy(&c->bind_addr, &c->peer_addr, sizeof(SOCKADDR_LIST));
stunnel文档包含了对最近的Linux内核的这个建议:
远程模式(2.2.x和> = 2.6.28)要求stunnel以root身份执行。 setuid选项也会打破这个功能。
Linux> = 2.6.28需要对iptables和路由进行以下设置(可能位于/etc/rc.local或同等文件中):
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
如果你有本地服务器进程的控制权,那么你可以这样做。 在代理进程和服务器进程之间创建一个持久的UNIX域套接字连接,然后在该UNIX域套接字上使用sendmsg()
传递一个包含TCP套接字文件描述符的SCM_RIGHTS
消息。 代理进程可以关闭其对TCP套接字的句柄。
当服务器进程从SCM_RIGHTS
消息中的代理传递一个文件描述符时,它只需将其添加到正常的一组客户端套接字中,就好像它来自accept()
。