github.com/igoogolx/clash@v1.19.8/transport/vmess/conn.go (about) 1 package vmess 2 3 import ( 4 "crypto/aes" 5 "crypto/cipher" 6 "crypto/hmac" 7 "crypto/md5" 8 "crypto/rand" 9 "crypto/sha256" 10 "encoding/binary" 11 "errors" 12 "hash/fnv" 13 "io" 14 mathRand "math/rand" 15 "net" 16 "time" 17 18 "github.com/Dreamacro/protobytes" 19 "golang.org/x/crypto/chacha20poly1305" 20 ) 21 22 // Conn wrapper a net.Conn with vmess protocol 23 type Conn struct { 24 net.Conn 25 reader io.Reader 26 writer io.Writer 27 dst *DstAddr 28 id *ID 29 reqBodyIV []byte 30 reqBodyKey []byte 31 respBodyIV []byte 32 respBodyKey []byte 33 respV byte 34 security byte 35 option byte 36 isAead bool 37 isVless bool 38 39 received bool 40 } 41 42 func (vc *Conn) Write(b []byte) (int, error) { 43 return vc.writer.Write(b) 44 } 45 46 func (vc *Conn) Read(b []byte) (int, error) { 47 if vc.received { 48 return vc.reader.Read(b) 49 } 50 51 if err := vc.recvResponse(); err != nil { 52 return 0, err 53 } 54 vc.received = true 55 return vc.reader.Read(b) 56 } 57 58 func (vc *Conn) sendRequest() error { 59 if vc.isVless { 60 buf := protobytes.BytesWriter{} 61 buf.PutUint8(0) // Protocol Version 62 buf.PutSlice(vc.id.UUID.Bytes()) // UUID 63 buf.PutUint8(0) // Addons Length 64 // buf.PutString("") // Addons Data 65 66 // Command 67 if vc.dst.UDP { 68 buf.PutUint8(CommandUDP) 69 } else { 70 buf.PutUint8(CommandTCP) 71 } 72 73 // Port AddrType Addr 74 buf.PutUint16be(uint16(vc.dst.Port)) 75 buf.PutUint8(vc.dst.AddrType) 76 buf.PutSlice(vc.dst.Addr) 77 78 _, err := vc.Conn.Write(buf.Bytes()) 79 return err 80 } 81 82 timestamp := time.Now() 83 84 mbuf := protobytes.BytesWriter{} 85 86 if !vc.isAead { 87 h := hmac.New(md5.New, vc.id.UUID.Bytes()) 88 binary.Write(h, binary.BigEndian, uint64(timestamp.Unix())) 89 mbuf.PutSlice(h.Sum(nil)) 90 } 91 92 buf := protobytes.BytesWriter{} 93 94 // Ver IV Key V Opt 95 buf.PutUint8(Version) 96 buf.PutSlice(vc.reqBodyIV[:]) 97 buf.PutSlice(vc.reqBodyKey[:]) 98 buf.PutUint8(vc.respV) 99 buf.PutUint8(vc.option) 100 101 p := mathRand.Intn(16) 102 // P Sec Reserve Cmd 103 buf.PutUint8(byte(p<<4) | vc.security) 104 buf.PutUint8(0) 105 if vc.dst.UDP { 106 buf.PutUint8(CommandUDP) 107 } else { 108 buf.PutUint8(CommandTCP) 109 } 110 111 // Port AddrType Addr 112 buf.PutUint16be(uint16(vc.dst.Port)) 113 buf.PutUint8(vc.dst.AddrType) 114 buf.PutSlice(vc.dst.Addr) 115 116 // padding 117 if p > 0 { 118 buf.ReadFull(rand.Reader, p) 119 } 120 121 fnv1a := fnv.New32a() 122 fnv1a.Write(buf.Bytes()) 123 buf.PutSlice(fnv1a.Sum(nil)) 124 125 if !vc.isAead { 126 block, err := aes.NewCipher(vc.id.CmdKey) 127 if err != nil { 128 return err 129 } 130 131 stream := cipher.NewCFBEncrypter(block, hashTimestamp(timestamp)) 132 stream.XORKeyStream(buf.Bytes(), buf.Bytes()) 133 mbuf.PutSlice(buf.Bytes()) 134 _, err = vc.Conn.Write(mbuf.Bytes()) 135 return err 136 } 137 138 var fixedLengthCmdKey [16]byte 139 copy(fixedLengthCmdKey[:], vc.id.CmdKey) 140 vmessout := sealVMessAEADHeader(fixedLengthCmdKey, buf.Bytes(), timestamp) 141 _, err := vc.Conn.Write(vmessout) 142 return err 143 } 144 145 func (vc *Conn) recvResponse() error { 146 if vc.isVless { 147 var buffer [2]byte 148 if _, err := io.ReadFull(vc.Conn, buffer[:]); err != nil { 149 return err 150 } 151 152 if buffer[0] != 0 { 153 return errors.New("unexpected response version") 154 } 155 156 length := int64(buffer[1]) 157 if length != 0 { // addon data length > 0 158 io.CopyN(io.Discard, vc.Conn, length) // just discard 159 } 160 161 return nil 162 } 163 164 var buf []byte 165 if !vc.isAead { 166 block, err := aes.NewCipher(vc.respBodyKey[:]) 167 if err != nil { 168 return err 169 } 170 171 stream := cipher.NewCFBDecrypter(block, vc.respBodyIV[:]) 172 buf = make([]byte, 4) 173 _, err = io.ReadFull(vc.Conn, buf) 174 if err != nil { 175 return err 176 } 177 stream.XORKeyStream(buf, buf) 178 } else { 179 aeadResponseHeaderLengthEncryptionKey := kdf(vc.respBodyKey[:], kdfSaltConstAEADRespHeaderLenKey)[:16] 180 aeadResponseHeaderLengthEncryptionIV := kdf(vc.respBodyIV[:], kdfSaltConstAEADRespHeaderLenIV)[:12] 181 182 aeadResponseHeaderLengthEncryptionKeyAESBlock, _ := aes.NewCipher(aeadResponseHeaderLengthEncryptionKey) 183 aeadResponseHeaderLengthEncryptionAEAD, _ := cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock) 184 185 aeadEncryptedResponseHeaderLength := make([]byte, 18) 186 if _, err := io.ReadFull(vc.Conn, aeadEncryptedResponseHeaderLength); err != nil { 187 return err 188 } 189 190 decryptedResponseHeaderLengthBinaryBuffer, err := aeadResponseHeaderLengthEncryptionAEAD.Open(nil, aeadResponseHeaderLengthEncryptionIV, aeadEncryptedResponseHeaderLength[:], nil) 191 if err != nil { 192 return err 193 } 194 195 decryptedResponseHeaderLength := binary.BigEndian.Uint16(decryptedResponseHeaderLengthBinaryBuffer) 196 aeadResponseHeaderPayloadEncryptionKey := kdf(vc.respBodyKey[:], kdfSaltConstAEADRespHeaderPayloadKey)[:16] 197 aeadResponseHeaderPayloadEncryptionIV := kdf(vc.respBodyIV[:], kdfSaltConstAEADRespHeaderPayloadIV)[:12] 198 aeadResponseHeaderPayloadEncryptionKeyAESBlock, _ := aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey) 199 aeadResponseHeaderPayloadEncryptionAEAD, _ := cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock) 200 201 encryptedResponseHeaderBuffer := make([]byte, decryptedResponseHeaderLength+16) 202 if _, err := io.ReadFull(vc.Conn, encryptedResponseHeaderBuffer); err != nil { 203 return err 204 } 205 206 buf, err = aeadResponseHeaderPayloadEncryptionAEAD.Open(nil, aeadResponseHeaderPayloadEncryptionIV, encryptedResponseHeaderBuffer, nil) 207 if err != nil { 208 return err 209 } 210 211 if len(buf) < 4 { 212 return errors.New("unexpected buffer length") 213 } 214 } 215 216 if buf[0] != vc.respV { 217 return errors.New("unexpected response header") 218 } 219 220 if buf[2] != 0 { 221 return errors.New("dynamic port is not supported now") 222 } 223 224 return nil 225 } 226 227 func hashTimestamp(t time.Time) []byte { 228 md5hash := md5.New() 229 ts := make([]byte, 8) 230 binary.BigEndian.PutUint64(ts, uint64(t.Unix())) 231 md5hash.Write(ts) 232 md5hash.Write(ts) 233 md5hash.Write(ts) 234 md5hash.Write(ts) 235 return md5hash.Sum(nil) 236 } 237 238 // newConn return a Conn instance 239 func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool, isVless bool) (*Conn, error) { 240 var ( 241 reqBodyKey []byte 242 reqBodyIV []byte 243 respBodyKey []byte 244 respBodyIV []byte 245 respV byte 246 option byte 247 ) 248 249 if !isVless { 250 randBytes := make([]byte, 33) 251 rand.Read(randBytes) 252 reqBodyIV = make([]byte, 16) 253 reqBodyKey = make([]byte, 16) 254 copy(reqBodyIV[:], randBytes[:16]) 255 copy(reqBodyKey[:], randBytes[16:32]) 256 respV = randBytes[32] 257 option = OptionChunkStream 258 259 if isAead { 260 bodyKey := sha256.Sum256(reqBodyKey) 261 bodyIV := sha256.Sum256(reqBodyIV) 262 respBodyKey = bodyKey[:16] 263 respBodyIV = bodyIV[:16] 264 } else { 265 bodyKey := md5.Sum(reqBodyKey) 266 bodyIV := md5.Sum(reqBodyIV) 267 respBodyKey = bodyKey[:] 268 respBodyIV = bodyIV[:] 269 } 270 } 271 272 var writer io.Writer 273 var reader io.Reader 274 switch security { 275 case SecurityZero: 276 security = SecurityNone 277 if !dst.UDP { 278 reader = conn 279 writer = conn 280 option = 0 281 } else { 282 reader = newChunkReader(conn) 283 writer = newChunkWriter(conn) 284 } 285 case SecurityNone: 286 reader = newChunkReader(conn) 287 writer = newChunkWriter(conn) 288 case SecurityAES128GCM: 289 block, _ := aes.NewCipher(reqBodyKey[:]) 290 aead, _ := cipher.NewGCM(block) 291 writer = newAEADWriter(conn, aead, reqBodyIV[:]) 292 293 block, _ = aes.NewCipher(respBodyKey[:]) 294 aead, _ = cipher.NewGCM(block) 295 reader = newAEADReader(conn, aead, respBodyIV[:]) 296 case SecurityCHACHA20POLY1305: 297 key := make([]byte, 32) 298 t := md5.Sum(reqBodyKey[:]) 299 copy(key, t[:]) 300 t = md5.Sum(key[:16]) 301 copy(key[16:], t[:]) 302 aead, _ := chacha20poly1305.New(key) 303 writer = newAEADWriter(conn, aead, reqBodyIV[:]) 304 305 t = md5.Sum(respBodyKey[:]) 306 copy(key, t[:]) 307 t = md5.Sum(key[:16]) 308 copy(key[16:], t[:]) 309 aead, _ = chacha20poly1305.New(key) 310 reader = newAEADReader(conn, aead, respBodyIV[:]) 311 } 312 313 c := &Conn{ 314 Conn: conn, 315 id: id, 316 dst: dst, 317 reqBodyIV: reqBodyIV, 318 reqBodyKey: reqBodyKey, 319 respV: respV, 320 respBodyIV: respBodyIV[:], 321 respBodyKey: respBodyKey[:], 322 reader: reader, 323 writer: writer, 324 security: security, 325 option: option, 326 isAead: isAead, 327 isVless: isVless, 328 } 329 if err := c.sendRequest(); err != nil { 330 return nil, err 331 } 332 return c, nil 333 }