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