Total Pageviews

Sunday, 10 November 2019

一个简单的用Golang实现的socks5 Proxy

这次使用Golang实现一个Socket5的简单代理。Socket5和HTTP并没有太大的不同,他们都可以完全给予TCP协议,只是请求的信息结构不同,所以这次我们不能像上次HTTP Proxy一样,解析请求和应答,要按照Socket的协议方式解析。

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

数据转发

建立好连接之后,就是数据传递转发,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:5083
COMMAND     PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
socks-pro 31318 root    3u  IPv6 1804076      0t0  TCP *:qfp (LISTEN)
[root@host ~]# 
这个socks-proxy-by-flysnow可以用作各种tunnel程序的后端程序。