github.com/sagernet/sing@v0.2.6/protocol/socks/socks4/protocol.go (about) 1 package socks4 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "io" 7 "net/netip" 8 9 "github.com/sagernet/sing/common" 10 "github.com/sagernet/sing/common/buf" 11 E "github.com/sagernet/sing/common/exceptions" 12 M "github.com/sagernet/sing/common/metadata" 13 "github.com/sagernet/sing/common/rw" 14 ) 15 16 const ( 17 Version byte = 4 18 19 CommandConnect byte = 1 20 CommandBind byte = 2 21 22 ReplyCodeGranted byte = 90 23 ReplyCodeRejectedOrFailed byte = 91 24 ReplyCodeCannotConnectToIdentd byte = 92 25 ReplyCodeIdentdReportDifferentUserID byte = 93 26 ) 27 28 type Request struct { 29 Command byte 30 Destination M.Socksaddr 31 Username string 32 } 33 34 func ReadRequest(reader io.Reader) (request Request, err error) { 35 version, err := rw.ReadByte(reader) 36 if err != nil { 37 return 38 } 39 if version != 4 { 40 err = E.New("excepted socks version 4, got ", version) 41 return 42 } 43 return ReadRequest0(reader) 44 } 45 46 func ReadRequest0(reader io.Reader) (request Request, err error) { 47 request.Command, err = rw.ReadByte(reader) 48 if err != nil { 49 return 50 } 51 err = binary.Read(reader, binary.BigEndian, &request.Destination.Port) 52 if err != nil { 53 return 54 } 55 var dstIP [4]byte 56 _, err = io.ReadFull(reader, dstIP[:]) 57 if err != nil { 58 return 59 } 60 var readHostName bool 61 if dstIP[0] == 0 && dstIP[1] == 0 && dstIP[2] == 0 && dstIP[3] != 0 { 62 readHostName = true 63 } else { 64 request.Destination.Addr = netip.AddrFrom4(dstIP) 65 } 66 request.Username, err = readString(reader) 67 if readHostName { 68 request.Destination.Fqdn, err = readString(reader) 69 request.Destination = M.ParseSocksaddrHostPort(request.Destination.Fqdn, request.Destination.Port) 70 } 71 return 72 } 73 74 func WriteRequest(writer io.Writer, request Request) error { 75 var requestLen int 76 requestLen += 1 // version 77 requestLen += 1 // command 78 requestLen += 2 // port 79 requestLen += 4 // ip 80 requestLen += 1 // NUL 81 if !request.Destination.IsIPv4() { 82 requestLen += len(request.Destination.AddrString()) + 1 83 } 84 if request.Username != "" { 85 requestLen += len(request.Username) 86 } 87 88 _buffer := buf.StackNewSize(requestLen) 89 defer common.KeepAlive(_buffer) 90 buffer := common.Dup(_buffer) 91 defer buffer.Release() 92 93 common.Must( 94 buffer.WriteByte(Version), 95 buffer.WriteByte(request.Command), 96 binary.Write(buffer, binary.BigEndian, request.Destination.Port), 97 ) 98 if request.Destination.IsIPv4() { 99 common.Must1(buffer.Write(request.Destination.Addr.AsSlice())) 100 } else { 101 // 0.0.0.X 102 common.Must(buffer.WriteZeroN(3)) 103 common.Must(buffer.WriteByte(1)) 104 } 105 if request.Username != "" { 106 common.Must1(buffer.WriteString(request.Username)) 107 } 108 common.Must(buffer.WriteZero()) 109 if !request.Destination.IsIPv4() { 110 common.Must1(buffer.WriteString(request.Destination.AddrString())) 111 common.Must(buffer.WriteZero()) 112 } 113 return rw.WriteBytes(writer, buffer.Bytes()) 114 } 115 116 type Response struct { 117 ReplyCode byte 118 Destination M.Socksaddr 119 } 120 121 func ReadResponse(reader io.Reader) (response Response, err error) { 122 version, err := rw.ReadByte(reader) 123 if err != nil { 124 return 125 } 126 if version != 0 { 127 err = E.New("excepted socks4 response version 0, got ", version) 128 return 129 } 130 response.ReplyCode, err = rw.ReadByte(reader) 131 if err != nil { 132 return 133 } 134 err = binary.Read(reader, binary.BigEndian, &response.Destination.Port) 135 if err != nil { 136 return 137 } 138 var dstIP [4]byte 139 _, err = io.ReadFull(reader, dstIP[:]) 140 if err != nil { 141 return 142 } 143 response.Destination.Addr = netip.AddrFrom4(dstIP) 144 return 145 } 146 147 func WriteResponse(writer io.Writer, response Response) error { 148 _buffer := buf.StackNewSize(8) 149 defer common.KeepAlive(_buffer) 150 buffer := common.Dup(_buffer) 151 defer buffer.Release() 152 common.Must( 153 buffer.WriteByte(0), 154 buffer.WriteByte(response.ReplyCode), 155 binary.Write(buffer, binary.BigEndian, response.Destination.Port), 156 common.Error(buffer.Write(response.Destination.Addr.AsSlice())), 157 ) 158 return rw.WriteBytes(writer, buffer.Bytes()) 159 } 160 161 func readString(reader io.Reader) (string, error) { 162 buffer := bytes.Buffer{} 163 for { 164 b, err := rw.ReadByte(reader) 165 if err != nil { 166 return "", err 167 } 168 if b == 0 { 169 break 170 } 171 buffer.WriteByte(b) 172 } 173 return buffer.String(), nil 174 }