« 再谈 C 语言的模块化设计 | 返回首页 | 闲扯几句 GC 的话题 »

写了一个 proxy 用途你懂的

用 linode 有一年多了 ,除了架设 blog ,我也折腾点小程序放在上面跑。在互联网上有一台自己的主机,对于一个程序员来说,还是很有必要的。当然这 vps 绝对不能选国内的,原因你懂的。这样,附带的一个好处就是可以用 ssh -D ,相信许多同学都喜欢用。

没来由的,我想自己写一个小程序,完成 ssh -D 一样的功能,不过,不走 SSL 加密。当然也不能走明文,由于众所周知的原因,明文通讯很容易引起奇异的连接断开或长期超时的 bug ,此 bug 绝对不在于你的程序。网络连接永远不是 100% 可靠的嘛。

我花了两天实现我的构想,本来第一天已经完成了,但实现的过于复杂,且 bug 重重。花了一个通宵都没完全解决。在补了个好觉后,我在梦中意识到,应该简化方案,然后在第二天重写了一遍,终于可以跑起来了。代码是用 Go 实现的,比较玩具,quick & dirty 。不过我还是把它们放在后面。有兴趣的同学可以拿去改进。目前这个雏形已经够我用了,所以即使有改进或许也懒得再贴出来了。

主体想法是由两个部分的程序构成,一边放在本机上,对 localhost 开启一个兼容 socks5 的本地端口。当然只这一部分是不够用的,我们需要在墙外再设置一个程序,它才是真正的 proxy server 。这两个程序之间保持一个 TCP 连接。由本地的程序负责把本地的 proxy 请求都转发到墙外。

最初的想法,我阅读了 socks5 的 RFC1928 ,觉得实现一个 socks5 server 没什么大不了的。我要做的只是把所有的 socks5 请求编码成私有协议,通过唯一的 TCP 连接转发到墙外,由墙外的程序正确的 proxy 。事实证明,想在一天时间内正确的实现 socks5 协议还是有点难度的。即使用 Go 语言,对于我这个新手来说也不现实。ps , 通宵不睡觉调 bug 的效率也太低,不推荐在 30 岁以后还经常干。

睡了一觉后,我修正了我的方案。整个系统由三部分构成:

本地 n:1 连接转发服务 ----(传说中的墙)---- 墙外 1:n 连接转发服务 ---- socks5 服务器

简单的说,本地可以向本地服务提起若干个 TCP 连接,请求 socks5 服务。然后,我的程序把他们合成一个,通过一个长连接,通通转发到墙外。这中间可以做简单的加密或数据压缩。

在墙外的 vps 上,我放置一个私有程序,接收这些合并起来的连接。然后分解为一个个独立的请求。然后去连接 vps 本地的一个 socks5 服务器。

因为是个人使用,墙的两边的自写程序,只保持一个连接,一旦连接建立起来后,墙外的服务不在监听接受新的连接。只做 1:1 的服务。这样比较安全。不过想扩展成多个服务的,对于 Go 语言来说也相当容易,只是我懒的做罢了。

私有程序没有做身份认证。这是因为我只提供 1:1 服务,所以即使不认证也不会被人盗用。我只需要在需要使用时,手动开启即可。当然加上认证机制也不难,没加的理由还是我比较懒 :)

真正的 socks5 服务器就不用自己写啦。我用的 ssocks 比较轻量,也很单纯,就是一个 socks5 server 而已。不过这个东东目前的版本(0.0.10)有个小问题,它默认的监听绑定地址是 0.0.0.0 且不能配置(写死在代码里了)。在 README 中,我们看到 TODO 里作者有让它可配置的计划。在我这个应用中,我希望 socks5 server 只给本地用。所以需要自己做点小修改了。

只需要把 src/libsocks/net-util.c 中 new_listen_socket 函数里

      addrS->sin_addr.s_addr = htonl(INADDR_ANY);  /* All Local addresses */

这一行中的 INADDR_ANY 改成 INADDR_LOOPBACK 即可。

我的程序的源代码看这里。如有 bug 欢迎指出,若有问题概不解答 :) 。写的很乱,估计自己都不想再看一遍了。


ps. 最近买了一套新桌游,七大奇迹,很是不错,推荐一下。

Comments

挖墙脚http://waqiangjiao.org 提供shadowsocks免费账号
处于神奇的原因,现在出口的延迟抖动总会在某个包卡壳一下,直接造成带宽足够却死慢的情况。最近试了final speed,才意识到自主流控还有这么大的挖掘空间。 其实做个中间层还蛮有意义的,把n-1 mux,用udp乱序发送,远程的再重整序列demux出来,一端挂socks5,另一端挂relay,理应效果会比你现在用的tcp-pipe好。 流控粗糙一些可以做双倍发送,去网络抖动,接收端消重,往细里可以心跳预估延迟,做ack重发。 至于可靠重发机制完全可以用本地内存把数据存下来把所有的数据预判为丢失,用ack删除本地重发map内的包就好。
这个不就是shadowsocks实现的功能吗,而且sock5实现好像也没你想象的那么复杂!
shadowsocks
云风哥 我修改了一下你的代码让其可以在go1.2上面编译。不过代理的功能好像使用不了。主要修改如下: os.Error -> error SetReadTimeOut ->SetReadDeadline(time.Now().Add(10*time.Second))
云风哥 我修改了一下你的代码让其可以在go1.2上面编译。不过代理的功能好像使用不了。主要修改如下: os.Error -> error SetReadTimeOut ->SetReadDeadline(time.Now().Add(10*time.Second))
使用一个 TCP 连接内外可不是一个好的选择。
端口转发神器 socat
我也想过替代ssh -D. 后来没有自己写代码,使用dante(一个socks5服务器) + stunnel来做了。算是socks5 over ssl了。
技术牛人就该保持这种情况,动手去实现自己想实现的东西;现有的工具虽然好,但是目的不是最终的实现,而是去尝试如何实现这个过程。
翻墙翻啊翻啊的累了
咳~~,一个ssh -D直接搞定了啊,从tunnel到SOCKS5全有,还是高强度加密的,还需要写啥代码咧。 如果说花一天熟悉go还过得去,但要实现功能的话,现有的强大工具不好使么....
@shuaiming 前几天看到已经有人搞出Windows下的了 http://glacjay.is-a-geek.org/blog/archives/308
对了,还有不支持windows平台(tun/tap)
我利用python和linux /dev/net/tun,也写了一个简单(不超过100行)用来翻墙的工具,有点类似于基于UDP vpn,但是可以穿越常见nat。作SA的,没啥时间写程序,有想法没有能完全实现 1 对于应用是透明的,无需设置代理(用tun/tap模拟以太网设备或者路由设备) 2 可以穿越nat,将不同的隐藏在nat后面两个点虚拟成一个网络中,或者一个以太网中。 3 如果再加上路由选路,或者交换机mac选路的功能(这个目前没有实现) 4 自己可以对加密,当前只是作了以下压缩。 缺点 1 要一个有外网地址的机器作为服务器协助穿越nat。 我觉得,这有点像分布式虚拟网络,等有空整理出来,应该会有人有兴趣
赞下…
对啊,想不通,为什么不直接用ssh tunnel + firefox proxy来翻?
喜欢go语言,因为花括号强制放行尾,呵呵!
对于以编程为爱好的人来说,实现本身就是目的。 写一晚上程序和打一晚上游戏,对于宅男来说,都是一种消遣。
又一种X-over-Y的运动。建议多调研,少重复轮子。实在要简单,你就用HTTP代理,IPsec运载模式可保护网络层。实在要互操作性,就一个开天鹅服务器加若干公路战士。
学习了啊,顿时感觉非常的复杂。。
要想翻墙,走私有协议是行不通的,很容易被ban。想想利用HTTP或其他常见协议来翻。
要想翻墙,走私有协议不是行不通的,很容易被ban。想想利用HTTP或其他常见协议来翻。
@est 看上去是的
我还小...不懂翻墙这么可怕的运动。。。
Is it basically TCP-over-TCP?
@Anonymous Windows平台有socksCap32。何况云风说了,这是为自己写的东西,sock5已经有很好的普适性了。 PS: 我之前也想写个类似的东西私用,一直没动手,翻看了下RFC1928真的很简单呢~
感觉上挺像stunnel,不过stunnel的协议用的是ssl的标准协议,tcp over ssl。
为啥不作成像OpenVPN那样呢,不是所有程序天生都支持socks5代理的,GNU/Linux你可以用tsocks,其他平台呢?

Post a comment

非这个主题相关的留言请到:留言本