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