mirror of
https://github.com/v2fly/v2ray-core.git
synced 2025-01-17 23:06:30 -05:00
Resync Hysteria2 Import (#3280)
* Revert "Remove hysteria2"
This reverts commit 1d21d7a077
.
* Update Hysteria2
* Rename hysteria2 module
* fix broken hy2 import version
---------
Co-authored-by: JimmyHuang454 <jimmyhuang454@gmail.com>
This commit is contained in:
parent
ccacd52b24
commit
54fbeeba6d
7
go.mod
7
go.mod
@ -6,6 +6,7 @@ toolchain go1.22.7
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/adrg/xdg v0.5.3
|
github.com/adrg/xdg v0.5.3
|
||||||
|
github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7
|
||||||
github.com/go-chi/chi/v5 v5.1.0
|
github.com/go-chi/chi/v5 v5.1.0
|
||||||
github.com/go-chi/render v1.0.3
|
github.com/go-chi/render v1.0.3
|
||||||
github.com/go-playground/validator/v10 v10.22.1
|
github.com/go-playground/validator/v10 v10.22.1
|
||||||
@ -28,6 +29,7 @@ require (
|
|||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/v2fly/BrowserBridge v0.0.0-20210430233438-0570fc1d7d08
|
github.com/v2fly/BrowserBridge v0.0.0-20210430233438-0570fc1d7d08
|
||||||
github.com/v2fly/VSign v0.0.0-20201108000810-e2adc24bf848
|
github.com/v2fly/VSign v0.0.0-20201108000810-e2adc24bf848
|
||||||
|
github.com/v2fly/hysteria/core/v2 v2.0.0-20250113081444-b0a0747ac7ab
|
||||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
|
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
|
||||||
github.com/v2fly/struc v0.0.0-20241227015403-8e8fa1badfd6
|
github.com/v2fly/struc v0.0.0-20241227015403-8e8fa1badfd6
|
||||||
github.com/vincent-petithory/dataurl v1.0.0
|
github.com/vincent-petithory/dataurl v1.0.0
|
||||||
@ -65,11 +67,9 @@ require (
|
|||||||
github.com/klauspost/compress v1.17.4 // indirect
|
github.com/klauspost/compress v1.17.4 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||||
github.com/klauspost/reedsolomon v1.11.7 // indirect
|
github.com/klauspost/reedsolomon v1.11.7 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect
|
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect
|
||||||
github.com/mustafaturan/monoton v1.0.0 // indirect
|
github.com/mustafaturan/monoton v1.0.0 // indirect
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
|
||||||
github.com/onsi/ginkgo/v2 v2.17.0 // indirect
|
github.com/onsi/ginkgo/v2 v2.17.0 // indirect
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||||
github.com/pion/logging v0.2.2 // indirect
|
github.com/pion/logging v0.2.2 // indirect
|
||||||
@ -77,8 +77,10 @@ require (
|
|||||||
github.com/pion/sctp v1.8.7 // indirect
|
github.com/pion/sctp v1.8.7 // indirect
|
||||||
github.com/pion/transport/v3 v3.0.7 // indirect
|
github.com/pion/transport/v3 v3.0.7 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||||
github.com/secure-io/siv-go v0.0.0-20180922214919-5ff40651e2c4 // indirect
|
github.com/secure-io/siv-go v0.0.0-20180922214919-5ff40651e2c4 // indirect
|
||||||
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/xtaci/smux v1.5.24 // indirect
|
github.com/xtaci/smux v1.5.24 // indirect
|
||||||
go.uber.org/mock v0.4.0 // indirect
|
go.uber.org/mock v0.4.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||||
@ -87,5 +89,4 @@ require (
|
|||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
golang.org/x/tools v0.22.0 // indirect
|
golang.org/x/tools v0.22.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
|
||||||
)
|
)
|
||||||
|
24
go.sum
24
go.sum
@ -25,6 +25,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
|
|||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||||
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
|
github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7 h1:zO38yBOvQ1dLHbSuaU5BFZ8zalnSDQslj+i/9AGOk9s=
|
||||||
|
github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7/go.mod h1:LoSUY2chVqNQCDyi4IZGqPpXLy1FuCkE37PKwtJvNGg=
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
@ -50,7 +52,6 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
|
|||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@ -189,10 +190,11 @@ github.com/klauspost/reedsolomon v1.11.7/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ
|
|||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/lunixbochs/struc v0.0.0-20190916212049-a5c72983bc42/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
|
github.com/lunixbochs/struc v0.0.0-20190916212049-a5c72983bc42/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
|
||||||
@ -223,8 +225,6 @@ github.com/mustafaturan/monoton v1.0.0/go.mod h1:FOnE7NV3s3EWPXb8/7+/OSdiMBbdlkV
|
|||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||||
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
github.com/onsi/ginkgo/v2 v2.17.0 h1:kdnunFXpBjbzN56hcJHrXZ8M+LOkenKA7NnBzTNigTI=
|
github.com/onsi/ginkgo/v2 v2.17.0 h1:kdnunFXpBjbzN56hcJHrXZ8M+LOkenKA7NnBzTNigTI=
|
||||||
github.com/onsi/ginkgo/v2 v2.17.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
|
github.com/onsi/ginkgo/v2 v2.17.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
|
||||||
@ -273,6 +273,8 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
|
|||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
|
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
|
||||||
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
||||||
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
||||||
@ -281,6 +283,8 @@ github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr
|
|||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
@ -309,6 +313,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
@ -329,6 +335,8 @@ github.com/v2fly/BrowserBridge v0.0.0-20210430233438-0570fc1d7d08 h1:4Yh46CVE3k/
|
|||||||
github.com/v2fly/BrowserBridge v0.0.0-20210430233438-0570fc1d7d08/go.mod h1:KAuQNm+LWQCOFqdBcUgihPzRpVXRKzGbTNhfEfRZ4wY=
|
github.com/v2fly/BrowserBridge v0.0.0-20210430233438-0570fc1d7d08/go.mod h1:KAuQNm+LWQCOFqdBcUgihPzRpVXRKzGbTNhfEfRZ4wY=
|
||||||
github.com/v2fly/VSign v0.0.0-20201108000810-e2adc24bf848 h1:p1UzXK6VAutXFFQMnre66h7g1BjRKUnLv0HfmmRoz7w=
|
github.com/v2fly/VSign v0.0.0-20201108000810-e2adc24bf848 h1:p1UzXK6VAutXFFQMnre66h7g1BjRKUnLv0HfmmRoz7w=
|
||||||
github.com/v2fly/VSign v0.0.0-20201108000810-e2adc24bf848/go.mod h1:p80Bv154ZtrGpXMN15slDCqc9UGmfBuUzheDFBYaW/M=
|
github.com/v2fly/VSign v0.0.0-20201108000810-e2adc24bf848/go.mod h1:p80Bv154ZtrGpXMN15slDCqc9UGmfBuUzheDFBYaW/M=
|
||||||
|
github.com/v2fly/hysteria/core/v2 v2.0.0-20250113081444-b0a0747ac7ab h1:GstVKviVuxRZXxHzeWq0N2M4LG5A5W1HvFX1b7aQ48w=
|
||||||
|
github.com/v2fly/hysteria/core/v2 v2.0.0-20250113081444-b0a0747ac7ab/go.mod h1:yWDV7zOoL0pPhVlWV6Hqf46gWYenwwT9g4Y+e5yPRz8=
|
||||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI=
|
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI=
|
||||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
|
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
|
||||||
github.com/v2fly/struc v0.0.0-20241227015403-8e8fa1badfd6 h1:Qea2jW7g1hvQ9TkYq3aT2h0NDWjPQHtvDfmKXoWgJ9E=
|
github.com/v2fly/struc v0.0.0-20241227015403-8e8fa1badfd6 h1:Qea2jW7g1hvQ9TkYq3aT2h0NDWjPQHtvDfmKXoWgJ9E=
|
||||||
@ -352,6 +360,8 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
|||||||
go.starlark.net v0.0.0-20230612165344-9532f5667272 h1:2/wtqS591wZyD2OsClsVBKRPEvBsQt/Js+fsCiYhwu8=
|
go.starlark.net v0.0.0-20230612165344-9532f5667272 h1:2/wtqS591wZyD2OsClsVBKRPEvBsQt/Js+fsCiYhwu8=
|
||||||
go.starlark.net v0.0.0-20230612165344-9532f5667272/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds=
|
go.starlark.net v0.0.0-20230612165344-9532f5667272/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds=
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||||
|
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
@ -572,8 +582,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
|
|||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
|
78
infra/conf/v4/hysteria2.go
Normal file
78
infra/conf/v4/hysteria2.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package v4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/net/packetaddr"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/protocol"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/serial"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/proxy/hysteria2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hysteria2ServerTarget is configuration of a single hysteria2 server
|
||||||
|
type Hysteria2ServerTarget struct {
|
||||||
|
Address *cfgcommon.Address `json:"address"`
|
||||||
|
Port uint16 `json:"port"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Level byte `json:"level"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hysteria2ClientConfig is configuration of hysteria2 servers
|
||||||
|
type Hysteria2ClientConfig struct {
|
||||||
|
Servers []*Hysteria2ServerTarget `json:"servers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build implements Buildable
|
||||||
|
func (c *Hysteria2ClientConfig) Build() (proto.Message, error) {
|
||||||
|
config := new(hysteria2.ClientConfig)
|
||||||
|
|
||||||
|
if len(c.Servers) == 0 {
|
||||||
|
return nil, newError("0 Hysteria2 server configured.")
|
||||||
|
}
|
||||||
|
|
||||||
|
serverSpecs := make([]*protocol.ServerEndpoint, len(c.Servers))
|
||||||
|
for idx, rec := range c.Servers {
|
||||||
|
if rec.Address == nil {
|
||||||
|
return nil, newError("Hysteria2 server address is not set.")
|
||||||
|
}
|
||||||
|
if rec.Port == 0 {
|
||||||
|
return nil, newError("Invalid Hysteria2 port.")
|
||||||
|
}
|
||||||
|
account := &hysteria2.Account{}
|
||||||
|
hysteria2 := &protocol.ServerEndpoint{
|
||||||
|
Address: rec.Address.Build(),
|
||||||
|
Port: uint32(rec.Port),
|
||||||
|
User: []*protocol.User{
|
||||||
|
{
|
||||||
|
Level: uint32(rec.Level),
|
||||||
|
Email: rec.Email,
|
||||||
|
Account: serial.ToTypedMessage(account),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
serverSpecs[idx] = hysteria2
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Server = serverSpecs
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hysteria2ServerConfig is Inbound configuration
|
||||||
|
type Hysteria2ServerConfig struct {
|
||||||
|
PacketEncoding string `json:"packetEncoding"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build implements Buildable
|
||||||
|
func (c *Hysteria2ServerConfig) Build() (proto.Message, error) {
|
||||||
|
config := new(hysteria2.ServerConfig)
|
||||||
|
switch c.PacketEncoding {
|
||||||
|
case "Packet":
|
||||||
|
config.PacketEncoding = packetaddr.PacketAddrType_Packet
|
||||||
|
case "", "None":
|
||||||
|
config.PacketEncoding = packetaddr.PacketAddrType_None
|
||||||
|
}
|
||||||
|
return config, nil
|
||||||
|
}
|
@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/v2fly/v2ray-core/v5/transport/internet/domainsocket"
|
"github.com/v2fly/v2ray-core/v5/transport/internet/domainsocket"
|
||||||
httpheader "github.com/v2fly/v2ray-core/v5/transport/internet/headers/http"
|
httpheader "github.com/v2fly/v2ray-core/v5/transport/internet/headers/http"
|
||||||
"github.com/v2fly/v2ray-core/v5/transport/internet/http"
|
"github.com/v2fly/v2ray-core/v5/transport/internet/http"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2"
|
||||||
"github.com/v2fly/v2ray-core/v5/transport/internet/kcp"
|
"github.com/v2fly/v2ray-core/v5/transport/internet/kcp"
|
||||||
"github.com/v2fly/v2ray-core/v5/transport/internet/quic"
|
"github.com/v2fly/v2ray-core/v5/transport/internet/quic"
|
||||||
"github.com/v2fly/v2ray-core/v5/transport/internet/tcp"
|
"github.com/v2fly/v2ray-core/v5/transport/internet/tcp"
|
||||||
@ -143,6 +144,26 @@ type Hy2ConfigCongestion struct {
|
|||||||
DownMbps uint64 `json:"down_mbps"`
|
DownMbps uint64 `json:"down_mbps"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Hy2Config struct {
|
||||||
|
Password string `json:"password"`
|
||||||
|
Congestion Hy2ConfigCongestion `json:"congestion"`
|
||||||
|
UseUdpExtension bool `json:"use_udp_extension"`
|
||||||
|
IgnoreClientBandwidth bool `json:"ignore_client_bandwidth"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build implements Buildable.
|
||||||
|
func (c *Hy2Config) Build() (proto.Message, error) {
|
||||||
|
return &hysteria2.Config{Password: c.Password,
|
||||||
|
Congestion: &hysteria2.Congestion{
|
||||||
|
Type: c.Congestion.Type,
|
||||||
|
DownMbps: c.Congestion.DownMbps,
|
||||||
|
UpMbps: c.Congestion.UpMbps,
|
||||||
|
},
|
||||||
|
UseUdpExtension: c.UseUdpExtension,
|
||||||
|
IgnoreClientBandwidth: c.IgnoreClientBandwidth,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
type WebSocketConfig struct {
|
type WebSocketConfig struct {
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Headers map[string]string `json:"headers"`
|
Headers map[string]string `json:"headers"`
|
||||||
@ -285,6 +306,8 @@ func (p TransportProtocol) Build() (string, error) {
|
|||||||
return "quic", nil
|
return "quic", nil
|
||||||
case "gun", "grpc":
|
case "gun", "grpc":
|
||||||
return "gun", nil
|
return "gun", nil
|
||||||
|
case "hy2", "hysteria2":
|
||||||
|
return "hysteria2", nil
|
||||||
default:
|
default:
|
||||||
return "", newError("Config: unknown transport protocol: ", p)
|
return "", newError("Config: unknown transport protocol: ", p)
|
||||||
}
|
}
|
||||||
@ -302,6 +325,7 @@ type StreamConfig struct {
|
|||||||
QUICSettings *QUICConfig `json:"quicSettings"`
|
QUICSettings *QUICConfig `json:"quicSettings"`
|
||||||
GunSettings *GunConfig `json:"gunSettings"`
|
GunSettings *GunConfig `json:"gunSettings"`
|
||||||
GRPCSettings *GunConfig `json:"grpcSettings"`
|
GRPCSettings *GunConfig `json:"grpcSettings"`
|
||||||
|
Hy2Settings *Hy2Config `json:"hy2Settings"`
|
||||||
SocketSettings *socketcfg.SocketConfig `json:"sockopt"`
|
SocketSettings *socketcfg.SocketConfig `json:"sockopt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,6 +427,16 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
|
|||||||
Settings: serial.ToTypedMessage(gs),
|
Settings: serial.ToTypedMessage(gs),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if c.Hy2Settings != nil {
|
||||||
|
hy2, err := c.Hy2Settings.Build()
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Failed to build hy2 config.").Base(err)
|
||||||
|
}
|
||||||
|
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
|
||||||
|
ProtocolName: "hysteria2",
|
||||||
|
Settings: serial.ToTypedMessage(hy2),
|
||||||
|
})
|
||||||
|
}
|
||||||
if c.SocketSettings != nil {
|
if c.SocketSettings != nil {
|
||||||
ss, err := c.SocketSettings.Build()
|
ss, err := c.SocketSettings.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -35,6 +35,7 @@ var (
|
|||||||
"vless": func() interface{} { return new(VLessInboundConfig) },
|
"vless": func() interface{} { return new(VLessInboundConfig) },
|
||||||
"vmess": func() interface{} { return new(VMessInboundConfig) },
|
"vmess": func() interface{} { return new(VMessInboundConfig) },
|
||||||
"trojan": func() interface{} { return new(TrojanServerConfig) },
|
"trojan": func() interface{} { return new(TrojanServerConfig) },
|
||||||
|
"hysteria2": func() interface{} { return new(Hysteria2ServerConfig) },
|
||||||
}, "protocol", "settings")
|
}, "protocol", "settings")
|
||||||
|
|
||||||
outboundConfigLoader = loader.NewJSONConfigLoader(loader.ConfigCreatorCache{
|
outboundConfigLoader = loader.NewJSONConfigLoader(loader.ConfigCreatorCache{
|
||||||
@ -46,6 +47,7 @@ var (
|
|||||||
"vless": func() interface{} { return new(VLessOutboundConfig) },
|
"vless": func() interface{} { return new(VLessOutboundConfig) },
|
||||||
"vmess": func() interface{} { return new(VMessOutboundConfig) },
|
"vmess": func() interface{} { return new(VMessOutboundConfig) },
|
||||||
"trojan": func() interface{} { return new(TrojanClientConfig) },
|
"trojan": func() interface{} { return new(TrojanClientConfig) },
|
||||||
|
"hysteria2": func() interface{} { return new(Hysteria2ClientConfig) },
|
||||||
"dns": func() interface{} { return new(DNSOutboundConfig) },
|
"dns": func() interface{} { return new(DNSOutboundConfig) },
|
||||||
"loopback": func() interface{} { return new(LoopbackConfig) },
|
"loopback": func() interface{} { return new(LoopbackConfig) },
|
||||||
}, "protocol", "settings")
|
}, "protocol", "settings")
|
||||||
|
@ -53,6 +53,7 @@ import (
|
|||||||
_ "github.com/v2fly/v2ray-core/v5/proxy/vlite/inbound"
|
_ "github.com/v2fly/v2ray-core/v5/proxy/vlite/inbound"
|
||||||
_ "github.com/v2fly/v2ray-core/v5/proxy/vlite/outbound"
|
_ "github.com/v2fly/v2ray-core/v5/proxy/vlite/outbound"
|
||||||
|
|
||||||
|
_ "github.com/v2fly/v2ray-core/v5/proxy/hysteria2"
|
||||||
_ "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks2022"
|
_ "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks2022"
|
||||||
|
|
||||||
// Transports
|
// Transports
|
||||||
@ -82,6 +83,8 @@ import (
|
|||||||
|
|
||||||
_ "github.com/v2fly/v2ray-core/v5/transport/internet/httpupgrade"
|
_ "github.com/v2fly/v2ray-core/v5/transport/internet/httpupgrade"
|
||||||
|
|
||||||
|
_ "github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2"
|
||||||
|
|
||||||
// Transport headers
|
// Transport headers
|
||||||
_ "github.com/v2fly/v2ray-core/v5/transport/internet/headers/http"
|
_ "github.com/v2fly/v2ray-core/v5/transport/internet/headers/http"
|
||||||
_ "github.com/v2fly/v2ray-core/v5/transport/internet/headers/noop"
|
_ "github.com/v2fly/v2ray-core/v5/transport/internet/headers/noop"
|
||||||
|
216
proxy/hysteria2/client.go
Normal file
216
proxy/hysteria2/client.go
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
package hysteria2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
hyProtocol "github.com/v2fly/hysteria/core/v2/international/protocol"
|
||||||
|
|
||||||
|
core "github.com/v2fly/v2ray-core/v5"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/buf"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/net"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/net/packetaddr"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/protocol"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/retry"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/session"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/signal"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/task"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/features/policy"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/proxy"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport/internet"
|
||||||
|
hyTransport "github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport/internet/udp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client is an inbound handler
|
||||||
|
type Client struct {
|
||||||
|
serverPicker protocol.ServerPicker
|
||||||
|
policyManager policy.Manager
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient create a new client.
|
||||||
|
func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
|
||||||
|
serverList := protocol.NewServerList()
|
||||||
|
for _, rec := range config.Server {
|
||||||
|
s, err := protocol.NewServerSpecFromPB(rec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("failed to parse server spec").Base(err)
|
||||||
|
}
|
||||||
|
serverList.AddServer(s)
|
||||||
|
}
|
||||||
|
if serverList.Size() == 0 {
|
||||||
|
return nil, newError("0 server")
|
||||||
|
}
|
||||||
|
|
||||||
|
v := core.MustFromContext(ctx)
|
||||||
|
client := &Client{
|
||||||
|
serverPicker: protocol.NewRoundRobinServerPicker(serverList),
|
||||||
|
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process implements OutboundHandler.Process().
|
||||||
|
func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
||||||
|
outbound := session.OutboundFromContext(ctx)
|
||||||
|
if outbound == nil || !outbound.Target.IsValid() {
|
||||||
|
return newError("target not specified")
|
||||||
|
}
|
||||||
|
destination := outbound.Target
|
||||||
|
network := destination.Network
|
||||||
|
|
||||||
|
var server *protocol.ServerSpec
|
||||||
|
var conn internet.Connection
|
||||||
|
|
||||||
|
err := retry.ExponentialBackoff(5, 100).On(func() error {
|
||||||
|
server = c.serverPicker.PickServer()
|
||||||
|
rawConn, err := dialer.Dial(ctx, server.Destination())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
conn = rawConn
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return newError("failed to find an available destination").AtWarning().Base(err)
|
||||||
|
}
|
||||||
|
newError("tunneling request to ", destination, " via ", server.Destination().NetAddr()).WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
iConn := conn
|
||||||
|
if statConn, ok := conn.(*internet.StatCouterConnection); ok {
|
||||||
|
iConn = statConn.Connection // will not count the UDP traffic.
|
||||||
|
}
|
||||||
|
hyConn, IsHy2Transport := iConn.(*hyTransport.HyConn)
|
||||||
|
|
||||||
|
if !IsHy2Transport && network == net.Network_UDP {
|
||||||
|
// hysteria2 need to use udp extension to proxy UDP.
|
||||||
|
return newError(hyTransport.CanNotUseUdpExtension)
|
||||||
|
}
|
||||||
|
|
||||||
|
user := server.PickUser()
|
||||||
|
userLevel := uint32(0)
|
||||||
|
if user != nil {
|
||||||
|
userLevel = user.Level
|
||||||
|
}
|
||||||
|
sessionPolicy := c.policyManager.ForLevel(userLevel)
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
|
||||||
|
|
||||||
|
if packetConn, err := packetaddr.ToPacketAddrConn(link, destination); err == nil {
|
||||||
|
postRequest := func() error {
|
||||||
|
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
|
||||||
|
|
||||||
|
var buffer [2048]byte
|
||||||
|
n, addr, err := packetConn.ReadFrom(buffer[:])
|
||||||
|
if err != nil {
|
||||||
|
return newError("failed to read a packet").Base(err)
|
||||||
|
}
|
||||||
|
dest := net.DestinationFromAddr(addr)
|
||||||
|
|
||||||
|
bufferWriter := buf.NewBufferedWriter(buf.NewWriter(conn))
|
||||||
|
connWriter := &ConnWriter{Writer: bufferWriter, Target: dest}
|
||||||
|
packetWriter := &PacketWriter{Writer: connWriter, Target: dest, HyConn: hyConn}
|
||||||
|
|
||||||
|
// write some request payload to buffer
|
||||||
|
if _, err := packetWriter.WriteTo(buffer[:n], addr); err != nil {
|
||||||
|
return newError("failed to write a request payload").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer
|
||||||
|
if err = bufferWriter.SetBuffered(false); err != nil {
|
||||||
|
return newError("failed to flush payload").Base(err).AtWarning()
|
||||||
|
}
|
||||||
|
|
||||||
|
return udp.CopyPacketConn(packetWriter, packetConn, udp.UpdateActivity(timer))
|
||||||
|
}
|
||||||
|
|
||||||
|
getResponse := func() error {
|
||||||
|
defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
|
||||||
|
|
||||||
|
packetReader := &PacketReader{Reader: conn, HyConn: hyConn}
|
||||||
|
packetConnectionReader := &PacketConnectionReader{reader: packetReader}
|
||||||
|
|
||||||
|
return udp.CopyPacketConn(packetConn, packetConnectionReader, udp.UpdateActivity(timer))
|
||||||
|
}
|
||||||
|
|
||||||
|
responseDoneAndCloseWriter := task.OnSuccess(getResponse, task.Close(link.Writer))
|
||||||
|
if err := task.Run(ctx, postRequest, responseDoneAndCloseWriter); err != nil {
|
||||||
|
return newError("connection ends").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
postRequest := func() error {
|
||||||
|
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
|
||||||
|
|
||||||
|
var bodyWriter buf.Writer
|
||||||
|
bufferWriter := buf.NewBufferedWriter(buf.NewWriter(conn))
|
||||||
|
connWriter := &ConnWriter{Writer: bufferWriter, Target: destination}
|
||||||
|
bodyWriter = connWriter
|
||||||
|
|
||||||
|
if network == net.Network_UDP {
|
||||||
|
bodyWriter = &PacketWriter{Writer: connWriter, Target: destination, HyConn: hyConn}
|
||||||
|
} else {
|
||||||
|
// write some request payload to buffer
|
||||||
|
err = buf.CopyOnceTimeout(link.Reader, bodyWriter, proxy.FirstPayloadTimeout)
|
||||||
|
switch err {
|
||||||
|
case buf.ErrNotTimeoutReader, buf.ErrReadTimeout:
|
||||||
|
if err := connWriter.WriteTCPHeader(); err != nil {
|
||||||
|
return newError("failed to write request header").Base(err).AtWarning()
|
||||||
|
}
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
return newError("failed to write a request payload").Base(err).AtWarning()
|
||||||
|
}
|
||||||
|
// Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer
|
||||||
|
if err = bufferWriter.SetBuffered(false); err != nil {
|
||||||
|
return newError("failed to flush payload").Base(err).AtWarning()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = buf.Copy(link.Reader, bodyWriter, buf.UpdateActivity(timer)); err != nil {
|
||||||
|
return newError("failed to transfer request payload").Base(err).AtInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
getResponse := func() error {
|
||||||
|
defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
|
||||||
|
|
||||||
|
var reader buf.Reader
|
||||||
|
if network == net.Network_UDP {
|
||||||
|
reader = &PacketReader{
|
||||||
|
Reader: conn, HyConn: hyConn,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ok, msg, err := hyProtocol.ReadTCPResponse(conn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return newError(msg)
|
||||||
|
}
|
||||||
|
reader = buf.NewReader(conn)
|
||||||
|
}
|
||||||
|
return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer))
|
||||||
|
}
|
||||||
|
|
||||||
|
responseDoneAndCloseWriter := task.OnSuccess(getResponse, task.Close(link.Writer))
|
||||||
|
if err := task.Run(ctx, postRequest, responseDoneAndCloseWriter); err != nil {
|
||||||
|
return newError("connection ends").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||||
|
return NewClient(ctx, config.(*ClientConfig))
|
||||||
|
}))
|
||||||
|
}
|
18
proxy/hysteria2/config.go
Normal file
18
proxy/hysteria2/config.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package hysteria2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MemoryAccount is an account type converted from Account.
|
||||||
|
type MemoryAccount struct{}
|
||||||
|
|
||||||
|
// AsAccount implements protocol.AsAccount.
|
||||||
|
func (a *Account) AsAccount() (protocol.Account, error) {
|
||||||
|
return &MemoryAccount{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals implements protocol.Account.Equals().
|
||||||
|
func (a *MemoryAccount) Equals(another protocol.Account) bool {
|
||||||
|
return false
|
||||||
|
}
|
282
proxy/hysteria2/config.pb.go
Normal file
282
proxy/hysteria2/config.pb.go
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
package hysteria2
|
||||||
|
|
||||||
|
import (
|
||||||
|
packetaddr "github.com/v2fly/v2ray-core/v5/common/net/packetaddr"
|
||||||
|
protocol "github.com/v2fly/v2ray-core/v5/common/protocol"
|
||||||
|
_ "github.com/v2fly/v2ray-core/v5/common/protoext"
|
||||||
|
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 Account struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Account) Reset() {
|
||||||
|
*x = Account{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_proxy_hysteria2_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Account) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Account) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Account) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_proxy_hysteria2_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 Account.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Account) Descriptor() ([]byte, []int) {
|
||||||
|
return file_proxy_hysteria2_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientConfig struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Server []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=server,proto3" json:"server,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ClientConfig) Reset() {
|
||||||
|
*x = ClientConfig{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_proxy_hysteria2_config_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ClientConfig) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ClientConfig) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ClientConfig) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_proxy_hysteria2_config_proto_msgTypes[1]
|
||||||
|
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 ClientConfig.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ClientConfig) Descriptor() ([]byte, []int) {
|
||||||
|
return file_proxy_hysteria2_config_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ClientConfig) GetServer() []*protocol.ServerEndpoint {
|
||||||
|
if x != nil {
|
||||||
|
return x.Server
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfig struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
PacketEncoding packetaddr.PacketAddrType `protobuf:"varint,1,opt,name=packet_encoding,json=packetEncoding,proto3,enum=v2ray.core.net.packetaddr.PacketAddrType" json:"packet_encoding,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ServerConfig) Reset() {
|
||||||
|
*x = ServerConfig{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_proxy_hysteria2_config_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ServerConfig) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ServerConfig) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ServerConfig) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_proxy_hysteria2_config_proto_msgTypes[2]
|
||||||
|
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 ServerConfig.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ServerConfig) Descriptor() ([]byte, []int) {
|
||||||
|
return file_proxy_hysteria2_config_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ServerConfig) GetPacketEncoding() packetaddr.PacketAddrType {
|
||||||
|
if x != nil {
|
||||||
|
return x.PacketEncoding
|
||||||
|
}
|
||||||
|
return packetaddr.PacketAddrType(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_proxy_hysteria2_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_proxy_hysteria2_config_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x1c, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x68, 0x79, 0x73, 0x74, 0x65, 0x72, 0x69, 0x61,
|
||||||
|
0x32, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1a,
|
||||||
|
0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79,
|
||||||
|
0x2e, 0x68, 0x79, 0x73, 0x74, 0x65, 0x72, 0x69, 0x61, 0x32, 0x1a, 0x22, 0x63, 0x6f, 0x6d, 0x6d,
|
||||||
|
0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x61, 0x64, 0x64,
|
||||||
|
0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21,
|
||||||
|
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f,
|
||||||
|
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x1a, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x65,
|
||||||
|
0x78, 0x74, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72,
|
||||||
|
0x6f, 0x74, 0x6f, 0x22, 0x09, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x6d,
|
||||||
|
0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x42,
|
||||||
|
0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a,
|
||||||
|
0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
|
||||||
|
0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76,
|
||||||
|
0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76,
|
||||||
|
0x65, 0x72, 0x3a, 0x19, 0x82, 0xb5, 0x18, 0x15, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75,
|
||||||
|
0x6e, 0x64, 0x12, 0x09, 0x68, 0x79, 0x73, 0x74, 0x65, 0x72, 0x69, 0x61, 0x32, 0x22, 0x7c, 0x0a,
|
||||||
|
0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x52, 0x0a,
|
||||||
|
0x0f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67,
|
||||||
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63,
|
||||||
|
0x6f, 0x72, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x61, 0x64,
|
||||||
|
0x64, 0x72, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x54, 0x79, 0x70,
|
||||||
|
0x65, 0x52, 0x0e, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e,
|
||||||
|
0x67, 0x3a, 0x18, 0x82, 0xb5, 0x18, 0x14, 0x0a, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64,
|
||||||
|
0x12, 0x09, 0x68, 0x79, 0x73, 0x74, 0x65, 0x72, 0x69, 0x61, 0x32, 0x42, 0x6f, 0x0a, 0x1e, 0x63,
|
||||||
|
0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72,
|
||||||
|
0x6f, 0x78, 0x79, 0x2e, 0x68, 0x79, 0x73, 0x74, 0x65, 0x72, 0x69, 0x61, 0x32, 0x50, 0x01, 0x5a,
|
||||||
|
0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c,
|
||||||
|
0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f,
|
||||||
|
0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x68, 0x79, 0x73, 0x74, 0x65, 0x72, 0x69, 0x61, 0x32, 0xaa,
|
||||||
|
0x02, 0x1a, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x72, 0x6f,
|
||||||
|
0x78, 0x79, 0x2e, 0x48, 0x79, 0x73, 0x74, 0x65, 0x72, 0x69, 0x61, 0x32, 0x62, 0x06, 0x70, 0x72,
|
||||||
|
0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_proxy_hysteria2_config_proto_rawDescOnce sync.Once
|
||||||
|
file_proxy_hysteria2_config_proto_rawDescData = file_proxy_hysteria2_config_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_proxy_hysteria2_config_proto_rawDescGZIP() []byte {
|
||||||
|
file_proxy_hysteria2_config_proto_rawDescOnce.Do(func() {
|
||||||
|
file_proxy_hysteria2_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_proxy_hysteria2_config_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_proxy_hysteria2_config_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_proxy_hysteria2_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||||
|
var file_proxy_hysteria2_config_proto_goTypes = []any{
|
||||||
|
(*Account)(nil), // 0: v2ray.core.proxy.hysteria2.Account
|
||||||
|
(*ClientConfig)(nil), // 1: v2ray.core.proxy.hysteria2.ClientConfig
|
||||||
|
(*ServerConfig)(nil), // 2: v2ray.core.proxy.hysteria2.ServerConfig
|
||||||
|
(*protocol.ServerEndpoint)(nil), // 3: v2ray.core.common.protocol.ServerEndpoint
|
||||||
|
(packetaddr.PacketAddrType)(0), // 4: v2ray.core.net.packetaddr.PacketAddrType
|
||||||
|
}
|
||||||
|
var file_proxy_hysteria2_config_proto_depIdxs = []int32{
|
||||||
|
3, // 0: v2ray.core.proxy.hysteria2.ClientConfig.server:type_name -> v2ray.core.common.protocol.ServerEndpoint
|
||||||
|
4, // 1: v2ray.core.proxy.hysteria2.ServerConfig.packet_encoding:type_name -> v2ray.core.net.packetaddr.PacketAddrType
|
||||||
|
2, // [2:2] is the sub-list for method output_type
|
||||||
|
2, // [2:2] is the sub-list for method input_type
|
||||||
|
2, // [2:2] is the sub-list for extension type_name
|
||||||
|
2, // [2:2] is the sub-list for extension extendee
|
||||||
|
0, // [0:2] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_proxy_hysteria2_config_proto_init() }
|
||||||
|
func file_proxy_hysteria2_config_proto_init() {
|
||||||
|
if File_proxy_hysteria2_config_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_proxy_hysteria2_config_proto_msgTypes[0].Exporter = func(v any, i int) any {
|
||||||
|
switch v := v.(*Account); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_proxy_hysteria2_config_proto_msgTypes[1].Exporter = func(v any, i int) any {
|
||||||
|
switch v := v.(*ClientConfig); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_proxy_hysteria2_config_proto_msgTypes[2].Exporter = func(v any, i int) any {
|
||||||
|
switch v := v.(*ServerConfig); 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_proxy_hysteria2_config_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 3,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_proxy_hysteria2_config_proto_goTypes,
|
||||||
|
DependencyIndexes: file_proxy_hysteria2_config_proto_depIdxs,
|
||||||
|
MessageInfos: file_proxy_hysteria2_config_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_proxy_hysteria2_config_proto = out.File
|
||||||
|
file_proxy_hysteria2_config_proto_rawDesc = nil
|
||||||
|
file_proxy_hysteria2_config_proto_goTypes = nil
|
||||||
|
file_proxy_hysteria2_config_proto_depIdxs = nil
|
||||||
|
}
|
28
proxy/hysteria2/config.proto
Normal file
28
proxy/hysteria2/config.proto
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package v2ray.core.proxy.hysteria2;
|
||||||
|
option csharp_namespace = "V2Ray.Core.Proxy.Hysteria2";
|
||||||
|
option go_package = "github.com/v2fly/v2ray-core/v5/proxy/hysteria2";
|
||||||
|
option java_package = "com.v2ray.core.proxy.hysteria2";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
import "common/net/packetaddr/config.proto";
|
||||||
|
import "common/protocol/server_spec.proto";
|
||||||
|
import "common/protoext/extensions.proto";
|
||||||
|
|
||||||
|
message Account {
|
||||||
|
}
|
||||||
|
|
||||||
|
message ClientConfig {
|
||||||
|
option (v2ray.core.common.protoext.message_opt).type = "outbound";
|
||||||
|
option (v2ray.core.common.protoext.message_opt).short_name = "hysteria2";
|
||||||
|
|
||||||
|
repeated v2ray.core.common.protocol.ServerEndpoint server = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ServerConfig {
|
||||||
|
option (v2ray.core.common.protoext.message_opt).type = "inbound";
|
||||||
|
option (v2ray.core.common.protoext.message_opt).short_name = "hysteria2";
|
||||||
|
|
||||||
|
v2ray.core.net.packetaddr.PacketAddrType packet_encoding = 1;
|
||||||
|
}
|
9
proxy/hysteria2/errors.generated.go
Normal file
9
proxy/hysteria2/errors.generated.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package hysteria2
|
||||||
|
|
||||||
|
import "github.com/v2fly/v2ray-core/v5/common/errors"
|
||||||
|
|
||||||
|
type errPathObjHolder struct{}
|
||||||
|
|
||||||
|
func newError(values ...interface{}) *errors.Error {
|
||||||
|
return errors.New(values...).WithPathObj(errPathObjHolder{})
|
||||||
|
}
|
1
proxy/hysteria2/hysteria2.go
Normal file
1
proxy/hysteria2/hysteria2.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package hysteria2
|
210
proxy/hysteria2/protocol.go
Normal file
210
proxy/hysteria2/protocol.go
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
package hysteria2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
hyProtocol "github.com/v2fly/hysteria/core/v2/international/protocol"
|
||||||
|
"github.com/apernet/quic-go/quicvarint"
|
||||||
|
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/buf"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/net"
|
||||||
|
hyTransport "github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
paddingChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConnWriter is TCP Connection Writer Wrapper
|
||||||
|
type ConnWriter struct {
|
||||||
|
io.Writer
|
||||||
|
Target net.Destination
|
||||||
|
TCPHeaderSent bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write implements io.Writer
|
||||||
|
func (c *ConnWriter) Write(p []byte) (n int, err error) {
|
||||||
|
if !c.TCPHeaderSent {
|
||||||
|
if err := c.writeTCPHeader(); err != nil {
|
||||||
|
return 0, newError("failed to write request header").Base(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Writer.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteMultiBuffer implements buf.Writer
|
||||||
|
func (c *ConnWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||||
|
defer buf.ReleaseMulti(mb)
|
||||||
|
|
||||||
|
for _, b := range mb {
|
||||||
|
if !b.IsEmpty() {
|
||||||
|
if _, err := c.Write(b.Bytes()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConnWriter) WriteTCPHeader() error {
|
||||||
|
if !c.TCPHeaderSent {
|
||||||
|
if err := c.writeTCPHeader(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func QuicLen(s int) int {
|
||||||
|
return int(quicvarint.Len(uint64(s)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConnWriter) writeTCPHeader() error {
|
||||||
|
c.TCPHeaderSent = true
|
||||||
|
|
||||||
|
paddingLen := 64 + rand.Intn(512-64)
|
||||||
|
padding := make([]byte, paddingLen)
|
||||||
|
for i := range padding {
|
||||||
|
padding[i] = paddingChars[rand.Intn(len(paddingChars))]
|
||||||
|
}
|
||||||
|
addressAndPort := c.Target.NetAddr()
|
||||||
|
addressLen := len(addressAndPort)
|
||||||
|
if addressLen > hyProtocol.MaxAddressLength {
|
||||||
|
return newError("address length too large: ", addressLen)
|
||||||
|
}
|
||||||
|
size := QuicLen(addressLen) + addressLen + QuicLen(paddingLen) + paddingLen
|
||||||
|
|
||||||
|
buf := make([]byte, size)
|
||||||
|
i := hyProtocol.VarintPut(buf, uint64(addressLen))
|
||||||
|
i += copy(buf[i:], addressAndPort)
|
||||||
|
i += hyProtocol.VarintPut(buf[i:], uint64(paddingLen))
|
||||||
|
copy(buf[i:], padding)
|
||||||
|
|
||||||
|
_, err := c.Writer.Write(buf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PacketWriter UDP Connection Writer Wrapper
|
||||||
|
type PacketWriter struct {
|
||||||
|
io.Writer
|
||||||
|
HyConn *hyTransport.HyConn
|
||||||
|
Target net.Destination
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteMultiBuffer implements buf.Writer
|
||||||
|
func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||||
|
for _, b := range mb {
|
||||||
|
if b.IsEmpty() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := w.writePacket(b.Bytes(), w.Target); err != nil {
|
||||||
|
buf.ReleaseMulti(mb)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteMultiBufferWithMetadata writes udp packet with destination specified
|
||||||
|
func (w *PacketWriter) WriteMultiBufferWithMetadata(mb buf.MultiBuffer, dest net.Destination) error {
|
||||||
|
for _, b := range mb {
|
||||||
|
if b.IsEmpty() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := w.writePacket(b.Bytes(), dest); err != nil {
|
||||||
|
buf.ReleaseMulti(mb)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *PacketWriter) WriteTo(payload []byte, addr net.Addr) (int, error) {
|
||||||
|
dest := net.DestinationFromAddr(addr)
|
||||||
|
|
||||||
|
return w.writePacket(payload, dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *PacketWriter) writePacket(payload []byte, dest net.Destination) (int, error) {
|
||||||
|
return w.HyConn.WritePacket(payload, dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnReader is TCP Connection Reader Wrapper
|
||||||
|
type ConnReader struct {
|
||||||
|
io.Reader
|
||||||
|
Target net.Destination
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read implements io.Reader
|
||||||
|
func (c *ConnReader) Read(p []byte) (int, error) {
|
||||||
|
return c.Reader.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadMultiBuffer implements buf.Reader
|
||||||
|
func (c *ConnReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||||
|
b := buf.New()
|
||||||
|
_, err := b.ReadFrom(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf.MultiBuffer{b}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PacketPayload combines udp payload and destination
|
||||||
|
type PacketPayload struct {
|
||||||
|
Target net.Destination
|
||||||
|
Buffer buf.MultiBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// PacketReader is UDP Connection Reader Wrapper
|
||||||
|
type PacketReader struct {
|
||||||
|
io.Reader
|
||||||
|
HyConn *hyTransport.HyConn
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadMultiBuffer implements buf.Reader
|
||||||
|
func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||||
|
p, err := r.ReadMultiBufferWithMetadata()
|
||||||
|
if p != nil {
|
||||||
|
return p.Buffer, err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadMultiBufferWithMetadata reads udp packet with destination
|
||||||
|
func (r *PacketReader) ReadMultiBufferWithMetadata() (*PacketPayload, error) {
|
||||||
|
_, data, dest, err := r.HyConn.ReadPacket()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b := buf.FromBytes(data)
|
||||||
|
return &PacketPayload{Target: *dest, Buffer: buf.MultiBuffer{b}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type PacketConnectionReader struct {
|
||||||
|
reader *PacketReader
|
||||||
|
payload *PacketPayload
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *PacketConnectionReader) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
if r.payload == nil || r.payload.Buffer.IsEmpty() {
|
||||||
|
r.payload, err = r.reader.ReadMultiBufferWithMetadata()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = &net.UDPAddr{
|
||||||
|
IP: r.payload.Target.Address.IP(),
|
||||||
|
Port: int(r.payload.Target.Port),
|
||||||
|
}
|
||||||
|
|
||||||
|
r.payload.Buffer, n = buf.SplitFirstBytes(r.payload.Buffer, p)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
216
proxy/hysteria2/server.go
Normal file
216
proxy/hysteria2/server.go
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
package hysteria2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
hyProtocol "github.com/v2fly/hysteria/core/v2/international/protocol"
|
||||||
|
|
||||||
|
core "github.com/v2fly/v2ray-core/v5"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/buf"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/errors"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/log"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/net"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/net/packetaddr"
|
||||||
|
udp_proto "github.com/v2fly/v2ray-core/v5/common/protocol/udp"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/session"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/signal"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/task"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/features/policy"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/features/routing"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport/internet"
|
||||||
|
hyTransport "github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport/internet/udp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||||
|
return NewServer(ctx, config.(*ServerConfig))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server is an inbound connection handler that handles messages in protocol.
|
||||||
|
type Server struct {
|
||||||
|
policyManager policy.Manager
|
||||||
|
packetEncoding packetaddr.PacketAddrType
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServer creates a new inbound handler.
|
||||||
|
func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
|
||||||
|
v := core.MustFromContext(ctx)
|
||||||
|
server := &Server{
|
||||||
|
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
|
||||||
|
packetEncoding: config.PacketEncoding,
|
||||||
|
}
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network implements proxy.Inbound.Network().
|
||||||
|
func (s *Server) Network() []net.Network {
|
||||||
|
return []net.Network{net.Network_TCP, net.Network_UNIX}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process implements proxy.Inbound.Process().
|
||||||
|
func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher routing.Dispatcher) error {
|
||||||
|
sid := session.ExportIDToError(ctx)
|
||||||
|
|
||||||
|
iConn := conn
|
||||||
|
if statConn, ok := conn.(*internet.StatCouterConnection); ok {
|
||||||
|
iConn = statConn.Connection // will not count the UDP traffic.
|
||||||
|
}
|
||||||
|
hyConn, IsHy2Transport := iConn.(*hyTransport.HyConn)
|
||||||
|
|
||||||
|
if IsHy2Transport && hyConn.IsUDPExtension {
|
||||||
|
network = net.Network_UDP
|
||||||
|
}
|
||||||
|
|
||||||
|
if !IsHy2Transport && network == net.Network_UDP {
|
||||||
|
return newError(hyTransport.CanNotUseUdpExtension)
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionPolicy := s.policyManager.ForLevel(0)
|
||||||
|
if err := conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil {
|
||||||
|
return newError("unable to set read deadline").Base(err).AtWarning()
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferedReader := &buf.BufferedReader{
|
||||||
|
Reader: buf.NewReader(conn),
|
||||||
|
}
|
||||||
|
clientReader := &ConnReader{Reader: bufferedReader}
|
||||||
|
|
||||||
|
if err := conn.SetReadDeadline(time.Time{}); err != nil {
|
||||||
|
return newError("unable to set read deadline").Base(err).AtWarning()
|
||||||
|
}
|
||||||
|
|
||||||
|
if network == net.Network_UDP { // handle udp request
|
||||||
|
return s.handleUDPPayload(ctx,
|
||||||
|
&PacketReader{Reader: clientReader, HyConn: hyConn},
|
||||||
|
&PacketWriter{Writer: conn, HyConn: hyConn}, dispatcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
var reqAddr string
|
||||||
|
var err error
|
||||||
|
reqAddr, err = hyProtocol.ReadTCPRequest(conn)
|
||||||
|
if err != nil {
|
||||||
|
return newError("failed to parse header").Base(err)
|
||||||
|
}
|
||||||
|
err = hyProtocol.WriteTCPResponse(conn, true, "")
|
||||||
|
if err != nil {
|
||||||
|
return newError("failed to send response").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
address, stringPort, err := net.SplitHostPort(reqAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
port, err := net.PortFromString(stringPort)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
destination := net.Destination{Network: network, Address: net.ParseAddress(address), Port: port}
|
||||||
|
|
||||||
|
inbound := session.InboundFromContext(ctx)
|
||||||
|
if inbound == nil {
|
||||||
|
panic("no inbound metadata")
|
||||||
|
}
|
||||||
|
sessionPolicy = s.policyManager.ForLevel(0)
|
||||||
|
|
||||||
|
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
|
||||||
|
From: conn.RemoteAddr(),
|
||||||
|
To: destination,
|
||||||
|
Status: log.AccessAccepted,
|
||||||
|
Reason: "",
|
||||||
|
})
|
||||||
|
|
||||||
|
newError("received request for ", destination).WriteToLog(sid)
|
||||||
|
return s.handleConnection(ctx, sessionPolicy, destination, clientReader, buf.NewWriter(conn), dispatcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleConnection(ctx context.Context, sessionPolicy policy.Session,
|
||||||
|
destination net.Destination,
|
||||||
|
clientReader buf.Reader,
|
||||||
|
clientWriter buf.Writer, dispatcher routing.Dispatcher,
|
||||||
|
) error {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
|
||||||
|
ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer)
|
||||||
|
|
||||||
|
link, err := dispatcher.Dispatch(ctx, destination)
|
||||||
|
if err != nil {
|
||||||
|
return newError("failed to dispatch request to ", destination).Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
requestDone := func() error {
|
||||||
|
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
|
||||||
|
|
||||||
|
if err := buf.Copy(clientReader, link.Writer, buf.UpdateActivity(timer)); err != nil {
|
||||||
|
return newError("failed to transfer request").Base(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
responseDone := func() error {
|
||||||
|
defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
|
||||||
|
|
||||||
|
if err := buf.Copy(link.Reader, clientWriter, buf.UpdateActivity(timer)); err != nil {
|
||||||
|
return newError("failed to write response").Base(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
requestDonePost := task.OnSuccess(requestDone, task.Close(link.Writer))
|
||||||
|
if err := task.Run(ctx, requestDonePost, responseDone); err != nil {
|
||||||
|
common.Must(common.Interrupt(link.Reader))
|
||||||
|
common.Must(common.Interrupt(link.Writer))
|
||||||
|
return newError("connection ends").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReader, clientWriter *PacketWriter, dispatcher routing.Dispatcher) error {
|
||||||
|
udpDispatcherConstructor := udp.NewSplitDispatcher
|
||||||
|
switch s.packetEncoding {
|
||||||
|
case packetaddr.PacketAddrType_None:
|
||||||
|
case packetaddr.PacketAddrType_Packet:
|
||||||
|
packetAddrDispatcherFactory := udp.NewPacketAddrDispatcherCreator(ctx)
|
||||||
|
udpDispatcherConstructor = packetAddrDispatcherFactory.NewPacketAddrDispatcher
|
||||||
|
}
|
||||||
|
|
||||||
|
udpServer := udpDispatcherConstructor(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
|
||||||
|
if err := clientWriter.WriteMultiBufferWithMetadata(buf.MultiBuffer{packet.Payload}, packet.Source); err != nil {
|
||||||
|
newError("failed to write response").Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
inbound := session.InboundFromContext(ctx)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
p, err := clientReader.ReadMultiBufferWithMetadata()
|
||||||
|
if err != nil {
|
||||||
|
if errors.Cause(err) != io.EOF {
|
||||||
|
return newError("unexpected EOF").Base(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
currentPacketCtx := ctx
|
||||||
|
currentPacketCtx = log.ContextWithAccessMessage(currentPacketCtx, &log.AccessMessage{
|
||||||
|
From: inbound.Source,
|
||||||
|
To: p.Target,
|
||||||
|
Status: log.AccessAccepted,
|
||||||
|
Reason: "",
|
||||||
|
})
|
||||||
|
newError("tunnelling request to ", p.Target).WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
|
||||||
|
for _, b := range p.Buffer {
|
||||||
|
udpServer.Dispatch(currentPacketCtx, p.Target, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
468
testing/scenarios/hy2_test.go
Normal file
468
testing/scenarios/hy2_test.go
Normal file
@ -0,0 +1,468 @@
|
|||||||
|
package scenarios
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
"google.golang.org/protobuf/types/known/anypb"
|
||||||
|
|
||||||
|
core "github.com/v2fly/v2ray-core/v5"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/app/log"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/app/proxyman"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common"
|
||||||
|
clog "github.com/v2fly/v2ray-core/v5/common/log"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/net"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/protocol"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/protocol/tls/cert"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/serial"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/uuid"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/proxy/dokodemo"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/proxy/freedom"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/proxy/hysteria2"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/proxy/vmess"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/testing/servers/tcp"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/testing/servers/udp"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport/internet"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport/internet/headers/http"
|
||||||
|
hyTransport "github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2"
|
||||||
|
tcpTransport "github.com/v2fly/v2ray-core/v5/transport/internet/tcp"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport/internet/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVMessHysteria2Congestion(t *testing.T) {
|
||||||
|
for _, v := range []string{"bbr", "brutal"} {
|
||||||
|
testVMessHysteria2(t, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testVMessHysteria2(t *testing.T, congestionType string) {
|
||||||
|
tcpServer := tcp.Server{
|
||||||
|
MsgProcessor: xor,
|
||||||
|
}
|
||||||
|
dest, err := tcpServer.Start()
|
||||||
|
common.Must(err)
|
||||||
|
defer tcpServer.Close()
|
||||||
|
|
||||||
|
userID := protocol.NewID(uuid.New())
|
||||||
|
serverPort := udp.PickPort()
|
||||||
|
serverConfig := &core.Config{
|
||||||
|
App: []*anypb.Any{
|
||||||
|
serial.ToTypedMessage(&log.Config{
|
||||||
|
Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Inbound: []*core.InboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||||
|
PortRange: net.SinglePortRange(serverPort),
|
||||||
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
|
||||||
|
StreamSettings: &internet.StreamConfig{
|
||||||
|
ProtocolName: "hysteria2",
|
||||||
|
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||||
|
SecuritySettings: []*anypb.Any{
|
||||||
|
serial.ToTypedMessage(
|
||||||
|
&tls.Config{
|
||||||
|
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
TransportSettings: []*internet.TransportConfig{
|
||||||
|
{
|
||||||
|
ProtocolName: "hysteria2",
|
||||||
|
Settings: serial.ToTypedMessage(&hyTransport.Config{
|
||||||
|
Congestion: &hyTransport.Congestion{Type: congestionType, UpMbps: 100, DownMbps: 100},
|
||||||
|
Password: "password",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
ProxySettings: serial.ToTypedMessage(&inbound.Config{
|
||||||
|
User: []*protocol.User{
|
||||||
|
{
|
||||||
|
Account: serial.ToTypedMessage(&vmess.Account{
|
||||||
|
Id: userID.String(),
|
||||||
|
AlterId: 0,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbound: []*core.OutboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
clientPort := tcp.PickPort()
|
||||||
|
clientConfig := &core.Config{
|
||||||
|
App: []*anypb.Any{
|
||||||
|
serial.ToTypedMessage(&log.Config{
|
||||||
|
Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Inbound: []*core.InboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||||
|
PortRange: net.SinglePortRange(clientPort),
|
||||||
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
}),
|
||||||
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
|
Address: net.NewIPOrDomain(dest.Address),
|
||||||
|
Port: uint32(dest.Port),
|
||||||
|
NetworkList: &net.NetworkList{
|
||||||
|
Network: []net.Network{net.Network_TCP},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbound: []*core.OutboundHandlerConfig{
|
||||||
|
{
|
||||||
|
SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
|
||||||
|
StreamSettings: &internet.StreamConfig{
|
||||||
|
ProtocolName: "hysteria2",
|
||||||
|
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||||
|
SecuritySettings: []*anypb.Any{
|
||||||
|
serial.ToTypedMessage(
|
||||||
|
&tls.Config{
|
||||||
|
ServerName: "www.v2fly.org",
|
||||||
|
AllowInsecure: true,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
TransportSettings: []*internet.TransportConfig{
|
||||||
|
{
|
||||||
|
ProtocolName: "hysteria2",
|
||||||
|
Settings: serial.ToTypedMessage(&hyTransport.Config{
|
||||||
|
Congestion: &hyTransport.Congestion{Type: congestionType, UpMbps: 100, DownMbps: 100},
|
||||||
|
Password: "password",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
ProxySettings: serial.ToTypedMessage(&outbound.Config{
|
||||||
|
Receiver: []*protocol.ServerEndpoint{
|
||||||
|
{
|
||||||
|
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
Port: uint32(serverPort),
|
||||||
|
User: []*protocol.User{
|
||||||
|
{
|
||||||
|
Account: serial.ToTypedMessage(&vmess.Account{
|
||||||
|
Id: userID.String(),
|
||||||
|
AlterId: 0,
|
||||||
|
SecuritySettings: &protocol.SecurityConfig{
|
||||||
|
Type: protocol.SecurityType_NONE,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
servers, err := InitializeServerConfigs(serverConfig, clientConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to initialize all servers: ", err.Error())
|
||||||
|
}
|
||||||
|
defer CloseAllServers(servers)
|
||||||
|
|
||||||
|
var errg errgroup.Group
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := errg.Wait(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHysteria2Offical(t *testing.T) {
|
||||||
|
for _, v := range []bool{true, false} {
|
||||||
|
testHysteria2Offical(t, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testHysteria2Offical(t *testing.T, isUDP bool) {
|
||||||
|
var dest net.Destination
|
||||||
|
var err error
|
||||||
|
if isUDP {
|
||||||
|
udpServer := udp.Server{
|
||||||
|
MsgProcessor: xor,
|
||||||
|
}
|
||||||
|
dest, err = udpServer.Start()
|
||||||
|
common.Must(err)
|
||||||
|
defer udpServer.Close()
|
||||||
|
} else {
|
||||||
|
tcpServer := tcp.Server{
|
||||||
|
MsgProcessor: xor,
|
||||||
|
}
|
||||||
|
dest, err = tcpServer.Start()
|
||||||
|
common.Must(err)
|
||||||
|
defer tcpServer.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
serverPort := udp.PickPort()
|
||||||
|
serverConfig := &core.Config{
|
||||||
|
App: []*anypb.Any{
|
||||||
|
serial.ToTypedMessage(&log.Config{
|
||||||
|
Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Inbound: []*core.InboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||||
|
PortRange: net.SinglePortRange(serverPort),
|
||||||
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
StreamSettings: &internet.StreamConfig{
|
||||||
|
ProtocolName: "hysteria2",
|
||||||
|
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||||
|
SecuritySettings: []*anypb.Any{
|
||||||
|
serial.ToTypedMessage(
|
||||||
|
&tls.Config{
|
||||||
|
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
TransportSettings: []*internet.TransportConfig{
|
||||||
|
{
|
||||||
|
ProtocolName: "hysteria2",
|
||||||
|
Settings: serial.ToTypedMessage(&hyTransport.Config{
|
||||||
|
Congestion: &hyTransport.Congestion{Type: "brutal", UpMbps: 100, DownMbps: 100},
|
||||||
|
UseUdpExtension: true,
|
||||||
|
Password: "password",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
ProxySettings: serial.ToTypedMessage(&hysteria2.ServerConfig{}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbound: []*core.OutboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
clientPort := tcp.PickPort()
|
||||||
|
clientConfig := &core.Config{
|
||||||
|
App: []*anypb.Any{
|
||||||
|
serial.ToTypedMessage(&log.Config{
|
||||||
|
Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Inbound: []*core.InboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||||
|
PortRange: net.SinglePortRange(clientPort),
|
||||||
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
}),
|
||||||
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
|
Address: net.NewIPOrDomain(dest.Address),
|
||||||
|
Port: uint32(dest.Port),
|
||||||
|
NetworkList: &net.NetworkList{
|
||||||
|
Network: []net.Network{net.Network_TCP, net.Network_UDP},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbound: []*core.OutboundHandlerConfig{
|
||||||
|
{
|
||||||
|
SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
|
||||||
|
StreamSettings: &internet.StreamConfig{
|
||||||
|
ProtocolName: "hysteria2",
|
||||||
|
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||||
|
SecuritySettings: []*anypb.Any{
|
||||||
|
serial.ToTypedMessage(
|
||||||
|
&tls.Config{
|
||||||
|
ServerName: "www.v2fly.org",
|
||||||
|
AllowInsecure: true,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
TransportSettings: []*internet.TransportConfig{
|
||||||
|
{
|
||||||
|
ProtocolName: "hysteria2",
|
||||||
|
Settings: serial.ToTypedMessage(&hyTransport.Config{
|
||||||
|
Congestion: &hyTransport.Congestion{Type: "brutal", UpMbps: 100, DownMbps: 100},
|
||||||
|
UseUdpExtension: true,
|
||||||
|
Password: "password",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
ProxySettings: serial.ToTypedMessage(&hysteria2.ClientConfig{
|
||||||
|
Server: []*protocol.ServerEndpoint{
|
||||||
|
{
|
||||||
|
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
Port: uint32(serverPort),
|
||||||
|
User: []*protocol.User{
|
||||||
|
{
|
||||||
|
Account: serial.ToTypedMessage(&hysteria2.Account{}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
servers, err := InitializeServerConfigs(serverConfig, clientConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to initialize all servers: ", err.Error())
|
||||||
|
}
|
||||||
|
defer CloseAllServers(servers)
|
||||||
|
|
||||||
|
var errg errgroup.Group
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
if isUDP {
|
||||||
|
errg.Go(testUDPConn(clientPort, 1500, time.Second*4))
|
||||||
|
} else {
|
||||||
|
errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := errg.Wait(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHysteria2OnTCP(t *testing.T) {
|
||||||
|
tcpServer := tcp.Server{
|
||||||
|
MsgProcessor: xor,
|
||||||
|
}
|
||||||
|
dest, err := tcpServer.Start()
|
||||||
|
common.Must(err)
|
||||||
|
defer tcpServer.Close()
|
||||||
|
|
||||||
|
serverPort := udp.PickPort()
|
||||||
|
serverConfig := &core.Config{
|
||||||
|
App: []*anypb.Any{
|
||||||
|
serial.ToTypedMessage(&log.Config{
|
||||||
|
Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Inbound: []*core.InboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||||
|
PortRange: net.SinglePortRange(serverPort),
|
||||||
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
StreamSettings: &internet.StreamConfig{
|
||||||
|
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||||
|
SecuritySettings: []*anypb.Any{
|
||||||
|
serial.ToTypedMessage(
|
||||||
|
&tls.Config{
|
||||||
|
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
TransportSettings: []*internet.TransportConfig{
|
||||||
|
{
|
||||||
|
Protocol: internet.TransportProtocol_TCP,
|
||||||
|
Settings: serial.ToTypedMessage(&tcpTransport.Config{
|
||||||
|
HeaderSettings: serial.ToTypedMessage(&http.Config{}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
ProxySettings: serial.ToTypedMessage(&hysteria2.ServerConfig{}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbound: []*core.OutboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
clientPort := tcp.PickPort()
|
||||||
|
clientConfig := &core.Config{
|
||||||
|
App: []*anypb.Any{
|
||||||
|
serial.ToTypedMessage(&log.Config{
|
||||||
|
Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Inbound: []*core.InboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||||
|
PortRange: net.SinglePortRange(clientPort),
|
||||||
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
}),
|
||||||
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
|
Address: net.NewIPOrDomain(dest.Address),
|
||||||
|
Port: uint32(dest.Port),
|
||||||
|
NetworkList: &net.NetworkList{
|
||||||
|
Network: []net.Network{net.Network_TCP},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbound: []*core.OutboundHandlerConfig{
|
||||||
|
{
|
||||||
|
SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
|
||||||
|
StreamSettings: &internet.StreamConfig{
|
||||||
|
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||||
|
SecuritySettings: []*anypb.Any{
|
||||||
|
serial.ToTypedMessage(
|
||||||
|
&tls.Config{
|
||||||
|
ServerName: "www.v2fly.org",
|
||||||
|
AllowInsecure: true,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
TransportSettings: []*internet.TransportConfig{
|
||||||
|
{
|
||||||
|
Protocol: internet.TransportProtocol_TCP,
|
||||||
|
Settings: serial.ToTypedMessage(&tcpTransport.Config{
|
||||||
|
HeaderSettings: serial.ToTypedMessage(&http.Config{}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
ProxySettings: serial.ToTypedMessage(&hysteria2.ClientConfig{
|
||||||
|
Server: []*protocol.ServerEndpoint{
|
||||||
|
{
|
||||||
|
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
Port: uint32(serverPort),
|
||||||
|
User: []*protocol.User{
|
||||||
|
{
|
||||||
|
Account: serial.ToTypedMessage(&hysteria2.Account{}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
servers, err := InitializeServerConfigs(serverConfig, clientConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to initialize all servers: ", err.Error())
|
||||||
|
}
|
||||||
|
defer CloseAllServers(servers)
|
||||||
|
|
||||||
|
var errg errgroup.Group
|
||||||
|
for i := 0; i < 1; i++ {
|
||||||
|
errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := errg.Wait(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
271
transport/internet/hysteria2/config.pb.go
Normal file
271
transport/internet/hysteria2/config.pb.go
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
package hysteria2
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/v2fly/v2ray-core/v5/common/protoext"
|
||||||
|
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 Congestion struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
|
||||||
|
UpMbps uint64 `protobuf:"varint,2,opt,name=up_mbps,json=upMbps,proto3" json:"up_mbps,omitempty"`
|
||||||
|
DownMbps uint64 `protobuf:"varint,3,opt,name=down_mbps,json=downMbps,proto3" json:"down_mbps,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Congestion) Reset() {
|
||||||
|
*x = Congestion{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_transport_internet_hysteria2_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Congestion) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Congestion) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Congestion) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_transport_internet_hysteria2_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 Congestion.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Congestion) Descriptor() ([]byte, []int) {
|
||||||
|
return file_transport_internet_hysteria2_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Congestion) GetType() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Type
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Congestion) GetUpMbps() uint64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.UpMbps
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Congestion) GetDownMbps() uint64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.DownMbps
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"`
|
||||||
|
Congestion *Congestion `protobuf:"bytes,4,opt,name=congestion,proto3" json:"congestion,omitempty"`
|
||||||
|
IgnoreClientBandwidth bool `protobuf:"varint,5,opt,name=ignore_client_bandwidth,json=ignoreClientBandwidth,proto3" json:"ignore_client_bandwidth,omitempty"`
|
||||||
|
UseUdpExtension bool `protobuf:"varint,6,opt,name=use_udp_extension,json=useUdpExtension,proto3" json:"use_udp_extension,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) Reset() {
|
||||||
|
*x = Config{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_transport_internet_hysteria2_config_proto_msgTypes[1]
|
||||||
|
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_hysteria2_config_proto_msgTypes[1]
|
||||||
|
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_hysteria2_config_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetPassword() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Password
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetCongestion() *Congestion {
|
||||||
|
if x != nil {
|
||||||
|
return x.Congestion
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetIgnoreClientBandwidth() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.IgnoreClientBandwidth
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetUseUdpExtension() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.UseUdpExtension
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_transport_internet_hysteria2_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_transport_internet_hysteria2_config_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x29, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
||||||
|
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x79, 0x73, 0x74, 0x65, 0x72, 0x69, 0x61, 0x32, 0x2f, 0x63,
|
||||||
|
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x27, 0x76, 0x32, 0x72,
|
||||||
|
0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
||||||
|
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x79, 0x73, 0x74, 0x65,
|
||||||
|
0x72, 0x69, 0x61, 0x32, 0x1a, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x65, 0x78, 0x74, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73,
|
||||||
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x56, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x67, 0x65, 0x73,
|
||||||
|
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||||
|
0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x70, 0x5f, 0x6d,
|
||||||
|
0x62, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x75, 0x70, 0x4d, 0x62, 0x70,
|
||||||
|
0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x6f, 0x77, 0x6e, 0x5f, 0x6d, 0x62, 0x70, 0x73, 0x18, 0x03,
|
||||||
|
0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x6f, 0x77, 0x6e, 0x4d, 0x62, 0x70, 0x73, 0x22, 0xf9,
|
||||||
|
0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73,
|
||||||
|
0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73,
|
||||||
|
0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x53, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x67, 0x65, 0x73, 0x74,
|
||||||
|
0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x76, 0x32, 0x72, 0x61,
|
||||||
|
0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
|
||||||
|
0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x79, 0x73, 0x74, 0x65, 0x72,
|
||||||
|
0x69, 0x61, 0x32, 0x2e, 0x43, 0x6f, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a,
|
||||||
|
0x63, 0x6f, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x69, 0x67,
|
||||||
|
0x6e, 0x6f, 0x72, 0x65, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x61, 0x6e, 0x64,
|
||||||
|
0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x69, 0x67, 0x6e,
|
||||||
|
0x6f, 0x72, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64,
|
||||||
|
0x74, 0x68, 0x12, 0x2a, 0x0a, 0x11, 0x75, 0x73, 0x65, 0x5f, 0x75, 0x64, 0x70, 0x5f, 0x65, 0x78,
|
||||||
|
0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x75,
|
||||||
|
0x73, 0x65, 0x55, 0x64, 0x70, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x1a,
|
||||||
|
0x82, 0xb5, 0x18, 0x16, 0x0a, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12,
|
||||||
|
0x09, 0x68, 0x79, 0x73, 0x74, 0x65, 0x72, 0x69, 0x61, 0x32, 0x42, 0x96, 0x01, 0x0a, 0x2b, 0x63,
|
||||||
|
0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72,
|
||||||
|
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
||||||
|
0x2e, 0x68, 0x79, 0x73, 0x74, 0x65, 0x72, 0x69, 0x61, 0x32, 0x50, 0x01, 0x5a, 0x3b, 0x67, 0x69,
|
||||||
|
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76,
|
||||||
|
0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f, 0x74, 0x72, 0x61,
|
||||||
|
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f,
|
||||||
|
0x68, 0x79, 0x73, 0x74, 0x65, 0x72, 0x69, 0x61, 0x32, 0xaa, 0x02, 0x27, 0x56, 0x32, 0x52, 0x61,
|
||||||
|
0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
|
||||||
|
0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x48, 0x79, 0x73, 0x74, 0x65, 0x72,
|
||||||
|
0x69, 0x61, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_transport_internet_hysteria2_config_proto_rawDescOnce sync.Once
|
||||||
|
file_transport_internet_hysteria2_config_proto_rawDescData = file_transport_internet_hysteria2_config_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_transport_internet_hysteria2_config_proto_rawDescGZIP() []byte {
|
||||||
|
file_transport_internet_hysteria2_config_proto_rawDescOnce.Do(func() {
|
||||||
|
file_transport_internet_hysteria2_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_hysteria2_config_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_transport_internet_hysteria2_config_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_transport_internet_hysteria2_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||||
|
var file_transport_internet_hysteria2_config_proto_goTypes = []any{
|
||||||
|
(*Congestion)(nil), // 0: v2ray.core.transport.internet.hysteria2.Congestion
|
||||||
|
(*Config)(nil), // 1: v2ray.core.transport.internet.hysteria2.Config
|
||||||
|
}
|
||||||
|
var file_transport_internet_hysteria2_config_proto_depIdxs = []int32{
|
||||||
|
0, // 0: v2ray.core.transport.internet.hysteria2.Config.congestion:type_name -> v2ray.core.transport.internet.hysteria2.Congestion
|
||||||
|
1, // [1:1] is the sub-list for method output_type
|
||||||
|
1, // [1:1] is the sub-list for method input_type
|
||||||
|
1, // [1:1] is the sub-list for extension type_name
|
||||||
|
1, // [1:1] is the sub-list for extension extendee
|
||||||
|
0, // [0:1] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_transport_internet_hysteria2_config_proto_init() }
|
||||||
|
func file_transport_internet_hysteria2_config_proto_init() {
|
||||||
|
if File_transport_internet_hysteria2_config_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_transport_internet_hysteria2_config_proto_msgTypes[0].Exporter = func(v any, i int) any {
|
||||||
|
switch v := v.(*Congestion); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_transport_internet_hysteria2_config_proto_msgTypes[1].Exporter = func(v any, i int) any {
|
||||||
|
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_hysteria2_config_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 2,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_transport_internet_hysteria2_config_proto_goTypes,
|
||||||
|
DependencyIndexes: file_transport_internet_hysteria2_config_proto_depIdxs,
|
||||||
|
MessageInfos: file_transport_internet_hysteria2_config_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_transport_internet_hysteria2_config_proto = out.File
|
||||||
|
file_transport_internet_hysteria2_config_proto_rawDesc = nil
|
||||||
|
file_transport_internet_hysteria2_config_proto_goTypes = nil
|
||||||
|
file_transport_internet_hysteria2_config_proto_depIdxs = nil
|
||||||
|
}
|
26
transport/internet/hysteria2/config.proto
Normal file
26
transport/internet/hysteria2/config.proto
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package v2ray.core.transport.internet.hysteria2;
|
||||||
|
option csharp_namespace = "V2Ray.Core.Transport.Internet.Hysteria2";
|
||||||
|
option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2";
|
||||||
|
option java_package = "com.v2ray.core.transport.internet.hysteria2";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
import "common/protoext/extensions.proto";
|
||||||
|
|
||||||
|
message Congestion{
|
||||||
|
string type = 1;
|
||||||
|
|
||||||
|
uint64 up_mbps = 2;
|
||||||
|
uint64 down_mbps = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Config {
|
||||||
|
option (v2ray.core.common.protoext.message_opt).type = "transport";
|
||||||
|
option (v2ray.core.common.protoext.message_opt).short_name = "hysteria2";
|
||||||
|
|
||||||
|
string password = 3;
|
||||||
|
Congestion congestion = 4;
|
||||||
|
bool ignore_client_bandwidth = 5;
|
||||||
|
bool use_udp_extension = 6;
|
||||||
|
}
|
130
transport/internet/hysteria2/conn.go
Normal file
130
transport/internet/hysteria2/conn.go
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
package hysteria2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
hyClient "github.com/v2fly/hysteria/core/v2/client"
|
||||||
|
"github.com/v2fly/hysteria/core/v2/international/protocol"
|
||||||
|
hyServer "github.com/v2fly/hysteria/core/v2/server"
|
||||||
|
"github.com/apernet/quic-go"
|
||||||
|
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
const CanNotUseUdpExtension = "Only hysteria2 proxy protocol can use udpExtension."
|
||||||
|
const Hy2MustNeedTLS = "Hysteria2 based on QUIC that requires TLS."
|
||||||
|
|
||||||
|
type HyConn struct {
|
||||||
|
IsUDPExtension bool
|
||||||
|
IsServer bool
|
||||||
|
ClientUDPSession hyClient.HyUDPConn
|
||||||
|
ServerUDPSession *hyServer.UdpSessionEntry
|
||||||
|
|
||||||
|
stream quic.Stream
|
||||||
|
local net.Addr
|
||||||
|
remote net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HyConn) Read(b []byte) (int, error) {
|
||||||
|
if c.IsUDPExtension {
|
||||||
|
n, data, _, err := c.ReadPacket()
|
||||||
|
copy(b, data)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
return c.stream.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HyConn) Write(b []byte) (int, error) {
|
||||||
|
if c.IsUDPExtension {
|
||||||
|
dest, _ := net.ParseDestination("udp:v2fly.org:6666")
|
||||||
|
return c.WritePacket(b, dest)
|
||||||
|
}
|
||||||
|
return c.stream.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HyConn) WritePacket(b []byte, dest net.Destination) (int, error) {
|
||||||
|
if !c.IsUDPExtension {
|
||||||
|
return 0, newError(CanNotUseUdpExtension)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.IsServer {
|
||||||
|
msg := &protocol.UDPMessage{
|
||||||
|
SessionID: c.ServerUDPSession.ID,
|
||||||
|
PacketID: 0,
|
||||||
|
FragID: 0,
|
||||||
|
FragCount: 1,
|
||||||
|
Addr: dest.NetAddr(),
|
||||||
|
Data: b,
|
||||||
|
}
|
||||||
|
c.ServerUDPSession.SendCh <- msg
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
return len(b), c.ClientUDPSession.Send(b, dest.NetAddr())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HyConn) ReadPacket() (int, []byte, *net.Destination, error) {
|
||||||
|
if !c.IsUDPExtension {
|
||||||
|
return 0, nil, nil, newError(CanNotUseUdpExtension)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.IsServer {
|
||||||
|
msg, ok := <-c.ServerUDPSession.ReceiveCh
|
||||||
|
if !ok {
|
||||||
|
return 0, nil, nil, newError("UDP session receive channel closed")
|
||||||
|
}
|
||||||
|
dest, err := net.ParseDestination("udp:" + msg.Addr)
|
||||||
|
return len(msg.Data), msg.Data, &dest, err
|
||||||
|
}
|
||||||
|
data, address, err := c.ClientUDPSession.Receive()
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, nil, err
|
||||||
|
}
|
||||||
|
dest, err := net.ParseDestination("udp:" + address)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, nil, err
|
||||||
|
}
|
||||||
|
return len(data), data, &dest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HyConn) Close() error {
|
||||||
|
if c.IsUDPExtension {
|
||||||
|
if !c.IsServer && c.ClientUDPSession == nil || (c.IsServer && c.ServerUDPSession == nil) {
|
||||||
|
return newError(CanNotUseUdpExtension)
|
||||||
|
}
|
||||||
|
if c.IsServer {
|
||||||
|
c.ServerUDPSession.CloseWithErr(nil)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.ClientUDPSession.Close()
|
||||||
|
}
|
||||||
|
return c.stream.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HyConn) LocalAddr() net.Addr {
|
||||||
|
return c.local
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HyConn) RemoteAddr() net.Addr {
|
||||||
|
return c.remote
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HyConn) SetDeadline(t time.Time) error {
|
||||||
|
if c.IsUDPExtension {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.stream.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HyConn) SetReadDeadline(t time.Time) error {
|
||||||
|
if c.IsUDPExtension {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.stream.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HyConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
if c.IsUDPExtension {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.stream.SetWriteDeadline(t)
|
||||||
|
}
|
207
transport/internet/hysteria2/dialer.go
Normal file
207
transport/internet/hysteria2/dialer.go
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
package hysteria2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
hyClient "github.com/v2fly/hysteria/core/v2/client"
|
||||||
|
hyProtocol "github.com/v2fly/hysteria/core/v2/international/protocol"
|
||||||
|
"github.com/apernet/quic-go/quicvarint"
|
||||||
|
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/net"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/session"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport/internet"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport/internet/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dialerConf struct {
|
||||||
|
net.Destination
|
||||||
|
*internet.MemoryStreamConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
var RunningClient map[dialerConf](hyClient.Client)
|
||||||
|
var ClientMutex sync.Mutex
|
||||||
|
var MBps uint64 = 1000000 / 8 // MByte
|
||||||
|
|
||||||
|
func GetClientTLSConfig(dest net.Destination, streamSettings *internet.MemoryStreamConfig) (*hyClient.TLSConfig, error) {
|
||||||
|
config := tls.ConfigFromStreamSettings(streamSettings)
|
||||||
|
if config == nil {
|
||||||
|
return nil, newError(Hy2MustNeedTLS)
|
||||||
|
}
|
||||||
|
tlsConfig := config.GetTLSConfig(tls.WithDestination(dest))
|
||||||
|
|
||||||
|
return &hyClient.TLSConfig{
|
||||||
|
RootCAs: tlsConfig.RootCAs,
|
||||||
|
ServerName: tlsConfig.ServerName,
|
||||||
|
InsecureSkipVerify: tlsConfig.InsecureSkipVerify,
|
||||||
|
VerifyPeerCertificate: tlsConfig.VerifyPeerCertificate,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveAddress(dest net.Destination) (net.Addr, error) {
|
||||||
|
var destAddr *net.UDPAddr
|
||||||
|
if dest.Address.Family().IsIP() {
|
||||||
|
destAddr = &net.UDPAddr{
|
||||||
|
IP: dest.Address.IP(),
|
||||||
|
Port: int(dest.Port),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addr, err := net.ResolveUDPAddr("udp", dest.NetAddr())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
destAddr = addr
|
||||||
|
}
|
||||||
|
return destAddr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type connFactory struct {
|
||||||
|
hyClient.ConnFactory
|
||||||
|
|
||||||
|
NewFunc func(addr net.Addr) (net.PacketConn, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *connFactory) New(addr net.Addr) (net.PacketConn, error) {
|
||||||
|
return f.NewFunc(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHyClient(dest net.Destination, streamSettings *internet.MemoryStreamConfig) (hyClient.Client, error) {
|
||||||
|
tlsConfig, err := GetClientTLSConfig(dest, streamSettings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
serverAddr, err := ResolveAddress(dest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
config := streamSettings.ProtocolSettings.(*Config)
|
||||||
|
client, _, err := hyClient.NewClient(&hyClient.Config{
|
||||||
|
Auth: config.GetPassword(),
|
||||||
|
TLSConfig: *tlsConfig,
|
||||||
|
ServerAddr: serverAddr,
|
||||||
|
ConnFactory: &connFactory{
|
||||||
|
NewFunc: func(addr net.Addr) (net.PacketConn, error) {
|
||||||
|
rawConn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{
|
||||||
|
IP: []byte{0, 0, 0, 0},
|
||||||
|
Port: 0,
|
||||||
|
}, streamSettings.SocketSettings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rawConn.(*net.UDPConn), nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BandwidthConfig: hyClient.BandwidthConfig{MaxTx: config.Congestion.GetUpMbps() * MBps, MaxRx: config.GetCongestion().GetDownMbps() * MBps},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CloseHyClient(dest net.Destination, streamSettings *internet.MemoryStreamConfig) error {
|
||||||
|
ClientMutex.Lock()
|
||||||
|
defer ClientMutex.Unlock()
|
||||||
|
|
||||||
|
client, found := RunningClient[dialerConf{dest, streamSettings}]
|
||||||
|
if found {
|
||||||
|
delete(RunningClient, dialerConf{dest, streamSettings})
|
||||||
|
return client.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHyClient(dest net.Destination, streamSettings *internet.MemoryStreamConfig) (hyClient.Client, error) {
|
||||||
|
var err error
|
||||||
|
var client hyClient.Client
|
||||||
|
|
||||||
|
ClientMutex.Lock()
|
||||||
|
client, found := RunningClient[dialerConf{dest, streamSettings}]
|
||||||
|
ClientMutex.Unlock()
|
||||||
|
if !found || !CheckHyClientHealthy(client) {
|
||||||
|
if found {
|
||||||
|
// retry
|
||||||
|
CloseHyClient(dest, streamSettings)
|
||||||
|
}
|
||||||
|
client, err = NewHyClient(dest, streamSettings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ClientMutex.Lock()
|
||||||
|
RunningClient[dialerConf{dest, streamSettings}] = client
|
||||||
|
ClientMutex.Unlock()
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckHyClientHealthy(client hyClient.Client) bool {
|
||||||
|
quicConn := client.GetQuicConn()
|
||||||
|
if quicConn == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-quicConn.Context().Done():
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) {
|
||||||
|
config := streamSettings.ProtocolSettings.(*Config)
|
||||||
|
|
||||||
|
client, err := GetHyClient(dest, streamSettings)
|
||||||
|
if err != nil {
|
||||||
|
CloseHyClient(dest, streamSettings)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
quicConn := client.GetQuicConn()
|
||||||
|
conn := &HyConn{
|
||||||
|
local: quicConn.LocalAddr(),
|
||||||
|
remote: quicConn.RemoteAddr(),
|
||||||
|
}
|
||||||
|
|
||||||
|
outbound := session.OutboundFromContext(ctx)
|
||||||
|
network := net.Network_TCP
|
||||||
|
if outbound != nil {
|
||||||
|
network = outbound.Target.Network
|
||||||
|
}
|
||||||
|
|
||||||
|
if network == net.Network_UDP && config.GetUseUdpExtension() { // only hysteria2 can use udpExtension
|
||||||
|
conn.IsUDPExtension = true
|
||||||
|
conn.IsServer = false
|
||||||
|
conn.ClientUDPSession, err = client.UDP()
|
||||||
|
if err != nil {
|
||||||
|
CloseHyClient(dest, streamSettings)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.stream, err = client.OpenStream()
|
||||||
|
if err != nil {
|
||||||
|
CloseHyClient(dest, streamSettings)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// write TCP frame type
|
||||||
|
frameSize := int(quicvarint.Len(hyProtocol.FrameTypeTCPRequest))
|
||||||
|
buf := make([]byte, frameSize)
|
||||||
|
hyProtocol.VarintPut(buf, hyProtocol.FrameTypeTCPRequest)
|
||||||
|
_, err = conn.stream.Write(buf)
|
||||||
|
if err != nil {
|
||||||
|
CloseHyClient(dest, streamSettings)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RunningClient = make(map[dialerConf]hyClient.Client)
|
||||||
|
common.Must(internet.RegisterTransportDialer(protocolName, Dial))
|
||||||
|
}
|
9
transport/internet/hysteria2/errors.generated.go
Normal file
9
transport/internet/hysteria2/errors.generated.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package hysteria2
|
||||||
|
|
||||||
|
import "github.com/v2fly/v2ray-core/v5/common/errors"
|
||||||
|
|
||||||
|
type errPathObjHolder struct{}
|
||||||
|
|
||||||
|
func newError(values ...interface{}) *errors.Error {
|
||||||
|
return errors.New(values...).WithPathObj(errPathObjHolder{})
|
||||||
|
}
|
128
transport/internet/hysteria2/hub.go
Normal file
128
transport/internet/hysteria2/hub.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package hysteria2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
hyServer "github.com/v2fly/hysteria/core/v2/server"
|
||||||
|
"github.com/apernet/quic-go"
|
||||||
|
"github.com/apernet/quic-go/http3"
|
||||||
|
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/net"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport/internet"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport/internet/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Listener is an internet.Listener that listens for TCP connections.
|
||||||
|
type Listener struct {
|
||||||
|
hyServer hyServer.Server
|
||||||
|
rawConn net.PacketConn
|
||||||
|
addConn internet.ConnHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr implements internet.Listener.Addr.
|
||||||
|
func (l *Listener) Addr() net.Addr {
|
||||||
|
return l.rawConn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements internet.Listener.Close.
|
||||||
|
func (l *Listener) Close() error {
|
||||||
|
return l.hyServer.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) StreamHijacker(ft http3.FrameType, conn quic.Connection, stream quic.Stream, err error) (bool, error) {
|
||||||
|
// err always == nil
|
||||||
|
|
||||||
|
tcpConn := &HyConn{
|
||||||
|
stream: stream,
|
||||||
|
local: conn.LocalAddr(),
|
||||||
|
remote: conn.RemoteAddr(),
|
||||||
|
}
|
||||||
|
l.addConn(tcpConn)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) UdpHijacker(entry *hyServer.UdpSessionEntry, originalAddr string) {
|
||||||
|
addr, err := net.ResolveUDPAddr("udp", originalAddr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
udpConn := &HyConn{
|
||||||
|
IsUDPExtension: true,
|
||||||
|
IsServer: true,
|
||||||
|
ServerUDPSession: entry,
|
||||||
|
remote: addr,
|
||||||
|
local: l.rawConn.LocalAddr(),
|
||||||
|
}
|
||||||
|
l.addConn(udpConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen creates a new Listener based on configurations.
|
||||||
|
func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) {
|
||||||
|
tlsConfig, err := GetServerTLSConfig(streamSettings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if address.Family().IsDomain() {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
config := streamSettings.ProtocolSettings.(*Config)
|
||||||
|
rawConn, err := internet.ListenSystemPacket(context.Background(),
|
||||||
|
&net.UDPAddr{
|
||||||
|
IP: address.IP(),
|
||||||
|
Port: int(port),
|
||||||
|
}, streamSettings.SocketSettings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
listener := &Listener{
|
||||||
|
rawConn: rawConn,
|
||||||
|
addConn: handler,
|
||||||
|
}
|
||||||
|
|
||||||
|
hyServer, err := hyServer.NewServer(&hyServer.Config{
|
||||||
|
Conn: rawConn,
|
||||||
|
TLSConfig: *tlsConfig,
|
||||||
|
DisableUDP: !config.GetUseUdpExtension(),
|
||||||
|
Authenticator: &Authenticator{Password: config.GetPassword()},
|
||||||
|
StreamHijacker: listener.StreamHijacker, // acceptStreams
|
||||||
|
BandwidthConfig: hyServer.BandwidthConfig{MaxTx: config.Congestion.GetUpMbps() * MBps, MaxRx: config.GetCongestion().GetDownMbps() * MBps},
|
||||||
|
UdpSessionHijacker: listener.UdpHijacker, // acceptUDPSession
|
||||||
|
IgnoreClientBandwidth: config.GetIgnoreClientBandwidth(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.hyServer = hyServer
|
||||||
|
go hyServer.Serve()
|
||||||
|
return listener, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetServerTLSConfig(streamSettings *internet.MemoryStreamConfig) (*hyServer.TLSConfig, error) {
|
||||||
|
config := tls.ConfigFromStreamSettings(streamSettings)
|
||||||
|
if config == nil {
|
||||||
|
return nil, newError(Hy2MustNeedTLS)
|
||||||
|
}
|
||||||
|
tlsConfig := config.GetTLSConfig()
|
||||||
|
|
||||||
|
return &hyServer.TLSConfig{Certificates: tlsConfig.Certificates, GetCertificate: tlsConfig.GetCertificate}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Authenticator struct {
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Authenticator) Authenticate(addr net.Addr, auth string, tx uint64) (ok bool, id string) {
|
||||||
|
if auth == a.Password || a.Password == "" {
|
||||||
|
return true, "user"
|
||||||
|
}
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(internet.RegisterTransportListener(protocolName, Listen))
|
||||||
|
}
|
155
transport/internet/hysteria2/hy2_transport_test.go
Normal file
155
transport/internet/hysteria2/hy2_transport_test.go
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
package hysteria2_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/buf"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/net"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/protocol/tls/cert"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/session"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/testing/servers/udp"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport/internet"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport/internet/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTCP(t *testing.T) {
|
||||||
|
port := udp.PickPort()
|
||||||
|
|
||||||
|
listener, err := hysteria2.Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{
|
||||||
|
ProtocolName: "hysteria2",
|
||||||
|
ProtocolSettings: &hysteria2.Config{Password: "123"},
|
||||||
|
SecurityType: "tls",
|
||||||
|
SecuritySettings: &tls.Config{
|
||||||
|
Certificate: []*tls.Certificate{
|
||||||
|
tls.ParseCertificate(
|
||||||
|
cert.MustGenerate(nil,
|
||||||
|
cert.DNSNames("www.v2fly.org"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, func(conn internet.Connection) {
|
||||||
|
go func() {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
b := buf.New()
|
||||||
|
defer b.Release()
|
||||||
|
|
||||||
|
for {
|
||||||
|
b.Clear()
|
||||||
|
if _, err := b.ReadFrom(conn); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.Must2(conn.Write(b.Bytes()))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
dctx := context.Background()
|
||||||
|
conn, err := hysteria2.Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{
|
||||||
|
ProtocolName: "hysteria2",
|
||||||
|
ProtocolSettings: &hysteria2.Config{Password: "123"},
|
||||||
|
SecurityType: "tls",
|
||||||
|
SecuritySettings: &tls.Config{
|
||||||
|
ServerName: "www.v2fly.org",
|
||||||
|
AllowInsecure: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
common.Must(err)
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
const N = 1000
|
||||||
|
b1 := make([]byte, N)
|
||||||
|
common.Must2(rand.Read(b1))
|
||||||
|
b2 := buf.New()
|
||||||
|
|
||||||
|
common.Must2(conn.Write(b1))
|
||||||
|
|
||||||
|
b2.Clear()
|
||||||
|
common.Must2(b2.ReadFullFrom(conn, N))
|
||||||
|
if r := cmp.Diff(b2.Bytes(), b1); r != "" {
|
||||||
|
t.Error(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUDP(t *testing.T) {
|
||||||
|
port := udp.PickPort()
|
||||||
|
|
||||||
|
listener, err := hysteria2.Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{
|
||||||
|
ProtocolName: "hysteria2",
|
||||||
|
ProtocolSettings: &hysteria2.Config{Password: "123", UseUdpExtension: true},
|
||||||
|
SecurityType: "tls",
|
||||||
|
SecuritySettings: &tls.Config{
|
||||||
|
Certificate: []*tls.Certificate{
|
||||||
|
tls.ParseCertificate(
|
||||||
|
cert.MustGenerate(nil,
|
||||||
|
cert.DNSNames("www.v2fly.org"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, func(conn internet.Connection) {
|
||||||
|
fmt.Println("incoming")
|
||||||
|
go func() {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
b := buf.New()
|
||||||
|
defer b.Release()
|
||||||
|
|
||||||
|
for {
|
||||||
|
b.Clear()
|
||||||
|
if _, err := b.ReadFrom(conn); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.Must2(conn.Write(b.Bytes()))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
address, err := net.ParseDestination("udp:127.0.0.1:1180")
|
||||||
|
dctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: address})
|
||||||
|
|
||||||
|
conn, err := hysteria2.Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{
|
||||||
|
ProtocolName: "hysteria2",
|
||||||
|
ProtocolSettings: &hysteria2.Config{Password: "123", UseUdpExtension: true},
|
||||||
|
SecurityType: "tls",
|
||||||
|
SecuritySettings: &tls.Config{
|
||||||
|
ServerName: "www.v2fly.org",
|
||||||
|
AllowInsecure: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
common.Must(err)
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
const N = 1000
|
||||||
|
b1 := make([]byte, N)
|
||||||
|
common.Must2(rand.Read(b1))
|
||||||
|
common.Must2(conn.Write(b1))
|
||||||
|
|
||||||
|
b2 := buf.New()
|
||||||
|
b2.Clear()
|
||||||
|
common.Must2(b2.ReadFullFrom(conn, N))
|
||||||
|
if r := cmp.Diff(b2.Bytes(), b1); r != "" {
|
||||||
|
t.Error(r)
|
||||||
|
}
|
||||||
|
}
|
18
transport/internet/hysteria2/hysteria2.go
Normal file
18
transport/internet/hysteria2/hysteria2.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package hysteria2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport/internet"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen
|
||||||
|
|
||||||
|
const (
|
||||||
|
protocolName = "hysteria2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} {
|
||||||
|
return new(Config)
|
||||||
|
}))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user