diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 57e14693..678831c6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,14 +24,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Restore Cache - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 with: path: resources key: xray-geodat- - name: Update Geodat id: update - uses: nick-fields/retry@v2 + uses: nick-fields/retry@v3 with: timeout_minutes: 60 retry_wait_seconds: 60 @@ -57,7 +57,7 @@ jobs: done - name: Save Cache - uses: actions/cache/save@v3 + uses: actions/cache/save@v4 if: ${{ steps.update.outputs.unhit }} with: path: resources @@ -165,7 +165,7 @@ jobs: echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '1.21' check-latest: true @@ -173,39 +173,14 @@ jobs: - name: Get project dependencies run: go mod download - - name: Replace Custom to Commit ID - if: github.event_name != 'release' - run: | - ID=$(git rev-parse --short ${{ github.sha }}) - if [ "${{ github.event_name }}" == 'pull_request' ] - then - ID=$(git rev-parse --short ${{ github.event.pull_request.head.sha }}) - fi - sed -i '/build/ s/Custom/'$ID'/' ./core/core.go - - name: Build Xray run: | mkdir -p build_assets - go build -v -o build_assets/xray -trimpath -ldflags "-s -w -buildid=" ./main - - - name: Build background Xray on Windows - if: matrix.goos == 'windows' - run: | - go build -v -o build_assets/wxray.exe -trimpath -ldflags "-s -w -H windowsgui -buildid=" ./main - - - name: Build Mips softfloat Xray - if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle' - run: | - GOMIPS=softfloat go build -v -o build_assets/xray_softfloat -trimpath -ldflags "-s -w -buildid=" ./main - - - name: Rename Windows Xray - if: matrix.goos == 'windows' - run: | - cd ./build_assets || exit 1 - mv xray xray.exe + make + find . -maxdepth 1 -type f -regex '.*\(wxray\|xray\|xray_softfloat\)\(\|.exe\)' -exec mv {} ./build_assets/ \; - name: Restore Cache - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 with: path: resources key: xray-geodat- @@ -235,7 +210,7 @@ jobs: mv build_assets Xray-${{ env.ASSET_NAME }} - name: Upload files to Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Xray-${{ env.ASSET_NAME }} path: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0ab32cd4..59ee22c1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,14 +28,14 @@ jobs: os: [windows-latest, ubuntu-latest, macos-latest] steps: - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '1.21' check-latest: true - name: Checkout codebase uses: actions/checkout@v4 - name: Restore Cache - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 with: path: resources key: xray-geodat- diff --git a/.gitignore b/.gitignore index 9242f587..c77bc579 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ *.zip *.tar.gz xray +xray_softfloat mockgen vprotogen !infra/vprotogen/ @@ -26,3 +27,4 @@ errorgen !common/errors/errorgen/ *.dat .vscode +/build_assets diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..f69a9b2b --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ +NAME = xray + +VERSION=$(shell git describe --always --dirty) + +LDFLAGS = -X github.com/xtls/xray-core/core.build=$(VERSION) -s -w -buildid= +PARAMS = -trimpath -ldflags "$(LDFLAGS)" -v +MAIN = ./main +PREFIX ?= $(shell go env GOPATH) +ifeq ($(GOOS),windows) +OUTPUT = $(NAME).exe +ADDITION = go build -o w$(NAME).exe -trimpath -ldflags "-H windowsgui $(LDFLAGS)" -v $(MAIN) +else +OUTPUT = $(NAME) +endif +ifeq ($(shell echo "$(GOARCH)" | grep -Pq "(mips|mipsle)" && echo true),true) # +ADDITION = GOMIPS=softfloat go build -o $(NAME)_softfloat -trimpath -ldflags "$(LDFLAGS)" -v $(MAIN) +endif +.PHONY: clean + +build: + go build -o $(OUTPUT) $(PARAMS) $(MAIN) + $(ADDITION) + +install: + go build -o $(PREFIX)/bin/$(OUTPUT) $(PARAMS) $(MAIN) + +clean: + go clean -v -i $(PWD) + rm -f xray xray.exe wxray.exe xray_softfloat \ No newline at end of file diff --git a/README.md b/README.md index 7d84f044..467a83e9 100644 --- a/README.md +++ b/README.md @@ -23,17 +23,18 @@ - Linux Script - [XTLS/Xray-install](https://github.com/XTLS/Xray-install) - Docker + - Official: [ghcr.io/xtls/xray-core](https://ghcr.io/xtls/xray-core) - [iamybj/docker-xray](https://hub.docker.com/r/iamybj/docker-xray) - [teddysun/xray](https://hub.docker.com/r/teddysun/xray) - Web Panel - - [X-UI](https://github.com/FranzKafkaYu/x-ui), [X-UI-English](https://github.com/NidukaAkalanka/x-ui-english), [3X-UI](https://github.com/MHSanaei/3x-ui), [X-UI](https://github.com/alireza0/x-ui), [X-UI](https://github.com/diditra/x-ui) + - [X-UI-English](https://github.com/NidukaAkalanka/x-ui-english), [3X-UI](https://github.com/MHSanaei/3x-ui), [X-UI](https://github.com/alireza0/x-ui), [X-UI](https://github.com/diditra/x-ui) - [Xray-UI](https://github.com/qist/xray-ui), [X-UI](https://github.com/sing-web/x-ui) - [Hiddify](https://github.com/hiddify/hiddify-config) - [Marzban](https://github.com/Gozargah/Marzban) - [Libertea](https://github.com/VZiChoushaDui/Libertea) - One Click - [Xray-REALITY](https://github.com/zxcvos/Xray-script), [xray-reality](https://github.com/sajjaddg/xray-reality), [reality-ezpz](https://github.com/aleskxyz/reality-ezpz) - - [Xray-script](https://github.com/kirin10000/Xray-script), [Xray_bash_onekey](https://github.com/hello-yunshu/Xray_bash_onekey), [XTool](https://github.com/LordPenguin666/XTool) + - [Xray_bash_onekey](https://github.com/hello-yunshu/Xray_bash_onekey), [XTool](https://github.com/LordPenguin666/XTool) - [v2ray-agent](https://github.com/mack-a/v2ray-agent), [Xray_onekey](https://github.com/wulabing/Xray_onekey), [ProxySU](https://github.com/proxysu/ProxySU) - Magisk - [Xray4Magisk](https://github.com/Asterisk4Magisk/Xray4Magisk) @@ -56,6 +57,7 @@ - [REALITY (English)](https://cscot.pages.dev/2023/03/02/Xray-REALITY-tutorial/) - [XTLS-Iran-Reality (English)](https://github.com/SasukeFreestyle/XTLS-Iran-Reality) - [Xray REALITY with 'steal oneself' (English)](https://computerscot.github.io/vless-xtls-utls-reality-steal-oneself.html) + - [Xray with WireGuard inbound (English)](https://g800.pages.dev/wireguard) ## GUI Clients @@ -123,16 +125,23 @@ ## Compilation -### Windows +### Windows (PowerShell) -```bash +```powershell +$env:CGO_ENABLED=0 go build -o xray.exe -trimpath -ldflags "-s -w -buildid=" ./main ``` ### Linux / macOS ```bash -go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main +CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main +``` + +### Reproducible Releases + +```bash +make ``` ## Stargazers over time diff --git a/app/dns/dns.go b/app/dns/dns.go index 6efcb825..3b173677 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -215,7 +215,8 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) { newError("failed to lookup ip for domain ", domain, " at server ", client.Name()).Base(err).WriteToLog() errs = append(errs, err) } - if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch && err != dns.ErrEmptyResponse { + // 5 for RcodeRefused in miekg/dns, hardcode to reduce binary size + if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch && err != dns.ErrEmptyResponse && dns.RCodeFromError(err) != 5 { return nil, err } } diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index d290b016..2df2b2c3 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -229,6 +229,10 @@ func (h *Handler) Address() net.Address { return h.senderSettings.Via.AsAddress() } +func (h *Handler) DestIpAddress() net.IP { + return internet.DestIpAddress() +} + // Dial implements internet.Dialer. func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connection, error) { if h.senderSettings != nil { diff --git a/app/proxyman/outbound/handler_test.go b/app/proxyman/outbound/handler_test.go index c5afea70..e5b67308 100644 --- a/app/proxyman/outbound/handler_test.go +++ b/app/proxyman/outbound/handler_test.go @@ -2,9 +2,14 @@ package outbound_test import ( "context" + "fmt" + "sync" + "sync/atomic" "testing" + "time" "github.com/xtls/xray-core/app/policy" + "github.com/xtls/xray-core/app/proxyman" . "github.com/xtls/xray-core/app/proxyman/outbound" "github.com/xtls/xray-core/app/stats" "github.com/xtls/xray-core/common/net" @@ -78,3 +83,91 @@ func TestOutboundWithStatCounter(t *testing.T) { t.Errorf("Expected conn to be CounterConnection") } } + +func TestTagsCache(t *testing.T) { + + test_duration := 10 * time.Second + threads_num := 50 + delay := 10 * time.Millisecond + tags_prefix := "node" + + tags := sync.Map{} + counter := atomic.Uint64{} + + ohm, err := New(context.Background(), &proxyman.OutboundConfig{}) + if err != nil { + t.Error("failed to create outbound handler manager") + } + config := &core.Config{ + App: []*serial.TypedMessage{}, + } + v, _ := core.New(config) + v.AddFeature(ohm) + ctx := context.WithValue(context.Background(), xrayKey, v) + + stop_add_rm := false + wg_add_rm := sync.WaitGroup{} + addHandlers := func() { + defer wg_add_rm.Done() + for !stop_add_rm { + time.Sleep(delay) + idx := counter.Add(1) + tag := fmt.Sprintf("%s%d", tags_prefix, idx) + cfg := &core.OutboundHandlerConfig{ + Tag: tag, + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + } + if h, err := NewHandler(ctx, cfg); err == nil { + if err := ohm.AddHandler(ctx, h); err == nil { + // t.Log("add handler:", tag) + tags.Store(tag, nil) + } else { + t.Error("failed to add handler:", tag) + } + } else { + t.Error("failed to create handler:", tag) + } + } + } + + rmHandlers := func() { + defer wg_add_rm.Done() + for !stop_add_rm { + time.Sleep(delay) + tags.Range(func(key interface{}, value interface{}) bool { + if _, ok := tags.LoadAndDelete(key); ok { + // t.Log("remove handler:", key) + ohm.RemoveHandler(ctx, key.(string)) + return false + } + return true + }) + } + } + + selectors := []string{tags_prefix} + wg_get := sync.WaitGroup{} + stop_get := false + getTags := func() { + defer wg_get.Done() + for !stop_get { + time.Sleep(delay) + _ = ohm.Select(selectors) + // t.Logf("get tags: %v", tag) + } + } + + for i := 0; i < threads_num; i++ { + wg_add_rm.Add(2) + go rmHandlers() + go addHandlers() + wg_get.Add(1) + go getTags() + } + + time.Sleep(test_duration) + stop_add_rm = true + wg_add_rm.Wait() + stop_get = true + wg_get.Wait() +} diff --git a/app/proxyman/outbound/outbound.go b/app/proxyman/outbound/outbound.go index 8ebcde17..40f32965 100644 --- a/app/proxyman/outbound/outbound.go +++ b/app/proxyman/outbound/outbound.go @@ -4,6 +4,7 @@ package outbound import ( "context" + "sort" "strings" "sync" @@ -21,12 +22,14 @@ type Manager struct { taggedHandler map[string]outbound.Handler untaggedHandlers []outbound.Handler running bool + tagsCache *sync.Map } // New creates a new Manager. func New(ctx context.Context, config *proxyman.OutboundConfig) (*Manager, error) { m := &Manager{ taggedHandler: make(map[string]outbound.Handler), + tagsCache: &sync.Map{}, } return m, nil } @@ -103,6 +106,8 @@ func (m *Manager) AddHandler(ctx context.Context, handler outbound.Handler) erro m.access.Lock() defer m.access.Unlock() + m.tagsCache = &sync.Map{} + if m.defaultHandler == nil { m.defaultHandler = handler } @@ -132,6 +137,8 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { m.access.Lock() defer m.access.Unlock() + m.tagsCache = &sync.Map{} + delete(m.taggedHandler, tag) if m.defaultHandler != nil && m.defaultHandler.Tag() == tag { m.defaultHandler = nil @@ -142,24 +149,29 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { // Select implements outbound.HandlerSelector. func (m *Manager) Select(selectors []string) []string { + + key := strings.Join(selectors, ",") + if cache, ok := m.tagsCache.Load(key); ok { + return cache.([]string) + } + m.access.RLock() defer m.access.RUnlock() tags := make([]string, 0, len(selectors)) for tag := range m.taggedHandler { - match := false for _, selector := range selectors { if strings.HasPrefix(tag, selector) { - match = true + tags = append(tags, tag) break } } - if match { - tags = append(tags, tag) - } } + sort.Strings(tags) + m.tagsCache.Store(key, tags) + return tags } diff --git a/app/router/balancing.go b/app/router/balancing.go index 50b84388..458e6838 100644 --- a/app/router/balancing.go +++ b/app/router/balancing.go @@ -2,6 +2,7 @@ package router import ( "context" + sync "sync" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/features/extension" @@ -23,6 +24,24 @@ func (s *RandomStrategy) PickOutbound(tags []string) string { return tags[dice.Roll(n)] } +type RoundRobinStrategy struct { + mu sync.Mutex + index int +} + +func (s *RoundRobinStrategy) PickOutbound(tags []string) string { + n := len(tags) + if n == 0 { + panic("0 tags") + } + + s.mu.Lock() + defer s.mu.Unlock() + tag := tags[s.index%n] + s.index = (s.index + 1) % n + return tag +} + type Balancer struct { selectors []string strategy BalancingStrategy diff --git a/app/router/config.go b/app/router/config.go index f50f02a1..5dc32fa8 100644 --- a/app/router/config.go +++ b/app/router/config.go @@ -129,6 +129,12 @@ func (br *BalancingRule) Build(ohm outbound.Manager) (*Balancer, error) { strategy: &LeastPingStrategy{}, ohm: ohm, }, nil + case "roundRobin": + return &Balancer{ + selectors: br.OutboundSelector, + strategy: &RoundRobinStrategy{}, + ohm: ohm, + }, nil case "random": fallthrough default: diff --git a/common/log/logger.go b/common/log/logger.go index 79507964..d964a212 100644 --- a/common/log/logger.go +++ b/common/log/logger.go @@ -27,6 +27,11 @@ type generalLogger struct { done *done.Instance } +type serverityLogger struct { + inner *generalLogger + logLevel Severity +} + // NewLogger returns a generic log handler that can handle all type of messages. func NewLogger(logWriterCreator WriterCreator) Handler { return &generalLogger{ @@ -37,6 +42,32 @@ func NewLogger(logWriterCreator WriterCreator) Handler { } } +func ReplaceWithSeverityLogger(serverity Severity) { + w := CreateStdoutLogWriter() + g := &generalLogger{ + creator: w, + buffer: make(chan Message, 16), + access: semaphore.New(1), + done: done.New(), + } + s := &serverityLogger{ + inner: g, + logLevel: serverity, + } + RegisterHandler(s) +} + +func (l *serverityLogger) Handle(msg Message) { + switch msg := msg.(type) { + case *GeneralMessage: + if msg.Severity <= l.logLevel { + l.inner.Handle(msg) + } + default: + l.inner.Handle(msg) + } +} + func (l *generalLogger) run() { defer l.access.Signal() @@ -67,6 +98,7 @@ func (l *generalLogger) run() { } func (l *generalLogger) Handle(msg Message) { + select { case l.buffer <- msg: default: diff --git a/common/net/destination.go b/common/net/destination.go index 055395e9..90f8298b 100644 --- a/common/net/destination.go +++ b/common/net/destination.go @@ -97,6 +97,35 @@ func (d Destination) NetAddr() string { return addr } +// RawNetAddr converts a net.Addr from its Destination presentation. +func (d Destination) RawNetAddr() net.Addr { + var addr net.Addr + switch d.Network { + case Network_TCP: + if d.Address.Family().IsIP() { + addr = &net.TCPAddr{ + IP: d.Address.IP(), + Port: int(d.Port), + } + } + case Network_UDP: + if d.Address.Family().IsIP() { + addr = &net.UDPAddr{ + IP: d.Address.IP(), + Port: int(d.Port), + } + } + case Network_UNIX: + if d.Address.Family().IsDomain() { + addr = &net.UnixAddr{ + Name: d.Address.String(), + Net: d.Network.SystemString(), + } + } + } + return addr +} + // String returns the strings form of this Destination. func (d Destination) String() string { prefix := "unknown:" diff --git a/common/ocsp/ocsp.go b/common/ocsp/ocsp.go index 02140c0d..b2c0bc58 100644 --- a/common/ocsp/ocsp.go +++ b/common/ocsp/ocsp.go @@ -28,6 +28,9 @@ func GetOCSPStapling(cert [][]byte, path string) ([]byte, error) { ocspData, err := GetOCSPForFile(path) if err != nil { ocspData, err = GetOCSPForCert(cert) + if err != nil { + return nil, err + } if !CheckOCSPFileIsNotExist(path) { err = os.Remove(path) if err != nil { diff --git a/common/reflect/marshal.go b/common/reflect/marshal.go new file mode 100644 index 00000000..736afc01 --- /dev/null +++ b/common/reflect/marshal.go @@ -0,0 +1,176 @@ +package reflect + +import ( + "encoding/json" + "reflect" + + cserial "github.com/xtls/xray-core/common/serial" +) + +func MarshalToJson(v interface{}) (string, bool) { + if itf := marshalInterface(v, true); itf != nil { + if b, err := json.MarshalIndent(itf, "", " "); err == nil { + return string(b[:]), true + } + } + return "", false +} + +func marshalTypedMessage(v *cserial.TypedMessage, ignoreNullValue bool) interface{} { + if v == nil { + return nil + } + tmsg, err := v.GetInstance() + if err != nil { + return nil + } + r := marshalInterface(tmsg, ignoreNullValue) + if msg, ok := r.(map[string]interface{}); ok { + msg["_TypedMessage_"] = v.Type + } + return r +} + +func marshalSlice(v reflect.Value, ignoreNullValue bool) interface{} { + r := make([]interface{}, 0) + for i := 0; i < v.Len(); i++ { + rv := v.Index(i) + if rv.CanInterface() { + value := rv.Interface() + r = append(r, marshalInterface(value, ignoreNullValue)) + } + } + return r +} + +func marshalStruct(v reflect.Value, ignoreNullValue bool) interface{} { + r := make(map[string]interface{}) + t := v.Type() + for i := 0; i < v.NumField(); i++ { + rv := v.Field(i) + if rv.CanInterface() { + ft := t.Field(i) + name := ft.Name + value := rv.Interface() + tv := marshalInterface(value, ignoreNullValue) + if tv != nil || !ignoreNullValue { + r[name] = tv + } + } + } + return r +} + +func marshalMap(v reflect.Value, ignoreNullValue bool) interface{} { + // policy.level is map[uint32] *struct + kt := v.Type().Key() + vt := reflect.TypeOf((*interface{})(nil)) + mt := reflect.MapOf(kt, vt) + r := reflect.MakeMap(mt) + for _, key := range v.MapKeys() { + rv := v.MapIndex(key) + if rv.CanInterface() { + iv := rv.Interface() + tv := marshalInterface(iv, ignoreNullValue) + if tv != nil || !ignoreNullValue { + r.SetMapIndex(key, reflect.ValueOf(&tv)) + } + } + } + return r.Interface() +} + +func marshalIString(v interface{}) (r string, ok bool) { + defer func() { + if err := recover(); err != nil { + r = "" + ok = false + } + }() + + if iStringFn, ok := v.(interface{ String() string }); ok { + return iStringFn.String(), true + } + return "", false +} + +func marshalKnownType(v interface{}, ignoreNullValue bool) (interface{}, bool) { + switch ty := v.(type) { + case cserial.TypedMessage: + return marshalTypedMessage(&ty, ignoreNullValue), true + case *cserial.TypedMessage: + return marshalTypedMessage(ty, ignoreNullValue), true + case map[string]json.RawMessage: + return ty, true + case []json.RawMessage: + return ty, true + case *json.RawMessage: + return ty, true + case json.RawMessage: + return ty, true + default: + return nil, false + } +} + +func isValueKind(kind reflect.Kind) bool { + switch kind { + case reflect.Bool, + reflect.Int, + reflect.Int8, + reflect.Int16, + reflect.Int32, + reflect.Int64, + reflect.Uint, + reflect.Uint8, + reflect.Uint16, + reflect.Uint32, + reflect.Uint64, + reflect.Uintptr, + reflect.Float32, + reflect.Float64, + reflect.Complex64, + reflect.Complex128, + reflect.String: + return true + default: + return false + } +} + +func marshalInterface(v interface{}, ignoreNullValue bool) interface{} { + + if r, ok := marshalKnownType(v, ignoreNullValue); ok { + return r + } + + rv := reflect.ValueOf(v) + if rv.Kind() == reflect.Ptr { + rv = rv.Elem() + } + k := rv.Kind() + if k == reflect.Invalid { + return nil + } + if isValueKind(k) { + return v + } + + switch k { + case reflect.Struct: + return marshalStruct(rv, ignoreNullValue) + case reflect.Slice: + return marshalSlice(rv, ignoreNullValue) + case reflect.Array: + return marshalSlice(rv, ignoreNullValue) + case reflect.Map: + return marshalMap(rv, ignoreNullValue) + default: + break + } + + if str, ok := marshalIString(v); ok { + return str + } + return nil +} diff --git a/common/reflect/marshal_test.go b/common/reflect/marshal_test.go new file mode 100644 index 00000000..377ad4e9 --- /dev/null +++ b/common/reflect/marshal_test.go @@ -0,0 +1,187 @@ +package reflect_test + +import ( + "bytes" + "encoding/json" + "strings" + "testing" + + . "github.com/xtls/xray-core/common/reflect" + cserial "github.com/xtls/xray-core/common/serial" + iserial "github.com/xtls/xray-core/infra/conf/serial" +) + +func TestMashalStruct(t *testing.T) { + type Foo = struct { + N int `json:"n"` + Np *int `json:"np"` + S string `json:"s"` + Arr *[]map[string]map[string]string `json:"arr"` + } + + n := 1 + np := &n + arr := make([]map[string]map[string]string, 0) + m1 := make(map[string]map[string]string, 0) + m2 := make(map[string]string, 0) + m2["hello"] = "world" + m1["foo"] = m2 + + arr = append(arr, m1) + + f1 := Foo{ + N: n, + Np: np, + S: "hello", + Arr: &arr, + } + + s, ok1 := MarshalToJson(f1) + sp, ok2 := MarshalToJson(&f1) + + if !ok1 || !ok2 || s != sp { + t.Error("marshal failed") + } + + f2 := Foo{} + if json.Unmarshal([]byte(s), &f2) != nil { + t.Error("json unmarshal failed") + } + + v := (*f2.Arr)[0]["foo"]["hello"] + + if f1.N != f2.N || *(f1.Np) != *(f2.Np) || f1.S != f2.S || v != "world" { + t.Error("f1 not equal to f2") + } +} + +func TestMarshalConfigJson(t *testing.T) { + + buf := bytes.NewBufferString(getConfig()) + config, err := iserial.DecodeJSONConfig(buf) + if err != nil { + t.Error("decode JSON config failed") + } + + bc, err := config.Build() + if err != nil { + t.Error("build core config failed") + } + + tmsg := cserial.ToTypedMessage(bc) + tc, ok := MarshalToJson(tmsg) + if !ok { + t.Error("marshal config failed") + } + + // t.Log(tc) + + keywords := []string{ + "4784f9b8-a879-4fec-9718-ebddefa47750", + "bing.com", + "DomainStrategy", + "InboundTag", + "Level", + "Stats", + "UserDownlink", + "UserUplink", + "System", + "InboundDownlink", + "OutboundUplink", + } + for _, kw := range keywords { + if !strings.Contains(tc, kw) { + t.Error("marshaled config error") + } + } +} + +func getConfig() string { + return `{ + "log": { + "loglevel": "debug" + }, + "stats": {}, + "policy": { + "levels": { + "0": { + "statsUserUplink": true, + "statsUserDownlink": true + } + }, + "system": { + "statsInboundUplink": true, + "statsInboundDownlink": true, + "statsOutboundUplink": true, + "statsOutboundDownlink": true + } + }, + "inbounds": [ + { + "tag": "agentin", + "protocol": "http", + "port": 8080, + "listen": "127.0.0.1", + "settings": {} + }, + { + "listen": "127.0.0.1", + "port": 10085, + "protocol": "dokodemo-door", + "settings": { + "address": "127.0.0.1" + }, + "tag": "api-in" + } + ], + "api": { + "tag": "api", + "services": [ + "HandlerService", + "StatsService" + ] + }, + "routing": { + "rules": [ + { + "inboundTag": [ + "api-in" + ], + "outboundTag": "api", + "type": "field" + } + ], + "domainStrategy": "AsIs" + }, + "outbounds": [ + { + "protocol": "vless", + "settings": { + "vnext": [ + { + "address": "1.2.3.4", + "port": 1234, + "users": [ + { + "id": "4784f9b8-a879-4fec-9718-ebddefa47750", + "encryption": "none" + } + ] + } + ] + }, + "tag": "agentout", + "streamSettings": { + "network": "ws", + "security": "none", + "wsSettings": { + "path": "/?ed=2048", + "headers": { + "Host": "bing.com" + } + } + } + } + ] + }` +} diff --git a/common/singbridge/dialer.go b/common/singbridge/dialer.go index dfc128d8..896c97fe 100644 --- a/common/singbridge/dialer.go +++ b/common/singbridge/dialer.go @@ -43,7 +43,7 @@ func NewOutboundDialer(outbound proxy.Outbound, dialer internet.Dialer) *XrayOut } func (d *XrayOutboundDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - ctx = session.ContextWithOutbound(context.Background(), &session.Outbound{ + ctx = session.ContextWithOutbound(ctx, &session.Outbound{ Target: ToDestination(destination, ToNetwork(network)), }) opts := []pipe.Option{pipe.WithSizeLimit(64 * 1024)} diff --git a/common/xudp/xudp.go b/common/xudp/xudp.go index af18119f..566ba749 100644 --- a/common/xudp/xudp.go +++ b/common/xudp/xudp.go @@ -6,7 +6,9 @@ import ( "encoding/base64" "fmt" "io" + "strconv" "strings" + "time" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/net" @@ -32,13 +34,16 @@ func init() { if strings.ToLower(platform.NewEnvFlag(platform.XUDPLog).GetValue(func() string { return "" })) == "true" { Show = true } - if raw := platform.NewEnvFlag(platform.XUDPBaseKey).GetValue(func() string { return "" }); raw != "" { - if BaseKey, _ = base64.RawURLEncoding.DecodeString(raw); len(BaseKey) == 32 { - return - } - panic(platform.XUDPBaseKey + ": invalid value: " + raw) - } rand.Read(BaseKey) + go func() { + time.Sleep(100 * time.Millisecond) // this is not nice, but need to give some time for Android to setup ENV + if raw := platform.NewEnvFlag(platform.XUDPBaseKey).GetValue(func() string { return "" }); raw != "" { + if BaseKey, _ = base64.RawURLEncoding.DecodeString(raw); len(BaseKey) == 32 { + return + } + panic(platform.XUDPBaseKey + ": invalid value (BaseKey must be 32 bytes): " + raw + " len " + strconv.Itoa(len(BaseKey))) + } + }() } func GetGlobalID(ctx context.Context) (globalID [8]byte) { diff --git a/core/config.go b/core/config.go index f4077449..1aa21f5f 100644 --- a/core/config.go +++ b/core/config.go @@ -24,10 +24,14 @@ type ConfigLoader func(input interface{}) (*Config, error) // ConfigBuilder is a builder to build core.Config from filenames and formats type ConfigBuilder func(files []string, formats []string) (*Config, error) +// ConfigMerger merge multiple json configs into on config +type ConfigsMerger func(files []string, formats []string) (string, error) + var ( configLoaderByName = make(map[string]*ConfigFormat) configLoaderByExt = make(map[string]*ConfigFormat) ConfigBuilderForFiles ConfigBuilder + ConfigMergedFormFiles ConfigsMerger ) // RegisterConfigLoader add a new ConfigLoader. @@ -49,6 +53,23 @@ func RegisterConfigLoader(format *ConfigFormat) error { return nil } +func GetMergedConfig(args cmdarg.Arg) (string, error) { + files := make([]string, 0) + formats := make([]string, 0) + supported := []string{"json", "yaml", "toml"} + for _, file := range args { + format := getFormat(file) + for _, s := range supported { + if s == format { + files = append(files, file) + formats = append(formats, format) + break + } + } + } + return ConfigMergedFormFiles(files, formats) +} + func GetFormatByExtension(ext string) string { switch strings.ToLower(ext) { case "pb", "protobuf": diff --git a/core/core.go b/core/core.go index d11fa273..09a591f2 100644 --- a/core/core.go +++ b/core/core.go @@ -21,7 +21,7 @@ import ( var ( Version_x byte = 1 Version_y byte = 8 - Version_z byte = 6 + Version_z byte = 7 ) var ( diff --git a/go.mod b/go.mod index c0e7bfbe..9dc7a917 100644 --- a/go.mod +++ b/go.mod @@ -7,26 +7,26 @@ require ( github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.6.0 github.com/gorilla/websocket v1.5.1 - github.com/miekg/dns v1.1.57 + github.com/miekg/dns v1.1.58 github.com/pelletier/go-toml v1.9.5 github.com/pires/go-proxyproto v0.7.0 - github.com/quic-go/quic-go v0.40.0 - github.com/refraction-networking/utls v1.5.4 - github.com/sagernet/sing v0.2.17 - github.com/sagernet/sing-shadowsocks v0.2.5 + github.com/quic-go/quic-go v0.41.0 + github.com/refraction-networking/utls v1.6.2 + github.com/sagernet/sing v0.3.0 + github.com/sagernet/sing-shadowsocks v0.2.6 github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb github.com/stretchr/testify v1.8.4 github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 - go4.org/netipx v0.0.0-20230824141953-6213f710f925 - golang.org/x/crypto v0.15.0 - golang.org/x/net v0.18.0 - golang.org/x/sync v0.5.0 - golang.org/x/sys v0.14.0 - golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb - google.golang.org/grpc v1.59.0 - google.golang.org/protobuf v1.31.0 + go4.org/netipx v0.0.0-20231129151722-fdeea329fbba + golang.org/x/crypto v0.18.0 + golang.org/x/net v0.20.0 + golang.org/x/sync v0.6.0 + golang.org/x/sys v0.16.0 + golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 + google.golang.org/grpc v1.61.0 + google.golang.org/protobuf v1.32.0 gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b h12.io/socks v1.0.3 lukechampine.com/blake3 v1.2.1 @@ -34,30 +34,28 @@ require ( require ( github.com/andybalholm/brotli v1.0.6 // indirect - github.com/cloudflare/circl v1.3.6 // indirect + github.com/cloudflare/circl v1.3.7 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect github.com/francoispqt/gojay v1.2.13 // indirect - github.com/gaukas/godicttls v0.0.4 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect - github.com/klauspost/compress v1.17.2 // indirect + github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 // indirect + github.com/klauspost/compress v1.17.4 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect - github.com/onsi/ginkgo/v2 v2.13.1 // indirect + github.com/onsi/ginkgo/v2 v2.13.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect - github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect - go.uber.org/mock v0.3.0 // indirect - golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + github.com/vishvananda/netns v0.0.4 // indirect + go.uber.org/mock v0.4.0 // indirect + golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.4.0 // indirect - golang.org/x/tools v0.15.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.17.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 1239d8f0..31542da8 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg= -github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -29,8 +29,6 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= -github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4= github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= @@ -63,8 +61,8 @@ github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+u github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk= -github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 h1:dHLYa5D8/Ta0aLR2XcPsrkpAgGeFs6thhMcQK0oQ0n8= +github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -78,8 +76,8 @@ github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= -github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= @@ -92,14 +90,14 @@ github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= -github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= +github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= +github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/onsi/ginkgo/v2 v2.13.1 h1:LNGfMbR2OVGBfXjvRZIZ2YCTQdGKtPLvuI1rMCCj3OU= -github.com/onsi/ginkgo/v2 v2.13.1/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= +github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs= +github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= @@ -116,19 +114,17 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= -github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= -github.com/quic-go/quic-go v0.40.0 h1:GYd1iznlKm7dpHD7pOVpUvItgMPo/jrMgDWZhMCecqw= -github.com/quic-go/quic-go v0.40.0/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c= -github.com/refraction-networking/utls v1.5.4 h1:9k6EO2b8TaOGsQ7Pl7p9w6PUhx18/ZCeT0WNTZ7Uw4o= -github.com/refraction-networking/utls v1.5.4/go.mod h1:SPuDbBmgLGp8s+HLNc83FuavwZCFoMmExj+ltUHiHUw= +github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k= +github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA= +github.com/refraction-networking/utls v1.6.2 h1:iTeeGY0o6nMNcGyirxkD5bFIsVctP5InGZ3E0HrzS7k= +github.com/refraction-networking/utls v1.6.2/go.mod h1:yil9+7qSl+gBwJqztoQseO6Pr3h62pQoY1lXiNR/FPs= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/sagernet/sing v0.2.17 h1:vMPKb3MV0Aa5ws4dCJkRI8XEjrsUcDn810czd0FwmzI= -github.com/sagernet/sing v0.2.17/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= -github.com/sagernet/sing-shadowsocks v0.2.5 h1:qxIttos4xu6ii7MTVJYA8EFQR7Q3KG6xMqmLJIFtBaY= -github.com/sagernet/sing-shadowsocks v0.2.5/go.mod h1:MGWGkcU2xW2G2mfArT9/QqpVLOGU+dBaahZCtPHdt7A= +github.com/sagernet/sing v0.3.0 h1:PIDVFZHnQAAYRL1UYqNM+0k5s8f/tb1lUW6UDcQiOc8= +github.com/sagernet/sing v0.3.0/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g= +github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s= +github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM= github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U= github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -168,27 +164,28 @@ github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49u github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 h1:tkMT5pTye+1NlKIXETU78NXw0fyjnaNHmJyyLyzw8+U= github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3/go.mod h1:cAAsePK2e15YDAMJNyOpGYEWNe4sIghTY7gpz4cX/Ik= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= +github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 h1:capMfFYRgH9BCLd6A3Er/cH3A9Nz3CU2KwxwOQZIePI= github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= -go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= -go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ= -go4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= +go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= +go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= +golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -206,8 +203,8 @@ golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn 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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -219,8 +216,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/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.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -233,8 +230,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -243,8 +240,8 @@ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY= -golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -252,16 +249,16 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= -golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= -golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb h1:c5tyN8sSp8jSDxdCCDXVOpJwYXXhmTkNMt+g0zTSOic= -golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= +golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= +golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= @@ -274,18 +271,18 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= +google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/infra/conf/freedom.go b/infra/conf/freedom.go index dd812db6..90c99b37 100644 --- a/infra/conf/freedom.go +++ b/infra/conf/freedom.go @@ -17,6 +17,7 @@ type FreedomConfig struct { Redirect string `json:"redirect"` UserLevel uint32 `json:"userLevel"` Fragment *Fragment `json:"fragment"` + ProxyProtocol uint32 `json:"proxyProtocol"` } type Fragment struct { @@ -165,5 +166,8 @@ func (c *FreedomConfig) Build() (proto.Message, error) { config.DestinationOverride.Server.Address = v2net.NewIPOrDomain(v2net.ParseAddress(host)) } } + if c.ProxyProtocol > 0 && c.ProxyProtocol <= 2 { + config.ProxyProtocol = c.ProxyProtocol + } return config, nil } diff --git a/infra/conf/router.go b/infra/conf/router.go index a9f57cd6..a3285e85 100644 --- a/infra/conf/router.go +++ b/infra/conf/router.go @@ -43,6 +43,8 @@ func (r *BalancingRule) Build() (*router.BalancingRule, error) { strategy = strategyRandom case strategyLeastPing: strategy = "leastPing" + case strategyRoundRobin: + strategy = "roundRobin" default: return nil, newError("unknown balancing strategy: " + r.Strategy.Type) } @@ -636,7 +638,7 @@ func ParseRule(msg json.RawMessage) (*router.RoutingRule, error) { if err != nil { return nil, newError("invalid router rule").Base(err) } - if strings.EqualFold(rawRule.Type, "field") { + if rawRule.Type == "" || strings.EqualFold(rawRule.Type, "field") { fieldrule, err := parseFieldRule(msg) if err != nil { return nil, newError("invalid field rule").Base(err) diff --git a/infra/conf/router_strategy.go b/infra/conf/router_strategy.go index b8536330..ef2abc26 100644 --- a/infra/conf/router_strategy.go +++ b/infra/conf/router_strategy.go @@ -1,6 +1,7 @@ package conf const ( - strategyRandom string = "random" - strategyLeastPing string = "leastping" + strategyRandom string = "random" + strategyLeastPing string = "leastping" + strategyRoundRobin string = "roundrobin" ) diff --git a/infra/conf/serial/builder.go b/infra/conf/serial/builder.go index 443dbdb0..88ea9e65 100644 --- a/infra/conf/serial/builder.go +++ b/infra/conf/serial/builder.go @@ -3,12 +3,25 @@ package serial import ( "io" + creflect "github.com/xtls/xray-core/common/reflect" "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/infra/conf" "github.com/xtls/xray-core/main/confloader" ) -func BuildConfig(files []string, formats []string) (*core.Config, error) { +func MergeConfigFromFiles(files []string, formats []string) (string, error) { + c, err := mergeConfigs(files, formats) + if err != nil { + return "", err + } + + if j, ok := creflect.MarshalToJson(c); ok { + return j, nil + } + return "", newError("marshal to json failed.").AtError() +} + +func mergeConfigs(files []string, formats []string) (*conf.Config, error) { cf := &conf.Config{} for i, file := range files { newError("Reading config: ", file).AtInfo().WriteToLog() @@ -26,7 +39,15 @@ func BuildConfig(files []string, formats []string) (*core.Config, error) { } cf.Override(c, file) } - return cf.Build() + return cf, nil +} + +func BuildConfig(files []string, formats []string) (*core.Config, error) { + config, err := mergeConfigs(files, formats) + if err != nil { + return nil, err + } + return config.Build() } type readerDecoder func(io.Reader) (*conf.Config, error) @@ -39,4 +60,5 @@ func init() { ReaderDecoderByFormat["toml"] = DecodeTOMLConfig core.ConfigBuilderForFiles = BuildConfig + core.ConfigMergedFormFiles = MergeConfigFromFiles } diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index e1471bde..8c113e92 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -357,6 +357,7 @@ type TLSConfig struct { RejectUnknownSNI bool `json:"rejectUnknownSni"` PinnedPeerCertificateChainSha256 *[]string `json:"pinnedPeerCertificateChainSha256"` PinnedPeerCertificatePublicKeySha256 *[]string `json:"pinnedPeerCertificatePublicKeySha256"` + MasterKeyLog string `json:"masterKeyLog"` } // Build implements Buildable. @@ -412,11 +413,14 @@ func (c *TLSConfig) Build() (proto.Message, error) { } } + config.MasterKeyLog = c.MasterKeyLog + return config, nil } type REALITYConfig struct { Show bool `json:"show"` + MasterKeyLog string `json:"masterKeyLog"` Dest json.RawMessage `json:"dest"` Type string `json:"type"` Xver uint64 `json:"xver"` @@ -437,6 +441,7 @@ type REALITYConfig struct { func (c *REALITYConfig) Build() (proto.Message, error) { config := new(reality.Config) config.Show = c.Show + config.MasterKeyLog = c.MasterKeyLog var err error if c.Dest != nil { var i uint16 diff --git a/infra/conf/trojan.go b/infra/conf/trojan.go index 2cd1e520..6bc2385f 100644 --- a/infra/conf/trojan.go +++ b/infra/conf/trojan.go @@ -2,8 +2,10 @@ package conf import ( "encoding/json" + "path/filepath" "runtime" "strconv" + "strings" "syscall" "github.com/xtls/xray-core/common/net" @@ -147,22 +149,19 @@ func (c *TrojanServerConfig) Build() (proto.Message, error) { if fb.Type == "" && fb.Dest != "" { if fb.Dest == "serve-ws-none" { fb.Type = "serve" + } else if filepath.IsAbs(fb.Dest) || fb.Dest[0] == '@' { + fb.Type = "unix" + if strings.HasPrefix(fb.Dest, "@@") && (runtime.GOOS == "linux" || runtime.GOOS == "android") { + fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy + copy(fullAddr, fb.Dest[1:]) + fb.Dest = string(fullAddr) + } } else { - switch fb.Dest[0] { - case '@', '/': - fb.Type = "unix" - if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && (runtime.GOOS == "linux" || runtime.GOOS == "android") { - fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy - copy(fullAddr, fb.Dest[1:]) - fb.Dest = string(fullAddr) - } - default: - if _, err := strconv.Atoi(fb.Dest); err == nil { - fb.Dest = "127.0.0.1:" + fb.Dest - } - if _, _, err := net.SplitHostPort(fb.Dest); err == nil { - fb.Type = "tcp" - } + if _, err := strconv.Atoi(fb.Dest); err == nil { + fb.Dest = "127.0.0.1:" + fb.Dest + } + if _, _, err := net.SplitHostPort(fb.Dest); err == nil { + fb.Type = "tcp" } } } diff --git a/infra/conf/vless.go b/infra/conf/vless.go index 2e5c5d64..9d293555 100644 --- a/infra/conf/vless.go +++ b/infra/conf/vless.go @@ -2,8 +2,10 @@ package conf import ( "encoding/json" + "path/filepath" "runtime" "strconv" + "strings" "syscall" "github.com/xtls/xray-core/common/net" @@ -103,22 +105,19 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) { if fb.Type == "" && fb.Dest != "" { if fb.Dest == "serve-ws-none" { fb.Type = "serve" + } else if filepath.IsAbs(fb.Dest) || fb.Dest[0] == '@' { + fb.Type = "unix" + if strings.HasPrefix(fb.Dest, "@@") && (runtime.GOOS == "linux" || runtime.GOOS == "android") { + fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy + copy(fullAddr, fb.Dest[1:]) + fb.Dest = string(fullAddr) + } } else { - switch fb.Dest[0] { - case '@', '/': - fb.Type = "unix" - if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && (runtime.GOOS == "linux" || runtime.GOOS == "android") { - fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy - copy(fullAddr, fb.Dest[1:]) - fb.Dest = string(fullAddr) - } - default: - if _, err := strconv.Atoi(fb.Dest); err == nil { - fb.Dest = "127.0.0.1:" + fb.Dest - } - if _, _, err := net.SplitHostPort(fb.Dest); err == nil { - fb.Type = "tcp" - } + if _, err := strconv.Atoi(fb.Dest); err == nil { + fb.Dest = "127.0.0.1:" + fb.Dest + } + if _, _, err := net.SplitHostPort(fb.Dest); err == nil { + fb.Type = "tcp" } } } diff --git a/infra/conf/xray.go b/infra/conf/xray.go index 0935b1b0..10944826 100644 --- a/infra/conf/xray.go +++ b/infra/conf/xray.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "os" + "path/filepath" "strings" "github.com/xtls/xray-core/app/dispatcher" @@ -188,7 +189,7 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) { } else { // Listen on specific IP or Unix Domain Socket receiverSettings.Listen = c.ListenOn.Build() - listenDS := c.ListenOn.Family().IsDomain() && (c.ListenOn.Domain()[0] == '/' || c.ListenOn.Domain()[0] == '@') + listenDS := c.ListenOn.Family().IsDomain() && (filepath.IsAbs(c.ListenOn.Domain()) || c.ListenOn.Domain()[0] == '@') listenIP := c.ListenOn.Family().IsIP() || (c.ListenOn.Family().IsDomain() && c.ListenOn.Domain() == "localhost") if listenIP { // Listen on specific IP, must set PortList diff --git a/main/commands/all/commands.go b/main/commands/all/commands.go index 9b8b49e0..41d0e0f1 100644 --- a/main/commands/all/commands.go +++ b/main/commands/all/commands.go @@ -16,5 +16,6 @@ func init() { tls.CmdTLS, cmdUUID, cmdX25519, + cmdWG, ) } diff --git a/main/commands/all/curve25519.go b/main/commands/all/curve25519.go new file mode 100644 index 00000000..25cc812e --- /dev/null +++ b/main/commands/all/curve25519.go @@ -0,0 +1,57 @@ +package all + +import ( + "crypto/rand" + "encoding/base64" + "fmt" + + "golang.org/x/crypto/curve25519" +) + +func Curve25519Genkey(StdEncoding bool, input_base64 string) { + var output string + var err error + var privateKey, publicKey []byte + var encoding *base64.Encoding + if *input_stdEncoding || StdEncoding { + encoding = base64.StdEncoding + } else { + encoding = base64.RawURLEncoding + } + + if len(input_base64) > 0 { + privateKey, err = encoding.DecodeString(input_base64) + if err != nil { + output = err.Error() + goto out + } + if len(privateKey) != curve25519.ScalarSize { + output = "Invalid length of private key." + goto out + } + } + + if privateKey == nil { + privateKey = make([]byte, curve25519.ScalarSize) + if _, err = rand.Read(privateKey); err != nil { + output = err.Error() + goto out + } + } + + // Modify random bytes using algorithm described at: + // https://cr.yp.to/ecdh.html. + privateKey[0] &= 248 + privateKey[31] &= 127 | 64 + + if publicKey, err = curve25519.X25519(privateKey, curve25519.Basepoint); err != nil { + output = err.Error() + goto out + } + + output = fmt.Sprintf("Private key: %v\nPublic key: %v", + encoding.EncodeToString(privateKey), + encoding.EncodeToString(publicKey)) +out: + fmt.Println(output) +} diff --git a/main/commands/all/wg.go b/main/commands/all/wg.go new file mode 100644 index 00000000..70da4668 --- /dev/null +++ b/main/commands/all/wg.go @@ -0,0 +1,27 @@ +package all + +import ( + "github.com/xtls/xray-core/main/commands/base" +) + +var cmdWG = &base.Command{ + UsageLine: `{{.Exec}} wg [-i "private key (base64.StdEncoding)"]`, + Short: `Generate key pair for wireguard key exchange`, + Long: ` +Generate key pair for wireguard key exchange. + +Random: {{.Exec}} wg + +From private key: {{.Exec}} wg -i "private key (base64.StdEncoding)" +`, +} + +func init() { + cmdWG.Run = executeWG // break init loop +} + +var input_wireguard = cmdWG.Flag.String("i", "", "") + +func executeWG(cmd *base.Command, args []string) { + Curve25519Genkey(true, *input_wireguard) +} diff --git a/main/commands/all/x25519.go b/main/commands/all/x25519.go index 814cca72..73f669b2 100644 --- a/main/commands/all/x25519.go +++ b/main/commands/all/x25519.go @@ -1,12 +1,7 @@ package all import ( - "crypto/rand" - "encoding/base64" - "fmt" - "github.com/xtls/xray-core/main/commands/base" - "golang.org/x/crypto/curve25519" ) var cmdX25519 = &base.Command{ @@ -26,55 +21,9 @@ func init() { cmdX25519.Run = executeX25519 // break init loop } -var input_base64 = cmdX25519.Flag.String("i", "", "") var input_stdEncoding = cmdX25519.Flag.Bool("std-encoding", false, "") +var input_x25519 = cmdX25519.Flag.String("i", "", "") func executeX25519(cmd *base.Command, args []string) { - var output string - var err error - var privateKey []byte - var publicKey []byte - var encoding *base64.Encoding - if len(*input_base64) > 0 { - privateKey, err = base64.RawURLEncoding.DecodeString(*input_base64) - if err != nil { - output = err.Error() - goto out - } - if len(privateKey) != curve25519.ScalarSize { - output = "Invalid length of private key." - goto out - } - } - - if privateKey == nil { - privateKey = make([]byte, curve25519.ScalarSize) - if _, err = rand.Read(privateKey); err != nil { - output = err.Error() - goto out - } - } - - // Modify random bytes using algorithm described at: - // https://cr.yp.to/ecdh.html. - privateKey[0] &= 248 - privateKey[31] &= 127 - privateKey[31] |= 64 - - if publicKey, err = curve25519.X25519(privateKey, curve25519.Basepoint); err != nil { - output = err.Error() - goto out - } - - if *input_stdEncoding { - encoding = base64.StdEncoding - } else { - encoding = base64.RawURLEncoding - } - - output = fmt.Sprintf("Private key: %v\nPublic key: %v", - encoding.EncodeToString(privateKey), - encoding.EncodeToString(publicKey)) -out: - fmt.Println(output) + Curve25519Genkey(false, *input_x25519) } diff --git a/main/run.go b/main/run.go index 1f8a4b88..f54d7480 100644 --- a/main/run.go +++ b/main/run.go @@ -12,8 +12,10 @@ import ( "runtime/debug" "strings" "syscall" + "time" "github.com/xtls/xray-core/common/cmdarg" + clog "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/platform" "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/main/commands/base" @@ -34,7 +36,9 @@ The -format=json flag sets the format of config files. Default "auto". The -test flag tells Xray to test config files only, -without launching the server +without launching the server. + +The -dump flag tells Xray to print the merged config. `, } @@ -45,6 +49,7 @@ func init() { var ( configFiles cmdarg.Arg // "Config file for Xray.", the option is customed type, parse in main configDir string + dump = cmdRun.Flag.Bool("dump", false, "Dump merged config only, without launching Xray server.") test = cmdRun.Flag.Bool("test", false, "Test config file only, without launching Xray server.") format = cmdRun.Flag.String("format", "auto", "Format of input file.") @@ -61,6 +66,12 @@ var ( ) func executeRun(cmd *base.Command, args []string) { + if *dump { + clog.ReplaceWithSeverityLogger(clog.Severity_Warning) + errCode := dumpConfig() + os.Exit(errCode) + } + printVersion() server, err := startXray() if err != nil { @@ -97,6 +108,18 @@ func executeRun(cmd *base.Command, args []string) { } } +func dumpConfig() int { + files := getConfigFilePath(false) + if config, err := core.GetMergedConfig(files); err != nil { + fmt.Println(err) + time.Sleep(1 * time.Second) + return 23 + } else { + fmt.Print(config) + } + return 0 +} + func fileExists(file string) bool { info, err := os.Stat(file) return err == nil && !info.IsDir() @@ -139,12 +162,16 @@ func readConfDir(dirPath string) { } } -func getConfigFilePath() cmdarg.Arg { +func getConfigFilePath(verbose bool) cmdarg.Arg { if dirExists(configDir) { - log.Println("Using confdir from arg:", configDir) + if verbose { + log.Println("Using confdir from arg:", configDir) + } readConfDir(configDir) } else if envConfDir := platform.GetConfDirPath(); dirExists(envConfDir) { - log.Println("Using confdir from env:", envConfDir) + if verbose { + log.Println("Using confdir from env:", envConfDir) + } readConfDir(envConfDir) } @@ -155,17 +182,23 @@ func getConfigFilePath() cmdarg.Arg { if workingDir, err := os.Getwd(); err == nil { configFile := filepath.Join(workingDir, "config.json") if fileExists(configFile) { - log.Println("Using default config: ", configFile) + if verbose { + log.Println("Using default config: ", configFile) + } return cmdarg.Arg{configFile} } } if configFile := platform.GetConfigurationPath(); fileExists(configFile) { - log.Println("Using config from env: ", configFile) + if verbose { + log.Println("Using config from env: ", configFile) + } return cmdarg.Arg{configFile} } - log.Println("Using config from STDIN") + if verbose { + log.Println("Using config from STDIN") + } return cmdarg.Arg{"stdin:"} } @@ -178,7 +211,7 @@ func getConfigFormat() string { } func startXray() (core.Server, error) { - configFiles := getConfigFilePath() + configFiles := getConfigFilePath(true) // config, err := core.LoadConfig(getConfigFormat(), configFiles[0], configFiles) diff --git a/proxy/freedom/config.pb.go b/proxy/freedom/config.pb.go index 229630d4..81ce18e5 100644 --- a/proxy/freedom/config.pb.go +++ b/proxy/freedom/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 -// protoc v4.23.1 +// protoc-gen-go v1.32.0 +// protoc v4.25.2 // source: proxy/freedom/config.proto package freedom @@ -239,6 +239,7 @@ type Config struct { DestinationOverride *DestinationOverride `protobuf:"bytes,3,opt,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"` UserLevel uint32 `protobuf:"varint,4,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` Fragment *Fragment `protobuf:"bytes,5,opt,name=fragment,proto3" json:"fragment,omitempty"` + ProxyProtocol uint32 `protobuf:"varint,6,opt,name=proxy_protocol,json=proxyProtocol,proto3" json:"proxy_protocol,omitempty"` } func (x *Config) Reset() { @@ -309,6 +310,13 @@ func (x *Config) GetFragment() *Fragment { return nil } +func (x *Config) GetProxyProtocol() uint32 { + if x != nil { + return x.ProxyProtocol + } + return 0 +} + var File_proxy_freedom_config_proto protoreflect.FileDescriptor var file_proxy_freedom_config_proto_rawDesc = []byte{ @@ -335,7 +343,7 @@ var file_proxy_freedom_config_proto_rawDesc = []byte{ 0x6c, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x61, 0x78, 0x22, 0xdb, 0x03, 0x0a, 0x06, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x61, 0x78, 0x22, 0x82, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x52, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, @@ -354,24 +362,27 @@ var file_proxy_freedom_config_proto_rawDesc = []byte{ 0x76, 0x65, 0x6c, 0x12, 0x38, 0x0a, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x46, 0x72, 0x61, 0x67, 0x6d, - 0x65, 0x6e, 0x74, 0x52, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0xa9, 0x01, - 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, - 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, - 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, - 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, - 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x04, 0x12, - 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x05, 0x12, 0x0c, 0x0a, - 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x06, 0x12, 0x0d, 0x0a, 0x09, 0x46, - 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, - 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, - 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, - 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x42, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, - 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, - 0x64, 0x6f, 0x6d, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, - 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0xaa, 0x02, - 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x46, 0x72, 0x65, 0x65, - 0x64, 0x6f, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x6e, 0x74, 0x52, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x25, 0x0a, + 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, + 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, + 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, + 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, + 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, + 0x49, 0x50, 0x34, 0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, + 0x36, 0x34, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, + 0x10, 0x06, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, + 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, + 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, + 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, + 0x42, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, + 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, + 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x66, 0x72, + 0x65, 0x65, 0x64, 0x6f, 0x6d, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, + 0x78, 0x79, 0x2e, 0x46, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( diff --git a/proxy/freedom/config.proto b/proxy/freedom/config.proto index 0f328022..1eabedb7 100644 --- a/proxy/freedom/config.proto +++ b/proxy/freedom/config.proto @@ -39,5 +39,6 @@ message Config { uint32 timeout = 2 [deprecated = true]; DestinationOverride destination_override = 3; uint32 user_level = 4; - Fragment fragment = 5; + Fragment fragment = 5; + uint32 proxy_protocol = 6; } diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index 809d4df8..55853646 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -9,6 +9,7 @@ import ( "math/big" "time" + "github.com/pires/go-proxyproto" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/dice" @@ -152,6 +153,18 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte if err != nil { return err } + + if h.config.ProxyProtocol > 0 && h.config.ProxyProtocol <= 2 { + version := byte(h.config.ProxyProtocol) + srcAddr := inbound.Source.RawNetAddr() + dstAddr := rawConn.RemoteAddr() + header := proxyproto.HeaderProxyFromAddrs(version, srcAddr, dstAddr) + if _, err = header.WriteTo(rawConn); err != nil { + rawConn.Close() + return err + } + } + conn = rawConn return nil }) diff --git a/proxy/shadowsocks_2022/inbound.go b/proxy/shadowsocks_2022/inbound.go index 246fc7f1..00314c90 100644 --- a/proxy/shadowsocks_2022/inbound.go +++ b/proxy/shadowsocks_2022/inbound.go @@ -88,13 +88,13 @@ func (i *Inbound) Process(ctx context.Context, network net.Network, connection s } for _, buffer := range mb { packet := B.As(buffer.Bytes()).ToOwned() + buffer.Release() err = i.service.NewPacket(ctx, pc, packet, metadata) if err != nil { packet.Release() buf.ReleaseMulti(mb) return err } - buffer.Release() } } } diff --git a/proxy/shadowsocks_2022/inbound_multi.go b/proxy/shadowsocks_2022/inbound_multi.go index c3832a91..df837894 100644 --- a/proxy/shadowsocks_2022/inbound_multi.go +++ b/proxy/shadowsocks_2022/inbound_multi.go @@ -177,13 +177,13 @@ func (i *MultiUserInbound) Process(ctx context.Context, network net.Network, con } for _, buffer := range mb { packet := B.As(buffer.Bytes()).ToOwned() + buffer.Release() err = i.service.NewPacket(ctx, pc, packet, metadata) if err != nil { packet.Release() buf.ReleaseMulti(mb) return err } - buffer.Release() } } } diff --git a/proxy/shadowsocks_2022/inbound_relay.go b/proxy/shadowsocks_2022/inbound_relay.go index e2cb7d50..7317f8dd 100644 --- a/proxy/shadowsocks_2022/inbound_relay.go +++ b/proxy/shadowsocks_2022/inbound_relay.go @@ -109,13 +109,13 @@ func (i *RelayInbound) Process(ctx context.Context, network net.Network, connect } for _, buffer := range mb { packet := B.As(buffer.Bytes()).ToOwned() + buffer.Release() err = i.service.NewPacket(ctx, pc, packet, metadata) if err != nil { packet.Release() buf.ReleaseMulti(mb) return err } - buffer.Release() } } } diff --git a/proxy/shadowsocks_2022/outbound.go b/proxy/shadowsocks_2022/outbound.go index a06daac7..bc1eb556 100644 --- a/proxy/shadowsocks_2022/outbound.go +++ b/proxy/shadowsocks_2022/outbound.go @@ -2,7 +2,6 @@ package shadowsocks_2022 import ( "context" - "runtime" "time" shadowsocks "github.com/sagernet/sing-shadowsocks" @@ -102,27 +101,25 @@ func (o *Outbound) Process(ctx context.Context, link *transport.Link, dialer int if err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout { return newError("read payload").Base(err) } - _payload := B.StackNew() - payload := C.Dup(_payload) - defer payload.Release() + payload := B.New() for { - payload.FullReset() + payload.Reset() nb, n := buf.SplitBytes(mb, payload.FreeBytes()) if n > 0 { payload.Truncate(n) _, err = serverConn.Write(payload.Bytes()) if err != nil { + payload.Release() return newError("write payload").Base(err) } handshake = true } if nb.IsEmpty() { break - } else { - mb = nb } + mb = nb } - runtime.KeepAlive(_payload) + payload.Release() } if !handshake { _, err = serverConn.Write(nil) diff --git a/proxy/vless/encoding/addons.go b/proxy/vless/encoding/addons.go index e3e5071b..9426f6a0 100644 --- a/proxy/vless/encoding/addons.go +++ b/proxy/vless/encoding/addons.go @@ -62,11 +62,7 @@ func DecodeHeaderAddons(buffer *buf.Buffer, reader io.Reader) (*Addons, error) { // EncodeBodyAddons returns a Writer that auto-encrypt content written by caller. func EncodeBodyAddons(writer io.Writer, request *protocol.RequestHeader, requestAddons *Addons, state *proxy.TrafficState, context context.Context) buf.Writer { if request.Command == protocol.RequestCommandUDP { - w := writer.(buf.Writer) - if requestAddons.Flow == vless.XRV { - w = proxy.NewVisionWriter(w, state, context) - } - return NewMultiLengthPacketWriter(w) + return NewMultiLengthPacketWriter(writer.(buf.Writer)) } w := buf.NewWriter(writer) if requestAddons.Flow == vless.XRV { diff --git a/proxy/vless/encoding/encoding.go b/proxy/vless/encoding/encoding.go index b7fb66f5..455f896b 100644 --- a/proxy/vless/encoding/encoding.go +++ b/proxy/vless/encoding/encoding.go @@ -176,7 +176,6 @@ func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*A // XtlsRead filter and read xtls protocol func XtlsRead(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *proxy.TrafficState, ctx context.Context) error { err := func() error { - visionReader := proxy.NewVisionReader(reader, trafficState, ctx) for { if trafficState.ReaderSwitchToDirectCopy { var writerConn net.Conn @@ -188,7 +187,7 @@ func XtlsRead(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater } return proxy.CopyRawConnIfExist(ctx, conn, writerConn, writer, timer) } - buffer, err := visionReader.ReadMultiBuffer() + buffer, err := reader.ReadMultiBuffer() if !buffer.IsEmpty() { timer.Update() if trafficState.ReaderSwitchToDirectCopy { @@ -225,15 +224,6 @@ func XtlsWrite(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdate var ct stats.Counter for { buffer, err := reader.ReadMultiBuffer() - if trafficState.WriterSwitchToDirectCopy { - if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.CanSpliceCopy == 2 { - inbound.CanSpliceCopy = 1 // force the value to 1, don't use setter - } - rawConn, _, writerCounter := proxy.UnwrapRawConn(conn) - writer = buf.NewWriter(rawConn) - ct = writerCounter - trafficState.WriterSwitchToDirectCopy = false - } if !buffer.IsEmpty() { if ct != nil { ct.Add(int64(buffer.Len())) @@ -242,6 +232,15 @@ func XtlsWrite(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdate if werr := writer.WriteMultiBuffer(buffer); werr != nil { return werr } + if trafficState.WriterSwitchToDirectCopy { + rawConn, _, writerCounter := proxy.UnwrapRawConn(conn) + writer = buf.NewWriter(rawConn) + ct = writerCounter + trafficState.WriterSwitchToDirectCopy = false + if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.CanSpliceCopy == 2 { + inbound.CanSpliceCopy = 1 // force the value to 1, don't use setter + } + } } if err != nil { return err diff --git a/proxy/vless/inbound/inbound.go b/proxy/vless/inbound/inbound.go index 597ecaed..03d531c0 100644 --- a/proxy/vless/inbound/inbound.go +++ b/proxy/vless/inbound/inbound.go @@ -550,6 +550,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s if requestAddons.Flow == vless.XRV { ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice + clientReader = proxy.NewVisionReader(clientReader, trafficState, ctx1) err = encoding.XtlsRead(clientReader, serverWriter, timer, connection, input, rawInput, trafficState, ctx1) } else { // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer diff --git a/proxy/vless/outbound/outbound.go b/proxy/vless/outbound/outbound.go index cd30617c..a9368813 100644 --- a/proxy/vless/outbound/outbound.go +++ b/proxy/vless/outbound/outbound.go @@ -138,7 +138,6 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte if !allowUDP443 && request.Port == 443 { return newError("XTLS rejected UDP/443 traffic").AtInfo() } - requestAddons.Flow = "" case protocol.RequestCommandMux: fallthrough // let server break Mux connections that contain TCP requests case protocol.RequestCommandTCP: @@ -185,7 +184,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte clientReader := link.Reader // .(*pipe.Reader) clientWriter := link.Writer // .(*pipe.Writer) trafficState := proxy.NewTrafficState(account.ID.Bytes()) - if request.Command == protocol.RequestCommandUDP && h.cone && request.Port != 53 && request.Port != 443 { + if request.Command == protocol.RequestCommandUDP && (requestAddons.Flow == vless.XRV || (h.cone && request.Port != 53 && request.Port != 443)) { request.Command = protocol.RequestCommandMux request.Address = net.DomainAddress("v1.mux.cool") request.Port = net.Port(666) @@ -266,8 +265,15 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte // default: serverReader := buf.NewReader(conn) serverReader := encoding.DecodeBodyAddons(conn, request, responseAddons) + if requestAddons.Flow == vless.XRV { + serverReader = proxy.NewVisionReader(serverReader, trafficState, ctx) + } if request.Command == protocol.RequestCommandMux && request.Port == 666 { - serverReader = xudp.NewPacketReader(conn) + if requestAddons.Flow == vless.XRV { + serverReader = xudp.NewPacketReader(&buf.BufferedReader{Reader: serverReader}) + } else { + serverReader = xudp.NewPacketReader(conn) + } } if requestAddons.Flow == vless.XRV { diff --git a/proxy/wireguard/client.go b/proxy/wireguard/client.go index def07878..2560c538 100644 --- a/proxy/wireguard/client.go +++ b/proxy/wireguard/client.go @@ -22,7 +22,9 @@ package wireguard import ( "context" + "fmt" "net/netip" + "strings" "sync" "github.com/xtls/xray-core/common" @@ -49,7 +51,6 @@ type Handler struct { policyManager policy.Manager dns dns.Client // cached configuration - ipc string endpoints []netip.Addr hasIPv4, hasIPv6 bool wgLock sync.Mutex @@ -69,7 +70,6 @@ func New(ctx context.Context, conf *DeviceConfig) (*Handler, error) { conf: conf, policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), dns: d, - ipc: createIPCRequest(conf), endpoints: endpoints, hasIPv4: hasIPv4, hasIPv6: hasIPv6, @@ -247,9 +247,76 @@ func (h *Handler) makeVirtualTun(bind *netBindClient) (Tunnel, error) { bind.dnsOption.IPv4Enable = h.hasIPv4 bind.dnsOption.IPv6Enable = h.hasIPv6 - if err = t.BuildDevice(h.ipc, bind); err != nil { + if err = t.BuildDevice(h.createIPCRequest(bind, h.conf), bind); err != nil { _ = t.Close() return nil, err } return t, nil } + + +// serialize the config into an IPC request +func (h *Handler) createIPCRequest(bind *netBindClient, conf *DeviceConfig) string { + var request strings.Builder + + request.WriteString(fmt.Sprintf("private_key=%s\n", conf.SecretKey)) + + if !conf.IsClient { + // placeholder, we'll handle actual port listening on Xray + request.WriteString("listen_port=1337\n") + } + + for _, peer := range conf.Peers { + if peer.PublicKey != "" { + request.WriteString(fmt.Sprintf("public_key=%s\n", peer.PublicKey)) + } + + if peer.PreSharedKey != "" { + request.WriteString(fmt.Sprintf("preshared_key=%s\n", peer.PreSharedKey)) + } + + split := strings.Split(peer.Endpoint, ":") + addr := net.ParseAddress(split[0]) + if addr.Family().IsDomain() { + dialerIp := bind.dialer.DestIpAddress() + if dialerIp != nil { + addr = net.ParseAddress(dialerIp.String()) + newError("createIPCRequest use dialer dest ip: ", addr).WriteToLog() + } else { + ips, err := h.dns.LookupIP(addr.Domain(), dns.IPOption{ + IPv4Enable: h.hasIPv4 && h.conf.preferIP4(), + IPv6Enable: h.hasIPv6 && h.conf.preferIP6(), + }) + { // Resolve fallback + if (len(ips) == 0 || err != nil) && h.conf.hasFallback() { + ips, err = h.dns.LookupIP(addr.Domain(), dns.IPOption{ + IPv4Enable: h.hasIPv4 && h.conf.fallbackIP4(), + IPv6Enable: h.hasIPv6 && h.conf.fallbackIP6(), + }) + } + } + if err != nil { + newError("createIPCRequest failed to lookup DNS").Base(err).WriteToLog() + } else if len(ips) == 0 { + newError("createIPCRequest empty lookup DNS").WriteToLog() + } else { + addr = net.IPAddress(ips[dice.Roll(len(ips))]) + } + } + } + + if peer.Endpoint != "" { + request.WriteString(fmt.Sprintf("endpoint=%s:%s\n", addr, split[1])) + } + + for _, ip := range peer.AllowedIps { + request.WriteString(fmt.Sprintf("allowed_ip=%s\n", ip)) + } + + if peer.KeepAlive != 0 { + request.WriteString(fmt.Sprintf("persistent_keepalive_interval=%d\n", peer.KeepAlive)) + } + } + + return request.String()[:request.Len()] +} diff --git a/proxy/wireguard/config.pb.go b/proxy/wireguard/config.pb.go index ed8b135e..47bd2b77 100644 --- a/proxy/wireguard/config.pb.go +++ b/proxy/wireguard/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 -// protoc v4.25.0 +// protoc-gen-go v1.31.0 +// protoc v4.23.1 // source: proxy/wireguard/config.proto package wireguard diff --git a/testing/scenarios/dokodemo_test.go b/testing/scenarios/dokodemo_test.go index 69032b6e..0744cac9 100644 --- a/testing/scenarios/dokodemo_test.go +++ b/testing/scenarios/dokodemo_test.go @@ -171,7 +171,7 @@ func TestDokodemoUDP(t *testing.T) { common.Must(err) defer CloseServer(server) - clientPortRange := uint32(5) + clientPortRange := uint32(3) retry := 1 clientPort := uint32(udp.PickPort()) for { diff --git a/testing/scenarios/shadowsocks_2022_test.go b/testing/scenarios/shadowsocks_2022_test.go index aa595844..f06c38dc 100644 --- a/testing/scenarios/shadowsocks_2022_test.go +++ b/testing/scenarios/shadowsocks_2022_test.go @@ -32,14 +32,22 @@ func TestShadowsocks2022Tcp(t *testing.T) { } } -func TestShadowsocks2022Udp(t *testing.T) { - for _, method := range shadowaead_2022.List { - password := make([]byte, 32) - rand.Read(password) - t.Run(method, func(t *testing.T) { - testShadowsocks2022Udp(t, method, base64.StdEncoding.EncodeToString(password)) - }) - } +func TestShadowsocks2022UdpAES128(t *testing.T) { + password := make([]byte, 32) + rand.Read(password) + testShadowsocks2022Udp(t, shadowaead_2022.List[0], base64.StdEncoding.EncodeToString(password)) +} + +func TestShadowsocks2022UdpAES256(t *testing.T) { + password := make([]byte, 32) + rand.Read(password) + testShadowsocks2022Udp(t, shadowaead_2022.List[1], base64.StdEncoding.EncodeToString(password)) +} + +func TestShadowsocks2022UdpChacha(t *testing.T) { + password := make([]byte, 32) + rand.Read(password) + testShadowsocks2022Udp(t, shadowaead_2022.List[2], base64.StdEncoding.EncodeToString(password)) } func testShadowsocks2022Tcp(t *testing.T, method string, password string) { @@ -199,7 +207,7 @@ func testShadowsocks2022Udp(t *testing.T, method string, password string) { defer CloseAllServers(servers) var errGroup errgroup.Group - for i := 0; i < 10; i++ { + for i := 0; i < 2; i++ { errGroup.Go(testUDPConn(udpClientPort, 1024, time.Second*5)) } diff --git a/testing/scenarios/shadowsocks_test.go b/testing/scenarios/shadowsocks_test.go index d6b8ee82..e7620429 100644 --- a/testing/scenarios/shadowsocks_test.go +++ b/testing/scenarios/shadowsocks_test.go @@ -289,7 +289,7 @@ func TestShadowsocksAES128GCMUDP(t *testing.T) { defer CloseAllServers(servers) var errGroup errgroup.Group - for i := 0; i < 10; i++ { + for i := 0; i < 2; i++ { errGroup.Go(testUDPConn(clientPort, 1024, time.Second*5)) } if err := errGroup.Wait(); err != nil { @@ -391,7 +391,7 @@ func TestShadowsocksAES128GCMUDPMux(t *testing.T) { defer CloseAllServers(servers) var errGroup errgroup.Group - for i := 0; i < 10; i++ { + for i := 0; i < 2; i++ { errGroup.Go(testUDPConn(clientPort, 1024, time.Second*5)) } if err := errGroup.Wait(); err != nil { diff --git a/testing/scenarios/vmess_test.go b/testing/scenarios/vmess_test.go index 2239b13c..8b7e646f 100644 --- a/testing/scenarios/vmess_test.go +++ b/testing/scenarios/vmess_test.go @@ -475,7 +475,7 @@ func TestVMessGCMUDP(t *testing.T) { defer CloseAllServers(servers) var errg errgroup.Group - for i := 0; i < 10; i++ { + for i := 0; i < 2; i++ { errg.Go(testUDPConn(clientPort, 1024, time.Second*5)) } if err := errg.Wait(); err != nil { @@ -787,8 +787,8 @@ func TestVMessKCP(t *testing.T) { defer CloseAllServers(servers) var errg errgroup.Group - for i := 0; i < 10; i++ { - errg.Go(testTCPConn(clientPort, 10240*1024, time.Minute*2)) + for i := 0; i < 2; i++ { + errg.Go(testTCPConn(clientPort, 1024, time.Minute*2)) } if err := errg.Wait(); err != nil { t.Error(err) @@ -934,7 +934,7 @@ func TestVMessKCPLarge(t *testing.T) { var errg errgroup.Group for i := 0; i < 2; i++ { - errg.Go(testTCPConn(clientPort, 10240*1024, time.Minute*5)) + errg.Go(testTCPConn(clientPort, 513*1024, time.Minute*5)) } if err := errg.Wait(); err != nil { t.Error(err) @@ -1176,7 +1176,7 @@ func TestVMessGCMMuxUDP(t *testing.T) { for range "ab" { var errg errgroup.Group - for i := 0; i < 16; i++ { + for i := 0; i < 2; i++ { errg.Go(testTCPConn(clientPort, 1024, time.Second*10)) errg.Go(testUDPConn(clientUDPPort, 1024, time.Second*10)) } diff --git a/transport/internet/dialer.go b/transport/internet/dialer.go index deae4df0..3d5d046f 100644 --- a/transport/internet/dialer.go +++ b/transport/internet/dialer.go @@ -22,6 +22,9 @@ type Dialer interface { // Address returns the address used by this Dialer. Maybe nil if not known. Address() net.Address + + // DestIpAddress returns the ip of proxy server. It is useful in case of Android client, which prepare an IP before proxy connection is established + DestIpAddress() net.IP } // dialFunc is an interface to dial network connection to a specific destination. @@ -68,6 +71,11 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *MemoryStrea return nil, newError("unknown network ", dest.Network) } +// DestIpAddress returns the ip of proxy server. It is useful in case of Android client, which prepare an IP before proxy connection is established +func DestIpAddress() net.IP { + return effectiveSystemDialer.DestIpAddress() +} + var ( dnsClient dns.Client obm outbound.Manager diff --git a/transport/internet/quic/dialer.go b/transport/internet/quic/dialer.go index c6bc08aa..7c8122b6 100644 --- a/transport/internet/quic/dialer.go +++ b/transport/internet/quic/dialer.go @@ -208,12 +208,21 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me IP: dest.Address.IP(), Port: int(dest.Port), } - } else { - addr, err := net.ResolveUDPAddr("udp", dest.NetAddr()) - if err != nil { - return nil, err + } else { + dialerIp := internet.DestIpAddress() + if dialerIp != nil { + destAddr = &net.UDPAddr{ + IP: dialerIp, + Port: int(dest.Port), + } + newError("quic Dial use dialer dest addr: ", destAddr).WriteToLog() + } else { + addr, err := net.ResolveUDPAddr("udp", dest.NetAddr()) + if err != nil { + return nil, err + } + destAddr = addr } - destAddr = addr } config := streamSettings.ProtocolSettings.(*Config) diff --git a/transport/internet/reality/config.go b/transport/internet/reality/config.go index 58608720..be3611e5 100644 --- a/transport/internet/reality/config.go +++ b/transport/internet/reality/config.go @@ -1,7 +1,9 @@ package reality import ( + "io" "net" + "os" "time" "github.com/xtls/reality" @@ -25,6 +27,8 @@ func (c *Config) GetREALITYConfig() *reality.Config { NextProtos: nil, // should be nil SessionTicketsDisabled: true, + + KeyLogWriter: KeyLogWriterFromConfig(c), } config.ServerNames = make(map[string]bool) for _, serverName := range c.ServerNames { @@ -37,6 +41,19 @@ func (c *Config) GetREALITYConfig() *reality.Config { return config } +func KeyLogWriterFromConfig(c *Config) io.Writer { + if len(c.MasterKeyLog) <= 0 || c.MasterKeyLog == "none" { + return nil + } + + writer, err := os.OpenFile(c.MasterKeyLog, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644) + if err != nil { + newError("failed to open ", c.MasterKeyLog, " as master key log").AtError().Base(err).WriteToLog() + } + + return writer +} + func ConfigFromStreamSettings(settings *internet.MemoryStreamConfig) *Config { if settings == nil { return nil diff --git a/transport/internet/reality/config.pb.go b/transport/internet/reality/config.pb.go index 2b44d9b7..6631d0af 100644 --- a/transport/internet/reality/config.pb.go +++ b/transport/internet/reality/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 -// protoc v4.23.1 +// protoc-gen-go v1.32.0 +// protoc v4.25.1 // source: transport/internet/reality/config.proto package reality @@ -41,6 +41,7 @@ type Config struct { ShortId []byte `protobuf:"bytes,24,opt,name=short_id,json=shortId,proto3" json:"short_id,omitempty"` SpiderX string `protobuf:"bytes,25,opt,name=spider_x,json=spiderX,proto3" json:"spider_x,omitempty"` SpiderY []int64 `protobuf:"varint,26,rep,packed,name=spider_y,json=spiderY,proto3" json:"spider_y,omitempty"` + MasterKeyLog string `protobuf:"bytes,27,opt,name=master_key_log,json=masterKeyLog,proto3" json:"master_key_log,omitempty"` } func (x *Config) Reset() { @@ -187,6 +188,13 @@ func (x *Config) GetSpiderY() []int64 { return nil } +func (x *Config) GetMasterKeyLog() string { + if x != nil { + return x.MasterKeyLog + } + return "" +} + var File_transport_internet_reality_config_proto protoreflect.FileDescriptor var file_transport_internet_reality_config_proto_rawDesc = []byte{ @@ -194,7 +202,7 @@ var file_transport_internet_reality_config_proto_rawDesc = []byte{ 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x65, 0x74, 0x2e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x22, 0xdc, 0x03, 0x0a, 0x06, 0x43, + 0x65, 0x74, 0x2e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x22, 0x82, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, @@ -224,16 +232,18 @@ var file_transport_internet_reality_config_proto_rawDesc = []byte{ 0x74, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x78, 0x18, 0x19, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x58, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x79, 0x18, 0x1a, 0x20, 0x03, 0x28, 0x03, - 0x52, 0x07, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x59, 0x42, 0x7f, 0x0a, 0x23, 0x63, 0x6f, 0x6d, - 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, - 0x50, 0x01, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, - 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, - 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, - 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0xaa, 0x02, 0x1f, 0x58, 0x72, 0x61, 0x79, 0x2e, - 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x65, 0x74, 0x2e, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x52, 0x07, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x59, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x73, + 0x74, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x1b, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x42, + 0x7f, 0x0a, 0x23, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, + 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x72, + 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x01, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, + 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0xaa, 0x02, + 0x1f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/transport/internet/reality/config.proto b/transport/internet/reality/config.proto index f9ae3a4f..233f6e05 100644 --- a/transport/internet/reality/config.proto +++ b/transport/internet/reality/config.proto @@ -24,4 +24,5 @@ message Config { bytes short_id = 24; string spider_x = 25; repeated int64 spider_y = 26; + string master_key_log = 27; } diff --git a/transport/internet/reality/reality.go b/transport/internet/reality/reality.go index de8a6ac6..136a075d 100644 --- a/transport/internet/reality/reality.go +++ b/transport/internet/reality/reality.go @@ -112,6 +112,7 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati ServerName: config.ServerName, InsecureSkipVerify: true, SessionTicketsDisabled: true, + KeyLogWriter: KeyLogWriterFromConfig(config), } if utlsConfig.ServerName == "" { utlsConfig.ServerName = dest.Address.String() @@ -136,7 +137,10 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati if config.Show { newError(fmt.Sprintf("REALITY localAddr: %v\thello.SessionId[:16]: %v\n", localAddr, hello.SessionId[:16])).WriteToLog(session.ExportIDToError(ctx)) } - publicKey, _ := ecdh.X25519().NewPublicKey(config.PublicKey) + publicKey, err := ecdh.X25519().NewPublicKey(config.PublicKey) + if err != nil { + return nil, errors.New("REALITY: publicKey == nil") + } uConn.AuthKey, _ = uConn.HandshakeState.State13.EcdheKey.ECDH(publicKey) if uConn.AuthKey == nil { return nil, errors.New("REALITY: SharedKey == nil") diff --git a/transport/internet/sockopt_windows.go b/transport/internet/sockopt_windows.go index 703a53c2..e2f1f796 100644 --- a/transport/internet/sockopt_windows.go +++ b/transport/internet/sockopt_windows.go @@ -1,11 +1,16 @@ package internet import ( + "encoding/binary" + "net" "syscall" + "unsafe" ) const ( - TCP_FASTOPEN = 15 + TCP_FASTOPEN = 15 + IP_UNICAST_IF = 31 + IPV6_UNICAST_IF = 31 ) func setTFO(fd syscall.Handle, tfo int) error { @@ -21,6 +26,26 @@ func setTFO(fd syscall.Handle, tfo int) error { } func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error { + if config.Interface != "" { + inf, err := net.InterfaceByName(config.Interface) + if err != nil { + return newError("failed to find the interface").Base(err) + } + isV4 := (network == "tcp4") + if isV4 { + var bytes [4]byte + binary.BigEndian.PutUint32(bytes[:], uint32(inf.Index)) + idx := *(*uint32)(unsafe.Pointer(&bytes[0])) + if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IP, IP_UNICAST_IF, int(idx)); err != nil { + return newError("failed to set IP_UNICAST_IF").Base(err) + } + } else { + if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IPV6, IPV6_UNICAST_IF, inf.Index); err != nil { + return newError("failed to set IPV6_UNICAST_IF").Base(err) + } + } + } + if isTCPSocket(network) { if err := setTFO(syscall.Handle(fd), config.ParseTFOValue()); err != nil { return err diff --git a/transport/internet/system_dialer.go b/transport/internet/system_dialer.go index 5304595f..cdb6cb9c 100644 --- a/transport/internet/system_dialer.go +++ b/transport/internet/system_dialer.go @@ -16,6 +16,7 @@ var effectiveSystemDialer SystemDialer = &DefaultSystemDialer{} type SystemDialer interface { Dial(ctx context.Context, source net.Address, destination net.Destination, sockopt *SocketConfig) (net.Conn, error) + DestIpAddress() net.IP } type DefaultSystemDialer struct { @@ -108,6 +109,10 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne return dialer.DialContext(ctx, dest.Network.SystemString(), dest.NetAddr()) } +func (d *DefaultSystemDialer) DestIpAddress() net.IP { + return nil +} + type PacketConnWrapper struct { Conn net.PacketConn Dest net.Addr @@ -172,6 +177,10 @@ func (v *SimpleSystemDialer) Dial(ctx context.Context, src net.Address, dest net return v.adapter.Dial(dest.Network.SystemString(), dest.NetAddr()) } +func (d *SimpleSystemDialer) DestIpAddress() net.IP { + return nil +} + // UseAlternativeSystemDialer replaces the current system dialer with a given one. // Caller must ensure there is no race condition. // diff --git a/transport/internet/tls/config.go b/transport/internet/tls/config.go index 2e2b784a..325909e3 100644 --- a/transport/internet/tls/config.go +++ b/transport/internet/tls/config.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "crypto/x509" "encoding/base64" + "os" "strings" "sync" "time" @@ -364,6 +365,15 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config { config.PreferServerCipherSuites = c.PreferServerCipherSuites + if (len(c.MasterKeyLog) > 0 && c.MasterKeyLog != "none") { + writer, err := os.OpenFile(c.MasterKeyLog, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644) + if err != nil { + newError("failed to open ", c.MasterKeyLog, " as master key log").AtError().Base(err).WriteToLog() + } else { + config.KeyLogWriter = writer + } + } + return config } diff --git a/transport/internet/tls/config.pb.go b/transport/internet/tls/config.pb.go index 9bd5a84d..7602f3e9 100644 --- a/transport/internet/tls/config.pb.go +++ b/transport/internet/tls/config.pb.go @@ -208,6 +208,7 @@ type Config struct { // @Document This value replace allow_insecure. // @Critical PinnedPeerCertificatePublicKeySha256 [][]byte `protobuf:"bytes,14,rep,name=pinned_peer_certificate_public_key_sha256,json=pinnedPeerCertificatePublicKeySha256,proto3" json:"pinned_peer_certificate_public_key_sha256,omitempty"` + MasterKeyLog string `protobuf:"bytes,15,opt,name=master_key_log,json=masterKeyLog,proto3" json:"master_key_log,omitempty"` } func (x *Config) Reset() { @@ -340,6 +341,13 @@ func (x *Config) GetPinnedPeerCertificatePublicKeySha256() [][]byte { return nil } +func (x *Config) GetMasterKeyLog() string { + if x != nil { + return x.MasterKeyLog + } + return "" +} + var File_transport_internet_tls_config_proto protoreflect.FileDescriptor var file_transport_internet_tls_config_proto_rawDesc = []byte{ @@ -369,7 +377,7 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{ 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46, 0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, - 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xcc, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, + 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xf2, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65, 0x72, @@ -414,15 +422,17 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{ 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, - 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x42, 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, - 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, - 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, - 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, - 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x42, 0x73, 0x0a, 0x1f, + 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x50, + 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, + 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, + 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c, + 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/transport/internet/tls/config.proto b/transport/internet/tls/config.proto index 227840a2..b1c26a4c 100644 --- a/transport/internet/tls/config.proto +++ b/transport/internet/tls/config.proto @@ -83,4 +83,6 @@ message Config { @Critical */ repeated bytes pinned_peer_certificate_public_key_sha256 = 14; + + string master_key_log = 15; } diff --git a/transport/internet/tls/tls.go b/transport/internet/tls/tls.go index 2fd9a017..e73a495b 100644 --- a/transport/internet/tls/tls.go +++ b/transport/internet/tls/tls.go @@ -118,6 +118,7 @@ func copyConfig(c *tls.Config) *utls.Config { ServerName: c.ServerName, InsecureSkipVerify: c.InsecureSkipVerify, VerifyPeerCertificate: c.VerifyPeerCertificate, + KeyLogWriter: c.KeyLogWriter, } } diff --git a/transport/internet/udp/dispatcher.go b/transport/internet/udp/dispatcher.go index 32c8c8ac..c29d4b13 100644 --- a/transport/internet/udp/dispatcher.go +++ b/transport/internet/udp/dispatcher.go @@ -28,7 +28,7 @@ type connEntry struct { type Dispatcher struct { sync.RWMutex - conns map[net.Destination]*connEntry + conn *connEntry dispatcher routing.Dispatcher callback ResponseCallback callClose func() error @@ -36,19 +36,18 @@ type Dispatcher struct { func NewDispatcher(dispatcher routing.Dispatcher, callback ResponseCallback) *Dispatcher { return &Dispatcher{ - conns: make(map[net.Destination]*connEntry), dispatcher: dispatcher, callback: callback, } } -func (v *Dispatcher) RemoveRay(dest net.Destination) { +func (v *Dispatcher) RemoveRay() { v.Lock() defer v.Unlock() - if conn, found := v.conns[dest]; found { - common.Close(conn.link.Reader) - common.Close(conn.link.Writer) - delete(v.conns, dest) + if v.conn != nil { + common.Close(v.conn.link.Reader) + common.Close(v.conn.link.Writer) + v.conn = nil } } @@ -56,8 +55,8 @@ func (v *Dispatcher) getInboundRay(ctx context.Context, dest net.Destination) (* v.Lock() defer v.Unlock() - if entry, found := v.conns[dest]; found { - return entry, nil + if v.conn != nil { + return v.conn, nil } newError("establishing new connection for ", dest).WriteToLog() @@ -65,7 +64,7 @@ func (v *Dispatcher) getInboundRay(ctx context.Context, dest net.Destination) (* ctx, cancel := context.WithCancel(ctx) removeRay := func() { cancel() - v.RemoveRay(dest) + v.RemoveRay() } timer := signal.CancelAfterInactivity(ctx, removeRay, time.Minute) @@ -79,7 +78,7 @@ func (v *Dispatcher) getInboundRay(ctx context.Context, dest net.Destination) (* timer: timer, cancel: removeRay, } - v.conns[dest] = entry + v.conn = entry go handleInput(ctx, entry, dest, v.callback, v.callClose) return entry, nil } @@ -130,6 +129,9 @@ func handleInput(ctx context.Context, conn *connEntry, dest net.Destination, cal } timer.Update() for _, b := range mb { + if b.UDP != nil { + dest = *b.UDP + } callback(ctx, &udp.Packet{ Payload: b, Source: dest, @@ -153,7 +155,6 @@ func DialDispatcher(ctx context.Context, dispatcher routing.Dispatcher) (net.Pac } d := &Dispatcher{ - conns: make(map[net.Destination]*connEntry), dispatcher: dispatcher, callback: c.callback, callClose: c.Close, @@ -199,7 +200,9 @@ func (c *dispatcherConn) WriteTo(p []byte, addr net.Addr) (int, error) { n := copy(raw, p) buffer.Resize(0, int32(n)) - c.dispatcher.Dispatch(c.ctx, net.DestinationFromAddr(addr), buffer) + destination := net.DestinationFromAddr(addr) + buffer.UDP = &destination + c.dispatcher.Dispatch(c.ctx, destination, buffer) return n, nil } diff --git a/transport/internet/websocket/hub.go b/transport/internet/websocket/hub.go index 7951b1f4..c907e224 100644 --- a/transport/internet/websocket/hub.go +++ b/transport/internet/websocket/hub.go @@ -28,8 +28,8 @@ type requestHandler struct { var replacer = strings.NewReplacer("+", "-", "/", "_", "=", "") var upgrader = &websocket.Upgrader{ - ReadBufferSize: 4 * 1024, - WriteBufferSize: 4 * 1024, + ReadBufferSize: 0, + WriteBufferSize: 0, HandshakeTimeout: time.Second * 4, CheckOrigin: func(r *http.Request) bool { return true