Browse Source

THE NEXT FUTURE becomes THE REALITY NOW

Thank @yuhan6665 for testing
RPRX 2 năm trước cách đây
mục cha
commit
4d2e2b24d3

+ 8 - 2
core/core.go

@@ -12,13 +12,19 @@ package core
 //go:generate go run github.com/xtls/xray-core/common/errors/errorgen
 
 import (
+	"fmt"
 	"runtime"
 
 	"github.com/xtls/xray-core/common/serial"
 )
 
 var (
-	version  = "1.7.5"
+	Version_x byte = 1
+	Version_y byte = 7
+	Version_z byte = 5
+)
+
+var (
 	build    = "Custom"
 	codename = "Xray, Penetrates Everything."
 	intro    = "A unified platform for anti-censorship."
@@ -27,7 +33,7 @@ var (
 // Version returns Xray's version as a string, in the form of "x.y.z" where x, y and z are numbers.
 // ".z" part may be omitted in regular releases.
 func Version() string {
-	return version
+	return fmt.Sprintf("%v.%v.%v", Version_x, Version_y, Version_z)
 }
 
 // VersionStatement returns a list of strings representing the full version info.

+ 10 - 9
go.mod

@@ -12,7 +12,7 @@ require (
 	github.com/pelletier/go-toml v1.9.5
 	github.com/pires/go-proxyproto v0.6.2
 	github.com/quic-go/quic-go v0.32.0
-	github.com/refraction-networking/utls v1.2.2-0.20230207151345-a75a4b484849
+	github.com/refraction-networking/utls v1.2.2
 	github.com/sagernet/sing v0.1.6
 	github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7
 	github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c
@@ -20,9 +20,10 @@ require (
 	github.com/stretchr/testify v1.8.1
 	github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
 	github.com/xtls/go v0.0.0-20230107031059-4610f88d00f3
+	github.com/xtls/reality v0.0.0-20230210055008-e814936a3d99
 	go.starlark.net v0.0.0-20230128213706-3f75dec8e403
-	golang.org/x/crypto v0.5.0
-	golang.org/x/net v0.5.0
+	golang.org/x/crypto v0.6.0
+	golang.org/x/net v0.7.0
 	golang.org/x/sync v0.1.0
 	golang.org/x/sys v0.5.0
 	google.golang.org/grpc v1.53.0
@@ -41,19 +42,19 @@ require (
 	github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
 	github.com/klauspost/compress v1.15.15 // indirect
 	github.com/klauspost/cpuid/v2 v2.2.3 // indirect
-	github.com/onsi/ginkgo/v2 v2.8.0 // indirect
+	github.com/onsi/ginkgo/v2 v2.8.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
 	github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
 	github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
 	github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
 	go.uber.org/atomic v1.10.0 // indirect
-	golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect
-	golang.org/x/mod v0.7.0 // indirect
-	golang.org/x/text v0.6.0 // indirect
+	golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb // indirect
+	golang.org/x/mod v0.8.0 // indirect
+	golang.org/x/text v0.7.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
-	golang.org/x/tools v0.5.0 // indirect
-	google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 // indirect
+	golang.org/x/tools v0.6.0 // indirect
+	google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	lukechampine.com/blake3 v1.1.7 // indirect

+ 21 - 19
go.sum

@@ -112,9 +112,9 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
 github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
-github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI=
-github.com/onsi/ginkgo/v2 v2.8.0/go.mod h1:6JsQiECmxCa3V5st74AL/AmsV482EDdVrGaVW6z3oYU=
-github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y=
+github.com/onsi/ginkgo/v2 v2.8.1 h1:xFTEVwOFa1D/Ty24Ws1npBWkDYEV9BqZrsDxVrVkrrU=
+github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc=
+github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q=
 github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
 github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
@@ -138,8 +138,8 @@ github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV5
 github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
 github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA=
 github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo=
-github.com/refraction-networking/utls v1.2.2-0.20230207151345-a75a4b484849 h1:vNEcNapWFwnYJTBcVkHJa8VrdL40PNDLDbSGVY+ZV7I=
-github.com/refraction-networking/utls v1.2.2-0.20230207151345-a75a4b484849/go.mod h1:L1goe44KvhnTfctUffM2isnJpSjPlYShrhXDeZaoYKw=
+github.com/refraction-networking/utls v1.2.2 h1:uBE6V173CwG8MQrSBpNZHAix1fxOvuLKYyjFAu3uqo0=
+github.com/refraction-networking/utls v1.2.2/go.mod h1:L1goe44KvhnTfctUffM2isnJpSjPlYShrhXDeZaoYKw=
 github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
 github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@@ -193,6 +193,8 @@ github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49u
 github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
 github.com/xtls/go v0.0.0-20230107031059-4610f88d00f3 h1:a3Y4WVjCxwoyO4E2xdNvq577tW8lkSBgyrA8E9+2NtM=
 github.com/xtls/go v0.0.0-20230107031059-4610f88d00f3/go.mod h1:YJTRELIWrGxR1s8xcEBgxcxBfwQfMGjdvNLTjN9XFgY=
+github.com/xtls/reality v0.0.0-20230210055008-e814936a3d99 h1:H7I3fhMXA0GKSysu+KcSNMdX/o4MBElWR02/NIwhmpY=
+github.com/xtls/reality v0.0.0-20230210055008-e814936a3d99/go.mod h1:rkuAY1S9F8eI8gDiPDYvACE8e2uwkyg8qoOTuwWov7Y=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
 go.starlark.net v0.0.0-20230128213706-3f75dec8e403 h1:jPeC7Exc+m8OBJUlWbBLh0O5UZPM7yU5W4adnhhbG4U=
@@ -205,18 +207,18 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
-golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
+golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
+golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg=
-golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
+golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w=
+golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
 golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
-golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -230,8 +232,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
-golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
+golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -267,8 +269,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
-golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
@@ -283,8 +285,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4=
-golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
+golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -303,8 +305,8 @@ google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk
 google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 h1:vArvWooPH749rNHpBGgVl+U9B9dATjiEhJzcWGlovNs=
-google.golang.org/genproto v0.0.0-20230202175211-008b39050e57/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
+google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc h1:ijGwO+0vL2hJt5gaygqP2j6PfflOBrRot0IczKbmtio=
+google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
 google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
 google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=

+ 198 - 13
infra/conf/transport_internet.go

@@ -2,13 +2,17 @@ package conf
 
 import (
 	"encoding/base64"
+	"encoding/hex"
 	"encoding/json"
 	"math"
 	"net/url"
+	"runtime"
 	"strconv"
 	"strings"
+	"syscall"
 
 	"github.com/golang/protobuf/proto"
+	"github.com/xtls/xray-core/common/net"
 	"github.com/xtls/xray-core/common/platform/filesystem"
 	"github.com/xtls/xray-core/common/protocol"
 	"github.com/xtls/xray-core/common/serial"
@@ -18,6 +22,7 @@ import (
 	"github.com/xtls/xray-core/transport/internet/http"
 	"github.com/xtls/xray-core/transport/internet/kcp"
 	"github.com/xtls/xray-core/transport/internet/quic"
+	"github.com/xtls/xray-core/transport/internet/reality"
 	"github.com/xtls/xray-core/transport/internet/tcp"
 	"github.com/xtls/xray-core/transport/internet/tls"
 	"github.com/xtls/xray-core/transport/internet/websocket"
@@ -509,6 +514,170 @@ func (c *XTLSConfig) Build() (proto.Message, error) {
 	return config, nil
 }
 
+type REALITYConfig struct {
+	Show         bool            `json:"show"`
+	Dest         json.RawMessage `json:"dest"`
+	Type         string          `json:"type"`
+	Xver         uint64          `json:"xver"`
+	ServerNames  []string        `json:"serverNames"`
+	PrivateKey   string          `json:"privateKey"`
+	MinClientVer string          `json:"minClientVer"`
+	MaxClientVer string          `json:"maxClientVer"`
+	MaxTimeDiff  uint64          `json:"maxTimeDiff"`
+	ShortIds     []string        `json:"shortIds"`
+
+	Fingerprint string `json:"fingerprint"`
+	ServerName  string `json:"serverName"`
+	PublicKey   string `json:"publicKey"`
+	ShortId     string `json:"shortId"`
+	SpiderX     string `json:"spiderX"`
+}
+
+func (c *REALITYConfig) Build() (proto.Message, error) {
+	config := new(reality.Config)
+	config.Show = c.Show
+	var err error
+	if c.Dest != nil {
+		var i uint16
+		var s string
+		if err = json.Unmarshal(c.Dest, &i); err == nil {
+			s = strconv.Itoa(int(i))
+		} else {
+			_ = json.Unmarshal(c.Dest, &s)
+		}
+		if c.Type == "" && s != "" {
+			switch s[0] {
+			case '@', '/':
+				c.Type = "unix"
+				if s[0] == '@' && len(s) > 1 && s[1] == '@' && (runtime.GOOS == "linux" || runtime.GOOS == "android") {
+					fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy
+					copy(fullAddr, s[1:])
+					s = string(fullAddr)
+				}
+			default:
+				if _, err = strconv.Atoi(s); err == nil {
+					s = "127.0.0.1:" + s
+				}
+				if _, _, err = net.SplitHostPort(s); err == nil {
+					c.Type = "tcp"
+				}
+			}
+		}
+		if c.Type == "" {
+			return nil, newError(`please fill in a valid value for "dest"`)
+		}
+		if c.Xver > 2 {
+			return nil, newError(`invalid PROXY protocol version, "xver" only accepts 0, 1, 2`)
+		}
+		if len(c.ServerNames) == 0 {
+			return nil, newError(`empty "serverNames"`)
+		}
+		if c.PrivateKey == "" {
+			return nil, newError(`empty "privateKey"`)
+		}
+		if config.PrivateKey, err = base64.RawURLEncoding.DecodeString(c.PrivateKey); err != nil || len(config.PrivateKey) != 32 {
+			return nil, newError(`invalid "privateKey": `, c.PrivateKey)
+		}
+		if c.MinClientVer != "" {
+			config.MinClientVer = make([]byte, 3)
+			var u uint64
+			for i, s := range strings.Split(c.MinClientVer, ".") {
+				if i == 3 {
+					return nil, newError(`invalid "minClientVer": `, c.MinClientVer)
+				}
+				if u, err = strconv.ParseUint(s, 10, 8); err != nil {
+					return nil, newError(`"minClientVer[`, i, `]" should be lesser than 256`)
+				} else {
+					config.MinClientVer[i] = byte(u)
+				}
+			}
+		}
+		if c.MaxClientVer != "" {
+			config.MaxClientVer = make([]byte, 3)
+			var u uint64
+			for i, s := range strings.Split(c.MaxClientVer, ".") {
+				if i == 3 {
+					return nil, newError(`invalid "maxClientVer": `, c.MaxClientVer)
+				}
+				if u, err = strconv.ParseUint(s, 10, 8); err != nil {
+					return nil, newError(`"maxClientVer[`, i, `]" should be lesser than 256`)
+				} else {
+					config.MaxClientVer[i] = byte(u)
+				}
+			}
+		}
+		if len(c.ShortIds) == 0 {
+			return nil, newError(`empty "shortIds"`)
+		}
+		config.ShortIds = make([][]byte, len(c.ShortIds))
+		for i, s := range c.ShortIds {
+			config.ShortIds[i] = make([]byte, 8)
+			if _, err = hex.Decode(config.ShortIds[i], []byte(s)); err != nil {
+				return nil, newError(`invalid "shortIds[`, i, `]": `, s)
+			}
+		}
+		config.Dest = s
+		config.Type = c.Type
+		config.Xver = c.Xver
+		config.ServerNames = c.ServerNames
+		config.MaxTimeDiff = c.MaxTimeDiff
+	} else {
+		if c.Fingerprint == "" {
+			return nil, newError(`empty "fingerprint"`)
+		}
+		if config.Fingerprint = strings.ToLower(c.Fingerprint); tls.GetFingerprint(config.Fingerprint) == nil {
+			return nil, newError(`unknown "fingerprint": `, config.Fingerprint)
+		}
+		if config.Fingerprint == "hellogolang" {
+			return nil, newError(`invalid "fingerprint": `, config.Fingerprint)
+		}
+		if c.PublicKey == "" {
+			return nil, newError(`empty "publicKey"`)
+		}
+		if config.PublicKey, err = base64.RawURLEncoding.DecodeString(c.PublicKey); err != nil || len(config.PublicKey) != 32 {
+			return nil, newError(`invalid "publicKey": `, c.PublicKey)
+		}
+		if c.ShortId == "" {
+			return nil, newError(`empty "shortId"`)
+		}
+		config.ShortId = make([]byte, 8)
+		if _, err = hex.Decode(config.ShortId, []byte(c.ShortId)); err != nil {
+			return nil, newError(`invalid "shortId": `, c.ShortId)
+		}
+		if c.SpiderX == "" {
+			return nil, newError(`empty "spiderX"`)
+		}
+		if c.SpiderX[0] != '/' {
+			return nil, newError(`invalid "spiderX": `, c.SpiderX)
+		}
+		config.SpiderY = make([]int64, 10)
+		u, _ := url.Parse(c.SpiderX)
+		q := u.Query()
+		parse := func(param string, index int) {
+			if q.Get(param) != "" {
+				s := strings.Split(q.Get(param), "-")
+				if len(s) == 1 {
+					config.SpiderY[index], _ = strconv.ParseInt(s[0], 10, 64)
+					config.SpiderY[index+1], _ = strconv.ParseInt(s[0], 10, 64)
+				} else {
+					config.SpiderY[index], _ = strconv.ParseInt(s[0], 10, 64)
+					config.SpiderY[index+1], _ = strconv.ParseInt(s[1], 10, 64)
+				}
+			}
+			q.Del(param)
+		}
+		parse("p", 0) // padding
+		parse("c", 2) // concurrency
+		parse("t", 4) // times
+		parse("i", 6) // interval
+		parse("r", 8) // return
+		u.RawQuery = q.Encode()
+		config.SpiderX = u.String()
+		config.ServerName = c.ServerName
+	}
+	return config, nil
+}
+
 type TransportProtocol string
 
 // Build implements Buildable.
@@ -598,19 +767,20 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
 }
 
 type StreamConfig struct {
-	Network        *TransportProtocol  `json:"network"`
-	Security       string              `json:"security"`
-	TLSSettings    *TLSConfig          `json:"tlsSettings"`
-	XTLSSettings   *XTLSConfig         `json:"xtlsSettings"`
-	TCPSettings    *TCPConfig          `json:"tcpSettings"`
-	KCPSettings    *KCPConfig          `json:"kcpSettings"`
-	WSSettings     *WebSocketConfig    `json:"wsSettings"`
-	HTTPSettings   *HTTPConfig         `json:"httpSettings"`
-	DSSettings     *DomainSocketConfig `json:"dsSettings"`
-	QUICSettings   *QUICConfig         `json:"quicSettings"`
-	SocketSettings *SocketConfig       `json:"sockopt"`
-	GRPCConfig     *GRPCConfig         `json:"grpcSettings"`
-	GUNConfig      *GRPCConfig         `json:"gunSettings"`
+	Network         *TransportProtocol  `json:"network"`
+	Security        string              `json:"security"`
+	TLSSettings     *TLSConfig          `json:"tlsSettings"`
+	XTLSSettings    *XTLSConfig         `json:"xtlsSettings"`
+	REALITYSettings *REALITYConfig      `json:"realitySettings"`
+	TCPSettings     *TCPConfig          `json:"tcpSettings"`
+	KCPSettings     *KCPConfig          `json:"kcpSettings"`
+	WSSettings      *WebSocketConfig    `json:"wsSettings"`
+	HTTPSettings    *HTTPConfig         `json:"httpSettings"`
+	DSSettings      *DomainSocketConfig `json:"dsSettings"`
+	QUICSettings    *QUICConfig         `json:"quicSettings"`
+	SocketSettings  *SocketConfig       `json:"sockopt"`
+	GRPCConfig      *GRPCConfig         `json:"grpcSettings"`
+	GUNConfig       *GRPCConfig         `json:"gunSettings"`
 }
 
 // Build implements Buildable.
@@ -660,6 +830,21 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
 		config.SecuritySettings = append(config.SecuritySettings, tm)
 		config.SecurityType = tm.Type
 	}
+	if strings.EqualFold(c.Security, "reality") {
+		if config.ProtocolName != "tcp" && config.ProtocolName != "http" && config.ProtocolName != "domainsocket" {
+			return nil, newError("REALITY only supports TCP, H2 and DomainSocket for now.")
+		}
+		if c.REALITYSettings == nil {
+			return nil, newError(`REALITY: Empty "realitySettings".`)
+		}
+		ts, err := c.REALITYSettings.Build()
+		if err != nil {
+			return nil, newError("Failed to build REALITY config.").Base(err)
+		}
+		tm := serial.ToTypedMessage(ts)
+		config.SecuritySettings = append(config.SecuritySettings, tm)
+		config.SecurityType = tm.Type
+	}
 	if c.TCPSettings != nil {
 		ts, err := c.TCPSettings.Build()
 		if err != nil {

+ 1 - 0
main/commands/all/commands.go

@@ -15,5 +15,6 @@ func init() {
 		// cmdConvert,
 		tls.CmdTLS,
 		cmdUUID,
+		cmdX25519,
 	)
 }

+ 63 - 0
main/commands/all/x25519.go

@@ -0,0 +1,63 @@
+package all
+
+import (
+	"crypto/rand"
+	"encoding/base64"
+	"fmt"
+	"io"
+
+	"github.com/xtls/xray-core/main/commands/base"
+	"golang.org/x/crypto/curve25519"
+)
+
+var cmdX25519 = &base.Command{
+	UsageLine: `{{.Exec}} x25519 [-i "private key (base64.RawURLEncoding)"]`,
+	Short:     `Generate key pair for x25519 key exchange`,
+	Long: `
+Generate key pair for x25519 key exchange.
+
+Random: {{.Exec}} x25519
+
+From private key: {{.Exec}} x25519 -i "private key (base64.RawURLEncoding)"
+`,
+}
+
+func init() {
+	cmdX25519.Run = executeX25519 // break init loop
+}
+
+var input_base64 = cmdX25519.Flag.String("i", "", "")
+
+func executeX25519(cmd *base.Command, args []string) {
+	var output string
+	var err error
+	var privateKey []byte
+	var publicKey []byte
+	if len(*input_base64) > 0 {
+		privateKey, err = base64.RawURLEncoding.DecodeString(*input_base64)
+		if err != nil {
+			output = err.Error()
+			goto out
+		}
+		if len(privateKey) != curve25519.ScalarSize {
+			output = "Invalid length of private key."
+			goto out
+		}
+	}
+	if privateKey == nil {
+		privateKey = make([]byte, curve25519.ScalarSize)
+		if _, err = io.ReadFull(rand.Reader, privateKey); err != nil {
+			output = err.Error()
+			goto out
+		}
+	}
+	if publicKey, err = curve25519.X25519(privateKey, curve25519.Basepoint); err != nil {
+		output = err.Error()
+		goto out
+	}
+	output = fmt.Sprintf("Private key: %v\nPublic key: %v",
+		base64.RawURLEncoding.EncodeToString(privateKey),
+		base64.RawURLEncoding.EncodeToString(publicKey))
+out:
+	fmt.Println(output)
+}

+ 1 - 0
main/distro/all/all.go

@@ -56,6 +56,7 @@ import (
 	_ "github.com/xtls/xray-core/transport/internet/http"
 	_ "github.com/xtls/xray-core/transport/internet/kcp"
 	_ "github.com/xtls/xray-core/transport/internet/quic"
+	_ "github.com/xtls/xray-core/transport/internet/reality"
 	_ "github.com/xtls/xray-core/transport/internet/tcp"
 	_ "github.com/xtls/xray-core/transport/internet/tls"
 	_ "github.com/xtls/xray-core/transport/internet/udp"

+ 7 - 0
proxy/trojan/server.go

@@ -24,6 +24,7 @@ import (
 	"github.com/xtls/xray-core/features/policy"
 	"github.com/xtls/xray-core/features/routing"
 	"github.com/xtls/xray-core/features/stats"
+	"github.com/xtls/xray-core/transport/internet/reality"
 	"github.com/xtls/xray-core/transport/internet/stat"
 	"github.com/xtls/xray-core/transport/internet/tls"
 	"github.com/xtls/xray-core/transport/internet/udp"
@@ -411,6 +412,12 @@ func (s *Server) fallback(ctx context.Context, sid errors.ExportOption, err erro
 		alpn = cs.NegotiatedProtocol
 		newError("realName = " + name).AtInfo().WriteToLog(sid)
 		newError("realAlpn = " + alpn).AtInfo().WriteToLog(sid)
+	} else if realityConn, ok := iConn.(*reality.Conn); ok {
+		cs := realityConn.ConnectionState()
+		name = cs.ServerName
+		alpn = cs.NegotiatedProtocol
+		newError("realName = " + name).AtInfo().WriteToLog(sid)
+		newError("realAlpn = " + alpn).AtInfo().WriteToLog(sid)
 	}
 	name = strings.ToLower(name)
 	alpn = strings.ToLower(alpn)

+ 13 - 2
proxy/vless/inbound/inbound.go

@@ -26,7 +26,7 @@ import (
 	"github.com/xtls/xray-core/common/session"
 	"github.com/xtls/xray-core/common/signal"
 	"github.com/xtls/xray-core/common/task"
-	core "github.com/xtls/xray-core/core"
+	"github.com/xtls/xray-core/core"
 	"github.com/xtls/xray-core/features/dns"
 	feature_inbound "github.com/xtls/xray-core/features/inbound"
 	"github.com/xtls/xray-core/features/policy"
@@ -34,6 +34,7 @@ import (
 	"github.com/xtls/xray-core/features/stats"
 	"github.com/xtls/xray-core/proxy/vless"
 	"github.com/xtls/xray-core/proxy/vless/encoding"
+	"github.com/xtls/xray-core/transport/internet/reality"
 	"github.com/xtls/xray-core/transport/internet/stat"
 	"github.com/xtls/xray-core/transport/internet/tls"
 	"github.com/xtls/xray-core/transport/internet/xtls"
@@ -246,6 +247,12 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
 				alpn = cs.NegotiatedProtocol
 				newError("realName = " + name).AtInfo().WriteToLog(sid)
 				newError("realAlpn = " + alpn).AtInfo().WriteToLog(sid)
+			} else if realityConn, ok := iConn.(*reality.Conn); ok {
+				cs := realityConn.ConnectionState()
+				name = cs.ServerName
+				alpn = cs.NegotiatedProtocol
+				newError("realName = " + name).AtInfo().WriteToLog(sid)
+				newError("realAlpn = " + alpn).AtInfo().WriteToLog(sid)
 			}
 			name = strings.ToLower(name)
 			alpn = strings.ToLower(alpn)
@@ -494,10 +501,14 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
 						}
 						t = reflect.TypeOf(tlsConn.Conn).Elem()
 						p = uintptr(unsafe.Pointer(tlsConn.Conn))
+					} else if realityConn, ok := iConn.(*reality.Conn); ok {
+						netConn = realityConn.NetConn()
+						t = reflect.TypeOf(realityConn.Conn).Elem()
+						p = uintptr(unsafe.Pointer(realityConn.Conn))
 					} else if _, ok := iConn.(*tls.UConn); ok {
 						return newError("XTLS only supports UTLS fingerprint for the outbound.").AtWarning()
 					} else if _, ok := iConn.(*xtls.Conn); ok {
-						return newError(`failed to use ` + requestAddons.Flow + `, vision "security" must be "tls"`).AtWarning()
+						return newError(`failed to use ` + requestAddons.Flow + `, vision "security" must be "tls" or "reality"`).AtWarning()
 					} else {
 						return newError("XTLS only supports TCP, mKCP and DomainSocket for now.").AtWarning()
 					}

+ 7 - 2
proxy/vless/outbound/outbound.go

@@ -22,13 +22,14 @@ import (
 	"github.com/xtls/xray-core/common/signal"
 	"github.com/xtls/xray-core/common/task"
 	"github.com/xtls/xray-core/common/xudp"
-	core "github.com/xtls/xray-core/core"
+	"github.com/xtls/xray-core/core"
 	"github.com/xtls/xray-core/features/policy"
 	"github.com/xtls/xray-core/features/stats"
 	"github.com/xtls/xray-core/proxy/vless"
 	"github.com/xtls/xray-core/proxy/vless/encoding"
 	"github.com/xtls/xray-core/transport"
 	"github.com/xtls/xray-core/transport/internet"
+	"github.com/xtls/xray-core/transport/internet/reality"
 	"github.com/xtls/xray-core/transport/internet/stat"
 	"github.com/xtls/xray-core/transport/internet/tls"
 	"github.com/xtls/xray-core/transport/internet/xtls"
@@ -164,8 +165,12 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
 					netConn = utlsConn.NetConn()
 					t = reflect.TypeOf(utlsConn.Conn).Elem()
 					p = uintptr(unsafe.Pointer(utlsConn.Conn))
+				} else if realityConn, ok := iConn.(*reality.UConn); ok {
+					netConn = realityConn.NetConn()
+					t = reflect.TypeOf(realityConn.Conn).Elem()
+					p = uintptr(unsafe.Pointer(realityConn.Conn))
 				} else if _, ok := iConn.(*xtls.Conn); ok {
-					return newError(`failed to use ` + requestAddons.Flow + `, vision "security" must be "tls"`).AtWarning()
+					return newError(`failed to use ` + requestAddons.Flow + `, vision "security" must be "tls" or "reality"`).AtWarning()
 				} else {
 					return newError("XTLS only supports TCP, mKCP and DomainSocket for now.").AtWarning()
 				}

+ 3 - 0
transport/internet/domainsocket/dial.go

@@ -9,6 +9,7 @@ import (
 	"github.com/xtls/xray-core/common"
 	"github.com/xtls/xray-core/common/net"
 	"github.com/xtls/xray-core/transport/internet"
+	"github.com/xtls/xray-core/transport/internet/reality"
 	"github.com/xtls/xray-core/transport/internet/stat"
 	"github.com/xtls/xray-core/transport/internet/tls"
 	"github.com/xtls/xray-core/transport/internet/xtls"
@@ -30,6 +31,8 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
 		return tls.Client(conn, config.GetTLSConfig(tls.WithDestination(dest))), nil
 	} else if config := xtls.ConfigFromStreamSettings(streamSettings); config != nil {
 		return xtls.Client(conn, config.GetXTLSConfig(xtls.WithDestination(dest))), nil
+	} else if config := reality.ConfigFromStreamSettings(streamSettings); config != nil {
+		return reality.UClient(conn, config, ctx, dest)
 	}
 
 	return conn, nil

+ 26 - 15
transport/internet/domainsocket/listener.go

@@ -10,9 +10,11 @@ import (
 	"strings"
 
 	goxtls "github.com/xtls/go"
+	goreality "github.com/xtls/reality"
 	"github.com/xtls/xray-core/common"
 	"github.com/xtls/xray-core/common/net"
 	"github.com/xtls/xray-core/transport/internet"
+	"github.com/xtls/xray-core/transport/internet/reality"
 	"github.com/xtls/xray-core/transport/internet/stat"
 	"github.com/xtls/xray-core/transport/internet/tls"
 	"github.com/xtls/xray-core/transport/internet/xtls"
@@ -20,13 +22,14 @@ import (
 )
 
 type Listener struct {
-	addr       *net.UnixAddr
-	ln         net.Listener
-	tlsConfig  *gotls.Config
-	xtlsConfig *goxtls.Config
-	config     *Config
-	addConn    internet.ConnHandler
-	locker     *fileLocker
+	addr          *net.UnixAddr
+	ln            net.Listener
+	tlsConfig     *gotls.Config
+	xtlsConfig    *goxtls.Config
+	realityConfig *goreality.Config
+	config        *Config
+	addConn       internet.ConnHandler
+	locker        *fileLocker
 }
 
 func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) {
@@ -64,6 +67,9 @@ func Listen(ctx context.Context, address net.Address, port net.Port, streamSetti
 	if config := xtls.ConfigFromStreamSettings(streamSettings); config != nil {
 		ln.xtlsConfig = config.GetXTLSConfig()
 	}
+	if config := reality.ConfigFromStreamSettings(streamSettings); config != nil {
+		ln.realityConfig = config.GetREALITYConfig()
+	}
 
 	go ln.run()
 
@@ -91,14 +97,19 @@ func (ln *Listener) run() {
 			newError("failed to accepted raw connections").Base(err).AtWarning().WriteToLog()
 			continue
 		}
-
-		if ln.tlsConfig != nil {
-			conn = tls.Server(conn, ln.tlsConfig)
-		} else if ln.xtlsConfig != nil {
-			conn = xtls.Server(conn, ln.xtlsConfig)
-		}
-
-		ln.addConn(stat.Connection(conn))
+		go func() {
+			if ln.tlsConfig != nil {
+				conn = tls.Server(conn, ln.tlsConfig)
+			} else if ln.xtlsConfig != nil {
+				conn = xtls.Server(conn, ln.xtlsConfig)
+			} else if ln.realityConfig != nil {
+				if conn, err = reality.Server(conn, ln.realityConfig); err != nil {
+					newError(err).AtInfo().WriteToLog()
+					return
+				}
+			}
+			ln.addConn(stat.Connection(conn))
+		}()
 	}
 }
 

+ 12 - 3
transport/internet/http/dialer.go

@@ -14,6 +14,7 @@ import (
 	"github.com/xtls/xray-core/common/net/cnc"
 	"github.com/xtls/xray-core/common/session"
 	"github.com/xtls/xray-core/transport/internet"
+	"github.com/xtls/xray-core/transport/internet/reality"
 	"github.com/xtls/xray-core/transport/internet/stat"
 	"github.com/xtls/xray-core/transport/internet/tls"
 	"github.com/xtls/xray-core/transport/pipe"
@@ -40,8 +41,9 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
 
 	httpSettings := streamSettings.ProtocolSettings.(*Config)
 	tlsConfigs := tls.ConfigFromStreamSettings(streamSettings)
-	if tlsConfigs == nil {
-		return nil, newError("TLS must be enabled for http transport.").AtWarning()
+	realityConfigs := reality.ConfigFromStreamSettings(streamSettings)
+	if tlsConfigs == nil && realityConfigs == nil {
+		return nil, newError("TLS or REALITY must be enabled for http transport.").AtWarning()
 	}
 	sockopt := streamSettings.SocketSettings
 
@@ -74,6 +76,10 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
 				return nil, err
 			}
 
+			if realityConfigs != nil {
+				return reality.UClient(pconn, realityConfigs, ctx, dest)
+			}
+
 			var cn tls.Interface
 			if fingerprint := tls.GetFingerprint(tlsConfigs.Fingerprint); fingerprint != nil {
 				cn = tls.UClient(pconn, tlsConfig, fingerprint).(*tls.UConn)
@@ -99,7 +105,10 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
 			}
 			return cn, nil
 		},
-		TLSClientConfig: tlsConfigs.GetTLSConfig(tls.WithDestination(dest)),
+	}
+
+	if tlsConfigs != nil {
+		transport.TLSClientConfig = tlsConfigs.GetTLSConfig(tls.WithDestination(dest))
 	}
 
 	if httpSettings.IdleTimeout > 0 || httpSettings.HealthCheckTimeout > 0 {

+ 45 - 0
transport/internet/reality/config.go

@@ -0,0 +1,45 @@
+package reality
+
+import (
+	"time"
+
+	"github.com/xtls/reality"
+	"github.com/xtls/xray-core/transport/internet"
+)
+
+func (c *Config) GetREALITYConfig() *reality.Config {
+	config := &reality.Config{
+		Show: c.Show,
+		Type: c.Type,
+		Dest: c.Dest,
+		Xver: byte(c.Xver),
+
+		PrivateKey:   c.PrivateKey,
+		MinClientVer: c.MinClientVer,
+		MaxClientVer: c.MaxClientVer,
+		MaxTimeDiff:  time.Duration(c.MaxTimeDiff) * time.Millisecond,
+
+		NextProtos:             nil, // should be nil
+		SessionTicketsDisabled: true,
+	}
+	config.ServerNames = make(map[string]bool)
+	for _, serverName := range c.ServerNames {
+		config.ServerNames[serverName] = true
+	}
+	config.ShortIds = make(map[[8]byte]bool)
+	for _, shortId := range c.ShortIds {
+		config.ShortIds[*(*[8]byte)(shortId)] = true
+	}
+	return config
+}
+
+func ConfigFromStreamSettings(settings *internet.MemoryStreamConfig) *Config {
+	if settings == nil {
+		return nil
+	}
+	config, ok := settings.SecuritySettings.(*Config)
+	if !ok {
+		return nil
+	}
+	return config
+}

+ 300 - 0
transport/internet/reality/config.pb.go

@@ -0,0 +1,300 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.28.1
+// 	protoc        v3.21.12
+// source: transport/internet/reality/config.proto
+
+package reality
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Config struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Show         bool     `protobuf:"varint,1,opt,name=show,proto3" json:"show,omitempty"`
+	Dest         string   `protobuf:"bytes,2,opt,name=dest,proto3" json:"dest,omitempty"`
+	Type         string   `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"`
+	Xver         uint64   `protobuf:"varint,4,opt,name=xver,proto3" json:"xver,omitempty"`
+	ServerNames  []string `protobuf:"bytes,5,rep,name=server_names,json=serverNames,proto3" json:"server_names,omitempty"`
+	PrivateKey   []byte   `protobuf:"bytes,6,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"`
+	MinClientVer []byte   `protobuf:"bytes,7,opt,name=min_client_ver,json=minClientVer,proto3" json:"min_client_ver,omitempty"`
+	MaxClientVer []byte   `protobuf:"bytes,8,opt,name=max_client_ver,json=maxClientVer,proto3" json:"max_client_ver,omitempty"`
+	MaxTimeDiff  uint64   `protobuf:"varint,9,opt,name=max_time_diff,json=maxTimeDiff,proto3" json:"max_time_diff,omitempty"`
+	ShortIds     [][]byte `protobuf:"bytes,10,rep,name=short_ids,json=shortIds,proto3" json:"short_ids,omitempty"`
+	Fingerprint  string   `protobuf:"bytes,21,opt,name=Fingerprint,proto3" json:"Fingerprint,omitempty"`
+	ServerName   string   `protobuf:"bytes,22,opt,name=server_name,json=serverName,proto3" json:"server_name,omitempty"`
+	PublicKey    []byte   `protobuf:"bytes,23,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
+	ShortId      []byte   `protobuf:"bytes,24,opt,name=short_id,json=shortId,proto3" json:"short_id,omitempty"`
+	SpiderX      string   `protobuf:"bytes,25,opt,name=spider_x,json=spiderX,proto3" json:"spider_x,omitempty"`
+	SpiderY      []int64  `protobuf:"varint,26,rep,packed,name=spider_y,json=spiderY,proto3" json:"spider_y,omitempty"`
+}
+
+func (x *Config) Reset() {
+	*x = Config{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_transport_internet_reality_config_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Config) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Config) ProtoMessage() {}
+
+func (x *Config) ProtoReflect() protoreflect.Message {
+	mi := &file_transport_internet_reality_config_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Config.ProtoReflect.Descriptor instead.
+func (*Config) Descriptor() ([]byte, []int) {
+	return file_transport_internet_reality_config_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Config) GetShow() bool {
+	if x != nil {
+		return x.Show
+	}
+	return false
+}
+
+func (x *Config) GetDest() string {
+	if x != nil {
+		return x.Dest
+	}
+	return ""
+}
+
+func (x *Config) GetType() string {
+	if x != nil {
+		return x.Type
+	}
+	return ""
+}
+
+func (x *Config) GetXver() uint64 {
+	if x != nil {
+		return x.Xver
+	}
+	return 0
+}
+
+func (x *Config) GetServerNames() []string {
+	if x != nil {
+		return x.ServerNames
+	}
+	return nil
+}
+
+func (x *Config) GetPrivateKey() []byte {
+	if x != nil {
+		return x.PrivateKey
+	}
+	return nil
+}
+
+func (x *Config) GetMinClientVer() []byte {
+	if x != nil {
+		return x.MinClientVer
+	}
+	return nil
+}
+
+func (x *Config) GetMaxClientVer() []byte {
+	if x != nil {
+		return x.MaxClientVer
+	}
+	return nil
+}
+
+func (x *Config) GetMaxTimeDiff() uint64 {
+	if x != nil {
+		return x.MaxTimeDiff
+	}
+	return 0
+}
+
+func (x *Config) GetShortIds() [][]byte {
+	if x != nil {
+		return x.ShortIds
+	}
+	return nil
+}
+
+func (x *Config) GetFingerprint() string {
+	if x != nil {
+		return x.Fingerprint
+	}
+	return ""
+}
+
+func (x *Config) GetServerName() string {
+	if x != nil {
+		return x.ServerName
+	}
+	return ""
+}
+
+func (x *Config) GetPublicKey() []byte {
+	if x != nil {
+		return x.PublicKey
+	}
+	return nil
+}
+
+func (x *Config) GetShortId() []byte {
+	if x != nil {
+		return x.ShortId
+	}
+	return nil
+}
+
+func (x *Config) GetSpiderX() string {
+	if x != nil {
+		return x.SpiderX
+	}
+	return ""
+}
+
+func (x *Config) GetSpiderY() []int64 {
+	if x != nil {
+		return x.SpiderY
+	}
+	return nil
+}
+
+var File_transport_internet_reality_config_proto protoreflect.FileDescriptor
+
+var file_transport_internet_reality_config_proto_rawDesc = []byte{
+	0x0a, 0x27, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
+	0x72, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2f, 0x63, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x78, 0x72, 0x61, 0x79, 0x2e,
+	0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+	0x65, 0x74, 0x2e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x22, 0xdc, 0x03, 0x0a, 0x06, 0x43,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73,
+	0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a,
+	0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70,
+	0x65, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52,
+	0x04, 0x78, 0x76, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f,
+	0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72,
+	0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76,
+	0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70,
+	0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x69, 0x6e,
+	0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28,
+	0x0c, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x12,
+	0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x65,
+	0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x43, 0x6c, 0x69, 0x65,
+	0x6e, 0x74, 0x56, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x69, 0x6d,
+	0x65, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61,
+	0x78, 0x54, 0x69, 0x6d, 0x65, 0x44, 0x69, 0x66, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x6f,
+	0x72, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x73, 0x68,
+	0x6f, 0x72, 0x74, 0x49, 0x64, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72,
+	0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x46, 0x69, 0x6e,
+	0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76,
+	0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73,
+	0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62,
+	0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70,
+	0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x68, 0x6f, 0x72,
+	0x74, 0x5f, 0x69, 0x64, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x68, 0x6f, 0x72,
+	0x74, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x78, 0x18,
+	0x19, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x58, 0x12, 0x19,
+	0x0a, 0x08, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x79, 0x18, 0x1a, 0x20, 0x03, 0x28, 0x03,
+	0x52, 0x07, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x59, 0x42, 0x7f, 0x0a, 0x23, 0x63, 0x6f, 0x6d,
+	0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
+	0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79,
+	0x50, 0x01, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78,
+	0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72,
+	0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
+	0x2f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0xaa, 0x02, 0x1f, 0x58, 0x72, 0x61, 0x79, 0x2e,
+	0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+	0x65, 0x74, 0x2e, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x33,
+}
+
+var (
+	file_transport_internet_reality_config_proto_rawDescOnce sync.Once
+	file_transport_internet_reality_config_proto_rawDescData = file_transport_internet_reality_config_proto_rawDesc
+)
+
+func file_transport_internet_reality_config_proto_rawDescGZIP() []byte {
+	file_transport_internet_reality_config_proto_rawDescOnce.Do(func() {
+		file_transport_internet_reality_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_reality_config_proto_rawDescData)
+	})
+	return file_transport_internet_reality_config_proto_rawDescData
+}
+
+var file_transport_internet_reality_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_transport_internet_reality_config_proto_goTypes = []interface{}{
+	(*Config)(nil), // 0: xray.transport.internet.reality.Config
+}
+var file_transport_internet_reality_config_proto_depIdxs = []int32{
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_transport_internet_reality_config_proto_init() }
+func file_transport_internet_reality_config_proto_init() {
+	if File_transport_internet_reality_config_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_transport_internet_reality_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Config); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_transport_internet_reality_config_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_transport_internet_reality_config_proto_goTypes,
+		DependencyIndexes: file_transport_internet_reality_config_proto_depIdxs,
+		MessageInfos:      file_transport_internet_reality_config_proto_msgTypes,
+	}.Build()
+	File_transport_internet_reality_config_proto = out.File
+	file_transport_internet_reality_config_proto_rawDesc = nil
+	file_transport_internet_reality_config_proto_goTypes = nil
+	file_transport_internet_reality_config_proto_depIdxs = nil
+}

+ 27 - 0
transport/internet/reality/config.proto

@@ -0,0 +1,27 @@
+syntax = "proto3";
+
+package xray.transport.internet.reality;
+option csharp_namespace = "Xray.Transport.Internet.Reality";
+option go_package = "github.com/xtls/xray-core/transport/internet/reality";
+option java_package = "com.xray.transport.internet.reality";
+option java_multiple_files = true;
+
+message Config {
+  bool show = 1;
+  string dest = 2;
+  string type = 3;
+  uint64 xver = 4;
+  repeated string server_names = 5;
+  bytes private_key = 6;
+  bytes min_client_ver = 7;
+  bytes max_client_ver = 8;
+  uint64 max_time_diff = 9;
+  repeated bytes short_ids = 10;
+
+  string Fingerprint = 21;
+  string server_name = 22;
+  bytes public_key = 23;
+  bytes short_id = 24;
+  string spider_x = 25;
+  repeated int64 spider_y = 26;
+}

+ 9 - 0
transport/internet/reality/errors.generated.go

@@ -0,0 +1,9 @@
+package reality
+
+import "github.com/xtls/xray-core/common/errors"
+
+type errPathObjHolder struct{}
+
+func newError(values ...interface{}) *errors.Error {
+	return errors.New(values...).WithPathObj(errPathObjHolder{})
+}

+ 269 - 0
transport/internet/reality/reality.go

@@ -0,0 +1,269 @@
+package reality
+
+import (
+	"bytes"
+	"context"
+	"crypto/aes"
+	"crypto/cipher"
+	"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"
+	"golang.org/x/crypto/hkdf"
+	"golang.org/x/net/http2"
+)
+
+//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
+
+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) {
+	realityConn, err := reality.Server(c, config)
+	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,
+	}
+	if utlsConfig.ServerName == "" && dest.Address.Family().IsDomain() {
+		utlsConfig.ServerName = dest.Address.Domain()
+	}
+	uConn.ServerName = utlsConfig.ServerName
+	fingerprint := tls.GetFingerprint(config.Fingerprint)
+	if fingerprint == nil {
+		return nil, newError("REALITY: failed to get fingerprint").AtError()
+	}
+	uConn.UConn = utls.UClient(c, utlsConfig, *fingerprint)
+	{
+		uConn.BuildHandshakeState()
+		hello := uConn.HandshakeState.Hello
+		hello.SessionId = make([]byte, 32)
+		copy(hello.Raw[39:], hello.SessionId) // the location of session ID
+		binary.BigEndian.PutUint64(hello.SessionId, uint64(time.Now().Unix()))
+		hello.SessionId[0] = core.Version_x
+		hello.SessionId[1] = core.Version_y
+		hello.SessionId[2] = core.Version_z
+		copy(hello.SessionId[8:], config.ShortId)
+		if config.Show {
+			fmt.Printf("REALITY localAddr: %v\thello.sessionId[:16]: %v\n", localAddr, hello.SessionId[:16])
+		}
+		uConn.AuthKey = uConn.HandshakeState.State13.EcdheParams.SharedKey(config.PublicKey)
+		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
+		}
+		block, _ := aes.NewCipher(uConn.AuthKey)
+		aead, _ := cipher.NewGCM(block)
+		aead.Seal(hello.SessionId[:0], hello.Random[20:], hello.SessionId[:16], hello.Raw)
+		copy(hello.Raw[39:], hello.SessionId)
+		if config.Show {
+			fmt.Printf("REALITY localAddr: %v\thello.sessionId: %v\n", localAddr, hello.SessionId)
+			fmt.Printf("REALITY localAddr: %v\tuConn.AuthKey: %v\n", localAddr, uConn.AuthKey)
+		}
+	}
+	if err := uConn.Handshake(); err != nil {
+		return nil, err
+	}
+	if config.Show {
+		fmt.Printf("REALITY localAddr: %v\tuConn.Verified: %v\n", localAddr, uConn.Verified)
+	}
+	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) {
+						fmt.Printf("REALITY localAddr: %v\tDialTLSContext\n", localAddr)
+						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()
+				}
+				req.Header.Set("User-Agent", fingerprint.Client) // TODO: User-Agent map
+				if first && config.Show {
+					fmt.Printf("REALITY localAddr: %v\treq.UserAgent(): %v\n", localAddr, req.UserAgent())
+				}
+				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
+					}
+					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 {
+						fmt.Printf("REALITY localAddr: %v\treq.Referer(): %v\n", localAddr, req.Referer())
+						fmt.Printf("REALITY localAddr: %v\tlen(body): %v\n", localAddr, len(body))
+						fmt.Printf("REALITY localAddr: %v\tlen(paths): %v\n", localAddr, len(paths))
+					}
+					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
+}
+
+var href = regexp.MustCompile(`href="([/h].*?)"`)
+var dot = []byte(".")
+
+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()
+}

+ 5 - 0
transport/internet/tcp/dialer.go

@@ -7,6 +7,7 @@ import (
 	"github.com/xtls/xray-core/common/net"
 	"github.com/xtls/xray-core/common/session"
 	"github.com/xtls/xray-core/transport/internet"
+	"github.com/xtls/xray-core/transport/internet/reality"
 	"github.com/xtls/xray-core/transport/internet/stat"
 	"github.com/xtls/xray-core/transport/internet/tls"
 	"github.com/xtls/xray-core/transport/internet/xtls"
@@ -33,6 +34,10 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
 	} else if config := xtls.ConfigFromStreamSettings(streamSettings); config != nil {
 		xtlsConfig := config.GetXTLSConfig(xtls.WithDestination(dest))
 		conn = xtls.Client(conn, xtlsConfig)
+	} else if config := reality.ConfigFromStreamSettings(streamSettings); config != nil {
+		if conn, err = reality.UClient(conn, config, ctx, dest); err != nil {
+			return nil, err
+		}
 	}
 
 	tcpSettings := streamSettings.ProtocolSettings.(*Config)

+ 29 - 18
transport/internet/tcp/hub.go

@@ -7,10 +7,12 @@ import (
 	"time"
 
 	goxtls "github.com/xtls/go"
+	goreality "github.com/xtls/reality"
 	"github.com/xtls/xray-core/common"
 	"github.com/xtls/xray-core/common/net"
 	"github.com/xtls/xray-core/common/session"
 	"github.com/xtls/xray-core/transport/internet"
+	"github.com/xtls/xray-core/transport/internet/reality"
 	"github.com/xtls/xray-core/transport/internet/stat"
 	"github.com/xtls/xray-core/transport/internet/tls"
 	"github.com/xtls/xray-core/transport/internet/xtls"
@@ -18,13 +20,14 @@ import (
 
 // Listener is an internet.Listener that listens for TCP connections.
 type Listener struct {
-	listener   net.Listener
-	tlsConfig  *gotls.Config
-	xtlsConfig *goxtls.Config
-	authConfig internet.ConnectionAuthenticator
-	config     *Config
-	addConn    internet.ConnHandler
-	locker     *internet.FileLocker // for unix domain socket
+	listener      net.Listener
+	tlsConfig     *gotls.Config
+	xtlsConfig    *goxtls.Config
+	realityConfig *goreality.Config
+	authConfig    internet.ConnectionAuthenticator
+	config        *Config
+	addConn       internet.ConnHandler
+	locker        *internet.FileLocker // for unix domain socket
 }
 
 // ListenTCP creates a new Listener based on configurations.
@@ -78,6 +81,9 @@ func ListenTCP(ctx context.Context, address net.Address, port net.Port, streamSe
 	if config := xtls.ConfigFromStreamSettings(streamSettings); config != nil {
 		l.xtlsConfig = config.GetXTLSConfig()
 	}
+	if config := reality.ConfigFromStreamSettings(streamSettings); config != nil {
+		l.realityConfig = config.GetREALITYConfig()
+	}
 
 	if tcpSettings.HeaderSettings != nil {
 		headerConfig, err := tcpSettings.HeaderSettings.GetInstance()
@@ -109,17 +115,22 @@ func (v *Listener) keepAccepting() {
 			}
 			continue
 		}
-
-		if v.tlsConfig != nil {
-			conn = tls.Server(conn, v.tlsConfig)
-		} else if v.xtlsConfig != nil {
-			conn = xtls.Server(conn, v.xtlsConfig)
-		}
-		if v.authConfig != nil {
-			conn = v.authConfig.Server(conn)
-		}
-
-		v.addConn(stat.Connection(conn))
+		go func() {
+			if v.tlsConfig != nil {
+				conn = tls.Server(conn, v.tlsConfig)
+			} else if v.xtlsConfig != nil {
+				conn = xtls.Server(conn, v.xtlsConfig)
+			} else if v.realityConfig != nil {
+				if conn, err = reality.Server(conn, v.realityConfig); err != nil {
+					newError(err).AtInfo().WriteToLog()
+					return
+				}
+			}
+			if v.authConfig != nil {
+				conn = v.authConfig.Server(conn)
+			}
+			v.addConn(stat.Connection(conn))
+		}()
 	}
 }