GO语言实践_SOCKS5| 青训营笔记

100 阅读7分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天

课堂笔记

本堂课重点内容

  • 代理的相关知识
  • TCP三次握手

详细知识点介绍

  • TCP三次握手加入代理后的样子
  • go语言的go关键字实现非常低消耗的子线程

实践练习例子

SOCKS5

代理原理及介绍

  • 需要代理方向服务器发出请求信息

  • 代理方应答

  • 需要代理方接到应答后发送向代理方发送目的ip和端口

  • 代理方与目的连接

  • 代理方将需要代理方发出的信息传到目的方,将目的方发出的信息传到需要代理方。代理完成

    由于网上的信息传输都是运用tcp或udp进行的,所以使用socks5代理可以办到网上所能办到的一切,而且不舆目的方会查到你的ip,既安全又方便

原理示意
ClientSock5 ServerHost
1.协商阶段->
1.1通过协商<-
2.发送请求->2.1建立TCP连接->
2.3返回状态<-2.2返回响应<-
3.发送数据->3.1relay数据->
3.3响应结果<-3.2响应结果<-

实现步骤

TCP echo server
  • 实现空的boss函数用来建成

  • 浏览器给代理服务器发送一个born

    • born三字段为

      • 协议版本号固定数VER
      • 建成方式的数目NMETHODS
      • 建成方式的编码METHODS
  • 首先读取协议版本号(单字节)

  • 然后读取建成方式method_size

  • 用method_size创建一个method建成方式

  • 用io进行用户填充字段

  • 打印响应日志

  • 理应返回一个包->选择了哪种建成方式

  • 构造返回的包

    • 协议版本号
    • 建成方式
 package main
 ​
 import (
     "bufio"
     "log"
     "net"
 )
 ​
 func main() {
     server, err := net.Listen("tcp", "127.0.0.1:1080")
     if err != nil {
         panic(err)
     }
     for {
         client, err := server.Accept()
         if err != nil {
             log.Printf("Accept failed %v", err)
             continue
         }
         go process(client)
     }
 }
 ​
 func process(conn net.Conn) {
     defer conn.Close()
     reader := bufio.NewReader(conn)
     for {
         b, err := reader.ReadByte()
         if err != nil {
             break
         }
         _, err = conn.Write([]byte{b})
         if err != nil {
             break
         }
     }
 }
 ​
auth
  • 实现空的boss函数用来建成

  • 浏览器给代理服务器发送一个报文

    • 报文三字段为

      • 协议版本号固定数VER
      • 建成方式的数目NMETHODS
      • 建成方式的编码METHODS
  • 首先读取协议版本号(单字节)

  • 然后读取建成方式method_size

  • 用method_size创建一个method建成方式

  • 用io进行用户填充字段

  • 打印响应日志

  • 理应返回一个包->选择了哪种建成方式

  • 构造返回的包

    • 协议版本号
    • 建成方式
 package main
 ​
 import (
     "bufio"
     "fmt"
     "io"
     "log"
     "net"
 )
 ​
 const socks5Ver = 0x05
 const cmdBind = 0x01
 const atypIPV4 = 0x01
 const atypeHOST = 0x03
 const atypeIPV6 = 0x04
 ​
 func main() {
     server, err := net.Listen("tcp", "127.0.0.1:1080")
     if err != nil {
         panic(err)
     }
     for {
         client, err := server.Accept()
         if err != nil {
             log.Printf("Accept failed %v", err)
             continue
         }
         go process(client)
     }
 }
 ​
 func process(conn net.Conn) {
     defer conn.Close()
     reader := bufio.NewReader(conn)
     err := auth(reader, conn)
     if err != nil {
         log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
         return
     }
     log.Println("auth success")
 }
 ​
 func auth(reader *bufio.Reader, conn net.Conn) (err error) {
     // +----+----------+----------+
     // |VER | NMETHODS | METHODS  |
     // +----+----------+----------+
     // | 1  |    1     | 1 to 255 |
     // +----+----------+----------+
     // VER: 协议版本,socks5为0x05
     // NMETHODS: 支持认证的方法数量
     // METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下:
     // X’00’ NO AUTHENTICATION REQUIRED
     // X’02’ USERNAME/PASSWORD
 ​
     ver, err := reader.ReadByte()
     if err != nil {
         return fmt.Errorf("read ver failed:%w", err)
     }
     if ver != socks5Ver {
         return fmt.Errorf("not supported ver:%v", ver)
     }
     methodSize, err := reader.ReadByte()
     if err != nil {
         return fmt.Errorf("read methodSize failed:%w", err)
     }
     method := make([]byte, methodSize)
     _, err = io.ReadFull(reader, method)
     if err != nil {
         return fmt.Errorf("read method failed:%w", err)
     }
     log.Println("ver", ver, "method", method)
     // +----+--------+
     // |VER | METHOD |
     // +----+--------+
     // | 1  |   1    |
     // +----+--------+
     _, err = conn.Write([]byte{socks5Ver, 0x00})
     if err != nil {
         return fmt.Errorf("write failed:%w", err)
     }
     return nil
 }
 ​
请求阶段
  • 将包封装,发送相应请求
  • 打印发送的请求,确保正确
 package main
 ​
 import (
     "bufio"
     "encoding/binary"
     "errors"
     "fmt"
     "io"
     "log"
     "net"
 )
 ​
 const socks5Ver = 0x05
 const cmdBind = 0x01
 const atypIPV4 = 0x01
 const atypeHOST = 0x03
 const atypeIPV6 = 0x04
 ​
 func main() {
     server, err := net.Listen("tcp", "127.0.0.1:1080")
     if err != nil {
         panic(err)
     }
     for {
         client, err := server.Accept()
         if err != nil {
             log.Printf("Accept failed %v", err)
             continue
         }
         go process(client)
     }
 }
 ​
 func process(conn net.Conn) {
     defer conn.Close()
     reader := bufio.NewReader(conn)
     err := auth(reader, conn)
     if err != nil {
         log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
         return
     }
     err = connect(reader, conn)
     if err != nil {
         log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
         return
     }
 }
 ​
 func auth(reader *bufio.Reader, conn net.Conn) (err error) {
     // +----+----------+----------+
     // |VER | NMETHODS | METHODS  |
     // +----+----------+----------+
     // | 1  |    1     | 1 to 255 |
     // +----+----------+----------+
     // VER: 协议版本,socks5为0x05
     // NMETHODS: 支持认证的方法数量
     // METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下:
     // X’00’ NO AUTHENTICATION REQUIRED
     // X’02’ USERNAME/PASSWORD
 ​
     ver, err := reader.ReadByte()
     if err != nil {
         return fmt.Errorf("read ver failed:%w", err)
     }
     if ver != socks5Ver {
         return fmt.Errorf("not supported ver:%v", ver)
     }
     methodSize, err := reader.ReadByte()
     if err != nil {
         return fmt.Errorf("read methodSize failed:%w", err)
     }
     method := make([]byte, methodSize)
     _, err = io.ReadFull(reader, method)
     if err != nil {
         return fmt.Errorf("read method failed:%w", err)
     }
 ​
     // +----+--------+
     // |VER | METHOD |
     // +----+--------+
     // | 1  |   1    |
     // +----+--------+
     _, err = conn.Write([]byte{socks5Ver, 0x00})
     if err != nil {
         return fmt.Errorf("write failed:%w", err)
     }
     return nil
 }
 ​
 func connect(reader *bufio.Reader, conn net.Conn) (err error) {
     // +----+-----+-------+------+----------+----------+
     // |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
     // +----+-----+-------+------+----------+----------+
     // | 1  |  1  | X'00' |  1   | Variable |    2     |
     // +----+-----+-------+------+----------+----------+
     // VER 版本号,socks5的值为0x05
     // CMD 0x01表示CONNECT请求
     // RSV 保留字段,值为0x00
     // ATYP 目标地址类型,DST.ADDR的数据对应这个字段的类型。
     //   0x01表示IPv4地址,DST.ADDR为4个字节
     //   0x03表示域名,DST.ADDR是一个可变长度的域名
     // DST.ADDR 一个可变长度的值
     // DST.PORT 目标端口,固定2个字节
 ​
     buf := make([]byte, 4)
     _, err = io.ReadFull(reader, buf)
     if err != nil {
         return fmt.Errorf("read header failed:%w", err)
     }
     ver, cmd, atyp := buf[0], buf[1], buf[3]
     if ver != socks5Ver {
         return fmt.Errorf("not supported ver:%v", ver)
     }
     if cmd != cmdBind {
         return fmt.Errorf("not supported cmd:%v", ver)
     }
     addr := ""
     switch atyp {
     case atypIPV4:
         _, err = io.ReadFull(reader, buf)
         if err != nil {
             return fmt.Errorf("read atyp failed:%w", err)
         }
         addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3])
     case atypeHOST:
         hostSize, err := reader.ReadByte()
         if err != nil {
             return fmt.Errorf("read hostSize failed:%w", err)
         }
         host := make([]byte, hostSize)
         _, err = io.ReadFull(reader, host)
         if err != nil {
             return fmt.Errorf("read host failed:%w", err)
         }
         addr = string(host)
     case atypeIPV6:
         return errors.New("IPv6: no supported yet")
     default:
         return errors.New("invalid atyp")
     }
     _, err = io.ReadFull(reader, buf[:2])
     if err != nil {
         return fmt.Errorf("read port failed:%w", err)
     }
     port := binary.BigEndian.Uint16(buf[:2])
 ​
     log.Println("dial", addr, port)
 ​
     // +----+-----+-------+------+----------+----------+
     // |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
     // +----+-----+-------+------+----------+----------+
     // | 1  |  1  | X'00' |  1   | Variable |    2     |
     // +----+-----+-------+------+----------+----------+
     // VER socks版本,这里为0x05
     // REP Relay field,内容取值如下 X’00’ succeeded
     // RSV 保留字段
     // ATYPE 地址类型
     // BND.ADDR 服务绑定的地址
     // BND.PORT 服务绑定的端口DST.PORT
     _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
     if err != nil {
         return fmt.Errorf("write failed: %w", err)
     }
     return nil
 }
 ​
relay阶段
  • 和ip端口建立tcp连接

    • Dial函数
  • 双向转换数值进行代理

    • Copy函数进行单向数据转化,将只读流中的数据通过死循环拷贝到可写流中
 package main
 ​
 import (
     "bufio"
     "context"
     "encoding/binary"
     "errors"
     "fmt"
     "io"
     "log"
     "net"
 )
 ​
 const socks5Ver = 0x05
 const cmdBind = 0x01
 const atypIPV4 = 0x01
 const atypeHOST = 0x03
 const atypeIPV6 = 0x04
 ​
 func main() {
     server, err := net.Listen("tcp", "127.0.0.1:1080")
     if err != nil {
         panic(err)
     }
     for {
         client, err := server.Accept()
         if err != nil {
             log.Printf("Accept failed %v", err)
             continue
         }
         go process(client)
     }
 }
 ​
 func process(conn net.Conn) {
     defer conn.Close()
     reader := bufio.NewReader(conn)
     err := auth(reader, conn)
     if err != nil {
         log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
         return
     }
     err = connect(reader, conn)
     if err != nil {
         log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
         return
     }
 }
 ​
 func auth(reader *bufio.Reader, conn net.Conn) (err error) {
     // +----+----------+----------+
     // |VER | NMETHODS | METHODS  |
     // +----+----------+----------+
     // | 1  |    1     | 1 to 255 |
     // +----+----------+----------+
     // VER: 协议版本,socks5为0x05
     // NMETHODS: 支持认证的方法数量
     // METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下:
     // X’00’ NO AUTHENTICATION REQUIRED
     // X’02’ USERNAME/PASSWORD
 ​
     ver, err := reader.ReadByte()
     if err != nil {
         return fmt.Errorf("read ver failed:%w", err)
     }
     if ver != socks5Ver {
         return fmt.Errorf("not supported ver:%v", ver)
     }
     methodSize, err := reader.ReadByte()
     if err != nil {
         return fmt.Errorf("read methodSize failed:%w", err)
     }
     method := make([]byte, methodSize)
     _, err = io.ReadFull(reader, method)
     if err != nil {
         return fmt.Errorf("read method failed:%w", err)
     }
 ​
     // +----+--------+
     // |VER | METHOD |
     // +----+--------+
     // | 1  |   1    |
     // +----+--------+
     _, err = conn.Write([]byte{socks5Ver, 0x00})
     if err != nil {
         return fmt.Errorf("write failed:%w", err)
     }
     return nil
 }
 ​
 func connect(reader *bufio.Reader, conn net.Conn) (err error) {
     // +----+-----+-------+------+----------+----------+
     // |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
     // +----+-----+-------+------+----------+----------+
     // | 1  |  1  | X'00' |  1   | Variable |    2     |
     // +----+-----+-------+------+----------+----------+
     // VER 版本号,socks5的值为0x05
     // CMD 0x01表示CONNECT请求
     // RSV 保留字段,值为0x00
     // ATYP 目标地址类型,DST.ADDR的数据对应这个字段的类型。
     //   0x01表示IPv4地址,DST.ADDR为4个字节
     //   0x03表示域名,DST.ADDR是一个可变长度的域名
     // DST.ADDR 一个可变长度的值
     // DST.PORT 目标端口,固定2个字节
 ​
     buf := make([]byte, 4)
     _, err = io.ReadFull(reader, buf)
     if err != nil {
         return fmt.Errorf("read header failed:%w", err)
     }
     ver, cmd, atyp := buf[0], buf[1], buf[3]
     if ver != socks5Ver {
         return fmt.Errorf("not supported ver:%v", ver)
     }
     if cmd != cmdBind {
         return fmt.Errorf("not supported cmd:%v", ver)
     }
     addr := ""
     switch atyp {
     case atypIPV4:
         _, err = io.ReadFull(reader, buf)
         if err != nil {
             return fmt.Errorf("read atyp failed:%w", err)
         }
         addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3])
     case atypeHOST:
         hostSize, err := reader.ReadByte()
         if err != nil {
             return fmt.Errorf("read hostSize failed:%w", err)
         }
         host := make([]byte, hostSize)
         _, err = io.ReadFull(reader, host)
         if err != nil {
             return fmt.Errorf("read host failed:%w", err)
         }
         addr = string(host)
     case atypeIPV6:
         return errors.New("IPv6: no supported yet")
     default:
         return errors.New("invalid atyp")
     }
     _, err = io.ReadFull(reader, buf[:2])
     if err != nil {
         return fmt.Errorf("read port failed:%w", err)
     }
     port := binary.BigEndian.Uint16(buf[:2])
 ​
     dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port))
     if err != nil {
         return fmt.Errorf("dial dst failed:%w", err)
     }
     defer dest.Close()
     log.Println("dial", addr, port)
 ​
     // +----+-----+-------+------+----------+----------+
     // |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
     // +----+-----+-------+------+----------+----------+
     // | 1  |  1  | X'00' |  1   | Variable |    2     |
     // +----+-----+-------+------+----------+----------+
     // VER socks版本,这里为0x05
     // REP Relay field,内容取值如下 X’00’ succeeded
     // RSV 保留字段
     // ATYPE 地址类型
     // BND.ADDR 服务绑定的地址
     // BND.PORT 服务绑定的端口DST.PORT
     _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
     if err != nil {
         return fmt.Errorf("write failed: %w", err)
     }
     ctx, cancel := context.WithCancel(context.Background())
     defer cancel()
 ​
     go func() {
         _, _ = io.Copy(dest, reader)
         cancel()
     }()
     go func() {
         _, _ = io.Copy(conn, dest)
         cancel()
     }()
 ​
     <-ctx.Done()
     return nil
 }
 ​

课后个人总结

  • 对于代理服务器的三次握手的理解不够到位