基于Golang实现socks5代理协议的服务器的搭建 | 青训营笔记

309 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记

今天学习了如何使用Go语言实现实现了socks5协议的服务器的搭建,下面就让我们一起来看看吧

什么是socks5协议?

简单来说,socks5协议可以在客户端和服务端之间担当中间联系人这个角色,它可以使某些位于内网中的客户端通过socks5服务器与外网进行数据交换,其本质是客户端与socks5服务器使用TCP|IP协议进行连接,客户端向socks5服务器发送请求,socks5服务器将请求发送给对应服务器请求资源,接收到数据后再发送给客户端,这样就实现了代理服务器的功能,而客户端到底请求什么数据,这些socks5服务器并不关心

具体实现思路

首先需要让服务器启动后监听一个端口,监听到客户端发送的请求后再进行下一步处理
func main() {
server, err := net.Listen("tcp", "127.0.0.1:1080")
if err != nil {
   panic(err)
}
for {
   client, err2 := server.Accept()
   if err2 != nil {
      fmt.Printf("Accept failed%v", err2)
      continue
   }
   go process(client)
}

首先是客户端发送认证请求,请求中应该包含版本号、请求方法的数目、请求方法的信息,服务器接受认证请求,选择提供的可用的相应方式响应请求,如果没有可用的请求方法,则断开与客户端的连接

func auth(reader *bufio.Reader, conn net.Conn) (err error) {
   ver, err := reader.ReadByte()
   if err != nil {
      return fmt.Errorf("read ver failed:%w", err)
   }
   if ver != socks5Ver {
      return fmt.Errorf("not support 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)
   }
   _, err = conn.Write([]byte{socks5Ver, 0x00})
   if err != nil {
      return fmt.Errorf("write back failed:%w", err)
   }
   return nil
}

socks5服务器响应后,客户带应该再次发送代理请求,服务器根据客户端发送的请求来确定资源地址类型,使用net.Get()来建立与远程服务器的连接,并使用同时开两个协程,使用通过io.Copy()函数来互相传递客户端与服务器端的信息实现代理的功能,在客户端请求远程服务器资源时,服务器充当中介,来传递请求与回应,socks5服务器对于 传递的信息并不感兴趣

func connect(reader *bufio.Reader, conn net.Conn) (err error) {
   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 unspoort ver:%v", ver)
   }
   if cmd != cmdBind {
      return fmt.Errorf("not support cmd:%v", cmdBind)
   }
   addr := ""
   switch atyp {
   case atypIPV4:
      _, err = io.ReadFull(reader, buf)
      if err != nil {
         return fmt.Errorf("read atypIPV4 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 atypHost failed:%w", err)
      }
      addr = string(host)
   case atypeIPV6:
      return errors.New("IPV6: not support yet")
   default:
      return errors.New("invalid atyp")
   }
   _, err = io.ReadFull(reader, buf[:2])
   if err != nil {
      return fmt.Errorf("read ports failed:%w", err)
   }
   port := binary.BigEndian.Uint16(buf[:2])
   log.Println("Dial ", addr, port)
   
   _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
   if err != nil {
      return fmt.Errorf("write back failed:%w", err)
   }
   dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port))
   if err != nil {
      return fmt.Errorf("dail failed :%w", err)
   }
   defer dest.Close()
   ctx, cancle := context.WithCancel(context.Background())
   defer cancle()
   go func() {
      _, _ = io.Copy(dest, reader)
      cancle()
   }()
   go func() {
      _, _ = io.Copy(conn, dest)
      cancle()
   }()
   <-ctx.Done()
   return nil
}

总的流程再梳理一下,socks5服务器在与客户端完成TCP协议三次握手后,建立连接,客户端再向socks5服务器发送认证请求以确认完成代理,确认完成后,客户端再发送代理请求,包含请求的资源地址,服务器响应代理请求并解析地址后向目标服务器发送连接请求后,在socks5服务器中建立双向数据传输通道,socks5服务器只是一个中介,这还可以增强通信的安全性 最后我们实现process函数,在函数中先实现auth()函数处理认证请求,再用connect()函数实现请求资源与双向资源传输

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")
   err = connect(reader, conn)
   if err != nil {
      log.Println("client %v connect failed %v", conn.RemoteAddr(), err)
   }
   log.Println("connect success")
}

这样,socks5协议实现的服务器就完成了