mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-09-25 08:08:00 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0991024b45 | ||
|
|
fdb9b616fc | ||
|
|
d792fba59c | ||
|
|
55956f8d70 | ||
|
|
94ffd50060 | ||
|
|
c4dfcd4c1a | ||
|
|
cb8cd048c1 | ||
|
|
455f6bc2d5 | ||
|
|
ba538619eb | ||
|
|
d43a808ea5 | ||
|
|
ca4b156b57 | ||
|
|
aba22722a6 | ||
|
|
569459c54c | ||
|
|
2b42699623 | ||
|
|
66a8100737 | ||
|
|
36303694d1 | ||
|
|
4dcf802ae3 | ||
|
|
a2cec2e580 | ||
|
|
1cd7d25fec | ||
|
|
09002ab763 | ||
|
|
cb206ddc74 | ||
|
|
fa466f8174 | ||
|
|
e26f5e9548 |
@@ -11,6 +11,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
check-assets:
|
check-assets:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
||||||
steps:
|
steps:
|
||||||
- name: Restore Geodat Cache
|
- name: Restore Geodat Cache
|
||||||
uses: actions/cache/restore@v5
|
uses: actions/cache/restore@v5
|
||||||
@@ -75,6 +76,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
||||||
env:
|
env:
|
||||||
GOOS: ${{ matrix.goos}}
|
GOOS: ${{ matrix.goos}}
|
||||||
GOARCH: ${{ matrix.goarch }}
|
GOARCH: ${{ matrix.goarch }}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
check-assets:
|
check-assets:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
||||||
steps:
|
steps:
|
||||||
- name: Restore Geodat Cache
|
- name: Restore Geodat Cache
|
||||||
uses: actions/cache/restore@v5
|
uses: actions/cache/restore@v5
|
||||||
@@ -161,6 +162,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
||||||
env:
|
env:
|
||||||
GOOS: ${{ matrix.goos }}
|
GOOS: ${{ matrix.goos }}
|
||||||
GOARCH: ${{ matrix.goarch }}
|
GOARCH: ${{ matrix.goarch }}
|
||||||
@@ -208,6 +210,9 @@ jobs:
|
|||||||
go build -o build_assets/xray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
|
go build -o build_assets/xray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
|
||||||
# The line below is for without running conhost.exe version. Commented for not being used. Provided for reference.
|
# The line below is for without running conhost.exe version. Commented for not being used. Provided for reference.
|
||||||
# go build -o build_assets/wxray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-H windowsgui -X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
|
# go build -o build_assets/wxray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-H windowsgui -X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
|
||||||
|
elif [[ ${GOOS} == 'android' ]]; then
|
||||||
|
echo 'Building Xray for Android...'
|
||||||
|
go build -o build_assets/xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid= -checklinkname=0" -v ./main
|
||||||
else
|
else
|
||||||
echo 'Building Xray...'
|
echo 'Building Xray...'
|
||||||
if [[ ${GOARCH} == 'mips' || ${GOARCH} == 'mipsle' ]]; then
|
if [[ ${GOARCH} == 'mips' || ${GOARCH} == 'mipsle' ]]; then
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Test
|
name: Tests and Checkings
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -8,6 +8,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
check-assets:
|
check-assets:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
||||||
steps:
|
steps:
|
||||||
- name: Restore Geodat Cache
|
- name: Restore Geodat Cache
|
||||||
uses: actions/cache/restore@v5
|
uses: actions/cache/restore@v5
|
||||||
@@ -36,6 +37,7 @@ jobs:
|
|||||||
|
|
||||||
check-proto:
|
check-proto:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout codebase
|
- name: Checkout codebase
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
@@ -50,8 +52,28 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
check-format:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout codebase
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
check-latest: true
|
||||||
|
cache: false
|
||||||
|
- name: Check Format
|
||||||
|
run: |
|
||||||
|
go install -v mvdan.cc/gofumpt@latest
|
||||||
|
go run ./infra/vformat/main.go -mode check -pwd ./
|
||||||
|
|
||||||
test:
|
test:
|
||||||
needs: check-assets
|
needs: check-assets
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|||||||
@@ -145,6 +145,8 @@
|
|||||||
- [v2rayN](https://github.com/2dust/v2rayN)
|
- [v2rayN](https://github.com/2dust/v2rayN)
|
||||||
- [GenyConnect](https://github.com/genyleap/GenyConnect)
|
- [GenyConnect](https://github.com/genyleap/GenyConnect)
|
||||||
- [OneXray](https://github.com/OneXray/OneXray)
|
- [OneXray](https://github.com/OneXray/OneXray)
|
||||||
|
- HarmonyOS
|
||||||
|
- [Hey](https://github.com/popsiclelmlm/Hey)
|
||||||
|
|
||||||
## Others that support VLESS, XTLS, REALITY, XUDP, PLUX...
|
## Others that support VLESS, XTLS, REALITY, XUDP, PLUX...
|
||||||
|
|
||||||
@@ -162,6 +164,7 @@
|
|||||||
- [xtls-sdk](https://github.com/remnawave/xtls-sdk)
|
- [xtls-sdk](https://github.com/remnawave/xtls-sdk)
|
||||||
- [xtlsapi](https://github.com/hiddify/xtlsapi)
|
- [xtlsapi](https://github.com/hiddify/xtlsapi)
|
||||||
- [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite)
|
- [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite)
|
||||||
|
- [flutter_vless](https://github.com/XIIIFOX/flutter_vless)
|
||||||
- [Xray-core-python](https://github.com/LorenEteval/Xray-core-python)
|
- [Xray-core-python](https://github.com/LorenEteval/Xray-core-python)
|
||||||
- [xray-api](https://github.com/XVGuardian/xray-api)
|
- [xray-api](https://github.com/XVGuardian/xray-api)
|
||||||
- [XrayR](https://github.com/XrayR-project/XrayR)
|
- [XrayR](https://github.com/XrayR-project/XrayR)
|
||||||
@@ -206,6 +209,13 @@ Make sure that you are using the same Go version, and remember to set the git co
|
|||||||
CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid=" -v ./main
|
CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid=" -v ./main
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For Android:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
GOOS=android GOARCH=arm64 CGO_ENABLED=1 CC=/path/to/aarch64-linux-android24-clang go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid= -checklinkname=0" -v ./main
|
||||||
|
GOOS=android GOARCH=amd64 CGO_ENABLED=1 CC=/path/to/x86_64-linux-android24-clang go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid= -checklinkname=0" -v ./main
|
||||||
|
```
|
||||||
|
|
||||||
If you are compiling a 32-bit MIPS/MIPSLE target, use this command instead:
|
If you are compiling a 32-bit MIPS/MIPSLE target, use this command instead:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ package commander
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/signal/done"
|
"github.com/xtls/xray-core/common/signal/done"
|
||||||
core "github.com/xtls/xray-core/core"
|
core "github.com/xtls/xray-core/core"
|
||||||
"github.com/xtls/xray-core/features/outbound"
|
"github.com/xtls/xray-core/features/outbound"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -69,16 +69,15 @@ func (c *Commander) Start() error {
|
|||||||
}
|
}
|
||||||
c.Unlock()
|
c.Unlock()
|
||||||
|
|
||||||
var listen = func(listener net.Listener) {
|
listen := func(listener net.Listener) {
|
||||||
if err := c.server.Serve(listener); err != nil {
|
if err := c.server.Serve(listener); err != nil {
|
||||||
errors.LogErrorInner(context.Background(), err, "failed to start grpc server")
|
errors.LogErrorInner(context.Background(), err, "failed to start grpc server")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if len(c.listen) > 0 {
|
if len(c.listen) > 0 {
|
||||||
var addr net.Addr
|
var addr net.Addr
|
||||||
|
|
||||||
if strings.HasPrefix(c.listen, "/") || strings.HasPrefix(c.listen, "@") {
|
if strings.HasPrefix(c.listen, "/") || strings.HasPrefix(c.listen, "@") {
|
||||||
addr = &net.UnixAddr{Name: c.listen, Net: "unix"}
|
addr = &net.UnixAddr{Name: c.listen, Net: "unix"}
|
||||||
} else {
|
} else {
|
||||||
@@ -89,7 +88,7 @@ func (c *Commander) Start() error {
|
|||||||
}
|
}
|
||||||
addr = tcpAddr
|
addr = tcpAddr
|
||||||
}
|
}
|
||||||
l, err := internet.ListenSystem(context.Background(), addr, nil)
|
l, err := internet.ListenSystem(context.Background(), addr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors.LogErrorInner(context.Background(), err, "API server failed to listen on ", c.listen)
|
errors.LogErrorInner(context.Background(), err, "API server failed to listen on ", c.listen)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -139,7 +139,8 @@ func (c *CacheController) writeAndShrink(expiredKeys []string) {
|
|||||||
|
|
||||||
if lenAfter == 0 {
|
if lenAfter == 0 {
|
||||||
if c.highWatermark >= minSizeForEmptyRebuild {
|
if c.highWatermark >= minSizeForEmptyRebuild {
|
||||||
errors.LogDebug(context.Background(), c.name,
|
errors.LogDebug(
|
||||||
|
context.Background(), c.name,
|
||||||
" rebuilding empty cache map to reclaim memory.",
|
" rebuilding empty cache map to reclaim memory.",
|
||||||
" size_before_cleanup=", lenBefore,
|
" size_before_cleanup=", lenBefore,
|
||||||
" peak_size_before_rebuild=", c.highWatermark,
|
" peak_size_before_rebuild=", c.highWatermark,
|
||||||
@@ -153,7 +154,8 @@ func (c *CacheController) writeAndShrink(expiredKeys []string) {
|
|||||||
|
|
||||||
if reductionFromPeak := c.highWatermark - lenAfter; reductionFromPeak > shrinkAbsoluteThreshold &&
|
if reductionFromPeak := c.highWatermark - lenAfter; reductionFromPeak > shrinkAbsoluteThreshold &&
|
||||||
float64(reductionFromPeak) > float64(c.highWatermark)*shrinkRatioThreshold {
|
float64(reductionFromPeak) > float64(c.highWatermark)*shrinkRatioThreshold {
|
||||||
errors.LogDebug(context.Background(), c.name,
|
errors.LogDebug(
|
||||||
|
context.Background(), c.name,
|
||||||
" shrinking cache map to reclaim memory.",
|
" shrinking cache map to reclaim memory.",
|
||||||
" new_size=", lenAfter,
|
" new_size=", lenAfter,
|
||||||
" peak_size_before_shrink=", c.highWatermark,
|
" peak_size_before_shrink=", c.highWatermark,
|
||||||
@@ -165,7 +167,6 @@ func (c *CacheController) writeAndShrink(expiredKeys []string) {
|
|||||||
c.highWatermark = lenAfter
|
c.highWatermark = lenAfter
|
||||||
go c.migrate()
|
go c.migrate()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type migrationEntry struct {
|
type migrationEntry struct {
|
||||||
|
|||||||
+2
-2
@@ -86,7 +86,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
|
|||||||
return nil, errors.New("failed to create hosts").Base(err)
|
return nil, errors.New("failed to create hosts").Base(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultTag = config.Tag
|
defaultTag := config.Tag
|
||||||
if len(config.Tag) == 0 {
|
if len(config.Tag) == 0 {
|
||||||
defaultTag = generateRandomTag()
|
defaultTag = generateRandomTag()
|
||||||
}
|
}
|
||||||
@@ -139,7 +139,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
|
|||||||
serveExpiredTTL = *ns.ServeExpiredTTL
|
serveExpiredTTL = *ns.ServeExpiredTTL
|
||||||
}
|
}
|
||||||
|
|
||||||
var tag = defaultTag
|
tag := defaultTag
|
||||||
if len(ns.Tag) > 0 {
|
if len(ns.Tag) > 0 {
|
||||||
tag = ns.Tag
|
tag = ns.Tag
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,15 +127,20 @@ func genEDNS0Options(clientIP net.IP, padding int) *dnsmessage.Resource {
|
|||||||
return opt
|
return opt
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() uint16, reqOpts *dnsmessage.Resource) []*dnsRequest {
|
func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() uint16, reqOpts *dnsmessage.Resource) ([]*dnsRequest, error) {
|
||||||
|
name, err := dnsmessage.NewName(domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
qA := dnsmessage.Question{
|
qA := dnsmessage.Question{
|
||||||
Name: dnsmessage.MustNewName(domain),
|
Name: name,
|
||||||
Type: dnsmessage.TypeA,
|
Type: dnsmessage.TypeA,
|
||||||
Class: dnsmessage.ClassINET,
|
Class: dnsmessage.ClassINET,
|
||||||
}
|
}
|
||||||
|
|
||||||
qAAAA := dnsmessage.Question{
|
qAAAA := dnsmessage.Question{
|
||||||
Name: dnsmessage.MustNewName(domain),
|
Name: name,
|
||||||
Type: dnsmessage.TypeAAAA,
|
Type: dnsmessage.TypeAAAA,
|
||||||
Class: dnsmessage.ClassINET,
|
Class: dnsmessage.ClassINET,
|
||||||
}
|
}
|
||||||
@@ -175,7 +180,7 @@ func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() ui
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return reqs
|
return reqs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseResponse parses DNS answers from the returned payload
|
// parseResponse parses DNS answers from the returned payload
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -24,7 +25,8 @@ func Test_parseResponse(t *testing.T) {
|
|||||||
|
|
||||||
ans = new(dns.Msg)
|
ans = new(dns.Msg)
|
||||||
ans.Id = 1
|
ans.Id = 1
|
||||||
ans.Answer = append(ans.Answer,
|
ans.Answer = append(
|
||||||
|
ans.Answer,
|
||||||
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
|
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
|
||||||
common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")),
|
common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")),
|
||||||
common.Must2(dns.NewRR("google.com. IN A 8.8.8.8")),
|
common.Must2(dns.NewRR("google.com. IN A 8.8.8.8")),
|
||||||
@@ -34,7 +36,8 @@ func Test_parseResponse(t *testing.T) {
|
|||||||
|
|
||||||
ans = new(dns.Msg)
|
ans = new(dns.Msg)
|
||||||
ans.Id = 2
|
ans.Id = 2
|
||||||
ans.Answer = append(ans.Answer,
|
ans.Answer = append(
|
||||||
|
ans.Answer,
|
||||||
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
|
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
|
||||||
common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")),
|
common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")),
|
||||||
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
|
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
|
||||||
@@ -131,10 +134,15 @@ func Test_buildReqMsgs(t *testing.T) {
|
|||||||
IPv6Enable: false,
|
IPv6Enable: false,
|
||||||
FakeEnable: false,
|
FakeEnable: false,
|
||||||
}, nil}, 0},
|
}, nil}, 0},
|
||||||
|
{"name too long", args{strings.Repeat("a", 256), dns_feature.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: true,
|
||||||
|
FakeEnable: false,
|
||||||
|
}, nil}, 0},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if got := buildReqMsgs(tt.args.domain, tt.args.option, stubID, tt.args.reqOpts); !(len(got) == tt.want) {
|
if got, _ := buildReqMsgs(tt.args.domain, tt.args.option, stubID, tt.args.reqOpts); !(len(got) == tt.want) {
|
||||||
t.Errorf("buildReqMsgs() = %v, want %v", got, tt.want)
|
t.Errorf("buildReqMsgs() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -129,15 +129,16 @@ func TestFakeDnsHolderCreateMappingAndRollOver(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFakeDNSMulti(t *testing.T) {
|
func TestFakeDNSMulti(t *testing.T) {
|
||||||
fakeMulti, err := NewFakeDNSHolderMulti(&FakeDnsPoolMulti{
|
fakeMulti, err := NewFakeDNSHolderMulti(
|
||||||
Pools: []*FakeDnsPool{{
|
&FakeDnsPoolMulti{
|
||||||
IpPool: "240.0.0.0/12",
|
Pools: []*FakeDnsPool{{
|
||||||
LruSize: 256,
|
IpPool: "240.0.0.0/12",
|
||||||
}, {
|
LruSize: 256,
|
||||||
IpPool: "fddd:c5b4:ff5f:f4f0::/64",
|
}, {
|
||||||
LruSize: 256,
|
IpPool: "fddd:c5b4:ff5f:f4f0::/64",
|
||||||
}},
|
LruSize: 256,
|
||||||
},
|
}},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ func NewClient(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var timeoutMs = 4000 * time.Millisecond
|
timeoutMs := 4000 * time.Millisecond
|
||||||
if ns.TimeoutMs > 0 {
|
if ns.TimeoutMs > 0 {
|
||||||
timeoutMs = time.Duration(ns.TimeoutMs) * time.Millisecond
|
timeoutMs = time.Duration(ns.TimeoutMs) * time.Millisecond
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,14 +137,32 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
|
|||||||
if s.Name()+"." == "DOH//"+fqdn {
|
if s.Name()+"." == "DOH//"+fqdn {
|
||||||
errors.LogError(ctx, s.Name(), " tries to resolve itself! Use IP or set \"hosts\" instead")
|
errors.LogError(ctx, s.Name(), " tries to resolve itself! Use IP or set \"hosts\" instead")
|
||||||
if noResponseErrCh != nil {
|
if noResponseErrCh != nil {
|
||||||
noResponseErrCh <- errors.New("tries to resolve itself!", s.Name())
|
err := errors.New("tries to resolve itself!", s.Name())
|
||||||
|
if option.IPv4Enable {
|
||||||
|
noResponseErrCh <- err
|
||||||
|
}
|
||||||
|
if option.IPv6Enable {
|
||||||
|
noResponseErrCh <- err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// As we don't want our traffic pattern looks like DoH, we use Random-Length Padding instead of Block-Length Padding recommended in RFC 8467
|
// As we don't want our traffic pattern looks like DoH, we use Random-Length Padding instead of Block-Length Padding recommended in RFC 8467
|
||||||
// Although DoH server like 1.1.1.1 will pad the response to Block-Length 468, at least it is better than no padding for response at all
|
// Although DoH server like 1.1.1.1 will pad the response to Block-Length 468, at least it is better than no padding for response at all
|
||||||
reqs := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, int(crypto.RandBetween(100, 300))))
|
reqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, int(crypto.RandBetween(100, 300))))
|
||||||
|
if err != nil {
|
||||||
|
errors.LogErrorInner(ctx, err, "failed to build dns query for ", fqdn)
|
||||||
|
if noResponseErrCh != nil {
|
||||||
|
if option.IPv4Enable {
|
||||||
|
noResponseErrCh <- err
|
||||||
|
}
|
||||||
|
if option.IPv6Enable {
|
||||||
|
noResponseErrCh <- err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var deadline time.Time
|
var deadline time.Time
|
||||||
if d, ok := ctx.Deadline(); ok {
|
if d, ok := ctx.Deadline(); ok {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ type LocalNameServer struct {
|
|||||||
|
|
||||||
// QueryIP implements Server.
|
// QueryIP implements Server.
|
||||||
func (s *LocalNameServer) QueryIP(ctx context.Context, domain string, option dns.IPOption) (ips []net.IP, ttl uint32, err error) {
|
func (s *LocalNameServer) QueryIP(ctx context.Context, domain string, option dns.IPOption) (ips []net.IP, ttl uint32, err error) {
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
ips, ttl, err = s.client.LookupIP(domain, option)
|
ips, ttl, err = s.client.LookupIP(domain, option)
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,19 @@ func (s *QUICNameServer) getCacheController() *CacheController { return s.cacheC
|
|||||||
func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
|
func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
|
||||||
errors.LogInfo(ctx, s.Name(), " querying: ", fqdn)
|
errors.LogInfo(ctx, s.Name(), " querying: ", fqdn)
|
||||||
|
|
||||||
reqs := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
|
reqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
|
||||||
|
if err != nil {
|
||||||
|
errors.LogErrorInner(ctx, err, "failed to build dns query for ", fqdn)
|
||||||
|
if noResponseErrCh != nil {
|
||||||
|
if option.IPv4Enable {
|
||||||
|
noResponseErrCh <- err
|
||||||
|
}
|
||||||
|
if option.IPv6Enable {
|
||||||
|
noResponseErrCh <- err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var deadline time.Time
|
var deadline time.Time
|
||||||
if d, ok := ctx.Deadline(); ok {
|
if d, ok := ctx.Deadline(); ok {
|
||||||
|
|||||||
@@ -113,7 +113,19 @@ func (s *TCPNameServer) getCacheController() *CacheController {
|
|||||||
func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
|
func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
|
||||||
errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn)
|
errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn)
|
||||||
|
|
||||||
reqs := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
|
reqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
|
||||||
|
if err != nil {
|
||||||
|
errors.LogErrorInner(ctx, err, "failed to build dns query for ", fqdn)
|
||||||
|
if noResponseErrCh != nil {
|
||||||
|
if option.IPv4Enable {
|
||||||
|
noResponseErrCh <- err
|
||||||
|
}
|
||||||
|
if option.IPv6Enable {
|
||||||
|
noResponseErrCh <- err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var deadline time.Time
|
var deadline time.Time
|
||||||
if d, ok := ctx.Deadline(); ok {
|
if d, ok := ctx.Deadline(); ok {
|
||||||
|
|||||||
@@ -131,8 +131,6 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot
|
|||||||
newReq.msg = &newMsg
|
newReq.msg = &newMsg
|
||||||
s.addPendingRequest(&newReq)
|
s.addPendingRequest(&newReq)
|
||||||
b, _ := dns.PackMessage(newReq.msg)
|
b, _ := dns.PackMessage(newReq.msg)
|
||||||
copyDest := net.UDPDestination(s.address.Address, s.address.Port)
|
|
||||||
b.UDP = ©Dest
|
|
||||||
s.udpServer.Dispatch(toDnsContext(newReq.ctx, s.address.String()), *s.address, b)
|
s.udpServer.Dispatch(toDnsContext(newReq.ctx, s.address.String()), *s.address, b)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -163,7 +161,19 @@ func (s *ClassicNameServer) getCacheController() *CacheController {
|
|||||||
func (s *ClassicNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
|
func (s *ClassicNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
|
||||||
errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn)
|
errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn)
|
||||||
|
|
||||||
reqs := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
|
reqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
|
||||||
|
if err != nil {
|
||||||
|
errors.LogErrorInner(ctx, err, "failed to build dns query for ", fqdn)
|
||||||
|
if noResponseErrCh != nil {
|
||||||
|
if option.IPv4Enable {
|
||||||
|
noResponseErrCh <- err
|
||||||
|
}
|
||||||
|
if option.IPv6Enable {
|
||||||
|
noResponseErrCh <- err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for _, req := range reqs {
|
for _, req := range reqs {
|
||||||
udpReq := &udpDnsRequest{
|
udpReq := &udpDnsRequest{
|
||||||
@@ -179,8 +189,6 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
copyDest := net.UDPDestination(s.address.Address, s.address.Port)
|
|
||||||
b.UDP = ©Dest
|
|
||||||
s.udpServer.Dispatch(toDnsContext(ctx, s.address.String()), *s.address, b)
|
s.udpServer.Dispatch(toDnsContext(ctx, s.address.String()), *s.address, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ func (p *MetricsHandler) Type() interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *MetricsHandler) Start() error {
|
func (p *MetricsHandler) Start() error {
|
||||||
|
|
||||||
// direct listen a port if listen is set
|
// direct listen a port if listen is set
|
||||||
if p.listen != "" {
|
if p.listen != "" {
|
||||||
TCPlistener, err := net.Listen("tcp", p.listen)
|
TCPlistener, err := net.Listen("tcp", p.listen)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package burst
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/app/observatory"
|
"github.com/xtls/xray-core/app/observatory"
|
||||||
@@ -72,7 +71,6 @@ func (o *Observer) Start() error {
|
|||||||
o.hp.StartScheduler(func() ([]string, error) {
|
o.hp.StartScheduler(func() ([]string, error) {
|
||||||
hs, ok := o.ohm.(outbound.HandlerSelector)
|
hs, ok := o.ohm.(outbound.HandlerSelector)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
||||||
return nil, errors.New("outbound.Manager is not a HandlerSelector")
|
return nil, errors.New("outbound.Manager is not a HandlerSelector")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/dice"
|
"github.com/xtls/xray-core/common/dice"
|
||||||
@@ -24,11 +25,12 @@ type HealthPingSettings struct {
|
|||||||
|
|
||||||
// HealthPing is the health checker for balancers
|
// HealthPing is the health checker for balancers
|
||||||
type HealthPing struct {
|
type HealthPing struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
dispatcher routing.Dispatcher
|
cancelCtx context.CancelFunc
|
||||||
access sync.Mutex
|
cancelPending atomic.Pointer[context.CancelFunc]
|
||||||
ticker *time.Ticker
|
dispatcher routing.Dispatcher
|
||||||
tickerClose chan struct{}
|
access sync.Mutex
|
||||||
|
ticker *time.Ticker
|
||||||
|
|
||||||
Settings *HealthPingSettings
|
Settings *HealthPingSettings
|
||||||
Results map[string]*HealthPingRTTS
|
Results map[string]*HealthPingRTTS
|
||||||
@@ -62,10 +64,10 @@ func NewHealthPing(ctx context.Context, dispatcher routing.Dispatcher, config *H
|
|||||||
settings.Destination = "https://connectivitycheck.gstatic.com/generate_204"
|
settings.Destination = "https://connectivitycheck.gstatic.com/generate_204"
|
||||||
}
|
}
|
||||||
if settings.Interval == 0 {
|
if settings.Interval == 0 {
|
||||||
settings.Interval = time.Duration(1) * time.Minute
|
settings.Interval = 1 * time.Minute
|
||||||
} else if settings.Interval < 10 {
|
} else if settings.Interval < 10*time.Second {
|
||||||
errors.LogWarning(ctx, "health check interval is too small, 10s is applied")
|
errors.LogWarning(ctx, "health check interval is too small, 10s is applied")
|
||||||
settings.Interval = time.Duration(10) * time.Second
|
settings.Interval = 10 * time.Second
|
||||||
}
|
}
|
||||||
if settings.SamplingCount <= 0 {
|
if settings.SamplingCount <= 0 {
|
||||||
settings.SamplingCount = 10
|
settings.SamplingCount = 10
|
||||||
@@ -73,10 +75,12 @@ func NewHealthPing(ctx context.Context, dispatcher routing.Dispatcher, config *H
|
|||||||
if settings.Timeout <= 0 {
|
if settings.Timeout <= 0 {
|
||||||
// results are saved after all health pings finish,
|
// results are saved after all health pings finish,
|
||||||
// a larger timeout could possibly makes checks run longer
|
// a larger timeout could possibly makes checks run longer
|
||||||
settings.Timeout = time.Duration(5) * time.Second
|
settings.Timeout = 5 * time.Second
|
||||||
}
|
}
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
return &HealthPing{
|
return &HealthPing{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
cancelCtx: cancel,
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
Settings: settings,
|
Settings: settings,
|
||||||
Results: nil,
|
Results: nil,
|
||||||
@@ -90,9 +94,9 @@ func (h *HealthPing) StartScheduler(selector func() ([]string, error)) {
|
|||||||
}
|
}
|
||||||
interval := h.Settings.Interval * time.Duration(h.Settings.SamplingCount)
|
interval := h.Settings.Interval * time.Duration(h.Settings.SamplingCount)
|
||||||
ticker := time.NewTicker(interval)
|
ticker := time.NewTicker(interval)
|
||||||
tickerClose := make(chan struct{})
|
|
||||||
h.ticker = ticker
|
h.ticker = ticker
|
||||||
h.tickerClose = tickerClose
|
|
||||||
|
// init run to get a fast check result
|
||||||
go func() {
|
go func() {
|
||||||
tags, err := selector()
|
tags, err := selector()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -110,13 +114,20 @@ func (h *HealthPing) StartScheduler(selector func() ([]string, error)) {
|
|||||||
errors.LogWarning(h.ctx, "error select outbounds for scheduled health check: ", err)
|
errors.LogWarning(h.ctx, "error select outbounds for scheduled health check: ", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
h.doCheck(tags, interval, h.Settings.SamplingCount)
|
subCtx, cancel := context.WithCancel(h.ctx)
|
||||||
|
old := h.cancelPending.Swap(&cancel)
|
||||||
|
if old != nil {
|
||||||
|
errors.LogDebug(h.ctx, "scheduled health check not finished before next round, canceling previous one")
|
||||||
|
(*old)()
|
||||||
|
}
|
||||||
|
h.doCheck(subCtx, tags, interval, h.Settings.SamplingCount)
|
||||||
|
h.cancelPending.CompareAndSwap(&cancel, nil)
|
||||||
h.Cleanup(tags)
|
h.Cleanup(tags)
|
||||||
}()
|
}()
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
continue
|
continue
|
||||||
case <-tickerClose:
|
case <-h.ctx.Done():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,8 +141,7 @@ func (h *HealthPing) StopScheduler() {
|
|||||||
}
|
}
|
||||||
h.ticker.Stop()
|
h.ticker.Stop()
|
||||||
h.ticker = nil
|
h.ticker = nil
|
||||||
close(h.tickerClose)
|
h.cancelCtx()
|
||||||
h.tickerClose = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check implements the HealthChecker
|
// Check implements the HealthChecker
|
||||||
@@ -140,7 +150,7 @@ func (h *HealthPing) Check(tags []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
errors.LogInfo(h.ctx, "perform one-time health check for tags ", tags)
|
errors.LogInfo(h.ctx, "perform one-time health check for tags ", tags)
|
||||||
h.doCheck(tags, 0, 1)
|
h.doCheck(h.ctx, tags, 0, 1)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,13 +161,14 @@ type rtt struct {
|
|||||||
|
|
||||||
// doCheck performs the 'rounds' amount checks in given 'duration'. You should make
|
// doCheck performs the 'rounds' amount checks in given 'duration'. You should make
|
||||||
// sure all tags are valid for current balancer
|
// sure all tags are valid for current balancer
|
||||||
func (h *HealthPing) doCheck(tags []string, duration time.Duration, rounds int) {
|
// cancel ctx will stop all pending checks
|
||||||
|
func (h *HealthPing) doCheck(ctx context.Context, tags []string, duration time.Duration, rounds int) {
|
||||||
count := len(tags) * rounds
|
count := len(tags) * rounds
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ch := make(chan *rtt, count)
|
ch := make(chan *rtt, count)
|
||||||
|
timers := make([]*time.Timer, 0, count)
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
handler := tag
|
handler := tag
|
||||||
client := newPingClient(
|
client := newPingClient(
|
||||||
@@ -172,7 +183,7 @@ func (h *HealthPing) doCheck(tags []string, duration time.Duration, rounds int)
|
|||||||
if duration > 0 {
|
if duration > 0 {
|
||||||
delay = time.Duration(dice.RollInt63n(int64(duration)))
|
delay = time.Duration(dice.RollInt63n(int64(duration)))
|
||||||
}
|
}
|
||||||
time.AfterFunc(delay, func() {
|
timers = append(timers, time.AfterFunc(delay, func() {
|
||||||
errors.LogDebug(h.ctx, "checking ", handler)
|
errors.LogDebug(h.ctx, "checking ", handler)
|
||||||
delay, err := client.MeasureDelay(h.Settings.HttpMethod)
|
delay, err := client.MeasureDelay(h.Settings.HttpMethod)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -200,14 +211,21 @@ func (h *HealthPing) doCheck(tags []string, duration time.Duration, rounds int)
|
|||||||
handler: handler,
|
handler: handler,
|
||||||
value: rttFailed,
|
value: rttFailed,
|
||||||
}
|
}
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
rtt := <-ch
|
select {
|
||||||
if rtt.value > 0 {
|
case rtt := <-ch:
|
||||||
// should not put results when network is down
|
if rtt.value > 0 {
|
||||||
h.PutResult(rtt.handler, rtt.value)
|
// should not put results when network is down
|
||||||
|
h.PutResult(rtt.handler, rtt.value)
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
for _, timer := range timers {
|
||||||
|
timer.Stop()
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func (h *HealthPingRTTS) Put(d time.Duration) {
|
|||||||
if h.rtts == nil {
|
if h.rtts == nil {
|
||||||
h.rtts = make([]*pingRTT, h.cap)
|
h.rtts = make([]*pingRTT, h.cap)
|
||||||
for i := 0; i < h.cap; i++ {
|
for i := 0; i < h.cap; i++ {
|
||||||
h.rtts[i] = &pingRTT{}
|
h.rtts[i] = &pingRTT{value: rttUntested}
|
||||||
}
|
}
|
||||||
h.idx = -1
|
h.idx = -1
|
||||||
}
|
}
|
||||||
@@ -88,7 +88,7 @@ func (h *HealthPingRTTS) getStatistics() *HealthPingStats {
|
|||||||
validRTTs := make([]time.Duration, 0)
|
validRTTs := make([]time.Duration, 0)
|
||||||
for _, rtt := range h.rtts {
|
for _, rtt := range h.rtts {
|
||||||
switch {
|
switch {
|
||||||
case rtt.value == 0 || time.Since(rtt.time) > h.validity:
|
case rtt.value == rttUntested || time.Since(rtt.time) > h.validity:
|
||||||
continue
|
continue
|
||||||
case rtt.value == rttFailed:
|
case rtt.value == rttFailed:
|
||||||
stats.Fail++
|
stats.Fail++
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ func (o *Observer) probe(outbound string) ProbeResult {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var errorMessage = "the outbound " + outbound + " is dead: GET request failed:" + err.Error() + "with outbound handler report underlying connection failed"
|
errorMessage := "the outbound " + outbound + " is dead: GET request failed:" + err.Error() + "with outbound handler report underlying connection failed"
|
||||||
errors.LogInfoInner(o.ctx, errorCollectorForRequest.UnderlyingError(), errorMessage)
|
errors.LogInfoInner(o.ctx, errorCollectorForRequest.UnderlyingError(), errorMessage)
|
||||||
return ProbeResult{Alive: false, LastErrorReason: errorMessage}
|
return ProbeResult{Alive: false, LastErrorReason: errorMessage}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ func (s *handlerServer) GetInboundUsers(ctx context.Context, request *GetInbound
|
|||||||
if len(request.Email) > 0 {
|
if len(request.Email) > 0 {
|
||||||
return &GetInboundUserResponse{Users: []*protocol.User{protocol.ToProtoUser(um.GetUser(ctx, request.Email))}}, nil
|
return &GetInboundUserResponse{Users: []*protocol.User{protocol.ToProtoUser(um.GetUser(ctx, request.Email))}}, nil
|
||||||
}
|
}
|
||||||
var result = make([]*protocol.User, 0, 100)
|
result := make([]*protocol.User, 0, 100)
|
||||||
users := um.GetUsers(ctx)
|
users := um.GetUsers(ctx)
|
||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
result = append(result, protocol.ToProtoUser(u))
|
result = append(result, protocol.ToProtoUser(u))
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ import (
|
|||||||
|
|
||||||
// Manager manages all inbound handlers.
|
// Manager manages all inbound handlers.
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
access sync.RWMutex
|
access sync.RWMutex
|
||||||
untaggedHandlers []inbound.Handler
|
untaggedHandlers []inbound.Handler
|
||||||
taggedHandlers map[string]inbound.Handler
|
taggedHandlers map[string]inbound.Handler
|
||||||
running bool
|
running bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new Manager for inbound handlers.
|
// New returns a new Manager for inbound handlers.
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ func (h *Handler) SetOutboundGateway(ctx context.Context, ob *session.Outbound)
|
|||||||
errors.LogDebug(ctx, "use inbound source ip as sendthrough: ", inbound.Source.Address.String())
|
errors.LogDebug(ctx, "use inbound source ip as sendthrough: ", inbound.Source.Address.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//case addr.Family().IsDomain():
|
// case addr.Family().IsDomain():
|
||||||
default:
|
default:
|
||||||
ob.Gateway = addr
|
ob.Gateway = addr
|
||||||
|
|
||||||
@@ -396,7 +396,6 @@ func (h *Handler) ProxySettings() *serial.TypedMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ParseRandomIP(addr net.Address, prefix string) net.Address {
|
func ParseRandomIP(addr net.Address, prefix string) net.Address {
|
||||||
|
|
||||||
_, ipnet, _ := net.ParseCIDR(addr.IP().String() + "/" + prefix)
|
_, ipnet, _ := net.ParseCIDR(addr.IP().String() + "/" + prefix)
|
||||||
|
|
||||||
ones, bits := ipnet.Mask.Size()
|
ones, bits := ipnet.Mask.Size()
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestInterfaces(t *testing.T) {
|
func TestInterfaces(t *testing.T) {
|
||||||
_ = (outbound.Handler)(new(Handler))
|
_ = outbound.Handler(new(Handler))
|
||||||
_ = (outbound.Manager)(new(Manager))
|
_ = outbound.Manager(new(Manager))
|
||||||
}
|
}
|
||||||
|
|
||||||
const xrayKey core.XrayKey = 1
|
const xrayKey core.XrayKey = 1
|
||||||
@@ -43,7 +43,7 @@ func TestOutboundWithoutStatCounter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
v, _ := core.New(config)
|
v, _ := core.New(config)
|
||||||
v.AddFeature((outbound.Manager)(new(Manager)))
|
v.AddFeature(outbound.Manager(new(Manager)))
|
||||||
ctx := context.WithValue(context.Background(), xrayKey, v)
|
ctx := context.WithValue(context.Background(), xrayKey, v)
|
||||||
ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{{}})
|
ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{{}})
|
||||||
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
|
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
|
||||||
@@ -73,7 +73,7 @@ func TestOutboundWithStatCounter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
v, _ := core.New(config)
|
v, _ := core.New(config)
|
||||||
v.AddFeature((outbound.Manager)(new(Manager)))
|
v.AddFeature(outbound.Manager(new(Manager)))
|
||||||
ctx := context.WithValue(context.Background(), xrayKey, v)
|
ctx := context.WithValue(context.Background(), xrayKey, v)
|
||||||
ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{{}})
|
ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{{}})
|
||||||
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
|
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
|
||||||
@@ -88,7 +88,6 @@ func TestOutboundWithStatCounter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTagsCache(t *testing.T) {
|
func TestTagsCache(t *testing.T) {
|
||||||
|
|
||||||
test_duration := 10 * time.Second
|
test_duration := 10 * time.Second
|
||||||
threads_num := 50
|
threads_num := 50
|
||||||
delay := 10 * time.Millisecond
|
delay := 10 * time.Millisecond
|
||||||
|
|||||||
@@ -162,7 +162,6 @@ func (m *Manager) ListHandlers(ctx context.Context) []outbound.Handler {
|
|||||||
|
|
||||||
// Select implements outbound.HandlerSelector.
|
// Select implements outbound.HandlerSelector.
|
||||||
func (m *Manager) Select(selectors []string) []string {
|
func (m *Manager) Select(selectors []string) []string {
|
||||||
|
|
||||||
key := strings.Join(selectors, ",")
|
key := strings.Join(selectors, ",")
|
||||||
if cache, ok := m.tagsCache.Load(key); ok {
|
if cache, ok := m.tagsCache.Load(key); ok {
|
||||||
return cache.([]string)
|
return cache.([]string)
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ func (s *routingServer) AddRule(ctx context.Context, request *AddRuleRequest) (*
|
|||||||
return &AddRuleResponse{}, bo.AddRule(request.Config, request.ShouldAppend)
|
return &AddRuleResponse{}, bo.AddRule(request.Config, request.ShouldAppend)
|
||||||
}
|
}
|
||||||
return nil, errors.New("unsupported router implementation")
|
return nil, errors.New("unsupported router implementation")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *routingServer) RemoveRule(ctx context.Context, request *RemoveRuleRequest) (*RemoveRuleResponse, error) {
|
func (s *routingServer) RemoveRule(ctx context.Context, request *RemoveRuleRequest) (*RemoveRuleResponse, error) {
|
||||||
|
|||||||
@@ -110,7 +110,6 @@ func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) {
|
|||||||
|
|
||||||
// AddRule implements routing.Router.
|
// AddRule implements routing.Router.
|
||||||
func (r *Router) AddRule(config *serial.TypedMessage, shouldAppend bool) error {
|
func (r *Router) AddRule(config *serial.TypedMessage, shouldAppend bool) error {
|
||||||
|
|
||||||
inst, err := config.GetInstance()
|
inst, err := config.GetInstance()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -227,7 +226,6 @@ func (r *Router) RemoveRule(tag string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return errors.New("empty tag name!")
|
return errors.New("empty tag name!")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListRule implements routing.Router
|
// ListRule implements routing.Router
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ func TestSelectLeastExpected(t *testing.T) {
|
|||||||
t.Errorf("expected: %v, actual: %v", expected, len(ns))
|
t.Errorf("expected: %v, actual: %v", expected, len(ns))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectLeastExpected2(t *testing.T) {
|
func TestSelectLeastExpected2(t *testing.T) {
|
||||||
strategy := &LeastLoadStrategy{
|
strategy := &LeastLoadStrategy{
|
||||||
settings: &StrategyLeastLoadConfig{
|
settings: &StrategyLeastLoadConfig{
|
||||||
@@ -102,6 +103,7 @@ func TestSelectLeastExpected2(t *testing.T) {
|
|||||||
t.Errorf("expected: %v, actual: %v", expected, len(ns))
|
t.Errorf("expected: %v, actual: %v", expected, len(ns))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectLeastExpectedAndBaselines(t *testing.T) {
|
func TestSelectLeastExpectedAndBaselines(t *testing.T) {
|
||||||
strategy := &LeastLoadStrategy{
|
strategy := &LeastLoadStrategy{
|
||||||
settings: &StrategyLeastLoadConfig{
|
settings: &StrategyLeastLoadConfig{
|
||||||
@@ -122,6 +124,7 @@ func TestSelectLeastExpectedAndBaselines(t *testing.T) {
|
|||||||
t.Errorf("expected: %v, actual: %v", expected, len(ns))
|
t.Errorf("expected: %v, actual: %v", expected, len(ns))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectLeastExpectedAndBaselines2(t *testing.T) {
|
func TestSelectLeastExpectedAndBaselines2(t *testing.T) {
|
||||||
strategy := &LeastLoadStrategy{
|
strategy := &LeastLoadStrategy{
|
||||||
settings: &StrategyLeastLoadConfig{
|
settings: &StrategyLeastLoadConfig{
|
||||||
@@ -142,6 +145,7 @@ func TestSelectLeastExpectedAndBaselines2(t *testing.T) {
|
|||||||
t.Errorf("expected: %v, actual: %v", expected, len(ns))
|
t.Errorf("expected: %v, actual: %v", expected, len(ns))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectLeastLoadBaselines(t *testing.T) {
|
func TestSelectLeastLoadBaselines(t *testing.T) {
|
||||||
strategy := &LeastLoadStrategy{
|
strategy := &LeastLoadStrategy{
|
||||||
settings: &StrategyLeastLoadConfig{
|
settings: &StrategyLeastLoadConfig{
|
||||||
@@ -160,6 +164,7 @@ func TestSelectLeastLoadBaselines(t *testing.T) {
|
|||||||
t.Errorf("expected: %v, actual: %v", expected, len(ns))
|
t.Errorf("expected: %v, actual: %v", expected, len(ns))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectLeastLoadBaselinesNoQualified(t *testing.T) {
|
func TestSelectLeastLoadBaselinesNoQualified(t *testing.T) {
|
||||||
strategy := &LeastLoadStrategy{
|
strategy := &LeastLoadStrategy{
|
||||||
settings: &StrategyLeastLoadConfig{
|
settings: &StrategyLeastLoadConfig{
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ import (
|
|||||||
routing_session "github.com/xtls/xray-core/features/routing/session"
|
routing_session "github.com/xtls/xray-core/features/routing/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func ptr[T any](v T) *T { return &v }
|
func ptr[T any](v T) *T { return &v }
|
||||||
|
|
||||||
type event struct {
|
type event struct {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestInterface(t *testing.T) {
|
func TestInterface(t *testing.T) {
|
||||||
_ = (stats.Manager)(new(Manager))
|
_ = stats.Manager(new(Manager))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStatsChannelRunnable(t *testing.T) {
|
func TestStatsChannelRunnable(t *testing.T) {
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ package version
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Version struct {
|
type Version struct {
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ func MergeMulti(dest MultiBuffer, src MultiBuffer) (MultiBuffer, MultiBuffer) {
|
|||||||
// MergeBytes merges the given bytes into MultiBuffer and return the new address of the merged MultiBuffer.
|
// MergeBytes merges the given bytes into MultiBuffer and return the new address of the merged MultiBuffer.
|
||||||
func MergeBytes(dest MultiBuffer, src []byte) MultiBuffer {
|
func MergeBytes(dest MultiBuffer, src []byte) MultiBuffer {
|
||||||
n := len(dest)
|
n := len(dest)
|
||||||
if n > 0 && !(dest)[n-1].IsFull() {
|
if n > 0 && !dest[n-1].IsFull() {
|
||||||
nBytes, _ := (dest)[n-1].Write(src)
|
nBytes, _ := dest[n-1].Write(src)
|
||||||
src = src[nBytes:]
|
src = src[nBytes:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,11 +121,11 @@ func TestPacketReader_ReadMultiBuffer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestReaderInterface(t *testing.T) {
|
func TestReaderInterface(t *testing.T) {
|
||||||
_ = (io.Reader)(new(ReadVReader))
|
_ = io.Reader(new(ReadVReader))
|
||||||
_ = (Reader)(new(ReadVReader))
|
_ = Reader(new(ReadVReader))
|
||||||
|
|
||||||
_ = (Reader)(new(BufferedReader))
|
_ = Reader(new(BufferedReader))
|
||||||
_ = (io.Reader)(new(BufferedReader))
|
_ = io.Reader(new(BufferedReader))
|
||||||
_ = (io.ByteReader)(new(BufferedReader))
|
_ = io.ByteReader(new(BufferedReader))
|
||||||
_ = (io.WriterTo)(new(BufferedReader))
|
_ = io.WriterTo(new(BufferedReader))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func (r *posixReader) Init(bs []*Buffer) {
|
|||||||
}
|
}
|
||||||
for idx, b := range bs {
|
for idx, b := range bs {
|
||||||
iovecs = append(iovecs, syscall.Iovec{
|
iovecs = append(iovecs, syscall.Iovec{
|
||||||
Base: &(b.v[0]),
|
Base: &b.v[0],
|
||||||
})
|
})
|
||||||
iovecs[idx].SetLen(int(Size))
|
iovecs[idx].SetLen(int(Size))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ func TestIPMatcherAnyMatchAndMatches(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !matcher.AnyMatch([]net.IP{
|
if !matcher.AnyMatch([]net.IP{
|
||||||
net.IP{},
|
{},
|
||||||
ip("1.1.1.1"),
|
ip("1.1.1.1"),
|
||||||
ip("8.8.8.8"),
|
ip("8.8.8.8"),
|
||||||
}) {
|
}) {
|
||||||
@@ -345,7 +345,7 @@ func TestIPMatcherAnyMatchAndMatches(t *testing.T) {
|
|||||||
|
|
||||||
if matcher.Matches([]net.IP{
|
if matcher.Matches([]net.IP{
|
||||||
ip("8.8.8.8"),
|
ip("8.8.8.8"),
|
||||||
net.IP{},
|
{},
|
||||||
}) {
|
}) {
|
||||||
t.Fatal("expect Matches to be false when any IP is invalid")
|
t.Fatal("expect Matches to be false when any IP is invalid")
|
||||||
}
|
}
|
||||||
@@ -362,7 +362,7 @@ func TestIPMatcherFilterIPs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
matched, unmatched := matcher.FilterIPs([]net.IP{
|
matched, unmatched := matcher.FilterIPs([]net.IP{
|
||||||
net.IP{},
|
{},
|
||||||
ip("8.8.8.8"),
|
ip("8.8.8.8"),
|
||||||
ip("91.108.255.254"),
|
ip("91.108.255.254"),
|
||||||
ip("1.1.1.1"),
|
ip("1.1.1.1"),
|
||||||
|
|||||||
@@ -98,7 +98,6 @@ func (l *generalLogger) run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *generalLogger) Handle(msg Message) {
|
func (l *generalLogger) Handle(msg Message) {
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case l.buffer <- msg:
|
case l.buffer <- msg:
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -36,9 +36,10 @@ func TestNormalizeEnvName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEnvFlag(t *testing.T) {
|
func TestEnvFlag(t *testing.T) {
|
||||||
if v := (EnvFlag{
|
v := EnvFlag{
|
||||||
Name: "xxxxx.y",
|
Name: "xxxxx.y",
|
||||||
}.GetValueAsInt(10)); v != 10 {
|
}.GetValueAsInt(10)
|
||||||
|
if v != 10 {
|
||||||
t.Error("env value: ", v)
|
t.Error("env value: ", v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ type ID struct {
|
|||||||
|
|
||||||
// Equals returns true if this ID equals to the other one.
|
// Equals returns true if this ID equals to the other one.
|
||||||
func (id *ID) Equals(another *ID) bool {
|
func (id *ID) Equals(another *ID) bool {
|
||||||
return id.uuid.Equals(&(another.uuid))
|
return id.uuid.Equals(&another.uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (id *ID) Bytes() []byte {
|
func (id *ID) Bytes() []byte {
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ func TestSniffQUICComplex(t *testing.T) {
|
|||||||
t.Errorf("SniffQUIC() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("SniffQUIC() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (errors.Is(err, protocol.ErrProtoNeedMoreData)) != tt.needsMoreData {
|
if errors.Is(err, protocol.ErrProtoNeedMoreData) != tt.needsMoreData {
|
||||||
t.Errorf("SniffQUIC() error = %v, expectsNoClue %v", err, tt.needsMoreData)
|
t.Errorf("SniffQUIC() error = %v, expectsNoClue %v", err, tt.needsMoreData)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ServerSpec struct {
|
type ServerSpec struct {
|
||||||
Destination net.Destination
|
Destination net.Destination
|
||||||
User *MemoryUser
|
User *MemoryUser
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServerSpec(dest net.Destination, user *MemoryUser) *ServerSpec {
|
func NewServerSpec(dest net.Destination, user *MemoryUser) *ServerSpec {
|
||||||
|
|||||||
@@ -228,7 +228,6 @@ func isValueKind(kind reflect.Kind) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func marshalInterface(v interface{}, ignoreNullValue bool, insertTypeInfo bool) interface{} {
|
func marshalInterface(v interface{}, ignoreNullValue bool, insertTypeInfo bool) interface{} {
|
||||||
|
|
||||||
if r, ok := marshalKnownType(v, ignoreNullValue, insertTypeInfo); ok {
|
if r, ok := marshalKnownType(v, ignoreNullValue, insertTypeInfo); ok {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ func TestMashalAccount(t *testing.T) {
|
|||||||
|
|
||||||
j, ok := MarshalToJson(user, false)
|
j, ok := MarshalToJson(user, false)
|
||||||
if !ok || strings.Contains(j, "_TypedMessage_") {
|
if !ok || strings.Contains(j, "_TypedMessage_") {
|
||||||
|
|
||||||
t.Error("marshal account failed")
|
t.Error("marshal account failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,13 +78,12 @@ func TestMashalStruct(t *testing.T) {
|
|||||||
|
|
||||||
v := (*f2.Arr)[0]["foo"]["hello"]
|
v := (*f2.Arr)[0]["foo"]["hello"]
|
||||||
|
|
||||||
if f1.N != f2.N || *(f1.Np) != *(f2.Np) || f1.S != f2.S || v != "world" {
|
if f1.N != f2.N || *f1.Np != *f2.Np || f1.S != f2.S || v != "world" {
|
||||||
t.Error("f1 not equal to f2")
|
t.Error("f1 not equal to f2")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshalConfigJson(t *testing.T) {
|
func TestMarshalConfigJson(t *testing.T) {
|
||||||
|
|
||||||
buf := bytes.NewBufferString(getConfig())
|
buf := bytes.NewBufferString(getConfig())
|
||||||
config, err := iserial.DecodeJSONConfig(buf)
|
config, err := iserial.DecodeJSONConfig(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/xtls/xray-core/common/serial"
|
. "github.com/xtls/xray-core/common/serial"
|
||||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/custom"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetInstance(t *testing.T) {
|
func TestGetInstance(t *testing.T) {
|
||||||
@@ -23,31 +22,3 @@ func TestConvertingNilMessage(t *testing.T) {
|
|||||||
t.Error("expect nil, but actually not")
|
t.Error("expect nil, but actually not")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTypedMessageRoundTripPreservesFinalmaskCustomUDPMode(t *testing.T) {
|
|
||||||
msg := &custom.UDPConfig{
|
|
||||||
Mode: "standalone",
|
|
||||||
Client: []*custom.UDPItem{
|
|
||||||
{Rand: 12, Save: "txid"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
tm := ToTypedMessage(msg)
|
|
||||||
if tm == nil {
|
|
||||||
t.Fatal("expected typed message")
|
|
||||||
}
|
|
||||||
|
|
||||||
roundTrip, err := tm.GetInstance()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("GetInstance() failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
udp, ok := roundTrip.(*custom.UDPConfig)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("unexpected round-trip type: %T", roundTrip)
|
|
||||||
}
|
|
||||||
|
|
||||||
if udp.GetMode() != "standalone" {
|
|
||||||
t.Fatalf("mode lost during typed message round-trip: got %q", udp.GetMode())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,37 +10,44 @@ func TestByteSizes(t *testing.T) {
|
|||||||
size := units.ByteSize(0)
|
size := units.ByteSize(0)
|
||||||
assertSizeString(t, size, "0")
|
assertSizeString(t, size, "0")
|
||||||
size++
|
size++
|
||||||
assertSizeValue(t,
|
assertSizeValue(
|
||||||
|
t,
|
||||||
assertSizeString(t, size, "1.00B"),
|
assertSizeString(t, size, "1.00B"),
|
||||||
size,
|
size,
|
||||||
)
|
)
|
||||||
size <<= 10
|
size <<= 10
|
||||||
assertSizeValue(t,
|
assertSizeValue(
|
||||||
|
t,
|
||||||
assertSizeString(t, size, "1.00KB"),
|
assertSizeString(t, size, "1.00KB"),
|
||||||
size,
|
size,
|
||||||
)
|
)
|
||||||
size <<= 10
|
size <<= 10
|
||||||
assertSizeValue(t,
|
assertSizeValue(
|
||||||
|
t,
|
||||||
assertSizeString(t, size, "1.00MB"),
|
assertSizeString(t, size, "1.00MB"),
|
||||||
size,
|
size,
|
||||||
)
|
)
|
||||||
size <<= 10
|
size <<= 10
|
||||||
assertSizeValue(t,
|
assertSizeValue(
|
||||||
|
t,
|
||||||
assertSizeString(t, size, "1.00GB"),
|
assertSizeString(t, size, "1.00GB"),
|
||||||
size,
|
size,
|
||||||
)
|
)
|
||||||
size <<= 10
|
size <<= 10
|
||||||
assertSizeValue(t,
|
assertSizeValue(
|
||||||
|
t,
|
||||||
assertSizeString(t, size, "1.00TB"),
|
assertSizeString(t, size, "1.00TB"),
|
||||||
size,
|
size,
|
||||||
)
|
)
|
||||||
size <<= 10
|
size <<= 10
|
||||||
assertSizeValue(t,
|
assertSizeValue(
|
||||||
|
t,
|
||||||
assertSizeString(t, size, "1.00PB"),
|
assertSizeString(t, size, "1.00PB"),
|
||||||
size,
|
size,
|
||||||
)
|
)
|
||||||
size <<= 10
|
size <<= 10
|
||||||
assertSizeValue(t,
|
assertSizeValue(
|
||||||
|
t,
|
||||||
assertSizeString(t, size, "1.00EB"),
|
assertSizeString(t, size, "1.00EB"),
|
||||||
size,
|
size,
|
||||||
)
|
)
|
||||||
|
|||||||
+77
-43
@@ -4,10 +4,10 @@ import (
|
|||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/klauspost/cpuid/v2"
|
"github.com/klauspost/cpuid/v2"
|
||||||
)
|
)
|
||||||
@@ -18,6 +18,7 @@ func GetRandomizer() *rand.Rand {
|
|||||||
fnvHash.Write([]byte(strconv.Itoa(cpuid.CPU.Family) + strconv.Itoa(cpuid.CPU.Model) + strconv.Itoa(cpuid.CPU.PhysicalCores) + strconv.Itoa(cpuid.CPU.LogicalCores) + strconv.Itoa(cpuid.CPU.CacheLine) + strconv.Itoa(cpuid.CPU.ThreadsPerCore)))
|
fnvHash.Write([]byte(strconv.Itoa(cpuid.CPU.Family) + strconv.Itoa(cpuid.CPU.Model) + strconv.Itoa(cpuid.CPU.PhysicalCores) + strconv.Itoa(cpuid.CPU.LogicalCores) + strconv.Itoa(cpuid.CPU.CacheLine) + strconv.Itoa(cpuid.CPU.ThreadsPerCore)))
|
||||||
return rand.New(rand.NewSource(int64(fnvHash.Sum64())))
|
return rand.New(rand.NewSource(int64(fnvHash.Sum64())))
|
||||||
}
|
}
|
||||||
|
|
||||||
var globalRng *rand.Rand = GetRandomizer()
|
var globalRng *rand.Rand = GetRandomizer()
|
||||||
|
|
||||||
// The Chrome version generator will suffer from deviation of a normal distribution.
|
// The Chrome version generator will suffer from deviation of a normal distribution.
|
||||||
@@ -26,83 +27,114 @@ func ChromeVersion() int {
|
|||||||
var startVersion int = 144
|
var startVersion int = 144
|
||||||
var timeStart int64 = time.Date(2026, 1, 13, 0, 0, 0, 0, time.UTC).Unix() / 86400
|
var timeStart int64 = time.Date(2026, 1, 13, 0, 0, 0, 0, time.UTC).Unix() / 86400
|
||||||
var timeCurrent int64 = time.Now().Unix() / 86400
|
var timeCurrent int64 = time.Now().Unix() / 86400
|
||||||
var timeDiff int = int((timeCurrent - timeStart - 35)) - int(math.Floor(math.Pow(globalRng.Float64(), 2) * 105))
|
var timeDiff int = int((timeCurrent - timeStart - 35)) - int(math.Floor(math.Pow(globalRng.Float64(), 2)*105))
|
||||||
return startVersion + (timeDiff / 35) // It's 31.15 currently.
|
return startVersion + (timeDiff / 35) // It's 31.15 currently.
|
||||||
}
|
}
|
||||||
|
|
||||||
var safariMinorMap [25]int = [25]int{0, 0, 0, 1, 1,
|
var safariMinorMap [25]int = [25]int{
|
||||||
|
0, 0, 0, 1, 1,
|
||||||
1, 2, 2, 2, 2, 3, 3, 3, 4, 4,
|
1, 2, 2, 2, 2, 3, 3, 3, 4, 4,
|
||||||
4, 5, 5, 5, 5, 5, 6, 6, 6, 6}
|
4, 5, 5, 5, 5, 5, 6, 6, 6, 6,
|
||||||
|
}
|
||||||
|
|
||||||
// The following version generators use deterministic generators, but with the distribution scaled by a curve.
|
// The following version generators use deterministic generators, but with the distribution scaled by a curve.
|
||||||
func CurlVersion() string {
|
func CurlVersion() string {
|
||||||
// curl 8.0.0 was released on 20/03/2023.
|
// curl 8.0.0 was released on 20/03/2023.
|
||||||
var timeCurrent int64 = time.Now().Unix() / 86400
|
var timeCurrent int64 = time.Now().Unix() / 86400
|
||||||
var timeStart int64 = time.Date(2023, 3, 20, 0, 0, 0, 0, time.UTC).Unix() / 86400
|
var timeStart int64 = time.Date(2023, 3, 20, 0, 0, 0, 0, time.UTC).Unix() / 86400
|
||||||
var timeDiff int = int((timeCurrent - timeStart - 60)) - int(math.Floor(math.Pow(globalRng.Float64(), 2) * 165))
|
var timeDiff int = int((timeCurrent - timeStart - 60)) - int(math.Floor(math.Pow(globalRng.Float64(), 2)*165))
|
||||||
var minorValue int = int(timeDiff / 57) // The release cadence is actually 56.67 days.
|
var minorValue int = int(timeDiff / 57) // The release cadence is actually 56.67 days.
|
||||||
return "8." + strconv.Itoa(minorValue) + ".0"
|
return "8." + strconv.Itoa(minorValue) + ".0"
|
||||||
}
|
}
|
||||||
|
|
||||||
func FirefoxVersion() int {
|
func FirefoxVersion() int {
|
||||||
// Firefox 128 ESR was released on 09/07/2023.
|
// Firefox 128 ESR was released on 09/07/2023.
|
||||||
var timeCurrent int64 = time.Now().Unix() / 86400
|
var timeCurrent int64 = time.Now().Unix() / 86400
|
||||||
var timeStart int64 = time.Date(2024, 7, 29, 0, 0, 0, 0, time.UTC).Unix() / 86400
|
var timeStart int64 = time.Date(2024, 7, 29, 0, 0, 0, 0, time.UTC).Unix() / 86400
|
||||||
var timeDiff = timeCurrent - timeStart - 25 - int64(math.Floor(math.Pow(globalRng.Float64(), 2) * 50))
|
timeDiff := timeCurrent - timeStart - 25 - int64(math.Floor(math.Pow(globalRng.Float64(), 2)*50))
|
||||||
return int(timeDiff / 30) + 128
|
return int(timeDiff/30) + 128
|
||||||
}
|
}
|
||||||
|
|
||||||
func SafariVersion() string {
|
func SafariVersion() string {
|
||||||
var anchoredTime time.Time = time.Now()
|
var anchoredTime time.Time = time.Now()
|
||||||
var releaseYear int = anchoredTime.Year()
|
var releaseYear int = anchoredTime.Year()
|
||||||
var splitPoint time.Time = time.Date(releaseYear, 9, 23, 0, 0, 0, 0, time.UTC)
|
var splitPoint time.Time = time.Date(releaseYear, 9, 23, 0, 0, 0, 0, time.UTC)
|
||||||
var delayedDays = int(math.Floor(math.Pow(globalRng.Float64(), 3) * 75))
|
delayedDays := int(math.Floor(math.Pow(globalRng.Float64(), 3) * 75))
|
||||||
splitPoint = splitPoint.AddDate(0, 0, delayedDays)
|
splitPoint = splitPoint.AddDate(0, 0, delayedDays)
|
||||||
if (anchoredTime.Compare(splitPoint) < 0) {
|
if anchoredTime.Compare(splitPoint) < 0 {
|
||||||
releaseYear --
|
releaseYear--
|
||||||
splitPoint = time.Date(releaseYear, 9, 23, 0, 0, 0, 0, time.UTC)
|
splitPoint = time.Date(releaseYear, 9, 23, 0, 0, 0, 0, time.UTC)
|
||||||
splitPoint = splitPoint.AddDate(0, 0, delayedDays)
|
splitPoint = splitPoint.AddDate(0, 0, delayedDays)
|
||||||
}
|
}
|
||||||
var minorVersion = safariMinorMap[(anchoredTime.Unix() - splitPoint.Unix()) / 1296000]
|
minorVersion := safariMinorMap[(anchoredTime.Unix()-splitPoint.Unix())/1296000]
|
||||||
return strconv.Itoa(releaseYear - 1999) + "." + strconv.Itoa(minorVersion)
|
return strconv.Itoa(releaseYear-1999) + "." + strconv.Itoa(minorVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The full Chromium brand GREASE implementation
|
// The full Chromium brand GREASE implementation
|
||||||
var clientHintGreaseNA = []string{" ", "(", ":", "-", ".", "/", ")", ";", "=", "?", "_"}
|
var (
|
||||||
var clientHintVersionNA = []string{"8", "99", "24"}
|
clientHintGreaseNA = []string{" ", "(", ":", "-", ".", "/", ")", ";", "=", "?", "_"}
|
||||||
var clientHintShuffle3 = [][3]int{{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}}
|
clientHintVersionNA = []string{"8", "99", "24"}
|
||||||
var clientHintShuffle4 = [][4]int{
|
clientHintShuffle3 = [][3]int{{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}}
|
||||||
{0, 1, 2, 3}, {0, 1, 3, 2}, {0, 2, 1, 3}, {0, 2, 3, 1}, {0, 3, 1, 2}, {0, 3, 2, 1},
|
clientHintShuffle4 = [][4]int{
|
||||||
{1, 0, 2, 3}, {1, 0, 3, 2}, {1, 2, 0, 3}, {1, 2, 3, 0}, {1, 3, 0, 2}, {1, 3, 2, 0},
|
{0, 1, 2, 3},
|
||||||
{2, 0, 1, 3}, {2, 0, 3, 1}, {2, 1, 0, 3}, {2, 1, 3, 0}, {2, 3, 0, 1}, {2, 3, 1, 0},
|
{0, 1, 3, 2},
|
||||||
{3, 0, 1, 2}, {3, 0, 2, 1}, {3, 1, 0, 2}, {3, 1, 2, 0}, {3, 2, 0, 1}, {3, 2, 1, 0}}
|
{0, 2, 1, 3},
|
||||||
|
{0, 2, 3, 1},
|
||||||
|
{0, 3, 1, 2},
|
||||||
|
{0, 3, 2, 1},
|
||||||
|
{1, 0, 2, 3},
|
||||||
|
{1, 0, 3, 2},
|
||||||
|
{1, 2, 0, 3},
|
||||||
|
{1, 2, 3, 0},
|
||||||
|
{1, 3, 0, 2},
|
||||||
|
{1, 3, 2, 0},
|
||||||
|
{2, 0, 1, 3},
|
||||||
|
{2, 0, 3, 1},
|
||||||
|
{2, 1, 0, 3},
|
||||||
|
{2, 1, 3, 0},
|
||||||
|
{2, 3, 0, 1},
|
||||||
|
{2, 3, 1, 0},
|
||||||
|
{3, 0, 1, 2},
|
||||||
|
{3, 0, 2, 1},
|
||||||
|
{3, 1, 0, 2},
|
||||||
|
{3, 1, 2, 0},
|
||||||
|
{3, 2, 0, 1},
|
||||||
|
{3, 2, 1, 0},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
func getGreasedChInvalidBrand(seed int) string {
|
func getGreasedChInvalidBrand(seed int) string {
|
||||||
return "\"Not" + clientHintGreaseNA[seed % len(clientHintGreaseNA)] + "A" + clientHintGreaseNA[(seed + 1) % len(clientHintGreaseNA)] + "Brand\";v=\"" + clientHintVersionNA[seed % len(clientHintVersionNA)] + "\"";
|
return "\"Not" + clientHintGreaseNA[seed%len(clientHintGreaseNA)] + "A" + clientHintGreaseNA[(seed+1)%len(clientHintGreaseNA)] + "Brand\";v=\"" + clientHintVersionNA[seed%len(clientHintVersionNA)] + "\""
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGreasedChOrder(brandLength int, seed int) []int {
|
func getGreasedChOrder(brandLength int, seed int) []int {
|
||||||
switch brandLength {
|
switch brandLength {
|
||||||
case 1:
|
case 1:
|
||||||
return []int{0}
|
return []int{0}
|
||||||
case 2:
|
case 2:
|
||||||
return []int{seed % brandLength, (seed + 1) % brandLength}
|
return []int{seed % brandLength, (seed + 1) % brandLength}
|
||||||
case 3:
|
case 3:
|
||||||
return clientHintShuffle3[seed % len(clientHintShuffle3)][:]
|
return clientHintShuffle3[seed%len(clientHintShuffle3)][:]
|
||||||
default:
|
default:
|
||||||
return clientHintShuffle4[seed % len(clientHintShuffle4)][:]
|
return clientHintShuffle4[seed%len(clientHintShuffle4)][:]
|
||||||
}
|
}
|
||||||
//return []int{}
|
//return []int{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUngreasedChUa(majorVersion int, forkName string) []string {
|
func getUngreasedChUa(majorVersion int, forkName string) []string {
|
||||||
// Set the capacity to 4, the maximum allowed brand size, so Go will never allocate memory twice
|
// Set the capacity to 4, the maximum allowed brand size, so Go will never allocate memory twice
|
||||||
baseChUa := make([]string, 0, 4)
|
baseChUa := make([]string, 0, 4)
|
||||||
baseChUa = append(baseChUa, getGreasedChInvalidBrand(majorVersion),
|
baseChUa = append(baseChUa, getGreasedChInvalidBrand(majorVersion),
|
||||||
"\"Chromium\";v=\"" + strconv.Itoa(majorVersion) + "\"")
|
"\"Chromium\";v=\""+strconv.Itoa(majorVersion)+"\"")
|
||||||
switch forkName {
|
switch forkName {
|
||||||
case "chrome":
|
case "chrome":
|
||||||
baseChUa = append(baseChUa, "\"Google Chrome\";v=\"" + strconv.Itoa(majorVersion) + "\"")
|
baseChUa = append(baseChUa, "\"Google Chrome\";v=\""+strconv.Itoa(majorVersion)+"\"")
|
||||||
case "edge":
|
case "edge":
|
||||||
baseChUa = append(baseChUa, "\"Microsoft Edge\";v=\"" + strconv.Itoa(majorVersion) + "\"")
|
baseChUa = append(baseChUa, "\"Microsoft Edge\";v=\""+strconv.Itoa(majorVersion)+"\"")
|
||||||
}
|
}
|
||||||
return baseChUa
|
return baseChUa
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGreasedChUa(majorVersion int, forkName string) string {
|
func getGreasedChUa(majorVersion int, forkName string) string {
|
||||||
ungreasedCh := getUngreasedChUa(majorVersion, forkName)
|
ungreasedCh := getUngreasedChUa(majorVersion, forkName)
|
||||||
shuffleMap := getGreasedChOrder(len(ungreasedCh), majorVersion)
|
shuffleMap := getGreasedChOrder(len(ungreasedCh), majorVersion)
|
||||||
@@ -114,16 +146,18 @@ func getGreasedChUa(majorVersion int, forkName string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The code below provides a coherent default browser user agent string based on a CPU-seeded PRNG.
|
// The code below provides a coherent default browser user agent string based on a CPU-seeded PRNG.
|
||||||
var CurlUA = "curl/" + CurlVersion()
|
var (
|
||||||
var AnchoredFirefoxVersion = strconv.Itoa(FirefoxVersion())
|
CurlUA = "curl/" + CurlVersion()
|
||||||
var FirefoxUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:" + AnchoredFirefoxVersion + ".0) Gecko/20100101 Firefox/" + AnchoredFirefoxVersion + ".0"
|
AnchoredFirefoxVersion = strconv.Itoa(FirefoxVersion())
|
||||||
var SafariUA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/" + SafariVersion() + " Safari/605.1.15"
|
FirefoxUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:" + AnchoredFirefoxVersion + ".0) Gecko/20100101 Firefox/" + AnchoredFirefoxVersion + ".0"
|
||||||
// Chromium browsers.
|
SafariUA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/" + SafariVersion() + " Safari/605.1.15"
|
||||||
var AnchoredChromeVersion = ChromeVersion()
|
// Chromium browsers.
|
||||||
var ChromeUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + strconv.Itoa(AnchoredChromeVersion) + ".0.0.0 Safari/537.36"
|
AnchoredChromeVersion = ChromeVersion()
|
||||||
var ChromeUACH = getGreasedChUa(AnchoredChromeVersion, "chrome")
|
ChromeUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + strconv.Itoa(AnchoredChromeVersion) + ".0.0.0 Safari/537.36"
|
||||||
var MSEdgeUA = ChromeUA + "Edg/" + strconv.Itoa(AnchoredChromeVersion) + ".0.0.0"
|
ChromeUACH = getGreasedChUa(AnchoredChromeVersion, "chrome")
|
||||||
var MSEdgeUACH = getGreasedChUa(AnchoredChromeVersion, "edge")
|
MSEdgeUA = ChromeUA + "Edg/" + strconv.Itoa(AnchoredChromeVersion) + ".0.0.0"
|
||||||
|
MSEdgeUACH = getGreasedChUa(AnchoredChromeVersion, "edge")
|
||||||
|
)
|
||||||
|
|
||||||
func applyMasqueradedHeaders(header http.Header, browser string, variant string) {
|
func applyMasqueradedHeaders(header http.Header, browser string, variant string) {
|
||||||
// Browser-specific.
|
// Browser-specific.
|
||||||
|
|||||||
@@ -109,4 +109,4 @@ func (m *TypedSyncMap[K, V]) Swap(key K, value V) (previous V, loaded bool) {
|
|||||||
previous = anyPrevious.(V)
|
previous = anyPrevious.(V)
|
||||||
}
|
}
|
||||||
return previous, loaded
|
return previous, loaded
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-20
@@ -19,18 +19,18 @@ import (
|
|||||||
// Filesystem paths and abstract sockets on other platforms are returned
|
// Filesystem paths and abstract sockets on other platforms are returned
|
||||||
// unchanged.
|
// unchanged.
|
||||||
func ResolveSocketPath(path string) string {
|
func ResolveSocketPath(path string) string {
|
||||||
if len(path) == 0 || path[0] != '@' {
|
if len(path) == 0 || path[0] != '@' {
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
if runtime.GOOS != "linux" && runtime.GOOS != "android" {
|
if runtime.GOOS != "linux" && runtime.GOOS != "android" {
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
if len(path) > 1 && path[1] == '@' {
|
if len(path) > 1 && path[1] == '@' {
|
||||||
fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path))
|
fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path))
|
||||||
copy(fullAddr, path[1:])
|
copy(fullAddr, path[1:])
|
||||||
return string(fullAddr)
|
return string(fullAddr)
|
||||||
}
|
}
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
// SplitHTTPUnixURL splits a target into an HTTP URL and an optional Unix
|
// SplitHTTPUnixURL splits a target into an HTTP URL and an optional Unix
|
||||||
@@ -44,12 +44,11 @@ func ResolveSocketPath(path string) string {
|
|||||||
// The :/ separator delimits the socket path from the HTTP request path.
|
// The :/ separator delimits the socket path from the HTTP request path.
|
||||||
// If omitted, "/" is used.
|
// If omitted, "/" is used.
|
||||||
func SplitHTTPUnixURL(raw string) (httpURL, socketPath string) {
|
func SplitHTTPUnixURL(raw string) (httpURL, socketPath string) {
|
||||||
if len(raw) == 0 || (!filepath.IsAbs(raw) && raw[0] != '@') {
|
if len(raw) == 0 || (!filepath.IsAbs(raw) && raw[0] != '@') {
|
||||||
return raw, ""
|
return raw, ""
|
||||||
}
|
}
|
||||||
if idx := strings.Index(raw, ":/"); idx >= 0 {
|
if idx := strings.Index(raw, ":/"); idx >= 0 {
|
||||||
return "http://localhost" + raw[idx+1:], raw[:idx]
|
return "http://localhost" + raw[idx+1:], raw[:idx]
|
||||||
}
|
}
|
||||||
return "http://localhost/", raw
|
return "http://localhost/", raw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -19,8 +19,8 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
Version_x byte = 26
|
Version_x byte = 26
|
||||||
Version_y byte = 5
|
Version_y byte = 6
|
||||||
Version_z byte = 9
|
Version_z byte = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ func (ctx *Context) GetSourceIPs() []net.IP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSourcePort implements routing.Context.
|
// GetSourcePort implements routing.Context.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ require (
|
|||||||
github.com/klauspost/cpuid/v2 v2.3.0
|
github.com/klauspost/cpuid/v2 v2.3.0
|
||||||
github.com/miekg/dns v1.1.72
|
github.com/miekg/dns v1.1.72
|
||||||
github.com/pelletier/go-toml v1.9.5
|
github.com/pelletier/go-toml v1.9.5
|
||||||
|
github.com/pion/stun/v3 v3.1.2
|
||||||
github.com/pires/go-proxyproto v0.12.0
|
github.com/pires/go-proxyproto v0.12.0
|
||||||
github.com/refraction-networking/utls v1.8.3-0.20260301010127-aa6edf4b11af
|
github.com/refraction-networking/utls v1.8.3-0.20260301010127-aa6edf4b11af
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
@@ -23,9 +24,9 @@ require (
|
|||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||||
golang.org/x/crypto v0.51.0
|
golang.org/x/crypto v0.51.0
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
||||||
golang.org/x/net v0.54.0
|
golang.org/x/net v0.55.0
|
||||||
golang.org/x/sync v0.20.0
|
golang.org/x/sync v0.20.0
|
||||||
golang.org/x/sys v0.44.0
|
golang.org/x/sys v0.45.0
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
|
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
|
||||||
golang.zx2c4.com/wireguard/windows v1.0.1
|
golang.zx2c4.com/wireguard/windows v1.0.1
|
||||||
@@ -43,9 +44,13 @@ require (
|
|||||||
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/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
github.com/pion/dtls/v3 v3.1.2 // indirect
|
||||||
|
github.com/pion/logging v0.2.4 // indirect
|
||||||
|
github.com/pion/transport/v4 v4.0.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/quic-go/qpack v0.6.0 // indirect
|
github.com/quic-go/qpack v0.6.0 // indirect
|
||||||
github.com/vishvananda/netns v0.0.5 // indirect
|
github.com/vishvananda/netns v0.0.5 // indirect
|
||||||
|
github.com/wlynxg/anet v0.0.5 // indirect
|
||||||
golang.org/x/mod v0.35.0 // indirect
|
golang.org/x/mod v0.35.0 // indirect
|
||||||
golang.org/x/text v0.37.0 // indirect
|
golang.org/x/text v0.37.0 // indirect
|
||||||
golang.org/x/time v0.12.0 // indirect
|
golang.org/x/time v0.12.0 // indirect
|
||||||
|
|||||||
@@ -45,6 +45,14 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
|
|||||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
|
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
|
||||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||||
|
github.com/pion/dtls/v3 v3.1.2 h1:gqEdOUXLtCGW+afsBLO0LtDD8GnuBBjEy6HRtyofZTc=
|
||||||
|
github.com/pion/dtls/v3 v3.1.2/go.mod h1:Hw/igcX4pdY69z1Hgv5x7wJFrUkdgHwAn/Q/uo7YHRo=
|
||||||
|
github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8=
|
||||||
|
github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so=
|
||||||
|
github.com/pion/stun/v3 v3.1.2 h1:86IhD8wFn6IDW4b1/0QzoQS+f5PeA8OHHRn8UZW5ErY=
|
||||||
|
github.com/pion/stun/v3 v3.1.2/go.mod h1:H7gDic7nNwlUL05pbs6T1dtaBehh/KjupxfWw3ZI7cA=
|
||||||
|
github.com/pion/transport/v4 v4.0.1 h1:sdROELU6BZ63Ab7FrOLn13M6YdJLY20wldXW2Cu2k8o=
|
||||||
|
github.com/pion/transport/v4 v4.0.1/go.mod h1:nEuEA4AD5lPdcIegQDpVLgNoDGreqM/YqmEx3ovP4jM=
|
||||||
github.com/pires/go-proxyproto v0.12.0 h1:TTCxD66dU898tahivkqc3hoceZp7P44FnorWyo9d5vM=
|
github.com/pires/go-proxyproto v0.12.0 h1:TTCxD66dU898tahivkqc3hoceZp7P44FnorWyo9d5vM=
|
||||||
github.com/pires/go-proxyproto v0.12.0/go.mod h1:qUvfqUMEoX7T8g0q7TQLDnhMjdTrxnG0hvpMn+7ePNI=
|
github.com/pires/go-proxyproto v0.12.0/go.mod h1:qUvfqUMEoX7T8g0q7TQLDnhMjdTrxnG0hvpMn+7ePNI=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
@@ -67,6 +75,8 @@ github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW
|
|||||||
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
||||||
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
|
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
||||||
|
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||||
github.com/xtls/reality v0.0.0-20260322125925-9234c772ba8f h1:iy2JRioxmUpoJ3SzbFPyTxHZMbR/rSHP7dOOgYaq1O8=
|
github.com/xtls/reality v0.0.0-20260322125925-9234c772ba8f h1:iy2JRioxmUpoJ3SzbFPyTxHZMbR/rSHP7dOOgYaq1O8=
|
||||||
github.com/xtls/reality v0.0.0-20260322125925-9234c772ba8f/go.mod h1:DsJblcWDGt76+FVqBVwbwRhxyyNJsGV48gJLch0OOWI=
|
github.com/xtls/reality v0.0.0-20260322125925-9234c772ba8f/go.mod h1:DsJblcWDGt76+FVqBVwbwRhxyyNJsGV48gJLch0OOWI=
|
||||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
@@ -98,8 +108,8 @@ golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
|
|||||||
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=
|
||||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
|
||||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
@@ -111,8 +121,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||||
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
|||||||
@@ -48,4 +48,5 @@ var configLoader = NewJSONConfigLoader(
|
|||||||
"http": func() interface{} { return new(HTTPResponse) },
|
"http": func() interface{} { return new(HTTPResponse) },
|
||||||
},
|
},
|
||||||
"type",
|
"type",
|
||||||
"")
|
"",
|
||||||
|
)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package conf
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -199,7 +200,7 @@ func (v *PortRange) UnmarshalJSON(data []byte) error {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
v.From = uint32(from)
|
v.From = uint32(from)
|
||||||
v.To = uint32(to)
|
v.To = uint32(to)
|
||||||
if v.From > v.To {
|
if v.From > v.To || v.To > math.MaxUint16 {
|
||||||
return errors.New("invalid port range ", v.From, " -> ", v.To)
|
return errors.New("invalid port range ", v.From, " -> ", v.To)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+22
-20
@@ -12,8 +12,9 @@ import (
|
|||||||
|
|
||||||
type DNSOutboundRuleConfig struct {
|
type DNSOutboundRuleConfig struct {
|
||||||
Action string `json:"action"`
|
Action string `json:"action"`
|
||||||
QType *PortList `json:"qtype"`
|
QType *PortList `json:"qType"`
|
||||||
Domain *StringList `json:"domain"`
|
Domain *StringList `json:"domain"`
|
||||||
|
RCode uint32 `json:"rCode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSOutboundRuleConfig) Build() (*dns.DNSRuleConfig, error) {
|
func (c *DNSOutboundRuleConfig) Build() (*dns.DNSRuleConfig, error) {
|
||||||
@@ -24,8 +25,8 @@ func (c *DNSOutboundRuleConfig) Build() (*dns.DNSRuleConfig, error) {
|
|||||||
rule.Action = dns.RuleAction_Direct
|
rule.Action = dns.RuleAction_Direct
|
||||||
case "drop":
|
case "drop":
|
||||||
rule.Action = dns.RuleAction_Drop
|
rule.Action = dns.RuleAction_Drop
|
||||||
case "reject":
|
case "return":
|
||||||
rule.Action = dns.RuleAction_Reject
|
rule.Action = dns.RuleAction_Return
|
||||||
case "hijack":
|
case "hijack":
|
||||||
rule.Action = dns.RuleAction_Hijack
|
rule.Action = dns.RuleAction_Hijack
|
||||||
default:
|
default:
|
||||||
@@ -34,14 +35,8 @@ func (c *DNSOutboundRuleConfig) Build() (*dns.DNSRuleConfig, error) {
|
|||||||
|
|
||||||
if c.QType != nil {
|
if c.QType != nil {
|
||||||
for _, r := range c.QType.Range {
|
for _, r := range c.QType.Range {
|
||||||
if r.From > r.To {
|
for qType := r.From; qType <= r.To; qType++ {
|
||||||
return nil, errors.New("invalid qtype range: ", r.String())
|
rule.QType = append(rule.QType, int32(qType))
|
||||||
}
|
|
||||||
if r.To > 65535 {
|
|
||||||
return nil, errors.New("dns rule qtype out of range: ", r.String())
|
|
||||||
}
|
|
||||||
for qtype := r.From; qtype <= r.To; qtype++ {
|
|
||||||
rule.Qtype = append(rule.Qtype, int32(qtype))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,6 +49,11 @@ func (c *DNSOutboundRuleConfig) Build() (*dns.DNSRuleConfig, error) {
|
|||||||
rule.Domain = rules
|
rule.Domain = rules
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.RCode > 65535 {
|
||||||
|
return nil, errors.New("rCode out of range: ", c.RCode)
|
||||||
|
}
|
||||||
|
rule.RCode = c.RCode
|
||||||
|
|
||||||
return rule, nil
|
return rule, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,28 +133,30 @@ func (c *DNSOutboundConfig) buildLegacyDNSPolicy() ([]*dns.DNSRuleConfig, error)
|
|||||||
if c.BlockTypes != nil && len(*c.BlockTypes) > 0 {
|
if c.BlockTypes != nil && len(*c.BlockTypes) > 0 {
|
||||||
rule := &dns.DNSRuleConfig{Action: dns.RuleAction_Drop}
|
rule := &dns.DNSRuleConfig{Action: dns.RuleAction_Drop}
|
||||||
if mode == "reject" {
|
if mode == "reject" {
|
||||||
rule.Action = dns.RuleAction_Reject
|
rule.Action = dns.RuleAction_Return
|
||||||
|
rule.RCode = 5
|
||||||
}
|
}
|
||||||
for _, qtype := range *c.BlockTypes {
|
for _, qType := range *c.BlockTypes {
|
||||||
if qtype < 0 || qtype > 65535 {
|
if qType < 0 || qType > 65535 {
|
||||||
return nil, errors.New("legacy blockTypes qtype out of range: ", qtype)
|
return nil, errors.New("legacy blockTypes qType out of range: ", qType)
|
||||||
}
|
}
|
||||||
rule.Qtype = append(rule.Qtype, qtype)
|
rule.QType = append(rule.QType, qType)
|
||||||
}
|
}
|
||||||
rules = append(rules, rule)
|
rules = append(rules, rule)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
rule := &dns.DNSRuleConfig{Action: dns.RuleAction_Hijack}
|
rule := &dns.DNSRuleConfig{Action: dns.RuleAction_Hijack}
|
||||||
rule.Qtype = append(rule.Qtype, 1)
|
rule.QType = append(rule.QType, 1)
|
||||||
rule.Qtype = append(rule.Qtype, 28)
|
rule.QType = append(rule.QType, 28)
|
||||||
rules = append(rules, rule)
|
rules = append(rules, rule)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
rule := &dns.DNSRuleConfig{Action: dns.RuleAction_Reject}
|
rule := &dns.DNSRuleConfig{Action: dns.RuleAction_Return}
|
||||||
if mode == "reject" {
|
if mode == "reject" {
|
||||||
rule.Action = dns.RuleAction_Reject
|
rule.Action = dns.RuleAction_Return
|
||||||
|
rule.RCode = 5
|
||||||
} else if mode == "drop" {
|
} else if mode == "drop" {
|
||||||
rule.Action = dns.RuleAction_Drop
|
rule.Action = dns.RuleAction_Drop
|
||||||
} else if mode == "skip" {
|
} else if mode == "skip" {
|
||||||
|
|||||||
@@ -35,10 +35,10 @@ func TestDnsProxyConfig(t *testing.T) {
|
|||||||
Input: `{
|
Input: `{
|
||||||
"rules": [{
|
"rules": [{
|
||||||
"action": "direct",
|
"action": "direct",
|
||||||
"qtype": "1,3,23-24"
|
"qType": "1,3,23-24"
|
||||||
}, {
|
}, {
|
||||||
"action": "drop",
|
"action": "drop",
|
||||||
"qtype": 28,
|
"qType": 28,
|
||||||
"domain": ["domain:example.com", "full:example.com"]
|
"domain": ["domain:example.com", "full:example.com"]
|
||||||
}]
|
}]
|
||||||
}`,
|
}`,
|
||||||
@@ -48,11 +48,11 @@ func TestDnsProxyConfig(t *testing.T) {
|
|||||||
Rule: []*dns.DNSRuleConfig{
|
Rule: []*dns.DNSRuleConfig{
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Direct,
|
Action: dns.RuleAction_Direct,
|
||||||
Qtype: []int32{1, 3, 23, 24},
|
QType: []int32{1, 3, 23, 24},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Drop,
|
Action: dns.RuleAction_Drop,
|
||||||
Qtype: []int32{28},
|
QType: []int32{28},
|
||||||
Domain: []*geodata.DomainRule{
|
Domain: []*geodata.DomainRule{
|
||||||
{
|
{
|
||||||
Value: &geodata.DomainRule_Custom{
|
Value: &geodata.DomainRule_Custom{
|
||||||
@@ -78,7 +78,8 @@ func TestDnsProxyConfig(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Input: `{
|
Input: `{
|
||||||
"rules": [{
|
"rules": [{
|
||||||
"action": "reject",
|
"action": "return",
|
||||||
|
"rCode": 5,
|
||||||
"domain": "keyword:example"
|
"domain": "keyword:example"
|
||||||
}]
|
}]
|
||||||
}`,
|
}`,
|
||||||
@@ -87,7 +88,8 @@ func TestDnsProxyConfig(t *testing.T) {
|
|||||||
RewriteServer: &net.Endpoint{},
|
RewriteServer: &net.Endpoint{},
|
||||||
Rule: []*dns.DNSRuleConfig{
|
Rule: []*dns.DNSRuleConfig{
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Reject,
|
Action: dns.RuleAction_Return,
|
||||||
|
RCode: 5,
|
||||||
Domain: []*geodata.DomainRule{
|
Domain: []*geodata.DomainRule{
|
||||||
{
|
{
|
||||||
Value: &geodata.DomainRule_Custom{
|
Value: &geodata.DomainRule_Custom{
|
||||||
@@ -106,7 +108,7 @@ func TestDnsProxyConfig(t *testing.T) {
|
|||||||
Input: `{
|
Input: `{
|
||||||
"rules": [{
|
"rules": [{
|
||||||
"action": "drop",
|
"action": "drop",
|
||||||
"qtype": 257
|
"qType": 257
|
||||||
}]
|
}]
|
||||||
}`,
|
}`,
|
||||||
Parser: loadJSON(creator),
|
Parser: loadJSON(creator),
|
||||||
@@ -115,7 +117,7 @@ func TestDnsProxyConfig(t *testing.T) {
|
|||||||
Rule: []*dns.DNSRuleConfig{
|
Rule: []*dns.DNSRuleConfig{
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Drop,
|
Action: dns.RuleAction_Drop,
|
||||||
Qtype: []int32{257},
|
QType: []int32{257},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -140,10 +142,11 @@ func TestDnsProxyConfigLegacyCompatibility(t *testing.T) {
|
|||||||
Rule: []*dns.DNSRuleConfig{
|
Rule: []*dns.DNSRuleConfig{
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Hijack,
|
Action: dns.RuleAction_Hijack,
|
||||||
Qtype: []int32{1, 28},
|
QType: []int32{1, 28},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Reject,
|
Action: dns.RuleAction_Return,
|
||||||
|
RCode: 5,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -157,15 +160,17 @@ func TestDnsProxyConfigLegacyCompatibility(t *testing.T) {
|
|||||||
RewriteServer: &net.Endpoint{},
|
RewriteServer: &net.Endpoint{},
|
||||||
Rule: []*dns.DNSRuleConfig{
|
Rule: []*dns.DNSRuleConfig{
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Reject,
|
Action: dns.RuleAction_Return,
|
||||||
Qtype: []int32{1, 65},
|
QType: []int32{1, 65},
|
||||||
|
RCode: 5,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Hijack,
|
Action: dns.RuleAction_Hijack,
|
||||||
Qtype: []int32{1, 28},
|
QType: []int32{1, 28},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Reject,
|
Action: dns.RuleAction_Return,
|
||||||
|
RCode: 5,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -181,11 +186,11 @@ func TestDnsProxyConfigLegacyCompatibility(t *testing.T) {
|
|||||||
Rule: []*dns.DNSRuleConfig{
|
Rule: []*dns.DNSRuleConfig{
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Drop,
|
Action: dns.RuleAction_Drop,
|
||||||
Qtype: []int32{1},
|
QType: []int32{1},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Hijack,
|
Action: dns.RuleAction_Hijack,
|
||||||
Qtype: []int32{1, 28},
|
QType: []int32{1, 28},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Drop,
|
Action: dns.RuleAction_Drop,
|
||||||
@@ -204,11 +209,11 @@ func TestDnsProxyConfigLegacyCompatibility(t *testing.T) {
|
|||||||
Rule: []*dns.DNSRuleConfig{
|
Rule: []*dns.DNSRuleConfig{
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Drop,
|
Action: dns.RuleAction_Drop,
|
||||||
Qtype: []int32{65, 28},
|
QType: []int32{65, 28},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Hijack,
|
Action: dns.RuleAction_Hijack,
|
||||||
Qtype: []int32{1, 28},
|
QType: []int32{1, 28},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Direct,
|
Action: dns.RuleAction_Direct,
|
||||||
@@ -228,7 +233,7 @@ func TestDnsProxyConfigRejectsMixedLegacyAndNewFields(t *testing.T) {
|
|||||||
_, err := loadJSON(creator)(`{
|
_, err := loadJSON(creator)(`{
|
||||||
"rules": [{
|
"rules": [{
|
||||||
"action": "direct",
|
"action": "direct",
|
||||||
"qtype": 65
|
"qType": 65
|
||||||
}],
|
}],
|
||||||
"blockTypes": [65]
|
"blockTypes": [65]
|
||||||
}`)
|
}`)
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package conf
|
package conf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/app/observatory/burst"
|
"github.com/xtls/xray-core/app/observatory/burst"
|
||||||
"github.com/xtls/xray-core/app/router"
|
"github.com/xtls/xray-core/app/router"
|
||||||
"github.com/xtls/xray-core/infra/conf/cfgcommon/duration"
|
"github.com/xtls/xray-core/infra/conf/cfgcommon/duration"
|
||||||
@@ -16,17 +17,14 @@ const (
|
|||||||
strategyLeastLoad string = "leastload"
|
strategyLeastLoad string = "leastload"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var strategyConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
||||||
strategyConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
strategyRandom: func() interface{} { return new(strategyEmptyConfig) },
|
||||||
strategyRandom: func() interface{} { return new(strategyEmptyConfig) },
|
strategyLeastPing: func() interface{} { return new(strategyEmptyConfig) },
|
||||||
strategyLeastPing: func() interface{} { return new(strategyEmptyConfig) },
|
strategyRoundRobin: func() interface{} { return new(strategyEmptyConfig) },
|
||||||
strategyRoundRobin: func() interface{} { return new(strategyEmptyConfig) },
|
strategyLeastLoad: func() interface{} { return new(strategyLeastLoadConfig) },
|
||||||
strategyLeastLoad: func() interface{} { return new(strategyLeastLoadConfig) },
|
}, "type", "settings")
|
||||||
}, "type", "settings")
|
|
||||||
)
|
|
||||||
|
|
||||||
type strategyEmptyConfig struct {
|
type strategyEmptyConfig struct{}
|
||||||
}
|
|
||||||
|
|
||||||
func (v *strategyEmptyConfig) Build() (proto.Message, error) {
|
func (v *strategyEmptyConfig) Build() (proto.Message, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|||||||
@@ -78,18 +78,17 @@ func DecodeJSONConfig(reader io.Reader) (*conf.Config, error) {
|
|||||||
// byte-by-byte comment stripper and TeeReader, which are significant overhead on
|
// byte-by-byte comment stripper and TeeReader, which are significant overhead on
|
||||||
// large configs.
|
// large configs.
|
||||||
func DecodeJSONConfigStrict(reader io.Reader) (*conf.Config, error) {
|
func DecodeJSONConfigStrict(reader io.Reader) (*conf.Config, error) {
|
||||||
data, err := io.ReadAll(reader)
|
data, err := io.ReadAll(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("failed to read config file").Base(err)
|
return nil, errors.New("failed to read config file").Base(err)
|
||||||
}
|
}
|
||||||
jsonConfig := &conf.Config{}
|
jsonConfig := &conf.Config{}
|
||||||
if err := json.Unmarshal(data, jsonConfig); err != nil {
|
if err := json.Unmarshal(data, jsonConfig); err != nil {
|
||||||
return nil, errors.New("failed to parse remote JSON config").Base(err)
|
return nil, errors.New("failed to parse remote JSON config").Base(err)
|
||||||
}
|
}
|
||||||
return jsonConfig, nil
|
return jsonConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func LoadJSONConfig(reader io.Reader) (*core.Config, error) {
|
func LoadJSONConfig(reader io.Reader) (*core.Config, error) {
|
||||||
jsonConfig, err := DecodeJSONConfig(reader)
|
jsonConfig, err := DecodeJSONConfig(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -45,31 +45,31 @@ func (v *AuthenticatorRequest) Build() (*http.RequestConfig, error) {
|
|||||||
Value: []string{utils.ChromeUA},
|
Value: []string{utils.ChromeUA},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Sec-CH-UA",
|
Name: "Sec-CH-UA",
|
||||||
Value: []string{utils.ChromeUACH},
|
Value: []string{utils.ChromeUACH},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Sec-CH-UA-Mobile",
|
Name: "Sec-CH-UA-Mobile",
|
||||||
Value: []string{"?0"},
|
Value: []string{"?0"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Sec-CH-UA-Platform",
|
Name: "Sec-CH-UA-Platform",
|
||||||
Value: []string{"Windows"},
|
Value: []string{"Windows"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Sec-Fetch-Mode",
|
Name: "Sec-Fetch-Mode",
|
||||||
Value: []string{"no-cors", "cors", "same-origin"},
|
Value: []string{"no-cors", "cors", "same-origin"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Sec-Fetch-Dest",
|
Name: "Sec-Fetch-Dest",
|
||||||
Value: []string{"empty"},
|
Value: []string{"empty"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Sec-Fetch-Site",
|
Name: "Sec-Fetch-Site",
|
||||||
Value: []string{"none"},
|
Value: []string{"none"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Sec-Fetch-User",
|
Name: "Sec-Fetch-User",
|
||||||
Value: []string{"?1"},
|
Value: []string{"?1"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -96,7 +96,7 @@ func (v *AuthenticatorRequest) Build() (*http.RequestConfig, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(v.Path) > 0 {
|
if len(v.Path) > 0 {
|
||||||
config.Uri = append([]string(nil), (v.Path)...)
|
config.Uri = append([]string(nil), v.Path...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(v.Headers) > 0 {
|
if len(v.Headers) > 0 {
|
||||||
|
|||||||
+166
-120
@@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"math"
|
"math"
|
||||||
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -13,7 +14,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
@@ -23,15 +23,11 @@ import (
|
|||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
"github.com/xtls/xray-core/transport/internet/finalmask/fragment"
|
"github.com/xtls/xray-core/transport/internet/finalmask/fragment"
|
||||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/custom"
|
"github.com/xtls/xray-core/transport/internet/finalmask/header/custom"
|
||||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/dns"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/dtls"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/srtp"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/utp"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/wechat"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/wireguard"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/aes128gcm"
|
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/aes128gcm"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/header"
|
||||||
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original"
|
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original"
|
||||||
"github.com/xtls/xray-core/transport/internet/finalmask/noise"
|
"github.com/xtls/xray-core/transport/internet/finalmask/noise"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/realm"
|
||||||
"github.com/xtls/xray-core/transport/internet/finalmask/salamander"
|
"github.com/xtls/xray-core/transport/internet/finalmask/salamander"
|
||||||
finalsudoku "github.com/xtls/xray-core/transport/internet/finalmask/sudoku"
|
finalsudoku "github.com/xtls/xray-core/transport/internet/finalmask/sudoku"
|
||||||
"github.com/xtls/xray-core/transport/internet/finalmask/xdns"
|
"github.com/xtls/xray-core/transport/internet/finalmask/xdns"
|
||||||
@@ -48,12 +44,10 @@ import (
|
|||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var tcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
||||||
tcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
"none": func() interface{} { return new(NoOpConnectionAuthenticator) },
|
||||||
"none": func() interface{} { return new(NoOpConnectionAuthenticator) },
|
"http": func() interface{} { return new(Authenticator) },
|
||||||
"http": func() interface{} { return new(Authenticator) },
|
}, "type", "")
|
||||||
}, "type", "")
|
|
||||||
)
|
|
||||||
|
|
||||||
type KCPConfig struct {
|
type KCPConfig struct {
|
||||||
Mtu *uint32 `json:"mtu"`
|
Mtu *uint32 `json:"mtu"`
|
||||||
@@ -656,10 +650,8 @@ type TLSConfig struct {
|
|||||||
MasterKeyLog string `json:"masterKeyLog"`
|
MasterKeyLog string `json:"masterKeyLog"`
|
||||||
PinnedPeerCertSha256 string `json:"pinnedPeerCertSha256"`
|
PinnedPeerCertSha256 string `json:"pinnedPeerCertSha256"`
|
||||||
VerifyPeerCertByName string `json:"verifyPeerCertByName"`
|
VerifyPeerCertByName string `json:"verifyPeerCertByName"`
|
||||||
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
|
|
||||||
ECHServerKeys string `json:"echServerKeys"`
|
ECHServerKeys string `json:"echServerKeys"`
|
||||||
ECHConfigList string `json:"echConfigList"`
|
ECHConfigList string `json:"echConfigList"`
|
||||||
ECHForceQuery string `json:"echForceQuery"`
|
|
||||||
ECHSocketSettings *SocketConfig `json:"echSockopt"`
|
ECHSocketSettings *SocketConfig `json:"echSockopt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -704,12 +696,7 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
|||||||
config.MasterKeyLog = c.MasterKeyLog
|
config.MasterKeyLog = c.MasterKeyLog
|
||||||
|
|
||||||
if c.AllowInsecure {
|
if c.AllowInsecure {
|
||||||
if time.Now().After(time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC)) {
|
return nil, errors.PrintRemovedFeatureError(`"allowInsecure"`, `"pinnedPeerCertSha256"(pcs) and "verifyPeerCertByName"(vcn)`)
|
||||||
return nil, errors.PrintRemovedFeatureError(`"allowInsecure"`, `"pinnedPeerCertSha256"`)
|
|
||||||
} else {
|
|
||||||
errors.LogWarning(context.Background(), `"allowInsecure" will be removed automatically after 2026-06-01, please use "pinnedPeerCertSha256"(pcs) and "verifyPeerCertByName"(vcn) instead, PLEASE CONTACT YOUR SERVICE PROVIDER (AIRPORT)`)
|
|
||||||
config.AllowInsecure = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if c.PinnedPeerCertSha256 != "" {
|
if c.PinnedPeerCertSha256 != "" {
|
||||||
for v := range strings.SplitSeq(c.PinnedPeerCertSha256, ",") {
|
for v := range strings.SplitSeq(c.PinnedPeerCertSha256, ",") {
|
||||||
@@ -728,10 +715,6 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
|||||||
config.PinnedPeerCertSha256 = append(config.PinnedPeerCertSha256, hashValue)
|
config.PinnedPeerCertSha256 = append(config.PinnedPeerCertSha256, hashValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.VerifyPeerCertInNames != nil {
|
|
||||||
return nil, errors.PrintRemovedFeatureError(`"verifyPeerCertInNames"`, `"verifyPeerCertByName"`)
|
|
||||||
}
|
|
||||||
if c.VerifyPeerCertByName != "" {
|
if c.VerifyPeerCertByName != "" {
|
||||||
for v := range strings.SplitSeq(c.VerifyPeerCertByName, ",") {
|
for v := range strings.SplitSeq(c.VerifyPeerCertByName, ",") {
|
||||||
v = strings.TrimSpace(v)
|
v = strings.TrimSpace(v)
|
||||||
@@ -749,13 +732,6 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
config.EchServerKeys = EchPrivateKey
|
config.EchServerKeys = EchPrivateKey
|
||||||
}
|
}
|
||||||
switch c.ECHForceQuery {
|
|
||||||
case "none", "half", "full", "":
|
|
||||||
config.EchForceQuery = c.ECHForceQuery
|
|
||||||
default:
|
|
||||||
return nil, errors.New(`invalid "echForceQuery": `, c.ECHForceQuery)
|
|
||||||
}
|
|
||||||
config.EchForceQuery = c.ECHForceQuery
|
|
||||||
config.EchConfigList = c.ECHConfigList
|
config.EchConfigList = c.ECHConfigList
|
||||||
if c.ECHSocketSettings != nil {
|
if c.ECHSocketSettings != nil {
|
||||||
ss, err := c.ECHSocketSettings.Build()
|
ss, err := c.ECHSocketSettings.Build()
|
||||||
@@ -1037,7 +1013,7 @@ type HappyEyeballsConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *HappyEyeballsConfig) UnmarshalJSON(data []byte) error {
|
func (h *HappyEyeballsConfig) UnmarshalJSON(data []byte) error {
|
||||||
var innerHappyEyeballsConfig = struct {
|
innerHappyEyeballsConfig := struct {
|
||||||
PrioritizeIPv6 bool `json:"prioritizeIPv6"`
|
PrioritizeIPv6 bool `json:"prioritizeIPv6"`
|
||||||
TryDelayMs uint64 `json:"tryDelayMs"`
|
TryDelayMs uint64 `json:"tryDelayMs"`
|
||||||
Interleave uint32 `json:"interleave"`
|
Interleave uint32 `json:"interleave"`
|
||||||
@@ -1165,7 +1141,7 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
|||||||
return nil, errors.New("unsupported address and port strategy: ", c.AddressPortStrategy)
|
return nil, errors.New("unsupported address and port strategy: ", c.AddressPortStrategy)
|
||||||
}
|
}
|
||||||
|
|
||||||
var happyEyeballs = &internet.HappyEyeballsConfig{Interleave: 1, PrioritizeIpv6: false, TryDelayMs: 0, MaxConcurrentTry: 4}
|
happyEyeballs := &internet.HappyEyeballsConfig{Interleave: 1, PrioritizeIpv6: false, TryDelayMs: 0, MaxConcurrentTry: 4}
|
||||||
if c.HappyEyeballsSettings != nil {
|
if c.HappyEyeballsSettings != nil {
|
||||||
happyEyeballs.PrioritizeIpv6 = c.HappyEyeballsSettings.PrioritizeIPv6
|
happyEyeballs.PrioritizeIpv6 = c.HappyEyeballsSettings.PrioritizeIPv6
|
||||||
happyEyeballs.Interleave = c.HappyEyeballsSettings.Interleave
|
happyEyeballs.Interleave = c.HappyEyeballsSettings.Interleave
|
||||||
@@ -1241,20 +1217,14 @@ var (
|
|||||||
}, "type", "settings")
|
}, "type", "settings")
|
||||||
|
|
||||||
udpmaskLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
udpmaskLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
||||||
"header-custom": func() interface{} { return new(HeaderCustomUDP) },
|
"header-custom": func() interface{} { return new(HeaderCustomUDP) },
|
||||||
"header-dns": func() interface{} { return new(Dns) },
|
"mkcp-legacy": func() interface{} { return new(MkcpLegacy) },
|
||||||
"header-dtls": func() interface{} { return new(Dtls) },
|
"noise": func() interface{} { return new(NoiseMask) },
|
||||||
"header-srtp": func() interface{} { return new(Srtp) },
|
"salamander": func() interface{} { return new(Salamander) },
|
||||||
"header-utp": func() interface{} { return new(Utp) },
|
"sudoku": func() interface{} { return new(Sudoku) },
|
||||||
"header-wechat": func() interface{} { return new(Wechat) },
|
"xdns": func() interface{} { return new(Xdns) },
|
||||||
"header-wireguard": func() interface{} { return new(Wireguard) },
|
"xicmp": func() interface{} { return new(Xicmp) },
|
||||||
"mkcp-original": func() interface{} { return new(Original) },
|
"realm": func() interface{} { return new(Realm) },
|
||||||
"mkcp-aes128gcm": func() interface{} { return new(Aes128Gcm) },
|
|
||||||
"noise": func() interface{} { return new(NoiseMask) },
|
|
||||||
"salamander": func() interface{} { return new(Salamander) },
|
|
||||||
"sudoku": func() interface{} { return new(Sudoku) },
|
|
||||||
"xdns": func() interface{} { return new(Xdns) },
|
|
||||||
"xicmp": func() interface{} { return new(Xicmp) },
|
|
||||||
}, "type", "settings")
|
}, "type", "settings")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1734,82 +1704,70 @@ func (c *HeaderCustomUDP) Build() (proto.Message, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return &custom.UDPConfig{
|
if c.Mode == "standalone" {
|
||||||
Client: client,
|
return &custom.UDPStandaloneConfig{
|
||||||
Server: server,
|
Client: client,
|
||||||
Mode: c.Mode,
|
Server: server,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
} else {
|
||||||
|
return &custom.UDPConfig{
|
||||||
type Dns struct {
|
Client: client,
|
||||||
Domain string `json:"domain"`
|
Server: server,
|
||||||
}
|
}, nil
|
||||||
|
|
||||||
func (c *Dns) Build() (proto.Message, error) {
|
|
||||||
config := &dns.Config{}
|
|
||||||
config.Domain = "www.baidu.com"
|
|
||||||
|
|
||||||
if len(c.Domain) > 0 {
|
|
||||||
config.Domain = c.Domain
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Dtls struct{}
|
type MkcpLegacy struct {
|
||||||
|
Header string `json:"header"`
|
||||||
func (c *Dtls) Build() (proto.Message, error) {
|
Value string `json:"value"`
|
||||||
return &dtls.Config{}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Srtp struct{}
|
func (c *MkcpLegacy) Build() (proto.Message, error) {
|
||||||
|
if len(c.Header) == 0 {
|
||||||
func (c *Srtp) Build() (proto.Message, error) {
|
if len(c.Value) == 0 {
|
||||||
return &srtp.Config{}, nil
|
return &original.Config{}, nil
|
||||||
}
|
} else {
|
||||||
|
return &aes128gcm.Config{Password: c.Value}, nil
|
||||||
type Utp struct{}
|
}
|
||||||
|
}
|
||||||
func (c *Utp) Build() (proto.Message, error) {
|
switch strings.ToLower(c.Header) {
|
||||||
return &utp.Config{}, nil
|
case "dns":
|
||||||
}
|
domain := c.Value
|
||||||
|
if len(domain) == 0 {
|
||||||
type Wechat struct{}
|
domain = "www.baidu.com"
|
||||||
|
}
|
||||||
func (c *Wechat) Build() (proto.Message, error) {
|
return &header.Config{ID: 0, Domain: domain}, nil
|
||||||
return &wechat.Config{}, nil
|
case "dtls":
|
||||||
}
|
return &header.Config{ID: 1}, nil
|
||||||
|
case "srtp":
|
||||||
type Wireguard struct{}
|
return &header.Config{ID: 2}, nil
|
||||||
|
case "utp":
|
||||||
func (c *Wireguard) Build() (proto.Message, error) {
|
return &header.Config{ID: 3}, nil
|
||||||
return &wireguard.Config{}, nil
|
case "wechat":
|
||||||
}
|
return &header.Config{ID: 4}, nil
|
||||||
|
case "wireguard":
|
||||||
type Original struct{}
|
return &header.Config{ID: 5}, nil
|
||||||
|
default:
|
||||||
func (c *Original) Build() (proto.Message, error) {
|
return nil, errors.New("invalid header ", c.Header)
|
||||||
return &original.Config{}, nil
|
}
|
||||||
}
|
|
||||||
|
|
||||||
type Aes128Gcm struct {
|
|
||||||
Password string `json:"password"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Aes128Gcm) Build() (proto.Message, error) {
|
|
||||||
return &aes128gcm.Config{
|
|
||||||
Password: c.Password,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Salamander struct {
|
type Salamander struct {
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
|
PacketSize *Int32Range `json:"packetSize"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Salamander) Build() (proto.Message, error) {
|
func (c *Salamander) Build() (proto.Message, error) {
|
||||||
config := &salamander.Config{}
|
if c.PacketSize != nil {
|
||||||
config.Password = c.Password
|
return &salamander.GeckoConfig{
|
||||||
return config, nil
|
Password: c.Password,
|
||||||
|
MinPacketSize: c.PacketSize.From,
|
||||||
|
MaxPacketSize: c.PacketSize.To,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return &salamander.Config{
|
||||||
|
Password: c.Password,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Sudoku struct {
|
type Sudoku struct {
|
||||||
@@ -1885,23 +1843,111 @@ func (c *Xdns) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Xicmp struct {
|
type Xicmp struct {
|
||||||
ListenIp string `json:"listenIp"`
|
DGRAM bool `json:"dgram"`
|
||||||
Id uint16 `json:"id"`
|
IPs []string `json:"ips"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Xicmp) Build() (proto.Message, error) {
|
func (c *Xicmp) Build() (proto.Message, error) {
|
||||||
config := &xicmp.Config{
|
for _, ip := range c.IPs {
|
||||||
Ip: c.ListenIp,
|
if _, err := netip.ParseAddr(ip); err != nil {
|
||||||
Id: int32(c.Id),
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Ip == "" {
|
config := &xicmp.Config{
|
||||||
config.Ip = "0.0.0.0"
|
DGRAM: c.DGRAM,
|
||||||
|
IPs: c.IPs,
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Realm struct {
|
||||||
|
Url string `json:"url"`
|
||||||
|
StunServers []string `json:"stunServers"`
|
||||||
|
TlsConfig *TLSConfig `json:"tlsConfig"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Realm) Build() (proto.Message, error) {
|
||||||
|
var scheme, host, port, token, id string
|
||||||
|
var stunServers []string
|
||||||
|
var tlsConfig *tls.Config
|
||||||
|
|
||||||
|
u, err := url.Parse(c.Url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch u.Scheme {
|
||||||
|
case "realm":
|
||||||
|
scheme = "https"
|
||||||
|
case "realm+http":
|
||||||
|
scheme = "http"
|
||||||
|
default:
|
||||||
|
return nil, errors.New("invalid scheme", u.Scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
host = u.Hostname()
|
||||||
|
if host == "" {
|
||||||
|
return nil, errors.New("invalid host", host)
|
||||||
|
}
|
||||||
|
|
||||||
|
port = u.Port()
|
||||||
|
if port == "" {
|
||||||
|
port = "443"
|
||||||
|
if scheme == "http" {
|
||||||
|
port = "80"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err = url.PathUnescape(u.User.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if token == "" {
|
||||||
|
return nil, errors.New("invalid token", token)
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err = url.PathUnescape(strings.TrimPrefix(u.EscapedPath(), "/"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if id == "" {
|
||||||
|
return nil, errors.New("invalid id", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.StunServers) == 0 {
|
||||||
|
return nil, errors.New("empty stunServers")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range c.StunServers {
|
||||||
|
_, _, err = net.SplitHostPort(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stunServers = c.StunServers
|
||||||
|
|
||||||
|
if c.TlsConfig != nil {
|
||||||
|
tc, err := c.TlsConfig.Build()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tlsConfig = tc.(*tls.Config)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &realm.Config{
|
||||||
|
Scheme: scheme,
|
||||||
|
Host: host,
|
||||||
|
Port: port,
|
||||||
|
Token: token,
|
||||||
|
ID: id,
|
||||||
|
StunServers: stunServers,
|
||||||
|
TlsConfig: tlsConfig,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
type Mask struct {
|
type Mask struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Settings *json.RawMessage `json:"settings"`
|
Settings *json.RawMessage `json:"settings"`
|
||||||
|
|||||||
@@ -195,8 +195,7 @@ func TestHeaderCustomUDPBuild(t *testing.T) {
|
|||||||
"mode": "standalone"
|
"mode": "standalone"
|
||||||
}`,
|
}`,
|
||||||
Parser: parser,
|
Parser: parser,
|
||||||
Output: &finalmaskcustom.UDPConfig{
|
Output: &finalmaskcustom.UDPStandaloneConfig{
|
||||||
Mode: "standalone",
|
|
||||||
Client: []*finalmaskcustom.UDPItem{
|
Client: []*finalmaskcustom.UDPItem{
|
||||||
{
|
{
|
||||||
RandMax: 255,
|
RandMax: 255,
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package conf
|
package conf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/app/version"
|
"github.com/xtls/xray-core/app/version"
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type VersionConfig struct {
|
type VersionConfig struct {
|
||||||
|
|||||||
+3
-3
@@ -177,7 +177,8 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
|
|||||||
protocol := ss.GetEffectiveProtocol()
|
protocol := ss.GetEffectiveProtocol()
|
||||||
if (protocol == "websocket" || protocol == "httpupgrade" || protocol == "splithttp") &&
|
if (protocol == "websocket" || protocol == "httpupgrade" || protocol == "splithttp") &&
|
||||||
(c.StreamSetting.SocketSettings == nil || len(c.StreamSetting.SocketSettings.TrustedXForwardedFor) == 0) {
|
(c.StreamSetting.SocketSettings == nil || len(c.StreamSetting.SocketSettings.TrustedXForwardedFor) == 0) {
|
||||||
errors.LogWarning(context.Background(),
|
errors.LogWarning(
|
||||||
|
context.Background(),
|
||||||
`====== SECURITY WARNING ======`,
|
`====== SECURITY WARNING ======`,
|
||||||
"\n",
|
"\n",
|
||||||
`inbound "`, c.Tag, `" using `, protocol, ` has not configured "sockopt.trustedXForwardedFor".`,
|
`inbound "`, c.Tag, `" using `, protocol, ` has not configured "sockopt.trustedXForwardedFor".`,
|
||||||
@@ -285,7 +286,7 @@ func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
|
|||||||
|
|
||||||
if c.SendThrough != nil {
|
if c.SendThrough != nil {
|
||||||
address := ParseSendThough(c.SendThrough)
|
address := ParseSendThough(c.SendThrough)
|
||||||
//Check if CIDR exists
|
// Check if CIDR exists
|
||||||
if strings.Contains(*c.SendThrough, "/") {
|
if strings.Contains(*c.SendThrough, "/") {
|
||||||
senderSettings.ViaCidr = strings.Split(*c.SendThrough, "/")[1]
|
senderSettings.ViaCidr = strings.Split(*c.SendThrough, "/")[1]
|
||||||
} else {
|
} else {
|
||||||
@@ -469,7 +470,6 @@ func (c *Config) Override(o *Config, fn string) {
|
|||||||
c.InboundConfigs = append(c.InboundConfigs, o.InboundConfigs[i])
|
c.InboundConfigs = append(c.InboundConfigs, o.InboundConfigs[i])
|
||||||
errors.LogInfo(context.Background(), "[", fn, "] appended inbound with tag: ", o.InboundConfigs[i].Tag)
|
errors.LogInfo(context.Background(), "[", fn, "] appended inbound with tag: ", o.InboundConfigs[i].Tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+62
-19
@@ -12,7 +12,16 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var directory = flag.String("pwd", "", "Working directory of Xray vformat.")
|
var (
|
||||||
|
directory = flag.String("pwd", "", "Working directory of Xray vformat.")
|
||||||
|
action = flag.String("mode", "format", "Execution mode. Default is 'format'.\n'format' formatting source files and save changes to files.\n'check' list all paths of improper formatted file.\n'dryrun' formatting source files and shows all diffs, but will not make any changes to files.")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
isCheck bool
|
||||||
|
isDryrun bool
|
||||||
|
isFormat bool
|
||||||
|
)
|
||||||
|
|
||||||
// envFile returns the name of the Go environment configuration file.
|
// envFile returns the name of the Go environment configuration file.
|
||||||
// Copy from https://github.com/golang/go/blob/c4f2a9788a7be04daf931ac54382fbe2cb754938/src/cmd/go/internal/cfg/cfg.go#L150-L166
|
// Copy from https://github.com/golang/go/blob/c4f2a9788a7be04daf931ac54382fbe2cb754938/src/cmd/go/internal/cfg/cfg.go#L150-L166
|
||||||
@@ -90,9 +99,10 @@ func Run(binary string, args []string) ([]byte, error) {
|
|||||||
return output, nil
|
return output, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunMany(binary string, args, files []string) {
|
func RunMany(binary string, args, files []string) bool {
|
||||||
fmt.Println("Processing...")
|
fmt.Println("Processing with", binary, args, "...")
|
||||||
|
|
||||||
|
formatRequired := false
|
||||||
maxTasks := make(chan struct{}, runtime.NumCPU())
|
maxTasks := make(chan struct{}, runtime.NumCPU())
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
maxTasks <- struct{}{}
|
maxTasks <- struct{}{}
|
||||||
@@ -102,10 +112,12 @@ func RunMany(binary string, args, files []string) {
|
|||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
} else if len(output) > 0 {
|
} else if len(output) > 0 {
|
||||||
fmt.Println(string(output))
|
fmt.Println(string(output))
|
||||||
|
formatRequired = true
|
||||||
}
|
}
|
||||||
<-maxTasks
|
<-maxTasks
|
||||||
}(file)
|
}(file)
|
||||||
}
|
}
|
||||||
|
return formatRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -124,6 +136,19 @@ func main() {
|
|||||||
*directory = filepath.Join(pwd, *directory)
|
*directory = filepath.Join(pwd, *directory)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch *action {
|
||||||
|
case "format":
|
||||||
|
isFormat = true
|
||||||
|
case "check":
|
||||||
|
isCheck = true
|
||||||
|
case "dryrun":
|
||||||
|
isCheck = true
|
||||||
|
isDryrun = true
|
||||||
|
default:
|
||||||
|
fmt.Println("Unrecognized 'mode'. Will format all source files and save changes.")
|
||||||
|
isFormat = true
|
||||||
|
}
|
||||||
|
|
||||||
pwd := *directory
|
pwd := *directory
|
||||||
GOBIN := GetGOBIN()
|
GOBIN := GetGOBIN()
|
||||||
binPath := os.Getenv("PATH")
|
binPath := os.Getenv("PATH")
|
||||||
@@ -136,7 +161,6 @@ func main() {
|
|||||||
suffix = ".exe"
|
suffix = ".exe"
|
||||||
}
|
}
|
||||||
gofmt := "gofumpt" + suffix
|
gofmt := "gofumpt" + suffix
|
||||||
goimports := "gci" + suffix
|
|
||||||
|
|
||||||
if gofmtPath, err := exec.LookPath(gofmt); err != nil {
|
if gofmtPath, err := exec.LookPath(gofmt); err != nil {
|
||||||
fmt.Println("Can not find", gofmt, "in system path or current working directory.")
|
fmt.Println("Can not find", gofmt, "in system path or current working directory.")
|
||||||
@@ -145,13 +169,6 @@ func main() {
|
|||||||
gofmt = gofmtPath
|
gofmt = gofmtPath
|
||||||
}
|
}
|
||||||
|
|
||||||
if goimportsPath, err := exec.LookPath(goimports); err != nil {
|
|
||||||
fmt.Println("Can not find", goimports, "in system path or current working directory.")
|
|
||||||
os.Exit(1)
|
|
||||||
} else {
|
|
||||||
goimports = goimportsPath
|
|
||||||
}
|
|
||||||
|
|
||||||
rawFilesSlice := make([]string, 0, 1000)
|
rawFilesSlice := make([]string, 0, 1000)
|
||||||
walkErr := filepath.Walk(pwd, func(path string, info os.FileInfo, err error) error {
|
walkErr := filepath.Walk(pwd, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -179,15 +196,41 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
gofmtArgs := []string{
|
if isFormat {
|
||||||
"-s", "-l", "-e", "-w",
|
gofmtArgs := []string{
|
||||||
|
"-l", "-e", "-w",
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Formatting Go source files...")
|
||||||
|
RunMany(gofmt, gofmtArgs, rawFilesSlice)
|
||||||
|
fmt.Println("Do NOT forget to commit file changes.")
|
||||||
}
|
}
|
||||||
|
|
||||||
goimportsArgs := []string{
|
if isCheck {
|
||||||
"write",
|
gofmtListArgs := []string{
|
||||||
}
|
"-l", "-e",
|
||||||
|
}
|
||||||
|
|
||||||
RunMany(gofmt, gofmtArgs, rawFilesSlice)
|
fmt.Println("Checking files thar are not properly formatted...")
|
||||||
RunMany(goimports, goimportsArgs, rawFilesSlice)
|
formatRequired := RunMany(gofmt, gofmtListArgs, rawFilesSlice)
|
||||||
fmt.Println("Do NOT forget to commit file changes.")
|
if formatRequired {
|
||||||
|
fmt.Println("Format problem(s) found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if isDryrun {
|
||||||
|
if formatRequired {
|
||||||
|
gofmtShowArgs := []string{
|
||||||
|
"-d", "-e",
|
||||||
|
}
|
||||||
|
RunMany(gofmt, gofmtShowArgs, rawFilesSlice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if formatRequired {
|
||||||
|
fmt.Println("Please run 'go install -v mvdan.cc/gofumpt@latest', then run 'go run ./infra/vformat/main.go' to format the Go source files.")
|
||||||
|
os.Exit(1)
|
||||||
|
} else {
|
||||||
|
fmt.Println("All Go source file format check has been passed.")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ func executeBalancerInfo(cmd *base.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showBalancerInfo(resp.Balancer)
|
showBalancerInfo(resp.Balancer)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func showBalancerInfo(b *routerService.BalancerMsg) {
|
func showBalancerInfo(b *routerService.BalancerMsg) {
|
||||||
|
|||||||
@@ -62,7 +62,8 @@ func addInboundUserAction(ctx context.Context, client handlerService.HandlerServ
|
|||||||
Operation: cserial.ToTypedMessage(
|
Operation: cserial.ToTypedMessage(
|
||||||
&handlerService.AddUserOperation{
|
&handlerService.AddUserOperation{
|
||||||
User: user,
|
User: user,
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ func executeRemoveUsers(cmd *base.Command, args []string) {
|
|||||||
Operation: cserial.ToTypedMessage(
|
Operation: cserial.ToTypedMessage(
|
||||||
&handlerService.RemoveUserOperation{
|
&handlerService.RemoveUserOperation{
|
||||||
Email: email,
|
Email: email,
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
success += 1
|
success += 1
|
||||||
|
|||||||
@@ -38,9 +38,7 @@ Example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func executeAddRules(cmd *base.Command, args []string) {
|
func executeAddRules(cmd *base.Command, args []string) {
|
||||||
var (
|
var shouldAppend bool
|
||||||
shouldAppend bool
|
|
||||||
)
|
|
||||||
setSharedFlags(cmd)
|
setSharedFlags(cmd)
|
||||||
cmd.Flag.BoolVar(&shouldAppend, "append", false, "")
|
cmd.Flag.BoolVar(&shouldAppend, "append", false, "")
|
||||||
cmd.Flag.Parse(args)
|
cmd.Flag.Parse(args)
|
||||||
@@ -96,5 +94,4 @@ func executeAddRules(cmd *base.Command, args []string) {
|
|||||||
}
|
}
|
||||||
showJSONResponse(resp)
|
showJSONResponse(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,5 +56,4 @@ func executeRemoveRules(cmd *base.Command, args []string) {
|
|||||||
}
|
}
|
||||||
showJSONResponse(resp)
|
showJSONResponse(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,5 +136,4 @@ func executeSourceIpBlock(cmd *base.Command, args []string) {
|
|||||||
base.Fatalf("failed to perform AddRule: %s", err)
|
base.Fatalf("failed to perform AddRule: %s", err)
|
||||||
}
|
}
|
||||||
showJSONResponse(resp)
|
showJSONResponse(resp)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ Examples:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func executeTypedMessageToJson(cmd *base.Command, args []string) {
|
func executeTypedMessageToJson(cmd *base.Command, args []string) {
|
||||||
|
|
||||||
var injectTypeInfo bool
|
var injectTypeInfo bool
|
||||||
cmd.Flag.BoolVar(&injectTypeInfo, "t", false, "")
|
cmd.Flag.BoolVar(&injectTypeInfo, "t", false, "")
|
||||||
cmd.Flag.BoolVar(&injectTypeInfo, "type", false, "")
|
cmd.Flag.BoolVar(&injectTypeInfo, "type", false, "")
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ Examples:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func executeConvertConfigsToProtobuf(cmd *base.Command, args []string) {
|
func executeConvertConfigsToProtobuf(cmd *base.Command, args []string) {
|
||||||
|
|
||||||
var optFile string
|
var optFile string
|
||||||
var optDump bool
|
var optDump bool
|
||||||
var optType bool
|
var optType bool
|
||||||
@@ -60,7 +59,7 @@ func executeConvertConfigsToProtobuf(cmd *base.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(optFile) > 0 {
|
if len(optFile) > 0 {
|
||||||
switch core.GetFormat(optFile){
|
switch core.GetFormat(optFile) {
|
||||||
case "protobuf", "":
|
case "protobuf", "":
|
||||||
fmt.Println("Output ProtoBuf file is ", optFile)
|
fmt.Println("Output ProtoBuf file is ", optFile)
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -34,8 +34,10 @@ func init() {
|
|||||||
var input_echServerKeys = cmdECH.Flag.String("i", "", "ECHServerKeys (base64.StdEncoding)")
|
var input_echServerKeys = cmdECH.Flag.String("i", "", "ECHServerKeys (base64.StdEncoding)")
|
||||||
|
|
||||||
// var input_pqSignatureSchemesEnabled = cmdECH.Flag.Bool("pqSignatureSchemesEnabled", false, "")
|
// var input_pqSignatureSchemesEnabled = cmdECH.Flag.Bool("pqSignatureSchemesEnabled", false, "")
|
||||||
var input_serverName = cmdECH.Flag.String("serverName", "cloudflare-ech.com", "")
|
var (
|
||||||
var input_pem = cmdECH.Flag.Bool("pem", false, "True == turn on pem output")
|
input_serverName = cmdECH.Flag.String("serverName", "cloudflare-ech.com", "")
|
||||||
|
input_pem = cmdECH.Flag.Bool("pem", false, "True == turn on pem output")
|
||||||
|
)
|
||||||
|
|
||||||
func executeECH(cmd *base.Command, args []string) {
|
func executeECH(cmd *base.Command, args []string) {
|
||||||
var kem uint16
|
var kem uint16
|
||||||
|
|||||||
@@ -21,8 +21,10 @@ func init() {
|
|||||||
cmdX25519.Run = executeX25519 // break init loop
|
cmdX25519.Run = executeX25519 // break init loop
|
||||||
}
|
}
|
||||||
|
|
||||||
var input_stdEncoding = cmdX25519.Flag.Bool("std-encoding", false, "")
|
var (
|
||||||
var input_x25519 = cmdX25519.Flag.String("i", "", "")
|
input_stdEncoding = cmdX25519.Flag.Bool("std-encoding", false, "")
|
||||||
|
input_x25519 = cmdX25519.Flag.String("i", "", "")
|
||||||
|
)
|
||||||
|
|
||||||
func executeX25519(cmd *base.Command, args []string) {
|
func executeX25519(cmd *base.Command, args []string) {
|
||||||
Curve25519Genkey(false, *input_x25519)
|
Curve25519Genkey(false, *input_x25519)
|
||||||
|
|||||||
@@ -12,9 +12,7 @@ type (
|
|||||||
configFileLoader func(string) (io.Reader, error)
|
configFileLoader func(string) (io.Reader, error)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var EffectiveConfigFileLoader configFileLoader
|
||||||
EffectiveConfigFileLoader configFileLoader
|
|
||||||
)
|
|
||||||
|
|
||||||
// LoadConfig reads from a path/url/stdin
|
// LoadConfig reads from a path/url/stdin
|
||||||
// actual work is in external module
|
// actual work is in external module
|
||||||
|
|||||||
Vendored
+10
-12
@@ -52,7 +52,7 @@ func ConfigLoader(arg string) (out io.Reader, err error) {
|
|||||||
// When the ":/" separator is omitted on a socket target, the request is
|
// When the ":/" separator is omitted on a socket target, the request is
|
||||||
// made to "/".
|
// made to "/".
|
||||||
func FetchHTTPContent(target string) ([]byte, error) {
|
func FetchHTTPContent(target string) ([]byte, error) {
|
||||||
httpURL, socketPath := utils.SplitHTTPUnixURL(target)
|
httpURL, socketPath := utils.SplitHTTPUnixURL(target)
|
||||||
|
|
||||||
parsedTarget, err := url.Parse(httpURL)
|
parsedTarget, err := url.Parse(httpURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -63,15 +63,15 @@ func FetchHTTPContent(target string) ([]byte, error) {
|
|||||||
Timeout: 30 * time.Second,
|
Timeout: 30 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
if socketPath != "" {
|
if socketPath != "" {
|
||||||
dialAddr := utils.ResolveSocketPath(socketPath)
|
dialAddr := utils.ResolveSocketPath(socketPath)
|
||||||
client.Transport = &http.Transport{
|
client.Transport = &http.Transport{
|
||||||
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||||
var d net.Dialer
|
var d net.Dialer
|
||||||
return d.DialContext(ctx, "unix", dialAddr)
|
return d.DialContext(ctx, "unix", dialAddr)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := client.Do(&http.Request{
|
resp, err := client.Do(&http.Request{
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
@@ -95,7 +95,6 @@ func FetchHTTPContent(target string) ([]byte, error) {
|
|||||||
return content, nil
|
return content, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// isRemoteSource reports whether arg should be fetched via HTTP (regular
|
// isRemoteSource reports whether arg should be fetched via HTTP (regular
|
||||||
// network or Unix socket) rather than read from the local filesystem.
|
// network or Unix socket) rather than read from the local filesystem.
|
||||||
// Recognized forms:
|
// Recognized forms:
|
||||||
@@ -124,7 +123,6 @@ func isRemoteSource(arg string) bool {
|
|||||||
return err == nil && info.Mode()&os.ModeSocket != 0
|
return err == nil && info.Mode()&os.ModeSocket != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// httpUnixToCanonical converts the deprecated http+unix:///path/to/socket.sock/api
|
// httpUnixToCanonical converts the deprecated http+unix:///path/to/socket.sock/api
|
||||||
// URL into the canonical /path/to/socket.sock:/api form by inserting ":"
|
// URL into the canonical /path/to/socket.sock:/api form by inserting ":"
|
||||||
// between the ".sock" extension and the HTTP path. Inputs without a path
|
// between the ".sock" extension and the HTTP path. Inputs without a path
|
||||||
|
|||||||
+20
-11
@@ -28,7 +28,7 @@ type RuleAction int32
|
|||||||
const (
|
const (
|
||||||
RuleAction_Direct RuleAction = 0
|
RuleAction_Direct RuleAction = 0
|
||||||
RuleAction_Drop RuleAction = 1
|
RuleAction_Drop RuleAction = 1
|
||||||
RuleAction_Reject RuleAction = 2
|
RuleAction_Return RuleAction = 2
|
||||||
RuleAction_Hijack RuleAction = 3
|
RuleAction_Hijack RuleAction = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,13 +37,13 @@ var (
|
|||||||
RuleAction_name = map[int32]string{
|
RuleAction_name = map[int32]string{
|
||||||
0: "Direct",
|
0: "Direct",
|
||||||
1: "Drop",
|
1: "Drop",
|
||||||
2: "Reject",
|
2: "Return",
|
||||||
3: "Hijack",
|
3: "Hijack",
|
||||||
}
|
}
|
||||||
RuleAction_value = map[string]int32{
|
RuleAction_value = map[string]int32{
|
||||||
"Direct": 0,
|
"Direct": 0,
|
||||||
"Drop": 1,
|
"Drop": 1,
|
||||||
"Reject": 2,
|
"Return": 2,
|
||||||
"Hijack": 3,
|
"Hijack": 3,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -78,8 +78,9 @@ func (RuleAction) EnumDescriptor() ([]byte, []int) {
|
|||||||
type DNSRuleConfig struct {
|
type DNSRuleConfig struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Action RuleAction `protobuf:"varint,1,opt,name=action,proto3,enum=xray.proxy.dns.RuleAction" json:"action,omitempty"`
|
Action RuleAction `protobuf:"varint,1,opt,name=action,proto3,enum=xray.proxy.dns.RuleAction" json:"action,omitempty"`
|
||||||
Qtype []int32 `protobuf:"varint,2,rep,packed,name=qtype,proto3" json:"qtype,omitempty"`
|
QType []int32 `protobuf:"varint,2,rep,packed,name=q_type,json=qType,proto3" json:"q_type,omitempty"`
|
||||||
Domain []*geodata.DomainRule `protobuf:"bytes,3,rep,name=domain,proto3" json:"domain,omitempty"`
|
Domain []*geodata.DomainRule `protobuf:"bytes,3,rep,name=domain,proto3" json:"domain,omitempty"`
|
||||||
|
RCode uint32 `protobuf:"varint,4,opt,name=r_code,json=rCode,proto3" json:"r_code,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -121,9 +122,9 @@ func (x *DNSRuleConfig) GetAction() RuleAction {
|
|||||||
return RuleAction_Direct
|
return RuleAction_Direct
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *DNSRuleConfig) GetQtype() []int32 {
|
func (x *DNSRuleConfig) GetQType() []int32 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Qtype
|
return x.QType
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -135,6 +136,13 @@ func (x *DNSRuleConfig) GetDomain() []*geodata.DomainRule {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *DNSRuleConfig) GetRCode() uint32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.RCode
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
UserLevel uint32 `protobuf:"varint,1,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"`
|
UserLevel uint32 `protobuf:"varint,1,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"`
|
||||||
@@ -199,11 +207,12 @@ var File_proxy_dns_config_proto protoreflect.FileDescriptor
|
|||||||
|
|
||||||
const file_proxy_dns_config_proto_rawDesc = "" +
|
const file_proxy_dns_config_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\x16proxy/dns/config.proto\x12\x0exray.proxy.dns\x1a\x1ccommon/net/destination.proto\x1a\x1bcommon/geodata/geodat.proto\"\x92\x01\n" +
|
"\x16proxy/dns/config.proto\x12\x0exray.proxy.dns\x1a\x1ccommon/net/destination.proto\x1a\x1bcommon/geodata/geodat.proto\"\xaa\x01\n" +
|
||||||
"\rDNSRuleConfig\x122\n" +
|
"\rDNSRuleConfig\x122\n" +
|
||||||
"\x06action\x18\x01 \x01(\x0e2\x1a.xray.proxy.dns.RuleActionR\x06action\x12\x14\n" +
|
"\x06action\x18\x01 \x01(\x0e2\x1a.xray.proxy.dns.RuleActionR\x06action\x12\x15\n" +
|
||||||
"\x05qtype\x18\x02 \x03(\x05R\x05qtype\x127\n" +
|
"\x06q_type\x18\x02 \x03(\x05R\x05qType\x127\n" +
|
||||||
"\x06domain\x18\x03 \x03(\v2\x1f.xray.common.geodata.DomainRuleR\x06domain\"\x9c\x01\n" +
|
"\x06domain\x18\x03 \x03(\v2\x1f.xray.common.geodata.DomainRuleR\x06domain\x12\x15\n" +
|
||||||
|
"\x06r_code\x18\x04 \x01(\rR\x05rCode\"\x9c\x01\n" +
|
||||||
"\x06Config\x12\x1d\n" +
|
"\x06Config\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"user_level\x18\x01 \x01(\rR\tuserLevel\x121\n" +
|
"user_level\x18\x01 \x01(\rR\tuserLevel\x121\n" +
|
||||||
@@ -215,7 +224,7 @@ const file_proxy_dns_config_proto_rawDesc = "" +
|
|||||||
"\x06Direct\x10\x00\x12\b\n" +
|
"\x06Direct\x10\x00\x12\b\n" +
|
||||||
"\x04Drop\x10\x01\x12\n" +
|
"\x04Drop\x10\x01\x12\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\x06Reject\x10\x02\x12\n" +
|
"\x06Return\x10\x02\x12\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\x06Hijack\x10\x03BL\n" +
|
"\x06Hijack\x10\x03BL\n" +
|
||||||
"\x12com.xray.proxy.dnsP\x01Z#github.com/xtls/xray-core/proxy/dns\xaa\x02\x0eXray.Proxy.Dnsb\x06proto3"
|
"\x12com.xray.proxy.dnsP\x01Z#github.com/xtls/xray-core/proxy/dns\xaa\x02\x0eXray.Proxy.Dnsb\x06proto3"
|
||||||
|
|||||||
@@ -12,14 +12,15 @@ import "common/geodata/geodat.proto";
|
|||||||
enum RuleAction {
|
enum RuleAction {
|
||||||
Direct = 0;
|
Direct = 0;
|
||||||
Drop = 1;
|
Drop = 1;
|
||||||
Reject = 2;
|
Return = 2;
|
||||||
Hijack = 3;
|
Hijack = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message DNSRuleConfig {
|
message DNSRuleConfig {
|
||||||
RuleAction action = 1;
|
RuleAction action = 1;
|
||||||
repeated int32 qtype = 2;
|
repeated int32 q_type = 2;
|
||||||
repeated xray.common.geodata.DomainRule domain = 3;
|
repeated xray.common.geodata.DomainRule domain = 3;
|
||||||
|
uint32 r_code = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Config {
|
message Config {
|
||||||
|
|||||||
+41
-43
@@ -45,6 +45,7 @@ type DNSRule struct {
|
|||||||
action RuleAction
|
action RuleAction
|
||||||
qTypes []uint16
|
qTypes []uint16
|
||||||
domains geodata.DomainMatcher
|
domains geodata.DomainMatcher
|
||||||
|
rCode dnsmessage.RCode
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DNSRule) matchQType(qType uint16) bool {
|
func (r *DNSRule) matchQType(qType uint16) bool {
|
||||||
@@ -95,9 +96,10 @@ func (h *Handler) Init(config *Config, dnsClient dns.Client, policyManager polic
|
|||||||
for _, r := range config.Rule {
|
for _, r := range config.Rule {
|
||||||
rule := &DNSRule{
|
rule := &DNSRule{
|
||||||
action: r.Action,
|
action: r.Action,
|
||||||
qTypes: make([]uint16, 0, len(r.Qtype)),
|
qTypes: make([]uint16, 0, len(r.QType)),
|
||||||
|
rCode: dnsmessage.RCode(r.RCode),
|
||||||
}
|
}
|
||||||
for _, t := range r.Qtype {
|
for _, t := range r.QType {
|
||||||
rule.qTypes = append(rule.qTypes, uint16(t))
|
rule.qTypes = append(rule.qTypes, uint16(t))
|
||||||
}
|
}
|
||||||
if len(r.Domain) > 0 {
|
if len(r.Domain) > 0 {
|
||||||
@@ -136,17 +138,17 @@ func parseQuery(b []byte) (id uint16, qType dnsmessage.Type, domain string, ok b
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) applyRules(qType dnsmessage.Type, domain string) RuleAction {
|
func (h *Handler) applyRules(qType dnsmessage.Type, domain string) (RuleAction, dnsmessage.RCode) {
|
||||||
qCode := uint16(qType)
|
qCode := uint16(qType)
|
||||||
for _, r := range h.rules {
|
for _, r := range h.rules {
|
||||||
if r.Apply(qCode, domain) {
|
if r.Apply(qCode, domain) {
|
||||||
return r.action
|
return r.action, r.rCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if qType == dnsmessage.TypeA || qType == dnsmessage.TypeAAAA {
|
if qType == dnsmessage.TypeA || qType == dnsmessage.TypeAAAA {
|
||||||
return RuleAction_Hijack
|
return RuleAction_Hijack, dnsmessage.RCodeSuccess
|
||||||
}
|
}
|
||||||
return RuleAction_Reject
|
return RuleAction_Return, dnsmessage.RCodeSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process implements proxy.Outbound.
|
// Process implements proxy.Outbound.
|
||||||
@@ -213,7 +215,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if session.TimeoutOnlyFromContext(ctx) {
|
if session.TimeoutOnlyFromContext(ctx) {
|
||||||
ctx, _ = context.WithCancel(context.Background())
|
ctx = context.Background()
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
@@ -250,21 +252,22 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
switch h.applyRules(qType, domain) {
|
action, rCode := h.applyRules(qType, domain)
|
||||||
|
switch action {
|
||||||
case RuleAction_Drop:
|
case RuleAction_Drop:
|
||||||
b.Release()
|
b.Release()
|
||||||
errors.LogInfo(ctx, "blocked type ", qType, " query for domain ", domain)
|
errors.LogInfo(ctx, "blocked type ", qType, " query for domain ", domain)
|
||||||
case RuleAction_Reject:
|
case RuleAction_Return:
|
||||||
b.Release()
|
b.Release()
|
||||||
errors.LogInfo(ctx, "rejected type ", qType, " query for domain ", domain)
|
errors.LogInfo(ctx, "rejected type ", qType, " query for domain ", domain)
|
||||||
if err := h.rejectNonIPQuery(id, qType, domain, writer); err != nil {
|
if err := h.rejectNonIPQuery(id, qType, domain, writer, rCode); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case RuleAction_Hijack:
|
case RuleAction_Hijack:
|
||||||
b.Release()
|
b.Release()
|
||||||
if qType != dnsmessage.TypeA && qType != dnsmessage.TypeAAAA {
|
if qType != dnsmessage.TypeA && qType != dnsmessage.TypeAAAA {
|
||||||
errors.LogError(ctx, "can only hijack A/AAAA records")
|
errors.LogError(ctx, "can only hijack A/AAAA records")
|
||||||
if err := h.rejectNonIPQuery(id, qType, domain, writer); err != nil {
|
if err := h.rejectNonIPQuery(id, qType, domain, writer, rCode); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -309,48 +312,35 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
|
|||||||
|
|
||||||
func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter, timer *signal.ActivityTimer) {
|
func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter, timer *signal.ActivityTimer) {
|
||||||
var ips []net.IP
|
var ips []net.IP
|
||||||
|
var ttl uint32
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
var ttl4 uint32
|
|
||||||
var ttl6 uint32
|
|
||||||
|
|
||||||
switch qType {
|
switch qType {
|
||||||
case dnsmessage.TypeA:
|
case dnsmessage.TypeA:
|
||||||
ips, ttl4, err = h.client.LookupIP(domain, dns.IPOption{
|
ips, ttl, err = h.client.LookupIP(domain, dns.IPOption{
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
IPv6Enable: false,
|
IPv6Enable: false,
|
||||||
FakeEnable: true,
|
FakeEnable: true,
|
||||||
})
|
})
|
||||||
case dnsmessage.TypeAAAA:
|
case dnsmessage.TypeAAAA:
|
||||||
ips, ttl6, err = h.client.LookupIP(domain, dns.IPOption{
|
ips, ttl, err = h.client.LookupIP(domain, dns.IPOption{
|
||||||
IPv4Enable: false,
|
IPv4Enable: false,
|
||||||
IPv6Enable: true,
|
IPv6Enable: true,
|
||||||
FakeEnable: true,
|
FakeEnable: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
rcode := dns.RCodeFromError(err)
|
rCode := dns.RCodeFromError(err)
|
||||||
if rcode == 0 && len(ips) == 0 && !go_errors.Is(err, dns.ErrEmptyResponse) {
|
if rCode == 0 && len(ips) == 0 && !go_errors.Is(err, dns.ErrEmptyResponse) {
|
||||||
errors.LogInfoInner(context.Background(), err, "ip query")
|
errors.LogInfoInner(context.Background(), err, "ip query")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch qType {
|
|
||||||
case dnsmessage.TypeA:
|
|
||||||
for i, ip := range ips {
|
|
||||||
ips[i] = ip.To4()
|
|
||||||
}
|
|
||||||
case dnsmessage.TypeAAAA:
|
|
||||||
for i, ip := range ips {
|
|
||||||
ips[i] = ip.To16()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b := buf.New()
|
b := buf.New()
|
||||||
rawBytes := b.Extend(buf.Size)
|
rawBytes := b.Extend(buf.Size)
|
||||||
builder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{
|
builder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{
|
||||||
ID: id,
|
ID: id,
|
||||||
RCode: dnsmessage.RCode(rcode),
|
RCode: dnsmessage.RCode(rCode),
|
||||||
RecursionAvailable: true,
|
RecursionAvailable: true,
|
||||||
RecursionDesired: true,
|
RecursionDesired: true,
|
||||||
Response: true,
|
Response: true,
|
||||||
@@ -365,17 +355,25 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string,
|
|||||||
}))
|
}))
|
||||||
common.Must(builder.StartAnswers())
|
common.Must(builder.StartAnswers())
|
||||||
|
|
||||||
rHeader4 := dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, TTL: ttl4}
|
rHeader := dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, TTL: ttl}
|
||||||
rHeader6 := dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, TTL: ttl6}
|
switch qType {
|
||||||
for _, ip := range ips {
|
case dnsmessage.TypeA:
|
||||||
if len(ip) == net.IPv4len {
|
for _, ip := range ips {
|
||||||
var r dnsmessage.AResource
|
ip = ip.To4()
|
||||||
copy(r.A[:], ip)
|
if len(ip) == net.IPv4len {
|
||||||
common.Must(builder.AResource(rHeader4, r))
|
var r dnsmessage.AResource
|
||||||
} else {
|
copy(r.A[:], ip)
|
||||||
var r dnsmessage.AAAAResource
|
common.Must(builder.AResource(rHeader, r))
|
||||||
copy(r.AAAA[:], ip)
|
}
|
||||||
common.Must(builder.AAAAResource(rHeader6, r))
|
}
|
||||||
|
case dnsmessage.TypeAAAA:
|
||||||
|
for _, ip := range ips {
|
||||||
|
ip = ip.To16()
|
||||||
|
if len(ip) == net.IPv6len {
|
||||||
|
var r dnsmessage.AAAAResource
|
||||||
|
copy(r.AAAA[:], ip)
|
||||||
|
common.Must(builder.AAAAResource(rHeader, r))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
msgBytes, err := builder.Finish()
|
msgBytes, err := builder.Finish()
|
||||||
@@ -392,7 +390,7 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) rejectNonIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter) error {
|
func (h *Handler) rejectNonIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter, rCode dnsmessage.RCode) error {
|
||||||
domainT := strings.TrimSuffix(domain, ".")
|
domainT := strings.TrimSuffix(domain, ".")
|
||||||
if domainT == "" {
|
if domainT == "" {
|
||||||
return errors.New("empty domain name")
|
return errors.New("empty domain name")
|
||||||
@@ -401,7 +399,7 @@ func (h *Handler) rejectNonIPQuery(id uint16, qType dnsmessage.Type, domain stri
|
|||||||
rawBytes := b.Extend(buf.Size)
|
rawBytes := b.Extend(buf.Size)
|
||||||
builder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{
|
builder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{
|
||||||
ID: id,
|
ID: id,
|
||||||
RCode: dnsmessage.RCodeRefused,
|
RCode: rCode,
|
||||||
RecursionAvailable: true,
|
RecursionAvailable: true,
|
||||||
RecursionDesired: true,
|
RecursionDesired: true,
|
||||||
Response: true,
|
Response: true,
|
||||||
|
|||||||
@@ -424,7 +424,7 @@ func TestDNSRules(t *testing.T) {
|
|||||||
ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{
|
ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{
|
||||||
Rule: []*dns_proxy.DNSRuleConfig{
|
Rule: []*dns_proxy.DNSRuleConfig{
|
||||||
{
|
{
|
||||||
Qtype: []int32{int32(dns.TypeA)},
|
QType: []int32{int32(dns.TypeA)},
|
||||||
Domain: []*geodata.DomainRule{
|
Domain: []*geodata.DomainRule{
|
||||||
{
|
{
|
||||||
Value: &geodata.DomainRule_Custom{
|
Value: &geodata.DomainRule_Custom{
|
||||||
@@ -438,7 +438,7 @@ func TestDNSRules(t *testing.T) {
|
|||||||
Action: dns_proxy.RuleAction_Direct,
|
Action: dns_proxy.RuleAction_Direct,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Qtype: []int32{int32(dns.TypeA)},
|
QType: []int32{int32(dns.TypeA)},
|
||||||
Domain: []*geodata.DomainRule{
|
Domain: []*geodata.DomainRule{
|
||||||
{
|
{
|
||||||
Value: &geodata.DomainRule_Custom{
|
Value: &geodata.DomainRule_Custom{
|
||||||
@@ -449,7 +449,8 @@ func TestDNSRules(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: dns_proxy.RuleAction_Reject,
|
Action: dns_proxy.RuleAction_Return,
|
||||||
|
RCode: 5,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -190,9 +190,11 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := dispatcher.DispatchLink(ctx, dest, &transport.Link{
|
if err := dispatcher.DispatchLink(
|
||||||
Reader: reader,
|
ctx, dest, &transport.Link{
|
||||||
Writer: writer},
|
Reader: reader,
|
||||||
|
Writer: writer,
|
||||||
|
},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return errors.New("failed to dispatch request").Base(err)
|
return errors.New("failed to dispatch request").Base(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,10 +30,12 @@ import (
|
|||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
)
|
)
|
||||||
|
|
||||||
var useSplice bool
|
var (
|
||||||
var allNetworks [8]bool
|
useSplice bool
|
||||||
var defaultBlockPrivateRule *FinalRule
|
allNetworks [8]bool
|
||||||
var defaultBlockAllRule *FinalRule
|
defaultBlockPrivateRule *FinalRule
|
||||||
|
defaultBlockAllRule *FinalRule
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||||
@@ -677,7 +679,7 @@ type NoisePacketWriter struct {
|
|||||||
func (w *NoisePacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
func (w *NoisePacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||||
if w.firstWrite {
|
if w.firstWrite {
|
||||||
w.firstWrite = false
|
w.firstWrite = false
|
||||||
//Do not send Noise for dns requests(just to be safe)
|
// Do not send Noise for dns requests(just to be safe)
|
||||||
if w.UDPOverride.Port == 53 {
|
if w.UDPOverride.Port == 53 {
|
||||||
return w.Writer.WriteMultiBuffer(mb)
|
return w.Writer.WriteMultiBuffer(mb)
|
||||||
}
|
}
|
||||||
@@ -700,11 +702,11 @@ func (w *NoisePacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||||||
default:
|
default:
|
||||||
panic("unreachable, applyTo is ip/ipv4/ipv6")
|
panic("unreachable, applyTo is ip/ipv4/ipv6")
|
||||||
}
|
}
|
||||||
//User input string or base64 encoded string or hex string
|
// User input string or base64 encoded string or hex string
|
||||||
if n.Packet != nil {
|
if n.Packet != nil {
|
||||||
noise = n.Packet
|
noise = n.Packet
|
||||||
} else {
|
} else {
|
||||||
//Random noise
|
// Random noise
|
||||||
noise, err = GenerateRandomBytes(crypto.RandBetween(int64(n.LengthMin),
|
noise, err = GenerateRandomBytes(crypto.RandBetween(int64(n.LengthMin),
|
||||||
int64(n.LengthMax)))
|
int64(n.LengthMax)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,9 +192,11 @@ func (s *Server) handleConnect(ctx context.Context, _ *http.Request, buffer *buf
|
|||||||
if inbound.CanSpliceCopy == 2 {
|
if inbound.CanSpliceCopy == 2 {
|
||||||
inbound.CanSpliceCopy = 1
|
inbound.CanSpliceCopy = 1
|
||||||
}
|
}
|
||||||
if err := dispatcher.DispatchLink(ctx, dest, &transport.Link{
|
if err := dispatcher.DispatchLink(
|
||||||
Reader: reader,
|
ctx, dest, &transport.Link{
|
||||||
Writer: buf.NewWriter(conn)},
|
Reader: reader,
|
||||||
|
Writer: buf.NewWriter(conn),
|
||||||
|
},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return errors.New("failed to dispatch request").Base(err)
|
return errors.New("failed to dispatch request").Base(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ func (v *Validator) GetAll() []*protocol.MemoryUser {
|
|||||||
v.mutex.Lock()
|
v.mutex.Lock()
|
||||||
defer v.mutex.Unlock()
|
defer v.mutex.Unlock()
|
||||||
|
|
||||||
var users = make([]*protocol.MemoryUser, 0, len(v.users))
|
users := make([]*protocol.MemoryUser, 0, len(v.users))
|
||||||
for _, user := range v.users {
|
for _, user := range v.users {
|
||||||
users = append(users, user)
|
users = append(users, user)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,6 +253,9 @@ func FragUDPMessage(m *UDPMessage, maxSize int) []UDPMessage {
|
|||||||
}
|
}
|
||||||
fullPayload := m.Data
|
fullPayload := m.Data
|
||||||
maxPayloadSize := maxSize - m.HeaderSize()
|
maxPayloadSize := maxSize - m.HeaderSize()
|
||||||
|
if maxPayloadSize <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
off := 0
|
off := 0
|
||||||
fragID := uint8(0)
|
fragID := uint8(0)
|
||||||
fragCount := uint8((len(fullPayload) + maxPayloadSize - 1) / maxPayloadSize) // round up
|
fragCount := uint8((len(fullPayload) + maxPayloadSize - 1) / maxPayloadSize) // round up
|
||||||
|
|||||||
+1
-1
@@ -743,7 +743,7 @@ func CopyRawConnIfExist(ctx context.Context, readerConn net.Conn, writerConn net
|
|||||||
for {
|
for {
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
outbounds := session.OutboundsFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
var splice = inbound.CanSpliceCopy == 1
|
splice := inbound.CanSpliceCopy == 1
|
||||||
for _, ob := range outbounds {
|
for _, ob := range outbounds {
|
||||||
if ob.CanSpliceCopy != 1 {
|
if ob.CanSpliceCopy != 1 {
|
||||||
splice = false
|
splice = false
|
||||||
|
|||||||
+32
-13
@@ -1,14 +1,17 @@
|
|||||||
package socks
|
package socks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
|
gonet "net"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/protocol"
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -137,13 +140,13 @@ func (s *ServerSession) auth5(nMethod byte, reader io.Reader, writer io.Writer)
|
|||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerSession) handshake5(nMethod byte, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) {
|
func (s *ServerSession) handshake5(nMethod byte, reader io.Reader, writer net.Conn) (*protocol.RequestHeader, *TempUDPConn, error) {
|
||||||
var (
|
var (
|
||||||
username string
|
username string
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if username, err = s.auth5(nMethod, reader, writer); err != nil {
|
if username, err = s.auth5(nMethod, reader, writer); err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmd byte
|
var cmd byte
|
||||||
@@ -151,7 +154,7 @@ func (s *ServerSession) handshake5(nMethod byte, reader io.Reader, writer io.Wri
|
|||||||
buffer := buf.StackNew()
|
buffer := buf.StackNew()
|
||||||
if _, err := buffer.ReadFullFrom(reader, 3); err != nil {
|
if _, err := buffer.ReadFullFrom(reader, 3); err != nil {
|
||||||
buffer.Release()
|
buffer.Release()
|
||||||
return nil, errors.New("failed to read request").Base(err)
|
return nil, nil, errors.New("failed to read request").Base(err)
|
||||||
}
|
}
|
||||||
cmd = buffer.Byte(1)
|
cmd = buffer.Byte(1)
|
||||||
buffer.Release()
|
buffer.Release()
|
||||||
@@ -168,28 +171,29 @@ func (s *ServerSession) handshake5(nMethod byte, reader io.Reader, writer io.Wri
|
|||||||
case cmdUDPAssociate:
|
case cmdUDPAssociate:
|
||||||
if !s.config.UdpEnabled {
|
if !s.config.UdpEnabled {
|
||||||
writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0))
|
writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0))
|
||||||
return nil, errors.New("UDP is not enabled.")
|
return nil, nil, errors.New("UDP is not enabled.")
|
||||||
}
|
}
|
||||||
request.Command = protocol.RequestCommandUDP
|
request.Command = protocol.RequestCommandUDP
|
||||||
case cmdTCPBind:
|
case cmdTCPBind:
|
||||||
writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0))
|
writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0))
|
||||||
return nil, errors.New("TCP bind is not supported.")
|
return nil, nil, errors.New("TCP bind is not supported.")
|
||||||
default:
|
default:
|
||||||
writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0))
|
writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0))
|
||||||
return nil, errors.New("unknown command ", cmd)
|
return nil, nil, errors.New("unknown command ", cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
request.Version = socks5Version
|
request.Version = socks5Version
|
||||||
|
|
||||||
addr, port, err := addrParser.ReadAddressPort(nil, reader)
|
addr, port, err := addrParser.ReadAddressPort(nil, reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("failed to read address").Base(err)
|
return nil, nil, errors.New("failed to read address").Base(err)
|
||||||
}
|
}
|
||||||
request.Address = addr
|
request.Address = addr
|
||||||
request.Port = port
|
request.Port = port
|
||||||
|
|
||||||
responseAddress := s.address
|
responseAddress := s.address
|
||||||
responsePort := s.port
|
responsePort := s.port
|
||||||
|
var tempUDPConn *TempUDPConn
|
||||||
//nolint:gocritic // Use if else chain for clarity
|
//nolint:gocritic // Use if else chain for clarity
|
||||||
if request.Command == protocol.RequestCommandUDP {
|
if request.Command == protocol.RequestCommandUDP {
|
||||||
if s.config.Address != nil {
|
if s.config.Address != nil {
|
||||||
@@ -199,20 +203,34 @@ func (s *ServerSession) handshake5(nMethod byte, reader io.Reader, writer io.Wri
|
|||||||
// Use conn.LocalAddr() IP as remote address in the response by default
|
// Use conn.LocalAddr() IP as remote address in the response by default
|
||||||
responseAddress = s.localAddress
|
responseAddress = s.localAddress
|
||||||
}
|
}
|
||||||
|
udpHub, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{IP: responseAddress.IP(), Port: 0}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.New("failed to create UDP listener").Base(err)
|
||||||
|
}
|
||||||
|
responsePort = net.Port(udpHub.LocalAddr().(*net.UDPAddr).Port)
|
||||||
|
expectedRemote := &gonet.UDPAddr{}
|
||||||
|
if request.Address.IP().IsUnspecified() {
|
||||||
|
expectedRemote.IP = writer.RemoteAddr().(*net.TCPAddr).IP // unix?
|
||||||
|
} else {
|
||||||
|
expectedRemote.IP = request.Address.IP() // panic?
|
||||||
|
expectedRemote.Port = int(request.Port) // 0 is allowed
|
||||||
|
}
|
||||||
|
tempUDPConn = NewTempUDPConn(udpHub, writer, expectedRemote)
|
||||||
}
|
}
|
||||||
if err := writeSocks5Response(writer, statusSuccess, responseAddress, responsePort); err != nil {
|
if err := writeSocks5Response(writer, statusSuccess, responseAddress, responsePort); err != nil {
|
||||||
return nil, err
|
common.CloseIfExists(tempUDPConn)
|
||||||
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return request, nil
|
return request, tempUDPConn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handshake performs a Socks4/4a/5 handshake.
|
// Handshake performs a Socks4/4a/5 handshake.
|
||||||
func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) {
|
func (s *ServerSession) Handshake(reader io.Reader, writer net.Conn) (*protocol.RequestHeader, *TempUDPConn, error) {
|
||||||
buffer := buf.StackNew()
|
buffer := buf.StackNew()
|
||||||
if _, err := buffer.ReadFullFrom(reader, 2); err != nil {
|
if _, err := buffer.ReadFullFrom(reader, 2); err != nil {
|
||||||
buffer.Release()
|
buffer.Release()
|
||||||
return nil, errors.New("insufficient header").Base(err)
|
return nil, nil, errors.New("insufficient header").Base(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
version := buffer.Byte(0)
|
version := buffer.Byte(0)
|
||||||
@@ -221,11 +239,12 @@ func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol
|
|||||||
|
|
||||||
switch version {
|
switch version {
|
||||||
case socks4Version:
|
case socks4Version:
|
||||||
return s.handshake4(cmd, reader, writer)
|
header, err := s.handshake4(cmd, reader, writer)
|
||||||
|
return header, nil, err
|
||||||
case socks5Version:
|
case socks5Version:
|
||||||
return s.handshake5(cmd, reader, writer)
|
return s.handshake5(cmd, reader, writer)
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("unknown Socks version: ", version)
|
return nil, nil, errors.New("unknown Socks version: ", version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+21
-27
@@ -29,7 +29,6 @@ type Server struct {
|
|||||||
config *ServerConfig
|
config *ServerConfig
|
||||||
policyManager policy.Manager
|
policyManager policy.Manager
|
||||||
cone bool
|
cone bool
|
||||||
udpFilter *UDPFilter
|
|
||||||
httpServer *http.Server
|
httpServer *http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +45,6 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
|
|||||||
}
|
}
|
||||||
if config.AuthType == AuthType_PASSWORD {
|
if config.AuthType == AuthType_PASSWORD {
|
||||||
httpConfig.Accounts = config.Accounts
|
httpConfig.Accounts = config.Accounts
|
||||||
s.udpFilter = new(UDPFilter) // We only use this when auth is enabled
|
|
||||||
}
|
}
|
||||||
s.httpServer, _ = http.NewServer(ctx, httpConfig)
|
s.httpServer, _ = http.NewServer(ctx, httpConfig)
|
||||||
return s, nil
|
return s, nil
|
||||||
@@ -60,11 +58,7 @@ func (s *Server) policy() policy.Session {
|
|||||||
|
|
||||||
// Network implements proxy.Inbound.
|
// Network implements proxy.Inbound.
|
||||||
func (s *Server) Network() []net.Network {
|
func (s *Server) Network() []net.Network {
|
||||||
list := []net.Network{net.Network_TCP}
|
return []net.Network{net.Network_TCP}
|
||||||
if s.config.UdpEnabled {
|
|
||||||
list = append(list, net.Network_UDP)
|
|
||||||
}
|
|
||||||
return list
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process implements proxy.Inbound.
|
// Process implements proxy.Inbound.
|
||||||
@@ -94,8 +88,6 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con
|
|||||||
return s.httpServer.ProcessWithFirstbyte(ctx, network, conn, dispatcher, firstbyte...)
|
return s.httpServer.ProcessWithFirstbyte(ctx, network, conn, dispatcher, firstbyte...)
|
||||||
}
|
}
|
||||||
return s.processTCP(ctx, conn, dispatcher, firstbyte)
|
return s.processTCP(ctx, conn, dispatcher, firstbyte)
|
||||||
case net.Network_UDP:
|
|
||||||
return s.handleUDPPayload(ctx, conn, dispatcher)
|
|
||||||
default:
|
default:
|
||||||
return errors.New("unknown network: ", network)
|
return errors.New("unknown network: ", network)
|
||||||
}
|
}
|
||||||
@@ -126,7 +118,8 @@ func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatche
|
|||||||
Reader: buf.NewReader(conn),
|
Reader: buf.NewReader(conn),
|
||||||
Buffer: buf.MultiBuffer{buf.FromBytes(firstbyte)},
|
Buffer: buf.MultiBuffer{buf.FromBytes(firstbyte)},
|
||||||
}
|
}
|
||||||
request, err := svrSession.Handshake(reader, conn)
|
request, tempUDPConn, err := svrSession.Handshake(reader, conn)
|
||||||
|
defer common.CloseIfExists(tempUDPConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if inbound.Source.IsValid() {
|
if inbound.Source.IsValid() {
|
||||||
log.Record(&log.AccessMessage{
|
log.Record(&log.AccessMessage{
|
||||||
@@ -160,9 +153,11 @@ func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatche
|
|||||||
if inbound.CanSpliceCopy == 2 {
|
if inbound.CanSpliceCopy == 2 {
|
||||||
inbound.CanSpliceCopy = 1
|
inbound.CanSpliceCopy = 1
|
||||||
}
|
}
|
||||||
if err := dispatcher.DispatchLink(ctx, dest, &transport.Link{
|
if err := dispatcher.DispatchLink(
|
||||||
Reader: reader,
|
ctx, dest, &transport.Link{
|
||||||
Writer: buf.NewWriter(conn)},
|
Reader: reader,
|
||||||
|
Writer: buf.NewWriter(conn),
|
||||||
|
},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return errors.New("failed to dispatch request").Base(err)
|
return errors.New("failed to dispatch request").Base(err)
|
||||||
}
|
}
|
||||||
@@ -170,26 +165,25 @@ func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatche
|
|||||||
}
|
}
|
||||||
|
|
||||||
if request.Command == protocol.RequestCommandUDP {
|
if request.Command == protocol.RequestCommandUDP {
|
||||||
if s.udpFilter != nil {
|
if tempUDPConn == nil {
|
||||||
s.udpFilter.Add(conn.RemoteAddr())
|
return errors.New("UDP associate with listen port failed")
|
||||||
}
|
}
|
||||||
return s.handleUDP(conn)
|
tempUDPConn.SetTimeout(plcy.Timeouts.ConnectionIdle)
|
||||||
|
errCh := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
errCh <- s.handleUDPPayload(ctx, tempUDPConn, dispatcher)
|
||||||
|
}()
|
||||||
|
// Associated TCP keeps the UDP alive
|
||||||
|
// Close UDP if TCP connection is closed
|
||||||
|
// Or Close TCP if UDP is idle timeout
|
||||||
|
io.Copy(buf.DiscardBytes, conn)
|
||||||
|
tempUDPConn.Close()
|
||||||
|
return <-errCh
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Server) handleUDP(c io.Reader) error {
|
|
||||||
// The TCP connection closes after this method returns. We need to wait until
|
|
||||||
// the client closes it.
|
|
||||||
return common.Error2(io.Copy(buf.DiscardBytes, c))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
||||||
if s.udpFilter != nil && !s.udpFilter.Check(conn.RemoteAddr()) {
|
|
||||||
errors.LogDebug(ctx, "Unauthorized UDP access from ", conn.RemoteAddr().String())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
udpServer := udp.NewDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
|
udpServer := udp.NewDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
|
||||||
payload := packet.Payload
|
payload := packet.Payload
|
||||||
errors.LogDebug(ctx, "writing back UDP response with ", payload.Len(), " bytes")
|
errors.LogDebug(ctx, "writing back UDP response with ", payload.Len(), " bytes")
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package socks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/signal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewTempUDPConn(udpConn net.PacketConn, tcpConn net.Conn, expectedRemote *net.UDPAddr) *TempUDPConn {
|
||||||
|
t := &TempUDPConn{
|
||||||
|
PacketConn: udpConn,
|
||||||
|
AssociatedTCPConn: tcpConn,
|
||||||
|
}
|
||||||
|
t.ExpectedRemote.Store(expectedRemote)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// TempUDPConn wait for the first packet to determine the remote address
|
||||||
|
// SetTimeout MUST be called before any read/write operation
|
||||||
|
type TempUDPConn struct {
|
||||||
|
net.PacketConn
|
||||||
|
AssociatedTCPConn net.Conn
|
||||||
|
ExpectedRemote atomic.Pointer[net.UDPAddr]
|
||||||
|
Timer *signal.ActivityTimer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TempUDPConn) Read(b []byte) (n int, err error) {
|
||||||
|
var remote net.Addr
|
||||||
|
for {
|
||||||
|
n, remote, err = c.PacketConn.ReadFrom(b)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
remote := remote.(*net.UDPAddr)
|
||||||
|
expected := c.ExpectedRemote.Load()
|
||||||
|
if remote.IP.Equal(expected.IP) {
|
||||||
|
if remote.Port == expected.Port {
|
||||||
|
c.Timer.Update()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if expected.Port == 0 {
|
||||||
|
c.ExpectedRemote.Store(remote)
|
||||||
|
c.Timer.Update()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TempUDPConn) Write(b []byte) (n int, err error) {
|
||||||
|
c.Timer.Update()
|
||||||
|
return c.PacketConn.WriteTo(b, c.ExpectedRemote.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TempUDPConn) RemoteAddr() net.Addr {
|
||||||
|
return c.ExpectedRemote.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TempUDPConn) SetTimeout(d time.Duration) {
|
||||||
|
c.Timer = signal.CancelAfterInactivity(context.Background(), func() {
|
||||||
|
c.Close()
|
||||||
|
}, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TempUDPConn) Close() error {
|
||||||
|
c.Timer.SetTimeout(0)
|
||||||
|
c.AssociatedTCPConn.Close()
|
||||||
|
return c.PacketConn.Close()
|
||||||
|
}
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package socks
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
In the sock implementation of * ray, UDP authentication is flawed and can be bypassed.
|
|
||||||
Tracking a UDP connection may be a bit troublesome.
|
|
||||||
Here is a simple solution.
|
|
||||||
We create a filter, add remote IP to the pool when it try to establish a UDP connection with auth.
|
|
||||||
And drop UDP packets from unauthorized IP.
|
|
||||||
After discussion, we believe it is not necessary to add a timeout mechanism to this filter.
|
|
||||||
*/
|
|
||||||
|
|
||||||
type UDPFilter struct {
|
|
||||||
ips sync.Map
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *UDPFilter) Add(addr net.Addr) bool {
|
|
||||||
ip, _, _ := net.SplitHostPort(addr.String())
|
|
||||||
f.ips.Store(ip, true)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *UDPFilter) Check(addr net.Addr) bool {
|
|
||||||
ip, _, _ := net.SplitHostPort(addr.String())
|
|
||||||
_, ok := f.ips.Load(ip)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
|
|||||||
@@ -312,7 +312,6 @@ func (s *Server) handleUDPPayload(ctx context.Context, sessionPolicy policy.Sess
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := task.Run(ctx, requestDone); err != nil {
|
if err := task.Run(ctx, requestDone); err != nil {
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ func (v *Validator) GetByEmail(email string) *protocol.MemoryUser {
|
|||||||
|
|
||||||
// Get all users
|
// Get all users
|
||||||
func (v *Validator) GetAll() []*protocol.MemoryUser {
|
func (v *Validator) GetAll() []*protocol.MemoryUser {
|
||||||
var u = make([]*protocol.MemoryUser, 0, 100)
|
u := make([]*protocol.MemoryUser, 0, 100)
|
||||||
v.email.Range(func(key, value interface{}) bool {
|
v.email.Range(func(key, value interface{}) bool {
|
||||||
u = append(u, value.(*protocol.MemoryUser))
|
u = append(u, value.(*protocol.MemoryUser))
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ func (t *stackGVisor) Start() error {
|
|||||||
tcpForwarder := tcp.NewForwarder(ipStack, 0, 65535, func(r *tcp.ForwarderRequest) {
|
tcpForwarder := tcp.NewForwarder(ipStack, 0, 65535, func(r *tcp.ForwarderRequest) {
|
||||||
go func(r *tcp.ForwarderRequest) {
|
go func(r *tcp.ForwarderRequest) {
|
||||||
var wq waiter.Queue
|
var wq waiter.Queue
|
||||||
var id = r.ID()
|
id := r.ID()
|
||||||
|
|
||||||
// Perform a TCP three-way handshake.
|
// Perform a TCP three-way handshake.
|
||||||
ep, err := r.CreateEndpoint(&wq)
|
ep, err := r.CreateEndpoint(&wq)
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ func (e *LinkEndpoint) IsAttached() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *LinkEndpoint) Wait() {
|
func (e *LinkEndpoint) Wait() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *LinkEndpoint) ARPHardwareType() header.ARPHardwareType {
|
func (e *LinkEndpoint) ARPHardwareType() header.ARPHardwareType {
|
||||||
@@ -91,7 +90,6 @@ func (e *LinkEndpoint) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *LinkEndpoint) SetOnCloseAction(_ func()) {
|
func (e *LinkEndpoint) SetOnCloseAction(_ func()) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *LinkEndpoint) WritePackets(packetBufferList stack.PacketBufferList) (int, tcpip.Error) {
|
func (e *LinkEndpoint) WritePackets(packetBufferList stack.PacketBufferList) (int, tcpip.Error) {
|
||||||
|
|||||||
@@ -44,8 +44,10 @@ type DarwinTun struct {
|
|||||||
ownsFd bool // true for macOS (we created the fd), false for iOS (fd from system)
|
ownsFd bool // true for macOS (we created the fd), false for iOS (fd from system)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Tun = (*DarwinTun)(nil)
|
var (
|
||||||
var _ GVisorDevice = (*DarwinTun)(nil)
|
_ Tun = (*DarwinTun)(nil)
|
||||||
|
_ GVisorDevice = (*DarwinTun)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
func NewTun(options *Config) (Tun, error) {
|
func NewTun(options *Config) (Tun, error) {
|
||||||
// Check if fd is provided via environment (iOS mode)
|
// Check if fd is provided via environment (iOS mode)
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ import (
|
|||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DefaultTun struct {
|
type DefaultTun struct{}
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultTun implements Tun
|
// DefaultTun implements Tun
|
||||||
var _ Tun = (*DefaultTun)(nil)
|
var _ Tun = (*DefaultTun)(nil)
|
||||||
|
|||||||
@@ -27,8 +27,10 @@ type FreeBSDTun struct {
|
|||||||
mtu uint32
|
mtu uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Tun = (*FreeBSDTun)(nil)
|
var (
|
||||||
var _ GVisorDevice = (*FreeBSDTun)(nil)
|
_ Tun = (*FreeBSDTun)(nil)
|
||||||
|
_ GVisorDevice = (*FreeBSDTun)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
// NewTun builds new tun interface handler
|
// NewTun builds new tun interface handler
|
||||||
func NewTun(options *Config) (Tun, error) {
|
func NewTun(options *Config) (Tun, error) {
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func TestRequestSerialization(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addonsComparer := func(x, y *Addons) bool {
|
addonsComparer := func(x, y *Addons) bool {
|
||||||
return (x.Flow == y.Flow) && (cmp.Equal(x.Seed, y.Seed))
|
return (x.Flow == y.Flow) && cmp.Equal(x.Seed, y.Seed)
|
||||||
}
|
}
|
||||||
if r := cmp.Diff(actualAddons, expectedAddons, cmp.Comparer(addonsComparer)); r != "" {
|
if r := cmp.Diff(actualAddons, expectedAddons, cmp.Comparer(addonsComparer)); r != "" {
|
||||||
t.Error(r)
|
t.Error(r)
|
||||||
@@ -125,7 +125,7 @@ func TestMuxRequest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addonsComparer := func(x, y *Addons) bool {
|
addonsComparer := func(x, y *Addons) bool {
|
||||||
return (x.Flow == y.Flow) && (cmp.Equal(x.Seed, y.Seed))
|
return (x.Flow == y.Flow) && cmp.Equal(x.Seed, y.Seed)
|
||||||
}
|
}
|
||||||
if r := cmp.Diff(actualAddons, expectedAddons, cmp.Comparer(addonsComparer)); r != "" {
|
if r := cmp.Diff(actualAddons, expectedAddons, cmp.Comparer(addonsComparer)); r != "" {
|
||||||
t.Error(r)
|
t.Error(r)
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
var nfsKey []byte
|
var nfsKey []byte
|
||||||
var lastCTR cipher.Stream
|
var lastCTR cipher.Stream
|
||||||
for j, k := range i.NfsPKeys {
|
for j, k := range i.NfsPKeys {
|
||||||
var index = 32
|
index := 32
|
||||||
if k, ok := k.(*ecdh.PublicKey); ok {
|
if k, ok := k.(*ecdh.PublicKey); ok {
|
||||||
privateKey, _ := ecdh.X25519().GenerateKey(rand.Reader)
|
privateKey, _ := ecdh.X25519().GenerateKey(rand.Reader)
|
||||||
copy(relays, privateKey.PublicKey().Bytes())
|
copy(relays, privateKey.PublicKey().Bytes())
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user