|
| 1 | +package main |
| 2 | + |
| 3 | +import ( |
| 4 | + "bufio" |
| 5 | + "context" |
| 6 | + "encoding/binary" |
| 7 | + "errors" |
| 8 | + "fmt" |
| 9 | + "io" |
| 10 | + "log" |
| 11 | + "net" |
| 12 | +) |
| 13 | + |
| 14 | +const socks5Ver = 0x05 |
| 15 | +const cmdBind = 0x01 |
| 16 | +const atypIPV4 = 0x01 |
| 17 | +const atypeHOST = 0x03 |
| 18 | +const atypeIPV6 = 0x04 |
| 19 | + |
| 20 | +func main() { |
| 21 | + server, err := net.Listen("tcp", "0.0.0.0:1080") |
| 22 | + if err != nil { |
| 23 | + panic(err) |
| 24 | + } |
| 25 | + for { |
| 26 | + client, err := server.Accept() |
| 27 | + if err != nil { |
| 28 | + log.Printf("Accept failed %v", err) |
| 29 | + continue |
| 30 | + } |
| 31 | + go process(client) |
| 32 | + } |
| 33 | +} |
| 34 | + |
| 35 | +func process(conn net.Conn) { |
| 36 | + defer conn.Close() |
| 37 | + reader := bufio.NewReader(conn) |
| 38 | + err := auth(reader, conn) |
| 39 | + if err != nil { |
| 40 | + log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err) |
| 41 | + return |
| 42 | + } |
| 43 | + err = connect(reader, conn) |
| 44 | + if err != nil { |
| 45 | + log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err) |
| 46 | + return |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +func auth(reader *bufio.Reader, conn net.Conn) (err error) { |
| 51 | + // +----+----------+----------+ |
| 52 | + // |VER | NMETHODS | METHODS | |
| 53 | + // +----+----------+----------+ |
| 54 | + // | 1 | 1 | 1 to 255 | |
| 55 | + // +----+----------+----------+ |
| 56 | + // VER: 协议版本,socks5为0x05 |
| 57 | + // NMETHODS: 支持认证的方法数量 |
| 58 | + // METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下: |
| 59 | + // X’00’ NO AUTHENTICATION REQUIRED |
| 60 | + // X’02’ USERNAME/PASSWORD |
| 61 | + |
| 62 | + ver, err := reader.ReadByte() |
| 63 | + if err != nil { |
| 64 | + return fmt.Errorf("read ver failed:%w", err) |
| 65 | + } |
| 66 | + if ver != socks5Ver { |
| 67 | + return fmt.Errorf("not supported ver:%v", ver) |
| 68 | + } |
| 69 | + methodSize, err := reader.ReadByte() |
| 70 | + if err != nil { |
| 71 | + return fmt.Errorf("read methodSize failed:%w", err) |
| 72 | + } |
| 73 | + method := make([]byte, methodSize) |
| 74 | + _, err = io.ReadFull(reader, method) |
| 75 | + if err != nil { |
| 76 | + return fmt.Errorf("read method failed:%w", err) |
| 77 | + } |
| 78 | + |
| 79 | + // +----+--------+ |
| 80 | + // |VER | METHOD | |
| 81 | + // +----+--------+ |
| 82 | + // | 1 | 1 | |
| 83 | + // +----+--------+ |
| 84 | + _, err = conn.Write([]byte{socks5Ver, 0x00}) |
| 85 | + if err != nil { |
| 86 | + return fmt.Errorf("write failed:%w", err) |
| 87 | + } |
| 88 | + return nil |
| 89 | +} |
| 90 | + |
| 91 | +func connect(reader *bufio.Reader, conn net.Conn) (err error) { |
| 92 | + // +----+-----+-------+------+----------+----------+ |
| 93 | + // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | |
| 94 | + // +----+-----+-------+------+----------+----------+ |
| 95 | + // | 1 | 1 | X'00' | 1 | Variable | 2 | |
| 96 | + // +----+-----+-------+------+----------+----------+ |
| 97 | + // VER 版本号,socks5的值为0x05 |
| 98 | + // CMD 0x01表示CONNECT请求 |
| 99 | + // RSV 保留字段,值为0x00 |
| 100 | + // ATYP 目标地址类型,DST.ADDR的数据对应这个字段的类型。 |
| 101 | + // 0x01表示IPv4地址,DST.ADDR为4个字节 |
| 102 | + // 0x03表示域名,DST.ADDR是一个可变长度的域名 |
| 103 | + // DST.ADDR 一个可变长度的值 |
| 104 | + // DST.PORT 目标端口,固定2个字节 |
| 105 | + |
| 106 | + buf := make([]byte, 4) |
| 107 | + _, err = io.ReadFull(reader, buf) |
| 108 | + if err != nil { |
| 109 | + return fmt.Errorf("read header failed:%w", err) |
| 110 | + } |
| 111 | + ver, cmd, atyp := buf[0], buf[1], buf[3] |
| 112 | + if ver != socks5Ver { |
| 113 | + return fmt.Errorf("not supported ver:%v", ver) |
| 114 | + } |
| 115 | + if cmd != cmdBind { |
| 116 | + return fmt.Errorf("not supported cmd:%v", ver) |
| 117 | + } |
| 118 | + addr := "" |
| 119 | + switch atyp { |
| 120 | + case atypIPV4: |
| 121 | + _, err = io.ReadFull(reader, buf) |
| 122 | + if err != nil { |
| 123 | + return fmt.Errorf("read atyp failed:%w", err) |
| 124 | + } |
| 125 | + addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3]) |
| 126 | + case atypeHOST: |
| 127 | + hostSize, err := reader.ReadByte() |
| 128 | + if err != nil { |
| 129 | + return fmt.Errorf("read hostSize failed:%w", err) |
| 130 | + } |
| 131 | + host := make([]byte, hostSize) |
| 132 | + _, err = io.ReadFull(reader, host) |
| 133 | + if err != nil { |
| 134 | + return fmt.Errorf("read host failed:%w", err) |
| 135 | + } |
| 136 | + addr = string(host) |
| 137 | + case atypeIPV6: |
| 138 | + return errors.New("IPv6: no supported yet") |
| 139 | + default: |
| 140 | + return errors.New("invalid atyp") |
| 141 | + } |
| 142 | + _, err = io.ReadFull(reader, buf[:2]) |
| 143 | + if err != nil { |
| 144 | + return fmt.Errorf("read port failed:%w", err) |
| 145 | + } |
| 146 | + port := binary.BigEndian.Uint16(buf[:2]) |
| 147 | + |
| 148 | + dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port)) |
| 149 | + if err != nil { |
| 150 | + return fmt.Errorf("dial dst failed:%w", err) |
| 151 | + } |
| 152 | + defer dest.Close() |
| 153 | + log.Println("dial", addr, port) |
| 154 | + |
| 155 | + // +----+-----+-------+------+----------+----------+ |
| 156 | + // |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | |
| 157 | + // +----+-----+-------+------+----------+----------+ |
| 158 | + // | 1 | 1 | X'00' | 1 | Variable | 2 | |
| 159 | + // +----+-----+-------+------+----------+----------+ |
| 160 | + // VER socks版本,这里为0x05 |
| 161 | + // REP Relay field,内容取值如下 X’00’ succeeded |
| 162 | + // RSV 保留字段 |
| 163 | + // ATYPE 地址类型 |
| 164 | + // BND.ADDR 服务绑定的地址 |
| 165 | + // BND.PORT 服务绑定的端口DST.PORT |
| 166 | + _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0}) |
| 167 | + if err != nil { |
| 168 | + return fmt.Errorf("write failed: %w", err) |
| 169 | + } |
| 170 | + ctx, cancel := context.WithCancel(context.Background()) |
| 171 | + defer cancel() |
| 172 | + |
| 173 | + go func() { |
| 174 | + _, _ = io.Copy(dest, reader) |
| 175 | + cancel() |
| 176 | + }() |
| 177 | + go func() { |
| 178 | + _, _ = io.Copy(conn, dest) |
| 179 | + cancel() |
| 180 | + }() |
| 181 | + |
| 182 | + <-ctx.Done() |
| 183 | + return nil |
| 184 | +} |
0 commit comments