github.com/xtls/xray-core@v1.8.12-0.20240518155711-3168d27b0bdb/transport/internet/reality/reality.go (about) 1 package reality 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/aes" 7 "crypto/cipher" 8 "crypto/ecdh" 9 "crypto/ed25519" 10 "crypto/hmac" 11 "crypto/rand" 12 "crypto/sha256" 13 "crypto/sha512" 14 gotls "crypto/tls" 15 "crypto/x509" 16 "encoding/binary" 17 "fmt" 18 "io" 19 "math/big" 20 "net/http" 21 "reflect" 22 "regexp" 23 "strings" 24 "sync" 25 "time" 26 "unsafe" 27 28 utls "github.com/refraction-networking/utls" 29 "github.com/xtls/reality" 30 "github.com/xtls/xray-core/common/errors" 31 "github.com/xtls/xray-core/common/net" 32 "github.com/xtls/xray-core/common/session" 33 "github.com/xtls/xray-core/core" 34 "github.com/xtls/xray-core/transport/internet/tls" 35 "golang.org/x/crypto/chacha20poly1305" 36 "golang.org/x/crypto/hkdf" 37 "golang.org/x/net/http2" 38 ) 39 40 //go:generate go run github.com/xtls/xray-core/common/errors/errorgen 41 42 //go:linkname aesgcmPreferred github.com/refraction-networking/utls.aesgcmPreferred 43 func aesgcmPreferred(ciphers []uint16) bool 44 45 type Conn struct { 46 *reality.Conn 47 } 48 49 func (c *Conn) HandshakeAddress() net.Address { 50 if err := c.Handshake(); err != nil { 51 return nil 52 } 53 state := c.ConnectionState() 54 if state.ServerName == "" { 55 return nil 56 } 57 return net.ParseAddress(state.ServerName) 58 } 59 60 func Server(c net.Conn, config *reality.Config) (net.Conn, error) { 61 realityConn, err := reality.Server(context.Background(), c, config) 62 return &Conn{Conn: realityConn}, err 63 } 64 65 type UConn struct { 66 *utls.UConn 67 ServerName string 68 AuthKey []byte 69 Verified bool 70 } 71 72 func (c *UConn) HandshakeAddress() net.Address { 73 if err := c.Handshake(); err != nil { 74 return nil 75 } 76 state := c.ConnectionState() 77 if state.ServerName == "" { 78 return nil 79 } 80 return net.ParseAddress(state.ServerName) 81 } 82 83 func (c *UConn) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { 84 p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates") 85 certs := *(*([]*x509.Certificate))(unsafe.Pointer(uintptr(unsafe.Pointer(c.Conn)) + p.Offset)) 86 if pub, ok := certs[0].PublicKey.(ed25519.PublicKey); ok { 87 h := hmac.New(sha512.New, c.AuthKey) 88 h.Write(pub) 89 if bytes.Equal(h.Sum(nil), certs[0].Signature) { 90 c.Verified = true 91 return nil 92 } 93 } 94 opts := x509.VerifyOptions{ 95 DNSName: c.ServerName, 96 Intermediates: x509.NewCertPool(), 97 } 98 for _, cert := range certs[1:] { 99 opts.Intermediates.AddCert(cert) 100 } 101 if _, err := certs[0].Verify(opts); err != nil { 102 return err 103 } 104 return nil 105 } 106 107 func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destination) (net.Conn, error) { 108 localAddr := c.LocalAddr().String() 109 uConn := &UConn{} 110 utlsConfig := &utls.Config{ 111 VerifyPeerCertificate: uConn.VerifyPeerCertificate, 112 ServerName: config.ServerName, 113 InsecureSkipVerify: true, 114 SessionTicketsDisabled: true, 115 KeyLogWriter: KeyLogWriterFromConfig(config), 116 } 117 if utlsConfig.ServerName == "" { 118 utlsConfig.ServerName = dest.Address.String() 119 } 120 uConn.ServerName = utlsConfig.ServerName 121 fingerprint := tls.GetFingerprint(config.Fingerprint) 122 if fingerprint == nil { 123 return nil, newError("REALITY: failed to get fingerprint").AtError() 124 } 125 uConn.UConn = utls.UClient(c, utlsConfig, *fingerprint) 126 { 127 uConn.BuildHandshakeState() 128 hello := uConn.HandshakeState.Hello 129 hello.SessionId = make([]byte, 32) 130 copy(hello.Raw[39:], hello.SessionId) // the fixed location of `Session ID` 131 hello.SessionId[0] = core.Version_x 132 hello.SessionId[1] = core.Version_y 133 hello.SessionId[2] = core.Version_z 134 hello.SessionId[3] = 0 // reserved 135 binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix())) 136 copy(hello.SessionId[8:], config.ShortId) 137 if config.Show { 138 newError(fmt.Sprintf("REALITY localAddr: %v\thello.SessionId[:16]: %v\n", localAddr, hello.SessionId[:16])).WriteToLog(session.ExportIDToError(ctx)) 139 } 140 publicKey, err := ecdh.X25519().NewPublicKey(config.PublicKey) 141 if err != nil { 142 return nil, errors.New("REALITY: publicKey == nil") 143 } 144 uConn.AuthKey, _ = uConn.HandshakeState.State13.EcdheKey.ECDH(publicKey) 145 if uConn.AuthKey == nil { 146 return nil, errors.New("REALITY: SharedKey == nil") 147 } 148 if _, err := hkdf.New(sha256.New, uConn.AuthKey, hello.Random[:20], []byte("REALITY")).Read(uConn.AuthKey); err != nil { 149 return nil, err 150 } 151 var aead cipher.AEAD 152 if aesgcmPreferred(hello.CipherSuites) { 153 block, _ := aes.NewCipher(uConn.AuthKey) 154 aead, _ = cipher.NewGCM(block) 155 } else { 156 aead, _ = chacha20poly1305.New(uConn.AuthKey) 157 } 158 if config.Show { 159 newError(fmt.Sprintf("REALITY localAddr: %v\tuConn.AuthKey[:16]: %v\tAEAD: %T\n", localAddr, uConn.AuthKey[:16], aead)).WriteToLog(session.ExportIDToError(ctx)) 160 } 161 aead.Seal(hello.SessionId[:0], hello.Random[20:], hello.SessionId[:16], hello.Raw) 162 copy(hello.Raw[39:], hello.SessionId) 163 } 164 if err := uConn.HandshakeContext(ctx); err != nil { 165 return nil, err 166 } 167 if config.Show { 168 newError(fmt.Sprintf("REALITY localAddr: %v\tuConn.Verified: %v\n", localAddr, uConn.Verified)).WriteToLog(session.ExportIDToError(ctx)) 169 } 170 if !uConn.Verified { 171 go func() { 172 client := &http.Client{ 173 Transport: &http2.Transport{ 174 DialTLSContext: func(ctx context.Context, network, addr string, cfg *gotls.Config) (net.Conn, error) { 175 newError(fmt.Sprintf("REALITY localAddr: %v\tDialTLSContext\n", localAddr)).WriteToLog(session.ExportIDToError(ctx)) 176 return uConn, nil 177 }, 178 }, 179 } 180 prefix := []byte("https://" + uConn.ServerName) 181 maps.Lock() 182 if maps.maps == nil { 183 maps.maps = make(map[string]map[string]bool) 184 } 185 paths := maps.maps[uConn.ServerName] 186 if paths == nil { 187 paths = make(map[string]bool) 188 paths[config.SpiderX] = true 189 maps.maps[uConn.ServerName] = paths 190 } 191 firstURL := string(prefix) + getPathLocked(paths) 192 maps.Unlock() 193 get := func(first bool) { 194 var ( 195 req *http.Request 196 resp *http.Response 197 err error 198 body []byte 199 ) 200 if first { 201 req, _ = http.NewRequest("GET", firstURL, nil) 202 } else { 203 maps.Lock() 204 req, _ = http.NewRequest("GET", string(prefix)+getPathLocked(paths), nil) 205 maps.Unlock() 206 } 207 if req == nil { 208 return 209 } 210 req.Header.Set("User-Agent", fingerprint.Client) // TODO: User-Agent map 211 if first && config.Show { 212 newError(fmt.Sprintf("REALITY localAddr: %v\treq.UserAgent(): %v\n", localAddr, req.UserAgent())).WriteToLog(session.ExportIDToError(ctx)) 213 } 214 times := 1 215 if !first { 216 times = int(randBetween(config.SpiderY[4], config.SpiderY[5])) 217 } 218 for j := 0; j < times; j++ { 219 if !first && j == 0 { 220 req.Header.Set("Referer", firstURL) 221 } 222 req.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", int(randBetween(config.SpiderY[0], config.SpiderY[1])))}) 223 if resp, err = client.Do(req); err != nil { 224 break 225 } 226 defer resp.Body.Close() 227 req.Header.Set("Referer", req.URL.String()) 228 if body, err = io.ReadAll(resp.Body); err != nil { 229 break 230 } 231 maps.Lock() 232 for _, m := range href.FindAllSubmatch(body, -1) { 233 m[1] = bytes.TrimPrefix(m[1], prefix) 234 if !bytes.Contains(m[1], dot) { 235 paths[string(m[1])] = true 236 } 237 } 238 req.URL.Path = getPathLocked(paths) 239 if config.Show { 240 newError(fmt.Sprintf("REALITY localAddr: %v\treq.Referer(): %v\n", localAddr, req.Referer())).WriteToLog(session.ExportIDToError(ctx)) 241 newError(fmt.Sprintf("REALITY localAddr: %v\tlen(body): %v\n", localAddr, len(body))).WriteToLog(session.ExportIDToError(ctx)) 242 newError(fmt.Sprintf("REALITY localAddr: %v\tlen(paths): %v\n", localAddr, len(paths))).WriteToLog(session.ExportIDToError(ctx)) 243 } 244 maps.Unlock() 245 if !first { 246 time.Sleep(time.Duration(randBetween(config.SpiderY[6], config.SpiderY[7])) * time.Millisecond) // interval 247 } 248 } 249 } 250 get(true) 251 concurrency := int(randBetween(config.SpiderY[2], config.SpiderY[3])) 252 for i := 0; i < concurrency; i++ { 253 go get(false) 254 } 255 // Do not close the connection 256 }() 257 time.Sleep(time.Duration(randBetween(config.SpiderY[8], config.SpiderY[9])) * time.Millisecond) // return 258 return nil, errors.New("REALITY: processed invalid connection") 259 } 260 return uConn, nil 261 } 262 263 var ( 264 href = regexp.MustCompile(`href="([/h].*?)"`) 265 dot = []byte(".") 266 ) 267 268 var maps struct { 269 sync.Mutex 270 maps map[string]map[string]bool 271 } 272 273 func getPathLocked(paths map[string]bool) string { 274 stopAt := int(randBetween(0, int64(len(paths)-1))) 275 i := 0 276 for s := range paths { 277 if i == stopAt { 278 return s 279 } 280 i++ 281 } 282 return "/" 283 } 284 285 func randBetween(left int64, right int64) int64 { 286 if left == right { 287 return left 288 } 289 bigInt, _ := rand.Int(rand.Reader, big.NewInt(right-left)) 290 return left + bigInt.Int64() 291 }