-
客户端连接协议头
-
服务端确认连接协议头
-
连接远端服务器
-
转发远端服务器响应到客户端
程序入口
func main() {
ls, err := net.Listen("tcp", ":1080")
if err != nil {
fmt.Printf("Listen failed: %v\n", err)
return
}
for {
conn, err := ls.Accept()
if err != nil {
fmt.Printf("Accept failed: %v\n", err)
continue
}
go process(conn)
}
}
处理逻辑
func process(conn net.Conn) {
if err := Socks5Auth(conn); err != nil {
fmt.Println("Auth error: ", err)
conn.Close()
return
}
target, err := Socks5Connect(conn)
if err != nil {
fmt.Println("Connect error: ", err)
conn.Close()
return
}
Socks5Forward(conn, target)
}
socks5校验逻辑
func Socks5Auth(conn net.Conn) (err error) {
buf := make([]byte, 256)
n, err := io.ReadFull(conn, buf[:2])
if n != 2 {
return errors.New("read header: " + err.Error())
}
ver, nMethods := int(buf[0]), int(buf[1])
if ver != 5 {
return errors.New("invalid version")
}
n, err = io.ReadFull(conn, buf[:nMethods])
if n != nMethods {
return errors.New("read methods: " + err.Error())
}
n, err = conn.Write([]byte{0x05, 0x00})
if n != 2 {
return errors.New("write rsp err: " + err.Error())
}
return nil
}
代理连接远端服务器
func Socks5Connect(conn net.Conn) (net.Conn, error) {
buf := make([]byte, 256)
n, err := io.ReadFull(conn, buf[:4])
if n != 4 {
return nil, errors.New("read header: " + err.Error())
}
ver, cmd, _, atype := buf[0], buf[1], buf[2], buf[3]
if ver != 5 || cmd != 1 {
return nil, errors.New("invalid ver/cmd")
}
addr := ""
switch atype {
case 1:
n, err = io.ReadFull(conn, buf[:4])
if n != 4 {
return nil, errors.New("invalid ipv4: " + err.Error())
}
addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3])
case 3:
n, err := io.ReadFull(conn, buf[:1])
if n != 1 {
return nil, errors.New("invalid hostname: " + err.Error())
}
addrLen := int(buf[0])
n, err = io.ReadFull(conn, buf[:addrLen])
if n != addrLen {
return nil, errors.New("invalid hostname: " + err.Error())
}
addr = string(buf[:addrLen])
case 4:
return nil, errors.New("ipv6: no supported yet")
default:
return nil, errors.New("invalid atype")
}
n, err = io.ReadFull(conn, buf[:2])
if n != 2 {
return nil, errors.New("read port: " + err.Error())
}
port := binary.BigEndian.Uint16(buf[:2])
destAddrPort := fmt.Sprintf("%s:%d", addr, port)
dest, err := net.Dial("tcp", destAddrPort)
if err != nil {
return nil, errors.New("dial dst: " + err.Error())
}
n, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
if err != nil {
dest.Close()
return nil, errors.New("write rsp: " + err.Error())
}
return dest, nil
}
代理转发响应
因为TCP全双工通信,所以通过两个goroutine来进行读写实现转发
func Socks5Forward(conn, target net.Conn) {
fmt.Println("转发一条访问")
forward := func(src, dest net.Conn) {
defer src.Close()
defer dest.Close()
io.Copy(src, dest)
}
go forward(conn, target)
go forward(target, conn)
}
测试和压测
可以通过下面方式发起代理请求
curl --proxy "socks5://127.0.0.1:1080" http://www.baidu.com
github.com/cnlh/benchmark
1.写个http服务
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
r.Run(":8080")
}
2.代理压测
benchmark -c 10000 -n 1000000 http://127.0.0.1:8080/ping