github.com/xmplusdev/xray-core@v1.8.10/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/xmplusdev/xray-core/common/errors" 31 "github.com/xmplusdev/xray-core/common/net" 32 "github.com/xmplusdev/xray-core/common/session" 33 "github.com/xmplusdev/xray-core/core" 34 "github.com/xmplusdev/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/xmplusdev/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 } else if strings.ToLower(utlsConfig.ServerName) == "nosni" { // If ServerName is set to "nosni", we set it empty. 120 utlsConfig.ServerName = "" 121 } 122 uConn.ServerName = utlsConfig.ServerName 123 fingerprint := tls.GetFingerprint(config.Fingerprint) 124 if fingerprint == nil { 125 return nil, newError("REALITY: failed to get fingerprint").AtError() 126 } 127 uConn.UConn = utls.UClient(c, utlsConfig, *fingerprint) 128 { 129 uConn.BuildHandshakeState() 130 hello := uConn.HandshakeState.Hello 131 hello.SessionId = make([]byte, 32) 132 copy(hello.Raw[39:], hello.SessionId) // the fixed location of `Session ID` 133 hello.SessionId[0] = core.Version_x 134 hello.SessionId[1] = core.Version_y 135 hello.SessionId[2] = core.Version_z 136 hello.SessionId[3] = 0 // reserved 137 binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix())) 138 copy(hello.SessionId[8:], config.ShortId) 139 if config.Show { 140 newError(fmt.Sprintf("REALITY localAddr: %v\thello.SessionId[:16]: %v\n", localAddr, hello.SessionId[:16])).WriteToLog(session.ExportIDToError(ctx)) 141 } 142 publicKey, err := ecdh.X25519().NewPublicKey(config.PublicKey) 143 if err != nil { 144 return nil, errors.New("REALITY: publicKey == nil") 145 } 146 uConn.AuthKey, _ = uConn.HandshakeState.State13.EcdheKey.ECDH(publicKey) 147 if uConn.AuthKey == nil { 148 return nil, errors.New("REALITY: SharedKey == nil") 149 } 150 if _, err := hkdf.New(sha256.New, uConn.AuthKey, hello.Random[:20], []byte("REALITY")).Read(uConn.AuthKey); err != nil { 151 return nil, err 152 } 153 var aead cipher.AEAD 154 if aesgcmPreferred(hello.CipherSuites) { 155 block, _ := aes.NewCipher(uConn.AuthKey) 156 aead, _ = cipher.NewGCM(block) 157 } else { 158 aead, _ = chacha20poly1305.New(uConn.AuthKey) 159 } 160 if config.Show { 161 newError(fmt.Sprintf("REALITY localAddr: %v\tuConn.AuthKey[:16]: %v\tAEAD: %T\n", localAddr, uConn.AuthKey[:16], aead)).WriteToLog(session.ExportIDToError(ctx)) 162 } 163 aead.Seal(hello.SessionId[:0], hello.Random[20:], hello.SessionId[:16], hello.Raw) 164 copy(hello.Raw[39:], hello.SessionId) 165 } 166 if err := uConn.HandshakeContext(ctx); err != nil { 167 return nil, err 168 } 169 if config.Show { 170 newError(fmt.Sprintf("REALITY localAddr: %v\tuConn.Verified: %v\n", localAddr, uConn.Verified)).WriteToLog(session.ExportIDToError(ctx)) 171 } 172 if !uConn.Verified { 173 go func() { 174 client := &http.Client{ 175 Transport: &http2.Transport{ 176 DialTLSContext: func(ctx context.Context, network, addr string, cfg *gotls.Config) (net.Conn, error) { 177 newError(fmt.Sprintf("REALITY localAddr: %v\tDialTLSContext\n", localAddr)).WriteToLog(session.ExportIDToError(ctx)) 178 return uConn, nil 179 }, 180 }, 181 } 182 prefix := []byte("https://" + uConn.ServerName) 183 maps.Lock() 184 if maps.maps == nil { 185 maps.maps = make(map[string]map[string]bool) 186 } 187 paths := maps.maps[uConn.ServerName] 188 if paths == nil { 189 paths = make(map[string]bool) 190 paths[config.SpiderX] = true 191 maps.maps[uConn.ServerName] = paths 192 } 193 firstURL := string(prefix) + getPathLocked(paths) 194 maps.Unlock() 195 get := func(first bool) { 196 var ( 197 req *http.Request 198 resp *http.Response 199 err error 200 body []byte 201 ) 202 if first { 203 req, _ = http.NewRequest("GET", firstURL, nil) 204 } else { 205 maps.Lock() 206 req, _ = http.NewRequest("GET", string(prefix)+getPathLocked(paths), nil) 207 maps.Unlock() 208 } 209 req.Header.Set("User-Agent", fingerprint.Client) // TODO: User-Agent map 210 if first && config.Show { 211 newError(fmt.Sprintf("REALITY localAddr: %v\treq.UserAgent(): %v\n", localAddr, req.UserAgent())).WriteToLog(session.ExportIDToError(ctx)) 212 } 213 times := 1 214 if !first { 215 times = int(randBetween(config.SpiderY[4], config.SpiderY[5])) 216 } 217 for j := 0; j < times; j++ { 218 if !first && j == 0 { 219 req.Header.Set("Referer", firstURL) 220 } 221 req.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", int(randBetween(config.SpiderY[0], config.SpiderY[1])))}) 222 if resp, err = client.Do(req); err != nil { 223 break 224 } 225 req.Header.Set("Referer", req.URL.String()) 226 if body, err = io.ReadAll(resp.Body); err != nil { 227 break 228 } 229 maps.Lock() 230 for _, m := range href.FindAllSubmatch(body, -1) { 231 m[1] = bytes.TrimPrefix(m[1], prefix) 232 if !bytes.Contains(m[1], dot) { 233 paths[string(m[1])] = true 234 } 235 } 236 req.URL.Path = getPathLocked(paths) 237 if config.Show { 238 newError(fmt.Sprintf("REALITY localAddr: %v\treq.Referer(): %v\n", localAddr, req.Referer())).WriteToLog(session.ExportIDToError(ctx)) 239 newError(fmt.Sprintf("REALITY localAddr: %v\tlen(body): %v\n", localAddr, len(body))).WriteToLog(session.ExportIDToError(ctx)) 240 newError(fmt.Sprintf("REALITY localAddr: %v\tlen(paths): %v\n", localAddr, len(paths))).WriteToLog(session.ExportIDToError(ctx)) 241 } 242 maps.Unlock() 243 if !first { 244 time.Sleep(time.Duration(randBetween(config.SpiderY[6], config.SpiderY[7])) * time.Millisecond) // interval 245 } 246 } 247 } 248 get(true) 249 concurrency := int(randBetween(config.SpiderY[2], config.SpiderY[3])) 250 for i := 0; i < concurrency; i++ { 251 go get(false) 252 } 253 // Do not close the connection 254 }() 255 time.Sleep(time.Duration(randBetween(config.SpiderY[8], config.SpiderY[9])) * time.Millisecond) // return 256 return nil, errors.New("REALITY: processed invalid connection") 257 } 258 return uConn, nil 259 } 260 261 var ( 262 href = regexp.MustCompile(`href="([/h].*?)"`) 263 dot = []byte(".") 264 ) 265 266 var maps struct { 267 sync.Mutex 268 maps map[string]map[string]bool 269 } 270 271 func getPathLocked(paths map[string]bool) string { 272 stopAt := int(randBetween(0, int64(len(paths)-1))) 273 i := 0 274 for s := range paths { 275 if i == stopAt { 276 return s 277 } 278 i++ 279 } 280 return "/" 281 } 282 283 func randBetween(left int64, right int64) int64 { 284 if left == right { 285 return left 286 } 287 bigInt, _ := rand.Int(rand.Reader, big.NewInt(right-left)) 288 return left + bigInt.Int64() 289 }