mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-08-07 22:35:16 +03:00
Update sing-box core
This commit is contained in:
2
.github/CRONET_GO_VERSION
vendored
2
.github/CRONET_GO_VERSION
vendored
@@ -1 +1 @@
|
|||||||
98d539ce67568fb911654e66a14cf4247ed833ec
|
617d38f41f935b46a68f550d9add2e38abb3f168
|
||||||
|
|||||||
2
.github/setup_go_for_macos1013.sh
vendored
2
.github/setup_go_for_macos1013.sh
vendored
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
VERSION="1.25.10"
|
VERSION="1.25.12"
|
||||||
PATCH_COMMITS=(
|
PATCH_COMMITS=(
|
||||||
"afe69d3cec1c6dcf0f1797b20546795730850070"
|
"afe69d3cec1c6dcf0f1797b20546795730850070"
|
||||||
"1ed289b0cf87dc5aae9c6fe1aa5f200a83412938"
|
"1ed289b0cf87dc5aae9c6fe1aa5f200a83412938"
|
||||||
|
|||||||
14
.github/setup_go_for_windows7.sh
vendored
14
.github/setup_go_for_windows7.sh
vendored
@@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
VERSION="1.25.10"
|
VERSION="1.25.12"
|
||||||
PATCH_COMMITS=(
|
PATCH_COMMITS=(
|
||||||
"466f6c7a29bc098b0d4c987b803c779222894a11"
|
"da4094da73b3b419e3f347594d805e2831f65667"
|
||||||
"1bdabae205052afe1dadb2ad6f1ba612cdbc532a"
|
"824aa60e77f06dbae86c20a164c78df722eb7047"
|
||||||
"a90777dcf692dd2168577853ba743b4338721b06"
|
"a3b6ba31c8cc67b6d899b978bba7b53e95afc46b"
|
||||||
"f6bddda4e8ff58a957462a1a09562924d5f3d05c"
|
"edfa8de63435a409a59f60731b66ab5940d6d3a4"
|
||||||
"bed309eff415bcb3c77dd4bc3277b682b89a388d"
|
"284f9b24d6284984966a8431e30fdc2583938f96"
|
||||||
"34b899c2fb39b092db4fa67c4417e41dc046be4b"
|
"9864798dee8dd47b55d1d5100d2f1b909a2a6e6c"
|
||||||
)
|
)
|
||||||
CURL_ARGS=(
|
CURL_ARGS=(
|
||||||
-fL
|
-fL
|
||||||
|
|||||||
789
.github/workflows/build.yml
vendored
789
.github/workflows/build.yml
vendored
File diff suppressed because it is too large
Load Diff
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@@ -55,7 +55,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ~1.25.10
|
go-version: 1.25.12
|
||||||
- name: Clone cronet-go
|
- name: Clone cronet-go
|
||||||
if: matrix.naive
|
if: matrix.naive
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
5
.github/workflows/linux.yml
vendored
5
.github/workflows/linux.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ~1.25.10
|
go-version: 1.25.12
|
||||||
- name: Check input version
|
- name: Check input version
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
@@ -72,7 +72,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ~1.25.10
|
go-version: 1.25.12
|
||||||
- name: Clone cronet-go
|
- name: Clone cronet-go
|
||||||
if: matrix.naive
|
if: matrix.naive
|
||||||
run: |
|
run: |
|
||||||
@@ -194,6 +194,7 @@ jobs:
|
|||||||
run: |-
|
run: |-
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
sudo gem install fpm
|
sudo gem install fpm
|
||||||
|
echo '%_rpmformat 4' > "$HOME/.rpmmacros"
|
||||||
cp .fpm_systemd .fpm
|
cp .fpm_systemd .fpm
|
||||||
fpm -t rpm \
|
fpm -t rpm \
|
||||||
--name "${NAME}" \
|
--name "${NAME}" \
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ type OutboundWithPreferredRoutes interface {
|
|||||||
PreferredAddress(address netip.Addr) bool
|
PreferredAddress(address netip.Addr) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OutboundWithMultiplex interface {
|
||||||
|
Outbound
|
||||||
|
MultiplexEnabled() bool
|
||||||
|
}
|
||||||
|
|
||||||
type DirectRouteOutbound interface {
|
type DirectRouteOutbound interface {
|
||||||
Outbound
|
Outbound
|
||||||
NewDirectRouteConnection(metadata InboundContext, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error)
|
NewDirectRouteConnection(metadata InboundContext, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error)
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ func prepareAppStore(ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(builds.Data) == 0 {
|
if len(builds.Data) == 0 {
|
||||||
log.Fatal(platform, " ", tag, " no build found")
|
log.Fatal(string(platform), " ", tag, " no build found")
|
||||||
}
|
}
|
||||||
buildID := common.Ptr(builds.Data[0].ID)
|
buildID := common.Ptr(builds.Data[0].ID)
|
||||||
if version.ID == "" {
|
if version.ID == "" {
|
||||||
|
|||||||
165
cmd/internal/merge_aar/main.go
Normal file
165
cmd/internal/merge_aar/main.go
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"crypto/sha256"
|
||||||
|
"flag"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
var outputPath string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.StringVar(&outputPath, "output", "", "output AAR path")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
err := merge()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func merge() error {
|
||||||
|
inputPaths := flag.Args()
|
||||||
|
if outputPath == "" {
|
||||||
|
return E.New("missing output path")
|
||||||
|
}
|
||||||
|
if len(inputPaths) == 0 {
|
||||||
|
return E.New("missing input AAR paths")
|
||||||
|
}
|
||||||
|
archiveReaders := make([]*zip.ReadCloser, 0, len(inputPaths))
|
||||||
|
for _, inputPath := range inputPaths {
|
||||||
|
archiveReader, err := zip.OpenReader(inputPath)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "open input AAR: ", inputPath)
|
||||||
|
}
|
||||||
|
archiveReaders = append(archiveReaders, archiveReader)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
for _, archiveReader := range archiveReaders {
|
||||||
|
archiveReader.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
referenceEntries := make(map[string][sha256.Size]byte)
|
||||||
|
selectedEntries := make([]*zip.File, 0)
|
||||||
|
selectedJNIEntries := make(map[string]bool)
|
||||||
|
for inputIndex, archiveReader := range archiveReaders {
|
||||||
|
seenEntries := make(map[string]bool)
|
||||||
|
for _, archiveFile := range archiveReader.File {
|
||||||
|
if strings.HasPrefix(archiveFile.Name, "jni/") {
|
||||||
|
if archiveFile.FileInfo().IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if selectedJNIEntries[archiveFile.Name] {
|
||||||
|
return E.New("duplicate AAR JNI entry: ", archiveFile.Name)
|
||||||
|
}
|
||||||
|
selectedJNIEntries[archiveFile.Name] = true
|
||||||
|
selectedEntries = append(selectedEntries, archiveFile)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
entryDigest, err := digestEntry(archiveFile)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "read AAR entry: ", archiveFile.Name)
|
||||||
|
}
|
||||||
|
if inputIndex == 0 {
|
||||||
|
referenceEntries[archiveFile.Name] = entryDigest
|
||||||
|
selectedEntries = append(selectedEntries, archiveFile)
|
||||||
|
} else {
|
||||||
|
referenceDigest, loaded := referenceEntries[archiveFile.Name]
|
||||||
|
if !loaded {
|
||||||
|
return E.New("unexpected AAR entry: ", archiveFile.Name)
|
||||||
|
}
|
||||||
|
if referenceDigest != entryDigest {
|
||||||
|
return E.New("AAR entry differs between architectures: ", archiveFile.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seenEntries[archiveFile.Name] = true
|
||||||
|
}
|
||||||
|
if inputIndex > 0 {
|
||||||
|
for referenceName := range referenceEntries {
|
||||||
|
if !seenEntries[referenceName] {
|
||||||
|
return E.New("missing AAR entry: ", referenceName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
absoluteOutputPath, err := filepath.Abs(outputPath)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "resolve output AAR path")
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(filepath.Dir(absoluteOutputPath), 0o755)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "create output AAR directory")
|
||||||
|
}
|
||||||
|
temporaryFile, err := os.CreateTemp(filepath.Dir(absoluteOutputPath), ".merge-aar-*.aar")
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "create temporary output AAR")
|
||||||
|
}
|
||||||
|
temporaryPath := temporaryFile.Name()
|
||||||
|
defer os.Remove(temporaryPath)
|
||||||
|
archiveWriter := zip.NewWriter(temporaryFile)
|
||||||
|
for _, archiveFile := range selectedEntries {
|
||||||
|
rawReader, openErr := archiveFile.OpenRaw()
|
||||||
|
if openErr != nil {
|
||||||
|
archiveWriter.Close()
|
||||||
|
temporaryFile.Close()
|
||||||
|
return E.Cause(openErr, "open raw AAR entry: ", archiveFile.Name)
|
||||||
|
}
|
||||||
|
header := archiveFile.FileHeader
|
||||||
|
rawWriter, createErr := archiveWriter.CreateRaw(&header)
|
||||||
|
if createErr != nil {
|
||||||
|
archiveWriter.Close()
|
||||||
|
temporaryFile.Close()
|
||||||
|
return E.Cause(createErr, "create output AAR entry: ", archiveFile.Name)
|
||||||
|
}
|
||||||
|
_, copyErr := io.Copy(rawWriter, rawReader)
|
||||||
|
if copyErr != nil {
|
||||||
|
archiveWriter.Close()
|
||||||
|
temporaryFile.Close()
|
||||||
|
return E.Cause(copyErr, "copy output AAR entry: ", archiveFile.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = archiveWriter.Close()
|
||||||
|
if err != nil {
|
||||||
|
temporaryFile.Close()
|
||||||
|
return E.Cause(err, "finalize output AAR")
|
||||||
|
}
|
||||||
|
err = temporaryFile.Close()
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "close output AAR")
|
||||||
|
}
|
||||||
|
err = os.Rename(temporaryPath, absoluteOutputPath)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "replace output AAR")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func digestEntry(archiveFile *zip.File) ([sha256.Size]byte, error) {
|
||||||
|
entryReader, err := archiveFile.Open()
|
||||||
|
if err != nil {
|
||||||
|
return [sha256.Size]byte{}, err
|
||||||
|
}
|
||||||
|
digest := sha256.New()
|
||||||
|
_, err = io.Copy(digest, entryReader)
|
||||||
|
closeErr := entryReader.Close()
|
||||||
|
if err != nil {
|
||||||
|
return [sha256.Size]byte{}, err
|
||||||
|
}
|
||||||
|
if closeErr != nil {
|
||||||
|
return [sha256.Size]byte{}, closeErr
|
||||||
|
}
|
||||||
|
var result [sha256.Size]byte
|
||||||
|
copy(result[:], digest.Sum(nil))
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
172
cmd/internal/merge_apple_xcframework/main.go
Normal file
172
cmd/internal/merge_apple_xcframework/main.go
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
|
||||||
|
"howett.net/plist"
|
||||||
|
)
|
||||||
|
|
||||||
|
type xcFrameworkInfo struct {
|
||||||
|
AvailableLibraries []xcFrameworkLibrary `plist:"AvailableLibraries"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type xcFrameworkLibrary struct {
|
||||||
|
BinaryPath string `plist:"BinaryPath"`
|
||||||
|
LibraryIdentifier string `plist:"LibraryIdentifier"`
|
||||||
|
LibraryPath string `plist:"LibraryPath"`
|
||||||
|
SupportedArchitectures []string `plist:"SupportedArchitectures"`
|
||||||
|
SupportedPlatform string `plist:"SupportedPlatform"`
|
||||||
|
SupportedPlatformVariant string `plist:"SupportedPlatformVariant"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type frameworkSlice struct {
|
||||||
|
rootPath string
|
||||||
|
library xcFrameworkLibrary
|
||||||
|
}
|
||||||
|
|
||||||
|
var outputPath string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.StringVar(&outputPath, "output", "", "output XCFramework path")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
err := merge()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func merge() error {
|
||||||
|
inputPaths := flag.Args()
|
||||||
|
if outputPath == "" {
|
||||||
|
return E.New("missing output path")
|
||||||
|
}
|
||||||
|
if len(inputPaths) == 0 {
|
||||||
|
return E.New("missing input XCFramework paths")
|
||||||
|
}
|
||||||
|
frameworkGroups := make(map[string][]frameworkSlice)
|
||||||
|
for _, inputPath := range inputPaths {
|
||||||
|
infoFile, err := os.Open(filepath.Join(inputPath, "Info.plist"))
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "open XCFramework metadata: ", inputPath)
|
||||||
|
}
|
||||||
|
var info xcFrameworkInfo
|
||||||
|
decoder := plist.NewDecoder(infoFile)
|
||||||
|
err = decoder.Decode(&info)
|
||||||
|
closeErr := infoFile.Close()
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "decode XCFramework metadata: ", inputPath)
|
||||||
|
}
|
||||||
|
if closeErr != nil {
|
||||||
|
return E.Cause(closeErr, "close XCFramework metadata: ", inputPath)
|
||||||
|
}
|
||||||
|
for _, library := range info.AvailableLibraries {
|
||||||
|
groupName := library.SupportedPlatform + "|" + library.SupportedPlatformVariant
|
||||||
|
frameworkGroups[groupName] = append(frameworkGroups[groupName], frameworkSlice{
|
||||||
|
rootPath: inputPath,
|
||||||
|
library: library,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
groupNames := make([]string, 0, len(frameworkGroups))
|
||||||
|
for groupName := range frameworkGroups {
|
||||||
|
groupNames = append(groupNames, groupName)
|
||||||
|
}
|
||||||
|
sort.Strings(groupNames)
|
||||||
|
absoluteOutputPath, err := filepath.Abs(outputPath)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "resolve output XCFramework path")
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(filepath.Dir(absoluteOutputPath), 0o755)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "create output XCFramework directory")
|
||||||
|
}
|
||||||
|
temporaryDirectory, err := os.MkdirTemp(filepath.Dir(absoluteOutputPath), ".merge-xcframework-*")
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "create XCFramework merge directory")
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(temporaryDirectory)
|
||||||
|
frameworkPaths := make([]string, 0, len(groupNames))
|
||||||
|
for groupIndex, groupName := range groupNames {
|
||||||
|
frameworkSlices := frameworkGroups[groupName]
|
||||||
|
firstSlice := frameworkSlices[0]
|
||||||
|
firstFrameworkPath := filepath.Join(firstSlice.rootPath, firstSlice.library.LibraryIdentifier, firstSlice.library.LibraryPath)
|
||||||
|
if len(frameworkSlices) == 1 {
|
||||||
|
frameworkPaths = append(frameworkPaths, firstFrameworkPath)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
architectures := make(map[string]bool)
|
||||||
|
binaryPaths := make([]string, 0, len(frameworkSlices))
|
||||||
|
for _, currentSlice := range frameworkSlices {
|
||||||
|
if currentSlice.library.LibraryPath != firstSlice.library.LibraryPath || currentSlice.library.BinaryPath != firstSlice.library.BinaryPath {
|
||||||
|
return E.New("incompatible XCFramework slices for platform: ", currentSlice.library.SupportedPlatform)
|
||||||
|
}
|
||||||
|
for _, architecture := range currentSlice.library.SupportedArchitectures {
|
||||||
|
if architectures[architecture] {
|
||||||
|
return E.New("duplicate XCFramework architecture: ", architecture)
|
||||||
|
}
|
||||||
|
architectures[architecture] = true
|
||||||
|
}
|
||||||
|
binaryPaths = append(binaryPaths, filepath.Join(currentSlice.rootPath, currentSlice.library.LibraryIdentifier, currentSlice.library.BinaryPath))
|
||||||
|
}
|
||||||
|
mergedFrameworkPath := filepath.Join(temporaryDirectory, "framework-"+strconv.Itoa(groupIndex), filepath.Base(firstSlice.library.LibraryPath))
|
||||||
|
copyCommand := exec.Command("ditto", firstFrameworkPath, mergedFrameworkPath)
|
||||||
|
copyCommand.Stdout = os.Stdout
|
||||||
|
copyCommand.Stderr = os.Stderr
|
||||||
|
err = copyCommand.Run()
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "copy XCFramework slice")
|
||||||
|
}
|
||||||
|
binaryRelativePath, relativeErr := filepath.Rel(firstSlice.library.LibraryPath, firstSlice.library.BinaryPath)
|
||||||
|
if relativeErr != nil {
|
||||||
|
return E.Cause(relativeErr, "resolve XCFramework binary path")
|
||||||
|
}
|
||||||
|
if binaryRelativePath == "." || strings.HasPrefix(binaryRelativePath, ".."+string(filepath.Separator)) {
|
||||||
|
return E.New("invalid XCFramework binary path: ", firstSlice.library.BinaryPath)
|
||||||
|
}
|
||||||
|
mergedBinaryPath := filepath.Join(mergedFrameworkPath, binaryRelativePath)
|
||||||
|
temporaryBinaryPath := mergedBinaryPath + ".merged"
|
||||||
|
lipoArguments := append([]string{"lipo", "-create"}, binaryPaths...)
|
||||||
|
lipoArguments = append(lipoArguments, "-output", temporaryBinaryPath)
|
||||||
|
lipoCommand := exec.Command("xcrun", lipoArguments...)
|
||||||
|
lipoCommand.Stdout = os.Stdout
|
||||||
|
lipoCommand.Stderr = os.Stderr
|
||||||
|
err = lipoCommand.Run()
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "merge XCFramework binaries")
|
||||||
|
}
|
||||||
|
err = os.Rename(temporaryBinaryPath, mergedBinaryPath)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "replace merged XCFramework binary")
|
||||||
|
}
|
||||||
|
frameworkPaths = append(frameworkPaths, mergedFrameworkPath)
|
||||||
|
}
|
||||||
|
err = os.RemoveAll(absoluteOutputPath)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "remove output XCFramework")
|
||||||
|
}
|
||||||
|
xcodebuildArguments := []string{"-create-xcframework"}
|
||||||
|
for _, frameworkPath := range frameworkPaths {
|
||||||
|
xcodebuildArguments = append(xcodebuildArguments, "-framework", frameworkPath)
|
||||||
|
}
|
||||||
|
xcodebuildArguments = append(xcodebuildArguments, "-output", absoluteOutputPath)
|
||||||
|
xcodebuildCommand := exec.Command("xcodebuild", xcodebuildArguments...)
|
||||||
|
xcodebuildCommand.Stdout = os.Stdout
|
||||||
|
xcodebuildCommand.Stderr = os.Stderr
|
||||||
|
err = xcodebuildCommand.Run()
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "create XCFramework")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -106,6 +106,7 @@ func findAndReplaceProjectVersion(objectsMap map[string]any, projectContent stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
func findObjectKey(objectsMap map[string]any, bundleIDList []string) []string {
|
func findObjectKey(objectsMap map[string]any, bundleIDList []string) []string {
|
||||||
|
globalSettings := collectBuildSettings(objectsMap)
|
||||||
var objectKeyList []string
|
var objectKeyList []string
|
||||||
for objectKey, object := range objectsMap {
|
for objectKey, object := range objectsMap {
|
||||||
buildSettings := object.(map[string]any)["buildSettings"]
|
buildSettings := object.(map[string]any)["buildSettings"]
|
||||||
@@ -116,13 +117,51 @@ func findObjectKey(objectsMap map[string]any, bundleIDList []string) []string {
|
|||||||
if bundleIDObject == nil {
|
if bundleIDObject == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if common.Contains(bundleIDList, bundleIDObject.(string)) {
|
bundleID := expandBuildVariables(bundleIDObject.(string), globalSettings)
|
||||||
|
if common.Contains(bundleIDList, bundleID) {
|
||||||
objectKeyList = append(objectKeyList, objectKey)
|
objectKeyList = append(objectKeyList, objectKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return objectKeyList
|
return objectKeyList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func collectBuildSettings(objectsMap map[string]any) map[string]string {
|
||||||
|
settings := make(map[string]string)
|
||||||
|
for _, object := range objectsMap {
|
||||||
|
buildSettings, loaded := object.(map[string]any)["buildSettings"].(map[string]any)
|
||||||
|
if !loaded {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for key, value := range buildSettings {
|
||||||
|
valueString, isString := value.(string)
|
||||||
|
if !isString {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
settings[key] = valueString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return settings
|
||||||
|
}
|
||||||
|
|
||||||
|
var buildVariableRegexp = regexp.MustCompile(`\$[({]([A-Za-z0-9_]+)[)}]`)
|
||||||
|
|
||||||
|
func expandBuildVariables(value string, settings map[string]string) string {
|
||||||
|
for {
|
||||||
|
expanded := buildVariableRegexp.ReplaceAllStringFunc(value, func(match string) string {
|
||||||
|
name := buildVariableRegexp.FindStringSubmatch(match)[1]
|
||||||
|
replacement, loaded := settings[name]
|
||||||
|
if !loaded {
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
return replacement
|
||||||
|
})
|
||||||
|
if expanded == value {
|
||||||
|
return expanded
|
||||||
|
}
|
||||||
|
value = expanded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func findObjectKeyByDirectory(objectsMap map[string]any, directoryList []string) []string {
|
func findObjectKeyByDirectory(objectsMap map[string]any, directoryList []string) []string {
|
||||||
var objectKeyList []string
|
var objectKeyList []string
|
||||||
for objectKey, object := range objectsMap {
|
for objectKey, object := range objectsMap {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ type DefaultDialer struct {
|
|||||||
udpAddr4 string
|
udpAddr4 string
|
||||||
udpAddr6 string
|
udpAddr6 string
|
||||||
netns string
|
netns string
|
||||||
|
autoDetectBindFunc control.Func
|
||||||
connectionManager adapter.ConnectionManager
|
connectionManager adapter.ConnectionManager
|
||||||
networkManager adapter.NetworkManager
|
networkManager adapter.NetworkManager
|
||||||
networkStrategy *C.NetworkStrategy
|
networkStrategy *C.NetworkStrategy
|
||||||
@@ -60,6 +61,7 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
|||||||
networkType []C.InterfaceType
|
networkType []C.InterfaceType
|
||||||
fallbackNetworkType []C.InterfaceType
|
fallbackNetworkType []C.InterfaceType
|
||||||
networkFallbackDelay time.Duration
|
networkFallbackDelay time.Duration
|
||||||
|
autoDetectBindFunc control.Func
|
||||||
)
|
)
|
||||||
if networkManager != nil {
|
if networkManager != nil {
|
||||||
interfaceFinder = networkManager.InterfaceFinder()
|
interfaceFinder = networkManager.InterfaceFinder()
|
||||||
@@ -130,6 +132,7 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
|||||||
bindFunc := networkManager.AutoDetectInterfaceFunc()
|
bindFunc := networkManager.AutoDetectInterfaceFunc()
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
|
autoDetectBindFunc = bindFunc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if options.RoutingMark == 0 && defaultOptions.RoutingMark != 0 {
|
if options.RoutingMark == 0 && defaultOptions.RoutingMark != 0 {
|
||||||
@@ -224,6 +227,7 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
|||||||
udpAddr4: udpAddr4,
|
udpAddr4: udpAddr4,
|
||||||
udpAddr6: udpAddr6,
|
udpAddr6: udpAddr6,
|
||||||
netns: options.NetNs,
|
netns: options.NetNs,
|
||||||
|
autoDetectBindFunc: autoDetectBindFunc,
|
||||||
connectionManager: connectionManager,
|
connectionManager: connectionManager,
|
||||||
networkManager: networkManager,
|
networkManager: networkManager,
|
||||||
networkStrategy: networkStrategy,
|
networkStrategy: networkStrategy,
|
||||||
@@ -328,12 +332,18 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
|
|||||||
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
if d.networkStrategy == nil {
|
if d.networkStrategy == nil {
|
||||||
return d.trackPacketConn(listener.ListenNetworkNamespace[net.PacketConn](d.netns, func() (net.PacketConn, error) {
|
return d.trackPacketConn(listener.ListenNetworkNamespace[net.PacketConn](d.netns, func() (net.PacketConn, error) {
|
||||||
|
listenConfig := d.udpListener
|
||||||
|
if d.autoDetectBindFunc != nil && destination.Addr.IsValid() {
|
||||||
|
listenConfig.Control = control.Append(listenConfig.Control, func(network, address string, conn syscall.RawConn) error {
|
||||||
|
return d.autoDetectBindFunc(network, destination.String(), conn)
|
||||||
|
})
|
||||||
|
}
|
||||||
if destination.IsIPv6() {
|
if destination.IsIPv6() {
|
||||||
return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6)
|
return listenConfig.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6)
|
||||||
} else if destination.IsIPv4() && !destination.Addr.IsUnspecified() {
|
} else if destination.IsIPv4() && !destination.Addr.IsUnspecified() {
|
||||||
return d.udpListener.ListenPacket(ctx, N.NetworkUDP+"4", d.udpAddr4)
|
return listenConfig.ListenPacket(ctx, N.NetworkUDP+"4", d.udpAddr4)
|
||||||
} else {
|
} else {
|
||||||
return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4)
|
return listenConfig.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Searcher = (*androidSearcher)(nil)
|
var _ Searcher = (*androidSearcher)(nil)
|
||||||
@@ -16,6 +17,9 @@ type androidSearcher struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewSearcher(config Config) (Searcher, error) {
|
func NewSearcher(config Config) (Searcher, error) {
|
||||||
|
if config.PackageManager == nil {
|
||||||
|
return nil, E.New("missing package manager")
|
||||||
|
}
|
||||||
return &androidSearcher{config.PackageManager}, nil
|
return &androidSearcher{config.PackageManager}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
if len(certificate) > 0 {
|
if len(certificate) > 0 {
|
||||||
certPool := x509.NewCertPool()
|
certPool := x509.NewCertPool()
|
||||||
if !certPool.AppendCertsFromPEM(certificate) {
|
if !certPool.AppendCertsFromPEM(certificate) {
|
||||||
return nil, E.New("failed to parse certificate:\n\n", certificate)
|
return nil, E.New("failed to parse certificate:\n\n", string(certificate))
|
||||||
}
|
}
|
||||||
tlsConfig.RootCAs = certPool
|
tlsConfig.RootCAs = certPool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ func (c *STDServerConfig) certificateUpdated(path string) error {
|
|||||||
for _, certPath := range c.clientCertificatePath {
|
for _, certPath := range c.clientCertificatePath {
|
||||||
content, err := os.ReadFile(certPath)
|
content, err := os.ReadFile(certPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Error(E.Cause(err, "reload certificate from ", c.clientCertificatePath))
|
c.logger.Error(E.Cause(err, "reload certificate from ", certPath))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !clientCertificateCA.AppendCertsFromPEM(content) {
|
if !clientCertificateCA.AppendCertsFromPEM(content) {
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ func NewUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddre
|
|||||||
if len(certificate) > 0 {
|
if len(certificate) > 0 {
|
||||||
certPool := x509.NewCertPool()
|
certPool := x509.NewCertPool()
|
||||||
if !certPool.AppendCertsFromPEM(certificate) {
|
if !certPool.AppendCertsFromPEM(certificate) {
|
||||||
return nil, E.New("failed to parse certificate:\n\n", certificate)
|
return nil, E.New("failed to parse certificate:\n\n", string(certificate))
|
||||||
}
|
}
|
||||||
tlsConfig.RootCAs = certPool
|
tlsConfig.RootCAs = certPool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
@@ -72,7 +73,18 @@ func (s *HistoryStorage) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func URLTest(ctx context.Context, link string, detour N.Dialer) (t uint16, err error) {
|
func URLTest(ctx context.Context, link string, detour N.Dialer) (uint16, error) {
|
||||||
|
multiplexOutbound, isMultiplexOutbound := common.Cast[adapter.OutboundWithMultiplex](detour)
|
||||||
|
if isMultiplexOutbound && multiplexOutbound.MultiplexEnabled() {
|
||||||
|
_, err := urlTest(ctx, link, detour)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return urlTest(ctx, link, detour)
|
||||||
|
}
|
||||||
|
|
||||||
|
func urlTest(ctx context.Context, link string, detour N.Dialer) (t uint16, err error) {
|
||||||
if link == "" {
|
if link == "" {
|
||||||
link = "https://www.gstatic.com/generate_204"
|
link = "https://www.gstatic.com/generate_204"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,18 @@ func extractNegativeTTL(response *dns.Msg) (uint32, bool) {
|
|||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func stripDNSPadding(response *dns.Msg) {
|
||||||
|
for _, record := range response.Extra {
|
||||||
|
opt, isOpt := record.(*dns.OPT)
|
||||||
|
if !isOpt {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
opt.Option = common.Filter(opt.Option, func(it dns.EDNS0) bool {
|
||||||
|
return it.Option() != dns.EDNS0PADDING
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, message *dns.Msg, options adapter.DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) (*dns.Msg, error) {
|
func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, message *dns.Msg, options adapter.DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) (*dns.Msg, error) {
|
||||||
transportStack := transportStackFromContext(ctx)
|
transportStack := transportStackFromContext(ctx)
|
||||||
if containsTransport(transportStack, transport.Tag()) {
|
if containsTransport(transportStack, transport.Tag()) {
|
||||||
@@ -198,6 +210,8 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
stripDNSPadding(response)
|
||||||
}
|
}
|
||||||
/*if question.Qtype == dns.TypeA || question.Qtype == dns.TypeAAAA {
|
/*if question.Qtype == dns.TypeA || question.Qtype == dns.TypeAAAA {
|
||||||
validResponse := response
|
validResponse := response
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ func (t *Transport) Close() error {
|
|||||||
func (t *Transport) Reset() {
|
func (t *Transport) Reset() {
|
||||||
t.transportLock.Lock()
|
t.transportLock.Lock()
|
||||||
t.updatedAt = time.Time{}
|
t.updatedAt = time.Time{}
|
||||||
|
t.lastError = nil
|
||||||
t.servers = nil
|
t.servers = nil
|
||||||
t.transportLock.Unlock()
|
t.transportLock.Unlock()
|
||||||
}
|
}
|
||||||
@@ -295,9 +296,14 @@ func (t *Transport) fetchServersResponse(iface *control.Interface, packetConn ne
|
|||||||
func (t *Transport) recreateServers(iface *control.Interface, dhcpPacket *dhcpv4.DHCPv4) error {
|
func (t *Transport) recreateServers(iface *control.Interface, dhcpPacket *dhcpv4.DHCPv4) error {
|
||||||
searchList := dhcpPacket.DomainSearch()
|
searchList := dhcpPacket.DomainSearch()
|
||||||
if searchList != nil && len(searchList.Labels) > 0 {
|
if searchList != nil && len(searchList.Labels) > 0 {
|
||||||
t.search = searchList.Labels
|
t.search = common.Filter(common.Map(searchList.Labels, mDNS.Fqdn), func(it string) bool {
|
||||||
|
return it != "."
|
||||||
|
})
|
||||||
} else if dhcpPacket.DomainName() != "" {
|
} else if dhcpPacket.DomainName() != "" {
|
||||||
t.search = []string{dhcpPacket.DomainName()}
|
domainName := mDNS.Fqdn(dhcpPacket.DomainName())
|
||||||
|
if domainName != "." {
|
||||||
|
t.search = []string{domainName}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
serverAddrs := common.Map(dhcpPacket.DNS(), func(it net.IP) M.Socksaddr {
|
serverAddrs := common.Map(dhcpPacket.DNS(), func(it net.IP) M.Socksaddr {
|
||||||
return M.SocksaddrFrom(M.AddrFromIP(it), 53)
|
return M.SocksaddrFrom(M.AddrFromIP(it), 53)
|
||||||
|
|||||||
@@ -120,13 +120,16 @@ func NewHTTPSRaw(
|
|||||||
serverAddr M.Socksaddr,
|
serverAddr M.Socksaddr,
|
||||||
tlsConfig tls.Config,
|
tlsConfig tls.Config,
|
||||||
) *HTTPSTransport {
|
) *HTTPSTransport {
|
||||||
|
if tlsConfig != nil {
|
||||||
|
dialer = tls.NewDialer(dialer, tlsConfig)
|
||||||
|
}
|
||||||
return &HTTPSTransport{
|
return &HTTPSTransport{
|
||||||
TransportAdapter: adapter,
|
TransportAdapter: adapter,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
dialer: dialer,
|
dialer: dialer,
|
||||||
destination: destination,
|
destination: destination,
|
||||||
headers: headers,
|
headers: headers,
|
||||||
transport: NewHTTPSTransportWrapper(tls.NewDialer(dialer, tlsConfig), serverAddr),
|
transport: NewHTTPSTransportWrapper(dialer, serverAddr, destination),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,13 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/tls"
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
)
|
)
|
||||||
@@ -22,27 +24,36 @@ type HTTPSTransportWrapper struct {
|
|||||||
fallback *atomic.Bool
|
fallback *atomic.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPSTransportWrapper(dialer tls.Dialer, serverAddr M.Socksaddr) *HTTPSTransportWrapper {
|
func NewHTTPSTransportWrapper(dialer N.Dialer, serverAddr M.Socksaddr, destination *url.URL) *HTTPSTransportWrapper {
|
||||||
var fallback atomic.Bool
|
var fallback atomic.Bool
|
||||||
|
if destination.Scheme == "http" {
|
||||||
|
// plain HTTP DoH used by Tailscale
|
||||||
|
fallback.Store(true)
|
||||||
|
}
|
||||||
return &HTTPSTransportWrapper{
|
return &HTTPSTransportWrapper{
|
||||||
http2Transport: &http2.Transport{
|
http2Transport: &http2.Transport{
|
||||||
DialTLSContext: func(ctx context.Context, _, _ string, _ *tls.STDConfig) (net.Conn, error) {
|
DialTLSContext: func(ctx context.Context, _, _ string, _ *tls.STDConfig) (net.Conn, error) {
|
||||||
tlsConn, err := dialer.DialTLSContext(ctx, serverAddr)
|
resultConn, err := dialer.DialContext(ctx, N.NetworkTCP, serverAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
state := tlsConn.ConnectionState()
|
if tlsConn, isTLSConn := resultConn.(tls.Conn); isTLSConn {
|
||||||
if state.NegotiatedProtocol == http2.NextProtoTLS {
|
state := tlsConn.ConnectionState()
|
||||||
return tlsConn, nil
|
if state.NegotiatedProtocol != http2.NextProtoTLS {
|
||||||
|
tlsConn.Close()
|
||||||
|
fallback.Store(true)
|
||||||
|
return nil, errFallback
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tlsConn.Close()
|
return resultConn, nil
|
||||||
fallback.Store(true)
|
|
||||||
return nil, errFallback
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
httpTransport: &http.Transport{
|
httpTransport: &http.Transport{
|
||||||
|
DialContext: func(ctx context.Context, _, addr string) (net.Conn, error) {
|
||||||
|
return dialer.DialContext(ctx, N.NetworkTCP, serverAddr)
|
||||||
|
},
|
||||||
DialTLSContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
DialTLSContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||||
return dialer.DialTLSContext(ctx, serverAddr)
|
return dialer.DialContext(ctx, N.NetworkTCP, serverAddr)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
fallback: &fallback,
|
fallback: &fallback,
|
||||||
@@ -52,16 +63,15 @@ func NewHTTPSTransportWrapper(dialer tls.Dialer, serverAddr M.Socksaddr) *HTTPST
|
|||||||
func (h *HTTPSTransportWrapper) RoundTrip(request *http.Request) (*http.Response, error) {
|
func (h *HTTPSTransportWrapper) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||||
if h.fallback.Load() {
|
if h.fallback.Load() {
|
||||||
return h.httpTransport.RoundTrip(request)
|
return h.httpTransport.RoundTrip(request)
|
||||||
} else {
|
|
||||||
response, err := h.http2Transport.RoundTrip(request)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, errFallback) {
|
|
||||||
return h.httpTransport.RoundTrip(request)
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return response, nil
|
|
||||||
}
|
}
|
||||||
|
response, err := h.http2Transport.RoundTrip(request)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, errFallback) {
|
||||||
|
return h.httpTransport.RoundTrip(request)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HTTPSTransportWrapper) CloseIdleConnections() {
|
func (h *HTTPSTransportWrapper) CloseIdleConnections() {
|
||||||
|
|||||||
@@ -126,6 +126,12 @@ func (t *HTTP3Transport) newTransport() *http3.Transport {
|
|||||||
conn.Close()
|
conn.Close()
|
||||||
return nil, dialErr
|
return nil, dialErr
|
||||||
}
|
}
|
||||||
|
// quic-go does not take ownership of the packet conn passed to
|
||||||
|
// DialEarly: when the connection ends it only stops reading.
|
||||||
|
go func() {
|
||||||
|
<-quicConn.Context().Done()
|
||||||
|
conn.Close()
|
||||||
|
}()
|
||||||
return quicConn, nil
|
return quicConn, nil
|
||||||
},
|
},
|
||||||
TLSClientConfig: t.tlsConfig,
|
TLSClientConfig: t.tlsConfig,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/quic-go"
|
"github.com/sagernet/quic-go"
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
@@ -117,6 +118,12 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
|||||||
rawConn.Close()
|
rawConn.Close()
|
||||||
return nil, E.Cause(err, "establish QUIC connection")
|
return nil, E.Cause(err, "establish QUIC connection")
|
||||||
}
|
}
|
||||||
|
// quic-go does not take ownership of the packet conn passed to
|
||||||
|
// DialEarly: when the connection ends it only stops reading.
|
||||||
|
go func() {
|
||||||
|
<-earlyConnection.Context().Done()
|
||||||
|
rawConn.Close()
|
||||||
|
}()
|
||||||
return earlyConnection, nil
|
return earlyConnection, nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -144,6 +151,11 @@ func (t *Transport) exchange(ctx context.Context, message *mDNS.Msg, conn *quic.
|
|||||||
return nil, E.Cause(err, "open stream")
|
return nil, E.Cause(err, "open stream")
|
||||||
}
|
}
|
||||||
defer stream.CancelRead(0)
|
defer stream.CancelRead(0)
|
||||||
|
stopWatch := context.AfterFunc(ctx, func() {
|
||||||
|
stream.CancelRead(0)
|
||||||
|
_ = stream.SetWriteDeadline(time.Now())
|
||||||
|
})
|
||||||
|
defer stopWatch()
|
||||||
err = transport.WriteMessage(stream, 0, message)
|
err = transport.WriteMessage(stream, 0, message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stream.Close()
|
stream.Close()
|
||||||
|
|||||||
@@ -2,6 +2,23 @@
|
|||||||
icon: material/alert-decagram
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
|
#### 1.13.16
|
||||||
|
|
||||||
|
* Remove client metadata from AnyTLS requests by default **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
We found that the AnyTLS client implementation uploads metadata that is
|
||||||
|
**not used by the open-source server**, and there are reports of vendors using
|
||||||
|
it to profile and discriminate against users. We now leave it empty by default
|
||||||
|
and allow you to customize it, see
|
||||||
|
[AnyTLS client metadata](/manual/misc/anytls-client-metadata/).
|
||||||
|
|
||||||
|
#### 1.13.15
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
#### 1.13.14
|
#### 1.13.14
|
||||||
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ icon: material/new-box
|
|||||||
"idle_session_check_interval": "30s",
|
"idle_session_check_interval": "30s",
|
||||||
"idle_session_timeout": "30s",
|
"idle_session_timeout": "30s",
|
||||||
"min_idle_session": 5,
|
"min_idle_session": 5,
|
||||||
|
"client_metadata": "",
|
||||||
"tls": {},
|
"tls": {},
|
||||||
|
|
||||||
... // Dial Fields
|
... // Dial Fields
|
||||||
@@ -55,6 +56,12 @@ In the check, close sessions that have been idle for longer than this. Default:
|
|||||||
|
|
||||||
In the check, at least the first `n` idle sessions are kept open. Default value: `n`=0
|
In the check, at least the first `n` idle sessions are kept open. Default value: `n`=0
|
||||||
|
|
||||||
|
#### client_metadata
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.13.16"
|
||||||
|
|
||||||
|
Check [AnyTLS client metadata](/manual/misc/anytls-client-metadata/).
|
||||||
|
|
||||||
#### tls
|
#### tls
|
||||||
|
|
||||||
==Required==
|
==Required==
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ icon: material/new-box
|
|||||||
"idle_session_check_interval": "30s",
|
"idle_session_check_interval": "30s",
|
||||||
"idle_session_timeout": "30s",
|
"idle_session_timeout": "30s",
|
||||||
"min_idle_session": 5,
|
"min_idle_session": 5,
|
||||||
|
"client_metadata": "",
|
||||||
"tls": {},
|
"tls": {},
|
||||||
|
|
||||||
... // 拨号字段
|
... // 拨号字段
|
||||||
@@ -55,6 +56,12 @@ AnyTLS 密码。
|
|||||||
|
|
||||||
在检查中,至少前 `n` 个空闲会话保持打开状态。默认值:`n`=0
|
在检查中,至少前 `n` 个空闲会话保持打开状态。默认值:`n`=0
|
||||||
|
|
||||||
|
#### client_metadata
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.13.16 起"
|
||||||
|
|
||||||
|
参阅 [AnyTLS 客户端元数据](/zh/manual/misc/anytls-client-metadata/)。
|
||||||
|
|
||||||
#### tls
|
#### tls
|
||||||
|
|
||||||
==必填==
|
==必填==
|
||||||
|
|||||||
67
docs/manual/misc/anytls-client-metadata.md
Normal file
67
docs/manual/misc/anytls-client-metadata.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
icon: material/incognito
|
||||||
|
---
|
||||||
|
|
||||||
|
# AnyTLS client metadata
|
||||||
|
|
||||||
|
The AnyTLS protocol has a design flaw: its settings frame requires the client
|
||||||
|
to send its software name and version to the server, and the protocol
|
||||||
|
specification requires that clients not disguise this information.
|
||||||
|
|
||||||
|
This field serves no protocol purpose — AnyTLS already has a separate version
|
||||||
|
field for compatibility negotiation, and the open-source server implementation
|
||||||
|
does not use client metadata. However, the field allows vendors to collect and track client types, and for
|
||||||
|
platform-specific clients, potentially infer private information such as the operating system type and version range —
|
||||||
|
something that should not, and is not expected by users to, appear in an
|
||||||
|
anti-censorship protocol. We have received reports that
|
||||||
|
commercial proxy providers use this information to identify and block
|
||||||
|
connections from the official library provided by AnyTLS for sing-box
|
||||||
|
integration, reportedly because abusive users connect to their servers with
|
||||||
|
sing-box or with clients using the same official library. This indicates that
|
||||||
|
client metadata is being collected and used for discrimination in practice.
|
||||||
|
|
||||||
|
The protocol specification states that "disguising it has no value." We
|
||||||
|
disagree: the situation is analogous to browsers implementing TLS ECH GREASE —
|
||||||
|
without it, privacy-protecting clients can be fingerprinted and treated
|
||||||
|
differently.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
### 2025-02-20
|
||||||
|
|
||||||
|
We merged the
|
||||||
|
[pull request adding this protocol](https://github.com/SagerNet/sing-box/pull/2615).
|
||||||
|
Since the metadata was fixed at `sing-anytls/<library version>` in the
|
||||||
|
implementation provided for our use, and we did not carefully review the
|
||||||
|
protocol specification and other implementations, we wrongly believed that it
|
||||||
|
was not private information.
|
||||||
|
|
||||||
|
### 2025-04-05
|
||||||
|
|
||||||
|
The protocol document
|
||||||
|
[added](https://github.com/anytls/anytls-go/commit/8812aae7ab29dd88bb89067b9ca676e2e7e29171)
|
||||||
|
the requirement that third-party implementations fill in the real software
|
||||||
|
name and version, claiming that "disguising it has no value".
|
||||||
|
|
||||||
|
### 2026-07-18
|
||||||
|
|
||||||
|
A [pull request submitted to sing-box](https://github.com/SagerNet/sing-box/pull/4311)
|
||||||
|
was found to additionally upload the `sing-box` name and the actual version;
|
||||||
|
the change was subsequently reverted and was never released.
|
||||||
|
|
||||||
|
### 2026-08-03
|
||||||
|
|
||||||
|
sing-box 1.13.16 and 1.14.0-beta.5 have been released; the client metadata in
|
||||||
|
AnyTLS requests is now empty by default. For compatibility, the
|
||||||
|
[client_metadata](/configuration/outbound/anytls/#client_metadata) outbound
|
||||||
|
option allows users to set a custom value.
|
||||||
|
|
||||||
|
Since the open-source server implementation does not use this information and
|
||||||
|
it has no legitimate use, this is not considered a breaking change.
|
||||||
|
|
||||||
|
## Recommendations
|
||||||
|
|
||||||
|
We recommend that the AnyTLS protocol remove the client metadata, or replace
|
||||||
|
it with an option that is not sent by default and can be customized by the
|
||||||
|
user; and that other client implementations also take action, to jointly stop
|
||||||
|
statistics collection and discrimination based on client metadata.
|
||||||
35
docs/manual/misc/anytls-client-metadata.zh.md
Normal file
35
docs/manual/misc/anytls-client-metadata.zh.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
icon: material/incognito
|
||||||
|
---
|
||||||
|
|
||||||
|
# AnyTLS 客户端元数据
|
||||||
|
|
||||||
|
AnyTLS 协议具有设计缺陷:其 settings 帧要求客户端向服务器发送软件名称和版本,且协议规范要求客户端不得伪装此信息。
|
||||||
|
|
||||||
|
此字段不承担协议功能——AnyTLS 已有独立的版本字段用于兼容性协商,且开源服务端实现不使用客户端元数据。然而,此字段使得供应商可以收集并统计客户端类型,对于某些平台特定的客户端,还可能推断出操作系统类型与版本范围等隐私信息,而这不应该,也不是被用户预期的,在一个反审查协议中出现。我们收到报告,有商业代理提供商利用此信息识别和阻止来自 sing-box 使用的、由 AnyTLS 提供的用于 sing-box 集成的官方代码库的连接,据传原因是恶意用户使用 sing-box 或使用相同官方代码库的客户端连接到服务器,这表明客户端元数据在实践中已被用于收集和区别对待。
|
||||||
|
|
||||||
|
协议规范称「伪装它没有任何意义」。我们不同意:这类似于浏览器实现 TLS ECH GREASE——如果没有这一机制,保护隐私的客户端会被识别并受到差别对待。
|
||||||
|
|
||||||
|
## 状态
|
||||||
|
|
||||||
|
### 2025-02-20
|
||||||
|
|
||||||
|
我们合并了[添加此协议的 PR](https://github.com/SagerNet/sing-box/pull/2615)。由于在供我们使用的实现中,metadata 被固定在 `sing-anytls/<library version>`,且我们没有仔细审查协议规范和其他实现,我们错误地认为这不是隐私信息。
|
||||||
|
|
||||||
|
### 2025-04-05
|
||||||
|
|
||||||
|
协议文档[加入](https://github.com/anytls/anytls-go/commit/8812aae7ab29dd88bb89067b9ca676e2e7e29171)了要求第三方实现填写真实软件名称与版本号的条款,并声称「伪装它没有任何意义」。
|
||||||
|
|
||||||
|
### 2026-07-18
|
||||||
|
|
||||||
|
[向 sing-box 提出的 PR](https://github.com/SagerNet/sing-box/pull/4311) 被发现额外上传了 `sing-box` 和实际版本的字符串,随后此更改被回退,没有发布。
|
||||||
|
|
||||||
|
### 2026-08-03
|
||||||
|
|
||||||
|
发布了 sing-box 1.13.16 和 1.14.0-beta.5,现在 AnyTLS 请求中的客户端元数据默认为空。出于兼容性考虑,[client_metadata](/zh/configuration/outbound/anytls/#client_metadata) 出站选项允许用户自定义此值。
|
||||||
|
|
||||||
|
由于开源服务端实现不使用此信息,且它没有合理用途,这不被视为破坏性更改。
|
||||||
|
|
||||||
|
## 建议
|
||||||
|
|
||||||
|
我们建议 AnyTLS 协议移除客户端元数据,或将其替换为非默认提供、且用户可以自定义的选项;并建议其他客户端实现也采取行动,共同阻止基于客户端元数据的统计和区别对待。
|
||||||
@@ -3,11 +3,13 @@ package deprecated
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stderrManager struct {
|
type stderrManager struct {
|
||||||
|
access sync.Mutex
|
||||||
logger logger.Logger
|
logger logger.Logger
|
||||||
reported map[string]bool
|
reported map[string]bool
|
||||||
}
|
}
|
||||||
@@ -20,6 +22,8 @@ func NewStderrManager(logger logger.Logger) Manager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *stderrManager) ReportDeprecated(feature Note) {
|
func (f *stderrManager) ReportDeprecated(feature Note) {
|
||||||
|
f.access.Lock()
|
||||||
|
defer f.access.Unlock()
|
||||||
if f.reported[feature.Name] {
|
if f.reported[feature.Name] {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
"github.com/sagernet/sing/common/task"
|
|
||||||
|
|
||||||
mDNS "github.com/miekg/dns"
|
mDNS "github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
@@ -58,24 +57,23 @@ func (p *platformTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*m
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var responseMessage *mDNS.Msg
|
done := make(chan error, 1)
|
||||||
var group task.Group
|
go func() {
|
||||||
group.Append0(func(ctx context.Context) error {
|
exchangeErr := p.iif.Exchange(response, messageBytes)
|
||||||
err = p.iif.Exchange(response, messageBytes)
|
if exchangeErr == nil {
|
||||||
|
exchangeErr = response.error
|
||||||
|
}
|
||||||
|
done <- exchangeErr
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case err = <-done:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
if response.error != nil {
|
return &response.message, nil
|
||||||
return response.error
|
case <-ctx.Done():
|
||||||
}
|
return nil, ctx.Err()
|
||||||
responseMessage = &response.message
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
err = group.Run(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
return responseMessage, nil
|
|
||||||
} else {
|
} else {
|
||||||
question := message.Question[0]
|
question := message.Question[0]
|
||||||
var network string
|
var network string
|
||||||
@@ -87,24 +85,23 @@ func (p *platformTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*m
|
|||||||
default:
|
default:
|
||||||
return nil, E.New("only IP queries are supported by current version of Android")
|
return nil, E.New("only IP queries are supported by current version of Android")
|
||||||
}
|
}
|
||||||
var responseAddrs []netip.Addr
|
done := make(chan error, 1)
|
||||||
var group task.Group
|
go func() {
|
||||||
group.Append0(func(ctx context.Context) error {
|
lookupErr := p.iif.Lookup(response, network, question.Name)
|
||||||
err := p.iif.Lookup(response, network, question.Name)
|
if lookupErr == nil {
|
||||||
|
lookupErr = response.error
|
||||||
|
}
|
||||||
|
done <- lookupErr
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case err := <-done:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
if response.error != nil {
|
return dns.FixedResponse(message.Id, question, response.addresses, C.DefaultDNSTTL), nil
|
||||||
return response.error
|
case <-ctx.Done():
|
||||||
}
|
return nil, ctx.Err()
|
||||||
responseAddrs = response.addresses
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
err := group.Run(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
return dns.FixedResponse(message.Id, question, responseAddrs, C.DefaultDNSTTL), nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
78
go.mod
78
go.mod
@@ -7,10 +7,11 @@ require (
|
|||||||
github.com/Diniboy1123/connect-ip-go v0.0.0-20260409225322-8d7bb0a858a2
|
github.com/Diniboy1123/connect-ip-go v0.0.0-20260409225322-8d7bb0a858a2
|
||||||
github.com/anthropics/anthropic-sdk-go v1.26.0
|
github.com/anthropics/anthropic-sdk-go v1.26.0
|
||||||
github.com/anytls/sing-anytls v0.0.11
|
github.com/anytls/sing-anytls v0.0.11
|
||||||
github.com/caddyserver/certmagic v0.25.2
|
github.com/caddyserver/certmagic v0.25.3-0.20260421143802-60d9d8b415d6
|
||||||
github.com/coder/websocket v1.8.14
|
github.com/coder/websocket v1.8.14
|
||||||
github.com/cretz/bine v0.2.0
|
github.com/cretz/bine v0.2.0
|
||||||
github.com/database64128/tfo-go/v2 v2.3.2
|
github.com/database64128/tfo-go/v2 v2.3.2
|
||||||
|
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa
|
||||||
github.com/enfein/mieru/v3 v3.33.0
|
github.com/enfein/mieru/v3 v3.33.0
|
||||||
github.com/go-chi/chi/v5 v5.2.5
|
github.com/go-chi/chi/v5 v5.2.5
|
||||||
github.com/go-chi/render v1.0.3
|
github.com/go-chi/render v1.0.3
|
||||||
@@ -36,22 +37,22 @@ require (
|
|||||||
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1
|
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1
|
||||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||||
github.com/sagernet/cors v1.2.1
|
github.com/sagernet/cors v1.2.1
|
||||||
github.com/sagernet/cronet-go v0.0.0-20260620140045-05ab0dc17597
|
github.com/sagernet/cronet-go v0.0.0-20260712143338-d22f2ea3630e
|
||||||
github.com/sagernet/cronet-go/all v0.0.0-20260620140045-05ab0dc17597
|
github.com/sagernet/cronet-go/all v0.0.0-20260712143338-d22f2ea3630e
|
||||||
github.com/sagernet/fswatch v0.1.2
|
github.com/sagernet/fswatch v0.1.2
|
||||||
github.com/sagernet/gomobile v0.1.12
|
github.com/sagernet/gomobile v0.1.12
|
||||||
github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1
|
github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1
|
||||||
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4
|
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4
|
||||||
github.com/sagernet/sing v0.8.11
|
github.com/sagernet/sing v0.8.12-0.20260726145744-ef2df370afca
|
||||||
github.com/sagernet/sing-mux v0.3.5
|
github.com/sagernet/sing-mux v0.3.5
|
||||||
github.com/sagernet/sing-quic v0.6.1
|
github.com/sagernet/sing-quic v0.6.4-0.20260803041914-d83826c306d7
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.8
|
github.com/sagernet/sing-shadowsocks v0.2.8
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.1
|
github.com/sagernet/sing-shadowsocks2 v0.2.1
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
|
github.com/sagernet/sing-shadowtls v0.2.1
|
||||||
github.com/sagernet/sing-tun v0.8.11
|
github.com/sagernet/sing-tun v0.8.12-0.20260727151122-3a09076491df
|
||||||
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1
|
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1
|
||||||
github.com/sagernet/smux v1.5.50-sing-box-mod.1
|
github.com/sagernet/smux v1.5.50-sing-box-mod.1
|
||||||
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.7
|
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.8
|
||||||
github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20260224074747-506b7631853c
|
github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20260224074747-506b7631853c
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
||||||
github.com/shtorm-7/go-cache/v2 v2.1.0-extended-1.0.2
|
github.com/shtorm-7/go-cache/v2 v2.1.0-extended-1.0.2
|
||||||
@@ -119,7 +120,6 @@ require (
|
|||||||
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect
|
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect
|
||||||
github.com/database64128/netx-go v0.1.1 // indirect
|
github.com/database64128/netx-go v0.1.1 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
|
|
||||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
|
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
|
||||||
github.com/dolonet/mtg-multi v1.8.0
|
github.com/dolonet/mtg-multi v1.8.0
|
||||||
github.com/ebitengine/purego v0.10.1 // indirect
|
github.com/ebitengine/purego v0.10.1 // indirect
|
||||||
@@ -162,35 +162,35 @@ require (
|
|||||||
github.com/prometheus-community/pro-bing v0.4.0 // indirect
|
github.com/prometheus-community/pro-bing v0.4.0 // indirect
|
||||||
github.com/quic-go/qpack v0.6.0 // indirect
|
github.com/quic-go/qpack v0.6.0 // indirect
|
||||||
github.com/safchain/ethtool v0.3.0 // indirect
|
github.com/safchain/ethtool v0.3.0 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260620135226-def9ff0fb992 // indirect
|
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260712142643-1e5048bd5587 // indirect
|
||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
||||||
github.com/sagernet/nftables v0.3.0-mod.2 // indirect
|
github.com/sagernet/nftables v0.3.0-mod.2 // indirect
|
||||||
github.com/spf13/pflag v1.0.10 // indirect
|
github.com/spf13/pflag v1.0.10 // indirect
|
||||||
@@ -239,4 +239,4 @@ replace github.com/Diniboy1123/connect-ip-go => github.com/shtorm-7/connect-ip-g
|
|||||||
|
|
||||||
replace github.com/shtorm-7/go-cache/v2 => github.com/shtorm-7/go-cache/v2 v2.1.0-extended-1.2.0
|
replace github.com/shtorm-7/go-cache/v2 => github.com/shtorm-7/go-cache/v2 v2.1.0-extended-1.2.0
|
||||||
|
|
||||||
replace github.com/sagernet/sing => github.com/shtorm-7/sing v0.8.10-extended-1.2.0
|
replace github.com/sagernet/sing => github.com/shtorm-7/sing v0.8.12-extended-1.2.0
|
||||||
|
|||||||
144
go.sum
144
go.sum
@@ -34,8 +34,8 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
|||||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||||
github.com/caddyserver/certmagic v0.25.2 h1:D7xcS7ggX/WEY54x0czj7ioTkmDWKIgxtIi2OcQclUc=
|
github.com/caddyserver/certmagic v0.25.3-0.20260421143802-60d9d8b415d6 h1:LYSB6VgWzKtNrcxElw3c97BP40Oc7bizKxA9K1Vi/5k=
|
||||||
github.com/caddyserver/certmagic v0.25.2/go.mod h1:llW/CvsNmza8S6hmsuggsZeiX+uS27dkqY27wDIuBWg=
|
github.com/caddyserver/certmagic v0.25.3-0.20260421143802-60d9d8b415d6/go.mod h1:llW/CvsNmza8S6hmsuggsZeiX+uS27dkqY27wDIuBWg=
|
||||||
github.com/caddyserver/zerossl v0.1.5 h1:dkvOjBAEEtY6LIGAHei7sw2UgqSD6TrWweXpV7lvEvE=
|
github.com/caddyserver/zerossl v0.1.5 h1:dkvOjBAEEtY6LIGAHei7sw2UgqSD6TrWweXpV7lvEvE=
|
||||||
github.com/caddyserver/zerossl v0.1.5/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
github.com/caddyserver/zerossl v0.1.5/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
@@ -287,68 +287,68 @@ github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkk
|
|||||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
|
||||||
github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
|
github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
|
||||||
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
|
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
|
||||||
github.com/sagernet/cronet-go v0.0.0-20260620140045-05ab0dc17597 h1:QkwE/ZFnShDuPF+ExmAyZlQaMwFNgkYZMekrabiStfg=
|
github.com/sagernet/cronet-go v0.0.0-20260712143338-d22f2ea3630e h1:Y5mhsZrYuZ9jraIqg7hg1fw4zoVreae81fWvmozbCsQ=
|
||||||
github.com/sagernet/cronet-go v0.0.0-20260620140045-05ab0dc17597/go.mod h1:T/mwtrpC4JlWfScw73CmSBvHzIvc7BatQ1MhRr+cYNw=
|
github.com/sagernet/cronet-go v0.0.0-20260712143338-d22f2ea3630e/go.mod h1:T/mwtrpC4JlWfScw73CmSBvHzIvc7BatQ1MhRr+cYNw=
|
||||||
github.com/sagernet/cronet-go/all v0.0.0-20260620140045-05ab0dc17597 h1:cLALmGKP9eOS8622gWQIiVbZlOfH29PGNsoxbEloIdk=
|
github.com/sagernet/cronet-go/all v0.0.0-20260712143338-d22f2ea3630e h1:rdNlS1dRSi7jQe/ingFf7QmV9ZCUZMbxAfHnBrdVW7g=
|
||||||
github.com/sagernet/cronet-go/all v0.0.0-20260620140045-05ab0dc17597/go.mod h1:zVHZ5tgDTwbNvUGffAgLmouYs4in0grEzhSdaggoZOw=
|
github.com/sagernet/cronet-go/all v0.0.0-20260712143338-d22f2ea3630e/go.mod h1:WNl4xfTNuR+f7SObmuBtrk0p4MhlmvuuiWYoty3U52E=
|
||||||
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260620135226-def9ff0fb992 h1:J9l8PP4vb79Wm5zKaMO6LNZ/AiP1FvyAWJBlkKHrRBU=
|
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260712142643-1e5048bd5587 h1:ENmDXbGH92/jsMwhjIxK2a0URkA8ILC3npjqmTGj0Yc=
|
||||||
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260620135226-def9ff0fb992/go.mod h1:XXDwdjX/T8xftoeJxQmbBoYXZp8MAPFR2CwbFuTpEtw=
|
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:XXDwdjX/T8xftoeJxQmbBoYXZp8MAPFR2CwbFuTpEtw=
|
||||||
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260620135226-def9ff0fb992 h1:wXDjUNeKuihv85Kg51FomkiEH7xGsDgRcfRLiyZxacQ=
|
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260712142643-1e5048bd5587 h1:5xn/EZOO5LriSEih91thvTuR+gxb59jNxNQiB/KQPEc=
|
||||||
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260620135226-def9ff0fb992/go.mod h1:iNiUGoLtnr8/JTuVNj7XJbmpOAp2C6+B81KDrPxwaZM=
|
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:iNiUGoLtnr8/JTuVNj7XJbmpOAp2C6+B81KDrPxwaZM=
|
||||||
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260620135226-def9ff0fb992 h1:IF04nGyY3Q6Nbk9XJwTX1mckwhf12iIx4RhZ4TLOZIU=
|
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260712142643-1e5048bd5587 h1:2/UN0LvWnAM0Sc9B/zE2qWylGrX2hQXhknyQyTlvz4k=
|
||||||
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260620135226-def9ff0fb992/go.mod h1:19ILNUOGIzRdOqa2mq+iY0JoHxuieB7/lnjYeaA2vEc=
|
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260712142643-1e5048bd5587/go.mod h1:19ILNUOGIzRdOqa2mq+iY0JoHxuieB7/lnjYeaA2vEc=
|
||||||
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260620135226-def9ff0fb992 h1:dlnG1E42xx8ms2fyZigYwsYJ1Gqoj2QT8WeGlpOAWK8=
|
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260712142643-1e5048bd5587 h1:SDULc9o0HkneJD38H1G+HRLby69zfdtyQk5qaKtn/tU=
|
||||||
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260620135226-def9ff0fb992/go.mod h1:JxzGyQf94Cr6sBShKqODGDyRUlESfJK/Njcz9Lz6qMQ=
|
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:JxzGyQf94Cr6sBShKqODGDyRUlESfJK/Njcz9Lz6qMQ=
|
||||||
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260620135226-def9ff0fb992 h1:aYAQQN3jZP89MmT1ZzpJz52jAsXx1WApmv5Qidc+ez4=
|
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260712142643-1e5048bd5587 h1:hfcM9YccWN4O2LENHN16Jgm4g1/1PRouV0RmvvrS1f0=
|
||||||
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260620135226-def9ff0fb992/go.mod h1:KN+9T9TBycGOLzmKU4QdcHAJEj6Nlx48ifnlTvvHMvs=
|
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:KN+9T9TBycGOLzmKU4QdcHAJEj6Nlx48ifnlTvvHMvs=
|
||||||
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260620135226-def9ff0fb992 h1:n81+aLphvjLpL2M6lI+BC8Ldw4S/FPA3CFDXTWL4g3I=
|
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260712142643-1e5048bd5587 h1:arY9CL3C7lwJfG2Cdz2ZLvHLzT1wnzCVyAomNER6CrY=
|
||||||
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260620135226-def9ff0fb992/go.mod h1:kojvtUc29KKnk8hs2QIANynVR59921SnGWA9kXohHc0=
|
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:kojvtUc29KKnk8hs2QIANynVR59921SnGWA9kXohHc0=
|
||||||
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260620135226-def9ff0fb992 h1:mOseesF+CxgPG2U1a2Yh2fUUMdOaPxSuk4eIL6g7EU8=
|
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260712142643-1e5048bd5587 h1:HrtCf6KPJsW9KJ4L4T8HMW1vx+3xefLRLObpjZEtzF4=
|
||||||
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260620135226-def9ff0fb992/go.mod h1:hkQzRE5GDbaH1/ioqYh0Taho4L6i0yLRCVEZ5xHz5M0=
|
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260712142643-1e5048bd5587/go.mod h1:hkQzRE5GDbaH1/ioqYh0Taho4L6i0yLRCVEZ5xHz5M0=
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260620135226-def9ff0fb992 h1:JK9kP72IxAoyVQTnh9gGkh8S9RjP4FBkj95WjrLPyKs=
|
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260712142643-1e5048bd5587 h1:Inzzp4hyvcC0lawhndaK8iwEN0vGLDJLCThbaKZpksg=
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260620135226-def9ff0fb992/go.mod h1:tzVJFTOm66UxLxy6K0ZN5Ic2PC79e+sKKnt+V9puEa4=
|
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:tzVJFTOm66UxLxy6K0ZN5Ic2PC79e+sKKnt+V9puEa4=
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260620135226-def9ff0fb992 h1:3EtgLRsUpmeRaOBynTARxVC8tDegykigutmpAow2ayc=
|
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260712142643-1e5048bd5587 h1:qN6vqr9nFnZqjZXhLez+7tMcbm3FMfhaJRWNMlK3SLI=
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260620135226-def9ff0fb992/go.mod h1:M/pN6m3j0HFU6/y83n0HU6GLYys3tYdr/xTE8hVEGMo=
|
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260712142643-1e5048bd5587/go.mod h1:M/pN6m3j0HFU6/y83n0HU6GLYys3tYdr/xTE8hVEGMo=
|
||||||
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260620135226-def9ff0fb992 h1:Vr3I2sC9E/1FurpZwJXAL29C7jJROyN3JfulQWjhKuA=
|
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260712142643-1e5048bd5587 h1:Zwzpw6555p3rWw844JxNZ+5iRqli6ZBOEtMH3qq1c7s=
|
||||||
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260620135226-def9ff0fb992/go.mod h1:cGh5hO6eljCo6KMQ/Cel8Xgq4+etL0awZLRBDVG1EZQ=
|
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:cGh5hO6eljCo6KMQ/Cel8Xgq4+etL0awZLRBDVG1EZQ=
|
||||||
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260620135226-def9ff0fb992 h1:flxwC8loz0C4LQ/tLK7LvNMKx4iqaXayPggzzCxCevI=
|
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260712142643-1e5048bd5587 h1:CwsGHfo1HwvEhA2Wnh3O8WxazN3d4Un44LXhiuIvA/w=
|
||||||
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260620135226-def9ff0fb992/go.mod h1:JFE0/cxaKkx0wqPMZU7MgaplQlU0zudv82dROJjClKU=
|
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260712142643-1e5048bd5587/go.mod h1:JFE0/cxaKkx0wqPMZU7MgaplQlU0zudv82dROJjClKU=
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260620135226-def9ff0fb992 h1:G4vwPmOVR/jXDngIUC9owbEtMKXLZl/BgUHhEWWl8ec=
|
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260712142643-1e5048bd5587 h1:Pn6vsVFOJkj9q/XKBOZosfuDLB6luNMLkqq6YLUPXEk=
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260620135226-def9ff0fb992/go.mod h1:vU8VftFeSt7fURCa3JXD6+k6ss1YAX+idQjPvHmJ2tI=
|
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:vU8VftFeSt7fURCa3JXD6+k6ss1YAX+idQjPvHmJ2tI=
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260620135226-def9ff0fb992 h1:zHIxR2FlJOW5GRmgwyA2Gjgx7potOCtlOmdl8k4mwJI=
|
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260712142643-1e5048bd5587 h1:vgRWcEr2jlgICj28XwDMXPQcy8mTob1ZcO+tPdiZjes=
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260620135226-def9ff0fb992/go.mod h1:vCe4OUuL+XOUge9v3MyTD45BnuAXiH+DkjN9quDXJzQ=
|
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260712142643-1e5048bd5587/go.mod h1:vCe4OUuL+XOUge9v3MyTD45BnuAXiH+DkjN9quDXJzQ=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260620135226-def9ff0fb992 h1:U1OrR5zP+lkOPqrDpZsn8sPK1XBWZ84isXaFBZLsOfs=
|
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260712142643-1e5048bd5587 h1:uiO62HvSAdRJR3d1Jc4duxWig3kkdwHBSQ3TmUFqw48=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260620135226-def9ff0fb992/go.mod h1:w9amBWrvjtohQzBGCKJ7LCh22LhTIJs4sE7cYaKQzM0=
|
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260712142643-1e5048bd5587/go.mod h1:w9amBWrvjtohQzBGCKJ7LCh22LhTIJs4sE7cYaKQzM0=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260620135226-def9ff0fb992 h1:F59ptY4AdtKEg73OWaL+lTb5yoxp5b/gTuDbvA6xMyg=
|
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260712142643-1e5048bd5587 h1:mXioRdq9h2YlIr9XGM51kXgIkToywwVfnGmJiaz/uzM=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260620135226-def9ff0fb992/go.mod h1:TqlsFtcYS/etTeck46kHBeT8Le0Igw1Q/AV88UnMS3s=
|
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:TqlsFtcYS/etTeck46kHBeT8Le0Igw1Q/AV88UnMS3s=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260620135226-def9ff0fb992 h1:Y5axK4sCWXH+2OCpKYPI8nX3OSBuRb/6yC+5xBi9/uo=
|
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260712142643-1e5048bd5587 h1:oOlfOL+sq0KNZRsk06+5KgvtKw2TEXl2EZnLakTx+WM=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260620135226-def9ff0fb992/go.mod h1:B6Qd0vys8sv9OKVRN6J9RqDzYRGE938Fb2zrYdBDyTQ=
|
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260712142643-1e5048bd5587/go.mod h1:B6Qd0vys8sv9OKVRN6J9RqDzYRGE938Fb2zrYdBDyTQ=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260620135226-def9ff0fb992 h1:QCBwCdv9y+RJj7p0b9Db2p9fFt1wtnawD0sn9oV3vRs=
|
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260712142643-1e5048bd5587 h1:UDCa0lYiSUXD4wbxA/G5pGPSXhJaesCQCh5IEiZKb3M=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260620135226-def9ff0fb992/go.mod h1:3tXMMFY7AHugOVBZ5Al7cL7JKsnFOe5bMVr0hZPk3ow=
|
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260712142643-1e5048bd5587/go.mod h1:3tXMMFY7AHugOVBZ5Al7cL7JKsnFOe5bMVr0hZPk3ow=
|
||||||
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260620135226-def9ff0fb992 h1:6gXFFaAMiGCPZdfUs64qzj9cl7EleVs9HsNfRi8jWWw=
|
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260712142643-1e5048bd5587 h1:Yg2Ut7mPs0GK4W6p9LDh8RrDOnhTe4YpvG/WeJyUqMo=
|
||||||
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260620135226-def9ff0fb992/go.mod h1:Wt5uFdU3tnmm8YzobYewwdF7Mt6SucRQg6xeTNWC3Tk=
|
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:Wt5uFdU3tnmm8YzobYewwdF7Mt6SucRQg6xeTNWC3Tk=
|
||||||
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260620135226-def9ff0fb992 h1:VGoIX2u4CWVg9kiyjQdIpFMaFUNCW3yz0pyrEKI5X0o=
|
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260712142643-1e5048bd5587 h1:NtagC/YHvucD0Azh86aVghOk4z5f7oOAAy25Uw4knXQ=
|
||||||
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260620135226-def9ff0fb992/go.mod h1:lyIF6wKBLwWa5ZXaAKbAoewewl+yCHo2iYev39Mbj4E=
|
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260712142643-1e5048bd5587/go.mod h1:lyIF6wKBLwWa5ZXaAKbAoewewl+yCHo2iYev39Mbj4E=
|
||||||
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260620135226-def9ff0fb992 h1:SjPuqtUNCzIDaEm7iY86JZ7L+ixTmai4i2DIC++eUvw=
|
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260712142643-1e5048bd5587 h1:INzfLHBKjgJxoUDBeCaiB9hYqjhn5yfudkMz2phYypA=
|
||||||
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260620135226-def9ff0fb992/go.mod h1:H46PnSTTZNcZokLLiDeMDaHiS1l14PH3tzWi0eykjD8=
|
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260712142643-1e5048bd5587/go.mod h1:H46PnSTTZNcZokLLiDeMDaHiS1l14PH3tzWi0eykjD8=
|
||||||
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260620135226-def9ff0fb992 h1:ao2FrDzTYhu2MYsMri9nzqIdnAL7ooUWQN6/FFr+Lbk=
|
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260712142643-1e5048bd5587 h1:IX5NCEV9nojHdjpOkxKVN4L5FUuA5nwawXlSTdFXjCE=
|
||||||
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260620135226-def9ff0fb992/go.mod h1:RBhSUDAKWq7fswtV4nQUQhuaTLcX3ettR7teA7/yf2w=
|
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260712142643-1e5048bd5587/go.mod h1:RBhSUDAKWq7fswtV4nQUQhuaTLcX3ettR7teA7/yf2w=
|
||||||
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260620135226-def9ff0fb992 h1:5gPMu6EUlX6gqCRTOJrJk1FMILO6ugtnopQF1c2R/lY=
|
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260712142643-1e5048bd5587 h1:pzgA94sR7kvrP+H86/to2oN3efYORQmh0Q3b6AyfRJQ=
|
||||||
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260620135226-def9ff0fb992/go.mod h1:wRzoIOGG4xbpp3Gh3triLKwMwYriScXzFtunLYhY4w0=
|
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260712142643-1e5048bd5587/go.mod h1:wRzoIOGG4xbpp3Gh3triLKwMwYriScXzFtunLYhY4w0=
|
||||||
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260620135226-def9ff0fb992 h1:UaLOQKbjnLrO943Sm+ff/jm+NmemRuJXiImmtqShd8s=
|
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260712142643-1e5048bd5587 h1:nkS6jhF90E24PW37Xemhs6+hUM0l2iRcQcZFhPJL8R8=
|
||||||
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260620135226-def9ff0fb992/go.mod h1:LNiZXmWil1OPwKCheqQjtakZlJuKGFz+iv2eGF76Hhs=
|
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:LNiZXmWil1OPwKCheqQjtakZlJuKGFz+iv2eGF76Hhs=
|
||||||
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260620135226-def9ff0fb992 h1:wxlsDfxDowVk99Ay2hzfuIXPpXH1lGQxSL/2sX/D7jw=
|
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260712142643-1e5048bd5587 h1:EbydHlp6vWdqhb8e8zBPVJv/F3HPob+FgO6rsSdNSr0=
|
||||||
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260620135226-def9ff0fb992/go.mod h1:YFDGKTkpkJGc5+hnX/RYosZyTWg9h+68VB55fYRRLYc=
|
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260712142643-1e5048bd5587/go.mod h1:YFDGKTkpkJGc5+hnX/RYosZyTWg9h+68VB55fYRRLYc=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260620135226-def9ff0fb992 h1:pgRpWh2JPE73mtedovPPu3gmmAqHz3Rfz79QVxciu2o=
|
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260712142643-1e5048bd5587 h1:sSfVTswgqQZJqh9wTP0Acvvo5/qYARAoHegRYwZ+gyU=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260620135226-def9ff0fb992/go.mod h1:aaX0YGl8nhGmfRWI8bc3BtDjY8Vzx6O0cS/e1uqxDq4=
|
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260712142643-1e5048bd5587/go.mod h1:aaX0YGl8nhGmfRWI8bc3BtDjY8Vzx6O0cS/e1uqxDq4=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260620135226-def9ff0fb992 h1:9jtXyxukTS2ZdhMy3u1hg4StkZpgP48BOfgYhXf835w=
|
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260712142643-1e5048bd5587 h1:lHqqbALbKdJdq/1rcI4yyg3zvibHDw7wmhDYVbjy498=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260620135226-def9ff0fb992/go.mod h1:EdzMKA96xITc42QEI+ct4SwqX8Dn3ltKK8wzdkLWpSc=
|
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:EdzMKA96xITc42QEI+ct4SwqX8Dn3ltKK8wzdkLWpSc=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260620135226-def9ff0fb992 h1:gKgD1LJZbZzacaaqBQX/YKy4dyomhn8xtfmDKayVLW4=
|
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260712142643-1e5048bd5587 h1:23GyWjb58Nk9a7WXgRfFUiTrMD9kvYSRHx73AaBthYc=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260620135226-def9ff0fb992/go.mod h1:qix4kv1TTAJ5tY4lJ9vjhe9EY4mM+B7H5giOhbxDVcc=
|
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260712142643-1e5048bd5587/go.mod h1:qix4kv1TTAJ5tY4lJ9vjhe9EY4mM+B7H5giOhbxDVcc=
|
||||||
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260620135226-def9ff0fb992 h1:Lv8gtKP4QRn6Yjv3h5L215aGQBgwCyAE5YqejmN9Bqc=
|
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260712142643-1e5048bd5587 h1:64EMjgVuZMD4TX5b7oWUfWRVp8aVl3hg4aWrriQKOWo=
|
||||||
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260620135226-def9ff0fb992/go.mod h1:lm9w/oCCRyBiUa3G8lDQTT8x/ONUvgVR2iV9fVzUZB8=
|
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:lm9w/oCCRyBiUa3G8lDQTT8x/ONUvgVR2iV9fVzUZB8=
|
||||||
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260620135226-def9ff0fb992 h1:2wsRAcqJHOTlj6zGc3oQyxAZHDnGwyB/pTdAOUtLgHY=
|
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260712142643-1e5048bd5587 h1:x8cvgMQUs0EgVwt3iT/isRQ9KImvXvPobLKO5bHTN4o=
|
||||||
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260620135226-def9ff0fb992/go.mod h1:n34YyLgapgjWdKa0IoeczjAFCwD3/dxbsH5sucKw0bw=
|
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:n34YyLgapgjWdKa0IoeczjAFCwD3/dxbsH5sucKw0bw=
|
||||||
github.com/sagernet/fswatch v0.1.2 h1:/TT7k4mkce1qFPxamLO842WjqBgbTBiXP2mlUjp9PFk=
|
github.com/sagernet/fswatch v0.1.2 h1:/TT7k4mkce1qFPxamLO842WjqBgbTBiXP2mlUjp9PFk=
|
||||||
github.com/sagernet/fswatch v0.1.2/go.mod h1:5BpGmpUQVd3Mc5r313HRpvADHRg3/rKn5QbwFteB880=
|
github.com/sagernet/fswatch v0.1.2/go.mod h1:5BpGmpUQVd3Mc5r313HRpvADHRg3/rKn5QbwFteB880=
|
||||||
github.com/sagernet/gomobile v0.1.12 h1:XwzjZaclFF96deLqwAgK8gU3w0M2A8qxgDmhV+A0wjg=
|
github.com/sagernet/gomobile v0.1.12 h1:XwzjZaclFF96deLqwAgK8gU3w0M2A8qxgDmhV+A0wjg=
|
||||||
@@ -361,16 +361,16 @@ github.com/sagernet/nftables v0.3.0-mod.2 h1:ck2KMU02OxL1eDFgGaWYglMDpoOZ7OHzxje
|
|||||||
github.com/sagernet/nftables v0.3.0-mod.2/go.mod h1:8kslHG4VvYNihcco+i6uxIX7qbT8A56T0y5q7U44ZaQ=
|
github.com/sagernet/nftables v0.3.0-mod.2/go.mod h1:8kslHG4VvYNihcco+i6uxIX7qbT8A56T0y5q7U44ZaQ=
|
||||||
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4 h1:6qvrUW79S+CrPwWz6cMePXohgjHoKxLo3c+MDhNwc3o=
|
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4 h1:6qvrUW79S+CrPwWz6cMePXohgjHoKxLo3c+MDhNwc3o=
|
||||||
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4/go.mod h1:OqILvS182CyOol5zNNo6bguvOGgXzV459+chpRaUC+4=
|
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4/go.mod h1:OqILvS182CyOol5zNNo6bguvOGgXzV459+chpRaUC+4=
|
||||||
github.com/sagernet/sing-quic v0.6.1 h1:lx0tcm99wIA1RkyvILNzRSsMy1k7TTQYIhx71E/WBlw=
|
github.com/sagernet/sing-quic v0.6.4-0.20260803041914-d83826c306d7 h1:D46kmyvKMNVFvL3KXdq3T4vy8r29xCMmSqNuQHJBybE=
|
||||||
github.com/sagernet/sing-quic v0.6.1/go.mod h1:K5bWvITOm4vE10fwLfrWpw27bCoVJ+tfQ79tOWg+Ko8=
|
github.com/sagernet/sing-quic v0.6.4-0.20260803041914-d83826c306d7/go.mod h1:K5bWvITOm4vE10fwLfrWpw27bCoVJ+tfQ79tOWg+Ko8=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
|
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
|
github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
|
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
|
github.com/sagernet/sing-shadowtls v0.2.1 h1:ZiHZdnEnP+YS73NMsxiZmIFCwNd0M4k7PkGCKNXhbaM=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
|
github.com/sagernet/sing-shadowtls v0.2.1/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
|
||||||
github.com/sagernet/sing-tun v0.8.11 h1:BFu4+8LNl2JiTQtto5f+5AbkH90qgdoZEAqUbGiEXCg=
|
github.com/sagernet/sing-tun v0.8.12-0.20260727151122-3a09076491df h1:IVG68QmUeO9xlvrBMB/0u/prBot+jIiEjCsNterk7qs=
|
||||||
github.com/sagernet/sing-tun v0.8.11/go.mod h1:QvarqUtHfj1ULaRR+6kZOS/OoCE+pYGq67A5tyIy+dQ=
|
github.com/sagernet/sing-tun v0.8.12-0.20260727151122-3a09076491df/go.mod h1:QvarqUtHfj1ULaRR+6kZOS/OoCE+pYGq67A5tyIy+dQ=
|
||||||
github.com/sagernet/smux v1.5.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478=
|
github.com/sagernet/smux v1.5.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478=
|
||||||
github.com/sagernet/smux v1.5.50-sing-box-mod.1/go.mod h1:NjhsCEWedJm7eFLyhuBgIEzwfhRmytrUoiLluxs5Sk8=
|
github.com/sagernet/smux v1.5.50-sing-box-mod.1/go.mod h1:NjhsCEWedJm7eFLyhuBgIEzwfhRmytrUoiLluxs5Sk8=
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
|
||||||
@@ -383,8 +383,8 @@ github.com/shtorm-7/go-cache/v2 v2.1.0-extended-1.2.0 h1:aOd9Vy2LGSwgMM+4805AgLB
|
|||||||
github.com/shtorm-7/go-cache/v2 v2.1.0-extended-1.2.0/go.mod h1:Ek4yz5OK6stwhLKgLsRRYDI+FA+ZWvRJiWLjsi/vMM4=
|
github.com/shtorm-7/go-cache/v2 v2.1.0-extended-1.2.0/go.mod h1:Ek4yz5OK6stwhLKgLsRRYDI+FA+ZWvRJiWLjsi/vMM4=
|
||||||
github.com/shtorm-7/mtg-multi v1.11.0-extended-1.0.0 h1:iBLll4ZZG8ULQcHWs6gGslZWtBN72Zo1zjySzMVHF7g=
|
github.com/shtorm-7/mtg-multi v1.11.0-extended-1.0.0 h1:iBLll4ZZG8ULQcHWs6gGslZWtBN72Zo1zjySzMVHF7g=
|
||||||
github.com/shtorm-7/mtg-multi v1.11.0-extended-1.0.0/go.mod h1:3rvdhwdPABkwKBdvgMt3VwMn9uSq8hpoHRezZ5jRJU0=
|
github.com/shtorm-7/mtg-multi v1.11.0-extended-1.0.0/go.mod h1:3rvdhwdPABkwKBdvgMt3VwMn9uSq8hpoHRezZ5jRJU0=
|
||||||
github.com/shtorm-7/sing v0.8.10-extended-1.2.0 h1:5yw9j0+P2QkRWvxBvb71wvNdpAlHmmpBv4hj2gqvass=
|
github.com/shtorm-7/sing v0.8.12-extended-1.2.0 h1:/CYLFBi+Xrj0R6hFvYoqmDfcLe3WB6McZq5t9UvVhrc=
|
||||||
github.com/shtorm-7/sing v0.8.10-extended-1.2.0/go.mod h1:olXxWQNqRW/l2Q6JI3b2Qmz8iQnIFlOeeH8bx6JhgUA=
|
github.com/shtorm-7/sing v0.8.12-extended-1.2.0/go.mod h1:olXxWQNqRW/l2Q6JI3b2Qmz8iQnIFlOeeH8bx6JhgUA=
|
||||||
github.com/shtorm-7/sing-mux v0.3.4-extended-1.0.0 h1:a5OoXr3e2ACbM6vDIaaGL44IdHQ6wPjcSoU13vfC0Sw=
|
github.com/shtorm-7/sing-mux v0.3.4-extended-1.0.0 h1:a5OoXr3e2ACbM6vDIaaGL44IdHQ6wPjcSoU13vfC0Sw=
|
||||||
github.com/shtorm-7/sing-mux v0.3.4-extended-1.0.0/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk=
|
github.com/shtorm-7/sing-mux v0.3.4-extended-1.0.0/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk=
|
||||||
github.com/shtorm-7/sing-vmess v0.2.8-extended-1.0.0 h1:OjvqZOhYSi9eIJGYC0iPDPqvgo/asujvhAGkFzbZ5/Y=
|
github.com/shtorm-7/sing-vmess v0.2.8-extended-1.0.0 h1:OjvqZOhYSi9eIJGYC0iPDPqvgo/asujvhAGkFzbZ5/Y=
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ nav:
|
|||||||
- Hysteria 2: manual/proxy-protocol/hysteria2.md
|
- Hysteria 2: manual/proxy-protocol/hysteria2.md
|
||||||
- Misc:
|
- Misc:
|
||||||
- TunnelVision: manual/misc/tunnelvision.md
|
- TunnelVision: manual/misc/tunnelvision.md
|
||||||
|
- AnyTLS client metadata: manual/misc/anytls-client-metadata.md
|
||||||
- Configuration:
|
- Configuration:
|
||||||
- configuration/index.md
|
- configuration/index.md
|
||||||
- Log:
|
- Log:
|
||||||
|
|||||||
@@ -22,4 +22,5 @@ type AnyTLSOutboundOptions struct {
|
|||||||
IdleSessionCheckInterval badoption.Duration `json:"idle_session_check_interval,omitempty"`
|
IdleSessionCheckInterval badoption.Duration `json:"idle_session_check_interval,omitempty"`
|
||||||
IdleSessionTimeout badoption.Duration `json:"idle_session_timeout,omitempty"`
|
IdleSessionTimeout badoption.Duration `json:"idle_session_timeout,omitempty"`
|
||||||
MinIdleSession int `json:"min_idle_session,omitempty"`
|
MinIdleSession int `json:"min_idle_session,omitempty"`
|
||||||
|
ClientMetadata string `json:"client_metadata,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ type DERPMeshOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type _DERPSTUNListenOptions struct {
|
type _DERPSTUNListenOptions struct {
|
||||||
Enabled bool
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
ListenOptions
|
ListenOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
64
protocol/anytls/client_metadata.go
Normal file
64
protocol/anytls/client_metadata.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package anytls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"net"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
|
||||||
|
anytls "github.com/anytls/sing-anytls"
|
||||||
|
"github.com/anytls/sing-anytls/session"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
commandSettings = 4
|
||||||
|
frameHeaderSize = 7
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
clientSessionField, _ = reflect.TypeFor[anytls.Client]().FieldByName("sessionClient")
|
||||||
|
streamSessionField, _ = reflect.TypeFor[session.Stream]().FieldByName("sess")
|
||||||
|
sessionConnLockField, _ = reflect.TypeFor[session.Session]().FieldByName("connLock")
|
||||||
|
sessionBufferField, _ = reflect.TypeFor[session.Session]().FieldByName("buffer")
|
||||||
|
)
|
||||||
|
|
||||||
|
func sessionClientOf(client *anytls.Client) *session.Client {
|
||||||
|
return *(**session.Client)(unsafe.Add(unsafe.Pointer(client), clientSessionField.Offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Outbound) rewriteClientMetadata(conn net.Conn) {
|
||||||
|
sess := *(**session.Session)(unsafe.Add(unsafe.Pointer(conn.(*session.Stream)), streamSessionField.Offset))
|
||||||
|
connLock := (*sync.Mutex)(unsafe.Add(unsafe.Pointer(sess), sessionConnLockField.Offset))
|
||||||
|
bufferPointer := (*[]byte)(unsafe.Add(unsafe.Pointer(sess), sessionBufferField.Offset))
|
||||||
|
connLock.Lock()
|
||||||
|
defer connLock.Unlock()
|
||||||
|
buffer := *bufferPointer
|
||||||
|
offset := 0
|
||||||
|
for offset+frameHeaderSize <= len(buffer) {
|
||||||
|
dataLength := int(binary.BigEndian.Uint16(buffer[offset+5 : offset+7]))
|
||||||
|
frameEnd := offset + frameHeaderSize + dataLength
|
||||||
|
if frameEnd > len(buffer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if buffer[offset] == commandSettings {
|
||||||
|
data := []byte(strings.Join(common.Map(strings.Split(string(buffer[offset+frameHeaderSize:frameEnd]), "\n"), func(line string) string {
|
||||||
|
if strings.HasPrefix(line, "client=") {
|
||||||
|
return "client=" + h.clientMetadata
|
||||||
|
}
|
||||||
|
return line
|
||||||
|
}), "\n"))
|
||||||
|
newBuffer := make([]byte, 0, offset+frameHeaderSize+len(data)+len(buffer)-frameEnd)
|
||||||
|
newBuffer = append(newBuffer, buffer[:offset+5]...)
|
||||||
|
newBuffer = binary.BigEndian.AppendUint16(newBuffer, uint16(len(data)))
|
||||||
|
newBuffer = append(newBuffer, data...)
|
||||||
|
newBuffer = append(newBuffer, buffer[frameEnd:]...)
|
||||||
|
*bufferPointer = newBuffer
|
||||||
|
return
|
||||||
|
}
|
||||||
|
offset = frameEnd
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,20 +19,25 @@ import (
|
|||||||
"github.com/sagernet/sing/common/uot"
|
"github.com/sagernet/sing/common/uot"
|
||||||
|
|
||||||
anytls "github.com/anytls/sing-anytls"
|
anytls "github.com/anytls/sing-anytls"
|
||||||
|
"github.com/anytls/sing-anytls/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterOutbound(registry *outbound.Registry) {
|
func RegisterOutbound(registry *outbound.Registry) {
|
||||||
outbound.Register[option.AnyTLSOutboundOptions](registry, C.TypeAnyTLS, NewOutbound)
|
outbound.Register[option.AnyTLSOutboundOptions](registry, C.TypeAnyTLS, NewOutbound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ adapter.OutboundWithMultiplex = (*Outbound)(nil)
|
||||||
|
|
||||||
type Outbound struct {
|
type Outbound struct {
|
||||||
outbound.Adapter
|
outbound.Adapter
|
||||||
dialer tls.Dialer
|
dialer tls.Dialer
|
||||||
server M.Socksaddr
|
server M.Socksaddr
|
||||||
tlsConfig tls.Config
|
tlsConfig tls.Config
|
||||||
client *anytls.Client
|
clientMetadata string
|
||||||
uotClient *uot.Client
|
client *anytls.Client
|
||||||
logger log.ContextLogger
|
sessionClient *session.Client
|
||||||
|
uotClient *uot.Client
|
||||||
|
logger log.ContextLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.AnyTLSOutboundOptions) (adapter.Outbound, error) {
|
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.AnyTLSOutboundOptions) (adapter.Outbound, error) {
|
||||||
@@ -81,14 +86,30 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
outbound.client = client
|
outbound.client = client
|
||||||
|
outbound.clientMetadata = options.ClientMetadata
|
||||||
|
outbound.sessionClient = sessionClientOf(client)
|
||||||
|
|
||||||
outbound.uotClient = &uot.Client{
|
outbound.uotClient = &uot.Client{
|
||||||
Dialer: anytlsDialer(client.CreateProxy),
|
Dialer: (anytlsDialer)(outbound.createProxy),
|
||||||
Version: uot.Version,
|
Version: uot.Version,
|
||||||
}
|
}
|
||||||
return outbound, nil
|
return outbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Outbound) createProxy(ctx context.Context, destination M.Socksaddr) (net.Conn, error) {
|
||||||
|
conn, err := h.sessionClient.CreateStream(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
h.rewriteClientMetadata(conn)
|
||||||
|
err = M.SocksaddrSerializer.WriteAddrPort(conn, destination)
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
type anytlsDialer func(ctx context.Context, destination M.Socksaddr) (net.Conn, error)
|
type anytlsDialer func(ctx context.Context, destination M.Socksaddr) (net.Conn, error)
|
||||||
|
|
||||||
func (d anytlsDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (d anytlsDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
@@ -103,6 +124,10 @@ func (h *Outbound) dialOut(ctx context.Context) (net.Conn, error) {
|
|||||||
return h.dialer.DialTLSContext(ctx, h.server)
|
return h.dialer.DialTLSContext(ctx, h.server)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Outbound) MultiplexEnabled() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Outbound = h.Tag()
|
metadata.Outbound = h.Tag()
|
||||||
@@ -110,7 +135,7 @@ func (h *Outbound) DialContext(ctx context.Context, network string, destination
|
|||||||
switch N.NetworkName(network) {
|
switch N.NetworkName(network) {
|
||||||
case N.NetworkTCP:
|
case N.NetworkTCP:
|
||||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||||
return h.client.CreateProxy(ctx, destination)
|
return h.createProxy(ctx, destination)
|
||||||
case N.NetworkUDP:
|
case N.NetworkUDP:
|
||||||
h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination)
|
||||||
return h.uotClient.DialContext(ctx, network, destination)
|
return h.uotClient.DialContext(ctx, network, destination)
|
||||||
|
|||||||
@@ -108,7 +108,10 @@ func (h *Outbound) fetchMyAddresses() {
|
|||||||
func (h *Outbound) isMyLoopbackAddress(addresses ...netip.Addr) bool {
|
func (h *Outbound) isMyLoopbackAddress(addresses ...netip.Addr) bool {
|
||||||
for _, prefix := range h.myAddresses.Load() {
|
for _, prefix := range h.myAddresses.Load() {
|
||||||
for _, address := range addresses {
|
for _, address := range addresses {
|
||||||
if prefix.Addr() != address && prefix.Contains(address) {
|
if !C.IsDarwin && prefix.Addr() == address {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if prefix.Contains(address) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ func (n *Inbound) newConnection(ctx context.Context, waitForClose bool, conn net
|
|||||||
} else {
|
} else {
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
wrapper := v2rayhttp.NewHTTP2Wrapper(conn)
|
wrapper := v2rayhttp.NewHTTP2Wrapper(conn)
|
||||||
n.router.RouteConnectionEx(ctx, conn, metadata, N.OnceClose(func(it error) {
|
n.router.RouteConnectionEx(ctx, wrapper, metadata, N.OnceClose(func(it error) {
|
||||||
close(done)
|
close(done)
|
||||||
}))
|
}))
|
||||||
<-done
|
<-done
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ func RegisterOutbound(registry *outbound.Registry) {
|
|||||||
outbound.Register[option.ShadowsocksOutboundOptions](registry, C.TypeShadowsocks, NewOutbound)
|
outbound.Register[option.ShadowsocksOutboundOptions](registry, C.TypeShadowsocks, NewOutbound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ adapter.OutboundWithMultiplex = (*Outbound)(nil)
|
||||||
|
|
||||||
type Outbound struct {
|
type Outbound struct {
|
||||||
outbound.Adapter
|
outbound.Adapter
|
||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
@@ -124,6 +126,10 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Outbound) MultiplexEnabled() bool {
|
||||||
|
return h.multiplexDialer != nil
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Outbound) InterfaceUpdated() {
|
func (h *Outbound) InterfaceUpdated() {
|
||||||
if h.multiplexDialer != nil {
|
if h.multiplexDialer != nil {
|
||||||
h.multiplexDialer.Reset()
|
h.multiplexDialer.Reset()
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
for _, hostKey := range options.HostKey {
|
for _, hostKey := range options.HostKey {
|
||||||
key, _, _, _, err := ssh.ParseAuthorizedKey([]byte(hostKey))
|
key, _, _, _, err := ssh.ParseAuthorizedKey([]byte(hostKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.New("parse host key ", key)
|
return nil, E.Cause(err, "parse host key: ", hostKey)
|
||||||
}
|
}
|
||||||
outbound.hostKey = append(outbound.hostKey, key)
|
outbound.hostKey = append(outbound.hostKey, key)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -161,8 +161,9 @@ func (t *DNSTransport) updateDNSServers(routeConfig *router.Config, dnsConfig *n
|
|||||||
|
|
||||||
func (t *DNSTransport) createResolver(directDialer func() N.Dialer, resolver *dnstype.Resolver) (adapter.DNSTransport, error) {
|
func (t *DNSTransport) createResolver(directDialer func() N.Dialer, resolver *dnstype.Resolver) (adapter.DNSTransport, error) {
|
||||||
serverURL, parseURLErr := url.Parse(resolver.Addr)
|
serverURL, parseURLErr := url.Parse(resolver.Addr)
|
||||||
|
isHTTPScheme := parseURLErr == nil && (serverURL.Scheme == "http" || serverURL.Scheme == "https")
|
||||||
var myDialer N.Dialer
|
var myDialer N.Dialer
|
||||||
if parseURLErr == nil && serverURL.Scheme == "http" {
|
if isHTTPScheme && serverURL.Scheme == "http" {
|
||||||
myDialer = t.endpoint
|
myDialer = t.endpoint
|
||||||
} else {
|
} else {
|
||||||
myDialer = directDialer()
|
myDialer = directDialer()
|
||||||
@@ -170,36 +171,39 @@ func (t *DNSTransport) createResolver(directDialer func() N.Dialer, resolver *dn
|
|||||||
if len(resolver.BootstrapResolution) > 0 {
|
if len(resolver.BootstrapResolution) > 0 {
|
||||||
bootstrapTransport := transport.NewUDPRaw(t.logger, t.TransportAdapter, myDialer, M.SocksaddrFrom(resolver.BootstrapResolution[0], 53))
|
bootstrapTransport := transport.NewUDPRaw(t.logger, t.TransportAdapter, myDialer, M.SocksaddrFrom(resolver.BootstrapResolution[0], 53))
|
||||||
myDialer = dialer.NewResolveDialer(t.ctx, myDialer, false, "", adapter.DNSQueryOptions{Transport: bootstrapTransport}, 0)
|
myDialer = dialer.NewResolveDialer(t.ctx, myDialer, false, "", adapter.DNSQueryOptions{Transport: bootstrapTransport}, 0)
|
||||||
}
|
|
||||||
if serverAddr := M.ParseSocksaddr(resolver.Addr); serverAddr.IsValid() {
|
|
||||||
if serverAddr.Port == 0 {
|
|
||||||
serverAddr.Port = 53
|
|
||||||
}
|
|
||||||
return transport.NewUDPRaw(t.logger, t.TransportAdapter, myDialer, serverAddr), nil
|
|
||||||
} else if parseURLErr != nil {
|
|
||||||
return nil, E.Cause(parseURLErr, "parse resolver address")
|
|
||||||
} else {
|
} else {
|
||||||
|
myDialer = dialer.NewResolveDialer(t.ctx, myDialer, false, "", t.endpoint.queryOptions, 0)
|
||||||
|
}
|
||||||
|
if isHTTPScheme {
|
||||||
|
serverAddr := M.ParseSocksaddrHostPortStr(serverURL.Hostname(), serverURL.Port())
|
||||||
switch serverURL.Scheme {
|
switch serverURL.Scheme {
|
||||||
case "https":
|
case "https":
|
||||||
serverAddr = M.ParseSocksaddrHostPortStr(serverURL.Hostname(), serverURL.Port())
|
|
||||||
if serverAddr.Port == 0 {
|
if serverAddr.Port == 0 {
|
||||||
serverAddr.Port = 443
|
serverAddr.Port = 443
|
||||||
}
|
}
|
||||||
tlsConfig := common.Must1(tls.NewClient(t.ctx, t.logger, serverAddr.AddrString(), option.OutboundTLSOptions{
|
tlsConfig := common.Must1(tls.NewClient(t.ctx, t.logger, serverAddr.AddrString(), option.OutboundTLSOptions{
|
||||||
ALPN: []string{http2.NextProtoTLS, "http/1.1"},
|
Enabled: true,
|
||||||
|
ALPN: []string{http2.NextProtoTLS, "http/1.1"},
|
||||||
}))
|
}))
|
||||||
return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, tlsConfig), nil
|
return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, tlsConfig), nil
|
||||||
case "http":
|
case "http":
|
||||||
serverAddr = M.ParseSocksaddrHostPortStr(serverURL.Hostname(), serverURL.Port())
|
|
||||||
if serverAddr.Port == 0 {
|
if serverAddr.Port == 0 {
|
||||||
serverAddr.Port = 80
|
serverAddr.Port = 80
|
||||||
}
|
}
|
||||||
return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, nil), nil
|
return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, nil), nil
|
||||||
// case "tls":
|
|
||||||
default:
|
|
||||||
return nil, E.New("unknown resolver scheme: ", serverURL.Scheme)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
serverAddr := M.ParseSocksaddr(resolver.Addr)
|
||||||
|
if !serverAddr.IsValid() {
|
||||||
|
if parseURLErr != nil {
|
||||||
|
return nil, E.Cause(parseURLErr, "parse resolver address")
|
||||||
|
}
|
||||||
|
return nil, E.New("invalid resolver address: ", resolver.Addr)
|
||||||
|
}
|
||||||
|
if serverAddr.Port == 0 {
|
||||||
|
serverAddr.Port = 53
|
||||||
|
}
|
||||||
|
return transport.NewUDPRaw(t.logger, t.TransportAdapter, myDialer, serverAddr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildRoutePrefixes(routeConfig *router.Config) []netip.Prefix {
|
func buildRoutePrefixes(routeConfig *router.Config) []netip.Prefix {
|
||||||
@@ -279,7 +283,7 @@ func (t *DNSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for domainSuffix, transports := range routes {
|
for domainSuffix, transports := range routes {
|
||||||
if strings.HasSuffix(question.Name, domainSuffix) {
|
if mDNS.IsSubDomain(domainSuffix, question.Name) {
|
||||||
if len(transports) == 0 {
|
if len(transports) == 0 {
|
||||||
return &mDNS.Msg{
|
return &mDNS.Msg{
|
||||||
MsgHdr: mDNS.MsgHdr{
|
MsgHdr: mDNS.MsgHdr{
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -71,7 +70,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
version.SetVersion(tailscaleroot.VersionDotTxt + " (sing-box " + C.Version + ")")
|
version.SetVersion(strings.TrimSpace(tailscaleroot.VersionDotTxt) + "-0-(sing-box " + C.Version + ")")
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterEndpoint(registry *endpoint.Registry) {
|
func RegisterEndpoint(registry *endpoint.Registry) {
|
||||||
@@ -83,6 +82,7 @@ type Endpoint struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
router adapter.Router
|
router adapter.Router
|
||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
|
queryOptions adapter.DNSQueryOptions
|
||||||
dnsRouter adapter.DNSRouter
|
dnsRouter adapter.DNSRouter
|
||||||
network adapter.NetworkManager
|
network adapter.NetworkManager
|
||||||
platformInterface adapter.PlatformInterface
|
platformInterface adapter.PlatformInterface
|
||||||
@@ -93,6 +93,7 @@ type Endpoint struct {
|
|||||||
onReconfigHook wgengine.ReconfigListener
|
onReconfigHook wgengine.ReconfigListener
|
||||||
|
|
||||||
cfg *wgcfg.Config
|
cfg *wgcfg.Config
|
||||||
|
routerCfg *router.Config
|
||||||
dnsCfg *tsDNS.Config
|
dnsCfg *tsDNS.Config
|
||||||
routeDomains common.TypedValue[map[string]bool]
|
routeDomains common.TypedValue[map[string]bool]
|
||||||
routePrefixes atomic.Pointer[netipx.IPSet]
|
routePrefixes atomic.Pointer[netipx.IPSet]
|
||||||
@@ -188,27 +189,17 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
} else {
|
} else {
|
||||||
udpTimeout = C.UDPTimeout
|
udpTimeout = C.UDPTimeout
|
||||||
}
|
}
|
||||||
var remoteIsDomain bool
|
|
||||||
if options.ControlURL != "" {
|
|
||||||
controlURL, err := url.Parse(options.ControlURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "parse control URL")
|
|
||||||
}
|
|
||||||
remoteIsDomain = M.ParseSocksaddr(controlURL.Hostname()).IsDomain()
|
|
||||||
} else {
|
|
||||||
// controlplane.tailscale.com
|
|
||||||
remoteIsDomain = true
|
|
||||||
}
|
|
||||||
outboundDialer, err := dialer.NewWithOptions(dialer.Options{
|
outboundDialer, err := dialer.NewWithOptions(dialer.Options{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
Options: options.DialerOptions,
|
Options: options.DialerOptions,
|
||||||
RemoteIsDomain: remoteIsDomain,
|
RemoteIsDomain: true,
|
||||||
ResolverOnDetour: true,
|
ResolverOnDetour: true,
|
||||||
NewDialer: true,
|
NewDialer: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
dialerQueryOptions := outboundDialer.(dialer.ResolveDialer).QueryOptions()
|
||||||
dnsRouter := service.FromContext[adapter.DNSRouter](ctx)
|
dnsRouter := service.FromContext[adapter.DNSRouter](ctx)
|
||||||
server := &tsnet.Server{
|
server := &tsnet.Server{
|
||||||
Dir: stateDirectory,
|
Dir: stateDirectory,
|
||||||
@@ -225,7 +216,7 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
AdvertiseTags: options.AdvertiseTags,
|
AdvertiseTags: options.AdvertiseTags,
|
||||||
Dialer: &endpointDialer{Dialer: outboundDialer, logger: logger},
|
Dialer: &endpointDialer{Dialer: outboundDialer, logger: logger},
|
||||||
LookupHook: func(ctx context.Context, host string) ([]netip.Addr, error) {
|
LookupHook: func(ctx context.Context, host string) ([]netip.Addr, error) {
|
||||||
return dnsRouter.Lookup(ctx, host, outboundDialer.(dialer.ResolveDialer).QueryOptions())
|
return dnsRouter.Lookup(ctx, host, dialerQueryOptions)
|
||||||
},
|
},
|
||||||
DNS: &dnsConfigurtor{},
|
DNS: &dnsConfigurtor{},
|
||||||
HTTPClient: &http.Client{
|
HTTPClient: &http.Client{
|
||||||
@@ -242,10 +233,11 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
return &Endpoint{
|
return &Endpoint{
|
||||||
Adapter: endpoint.NewAdapter(C.TypeTailscale, tag, []string{N.NetworkTCP, N.NetworkUDP, N.NetworkICMP}, nil),
|
Adapter: endpoint.NewAdapterWithDialerOptions(C.TypeTailscale, tag, []string{N.NetworkTCP, N.NetworkUDP, N.NetworkICMP}, options.DialerOptions),
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
router: router,
|
router: router,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
queryOptions: dialerQueryOptions,
|
||||||
dnsRouter: dnsRouter,
|
dnsRouter: dnsRouter,
|
||||||
network: service.FromContext[adapter.NetworkManager](ctx),
|
network: service.FromContext[adapter.NetworkManager](ctx),
|
||||||
platformInterface: service.FromContext[adapter.PlatformInterface](ctx),
|
platformInterface: service.FromContext[adapter.PlatformInterface](ctx),
|
||||||
@@ -704,12 +696,17 @@ func (t *Endpoint) NewConnectionEx(ctx context.Context, conn net.Conn, source M.
|
|||||||
metadata.Inbound = t.Tag()
|
metadata.Inbound = t.Tag()
|
||||||
metadata.InboundType = t.Type()
|
metadata.InboundType = t.Type()
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
addr4, addr6 := t.server.TailscaleIPs()
|
destinationAddress := tsaddr.UnmapVia(destination.Addr)
|
||||||
switch destination.Addr {
|
if destinationAddress != destination.Addr {
|
||||||
case addr4:
|
destination.Addr = destinationAddress
|
||||||
destination.Addr = netip.AddrFrom4([4]uint8{127, 0, 0, 1})
|
} else {
|
||||||
case addr6:
|
addr4, addr6 := t.server.TailscaleIPs()
|
||||||
destination.Addr = netip.IPv6Loopback()
|
switch destination.Addr {
|
||||||
|
case addr4:
|
||||||
|
destination.Addr = netip.AddrFrom4([4]uint8{127, 0, 0, 1})
|
||||||
|
case addr6:
|
||||||
|
destination.Addr = netip.IPv6Loopback()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
t.logger.InfoContext(ctx, "inbound connection from ", source)
|
t.logger.InfoContext(ctx, "inbound connection from ", source)
|
||||||
@@ -722,16 +719,22 @@ func (t *Endpoint) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
|||||||
metadata.Inbound = t.Tag()
|
metadata.Inbound = t.Tag()
|
||||||
metadata.InboundType = t.Type()
|
metadata.InboundType = t.Type()
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
addr4, addr6 := t.server.TailscaleIPs()
|
originDestination := destination
|
||||||
switch destination.Addr {
|
destinationAddress := tsaddr.UnmapVia(destination.Addr)
|
||||||
case addr4:
|
if destinationAddress != destination.Addr {
|
||||||
metadata.OriginDestination = destination
|
destination.Addr = destinationAddress
|
||||||
destination.Addr = netip.AddrFrom4([4]uint8{127, 0, 0, 1})
|
} else {
|
||||||
conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, destination)
|
addr4, addr6 := t.server.TailscaleIPs()
|
||||||
case addr6:
|
switch destination.Addr {
|
||||||
metadata.OriginDestination = destination
|
case addr4:
|
||||||
destination.Addr = netip.IPv6Loopback()
|
destination.Addr = netip.AddrFrom4([4]uint8{127, 0, 0, 1})
|
||||||
conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, destination)
|
case addr6:
|
||||||
|
destination.Addr = netip.IPv6Loopback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if destination != originDestination {
|
||||||
|
metadata.OriginDestination = originDestination
|
||||||
|
conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), originDestination, destination)
|
||||||
}
|
}
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
t.logger.InfoContext(ctx, "inbound packet connection from ", source)
|
t.logger.InfoContext(ctx, "inbound packet connection from ", source)
|
||||||
@@ -797,7 +800,9 @@ func (t *Endpoint) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCf
|
|||||||
if cfg == nil || dnsCfg == nil {
|
if cfg == nil || dnsCfg == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (t.cfg != nil && reflect.DeepEqual(t.cfg, cfg)) && (t.dnsCfg != nil && reflect.DeepEqual(t.dnsCfg, dnsCfg)) {
|
if t.cfg != nil && reflect.DeepEqual(t.cfg, cfg) &&
|
||||||
|
t.routerCfg != nil && reflect.DeepEqual(t.routerCfg, routerCfg) &&
|
||||||
|
t.dnsCfg != nil && reflect.DeepEqual(t.dnsCfg, dnsCfg) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var inet4Address, inet6Address netip.Addr
|
var inet4Address, inet6Address netip.Addr
|
||||||
@@ -810,6 +815,7 @@ func (t *Endpoint) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCf
|
|||||||
}
|
}
|
||||||
t.icmpForwarder.SetLocalAddresses(inet4Address, inet6Address)
|
t.icmpForwarder.SetLocalAddresses(inet4Address, inet6Address)
|
||||||
t.cfg = cfg
|
t.cfg = cfg
|
||||||
|
t.routerCfg = routerCfg
|
||||||
t.dnsCfg = dnsCfg
|
t.dnsCfg = dnsCfg
|
||||||
|
|
||||||
routeDomains := make(map[string]bool)
|
routeDomains := make(map[string]bool)
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import (
|
|||||||
singTun "github.com/sagernet/sing-tun"
|
singTun "github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
wgTun "github.com/sagernet/wireguard-go/tun"
|
wgTun "github.com/sagernet/wireguard-go/tun"
|
||||||
|
|
||||||
|
"github.com/dblohm7/wingoes/com"
|
||||||
|
"golang.org/x/sys/windows/svc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tunDeviceAdapter struct {
|
type tunDeviceAdapter struct {
|
||||||
@@ -21,7 +24,23 @@ type tunDeviceAdapter struct {
|
|||||||
closeOnce sync.Once
|
closeOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTunDeviceAdapter(tun singTun.Tun, mtu int, _ logger.ContextLogger) (wgTun.Device, error) {
|
var comRuntimeOnce sync.Once
|
||||||
|
|
||||||
|
func newTunDeviceAdapter(tun singTun.Tun, mtu int, contextLogger logger.ContextLogger) (wgTun.Device, error) {
|
||||||
|
// wgengine/router/osrouter/ifconfig_windows.go setPrivateNetwork assumes COM
|
||||||
|
// is initialized process-wide, which upstream performs only in tailscaled's
|
||||||
|
// main package; library consumers must do it themselves.
|
||||||
|
comRuntimeOnce.Do(func() {
|
||||||
|
processType := com.ConsoleApp
|
||||||
|
isService, serviceErr := svc.IsWindowsService()
|
||||||
|
if serviceErr == nil && isService {
|
||||||
|
processType = com.Service
|
||||||
|
}
|
||||||
|
err := com.StartRuntime(processType)
|
||||||
|
if err != nil {
|
||||||
|
contextLogger.Warn("initialize COM runtime: ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
winTun, ok := tun.(singTun.WinTun)
|
winTun, ok := tun.(singTun.WinTun)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("not a windows tun device")
|
return nil, errors.New("not a windows tun device")
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ func RegisterOutbound(registry *outbound.Registry) {
|
|||||||
outbound.Register[option.TrojanOutboundOptions](registry, C.TypeTrojan, NewOutbound)
|
outbound.Register[option.TrojanOutboundOptions](registry, C.TypeTrojan, NewOutbound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ adapter.OutboundWithMultiplex = (*Outbound)(nil)
|
||||||
|
|
||||||
type Outbound struct {
|
type Outbound struct {
|
||||||
outbound.Adapter
|
outbound.Adapter
|
||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
@@ -107,6 +109,10 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Outbound) MultiplexEnabled() bool {
|
||||||
|
return h.multiplexDialer != nil
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Outbound) InterfaceUpdated() {
|
func (h *Outbound) InterfaceUpdated() {
|
||||||
if h.transport != nil {
|
if h.transport != nil {
|
||||||
h.transport.Close()
|
h.transport.Close()
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
IncludePackage: options.IncludePackage,
|
IncludePackage: options.IncludePackage,
|
||||||
ExcludePackage: options.ExcludePackage,
|
ExcludePackage: options.ExcludePackage,
|
||||||
InterfaceMonitor: networkManager.InterfaceMonitor(),
|
InterfaceMonitor: networkManager.InterfaceMonitor(),
|
||||||
|
Logger: logger,
|
||||||
EXP_MultiPendingPackets: multiPendingPackets,
|
EXP_MultiPendingPackets: multiPendingPackets,
|
||||||
},
|
},
|
||||||
udpTimeout: udpTimeout,
|
udpTimeout: udpTimeout,
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ func RegisterOutbound(registry *outbound.Registry) {
|
|||||||
outbound.Register[option.VLESSOutboundOptions](registry, C.TypeVLESS, NewOutbound)
|
outbound.Register[option.VLESSOutboundOptions](registry, C.TypeVLESS, NewOutbound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ adapter.OutboundWithMultiplex = (*Outbound)(nil)
|
||||||
|
|
||||||
type Outbound struct {
|
type Outbound struct {
|
||||||
outbound.Adapter
|
outbound.Adapter
|
||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
@@ -152,6 +154,10 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Outbound) MultiplexEnabled() bool {
|
||||||
|
return h.multiplexDialer != nil
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Outbound) InterfaceUpdated() {
|
func (h *Outbound) InterfaceUpdated() {
|
||||||
if h.transport != nil {
|
if h.transport != nil {
|
||||||
h.transport.Close()
|
h.transport.Close()
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ func RegisterOutbound(registry *outbound.Registry) {
|
|||||||
outbound.Register[option.VMessOutboundOptions](registry, C.TypeVMess, NewOutbound)
|
outbound.Register[option.VMessOutboundOptions](registry, C.TypeVMess, NewOutbound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ adapter.OutboundWithMultiplex = (*Outbound)(nil)
|
||||||
|
|
||||||
type Outbound struct {
|
type Outbound struct {
|
||||||
outbound.Adapter
|
outbound.Adapter
|
||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
@@ -105,6 +107,10 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
return outbound, nil
|
return outbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Outbound) MultiplexEnabled() bool {
|
||||||
|
return h.multiplexDialer != nil
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Outbound) InterfaceUpdated() {
|
func (h *Outbound) InterfaceUpdated() {
|
||||||
if h.transport != nil {
|
if h.transport != nil {
|
||||||
h.transport.Close()
|
h.transport.Close()
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ func (s *LocalRuleSet) reloadFile(path string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer setFile.Close()
|
||||||
ruleSet, err = srs.Read(setFile, false)
|
ruleSet, err = srs.Read(setFile, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -98,10 +98,11 @@ func (s *RemoteRuleSet) StartContext(ctx context.Context, startContext *adapter.
|
|||||||
if savedSet := s.cacheFile.LoadRuleSet(s.options.Tag); savedSet != nil {
|
if savedSet := s.cacheFile.LoadRuleSet(s.options.Tag); savedSet != nil {
|
||||||
err := s.loadBytes(savedSet.Content)
|
err := s.loadBytes(savedSet.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "restore cached rule-set")
|
s.logger.Warn(E.Cause(err, "restore cached rule-set, will refetch"))
|
||||||
|
} else {
|
||||||
|
s.lastUpdated = savedSet.LastUpdated
|
||||||
|
s.lastEtag = savedSet.LastEtag
|
||||||
}
|
}
|
||||||
s.lastUpdated = savedSet.LastUpdated
|
|
||||||
s.lastEtag = savedSet.LastEtag
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s.lastUpdated.IsZero() {
|
if s.lastUpdated.IsZero() {
|
||||||
|
|||||||
@@ -204,10 +204,12 @@ func (i *Service) onNetworkUpdate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (conf *TransportLink) nameList(ndots int, name string) []string {
|
func (conf *TransportLink) nameList(ndots int, name string) []string {
|
||||||
search := common.Map(common.Filter(conf.domain, func(it LinkDomain) bool {
|
search := common.Filter(common.Map(common.Filter(conf.domain, func(it LinkDomain) bool {
|
||||||
return !it.RoutingOnly
|
return !it.RoutingOnly
|
||||||
}), func(it LinkDomain) string {
|
}), func(it LinkDomain) string {
|
||||||
return it.Domain
|
return mDNS.Fqdn(it.Domain)
|
||||||
|
}), func(it string) bool {
|
||||||
|
return it != "."
|
||||||
})
|
})
|
||||||
|
|
||||||
l := len(name)
|
l := len(name)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -200,7 +199,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
|||||||
if domain.Domain == "." && domain.RoutingOnly && !t.acceptDefaultResolvers {
|
if domain.Domain == "." && domain.RoutingOnly && !t.acceptDefaultResolvers {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if strings.HasSuffix(question.Name, domain.Domain) {
|
if mDNS.IsSubDomain(domain.Domain, question.Name) {
|
||||||
selectedLink = link
|
selectedLink = link
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,22 +87,27 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
|||||||
request.Header.Set("Upgrade", "websocket")
|
request.Header.Set("Upgrade", "websocket")
|
||||||
err = request.Write(conn)
|
err = request.Write(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
bufReader := std_bufio.NewReader(conn)
|
bufReader := std_bufio.NewReader(conn)
|
||||||
response, err := http.ReadResponse(bufReader, request)
|
response, err := http.ReadResponse(bufReader, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if response.StatusCode != 101 ||
|
if response.StatusCode != 101 ||
|
||||||
!strings.EqualFold(response.Header.Get("Connection"), "upgrade") ||
|
!strings.EqualFold(response.Header.Get("Connection"), "upgrade") ||
|
||||||
!strings.EqualFold(response.Header.Get("Upgrade"), "websocket") {
|
!strings.EqualFold(response.Header.Get("Upgrade"), "websocket") {
|
||||||
|
conn.Close()
|
||||||
|
response.Body.Close()
|
||||||
return nil, E.New("v2ray-http-upgrade: unexpected status: ", response.Status)
|
return nil, E.New("v2ray-http-upgrade: unexpected status: ", response.Status)
|
||||||
}
|
}
|
||||||
if bufReader.Buffered() > 0 {
|
if bufReader.Buffered() > 0 {
|
||||||
buffer := buf.NewSize(bufReader.Buffered())
|
buffer := buf.NewSize(bufReader.Buffered())
|
||||||
_, err = buffer.ReadFullFrom(bufReader, buffer.Len())
|
_, err = buffer.ReadFullFrom(bufReader, buffer.Len())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
conn = bufio.NewCachedConn(conn, buffer)
|
conn = bufio.NewCachedConn(conn, buffer)
|
||||||
|
|||||||
@@ -78,6 +78,12 @@ func (c *Client) offerNew() (*quic.Conn, error) {
|
|||||||
packetConn.Close()
|
packetConn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// quic-go does not take ownership of the packet conn passed to Dial:
|
||||||
|
// when the connection ends it only stops reading.
|
||||||
|
go func() {
|
||||||
|
<-quicConn.Context().Done()
|
||||||
|
packetConn.Close()
|
||||||
|
}()
|
||||||
c.conn.Store(quicConn)
|
c.conn.Store(quicConn)
|
||||||
c.rawConn = udpConn
|
c.rawConn = udpConn
|
||||||
return quicConn, nil
|
return quicConn, nil
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package v2rayquic
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/quic-go"
|
"github.com/sagernet/quic-go"
|
||||||
qtls "github.com/sagernet/sing-quic"
|
qtls "github.com/sagernet/sing-quic"
|
||||||
@@ -37,5 +38,8 @@ func (s *StreamWrapper) Upstream() any {
|
|||||||
func (s *StreamWrapper) Close() error {
|
func (s *StreamWrapper) Close() error {
|
||||||
s.CancelRead(0)
|
s.CancelRead(0)
|
||||||
s.Stream.Close()
|
s.Stream.Close()
|
||||||
|
// quic-go's Stream.Close does not unblock a Write blocked on flow control,
|
||||||
|
// but a past write deadline does; buffered data and the FIN are unaffected.
|
||||||
|
s.Stream.SetWriteDeadline(time.Now())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,12 +93,14 @@ func (c *Client) dialContext(ctx context.Context, requestURL *url.URL, headers h
|
|||||||
reader, _, err := ws.Dialer{Header: ws.HandshakeHeaderHTTP(headers), Protocols: protocols}.Upgrade(deadlineConn, requestURL)
|
reader, _, err := ws.Dialer{Header: ws.HandshakeHeaderHTTP(headers), Protocols: protocols}.Upgrade(deadlineConn, requestURL)
|
||||||
deadlineConn.SetDeadline(time.Time{})
|
deadlineConn.SetDeadline(time.Time{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if reader != nil {
|
if reader != nil {
|
||||||
buffer := buf.NewSize(reader.Buffered())
|
buffer := buf.NewSize(reader.Buffered())
|
||||||
_, err = buffer.ReadFullFrom(reader, buffer.Len())
|
_, err = buffer.ReadFullFrom(reader, buffer.Len())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
conn = bufio.NewCachedConn(conn, buffer)
|
conn = bufio.NewCachedConn(conn, buffer)
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ func (c *EarlyWebsocketConn) writeRequest(content []byte) error {
|
|||||||
if len(lateData) > 0 {
|
if len(lateData) > 0 {
|
||||||
_, err = conn.Write(lateData)
|
_, err = conn.Write(lateData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user