mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-09-23 01:27:04 +00:00
Hysteria: Upgrade to official v2.12.2 (#6565)
https://github.com/XTLS/Xray-core/pull/6565#issuecomment-5202554275 Fixes https://github.com/XTLS/Xray-core/issues/6408 Fixes https://github.com/XTLS/Xray-core/issues/6598 ...
This commit is contained in:
@@ -3,7 +3,7 @@ module github.com/xtls/xray-core
|
|||||||
go 1.26
|
go 1.26
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/apernet/quic-go v0.59.1-0.20260425001925-6c6cc9bcb716
|
github.com/apernet/quic-go v0.61.1-0.20260806010916-184d081eef3e
|
||||||
github.com/cloudflare/circl v1.6.5
|
github.com/cloudflare/circl v1.6.5
|
||||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344
|
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344
|
||||||
github.com/golang/mock v1.7.0-rc.1
|
github.com/golang/mock v1.7.0-rc.1
|
||||||
@@ -11,6 +11,7 @@ require (
|
|||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
github.com/klauspost/cpuid/v2 v2.4.0
|
github.com/klauspost/cpuid/v2 v2.4.0
|
||||||
|
github.com/libp2p/go-nat v1.0.1-0.20250821073202-01afc089f138
|
||||||
github.com/miekg/dns v1.1.73
|
github.com/miekg/dns v1.1.73
|
||||||
github.com/pelletier/go-toml v1.9.5
|
github.com/pelletier/go-toml v1.9.5
|
||||||
github.com/pion/stun/v3 v3.1.7
|
github.com/pion/stun/v3 v3.1.7
|
||||||
@@ -41,9 +42,14 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||||
github.com/google/btree v1.1.2 // indirect
|
github.com/google/btree v1.1.2 // indirect
|
||||||
|
github.com/google/gopacket v1.1.19 // indirect
|
||||||
|
github.com/huin/goupnp v1.2.0 // indirect
|
||||||
|
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||||
github.com/juju/ratelimit v1.0.2 // indirect
|
github.com/juju/ratelimit v1.0.2 // indirect
|
||||||
github.com/klauspost/compress v1.17.4 // indirect
|
github.com/klauspost/compress v1.17.4 // indirect
|
||||||
|
github.com/koron/go-ssdp v0.0.4 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
github.com/libp2p/go-netroute v0.2.1 // indirect
|
||||||
github.com/pion/dtls/v3 v3.1.5 // indirect
|
github.com/pion/dtls/v3 v3.1.5 // indirect
|
||||||
github.com/pion/logging v0.2.4 // indirect
|
github.com/pion/logging v0.2.4 // indirect
|
||||||
github.com/pion/transport/v4 v4.1.0 // indirect
|
github.com/pion/transport/v4 v4.1.0 // indirect
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
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.59.1-0.20260425001925-6c6cc9bcb716 h1:J1O+xpLuJWkdYbw5JPGwBqIHs2J8tiEP7Py9lPqkN2I=
|
github.com/apernet/quic-go v0.61.1-0.20260806010916-184d081eef3e h1:5mgtR5gwIgBKMiGI1QdXldZZ+SNor06Nbu1wCBulQBg=
|
||||||
github.com/apernet/quic-go v0.59.1-0.20260425001925-6c6cc9bcb716/go.mod h1:Npbg8qBtAZlsAB3FWmqwlVh5jtVG6a4DlYsOylUpvzA=
|
github.com/apernet/quic-go v0.61.1-0.20260806010916-184d081eef3e/go.mod h1:x7qxEvX6MCVtDuBKHj3E+88+BtrbEMuAL5qGUKItjW8=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cloudflare/circl v1.6.5 h1:O64F26HEqNhznd/hrC5KZXVKYuKM2rx4deZDTc4ihQA=
|
github.com/cloudflare/circl v1.6.5 h1:O64F26HEqNhznd/hrC5KZXVKYuKM2rx4deZDTc4ihQA=
|
||||||
@@ -21,22 +21,34 @@ github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
|||||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||||
|
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364 h1:5XxdakFhqd9dnXoAZy1Mb2R/DZ6D1e+0bGC/JhucGYI=
|
github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364 h1:5XxdakFhqd9dnXoAZy1Mb2R/DZ6D1e+0bGC/JhucGYI=
|
||||||
github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c=
|
github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c=
|
||||||
|
github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY=
|
||||||
|
github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
|
||||||
|
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||||
|
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||||
github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
|
github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
|
||||||
github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
|
github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
|
||||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||||
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
|
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
|
||||||
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
|
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
|
||||||
|
github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0=
|
||||||
|
github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/libp2p/go-nat v1.0.1-0.20250821073202-01afc089f138 h1:YohuNPT/1k3VcThCQlBZ43PCPWPfMRS1zcxWBF2SLK8=
|
||||||
|
github.com/libp2p/go-nat v1.0.1-0.20250821073202-01afc089f138/go.mod h1:TXQg5tfSy+bUjnhT5728j5j/MBj7keIYqqZ1+8k/ui8=
|
||||||
|
github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU=
|
||||||
|
github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ=
|
||||||
github.com/miekg/dns v1.1.73 h1:uhT8nJxmTrPJYClxVxTCX+CVn6qnzSiybRk72Z6DgrE=
|
github.com/miekg/dns v1.1.73 h1:uhT8nJxmTrPJYClxVxTCX+CVn6qnzSiybRk72Z6DgrE=
|
||||||
github.com/miekg/dns v1.1.73/go.mod h1:RW2Obtfd5NZHvOFe3zYG0W8koWOQtAzyHaLo8vASBuQ=
|
github.com/miekg/dns v1.1.73/go.mod h1:RW2Obtfd5NZHvOFe3zYG0W8koWOQtAzyHaLo8vASBuQ=
|
||||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||||
@@ -53,6 +65,8 @@ github.com/pion/transport/v4 v4.1.0 h1:8S+nF2reM2cJuqC6g78OVy2BBgmbdns+acx3jA97B
|
|||||||
github.com/pion/transport/v4 v4.1.0/go.mod h1:06hFI+jCFcok2X2MekVufNZ/uzNZXivGBPfviSVcjgM=
|
github.com/pion/transport/v4 v4.1.0/go.mod h1:06hFI+jCFcok2X2MekVufNZ/uzNZXivGBPfviSVcjgM=
|
||||||
github.com/pires/go-proxyproto v0.15.0 h1:dTshmNbFm/D+0+sbrxUuddPOZ5Y0B7c5NhtsBkm6LqI=
|
github.com/pires/go-proxyproto v0.15.0 h1:dTshmNbFm/D+0+sbrxUuddPOZ5Y0B7c5NhtsBkm6LqI=
|
||||||
github.com/pires/go-proxyproto v0.15.0/go.mod h1:OXsCrKwrK2tXS9YrI5tkHx5xaQlO8FH3lFW76orFh24=
|
github.com/pires/go-proxyproto v0.15.0/go.mod h1:OXsCrKwrK2tXS9YrI5tkHx5xaQlO8FH3lFW76orFh24=
|
||||||
|
github.com/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4TzM7MFjy0=
|
||||||
|
github.com/quic-go/go-ossfuzz-seeds v0.1.0/go.mod h1:3IOHRbJIc+L6YKMwfDtJAM9Vj9k0YY4muhuyUYk5tbk=
|
||||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||||
github.com/refraction-networking/utls v1.8.3-0.20260301010127-aa6edf4b11af h1:er2acxbi3N1nvEq6HXHUAR1nTWEJmQfqiGR8EVT9rfs=
|
github.com/refraction-networking/utls v1.8.3-0.20260301010127-aa6edf4b11af h1:er2acxbi3N1nvEq6HXHUAR1nTWEJmQfqiGR8EVT9rfs=
|
||||||
@@ -100,6 +114,8 @@ golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
|
|||||||
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
|
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
@@ -129,6 +145,7 @@ golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
|||||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|||||||
@@ -816,9 +816,11 @@ func (c *Xicmp) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Realm struct {
|
type Realm struct {
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
StunServers []string `json:"stunServers"`
|
StunServers []string `json:"stunServers"`
|
||||||
TlsConfig *TLSConfig `json:"tlsConfig"`
|
TlsConfig *TLSConfig `json:"tlsConfig"`
|
||||||
|
IPMode string `json:"ipMode"`
|
||||||
|
PortMapping *realm.PortMapping `json:"portMapping"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Realm) Build() (proto.Message, error) {
|
func (c *Realm) Build() (proto.Message, error) {
|
||||||
@@ -898,6 +900,8 @@ func (c *Realm) Build() (proto.Message, error) {
|
|||||||
ID: id,
|
ID: id,
|
||||||
StunServers: stunServers,
|
StunServers: stunServers,
|
||||||
TlsConfig: tlsConfig,
|
TlsConfig: tlsConfig,
|
||||||
|
IPMode: strings.ToLower(c.IPMode),
|
||||||
|
PortMapping: c.PortMapping,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -928,20 +932,24 @@ func (c *Mask) Build(tcp bool) (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type QuicParamsConfig struct {
|
type QuicParamsConfig struct {
|
||||||
Congestion string `json:"congestion"`
|
Congestion string `json:"congestion"`
|
||||||
Debug bool `json:"debug"`
|
Debug bool `json:"debug"`
|
||||||
BbrProfile string `json:"bbrProfile"`
|
BbrProfile string `json:"bbrProfile"`
|
||||||
BrutalUp Bandwidth `json:"brutalUp"`
|
BrutalUp Bandwidth `json:"brutalUp"`
|
||||||
BrutalDown Bandwidth `json:"brutalDown"`
|
BrutalDown Bandwidth `json:"brutalDown"`
|
||||||
UdpHop UdpHop `json:"udpHop"`
|
BrutalDisableLossCompensation bool `json:"brutalDisableLossCompensation"`
|
||||||
InitStreamReceiveWindow uint64 `json:"initStreamReceiveWindow"`
|
UdpHop UdpHop `json:"udpHop"`
|
||||||
MaxStreamReceiveWindow uint64 `json:"maxStreamReceiveWindow"`
|
InitStreamReceiveWindow uint64 `json:"initStreamReceiveWindow"`
|
||||||
InitConnectionReceiveWindow uint64 `json:"initConnectionReceiveWindow"`
|
MaxStreamReceiveWindow uint64 `json:"maxStreamReceiveWindow"`
|
||||||
MaxConnectionReceiveWindow uint64 `json:"maxConnectionReceiveWindow"`
|
InitConnectionReceiveWindow uint64 `json:"initConnectionReceiveWindow"`
|
||||||
MaxIdleTimeout int64 `json:"maxIdleTimeout"`
|
MaxConnectionReceiveWindow uint64 `json:"maxConnectionReceiveWindow"`
|
||||||
KeepAlivePeriod int64 `json:"keepAlivePeriod"`
|
MaxIdleTimeout int64 `json:"maxIdleTimeout"`
|
||||||
DisablePathMTUDiscovery bool `json:"disablePathMTUDiscovery"`
|
KeepAlivePeriod int64 `json:"keepAlivePeriod"`
|
||||||
MaxIncomingStreams int64 `json:"maxIncomingStreams"`
|
DisablePathMTUDiscovery bool `json:"disablePathMTUDiscovery"`
|
||||||
|
DisableChromeParrot bool `json:"disableChromeParrot"`
|
||||||
|
DisableGSO bool `json:"disableGSO"`
|
||||||
|
MaxIncomingStreams int64 `json:"maxIncomingStreams"`
|
||||||
|
DisableStatelessReset bool `json:"disableStatelessReset"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FinalMask struct {
|
type FinalMask struct {
|
||||||
|
|||||||
@@ -285,10 +285,11 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
config.QuicParams = &internet.QuicParams{
|
config.QuicParams = &internet.QuicParams{
|
||||||
Congestion: c.FinalMask.QuicParams.Congestion,
|
Congestion: c.FinalMask.QuicParams.Congestion,
|
||||||
BbrProfile: profile,
|
BbrProfile: profile,
|
||||||
BrutalUp: up,
|
BrutalUp: up,
|
||||||
BrutalDown: down,
|
BrutalDown: down,
|
||||||
|
BrutalDisableLossCompensation: c.FinalMask.QuicParams.BrutalDisableLossCompensation,
|
||||||
UdpHop: &internet.UdpHop{
|
UdpHop: &internet.UdpHop{
|
||||||
Ports: c.FinalMask.QuicParams.UdpHop.PortList.Build().Ports(),
|
Ports: c.FinalMask.QuicParams.UdpHop.PortList.Build().Ports(),
|
||||||
IntervalMin: int64(c.FinalMask.QuicParams.UdpHop.Interval.From),
|
IntervalMin: int64(c.FinalMask.QuicParams.UdpHop.Interval.From),
|
||||||
@@ -301,7 +302,10 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
|
|||||||
MaxIdleTimeout: c.FinalMask.QuicParams.MaxIdleTimeout,
|
MaxIdleTimeout: c.FinalMask.QuicParams.MaxIdleTimeout,
|
||||||
KeepAlivePeriod: c.FinalMask.QuicParams.KeepAlivePeriod,
|
KeepAlivePeriod: c.FinalMask.QuicParams.KeepAlivePeriod,
|
||||||
DisablePathMtuDiscovery: c.FinalMask.QuicParams.DisablePathMTUDiscovery,
|
DisablePathMtuDiscovery: c.FinalMask.QuicParams.DisablePathMTUDiscovery,
|
||||||
|
DisableChromeParrot: c.FinalMask.QuicParams.DisableChromeParrot,
|
||||||
|
DisableGSO: c.FinalMask.QuicParams.DisableGSO,
|
||||||
MaxIncomingStreams: c.FinalMask.QuicParams.MaxIncomingStreams,
|
MaxIncomingStreams: c.FinalMask.QuicParams.MaxIncomingStreams,
|
||||||
|
DisableStatelessReset: c.FinalMask.QuicParams.DisableStatelessReset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -751,6 +751,7 @@ type Masquerade struct {
|
|||||||
|
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
RewriteHost bool `json:"rewriteHost"`
|
RewriteHost bool `json:"rewriteHost"`
|
||||||
|
XForwarded bool `json:"xForwarded"`
|
||||||
Insecure bool `json:"insecure"`
|
Insecure bool `json:"insecure"`
|
||||||
|
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
@@ -791,6 +792,7 @@ func (c *HysteriaConfig) Build() (proto.Message, error) {
|
|||||||
config.MasqFile = c.Masquerade.Dir
|
config.MasqFile = c.Masquerade.Dir
|
||||||
config.MasqUrl = c.Masquerade.Url
|
config.MasqUrl = c.Masquerade.Url
|
||||||
config.MasqUrlRewriteHost = c.Masquerade.RewriteHost
|
config.MasqUrlRewriteHost = c.Masquerade.RewriteHost
|
||||||
|
config.MasqUrlXForwarded = c.Masquerade.XForwarded
|
||||||
config.MasqUrlInsecure = c.Masquerade.Insecure
|
config.MasqUrlInsecure = c.Masquerade.Insecure
|
||||||
config.MasqString = c.Masquerade.Content
|
config.MasqString = c.Masquerade.Content
|
||||||
config.MasqStringHeaders = c.Masquerade.Headers
|
config.MasqStringHeaders = c.Masquerade.Headers
|
||||||
|
|||||||
@@ -443,22 +443,26 @@ func (x *UdpHop) GetIntervalMax() int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type QuicParams struct {
|
type QuicParams struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Congestion string `protobuf:"bytes,1,opt,name=congestion,proto3" json:"congestion,omitempty"`
|
Congestion string `protobuf:"bytes,1,opt,name=congestion,proto3" json:"congestion,omitempty"`
|
||||||
BbrProfile string `protobuf:"bytes,2,opt,name=bbr_profile,json=bbrProfile,proto3" json:"bbr_profile,omitempty"`
|
BbrProfile string `protobuf:"bytes,2,opt,name=bbr_profile,json=bbrProfile,proto3" json:"bbr_profile,omitempty"`
|
||||||
BrutalUp uint64 `protobuf:"varint,3,opt,name=brutal_up,json=brutalUp,proto3" json:"brutal_up,omitempty"`
|
BrutalUp uint64 `protobuf:"varint,3,opt,name=brutal_up,json=brutalUp,proto3" json:"brutal_up,omitempty"`
|
||||||
BrutalDown uint64 `protobuf:"varint,4,opt,name=brutal_down,json=brutalDown,proto3" json:"brutal_down,omitempty"`
|
BrutalDown uint64 `protobuf:"varint,4,opt,name=brutal_down,json=brutalDown,proto3" json:"brutal_down,omitempty"`
|
||||||
UdpHop *UdpHop `protobuf:"bytes,5,opt,name=udp_hop,json=udpHop,proto3" json:"udp_hop,omitempty"`
|
BrutalDisableLossCompensation bool `protobuf:"varint,5,opt,name=brutal_disable_loss_compensation,json=brutalDisableLossCompensation,proto3" json:"brutal_disable_loss_compensation,omitempty"`
|
||||||
InitStreamReceiveWindow uint64 `protobuf:"varint,6,opt,name=init_stream_receive_window,json=initStreamReceiveWindow,proto3" json:"init_stream_receive_window,omitempty"`
|
UdpHop *UdpHop `protobuf:"bytes,6,opt,name=udp_hop,json=udpHop,proto3" json:"udp_hop,omitempty"`
|
||||||
MaxStreamReceiveWindow uint64 `protobuf:"varint,7,opt,name=max_stream_receive_window,json=maxStreamReceiveWindow,proto3" json:"max_stream_receive_window,omitempty"`
|
InitStreamReceiveWindow uint64 `protobuf:"varint,7,opt,name=init_stream_receive_window,json=initStreamReceiveWindow,proto3" json:"init_stream_receive_window,omitempty"`
|
||||||
InitConnReceiveWindow uint64 `protobuf:"varint,8,opt,name=init_conn_receive_window,json=initConnReceiveWindow,proto3" json:"init_conn_receive_window,omitempty"`
|
MaxStreamReceiveWindow uint64 `protobuf:"varint,8,opt,name=max_stream_receive_window,json=maxStreamReceiveWindow,proto3" json:"max_stream_receive_window,omitempty"`
|
||||||
MaxConnReceiveWindow uint64 `protobuf:"varint,9,opt,name=max_conn_receive_window,json=maxConnReceiveWindow,proto3" json:"max_conn_receive_window,omitempty"`
|
InitConnReceiveWindow uint64 `protobuf:"varint,9,opt,name=init_conn_receive_window,json=initConnReceiveWindow,proto3" json:"init_conn_receive_window,omitempty"`
|
||||||
MaxIdleTimeout int64 `protobuf:"varint,10,opt,name=max_idle_timeout,json=maxIdleTimeout,proto3" json:"max_idle_timeout,omitempty"`
|
MaxConnReceiveWindow uint64 `protobuf:"varint,10,opt,name=max_conn_receive_window,json=maxConnReceiveWindow,proto3" json:"max_conn_receive_window,omitempty"`
|
||||||
KeepAlivePeriod int64 `protobuf:"varint,11,opt,name=keep_alive_period,json=keepAlivePeriod,proto3" json:"keep_alive_period,omitempty"`
|
MaxIdleTimeout int64 `protobuf:"varint,11,opt,name=max_idle_timeout,json=maxIdleTimeout,proto3" json:"max_idle_timeout,omitempty"`
|
||||||
DisablePathMtuDiscovery bool `protobuf:"varint,12,opt,name=disable_path_mtu_discovery,json=disablePathMtuDiscovery,proto3" json:"disable_path_mtu_discovery,omitempty"`
|
KeepAlivePeriod int64 `protobuf:"varint,12,opt,name=keep_alive_period,json=keepAlivePeriod,proto3" json:"keep_alive_period,omitempty"`
|
||||||
MaxIncomingStreams int64 `protobuf:"varint,13,opt,name=max_incoming_streams,json=maxIncomingStreams,proto3" json:"max_incoming_streams,omitempty"`
|
DisablePathMtuDiscovery bool `protobuf:"varint,13,opt,name=disable_path_mtu_discovery,json=disablePathMtuDiscovery,proto3" json:"disable_path_mtu_discovery,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
DisableChromeParrot bool `protobuf:"varint,14,opt,name=disable_chrome_parrot,json=disableChromeParrot,proto3" json:"disable_chrome_parrot,omitempty"`
|
||||||
sizeCache protoimpl.SizeCache
|
DisableGSO bool `protobuf:"varint,15,opt,name=disableGSO,proto3" json:"disableGSO,omitempty"`
|
||||||
|
MaxIncomingStreams int64 `protobuf:"varint,16,opt,name=max_incoming_streams,json=maxIncomingStreams,proto3" json:"max_incoming_streams,omitempty"`
|
||||||
|
DisableStatelessReset bool `protobuf:"varint,17,opt,name=disable_stateless_reset,json=disableStatelessReset,proto3" json:"disable_stateless_reset,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *QuicParams) Reset() {
|
func (x *QuicParams) Reset() {
|
||||||
@@ -519,6 +523,13 @@ func (x *QuicParams) GetBrutalDown() uint64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *QuicParams) GetBrutalDisableLossCompensation() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.BrutalDisableLossCompensation
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (x *QuicParams) GetUdpHop() *UdpHop {
|
func (x *QuicParams) GetUdpHop() *UdpHop {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.UdpHop
|
return x.UdpHop
|
||||||
@@ -575,6 +586,20 @@ func (x *QuicParams) GetDisablePathMtuDiscovery() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *QuicParams) GetDisableChromeParrot() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.DisableChromeParrot
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *QuicParams) GetDisableGSO() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.DisableGSO
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (x *QuicParams) GetMaxIncomingStreams() int64 {
|
func (x *QuicParams) GetMaxIncomingStreams() int64 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.MaxIncomingStreams
|
return x.MaxIncomingStreams
|
||||||
@@ -582,6 +607,13 @@ func (x *QuicParams) GetMaxIncomingStreams() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *QuicParams) GetDisableStatelessReset() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.DisableStatelessReset
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type ProxyConfig struct {
|
type ProxyConfig struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||||
@@ -1020,7 +1052,7 @@ const file_transport_internet_config_proto_rawDesc = "" +
|
|||||||
"\x06UdpHop\x12\x14\n" +
|
"\x06UdpHop\x12\x14\n" +
|
||||||
"\x05ports\x18\x01 \x03(\rR\x05ports\x12!\n" +
|
"\x05ports\x18\x01 \x03(\rR\x05ports\x12!\n" +
|
||||||
"\finterval_min\x18\x02 \x01(\x03R\vintervalMin\x12!\n" +
|
"\finterval_min\x18\x02 \x01(\x03R\vintervalMin\x12!\n" +
|
||||||
"\finterval_max\x18\x03 \x01(\x03R\vintervalMax\"\xf2\x04\n" +
|
"\finterval_max\x18\x03 \x01(\x03R\vintervalMax\"\xc7\x06\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"QuicParams\x12\x1e\n" +
|
"QuicParams\x12\x1e\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
@@ -1030,17 +1062,23 @@ const file_transport_internet_config_proto_rawDesc = "" +
|
|||||||
"bbrProfile\x12\x1b\n" +
|
"bbrProfile\x12\x1b\n" +
|
||||||
"\tbrutal_up\x18\x03 \x01(\x04R\bbrutalUp\x12\x1f\n" +
|
"\tbrutal_up\x18\x03 \x01(\x04R\bbrutalUp\x12\x1f\n" +
|
||||||
"\vbrutal_down\x18\x04 \x01(\x04R\n" +
|
"\vbrutal_down\x18\x04 \x01(\x04R\n" +
|
||||||
"brutalDown\x128\n" +
|
"brutalDown\x12G\n" +
|
||||||
"\audp_hop\x18\x05 \x01(\v2\x1f.xray.transport.internet.UdpHopR\x06udpHop\x12;\n" +
|
" brutal_disable_loss_compensation\x18\x05 \x01(\bR\x1dbrutalDisableLossCompensation\x128\n" +
|
||||||
"\x1ainit_stream_receive_window\x18\x06 \x01(\x04R\x17initStreamReceiveWindow\x129\n" +
|
"\audp_hop\x18\x06 \x01(\v2\x1f.xray.transport.internet.UdpHopR\x06udpHop\x12;\n" +
|
||||||
"\x19max_stream_receive_window\x18\a \x01(\x04R\x16maxStreamReceiveWindow\x127\n" +
|
"\x1ainit_stream_receive_window\x18\a \x01(\x04R\x17initStreamReceiveWindow\x129\n" +
|
||||||
"\x18init_conn_receive_window\x18\b \x01(\x04R\x15initConnReceiveWindow\x125\n" +
|
"\x19max_stream_receive_window\x18\b \x01(\x04R\x16maxStreamReceiveWindow\x127\n" +
|
||||||
"\x17max_conn_receive_window\x18\t \x01(\x04R\x14maxConnReceiveWindow\x12(\n" +
|
"\x18init_conn_receive_window\x18\t \x01(\x04R\x15initConnReceiveWindow\x125\n" +
|
||||||
"\x10max_idle_timeout\x18\n" +
|
"\x17max_conn_receive_window\x18\n" +
|
||||||
" \x01(\x03R\x0emaxIdleTimeout\x12*\n" +
|
" \x01(\x04R\x14maxConnReceiveWindow\x12(\n" +
|
||||||
"\x11keep_alive_period\x18\v \x01(\x03R\x0fkeepAlivePeriod\x12;\n" +
|
"\x10max_idle_timeout\x18\v \x01(\x03R\x0emaxIdleTimeout\x12*\n" +
|
||||||
"\x1adisable_path_mtu_discovery\x18\f \x01(\bR\x17disablePathMtuDiscovery\x120\n" +
|
"\x11keep_alive_period\x18\f \x01(\x03R\x0fkeepAlivePeriod\x12;\n" +
|
||||||
"\x14max_incoming_streams\x18\r \x01(\x03R\x12maxIncomingStreams\"Q\n" +
|
"\x1adisable_path_mtu_discovery\x18\r \x01(\bR\x17disablePathMtuDiscovery\x122\n" +
|
||||||
|
"\x15disable_chrome_parrot\x18\x0e \x01(\bR\x13disableChromeParrot\x12\x1e\n" +
|
||||||
|
"\n" +
|
||||||
|
"disableGSO\x18\x0f \x01(\bR\n" +
|
||||||
|
"disableGSO\x120\n" +
|
||||||
|
"\x14max_incoming_streams\x18\x10 \x01(\x03R\x12maxIncomingStreams\x126\n" +
|
||||||
|
"\x17disable_stateless_reset\x18\x11 \x01(\bR\x15disableStatelessReset\"Q\n" +
|
||||||
"\vProxyConfig\x12\x10\n" +
|
"\vProxyConfig\x12\x10\n" +
|
||||||
"\x03tag\x18\x01 \x01(\tR\x03tag\x120\n" +
|
"\x03tag\x18\x01 \x01(\tR\x03tag\x120\n" +
|
||||||
"\x13transportLayerProxy\x18\x02 \x01(\bR\x13transportLayerProxy\"\x93\x01\n" +
|
"\x13transportLayerProxy\x18\x02 \x01(\bR\x13transportLayerProxy\"\x93\x01\n" +
|
||||||
|
|||||||
@@ -75,15 +75,19 @@ message QuicParams {
|
|||||||
string bbr_profile = 2;
|
string bbr_profile = 2;
|
||||||
uint64 brutal_up = 3;
|
uint64 brutal_up = 3;
|
||||||
uint64 brutal_down = 4;
|
uint64 brutal_down = 4;
|
||||||
UdpHop udp_hop = 5;
|
bool brutal_disable_loss_compensation = 5;
|
||||||
uint64 init_stream_receive_window = 6;
|
UdpHop udp_hop = 6;
|
||||||
uint64 max_stream_receive_window = 7;
|
uint64 init_stream_receive_window = 7;
|
||||||
uint64 init_conn_receive_window = 8;
|
uint64 max_stream_receive_window = 8;
|
||||||
uint64 max_conn_receive_window = 9;
|
uint64 init_conn_receive_window = 9;
|
||||||
int64 max_idle_timeout = 10;
|
uint64 max_conn_receive_window = 10;
|
||||||
int64 keep_alive_period = 11;
|
int64 max_idle_timeout = 11;
|
||||||
bool disable_path_mtu_discovery = 12;
|
int64 keep_alive_period = 12;
|
||||||
int64 max_incoming_streams = 13;
|
bool disable_path_mtu_discovery = 13;
|
||||||
|
bool disable_chrome_parrot = 14;
|
||||||
|
bool disableGSO = 15;
|
||||||
|
int64 max_incoming_streams = 16;
|
||||||
|
bool disable_stateless_reset = 17;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ProxyConfig {
|
message ProxyConfig {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pion/stun/v3"
|
"github.com/pion/stun/v3"
|
||||||
@@ -15,35 +16,67 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type realmConnClient struct {
|
type realmConnClient struct {
|
||||||
|
wg sync.WaitGroup
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
net.PacketConn
|
net.PacketConn
|
||||||
peer *net.UDPAddr
|
peer *net.UDPAddr
|
||||||
|
|
||||||
realmClient *Client
|
realmClient *Client
|
||||||
realmID string
|
realmID string
|
||||||
stunServers []string
|
stunServers []string
|
||||||
|
family Family
|
||||||
|
mapper *PortMapper
|
||||||
stunTimeout time.Duration
|
stunTimeout time.Duration
|
||||||
punchTimeout time.Duration
|
punchTimeout time.Duration
|
||||||
punchInterval time.Duration
|
punchInterval time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConnClient(config *Config, raw net.PacketConn) (net.PacketConn, error) {
|
func NewConnClient(config *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
family := Family_Dual
|
||||||
|
switch config.IPMode {
|
||||||
|
case "dual":
|
||||||
|
case "v4":
|
||||||
|
family = Family_V4
|
||||||
|
case "v6":
|
||||||
|
family = Family_V6
|
||||||
|
}
|
||||||
|
|
||||||
|
var mapper *PortMapper
|
||||||
|
if config.PortMapping != nil && config.PortMapping.Enabled {
|
||||||
|
var err error
|
||||||
|
start := time.Now()
|
||||||
|
mapper, err = NewPortMapper(context.Background(), raw.LocalAddr().(*net.UDPAddr).Port, PortMapConfig{Timeout: time.Duration(config.PortMapping.Timeout) * time.Second, Lifetime: time.Duration(config.PortMapping.Lifetime) * time.Second})
|
||||||
|
if err != nil {
|
||||||
|
errors.LogErrorInner(context.Background(), err, "[realm] [port mapping] [", raw.LocalAddr().(*net.UDPAddr).Port, "] init failed after ", time.Since(start))
|
||||||
|
} else {
|
||||||
|
errors.LogDebug(context.Background(), "[realm] [port mapping] [", mapper.InternalPort(), "] gateway ", mapper.GatewayType(), ", external ", mapper.ExternalAddr())
|
||||||
|
errors.LogDebug(context.Background(), "[realm] [port mapping] [", mapper.InternalPort(), "] init success with ", time.Since(start))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
conn := &realmConnClient{
|
conn := &realmConnClient{
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
PacketConn: raw,
|
PacketConn: raw,
|
||||||
|
|
||||||
realmClient: NewClient(config.Scheme, config.Host, config.Port, config.Token, config.TlsConfig),
|
realmClient: NewClient(config.Scheme, config.Host, config.Port, config.Token, config.TlsConfig),
|
||||||
realmID: config.ID,
|
realmID: config.ID,
|
||||||
stunServers: config.StunServers,
|
stunServers: config.StunServers,
|
||||||
|
family: family,
|
||||||
|
mapper: mapper,
|
||||||
stunTimeout: defaultSTUNTimeout,
|
stunTimeout: defaultSTUNTimeout,
|
||||||
punchTimeout: defaultPunchTimeout,
|
punchTimeout: defaultPunchTimeout,
|
||||||
punchInterval: defaultPunchInterval,
|
punchInterval: defaultPunchInterval,
|
||||||
}
|
}
|
||||||
|
|
||||||
return conn.getpeer()
|
return conn.getpeer()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *realmConnClient) getpeer() (net.PacketConn, error) {
|
func (c *realmConnClient) getpeer() (net.PacketConn, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
servers := resolveSTUNServers(c.PacketConn.LocalAddr().(*net.UDPAddr).IP, c.stunServers)
|
servers := resolveSTUNServers(c.PacketConn.LocalAddr().(*net.UDPAddr).IP, c.stunServers, c.family)
|
||||||
errors.LogDebug(context.Background(), "[realm] update stun servers ", servers, " with ", time.Since(start))
|
errors.LogDebug(context.Background(), "[realm] update stun servers ", servers, " with ", time.Since(start))
|
||||||
if len(servers) == 0 {
|
if len(servers) == 0 {
|
||||||
return nil, errors.New("empty locals")
|
return nil, errors.New("empty locals")
|
||||||
@@ -70,7 +103,7 @@ func (c *realmConnClient) getpeer() (net.PacketConn, error) {
|
|||||||
|
|
||||||
peers, _ := parseAddrPorts(resp.Addresses)
|
peers, _ := parseAddrPorts(resp.Addresses)
|
||||||
errors.LogDebug(context.Background(), "[realm] update peers ", peers)
|
errors.LogDebug(context.Background(), "[realm] update peers ", peers)
|
||||||
filteredPeers, seen := candidatePunchAddrs(locals, peers)
|
filteredPeers, seen := candidatePunchAddrs(locals, peers, c.family)
|
||||||
errors.LogDebug(context.Background(), "[realm] filtered peers ", filteredPeers)
|
errors.LogDebug(context.Background(), "[realm] filtered peers ", filteredPeers)
|
||||||
expandedPeers := expandSymmetricNATCandidates(filteredPeers, seen)
|
expandedPeers := expandSymmetricNATCandidates(filteredPeers, seen)
|
||||||
errors.LogDebug(context.Background(), "[realm] expanded peers ", expandedPeers)
|
errors.LogDebug(context.Background(), "[realm] expanded peers ", expandedPeers)
|
||||||
@@ -86,6 +119,11 @@ func (c *realmConnClient) getpeer() (net.PacketConn, error) {
|
|||||||
}
|
}
|
||||||
errors.LogDebug(context.Background(), "[realm] punch peer ", peer, " with ", time.Since(start))
|
errors.LogDebug(context.Background(), "[realm] punch peer ", peer, " with ", time.Since(start))
|
||||||
|
|
||||||
|
if c.mapper != nil {
|
||||||
|
c.wg.Add(1)
|
||||||
|
go portMapLoop(c.ctx, c.mapper, c.wg.Done)
|
||||||
|
}
|
||||||
|
|
||||||
c.peer = peer
|
c.peer = peer
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
@@ -116,10 +154,12 @@ func (c *realmConnClient) discover(servers []*net.UDPAddr) []netip.AddrPort {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.PacketConn.SetReadDeadline(time.Time{})
|
c.PacketConn.SetReadDeadline(time.Time{})
|
||||||
|
if c.mapper != nil {
|
||||||
|
results = insertAddr(results, c.mapper.ExternalAddr())
|
||||||
|
}
|
||||||
slices.SortFunc(results, func(a, b netip.AddrPort) int {
|
slices.SortFunc(results, func(a, b netip.AddrPort) int {
|
||||||
return strings.Compare(a.String(), b.String())
|
return strings.Compare(a.String(), b.String())
|
||||||
})
|
})
|
||||||
|
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,3 +209,48 @@ func (c *realmConnClient) punch(meta PunchMetadata, peers []netip.AddrPort) (*ne
|
|||||||
func (c *realmConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
func (c *realmConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
return c.PacketConn.WriteTo(p, c.peer)
|
return c.PacketConn.WriteTo(p, c.peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *realmConnClient) Close() error {
|
||||||
|
// Sadly, closing the core does not first close the sockets created for outbound connections
|
||||||
|
c.cancel()
|
||||||
|
c.wg.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func portMapLoop(ctx context.Context, mapper *PortMapper, done func()) {
|
||||||
|
defer func() {
|
||||||
|
err := mapper.Close()
|
||||||
|
done()
|
||||||
|
errors.LogDebug(context.Background(), "[realm] [port mapping] [", mapper.InternalPort(), "] removed with ", err)
|
||||||
|
}()
|
||||||
|
interval := mapper.Lifetime() / 2
|
||||||
|
if interval <= 0 {
|
||||||
|
interval = time.Minute
|
||||||
|
}
|
||||||
|
t := time.NewTicker(interval)
|
||||||
|
defer t.Stop()
|
||||||
|
failing := false
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-t.C:
|
||||||
|
changed, err := mapper.Renew(ctx)
|
||||||
|
if err != nil {
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !failing {
|
||||||
|
errors.LogError(context.Background(), "[realm] [port mapping] [", mapper.InternalPort(), "] renewal failed")
|
||||||
|
failing = true
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
errors.LogDebug(context.Background(), "[realm] [port mapping] [", mapper.InternalPort(), "] external ", mapper.ExternalAddr(), ", changed ", changed)
|
||||||
|
if failing {
|
||||||
|
errors.LogError(context.Background(), "[realm] [port mapping] [", mapper.InternalPort(), "] recovered")
|
||||||
|
failing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,6 +22,115 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Family int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
Family_Dual Family = 0
|
||||||
|
Family_V4 Family = 1
|
||||||
|
Family_V6 Family = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum value maps for Family.
|
||||||
|
var (
|
||||||
|
Family_name = map[int32]string{
|
||||||
|
0: "Dual",
|
||||||
|
1: "V4",
|
||||||
|
2: "V6",
|
||||||
|
}
|
||||||
|
Family_value = map[string]int32{
|
||||||
|
"Dual": 0,
|
||||||
|
"V4": 1,
|
||||||
|
"V6": 2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x Family) Enum() *Family {
|
||||||
|
p := new(Family)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Family) String() string {
|
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Family) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_transport_internet_finalmask_realm_config_proto_enumTypes[0].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Family) Type() protoreflect.EnumType {
|
||||||
|
return &file_transport_internet_finalmask_realm_config_proto_enumTypes[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Family) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Family.Descriptor instead.
|
||||||
|
func (Family) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return file_transport_internet_finalmask_realm_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PortMapping struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
||||||
|
Timeout int64 `protobuf:"varint,2,opt,name=timeout,proto3" json:"timeout,omitempty"`
|
||||||
|
Lifetime int64 `protobuf:"varint,3,opt,name=lifetime,proto3" json:"lifetime,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PortMapping) Reset() {
|
||||||
|
*x = PortMapping{}
|
||||||
|
mi := &file_transport_internet_finalmask_realm_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PortMapping) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*PortMapping) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *PortMapping) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_transport_internet_finalmask_realm_config_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use PortMapping.ProtoReflect.Descriptor instead.
|
||||||
|
func (*PortMapping) Descriptor() ([]byte, []int) {
|
||||||
|
return file_transport_internet_finalmask_realm_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PortMapping) GetEnabled() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.Enabled
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PortMapping) GetTimeout() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Timeout
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PortMapping) GetLifetime() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Lifetime
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Scheme string `protobuf:"bytes,1,opt,name=scheme,proto3" json:"scheme,omitempty"`
|
Scheme string `protobuf:"bytes,1,opt,name=scheme,proto3" json:"scheme,omitempty"`
|
||||||
@@ -31,13 +140,15 @@ type Config struct {
|
|||||||
ID string `protobuf:"bytes,5,opt,name=ID,proto3" json:"ID,omitempty"`
|
ID string `protobuf:"bytes,5,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||||
StunServers []string `protobuf:"bytes,6,rep,name=stun_servers,json=stunServers,proto3" json:"stun_servers,omitempty"`
|
StunServers []string `protobuf:"bytes,6,rep,name=stun_servers,json=stunServers,proto3" json:"stun_servers,omitempty"`
|
||||||
TlsConfig *tls.Config `protobuf:"bytes,7,opt,name=tls_config,json=tlsConfig,proto3" json:"tls_config,omitempty"`
|
TlsConfig *tls.Config `protobuf:"bytes,7,opt,name=tls_config,json=tlsConfig,proto3" json:"tls_config,omitempty"`
|
||||||
|
IPMode string `protobuf:"bytes,8,opt,name=IPMode,proto3" json:"IPMode,omitempty"`
|
||||||
|
PortMapping *PortMapping `protobuf:"bytes,9,opt,name=port_mapping,json=portMapping,proto3" json:"port_mapping,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
*x = Config{}
|
*x = Config{}
|
||||||
mi := &file_transport_internet_finalmask_realm_config_proto_msgTypes[0]
|
mi := &file_transport_internet_finalmask_realm_config_proto_msgTypes[1]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -49,7 +160,7 @@ func (x *Config) String() string {
|
|||||||
func (*Config) ProtoMessage() {}
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_transport_internet_finalmask_realm_config_proto_msgTypes[0]
|
mi := &file_transport_internet_finalmask_realm_config_proto_msgTypes[1]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -62,7 +173,7 @@ func (x *Config) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
func (*Config) Descriptor() ([]byte, []int) {
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
return file_transport_internet_finalmask_realm_config_proto_rawDescGZIP(), []int{0}
|
return file_transport_internet_finalmask_realm_config_proto_rawDescGZIP(), []int{1}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetScheme() string {
|
func (x *Config) GetScheme() string {
|
||||||
@@ -114,11 +225,29 @@ func (x *Config) GetTlsConfig() *tls.Config {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetIPMode() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.IPMode
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetPortMapping() *PortMapping {
|
||||||
|
if x != nil {
|
||||||
|
return x.PortMapping
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var File_transport_internet_finalmask_realm_config_proto protoreflect.FileDescriptor
|
var File_transport_internet_finalmask_realm_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
const file_transport_internet_finalmask_realm_config_proto_rawDesc = "" +
|
const file_transport_internet_finalmask_realm_config_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"/transport/internet/finalmask/realm/config.proto\x12'xray.transport.internet.finalmask.realm\x1a#transport/internet/tls/config.proto\"\xd5\x01\n" +
|
"/transport/internet/finalmask/realm/config.proto\x12'xray.transport.internet.finalmask.realm\x1a#transport/internet/tls/config.proto\"]\n" +
|
||||||
|
"\vPortMapping\x12\x18\n" +
|
||||||
|
"\aenabled\x18\x01 \x01(\bR\aenabled\x12\x18\n" +
|
||||||
|
"\atimeout\x18\x02 \x01(\x03R\atimeout\x12\x1a\n" +
|
||||||
|
"\blifetime\x18\x03 \x01(\x03R\blifetime\"\xc6\x02\n" +
|
||||||
"\x06Config\x12\x16\n" +
|
"\x06Config\x12\x16\n" +
|
||||||
"\x06scheme\x18\x01 \x01(\tR\x06scheme\x12\x12\n" +
|
"\x06scheme\x18\x01 \x01(\tR\x06scheme\x12\x12\n" +
|
||||||
"\x04host\x18\x02 \x01(\tR\x04host\x12\x12\n" +
|
"\x04host\x18\x02 \x01(\tR\x04host\x12\x12\n" +
|
||||||
@@ -127,7 +256,13 @@ const file_transport_internet_finalmask_realm_config_proto_rawDesc = "" +
|
|||||||
"\x02ID\x18\x05 \x01(\tR\x02ID\x12!\n" +
|
"\x02ID\x18\x05 \x01(\tR\x02ID\x12!\n" +
|
||||||
"\fstun_servers\x18\x06 \x03(\tR\vstunServers\x12B\n" +
|
"\fstun_servers\x18\x06 \x03(\tR\vstunServers\x12B\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"tls_config\x18\a \x01(\v2#.xray.transport.internet.tls.ConfigR\ttlsConfigB\x97\x01\n" +
|
"tls_config\x18\a \x01(\v2#.xray.transport.internet.tls.ConfigR\ttlsConfig\x12\x16\n" +
|
||||||
|
"\x06IPMode\x18\b \x01(\tR\x06IPMode\x12W\n" +
|
||||||
|
"\fport_mapping\x18\t \x01(\v24.xray.transport.internet.finalmask.realm.PortMappingR\vportMapping*\"\n" +
|
||||||
|
"\x06Family\x12\b\n" +
|
||||||
|
"\x04Dual\x10\x00\x12\x06\n" +
|
||||||
|
"\x02V4\x10\x01\x12\x06\n" +
|
||||||
|
"\x02V6\x10\x02B\x97\x01\n" +
|
||||||
"+com.xray.transport.internet.finalmask.realmP\x01Z<github.com/xtls/xray-core/transport/internet/finalmask/realm\xaa\x02'Xray.Transport.Internet.Finalmask.Realmb\x06proto3"
|
"+com.xray.transport.internet.finalmask.realmP\x01Z<github.com/xtls/xray-core/transport/internet/finalmask/realm\xaa\x02'Xray.Transport.Internet.Finalmask.Realmb\x06proto3"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -142,18 +277,22 @@ func file_transport_internet_finalmask_realm_config_proto_rawDescGZIP() []byte {
|
|||||||
return file_transport_internet_finalmask_realm_config_proto_rawDescData
|
return file_transport_internet_finalmask_realm_config_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_transport_internet_finalmask_realm_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
var file_transport_internet_finalmask_realm_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
|
var file_transport_internet_finalmask_realm_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||||
var file_transport_internet_finalmask_realm_config_proto_goTypes = []any{
|
var file_transport_internet_finalmask_realm_config_proto_goTypes = []any{
|
||||||
(*Config)(nil), // 0: xray.transport.internet.finalmask.realm.Config
|
(Family)(0), // 0: xray.transport.internet.finalmask.realm.Family
|
||||||
(*tls.Config)(nil), // 1: xray.transport.internet.tls.Config
|
(*PortMapping)(nil), // 1: xray.transport.internet.finalmask.realm.PortMapping
|
||||||
|
(*Config)(nil), // 2: xray.transport.internet.finalmask.realm.Config
|
||||||
|
(*tls.Config)(nil), // 3: xray.transport.internet.tls.Config
|
||||||
}
|
}
|
||||||
var file_transport_internet_finalmask_realm_config_proto_depIdxs = []int32{
|
var file_transport_internet_finalmask_realm_config_proto_depIdxs = []int32{
|
||||||
1, // 0: xray.transport.internet.finalmask.realm.Config.tls_config:type_name -> xray.transport.internet.tls.Config
|
3, // 0: xray.transport.internet.finalmask.realm.Config.tls_config:type_name -> xray.transport.internet.tls.Config
|
||||||
1, // [1:1] is the sub-list for method output_type
|
1, // 1: xray.transport.internet.finalmask.realm.Config.port_mapping:type_name -> xray.transport.internet.finalmask.realm.PortMapping
|
||||||
1, // [1:1] is the sub-list for method input_type
|
2, // [2:2] is the sub-list for method output_type
|
||||||
1, // [1:1] is the sub-list for extension type_name
|
2, // [2:2] is the sub-list for method input_type
|
||||||
1, // [1:1] is the sub-list for extension extendee
|
2, // [2:2] is the sub-list for extension type_name
|
||||||
0, // [0:1] is the sub-list for field 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_transport_internet_finalmask_realm_config_proto_init() }
|
func init() { file_transport_internet_finalmask_realm_config_proto_init() }
|
||||||
@@ -166,13 +305,14 @@ func file_transport_internet_finalmask_realm_config_proto_init() {
|
|||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_realm_config_proto_rawDesc), len(file_transport_internet_finalmask_realm_config_proto_rawDesc)),
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_realm_config_proto_rawDesc), len(file_transport_internet_finalmask_realm_config_proto_rawDesc)),
|
||||||
NumEnums: 0,
|
NumEnums: 1,
|
||||||
NumMessages: 1,
|
NumMessages: 2,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
GoTypes: file_transport_internet_finalmask_realm_config_proto_goTypes,
|
GoTypes: file_transport_internet_finalmask_realm_config_proto_goTypes,
|
||||||
DependencyIndexes: file_transport_internet_finalmask_realm_config_proto_depIdxs,
|
DependencyIndexes: file_transport_internet_finalmask_realm_config_proto_depIdxs,
|
||||||
|
EnumInfos: file_transport_internet_finalmask_realm_config_proto_enumTypes,
|
||||||
MessageInfos: file_transport_internet_finalmask_realm_config_proto_msgTypes,
|
MessageInfos: file_transport_internet_finalmask_realm_config_proto_msgTypes,
|
||||||
}.Build()
|
}.Build()
|
||||||
File_transport_internet_finalmask_realm_config_proto = out.File
|
File_transport_internet_finalmask_realm_config_proto = out.File
|
||||||
|
|||||||
@@ -8,6 +8,18 @@ option java_multiple_files = true;
|
|||||||
|
|
||||||
import "transport/internet/tls/config.proto";
|
import "transport/internet/tls/config.proto";
|
||||||
|
|
||||||
|
enum Family {
|
||||||
|
Dual = 0;
|
||||||
|
V4 = 1;
|
||||||
|
V6 = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PortMapping {
|
||||||
|
bool enabled = 1;
|
||||||
|
int64 timeout = 2;
|
||||||
|
int64 lifetime = 3;
|
||||||
|
}
|
||||||
|
|
||||||
message Config {
|
message Config {
|
||||||
string scheme = 1;
|
string scheme = 1;
|
||||||
string host = 2;
|
string host = 2;
|
||||||
@@ -16,4 +28,6 @@ message Config {
|
|||||||
string ID = 5;
|
string ID = 5;
|
||||||
repeated string stun_servers = 6;
|
repeated string stun_servers = 6;
|
||||||
xray.transport.internet.tls.Config tls_config = 7;
|
xray.transport.internet.tls.Config tls_config = 7;
|
||||||
|
string IPMode = 8;
|
||||||
|
PortMapping port_mapping = 9;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package realm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-nat"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultPortMapTimeout = 10 * time.Second
|
||||||
|
defaultPortMapLifetime = 10 * time.Minute
|
||||||
|
|
||||||
|
portMapDescription = "hysteria-realm"
|
||||||
|
portMapProtocol = "udp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrInvalidPortMapConfig = errors.New("invalid port mapping config")
|
||||||
|
|
||||||
|
type PortMapConfig struct {
|
||||||
|
Timeout time.Duration
|
||||||
|
Lifetime time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c PortMapConfig) withDefaults() (PortMapConfig, error) {
|
||||||
|
if c.Timeout == 0 {
|
||||||
|
c.Timeout = defaultPortMapTimeout
|
||||||
|
}
|
||||||
|
if c.Timeout < 0 {
|
||||||
|
return c, fmt.Errorf("%w: timeout must not be negative", ErrInvalidPortMapConfig)
|
||||||
|
}
|
||||||
|
if c.Lifetime == 0 {
|
||||||
|
c.Lifetime = defaultPortMapLifetime
|
||||||
|
}
|
||||||
|
if c.Lifetime < 0 {
|
||||||
|
return c, fmt.Errorf("%w: lifetime must not be negative", ErrInvalidPortMapConfig)
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortMapper maintains a UDP port mapping on the local gateway via UPnP or
|
||||||
|
// NAT-PMP. It does not renew the mapping by itself; the caller is expected
|
||||||
|
// to call Renew periodically (typically every Lifetime/2).
|
||||||
|
type PortMapper struct {
|
||||||
|
gateway nat.NAT
|
||||||
|
internalPort int
|
||||||
|
config PortMapConfig
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
externalAddr netip.AddrPort
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPortMapper discovers the local gateway and maps internalPort for UDP.
|
||||||
|
// It blocks for up to 2x config.Timeout (discovery + mapping).
|
||||||
|
func NewPortMapper(ctx context.Context, internalPort int, config PortMapConfig) (*PortMapper, error) {
|
||||||
|
if internalPort <= 0 || internalPort > 65535 {
|
||||||
|
return nil, fmt.Errorf("%w: invalid internal port %d", ErrInvalidPortMapConfig, internalPort)
|
||||||
|
}
|
||||||
|
config, err := config.withDefaults()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
discoverCtx, cancel := context.WithTimeout(ctx, config.Timeout)
|
||||||
|
gateway, err := nat.DiscoverGateway(discoverCtx)
|
||||||
|
cancel()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("gateway discovery failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m := &PortMapper{
|
||||||
|
gateway: gateway,
|
||||||
|
internalPort: internalPort,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
if _, err := m.Renew(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renew (re-)requests the port mapping and refreshes the external address.
|
||||||
|
// It reports whether the external address changed since the last call.
|
||||||
|
func (m *PortMapper) Renew(ctx context.Context) (bool, error) {
|
||||||
|
opCtx, cancel := context.WithTimeout(ctx, m.config.Timeout)
|
||||||
|
defer cancel()
|
||||||
|
externalPort, err := m.gateway.AddPortMapping(opCtx, portMapProtocol, m.internalPort, portMapDescription, m.config.Lifetime)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("add port mapping failed: %w", err)
|
||||||
|
}
|
||||||
|
externalIP, err := m.gateway.GetExternalAddress()
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("get external address failed: %w", err)
|
||||||
|
}
|
||||||
|
addr, ok := netip.AddrFromSlice(externalIP)
|
||||||
|
if !ok || addr.IsUnspecified() || addr.IsLoopback() {
|
||||||
|
return false, fmt.Errorf("gateway returned unusable external address: %s", externalIP)
|
||||||
|
}
|
||||||
|
externalAddr := netip.AddrPortFrom(addr.Unmap(), uint16(externalPort))
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
changed := externalAddr != m.externalAddr
|
||||||
|
m.externalAddr = externalAddr
|
||||||
|
m.mu.Unlock()
|
||||||
|
return changed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalAddr returns the gateway's external IP and the mapped external port.
|
||||||
|
func (m *PortMapper) ExternalAddr() netip.AddrPort {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
return m.externalAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalPort returns the mapped local UDP port.
|
||||||
|
func (m *PortMapper) InternalPort() int {
|
||||||
|
return m.internalPort
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lifetime returns the effective mapping lease duration.
|
||||||
|
func (m *PortMapper) Lifetime() time.Duration {
|
||||||
|
return m.config.Lifetime
|
||||||
|
}
|
||||||
|
|
||||||
|
// GatewayType returns the protocol used to talk to the gateway ("UPnP" or "NAT-PMP").
|
||||||
|
func (m *PortMapper) GatewayType() string {
|
||||||
|
return m.gateway.Type()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close removes the port mapping from the gateway. Best-effort.
|
||||||
|
func (m *PortMapper) Close() error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), m.config.Timeout)
|
||||||
|
defer cancel()
|
||||||
|
return m.gateway.DeletePortMapping(ctx, portMapProtocol, m.internalPort)
|
||||||
|
}
|
||||||
@@ -33,14 +33,16 @@ type STUNPacketEvent struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type realmConnServer struct {
|
type realmConnServer struct {
|
||||||
cleaned chan struct{}
|
wg sync.WaitGroup
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
net.PacketConn
|
net.PacketConn
|
||||||
|
|
||||||
realmClient *Client
|
realmClient *Client
|
||||||
realmID string
|
realmID string
|
||||||
stunServers []string
|
stunServers []string
|
||||||
|
family Family
|
||||||
|
mapper *PortMapper
|
||||||
stunTimeout time.Duration
|
stunTimeout time.Duration
|
||||||
punchTimeout time.Duration
|
punchTimeout time.Duration
|
||||||
punchInterval time.Duration
|
punchInterval time.Duration
|
||||||
@@ -57,8 +59,29 @@ type realmConnServer struct {
|
|||||||
func NewConnServer(config *Config, raw net.PacketConn) (net.PacketConn, error) {
|
func NewConnServer(config *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
family := Family_Dual
|
||||||
|
switch config.IPMode {
|
||||||
|
case "dual":
|
||||||
|
case "v4":
|
||||||
|
family = Family_V4
|
||||||
|
case "v6":
|
||||||
|
family = Family_V6
|
||||||
|
}
|
||||||
|
|
||||||
|
var mapper *PortMapper
|
||||||
|
if config.PortMapping != nil && config.PortMapping.Enabled {
|
||||||
|
var err error
|
||||||
|
start := time.Now()
|
||||||
|
mapper, err = NewPortMapper(context.Background(), raw.LocalAddr().(*net.UDPAddr).Port, PortMapConfig{Timeout: time.Duration(config.PortMapping.Timeout) * time.Second, Lifetime: time.Duration(config.PortMapping.Lifetime) * time.Second})
|
||||||
|
if err != nil {
|
||||||
|
errors.LogErrorInner(context.Background(), err, "[realm] [port mapping] [", raw.LocalAddr().(*net.UDPAddr).Port, "] init failed after ", time.Since(start))
|
||||||
|
} else {
|
||||||
|
errors.LogDebug(context.Background(), "[realm] [port mapping] [", mapper.InternalPort(), "] gateway ", mapper.GatewayType(), ", external ", mapper.ExternalAddr())
|
||||||
|
errors.LogDebug(context.Background(), "[realm] [port mapping] [", mapper.InternalPort(), "] init success with ", time.Since(start))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
conn := &realmConnServer{
|
conn := &realmConnServer{
|
||||||
cleaned: make(chan struct{}),
|
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
PacketConn: raw,
|
PacketConn: raw,
|
||||||
@@ -66,6 +89,8 @@ func NewConnServer(config *Config, raw net.PacketConn) (net.PacketConn, error) {
|
|||||||
realmClient: NewClient(config.Scheme, config.Host, config.Port, config.Token, config.TlsConfig),
|
realmClient: NewClient(config.Scheme, config.Host, config.Port, config.Token, config.TlsConfig),
|
||||||
realmID: config.ID,
|
realmID: config.ID,
|
||||||
stunServers: config.StunServers,
|
stunServers: config.StunServers,
|
||||||
|
family: family,
|
||||||
|
mapper: mapper,
|
||||||
stunTimeout: defaultSTUNTimeout,
|
stunTimeout: defaultSTUNTimeout,
|
||||||
punchTimeout: defaultPunchTimeout,
|
punchTimeout: defaultPunchTimeout,
|
||||||
punchInterval: defaultPunchInterval,
|
punchInterval: defaultPunchInterval,
|
||||||
@@ -74,6 +99,12 @@ func NewConnServer(config *Config, raw net.PacketConn) (net.PacketConn, error) {
|
|||||||
stun: make(chan STUNPacketEvent, defaultEventBuffer),
|
stun: make(chan STUNPacketEvent, defaultEventBuffer),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mapper != nil {
|
||||||
|
conn.wg.Add(1)
|
||||||
|
go portMapLoop(ctx, mapper, conn.wg.Done)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.wg.Add(1)
|
||||||
go conn.run()
|
go conn.run()
|
||||||
|
|
||||||
return conn, nil
|
return conn, nil
|
||||||
@@ -137,6 +168,8 @@ func (c *realmConnServer) discover(servers []*net.UDPAddr) []netip.AddrPort {
|
|||||||
results := make([]netip.AddrPort, 0, len(servers))
|
results := make([]netip.AddrPort, 0, len(servers))
|
||||||
for len(transactionIDs) > 0 {
|
for len(transactionIDs) > 0 {
|
||||||
select {
|
select {
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
goto end
|
||||||
case <-deadline.C:
|
case <-deadline.C:
|
||||||
goto end
|
goto end
|
||||||
case ev := <-c.stun:
|
case ev := <-c.stun:
|
||||||
@@ -148,6 +181,9 @@ func (c *realmConnServer) discover(servers []*net.UDPAddr) []netip.AddrPort {
|
|||||||
}
|
}
|
||||||
end:
|
end:
|
||||||
deadline.Stop()
|
deadline.Stop()
|
||||||
|
if c.mapper != nil {
|
||||||
|
results = insertAddr(results, c.mapper.ExternalAddr())
|
||||||
|
}
|
||||||
slices.SortFunc(results, func(a, b netip.AddrPort) int {
|
slices.SortFunc(results, func(a, b netip.AddrPort) int {
|
||||||
return strings.Compare(a.String(), b.String())
|
return strings.Compare(a.String(), b.String())
|
||||||
})
|
})
|
||||||
@@ -159,7 +195,7 @@ func (c *realmConnServer) getlocals(force bool) []netip.AddrPort {
|
|||||||
c.localsMu.Lock()
|
c.localsMu.Lock()
|
||||||
if force || time.Since(c.localsLast) > defaultStunCacheTTL {
|
if force || time.Since(c.localsLast) > defaultStunCacheTTL {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
servers := resolveSTUNServers(c.PacketConn.LocalAddr().(*net.UDPAddr).IP, c.stunServers)
|
servers := resolveSTUNServers(c.PacketConn.LocalAddr().(*net.UDPAddr).IP, c.stunServers, c.family)
|
||||||
errors.LogDebug(context.Background(), "[realm] update stun servers ", servers, " with ", time.Since(start))
|
errors.LogDebug(context.Background(), "[realm] update stun servers ", servers, " with ", time.Since(start))
|
||||||
if len(servers) > 0 {
|
if len(servers) > 0 {
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
@@ -232,7 +268,7 @@ retry:
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
errors.LogErrorInner(context.Background(), err, "[realm] ", c.realmID, " register session err retry in ", backoff)
|
errors.LogErrorInner(context.Background(), err, "[realm] ", c.realmID, " register session err retry in ", backoff)
|
||||||
if c.waitctx(c.ctx, backoff) {
|
if c.waitctx(c.ctx, backoff) {
|
||||||
close(c.cleaned)
|
c.wg.Done()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
backoff *= 2
|
backoff *= 2
|
||||||
@@ -259,7 +295,7 @@ retry:
|
|||||||
case <-c.ctx.Done():
|
case <-c.ctx.Done():
|
||||||
_ = c.realmClient.Deregister(context.Background(), c.realmID, resp.SessionID)
|
_ = c.realmClient.Deregister(context.Background(), c.realmID, resp.SessionID)
|
||||||
errors.LogDebug(context.Background(), "[realm] ", c.realmID, " ", resp.SessionID, " deregistered")
|
errors.LogDebug(context.Background(), "[realm] ", c.realmID, " ", resp.SessionID, " deregistered")
|
||||||
close(c.cleaned)
|
c.wg.Done()
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
goto retry
|
goto retry
|
||||||
@@ -360,7 +396,7 @@ func (c *realmConnServer) punchEvent(ctx context.Context, sid string, ev *PunchE
|
|||||||
|
|
||||||
peers, _ := parseAddrPorts(ev.Addresses)
|
peers, _ := parseAddrPorts(ev.Addresses)
|
||||||
errors.LogDebug(context.Background(), "[realm] ", ev.Nonce, " update peers ", peers)
|
errors.LogDebug(context.Background(), "[realm] ", ev.Nonce, " update peers ", peers)
|
||||||
filteredPeers, seen := candidatePunchAddrs(locals, peers)
|
filteredPeers, seen := candidatePunchAddrs(locals, peers, c.family)
|
||||||
errors.LogDebug(context.Background(), "[realm] ", ev.Nonce, " filtered peers ", filteredPeers)
|
errors.LogDebug(context.Background(), "[realm] ", ev.Nonce, " filtered peers ", filteredPeers)
|
||||||
expandedPeers := expandSymmetricNATCandidates(filteredPeers, seen)
|
expandedPeers := expandSymmetricNATCandidates(filteredPeers, seen)
|
||||||
errors.LogDebug(context.Background(), "[realm] ", ev.Nonce, " expanded peers ", expandedPeers)
|
errors.LogDebug(context.Background(), "[realm] ", ev.Nonce, " expanded peers ", expandedPeers)
|
||||||
@@ -398,6 +434,6 @@ func (c *realmConnServer) ReadFrom(p []byte) (int, net.Addr, error) {
|
|||||||
|
|
||||||
func (c *realmConnServer) Close() error {
|
func (c *realmConnServer) Close() error {
|
||||||
c.cancel()
|
c.cancel()
|
||||||
<-c.cleaned
|
c.wg.Wait()
|
||||||
return c.PacketConn.Close()
|
return c.PacketConn.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,14 +23,23 @@ const (
|
|||||||
symmetricNATMaxPortsPerHost = 32
|
symmetricNATMaxPortsPerHost = 32
|
||||||
)
|
)
|
||||||
|
|
||||||
func resolveSTUNServers(local net.IP, servers []string) []*net.UDPAddr {
|
func resolveSTUNServers(local net.IP, servers []string, family Family) []*net.UDPAddr {
|
||||||
var network string
|
var network string
|
||||||
if local.IsUnspecified() {
|
if family == Family_Dual {
|
||||||
network = "ip"
|
if local.IsUnspecified() {
|
||||||
} else {
|
network = "ip"
|
||||||
if local.To4() != nil {
|
|
||||||
network = "ip4"
|
|
||||||
} else {
|
} else {
|
||||||
|
if local.To4() != nil {
|
||||||
|
network = "ip4"
|
||||||
|
} else {
|
||||||
|
network = "ip6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if family == Family_V4 {
|
||||||
|
network = "ip4"
|
||||||
|
}
|
||||||
|
if family == Family_V6 {
|
||||||
network = "ip6"
|
network = "ip6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,7 +113,7 @@ func netIPPortToAddrPort(ip net.IP, port int) (netip.AddrPort, error) {
|
|||||||
return netip.AddrPortFrom(netip.AddrFrom16(addr), uint16(port)), nil
|
return netip.AddrPortFrom(netip.AddrFrom16(addr), uint16(port)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func candidatePunchAddrs(locals, peers []netip.AddrPort) ([]netip.AddrPort, map[netip.AddrPort]struct{}) {
|
func candidatePunchAddrs(locals, peers []netip.AddrPort, family Family) ([]netip.AddrPort, map[netip.AddrPort]struct{}) {
|
||||||
var allow4, allow6 bool
|
var allow4, allow6 bool
|
||||||
for _, local := range locals {
|
for _, local := range locals {
|
||||||
if local.Addr().Is4() {
|
if local.Addr().Is4() {
|
||||||
@@ -116,6 +125,12 @@ func candidatePunchAddrs(locals, peers []netip.AddrPort) ([]netip.AddrPort, map[
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if family == Family_V4 {
|
||||||
|
allow6 = false
|
||||||
|
}
|
||||||
|
if family == Family_V6 {
|
||||||
|
allow4 = false
|
||||||
|
}
|
||||||
seen := make(map[netip.AddrPort]struct{}, len(peers))
|
seen := make(map[netip.AddrPort]struct{}, len(peers))
|
||||||
candidates := make([]netip.AddrPort, 0, len(peers))
|
candidates := make([]netip.AddrPort, 0, len(peers))
|
||||||
for _, peer := range peers {
|
for _, peer := range peers {
|
||||||
@@ -218,3 +233,17 @@ func parseAddrPorts(addrs []string) ([]netip.AddrPort, error) {
|
|||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func insertAddr(addrs []netip.AddrPort, addr netip.AddrPort) []netip.AddrPort {
|
||||||
|
if !addr.IsValid() {
|
||||||
|
return addrs
|
||||||
|
}
|
||||||
|
out := append([]netip.AddrPort(nil), addrs...)
|
||||||
|
i, found := slices.BinarySearchFunc(out, addr, func(a, b netip.AddrPort) int {
|
||||||
|
return strings.Compare(a.String(), b.String())
|
||||||
|
})
|
||||||
|
if found {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
return slices.Insert(out, i, addr)
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,10 +29,11 @@ type Config struct {
|
|||||||
MasqFile string `protobuf:"bytes,5,opt,name=masq_file,json=masqFile,proto3" json:"masq_file,omitempty"`
|
MasqFile string `protobuf:"bytes,5,opt,name=masq_file,json=masqFile,proto3" json:"masq_file,omitempty"`
|
||||||
MasqUrl string `protobuf:"bytes,6,opt,name=masq_url,json=masqUrl,proto3" json:"masq_url,omitempty"`
|
MasqUrl string `protobuf:"bytes,6,opt,name=masq_url,json=masqUrl,proto3" json:"masq_url,omitempty"`
|
||||||
MasqUrlRewriteHost bool `protobuf:"varint,7,opt,name=masq_url_rewrite_host,json=masqUrlRewriteHost,proto3" json:"masq_url_rewrite_host,omitempty"`
|
MasqUrlRewriteHost bool `protobuf:"varint,7,opt,name=masq_url_rewrite_host,json=masqUrlRewriteHost,proto3" json:"masq_url_rewrite_host,omitempty"`
|
||||||
MasqUrlInsecure bool `protobuf:"varint,8,opt,name=masq_url_insecure,json=masqUrlInsecure,proto3" json:"masq_url_insecure,omitempty"`
|
MasqUrlXForwarded bool `protobuf:"varint,8,opt,name=masq_url_x_forwarded,json=masqUrlXForwarded,proto3" json:"masq_url_x_forwarded,omitempty"`
|
||||||
MasqString string `protobuf:"bytes,9,opt,name=masq_string,json=masqString,proto3" json:"masq_string,omitempty"`
|
MasqUrlInsecure bool `protobuf:"varint,9,opt,name=masq_url_insecure,json=masqUrlInsecure,proto3" json:"masq_url_insecure,omitempty"`
|
||||||
MasqStringHeaders map[string]string `protobuf:"bytes,10,rep,name=masq_string_headers,json=masqStringHeaders,proto3" json:"masq_string_headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
MasqString string `protobuf:"bytes,10,opt,name=masq_string,json=masqString,proto3" json:"masq_string,omitempty"`
|
||||||
MasqStringStatusCode int32 `protobuf:"varint,11,opt,name=masq_string_status_code,json=masqStringStatusCode,proto3" json:"masq_string_status_code,omitempty"`
|
MasqStringHeaders map[string]string `protobuf:"bytes,11,rep,name=masq_string_headers,json=masqStringHeaders,proto3" json:"masq_string_headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||||
|
MasqStringStatusCode int32 `protobuf:"varint,12,opt,name=masq_string_status_code,json=masqStringStatusCode,proto3" json:"masq_string_status_code,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -109,6 +110,13 @@ func (x *Config) GetMasqUrlRewriteHost() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetMasqUrlXForwarded() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.MasqUrlXForwarded
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (x *Config) GetMasqUrlInsecure() bool {
|
func (x *Config) GetMasqUrlInsecure() bool {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.MasqUrlInsecure
|
return x.MasqUrlInsecure
|
||||||
@@ -141,20 +149,21 @@ var File_transport_internet_hysteria_config_proto protoreflect.FileDescriptor
|
|||||||
|
|
||||||
const file_transport_internet_hysteria_config_proto_rawDesc = "" +
|
const file_transport_internet_hysteria_config_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"(transport/internet/hysteria/config.proto\x12 xray.transport.internet.hysteria\"\x8f\x04\n" +
|
"(transport/internet/hysteria/config.proto\x12 xray.transport.internet.hysteria\"\xc0\x04\n" +
|
||||||
"\x06Config\x12\x12\n" +
|
"\x06Config\x12\x12\n" +
|
||||||
"\x04auth\x18\x02 \x01(\tR\x04auth\x12(\n" +
|
"\x04auth\x18\x02 \x01(\tR\x04auth\x12(\n" +
|
||||||
"\x10udp_idle_timeout\x18\x03 \x01(\x03R\x0eudpIdleTimeout\x12\x1b\n" +
|
"\x10udp_idle_timeout\x18\x03 \x01(\x03R\x0eudpIdleTimeout\x12\x1b\n" +
|
||||||
"\tmasq_type\x18\x04 \x01(\tR\bmasqType\x12\x1b\n" +
|
"\tmasq_type\x18\x04 \x01(\tR\bmasqType\x12\x1b\n" +
|
||||||
"\tmasq_file\x18\x05 \x01(\tR\bmasqFile\x12\x19\n" +
|
"\tmasq_file\x18\x05 \x01(\tR\bmasqFile\x12\x19\n" +
|
||||||
"\bmasq_url\x18\x06 \x01(\tR\amasqUrl\x121\n" +
|
"\bmasq_url\x18\x06 \x01(\tR\amasqUrl\x121\n" +
|
||||||
"\x15masq_url_rewrite_host\x18\a \x01(\bR\x12masqUrlRewriteHost\x12*\n" +
|
"\x15masq_url_rewrite_host\x18\a \x01(\bR\x12masqUrlRewriteHost\x12/\n" +
|
||||||
"\x11masq_url_insecure\x18\b \x01(\bR\x0fmasqUrlInsecure\x12\x1f\n" +
|
"\x14masq_url_x_forwarded\x18\b \x01(\bR\x11masqUrlXForwarded\x12*\n" +
|
||||||
"\vmasq_string\x18\t \x01(\tR\n" +
|
"\x11masq_url_insecure\x18\t \x01(\bR\x0fmasqUrlInsecure\x12\x1f\n" +
|
||||||
|
"\vmasq_string\x18\n" +
|
||||||
|
" \x01(\tR\n" +
|
||||||
"masqString\x12o\n" +
|
"masqString\x12o\n" +
|
||||||
"\x13masq_string_headers\x18\n" +
|
"\x13masq_string_headers\x18\v \x03(\v2?.xray.transport.internet.hysteria.Config.MasqStringHeadersEntryR\x11masqStringHeaders\x125\n" +
|
||||||
" \x03(\v2?.xray.transport.internet.hysteria.Config.MasqStringHeadersEntryR\x11masqStringHeaders\x125\n" +
|
"\x17masq_string_status_code\x18\f \x01(\x05R\x14masqStringStatusCode\x1aD\n" +
|
||||||
"\x17masq_string_status_code\x18\v \x01(\x05R\x14masqStringStatusCode\x1aD\n" +
|
|
||||||
"\x16MasqStringHeadersEntry\x12\x10\n" +
|
"\x16MasqStringHeadersEntry\x12\x10\n" +
|
||||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01J\x04\b\x01\x10\x02B\x82\x01\n" +
|
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01J\x04\b\x01\x10\x02B\x82\x01\n" +
|
||||||
|
|||||||
@@ -11,12 +11,17 @@ message Config {
|
|||||||
string auth = 2;
|
string auth = 2;
|
||||||
|
|
||||||
int64 udp_idle_timeout = 3;
|
int64 udp_idle_timeout = 3;
|
||||||
|
|
||||||
string masq_type = 4;
|
string masq_type = 4;
|
||||||
|
|
||||||
string masq_file = 5;
|
string masq_file = 5;
|
||||||
|
|
||||||
string masq_url = 6;
|
string masq_url = 6;
|
||||||
bool masq_url_rewrite_host = 7;
|
bool masq_url_rewrite_host = 7;
|
||||||
bool masq_url_insecure = 8;
|
bool masq_url_x_forwarded = 8;
|
||||||
string masq_string = 9;
|
bool masq_url_insecure = 9;
|
||||||
map<string, string> masq_string_headers = 10;
|
|
||||||
int32 masq_string_status_code = 11;
|
string masq_string = 10;
|
||||||
|
map<string, string> masq_string_headers = 11;
|
||||||
|
int32 masq_string_status_code = 12;
|
||||||
}
|
}
|
||||||
@@ -33,6 +33,8 @@ type BrutalSender struct {
|
|||||||
pktInfoSlots [pktInfoSlotCount]pktInfo
|
pktInfoSlots [pktInfoSlotCount]pktInfo
|
||||||
ackRate float64
|
ackRate float64
|
||||||
|
|
||||||
|
disableLossCompensation bool
|
||||||
|
|
||||||
debug bool
|
debug bool
|
||||||
lastAckPrintTimestamp int64
|
lastAckPrintTimestamp int64
|
||||||
}
|
}
|
||||||
@@ -43,13 +45,14 @@ type pktInfo struct {
|
|||||||
LossCount uint64
|
LossCount uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBrutalSender(bps uint64) *BrutalSender {
|
func NewBrutalSender(bps uint64, disableLossCompensation bool) *BrutalSender {
|
||||||
debug, _ := strconv.ParseBool(os.Getenv(debugEnv))
|
debug, _ := strconv.ParseBool(os.Getenv(debugEnv))
|
||||||
bs := &BrutalSender{
|
bs := &BrutalSender{
|
||||||
bps: congestion.ByteCount(bps),
|
bps: congestion.ByteCount(bps),
|
||||||
maxDatagramSize: congestion.InitialPacketSize,
|
maxDatagramSize: congestion.InitialPacketSize,
|
||||||
ackRate: 1,
|
ackRate: 1,
|
||||||
debug: debug,
|
disableLossCompensation: disableLossCompensation,
|
||||||
|
debug: debug,
|
||||||
}
|
}
|
||||||
bs.pacer = common.NewPacer(func() congestion.ByteCount {
|
bs.pacer = common.NewPacer(func() congestion.ByteCount {
|
||||||
return congestion.ByteCount(float64(bs.bps) / bs.ackRate)
|
return congestion.ByteCount(float64(bs.bps) / bs.ackRate)
|
||||||
@@ -127,6 +130,10 @@ func (b *BrutalSender) SetMaxDatagramSize(size congestion.ByteCount) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *BrutalSender) updateAckRate(currentTimestamp int64) {
|
func (b *BrutalSender) updateAckRate(currentTimestamp int64) {
|
||||||
|
if b.disableLossCompensation {
|
||||||
|
b.ackRate = 1
|
||||||
|
return
|
||||||
|
}
|
||||||
minTimestamp := currentTimestamp - pktInfoSlotCount
|
minTimestamp := currentTimestamp - pktInfoSlotCount
|
||||||
var ackCount, lossCount uint64
|
var ackCount, lossCount uint64
|
||||||
for _, info := range b.pktInfoSlots {
|
for _, info := range b.pktInfoSlots {
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package brutal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/apernet/quic-go/congestion"
|
||||||
|
"github.com/apernet/quic-go/monotime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// feedAckRate drives a single sampling slot with the given number of acked and
|
||||||
|
// lost packets and returns the resulting ackRate.
|
||||||
|
func feedAckRate(disableLossCompensation bool, ackCount, lossCount int) float64 {
|
||||||
|
b := NewBrutalSender(1000000, disableLossCompensation)
|
||||||
|
acked := make([]congestion.AckedPacketInfo, ackCount)
|
||||||
|
lost := make([]congestion.LostPacketInfo, lossCount)
|
||||||
|
// eventTime lands in a fixed slot; a single event carries enough samples.
|
||||||
|
b.OnCongestionEventEx(0, monotime.Time(5*time.Second), acked, lost)
|
||||||
|
return b.ackRate
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBrutalLossCompensation(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
ack, loss int
|
||||||
|
want float64 // expected ackRate when compensation is ENABLED
|
||||||
|
}{
|
||||||
|
{"no loss", 100, 0, 1.0},
|
||||||
|
{"20% loss", 80, 20, 0.8},
|
||||||
|
{"50% loss clamps to floor", 50, 50, minAckRate}, // 0.5 clamped up to 0.8
|
||||||
|
{"few samples stays 1", 10, 5, 1.0}, // below minSampleCount
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Compensation enabled (default behavior): ackRate reacts to loss.
|
||||||
|
if got := feedAckRate(false, tt.ack, tt.loss); got != tt.want {
|
||||||
|
t.Errorf("compensation on: ackRate = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
// Compensation disabled: ackRate must stay pinned at 1 regardless.
|
||||||
|
if got := feedAckRate(true, tt.ack, tt.loss); got != 1.0 {
|
||||||
|
t.Errorf("compensation off: ackRate = %v, want 1.0", got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/apernet/quic-go"
|
"github.com/apernet/quic-go"
|
||||||
|
"github.com/apernet/quic-go/congestion"
|
||||||
"github.com/xtls/xray-core/transport/internet/hysteria/congestion/bbr"
|
"github.com/xtls/xray-core/transport/internet/hysteria/congestion/bbr"
|
||||||
"github.com/xtls/xray-core/transport/internet/hysteria/congestion/brutal"
|
"github.com/xtls/xray-core/transport/internet/hysteria/congestion/brutal"
|
||||||
)
|
)
|
||||||
@@ -36,13 +37,29 @@ func NormalizeBBRProfile(profile string) (string, error) {
|
|||||||
func UseBBR(conn *quic.Conn, profile bbr.Profile) {
|
func UseBBR(conn *quic.Conn, profile bbr.Profile) {
|
||||||
conn.SetCongestionControl(bbr.NewBbrSender(
|
conn.SetCongestionControl(bbr.NewBbrSender(
|
||||||
bbr.DefaultClock{},
|
bbr.DefaultClock{},
|
||||||
bbr.GetInitialPacketSize(conn.RemoteAddr()),
|
seedPacketSize(conn.InitialPacketSize(), bbr.GetInitialPacketSize(conn.RemoteAddr())),
|
||||||
profile,
|
profile,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func UseBrutal(conn *quic.Conn, tx uint64) {
|
// seedPacketSize picks the datagram size to seed a replacement congestion
|
||||||
conn.SetCongestionControl(brutal.NewBrutalSender(tx))
|
// controller with, given the size QUIC itself starts at and the guess derived
|
||||||
|
// from the remote address.
|
||||||
|
//
|
||||||
|
// The seed must not exceed what QUIC actually starts at. If it does, the first
|
||||||
|
// path MTU probe can land between the two: QUIC sees an increase and reports
|
||||||
|
// it, but the controller sees a decrease, which it cannot represent. Taking the
|
||||||
|
// smaller of the two keeps the address-based guess as a floor for connections
|
||||||
|
// whose path we can't reason about, while never seeding above QUIC.
|
||||||
|
func seedPacketSize(quicSize, byAddr congestion.ByteCount) congestion.ByteCount {
|
||||||
|
if quicSize <= 0 {
|
||||||
|
return byAddr
|
||||||
|
}
|
||||||
|
return min(quicSize, byAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UseBrutal(conn *quic.Conn, tx uint64, disableLossCompensation bool) {
|
||||||
|
conn.SetCongestionControl(brutal.NewBrutalSender(tx, disableLossCompensation))
|
||||||
}
|
}
|
||||||
|
|
||||||
func UseConfigured(conn *quic.Conn, congestionType, bbrProfile string) {
|
func UseConfigured(conn *quic.Conn, congestionType, bbrProfile string) {
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ func (c *client) dial(ctx context.Context) error {
|
|||||||
MaxIdleTimeout: time.Duration(quicParams.MaxIdleTimeout) * time.Second,
|
MaxIdleTimeout: time.Duration(quicParams.MaxIdleTimeout) * time.Second,
|
||||||
KeepAlivePeriod: time.Duration(quicParams.KeepAlivePeriod) * time.Second,
|
KeepAlivePeriod: time.Duration(quicParams.KeepAlivePeriod) * time.Second,
|
||||||
DisablePathMTUDiscovery: quicParams.DisablePathMtuDiscovery || (runtime.GOOS != "linux" && runtime.GOOS != "windows" && runtime.GOOS != "darwin"),
|
DisablePathMTUDiscovery: quicParams.DisablePathMtuDiscovery || (runtime.GOOS != "linux" && runtime.GOOS != "windows" && runtime.GOOS != "darwin"),
|
||||||
|
ChromeParrot: !quicParams.DisableChromeParrot,
|
||||||
EnableDatagrams: true,
|
EnableDatagrams: true,
|
||||||
MaxDatagramFrameSize: MaxDatagramFrameSize,
|
MaxDatagramFrameSize: MaxDatagramFrameSize,
|
||||||
OmitMaxDatagramFrameSize: time.Now().After(time.Date(2026, 9, 1, 0, 0, 0, 0, time.UTC)),
|
OmitMaxDatagramFrameSize: time.Now().After(time.Date(2026, 9, 1, 0, 0, 0, 0, time.UTC)),
|
||||||
@@ -172,7 +173,12 @@ func (c *client) dial(ctx context.Context) error {
|
|||||||
pktConn = newConn
|
pktConn = newConn
|
||||||
}
|
}
|
||||||
|
|
||||||
tr := &quic.Transport{Conn: pktConn}
|
tr := &quic.Transport{Conn: pktConn, DisableGSO: quicParams.DisableGSO}
|
||||||
|
|
||||||
|
if !quicParams.DisableChromeParrot {
|
||||||
|
tr.ConnectionIDGenerator = quic.ZeroLengthConnectionIDGenerator{}
|
||||||
|
c.tlsConfig.GetCertificate = nil
|
||||||
|
}
|
||||||
|
|
||||||
var conn *quic.Conn
|
var conn *quic.Conn
|
||||||
rt := &http3.Transport{
|
rt := &http3.Transport{
|
||||||
@@ -219,6 +225,7 @@ func (c *client) dial(ctx context.Context) error {
|
|||||||
|
|
||||||
// udp, _ := strconv.ParseBool(resp.Header.Get(ResponseHeaderUDPEnabled))
|
// udp, _ := strconv.ParseBool(resp.Header.Get(ResponseHeaderUDPEnabled))
|
||||||
down, _ := strconv.ParseUint(resp.Header.Get(CommonHeaderCCRX), 10, 64)
|
down, _ := strconv.ParseUint(resp.Header.Get(CommonHeaderCCRX), 10, 64)
|
||||||
|
errors.LogDebug(context.Background(), "ECHAccepted ", conn.ConnectionState().TLS.ECHAccepted)
|
||||||
|
|
||||||
switch quicParams.Congestion {
|
switch quicParams.Congestion {
|
||||||
case "reno":
|
case "reno":
|
||||||
@@ -228,10 +235,10 @@ func (c *client) dial(ctx context.Context) error {
|
|||||||
if quicParams.BrutalUp == 0 || down == 0 {
|
if quicParams.BrutalUp == 0 || down == 0 {
|
||||||
congestion.UseBBR(conn, bbr.Profile(quicParams.BbrProfile))
|
congestion.UseBBR(conn, bbr.Profile(quicParams.BbrProfile))
|
||||||
} else {
|
} else {
|
||||||
congestion.UseBrutal(conn, min(quicParams.BrutalUp, down))
|
congestion.UseBrutal(conn, min(quicParams.BrutalUp, down), quicParams.BrutalDisableLossCompensation)
|
||||||
}
|
}
|
||||||
case "force-brutal":
|
case "force-brutal":
|
||||||
congestion.UseBrutal(conn, quicParams.BrutalUp)
|
congestion.UseBrutal(conn, quicParams.BrutalUp, quicParams.BrutalDisableLossCompensation)
|
||||||
default:
|
default:
|
||||||
panic(quicParams.Congestion)
|
panic(quicParams.Congestion)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package hysteria
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
gotls "crypto/tls"
|
gotls "crypto/tls"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
@@ -78,10 +79,10 @@ func (h *httpHandler) AuthHTTP(w http.ResponseWriter, r *http.Request) bool {
|
|||||||
if quicParams.BrutalUp == 0 || down == 0 {
|
if quicParams.BrutalUp == 0 || down == 0 {
|
||||||
congestion.UseBBR(conn, bbr.Profile(quicParams.BbrProfile))
|
congestion.UseBBR(conn, bbr.Profile(quicParams.BbrProfile))
|
||||||
} else {
|
} else {
|
||||||
congestion.UseBrutal(conn, min(quicParams.BrutalUp, down))
|
congestion.UseBrutal(conn, min(quicParams.BrutalUp, down), quicParams.BrutalDisableLossCompensation)
|
||||||
}
|
}
|
||||||
case "force-brutal":
|
case "force-brutal":
|
||||||
congestion.UseBrutal(conn, quicParams.BrutalUp)
|
congestion.UseBrutal(conn, quicParams.BrutalUp, quicParams.BrutalDisableLossCompensation)
|
||||||
default:
|
default:
|
||||||
panic(quicParams.Congestion)
|
panic(quicParams.Congestion)
|
||||||
}
|
}
|
||||||
@@ -165,8 +166,9 @@ func (l *Listener) handleClient(conn *quic.Conn) {
|
|||||||
Handler: handler,
|
Handler: handler,
|
||||||
StreamDispatcher: handler.StreamDispatcher,
|
StreamDispatcher: handler.StreamDispatcher,
|
||||||
}
|
}
|
||||||
_ = h3s.ServeQUICConn(conn)
|
err := h3s.ServeQUICConn(conn)
|
||||||
_ = conn.CloseWithError(closeErrCodeOK, "")
|
_ = conn.CloseWithError(closeErrCodeOK, "")
|
||||||
|
errors.LogDebug(context.Background(), conn.RemoteAddr(), " ServeQUICConn exited with ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Listener) keepAccepting() {
|
func (l *Listener) keepAccepting() {
|
||||||
@@ -219,21 +221,43 @@ func Listen(ctx context.Context, address net.Address, port net.Port, streamSetti
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
transport := http.DefaultTransport.(*http.Transport)
|
transport := http.DefaultTransport.(*http.Transport)
|
||||||
if config.MasqUrlInsecure {
|
switch u.Scheme {
|
||||||
transport = transport.Clone()
|
case "http", "https":
|
||||||
transport.TLSClientConfig = &gotls.Config{
|
if config.MasqUrlInsecure {
|
||||||
InsecureSkipVerify: true,
|
transport = transport.Clone()
|
||||||
|
if transport.TLSClientConfig == nil {
|
||||||
|
transport.TLSClientConfig = &gotls.Config{}
|
||||||
|
}
|
||||||
|
transport.TLSClientConfig.InsecureSkipVerify = true
|
||||||
}
|
}
|
||||||
|
case "", "unix":
|
||||||
|
u = &url.URL{Scheme: "http", Host: "localhost"}
|
||||||
|
path := u.Path
|
||||||
|
dialer := &net.Dialer{Timeout: 30 * time.Second}
|
||||||
|
transport = transport.Clone()
|
||||||
|
transport.Proxy = nil
|
||||||
|
transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
return dialer.DialContext(ctx, "unix", path)
|
||||||
|
}
|
||||||
|
transport.MaxIdleConns = masqueradeProxyMaxIdleConnections
|
||||||
|
transport.MaxIdleConnsPerHost = masqueradeProxyMaxIdleConnsPerHost
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unknown scheme")
|
||||||
}
|
}
|
||||||
masqHandler = &httputil.ReverseProxy{
|
masqHandler = &httputil.ReverseProxy{
|
||||||
Rewrite: func(pr *httputil.ProxyRequest) {
|
Rewrite: func(r *httputil.ProxyRequest) {
|
||||||
pr.SetURL(u)
|
r.SetURL(u)
|
||||||
if !config.MasqUrlRewriteHost {
|
if !config.MasqUrlRewriteHost {
|
||||||
pr.Out.Host = pr.In.Host
|
r.Out.Host = r.In.Host
|
||||||
|
}
|
||||||
|
if config.MasqUrlXForwarded {
|
||||||
|
r.SetXForwarded()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Transport: transport,
|
Transport: transport,
|
||||||
|
BufferPool: newMasqueradeProxyBufferPool(),
|
||||||
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
||||||
|
errors.LogErrorInner(context.Background(), err, "HTTP reverse proxy error")
|
||||||
w.WriteHeader(http.StatusBadGateway)
|
w.WriteHeader(http.StatusBadGateway)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -307,7 +331,13 @@ func Listen(ctx context.Context, address net.Address, port net.Port, streamSetti
|
|||||||
pktConn = newConn
|
pktConn = newConn
|
||||||
}
|
}
|
||||||
|
|
||||||
tr := &quic.Transport{Conn: pktConn}
|
var k *quic.StatelessResetKey
|
||||||
|
if !quicParams.DisableStatelessReset {
|
||||||
|
k = &quic.StatelessResetKey{}
|
||||||
|
common.Must2(rand.Read((*k)[:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
tr := &quic.Transport{Conn: pktConn, DisableGSO: quicParams.DisableGSO, StatelessResetKey: k}
|
||||||
|
|
||||||
listener, err := tr.Listen(tlsConfig.GetTLSConfig(tls.WithNextProto("h3")), quicConfig)
|
listener, err := tr.Listen(tlsConfig.GetTLSConfig(tls.WithNextProto("h3")), quicConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -336,3 +366,34 @@ func Listen(ctx context.Context, address net.Address, port net.Port, streamSetti
|
|||||||
func init() {
|
func init() {
|
||||||
common.Must(internet.RegisterTransportListener(protocolName, Listen))
|
common.Must(internet.RegisterTransportListener(protocolName, Listen))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
masqueradeProxyBufferSize = 32 * 1024
|
||||||
|
masqueradeProxyMaxIdleConnections = 100
|
||||||
|
masqueradeProxyMaxIdleConnsPerHost = 32
|
||||||
|
)
|
||||||
|
|
||||||
|
type masqueradeProxyBufferPool struct {
|
||||||
|
pool sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMasqueradeProxyBufferPool() *masqueradeProxyBufferPool {
|
||||||
|
return &masqueradeProxyBufferPool{
|
||||||
|
pool: sync.Pool{
|
||||||
|
New: func() any {
|
||||||
|
return make([]byte, masqueradeProxyBufferSize)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *masqueradeProxyBufferPool) Get() []byte {
|
||||||
|
return p.pool.Get().([]byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *masqueradeProxyBufferPool) Put(buf []byte) {
|
||||||
|
if cap(buf) < masqueradeProxyBufferSize {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.pool.Put(buf[:masqueradeProxyBufferSize])
|
||||||
|
}
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
|||||||
KeepAlivePeriod: time.Duration(quicParams.KeepAlivePeriod) * time.Second,
|
KeepAlivePeriod: time.Duration(quicParams.KeepAlivePeriod) * time.Second,
|
||||||
MaxIncomingStreams: quicParams.MaxIncomingStreams,
|
MaxIncomingStreams: quicParams.MaxIncomingStreams,
|
||||||
DisablePathMTUDiscovery: quicParams.DisablePathMtuDiscovery || (runtime.GOOS != "linux" && runtime.GOOS != "windows" && runtime.GOOS != "darwin"),
|
DisablePathMTUDiscovery: quicParams.DisablePathMtuDiscovery || (runtime.GOOS != "linux" && runtime.GOOS != "windows" && runtime.GOOS != "darwin"),
|
||||||
|
ChromeParrot: !quicParams.DisableChromeParrot,
|
||||||
}
|
}
|
||||||
if quicParams.MaxIdleTimeout == 0 {
|
if quicParams.MaxIdleTimeout == 0 {
|
||||||
quicConfig.MaxIdleTimeout = net.ConnIdleTimeout
|
quicConfig.MaxIdleTimeout = net.ConnIdleTimeout
|
||||||
@@ -255,18 +256,25 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
|||||||
pktConn = newConn
|
pktConn = newConn
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := quic.DialEarly(ctx, pktConn, udpAddr, tlsCfg, cfg)
|
tr := &quic.Transport{Conn: pktConn, DisableGSO: quicParams.DisableGSO}
|
||||||
|
|
||||||
|
if !quicParams.DisableChromeParrot {
|
||||||
|
tr.ConnectionIDGenerator = quic.ZeroLengthConnectionIDGenerator{}
|
||||||
|
tlsCfg.GetCertificate = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := tr.DialEarly(ctx, udpAddr, tlsCfg, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
context.AfterFunc(conn.Context(), func() { pktConn.Close() })
|
context.AfterFunc(conn.Context(), func() { tr.Close(); pktConn.Close() })
|
||||||
|
|
||||||
switch quicParams.Congestion {
|
switch quicParams.Congestion {
|
||||||
case "reno":
|
case "reno":
|
||||||
case "", "bbr":
|
case "", "bbr":
|
||||||
congestion.UseBBR(conn, bbr.Profile(quicParams.BbrProfile))
|
congestion.UseBBR(conn, bbr.Profile(quicParams.BbrProfile))
|
||||||
case "force-brutal":
|
case "force-brutal":
|
||||||
congestion.UseBrutal(conn, quicParams.BrutalUp)
|
congestion.UseBrutal(conn, quicParams.BrutalUp, quicParams.BrutalDisableLossCompensation)
|
||||||
default:
|
default:
|
||||||
panic(quicParams.Congestion)
|
panic(quicParams.Congestion)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package splithttp
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
gotls "crypto/tls"
|
gotls "crypto/tls"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -506,7 +507,15 @@ func ListenXH(ctx context.Context, address net.Address, port net.Port, streamSet
|
|||||||
DisablePathMTUDiscovery: quicParams.DisablePathMtuDiscovery || (runtime.GOOS != "linux" && runtime.GOOS != "windows" && runtime.GOOS != "darwin"),
|
DisablePathMTUDiscovery: quicParams.DisablePathMtuDiscovery || (runtime.GOOS != "linux" && runtime.GOOS != "windows" && runtime.GOOS != "darwin"),
|
||||||
}
|
}
|
||||||
|
|
||||||
l.h3listener, err = quic.ListenEarly(Conn, tlsConfig, quicConfig)
|
var k *quic.StatelessResetKey
|
||||||
|
if !quicParams.DisableStatelessReset {
|
||||||
|
k = &quic.StatelessResetKey{}
|
||||||
|
common.Must2(rand.Read((*k)[:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
tr := &quic.Transport{Conn: Conn, DisableGSO: quicParams.DisableGSO, StatelessResetKey: k}
|
||||||
|
|
||||||
|
l.h3listener, err = tr.ListenEarly(tlsConfig, quicConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("failed to listen QUIC for XHTTP/3 on ", address, ":", port).Base(err)
|
return nil, errors.New("failed to listen QUIC for XHTTP/3 on ", address, ":", port).Base(err)
|
||||||
}
|
}
|
||||||
@@ -525,6 +534,8 @@ func ListenXH(ctx context.Context, address net.Address, port net.Port, streamSet
|
|||||||
if err := l.h3server.ServeListener(l.h3listener); err != nil {
|
if err := l.h3server.ServeListener(l.h3listener); err != nil {
|
||||||
errors.LogErrorInner(ctx, err, "failed to serve HTTP/3 for XHTTP/3")
|
errors.LogErrorInner(ctx, err, "failed to serve HTTP/3 for XHTTP/3")
|
||||||
}
|
}
|
||||||
|
_ = tr.Close()
|
||||||
|
_ = Conn.Close()
|
||||||
}()
|
}()
|
||||||
} else { // tcp
|
} else { // tcp
|
||||||
l.listener, err = internet.ListenSystem(ctx, &net.TCPAddr{
|
l.listener, err = internet.ListenSystem(ctx, &net.TCPAddr{
|
||||||
@@ -622,7 +633,7 @@ func (l *QListener) Accept(ctx context.Context) (*quic.Conn, error) {
|
|||||||
case "", "bbr":
|
case "", "bbr":
|
||||||
congestion.UseBBR(conn, bbr.Profile(l.quicParams.BbrProfile))
|
congestion.UseBBR(conn, bbr.Profile(l.quicParams.BbrProfile))
|
||||||
case "force-brutal":
|
case "force-brutal":
|
||||||
congestion.UseBrutal(conn, l.quicParams.BrutalUp)
|
congestion.UseBrutal(conn, l.quicParams.BrutalUp, l.quicParams.BrutalDisableLossCompensation)
|
||||||
default:
|
default:
|
||||||
panic(l.quicParams.Congestion)
|
panic(l.quicParams.Congestion)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user