青训营笔记---复习专辑(3) GO语言实战 第一弹 SOCKS5代理

22 阅读6分钟

1.实现tcp echo server

呈现效果为:给服务器发送什么,服务器就回复什么

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
      }
   }
}

2.SOCKS5代理-auth

认证阶段:浏览器会给代理服务器发送一个报文,共三个字段,第一个字段是协议版本号, 第二个字段鉴权方式的数目,第三个字段每个鉴权方式的编码

const socks5Ver=0x05

func auth(reader *bufio.Reader,conn net.Conn)(err,error){

//+----+--------+-------+
//| VER|NMETHODS|METHODS|
//+----+--------+-------+
//|  1 |   1    |1to255 |
//+----+--------+-------+
//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.Errof("read ver failed:%w",err)
}
if ver!=socks5Ver{
return fmt.Errof("not supported ver:%v",ver)
}
methodSize,err:=reader.ReadByte()
if err!=nil{
return fmt.Errof("read methodSize failed:%w",err)
}
method:=make([]byte,methodSize)
//创建一个method缓冲区
_,err:=io.ReadFull(reader,method)
//将缓冲区填充满
if err!=nil{
return fmt.Errof("read method failed:%w",err)
}
log.Println("ver",ver,"method",method)
//+------+--------+
//| VER  | METHOD |
//+------+--------+
//按照协议返回一个包,告诉浏览器我们选择了哪种鉴权方式
_,err:=conn.Write([]byte{socks5Ver,0x00})
if err!=nil{
return fmt.Error("write failed:%w",err)
}
return nil
}
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 process")
}

3.SOCKS5代理-请求阶段

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
//这里我们只支持connect连接,即就是让代理服务器和下一个服务器创建连接
//CMD 0x01表示connect连接
//RSV 保留字段,值为0x00
//ATYP目标地址类型,DST.ADDR的数据对应这个字段的类型
// 0x01 表示IPV4地址,DST.ADDR为4个字节
//0x03表示域名,DST.ADDR是一个可变长度的域名
//DST.PORT目标端口,固定2个字节
buf:=make([]byte,4)
//创建一个长度为4的缓冲区
_,err:=io.ReadFull(reader,buf)
if err!=nil{
return fmt.Errof("read headr 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.Errof("not supported cmd:%v",ver)
}

addr:=""
switch atype{
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])
//从buf[:2]中读取端口号并赋值给port
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' succeed
//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
}
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
}
}

4.SOCKS5代理-realy阶段

dest,err:=net.Dial("tcp",fmt.Sprintf("%V:%v",addr,port))
//用tcp协议往对应域名或ip加端口去建立tcp连接
if err!=nil{
return fmt.Errorf("dial dst failed:%w",err)
}
defer dest.Close()
//在函数结束的时候去关闭连接
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 failed:%w",err)
}
//建立浏览器和下一个服务器的双向数据转发
ctx,cancel:=context.WithCancel(context.Background())
defer cancel()
//context.Background() 返回一个空的Context
//我们可以用这个 空的 Context 作为 goroutine 的root 节点(如果把整个 goroutine //的关系看作 树状)
//使用context.WithCancel(parent)函数,创建一个可取消的子Context
//函数返回值有两个:子Context Cancel 取消函数
go func(){
_,i=io.Copy(dest,reader)
cancel()
}()
//从用户浏览器到底层服务器
go func(){
_,_=io.Copy(conn,dest)
cancel()
}()
//从底层服务器到用户浏览器

<-ctx.Done()
//这个函数执行的时机是cancel被调用的时机
//在任何一个gorutine执行出错的时候调用cancel()函数
return nil

5.SOCKS5代理-完整代码

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                                                              |
|              | }