这次使用Golang实现一个Socket5的简单代理。Socket5和HTTP并没有太大的不同,他们都可以完全给予TCP协议,只是请求的信息结构不同,所以这次我们不能像上次HTTP Proxy一样,解析请求和应答,要按照Socket的协议方式解析。
首先客户端会给服务端发送验证信息,这个是建立连接的前提。比如客户端:hi,哥们,借个火。服务端要认识它就说:好,给;如果不认识就说:你哪根葱啊!!
我们这里以最简单的不需要验证的方式为例进行介绍,这种方式进行了上面的一问一答后就可以开始建立连接了。对于其他验证方式,还需要再进行一次一问一答,主要是客户端提供验证信息,服务端回应验证是否正确,这些细节可以参考http://www.ietf.org/rfc/rfc1928.txt以及http://www.ietf.org/rfc/rfc1929.txt的Socket5协议定义。
Socket协议版本
Socket协议分为Socket4和Socket5两个版本,他们最明显的区别是Socket5同时支持TCP和UDP两个协议,而SOcket4只支持TCP。目前大部分使用的是Socket5,我们这里只简单的介绍Socket5协议。Socket5协议之授权认证
要想实现Socket5之间的连接会话,必须要懂SOcket5协议的实现细节和规范。这就好比我们都用普通话对话一样,彼此说的都明白,也可以给对方听得懂的回应。Socket5的客户端和服务端交流也一样,他们的语言就是Socket5协议。因为Socket5支持TCP和UDP两种,这里只介绍TCP这一种,UDP大同小异。首先客户端会给服务端发送验证信息,这个是建立连接的前提。比如客户端:hi,哥们,借个火。服务端要认识它就说:好,给;如果不认识就说:你哪根葱啊!!
我们这里以最简单的不需要验证的方式为例进行介绍,这种方式进行了上面的一问一答后就可以开始建立连接了。对于其他验证方式,还需要再进行一次一问一答,主要是客户端提供验证信息,服务端回应验证是否正确,这些细节可以参考http://www.ietf.org/rfc/rfc1928.txt以及http://www.ietf.org/rfc/rfc1929.txt的Socket5协议定义。
Socket5协议之建立连接。
Socket5的客户端和服务端进行双方授权验证通过之后,就开始建立连接了。连接由客户端发起,告诉Sokcet服务端客户端需要访问哪个远程服务器,其中包含,远程服务器的地址和端口,地址可以是IP4,IP6,也可以是域名。
详细描述参考http://www.ietf.org/rfc/rfc1928.txt
在linux vps上,安装go环境。
把以上的蓝色的代码保存为socks-proxy-by-flysnow.go,保存在linux vps上。
数据转发
建立好连接之后,就是数据传递转发,TCP协议可以直接转发。UDP的话需要特殊处理,具体参考协议定义,其实就是一个特殊格式的回应,和上面的一问一答差不多,更多协议细节 http://www.ietf.org/rfc/rfc1928.txt。完整代码
以下是完成的代码。package main
import (
"io"
"log"
"net"
"strconv"
)
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
l, err := net.Listen("tcp", ":5083")
if err != nil {
log.Panic(err)
}
for {
client, err := l.Accept()
if err != nil {
log.Panic(err)
}
go handleClientRequest(client)
}
}
func handleClientRequest(client net.Conn) {
if client == nil {
return
}
defer client.Close()
var b [1024]byte
n, err := client.Read(b[:])
if err != nil {
log.Println(err)
return
}
if b[0] == 0x05 { //只处理Socket5协议
//客户端回应:Socket服务端不需要验证方式
client.Write([]byte{0x05, 0x00})
n, err = client.Read(b[:])
var host, port string
switch b[3] {
case 0x01: //IP V4
host = net.IPv4(b[4], b[5], b[6], b[7]).String()
case 0x03: //域名
host = string(b[5 : n-2]) //b[4]表示域名的长度
case 0x04: //IP V6
host = net.IP{b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19]}.String()
}
port = strconv.Itoa(int(b[n-2])<<8 | int(b[n-1]))
server, err := net.Dial("tcp", net.JoinHostPort(host, port))
if err != nil {
log.Println(err)
return
}
defer server.Close()
client.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) //响应客户端连接成功
//进行转发
go io.Copy(server, client)
io.Copy(client, server)
}
}
这目前是一个简易版本的Socket5 代理,还有很多没有实现,要实现完成的Socket5,可以自己根据他定义的协议试试.在linux vps上,安装go环境。
把以上的蓝色的代码保存为socks-proxy-by-flysnow.go,保存在linux vps上。
然后,运行go build socks-proxy-by-flysnow.go
在当前目录下,就会生成可执行文件socks-proxy-by-flysnow。
[root@host ~]# ./socks-proxy-by-flysnow &[1] 31318[root@host ~]# lsof -i:5083COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEsocks-pro 31318 root 3u IPv6 1804076 0t0 TCP *:qfp (LISTEN)[root@host ~]#这个socks-proxy-by-flysnow可以用作各种tunnel程序的后端程序。