Compare commits

..

54 Commits

Author SHA1 Message Date
世界
fe8d46cce5 Fix TFO async write 2023-09-09 19:52:13 +08:00
世界
b1f289bce5 Fix TUIC context 2023-09-09 11:41:04 +08:00
世界
a8beb80876 Update Makefile 2023-09-07 22:37:55 +08:00
世界
ff209471d8 Fix QUIC defragger 2023-09-07 21:35:25 +08:00
unknown
806f7d0a2b Fix QUIC stream usage 2023-09-07 21:34:36 +08:00
世界
6b943caf37 Reject invalid connection 2023-09-07 21:34:36 +08:00
世界
4ea2d460f4 Fix router close 2023-09-07 21:34:35 +08:00
世界
c84c18f960 platform: Fix crash on android 2023-09-07 21:34:35 +08:00
世界
1402bdab41 Fix connect domain for IP outbounds 2023-09-07 21:34:35 +08:00
renovate[bot]
7082cf277e [dependencies] Update actions/checkout action to v4 2023-09-07 21:34:35 +08:00
世界
b9310154a7 clash-api: Move default mode to first 2023-09-07 21:34:35 +08:00
世界
55c34e3fb0 platform: Improve client 2023-09-07 21:34:35 +08:00
世界
68f2202eec documentation: Bump version 2023-08-31 23:32:44 +08:00
renovate[bot]
5057e50bb8 [dependencies] Update actions/stale action to v8
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-31 23:32:44 +08:00
世界
23e1a69955 Update Makefile 2023-08-31 23:32:44 +08:00
世界
b83c6c9d20 Fix return nil addr in conn 2023-08-30 21:28:03 +08:00
世界
67deac6d44 Fix hysteria packet write 2023-08-30 18:23:17 +08:00
世界
ea3731162b Update Makefile 2023-08-30 18:23:17 +08:00
世界
c75e32e722 documentation: Update changelog 2023-08-29 10:26:11 +08:00
世界
e7b35be5f6 Update Makefile 2023-08-29 10:26:11 +08:00
世界
5a309266f0 Fix TUIC client 2023-08-28 19:58:02 +08:00
世界
05669eaaad documentation: Update changelog 2023-08-25 20:33:16 +08:00
世界
e91a6e5439 Update dependencies 2023-08-25 20:21:14 +08:00
世界
43f72a6419 Add store_mode and platform Clash mode selector 2023-08-25 20:21:14 +08:00
世界
6dcacf3b5e Fix sniffer 2023-08-24 21:53:06 +08:00
世界
edad4d1ce7 Update dependencies 2023-08-24 11:31:29 +08:00
世界
262842c87d Fix test 2023-08-22 11:22:13 +08:00
世界
376f527742 documentation: Bump version 2023-08-21 18:24:31 +08:00
世界
c0bbb3849d Fix TUIC UDP 2023-08-21 18:16:38 +08:00
世界
738c25d818 Fix Makefile 2023-08-21 18:14:27 +08:00
dyhkwong
027af4d4ee Fix SOCKS5 UDP sniffing accidentially enabled 2023-08-20 19:06:37 +08:00
世界
6011f4483a Remove bad scripts 2023-08-20 17:57:39 +08:00
世界
fc22466e3b documentation: Bump version 2023-08-20 17:29:22 +08:00
世界
975e13a313 Add [include/exclude]_interface iproute2 options 2023-08-20 17:29:22 +08:00
世界
f46732bc0e Add udp over stream support for TUIC 2023-08-20 14:15:22 +08:00
世界
5c5c25e3ad Update tfo-go 2023-08-20 13:32:07 +08:00
世界
53a0bf2d11 minor: Remove unused parameter 2023-08-20 13:32:07 +08:00
renovate[bot]
7b79d98f59 [dependencies] Update golang Docker tag to v1.21 2023-08-20 13:31:56 +08:00
世界
1dd2c26f31 Fix UDP route 2023-08-20 13:31:55 +08:00
armv9
d14170348d Fix process search with fakeip 2023-08-20 13:31:55 +08:00
世界
9f94b21687 platform: Add group expand status 2023-08-20 13:31:55 +08:00
世界
cf57e46d69 Add new issue template 2023-08-20 13:31:49 +08:00
世界
b459001600 Update documentation 2023-08-20 13:31:48 +08:00
世界
73267fd6ad Fix ci build 2023-08-20 13:31:48 +08:00
世界
1019ecfdcf Add TCP MultiPath support 2023-08-20 13:31:48 +08:00
世界
81b847faca Pause recurring tasks when no network 2023-08-20 13:31:48 +08:00
世界
ce4c76cdd2 documentation: Add TUIC 2023-08-20 13:31:47 +08:00
世界
917420e79a Add TUIC protocol 2023-08-20 13:31:40 +08:00
世界
0b14dc3228 Update quic-go 2023-08-20 13:31:40 +08:00
世界
cbdaf3272b Update dependencies 2023-08-20 13:31:36 +08:00
世界
d51ab2b0a7 Fix missing HandshakeConn interface 2023-08-20 13:31:31 +08:00
世界
1363e16312 platform: Enable Clash API support by default 2023-08-20 13:31:29 +08:00
世界
f43d0141f3 Save fakeip metadata immediately 2023-08-20 13:31:27 +08:00
世界
90b3aad83a Fix network monitor 2023-08-20 13:30:50 +08:00
87 changed files with 1553 additions and 512 deletions

View File

@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
with:
fetch-depth: 0
- name: Get latest go version
@@ -48,7 +48,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
with:
fetch-depth: 0
- name: Setup Go
@@ -68,7 +68,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
with:
fetch-depth: 0
- name: Setup Go
@@ -199,7 +199,7 @@ jobs:
TAGS: with_clash_api,with_quic
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
with:
fetch-depth: 0
- name: Get latest go version

View File

@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Setup QEMU for Docker Buildx

View File

@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
with:
fetch-depth: 0
- name: Get latest go version

View File

@@ -1,20 +0,0 @@
name: Generate Documents
on:
push:
branches:
- dev
paths:
- docs/**
- .github/workflows/mkdocs.yml
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.x
- run: |
pip install mkdocs-material=="9.*" mkdocs-static-i18n=="0.53"
- run: |
mkdocs gh-deploy -m "{sha}" --force --ignore-version --no-history

View File

@@ -8,7 +8,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v7
- uses: actions/stale@v8
with:
stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 5 days'
days-before-stale: 60

104
Makefile
View File

@@ -1,7 +1,8 @@
NAME = sing-box
COMMIT = $(shell git rev-parse --short HEAD)
TAGS_GO118 = with_gvisor,with_dhcp,with_wireguard,with_utls,with_reality_server,with_clash_api
TAGS_GO120 ?= with_quic
TAGS_GO120 = with_quic
TAGS ?= $(TAGS_GO118),$(TAGS_GO120)
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_reality_server,with_shadowsocksr
GOHOSTOS = $(shell go env GOHOSTOS)
@@ -9,7 +10,7 @@ GOHOSTARCH = $(shell go env GOHOSTARCH)
VERSION=$(shell CGO_ENABLED=0 GOOS=$(GOHOSTOS) GOARCH=$(GOHOSTARCH) go run ./cmd/internal/read_tag)
PARAMS = -v -trimpath -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=$(VERSION)' -s -w -buildid="
MAIN_PARAMS = $(PARAMS) -tags "$(TAGS_GO118),$(TAGS_GO120)"
MAIN_PARAMS = $(PARAMS) -tags $(TAGS)
MAIN = ./cmd/sing-box
PREFIX ?= $(shell go env GOPATH)
@@ -57,24 +58,103 @@ proto_install:
go install -v google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install -v google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
snapshot:
go run ./cmd/internal/build goreleaser release --clean --snapshot || exit 1
mkdir dist/release
mv dist/*.tar.gz dist/*.zip dist/*.deb dist/*.rpm dist/release
ghr --delete --draft --prerelease -p 1 nightly dist/release
rm -r dist
release:
go run ./cmd/internal/build goreleaser release --clean --skip-publish || exit 1
mkdir dist/release
mv dist/*.tar.gz dist/*.zip dist/*.deb dist/*.rpm dist/release
ghr --delete --draft --prerelease -p 3 $(shell git describe --tags) dist/release
ghr --replace --draft --prerelease -p 3 "v${VERSION}" dist/release
rm -r dist
release_install:
go install -v github.com/goreleaser/goreleaser@latest
go install -v github.com/tcnksm/ghr@latest
update_android_version:
go run ./cmd/internal/update_android_version
build_android:
cd ../sing-box-for-android && ./gradlew :app:assembleRelease
upload_android:
mkdir -p dist/release_android
cp ../sing-box-for-android/app/build/outputs/apk/release/*.apk dist/release_android
ghr --replace --draft --prerelease -p 3 "v${VERSION}" dist/release_android
rm -rf dist/release_android
release_android: lib_android update_android_version build_android upload_android
publish_android:
cd ../sing-box-for-android && ./gradlew :app:appCenterAssembleAndUploadRelease
build_ios:
cd ../sing-box-for-apple && \
rm -rf build/SFI.xcarchive && \
xcodebuild archive -scheme SFI -configuration Release -archivePath build/SFI.xcarchive
upload_ios_app_store:
cd ../sing-box-for-apple && \
xcodebuild -exportArchive -archivePath build/SFI.xcarchive -exportOptionsPlist SFI/Upload.plist
release_ios: build_ios upload_ios_app_store
build_macos:
cd ../sing-box-for-apple && \
rm -rf build/SFM.xcarchive && \
xcodebuild archive -scheme SFM -configuration Release -archivePath build/SFM.xcarchive
upload_macos_app_store:
cd ../sing-box-for-apple && \
xcodebuild -exportArchive -archivePath build/SFM.xcarchive -exportOptionsPlist SFI/Upload.plist
release_macos: build_macos upload_macos_app_store
build_macos_independent:
cd ../sing-box-for-apple && \
rm -rf build/SFT.System.xcarchive && \
xcodebuild archive -scheme SFM.System -configuration Release -archivePath build/SFM.System.xcarchive
notarize_macos_independent:
cd ../sing-box-for-apple && \
xcodebuild -exportArchive -archivePath "build/SFM.System.xcarchive" -exportOptionsPlist SFM.System/Upload.plist
wait_notarize_macos_independent:
sleep 60
export_macos_independent:
rm -rf dist/SFM
mkdir -p dist/SFM
cd ../sing-box-for-apple && \
xcodebuild -exportNotarizedApp -archivePath build/SFM.System.xcarchive -exportPath "../sing-box/dist/SFM"
upload_macos_independent:
cd dist/SFM && \
rm -f *.zip && \
zip -ry "SFM-${VERSION}-universal.zip" SFM.app && \
ghr --replace --draft --prerelease "v${VERSION}" *.zip
release_macos_independent: build_macos_independent notarize_macos_independent wait_notarize_macos_independent export_macos_independent upload_macos_independent
build_tvos:
cd ../sing-box-for-apple && \
rm -rf build/SFT.xcarchive && \
export DEVELOPER_DIR=/Applications/Xcode-beta.app/Contents/Developer && \
xcodebuild archive -scheme SFT -configuration Release -archivePath build/SFT.xcarchive
upload_tvos_app_store:
cd ../sing-box-for-apple && \
xcodebuild -exportArchive -archivePath "build/SFT.xcarchive" -exportOptionsPlist SFI/Upload.plist
release_tvos: build_tvos upload_tvos_app_store
update_apple_version:
go run ./cmd/internal/update_apple_version
release_apple: lib_ios update_apple_version release_ios release_macos release_tvos release_macos_independent
rm -rf dist
release_apple_beta: update_apple_version release_ios release_macos release_tvos
rm -rf dist
test:
@go test -v ./... && \
cd test && \
@@ -87,10 +167,10 @@ test_stdio:
go mod tidy && \
go test -v -tags "$(TAGS_TEST),force_stdio" .
android:
lib_android:
go run ./cmd/internal/build_libbox -target android
ios:
lib_ios:
go run ./cmd/internal/build_libbox -target ios
lib:

View File

@@ -12,6 +12,7 @@ type ClashServer interface {
Service
PreStarter
Mode() string
ModeList() []string
StoreSelected() bool
StoreFakeIP() bool
CacheFile() ClashCacheFile
@@ -21,6 +22,8 @@ type ClashServer interface {
}
type ClashCacheFile interface {
LoadMode() string
StoreMode(mode string) error
LoadSelected(group string) string
StoreSelected(group string, selected string) error
LoadGroupExpand(group string) (isExpand bool, loaded bool)

View File

@@ -32,6 +32,7 @@ type Router interface {
Exchange(ctx context.Context, message *mdns.Msg) (*mdns.Msg, error)
Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error)
LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
ClearDNSCache()
InterfaceFinder() control.InterfaceFinder
UpdateInterfaces() error

4
box.go
View File

@@ -145,7 +145,9 @@ func New(options Options) (*Box, error) {
preServices := make(map[string]adapter.Service)
postServices := make(map[string]adapter.Service)
if needClashAPI {
clashServer, err := experimental.NewClashServer(ctx, router, logFactory.(log.ObservableFactory), common.PtrValueOrDefault(experimentalOptions.ClashAPI))
clashAPIOptions := common.PtrValueOrDefault(experimentalOptions.ClashAPI)
clashAPIOptions.ModeList = experimental.CalculateClashModeList(options.Options)
clashServer, err := experimental.NewClashServer(ctx, router, logFactory.(log.ObservableFactory), clashAPIOptions)
if err != nil {
return nil, E.Cause(err, "create clash api server")
}

View File

@@ -2,6 +2,7 @@ package build_shared
import (
"github.com/sagernet/sing-box/common/badversion"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/shell"
)
@@ -21,3 +22,15 @@ func ReadTag() (string, error) {
}
return version.String() + "-" + shortCommit, nil
}
func ReadTagVersion() (badversion.Version, error) {
currentTag := common.Must1(shell.Exec("git", "describe", "--tags").ReadOutput())
currentTagRev := common.Must1(shell.Exec("git", "describe", "--tags", "--abbrev=0").ReadOutput())
version := badversion.Parse(currentTagRev[1:])
if currentTagRev != currentTag {
if version.PreReleaseIdentifier == "" {
version.Patch++
}
}
return version, nil
}

View File

@@ -0,0 +1,51 @@
package main
import (
"os"
"path/filepath"
"strconv"
"strings"
"github.com/sagernet/sing-box/cmd/internal/build_shared"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common"
)
func main() {
newVersion := common.Must1(build_shared.ReadTagVersion())
androidPath, err := filepath.Abs("../sing-box-for-android")
if err != nil {
log.Fatal(err)
}
common.Must(os.Chdir(androidPath))
localProps := common.Must1(os.ReadFile("local.properties"))
var propsList [][]string
for _, propLine := range strings.Split(string(localProps), "\n") {
propsList = append(propsList, strings.Split(propLine, "="))
}
for _, propPair := range propsList {
if propPair[0] == "VERSION_NAME" {
if propPair[1] == newVersion.String() {
log.Info("version not changed")
return
}
propPair[1] = newVersion.String()
log.Info("updated version to ", newVersion.String())
}
}
for _, propPair := range propsList {
switch propPair[0] {
case "VERSION_CODE":
versionCode := common.Must1(strconv.ParseInt(propPair[1], 10, 64))
propPair[1] = strconv.Itoa(int(versionCode + 1))
log.Info("updated version code to ", propPair[1])
case "RELEASE_NOTES":
propPair[1] = "sing-box " + newVersion.String()
}
}
var newProps []string
for _, propPair := range propsList {
newProps = append(newProps, strings.Join(propPair, "="))
}
common.Must(os.WriteFile("local.properties", []byte(strings.Join(newProps, "\n")), 0o644))
}

View File

@@ -0,0 +1,80 @@
package main
import (
"os"
"path/filepath"
"regexp"
"strings"
"github.com/sagernet/sing-box/cmd/internal/build_shared"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common"
"howett.net/plist"
)
func main() {
newVersion := common.Must1(build_shared.ReadTagVersion())
applePath, err := filepath.Abs("../sing-box-for-apple")
if err != nil {
log.Fatal(err)
}
common.Must(os.Chdir(applePath))
projectFile := common.Must1(os.Open("sing-box.xcodeproj/project.pbxproj"))
var project map[string]any
decoder := plist.NewDecoder(projectFile)
common.Must(decoder.Decode(&project))
objectsMap := project["objects"].(map[string]any)
projectContent := string(common.Must1(os.ReadFile("sing-box.xcodeproj/project.pbxproj")))
newContent, updated0 := findAndReplace(objectsMap, projectContent, []string{"io.nekohasekai.sfa"}, newVersion.VersionString())
newContent, updated1 := findAndReplace(objectsMap, newContent, []string{"io.nekohasekai.sfa.independent", "io.nekohasekai.sfa.system"}, newVersion.String())
if updated0 || updated1 {
log.Info("updated version to ", newVersion.VersionString())
common.Must(os.WriteFile("sing-box.xcodeproj/project.pbxproj.bak", []byte(projectContent), 0o644))
common.Must(os.WriteFile("sing-box.xcodeproj/project.pbxproj", []byte(newContent), 0o644))
} else {
log.Info("version not changed")
}
}
func findAndReplace(objectsMap map[string]any, projectContent string, bundleIDList []string, newVersion string) (string, bool) {
objectKeyList := findObjectKey(objectsMap, bundleIDList)
var updated bool
for _, objectKey := range objectKeyList {
matchRegexp := common.Must1(regexp.Compile(objectKey + ".*= \\{"))
indexes := matchRegexp.FindStringIndex(projectContent)
if len(indexes) < 2 {
println(projectContent)
log.Fatal("failed to find object key ", objectKey, ": ", strings.Index(projectContent, objectKey))
}
indexStart := indexes[1]
indexEnd := indexStart + strings.Index(projectContent[indexStart:], "}")
versionStart := indexStart + strings.Index(projectContent[indexStart:indexEnd], "MARKETING_VERSION = ") + 20
versionEnd := versionStart + strings.Index(projectContent[versionStart:indexEnd], ";")
version := projectContent[versionStart:versionEnd]
if version == newVersion {
continue
}
updated = true
projectContent = projectContent[:versionStart] + newVersion + projectContent[versionEnd:]
}
return projectContent, updated
}
func findObjectKey(objectsMap map[string]any, bundleIDList []string) []string {
var objectKeyList []string
for objectKey, object := range objectsMap {
buildSettings := object.(map[string]any)["buildSettings"]
if buildSettings == nil {
continue
}
bundleIDObject := buildSettings.(map[string]any)["PRODUCT_BUNDLE_IDENTIFIER"]
if bundleIDObject == nil {
continue
}
if common.Contains(bundleIDList, bundleIDObject.(string)) {
objectKeyList = append(objectKeyList, objectKey)
}
}
return objectKeyList
}

View File

@@ -1,62 +0,0 @@
package baderror
import (
"context"
"io"
"net"
"strings"
E "github.com/sagernet/sing/common/exceptions"
)
func Contains(err error, msgList ...string) bool {
for _, msg := range msgList {
if strings.Contains(err.Error(), msg) {
return true
}
}
return false
}
func WrapH2(err error) error {
if err == nil {
return nil
}
err = E.Unwrap(err)
if err == io.ErrUnexpectedEOF {
return io.EOF
}
if Contains(err, "client disconnected", "body closed by handler", "response body closed", "; CANCEL") {
return net.ErrClosed
}
return err
}
func WrapGRPC(err error) error {
// grpc uses stupid internal error types
if err == nil {
return nil
}
if Contains(err, "EOF") {
return io.EOF
}
if Contains(err, "Canceled") {
return context.Canceled
}
if Contains(err,
"the client connection is closing",
"server closed the stream without sending trailers") {
return net.ErrClosed
}
return err
}
func WrapQUIC(err error) error {
if err == nil {
return nil
}
if Contains(err, "canceled by local with error code 0") {
return net.ErrClosed
}
return err
}

View File

@@ -57,6 +57,10 @@ func (v Version) After(anotherVersion Version) bool {
return false
}
func (v Version) VersionString() string {
return F.ToString(v.Major, ".", v.Minor, ".", v.Patch)
}
func (v Version) String() string {
version := F.ToString(v.Major, ".", v.Minor, ".", v.Patch)
if v.PreReleaseIdentifier != "" {

View File

@@ -13,12 +13,11 @@ import (
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/tfo-go"
)
type DefaultDialer struct {
dialer4 tfo.Dialer
dialer6 tfo.Dialer
dialer4 tcpDialer
dialer6 tcpDialer
udpDialer4 net.Dialer
udpDialer6 net.Dialer
udpListener net.ListenConfig
@@ -94,14 +93,22 @@ func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDi
udpAddr6 = M.SocksaddrFrom(bindAddr, 0).String()
}
if options.TCPMultiPath {
if !multipathTCPAvailable {
if !go121Available {
return nil, E.New("MultiPath TCP requires go1.21, please recompile your binary.")
}
setMultiPathTCP(&dialer4)
}
tcpDialer4, err := newTCPDialer(dialer4, options.TCPFastOpen)
if err != nil {
return nil, err
}
tcpDialer6, err := newTCPDialer(dialer6, options.TCPFastOpen)
if err != nil {
return nil, err
}
return &DefaultDialer{
tfo.Dialer{Dialer: dialer4, DisableTFO: !options.TCPFastOpen},
tfo.Dialer{Dialer: dialer6, DisableTFO: !options.TCPFastOpen},
tcpDialer4,
tcpDialer6,
udpDialer4,
udpDialer6,
listener,

View File

@@ -0,0 +1,15 @@
//go:build go1.20
package dialer
import (
"net"
"github.com/sagernet/tfo-go"
)
type tcpDialer = tfo.Dialer
func newTCPDialer(dialer net.Dialer, tfoEnabled bool) (tcpDialer, error) {
return tfo.Dialer{Dialer: dialer, DisableTFO: !tfoEnabled}, nil
}

View File

@@ -4,7 +4,7 @@ package dialer
import "net"
const multipathTCPAvailable = true
const go121Available = true
func setMultiPathTCP(dialer *net.Dialer) {
dialer.SetMultipathTCP(true)

View File

@@ -0,0 +1,18 @@
//go:build !go1.20
package dialer
import (
"net"
E "github.com/sagernet/sing/common/exceptions"
)
type tcpDialer = net.Dialer
func newTCPDialer(dialer net.Dialer, tfoEnabled bool) (tcpDialer, error) {
if tfoEnabled {
return dialer, E.New("TCP Fast Open requires go1.20, please recompile your binary.")
}
return dialer, nil
}

View File

@@ -6,7 +6,7 @@ import (
"net"
)
const multipathTCPAvailable = false
const go121Available = false
func setMultiPathTCP(dialer *net.Dialer) {
}

View File

@@ -1,3 +1,5 @@
//go:build go1.20
package dialer
import (
@@ -5,6 +7,7 @@ import (
"io"
"net"
"os"
"sync"
"time"
"github.com/sagernet/sing/common"
@@ -22,10 +25,11 @@ type slowOpenConn struct {
destination M.Socksaddr
conn net.Conn
create chan struct{}
access sync.Mutex
err error
}
func DialSlowContext(dialer *tfo.Dialer, ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
func DialSlowContext(dialer *tcpDialer, ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
if dialer.DisableTFO || N.NetworkName(network) != N.NetworkTCP {
switch N.NetworkName(network) {
case N.NetworkTCP, N.NetworkUDP:
@@ -58,15 +62,26 @@ func (c *slowOpenConn) Read(b []byte) (n int, err error) {
}
func (c *slowOpenConn) Write(b []byte) (n int, err error) {
if c.conn == nil {
c.conn, err = c.dialer.DialContext(c.ctx, c.network, c.destination.String(), b)
if err != nil {
c.err = E.Cause(err, "dial tcp fast open")
}
close(c.create)
return
if c.conn != nil {
return c.conn.Write(b)
}
return c.conn.Write(b)
c.access.Lock()
defer c.access.Unlock()
select {
case <-c.create:
if c.err != nil {
return 0, c.err
}
return c.conn.Write(b)
default:
}
c.conn, err = c.dialer.DialContext(c.ctx, c.network, c.destination.String(), b)
if err != nil {
c.conn = nil
c.err = E.Cause(err, "dial tcp fast open")
}
close(c.create)
return
}
func (c *slowOpenConn) Close() error {

20
common/dialer/tfo_stub.go Normal file
View File

@@ -0,0 +1,20 @@
//go:build !go1.20
package dialer
import (
"context"
"net"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
func DialSlowContext(dialer *tcpDialer, ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
switch N.NetworkName(network) {
case N.NetworkTCP, N.NetworkUDP:
return dialer.DialContext(ctx, network, destination.String())
default:
return dialer.DialContext(ctx, network, destination.AddrString())
}
}

View File

@@ -22,23 +22,29 @@ func PeekStream(ctx context.Context, conn net.Conn, buffer *buf.Buffer, timeout
if timeout == 0 {
timeout = C.ReadPayloadTimeout
}
err := conn.SetReadDeadline(time.Now().Add(timeout))
if err != nil {
return nil, E.Cause(err, "set read deadline")
}
_, err = buffer.ReadOnceFrom(conn)
err = E.Errors(err, conn.SetReadDeadline(time.Time{}))
if err != nil {
return nil, E.Cause(err, "read payload")
}
var metadata *adapter.InboundContext
deadline := time.Now().Add(timeout)
var errors []error
for _, sniffer := range sniffers {
metadata, err = sniffer(ctx, bytes.NewReader(buffer.Bytes()))
if metadata != nil {
return metadata, nil
for i := 0; i < 3; i++ {
err := conn.SetReadDeadline(deadline)
if err != nil {
return nil, E.Cause(err, "set read deadline")
}
_, err = buffer.ReadOnceFrom(conn)
err = E.Errors(err, conn.SetReadDeadline(time.Time{}))
if err != nil {
if i > 0 {
break
}
return nil, E.Cause(err, "read payload")
}
for _, sniffer := range sniffers {
metadata, err := sniffer(ctx, bytes.NewReader(buffer.Bytes()))
if metadata != nil {
return metadata, nil
}
errors = append(errors, err)
}
errors = append(errors, err)
}
return nil, E.Errors(errors...)
}

View File

@@ -10,7 +10,6 @@ import (
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/x/list"
)
type History struct {
@@ -21,7 +20,7 @@ type History struct {
type HistoryStorage struct {
access sync.RWMutex
delayHistory map[string]*History
callbacks list.List[func()]
updateHook chan<- struct{}
}
func NewHistoryStorage() *HistoryStorage {
@@ -30,16 +29,8 @@ func NewHistoryStorage() *HistoryStorage {
}
}
func (s *HistoryStorage) AddListener(listener func()) *list.Element[func()] {
s.access.Lock()
defer s.access.Unlock()
return s.callbacks.PushBack(listener)
}
func (s *HistoryStorage) RemoveListener(element *list.Element[func()]) {
s.access.Lock()
defer s.access.Unlock()
s.callbacks.Remove(element)
func (s *HistoryStorage) SetHook(hook chan<- struct{}) {
s.updateHook = hook
}
func (s *HistoryStorage) LoadURLTestHistory(tag string) *History {
@@ -66,13 +57,20 @@ func (s *HistoryStorage) StoreURLTestHistory(tag string, history *History) {
}
func (s *HistoryStorage) notifyUpdated() {
s.access.RLock()
defer s.access.RUnlock()
for element := s.callbacks.Front(); element != nil; element = element.Next() {
element.Value()
updateHook := s.updateHook
if updateHook != nil {
select {
case updateHook <- struct{}{}:
default:
}
}
}
func (s *HistoryStorage) Close() error {
s.updateHook = nil
return nil
}
func URLTest(ctx context.Context, link string, detour N.Dialer) (t uint16, err error) {
if link == "" {
link = "https://www.gstatic.com/generate_204"

View File

@@ -1,3 +1,99 @@
#### 1.4.1
* Fixes and improvements
#### 1.4.0
* Fix bugs and update dependencies
Important changes since 1.3:
* Add TUIC support **1**
* Add `udp_over_stream` option for TUIC client **2**
* Add MultiPath TCP support **3**
* Add `include_interface` and `exclude_interface` options for tun inbound
* Pause recurring tasks when no network or device idle
* Improve Android and Apple platform clients
*1*:
See [TUIC inbound](/configuration/inbound/tuic)
and [TUIC outbound](/configuration/outbound/tuic)
**2**:
This is the TUIC port of the [UDP over TCP protocol](/configuration/shared/udp-over-tcp), designed to provide a QUIC
stream based UDP relay mode that TUIC does not provide. Since it is an add-on protocol, you will need to use sing-box or
another program compatible with the protocol as a server.
This mode has no positive effect in a proper UDP proxy scenario and should only be applied to relay streaming UDP
traffic (basically QUIC streams).
*3*:
Requires sing-box to be compiled with Go 1.21.
#### 1.4.0-rc.3
* Fixes and improvements
#### 1.4.0-rc.2
* Fixes and improvements
#### 1.4.0-rc.1
* Fix TUIC UDP
#### 1.4.0-beta.6
* Add `udp_over_stream` option for TUIC client **1**
* Add `include_interface` and `exclude_interface` options for tun inbound
* Fixes and improvements
**1**:
This is the TUIC port of the [UDP over TCP protocol](/configuration/shared/udp-over-tcp), designed to provide a QUIC
stream based UDP relay mode that TUIC does not provide. Since it is an add-on protocol, you will need to use sing-box or
another program compatible with the protocol as a server.
This mode has no positive effect in a proper UDP proxy scenario and should only be applied to relay streaming UDP
traffic (basically QUIC streams).
#### 1.4.0-beta.5
* Fixes and improvements
#### 1.4.0-beta.4
* Graphical clients: Persistence group expansion state
* Fixes and improvements
#### 1.4.0-beta.3
* Fixes and improvements
#### 1.4.0-beta.2
* Add MultiPath TCP support **1**
* Drop QUIC support for Go 1.18 and 1.19 due to upstream changes
* Fixes and improvements
*1*:
Requires sing-box to be compiled with Go 1.21.
#### 1.4.0-beta.1
* Add TUIC support **1**
* Pause recurring tasks when no network or device idle
* Fixes and improvements
*1*:
See [TUIC inbound](/configuration/inbound/tuic)
and [TUIC outbound](/configuration/outbound/tuic)
#### 1.3.6
* Fixes and improvements
@@ -11,13 +107,16 @@
**1**:
Due to the requirement of tvOS 17, the app cannot be submitted to the App Store for the time being, and can only be downloaded through TestFlight.
Due to the requirement of tvOS 17, the app cannot be submitted to the App Store for the time being, and can only be
downloaded through TestFlight.
#### 1.3.4
* Fixes and improvements
* We're now on the [App Store](https://apps.apple.com/us/app/sing-box/id6451272673), always free! It should be noted that due to stricter and slower review, the release of Store versions will be delayed.
* We've made a standalone version of the macOS client (the original Application Extension relies on App Store distribution), which you can download as SFM-version-universal.zip in the release artifacts.
* We're now on the [App Store](https://apps.apple.com/us/app/sing-box/id6451272673), always free! It should be noted
that due to stricter and slower review, the release of Store versions will be delayed.
* We've made a standalone version of the macOS client (the original Application Extension relies on App Store
distribution), which you can download as SFM-version-universal.zip in the release artifacts.
#### 1.3.3

View File

@@ -12,7 +12,9 @@
"external_ui_download_detour": "",
"secret": "",
"default_mode": "",
"store_mode": false,
"store_selected": false,
"store_fakeip": false,
"cache_file": "",
"cache_id": ""
},
@@ -80,6 +82,10 @@ Default mode in clash, `rule` will be used if empty.
This setting has no direct effect, but can be used in routing and DNS rules via the `clash_mode` rule item.
#### store_mode
Store Clash mode in cache file.
#### store_selected
!!! note ""
@@ -88,6 +94,10 @@ This setting has no direct effect, but can be used in routing and DNS rules via
Store selected outbound for the `Selector` outbound in cache file.
#### store_fakeip
Store fakeip in cache file.
#### cache_file
Cache file path, `cache.db` will be used if empty.

View File

@@ -12,7 +12,9 @@
"external_ui_download_detour": "",
"secret": "",
"default_mode": "",
"store_mode": false,
"store_selected": false,
"store_fakeip": false,
"cache_file": "",
"cache_id": ""
},
@@ -78,6 +80,10 @@ Clash 中的默认模式,默认使用 `rule`。
此设置没有直接影响,但可以通过 `clash_mode` 规则项在路由和 DNS 规则中使用。
#### store_mode
将 Clash 模式存储在缓存文件中。
#### store_selected
!!! note ""
@@ -86,6 +92,10 @@ Clash 中的默认模式,默认使用 `rule`。
`Selector` 中出站的选定的目标出站存储在缓存文件中。
#### store_fakeip
将 fakeip 存储在缓存文件中。
#### cache_file
缓存文件路径,默认使用`cache.db`

View File

@@ -24,6 +24,12 @@
],
"endpoint_independent_nat": false,
"stack": "system",
"include_interface": [
"lan0"
],
"exclude_interface": [
"lan1"
],
"include_uid": [
0
],
@@ -153,6 +159,22 @@ TCP/IP stack.
gVisor and LWIP stacks is not included by default, see [Installation](/#installation).
#### include_interface
!!! error ""
Interface rules are only supported on Linux and require auto_route.
Limit interfaces in route. Not limited by default.
Conflict with `exclude_interface`.
#### exclude_interface
Exclude interfaces in route.
Conflict with `include_interface`.
#### include_uid
!!! error ""

View File

@@ -24,6 +24,12 @@
],
"endpoint_independent_nat": false,
"stack": "system",
"include_interface": [
"lan0"
],
"exclude_interface": [
"lan1"
],
"include_uid": [
0
],
@@ -149,6 +155,22 @@ TCP/IP 栈。
默认安装不包含 gVisor 和 LWIP 栈,请参阅 [安装](/zh/#_2)。
#### include_interface
!!! error ""
接口规则仅在 Linux 下被支持,并且需要 `auto_route`
限制被路由的接口。默认不限制。
`exclude_interface` 冲突。
#### exclude_interface
排除路由的接口。
`include_interface` 冲突。
#### include_uid
!!! error ""

View File

@@ -11,6 +11,7 @@
"password": "hello",
"congestion_control": "cubic",
"udp_relay_mode": "native",
"udp_over_stream": false,
"zero_rtt_handshake": false,
"heartbeat": "10s",
"network": "tcp",
@@ -67,6 +68,19 @@ UDP packet relay mode
`native` is used by default.
Conflict with `udp_over_stream`.
#### udp_over_stream
This is the TUIC port of the [UDP over TCP protocol](/configuration/shared/udp-over-tcp), designed to provide a QUIC
stream based UDP relay mode that TUIC does not provide. Since it is an add-on protocol, you will need to use sing-box or
another program compatible with the protocol as a server.
This mode has no positive effect in a proper UDP proxy scenario and should only be applied to relay streaming UDP
traffic (basically QUIC streams).
Conflict with `udp_relay_mode`.
#### network
Enabled network

View File

@@ -11,6 +11,7 @@
"password": "hello",
"congestion_control": "cubic",
"udp_relay_mode": "native",
"udp_over_stream": false,
"zero_rtt_handshake": false,
"heartbeat": "10s",
"network": "tcp",
@@ -65,6 +66,15 @@ UDP 包中继模式
| native | 原生 UDP |
| quic | 使用 QUIC 流的无损 UDP 中继,引入了额外的开销 |
`udp_over_stream` 冲突。
#### udp_over_stream
这是 TUIC 的 [UDP over TCP 协议](/configuration/shared/udp-over-tcp) 移植, 旨在提供 TUIC 不提供的 基于 QUIC 流的 UDP 中继模式。 由于它是一个附加协议,因此您需要使用 sing-box 或其他兼容的程序作为服务器。
此模式在正确的 UDP 代理场景中没有任何积极作用,仅适用于中继流式 UDP 流量(基本上是 QUIC 流)。
`udp_relay_mode` 冲突。
#### zero_rtt_handshake

View File

@@ -7,6 +7,7 @@ import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
)
type ClashServerConstructor = func(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error)
@@ -23,3 +24,28 @@ func NewClashServer(ctx context.Context, router adapter.Router, logFactory log.O
}
return clashServerConstructor(ctx, router, logFactory, options)
}
func CalculateClashModeList(options option.Options) []string {
var clashMode []string
for _, dnsRule := range common.PtrValueOrDefault(options.DNS).Rules {
if dnsRule.DefaultOptions.ClashMode != "" && !common.Contains(clashMode, dnsRule.DefaultOptions.ClashMode) {
clashMode = append(clashMode, dnsRule.DefaultOptions.ClashMode)
}
for _, defaultRule := range dnsRule.LogicalOptions.Rules {
if defaultRule.ClashMode != "" && !common.Contains(clashMode, defaultRule.ClashMode) {
clashMode = append(clashMode, defaultRule.ClashMode)
}
}
}
for _, rule := range common.PtrValueOrDefault(options.Route).Rules {
if rule.DefaultOptions.ClashMode != "" && !common.Contains(clashMode, rule.DefaultOptions.ClashMode) {
clashMode = append(clashMode, rule.DefaultOptions.ClashMode)
}
for _, defaultRule := range rule.LogicalOptions.Rules {
if defaultRule.ClashMode != "" && !common.Contains(clashMode, defaultRule.ClashMode) {
clashMode = append(clashMode, defaultRule.ClashMode)
}
}
}
return clashMode
}

View File

@@ -8,6 +8,7 @@ import (
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing/common"
"go.etcd.io/bbolt"
)
@@ -15,6 +16,15 @@ import (
var (
bucketSelected = []byte("selected")
bucketExpand = []byte("group_expand")
bucketMode = []byte("clash_mode")
bucketNameList = []string{
string(bucketSelected),
string(bucketExpand),
string(bucketMode),
}
cacheIDDefault = []byte("default")
)
var _ adapter.ClashCacheFile = (*CacheFile)(nil)
@@ -52,14 +62,14 @@ func Open(path string, cacheID string) (*CacheFile, error) {
if name[0] == 0 {
return b.ForEachBucket(func(k []byte) error {
bucketName := string(k)
if !(bucketName == string(bucketSelected) || bucketName == string(bucketExpand)) {
if !(common.Contains(bucketNameList, bucketName)) {
_ = b.DeleteBucket(name)
}
return nil
})
} else {
bucketName := string(name)
if !(bucketName == string(bucketSelected) || bucketName == string(bucketExpand) || strings.HasPrefix(bucketName, fakeipBucketPrefix)) {
if !(common.Contains(bucketNameList, bucketName) || strings.HasPrefix(bucketName, fakeipBucketPrefix)) {
_ = tx.DeleteBucket(name)
}
}
@@ -78,6 +88,39 @@ func Open(path string, cacheID string) (*CacheFile, error) {
}, nil
}
func (c *CacheFile) LoadMode() string {
var mode string
c.DB.View(func(t *bbolt.Tx) error {
bucket := t.Bucket(bucketMode)
if bucket == nil {
return nil
}
var modeBytes []byte
if len(c.cacheID) > 0 {
modeBytes = bucket.Get(c.cacheID)
} else {
modeBytes = bucket.Get(cacheIDDefault)
}
mode = string(modeBytes)
return nil
})
return mode
}
func (c *CacheFile) StoreMode(mode string) error {
return c.DB.Batch(func(t *bbolt.Tx) error {
bucket, err := t.CreateBucketIfNotExists(bucketMode)
if err != nil {
return err
}
if len(c.cacheID) > 0 {
return bucket.Put(c.cacheID, []byte(mode))
} else {
return bucket.Put(cacheIDDefault, []byte(mode))
}
})
}
func (c *CacheFile) bucket(t *bbolt.Tx, key []byte) *bbolt.Bucket {
if c.cacheID == nil {
return t.Bucket(key)

View File

@@ -2,7 +2,6 @@ package clashapi
import (
"net/http"
"strings"
"github.com/sagernet/sing-box/log"
@@ -10,11 +9,11 @@ import (
"github.com/go-chi/render"
)
func configRouter(server *Server, logFactory log.Factory, logger log.Logger) http.Handler {
func configRouter(server *Server, logFactory log.Factory) http.Handler {
r := chi.NewRouter()
r.Get("/", getConfigs(server, logFactory))
r.Put("/", updateConfigs)
r.Patch("/", patchConfigs(server, logger))
r.Patch("/", patchConfigs(server))
return r
}
@@ -48,7 +47,7 @@ func getConfigs(server *Server, logFactory log.Factory) func(w http.ResponseWrit
}
}
func patchConfigs(server *Server, logger log.Logger) func(w http.ResponseWriter, r *http.Request) {
func patchConfigs(server *Server) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
var newConfig configSchema
err := render.DecodeJSON(r.Body, &newConfig)
@@ -58,11 +57,7 @@ func patchConfigs(server *Server, logger log.Logger) func(w http.ResponseWriter,
return
}
if newConfig.Mode != "" {
mode := strings.ToLower(newConfig.Mode)
if server.mode != mode {
server.mode = mode
logger.Info("updated mode: ", mode)
}
server.SetMode(newConfig.Mode)
}
render.NoContent(w, r)
}

View File

@@ -46,6 +46,9 @@ type Server struct {
trafficManager *trafficontrol.Manager
urlTestHistory *urltest.HistoryStorage
mode string
modeList []string
modeUpdateHook chan<- struct{}
storeMode bool
storeSelected bool
storeFakeIP bool
cacheFilePath string
@@ -70,9 +73,10 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
Handler: chiRouter,
},
trafficManager: trafficManager,
mode: strings.ToLower(options.DefaultMode),
storeSelected: options.StoreSelected,
modeList: options.ModeList,
externalController: options.ExternalController != "",
storeMode: options.StoreMode,
storeSelected: options.StoreSelected,
storeFakeIP: options.StoreFakeIP,
externalUIDownloadURL: options.ExternalUIDownloadURL,
externalUIDownloadDetour: options.ExternalUIDownloadDetour,
@@ -81,10 +85,15 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
if server.urlTestHistory == nil {
server.urlTestHistory = urltest.NewHistoryStorage()
}
if server.mode == "" {
server.mode = "rule"
defaultMode := "Rule"
if options.DefaultMode != "" {
defaultMode = options.DefaultMode
}
if options.StoreSelected || options.StoreFakeIP || options.ExternalController == "" {
if !common.Contains(server.modeList, defaultMode) {
server.modeList = append([]string{defaultMode}, server.modeList...)
}
server.mode = defaultMode
if options.StoreMode || options.StoreSelected || options.StoreFakeIP || options.ExternalController == "" {
cachePath := os.ExpandEnv(options.CacheFile)
if cachePath == "" {
cachePath = "cache.db"
@@ -110,7 +119,7 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
r.Get("/logs", getLogs(logFactory))
r.Get("/traffic", traffic(trafficManager))
r.Get("/version", version)
r.Mount("/configs", configRouter(server, logFactory, server.logger))
r.Mount("/configs", configRouter(server, logFactory))
r.Mount("/proxies", proxyRouter(server, router))
r.Mount("/rules", ruleRouter(router))
r.Mount("/connections", connectionRouter(router, trafficManager))
@@ -143,6 +152,14 @@ func (s *Server) PreStart() error {
return E.Cause(err, "open cache file")
}
s.cacheFile = cacheFile
if s.storeMode {
mode := s.cacheFile.LoadMode()
if common.Any(s.modeList, func(it string) bool {
return strings.EqualFold(it, mode)
}) {
s.mode = mode
}
}
}
return nil
}
@@ -170,6 +187,7 @@ func (s *Server) Close() error {
common.PtrOrNil(s.httpServer),
s.trafficManager,
s.cacheFile,
s.urlTestHistory,
)
}
@@ -177,6 +195,43 @@ func (s *Server) Mode() string {
return s.mode
}
func (s *Server) ModeList() []string {
return s.modeList
}
func (s *Server) SetModeUpdateHook(hook chan<- struct{}) {
s.modeUpdateHook = hook
}
func (s *Server) SetMode(newMode string) {
if !common.Contains(s.modeList, newMode) {
newMode = common.Find(s.modeList, func(it string) bool {
return strings.EqualFold(it, newMode)
})
}
if !common.Contains(s.modeList, newMode) {
return
}
if newMode == s.mode {
return
}
s.mode = newMode
if s.modeUpdateHook != nil {
select {
case s.modeUpdateHook <- struct{}{}:
default:
}
}
s.router.ClearDNSCache()
if s.storeMode {
err := s.cacheFile.StoreMode(newMode)
if err != nil {
s.logger.Error(E.Cause(err, "save mode"))
}
}
s.logger.Info("updated mode: ", newMode)
}
func (s *Server) StoreSelected() bool {
return s.storeSelected
}

View File

@@ -9,4 +9,8 @@ const (
CommandSelectOutbound
CommandURLTest
CommandGroupExpand
CommandClashMode
CommandSetClashMode
CommandGetSystemProxyStatus
CommandSetSystemProxyEnabled
)

View File

@@ -0,0 +1,135 @@
package libbox
import (
"encoding/binary"
"io"
"net"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental/clashapi"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/rw"
)
func (c *CommandClient) SetClashMode(newMode string) error {
conn, err := c.directConnect()
if err != nil {
return err
}
defer conn.Close()
err = binary.Write(conn, binary.BigEndian, uint8(CommandSetClashMode))
if err != nil {
return err
}
err = rw.WriteVString(conn, newMode)
if err != nil {
return err
}
return readError(conn)
}
func (s *CommandServer) handleSetClashMode(conn net.Conn) error {
defer conn.Close()
newMode, err := rw.ReadVString(conn)
if err != nil {
return err
}
service := s.service
if service == nil {
return writeError(conn, E.New("service not ready"))
}
clashServer := service.instance.Router().ClashServer()
if clashServer == nil {
return writeError(conn, E.New("Clash API disabled"))
}
clashServer.(*clashapi.Server).SetMode(newMode)
return writeError(conn, nil)
}
func (c *CommandClient) handleModeConn(conn net.Conn) {
defer conn.Close()
for {
newMode, err := rw.ReadVString(conn)
if err != nil {
c.handler.Disconnected(err.Error())
return
}
c.handler.UpdateClashMode(newMode)
}
}
func (s *CommandServer) handleModeConn(conn net.Conn) error {
defer conn.Close()
ctx := connKeepAlive(conn)
for s.service == nil {
select {
case <-time.After(time.Second):
continue
case <-ctx.Done():
return ctx.Err()
}
}
clashServer := s.service.instance.Router().ClashServer()
if clashServer == nil {
defer conn.Close()
return binary.Write(conn, binary.BigEndian, uint16(0))
}
err := writeClashModeList(conn, clashServer)
if err != nil {
return err
}
for {
select {
case <-s.modeUpdate:
err = rw.WriteVString(conn, clashServer.Mode())
if err != nil {
return err
}
case <-ctx.Done():
return ctx.Err()
}
}
}
func readClashModeList(reader io.Reader) (modeList []string, currentMode string, err error) {
var modeListLength uint16
err = binary.Read(reader, binary.BigEndian, &modeListLength)
if err != nil {
return
}
if modeListLength == 0 {
return
}
modeList = make([]string, modeListLength)
for i := 0; i < int(modeListLength); i++ {
modeList[i], err = rw.ReadVString(reader)
if err != nil {
return
}
}
currentMode, err = rw.ReadVString(reader)
return
}
func writeClashModeList(writer io.Writer, clashServer adapter.ClashServer) error {
modeList := clashServer.ModeList()
err := binary.Write(writer, binary.BigEndian, uint16(len(modeList)))
if err != nil {
return err
}
if len(modeList) > 0 {
for _, mode := range modeList {
err = rw.WriteVString(writer, mode)
if err != nil {
return err
}
}
err = rw.WriteVString(writer, clashServer.Mode())
if err != nil {
return err
}
}
return nil
}

View File

@@ -3,7 +3,9 @@ package libbox
import (
"encoding/binary"
"net"
"os"
"path/filepath"
"time"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
@@ -26,13 +28,15 @@ type CommandClientHandler interface {
WriteLog(message string)
WriteStatus(message *StatusMessage)
WriteGroups(message OutboundGroupIterator)
InitializeClashMode(modeList StringIterator, currentMode string)
UpdateClashMode(newMode string)
}
func NewStandaloneCommandClient() *CommandClient {
return new(CommandClient)
}
func NewCommandClient(sharedDirectory string, handler CommandClientHandler, options *CommandClientOptions) *CommandClient {
func NewCommandClient(handler CommandClientHandler, options *CommandClientOptions) *CommandClient {
return &CommandClient{
handler: handler,
options: common.PtrValueOrDefault(options),
@@ -50,9 +54,24 @@ func (c *CommandClient) directConnect() (net.Conn, error) {
}
}
func (c *CommandClient) directConnectWithRetry() (net.Conn, error) {
var (
conn net.Conn
err error
)
for i := 0; i < 10; i++ {
conn, err = c.directConnect()
if err == nil {
return conn, nil
}
time.Sleep(time.Duration(100+i*50) * time.Millisecond)
}
return nil, err
}
func (c *CommandClient) Connect() error {
common.Close(c.conn)
conn, err := c.directConnect()
conn, err := c.directConnectWithRetry()
if err != nil {
return err
}
@@ -79,6 +98,23 @@ func (c *CommandClient) Connect() error {
}
c.handler.Connected()
go c.handleGroupConn(conn)
case CommandClashMode:
var (
modeList []string
currentMode string
)
modeList, currentMode, err = readClashModeList(conn)
if err != nil {
return err
}
c.handler.Connected()
c.handler.InitializeClashMode(newIterator(modeList), currentMode)
if len(modeList) == 0 {
conn.Close()
c.handler.Disconnected(os.ErrInvalid.Error())
return nil
}
go c.handleModeConn(conn)
}
return nil
}

View File

@@ -19,7 +19,7 @@ type OutboundGroup struct {
Type string
Selectable bool
Selected string
isExpand int8
IsExpand bool
items []*OutboundGroupItem
}
@@ -27,19 +27,6 @@ func (g *OutboundGroup) GetItems() OutboundGroupItemIterator {
return newIterator(g.items)
}
func (g *OutboundGroup) IsExpand() bool {
switch g.isExpand {
case -1:
return g.Selectable
case 0:
return false
case 1:
return true
default:
panic("unexpected expand value")
}
}
type OutboundGroupIterator interface {
Next() *OutboundGroup
HasNext() bool
@@ -129,7 +116,7 @@ func readGroups(reader io.Reader) (OutboundGroupIterator, error) {
return nil, err
}
err = binary.Read(reader, binary.BigEndian, &group.isExpand)
err = binary.Read(reader, binary.BigEndian, &group.IsExpand)
if err != nil {
return nil, err
}
@@ -192,12 +179,8 @@ func writeGroups(writer io.Writer, boxService *BoxService) error {
_, group.Selectable = iGroup.(*outbound.Selector)
group.Selected = iGroup.Now()
if cacheFile != nil {
if isExpand, loaded := cacheFile.LoadGroupExpand(group.Tag); !loaded {
group.isExpand = -1
} else if isExpand {
group.isExpand = 1
} else {
group.isExpand = 0
if isExpand, loaded := cacheFile.LoadGroupExpand(group.Tag); loaded {
group.IsExpand = isExpand
}
}
@@ -216,6 +199,9 @@ func writeGroups(writer io.Writer, boxService *BoxService) error {
}
group.items = append(group.items, &item)
}
if len(group.items) < 2 {
continue
}
groups = append(groups, group)
}
@@ -240,7 +226,7 @@ func writeGroups(writer io.Writer, boxService *BoxService) error {
if err != nil {
return err
}
err = binary.Write(writer, binary.BigEndian, group.isExpand)
err = binary.Write(writer, binary.BigEndian, group.IsExpand)
if err != nil {
return err
}

View File

@@ -8,6 +8,7 @@ import (
"sync"
"github.com/sagernet/sing-box/common/urltest"
"github.com/sagernet/sing-box/experimental/clashapi"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/debug"
@@ -28,12 +29,14 @@ type CommandServer struct {
observer *observable.Observer[string]
service *BoxService
urlTestListener *list.Element[func()]
urlTestUpdate chan struct{}
urlTestUpdate chan struct{}
modeUpdate chan struct{}
}
type CommandServerHandler interface {
ServiceReload() error
GetSystemProxyStatus() *SystemProxyStatus
SetSystemProxyEnabled(isEnabled bool) error
}
func NewCommandServer(handler CommandServerHandler, maxLines int32) *CommandServer {
@@ -43,20 +46,18 @@ func NewCommandServer(handler CommandServerHandler, maxLines int32) *CommandServ
maxLines: int(maxLines),
subscriber: observable.NewSubscriber[string](128),
urlTestUpdate: make(chan struct{}, 1),
modeUpdate: make(chan struct{}, 1),
}
server.observer = observable.NewObserver[string](server.subscriber, 64)
return server
}
func (s *CommandServer) SetService(newService *BoxService) {
if s.service != nil && s.listener != nil {
service.PtrFromContext[urltest.HistoryStorage](s.service.ctx).RemoveListener(s.urlTestListener)
s.urlTestListener = nil
if newService != nil {
service.PtrFromContext[urltest.HistoryStorage](newService.ctx).SetHook(s.urlTestUpdate)
newService.instance.Router().ClashServer().(*clashapi.Server).SetModeUpdateHook(s.modeUpdate)
}
s.service = newService
if newService != nil {
s.urlTestListener = service.PtrFromContext[urltest.HistoryStorage](newService.ctx).AddListener(s.notifyURLTestUpdate)
}
s.notifyURLTestUpdate()
}
@@ -156,6 +157,14 @@ func (s *CommandServer) handleConnection(conn net.Conn) error {
return s.handleURLTest(conn)
case CommandGroupExpand:
return s.handleSetGroupExpand(conn)
case CommandClashMode:
return s.handleModeConn(conn)
case CommandSetClashMode:
return s.handleSetClashMode(conn)
case CommandGetSystemProxyStatus:
return s.handleGetSystemProxyStatus(conn)
case CommandSetSystemProxyEnabled:
return s.handleSetSystemProxyEnabled(conn)
default:
return E.New("unknown command: ", command)
}

View File

@@ -0,0 +1,82 @@
package libbox
import (
"encoding/binary"
"net"
)
type SystemProxyStatus struct {
Available bool
Enabled bool
}
func (c *CommandClient) GetSystemProxyStatus() (*SystemProxyStatus, error) {
conn, err := c.directConnectWithRetry()
if err != nil {
return nil, err
}
defer conn.Close()
err = binary.Write(conn, binary.BigEndian, uint8(CommandGetSystemProxyStatus))
if err != nil {
return nil, err
}
var status SystemProxyStatus
err = binary.Read(conn, binary.BigEndian, &status.Available)
if err != nil {
return nil, err
}
if status.Available {
err = binary.Read(conn, binary.BigEndian, &status.Enabled)
if err != nil {
return nil, err
}
}
return &status, nil
}
func (s *CommandServer) handleGetSystemProxyStatus(conn net.Conn) error {
defer conn.Close()
status := s.handler.GetSystemProxyStatus()
err := binary.Write(conn, binary.BigEndian, status.Available)
if err != nil {
return err
}
if status.Available {
err = binary.Write(conn, binary.BigEndian, status.Enabled)
if err != nil {
return err
}
}
return nil
}
func (c *CommandClient) SetSystemProxyEnabled(isEnabled bool) error {
conn, err := c.directConnect()
if err != nil {
return err
}
defer conn.Close()
err = binary.Write(conn, binary.BigEndian, uint8(CommandSetSystemProxyEnabled))
if err != nil {
return err
}
err = binary.Write(conn, binary.BigEndian, isEnabled)
if err != nil {
return err
}
return readError(conn)
}
func (s *CommandServer) handleSetSystemProxyEnabled(conn net.Conn) error {
defer conn.Close()
var isEnabled bool
err := binary.Read(conn, binary.BigEndian, &isEnabled)
if err != nil {
return err
}
err = s.handler.SetSystemProxyEnabled(isEnabled)
if err != nil {
return writeError(conn, err)
}
return writeError(conn, nil)
}

View File

@@ -49,6 +49,9 @@ func (p *platformLocalDNSTransport) Start() error {
return nil
}
func (p *platformLocalDNSTransport) Reset() {
}
func (p *platformLocalDNSTransport) Close() error {
return nil
}

View File

@@ -4,6 +4,7 @@ package libbox
import (
"os"
"runtime"
"golang.org/x/sys/unix"
)
@@ -18,12 +19,14 @@ func RedirectStderr(path string) error {
if err != nil {
return err
}
if sUserID > 0 {
err = outputFile.Chown(sUserID, sGroupID)
if err != nil {
outputFile.Close()
os.Remove(outputFile.Name())
return err
if runtime.GOOS != "android" {
if sUserID > 0 {
err = outputFile.Chown(sUserID, sGroupID)
if err != nil {
outputFile.Close()
os.Remove(outputFile.Name())
return err
}
}
}
err = unix.Dup2(int(outputFile.Fd()), int(os.Stderr.Fd()))

View File

@@ -19,6 +19,7 @@ type PlatformInterface interface {
UsePlatformInterfaceGetter() bool
GetInterfaces() (NetworkInterfaceIterator, error)
UnderNetworkExtension() bool
ClearDNSCache()
}
type TunInterface interface {

View File

@@ -23,6 +23,7 @@ type Interface interface {
UsePlatformInterfaceGetter() bool
Interfaces() ([]NetworkInterface, error)
UnderNetworkExtension() bool
ClearDNSCache()
process.Searcher
io.Writer
}

View File

@@ -25,10 +25,11 @@ import (
)
type BoxService struct {
ctx context.Context
cancel context.CancelFunc
instance *box.Box
pauseManager pause.Manager
ctx context.Context
cancel context.CancelFunc
instance *box.Box
pauseManager pause.Manager
urlTestHistoryStorage *urltest.HistoryStorage
}
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
@@ -39,9 +40,10 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
runtimeDebug.FreeOSMemory()
ctx, cancel := context.WithCancel(context.Background())
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
ctx = service.ContextWithPtr(ctx, urltest.NewHistoryStorage())
sleepManager := pause.NewDefaultManager(ctx)
ctx = pause.ContextWithManager(ctx, sleepManager)
urlTestHistoryStorage := urltest.NewHistoryStorage()
ctx = service.ContextWithPtr(ctx, urlTestHistoryStorage)
pauseManager := pause.NewDefaultManager(ctx)
ctx = pause.ContextWithManager(ctx, pauseManager)
instance, err := box.New(box.Options{
Context: ctx,
Options: options,
@@ -53,10 +55,11 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
}
runtimeDebug.FreeOSMemory()
return &BoxService{
ctx: ctx,
cancel: cancel,
instance: instance,
pauseManager: sleepManager,
ctx: ctx,
cancel: cancel,
instance: instance,
urlTestHistoryStorage: urlTestHistoryStorage,
pauseManager: pauseManager,
}, nil
}
@@ -66,6 +69,7 @@ func (s *BoxService) Start() error {
func (s *BoxService) Close() error {
s.cancel()
s.urlTestHistoryStorage.Close()
return s.instance.Close()
}
@@ -194,3 +198,7 @@ func (w *platformInterfaceWrapper) Interfaces() ([]platform.NetworkInterface, er
func (w *platformInterfaceWrapper) UnderNetworkExtension() bool {
return w.iif.UnderNetworkExtension()
}
func (w *platformInterfaceWrapper) ClearDNSCache() {
w.iif.ClearDNSCache()
}

37
go.mod
View File

@@ -1,11 +1,11 @@
module github.com/sagernet/sing-box
go 1.18
go 1.20
require (
berty.tech/go-libtor v1.0.385
github.com/Dreamacro/clash v1.17.0
github.com/caddyserver/certmagic v0.19.1
github.com/caddyserver/certmagic v0.19.2
github.com/cretz/bine v0.2.0
github.com/dustin/go-humanize v1.0.1
github.com/fsnotify/fsnotify v1.6.0
@@ -13,7 +13,7 @@ require (
github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.3
github.com/gofrs/uuid/v5 v5.0.0
github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c
github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/mholt/acmez v1.2.0
github.com/miekg/dns v1.1.55
@@ -23,18 +23,18 @@ require (
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0
github.com/sagernet/gomobile v0.0.0-20230728014906-3de089147f59
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2
github.com/sagernet/quic-go v0.0.0-20230811130919-d6f54a117913
github.com/sagernet/quic-go v0.0.0-20230825040534-0cd917b2ddda
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a
github.com/sagernet/sing-dns v0.1.9-0.20230731012726-ad50da89b659
github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c
github.com/sagernet/sing-shadowsocks v0.2.4
github.com/sagernet/sing-shadowsocks2 v0.1.3
github.com/sagernet/sing v0.2.10-0.20230907044649-03c21c0a1205
github.com/sagernet/sing-dns v0.1.9-0.20230824120133-4d5cbceb40c1
github.com/sagernet/sing-mux v0.1.3-0.20230907005326-7befbadbf314
github.com/sagernet/sing-shadowsocks v0.2.5-0.20230907005610-126234728ca0
github.com/sagernet/sing-shadowsocks2 v0.1.4-0.20230907005906-5d2917b29248
github.com/sagernet/sing-shadowtls v0.1.4
github.com/sagernet/sing-tun v0.1.12-0.20230812113806-10d98f26797a
github.com/sagernet/sing-vmess v0.1.7
github.com/sagernet/sing-tun v0.1.12-0.20230821065522-7545dc2d5641
github.com/sagernet/sing-vmess v0.1.8-0.20230907010359-161fb0ac716b
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f
@@ -42,14 +42,15 @@ require (
github.com/stretchr/testify v1.8.4
go.etcd.io/bbolt v1.3.7
go.uber.org/zap v1.25.0
go4.org/netipx v0.0.0-20230728184502-ec4c8b891b28
go4.org/netipx v0.0.0-20230824141953-6213f710f925
golang.org/x/crypto v0.12.0
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63
golang.org/x/net v0.14.0
golang.org/x/sys v0.11.0
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
google.golang.org/grpc v1.57.0
google.golang.org/protobuf v1.31.0
howett.net/plist v1.0.0
)
//replace github.com/sagernet/sing => ../sing
@@ -60,7 +61,7 @@ require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/cloudflare/circl v1.2.1-0.20221019164342-6ab4dfed8f3c // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
@@ -77,7 +78,7 @@ require (
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.3.1 // indirect
github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect
@@ -86,10 +87,10 @@ require (
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.10.0 // indirect
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

74
go.sum
View File

@@ -9,8 +9,8 @@ github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/caddyserver/certmagic v0.19.1 h1:4jyOYm2DHvQI8YM0sk6qm62Gl5XznHxiMBMWjMTlQkw=
github.com/caddyserver/certmagic v0.19.1/go.mod h1:fsL01NomQ6N+kE2j37ZCnig2MFosG+MIO4ztnmG/zz8=
github.com/caddyserver/certmagic v0.19.2 h1:HZd1AKLx4592MalEGQS39DKs2ZOAJCEM/xYPMQ2/ui0=
github.com/caddyserver/certmagic v0.19.2/go.mod h1:fsL01NomQ6N+kE2j37ZCnig2MFosG+MIO4ztnmG/zz8=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -34,8 +34,8 @@ github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vz
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
@@ -56,8 +56,9 @@ github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbg
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c h1:P/3mFnHCv1A/ej4m8pF5EB6FUt9qEL2Q9lfrcUNwCYs=
github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4=
github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d h1:Ka64cclWedOkGzm9M2/XYuwJUdmWRUozmsxW0PyKA3A=
github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
@@ -94,8 +95,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.3.1 h1:O4BLOM3hwfVF3AcktIylQXyl7Yi2iBNVy5QsV+ySxbg=
github.com/quic-go/qtls-go1-20 v0.3.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/qtls-go1-20 v0.3.3 h1:17/glZSLI9P9fDAeyCHBFSWSqJcwx1byhLwP5eUIDCM=
github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM=
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
@@ -107,32 +108,32 @@ github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2 h1:dnkKrzapqtAwjTS
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2/go.mod h1:1JUiV7nGuf++YFm9eWZ8q2lrwHmhcUGzptMl/vL1+LA=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.0.0-20230811130919-d6f54a117913 h1:4dyzZWAEo9BNQN7yJsVSiN/Pm1hmUfkGJdEyWMkUnVE=
github.com/sagernet/quic-go v0.0.0-20230811130919-d6f54a117913/go.mod h1:w+nln6f/ZtyPpGbFxmgd5iYFVMmgS+gpD5hu5GAqC1I=
github.com/sagernet/quic-go v0.0.0-20230825040534-0cd917b2ddda h1:7J/hnOFqCThiCrVpvr0wKO+Dic/XPSulPr5yI8FVJMs=
github.com/sagernet/quic-go v0.0.0-20230825040534-0cd917b2ddda/go.mod h1:Iw8Tt3dMqC/61cMHa0nN5i/958oYuuMnQCMOSPx+xcg=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a h1:b89t6Mjgk4rJ5lrNMnCzy1/J116XkhgdB3YNd9FHyF4=
github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a/go.mod h1:9uOZwWkhT2Z2WldolLxX34s+1svAX4i4vvz5hy8u1MA=
github.com/sagernet/sing-dns v0.1.9-0.20230731012726-ad50da89b659 h1:1DAKccGNqTYJ8nsBR765FS0LVBVXfuFlFAHqKsGN3EI=
github.com/sagernet/sing-dns v0.1.9-0.20230731012726-ad50da89b659/go.mod h1:W7GHTZFS8RkoLI3bA2LFY27/0E+uoQESWtMFLepO/JA=
github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c h1:35/FowAvt3Z62mck0TXzVc4jS5R5CWq62qcV2P1cp0I=
github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c/go.mod h1:TKxqIvfQQgd36jp2tzsPavGjYTVZilV+atip1cssjIY=
github.com/sagernet/sing-shadowsocks v0.2.4 h1:s/CqXlvFAZhlIoHWUwPw5CoNnQ9Ibki9pckjuugtVfY=
github.com/sagernet/sing-shadowsocks v0.2.4/go.mod h1:80fNKP0wnqlu85GZXV1H1vDPC/2t+dQbFggOw4XuFUM=
github.com/sagernet/sing-shadowsocks2 v0.1.3 h1:WXoLvCFi5JTFBRYorf1YePGYIQyJ/zbsBM6Fwbl5kGA=
github.com/sagernet/sing-shadowsocks2 v0.1.3/go.mod h1:DOhJc/cLeqRv0wuePrQso+iUmDxOnWF4eT/oMcRzYFw=
github.com/sagernet/sing v0.2.10-0.20230907044649-03c21c0a1205 h1:U/OwMlCH1XFjrDrw5BESGxGsnynT6nDnHvNI9Xv0U78=
github.com/sagernet/sing v0.2.10-0.20230907044649-03c21c0a1205/go.mod h1:9uOZwWkhT2Z2WldolLxX34s+1svAX4i4vvz5hy8u1MA=
github.com/sagernet/sing-dns v0.1.9-0.20230824120133-4d5cbceb40c1 h1:5w+jXz8y/8UQAxO74TjftN5okYkpg5mGvVxXunlKdqI=
github.com/sagernet/sing-dns v0.1.9-0.20230824120133-4d5cbceb40c1/go.mod h1:Kg98PBJEg/08jsNFtmZWmPomhskn9Ausn50ecNm4M+8=
github.com/sagernet/sing-mux v0.1.3-0.20230907005326-7befbadbf314 h1:P5+NZGMH8KSI3L8lKw1znxdRi0tIpWbGYjmv8GrFHrQ=
github.com/sagernet/sing-mux v0.1.3-0.20230907005326-7befbadbf314/go.mod h1:TKxqIvfQQgd36jp2tzsPavGjYTVZilV+atip1cssjIY=
github.com/sagernet/sing-shadowsocks v0.2.5-0.20230907005610-126234728ca0 h1:9wHYWxH+fcs01PM2+DylA8LNNY3ElnZykQo9rysng8U=
github.com/sagernet/sing-shadowsocks v0.2.5-0.20230907005610-126234728ca0/go.mod h1:80fNKP0wnqlu85GZXV1H1vDPC/2t+dQbFggOw4XuFUM=
github.com/sagernet/sing-shadowsocks2 v0.1.4-0.20230907005906-5d2917b29248 h1:JTFfy/LDmVFEK4KZJEujmC1iO8+aoF4unYhhZZRzRq4=
github.com/sagernet/sing-shadowsocks2 v0.1.4-0.20230907005906-5d2917b29248/go.mod h1:DOhJc/cLeqRv0wuePrQso+iUmDxOnWF4eT/oMcRzYFw=
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
github.com/sagernet/sing-tun v0.1.12-0.20230812113806-10d98f26797a h1:YZ20/ohB4wDQlOd2SMaL+qnAoWyM2yuXIUOVjUqj87U=
github.com/sagernet/sing-tun v0.1.12-0.20230812113806-10d98f26797a/go.mod h1:XsyIVKd/Qp+2SdLZWGbavHtcpE7J7XU3S1zJmcoj9Ck=
github.com/sagernet/sing-vmess v0.1.7 h1:TM8FFLsXmlXH9XT8/oDgc6PC5BOzrg6OzyEe01is2r4=
github.com/sagernet/sing-vmess v0.1.7/go.mod h1:1qkC1L1T2sxnS/NuO6HU72S8TkltV+EXoKGR29m/Yss=
github.com/sagernet/sing-tun v0.1.12-0.20230821065522-7545dc2d5641 h1:a8lktNrCWZJisB+nPraW+qB73ZofgPtGmlfqNYcO79g=
github.com/sagernet/sing-tun v0.1.12-0.20230821065522-7545dc2d5641/go.mod h1:+YImslQMLgMQcVgZZ9IK4ue1o/605VSU90amHUcp4hA=
github.com/sagernet/sing-vmess v0.1.8-0.20230907010359-161fb0ac716b h1:2ezfJtH5JosiEwJhVa+rimQ6ps/t2+7h+mOzMoiaZnA=
github.com/sagernet/sing-vmess v0.1.8-0.20230907010359-161fb0ac716b/go.mod h1:1qkC1L1T2sxnS/NuO6HU72S8TkltV+EXoKGR29m/Yss=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9/go.mod h1:FUyTEc5ye5NjKnDTDMuiLF2M6T4BE6y6KZuax//UCEg=
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 h1:Px+hN4Vzgx+iCGVnWH5A8eR7JhNnIV3rGQmBxA7cw6Q=
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6/go.mod h1:zovq6vTvEM6ECiqE3Eeb9rpIylPpamPcmrJ9tv0Bt0M=
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4=
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
@@ -169,19 +170,19 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
go4.org/netipx v0.0.0-20230728184502-ec4c8b891b28 h1:zLxFnORHDFTSkJPawMU7LzsuGQJ4MUFS653jJHpORow=
go4.org/netipx v0.0.0-20230728184502-ec4c8b891b28/go.mod h1:TQvodOM+hJTioNQJilmLXu08JNb8i+ccq418+KWu1/Y=
go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ=
go4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@@ -195,7 +196,6 @@ golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -205,6 +205,7 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -220,8 +221,8 @@ golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E=
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
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=
@@ -239,8 +240,11 @@ google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=

View File

@@ -10,24 +10,26 @@ import (
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/tfo-go"
)
func (a *myInboundAdapter) ListenTCP() (net.Listener, error) {
var err error
bindAddr := M.SocksaddrFrom(a.listenOptions.Listen.Build(), a.listenOptions.ListenPort)
var tcpListener net.Listener
if !a.listenOptions.TCPFastOpen {
var listenConfig net.ListenConfig
if a.listenOptions.TCPMultiPath {
if !multipathTCPAvailable {
return nil, E.New("MultiPath TCP requires go1.21, please recompile your binary.")
}
setMultiPathTCP(&listenConfig)
var listenConfig net.ListenConfig
if a.listenOptions.TCPMultiPath {
if !go121Available {
return nil, E.New("MultiPath TCP requires go1.21, please recompile your binary.")
}
tcpListener, err = listenConfig.Listen(a.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
setMultiPathTCP(&listenConfig)
}
if a.listenOptions.TCPFastOpen {
if !go120Available {
return nil, E.New("TCP Fast Open requires go1.20, please recompile your binary.")
}
tcpListener, err = listenTFO(listenConfig, a.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
} else {
tcpListener, err = tfo.ListenTCP(M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.TCPAddr())
tcpListener, err = listenConfig.Listen(a.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
}
if err == nil {
a.logger.Info("tcp server started at ", tcpListener.Addr())

View File

@@ -0,0 +1,18 @@
//go:build go1.20
package inbound
import (
"context"
"net"
"github.com/sagernet/tfo-go"
)
const go120Available = true
func listenTFO(listenConfig net.ListenConfig, ctx context.Context, network string, address string) (net.Listener, error) {
var tfoConfig tfo.ListenConfig
tfoConfig.ListenConfig = listenConfig
return tfoConfig.Listen(ctx, network, address)
}

View File

@@ -4,7 +4,7 @@ package inbound
import "net"
const multipathTCPAvailable = true
const go121Available = true
func setMultiPathTCP(listenConfig *net.ListenConfig) {
listenConfig.SetMultipathTCP(true)

View File

@@ -0,0 +1,15 @@
//go:build !go1.20
package inbound
import (
"context"
"net"
"os"
)
const go120Available = false
func listenTFO(listenConfig net.ListenConfig, ctx context.Context, network string, address string) (net.Listener, error) {
return nil, os.ErrInvalid
}

View File

@@ -4,7 +4,7 @@ package inbound
import "net"
const multipathTCPAvailable = false
const go121Available = false
func setMultiPathTCP(listenConfig *net.ListenConfig) {
}

View File

@@ -582,7 +582,7 @@ func (c *naiveH2Conn) Close() error {
}
func (c *naiveH2Conn) LocalAddr() net.Addr {
return nil
return M.Socksaddr{}
}
func (c *naiveH2Conn) RemoteAddr() net.Addr {

View File

@@ -99,6 +99,12 @@ func (h *TUIC) newPacketConnection(ctx context.Context, conn N.PacketConn, metad
}
func (h *TUIC) Start() error {
if h.tlsConfig != nil {
err := h.tlsConfig.Start()
if err != nil {
return err
}
}
packetConn, err := h.myInboundAdapter.ListenUDP()
if err != nil {
return err

View File

@@ -77,6 +77,8 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
Inet6Address: common.Map(options.Inet6Address, option.ListenPrefix.Build),
AutoRoute: options.AutoRoute,
StrictRoute: options.StrictRoute,
IncludeInterface: options.IncludeInterface,
ExcludeInterface: options.ExcludeInterface,
Inet4RouteAddress: common.Map(options.Inet4RouteAddress, option.ListenPrefix.Build),
Inet6RouteAddress: common.Map(options.Inet6RouteAddress, option.ListenPrefix.Build),
IncludeUID: includeUID,

View File

@@ -7,10 +7,13 @@ type ClashAPIOptions struct {
ExternalUIDownloadDetour string `json:"external_ui_download_detour,omitempty"`
Secret string `json:"secret,omitempty"`
DefaultMode string `json:"default_mode,omitempty"`
StoreMode bool `json:"store_mode,omitempty"`
StoreSelected bool `json:"store_selected,omitempty"`
StoreFakeIP bool `json:"store_fakeip,omitempty"`
CacheFile string `json:"cache_file,omitempty"`
CacheID string `json:"cache_id,omitempty"`
ModeList []string `json:"-"`
}
type SelectorOutboundOptions struct {

View File

@@ -23,6 +23,7 @@ type TUICOutboundOptions struct {
Password string `json:"password,omitempty"`
CongestionControl string `json:"congestion_control,omitempty"`
UDPRelayMode string `json:"udp_relay_mode,omitempty"`
UDPOverStream bool `json:"udp_over_stream,omitempty"`
ZeroRTTHandshake bool `json:"zero_rtt_handshake,omitempty"`
Heartbeat Duration `json:"heartbeat,omitempty"`
Network NetworkList `json:"network,omitempty"`

View File

@@ -9,6 +9,8 @@ type TunInboundOptions struct {
StrictRoute bool `json:"strict_route,omitempty"`
Inet4RouteAddress Listable[ListenPrefix] `json:"inet4_route_address,omitempty"`
Inet6RouteAddress Listable[ListenPrefix] `json:"inet6_route_address,omitempty"`
IncludeInterface Listable[string] `json:"include_interface,omitempty"`
ExcludeInterface Listable[string] `json:"exclude_interface,omitempty"`
IncludeUID Listable[uint32] `json:"include_uid,omitempty"`
IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"`
ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"`

View File

@@ -70,6 +70,28 @@ func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata a
return CopyEarlyConn(ctx, conn, outConn)
}
func NewDirectConnection(ctx context.Context, router adapter.Router, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error {
ctx = adapter.WithContext(ctx, &metadata)
var outConn net.Conn
var err error
if len(metadata.DestinationAddresses) > 0 {
outConn, err = N.DialSerial(ctx, this, N.NetworkTCP, metadata.Destination, metadata.DestinationAddresses)
} else if metadata.Destination.IsFqdn() {
var destinationAddresses []netip.Addr
destinationAddresses, err = router.LookupDefault(ctx, metadata.Destination.Fqdn)
if err != nil {
return N.HandshakeFailure(conn, err)
}
outConn, err = N.DialSerial(ctx, this, N.NetworkTCP, metadata.Destination, destinationAddresses)
} else {
outConn, err = this.DialContext(ctx, N.NetworkTCP, metadata.Destination)
}
if err != nil {
return N.HandshakeFailure(conn, err)
}
return CopyEarlyConn(ctx, conn, outConn)
}
func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error {
ctx = adapter.WithContext(ctx, &metadata)
var outConn net.PacketConn
@@ -99,6 +121,42 @@ func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn,
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outConn))
}
func NewDirectPacketConnection(ctx context.Context, router adapter.Router, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error {
ctx = adapter.WithContext(ctx, &metadata)
var outConn net.PacketConn
var destinationAddress netip.Addr
var err error
if len(metadata.DestinationAddresses) > 0 {
outConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses)
} else if metadata.Destination.IsFqdn() {
var destinationAddresses []netip.Addr
destinationAddresses, err = router.LookupDefault(ctx, metadata.Destination.Fqdn)
if err != nil {
return N.HandshakeFailure(conn, err)
}
outConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, destinationAddresses)
} else {
outConn, err = this.ListenPacket(ctx, metadata.Destination)
}
if err != nil {
return N.HandshakeFailure(conn, err)
}
if destinationAddress.IsValid() {
if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded {
natConn.UpdateDestination(destinationAddress)
}
}
switch metadata.Protocol {
case C.ProtocolSTUN:
ctx, conn = canceler.NewPacketConn(ctx, conn, C.STUNTimeout)
case C.ProtocolQUIC:
ctx, conn = canceler.NewPacketConn(ctx, conn, C.QUICTimeout)
case C.ProtocolDNS:
ctx, conn = canceler.NewPacketConn(ctx, conn, C.DNSTimeout)
}
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outConn))
}
func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) error {
if cachedReader, isCached := conn.(N.CachedReader); isCached {
payload := cachedReader.ReadCached()

View File

@@ -80,11 +80,11 @@ func (h *Socks) DialContext(ctx context.Context, network string, destination M.S
return nil, E.Extend(N.ErrUnknownNetwork, network)
}
if h.resolve && destination.IsFqdn() {
addrs, err := h.router.LookupDefault(ctx, destination.Fqdn)
destinationAddresses, err := h.router.LookupDefault(ctx, destination.Fqdn)
if err != nil {
return nil, err
}
return N.DialSerial(ctx, h.client, network, destination, addrs)
return N.DialSerial(ctx, h.client, network, destination, destinationAddresses)
}
return h.client.DialContext(ctx, network, destination)
}
@@ -97,14 +97,25 @@ func (h *Socks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.
h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination)
return h.uotClient.ListenPacket(ctx, destination)
}
if h.resolve && destination.IsFqdn() {
destinationAddresses, err := h.router.LookupDefault(ctx, destination.Fqdn)
if err != nil {
return nil, err
}
packetConn, _, err := N.ListenSerial(ctx, h.client, destination, destinationAddresses)
if err != nil {
return nil, err
}
return packetConn, nil
}
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
return h.client.ListenPacket(ctx, destination)
}
func (h *Socks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewConnection(ctx, h, conn, metadata)
return NewDirectConnection(ctx, h.router, h, conn, metadata)
}
func (h *Socks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return NewPacketConnection(ctx, h, conn, metadata)
return NewDirectPacketConnection(ctx, h.router, h, conn, metadata)
}

View File

@@ -20,6 +20,7 @@ import (
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/uot"
"github.com/gofrs/uuid/v5"
)
@@ -31,7 +32,8 @@ var (
type TUIC struct {
myOutboundAdapter
client *tuic.Client
client *tuic.Client
udpStream bool
}
func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TUICOutboundOptions) (*TUIC, error) {
@@ -51,11 +53,14 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge
if err != nil {
return nil, E.Cause(err, "invalid uuid")
}
var udpStream bool
var tuicUDPStream bool
if options.UDPOverStream && options.UDPRelayMode != "" {
return nil, E.New("udp_over_stream is conflict with udp_relay_mode")
}
switch options.UDPRelayMode {
case "native":
case "quic":
udpStream = true
tuicUDPStream = true
}
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
@@ -69,7 +74,7 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge
UUID: userUUID,
Password: options.Password,
CongestionControl: options.CongestionControl,
UDPStream: udpStream,
UDPStream: tuicUDPStream,
ZeroRTTHandshake: options.ZeroRTTHandshake,
Heartbeat: time.Duration(options.Heartbeat),
})
@@ -85,7 +90,8 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge
tag: tag,
dependencies: withDialerDependency(options.DialerOptions),
},
client: client,
client: client,
udpStream: options.UDPOverStream,
}, nil
}
@@ -95,19 +101,43 @@ func (h *TUIC) DialContext(ctx context.Context, network string, destination M.So
h.logger.InfoContext(ctx, "outbound connection to ", destination)
return h.client.DialConn(ctx, destination)
case N.NetworkUDP:
conn, err := h.ListenPacket(ctx, destination)
if err != nil {
return nil, err
if h.udpStream {
h.logger.InfoContext(ctx, "outbound stream packet connection to ", destination)
streamConn, err := h.client.DialConn(ctx, uot.RequestDestination(uot.Version))
if err != nil {
return nil, err
}
return uot.NewLazyConn(streamConn, uot.Request{
IsConnect: true,
Destination: destination,
}), nil
} else {
conn, err := h.ListenPacket(ctx, destination)
if err != nil {
return nil, err
}
return bufio.NewBindPacketConn(conn, destination), nil
}
return bufio.NewBindPacketConn(conn, destination), nil
default:
return nil, E.New("unsupported network: ", network)
}
}
func (h *TUIC) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
return h.client.ListenPacket(ctx)
if h.udpStream {
h.logger.InfoContext(ctx, "outbound stream packet connection to ", destination)
streamConn, err := h.client.DialConn(ctx, uot.RequestDestination(uot.Version))
if err != nil {
return nil, err
}
return uot.NewLazyConn(streamConn, uot.Request{
IsConnect: false,
Destination: destination,
}), nil
} else {
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
return h.client.ListenPacket(ctx)
}
}
func (h *TUIC) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {

View File

@@ -202,26 +202,37 @@ func (w *WireGuard) DialContext(ctx context.Context, network string, destination
w.logger.InfoContext(ctx, "outbound packet connection to ", destination)
}
if destination.IsFqdn() {
addrs, err := w.router.LookupDefault(ctx, destination.Fqdn)
destinationAddresses, err := w.router.LookupDefault(ctx, destination.Fqdn)
if err != nil {
return nil, err
}
return N.DialSerial(ctx, w.tunDevice, network, destination, addrs)
return N.DialSerial(ctx, w.tunDevice, network, destination, destinationAddresses)
}
return w.tunDevice.DialContext(ctx, network, destination)
}
func (w *WireGuard) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
w.logger.InfoContext(ctx, "outbound packet connection to ", destination)
if destination.IsFqdn() {
destinationAddresses, err := w.router.LookupDefault(ctx, destination.Fqdn)
if err != nil {
return nil, err
}
packetConn, _, err := N.ListenSerial(ctx, w.tunDevice, destination, destinationAddresses)
if err != nil {
return nil, err
}
return packetConn, err
}
return w.tunDevice.ListenPacket(ctx, destination)
}
func (w *WireGuard) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewConnection(ctx, w, conn, metadata)
return NewDirectConnection(ctx, w.router, w, conn, metadata)
}
func (w *WireGuard) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return NewPacketConnection(ctx, w, conn, metadata)
return NewDirectPacketConnection(ctx, w.router, w, conn, metadata)
}
func (w *WireGuard) Start() error {

View File

@@ -521,7 +521,7 @@ func (r *Router) Close() error {
return E.Cause(err, "close dns transport[", i, "]")
})
}
if r.geositeReader != nil {
if r.geoIPReader != nil {
r.logger.Trace("closing geoip reader")
err = E.Append(err, common.Close(r.geoIPReader), func(err error) error {
return E.Cause(err, "close geoip reader")
@@ -770,20 +770,22 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
if metadata.Destination.Addr.IsUnspecified() {
metadata.Destination = destination
}
sniffMetadata, _ := sniff.PeekPacket(ctx, buffer.Bytes(), sniff.DomainNameQuery, sniff.QUICClientHello, sniff.STUNMessage)
if sniffMetadata != nil {
metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain
if metadata.InboundOptions.SniffOverrideDestination && M.IsDomainName(metadata.Domain) {
metadata.Destination = M.Socksaddr{
Fqdn: metadata.Domain,
Port: metadata.Destination.Port,
if metadata.InboundOptions.SniffEnabled {
sniffMetadata, _ := sniff.PeekPacket(ctx, buffer.Bytes(), sniff.DomainNameQuery, sniff.QUICClientHello, sniff.STUNMessage)
if sniffMetadata != nil {
metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain
if metadata.InboundOptions.SniffOverrideDestination && M.IsDomainName(metadata.Domain) {
metadata.Destination = M.Socksaddr{
Fqdn: metadata.Domain,
Port: metadata.Destination.Port,
}
}
if metadata.Domain != "" {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
} else {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol)
}
}
if metadata.Domain != "" {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
} else {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol)
}
}
conn = bufio.NewCachedPacketConn(conn, buffer, destination)
@@ -1003,14 +1005,7 @@ func (r *Router) notifyNetworkUpdate(event int) {
}
}
conntrack.Close()
for _, outbound := range r.outbounds {
listener, isListener := outbound.(adapter.InterfaceUpdateListener)
if isListener {
listener.InterfaceUpdated()
}
}
r.ResetNetwork()
return
}
@@ -1023,5 +1018,9 @@ func (r *Router) ResetNetwork() error {
listener.InterfaceUpdated()
}
}
for _, transport := range r.transports {
transport.Reset()
}
return nil
}

View File

@@ -146,6 +146,13 @@ func (r *Router) LookupDefault(ctx context.Context, domain string) ([]netip.Addr
return r.Lookup(ctx, domain, dns.DomainStrategyAsIS)
}
func (r *Router) ClearDNSCache() {
r.dnsClient.ClearCache()
if r.platformInterface != nil {
r.platformInterface.ClearDNSCache()
}
}
func LogDNSAnswers(logger log.ContextLogger, ctx context.Context, domain string, answers []mDNS.RR) {
for _, answer := range answers {
logger.InfoContext(ctx, "exchanged ", domain, " ", mDNS.Type(answer.Header().Rrtype).String(), " ", formatQuestion(answer.String()))

View File

@@ -16,7 +16,7 @@ type ClashModeItem struct {
func NewClashModeItem(router adapter.Router, mode string) *ClashModeItem {
return &ClashModeItem{
router: router,
mode: strings.ToLower(mode),
mode: mode,
}
}
@@ -25,7 +25,7 @@ func (r *ClashModeItem) Match(metadata *adapter.InboundContext) bool {
if clashServer == nil {
return false
}
return clashServer.Mode() == r.mode
return strings.EqualFold(clashServer.Mode(), r.mode)
}
func (r *ClashModeItem) String() string {

View File

@@ -74,6 +74,21 @@ func testSuit(t *testing.T, clientPort uint16, testPort uint16) {
// require.NoError(t, testPacketConnTimeout(t, dialUDP))
}
func testSuitLargeUDP(t *testing.T, clientPort uint16, testPort uint16) {
dialer := socks.NewClient(N.SystemDialer, M.ParseSocksaddrHostPort("127.0.0.1", clientPort), socks.Version5, "", "")
dialTCP := func() (net.Conn, error) {
return dialer.DialContext(context.Background(), "tcp", M.ParseSocksaddrHostPort("127.0.0.1", testPort))
}
dialUDP := func() (net.PacketConn, error) {
return dialer.ListenPacket(context.Background(), M.ParseSocksaddrHostPort("127.0.0.1", testPort))
}
require.NoError(t, testPingPongWithConn(t, testPort, dialTCP))
require.NoError(t, testPingPongWithPacketConn(t, testPort, dialUDP))
require.NoError(t, testLargeDataWithConn(t, testPort, dialTCP))
require.NoError(t, testLargeDataWithPacketConn(t, testPort, dialUDP))
require.NoError(t, testLargeDataWithPacketConnSize(t, testPort, 5000, dialUDP))
}
func testTCP(t *testing.T, clientPort uint16, testPort uint16) {
dialer := socks.NewClient(N.SystemDialer, M.ParseSocksaddrHostPort("127.0.0.1", clientPort), socks.Version5, "", "")
dialTCP := func() (net.Conn, error) {

View File

@@ -38,8 +38,8 @@ const (
ImageShadowsocksR = "teddysun/shadowsocks-r:latest"
ImageXRayCore = "teddysun/xray:latest"
ImageShadowsocksLegacy = "mritd/shadowsocks:latest"
ImageTUICServer = ""
ImageTUICClient = ""
ImageTUICServer = "kilvn/tuic-server:latest"
ImageTUICClient = "kilvn/tuic-client:latest"
)
var allImages = []string{
@@ -55,8 +55,8 @@ var allImages = []string{
ImageShadowsocksR,
ImageXRayCore,
ImageShadowsocksLegacy,
// ImageTUICServer,
// ImageTUICClient,
ImageTUICServer,
ImageTUICClient,
}
var localIP = netip.MustParseAddr("127.0.0.1")
@@ -364,6 +364,10 @@ func testLargeDataWithConn(t *testing.T, port uint16, cc func() (net.Conn, error
}
func testLargeDataWithPacketConn(t *testing.T, port uint16, pcc func() (net.PacketConn, error)) error {
return testLargeDataWithPacketConnSize(t, port, 1024, pcc)
}
func testLargeDataWithPacketConnSize(t *testing.T, port uint16, chunkSize int, pcc func() (net.PacketConn, error)) error {
l, err := listenPacket("udp", ":"+F.ToString(port))
if err != nil {
return err
@@ -373,7 +377,6 @@ func testLargeDataWithPacketConn(t *testing.T, port uint16, pcc func() (net.Pack
rAddr := &net.UDPAddr{IP: localIP.AsSlice(), Port: int(port)}
times := 50
chunkSize := int64(1024)
pingCh, pongCh, test := newLargeDataPair()
writeRandData := func(pc net.PacketConn, addr net.Addr) (map[int][]byte, error) {

View File

@@ -1,6 +1,7 @@
{
"relay": {
"server": "127.0.0.1:10000",
"server": "example.org:10000",
"ip": "127.0.0.1",
"uuid": "FE35D05B-8803-45C4-BAE6-723AD2CD5D3D",
"password": "tuic",
"certificates": [
@@ -8,7 +9,8 @@
]
},
"local": {
"server": "127.0.0.1:10001"
"server": "127.0.0.1:10001",
"max_packet_size": 65535
},
"log_level": "debug"
}

View File

@@ -5,5 +5,6 @@
},
"certificate": "/etc/tuic/cert.pem",
"private_key": "/etc/tuic/key.pem",
"max_external_packet_size": 65535,
"log_level": "debug"
}

View File

@@ -1,21 +1,21 @@
module test
go 1.18
go 1.20
require github.com/sagernet/sing-box v0.0.0
replace github.com/sagernet/sing-box => ../
require (
github.com/docker/docker v20.10.18+incompatible
github.com/docker/docker v24.0.5+incompatible
github.com/docker/go-connections v0.4.0
github.com/gofrs/uuid/v5 v5.0.0
github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a
github.com/sagernet/sing-shadowsocks v0.2.4
github.com/sagernet/sing-shadowsocks2 v0.1.3
github.com/spyzhov/ajson v0.7.1
github.com/sagernet/sing v0.2.10-0.20230907044649-03c21c0a1205
github.com/sagernet/sing-shadowsocks v0.2.5-0.20230907005610-126234728ca0
github.com/sagernet/sing-shadowsocks2 v0.1.4-0.20230907005906-5d2917b29248
github.com/spyzhov/ajson v0.9.0
github.com/stretchr/testify v1.8.4
go.uber.org/goleak v1.2.0
go.uber.org/goleak v1.2.1
golang.org/x/net v0.14.0
)
@@ -26,7 +26,7 @@ require (
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/caddyserver/certmagic v0.19.1 // indirect
github.com/caddyserver/certmagic v0.19.2 // indirect
github.com/cloudflare/circl v1.2.1-0.20221019164342-6ab4dfed8f3c // indirect
github.com/cretz/bine v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
@@ -37,7 +37,7 @@ require (
github.com/go-chi/chi/v5 v5.0.10 // indirect
github.com/go-chi/cors v1.2.1 // indirect
github.com/go-chi/render v1.0.3 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/mock v1.6.0 // indirect
@@ -45,7 +45,7 @@ require (
github.com/google/btree v1.1.2 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c // indirect
github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.15.15 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
@@ -65,39 +65,38 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.3.1 // indirect
github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 // indirect
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2 // indirect
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/sagernet/quic-go v0.0.0-20230811130919-d6f54a117913 // indirect
github.com/sagernet/quic-go v0.0.0-20230825040534-0cd917b2ddda // indirect
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
github.com/sagernet/sing-dns v0.1.9-0.20230731012726-ad50da89b659 // indirect
github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c // indirect
github.com/sagernet/sing-dns v0.1.9-0.20230824120133-4d5cbceb40c1 // indirect
github.com/sagernet/sing-mux v0.1.3-0.20230907005326-7befbadbf314 // indirect
github.com/sagernet/sing-shadowtls v0.1.4 // indirect
github.com/sagernet/sing-tun v0.1.12-0.20230812113806-10d98f26797a // indirect
github.com/sagernet/sing-vmess v0.1.7 // indirect
github.com/sagernet/sing-tun v0.1.12-0.20230821065522-7545dc2d5641 // indirect
github.com/sagernet/sing-vmess v0.1.8-0.20230907010359-161fb0ac716b // indirect
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 // indirect
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f // indirect
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect
go.etcd.io/bbolt v1.3.7 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.25.0 // indirect
go4.org/netipx v0.0.0-20230728184502-ec4c8b891b28 // indirect
go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.10.0 // indirect
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
google.golang.org/grpc v1.57.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect

View File

@@ -12,8 +12,8 @@ github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/caddyserver/certmagic v0.19.1 h1:4jyOYm2DHvQI8YM0sk6qm62Gl5XznHxiMBMWjMTlQkw=
github.com/caddyserver/certmagic v0.19.1/go.mod h1:fsL01NomQ6N+kE2j37ZCnig2MFosG+MIO4ztnmG/zz8=
github.com/caddyserver/certmagic v0.19.2 h1:HZd1AKLx4592MalEGQS39DKs2ZOAJCEM/xYPMQ2/ui0=
github.com/caddyserver/certmagic v0.19.2/go.mod h1:fsL01NomQ6N+kE2j37ZCnig2MFosG+MIO4ztnmG/zz8=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -27,8 +27,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc=
github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
@@ -44,8 +44,8 @@ github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vz
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
@@ -66,8 +66,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c h1:P/3mFnHCv1A/ej4m8pF5EB6FUt9qEL2Q9lfrcUNwCYs=
github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4=
github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d h1:Ka64cclWedOkGzm9M2/XYuwJUdmWRUozmsxW0PyKA3A=
github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4=
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
@@ -113,8 +113,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.3.1 h1:O4BLOM3hwfVF3AcktIylQXyl7Yi2iBNVy5QsV+ySxbg=
github.com/quic-go/qtls-go1-20 v0.3.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/qtls-go1-20 v0.3.3 h1:17/glZSLI9P9fDAeyCHBFSWSqJcwx1byhLwP5eUIDCM=
github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM=
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
@@ -123,50 +123,42 @@ github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2 h1:dnkKrzapqtAwjTS
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2/go.mod h1:1JUiV7nGuf++YFm9eWZ8q2lrwHmhcUGzptMl/vL1+LA=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.0.0-20230809023643-d720ed35ac2b h1:+hpCW1zw03nnJDx+5tF9ETb6bKl2VSftv4KMGZAHC2Q=
github.com/sagernet/quic-go v0.0.0-20230809023643-d720ed35ac2b/go.mod h1:w+nln6f/ZtyPpGbFxmgd5iYFVMmgS+gpD5hu5GAqC1I=
github.com/sagernet/quic-go v0.0.0-20230811130919-d6f54a117913/go.mod h1:w+nln6f/ZtyPpGbFxmgd5iYFVMmgS+gpD5hu5GAqC1I=
github.com/sagernet/quic-go v0.0.0-20230825040534-0cd917b2ddda h1:7J/hnOFqCThiCrVpvr0wKO+Dic/XPSulPr5yI8FVJMs=
github.com/sagernet/quic-go v0.0.0-20230825040534-0cd917b2ddda/go.mod h1:Iw8Tt3dMqC/61cMHa0nN5i/958oYuuMnQCMOSPx+xcg=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a h1:b89t6Mjgk4rJ5lrNMnCzy1/J116XkhgdB3YNd9FHyF4=
github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a/go.mod h1:9uOZwWkhT2Z2WldolLxX34s+1svAX4i4vvz5hy8u1MA=
github.com/sagernet/sing-dns v0.1.9-0.20230731012726-ad50da89b659 h1:1DAKccGNqTYJ8nsBR765FS0LVBVXfuFlFAHqKsGN3EI=
github.com/sagernet/sing-dns v0.1.9-0.20230731012726-ad50da89b659/go.mod h1:W7GHTZFS8RkoLI3bA2LFY27/0E+uoQESWtMFLepO/JA=
github.com/sagernet/sing-mux v0.1.3-0.20230803070305-ea4a972acd21 h1:IQ7oBBKz+lwIqwI9IMStlQ9YSUu3eKJmNTip0aLbvOI=
github.com/sagernet/sing-mux v0.1.3-0.20230803070305-ea4a972acd21/go.mod h1:TKxqIvfQQgd36jp2tzsPavGjYTVZilV+atip1cssjIY=
github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c/go.mod h1:TKxqIvfQQgd36jp2tzsPavGjYTVZilV+atip1cssjIY=
github.com/sagernet/sing-shadowsocks v0.2.4 h1:s/CqXlvFAZhlIoHWUwPw5CoNnQ9Ibki9pckjuugtVfY=
github.com/sagernet/sing-shadowsocks v0.2.4/go.mod h1:80fNKP0wnqlu85GZXV1H1vDPC/2t+dQbFggOw4XuFUM=
github.com/sagernet/sing-shadowsocks2 v0.1.3 h1:WXoLvCFi5JTFBRYorf1YePGYIQyJ/zbsBM6Fwbl5kGA=
github.com/sagernet/sing-shadowsocks2 v0.1.3/go.mod h1:DOhJc/cLeqRv0wuePrQso+iUmDxOnWF4eT/oMcRzYFw=
github.com/sagernet/sing v0.2.10-0.20230907044649-03c21c0a1205 h1:U/OwMlCH1XFjrDrw5BESGxGsnynT6nDnHvNI9Xv0U78=
github.com/sagernet/sing v0.2.10-0.20230907044649-03c21c0a1205/go.mod h1:9uOZwWkhT2Z2WldolLxX34s+1svAX4i4vvz5hy8u1MA=
github.com/sagernet/sing-dns v0.1.9-0.20230824120133-4d5cbceb40c1 h1:5w+jXz8y/8UQAxO74TjftN5okYkpg5mGvVxXunlKdqI=
github.com/sagernet/sing-dns v0.1.9-0.20230824120133-4d5cbceb40c1/go.mod h1:Kg98PBJEg/08jsNFtmZWmPomhskn9Ausn50ecNm4M+8=
github.com/sagernet/sing-mux v0.1.3-0.20230907005326-7befbadbf314 h1:P5+NZGMH8KSI3L8lKw1znxdRi0tIpWbGYjmv8GrFHrQ=
github.com/sagernet/sing-mux v0.1.3-0.20230907005326-7befbadbf314/go.mod h1:TKxqIvfQQgd36jp2tzsPavGjYTVZilV+atip1cssjIY=
github.com/sagernet/sing-shadowsocks v0.2.5-0.20230907005610-126234728ca0 h1:9wHYWxH+fcs01PM2+DylA8LNNY3ElnZykQo9rysng8U=
github.com/sagernet/sing-shadowsocks v0.2.5-0.20230907005610-126234728ca0/go.mod h1:80fNKP0wnqlu85GZXV1H1vDPC/2t+dQbFggOw4XuFUM=
github.com/sagernet/sing-shadowsocks2 v0.1.4-0.20230907005906-5d2917b29248 h1:JTFfy/LDmVFEK4KZJEujmC1iO8+aoF4unYhhZZRzRq4=
github.com/sagernet/sing-shadowsocks2 v0.1.4-0.20230907005906-5d2917b29248/go.mod h1:DOhJc/cLeqRv0wuePrQso+iUmDxOnWF4eT/oMcRzYFw=
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
github.com/sagernet/sing-tun v0.1.12-0.20230808120247-47ab78d303db h1:jOwG+7u4NtQVwXj5pFnGeNnDoa2cv83O5x4NLKN8y/c=
github.com/sagernet/sing-tun v0.1.12-0.20230808120247-47ab78d303db/go.mod h1:XsyIVKd/Qp+2SdLZWGbavHtcpE7J7XU3S1zJmcoj9Ck=
github.com/sagernet/sing-tun v0.1.12-0.20230811070056-38478f5fbcd2/go.mod h1:XsyIVKd/Qp+2SdLZWGbavHtcpE7J7XU3S1zJmcoj9Ck=
github.com/sagernet/sing-tun v0.1.12-0.20230812113214-bc5a1f835a28/go.mod h1:XsyIVKd/Qp+2SdLZWGbavHtcpE7J7XU3S1zJmcoj9Ck=
github.com/sagernet/sing-tun v0.1.12-0.20230812113806-10d98f26797a/go.mod h1:XsyIVKd/Qp+2SdLZWGbavHtcpE7J7XU3S1zJmcoj9Ck=
github.com/sagernet/sing-vmess v0.1.7 h1:TM8FFLsXmlXH9XT8/oDgc6PC5BOzrg6OzyEe01is2r4=
github.com/sagernet/sing-vmess v0.1.7/go.mod h1:1qkC1L1T2sxnS/NuO6HU72S8TkltV+EXoKGR29m/Yss=
github.com/sagernet/sing-tun v0.1.12-0.20230821065522-7545dc2d5641 h1:a8lktNrCWZJisB+nPraW+qB73ZofgPtGmlfqNYcO79g=
github.com/sagernet/sing-tun v0.1.12-0.20230821065522-7545dc2d5641/go.mod h1:+YImslQMLgMQcVgZZ9IK4ue1o/605VSU90amHUcp4hA=
github.com/sagernet/sing-vmess v0.1.8-0.20230907010359-161fb0ac716b h1:2ezfJtH5JosiEwJhVa+rimQ6ps/t2+7h+mOzMoiaZnA=
github.com/sagernet/sing-vmess v0.1.8-0.20230907010359-161fb0ac716b/go.mod h1:1qkC1L1T2sxnS/NuO6HU72S8TkltV+EXoKGR29m/Yss=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9/go.mod h1:FUyTEc5ye5NjKnDTDMuiLF2M6T4BE6y6KZuax//UCEg=
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 h1:Px+hN4Vzgx+iCGVnWH5A8eR7JhNnIV3rGQmBxA7cw6Q=
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6/go.mod h1:zovq6vTvEM6ECiqE3Eeb9rpIylPpamPcmrJ9tv0Bt0M=
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4=
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2dKX7EQP++1JLNtw4C2TNxd4/ov8YUpOPOSo=
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0=
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f h1:Kvo8w8Y9lzFGB/7z09MJ3TR99TFtfI/IuY87Ygcycho=
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f/go.mod h1:mySs0abhpc/gLlvhoq7HP1RzOaRmIXVeZGCh++zoApk=
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spyzhov/ajson v0.7.1 h1:1MDIlPc6x0zjNtpa7tDzRAyFAvRX+X8ZsvtYz5lZg6A=
github.com/spyzhov/ajson v0.7.1/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzyqMuVA=
github.com/spyzhov/ajson v0.9.0 h1:tF46gJGOenYVj+k9K1U1XpCxVWhmiyY5PsVCAs1+OJ0=
github.com/spyzhov/ajson v0.9.0/go.mod h1:a6oSw0MMb7Z5aD2tPoPO+jq11ETKgXUr2XktHdT8Wt8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -188,14 +180,14 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
go4.org/netipx v0.0.0-20230728184502-ec4c8b891b28 h1:zLxFnORHDFTSkJPawMU7LzsuGQJ4MUFS653jJHpORow=
go4.org/netipx v0.0.0-20230728184502-ec4c8b891b28/go.mod h1:TQvodOM+hJTioNQJilmLXu08JNb8i+ccq418+KWu1/Y=
go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ=
go4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -203,14 +195,13 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -228,7 +219,6 @@ golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -238,9 +228,9 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -259,8 +249,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E=
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
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=

View File

@@ -94,7 +94,7 @@ func testTUICSelf(t *testing.T, udpStream bool, zeroRTTHandshake bool) {
},
},
})
testSuit(t, clientPort, testPort)
testSuitLargeUDP(t, clientPort, testPort)
}
func TestTUICInbound(t *testing.T) {
@@ -130,6 +130,7 @@ func TestTUICInbound(t *testing.T) {
caPem: "/etc/tuic/ca.pem",
},
})
testSuitLargeUDP(t, clientPort, testPort)
}
func TestTUICOutbound(t *testing.T) {
@@ -174,5 +175,5 @@ func TestTUICOutbound(t *testing.T) {
},
},
})
testSuit(t, clientPort, testPort)
testSuitLargeUDP(t, clientPort, testPort)
}

View File

@@ -85,6 +85,9 @@ func (t *Transport) Start() error {
return nil
}
func (t *Transport) Reset() {
}
func (t *Transport) Close() error {
if t.interfaceCallback != nil {
t.router.InterfaceMonitor().UnregisterCallback(t.interfaceCallback)

View File

@@ -54,6 +54,9 @@ func (s *Transport) Start() error {
return nil
}
func (s *Transport) Reset() {
}
func (s *Transport) Close() error {
return nil
}

View File

@@ -394,7 +394,7 @@ func (c *Conn) Read(p []byte) (n int, err error) {
}
func (c *Conn) LocalAddr() net.Addr {
return nil
return M.Socksaddr{}
}
func (c *Conn) RemoteAddr() net.Addr {
@@ -502,7 +502,7 @@ func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
}
func (c *PacketConn) LocalAddr() net.Addr {
return nil
return M.Socksaddr{}
}
func (c *PacketConn) RemoteAddr() net.Addr {
@@ -526,11 +526,12 @@ func (c *PacketConn) NeedAdditionalReadDeadline() bool {
}
func (c *PacketConn) Read(b []byte) (n int, err error) {
return 0, os.ErrInvalid
n, _, err = c.ReadFrom(b)
return
}
func (c *PacketConn) Write(b []byte) (n int, err error) {
return 0, os.ErrInvalid
return c.WriteTo(b, c.destination)
}
func (c *PacketConn) Close() error {

View File

@@ -6,8 +6,8 @@ import (
"syscall"
"github.com/sagernet/quic-go"
"github.com/sagernet/sing-box/common/baderror"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/baderror"
)
type PacketConnWrapper struct {

View File

@@ -204,10 +204,13 @@ func ClientHandshake(conn net.Conn, key [KeyLength]byte, destination M.Socksaddr
common.Must1(header.Write(key[:]))
common.Must1(header.Write(CRLF))
common.Must(header.WriteByte(CommandTCP))
common.Must(M.SocksaddrSerializer.WriteAddrPort(header, destination))
err := M.SocksaddrSerializer.WriteAddrPort(header, destination)
if err != nil {
return err
}
common.Must1(header.Write(CRLF))
common.Must1(header.Write(payload))
_, err := conn.Write(header.Bytes())
_, err = conn.Write(header.Bytes())
if err != nil {
return E.Cause(err, "write request")
}
@@ -219,10 +222,13 @@ func ClientHandshakeBuffer(conn net.Conn, key [KeyLength]byte, destination M.Soc
common.Must1(header.Write(key[:]))
common.Must1(header.Write(CRLF))
common.Must(header.WriteByte(CommandTCP))
common.Must(M.SocksaddrSerializer.WriteAddrPort(header, destination))
err := M.SocksaddrSerializer.WriteAddrPort(header, destination)
if err != nil {
return err
}
common.Must1(header.Write(CRLF))
_, err := conn.Write(payload.Bytes())
_, err = conn.Write(payload.Bytes())
if err != nil {
return E.Cause(err, "write request")
}
@@ -244,7 +250,10 @@ func ClientHandshakePacket(conn net.Conn, key [KeyLength]byte, destination M.Soc
common.Must1(header.Write(key[:]))
common.Must1(header.Write(CRLF))
common.Must(header.WriteByte(CommandUDP))
common.Must(M.SocksaddrSerializer.WriteAddrPort(header, destination))
err := M.SocksaddrSerializer.WriteAddrPort(header, destination)
if err != nil {
return err
}
common.Must1(header.Write(CRLF))
common.Must(M.SocksaddrSerializer.WriteAddrPort(header, destination))
common.Must(binary.Write(header, binary.BigEndian, uint16(payloadLen)))
@@ -257,7 +266,7 @@ func ClientHandshakePacket(conn net.Conn, key [KeyLength]byte, destination M.Soc
}
}
_, err := conn.Write(payload.Bytes())
_, err = conn.Write(payload.Bytes())
if err != nil {
return E.Cause(err, "write payload")
}
@@ -289,10 +298,13 @@ func WritePacket(conn net.Conn, buffer *buf.Buffer, destination M.Socksaddr) err
defer buffer.Release()
bufferLen := buffer.Len()
header := buf.With(buffer.ExtendHeader(M.SocksaddrSerializer.AddrPortLen(destination) + 4))
common.Must(M.SocksaddrSerializer.WriteAddrPort(header, destination))
err := M.SocksaddrSerializer.WriteAddrPort(header, destination)
if err != nil {
return err
}
common.Must(binary.Write(header, binary.BigEndian, uint16(bufferLen)))
common.Must1(header.Write(CRLF))
_, err := conn.Write(buffer.Bytes())
_, err = conn.Write(buffer.Bytes())
if err != nil {
return E.Cause(err, "write packet")
}

View File

@@ -5,14 +5,13 @@ import (
"crypto/tls"
"io"
"net"
"os"
"runtime"
"sync"
"time"
"github.com/sagernet/quic-go"
"github.com/sagernet/sing-box/common/baderror"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/baderror"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
@@ -103,7 +102,7 @@ func (c *Client) offer(ctx context.Context) (*clientQUICConnection, error) {
}
func (c *Client) offerNew(ctx context.Context) (*clientQUICConnection, error) {
udpConn, err := c.dialer.DialContext(ctx, "udp", c.serverAddr)
udpConn, err := c.dialer.DialContext(c.ctx, "udp", c.serverAddr)
if err != nil {
return nil, err
}
@@ -184,8 +183,8 @@ func (c *Client) DialConn(ctx context.Context, destination M.Socksaddr) (net.Con
return nil, err
}
return &clientConn{
Stream: stream,
parent: conn,
stream: stream,
destination: destination,
}, nil
}
@@ -253,25 +252,33 @@ func (c *clientQUICConnection) closeWithError(err error) {
}
type clientConn struct {
quic.Stream
parent *clientQUICConnection
stream quic.Stream
destination M.Socksaddr
requestWritten bool
}
func (c *clientConn) NeedHandshake() bool {
return !c.requestWritten
}
func (c *clientConn) Read(b []byte) (n int, err error) {
n, err = c.stream.Read(b)
n, err = c.Stream.Read(b)
return n, baderror.WrapQUIC(err)
}
func (c *clientConn) Write(b []byte) (n int, err error) {
if !c.requestWritten {
request := buf.NewSize(2 + addressSerializer.AddrPortLen(c.destination) + len(b))
defer request.Release()
request.WriteByte(Version)
request.WriteByte(CommandConnect)
addressSerializer.WriteAddrPort(request, c.destination)
err = addressSerializer.WriteAddrPort(request, c.destination)
if err != nil {
return
}
request.Write(b)
_, err = c.stream.Write(request.Bytes())
_, err = c.Stream.Write(request.Bytes())
if err != nil {
c.parent.closeWithError(E.Cause(err, "create new connection"))
return 0, baderror.WrapQUIC(err)
@@ -279,17 +286,13 @@ func (c *clientConn) Write(b []byte) (n int, err error) {
c.requestWritten = true
return len(b), nil
}
n, err = c.stream.Write(b)
n, err = c.Stream.Write(b)
return n, baderror.WrapQUIC(err)
}
func (c *clientConn) Close() error {
stream := c.stream
if stream == nil {
return nil
}
stream.CancelRead(0)
return stream.Close()
c.Stream.CancelRead(0)
return c.Stream.Close()
}
func (c *clientConn) LocalAddr() net.Addr {
@@ -299,24 +302,3 @@ func (c *clientConn) LocalAddr() net.Addr {
func (c *clientConn) RemoteAddr() net.Addr {
return c.destination
}
func (c *clientConn) SetDeadline(t time.Time) error {
if c.stream == nil {
return os.ErrInvalid
}
return c.stream.SetDeadline(t)
}
func (c *clientConn) SetReadDeadline(t time.Time) error {
if c.stream == nil {
return os.ErrInvalid
}
return c.stream.SetReadDeadline(t)
}
func (c *clientConn) SetWriteDeadline(t time.Time) error {
if c.stream == nil {
return os.ErrInvalid
}
return c.stream.SetWriteDeadline(t)
}

View File

@@ -34,7 +34,7 @@ func (c *Client) handleMessage(conn *clientQUICConnection, data []byte) error {
}
switch data[1] {
case CommandPacket:
message := udpMessagePool.Get().(*udpMessage)
message := allocMessage()
err := decodeUDPMessage(message, data[2:])
if err != nil {
message.release()
@@ -82,7 +82,7 @@ func (c *Client) handleUniStream(conn *clientQUICConnection, stream quic.Receive
return E.New("unknown command ", command)
}
reader := io.MultiReader(bufio.NewCachedReader(stream, buffer), stream)
message := udpMessagePool.Get().(*udpMessage)
message := allocMessage()
err = readUDPMessage(message, reader)
if err != nil {
message.release()

View File

@@ -17,6 +17,7 @@ import (
"github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/cache"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
)
@@ -26,11 +27,16 @@ var udpMessagePool = sync.Pool{
},
}
func allocMessage() *udpMessage {
message := udpMessagePool.Get().(*udpMessage)
message.referenced = true
return message
}
func releaseMessages(messages []*udpMessage) {
for _, message := range messages {
if message != nil {
*message = udpMessage{}
udpMessagePool.Put(message)
message.release()
}
}
}
@@ -41,11 +47,14 @@ type udpMessage struct {
fragmentTotal uint8
fragmentID uint8
destination M.Socksaddr
dataLength uint16
data *buf.Buffer
referenced bool
}
func (m *udpMessage) release() {
if !m.referenced {
return
}
*m = udpMessage{}
udpMessagePool.Put(m)
}
@@ -72,7 +81,7 @@ func (m *udpMessage) pack() *buf.Buffer {
}
func (m *udpMessage) headerSize() int {
return 2 + 10 + addressSerializer.AddrPortLen(m.destination)
return 10 + addressSerializer.AddrPortLen(m.destination)
}
func fragUDPMessage(message *udpMessage, maxPacketSize int) []*udpMessage {
@@ -83,7 +92,7 @@ func fragUDPMessage(message *udpMessage, maxPacketSize int) []*udpMessage {
originPacket := message.data.Bytes()
udpMTU := maxPacketSize - message.headerSize()
for remaining := len(originPacket); remaining > 0; remaining -= udpMTU {
fragment := udpMessagePool.Get().(*udpMessage)
fragment := allocMessage()
*fragment = *message
if remaining > udpMTU {
fragment.data = buf.As(originPacket[:udpMTU])
@@ -106,18 +115,19 @@ func fragUDPMessage(message *udpMessage, maxPacketSize int) []*udpMessage {
}
type udpPacketConn struct {
ctx context.Context
cancel common.ContextCancelCauseFunc
sessionID uint16
quicConn quic.Connection
data chan *udpMessage
udpStream bool
udpMTU int
packetId atomic.Uint32
closeOnce sync.Once
isServer bool
defragger *udpDefragger
onDestroy func()
ctx context.Context
cancel common.ContextCancelCauseFunc
sessionID uint16
quicConn quic.Connection
data chan *udpMessage
udpStream bool
udpMTU int
udpMTUTime time.Time
packetId atomic.Uint32
closeOnce sync.Once
isServer bool
defragger *udpDefragger
onDestroy func()
}
func newUDPPacketConn(ctx context.Context, quicConn quic.Connection, udpStream bool, isServer bool, onDestroy func()) *udpPacketConn {
@@ -186,6 +196,15 @@ func (c *udpPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
}
}
func (c *udpPacketConn) needFragment() bool {
nowTime := time.Now()
if c.udpMTU > 0 && nowTime.Sub(c.udpMTUTime) < 5*time.Second {
c.udpMTUTime = nowTime
return true
}
return false
}
func (c *udpPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
defer buffer.Release()
select {
@@ -196,12 +215,15 @@ func (c *udpPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr)
if buffer.Len() > 0xffff {
return quic.ErrMessageTooLarge(0xffff)
}
if !destination.IsValid() {
return E.New("invalid destination address")
}
packetId := c.packetId.Add(1)
if packetId > math.MaxUint16 {
c.packetId.Store(0)
packetId = 0
}
message := udpMessagePool.Get().(*udpMessage)
message := allocMessage()
*message = udpMessage{
sessionID: c.sessionID,
packetID: uint16(packetId),
@@ -211,7 +233,7 @@ func (c *udpPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr)
}
defer message.releaseMessage()
var err error
if !c.udpStream && c.udpMTU > 0 && buffer.Len() > c.udpMTU {
if !c.udpStream && c.needFragment() && buffer.Len() > c.udpMTU {
err = c.writePackets(fragUDPMessage(message, c.udpMTU))
} else {
err = c.writePacket(message)
@@ -224,6 +246,7 @@ func (c *udpPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr)
return err
}
c.udpMTU = int(tooLargeErr)
c.udpMTUTime = time.Now()
return c.writePackets(fragUDPMessage(message, c.udpMTU))
}
@@ -236,20 +259,24 @@ func (c *udpPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
if len(p) > 0xffff {
return 0, quic.ErrMessageTooLarge(0xffff)
}
destination := M.SocksaddrFromNet(addr)
if !destination.IsValid() {
return 0, E.New("invalid destination address")
}
packetId := c.packetId.Add(1)
if packetId > math.MaxUint16 {
c.packetId.Store(0)
packetId = 0
}
message := udpMessagePool.Get().(*udpMessage)
message := allocMessage()
*message = udpMessage{
sessionID: c.sessionID,
packetID: uint16(packetId),
fragmentTotal: 1,
destination: M.SocksaddrFromNet(addr),
destination: destination,
data: buf.As(p),
}
if c.udpMTU > 0 && len(p) > c.udpMTU {
if !c.udpStream && c.needFragment() && len(p) > c.udpMTU {
err = c.writePackets(fragUDPMessage(message, c.udpMTU))
if err == nil {
return len(p), nil
@@ -265,6 +292,7 @@ func (c *udpPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
return
}
c.udpMTU = int(tooLargeErr)
c.udpMTUTime = time.Now()
err = c.writePackets(fragUDPMessage(message, c.udpMTU))
if err == nil {
return len(p), nil
@@ -412,17 +440,22 @@ func (d *udpDefragger) feed(m *udpMessage) *udpMessage {
if int(item.count) != len(item.messages) {
return nil
}
newMessage := udpMessagePool.Get().(*udpMessage)
newMessage := allocMessage()
*newMessage = *item.messages[0]
if m.dataLength > 0 {
newMessage.data = buf.NewSize(int(m.dataLength))
var dataLength uint16
for _, message := range item.messages {
dataLength += uint16(message.data.Len())
}
if dataLength > 0 {
newMessage.data = buf.NewSize(int(dataLength))
for _, message := range item.messages {
newMessage.data.Write(message.data.Bytes())
common.Must1(newMessage.data.Write(message.data.Bytes()))
message.releaseMessage()
}
item.messages = nil
return newMessage
}
item.messages = nil
return nil
}
@@ -447,7 +480,8 @@ func readUDPMessage(message *udpMessage, reader io.Reader) error {
if err != nil {
return err
}
err = binary.Read(reader, binary.BigEndian, &message.dataLength)
var dataLength uint16
err = binary.Read(reader, binary.BigEndian, &dataLength)
if err != nil {
return err
}
@@ -455,7 +489,7 @@ func readUDPMessage(message *udpMessage, reader io.Reader) error {
if err != nil {
return err
}
message.data = buf.NewSize(int(message.dataLength))
message.data = buf.NewSize(int(dataLength))
_, err = message.data.ReadFullFrom(reader, message.data.FreeLen())
if err != nil {
return err
@@ -481,7 +515,8 @@ func decodeUDPMessage(message *udpMessage, data []byte) error {
if err != nil {
return err
}
err = binary.Read(reader, binary.BigEndian, &message.dataLength)
var dataLength uint16
err = binary.Read(reader, binary.BigEndian, &dataLength)
if err != nil {
return err
}
@@ -489,7 +524,7 @@ func decodeUDPMessage(message *udpMessage, data []byte) error {
if err != nil {
return err
}
if reader.Len() != int(message.dataLength) {
if reader.Len() != int(dataLength) {
return io.ErrUnexpectedEOF
}
message.data = buf.As(data[len(data)-reader.Len():])

View File

@@ -13,9 +13,9 @@ import (
"time"
"github.com/sagernet/quic-go"
"github.com/sagernet/sing-box/common/baderror"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/baderror"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
@@ -264,7 +264,7 @@ func (s *serverSession) handleUniStream(stream quic.ReceiveStream) error {
return s.connErr
case <-s.authDone:
}
message := udpMessagePool.Get().(*udpMessage)
message := allocMessage()
err = readUDPMessage(message, io.MultiReader(bytes.NewReader(buffer.From(2)), stream))
if err != nil {
message.release()

View File

@@ -35,7 +35,7 @@ func (s *serverSession) handleMessage(data []byte) error {
}
switch data[1] {
case CommandPacket:
message := udpMessagePool.Get().(*udpMessage)
message := allocMessage()
err := decodeUDPMessage(message, data[2:])
if err != nil {
message.release()

View File

@@ -5,8 +5,9 @@ import (
"os"
"time"
"github.com/sagernet/sing-box/common/baderror"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/baderror"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/rw"
)
@@ -62,11 +63,11 @@ func (c *GRPCConn) Close() error {
}
func (c *GRPCConn) LocalAddr() net.Addr {
return nil
return M.Socksaddr{}
}
func (c *GRPCConn) RemoteAddr() net.Addr {
return nil
return M.Socksaddr{}
}
func (c *GRPCConn) SetDeadline(t time.Time) error {

View File

@@ -11,10 +11,11 @@ import (
"sync"
"time"
"github.com/sagernet/sing-box/common/baderror"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/baderror"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/rw"
)
@@ -138,7 +139,7 @@ func (c *GunConn) Close() error {
}
func (c *GunConn) LocalAddr() net.Addr {
return nil
return M.Socksaddr{}
}
func (c *GunConn) RemoteAddr() net.Addr {

View File

@@ -10,12 +10,13 @@ import (
"sync"
"time"
"github.com/sagernet/sing-box/common/baderror"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/baderror"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
@@ -174,11 +175,11 @@ func (c *HTTP2Conn) Close() error {
}
func (c *HTTP2Conn) LocalAddr() net.Addr {
return nil
return M.Socksaddr{}
}
func (c *HTTP2Conn) RemoteAddr() net.Addr {
return nil
return M.Socksaddr{}
}
func (c *HTTP2Conn) SetDeadline(t time.Time) error {

View File

@@ -150,7 +150,10 @@ func (c *Conn) Write(b []byte) (n int, err error) {
func (c *Conn) WriteBuffer(buffer *buf.Buffer) error {
if !c.requestWritten {
EncodeRequest(c.request, buf.With(buffer.ExtendHeader(RequestLen(c.request))))
err := EncodeRequest(c.request, buf.With(buffer.ExtendHeader(RequestLen(c.request))))
if err != nil {
return err
}
c.requestWritten = true
}
return c.ExtendedConn.WriteBuffer(buffer)
@@ -159,7 +162,11 @@ func (c *Conn) WriteBuffer(buffer *buf.Buffer) error {
func (c *Conn) WriteVectorised(buffers []*buf.Buffer) error {
if !c.requestWritten {
buffer := buf.NewSize(RequestLen(c.request))
EncodeRequest(c.request, buffer)
err := EncodeRequest(c.request, buffer)
if err != nil {
buffer.Release()
return err
}
c.requestWritten = true
return c.writer.WriteVectorised(append([]*buf.Buffer{buffer}, buffers...))
}

View File

@@ -156,14 +156,17 @@ func WriteRequest(writer io.Writer, request Request, payload []byte) error {
)
if request.Command != vmess.CommandMux {
common.Must(vmess.AddressSerializer.WriteAddrPort(buffer, request.Destination))
err := vmess.AddressSerializer.WriteAddrPort(buffer, request.Destination)
if err != nil {
return err
}
}
common.Must1(buffer.Write(payload))
return common.Error(writer.Write(buffer.Bytes()))
}
func EncodeRequest(request Request, buffer *buf.Buffer) {
func EncodeRequest(request Request, buffer *buf.Buffer) error {
var requestLen int
requestLen += 1 // version
requestLen += 16 // uuid
@@ -195,8 +198,12 @@ func EncodeRequest(request Request, buffer *buf.Buffer) {
)
if request.Command != vmess.CommandMux {
common.Must(vmess.AddressSerializer.WriteAddrPort(buffer, request.Destination))
err := vmess.AddressSerializer.WriteAddrPort(buffer, request.Destination)
if err != nil {
return err
}
}
return nil
}
func RequestLen(request Request) int {
@@ -251,10 +258,12 @@ func WritePacketRequest(writer io.Writer, request Request, payload []byte) error
common.Must(common.Error(buffer.WriteString(request.Flow)))
}
common.Must(
buffer.WriteByte(vmess.CommandUDP),
vmess.AddressSerializer.WriteAddrPort(buffer, request.Destination),
)
common.Must(buffer.WriteByte(vmess.CommandUDP))
err := vmess.AddressSerializer.WriteAddrPort(buffer, request.Destination)
if err != nil {
return err
}
if len(payload) > 0 {
common.Must(