likes
comments
collection
share

Socks5协议入门解析+GO语言简单实现

作者站长头像
站长
· 阅读数 55

本文主要介绍了socks5协议的构成,和基于go语言的tcp实现方案,并且只处理了connent类型的请求。协议详细地址datatracker.ietf.org/doc/html/rf…datatracker.ietf.org/doc/html/rf…

一、认证

认证方式分为了两种,一种是不用认证,一种是用户名密码认证

  • 客户端发送支持的协议数据

Socks5协议入门解析+GO语言简单实现

属性描述
VER直接取值0x05,表示socks5协议
NMETHODS客户端支持的认证方式数量
METHODS认证方式的实际值和NMETHODS对应
  • 服务端回复客户端数据

Socks5协议入门解析+GO语言简单实现

METHOD支持的值

o  X'00' NO AUTHENTICATION REQUIRED
o  X'01' GSSAPI
o  X'02' USERNAME/PASSWORD
o  X'03' to X'7F' IANA ASSIGNED
o  X'80' to X'FE' RESERVED FOR PRIVATE METHODS
o  X'FF' NO ACCEPTABLE METHODS

不需要密码的方式回复数据

属性描述
VER协议版本,回复0x05,表示socks5协议
METHOD回复0x00不需要密码的方式

需要密码的方式回复数据

属性描述
VER协议版本,回复0x05,表示socks5协议
METHOD回复0x02用户名和密码

客户端发送用户名和密码

Socks5协议入门解析+GO语言简单实现

属性描述
VER协议版本,0x05,表示socks5协议
ULEN用户名长度
UNAME用户名
PLEN密码长度
PASWD密码

服务端回复

Socks5协议入门解析+GO语言简单实现

属性描述
VER协议版本,0x05,表示socks5协议
STATUS0x00表示成功,其他的回复都是失败

二、连接

客户端会发送如下参数过来

Socks5协议入门解析+GO语言简单实现

属性描述
VER协议版本,0x05,表示socks5协议
CMD命令类型
RSV不用管
ATYP地址类型
DST.ADDR地址
DST.PORT端口

详细说明

o  VER    protocol version: X'05'
o  CMD
 o  CONNECT X'01' 
 o  BIND X'02'
 o  UDP ASSOCIATE X'03'
o  RSV    RESERVED
o  ATYP   address type of following address
 o  IP V4 address: X'01'
 o  DOMAINNAME: X'03'
 o  IP V6 address: X'04'
o  DST.ADDR       desired destination address
o  DST.PORT desired destination port in network octet
 order

请求目的地地址解析说明

In an address field (DST.ADDR, BND.ADDR), the ATYP field specifies
the type of address contained within the field:

      o  X'01'

the address is a version-4 IP address, with a length of 4 octets
# IPV4的地址读取4字节

      o  X'03'

the address field contains a fully-qualified domain name.  The first
octet of the address field contains the number of octets of name that
follow, there is no terminating NUL octet.
# 域名方式,第一个字节是域名长度,后面读取按照长度进行读取,转换为域名字符串

      o  X'04'

the address is a version-6 IP address, with a length of 16 octets.
# IPV6则取16字节

服务端回复信息

Socks5协议入门解析+GO语言简单实现

o  VER    protocol version: X'05'
o  REP    Reply field:
    o  X'00' succeeded
    o  X'01' general SOCKS server failure
    o  X'02' connection not allowed by ruleset
    o  X'03' Network unreachable
    o  X'04' Host unreachable
    o  X'05' Connection refused
    o  X'06' TTL expired
    o  X'07' Command not supported
    o  X'08' Address type not supported
    o  X'09' to X'FF' unassigned
o  RSV    RESERVED
o  ATYP   address type of following address
    o  IP V4 address: X'01'
    o  DOMAINNAME: X'03'
    o  IP V6 address: X'04'
o  BND.ADDR       server bound address
o  BND.PORT       server bound port in network octet order

此处只只介绍connect类型的指令,其他类型的方式相似,按照官方文档进行实现即可,这里我们读取出来目的地的地址,然后创建一个tcp的连接,把连接返回的dest.LocalAddr()的地址和端口,这里直接填入IPV4类型即可,填入BND.ADDR、BND.PORT连接就完成了,后续就正常进行数据的交换了

三、数据正式传输

由于上面我们已经拿到了client和desc的sockt连接,只需要把他们后续发送的内容交换到他们各自通道上就行。

四、GO语言代码

package main

import (
   "encoding/binary"
   "errors"
   "fmt"
   "io"
   "net"
)

func main() {
   server, err := net.Listen("tcp",":1080")
   if err != nil {
      panic(err)
   }
   for {
      client, err := server.Accept()
      if err != nil {
         panic(err)
      }
      go process(client)
   }
}

func process(client net.Conn)  {
   if err := Socks5Auth(client); err != nil {
      fmt.Println("auth error:", err)
      client.Close()
      return
   }
   target, err := Socks5Connect(client)
   if err != nil {
      fmt.Println("connect error:", err)
      client.Close()
      return
   }
   Socks5Forward(client, target)
}

func Socks5Auth(client net.Conn) (err error)  {
   buf := make([]byte,256)
   n, err := io.ReadFull(client,buf[:2])
   if n!= 2 {
      return errors.New("socks5 connect error")
   }
   ver, nmethods := int(buf[0]), int(buf[1])
   if ver != 5 {
      return errors.New("socks5 version error")
   }
   n, err = io.ReadFull(client, buf[:nmethods])
   if n!= nmethods {
      return errors.New("reading methods: " + err.Error())
   }
   // 用户名密码登陆
   n, err = client.Write([]byte{0x05, 0x02})

   n, err = io.ReadFull(client,buf[:2])
   // 读取用户名
   userNameLength := int(buf[1])
   n, err = io.ReadFull(client,buf[:userNameLength])
   userName := string(buf[:userNameLength])

   n, err = io.ReadFull(client,buf[:1])
   passwordLength := int(buf[0])
   n, err = io.ReadFull(client,buf[:passwordLength])
   password := string(buf[:passwordLength])
   if userName != "hzh" && password != "123456" {
      return errors.New("username or password err")
   }
   n, err = client.Write([]byte{0x05, 0x00})
   // 该回复是不需要用户名和密码
   // n, err = client.Write([]byte{0x05, 0x00})
   if n != 2 || err != nil {
      return errors.New("write rsp err:" + err.Error())
   }
   return nil
}

func Socks5Connect(client net.Conn) (net.Conn, error) {
   buf := make([]byte,256)
   n, err := io.ReadFull(client,buf[:4])
   if n != 4 {
      return nil, errors.New("read header: " + err.Error())
   }
   ver, cmd, _, atyp := buf[0], buf[1], buf[2], buf[3]
   if ver != 5 || cmd != 1 {
      return nil, errors.New("invalid ver/cmd")
   }

   addr := ""
   switch atyp {
   case 1:
      n, err = io.ReadFull(client, 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(client, buf[:1])
      if n != 1 {
         return nil, errors.New("invalid hostname: " + err.Error())
      }
      addrLen := int(buf[0])

      n, err = io.ReadFull(client, 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 atyp")
   }
   n, err = io.ReadFull(client, 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)
   fmt.Println(dest.LocalAddr())
   if err != nil {
      return nil, errors.New("dial dst: " + err.Error())
   }
   n, err = client.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
}

func Socks5Forward(client, target net.Conn) {
   defer client.Close()
   defer target.Close()
   forward := func(src, dest net.Conn,res chan int) {
      io.Copy(src, dest)
      res <- 1
   }
   res := make(chan int)
   go forward(client, target, res)
   go forward(target, client, res)
   // 等待线程执行完毕
   for i := 0; i < 2; i++ {
      <- res
   }
}
转载自:https://juejin.cn/post/7037351835748794382
评论
请登录