兑现一个代理服务

以天朝做程序员比较给人蛋疼,比如你想用GOOGLE,你就异常蛋疼。原因大家还知道。

接下来为,一开始协调以就此GOAGENT, VPN, SSH,
ShadowSocks等次第,GOAGENT和SHADOWSOCKS都是杀美妙之。而友好于挺早正开接触电脑的时光便生出想法自己写一个代理程序,因为各种各样的因由总是没有夺做,或者说好的需要连续能够吃满足,所以没什么动力。但是自学GO语言后,网络程序的开发变的尚未事先做C/C++时那蛋疼了,所以试着和谐写一个代理程序,然后为促成Eating
your own dog
food的施行。这里将一些经验总结记录下。GITHUB地址:https://github.com/eahydra/socks

 

SOCKS5协议

恰开之时段,是思念在开HTTP代理,但是HTTP协议比较复杂,牵扯到之概念比较多,自己而无熟悉,所以尽管从未选中做HTTP通道方式的代办程序。而代理协议里之所以底极端多之莫过于就算是SOCKS协议了。SOCKS4,SOCKS5等。因为自身一直于于是CHROME,
CHROME上的插件SwitchySharp也于好用,也支撑SOCKS5商议,更要的凡SOCKS5协议非常简单。

SOCKS5说道要细分三步,第一步就是是握手协商,协商双方的证实方式。我之做法简单粗暴:因为SOCKS5规定的握手协议中,规定了无限酷之多寡长度就是258独一直,所以我以促成之时节,直接申请258字节的BUFFER,然后读取。读取后也非对准证实方式展开判断是否合法,支持等等的,直接回到回0X05
0X00,也就是免需要证实。

次步就是是获得客户端发来之CMD协议。SOCKS5这里也确定了无与伦比丰富长度,263单字节。所以也同等次等申请出来以备后用。感谢GO语言封装的网络库,不管是啥域名,还是IPV4,IPV6之类的,调用net.Dial的下传上就是哼,net.Dial函数内部会协调搞定到IP的转移。然后便冲CMD里指定的靶子地址链接,成功后返回对应的情商数。目前我才支持了CONNECT,像UDP之类的哪怕从来不举行支撑,遇到了UDP指令直接告知客户端不支持,然后断掉链接。

其三步就是是始于收受客户端发送来的数额,然后发送至远端服务器,然后由远端服务器读数据再度倒车到客户端。这个就大概了,直接两执行代码搞定。

go io.Copy(dest, src)
io.Copy(src, dest)

 

部署

写好程序后,就得部署起来。感谢亚马逊提供了免费一年的AWS服务。怎么申请之类的自发性检索解决。GO的移植性这里虽体现出了,因为自身付出是当WINDOWS下,然后部署至AWS上之UBUNTU系统及,一个go
build搞定。然后程序走起后,直接装SwitchySharp到AWS上,效果显著。不过中间测试的下,发现链接到http://go-talks.appspot.com/github.com/extemporalgenome/gotalks/error-handling.slide#1
就够呛了,抓包发现,从AWS上作来了诸多RST,但是自己的先后没有打印出相应之CLOSE信息。这个上自己就算怀疑应该是颇啥(你懂得的)做了内容检测,解决的点子之一即是加密。所以我背后就是开了加密底作用。

加密

充实加密功能就是蛋疼了,因为自说不定未单独是如同种植加密算法,可能立刻会惦记就此RC4,后面就想用AES或者DES之类的。还有即使是怎么把加密算法封装起来,使其对现有的代码影响比较小。本来我是想协调手工封装成io.Reader和io.Writer的接口,这样的话就得组合一个调用链,很当然的把网络数据开展加解密。然后我不怕失去GO的标准库里索,有加密算法,RC4,DES,AES都有供,然后来只给crypto/cipher的管,这个包供了俩接口,一个深受StreamRead,另外一个深受StreamWrite,这俩接口正好可以满足io.Reader和io.Write。这一瞬间就概括多了。具体的代码如下(代码里不曾开什么工厂模式等等的,因为实在是没啥必要,写的大概多少暴点也非会见潜移默化其他代码):

func NewCipherStream(rwc io.ReadWriteCloser, cryptMethod string, password []byte) (*CipherStream, error) {
    var stream *CipherStream
    switch cryptMethod {
    default:
        stream = &CipherStream{
            reader:      rwc,
            writeCloser: rwc,
        }

    case "rc4":
        {
            rc4CipherRead, err := rc4.NewCipher(password)
            if err != nil {
                return nil, err
            }
            rc4CipherWrite, err := rc4.NewCipher(password)
            if err != nil {
                return nil, err
            }

            stream = &CipherStream{
                reader: &cipher.StreamReader{
                    S: rc4CipherRead,
                    R: rwc,
                },
                writeCloser: &cipher.StreamWriter{
                    S: rc4CipherWrite,
                    W: rwc,
                },
            }
        }
    case "des":
        {
            block, err := des.NewCipher(password)
            if err != nil {
                return nil, err
            }
            desRead := cipher.NewCFBDecrypter(block, desIV[:])
            desWrite := cipher.NewCFBEncrypter(block, desIV[:])
            return &CipherStream{
                reader: &cipher.StreamReader{
                    S: desRead,
                    R: rwc,
                },
                writeCloser: &cipher.StreamWriter{
                    S: desWrite,
                    W: rwc,
                },
            }, nil
        }
    }
    return stream, nil
}

一经无点名加密方法或者是未支持之加密方法,还是沿用原有的io.Reader和io.Writer接口。这样的话就足以减小对上层之影响。C++

盖支撑了多种加密方法,那么即使得用配备文件了。这个就是老简短,直接JSON格式搞定。

此外一个题材是,浏览器就认SOCKS协议,你现在加以了加密,那您得对浏览器隐藏掉,然后就是得支持俩模式,一个模式是地方的SOCKS运行方式,对浏览器提供的,另外一个模式就是是暨远端SOCKS服务的数目而加密。本来是怀念重新写一个地方SOCKS,但是想了生,实在是永不必要,因为此次的多数效益以存活的代码基础及还能满足,那么同样种实现方式就是代码拷贝出来,但是我一旦保护少分割;另外一种方式尽管是以老的代码上改。决定以存活代码上改,无非是于客户端的要来了后,要事先链接到自己之远端SOCKS服务,然后再提供数据中转服务。这里就提到到怎么与远端服务交互的问题。

PS:
现在最新版本支持SHADOWSOCKS协议,对AES,DES等加密算法的动做了改动。AES和DES需要的IV值会在数码头部传输,但特导相同赖。同时还会见对密码做相应的处理。具体看代码便只是明白。

跟远端SOCKS交互的化解方式

为缓解此问题,就得要订立协议。是否出必要增加协商为?我先是个想法是近似没必要,第二单想法是确实没有必要?真的没必要!因为SOCKS5的合计足够用,我啊未思量以一旦链接到远端还要加进一模仿额外的磋商,而此协议最后为会暨SOCKS5见仁见智不多,实现出来的代码又见面那个蛋疼。所以决定下SOCKS5商谈,做成一个调用链的款型。客户端来之握手,本地SOCKS也至远端SOCKS上进行握手。客户端发来之CMD直接转接到远端SOCKS。然后中间移动加密。这一瞬间实现就变的非常简单了。定义了一个构造体RemoteSocks,匿名组合了*CipherStream,这样虽得一直走加密了。具体定义代码如下:

type RemoteSocks struct {
    conn net.Conn
    *CipherStream
}

然后一起代码到AWS上,编译开飞,然后发现很流利。给基友狗眼坤分享了产,遇到了BUG,在外机器及链接YOUTUBE出现链接错误。然后想大概问题在哪,开始做代码REVIEW,发现自己实现的加密部分,RC4相关的片段读与描绘复用了与一个加密对象,然后是目标又未是线程安全的,问题应有是此处了,修正掉,然后测试之,OK了。

总结

到头来是有了协调之代理程序,用起颇开心,因为凡投机开的!Eating your own
dog
food很要紧。自己实现一个程序的早晚,可以绝不转跨越大酷的步,那样爱扯到蛋,可以一点点来。