Compare commits

..

19 Commits

Author SHA1 Message Date
世界
b5ef85367a documentation: Bump version 2024-03-19 14:28:19 +08:00
世界
f70b6b44d2 documentation: Update DNS manual 2024-03-19 14:21:47 +08:00
世界
de6f53b03f Add bypass_domain and search_domain platform HTTP proxy options 2024-03-19 14:21:46 +08:00
世界
9bb5e22be2 Update gVisor to 20240212.0-65-g71212d503 2024-03-19 14:21:46 +08:00
世界
bd34bdce15 Update quic-go to v0.42.0 2024-03-19 14:21:32 +08:00
世界
ac981efe79 Fixed order for Clash modes 2024-03-19 14:07:07 +08:00
气息
f584b45221 Fix DNS exchange index
Signed-off-by: 气息 <qdshizh@gmail.com>
2024-03-19 14:07:07 +08:00
PuerNya
0d293c0103 Always disable cache for fake-ip DNS transport if independent_cache disabled 2024-03-19 14:07:06 +08:00
世界
88cd2ad3b3 Fix missing rule_set_ipcidr_match_source item in DNS rules 2024-03-19 14:07:06 +08:00
世界
4070859ca7 Improve DNS truncate behavior 2024-03-19 14:07:06 +08:00
世界
6b04fe70a1 Fix DNS fallthrough incorrectly 2024-03-19 14:07:06 +08:00
世界
8982c0eb71 Add rejected DNS response cache support 2024-03-19 14:07:05 +08:00
世界
7d08edb915 Add support for client-subnet DNS options 2024-03-19 14:07:05 +08:00
世界
c1dc8f71a2 Add address filter support for DNS rules 2024-03-19 14:07:05 +08:00
世界
fdc3fcd0a0 Fix timezone for Android and iOS 2024-03-19 14:07:05 +08:00
世界
da0c589e8e Handle Windows power events 2024-03-19 14:07:05 +08:00
世界
8ca214d89f Improve domain suffix match behavior
For historical reasons, sing-box's `domain_suffix` rule matches literal prefixes instead of the same as other projects.

This change modifies the behavior of `domain_suffix`: If the rule value is prefixed with `.`,
the behavior is unchanged, otherwise it matches `(domain|.+\.domain)` instead.
2024-03-19 14:07:04 +08:00
世界
65cd2e2f6b Remove PROCESS_NAME_NATIVE dwFlag in process query output
The `process_path` rule of sing-box is inherited from Clash,
the original code uses the local system's path format (e.g. `\Device\HarddiskVolume1\folder\program.exe`),
but when the device has multiple disks, the HarddiskVolume serial number is not stable.

This change make QueryFullProcessImageNameW output a Win32 path (such as `C:\folder\program.exe`),
which will disrupt the existing `process_path` use cases in Windows.
2024-03-19 14:07:04 +08:00
世界
228545d9fd badtls: Support uTLS and TLS ECH for read waiter 2024-03-19 14:07:04 +08:00
118 changed files with 3496 additions and 1425 deletions

View File

@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
fetch-depth: 0
- name: Setup Go
@@ -38,7 +38,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
fetch-depth: 0
- name: Setup Go
@@ -58,7 +58,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
fetch-depth: 0
- name: Setup Go
@@ -78,7 +78,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
fetch-depth: 0
- name: Setup Go
@@ -208,7 +208,7 @@ jobs:
TAGS: with_clash_api,with_quic
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
fetch-depth: 0
- name: Setup Go

View File

@@ -4,35 +4,13 @@ on:
release:
types:
- released
workflow_dispatch:
inputs:
tag:
description: "The tag version you want to build"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Get commit to build
id: ref
run: |-
if [[ -z "${{ github.event.inputs.tag }}" ]]; then
ref="${{ github.ref_name }}"
else
ref="${{ github.event.inputs.tag }}"
fi
echo "ref=$ref"
echo "ref=$ref" >> $GITHUB_OUTPUT
if [[ $ref == *"-"* ]]; then
latest=latest-beta
else
latest=latest
fi
echo "latest=$latest"
echo "latest=$latest" >> $GITHUB_OUTPUT
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
ref: ${{ steps.ref.outputs.ref }}
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Setup QEMU for Docker Buildx
@@ -49,14 +27,13 @@ jobs:
with:
images: ghcr.io/sagernet/sing-box
- name: Build and release Docker images
uses: docker/build-push-action@v6
uses: docker/build-push-action@v5
with:
platforms: linux/386,linux/amd64,linux/arm64,linux/s390x
context: .
target: dist
build-args: |
BUILDKIT_CONTEXT_KEEP_GIT_DIR=1
tags: |
ghcr.io/sagernet/sing-box:${{ steps.ref.outputs.latest }}
ghcr.io/sagernet/sing-box:${{ steps.ref.outputs.ref }}
ghcr.io/sagernet/sing-box:latest
ghcr.io/sagernet/sing-box:${{ github.ref_name }}
push: true

View File

@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
fetch-depth: 0
- name: Setup Go
@@ -30,7 +30,7 @@ jobs:
with:
go-version: ^1.22
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
uses: golangci/golangci-lint-action@v4
with:
version: latest
args: --timeout=30m

View File

@@ -10,23 +10,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ^1.22
- name: Extract signing key
run: |-
mkdir -p $HOME/.gnupg
cat > $HOME/.gnupg/sagernet.key <<EOF
${{ secrets.GPG_KEY }}
echo "HOME=$HOME" >> "$GITHUB_ENV"
EOF
echo "HOME=$HOME" >> "$GITHUB_ENV"
- name: Publish release
uses: goreleaser/goreleaser-action@v6
uses: goreleaser/goreleaser-action@v5
with:
distribution: goreleaser-pro
version: latest
@@ -35,5 +27,3 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
FURY_TOKEN: ${{ secrets.FURY_TOKEN }}
NFPM_KEY_PATH: ${{ env.HOME }}/.gnupg/sagernet.key
NFPM_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}

2
.gitignore vendored
View File

@@ -14,5 +14,3 @@
/*.xcframework/
.DS_Store
/config.d/
/venv/

View File

@@ -6,7 +6,14 @@ linters:
- gci
- staticcheck
- paralleltest
- ineffassign
run:
skip-dirs:
- transport/simple-obfs
- transport/clashssr
- transport/cloudflaretls
- transport/shadowtls/tls
- transport/shadowtls/tls_go119
linters-settings:
gci:
@@ -16,13 +23,4 @@ linters-settings:
- prefix(github.com/sagernet/)
- default
staticcheck:
checks:
- all
- -SA1003
run:
go: "1.23"
issues:
exclude-dirs:
- transport/simple-obfs
go: '1.20'

View File

@@ -36,6 +36,7 @@ nfpms:
file_name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
builds:
- main
vendor: sagernet
homepage: https://sing-box.sagernet.org/
maintainer: nekohasekai <contact-git@sekai.icu>
description: The universal proxy platform.
@@ -54,20 +55,11 @@ nfpms:
dst: /usr/lib/systemd/system/sing-box@.service
- src: LICENSE
dst: /usr/share/licenses/sing-box/LICENSE
deb:
signature:
key_file: "{{ .Env.NFPM_KEY_PATH }}"
fields:
Bugs: https://github.com/SagerNet/sing-box/issues
rpm:
signature:
key_file: "{{ .Env.NFPM_KEY_PATH }}"
conflicts:
- sing-box-beta
- id: package_beta
<<: *template
package_name: sing-box-beta
file_name_template: '{{ .ProjectName }}-beta_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
formats:
- deb
- rpm
@@ -79,8 +71,10 @@ furies:
- account: sagernet
ids:
- package
disable: "{{ not (not .Prerelease) }}"
skip: |-
{{ eq .Prerelease "" }}
- account: sagernet
ids:
- package_beta
disable: "{{ not .Prerelease }}"
skip: |-
{{ ne .Prerelease "" }}

View File

@@ -48,8 +48,8 @@ builds:
- with_clash_api
env:
- CGO_ENABLED=0
- GOROOT={{ .Env.GOPATH }}/go1.20.14
gobinary: "{{ .Env.GOPATH }}/go1.20.14/bin/go"
- GOROOT=/nix/store/cpfvjwc7d5hai3iar78h86iwnvgppjrg-go-1.20.14/share/go
gobinary: /nix/store/cpfvjwc7d5hai3iar78h86iwnvgppjrg-go-1.20.14/bin/go
targets:
- windows_amd64_v1
- windows_386
@@ -113,6 +113,7 @@ nfpms:
file_name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
builds:
- main
vendor: sagernet
homepage: https://sing-box.sagernet.org/
maintainer: nekohasekai <contact-git@sekai.icu>
description: The universal proxy platform.
@@ -135,8 +136,6 @@ nfpms:
deb:
signature:
key_file: "{{ .Env.NFPM_KEY_PATH }}"
fields:
Bugs: https://github.com/SagerNet/sing-box/issues
rpm:
signature:
key_file: "{{ .Env.NFPM_KEY_PATH }}"

View File

@@ -64,7 +64,7 @@ proto_install:
go install -v google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
release:
go run ./cmd/internal/build goreleaser release --clean --skip publish
go run ./cmd/internal/build goreleaser release --clean --skip publish --skip fury
mkdir dist/release
mv dist/*.tar.gz \
dist/*.zip \
@@ -81,6 +81,7 @@ release_repo:
go run ./cmd/internal/build goreleaser release -f .goreleaser.fury.yaml --clean
release_install:
go install -v github.com/goreleaser/goreleaser@latest
go install -v github.com/tcnksm/ghr@latest
update_android_version:
@@ -197,15 +198,13 @@ lib_install:
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.3
docs:
venv/bin/mkdocs serve
mkdocs serve
publish_docs:
venv/bin/mkdocs gh-deploy -m "Update" --force --ignore-version --no-history
mkdocs gh-deploy -m "Update" --force --ignore-version --no-history
docs_install:
python -m venv venv
source ./venv/bin/activate && pip install --force-reinstall mkdocs-material=="9.*" mkdocs-static-i18n=="1.2.*"
pip install --force-reinstall mkdocs-material=="9.*" mkdocs-static-i18n=="1.2.*"
clean:
rm -rf bin dist sing-box
rm -f $(shell go env GOPATH)/sing-box

View File

@@ -4,6 +4,10 @@ The universal proxy platform.
[![Packaging status](https://repology.org/badge/vertical-allrepos/sing-box.svg)](https://repology.org/project/sing-box/versions)
## Documentation
https://sing-box.sagernet.org
## Support
https://community.sagernet.org/c/sing-box/

View File

@@ -61,12 +61,16 @@ type InboundContext struct {
}
func (c *InboundContext) ResetRuleCache() {
c.ResetRuleCacheContext()
c.DidMatch = false
}
func (c *InboundContext) ResetRuleCacheContext() {
c.IPCIDRMatchSource = false
c.SourceAddressMatch = false
c.SourcePortMatch = false
c.DestinationAddressMatch = false
c.DestinationPortMatch = false
c.DidMatch = false
}
type inboundContextKey struct{}
@@ -99,12 +103,3 @@ func ExtendContext(ctx context.Context) (context.Context, *InboundContext) {
}
return WithContext(ctx, &newMetadata), &newMetadata
}
func OverrideContext(ctx context.Context) context.Context {
if metadata := ContextFrom(ctx); metadata != nil {
var newMetadata InboundContext
newMetadata = *metadata
return WithContext(ctx, &newMetadata)
}
return ctx
}

View File

@@ -71,7 +71,6 @@ func RouterFromContext(ctx context.Context) Router {
type HeadlessRule interface {
Match(metadata *InboundContext) bool
String() string
}
type Rule interface {
@@ -80,19 +79,21 @@ type Rule interface {
Type() string
UpdateGeosite() error
Outbound() string
String() string
}
type DNSRule interface {
Rule
DisableCache() bool
RewriteTTL() *uint32
ClientSubnet() *netip.Prefix
ClientSubnet() *netip.Addr
WithAddressLimit() bool
MatchAddressLimit(metadata *InboundContext) bool
}
type RuleSet interface {
StartContext(ctx context.Context, startContext RuleSetStartContext) error
PostStart() error
Metadata() RuleSetMetadata
Close() error
HeadlessRule

View File

@@ -22,5 +22,4 @@ type V2RayServerTransportHandler interface {
type V2RayClientTransport interface {
DialContext(ctx context.Context) (net.Conn, error)
Close() error
}

10
box.go
View File

@@ -203,7 +203,7 @@ func (s *Box) PreStart() error {
defer func() {
v := recover()
if v != nil {
println(err.Error())
log.Error(E.Cause(err, "origin error"))
debug.PrintStack()
panic("panic on early close: " + fmt.Sprint(v))
}
@@ -222,9 +222,9 @@ func (s *Box) Start() error {
defer func() {
v := recover()
if v != nil {
println(err.Error())
log.Error(E.Cause(err, "origin error"))
debug.PrintStack()
println("panic on early start: " + fmt.Sprint(v))
panic("panic on early close: " + fmt.Sprint(v))
}
}()
s.Close()
@@ -235,7 +235,7 @@ func (s *Box) Start() error {
}
func (s *Box) preStart() error {
monitor := taskmonitor.New(s.logger, C.StartTimeout)
monitor := taskmonitor.New(s.logger, C.DefaultStartTimeout)
monitor.Start("start logger")
err := s.logFactory.Start()
monitor.Finish()
@@ -331,7 +331,7 @@ func (s *Box) Close() error {
default:
close(s.done)
}
monitor := taskmonitor.New(s.logger, C.StopTimeout)
monitor := taskmonitor.New(s.logger, C.DefaultStopTimeout)
var errors error
for serviceName, service := range s.postServices {
monitor.Start("close ", serviceName)

View File

@@ -12,7 +12,7 @@ import (
)
func (s *Box) startOutbounds() error {
monitor := taskmonitor.New(s.logger, C.StartTimeout)
monitor := taskmonitor.New(s.logger, C.DefaultStartTimeout)
outboundTags := make(map[adapter.Outbound]string)
outbounds := make(map[string]adapter.Outbound)
for i, outboundToStart := range s.outbounds {

View File

@@ -1,87 +0,0 @@
package main
import (
"bytes"
"io"
"os"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/srs"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/route"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/json"
"github.com/spf13/cobra"
)
var flagRuleSetMatchFormat string
var commandRuleSetMatch = &cobra.Command{
Use: "match <rule-set path> <domain>",
Short: "Check if a domain matches the rule set",
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
err := ruleSetMatch(args[0], args[1])
if err != nil {
log.Fatal(err)
}
},
}
func init() {
commandRuleSetMatch.Flags().StringVarP(&flagRuleSetMatchFormat, "format", "f", "source", "rule-set format")
commandRuleSet.AddCommand(commandRuleSetMatch)
}
func ruleSetMatch(sourcePath string, domain string) error {
var (
reader io.Reader
err error
)
if sourcePath == "stdin" {
reader = os.Stdin
} else {
reader, err = os.Open(sourcePath)
if err != nil {
return E.Cause(err, "read rule-set")
}
}
content, err := io.ReadAll(reader)
if err != nil {
return E.Cause(err, "read rule-set")
}
var plainRuleSet option.PlainRuleSet
switch flagRuleSetMatchFormat {
case C.RuleSetFormatSource:
var compat option.PlainRuleSetCompat
compat, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content)
if err != nil {
return err
}
plainRuleSet = compat.Upgrade()
case C.RuleSetFormatBinary:
plainRuleSet, err = srs.Read(bytes.NewReader(content), false)
if err != nil {
return err
}
default:
return E.New("unknown rule set format: ", flagRuleSetMatchFormat)
}
for i, ruleOptions := range plainRuleSet.Rules {
var currentRule adapter.HeadlessRule
currentRule, err = route.NewHeadlessRule(nil, ruleOptions)
if err != nil {
return E.Cause(err, "parse rule_set.rules.[", i, "]")
}
if currentRule.Match(&adapter.InboundContext{
Domain: domain,
}) {
println(F.ToString("match rules.[", i, "]: ", currentRule))
}
}
return nil
}

View File

@@ -199,7 +199,7 @@ func run() error {
}
func closeMonitor(ctx context.Context) {
time.Sleep(C.FatalStopTimeout)
time.Sleep(C.DefaultStopFatalTimeout)
select {
case <-ctx.Done():
return

View File

@@ -32,20 +32,14 @@ func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDi
var dialer net.Dialer
var listener net.ListenConfig
if options.BindInterface != "" {
var interfaceFinder control.InterfaceFinder
if router != nil {
interfaceFinder = router.InterfaceFinder()
} else {
interfaceFinder = control.NewDefaultInterfaceFinder()
}
bindFunc := control.BindToInterface(interfaceFinder, options.BindInterface, -1)
bindFunc := control.BindToInterface(router.InterfaceFinder(), options.BindInterface, -1)
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
} else if router != nil && router.AutoDetectInterface() {
} else if router.AutoDetectInterface() {
bindFunc := router.AutoDetectInterfaceFunc()
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
} else if router != nil && router.DefaultInterface() != "" {
} else if router.DefaultInterface() != "" {
bindFunc := control.BindToInterface(router.InterfaceFinder(), router.DefaultInterface(), -1)
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
@@ -53,7 +47,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDi
if options.RoutingMark != 0 {
dialer.Control = control.Append(dialer.Control, control.RoutingMark(options.RoutingMark))
listener.Control = control.Append(listener.Control, control.RoutingMark(options.RoutingMark))
} else if router != nil && router.DefaultMark() != 0 {
} else if router.DefaultMark() != 0 {
dialer.Control = control.Append(dialer.Control, control.RoutingMark(router.DefaultMark()))
listener.Control = control.Append(listener.Control, control.RoutingMark(router.DefaultMark()))
}
@@ -69,9 +63,6 @@ func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDi
} else {
dialer.Timeout = C.TCPTimeout
}
// TODO: Add an option to customize the keep alive period
dialer.KeepAlive = C.TCPKeepAliveInitial
dialer.Control = control.Append(dialer.Control, control.SetKeepAlivePeriod(C.TCPKeepAliveInitial, C.TCPKeepAliveInterval))
var udpFragment bool
if options.UDPFragment != nil {
udpFragment = *options.UDPFragment

View File

@@ -13,9 +13,6 @@ func New(router adapter.Router, options option.DialerOptions) (N.Dialer, error)
if options.IsWireGuardListener {
return NewDefault(router, options)
}
if router == nil {
return NewDefault(nil, options)
}
var (
dialer N.Dialer
err error

View File

@@ -1,16 +1,11 @@
package mux
import (
"context"
"net"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-mux"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
@@ -35,7 +30,7 @@ func NewClientWithOptions(dialer N.Dialer, logger logger.Logger, options option.
}
}
return mux.NewClient(mux.Options{
Dialer: &clientDialer{dialer},
Dialer: dialer,
Logger: logger,
Protocol: options.Protocol,
MaxConnections: options.MaxConnections,
@@ -45,15 +40,3 @@ func NewClientWithOptions(dialer N.Dialer, logger logger.Logger, options option.
Brutal: brutalOptions,
})
}
type clientDialer struct {
N.Dialer
}
func (d *clientDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
return d.Dialer.DialContext(adapter.OverrideContext(ctx), network, destination)
}
func (d *clientDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
return d.Dialer.ListenPacket(adapter.OverrideContext(ctx), destination)
}

View File

@@ -60,12 +60,12 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) {
isIPv4 := ip.Is4()
value, err := unix.SysctlRaw(spath)
value, err := syscall.Sysctl(spath)
if err != nil {
return "", err
}
buf := value
buf := []byte(value)
// from darwin-xnu/bsd/netinet/in_pcblist.c:get_pcblist_n
// size/offset are round up (aligned) to 8 bytes in darwin

View File

@@ -16,40 +16,30 @@ import (
)
type LinuxSystemProxy struct {
hasGSettings bool
kWriteConfigCmd string
sudoUser string
serverAddr M.Socksaddr
supportSOCKS bool
isEnabled bool
hasGSettings bool
hasKWriteConfig5 bool
sudoUser string
serverAddr M.Socksaddr
supportSOCKS bool
isEnabled bool
}
func NewSystemProxy(ctx context.Context, serverAddr M.Socksaddr, supportSOCKS bool) (*LinuxSystemProxy, error) {
hasGSettings := common.Error(exec.LookPath("gsettings")) == nil
kWriteConfigCmds := []string{
"kwriteconfig5",
"kwriteconfig6",
}
var kWriteConfigCmd string
for _, cmd := range kWriteConfigCmds {
if common.Error(exec.LookPath(cmd)) == nil {
kWriteConfigCmd = cmd
break
}
}
hasKWriteConfig5 := common.Error(exec.LookPath("kwriteconfig5")) == nil
var sudoUser string
if os.Getuid() == 0 {
sudoUser = os.Getenv("SUDO_USER")
}
if !hasGSettings && kWriteConfigCmd == "" {
if !hasGSettings && !hasKWriteConfig5 {
return nil, E.New("unsupported desktop environment")
}
return &LinuxSystemProxy{
hasGSettings: hasGSettings,
kWriteConfigCmd: kWriteConfigCmd,
sudoUser: sudoUser,
serverAddr: serverAddr,
supportSOCKS: supportSOCKS,
hasGSettings: hasGSettings,
hasKWriteConfig5: hasKWriteConfig5,
sudoUser: sudoUser,
serverAddr: serverAddr,
supportSOCKS: supportSOCKS,
}, nil
}
@@ -80,8 +70,8 @@ func (p *LinuxSystemProxy) Enable() error {
return err
}
}
if p.kWriteConfigCmd != "" {
err := p.runAsUser(p.kWriteConfigCmd, "--file", "kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "1")
if p.hasKWriteConfig5 {
err := p.runAsUser("kwriteconfig5", "--file", "kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "1")
if err != nil {
return err
}
@@ -93,7 +83,7 @@ func (p *LinuxSystemProxy) Enable() error {
if err != nil {
return err
}
err = p.runAsUser(p.kWriteConfigCmd, "--file", "kioslaverc", "--group", "Proxy Settings", "--key", "Authmode", "0")
err = p.runAsUser("kwriteconfig5", "--file", "kioslaverc", "--group", "Proxy Settings", "--key", "Authmode", "0")
if err != nil {
return err
}
@@ -113,8 +103,8 @@ func (p *LinuxSystemProxy) Disable() error {
return err
}
}
if p.kWriteConfigCmd != "" {
err := p.runAsUser(p.kWriteConfigCmd, "--file", "kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "0")
if p.hasKWriteConfig5 {
err := p.runAsUser("kwriteconfig5", "--file", "kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "0")
if err != nil {
return err
}
@@ -160,7 +150,7 @@ func (p *LinuxSystemProxy) setKDEProxy(proxyTypes ...string) error {
proxyUrl = "http://" + p.serverAddr.String()
}
err := p.runAsUser(
p.kWriteConfigCmd,
"kwriteconfig5",
"--file",
"kioslaverc",
"--group",

View File

@@ -27,10 +27,11 @@ func (c *echClientConfig) DialEarly(ctx context.Context, conn net.PacketConn, ad
return quic.DialEarly(ctx, conn, addr, c.config, config)
}
func (c *echClientConfig) CreateTransport(conn net.PacketConn, quicConnPtr *quic.EarlyConnection, serverAddr M.Socksaddr, quicConfig *quic.Config) http.RoundTripper {
func (c *echClientConfig) CreateTransport(conn net.PacketConn, quicConnPtr *quic.EarlyConnection, serverAddr M.Socksaddr, quicConfig *quic.Config, enableDatagrams bool) http.RoundTripper {
return &http3.RoundTripper{
TLSClientConfig: c.config,
QUICConfig: quicConfig,
QuicConfig: quicConfig,
EnableDatagrams: enableDatagrams,
Dial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
quicConn, err := quic.DialEarly(ctx, conn, serverAddr.UDPAddr(), tlsCfg, cfg)
if err != nil {

View File

@@ -3,18 +3,15 @@ package constant
import "time"
const (
TCPKeepAliveInitial = 10 * time.Minute
TCPKeepAliveInterval = 75 * time.Second
TCPTimeout = 5 * time.Second
ReadPayloadTimeout = 300 * time.Millisecond
DNSTimeout = 10 * time.Second
QUICTimeout = 30 * time.Second
STUNTimeout = 15 * time.Second
UDPTimeout = 5 * time.Minute
DefaultURLTestInterval = 3 * time.Minute
DefaultURLTestIdleTimeout = 30 * time.Minute
StartTimeout = 10 * time.Second
StopTimeout = 5 * time.Second
FatalStopTimeout = 10 * time.Second
FakeIPMetadataSaveInterval = 10 * time.Second
TCPTimeout = 5 * time.Second
ReadPayloadTimeout = 300 * time.Millisecond
DNSTimeout = 10 * time.Second
QUICTimeout = 30 * time.Second
STUNTimeout = 15 * time.Second
UDPTimeout = 5 * time.Minute
DefaultURLTestInterval = 3 * time.Minute
DefaultURLTestIdleTimeout = 30 * time.Minute
DefaultStartTimeout = 10 * time.Second
DefaultStopTimeout = 5 * time.Second
DefaultStopFatalTimeout = 10 * time.Second
)

View File

@@ -2,174 +2,6 @@
icon: material/alert-decagram
---
!!! failure "Help needed"
Due to problems with our Apple developer account, sing-box apps on Apple platforms are temporarily unavailable for download or update.
If your company or organization is willing to help us return to the App Store, please [contact us](mailto:contact@sagernet.org).
### 1.9.4
* Update quic-go to v0.46.0
* Update Hysteria2 BBR congestion control
* Filter HTTPS ipv4hint/ipv6hint with domain strategy
* Fix crash on Android when using process rules
* Fix non-IP queries accepted by address filter rules
* Fix UDP server for shadowsocks AEAD multi-user inbounds
* Fix default next protos for v2ray QUIC transport
* Fix default end value of port range configuration options
* Fix reset v2ray transports
* Fix panic caused by rule-set generation of duplicate keys for `domain_suffix`
* Fix UDP connnection leak when sniffing
* Fixes and improvements
### 1.9.3
* Fixes and improvements
### 1.9.2
* Fixes and improvements
### 1.9.1
* Fixes and improvements
### 1.9.0
* Fixes and improvements
Important changes since 1.8:
* `domain_suffix` behavior update **1**
* `process_path` format update on Windows **2**
* Add address filter DNS rule items **3**
* Add support for `client-subnet` DNS options **4**
* Add rejected DNS response cache support **5**
* Add `bypass_domain` and `search_domain` platform HTTP proxy options **6**
* Fix missing `rule_set_ipcidr_match_source` item in DNS rules **7**
* Handle Windows power events
* Always disable cache for fake-ip DNS transport if `dns.independent_cache` disabled
* Improve DNS truncate behavior
* Update Hysteria protocol
* Update quic-go to v0.43.1
* Update gVisor to 20240422.0
* Mitigating TunnelVision attacks **8**
**1**:
See [Migration](/migration/#domain_suffix-behavior-update).
**2**:
See [Migration](/migration/#process_path-format-update-on-windows).
**3**:
The new DNS feature allows you to more precisely bypass Chinese websites via **DNS leaks**. Do not use plain local DNS
if using this method.
See [Address Filter Fields](/configuration/dns/rule#address-filter-fields).
[Client example](/manual/proxy/client#traffic-bypass-usage-for-chinese-users) updated.
**4**:
See [DNS](/configuration/dns), [DNS Server](/configuration/dns/server) and [DNS Rules](/configuration/dns/rule).
Since this feature makes the scenario mentioned in `alpha.1` no longer leak DNS requests,
the [Client example](/manual/proxy/client#traffic-bypass-usage-for-chinese-users) has been updated.
**5**:
The new feature allows you to cache the check results of
[Address filter DNS rule items](/configuration/dns/rule/#address-filter-fields) until expiration.
**6**:
See [TUN](/configuration/inbound/tun) inbound.
**7**:
See [DNS Rule](/configuration/dns/rule/).
**8**:
See [TunnelVision](/manual/misc/tunnelvision).
#### 1.9.0-rc.22
* Fixes and improvements
#### 1.9.0-rc.20
* Prioritize `*_route_address` in linux auto-route
* Fix `*_route_address` in darwin auto-route
#### 1.8.14
* Fix hysteria2 panic
* Fixes and improvements
#### 1.9.0-rc.18
* Add custom prefix support in EDNS0 client subnet options
* Fix hysteria2 crash
* Fix `store_rdrc` corrupted
* Update quic-go to v0.43.1
* Fixes and improvements
#### 1.9.0-rc.16
* Mitigating TunnelVision attacks **1**
* Fixes and improvements
**1**:
See [TunnelVision](/manual/misc/tunnelvision).
#### 1.9.0-rc.15
* Fixes and improvements
#### 1.8.13
* Fix fake-ip mapping
* Fixes and improvements
#### 1.9.0-rc.14
* Fixes and improvements
#### 1.9.0-rc.13
* Update Hysteria protocol
* Update quic-go to v0.43.0
* Update gVisor to 20240422.0
* Fixes and improvements
#### 1.8.12
* Now we have official APT and DNF repositories **1**
* Fix packet MTU for QUIC protocols
* Fixes and improvements
**1**:
Including stable and beta versions, see https://sing-box.sagernet.org/installation/package-manager/
#### 1.9.0-rc.11
* Fixes and improvements
#### 1.8.11
* Fixes and improvements
#### 1.8.10
* Fixes and improvements
#### 1.9.0-beta.17
* Update `quic-go` to v0.42.0
@@ -331,7 +163,7 @@ See [Address Filter Fields](/configuration/dns/rule#address-filter-fields).
* Fixes and improvements
### 1.8.0
#### 1.8.0
* Fixes and improvements
@@ -622,7 +454,7 @@ New commands manage GeoIP, Geosite and rule set resources, and help you migrate
Logical rules in route rules, DNS rules, and the new headless rule now allow nesting of logical rules.
### 1.7.0
#### 1.7.0
* Fixes and improvements
@@ -782,7 +614,7 @@ Introduced in V2Ray 5.10.0.
The new HTTPUpgrade transport has better performance than WebSocket and is better suited for CDN abuse.
### 1.6.0
#### 1.6.0
* Fixes and improvements
@@ -961,7 +793,7 @@ introduce new issues.
None of the existing Golang BBR congestion control implementations have been reviewed or unit tested.
This update is intended to address the multi-send defects of the old implementation and may introduce new issues.
### 1.5.0
#### 1.5.0
* Fixes and improvements
@@ -1155,7 +987,7 @@ All inbounds and outbounds are supported, including `Naiveproxy`, `Hysteria`, `T
* Fixes and improvements
### 1.4.0
#### 1.4.0
* Fix bugs and update dependencies
@@ -1297,7 +1129,7 @@ The old testflight link and app are no longer valid.
* Fixes and improvements
### 1.3.0
#### 1.3.0
* Fix bugs and update dependencies
@@ -1489,7 +1321,7 @@ to `domain` rule.
* Flush DNS cache for macOS when tun start/close
* Fix tun's DNS hijacking compatibility with systemd-resolved
### 1.2.0
#### 1.2.0
* Fix bugs and update dependencies

View File

@@ -7,13 +7,6 @@ icon: material/apple
SFI/SFM/SFT allows users to manage and run local or remote sing-box configuration files, and provides
platform-specific function implementation, such as TUN transparent proxy implementation.
!!! failure "Unavailable"
Due to problems with our Apple developer account, sing-box apps on Apple platforms are temporarily unavailable for download or update.
If your company or organization is willing to help us return to the App Store, please [contact us](mailto:contact@sagernet.org).
## :material-graph: Requirements
* iOS 15.0+ / macOS 13.0+ / Apple tvOS 17.0+
@@ -22,12 +15,11 @@ platform-specific function implementation, such as TUN transparent proxy impleme
## :material-download: Download
* [App Store](https://apps.apple.com/us/app/sing-box/id6451272673)
* ~~TestFlight (Beta)~~
* ~~[TestFlight (Beta)](https://testflight.apple.com/join/AcqO44FH)~~
TestFlight quota is only available to [sponsors](https://github.com/sponsors/nekohasekai)
(one-time sponsorships are accepted).
Once you donate, you can get an invitation by sending us your Apple ID [via email](mailto:contact@sagernet.org),
or join our Telegram group for sponsors from [@yet_another_sponsor_bot](https://t.me/yet_another_sponsor_bot).
_Our Testflight distribution has been temporarily blocked by Apple (possibly due to too many beta versions)
and you cannot join the test, install or update the sing-box beta app right now.
Please wait patiently for processing._
## :material-file-download: Download (macOS standalone version)

View File

@@ -3,9 +3,9 @@
Maintained by Project S to provide a unified experience and platform-specific functionality.
| Platform | Client |
| ------------------------------------- | ---------------------------------------- |
|---------------------------------------|------------------------------------------|
| :material-android: Android | [sing-box for Android](./android/) |
| :material-apple: iOS/macOS/Apple tvOS | :material-alert: [Unavailable](./apple/) |
| :material-apple: iOS/macOS/Apple tvOS | [sing-box for Apple platforms](./apple/) |
| :material-laptop: Desktop | Working in progress |
Some third-party projects that claim to use sing-box or use sing-box as a selling point are not listed here. The core

View File

@@ -2,11 +2,11 @@
由 Project S 维护,提供统一的体验与平台特定的功能。
| 平台 | 客户端 |
| ------------------------------------- | ----------------------------------- |
| :material-android: Android | [sing-box for Android](./android/) |
| :material-apple: iOS/macOS/Apple tvOS | :material-alert: [不可用](./apple/) |
| :material-laptop: Desktop | 施工中 |
| 平台 | 客户端 |
|---------------------------------------|-----------------------------------------|
| :material-android: Android | [sing-box for Android](./android/) |
| :material-apple: iOS/macOS/Apple tvOS | [sing-box for Apple platforms](./apple/) |
| :material-laptop: Desktop | 施工中 |
此处没有列出一些声称使用或以 sing-box 为卖点的第三方项目。此类项目维护者的动机是获得更多用户,即使它们提供友好的商业
VPN 客户端功能, 但代码质量很差且包含广告。

View File

@@ -73,8 +73,6 @@ problematic in environments such as macOS, where DNS is proxied and cached by th
!!! question "Since sing-box 1.9.0"
Append a `edns0-subnet` OPT extra record with the specified IP prefix to every query by default.
If value is an IP address instead of prefix, `/32` or `/128` will be appended automatically.
Append a `edns0-subnet` OPT extra record with the specified IP address to every query by default.
Can be overrides by `servers.[].client_subnet` or `rules.[].client_subnet`.

View File

@@ -71,10 +71,8 @@ icon: material/new-box
!!! question "自 sing-box 1.9.0 起"
默认情况下,将带有指定 IP 前缀`edns0-subnet` OPT 附加记录附加到每个查询。
如果值是 IP 地址而不是前缀,则会自动附加 `/32``/128`
默认情况下,将带有指定 IP 地址`edns0-subnet` OPT 附加记录附加到每个查询。
可以被 `servers.[].client_subnet``rules.[].client_subnet` 覆盖。
#### fakeip

View File

@@ -125,7 +125,7 @@ icon: material/new-box
"server": "local",
"disable_cache": false,
"rewrite_ttl": 100,
"client_subnet": "127.0.0.1/24"
"client_subnet": "127.0.0.1"
},
{
"type": "logical",
@@ -134,7 +134,7 @@ icon: material/new-box
"server": "local",
"disable_cache": false,
"rewrite_ttl": 100,
"client_subnet": "127.0.0.1/24"
"client_subnet": "127.0.0.1"
}
]
}
@@ -339,9 +339,7 @@ Rewrite TTL in DNS responses.
!!! question "Since sing-box 1.9.0"
Append a `edns0-subnet` OPT extra record with the specified IP prefix to every query by default.
If value is an IP address instead of prefix, `/32` or `/128` will be appended automatically.
Append a `edns0-subnet` OPT extra record with the specified IP address to every query by default.
Will overrides `dns.client_subnet` and `servers.[].client_subnet`.

View File

@@ -124,7 +124,7 @@ icon: material/new-box
],
"server": "local",
"disable_cache": false,
"client_subnet": "127.0.0.1/24"
"client_subnet": "127.0.0.1"
},
{
"type": "logical",
@@ -132,7 +132,7 @@ icon: material/new-box
"rules": [],
"server": "local",
"disable_cache": false,
"client_subnet": "127.0.0.1/24"
"client_subnet": "127.0.0.1"
}
]
}
@@ -337,9 +337,7 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
!!! question "自 sing-box 1.9.0 起"
默认情况下,将带有指定 IP 前缀`edns0-subnet` OPT 附加记录附加到每个查询。
如果值是 IP 地址而不是前缀,则会自动附加 `/32``/128`
默认情况下,将带有指定 IP 地址`edns0-subnet` OPT 附加记录附加到每个查询。
将覆盖 `dns.client_subnet``servers.[].client_subnet`

View File

@@ -100,9 +100,7 @@ Default outbound will be used if empty.
!!! question "Since sing-box 1.9.0"
Append a `edns0-subnet` OPT extra record with the specified IP prefix to every query by default.
If value is an IP address instead of prefix, `/32` or `/128` will be appended automatically.
Append a `edns0-subnet` OPT extra record with the specified IP address to every query by default.
Can be overrides by `rules.[].client_subnet`.

View File

@@ -100,9 +100,7 @@ DNS 服务器的地址。
!!! question "自 sing-box 1.9.0 起"
默认情况下,将带有指定 IP 前缀`edns0-subnet` OPT 附加记录附加到每个查询。
如果值是 IP 地址而不是前缀,则会自动附加 `/32``/128`
默认情况下,将带有指定 IP 地址`edns0-subnet` OPT 附加记录附加到每个查询。
可以被 `rules.[].client_subnet` 覆盖。

View File

@@ -15,24 +15,24 @@
### Fields
| Type | Format | Injectable |
|---------------|-------------------------------|------------------|
| `direct` | [Direct](./direct/) | :material-close: |
| `mixed` | [Mixed](./mixed/) | TCP |
| `socks` | [SOCKS](./socks/) | TCP |
| `http` | [HTTP](./http/) | TCP |
| `shadowsocks` | [Shadowsocks](./shadowsocks/) | TCP |
| `vmess` | [VMess](./vmess/) | TCP |
| `trojan` | [Trojan](./trojan/) | TCP |
| `naive` | [Naive](./naive/) | :material-close: |
| `hysteria` | [Hysteria](./hysteria/) | :material-close: |
| `shadowtls` | [ShadowTLS](./shadowtls/) | TCP |
| `tuic` | [TUIC](./tuic/) | :material-close: |
| `hysteria2` | [Hysteria2](./hysteria2/) | :material-close: |
| `vless` | [VLESS](./vless/) | TCP |
| `tun` | [Tun](./tun/) | :material-close: |
| `redirect` | [Redirect](./redirect/) | :material-close: |
| `tproxy` | [TProxy](./tproxy/) | :material-close: |
| Type | Format | Injectable |
|---------------|-------------------------------|------------|
| `direct` | [Direct](./direct/) | X |
| `mixed` | [Mixed](./mixed/) | TCP |
| `socks` | [SOCKS](./socks/) | TCP |
| `http` | [HTTP](./http/) | TCP |
| `shadowsocks` | [Shadowsocks](./shadowsocks/) | TCP |
| `vmess` | [VMess](./vmess/) | TCP |
| `trojan` | [Trojan](./trojan/) | TCP |
| `naive` | [Naive](./naive/) | X |
| `hysteria` | [Hysteria](./hysteria/) | X |
| `shadowtls` | [ShadowTLS](./shadowtls/) | TCP |
| `tuic` | [TUIC](./tuic/) | X |
| `hysteria2` | [Hysteria2](./hysteria2/) | X |
| `vless` | [VLESS](./vless/) | TCP |
| `tun` | [Tun](./tun/) | X |
| `redirect` | [Redirect](./redirect/) | X |
| `tproxy` | [TProxy](./tproxy/) | X |
#### tag

View File

@@ -15,24 +15,24 @@
### 字段
| 类型 | 格式 | 注入支持 |
|---------------|-------------------------------|------------------|
| `direct` | [Direct](./direct/) | :material-close: |
| `mixed` | [Mixed](./mixed/) | TCP |
| `socks` | [SOCKS](./socks/) | TCP |
| `http` | [HTTP](./http/) | TCP |
| `shadowsocks` | [Shadowsocks](./shadowsocks/) | TCP |
| `vmess` | [VMess](./vmess/) | TCP |
| `trojan` | [Trojan](./trojan/) | TCP |
| `naive` | [Naive](./naive/) | :material-close: |
| `hysteria` | [Hysteria](./hysteria/) | :material-close: |
| `shadowtls` | [ShadowTLS](./shadowtls/) | TCP |
| `tuic` | [TUIC](./tuic/) | :material-close: |
| `hysteria2` | [Hysteria2](./hysteria2/) | :material-close: |
| `vless` | [VLESS](./vless/) | TCP |
| `tun` | [Tun](./tun/) | :material-close: |
| `redirect` | [Redirect](./redirect/) | :material-close: |
| `tproxy` | [TProxy](./tproxy/) | :material-close: |
| 类型 | 格式 | 注入支持 |
|---------------|------------------------------|------|
| `direct` | [Direct](./direct/) | X |
| `mixed` | [Mixed](./mixed/) | TCP |
| `socks` | [SOCKS](./socks/) | TCP |
| `http` | [HTTP](./http/) | TCP |
| `shadowsocks` | [Shadowsocks](./shadowsocks/) | TCP |
| `vmess` | [VMess](./vmess/) | TCP |
| `trojan` | [Trojan](./trojan/) | TCP |
| `naive` | [Naive](./naive/) | X |
| `hysteria` | [Hysteria](./hysteria/) | X |
| `shadowtls` | [ShadowTLS](./shadowtls/) | TCP |
| `tuic` | [TUIC](./tuic/) | X |
| `hysteria2` | [Hysteria2](./hysteria2/) | X |
| `vless` | [VLESS](./vless/) | TCP |
| `tun` | [Tun](./tun/) | X |
| `redirect` | [Redirect](./redirect/) | X |
| `tproxy` | [TProxy](./tproxy/) | X |
#### tag

View File

@@ -147,7 +147,7 @@ Enforce strict routing rules when `auto_route` is enabled:
* Let unsupported network unreachable
* Route all connections to tun
It prevents address leaks and makes DNS hijacking work on Android.
It prevents address leaks and makes DNS hijacking work on Android, but your device will not be accessible by others.
*In Windows*:

View File

@@ -147,7 +147,7 @@ tun 接口的 IPv6 前缀。
* 让不支持的网络无法到达
* 将所有连接路由到 tun
它可以防止地址泄漏,并使 DNS 劫持在 Android 上工作。
它可以防止地址泄漏,并使 DNS 劫持在 Android 上工作,但你的设备将无法其他设备被访问
*在 Windows 中*:

View File

@@ -9,6 +9,6 @@
}
```
### Fields
### 字段
No fields.
No fields.

View File

@@ -4,12 +4,6 @@ description: Welcome to the wiki page for the sing-box project.
# :material-home: Home
!!! failure "Help needed"
Due to problems with our Apple developer account, sing-box apps on Apple platforms are temporarily unavailable for download or update.
If your company or organization is willing to help us return to the App Store, please [contact us](mailto:contact@sagernet.org).
Welcome to the wiki page for the sing-box project.
The universal proxy platform.

View File

@@ -57,16 +57,16 @@ go build -tags "tag_a tag_b" ./cmd/sing-box
| Build Tag | Enabled by default | Description |
|------------------------------------|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `with_quic` | :material-check: | Build with QUIC support, see [QUIC and HTTP3 DNS transports](/configuration/dns/server/), [Naive inbound](/configuration/inbound/naive/), [Hysteria Inbound](/configuration/inbound/hysteria/), [Hysteria Outbound](/configuration/outbound/hysteria/) and [V2Ray Transport#QUIC](/configuration/shared/v2ray-transport#quic). |
| `with_grpc` | :material-close: | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). |
| `with_grpc` | :material-close: | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). |
| `with_dhcp` | :material-check: | Build with DHCP support, see [DHCP DNS transport](/configuration/dns/server/). |
| `with_wireguard` | :material-check: | Build with WireGuard support, see [WireGuard outbound](/configuration/outbound/wireguard/). |
| `with_ech` | :material-check: | Build with TLS ECH extension support for TLS outbound, see [TLS](/configuration/shared/tls#ech). |
| `with_utls` | :material-check: | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). |
| `with_ech` | :material-check: | Build with TLS ECH extension support for TLS outbound, see [TLS](/configuration/shared/tls#ech). |
| `with_utls` | :material-check: | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). |
| `with_reality_server` | :material-check: | Build with reality TLS server support, see [TLS](/configuration/shared/tls/). |
| `with_acme` | :material-check: | Build with ACME TLS certificate issuer support, see [TLS](/configuration/shared/tls/). |
| `with_clash_api` | :material-check: | Build with Clash API support, see [Experimental](/configuration/experimental#clash-api-fields). |
| `with_v2ray_api` | :material-close: | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). |
| `with_gvisor` | :material-check: | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). |
| `with_clash_api` | :material-check: | Build with Clash API support, see [Experimental](/configuration/experimental#clash-api-fields). |
| `with_v2ray_api` | :material-close: | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). |
| `with_gvisor` | :material-check: | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). |
| `with_embedded_tor` (CGO required) | :material-close: | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor/). |
It is not recommended to change the default build tag list unless you really know what you are adding.

View File

@@ -54,19 +54,19 @@ go build -tags "tag_a tag_b" ./cmd/sing-box
## :material-folder-settings: 构建标记
| 构建标记 | 默认启动 | 说明 |
|------------------------------------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 构建标记 | 默认启动 | 说明 |
|------------------------------------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `with_quic` | :material-check: | Build with QUIC support, see [QUIC and HTTP3 DNS transports](/configuration/dns/server/), [Naive inbound](/configuration/inbound/naive/), [Hysteria Inbound](/configuration/inbound/hysteria/), [Hysteria Outbound](/configuration/outbound/hysteria/) and [V2Ray Transport#QUIC](/configuration/shared/v2ray-transport#quic). |
| `with_grpc` | :material-close: | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). |
| `with_dhcp` | :material-check: | Build with DHCP support, see [DHCP DNS transport](/configuration/dns/server/). |
| `with_wireguard` | :material-check: | Build with WireGuard support, see [WireGuard outbound](/configuration/outbound/wireguard/). |
| `with_ech` | :material-check: | Build with TLS ECH extension support for TLS outbound, see [TLS](/configuration/shared/tls#ech). |
| `with_utls` | :material-check: | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). |
| `with_reality_server` | :material-check: | Build with reality TLS server support, see [TLS](/configuration/shared/tls/). |
| `with_acme` | :material-check: | Build with ACME TLS certificate issuer support, see [TLS](/configuration/shared/tls/). |
| `with_clash_api` | :material-check: | Build with Clash API support, see [Experimental](/configuration/experimental#clash-api-fields). |
| `with_v2ray_api` | :material-close: | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). |
| `with_gvisor` | :material-check: | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). |
| `with_embedded_tor` (CGO required) | :material-close: | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor/). |
| `with_grpc` | :material-close: | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). |
| `with_dhcp` | :material-check: | Build with DHCP support, see [DHCP DNS transport](/configuration/dns/server/). |
| `with_wireguard` | :material-check: | Build with WireGuard support, see [WireGuard outbound](/configuration/outbound/wireguard/). |
| `with_ech` | :material-check: | Build with TLS ECH extension support for TLS outbound, see [TLS](/configuration/shared/tls#ech). |
| `with_utls` | :material-check: | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). |
| `with_reality_server` | :material-check: | Build with reality TLS server support, see [TLS](/configuration/shared/tls/). |
| `with_acme` | :material-check: | Build with ACME TLS certificate issuer support, see [TLS](/configuration/shared/tls/). |
| `with_clash_api` | :material-check: | Build with Clash API support, see [Experimental](/configuration/experimental#clash-api-fields). |
| `with_v2ray_api` | :material-close: | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). |
| `with_gvisor` | :material-check: | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). |
| `with_embedded_tor` (CGO required) | :material-close: | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor/). |
除非您确实知道您正在启用什么,否则不建议更改默认构建标签列表。

View File

@@ -9,7 +9,7 @@ icon: material/package
=== ":material-debian: Debian / APT"
```bash
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc
sudo curl -fsSL https://deb.sagernet.org/gpg.key -o /etc/apt/keyrings/sagernet.asc
sudo chmod a+r /etc/apt/keyrings/sagernet.asc
echo "deb [arch=`dpkg --print-architecture` signed-by=/etc/apt/keyrings/sagernet.asc] https://deb.sagernet.org/ * *" | \
sudo tee /etc/apt/sources.list.d/sagernet.list > /dev/null
@@ -21,7 +21,7 @@ icon: material/package
```bash
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo
sudo dnf config-manager --add-repo https://sing-box.app/rpm.repo
sudo dnf install sing-box # or sing-box-beta
```
@@ -29,7 +29,7 @@ icon: material/package
```bash
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://sing-box.app/sing-box.repo
sudo yum-config-manager --add-repo https://sing-box.app/rpm.repo
sudo yum install sing-box # or sing-box-beta
```
@@ -57,38 +57,38 @@ icon: material/package
=== ":material-linux: Linux"
| Type | Platform | Command | Link |
|----------|---------------|------------------------------|---------------------------------------------------------------------------------------------------------------|
| AUR | Arch Linux | `? -S sing-box` | [![AUR package](https://repology.org/badge/version-for-repo/aur/sing-box.svg)][aur] |
| nixpkgs | NixOS | `nix-env -iA nixos.sing-box` | [![nixpkgs unstable package](https://repology.org/badge/version-for-repo/nix_unstable/sing-box.svg)][nixpkgs] |
| Homebrew | macOS / Linux | `brew install sing-box` | [![Homebrew package](https://repology.org/badge/version-for-repo/homebrew/sing-box.svg)][brew] |
| APK | Alpine | `apk add sing-box` | [![Alpine Linux Edge package](https://repology.org/badge/version-for-repo/alpine_edge/sing-box.svg)][alpine] |
| Type | Platform | Link | Command | Actively maintained |
|----------|---------------|-------------------------|------------------------------|---------------------|
| APK | Alpine | [sing-box][alpine] | `apk add sing-box` | :material-check: |
| AUR | Arch Linux | [sing-box][aur] ᴬᵁᴿ | `? -S sing-box` | :material-check: |
| nixpkgs | NixOS | [sing-box][nixpkgs] | `nix-env -iA nixos.sing-box` | :material-check: |
| Homebrew | macOS / Linux | [sing-box][brew] | `brew install sing-box` | :material-check: |
=== ":material-apple: macOS"
| Type | Platform | Command | Link |
|----------|----------|-------------------------|------------------------------------------------------------------------------------------------|
| Homebrew | macOS | `brew install sing-box` | [![Homebrew package](https://repology.org/badge/version-for-repo/homebrew/sing-box.svg)][brew] |
| Type | Platform | Link | Command | Actively maintained |
|----------|----------|------------------|-------------------------|---------------------|
| Homebrew | macOS | [sing-box][brew] | `brew install sing-box` | :material-check: |
=== ":material-microsoft-windows: Windows"
| Type | Platform | Command | Link |
|------------|----------|---------------------------|-----------------------------------------------------------------------------------------------------|
| Scoop | Windows | `scoop install sing-box` | [![Scoop package](https://repology.org/badge/version-for-repo/scoop/sing-box.svg)][scoop] |
| Chocolatey | Windows | `choco install sing-box` | [![Chocolatey package](https://repology.org/badge/version-for-repo/chocolatey/sing-box.svg)][choco] |
| winget | Windows | `winget install sing-box` | [![winget package](https://repology.org/badge/version-for-repo/winget/sing-box.svg)][winget] |
| Type | Platform | Link | Command | Actively maintained |
|------------|--------------------|---------------------|------------------------------|---------------------|
| Scoop | Windows | [sing-box][scoop] | `scoop install sing-box` | :material-check: |
| Chocolatey | Windows | [sing-box][choco] | `choco install sing-box` | :material-check: |
| winget | Windows | [sing-box][winget] | `winget install sing-box` | :material-alert: |
=== ":material-android: Android"
| Type | Platform | Command | Link |
|--------|----------|--------------------|----------------------------------------------------------------------------------------------|
| Termux | Android | `pkg add sing-box` | [![Termux package](https://repology.org/badge/version-for-repo/termux/sing-box.svg)][termux] |
| Type | Platform | Link | Command | Actively maintained |
|------------|--------------------|---------------------|------------------------------|---------------------|
| Termux | Android | [sing-box][termux] | `pkg add sing-box` | :material-check: |
=== ":material-freebsd: FreeBSD"
| Type | Platform | Command | Link |
|------------|----------|------------------------|--------------------------------------------------------------------------------------------|
| FreshPorts | FreeBSD | `pkg install sing-box` | [![FreeBSD port](https://repology.org/badge/version-for-repo/freebsd/sing-box.svg)][ports] |
| Type | Platform | Link | Command | Actively maintained |
|------------|----------|-------------------|------------------------|---------------------|
| FreshPorts | FreeBSD | [sing-box][ports] | `pkg install sing-box` | :material-alert: |
## :material-book-multiple: Service Management

View File

@@ -9,7 +9,7 @@ icon: material/package
=== ":material-debian: Debian / APT"
```bash
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc
sudo curl -fsSL https://deb.sagernet.org/gpg.key -o /etc/apt/keyrings/sagernet.asc
sudo chmod a+r /etc/apt/keyrings/sagernet.asc
echo "deb [arch=`dpkg --print-architecture` signed-by=/etc/apt/keyrings/sagernet.asc] https://deb.sagernet.org/ * *" | \
sudo tee /etc/apt/sources.list.d/sagernet.list > /dev/null
@@ -21,7 +21,7 @@ icon: material/package
```bash
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo
sudo dnf config-manager --add-repo https://sing-box.app/rpm.repo
sudo dnf install sing-box # or sing-box-beta
```
@@ -29,7 +29,7 @@ icon: material/package
```bash
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://sing-box.app/sing-box.repo
sudo yum-config-manager --add-repo https://sing-box.app/rpm.repo
sudo yum install sing-box # or sing-box-beta
```
@@ -57,38 +57,38 @@ icon: material/package
=== ":material-linux: Linux"
| 类型 | 平台 | 链接 | 命令 |
|----------|---------------|------------------------------|---------------------------------------------------------------------------------------------------------------|
| AUR | Arch Linux | `? -S sing-box` | [![AUR package](https://repology.org/badge/version-for-repo/aur/sing-box.svg)][aur] |
| nixpkgs | NixOS | `nix-env -iA nixos.sing-box` | [![nixpkgs unstable package](https://repology.org/badge/version-for-repo/nix_unstable/sing-box.svg)][nixpkgs] |
| Homebrew | macOS / Linux | `brew install sing-box` | [![Homebrew package](https://repology.org/badge/version-for-repo/homebrew/sing-box.svg)][brew] |
| APK | Alpine | `apk add sing-box` | [![Alpine Linux Edge package](https://repology.org/badge/version-for-repo/alpine_edge/sing-box.svg)][alpine] |
| 类型 | 平台 | 链接 | 命令 | 活跃维护 |
|----------|------------|---------------------|------------------------------|------------------|
| Alpine | Alpine | [sing-box][alpine] | `apk add sing-box` | :material-check: |
| AUR | Arch Linux | [sing-box][aur] ᴬᵁᴿ | `? -S sing-box` | :material-check: |
| nixpkgs | NixOS | [sing-box][nixpkgs] | `nix-env -iA nixos.sing-box` | :material-check: |
| Homebrew | Linux | [sing-box][brew] | `brew install sing-box` | :material-check: |
=== ":material-apple: macOS"
| 类型 | 平台 | 链接 | 命令 |
|----------|-------|-------------------------|------------------------------------------------------------------------------------------------|
| Homebrew | macOS | `brew install sing-box` | [![Homebrew package](https://repology.org/badge/version-for-repo/homebrew/sing-box.svg)][brew] |
| 类型 | 平台 | 链接 | 命令 | 活跃维护 |
|----------|-------|------------------|-------------------------|------------------|
| Homebrew | macOS | [sing-box][brew] | `brew install sing-box` | :material-check: |
=== ":material-microsoft-windows: Windows"
| 类型 | 平台 | 链接 | 命令 |
|------------|---------|---------------------------|-----------------------------------------------------------------------------------------------------|
| Scoop | Windows | `scoop install sing-box` | [![Scoop package](https://repology.org/badge/version-for-repo/scoop/sing-box.svg)][scoop] |
| Chocolatey | Windows | `choco install sing-box` | [![Chocolatey package](https://repology.org/badge/version-for-repo/chocolatey/sing-box.svg)][choco] |
| winget | Windows | `winget install sing-box` | [![winget package](https://repology.org/badge/version-for-repo/winget/sing-box.svg)][winget] |
| 类型 | 平台 | 链接 | 命令 | 活跃维护 |
|------------|---------|--------------------|---------------------------|------------------|
| Scoop | Windows | [sing-box][scoop] | `scoop install sing-box` | :material-check: |
| Chocolatey | Windows | [sing-box][choco] | `choco install sing-box` | :material-check: |
| winget | Windows | [sing-box][winget] | `winget install sing-box` | :material-alert: |
=== ":material-android: Android"
| 类型 | 平台 | 链接 | 命令 |
|--------|---------|--------------------|----------------------------------------------------------------------------------------------|
| Termux | Android | `pkg add sing-box` | [![Termux package](https://repology.org/badge/version-for-repo/termux/sing-box.svg)][termux] |
| 类型 | 平台 | 链接 | 命令 | 活跃维护 |
|--------|---------|--------------------|--------------------|------------------|
| Termux | Android | [sing-box][termux] | `pkg add sing-box` | :material-check: |
=== ":material-freebsd: FreeBSD"
| 类型 | 平台 | 链接 | 命令 |
|------------|---------|------------------------|--------------------------------------------------------------------------------------------|
| FreshPorts | FreeBSD | `pkg install sing-box` | [![FreeBSD port](https://repology.org/badge/version-for-repo/freebsd/sing-box.svg)][ports] |
| 类型 | 平台 | 链接 | 命令 | 活跃维护 |
|------------|---------|-------------------|------------------------|------------------|
| FreshPorts | FreeBSD | [sing-box][ports] | `pkg install sing-box` | :material-alert: |
## :material-book-multiple: 服务管理

View File

@@ -2,6 +2,5 @@
name=sing-box
baseurl=https://rpm.sagernet.org/
enabled=1
repo_gpgcheck=1
gpgcheck=1
gpgkey=https://sing-box.app/gpg.key
gpgkey=https://deb.sagernet.org/gpg.key

View File

@@ -1,38 +0,0 @@
---
icon: material/book-lock-open
---
# TunnelVision
TunnelVision is an attack that uses DHCP option 121 to set higher priority routes
so that traffic does not go through the VPN.
Reference: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-3661
## Status
### Android
Android does not handle DHCP option 121 and is not affected.
### Apple platforms
Update [sing-box graphical client](/clients/apple/#download) to `1.9.0-rc.16` or newer,
then enable `includeAllNetworks` in `Settings``Packet Tunnel` and you will be unaffected.
Note: when `includeAllNetworks` is enabled, the default TUN stack is changed to `gvisor`,
and the `system` and `mixed` stacks are not available.
### Linux
Update sing-box to `1.9.0-rc.16` or newer, rules generated by `auto-route` are unaffected.
### Windows
No solution yet.
## Workarounds
* Don't connect to untrusted networks
* Relay untrusted network through another device
* Just ignore it

View File

@@ -0,0 +1,208 @@
---
icon: material/alpha-t-box
---
# TUIC
A recently popular Chinese-made simple protocol based on QUIC, the selling point is the BBR congestion control algorithm.
!!! warning
Even though GFW rarely blocks UDP-based proxies, such protocols actually have far more characteristics than TCP based proxies.
| Specification | Binary Characteristics | Active Detect Hiddenness |
|-----------------------------------------------------------|------------------------|--------------------------|
| [GitHub](https://github.com/EAimTY/tuic/blob/dev/SPEC.md) | :material-alert: | :material-check: |
## Password Generator
| Generated UUID | Generated Password | Action |
|------------------------|----------------------------|-----------------------------------------------------------------|
| <code id="uuid"><code> | <code id="password"><code> | <button class="md-button" onclick="generate()">Refresh</button> |
<script>
function generateUUID() {
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
let r = Math.random() * 16 | 0,
v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
document.getElementById("uuid").textContent = uuid;
}
function generatePassword() {
const array = new Uint8Array(16);
window.crypto.getRandomValues(array);
document.getElementById("password").textContent = btoa(String.fromCharCode.apply(null, array));
}
function generate() {
generateUUID();
generatePassword();
}
generate();
</script>
## :material-server: Server Example
=== ":material-harddisk: With local certificate"
```json
{
"inbounds": [
{
"type": "tuic",
"listen": "::",
"listen_port": 8080,
"users": [
{
"name": "sekai",
"uuid": "<uuid>",
"password": "<password>"
}
],
"congestion_control": "bbr",
"tls": {
"enabled": true,
"server_name": "example.org",
"key_path": "/path/to/key.pem",
"certificate_path": "/path/to/certificate.pem"
}
}
]
}
```
=== ":material-auto-fix: With ACME"
```json
{
"inbounds": [
{
"type": "tuic",
"listen": "::",
"listen_port": 8080,
"users": [
{
"name": "sekai",
"uuid": "<uuid>",
"password": "<password>"
}
],
"congestion_control": "bbr",
"tls": {
"enabled": true,
"server_name": "example.org",
"acme": {
"domain": "example.org",
"email": "admin@example.org"
}
}
}
]
}
```
=== ":material-cloud: With ACME and Cloudflare API"
```json
{
"inbounds": [
{
"type": "tuic",
"listen": "::",
"listen_port": 8080,
"users": [
{
"name": "sekai",
"uuid": "<uuid>",
"password": "<password>"
}
],
"congestion_control": "bbr",
"tls": {
"enabled": true,
"server_name": "example.org",
"acme": {
"domain": "example.org",
"email": "admin@example.org",
"dns01_challenge": {
"provider": "cloudflare",
"api_token": "my_token"
}
}
}
}
]
}
```
## :material-cellphone-link: Client Example
=== ":material-web-check: With valid certificate"
```json
{
"outbounds": [
{
"type": "tuic",
"server": "127.0.0.1",
"server_port": 8080,
"uuid": "<uuid>",
"password": "<password>",
"congestion_control": "bbr",
"tls": {
"enabled": true,
"server_name": "example.org"
}
}
]
}
```
=== ":material-check: With self-sign certificate"
!!! info "Tip"
Use `sing-box merge` command to merge configuration and certificate into one file.
```json
{
"outbounds": [
{
"type": "tuic",
"server": "127.0.0.1",
"server_port": 8080,
"uuid": "<uuid>",
"password": "<password>",
"congestion_control": "bbr",
"tls": {
"enabled": true,
"server_name": "example.org",
"certificate_path": "/path/to/certificate.pem"
}
}
]
}
```
=== ":material-alert: Ignore certificate verification"
```json
{
"outbounds": [
{
"type": "tuic",
"server": "127.0.0.1",
"server_port": 8080,
"uuid": "<uuid>",
"password": "<password>",
"congestion_control": "bbr",
"tls": {
"enabled": true,
"server_name": "example.org",
"insecure": true
}
}
]
}
```

View File

@@ -380,8 +380,7 @@ flowchart TB
"mode": "and",
"rules": [
{
"rule_set": "geosite-geolocation-!cn",
"invert": true
"rule_set": "geosite-geolocation-!cn"
},
{
"rule_set": "geoip-cn"
@@ -463,15 +462,14 @@ flowchart TB
"mode": "and",
"rules": [
{
"rule_set": "geosite-geolocation-!cn",
"invert": true
"rule_set": "geosite-geolocation-!cn"
},
{
"rule_set": "geoip-cn"
}
],
"server": "google",
"client_subnet": "114.114.114.114/24" // Any China client IP address
"client_subnet": "114.114.114.114" // Any China client IP address
}
]
},

View File

@@ -4,6 +4,10 @@ icon: material/arrange-bring-forward
## 1.9.0
!!! warning "Unstable"
This version is still under development, and the following migration guide may be changed in the future.
### `domain_suffix` behavior update
For historical reasons, sing-box's `domain_suffix` rule matches literal prefixes instead of the same as other projects.

View File

@@ -4,6 +4,10 @@ icon: material/arrange-bring-forward
## 1.9.0
!!! warning "不稳定的"
该版本仍在开发中,迁移指南可能将在未来更改。
### `domain_suffix` 行为更新
由于历史原因sing-box 的 `domain_suffix` 规则匹配字面前缀,而不与其他项目相同。

View File

@@ -5,7 +5,8 @@ icon: material/forum
# Support
| Channel | Link |
| :---------------------------- | :------------------------------------------ |
|:------------------------------|:--------------------------------------------|
| Community | https://community.sagernet.org |
| GitHub Issues | https://github.com/SagerNet/sing-box/issues |
| Telegram notification channel | https://t.me/yapnc |
| Telegram user group | https://t.me/yapug |

View File

@@ -4,10 +4,11 @@ icon: material/forum
# 支持
| 通道 | 链接 |
| :---------------- | :------------------------------------------ |
| GitHub Issues | https://github.com/SagerNet/sing-box/issues |
| 通道 | 链接 |
|:--------------|:--------------------------------------------|
| 社区 | https://community.sagernet.org |
| GitHub Issues | https://github.com/SagerNet/sing-box/issues |
| Telegram 通知频道 | https://t.me/yapnc |
| Telegram 用户组 | https://t.me/yapug |
| 邮件 | contact@sagernet.org |
| Telegram 用户组 | https://t.me/yapug |
| 邮件 | contact@sagernet.org |

View File

@@ -57,7 +57,6 @@ type CacheFile struct {
type saveRDRCCacheKey struct {
TransportName string
QuestionName string
QType uint16
}
func New(ctx context.Context, options option.CacheFileOptions) *CacheFile {

View File

@@ -7,7 +7,6 @@ import (
"github.com/sagernet/bbolt"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
)
@@ -59,13 +58,12 @@ func (c *CacheFile) FakeIPSaveMetadata(metadata *adapter.FakeIPMetadata) error {
}
func (c *CacheFile) FakeIPSaveMetadataAsync(metadata *adapter.FakeIPMetadata) {
if c.saveMetadataTimer == nil {
c.saveMetadataTimer = time.AfterFunc(C.FakeIPMetadataSaveInterval, func() {
_ = c.FakeIPSaveMetadata(metadata)
})
} else {
c.saveMetadataTimer.Reset(C.FakeIPMetadataSaveInterval)
if timer := c.saveMetadataTimer; timer != nil {
timer.Stop()
}
c.saveMetadataTimer = time.AfterFunc(10*time.Second, func() {
_ = c.FakeIPSaveMetadata(metadata)
})
}
func (c *CacheFile) FakeIPStore(address netip.Addr, domain string) error {
@@ -74,7 +72,6 @@ func (c *CacheFile) FakeIPStore(address netip.Addr, domain string) error {
if err != nil {
return err
}
oldDomain := bucket.Get(address.AsSlice())
err = bucket.Put(address.AsSlice(), []byte(domain))
if err != nil {
return err
@@ -87,24 +84,12 @@ func (c *CacheFile) FakeIPStore(address netip.Addr, domain string) error {
if err != nil {
return err
}
if oldDomain != nil {
if err := bucket.Delete(oldDomain); err != nil {
return err
}
}
return bucket.Put([]byte(domain), address.AsSlice())
})
}
func (c *CacheFile) FakeIPStoreAsync(address netip.Addr, domain string, logger logger.Logger) {
c.saveFakeIPAccess.Lock()
if oldDomain, loaded := c.saveDomain[address]; loaded {
if address.Is4() {
delete(c.saveAddress4, oldDomain)
} else {
delete(c.saveAddress6, oldDomain)
}
}
c.saveDomain[address] = domain
if address.Is4() {
c.saveAddress4[domain] = address

View File

@@ -9,7 +9,7 @@ import (
"github.com/sagernet/sing/common/logger"
)
var bucketRDRC = []byte("rdrc2")
var bucketRDRC = []byte("rdrc")
func (c *CacheFile) StoreRDRC() bool {
return c.storeRDRC
@@ -19,17 +19,13 @@ func (c *CacheFile) RDRCTimeout() time.Duration {
return c.rdrcTimeout
}
func (c *CacheFile) LoadRDRC(transportName string, qName string, qType uint16) (rejected bool) {
func (c *CacheFile) LoadRDRC(transportName string, qName string) (rejected bool) {
c.saveRDRCAccess.RLock()
rejected, cached := c.saveRDRC[saveRDRCCacheKey{transportName, qName, qType}]
rejected, cached := c.saveRDRC[saveRDRCCacheKey{transportName, qName}]
c.saveRDRCAccess.RUnlock()
if cached {
return
}
key := buf.Get(2 + len(qName))
binary.BigEndian.PutUint16(key, qType)
copy(key[2:], qName)
defer buf.Put(key)
var deleteCache bool
err := c.DB.View(func(tx *bbolt.Tx) error {
bucket := c.bucket(tx, bucketRDRC)
@@ -40,7 +36,7 @@ func (c *CacheFile) LoadRDRC(transportName string, qName string, qType uint16) (
if bucket == nil {
return nil
}
content := bucket.Get(key)
content := bucket.Get([]byte(qName))
if content == nil {
return nil
}
@@ -65,13 +61,13 @@ func (c *CacheFile) LoadRDRC(transportName string, qName string, qType uint16) (
if bucket == nil {
return nil
}
return bucket.Delete(key)
return bucket.Delete([]byte(qName))
})
}
return
}
func (c *CacheFile) SaveRDRC(transportName string, qName string, qType uint16) error {
func (c *CacheFile) SaveRDRC(transportName string, qName string) error {
return c.DB.Batch(func(tx *bbolt.Tx) error {
bucket, err := c.createBucket(tx, bucketRDRC)
if err != nil {
@@ -81,24 +77,20 @@ func (c *CacheFile) SaveRDRC(transportName string, qName string, qType uint16) e
if err != nil {
return err
}
key := buf.Get(2 + len(qName))
binary.BigEndian.PutUint16(key, qType)
copy(key[2:], qName)
defer buf.Put(key)
expiresAt := buf.Get(8)
defer buf.Put(expiresAt)
binary.BigEndian.PutUint64(expiresAt, uint64(time.Now().Add(c.rdrcTimeout).Unix()))
return bucket.Put(key, expiresAt)
return bucket.Put([]byte(qName), expiresAt)
})
}
func (c *CacheFile) SaveRDRCAsync(transportName string, qName string, qType uint16, logger logger.Logger) {
saveKey := saveRDRCCacheKey{transportName, qName, qType}
func (c *CacheFile) SaveRDRCAsync(transportName string, qName string, logger logger.Logger) {
saveKey := saveRDRCCacheKey{transportName, qName}
c.saveRDRCAccess.Lock()
c.saveRDRC[saveKey] = true
c.saveRDRCAccess.Unlock()
go func() {
err := c.SaveRDRC(transportName, qName, qType)
err := c.SaveRDRC(transportName, qName)
if err != nil {
logger.Warn("save RDRC: ", err)
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/sagernet/sing-box"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/process"
"github.com/sagernet/sing-box/experimental/libbox/platform"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/control"
@@ -74,7 +75,7 @@ func (s *platformInterfaceStub) UsePlatformInterfaceGetter() bool {
return true
}
func (s *platformInterfaceStub) Interfaces() ([]control.Interface, error) {
func (s *platformInterfaceStub) Interfaces() ([]platform.NetworkInterface, error) {
return nil, os.ErrInvalid
}
@@ -82,10 +83,6 @@ func (s *platformInterfaceStub) UnderNetworkExtension() bool {
return false
}
func (s *platformInterfaceStub) IncludeAllNetworks() bool {
return false
}
func (s *platformInterfaceStub) ClearDNSCache() {
}
@@ -140,6 +137,7 @@ func FormatConfig(configContent string) (string, error) {
return "", err
}
var buffer bytes.Buffer
json.NewEncoder(&buffer)
encoder := json.NewEncoder(&buffer)
encoder.SetIndent("", " ")
err = encoder.Encode(options)

View File

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

View File

@@ -2,6 +2,7 @@ package platform
import (
"context"
"net/netip"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/process"
@@ -19,10 +20,16 @@ type Interface interface {
UsePlatformDefaultInterfaceMonitor() bool
CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor
UsePlatformInterfaceGetter() bool
Interfaces() ([]control.Interface, error)
Interfaces() ([]NetworkInterface, error)
UnderNetworkExtension() bool
IncludeAllNetworks() bool
ClearDNSCache()
ReadWIFIState() adapter.WIFIState
process.Searcher
}
type NetworkInterface struct {
Index int
MTU int
Name string
Addresses []netip.Prefix
}

View File

@@ -81,7 +81,7 @@ func (s *BoxService) Close() error {
select {
case <-done:
return
case <-time.After(C.FatalStopTimeout):
case <-time.After(C.DefaultStopFatalTimeout):
os.Exit(1)
}
}()
@@ -192,14 +192,14 @@ func (w *platformInterfaceWrapper) UsePlatformInterfaceGetter() bool {
return w.iif.UsePlatformInterfaceGetter()
}
func (w *platformInterfaceWrapper) Interfaces() ([]control.Interface, error) {
func (w *platformInterfaceWrapper) Interfaces() ([]platform.NetworkInterface, error) {
interfaceIterator, err := w.iif.GetInterfaces()
if err != nil {
return nil, err
}
var interfaces []control.Interface
var interfaces []platform.NetworkInterface
for _, netInterface := range iteratorToArray[*NetworkInterface](interfaceIterator) {
interfaces = append(interfaces, control.Interface{
interfaces = append(interfaces, platform.NetworkInterface{
Index: int(netInterface.Index),
MTU: int(netInterface.MTU),
Name: netInterface.Name,
@@ -213,10 +213,6 @@ func (w *platformInterfaceWrapper) UnderNetworkExtension() bool {
return w.iif.UnderNetworkExtension()
}
func (w *platformInterfaceWrapper) IncludeAllNetworks() bool {
return w.iif.IncludeAllNetworks()
}
func (w *platformInterfaceWrapper) ClearDNSCache() {
w.iif.ClearDNSCache()
}

View File

@@ -16,18 +16,25 @@ func (s *BoxService) Pause() {
if s.pauseTimer != nil {
s.pauseTimer.Stop()
}
s.pauseTimer = time.AfterFunc(3*time.Second, s.ResetNetwork)
s.pauseTimer = time.AfterFunc(time.Minute, s.pause)
}
func (s *BoxService) pause() {
s.pauseAccess.Lock()
defer s.pauseAccess.Unlock()
s.pauseManager.DevicePause()
_ = s.instance.Router().ResetNetwork()
s.pauseTimer = nil
}
func (s *BoxService) Wake() {
_ = s.instance.Router().ResetNetwork()
s.pauseAccess.Lock()
defer s.pauseAccess.Unlock()
if s.pauseTimer != nil {
s.pauseTimer.Stop()
s.pauseTimer = nil
return
}
s.pauseTimer = time.AfterFunc(3*time.Minute, s.ResetNetwork)
}
func (s *BoxService) ResetNetwork() {
_ = s.instance.Router().ResetNetwork()
s.pauseManager.DeviceWake()
}

48
go.mod
View File

@@ -11,30 +11,30 @@ require (
github.com/go-chi/chi/v5 v5.0.12
github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.3
github.com/gofrs/uuid/v5 v5.2.0
github.com/gofrs/uuid/v5 v5.0.0
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2
github.com/libdns/alidns v1.0.3
github.com/libdns/cloudflare v0.1.1
github.com/libdns/cloudflare v0.1.0
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/mholt/acmez v1.2.0
github.com/miekg/dns v1.1.59
github.com/miekg/dns v1.1.58
github.com/ooni/go-libtor v1.1.8
github.com/oschwald/maxminddb-golang v1.12.0
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
github.com/sagernet/gomobile v0.1.3
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f
github.com/sagernet/quic-go v0.46.0-beta.4
github.com/sagernet/gvisor v0.0.0-20240315080113-799fb6b6d311
github.com/sagernet/quic-go v0.42.0-beta.2
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.4.2
github.com/sagernet/sing-dns v0.2.3
github.com/sagernet/sing v0.4.0-beta.3
github.com/sagernet/sing-dns v0.2.0-beta.16
github.com/sagernet/sing-mux v0.2.0
github.com/sagernet/sing-quic v0.2.2
github.com/sagernet/sing-shadowsocks v0.2.7
github.com/sagernet/sing-quic v0.1.9-beta.3
github.com/sagernet/sing-shadowsocks v0.2.6
github.com/sagernet/sing-shadowsocks2 v0.2.0
github.com/sagernet/sing-shadowtls v0.1.4
github.com/sagernet/sing-tun v0.3.2
github.com/sagernet/sing-vmess v0.1.12
github.com/sagernet/sing-tun v0.2.5-beta.2
github.com/sagernet/sing-vmess v0.1.8
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6
github.com/sagernet/utls v1.5.4
@@ -44,11 +44,11 @@ require (
github.com/stretchr/testify v1.9.0
go.uber.org/zap v1.27.0
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/crypto v0.23.0
golang.org/x/net v0.25.0
golang.org/x/sys v0.21.0
golang.org/x/crypto v0.21.0
golang.org/x/net v0.22.0
golang.org/x/sys v0.18.0
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
google.golang.org/grpc v1.63.2
google.golang.org/grpc v1.62.1
google.golang.org/protobuf v1.33.0
howett.net/plist v1.0.1
)
@@ -64,6 +64,7 @@ require (
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
@@ -71,27 +72,26 @@ require (
github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/libdns/libdns v0.2.2 // indirect
github.com/libdns/libdns v0.2.1 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/onsi/ginkgo/v2 v2.9.7 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba // indirect
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
golang.org/x/tools v0.19.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/blake3 v1.3.0 // indirect
lukechampine.com/blake3 v1.2.1 // indirect
)

105
go.sum
View File

@@ -34,11 +34,14 @@ github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gofrs/uuid/v5 v5.2.0 h1:qw1GMx6/y8vhVsx626ImfKMuS5CvJmhIKKtuyvfajMM=
github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
@@ -62,17 +65,17 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ=
github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE=
github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054=
github.com/libdns/cloudflare v0.1.1/go.mod h1:9VK91idpOjg6v7/WbjkEW49bSCxj00ALesIFDhJ8PBU=
github.com/libdns/cloudflare v0.1.0 h1:93WkJaGaiXCe353LHEP36kAWCUw0YjFqwhkBkU2/iic=
github.com/libdns/cloudflare v0.1.0/go.mod h1:a44IP6J1YH6nvcNl1PverfJviADgXUnsozR3a7vBKN8=
github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss=
@@ -97,33 +100,33 @@ github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQ
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k=
github.com/sagernet/gomobile v0.1.3 h1:ohjIb1Ou2+1558PnZour3od69suSuvkdSVOlO1tC4B8=
github.com/sagernet/gomobile v0.1.3/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f h1:NkhuupzH5ch7b/Y/6ZHJWrnNLoiNnSJaow6DPb8VW2I=
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f/go.mod h1:KXmw+ouSJNOsuRpg4wgwwCQuunrGz4yoAqQjsLjc6N0=
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba h1:EY5AS7CCtfmARNv2zXUOrsEMPFDGYxaw65JzA2p51Vk=
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.46.0-beta.4 h1:k9f7VSKaM47AY6MPND0Qf1KRN7HwimPg9zdOFTXTiCk=
github.com/sagernet/quic-go v0.46.0-beta.4/go.mod h1:zJmVdJUNqEDXfubf4KtIOUHHerggjBduiGRLNzJspcM=
github.com/sagernet/gvisor v0.0.0-20240315080113-799fb6b6d311 h1:eUQ6kJZXK77xYZeeNrBb/7JMv0S0Wkk7EpmKUb3fsfc=
github.com/sagernet/gvisor v0.0.0-20240315080113-799fb6b6d311/go.mod h1:mDrXZSv401qiaFiiIUC59Zp4VG5f4nqXFqDmp5o3hYI=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.42.0-beta.2 h1:E6vTGhveFE9Tnhu5rrt3M5fnENuqBJOv8gPcFPz0cSM=
github.com/sagernet/quic-go v0.42.0-beta.2/go.mod h1:lf8OYop+fMxIlrfM/ZHpENt/7ZD4JaVNqMhOlq2QMwg=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
github.com/sagernet/sing v0.4.2 h1:jzGNJdZVRI0xlAfFugsIQUPvyB9SuWvbJK7zQCXc4QM=
github.com/sagernet/sing v0.4.2/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
github.com/sagernet/sing-dns v0.2.3 h1:YzeBUn2tR38F7HtvGEQ0kLRLmZWMEgi/+7wqa4Twb1k=
github.com/sagernet/sing-dns v0.2.3/go.mod h1:BJpJv6XLnrUbSyIntOT6DG9FW0f4fETmPAHvNjOprLg=
github.com/sagernet/sing v0.4.0-beta.3 h1:TYA4DJtxs0dWDG6ahjU/Fh+eg6vintiqGjXN4h9BbiA=
github.com/sagernet/sing v0.4.0-beta.3/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI=
github.com/sagernet/sing-dns v0.2.0-beta.16 h1:bzd4B8eHD7/WO3HrYknvgE8A56/R3n5oXBjNF97iPzQ=
github.com/sagernet/sing-dns v0.2.0-beta.16/go.mod h1:XU6Vqr6aHcMz/34Fcv8jmXpRCEuShzW+B7Qg1Xe1nxY=
github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=
github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ=
github.com/sagernet/sing-quic v0.2.2 h1:Ryp02zMhHh/ZDrG7MdLsmhuBU8+BEpOdJonFQiqIopo=
github.com/sagernet/sing-quic v0.2.2/go.mod h1:YLV1dUDv8Eyp/8e55O/EvfsrwxOgEDVgDCIoPqmDREE=
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
github.com/sagernet/sing-quic v0.1.9-beta.3 h1:turuRtN6xfAxWMseZEzwNOA6EO2ZAW82BrfPJUrtN3Q=
github.com/sagernet/sing-quic v0.1.9-beta.3/go.mod h1:oXe0g8T7edh2Xxl0QcpTO4Tret8M478LpMcr3CPuqWE=
github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s=
github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM=
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
github.com/sagernet/sing-tun v0.3.2 h1:z0bLUT/YXH9RrJS9DsIpB0Bb9afl2hVJOmHd0zA3HJY=
github.com/sagernet/sing-tun v0.3.2/go.mod h1:DxLIyhjWU/HwGYoX0vNGg2c5QgTQIakphU1MuERR5tQ=
github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg=
github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I=
github.com/sagernet/sing-tun v0.2.5-beta.2 h1:mOWW80jYEup8LIFG3VuWASWgiRzyusuX5kH+Lbl+V7s=
github.com/sagernet/sing-tun v0.2.5-beta.2/go.mod h1:E+KwQKzYkdGEhfIxjmoaB1ZkADaxeXUNzx6GRDRKOfE=
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 h1:z3SJQhVyU63FT26Wn/UByW6b7q8QKB0ZkPqsyqcz2PI=
@@ -163,18 +166,17 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 h1:6R2FC06FonbXQ8pK11/PDFY6N6LWlf9KlzibaCapmqc=
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -184,25 +186,28 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -214,5 +219,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=

View File

@@ -5,9 +5,7 @@ import (
"net"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
@@ -18,9 +16,6 @@ func (a *myInboundAdapter) ListenTCP() (net.Listener, error) {
bindAddr := M.SocksaddrFrom(a.listenOptions.Listen.Build(), a.listenOptions.ListenPort)
var tcpListener net.Listener
var listenConfig net.ListenConfig
// TODO: Add an option to customize the keep alive period
listenConfig.KeepAlive = C.TCPKeepAliveInitial
listenConfig.Control = control.Append(listenConfig.Control, control.SetKeepAlivePeriod(C.TCPKeepAliveInitial, C.TCPKeepAliveInterval))
if a.listenOptions.TCPMultiPath {
if !go121Available {
return nil, E.New("MultiPath TCP requires go1.21, please recompile your binary.")

View File

@@ -153,7 +153,7 @@ func (t *Tun) Start() error {
tunInterface tun.Tun
err error
)
monitor := taskmonitor.New(t.logger, C.StartTimeout)
monitor := taskmonitor.New(t.logger, C.DefaultStartTimeout)
monitor.Start("open tun interface")
if t.platformInterface != nil {
tunInterface, err = t.platformInterface.OpenTun(&t.tunOptions, t.platformOptions)
@@ -166,15 +166,7 @@ func (t *Tun) Start() error {
}
t.logger.Trace("creating stack")
t.tunIf = tunInterface
var (
forwarderBindInterface bool
includeAllNetworks bool
)
if t.platformInterface != nil {
forwarderBindInterface = true
includeAllNetworks = t.platformInterface.IncludeAllNetworks()
}
tunStack, err := tun.NewStack(t.stack, tun.StackOptions{
t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{
Context: t.ctx,
Tun: tunInterface,
TunOptions: t.tunOptions,
@@ -182,17 +174,15 @@ func (t *Tun) Start() error {
UDPTimeout: t.udpTimeout,
Handler: t,
Logger: t.logger,
ForwarderBindInterface: forwarderBindInterface,
ForwarderBindInterface: t.platformInterface != nil,
InterfaceFinder: t.router.InterfaceFinder(),
IncludeAllNetworks: includeAllNetworks,
})
if err != nil {
return err
}
monitor.Start("initiating tun stack")
err = tunStack.Start()
err = t.tunStack.Start()
monitor.Finish()
t.tunStack = tunStack
if err != nil {
return err
}

View File

@@ -13,9 +13,9 @@ import (
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2ray"
"github.com/sagernet/sing-box/transport/vless"
"github.com/sagernet/sing-vmess"
"github.com/sagernet/sing-vmess/packetaddr"
"github.com/sagernet/sing-vmess/vless"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth"
E "github.com/sagernet/sing/common/exceptions"

View File

@@ -53,9 +53,7 @@ func NewDefaultFactory(
if platformWriter != nil {
factory.platformFormatter.DisableColors = platformWriter.DisableColors()
}
if needObservable {
factory.observer = observable.NewObserver[Entry](factory.subscriber, 64)
}
factory.observer = observable.NewObserver[Entry](factory.subscriber, 64)
return factory
}
@@ -74,7 +72,7 @@ func (f *defaultFactory) Start() error {
func (f *defaultFactory) Close() error {
return common.Close(
common.PtrOrNil(f.file),
f.subscriber,
f.observer,
)
}

View File

@@ -66,9 +66,8 @@ nav:
- Proxy Protocol:
- Shadowsocks: manual/proxy-protocol/shadowsocks.md
- Trojan: manual/proxy-protocol/trojan.md
- TUIC: manual/proxy-protocol/tuic.md
- Hysteria 2: manual/proxy-protocol/hysteria2.md
- Misc:
- TunnelVision: manual/misc/tunnelvision.md
- Configuration:
- configuration/index.md
- Log:

112
ntp/service.go Normal file
View File

@@ -0,0 +1,112 @@
package ntp
import (
"context"
"os"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/common/settings"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/ntp"
)
var _ ntp.TimeService = (*Service)(nil)
type Service struct {
ctx context.Context
cancel common.ContextCancelCauseFunc
server M.Socksaddr
writeToSystem bool
dialer N.Dialer
logger logger.Logger
ticker *time.Ticker
clockOffset time.Duration
}
func NewService(ctx context.Context, router adapter.Router, logger logger.Logger, options option.NTPOptions) (*Service, error) {
ctx, cancel := common.ContextWithCancelCause(ctx)
server := M.ParseSocksaddrHostPort(options.Server, options.ServerPort)
if server.Port == 0 {
server.Port = 123
}
var interval time.Duration
if options.Interval > 0 {
interval = time.Duration(options.Interval)
} else {
interval = 30 * time.Minute
}
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
return &Service{
ctx: ctx,
cancel: cancel,
server: server,
writeToSystem: options.WriteToSystem,
dialer: outboundDialer,
logger: logger,
ticker: time.NewTicker(interval),
}, nil
}
func (s *Service) Start() error {
err := s.update()
if err != nil {
return E.Cause(err, "initialize time")
}
s.logger.Info("updated time: ", s.TimeFunc()().Local().Format(C.TimeLayout))
go s.loopUpdate()
return nil
}
func (s *Service) Close() error {
s.ticker.Stop()
s.cancel(os.ErrClosed)
return nil
}
func (s *Service) TimeFunc() func() time.Time {
return func() time.Time {
return time.Now().Add(s.clockOffset)
}
}
func (s *Service) loopUpdate() {
for {
select {
case <-s.ctx.Done():
return
case <-s.ticker.C:
}
err := s.update()
if err == nil {
s.logger.Debug("updated time: ", s.TimeFunc()().Local().Format(C.TimeLayout))
} else {
s.logger.Warn("update time: ", err)
}
}
}
func (s *Service) update() error {
response, err := ntp.Exchange(s.ctx, s.dialer, s.server)
if err != nil {
return err
}
s.clockOffset = response.ClockOffset
if s.writeToSystem {
writeErr := settings.SetSystemTime(s.TimeFunc()())
if writeErr != nil {
s.logger.Warn("write time to system: ", writeErr)
}
}
return nil
}

View File

@@ -19,7 +19,7 @@ type DNSServerOptions struct {
AddressFallbackDelay Duration `json:"address_fallback_delay,omitempty"`
Strategy DomainStrategy `json:"strategy,omitempty"`
Detour string `json:"detour,omitempty"`
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
}
type DNSClientOptions struct {
@@ -27,7 +27,7 @@ type DNSClientOptions struct {
DisableCache bool `json:"disable_cache,omitempty"`
DisableExpire bool `json:"disable_expire,omitempty"`
IndependentCache bool `json:"independent_cache,omitempty"`
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
}
type DNSFakeIPOptions struct {

View File

@@ -2,8 +2,9 @@ package option
type NTPOptions struct {
Enabled bool `json:"enabled,omitempty"`
Server string `json:"server,omitempty"`
ServerPort uint16 `json:"server_port,omitempty"`
Interval Duration `json:"interval,omitempty"`
WriteToSystem bool `json:"write_to_system,omitempty"`
ServerOptions
DialerOptions
}

View File

@@ -101,7 +101,7 @@ type DefaultDNSRule struct {
Server string `json:"server,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"`
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
}
func (r DefaultDNSRule) IsValid() bool {
@@ -115,13 +115,13 @@ func (r DefaultDNSRule) IsValid() bool {
}
type LogicalDNSRule struct {
Mode string `json:"mode"`
Rules []DNSRule `json:"rules,omitempty"`
Invert bool `json:"invert,omitempty"`
Server string `json:"server,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"`
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
Mode string `json:"mode"`
Rules []DNSRule `json:"rules,omitempty"`
Invert bool `json:"invert,omitempty"`
Server string `json:"server,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"`
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
}
func (r LogicalDNSRule) IsValid() bool {

View File

@@ -51,40 +51,6 @@ func (a *ListenAddress) Build() netip.Addr {
return (netip.Addr)(*a)
}
type AddrPrefix netip.Prefix
func (a AddrPrefix) MarshalJSON() ([]byte, error) {
prefix := netip.Prefix(a)
if prefix.Bits() == prefix.Addr().BitLen() {
return json.Marshal(prefix.Addr().String())
} else {
return json.Marshal(prefix.String())
}
}
func (a *AddrPrefix) UnmarshalJSON(content []byte) error {
var value string
err := json.Unmarshal(content, &value)
if err != nil {
return err
}
prefix, prefixErr := netip.ParsePrefix(value)
if prefixErr == nil {
*a = AddrPrefix(prefix)
return nil
}
addr, addrErr := netip.ParseAddr(value)
if addrErr == nil {
*a = AddrPrefix(netip.PrefixFrom(addr, addr.BitLen()))
return nil
}
return prefixErr
}
func (a AddrPrefix) Build() netip.Prefix {
return netip.Prefix(a)
}
type NetworkList string
func (v *NetworkList) UnmarshalJSON(content []byte) error {

View File

@@ -51,7 +51,7 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti
domainStrategy: dns.DomainStrategy(options.DomainStrategy),
fallbackDelay: time.Duration(options.FallbackDelay),
dialer: outboundDialer,
loopBack: newLoopBackDetector(router),
loopBack: newLoopBackDetector(),
}
if options.ProxyProtocol != 0 {
return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
@@ -148,7 +148,7 @@ func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net
if err != nil {
return nil, err
}
conn = h.loopBack.NewPacketConn(bufio.NewPacketConn(conn), destination)
conn = h.loopBack.NewPacketConn(bufio.NewPacketConn(conn))
if originDestination != destination {
conn = bufio.NewNATPacketConn(bufio.NewPacketConn(conn), destination, originDestination)
}
@@ -156,14 +156,14 @@ func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net
}
func (h *Direct) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
if h.loopBack.CheckConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) {
if h.loopBack.CheckConn(metadata.Source.AddrPort()) {
return E.New("reject loopback connection to ", metadata.Destination)
}
return NewConnection(ctx, h, conn, metadata)
}
func (h *Direct) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
if h.loopBack.CheckPacketConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) {
if h.loopBack.CheckPacketConn(metadata.Source.AddrPort()) {
return E.New("reject loopback packet connection to ", metadata.Destination)
}
return NewPacketConnection(ctx, h, conn, metadata)

View File

@@ -5,95 +5,63 @@ import (
"net/netip"
"sync"
"github.com/sagernet/sing-box/adapter"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
type loopBackDetector struct {
router adapter.Router
connAccess sync.RWMutex
packetConnAccess sync.RWMutex
connMap map[netip.AddrPort]netip.AddrPort
packetConnMap map[uint16]uint16
connMap map[netip.AddrPort]bool
packetConnMap map[netip.AddrPort]bool
}
func newLoopBackDetector(router adapter.Router) *loopBackDetector {
func newLoopBackDetector() *loopBackDetector {
return &loopBackDetector{
router: router,
connMap: make(map[netip.AddrPort]netip.AddrPort),
packetConnMap: make(map[uint16]uint16),
connMap: make(map[netip.AddrPort]bool),
packetConnMap: make(map[netip.AddrPort]bool),
}
}
func (l *loopBackDetector) NewConn(conn net.Conn) net.Conn {
source := M.AddrPortFromNet(conn.LocalAddr())
if !source.IsValid() {
connAddr := M.AddrPortFromNet(conn.LocalAddr())
if !connAddr.IsValid() {
return conn
}
if udpConn, isUDPConn := conn.(abstractUDPConn); isUDPConn {
if !source.Addr().IsLoopback() {
_, err := l.router.InterfaceFinder().InterfaceByAddr(source.Addr())
if err != nil {
return conn
}
}
if !N.IsPublicAddr(source.Addr()) {
return conn
}
l.packetConnAccess.Lock()
l.packetConnMap[source.Port()] = M.AddrPortFromNet(conn.RemoteAddr()).Port()
l.packetConnMap[connAddr] = true
l.packetConnAccess.Unlock()
return &loopBackDetectUDPWrapper{abstractUDPConn: udpConn, detector: l, connPort: source.Port()}
return &loopBackDetectUDPWrapper{abstractUDPConn: udpConn, detector: l, connAddr: connAddr}
} else {
l.connAccess.Lock()
l.connMap[source] = M.AddrPortFromNet(conn.RemoteAddr())
l.connMap[connAddr] = true
l.connAccess.Unlock()
return &loopBackDetectWrapper{Conn: conn, detector: l, connAddr: source}
return &loopBackDetectWrapper{Conn: conn, detector: l, connAddr: connAddr}
}
}
func (l *loopBackDetector) NewPacketConn(conn N.NetPacketConn, destination M.Socksaddr) N.NetPacketConn {
source := M.AddrPortFromNet(conn.LocalAddr())
if !source.IsValid() {
func (l *loopBackDetector) NewPacketConn(conn N.NetPacketConn) N.NetPacketConn {
connAddr := M.AddrPortFromNet(conn.LocalAddr())
if !connAddr.IsValid() {
return conn
}
if !source.Addr().IsLoopback() {
_, err := l.router.InterfaceFinder().InterfaceByAddr(source.Addr())
if err != nil {
return conn
}
}
l.packetConnAccess.Lock()
l.packetConnMap[source.Port()] = destination.AddrPort().Port()
l.packetConnMap[connAddr] = true
l.packetConnAccess.Unlock()
return &loopBackDetectPacketWrapper{NetPacketConn: conn, detector: l, connPort: source.Port()}
return &loopBackDetectPacketWrapper{NetPacketConn: conn, detector: l, connAddr: connAddr}
}
func (l *loopBackDetector) CheckConn(source netip.AddrPort, local netip.AddrPort) bool {
func (l *loopBackDetector) CheckConn(connAddr netip.AddrPort) bool {
l.connAccess.RLock()
defer l.connAccess.RUnlock()
destination, loaded := l.connMap[source]
return loaded && destination != local
return l.connMap[connAddr]
}
func (l *loopBackDetector) CheckPacketConn(source netip.AddrPort, local netip.AddrPort) bool {
if !source.IsValid() {
return false
}
if !source.Addr().IsLoopback() {
_, err := l.router.InterfaceFinder().InterfaceByAddr(source.Addr())
if err != nil {
return false
}
}
if N.IsPublicAddr(source.Addr()) {
return false
}
func (l *loopBackDetector) CheckPacketConn(connAddr netip.AddrPort) bool {
l.packetConnAccess.RLock()
defer l.packetConnAccess.RUnlock()
destinationPort, loaded := l.packetConnMap[source.Port()]
return loaded && destinationPort != local.Port()
return l.packetConnMap[connAddr]
}
type loopBackDetectWrapper struct {
@@ -127,14 +95,14 @@ func (w *loopBackDetectWrapper) Upstream() any {
type loopBackDetectPacketWrapper struct {
N.NetPacketConn
detector *loopBackDetector
connPort uint16
connAddr netip.AddrPort
closeOnce sync.Once
}
func (w *loopBackDetectPacketWrapper) Close() error {
w.closeOnce.Do(func() {
w.detector.packetConnAccess.Lock()
delete(w.detector.packetConnMap, w.connPort)
delete(w.detector.packetConnMap, w.connAddr)
w.detector.packetConnAccess.Unlock()
})
return w.NetPacketConn.Close()
@@ -160,14 +128,14 @@ type abstractUDPConn interface {
type loopBackDetectUDPWrapper struct {
abstractUDPConn
detector *loopBackDetector
connPort uint16
connAddr netip.AddrPort
closeOnce sync.Once
}
func (w *loopBackDetectUDPWrapper) Close() error {
w.closeOnce.Do(func() {
w.detector.packetConnAccess.Lock()
delete(w.detector.packetConnMap, w.connPort)
delete(w.detector.packetConnMap, w.connAddr)
w.detector.packetConnAccess.Unlock()
})
return w.abstractUDPConn.Close()

View File

@@ -130,8 +130,8 @@ func (h *Hysteria) NewPacketConnection(ctx context.Context, conn N.PacketConn, m
return NewPacketConnection(ctx, h, conn, metadata)
}
func (h *Hysteria) InterfaceUpdated() {
h.client.CloseWithError(E.New("network changed"))
func (h *Hysteria) InterfaceUpdated() error {
return h.client.CloseWithError(E.New("network changed"))
}
func (h *Hysteria) Close() error {

View File

@@ -116,8 +116,8 @@ func (h *Hysteria2) NewPacketConnection(ctx context.Context, conn N.PacketConn,
return NewPacketConnection(ctx, h, conn, metadata)
}
func (h *Hysteria2) InterfaceUpdated() {
h.client.CloseWithError(E.New("network changed"))
func (h *Hysteria2) InterfaceUpdated() error {
return h.client.CloseWithError(E.New("network changed"))
}
func (h *Hysteria2) Close() error {

View File

@@ -108,9 +108,6 @@ func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, met
}
func (h *Trojan) InterfaceUpdated() {
if h.transport != nil {
h.transport.Close()
}
if h.multiplexDialer != nil {
h.multiplexDialer.Reset()
}

View File

@@ -287,23 +287,8 @@ func (g *URLTestGroup) Close() error {
func (g *URLTestGroup) Select(network string) (adapter.Outbound, bool) {
var minDelay uint16
var minTime time.Time
var minOutbound adapter.Outbound
switch network {
case N.NetworkTCP:
if g.selectedOutboundTCP != nil {
if history := g.history.LoadURLTestHistory(RealTag(g.selectedOutboundTCP)); history != nil {
minOutbound = g.selectedOutboundTCP
minDelay = history.Delay
}
}
case N.NetworkUDP:
if g.selectedOutboundUDP != nil {
if history := g.history.LoadURLTestHistory(RealTag(g.selectedOutboundUDP)); history != nil {
minOutbound = g.selectedOutboundUDP
minDelay = history.Delay
}
}
}
for _, detour := range g.outbounds {
if !common.Contains(detour.Network(), network) {
continue
@@ -312,8 +297,9 @@ func (g *URLTestGroup) Select(network string) (adapter.Outbound, bool) {
if history == nil {
continue
}
if minDelay == 0 || minDelay > history.Delay+g.tolerance {
if minDelay == 0 || minDelay > history.Delay+g.tolerance || minDelay > history.Delay-g.tolerance && minTime.Before(history.Time) {
minDelay = history.Delay
minTime = history.Time
minOutbound = detour
}
}
@@ -385,9 +371,9 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint
continue
}
b.Go(realTag, func() (any, error) {
testCtx, cancel := context.WithTimeout(g.ctx, C.TCPTimeout)
ctx, cancel := context.WithTimeout(context.Background(), C.TCPTimeout)
defer cancel()
t, err := urltest.URLTest(testCtx, g.link, p)
t, err := urltest.URLTest(ctx, g.link, p)
if err != nil {
g.logger.Debug("outbound ", tag, " unavailable: ", err)
g.history.DeleteURLTestHistory(realTag)

View File

@@ -12,8 +12,8 @@ import (
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2ray"
"github.com/sagernet/sing-box/transport/vless"
"github.com/sagernet/sing-vmess/packetaddr"
"github.com/sagernet/sing-vmess/vless"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
@@ -127,9 +127,6 @@ func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, meta
}
func (h *VLESS) InterfaceUpdated() {
if h.transport != nil {
h.transport.Close()
}
if h.multiplexDialer != nil {
h.multiplexDialer.Reset()
}

View File

@@ -103,9 +103,6 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
}
func (h *VMess) InterfaceUpdated() {
if h.transport != nil {
h.transport.Close()
}
if h.multiplexDialer != nil {
h.multiplexDialer.Reset()
}

View File

@@ -19,7 +19,6 @@ import (
"github.com/sagernet/sing-box/transport/wireguard"
"github.com/sagernet/sing-dns"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
@@ -112,25 +111,6 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
}
func (w *WireGuard) Start() error {
if common.Any(w.peers, func(peer wireguard.PeerConfig) bool {
return !peer.Endpoint.IsValid()
}) {
// wait for all outbounds to be started and continue in PortStart
return nil
}
return w.start()
}
func (w *WireGuard) PostStart() error {
if common.All(w.peers, func(peer wireguard.PeerConfig) bool {
return peer.Endpoint.IsValid()
}) {
return nil
}
return w.start()
}
func (w *WireGuard) start() error {
err := wireguard.ResolvePeers(w.ctx, w.router, w.peers)
if err != nil {
return err

54
route/interface_finder.go Normal file
View File

@@ -0,0 +1,54 @@
package route
import (
"net"
"github.com/sagernet/sing/common/control"
)
var _ control.InterfaceFinder = (*myInterfaceFinder)(nil)
type myInterfaceFinder struct {
interfaces []net.Interface
}
func (f *myInterfaceFinder) update() error {
ifs, err := net.Interfaces()
if err != nil {
return err
}
f.interfaces = ifs
return nil
}
func (f *myInterfaceFinder) updateInterfaces(interfaces []net.Interface) {
f.interfaces = interfaces
}
func (f *myInterfaceFinder) InterfaceIndexByName(name string) (interfaceIndex int, err error) {
for _, netInterface := range f.interfaces {
if netInterface.Name == name {
return netInterface.Index, nil
}
}
netInterface, err := net.InterfaceByName(name)
if err != nil {
return
}
f.update()
return netInterface.Index, nil
}
func (f *myInterfaceFinder) InterfaceNameByIndex(index int) (interfaceName string, err error) {
for _, netInterface := range f.interfaces {
if netInterface.Index == index {
return netInterface.Name, nil
}
}
netInterface, err := net.InterfaceByIndex(index)
if err != nil {
return
}
f.update()
return netInterface.Name, nil
}

View File

@@ -23,11 +23,12 @@ import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental/libbox/platform"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/ntp"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/outbound"
"github.com/sagernet/sing-box/transport/fakeip"
"github.com/sagernet/sing-dns"
"github.com/sagernet/sing-mux"
mux "github.com/sagernet/sing-mux"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing-vmess"
"github.com/sagernet/sing/common"
@@ -39,7 +40,7 @@ import (
F "github.com/sagernet/sing/common/format"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/ntp"
serviceNTP "github.com/sagernet/sing/common/ntp"
"github.com/sagernet/sing/common/task"
"github.com/sagernet/sing/common/uot"
"github.com/sagernet/sing/common/winpowrprof"
@@ -69,6 +70,7 @@ type Router struct {
geositeCache map[string]adapter.Rule
needFindProcess bool
dnsClient *dns.Client
dnsIndependentCache bool
defaultDomainStrategy dns.DomainStrategy
dnsRules []adapter.DNSRule
ruleSets []adapter.RuleSet
@@ -79,7 +81,7 @@ type Router struct {
transportDomainStrategy map[dns.Transport]dns.DomainStrategy
dnsReverseMapping *DNSReverseMapping
fakeIPStore adapter.FakeIPStore
interfaceFinder *control.DefaultInterfaceFinder
interfaceFinder myInterfaceFinder
autoDetectInterface bool
defaultInterface string
defaultMark int
@@ -122,16 +124,16 @@ func NewRouter(
geositeOptions: common.PtrValueOrDefault(options.Geosite),
geositeCache: make(map[string]adapter.Rule),
needFindProcess: hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess,
dnsIndependentCache: dnsOptions.IndependentCache,
defaultDetour: options.Final,
defaultDomainStrategy: dns.DomainStrategy(dnsOptions.Strategy),
interfaceFinder: control.NewDefaultInterfaceFinder(),
autoDetectInterface: options.AutoDetectInterface,
defaultInterface: options.DefaultInterface,
defaultMark: options.DefaultMark,
pauseManager: service.FromContext[pause.Manager](ctx),
platformInterface: platformInterface,
needWIFIState: hasRule(options.Rules, isWIFIRule) || hasDNSRule(dnsOptions.Rules, isWIFIDNSRule),
needPackageManager: common.Any(inbounds, func(inbound option.Inbound) bool {
needPackageManager: C.IsAndroid && platformInterface == nil && common.Any(inbounds, func(inbound option.Inbound) bool {
return len(inbound.TunOptions.IncludePackage) > 0 || len(inbound.TunOptions.ExcludePackage) > 0
}),
}
@@ -221,7 +223,7 @@ func NewRouter(
if serverAddress == "" {
serverAddress = server.Address
}
notIpAddress := !M.ParseSocksaddr(serverAddress).Addr.IsValid()
_, notIpAddress := netip.ParseAddr(serverAddress)
if server.AddressResolver != "" {
if !transportTagMap[server.AddressResolver] {
return nil, E.New("parse dns server[", tag, "]: address resolver not found: ", server.AddressResolver)
@@ -231,11 +233,11 @@ func NewRouter(
} else {
continue
}
} else if notIpAddress && strings.Contains(server.Address, ".") {
} else if notIpAddress != nil && strings.Contains(server.Address, ".") {
return nil, E.New("parse dns server[", tag, "]: missing address_resolver")
}
}
var clientSubnet netip.Prefix
var clientSubnet netip.Addr
if server.ClientSubnet != nil {
clientSubnet = server.ClientSubnet.Build()
} else if dnsOptions.ClientSubnet != nil {
@@ -334,7 +336,7 @@ func NewRouter(
}
router.networkMonitor = networkMonitor
networkMonitor.RegisterCallback(func() {
_ = router.interfaceFinder.Update()
_ = router.interfaceFinder.update()
})
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor, router.logger, tun.DefaultInterfaceMonitorOptions{
OverrideAndroidVPN: options.OverrideAndroidVPN,
@@ -352,20 +354,20 @@ func NewRouter(
router.interfaceMonitor = interfaceMonitor
}
if ntpOptions.Enabled {
ntpDialer, err := dialer.New(router, ntpOptions.DialerOptions)
if runtime.GOOS == "windows" {
powerListener, err := winpowrprof.NewEventListener(router.notifyWindowsPowerEvent)
if err != nil {
return nil, E.Cause(err, "create NTP service")
return nil, E.Cause(err, "initialize power listener")
}
timeService := ntp.NewService(ntp.Options{
Context: ctx,
Dialer: ntpDialer,
Logger: logFactory.NewLogger("ntp"),
Server: ntpOptions.ServerOptions.Build(),
Interval: time.Duration(ntpOptions.Interval),
WriteToSystem: ntpOptions.WriteToSystem,
})
service.MustRegister[ntp.TimeService](ctx, timeService)
router.powerListener = powerListener
}
if ntpOptions.Enabled {
timeService, err := ntp.NewService(ctx, router, logFactory.NewLogger("ntp"), ntpOptions)
if err != nil {
return nil, err
}
service.ContextWith[serviceNTP.TimeService](ctx, timeService)
router.timeService = timeService
}
return router, nil
@@ -394,17 +396,20 @@ func (r *Router) Initialize(inbounds []adapter.Inbound, outbounds []adapter.Outb
defaultOutboundForPacketConnection = detour
}
}
var index, packetIndex int
if defaultOutboundForConnection == nil {
for _, detour := range outbounds {
for i, detour := range outbounds {
if common.Contains(detour.Network(), N.NetworkTCP) {
index = i
defaultOutboundForConnection = detour
break
}
}
}
if defaultOutboundForPacketConnection == nil {
for _, detour := range outbounds {
for i, detour := range outbounds {
if common.Contains(detour.Network(), N.NetworkUDP) {
packetIndex = i
defaultOutboundForPacketConnection = detour
break
}
@@ -421,6 +426,22 @@ func (r *Router) Initialize(inbounds []adapter.Inbound, outbounds []adapter.Outb
outbounds = append(outbounds, detour)
outboundByTag[detour.Tag()] = detour
}
if defaultOutboundForConnection != defaultOutboundForPacketConnection {
var description string
if defaultOutboundForConnection.Tag() != "" {
description = defaultOutboundForConnection.Tag()
} else {
description = F.ToString(index)
}
var packetDescription string
if defaultOutboundForPacketConnection.Tag() != "" {
packetDescription = defaultOutboundForPacketConnection.Tag()
} else {
packetDescription = F.ToString(packetIndex)
}
r.logger.Info("using ", defaultOutboundForConnection.Type(), "[", description, "] as default outbound for connection")
r.logger.Info("using ", defaultOutboundForPacketConnection.Type(), "[", packetDescription, "] as default outbound for packet connection")
}
r.inboundByTag = inboundByTag
r.outbounds = outbounds
r.defaultOutboundForConnection = defaultOutboundForConnection
@@ -442,7 +463,7 @@ func (r *Router) Outbounds() []adapter.Outbound {
}
func (r *Router) PreStart() error {
monitor := taskmonitor.New(r.logger, C.StartTimeout)
monitor := taskmonitor.New(r.logger, C.DefaultStartTimeout)
if r.interfaceMonitor != nil {
monitor.Start("initialize interface monitor")
err := r.interfaceMonitor.Start()
@@ -471,7 +492,7 @@ func (r *Router) PreStart() error {
}
func (r *Router) Start() error {
monitor := taskmonitor.New(r.logger, C.StartTimeout)
monitor := taskmonitor.New(r.logger, C.DefaultStartTimeout)
if r.needGeoIPDatabase {
monitor.Start("initialize geoip database")
err := r.prepareGeoIPDatabase()
@@ -509,12 +530,75 @@ func (r *Router) Start() error {
r.geositeReader = nil
}
if runtime.GOOS == "windows" {
powerListener, err := winpowrprof.NewEventListener(r.notifyWindowsPowerEvent)
if err == nil {
r.powerListener = powerListener
if len(r.ruleSets) > 0 {
monitor.Start("initialize rule-set")
ruleSetStartContext := NewRuleSetStartContext()
var ruleSetStartGroup task.Group
for i, ruleSet := range r.ruleSets {
ruleSetInPlace := ruleSet
ruleSetStartGroup.Append0(func(ctx context.Context) error {
err := ruleSetInPlace.StartContext(ctx, ruleSetStartContext)
if err != nil {
return E.Cause(err, "initialize rule-set[", i, "]")
}
return nil
})
}
ruleSetStartGroup.Concurrency(5)
ruleSetStartGroup.FastFail()
err := ruleSetStartGroup.Run(r.ctx)
monitor.Finish()
if err != nil {
return err
}
ruleSetStartContext.Close()
}
var (
needProcessFromRuleSet bool
needWIFIStateFromRuleSet bool
)
for _, ruleSet := range r.ruleSets {
metadata := ruleSet.Metadata()
if metadata.ContainsProcessRule {
needProcessFromRuleSet = true
}
if metadata.ContainsWIFIRule {
needWIFIStateFromRuleSet = true
}
}
if needProcessFromRuleSet || r.needFindProcess || r.needPackageManager {
if C.IsAndroid && r.platformInterface == nil {
monitor.Start("initialize package manager")
packageManager, err := tun.NewPackageManager(r)
monitor.Finish()
if err != nil {
return E.Cause(err, "create package manager")
}
monitor.Start("start package manager")
err = packageManager.Start()
monitor.Finish()
if err != nil {
return E.Cause(err, "start package manager")
}
r.packageManager = packageManager
}
if r.platformInterface != nil {
r.processSearcher = r.platformInterface
} else {
r.logger.Warn("initialize power listener: ", err)
monitor.Start("initialize process searcher")
searcher, err := process.NewSearcher(process.Config{
Logger: r.logger,
PackageManager: r.packageManager,
})
monitor.Finish()
if err != nil {
if err != os.ErrInvalid {
r.logger.Warn(E.Cause(err, "create process searcher"))
}
} else {
r.processSearcher = searcher
}
}
}
@@ -527,28 +611,29 @@ func (r *Router) Start() error {
}
}
if (needWIFIStateFromRuleSet || r.needWIFIState) && r.platformInterface != nil {
monitor.Start("initialize WIFI state")
r.needWIFIState = true
r.interfaceMonitor.RegisterCallback(func(_ int) {
r.updateWIFIState()
})
r.updateWIFIState()
monitor.Finish()
}
for i, rule := range r.rules {
monitor.Start("initialize rule[", i, "]")
err := rule.Start()
monitor.Finish()
if err != nil {
return E.Cause(err, "initialize rule[", i, "]")
}
}
monitor.Start("initialize DNS client")
r.dnsClient.Start()
monitor.Finish()
if C.IsAndroid && r.platformInterface == nil {
monitor.Start("initialize package manager")
packageManager, err := tun.NewPackageManager(r)
monitor.Finish()
if err != nil {
return E.Cause(err, "create package manager")
}
if r.needPackageManager {
monitor.Start("start package manager")
err = packageManager.Start()
monitor.Finish()
if err != nil {
return E.Cause(err, "start package manager")
}
}
r.packageManager = packageManager
}
for i, rule := range r.dnsRules {
monitor.Start("initialize DNS rule[", i, "]")
err := rule.Start()
@@ -577,7 +662,7 @@ func (r *Router) Start() error {
}
func (r *Router) Close() error {
monitor := taskmonitor.New(r.logger, C.StopTimeout)
monitor := taskmonitor.New(r.logger, C.DefaultStopTimeout)
var err error
for i, rule := range r.rules {
monitor.Start("close rule[", i, "]")
@@ -653,87 +738,12 @@ func (r *Router) Close() error {
}
func (r *Router) PostStart() error {
monitor := taskmonitor.New(r.logger, C.StopTimeout)
if len(r.ruleSets) > 0 {
monitor.Start("initialize rule-set")
ruleSetStartContext := NewRuleSetStartContext()
var ruleSetStartGroup task.Group
for i, ruleSet := range r.ruleSets {
ruleSetInPlace := ruleSet
ruleSetStartGroup.Append0(func(ctx context.Context) error {
err := ruleSetInPlace.StartContext(ctx, ruleSetStartContext)
if err != nil {
return E.Cause(err, "initialize rule-set[", i, "]")
}
return nil
})
}
ruleSetStartGroup.Concurrency(5)
ruleSetStartGroup.FastFail()
err := ruleSetStartGroup.Run(r.ctx)
monitor.Finish()
if err != nil {
return err
}
ruleSetStartContext.Close()
}
needFindProcess := r.needFindProcess
needWIFIState := r.needWIFIState
for _, ruleSet := range r.ruleSets {
metadata := ruleSet.Metadata()
if metadata.ContainsProcessRule {
needFindProcess = true
}
if metadata.ContainsWIFIRule {
needWIFIState = true
}
}
if C.IsAndroid && r.platformInterface == nil && !r.needPackageManager {
if needFindProcess {
monitor.Start("start package manager")
err := r.packageManager.Start()
monitor.Finish()
err := ruleSet.PostStart()
if err != nil {
return E.Cause(err, "start package manager")
return E.Cause(err, "post start rule-set[", i, "]")
}
} else {
r.packageManager = nil
}
}
if needFindProcess {
if r.platformInterface != nil {
r.processSearcher = r.platformInterface
} else {
monitor.Start("initialize process searcher")
searcher, err := process.NewSearcher(process.Config{
Logger: r.logger,
PackageManager: r.packageManager,
})
monitor.Finish()
if err != nil {
if err != os.ErrInvalid {
r.logger.Warn(E.Cause(err, "create process searcher"))
}
} else {
r.processSearcher = searcher
}
}
}
if needWIFIState && r.platformInterface != nil {
monitor.Start("initialize WIFI state")
r.needWIFIState = true
r.interfaceMonitor.RegisterCallback(func(_ int) {
r.updateWIFIState()
})
r.updateWIFIState()
monitor.Finish()
}
for i, rule := range r.rules {
monitor.Start("initialize rule[", i, "]")
err := rule.Start()
monitor.Finish()
if err != nil {
return E.Cause(err, "initialize rule[", i, "]")
}
}
r.started = true
@@ -951,57 +961,34 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
}*/
if metadata.InboundOptions.SniffEnabled || metadata.Destination.Addr.IsUnspecified() {
var (
buffer = buf.NewPacket()
destination M.Socksaddr
done = make(chan struct{})
err error
)
go func() {
sniffTimeout := C.ReadPayloadTimeout
if metadata.InboundOptions.SniffTimeout > 0 {
sniffTimeout = time.Duration(metadata.InboundOptions.SniffTimeout)
}
conn.SetReadDeadline(time.Now().Add(sniffTimeout))
destination, err = conn.ReadPacket(buffer)
conn.SetReadDeadline(time.Time{})
close(done)
}()
select {
case <-done:
case <-ctx.Done():
conn.Close()
return ctx.Err()
}
buffer := buf.NewPacket()
destination, err := conn.ReadPacket(buffer)
if err != nil {
buffer.Release()
if !errors.Is(err, os.ErrDeadlineExceeded) {
return err
}
} else {
if metadata.Destination.Addr.IsUnspecified() {
metadata.Destination = destination
}
if metadata.InboundOptions.SniffEnabled {
sniffMetadata, _ := sniff.PeekPacket(ctx, buffer.Bytes(), sniff.DomainNameQuery, sniff.QUICClientHello, sniff.STUNMessage)
if sniffMetadata != nil {
metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain
if metadata.InboundOptions.SniffOverrideDestination && M.IsDomainName(metadata.Domain) {
metadata.Destination = M.Socksaddr{
Fqdn: metadata.Domain,
Port: metadata.Destination.Port,
}
}
if metadata.Domain != "" {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
} else {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol)
return err
}
if metadata.Destination.Addr.IsUnspecified() {
metadata.Destination = destination
}
if metadata.InboundOptions.SniffEnabled {
sniffMetadata, _ := sniff.PeekPacket(ctx, buffer.Bytes(), sniff.DomainNameQuery, sniff.QUICClientHello, sniff.STUNMessage)
if sniffMetadata != nil {
metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain
if metadata.InboundOptions.SniffOverrideDestination && M.IsDomainName(metadata.Domain) {
metadata.Destination = M.Socksaddr{
Fqdn: metadata.Domain,
Port: metadata.Destination.Port,
}
}
if metadata.Domain != "" {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
} else {
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol)
}
}
conn = bufio.NewCachedPacketConn(conn, buffer, destination)
}
conn = bufio.NewCachedPacketConn(conn, buffer, destination)
}
if r.dnsReverseMapping != nil && metadata.Domain == "" {
domain, loaded := r.dnsReverseMapping.Query(metadata.Destination.Addr)
@@ -1104,18 +1091,24 @@ func (r *Router) match0(ctx context.Context, metadata *adapter.InboundContext, d
}
func (r *Router) InterfaceFinder() control.InterfaceFinder {
return r.interfaceFinder
return &r.interfaceFinder
}
func (r *Router) UpdateInterfaces() error {
if r.platformInterface == nil || !r.platformInterface.UsePlatformInterfaceGetter() {
return r.interfaceFinder.Update()
return r.interfaceFinder.update()
} else {
interfaces, err := r.platformInterface.Interfaces()
if err != nil {
return err
}
r.interfaceFinder.UpdateInterfaces(interfaces)
r.interfaceFinder.updateInterfaces(common.Map(interfaces, func(it platform.NetworkInterface) net.Interface {
return net.Interface{
Name: it.Name,
Index: it.Index,
MTU: it.MTU,
}
}))
return nil
}
}
@@ -1128,9 +1121,6 @@ func (r *Router) AutoDetectInterfaceFunc() control.Func {
if r.platformInterface != nil && r.platformInterface.UsePlatformAutoDetectInterfaceControl() {
return r.platformInterface.AutoDetectInterfaceControl()
} else {
if r.interfaceMonitor == nil {
return nil
}
return control.BindToInterfaceFunc(r.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int, err error) {
remoteAddr := M.ParseSocksaddr(address).Addr
if C.IsLinux {

View File

@@ -37,7 +37,7 @@ func (m *DNSReverseMapping) Query(address netip.Addr) (string, bool) {
return domain, loaded
}
func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, index int, isAddressQuery bool) (context.Context, dns.Transport, dns.DomainStrategy, adapter.DNSRule, int) {
func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, index int) (context.Context, dns.Transport, dns.DomainStrategy, adapter.DNSRule, int) {
metadata := adapter.ContextFrom(ctx)
if metadata == nil {
panic("no context")
@@ -48,9 +48,6 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, index int, isAd
dnsRules = dnsRules[index+1:]
}
for currentRuleIndex, rule := range dnsRules {
if rule.WithAddressLimit() && !isAddressQuery {
continue
}
metadata.ResetRuleCache()
if rule.Match(metadata) {
detour := rule.Outbound()
@@ -68,7 +65,7 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, index int, isAd
ruleIndex += index + 1
}
r.dnsLogger.DebugContext(ctx, "match[", ruleIndex, "] ", rule.String(), " => ", detour)
if isFakeIP || rule.DisableCache() {
if (isFakeIP && !r.dnsIndependentCache) || rule.DisableCache() {
ctx = dns.ContextWithDisableCache(ctx, true)
}
if rewriteTTL := rule.RewriteTTL(); rewriteTTL != nil {
@@ -129,9 +126,9 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
addressLimit bool
)
dnsCtx, transport, strategy, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message))
dnsCtx, transport, strategy, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex)
dnsCtx, cancel = context.WithTimeout(dnsCtx, C.DNSTimeout)
if rule != nil && rule.WithAddressLimit() {
if rule != nil && rule.WithAddressLimit() && isAddressQuery(message) {
addressLimit = true
response, err = r.dnsClient.ExchangeWithResponseCheck(dnsCtx, transport, message, strategy, func(response *mDNS.Msg) bool {
metadata.DestinationAddresses, _ = dns.MessageToAddresses(response)
@@ -208,7 +205,7 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
)
metadata.ResetRuleCache()
metadata.DestinationAddresses = nil
dnsCtx, transport, transportStrategy, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex, true)
dnsCtx, transport, transportStrategy, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex)
if strategy == dns.DomainStrategyAsIS {
strategy = transportStrategy
}
@@ -259,7 +256,7 @@ func (r *Router) ClearDNSCache() {
func isAddressQuery(message *mDNS.Msg) bool {
for _, question := range message.Question {
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA || question.Qtype == mDNS.TypeHTTPS {
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
return true
}
}

View File

@@ -211,12 +211,12 @@ func (r *abstractLogicalRule) Close() error {
func (r *abstractLogicalRule) Match(metadata *adapter.InboundContext) bool {
if r.mode == C.LogicalTypeAnd {
return common.All(r.rules, func(it adapter.HeadlessRule) bool {
metadata.ResetRuleCache()
metadata.ResetRuleCacheContext()
return it.Match(metadata)
}) != r.invert
} else {
return common.Any(r.rules, func(it adapter.HeadlessRule) bool {
metadata.ResetRuleCache()
metadata.ResetRuleCacheContext()
return it.Match(metadata)
}) != r.invert
}

View File

@@ -40,7 +40,7 @@ type DefaultDNSRule struct {
abstractDefaultRule
disableCache bool
rewriteTTL *uint32
clientSubnet *netip.Prefix
clientSubnet *netip.Addr
}
func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) {
@@ -51,7 +51,7 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
},
disableCache: options.DisableCache,
rewriteTTL: options.RewriteTTL,
clientSubnet: (*netip.Prefix)(options.ClientSubnet),
clientSubnet: (*netip.Addr)(options.ClientSubnet),
}
if len(options.Inbound) > 0 {
item := NewInboundRule(options.Inbound)
@@ -234,7 +234,7 @@ func (r *DefaultDNSRule) RewriteTTL() *uint32 {
return r.rewriteTTL
}
func (r *DefaultDNSRule) ClientSubnet() *netip.Prefix {
func (r *DefaultDNSRule) ClientSubnet() *netip.Addr {
return r.clientSubnet
}
@@ -272,7 +272,7 @@ type LogicalDNSRule struct {
abstractLogicalRule
disableCache bool
rewriteTTL *uint32
clientSubnet *netip.Prefix
clientSubnet *netip.Addr
}
func NewLogicalDNSRule(router adapter.Router, logger log.ContextLogger, options option.LogicalDNSRule) (*LogicalDNSRule, error) {
@@ -284,7 +284,6 @@ func NewLogicalDNSRule(router adapter.Router, logger log.ContextLogger, options
},
disableCache: options.DisableCache,
rewriteTTL: options.RewriteTTL,
clientSubnet: (*netip.Prefix)(options.ClientSubnet),
}
switch options.Mode {
case C.LogicalTypeAnd:
@@ -312,7 +311,7 @@ func (r *LogicalDNSRule) RewriteTTL() *uint32 {
return r.rewriteTTL
}
func (r *LogicalDNSRule) ClientSubnet() *netip.Prefix {
func (r *LogicalDNSRule) ClientSubnet() *netip.Addr {
return r.clientSubnet
}
@@ -335,12 +334,12 @@ func (r *LogicalDNSRule) WithAddressLimit() bool {
func (r *LogicalDNSRule) Match(metadata *adapter.InboundContext) bool {
if r.mode == C.LogicalTypeAnd {
return common.All(r.rules, func(it adapter.HeadlessRule) bool {
metadata.ResetRuleCache()
metadata.ResetRuleCacheContext()
return it.(adapter.DNSRule).Match(metadata)
}) != r.invert
} else {
return common.Any(r.rules, func(it adapter.HeadlessRule) bool {
metadata.ResetRuleCache()
metadata.ResetRuleCacheContext()
return it.(adapter.DNSRule).Match(metadata)
}) != r.invert
}
@@ -349,12 +348,12 @@ func (r *LogicalDNSRule) Match(metadata *adapter.InboundContext) bool {
func (r *LogicalDNSRule) MatchAddressLimit(metadata *adapter.InboundContext) bool {
if r.mode == C.LogicalTypeAnd {
return common.All(r.rules, func(it adapter.HeadlessRule) bool {
metadata.ResetRuleCache()
metadata.ResetRuleCacheContext()
return it.(adapter.DNSRule).MatchAddressLimit(metadata)
}) != r.invert
} else {
return common.Any(r.rules, func(it adapter.HeadlessRule) bool {
metadata.ResetRuleCache()
metadata.ResetRuleCacheContext()
return it.(adapter.DNSRule).MatchAddressLimit(metadata)
}) != r.invert
}

View File

@@ -129,18 +129,14 @@ func NewDefaultHeadlessRule(router adapter.Router, options option.DefaultHeadles
rule.allItems = append(rule.allItems, item)
}
if len(options.WIFISSID) > 0 {
if router != nil {
item := NewWIFISSIDItem(router, options.WIFISSID)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
item := NewWIFISSIDItem(router, options.WIFISSID)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if len(options.WIFIBSSID) > 0 {
if router != nil {
item := NewWIFIBSSIDItem(router, options.WIFIBSSID)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
item := NewWIFIBSSIDItem(router, options.WIFIBSSID)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
return rule, nil
}

View File

@@ -24,14 +24,12 @@ func (r *IPIsPrivateItem) Match(metadata *adapter.InboundContext) bool {
} else {
destination = metadata.Destination.Addr
}
if destination.IsValid() {
return !N.IsPublicAddr(destination)
if destination.IsValid() && !N.IsPublicAddr(destination) {
return true
}
if !r.isSource {
for _, destinationAddress := range metadata.DestinationAddresses {
if !N.IsPublicAddr(destinationAddress) {
return true
}
for _, destinationAddress := range metadata.DestinationAddresses {
if !N.IsPublicAddr(destinationAddress) {
return true
}
}
return false

View File

@@ -39,7 +39,7 @@ func NewPortRangeItem(isSource bool, rangeList []string) (*PortRangeItem, error)
}
}
if subIndex == len(portRange)-1 {
end = 0xFFFF
end = 0xFF
} else {
end, err = strconv.ParseUint(portRange[subIndex+1:], 10, 16)
if err != nil {

View File

@@ -3,14 +3,12 @@ package route
import (
"context"
"os"
"strings"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/srs"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/json"
)
@@ -70,11 +68,11 @@ func (s *LocalRuleSet) Match(metadata *adapter.InboundContext) bool {
return false
}
func (s *LocalRuleSet) String() string {
return strings.Join(F.MapToString(s.rules), " ")
func (s *LocalRuleSet) StartContext(ctx context.Context, startContext adapter.RuleSetStartContext) error {
return nil
}
func (s *LocalRuleSet) StartContext(ctx context.Context, startContext adapter.RuleSetStartContext) error {
func (s *LocalRuleSet) PostStart() error {
return nil
}

View File

@@ -7,7 +7,6 @@ import (
"net"
"net/http"
"runtime"
"strings"
"time"
"github.com/sagernet/sing-box/adapter"
@@ -15,7 +14,6 @@ import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
@@ -70,10 +68,6 @@ func (s *RemoteRuleSet) Match(metadata *adapter.InboundContext) bool {
return false
}
func (s *RemoteRuleSet) String() string {
return strings.Join(F.MapToString(s.rules), " ")
}
func (s *RemoteRuleSet) StartContext(ctx context.Context, startContext adapter.RuleSetStartContext) error {
var dialer N.Dialer
if s.options.RemoteOptions.DownloadDetour != "" {
@@ -112,6 +106,16 @@ func (s *RemoteRuleSet) StartContext(ctx context.Context, startContext adapter.R
return nil
}
func (s *RemoteRuleSet) PostStart() error {
if s.lastUpdated.IsZero() {
err := s.fetchOnce(s.ctx, nil)
if err != nil {
s.logger.Error("fetch rule-set ", s.options.Tag, ": ", err)
}
}
return nil
}
func (s *RemoteRuleSet) Metadata() adapter.RuleSetMetadata {
return s.metadata
}

View File

@@ -113,7 +113,7 @@ func testSuitLargeUDP(t *testing.T, clientPort uint16, testPort uint16) {
require.NoError(t, testPingPongWithPacketConn(t, testPort, dialUDP))
require.NoError(t, testLargeDataWithConn(t, testPort, dialTCP))
require.NoError(t, testLargeDataWithPacketConn(t, testPort, dialUDP))
require.NoError(t, testLargeDataWithPacketConnSize(t, testPort, 4096, dialUDP))
require.NoError(t, testLargeDataWithPacketConnSize(t, testPort, 5000, dialUDP))
}
func testTCP(t *testing.T, clientPort uint16, testPort uint16) {

View File

@@ -9,25 +9,26 @@ replace github.com/sagernet/sing-box => ../
require (
github.com/docker/docker v24.0.7+incompatible
github.com/docker/go-connections v0.4.0
github.com/gofrs/uuid/v5 v5.2.0
github.com/sagernet/quic-go v0.45.1-beta.2
github.com/sagernet/sing v0.4.2
github.com/sagernet/sing-dns v0.2.3
github.com/sagernet/sing-quic v0.2.0-beta.12
github.com/sagernet/sing-shadowsocks v0.2.7
github.com/sagernet/sing-shadowsocks2 v0.2.0
github.com/gofrs/uuid/v5 v5.0.0
github.com/sagernet/quic-go v0.40.0
github.com/sagernet/sing v0.2.20-0.20231212123824-8836b6754226
github.com/sagernet/sing-dns v0.1.11
github.com/sagernet/sing-quic v0.1.6-0.20231207143711-eb3cbf9ed054
github.com/sagernet/sing-shadowsocks v0.2.6
github.com/sagernet/sing-shadowsocks2 v0.1.6-0.20231207143709-50439739601a
github.com/spyzhov/ajson v0.9.0
github.com/stretchr/testify v1.9.0
github.com/stretchr/testify v1.8.4
go.uber.org/goleak v1.3.0
golang.org/x/net v0.25.0
golang.org/x/net v0.19.0
)
require (
berty.tech/go-libtor v1.0.385 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/caddyserver/certmagic v0.20.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/cloudflare/circl v1.3.6 // indirect
github.com/cretz/bine v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.5.0 // indirect
@@ -35,23 +36,28 @@ require (
github.com/docker/go-units v0.5.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gaukas/godicttls v0.0.4 // indirect
github.com/go-chi/chi/v5 v5.0.12 // indirect
github.com/go-chi/chi/v5 v5.0.10 // indirect
github.com/go-chi/cors v1.2.1 // indirect
github.com/go-chi/render v1.0.3 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/libdns/alidns v1.0.3 // indirect
github.com/libdns/cloudflare v0.1.1 // indirect
github.com/libdns/libdns v0.2.2 // indirect
github.com/libdns/cloudflare v0.1.0 // indirect
github.com/libdns/libdns v0.2.1 // indirect
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
github.com/mholt/acmez v1.2.0 // indirect
github.com/miekg/dns v1.1.59 // indirect
github.com/miekg/dns v1.1.57 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/onsi/ginkgo/v2 v2.9.7 // indirect
@@ -59,41 +65,43 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 // indirect
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f // indirect
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba // indirect
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e // indirect
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
github.com/sagernet/sing-mux v0.2.0 // indirect
github.com/sagernet/sing-mux v0.1.6-0.20231208180947-9053c29513a2 // indirect
github.com/sagernet/sing-shadowtls v0.1.4 // indirect
github.com/sagernet/sing-tun v0.3.2 // indirect
github.com/sagernet/sing-vmess v0.1.12 // indirect
github.com/sagernet/sing-tun v0.1.24-0.20231212060935-6a1419aeae11 // indirect
github.com/sagernet/sing-vmess v0.1.8 // indirect
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 // indirect
github.com/sagernet/utls v1.5.4 // indirect
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 // indirect
github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e // indirect
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.uber.org/zap v1.26.0 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/crypto v0.16.0 // indirect
golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/grpc v1.63.2 // indirect
google.golang.org/protobuf v1.33.0 // indirect
golang.org/x/tools v0.16.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.5.1 // indirect
lukechampine.com/blake3 v1.3.0 // indirect
lukechampine.com/blake3 v1.2.1 // indirect
)

View File

@@ -3,12 +3,14 @@ berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+f
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg=
github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
@@ -29,8 +31,12 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
@@ -40,18 +46,26 @@ github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gofrs/uuid/v5 v5.2.0 h1:qw1GMx6/y8vhVsx626ImfKMuS5CvJmhIKKtuyvfajMM=
github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
@@ -62,17 +76,17 @@ github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZY
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ=
github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE=
github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054=
github.com/libdns/cloudflare v0.1.1/go.mod h1:9VK91idpOjg6v7/WbjkEW49bSCxj00ALesIFDhJ8PBU=
github.com/libdns/cloudflare v0.1.0 h1:93WkJaGaiXCe353LHEP36kAWCUw0YjFqwhkBkU2/iic=
github.com/libdns/cloudflare v0.1.0/go.mod h1:a44IP6J1YH6nvcNl1PverfJviADgXUnsozR3a7vBKN8=
github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
@@ -89,6 +103,8 @@ github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrB
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -101,51 +117,55 @@ 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/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY=
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k=
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f h1:NkhuupzH5ch7b/Y/6ZHJWrnNLoiNnSJaow6DPb8VW2I=
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f/go.mod h1:KXmw+ouSJNOsuRpg4wgwwCQuunrGz4yoAqQjsLjc6N0=
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba h1:EY5AS7CCtfmARNv2zXUOrsEMPFDGYxaw65JzA2p51Vk=
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.45.1-beta.2 h1:zkEeCbhdFFkrxKcuIRBtXNKci/1t2J/39QSG/sPvlmc=
github.com/sagernet/quic-go v0.45.1-beta.2/go.mod h1:+N3FqM9DAzOWfe64uxXuBejVJwX7DeW7BslzLO6N/xI=
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e h1:DOkjByVeAR56dkszjnMZke4wr7yM/1xHaJF3G9olkEE=
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e/go.mod h1:fLxq/gtp0qzkaEwywlRRiGmjOK5ES/xUzyIKIFP2Asw=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.40.0 h1:DvQNPb72lzvNQDe9tcUyHTw8eRv6PLtM2mNYmdlzUMo=
github.com/sagernet/quic-go v0.40.0/go.mod h1:VqtdhlbkeeG5Okhb3eDMb/9o0EoglReHunNT9ukrJAI=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
github.com/sagernet/sing v0.4.2 h1:jzGNJdZVRI0xlAfFugsIQUPvyB9SuWvbJK7zQCXc4QM=
github.com/sagernet/sing v0.4.2/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
github.com/sagernet/sing-dns v0.2.3 h1:YzeBUn2tR38F7HtvGEQ0kLRLmZWMEgi/+7wqa4Twb1k=
github.com/sagernet/sing-dns v0.2.3/go.mod h1:BJpJv6XLnrUbSyIntOT6DG9FW0f4fETmPAHvNjOprLg=
github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=
github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ=
github.com/sagernet/sing-quic v0.2.0-beta.12 h1:BhvA5mmrDFEyDUQB5eeu+9UhF+ieyuNJ5Rsb0dAG3QY=
github.com/sagernet/sing-quic v0.2.0-beta.12/go.mod h1:YVpLfVi8BvYM7NMrjmnvcRm3E8iMETf1gFQmTQDN9jI=
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
github.com/sagernet/sing v0.2.20-0.20231212123824-8836b6754226 h1:rcII71ho6F/7Nyx7n2kESLcnvNMdcU4i8ZUGF2Fi7yA=
github.com/sagernet/sing v0.2.20-0.20231212123824-8836b6754226/go.mod h1:Ce5LNojQOgOiWhiD8pPD6E9H7e2KgtOe3Zxx4Ou5u80=
github.com/sagernet/sing-dns v0.1.11 h1:PPrMCVVrAeR3f5X23I+cmvacXJ+kzuyAsBiWyUKhGSE=
github.com/sagernet/sing-dns v0.1.11/go.mod h1:zJ/YjnYB61SYE+ubMcMqVdpaSvsyQ2iShQGO3vuLvvE=
github.com/sagernet/sing-mux v0.1.6-0.20231208180947-9053c29513a2 h1:rRlYQPbMKmzKX+43XC04gEQvxc45/AxfteRWfcl2/rw=
github.com/sagernet/sing-mux v0.1.6-0.20231208180947-9053c29513a2/go.mod h1:IdSrwwqBeJTrjLZJRFXE+F8mYXNI/rPAjzlgTFuEVmo=
github.com/sagernet/sing-quic v0.1.6-0.20231207143711-eb3cbf9ed054 h1:Ed7FskwQcep5oQ+QahgVK0F6jPPSV8Nqwjr9MwGatMU=
github.com/sagernet/sing-quic v0.1.6-0.20231207143711-eb3cbf9ed054/go.mod h1:u758WWv3G1OITG365CYblL0NfAruFL1PpLD9DUVTv1o=
github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s=
github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM=
github.com/sagernet/sing-shadowsocks2 v0.1.6-0.20231207143709-50439739601a h1:uYIKfpE1/EJpa+1Bja7b006VixeRuVduOpeuesMk2lU=
github.com/sagernet/sing-shadowsocks2 v0.1.6-0.20231207143709-50439739601a/go.mod h1:pjeylQ4ApvpEH7B4PUBrdyJf4xmQkg8BaIzT5fI2fR0=
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
github.com/sagernet/sing-tun v0.3.2 h1:z0bLUT/YXH9RrJS9DsIpB0Bb9afl2hVJOmHd0zA3HJY=
github.com/sagernet/sing-tun v0.3.2/go.mod h1:DxLIyhjWU/HwGYoX0vNGg2c5QgTQIakphU1MuERR5tQ=
github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg=
github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I=
github.com/sagernet/sing-tun v0.1.24-0.20231212060935-6a1419aeae11 h1:crTOVPJGOGWOW+Q2a0FQiiS/G2+W6uCLKtOofFMisQc=
github.com/sagernet/sing-tun v0.1.24-0.20231212060935-6a1419aeae11/go.mod h1:DgXPnBqtqWrZj37Mun/W61dW0Q56eLqTZYhcuNLaCtY=
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 h1:z3SJQhVyU63FT26Wn/UByW6b7q8QKB0ZkPqsyqcz2PI=
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6/go.mod h1:73xRZuxwkFk4aiLw28hG8W6o9cr2UPrGL9pdY2UTbvY=
github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co=
github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s=
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 h1:R0OMYAScomNAVpTfbHFpxqJpvwuhxSRi+g6z7gZhABs=
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8/go.mod h1:K4J7/npM+VAMUeUmTa2JaA02JmyheP0GpRBOUvn3ecc=
github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2FzELOFNFQtvsxH7NPmlo7X5JizEK51UCojo=
github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE=
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
github.com/spyzhov/ajson v0.9.0 h1:tF46gJGOenYVj+k9K1U1XpCxVWhmiyY5PsVCAs1+OJ0=
github.com/spyzhov/ajson v0.9.0/go.mod h1:a6oSw0MMb7Z5aD2tPoPO+jq11ETKgXUr2XktHdT8Wt8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -160,8 +180,8 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -169,27 +189,26 @@ golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaE
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No=
golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -197,37 +216,40 @@ golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -235,5 +257,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=

354
test/reality_test.go Normal file
View File

@@ -0,0 +1,354 @@
package main
import (
"net/netip"
"testing"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/vless"
)
func TestVLESSVisionReality(t *testing.T) {
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
userUUID := newUUID()
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort,
},
},
},
{
Type: C.TypeVLESS,
VLESSOptions: option.VLESSInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort,
},
Users: []option.VLESSUser{
{
Name: "sekai",
UUID: userUUID.String(),
Flow: vless.FlowVision,
},
},
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
TLS: &option.InboundTLSOptions{
Enabled: true,
ServerName: "google.com",
Reality: &option.InboundRealityOptions{
Enabled: true,
Handshake: option.InboundRealityHandshakeOptions{
ServerOptions: option.ServerOptions{
Server: "google.com",
ServerPort: 443,
},
},
ShortID: []string{"0123456789abcdef"},
PrivateKey: "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc",
},
},
},
},
},
{
Type: C.TypeTrojan,
Tag: "trojan",
TrojanOptions: option.TrojanInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: otherPort,
},
Users: []option.TrojanUser{
{
Name: "sekai",
Password: userUUID.String(),
},
},
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
TLS: &option.InboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
KeyPath: keyPem,
},
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeDirect,
},
{
Type: C.TypeTrojan,
Tag: "trojan-out",
TrojanOptions: option.TrojanOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: otherPort,
},
Password: userUUID.String(),
OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{
TLS: &option.OutboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
},
},
DialerOptions: option.DialerOptions{
Detour: "vless-out",
},
},
},
{
Type: C.TypeVLESS,
Tag: "vless-out",
VLESSOptions: option.VLESSOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
UUID: userUUID.String(),
Flow: vless.FlowVision,
OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{
TLS: &option.OutboundTLSOptions{
Enabled: true,
ServerName: "google.com",
Reality: &option.OutboundRealityOptions{
Enabled: true,
ShortID: "0123456789abcdef",
PublicKey: "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0",
},
UTLS: &option.OutboundUTLSOptions{
Enabled: true,
},
},
},
},
},
},
Route: &option.RouteOptions{
Rules: []option.Rule{
{
DefaultOptions: option.DefaultRule{
Inbound: []string{"mixed-in"},
Outbound: "trojan-out",
},
},
},
},
})
testSuit(t, clientPort, testPort)
}
func TestVLESSVisionRealityPlain(t *testing.T) {
userUUID := newUUID()
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort,
},
},
},
{
Type: C.TypeVLESS,
VLESSOptions: option.VLESSInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort,
},
Users: []option.VLESSUser{
{
Name: "sekai",
UUID: userUUID.String(),
Flow: vless.FlowVision,
},
},
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
TLS: &option.InboundTLSOptions{
Enabled: true,
ServerName: "google.com",
Reality: &option.InboundRealityOptions{
Enabled: true,
Handshake: option.InboundRealityHandshakeOptions{
ServerOptions: option.ServerOptions{
Server: "google.com",
ServerPort: 443,
},
},
ShortID: []string{"0123456789abcdef"},
PrivateKey: "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc",
},
},
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeDirect,
},
{
Type: C.TypeVLESS,
Tag: "vless-out",
VLESSOptions: option.VLESSOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
UUID: userUUID.String(),
Flow: vless.FlowVision,
OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{
TLS: &option.OutboundTLSOptions{
Enabled: true,
ServerName: "google.com",
Reality: &option.OutboundRealityOptions{
Enabled: true,
ShortID: "0123456789abcdef",
PublicKey: "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0",
},
UTLS: &option.OutboundUTLSOptions{
Enabled: true,
},
},
},
},
},
},
Route: &option.RouteOptions{
Rules: []option.Rule{
{
DefaultOptions: option.DefaultRule{
Inbound: []string{"mixed-in"},
Outbound: "vless-out",
},
},
},
},
})
testSuit(t, clientPort, testPort)
}
func TestVLESSRealityTransport(t *testing.T) {
t.Run("grpc", func(t *testing.T) {
testVLESSRealityTransport(t, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeGRPC,
})
})
t.Run("websocket", func(t *testing.T) {
testVLESSRealityTransport(t, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeWebsocket,
})
})
t.Run("h2", func(t *testing.T) {
testVLESSRealityTransport(t, &option.V2RayTransportOptions{
Type: C.V2RayTransportTypeHTTP,
})
})
}
func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOptions) {
userUUID := newUUID()
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort,
},
},
},
{
Type: C.TypeVLESS,
VLESSOptions: option.VLESSInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort,
},
Users: []option.VLESSUser{
{
Name: "sekai",
UUID: userUUID.String(),
},
},
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
TLS: &option.InboundTLSOptions{
Enabled: true,
ServerName: "google.com",
Reality: &option.InboundRealityOptions{
Enabled: true,
Handshake: option.InboundRealityHandshakeOptions{
ServerOptions: option.ServerOptions{
Server: "google.com",
ServerPort: 443,
},
},
ShortID: []string{"0123456789abcdef"},
PrivateKey: "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc",
},
},
},
Transport: transport,
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeDirect,
},
{
Type: C.TypeVLESS,
Tag: "vless-out",
VLESSOptions: option.VLESSOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
UUID: userUUID.String(),
OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{
TLS: &option.OutboundTLSOptions{
Enabled: true,
ServerName: "google.com",
Reality: &option.OutboundRealityOptions{
Enabled: true,
ShortID: "0123456789abcdef",
PublicKey: "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0",
},
UTLS: &option.OutboundUTLSOptions{
Enabled: true,
},
},
},
Transport: transport,
},
},
},
Route: &option.RouteOptions{
Rules: []option.Rule{
{
DefaultOptions: option.DefaultRule{
Inbound: []string{"mixed-in"},
Outbound: "vless-out",
},
},
},
},
})
testSuit(t, clientPort, testPort)
}

559
test/vless_test.go Normal file
View File

@@ -0,0 +1,559 @@
package main
import (
"net/netip"
"os"
"testing"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/vless"
"github.com/gofrs/uuid/v5"
"github.com/spyzhov/ajson"
"github.com/stretchr/testify/require"
)
func TestVLESS(t *testing.T) {
content, err := os.ReadFile("config/vless-server.json")
require.NoError(t, err)
config, err := ajson.Unmarshal(content)
require.NoError(t, err)
user := newUUID()
inbound := config.MustKey("inbounds").MustIndex(0)
inbound.MustKey("port").SetNumeric(float64(serverPort))
inbound.MustKey("settings").MustKey("clients").MustIndex(0).MustKey("id").SetString(user.String())
content, err = ajson.Marshal(config)
require.NoError(t, err)
startDockerContainer(t, DockerOptions{
Image: ImageV2RayCore,
Ports: []uint16{serverPort},
EntryPoint: "v2ray",
Cmd: []string{"run"},
Stdin: content,
})
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort,
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeVLESS,
VLESSOptions: option.VLESSOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
UUID: user.String(),
},
},
},
})
testTCP(t, clientPort, testPort)
}
func TestVLESSXRay(t *testing.T) {
t.Run("origin", func(t *testing.T) {
testVLESSXrayOutbound(t, "", "")
})
t.Run("xudp", func(t *testing.T) {
testVLESSXrayOutbound(t, "xudp", "")
})
t.Run("vision", func(t *testing.T) {
testVLESSXrayOutbound(t, "xudp", vless.FlowVision)
})
}
func testVLESSXrayOutbound(t *testing.T, packetEncoding string, flow string) {
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
content, err := os.ReadFile("config/vless-tls-server.json")
require.NoError(t, err)
config, err := ajson.Unmarshal(content)
require.NoError(t, err)
userID := newUUID()
inbound := config.MustKey("inbounds").MustIndex(0)
inbound.MustKey("port").SetNumeric(float64(serverPort))
user := inbound.MustKey("settings").MustKey("clients").MustIndex(0)
user.MustKey("id").SetString(userID.String())
user.MustKey("flow").SetString(flow)
content, err = ajson.Marshal(config)
require.NoError(t, err)
startDockerContainer(t, DockerOptions{
Image: ImageXRayCore,
Ports: []uint16{serverPort},
EntryPoint: "xray",
Stdin: content,
Bind: map[string]string{
certPem: "/path/to/certificate.crt",
keyPem: "/path/to/private.key",
},
})
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort,
},
},
},
{
Type: C.TypeTrojan,
Tag: "trojan",
TrojanOptions: option.TrojanInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: otherPort,
},
Users: []option.TrojanUser{
{
Name: "sekai",
Password: userID.String(),
},
},
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
TLS: &option.InboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
KeyPath: keyPem,
},
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeTrojan,
TrojanOptions: option.TrojanOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "host.docker.internal",
ServerPort: otherPort,
},
Password: userID.String(),
OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{
TLS: &option.OutboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
},
},
DialerOptions: option.DialerOptions{
Detour: "vless",
},
},
},
{
Type: C.TypeVLESS,
Tag: "vless",
VLESSOptions: option.VLESSOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
UUID: userID.String(),
Flow: flow,
PacketEncoding: &packetEncoding,
OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{
TLS: &option.OutboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
},
},
},
},
{
Type: C.TypeDirect,
Tag: "direct",
},
},
Route: &option.RouteOptions{
Rules: []option.Rule{
{
DefaultOptions: option.DefaultRule{
Inbound: []string{"trojan"},
Outbound: "direct",
},
},
},
},
})
testSuit(t, clientPort, testPort)
}
func TestVLESSSelf(t *testing.T) {
t.Run("origin", func(t *testing.T) {
testVLESSSelf(t, "")
})
t.Run("vision", func(t *testing.T) {
testVLESSSelf(t, vless.FlowVision)
})
t.Run("vision-tls", func(t *testing.T) {
testVLESSSelfTLS(t, vless.FlowVision)
})
}
func testVLESSSelf(t *testing.T, flow string) {
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
userUUID := newUUID()
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort,
},
},
},
{
Type: C.TypeVLESS,
VLESSOptions: option.VLESSInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort,
},
Users: []option.VLESSUser{
{
Name: "sekai",
UUID: userUUID.String(),
Flow: flow,
},
},
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
TLS: &option.InboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
KeyPath: keyPem,
},
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeDirect,
},
{
Type: C.TypeVLESS,
Tag: "vless-out",
VLESSOptions: option.VLESSOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
UUID: userUUID.String(),
Flow: flow,
OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{
TLS: &option.OutboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
},
},
},
},
},
Route: &option.RouteOptions{
Rules: []option.Rule{
{
DefaultOptions: option.DefaultRule{
Inbound: []string{"mixed-in"},
Outbound: "vless-out",
},
},
},
},
})
testSuit(t, clientPort, testPort)
}
func testVLESSSelfTLS(t *testing.T, flow string) {
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
userUUID := newUUID()
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort,
},
},
},
{
Type: C.TypeVLESS,
VLESSOptions: option.VLESSInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort,
},
Users: []option.VLESSUser{
{
Name: "sekai",
UUID: userUUID.String(),
Flow: flow,
},
},
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
TLS: &option.InboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
KeyPath: keyPem,
},
},
},
},
{
Type: C.TypeTrojan,
Tag: "trojan",
TrojanOptions: option.TrojanInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: otherPort,
},
Users: []option.TrojanUser{
{
Name: "sekai",
Password: userUUID.String(),
},
},
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
TLS: &option.InboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
KeyPath: keyPem,
},
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeDirect,
},
{
Type: C.TypeTrojan,
Tag: "trojan-out",
TrojanOptions: option.TrojanOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: otherPort,
},
Password: userUUID.String(),
OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{
TLS: &option.OutboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
},
},
DialerOptions: option.DialerOptions{
Detour: "vless-out",
},
},
},
{
Type: C.TypeVLESS,
Tag: "vless-out",
VLESSOptions: option.VLESSOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
UUID: userUUID.String(),
Flow: flow,
OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{
TLS: &option.OutboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
},
},
},
},
},
Route: &option.RouteOptions{
Rules: []option.Rule{
{
DefaultOptions: option.DefaultRule{
Inbound: []string{"mixed-in"},
Outbound: "trojan-out",
},
},
},
},
})
testSuit(t, clientPort, testPort)
}
func TestVLESSXrayInbound(t *testing.T) {
testVLESSXrayInbound(t, vless.FlowVision)
}
func testVLESSXrayInbound(t *testing.T, flow string) {
userId, err := uuid.DefaultGenerator.NewV4()
require.NoError(t, err)
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeVLESS,
VLESSOptions: option.VLESSInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort,
},
Users: []option.VLESSUser{
{
Name: "sekai",
UUID: userId.String(),
Flow: flow,
},
},
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
TLS: &option.InboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
KeyPath: keyPem,
},
},
},
},
{
Type: C.TypeTrojan,
Tag: "trojan",
TrojanOptions: option.TrojanInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: otherPort,
},
Users: []option.TrojanUser{
{
Name: "sekai",
Password: userId.String(),
},
},
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
TLS: &option.InboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
KeyPath: keyPem,
},
},
},
},
},
})
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: otherClientPort,
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeTrojan,
Tag: "trojan-out",
TrojanOptions: option.TrojanOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: otherPort,
},
Password: userId.String(),
OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{
TLS: &option.OutboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
},
},
DialerOptions: option.DialerOptions{
Detour: "vless-out",
},
},
},
{
Type: C.TypeSOCKS,
Tag: "vless-out",
SocksOptions: option.SocksOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: clientPort,
},
},
},
},
})
content, err := os.ReadFile("config/vless-tls-client.json")
require.NoError(t, err)
config, err := ajson.Unmarshal(content)
require.NoError(t, err)
config.MustKey("inbounds").MustIndex(0).MustKey("port").SetNumeric(float64(clientPort))
outbound := config.MustKey("outbounds").MustIndex(0)
settings := outbound.MustKey("settings").MustKey("vnext").MustIndex(0)
settings.MustKey("port").SetNumeric(float64(serverPort))
user := settings.MustKey("users").MustIndex(0)
user.MustKey("id").SetString(userId.String())
user.MustKey("flow").SetString(flow)
content, err = ajson.Marshal(config)
require.NoError(t, err)
content, err = ajson.Marshal(config)
require.NoError(t, err)
startDockerContainer(t, DockerOptions{
Image: ImageXRayCore,
Ports: []uint16{clientPort},
EntryPoint: "xray",
Stdin: content,
Bind: map[string]string{
certPem: "/path/to/certificate.crt",
keyPem: "/path/to/private.key",
},
})
testTCP(t, otherClientPort, testPort)
}

View File

@@ -40,13 +40,6 @@ func (s *MemoryStorage) FakeIPSaveMetadataAsync(metadata *adapter.FakeIPMetadata
func (s *MemoryStorage) FakeIPStore(address netip.Addr, domain string) error {
s.addressAccess.Lock()
s.domainAccess.Lock()
if oldDomain, loaded := s.addressCache[address]; loaded {
if address.Is4() {
delete(s.domainCache4, oldDomain)
} else {
delete(s.domainCache6, oldDomain)
}
}
s.addressCache[address] = domain
if address.Is4() {
s.domainCache4[domain] = address

View File

@@ -0,0 +1,55 @@
package fakeip
import (
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
var _ N.PacketConn = (*NATPacketConn)(nil)
type NATPacketConn struct {
N.PacketConn
origin M.Socksaddr
destination M.Socksaddr
}
func NewNATPacketConn(conn N.PacketConn, origin M.Socksaddr, destination M.Socksaddr) *NATPacketConn {
return &NATPacketConn{
PacketConn: conn,
origin: socksaddrWithoutPort(origin),
destination: socksaddrWithoutPort(destination),
}
}
func (c *NATPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
destination, err = c.PacketConn.ReadPacket(buffer)
if socksaddrWithoutPort(destination) == c.origin {
destination = M.Socksaddr{
Addr: c.destination.Addr,
Fqdn: c.destination.Fqdn,
Port: destination.Port,
}
}
return
}
func (c *NATPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
if socksaddrWithoutPort(destination) == c.destination {
destination = M.Socksaddr{
Addr: c.origin.Addr,
Fqdn: c.origin.Fqdn,
Port: destination.Port,
}
}
return c.PacketConn.WritePacket(buffer, destination)
}
func (c *NATPacketConn) Upstream() any {
return c.PacketConn
}
func socksaddrWithoutPort(destination M.Socksaddr) M.Socksaddr {
destination.Port = 0
return destination
}

Some files were not shown because too many files have changed in this diff Show More