2023-02-15 19:07:12 +03:00
package reality
import (
"bytes"
"context"
"crypto/aes"
"crypto/cipher"
2023-08-26 10:45:24 +03:00
"crypto/ecdh"
2023-02-15 19:07:12 +03:00
"crypto/ed25519"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"crypto/sha512"
gotls "crypto/tls"
"crypto/x509"
"encoding/binary"
"fmt"
"io"
"math/big"
"net/http"
"reflect"
"regexp"
"strings"
"sync"
"time"
"unsafe"
utls "github.com/refraction-networking/utls"
"github.com/xtls/reality"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/transport/internet/tls"
2023-06-15 19:22:53 +03:00
"golang.org/x/crypto/chacha20poly1305"
2023-02-15 19:07:12 +03:00
"golang.org/x/crypto/hkdf"
"golang.org/x/net/http2"
)
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
2023-06-15 19:22:53 +03:00
//go:linkname aesgcmPreferred github.com/refraction-networking/utls.aesgcmPreferred
func aesgcmPreferred ( ciphers [ ] uint16 ) bool
2023-02-15 19:07:12 +03:00
type Conn struct {
* reality . Conn
}
func ( c * Conn ) HandshakeAddress ( ) net . Address {
if err := c . Handshake ( ) ; err != nil {
return nil
}
state := c . ConnectionState ( )
if state . ServerName == "" {
return nil
}
return net . ParseAddress ( state . ServerName )
}
func Server ( c net . Conn , config * reality . Config ) ( net . Conn , error ) {
2023-02-26 14:26:57 +03:00
realityConn , err := reality . Server ( context . Background ( ) , c , config )
2023-02-15 19:07:12 +03:00
return & Conn { Conn : realityConn } , err
}
type UConn struct {
* utls . UConn
ServerName string
AuthKey [ ] byte
Verified bool
}
func ( c * UConn ) HandshakeAddress ( ) net . Address {
if err := c . Handshake ( ) ; err != nil {
return nil
}
state := c . ConnectionState ( )
if state . ServerName == "" {
return nil
}
return net . ParseAddress ( state . ServerName )
}
func ( c * UConn ) VerifyPeerCertificate ( rawCerts [ ] [ ] byte , verifiedChains [ ] [ ] * x509 . Certificate ) error {
p , _ := reflect . TypeOf ( c . Conn ) . Elem ( ) . FieldByName ( "peerCertificates" )
certs := * ( * ( [ ] * x509 . Certificate ) ) ( unsafe . Pointer ( uintptr ( unsafe . Pointer ( c . Conn ) ) + p . Offset ) )
if pub , ok := certs [ 0 ] . PublicKey . ( ed25519 . PublicKey ) ; ok {
h := hmac . New ( sha512 . New , c . AuthKey )
h . Write ( pub )
if bytes . Equal ( h . Sum ( nil ) , certs [ 0 ] . Signature ) {
c . Verified = true
return nil
}
}
opts := x509 . VerifyOptions {
DNSName : c . ServerName ,
Intermediates : x509 . NewCertPool ( ) ,
}
for _ , cert := range certs [ 1 : ] {
opts . Intermediates . AddCert ( cert )
}
if _ , err := certs [ 0 ] . Verify ( opts ) ; err != nil {
return err
}
return nil
}
func UClient ( c net . Conn , config * Config , ctx context . Context , dest net . Destination ) ( net . Conn , error ) {
localAddr := c . LocalAddr ( ) . String ( )
uConn := & UConn { }
utlsConfig := & utls . Config {
VerifyPeerCertificate : uConn . VerifyPeerCertificate ,
ServerName : config . ServerName ,
InsecureSkipVerify : true ,
SessionTicketsDisabled : true ,
2024-01-11 09:36:13 +03:00
KeyLogWriter : KeyLogWriterFromConfig ( config ) ,
2023-02-15 19:07:12 +03:00
}
2023-03-26 13:57:20 +03:00
if utlsConfig . ServerName == "" {
utlsConfig . ServerName = dest . Address . String ( )
2023-02-15 19:07:12 +03:00
}
uConn . ServerName = utlsConfig . ServerName
fingerprint := tls . GetFingerprint ( config . Fingerprint )
if fingerprint == nil {
2024-06-29 21:32:57 +03:00
return nil , errors . New ( "REALITY: failed to get fingerprint" ) . AtError ( )
2023-02-15 19:07:12 +03:00
}
uConn . UConn = utls . UClient ( c , utlsConfig , * fingerprint )
{
uConn . BuildHandshakeState ( )
hello := uConn . HandshakeState . Hello
hello . SessionId = make ( [ ] byte , 32 )
2023-06-15 19:37:46 +03:00
copy ( hello . Raw [ 39 : ] , hello . SessionId ) // the fixed location of `Session ID`
2023-02-15 19:07:12 +03:00
hello . SessionId [ 0 ] = core . Version_x
hello . SessionId [ 1 ] = core . Version_y
hello . SessionId [ 2 ] = core . Version_z
2023-06-15 19:37:46 +03:00
hello . SessionId [ 3 ] = 0 // reserved
2023-04-01 01:39:57 +03:00
binary . BigEndian . PutUint32 ( hello . SessionId [ 4 : ] , uint32 ( time . Now ( ) . Unix ( ) ) )
2023-02-15 19:07:12 +03:00
copy ( hello . SessionId [ 8 : ] , config . ShortId )
if config . Show {
2024-06-29 21:32:57 +03:00
errors . LogInfo ( ctx , fmt . Sprintf ( "REALITY localAddr: %v\thello.SessionId[:16]: %v\n" , localAddr , hello . SessionId [ : 16 ] ) )
2023-02-15 19:07:12 +03:00
}
2023-12-23 10:38:43 +03:00
publicKey , err := ecdh . X25519 ( ) . NewPublicKey ( config . PublicKey )
if err != nil {
return nil , errors . New ( "REALITY: publicKey == nil" )
}
2024-09-09 18:37:39 +03:00
if uConn . HandshakeState . State13 . EcdheKey == nil {
return nil , errors . New ( "Current fingerprint " , uConn . ClientHelloID . Client , uConn . ClientHelloID . Version , " does not support TLS 1.3, REALITY handshake cannot establish." )
}
2023-08-26 10:45:24 +03:00
uConn . AuthKey , _ = uConn . HandshakeState . State13 . EcdheKey . ECDH ( publicKey )
2023-02-15 19:07:12 +03:00
if uConn . AuthKey == nil {
return nil , errors . New ( "REALITY: SharedKey == nil" )
}
if _ , err := hkdf . New ( sha256 . New , uConn . AuthKey , hello . Random [ : 20 ] , [ ] byte ( "REALITY" ) ) . Read ( uConn . AuthKey ) ; err != nil {
return nil , err
}
2023-06-15 19:22:53 +03:00
var aead cipher . AEAD
if aesgcmPreferred ( hello . CipherSuites ) {
block , _ := aes . NewCipher ( uConn . AuthKey )
aead , _ = cipher . NewGCM ( block )
} else {
aead , _ = chacha20poly1305 . New ( uConn . AuthKey )
}
2023-03-20 18:39:56 +03:00
if config . Show {
2024-06-29 21:32:57 +03:00
errors . LogInfo ( ctx , fmt . Sprintf ( "REALITY localAddr: %v\tuConn.AuthKey[:16]: %v\tAEAD: %T\n" , localAddr , uConn . AuthKey [ : 16 ] , aead ) )
2023-03-20 18:39:56 +03:00
}
2023-02-15 19:07:12 +03:00
aead . Seal ( hello . SessionId [ : 0 ] , hello . Random [ 20 : ] , hello . SessionId [ : 16 ] , hello . Raw )
copy ( hello . Raw [ 39 : ] , hello . SessionId )
}
2023-08-27 08:55:58 +03:00
if err := uConn . HandshakeContext ( ctx ) ; err != nil {
2023-02-15 19:07:12 +03:00
return nil , err
}
if config . Show {
2024-06-29 21:32:57 +03:00
errors . LogInfo ( ctx , fmt . Sprintf ( "REALITY localAddr: %v\tuConn.Verified: %v\n" , localAddr , uConn . Verified ) )
2023-02-15 19:07:12 +03:00
}
if ! uConn . Verified {
go func ( ) {
client := & http . Client {
Transport : & http2 . Transport {
DialTLSContext : func ( ctx context . Context , network , addr string , cfg * gotls . Config ) ( net . Conn , error ) {
2024-06-29 21:32:57 +03:00
errors . LogInfo ( ctx , fmt . Sprintf ( "REALITY localAddr: %v\tDialTLSContext\n" , localAddr ) )
2023-02-15 19:07:12 +03:00
return uConn , nil
} ,
} ,
}
prefix := [ ] byte ( "https://" + uConn . ServerName )
maps . Lock ( )
if maps . maps == nil {
maps . maps = make ( map [ string ] map [ string ] bool )
}
paths := maps . maps [ uConn . ServerName ]
if paths == nil {
paths = make ( map [ string ] bool )
paths [ config . SpiderX ] = true
maps . maps [ uConn . ServerName ] = paths
}
firstURL := string ( prefix ) + getPathLocked ( paths )
maps . Unlock ( )
get := func ( first bool ) {
var (
req * http . Request
resp * http . Response
err error
body [ ] byte
)
if first {
req , _ = http . NewRequest ( "GET" , firstURL , nil )
} else {
maps . Lock ( )
req , _ = http . NewRequest ( "GET" , string ( prefix ) + getPathLocked ( paths ) , nil )
maps . Unlock ( )
}
2024-04-21 22:43:27 +03:00
if req == nil {
return
}
2023-02-15 19:07:12 +03:00
req . Header . Set ( "User-Agent" , fingerprint . Client ) // TODO: User-Agent map
if first && config . Show {
2024-06-29 21:32:57 +03:00
errors . LogInfo ( ctx , fmt . Sprintf ( "REALITY localAddr: %v\treq.UserAgent(): %v\n" , localAddr , req . UserAgent ( ) ) )
2023-02-15 19:07:12 +03:00
}
times := 1
if ! first {
times = int ( randBetween ( config . SpiderY [ 4 ] , config . SpiderY [ 5 ] ) )
}
for j := 0 ; j < times ; j ++ {
if ! first && j == 0 {
req . Header . Set ( "Referer" , firstURL )
}
req . AddCookie ( & http . Cookie { Name : "padding" , Value : strings . Repeat ( "0" , int ( randBetween ( config . SpiderY [ 0 ] , config . SpiderY [ 1 ] ) ) ) } )
if resp , err = client . Do ( req ) ; err != nil {
break
}
2024-04-17 12:47:47 +03:00
defer resp . Body . Close ( )
2023-02-15 19:07:12 +03:00
req . Header . Set ( "Referer" , req . URL . String ( ) )
if body , err = io . ReadAll ( resp . Body ) ; err != nil {
break
}
maps . Lock ( )
for _ , m := range href . FindAllSubmatch ( body , - 1 ) {
m [ 1 ] = bytes . TrimPrefix ( m [ 1 ] , prefix )
if ! bytes . Contains ( m [ 1 ] , dot ) {
paths [ string ( m [ 1 ] ) ] = true
}
}
req . URL . Path = getPathLocked ( paths )
if config . Show {
2024-06-29 21:32:57 +03:00
errors . LogInfo ( ctx , fmt . Sprintf ( "REALITY localAddr: %v\treq.Referer(): %v\n" , localAddr , req . Referer ( ) ) )
errors . LogInfo ( ctx , fmt . Sprintf ( "REALITY localAddr: %v\tlen(body): %v\n" , localAddr , len ( body ) ) )
errors . LogInfo ( ctx , fmt . Sprintf ( "REALITY localAddr: %v\tlen(paths): %v\n" , localAddr , len ( paths ) ) )
2023-02-15 19:07:12 +03:00
}
maps . Unlock ( )
if ! first {
time . Sleep ( time . Duration ( randBetween ( config . SpiderY [ 6 ] , config . SpiderY [ 7 ] ) ) * time . Millisecond ) // interval
}
}
}
get ( true )
concurrency := int ( randBetween ( config . SpiderY [ 2 ] , config . SpiderY [ 3 ] ) )
for i := 0 ; i < concurrency ; i ++ {
go get ( false )
}
// Do not close the connection
} ( )
time . Sleep ( time . Duration ( randBetween ( config . SpiderY [ 8 ] , config . SpiderY [ 9 ] ) ) * time . Millisecond ) // return
return nil , errors . New ( "REALITY: processed invalid connection" )
}
return uConn , nil
}
2023-03-17 08:17:01 +03:00
var (
href = regexp . MustCompile ( ` href="([/h].*?)" ` )
dot = [ ] byte ( "." )
)
2023-02-15 19:07:12 +03:00
var maps struct {
sync . Mutex
maps map [ string ] map [ string ] bool
}
func getPathLocked ( paths map [ string ] bool ) string {
stopAt := int ( randBetween ( 0 , int64 ( len ( paths ) - 1 ) ) )
i := 0
for s := range paths {
if i == stopAt {
return s
}
i ++
}
return "/"
}
func randBetween ( left int64 , right int64 ) int64 {
if left == right {
return left
}
bigInt , _ := rand . Int ( rand . Reader , big . NewInt ( right - left ) )
return left + bigInt . Int64 ( )
}