mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-16 05:39:08 +10:00
Compare commits
25 Commits
v1.8.5
...
v1.8.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69cc2cd07c | ||
|
|
1571d7f8cc | ||
|
|
9142e70af4 | ||
|
|
285278b7be | ||
|
|
6d4f54d56a | ||
|
|
7740293a66 | ||
|
|
5052e86a5d | ||
|
|
0732b63609 | ||
|
|
9dedf8be30 | ||
|
|
2b518d3819 | ||
|
|
b0cd7af416 | ||
|
|
a99c3108a7 | ||
|
|
2f4f4e18ee | ||
|
|
bc2d73b2d6 | ||
|
|
abd4d8b367 | ||
|
|
f998ccecde | ||
|
|
fc9804b20f | ||
|
|
d1ad342cb0 | ||
|
|
80cc0cd5db | ||
|
|
3041f34dfa | ||
|
|
3a9b787a6e | ||
|
|
bb70e8267e | ||
|
|
411f02ee4f | ||
|
|
340e74eed4 | ||
|
|
ad93b45021 |
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -1 +0,0 @@
|
||||
github: nekohasekai
|
||||
19
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
19
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -61,22 +61,7 @@ body:
|
||||
attributes:
|
||||
label: Logs
|
||||
description: |-
|
||||
In addition, if you encounter a crash with the graphical client, please also provide crash logs.
|
||||
If you encounter a crash with the graphical client, please provide crash logs.
|
||||
For Apple platform clients, please check `Settings - View Service Log` for crash logs.
|
||||
For the Android client, please check the `/sdcard/Android/data/io.nekohasekai.sfa/files/stderr.log` file for crash logs.
|
||||
render: shell
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Integrity requirements
|
||||
description: |-
|
||||
Please check all of the following options to prove that you have read and understood the requirements, otherwise this issue will be closed.
|
||||
Sing-box is not a project aimed to please users who can't make any meaningful contributions and gain unethical influence. If you deceive here to deliberately waste the time of the developers, you will be permanently blocked.
|
||||
options:
|
||||
- label: I confirm that I have read the documentation, understand the meaning of all the configuration items I wrote, and did not pile up seemingly useful options or default values.
|
||||
required: true
|
||||
- label: I confirm that I have provided the server and client configuration files and process that can be reproduced locally, instead of a complicated client configuration file that has been stripped of sensitive data.
|
||||
required: true
|
||||
- label: I confirm that I have provided the simplest configuration that can be used to reproduce the error I reported, instead of depending on remote servers, TUN, graphical interface clients, or other closed-source software.
|
||||
required: true
|
||||
- label: I confirm that I have provided the complete configuration files and logs, rather than just providing parts I think are useful out of confidence in my own intelligence.
|
||||
required: true
|
||||
render: shell
|
||||
21
.github/ISSUE_TEMPLATE/bug_report_zh.yml
vendored
21
.github/ISSUE_TEMPLATE/bug_report_zh.yml
vendored
@@ -61,22 +61,21 @@ body:
|
||||
attributes:
|
||||
label: 日志
|
||||
description: |-
|
||||
此外,如果您遭遇图形界面应用程序崩溃,请附加提供崩溃日志。
|
||||
如果您遭遇图形界面应用程序崩溃,请提供崩溃日志。
|
||||
对于 Apple 平台图形客户端程序,请检查 `Settings - View Service Log` 以导出崩溃日志。
|
||||
对于 Android 图形客户端程序,请检查 `/sdcard/Android/data/io.nekohasekai.sfa/files/stderr.log` 文件以导出崩溃日志。
|
||||
render: shell
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 完整性要求
|
||||
description: |-
|
||||
请勾选以下所有选项以证明您已经阅读并理解了以下要求,否则该 issue 将被关闭。
|
||||
sing-box 不是讨好无法作出任何意义上的贡献的最终用户并获取非道德影响力的项目,如果您在此处欺骗以故意浪费开发者的时间,您将被永久封锁。
|
||||
description: 我保证我提供了完整的可以在本地重现该问题的服务器、客户端配置文件与流程,而不是一个脱敏的复杂客户端配置文件,否则该 issue 将被关闭。
|
||||
options:
|
||||
- label: 我保证阅读了文档,了解所有我编写的配置文件项的含义,而不是大量堆砌看似有用的选项或默认值。
|
||||
required: true
|
||||
- label: 我保证提供了可以在本地重现该问题的服务器、客户端配置文件与流程,而不是一个脱敏的复杂客户端配置文件。
|
||||
required: true
|
||||
- label: 我保证提供了可用于重现我报告的错误的最简配置,而不是依赖远程服务器、TUN、图形界面客户端或者其他闭源软件。
|
||||
required: true
|
||||
- label: 我保证提供了完整的配置文件与日志,而不是出于对自身智力的自信而仅提供了部分认为有用的部分。
|
||||
- label: 我保证
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 负责性要求
|
||||
description: 我保证我阅读了文档,了解所有我编写的配置文件项的含义,而不是大量堆砌看似有用的选项或默认值,否则该 issue 将被关闭。
|
||||
options:
|
||||
- label: 我保证
|
||||
required: true
|
||||
36
.github/workflows/debug.yml
vendored
36
.github/workflows/debug.yml
vendored
@@ -25,10 +25,22 @@ jobs:
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Get latest go version
|
||||
id: version
|
||||
run: |
|
||||
echo go_version=$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g') >> $GITHUB_OUTPUT
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.21
|
||||
go-version: ${{ steps.version.outputs.go_version }}
|
||||
- name: Add cache to Go proxy
|
||||
run: |
|
||||
version=`git rev-parse HEAD`
|
||||
mkdir build
|
||||
pushd build
|
||||
go mod init build
|
||||
go get -v github.com/sagernet/sing-box@$version
|
||||
popd
|
||||
continue-on-error: true
|
||||
- name: Run Test
|
||||
run: |
|
||||
@@ -44,7 +56,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ~1.18
|
||||
go-version: 1.18.10
|
||||
- name: Cache go module
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
@@ -64,13 +76,13 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.20
|
||||
go-version: 1.20.7
|
||||
- name: Cache go module
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
key: go120-${{ hashFiles('**/go.sum') }}
|
||||
key: go118-${{ hashFiles('**/go.sum') }}
|
||||
- name: Run Test
|
||||
run: make ci_build
|
||||
cross:
|
||||
@@ -176,7 +188,8 @@ jobs:
|
||||
- name: freebsd-arm64
|
||||
goos: freebsd
|
||||
goarch: arm64
|
||||
fail-fast: true
|
||||
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
@@ -191,10 +204,19 @@ jobs:
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Get latest go version
|
||||
id: version
|
||||
run: |
|
||||
echo go_version=$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g') >> $GITHUB_OUTPUT
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.21
|
||||
go-version: ${{ steps.version.outputs.go_version }}
|
||||
- name: Build
|
||||
id: build
|
||||
run: make
|
||||
run: make
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: sing-box-${{ matrix.name }}
|
||||
path: sing-box*
|
||||
|
||||
22
.github/workflows/docker.yml
vendored
22
.github/workflows/docker.yml
vendored
@@ -1,10 +1,9 @@
|
||||
name: Build Docker Images
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "The tag version you want to build"
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -26,6 +25,15 @@ jobs:
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/sagernet/sing-box
|
||||
- name: Get tag to build
|
||||
id: tag
|
||||
run: |
|
||||
echo "latest=ghcr.io/sagernet/sing-box:latest" >> $GITHUB_OUTPUT
|
||||
if [[ -z "${{ github.event.inputs.tag }}" ]]; then
|
||||
echo "versioned=ghcr.io/sagernet/sing-box:${{ github.ref_name }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "versioned=ghcr.io/sagernet/sing-box:${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Build and release Docker images
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
@@ -34,6 +42,6 @@ jobs:
|
||||
build-args: |
|
||||
BUILDKIT_CONTEXT_KEEP_GIT_DIR=1
|
||||
tags: |
|
||||
ghcr.io/sagernet/sing-box:latest
|
||||
ghcr.io/sagernet/sing-box:${{ github.ref_name }}
|
||||
${{ steps.tag.outputs.latest }}
|
||||
${{ steps.tag.outputs.versioned }}
|
||||
push: true
|
||||
|
||||
8
.github/workflows/lint.yml
vendored
8
.github/workflows/lint.yml
vendored
@@ -25,10 +25,10 @@ jobs:
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ^1.21
|
||||
- name: Get latest go version
|
||||
id: version
|
||||
run: |
|
||||
echo go_version=$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g') >> $GITHUB_OUTPUT
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
|
||||
@@ -85,15 +85,8 @@ var GoBinPath string
|
||||
|
||||
func FindMobile() {
|
||||
goBin := filepath.Join(build.Default.GOPATH, "bin")
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
if !rw.FileExists(goBin + "/" + "gobind.exe") {
|
||||
log.Fatal("missing gomobile.exe installation")
|
||||
}
|
||||
} else {
|
||||
if !rw.FileExists(goBin + "/" + "gobind") {
|
||||
log.Fatal("missing gomobile installation")
|
||||
}
|
||||
if !rw.FileExists(goBin + "/" + "gobind") {
|
||||
log.Fatal("missing gomobile installation")
|
||||
}
|
||||
GoBinPath = goBin
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
@@ -8,7 +9,6 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -47,14 +47,10 @@ func compileRuleSet(sourcePath string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
content, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plainRuleSet, err := json.UnmarshalExtended[option.PlainRuleSetCompat](content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decoder := json.NewDecoder(json.NewCommentFilter(reader))
|
||||
decoder.DisallowUnknownFields()
|
||||
var plainRuleSet option.PlainRuleSetCompat
|
||||
err = decoder.Decode(&plainRuleSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -50,14 +50,18 @@ func formatRuleSet(sourcePath string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plainRuleSet, err := json.UnmarshalExtended[option.PlainRuleSetCompat](content)
|
||||
decoder := json.NewDecoder(json.NewCommentFilter(bytes.NewReader(content)))
|
||||
decoder.DisallowUnknownFields()
|
||||
var plainRuleSet option.PlainRuleSetCompat
|
||||
err = decoder.Decode(&plainRuleSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ruleSet := plainRuleSet.Upgrade()
|
||||
buffer := new(bytes.Buffer)
|
||||
encoder := json.NewEncoder(buffer)
|
||||
encoder.SetIndent("", " ")
|
||||
err = encoder.Encode(plainRuleSet)
|
||||
err = encoder.Encode(ruleSet)
|
||||
if err != nil {
|
||||
return E.Cause(err, "encode config")
|
||||
}
|
||||
|
||||
@@ -108,10 +108,6 @@ func (c *ReadWaitConn) WaitReadBuffer() (buffer *buf.Buffer, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ReadWaitConn) Upstream() any {
|
||||
return c.STDConn
|
||||
}
|
||||
|
||||
//go:linkname tlsReadRecord crypto/tls.(*Conn).readRecord
|
||||
func tlsReadRecord(c *tls.STDConn) error
|
||||
|
||||
|
||||
@@ -80,7 +80,6 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) {
|
||||
c.conn = nil
|
||||
c.err = E.Cause(err, "dial tcp fast open")
|
||||
}
|
||||
n = len(b)
|
||||
close(c.create)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -32,7 +32,3 @@ func (r *Reader) Lookup(addr netip.Addr) string {
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
func (r *Reader) Close() error {
|
||||
return r.reader.Close()
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ func writeDefaultRule(writer io.Writer, rule option.DefaultHeadlessRule) error {
|
||||
if len(rule.SourceIPCIDR) > 0 {
|
||||
err = writeRuleItemCIDR(writer, ruleItemSourceIPCIDR, rule.SourceIPCIDR)
|
||||
if err != nil {
|
||||
return E.Cause(err, "source_ip_cidr")
|
||||
return E.Cause(err, "source_ipcidr")
|
||||
}
|
||||
}
|
||||
if len(rule.IPCIDR) > 0 {
|
||||
|
||||
@@ -105,16 +105,5 @@ func startACME(ctx context.Context, options option.InboundACMEOptions) (*tls.Con
|
||||
},
|
||||
})
|
||||
config = certmagic.New(cache, *config)
|
||||
var tlsConfig *tls.Config
|
||||
if acmeConfig.DisableTLSALPNChallenge || acmeConfig.DNS01Solver != nil {
|
||||
tlsConfig = &tls.Config{
|
||||
GetCertificate: config.GetCertificate,
|
||||
}
|
||||
} else {
|
||||
tlsConfig = &tls.Config{
|
||||
GetCertificate: config.GetCertificate,
|
||||
NextProtos: []string{ACMETLS1Protocol},
|
||||
}
|
||||
}
|
||||
return tlsConfig, &acmeWrapper{ctx: ctx, cfg: config, cache: cache, domain: options.Domain}, nil
|
||||
return config.TLSConfig(), &acmeWrapper{ctx: ctx, cfg: config, cache: cache, domain: options.Domain}, nil
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
package tls
|
||||
|
||||
const ACMETLS1Protocol = "acme-tls/1"
|
||||
@@ -39,19 +39,11 @@ func (c *STDServerConfig) SetServerName(serverName string) {
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) NextProtos() []string {
|
||||
if c.acmeService != nil && len(c.config.NextProtos) > 1 && c.config.NextProtos[0] == ACMETLS1Protocol {
|
||||
return c.config.NextProtos[1:]
|
||||
} else {
|
||||
return c.config.NextProtos
|
||||
}
|
||||
return c.config.NextProtos
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) SetNextProtos(nextProto []string) {
|
||||
if c.acmeService != nil && len(c.config.NextProtos) > 1 && c.config.NextProtos[0] == ACMETLS1Protocol {
|
||||
c.config.NextProtos = append(c.config.NextProtos[:1], nextProto...)
|
||||
} else {
|
||||
c.config.NextProtos = nextProto
|
||||
}
|
||||
c.config.NextProtos = nextProto
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Config() (*STDConfig, error) {
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"net/http/pprof"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/common/humanize"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
@@ -48,20 +47,12 @@ func applyDebugListenOption(options option.DebugOptions) {
|
||||
encoder.SetIndent("", " ")
|
||||
encoder.Encode(memObject)
|
||||
})
|
||||
r.Route("/pprof", func(r chi.Router) {
|
||||
r.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
|
||||
if !strings.HasSuffix(request.URL.Path, "/") {
|
||||
http.Redirect(writer, request, request.URL.Path+"/", http.StatusMovedPermanently)
|
||||
} else {
|
||||
pprof.Index(writer, request)
|
||||
}
|
||||
})
|
||||
r.HandleFunc("/*", pprof.Index)
|
||||
r.HandleFunc("/cmdline", pprof.Cmdline)
|
||||
r.HandleFunc("/profile", pprof.Profile)
|
||||
r.HandleFunc("/symbol", pprof.Symbol)
|
||||
r.HandleFunc("/trace", pprof.Trace)
|
||||
})
|
||||
r.HandleFunc("/pprof", pprof.Index)
|
||||
r.HandleFunc("/pprof/*", pprof.Index)
|
||||
r.HandleFunc("/pprof/cmdline", pprof.Cmdline)
|
||||
r.HandleFunc("/pprof/profile", pprof.Profile)
|
||||
r.HandleFunc("/pprof/symbol", pprof.Symbol)
|
||||
r.HandleFunc("/pprof/trace", pprof.Trace)
|
||||
})
|
||||
debugHTTPServer = &http.Server{
|
||||
Addr: options.Listen,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build linux || darwin
|
||||
|
||||
package box
|
||||
|
||||
import (
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !(linux || darwin)
|
||||
//go:build !linux
|
||||
|
||||
package box
|
||||
|
||||
|
||||
@@ -2,154 +2,10 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.8.5
|
||||
#### 1.8.0-beta.8
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.8.4
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.8.2
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.8.1
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.8.0
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
Important changes since 1.7:
|
||||
|
||||
* Migrate cache file from Clash API to independent options **1**
|
||||
* Introducing [Rule Set](/configuration/rule-set/) **2**
|
||||
* Add `sing-box geoip`, `sing-box geosite` and `sing-box rule-set` commands **3**
|
||||
* Allow nested logical rules **4**
|
||||
* Independent `source_ip_is_private` and `ip_is_private` rules **5**
|
||||
* Add context to JSON decode error message **6**
|
||||
* Reject internal fake-ip queries **7**
|
||||
* Add GSO support for TUN and WireGuard system interface **8**
|
||||
* Add `idle_timeout` for URLTest outbound **9**
|
||||
* Add simple loopback detect
|
||||
* Optimize memory usage of idle connections
|
||||
* Update uTLS to 1.5.4 **10**
|
||||
* Update dependencies **11**
|
||||
|
||||
**1**:
|
||||
|
||||
See [Cache File](/configuration/experimental/cache-file/) and
|
||||
[Migration](/migration/#migrate-cache-file-from-clash-api-to-independent-options).
|
||||
|
||||
**2**:
|
||||
|
||||
Rule set is independent collections of rules that can be compiled into binaries to improve performance.
|
||||
Compared to legacy GeoIP and Geosite resources,
|
||||
it can include more types of rules, load faster,
|
||||
use less memory, and update automatically.
|
||||
|
||||
See [Route#rule_set](/configuration/route/#rule_set),
|
||||
[Route Rule](/configuration/route/rule/),
|
||||
[DNS Rule](/configuration/dns/rule/),
|
||||
[Rule Set](/configuration/rule-set/),
|
||||
[Source Format](/configuration/rule-set/source-format/) and
|
||||
[Headless Rule](/configuration/rule-set/headless-rule/).
|
||||
|
||||
For GEO resources migration, see [Migrate GeoIP to rule sets](/migration/#migrate-geoip-to-rule-sets) and
|
||||
[Migrate Geosite to rule sets](/migration/#migrate-geosite-to-rule-sets).
|
||||
|
||||
**3**:
|
||||
|
||||
New commands manage GeoIP, Geosite and rule set resources, and help you migrate GEO resources to rule sets.
|
||||
|
||||
**4**:
|
||||
|
||||
Logical rules in route rules, DNS rules, and the new headless rule now allow nesting of logical rules.
|
||||
|
||||
**5**:
|
||||
|
||||
The `private` GeoIP country never existed and was actually implemented inside V2Ray.
|
||||
Since GeoIP was deprecated, we made this rule independent, see [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
|
||||
**6**:
|
||||
|
||||
JSON parse errors will now include the current key path.
|
||||
Only takes effect when compiled with Go 1.21+.
|
||||
|
||||
**7**:
|
||||
|
||||
All internal DNS queries now skip DNS rules with `server` type `fakeip`,
|
||||
and the default DNS server can no longer be `fakeip`.
|
||||
|
||||
This change is intended to break incorrect usage and essentially requires no action.
|
||||
|
||||
**8**:
|
||||
|
||||
See [TUN](/configuration/inbound/tun/) inbound and [WireGuard](/configuration/outbound/wireguard/) outbound.
|
||||
|
||||
**9**:
|
||||
|
||||
When URLTest is idle for a certain period of time, the scheduled delay test will be paused.
|
||||
|
||||
**10**:
|
||||
|
||||
Added some new [fingerprints](/configuration/shared/tls#utls).
|
||||
Also, starting with this release, uTLS requires at least Go 1.20.
|
||||
|
||||
**11**:
|
||||
|
||||
Updated `cloudflare-tls`, `gomobile`, `smux`, `tfo-go` and `wireguard-go` to latest, `quic-go` to `0.40.1` and `gvisor` to `20231204.0`
|
||||
|
||||
|
||||
#### 1.8.0-rc.11
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.7.8
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.8.0-rc.10
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.7.7
|
||||
|
||||
* Fix V2Ray transport `path` validation behavior **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
See [V2Ray transport](/configuration/shared/v2ray-transport/).
|
||||
|
||||
#### 1.8.0-rc.7
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.8.0-rc.3
|
||||
|
||||
* Fix V2Ray transport `path` validation behavior **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
See [V2Ray transport](/configuration/shared/v2ray-transport/).
|
||||
|
||||
#### 1.7.6
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.8.0-rc.1
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.8.0-beta.9
|
||||
|
||||
* Add simple loopback detect
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.7.5
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
Maintained by Project S to provide a unified experience and platform-specific functionality.
|
||||
|
||||
| Platform | Client |
|
||||
|---------------------------------------|------------------------------------------|
|
||||
| Platform | Client |
|
||||
|---------------------------------------|-----------------------------------------|
|
||||
| :material-android: Android | [sing-box for Android](./android/) |
|
||||
| :material-apple: iOS/macOS/Apple tvOS | [sing-box for Apple platforms](./apple/) |
|
||||
| :material-laptop: Desktop | Working in progress |
|
||||
| :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
|
||||
motivation of the maintainers of such projects is to acquire more users, and even though they provide friendly VPN
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
|
||||
### 字段
|
||||
|
||||
| 键 | 格式 |
|
||||
|----------|-------------------------|
|
||||
| 键 | 格式 |
|
||||
|----------|------------------------|
|
||||
| `server` | 一组 [DNS 服务器](./server/) |
|
||||
| `rules` | 一组 [DNS 规则](./rule/) |
|
||||
|
||||
|
||||
@@ -30,17 +30,17 @@ The tag of the dns server.
|
||||
|
||||
The address of the dns server.
|
||||
|
||||
| Protocol | Format |
|
||||
|--------------------------------------|-------------------------------|
|
||||
| `System` | `local` |
|
||||
| `TCP` | `tcp://1.0.0.1` |
|
||||
| `UDP` | `8.8.8.8` `udp://8.8.4.4` |
|
||||
| `TLS` | `tls://dns.google` |
|
||||
| `HTTPS` | `https://1.1.1.1/dns-query` |
|
||||
| `QUIC` | `quic://dns.adguard.com` |
|
||||
| `HTTP3` | `h3://8.8.8.8/dns-query` |
|
||||
| `RCode` | `rcode://refused` |
|
||||
| `DHCP` | `dhcp://auto` or `dhcp://en0` |
|
||||
| Protocol | Format |
|
||||
|-------------------------------------|-------------------------------|
|
||||
| `System` | `local` |
|
||||
| `TCP` | `tcp://1.0.0.1` |
|
||||
| `UDP` | `8.8.8.8` `udp://8.8.4.4` |
|
||||
| `TLS` | `tls://dns.google` |
|
||||
| `HTTPS` | `https://1.1.1.1/dns-query` |
|
||||
| `QUIC` | `quic://dns.adguard.com` |
|
||||
| `HTTP3` | `h3://8.8.8.8/dns-query` |
|
||||
| `RCode` | `rcode://refused` |
|
||||
| `DHCP` | `dhcp://auto` or `dhcp://en0` |
|
||||
| [FakeIP](/configuration/dns/fakeip/) | `fakeip` |
|
||||
|
||||
!!! warning ""
|
||||
|
||||
@@ -30,17 +30,17 @@ DNS 服务器的标签。
|
||||
|
||||
DNS 服务器的地址。
|
||||
|
||||
| 协议 | 格式 |
|
||||
|--------------------------------------|------------------------------|
|
||||
| `System` | `local` |
|
||||
| `TCP` | `tcp://1.0.0.1` |
|
||||
| `UDP` | `8.8.8.8` `udp://8.8.4.4` |
|
||||
| `TLS` | `tls://dns.google` |
|
||||
| `HTTPS` | `https://1.1.1.1/dns-query` |
|
||||
| `QUIC` | `quic://dns.adguard.com` |
|
||||
| `HTTP3` | `h3://8.8.8.8/dns-query` |
|
||||
| `RCode` | `rcode://refused` |
|
||||
| `DHCP` | `dhcp://auto` 或 `dhcp://en0` |
|
||||
| 协议 | 格式 |
|
||||
|-------------------------------------|------------------------------|
|
||||
| `System` | `local` |
|
||||
| `TCP` | `tcp://1.0.0.1` |
|
||||
| `UDP` | `8.8.8.8` `udp://8.8.4.4` |
|
||||
| `TLS` | `tls://dns.google` |
|
||||
| `HTTPS` | `https://1.1.1.1/dns-query` |
|
||||
| `QUIC` | `quic://dns.adguard.com` |
|
||||
| `HTTP3` | `h3://8.8.8.8/dns-query` |
|
||||
| `RCode` | `rcode://refused` |
|
||||
| `DHCP` | `dhcp://auto` 或 `dhcp://en0` |
|
||||
| [FakeIP](/configuration/dns/fakeip/) | `fakeip` |
|
||||
|
||||
!!! warning ""
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
|
||||
### Fields
|
||||
|
||||
| Type | Format | Injectable |
|
||||
|---------------|-------------------------------|------------|
|
||||
| Type | Format | Injectable |
|
||||
|---------------|------------------------------|------------|
|
||||
| `direct` | [Direct](./direct/) | X |
|
||||
| `mixed` | [Mixed](./mixed/) | TCP |
|
||||
| `socks` | [SOCKS](./socks/) | TCP |
|
||||
|
||||
@@ -39,7 +39,6 @@ icon: material/alert-decagram
|
||||
"fc00::/7"
|
||||
],
|
||||
"endpoint_independent_nat": false,
|
||||
"udp_timeout": "5m",
|
||||
"stack": "system",
|
||||
"include_interface": [
|
||||
"lan0"
|
||||
|
||||
@@ -39,7 +39,6 @@ icon: material/alert-decagram
|
||||
"fc00::/7"
|
||||
],
|
||||
"endpoint_independent_nat": false,
|
||||
"udp_timeout": "5m",
|
||||
"stack": "system",
|
||||
"include_interface": [
|
||||
"lan0"
|
||||
|
||||
@@ -18,8 +18,8 @@ sing-box uses JSON for configuration files.
|
||||
|
||||
### Fields
|
||||
|
||||
| Key | Format |
|
||||
|----------------|---------------------------------|
|
||||
| Key | Format |
|
||||
|----------------|--------------------------------|
|
||||
| `log` | [Log](./log/) |
|
||||
| `dns` | [DNS](./dns/) |
|
||||
| `ntp` | [NTP](./ntp/) |
|
||||
|
||||
@@ -17,8 +17,8 @@ sing-box 使用 JSON 作为配置文件格式。
|
||||
|
||||
### 字段
|
||||
|
||||
| Key | Format |
|
||||
|----------------|------------------------|
|
||||
| Key | Format |
|
||||
|----------------|-----------------------|
|
||||
| `log` | [日志](./log/) |
|
||||
| `dns` | [DNS](./dns/) |
|
||||
| `inbounds` | [入站](./inbound/) |
|
||||
|
||||
@@ -30,14 +30,15 @@ icon: material/alert-decagram
|
||||
|
||||
### 字段
|
||||
|
||||
| 键 | 格式 |
|
||||
|-----------|-----------------------|
|
||||
| `geoip` | [GeoIP](./geoip/) |
|
||||
| `geosite` | [Geosite](./geosite/) |
|
||||
| 键 | 格式 |
|
||||
|------------|-----------------------------------|
|
||||
| `geoip` | [GeoIP](./geoip/) |
|
||||
| `geosite` | [Geosite](./geosite/) |
|
||||
|
||||
|
||||
#### rule
|
||||
|
||||
一组 [路由规则](./rule/) 。
|
||||
一组 [路由规则](./rule/)。
|
||||
|
||||
#### rule_set
|
||||
|
||||
|
||||
@@ -31,4 +31,4 @@ Version of Rule Set, must be `1`.
|
||||
|
||||
==Required==
|
||||
|
||||
List of [Headless Rule](./headless-rule.md/).
|
||||
List of [Headless Rule](./headless-rule/).
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"tcp_fast_open": false,
|
||||
"tcp_multi_path": false,
|
||||
"udp_fragment": false,
|
||||
"udp_timeout": "5m",
|
||||
"udp_timeout": 300,
|
||||
"detour": "another-in",
|
||||
"sniff": false,
|
||||
"sniff_override_destination": false,
|
||||
@@ -19,14 +19,14 @@
|
||||
|
||||
### Fields
|
||||
|
||||
| Field | Available Context |
|
||||
|--------------------------------|---------------------------------------------------------|
|
||||
| `listen` | Needs to listen on TCP or UDP. |
|
||||
| `listen_port` | Needs to listen on TCP or UDP. |
|
||||
| `tcp_fast_open` | Needs to listen on TCP. |
|
||||
| `tcp_multi_path` | Needs to listen on TCP. |
|
||||
| `udp_timeout` | Needs to assemble UDP connections. |
|
||||
| `udp_disable_domain_unmapping` | Needs to listen on UDP and accept domain UDP addresses. |
|
||||
| Field | Available Context |
|
||||
|--------------------------------|-------------------------------------------------------------------|
|
||||
| `listen` | Needs to listen on TCP or UDP. |
|
||||
| `listen_port` | Needs to listen on TCP or UDP. |
|
||||
| `tcp_fast_open` | Needs to listen on TCP. |
|
||||
| `tcp_multi_path` | Needs to listen on TCP. |
|
||||
| `udp_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. |
|
||||
| `udp_disable_domain_unmapping` | Needs to listen on UDP and accept domain UDP addresses. |
|
||||
|
||||
#### listen
|
||||
|
||||
@@ -56,9 +56,7 @@ Enable UDP fragmentation.
|
||||
|
||||
#### udp_timeout
|
||||
|
||||
UDP NAT expiration time in seconds.
|
||||
|
||||
`5m` is used by default.
|
||||
UDP NAT expiration time in seconds, default is 300 (5 minutes).
|
||||
|
||||
#### detour
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"tcp_fast_open": false,
|
||||
"tcp_multi_path": false,
|
||||
"udp_fragment": false,
|
||||
"udp_timeout": "5m",
|
||||
"udp_timeout": 300,
|
||||
"detour": "another-in",
|
||||
"sniff": false,
|
||||
"sniff_override_destination": false,
|
||||
@@ -18,13 +18,13 @@
|
||||
```
|
||||
|
||||
|
||||
| 字段 | 可用上下文 |
|
||||
|------------------|-----------------|
|
||||
| `listen` | 需要监听 TCP 或 UDP。 |
|
||||
| `listen_port` | 需要监听 TCP 或 UDP。 |
|
||||
| `tcp_fast_open` | 需要监听 TCP。 |
|
||||
| `tcp_multi_path` | 需要监听 TCP。 |
|
||||
| `udp_timeout` | 需要组装 UDP 连接。 |
|
||||
| 字段 | 可用上下文 |
|
||||
|-----------------------------------|-------------------------------------|
|
||||
| `listen` | 需要监听 TCP 或 UDP。 |
|
||||
| `listen_port` | 需要监听 TCP 或 UDP。 |
|
||||
| `tcp_fast_open` | 需要监听 TCP。 |
|
||||
| `tcp_multi_path` | 需要监听 TCP。 |
|
||||
| `udp_timeout` | 需要组装 UDP 连接, 当前为 Tun 和 Shadowsocks。 |
|
||||
|
|
||||
|
||||
### 字段
|
||||
@@ -57,9 +57,7 @@
|
||||
|
||||
#### udp_timeout
|
||||
|
||||
UDP NAT 过期时间,以秒为单位。
|
||||
|
||||
默认使用 `5m`。
|
||||
UDP NAT 过期时间,以秒为单位,默认为 300(5 分钟)。
|
||||
|
||||
#### detour
|
||||
|
||||
|
||||
@@ -173,9 +173,10 @@ By default, the maximum version is currently TLS 1.3.
|
||||
|
||||
#### cipher_suites
|
||||
|
||||
A list of enabled TLS 1.0–1.2 cipher suites. The order of the list is ignored. Note that TLS 1.3 cipher suites are not configurable.
|
||||
The elliptic curves that will be used in an ECDHE handshake, in preference order.
|
||||
|
||||
If empty, a safe default list is used. The default cipher suites might change over time.
|
||||
If empty, the default will be used. The client will use the first preference as the type for its key share in TLS 1.3.
|
||||
This may change in the future.
|
||||
|
||||
#### certificate
|
||||
|
||||
|
||||
@@ -170,9 +170,12 @@ TLS 版本值:
|
||||
|
||||
#### cipher_suites
|
||||
|
||||
启用的 TLS 1.0-1.2密码套件的列表。列表的顺序被忽略。请注意,TLS 1.3 的密码套件是不可配置的。
|
||||
将在 ECDHE 握手中使用的椭圆曲线,按优先顺序排列。
|
||||
|
||||
如果为空,则使用安全的默认列表。默认密码套件可能会随着时间的推移而改变。
|
||||
如果为空,将使用默认值。
|
||||
|
||||
客户端将使用第一个首选项作为其在 TLS 1.3 中的密钥共享类型。
|
||||
这在未来可能会改变。
|
||||
|
||||
#### certificate
|
||||
|
||||
|
||||
@@ -53,15 +53,9 @@ The client will choose randomly and the server will verify if not empty.
|
||||
|
||||
#### path
|
||||
|
||||
!!! warning
|
||||
|
||||
V2Ray's documentation says that the path between the server and the client must be consistent,
|
||||
but the actual code allows the client to add any suffix to the path.
|
||||
sing-box uses the same behavior as V2Ray, but note that the behavior does not exist in `WebSocket` and `HTTPUpgrade` transport.
|
||||
|
||||
Path of HTTP request.
|
||||
|
||||
The server will verify.
|
||||
The server will verify if not empty.
|
||||
|
||||
#### method
|
||||
|
||||
@@ -83,10 +77,7 @@ Specifies the time until idle clients should be closed with a GOAWAY frame. PING
|
||||
|
||||
In HTTP2 client:
|
||||
|
||||
Specifies the period of time after which a health check will be performed using a ping frame if no frames have been
|
||||
received on the connection.Please note that a ping response is considered a received frame, so if there is no other
|
||||
traffic on the connection, the health check will be executed every interval. If the value is zero, no health check will
|
||||
be performed.
|
||||
Specifies the period of time after which a health check will be performed using a ping frame if no frames have been received on the connection. Please note that a ping response is considered a received frame, so if there is no other traffic on the connection, the health check will be executed every interval. If the value is zero, no health check will be performed.
|
||||
|
||||
Zero is used by default.
|
||||
|
||||
@@ -94,9 +85,7 @@ Zero is used by default.
|
||||
|
||||
In HTTP2 client:
|
||||
|
||||
Specifies the timeout duration after sending a PING frame, within which a response must be received.
|
||||
If a response to the PING frame is not received within the specified timeout duration, the connection will be closed.
|
||||
The default timeout duration is 15 seconds.
|
||||
Specifies the timeout duration after sending a PING frame, within which a response must be received. If a response to the PING frame is not received within the specified timeout duration, the connection will be closed. The default timeout duration is 15 seconds.
|
||||
|
||||
### WebSocket
|
||||
|
||||
@@ -114,14 +103,12 @@ The default timeout duration is 15 seconds.
|
||||
|
||||
Path of HTTP request.
|
||||
|
||||
The server will verify.
|
||||
The server will verify if not empty.
|
||||
|
||||
#### headers
|
||||
|
||||
Extra headers of HTTP request.
|
||||
|
||||
The server will write in response if not empty.
|
||||
|
||||
#### max_early_data
|
||||
|
||||
Allowed payload size is in the request. Enabled if not zero.
|
||||
@@ -171,8 +158,7 @@ Service name of gRPC.
|
||||
|
||||
In standard gRPC server/client:
|
||||
|
||||
If the transport doesn't see any activity after a duration of this time,
|
||||
it pings the client to check if the connection is still active.
|
||||
If the transport doesn't see any activity after a duration of this time, it pings the client to check if the connection is still active.
|
||||
|
||||
In default gRPC server/client:
|
||||
|
||||
@@ -182,8 +168,7 @@ It has the same behavior as the corresponding setting in HTTP transport.
|
||||
|
||||
In standard gRPC server/client:
|
||||
|
||||
The timeout that after performing a keepalive check, the client will wait for activity.
|
||||
If no activity is detected, the connection will be closed.
|
||||
The timeout that after performing a keepalive check, the client will wait for activity. If no activity is detected, the connection will be closed.
|
||||
|
||||
In default gRPC server/client:
|
||||
|
||||
@@ -193,9 +178,7 @@ It has the same behavior as the corresponding setting in HTTP transport.
|
||||
|
||||
In standard gRPC client:
|
||||
|
||||
If enabled, the client transport sends keepalive pings even with no active connections.
|
||||
If disabled, when there are no active connections, `idle_timeout` and `ping_timeout` will be ignored and no keepalive
|
||||
pings will be sent.
|
||||
If enabled, the client transport sends keepalive pings even with no active connections. If disabled, when there are no active connections, `idle_timeout` and `ping_timeout` will be ignored and no keepalive pings will be sent.
|
||||
|
||||
Disabled by default.
|
||||
|
||||
@@ -220,7 +203,7 @@ The server will verify if not empty.
|
||||
|
||||
Path of HTTP request.
|
||||
|
||||
The server will verify.
|
||||
The server will verify if not empty.
|
||||
|
||||
#### headers
|
||||
|
||||
|
||||
@@ -48,30 +48,25 @@ V2Ray Transport 是 v2ray 发明的一组私有协议,并污染了其他协议
|
||||
|
||||
主机域名列表。
|
||||
|
||||
如果设置,客户端将随机选择,服务器将验证。
|
||||
客户端将随机选择,默认服务器将验证。
|
||||
|
||||
#### path
|
||||
|
||||
!!! warning
|
||||
|
||||
V2Ray 文档称服务端和客户端的路径必须一致,但实际代码允许客户端向路径添加任何后缀。
|
||||
sing-box 使用与 V2Ray 相同的行为,但请注意,该行为在 `WebSocket` 和 `HTTPUpgrade` 传输层中不存在。
|
||||
|
||||
HTTP 请求路径
|
||||
|
||||
服务器将验证。
|
||||
默认服务器将验证。
|
||||
|
||||
#### method
|
||||
|
||||
HTTP 请求方法
|
||||
|
||||
如果设置,服务器将验证。
|
||||
默认服务器将验证。
|
||||
|
||||
#### headers
|
||||
|
||||
HTTP 请求的额外标头
|
||||
|
||||
如果设置,服务器将写入响应。
|
||||
默认服务器将写入响应。
|
||||
|
||||
#### idle_timeout
|
||||
|
||||
@@ -107,13 +102,11 @@ HTTP 请求的额外标头
|
||||
|
||||
HTTP 请求路径
|
||||
|
||||
服务器将验证。
|
||||
默认服务器将验证。
|
||||
|
||||
#### headers
|
||||
|
||||
HTTP 请求的额外标头
|
||||
|
||||
如果设置,服务器将写入响应。
|
||||
HTTP 请求的额外标头。
|
||||
|
||||
#### max_early_data
|
||||
|
||||
@@ -203,16 +196,16 @@ gRPC 服务名称。
|
||||
|
||||
主机域名。
|
||||
|
||||
服务器将验证。
|
||||
默认服务器将验证。
|
||||
|
||||
#### path
|
||||
|
||||
HTTP 请求路径
|
||||
|
||||
服务器将验证。
|
||||
默认服务器将验证。
|
||||
|
||||
#### headers
|
||||
|
||||
HTTP 请求的额外标头。
|
||||
|
||||
如果设置,服务器将写入响应。
|
||||
默认服务器将写入响应。
|
||||
|
||||
@@ -19,7 +19,7 @@ The maxmind GeoIP National Database, as an IP classification database,
|
||||
is not entirely suitable for traffic bypassing,
|
||||
and all existing implementations suffer from high memory usage and difficult management.
|
||||
|
||||
sing-box 1.8.0 introduces [Rule Set](/configuration/rule-set/), which can completely replace GeoIP,
|
||||
sing-box 1.8.0 introduces [Rule Set](/configuration/rule_set/), which can completely replace GeoIP,
|
||||
check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
|
||||
#### Geosite
|
||||
@@ -29,7 +29,7 @@ Geosite is deprecated and may be removed in the future.
|
||||
Geosite, the `domain-list-community` project maintained by V2Ray as an early traffic bypassing solution,
|
||||
suffers from a number of problems, including lack of maintenance, inaccurate rules, and difficult management.
|
||||
|
||||
sing-box 1.8.0 introduces [Rule Set](/configuration/rule-set/), which can completely replace Geosite,
|
||||
sing-box 1.8.0 introduces [Rule Set](/configuration/rule_set/), which can completely replace Geosite,
|
||||
check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
||||
|
||||
Geosite,即由 V2Ray 维护的 domain-list-community 项目,作为早期流量绕过解决方案,存在着大量问题,包括缺少维护、规则不准确、管理困难。
|
||||
|
||||
@@ -18,7 +18,7 @@ GeoIP 已废弃且可能在不久的将来移除。
|
||||
maxmind GeoIP 国家数据库作为 IP 分类数据库,不完全适合流量绕过,
|
||||
且现有的实现均存在内存使用大与管理困难的问题。
|
||||
|
||||
sing-box 1.8.0 引入了[规则集](/configuration/rule-set/),
|
||||
sing-box 1.8.0 引入了[规则集](/configuration/rule_set/),
|
||||
可以完全替代 GeoIP, 参阅 [迁移指南](/zh/migration/#geoip)。
|
||||
|
||||
#### Geosite
|
||||
@@ -28,7 +28,7 @@ Geosite 已废弃且可能在不久的将来移除。
|
||||
Geosite,即由 V2Ray 维护的 domain-list-community 项目,作为早期流量绕过解决方案,
|
||||
存在着包括缺少维护、规则不准确和管理困难内的大量问题。
|
||||
|
||||
sing-box 1.8.0 引入了[规则集](/configuration/rule-set/),
|
||||
sing-box 1.8.0 引入了[规则集](/configuration/rule_set/),
|
||||
可以完全替代 Geosite,参阅 [迁移指南](/zh/migration/#geosite)。
|
||||
|
||||
## 1.6.0
|
||||
|
||||
@@ -53,19 +53,19 @@ go build -tags "tag_a tag_b" ./cmd/sing-box
|
||||
|
||||
## :material-folder-settings: Build Tags
|
||||
|
||||
| Build Tag | Enabled by default | Description |
|
||||
|------------------------------------|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 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_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/). |
|
||||
|
||||
It is not recommended to change the default build tag list unless you really know what you are adding.
|
||||
|
||||
@@ -28,18 +28,18 @@ icon: material/package
|
||||
|
||||
=== ":material-linux: Linux"
|
||||
|
||||
| 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: |
|
||||
| Type | Platform | Link | Command | Actively maintained |
|
||||
|----------|--------------------|---------------------|------------------------------|---------------------|
|
||||
| AUR | (Linux) Arch Linux | [sing-box][aur] ᴬᵁᴿ | `? -S sing-box` | :material-check: |
|
||||
| nixpkgs | (Linux) NixOS | [sing-box][nixpkgs] | `nix-env -iA nixos.sing-box` | :material-check: |
|
||||
| Homebrew | macOS / Linux | [sing-box][brew] | `brew install sing-box` | :material-check: |
|
||||
| Alpine | (Linux) Alpine | [sing-box][alpine] | `apk add sing-box` | :material-alert: |
|
||||
|
||||
=== ":material-apple: macOS"
|
||||
|
||||
| Type | Platform | Link | Command | Actively maintained |
|
||||
|----------|----------|------------------|-------------------------|---------------------|
|
||||
| Homebrew | macOS | [sing-box][brew] | `brew install sing-box` | :material-check: |
|
||||
| Type | Platform | Link | Command | Actively maintained |
|
||||
|----------|---------------|------------------|-------------------------|---------------------|
|
||||
| Homebrew | macOS / Linux | [sing-box][brew] | `brew install sing-box` | :material-check: |
|
||||
|
||||
=== ":material-microsoft-windows: Windows"
|
||||
|
||||
@@ -55,12 +55,6 @@ icon: material/package
|
||||
|------------|--------------------|---------------------|------------------------------|---------------------|
|
||||
| Termux | Android | [sing-box][termux] | `pkg add sing-box` | :material-check: |
|
||||
|
||||
=== ":material-freebsd: FreeBSD"
|
||||
|
||||
| Type | Platform | Link | Command | Actively maintained |
|
||||
|------------|----------|-------------------|------------------------|---------------------|
|
||||
| FreshPorts | FreeBSD | [sing-box][ports] | `pkg install sing-box` | :material-alert: |
|
||||
|
||||
## :material-book-multiple: Service Management
|
||||
|
||||
For Linux systems with [systemd][systemd], usually the installation already includes a sing-box service,
|
||||
@@ -83,20 +77,14 @@ you can manage the service using the following command:
|
||||
|
||||
[nixpkgs]: https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/tools/networking/sing-box/default.nix
|
||||
|
||||
[termux]: https://github.com/termux/termux-packages/tree/master/packages/sing-box
|
||||
|
||||
[brew]: https://formulae.brew.sh/formula/sing-box
|
||||
|
||||
[openwrt]: https://github.com/openwrt/packages/tree/master/net/sing-box
|
||||
|
||||
[immortalwrt]: https://github.com/immortalwrt/packages/tree/master/net/sing-box
|
||||
|
||||
[choco]: https://chocolatey.org/packages/sing-box
|
||||
|
||||
[scoop]: https://github.com/ScoopInstaller/Main/blob/master/bucket/sing-box.json
|
||||
|
||||
[winget]: https://github.com/microsoft/winget-pkgs/tree/master/manifests/s/SagerNet/sing-box
|
||||
|
||||
[termux]: https://github.com/termux/termux-packages/tree/master/packages/sing-box
|
||||
|
||||
[ports]: https://www.freshports.org/net/sing-box
|
||||
|
||||
[systemd]: https://systemd.io/
|
||||
[systemd]: https://systemd.io/
|
||||
@@ -28,18 +28,18 @@ icon: material/package
|
||||
|
||||
=== ":material-linux: Linux"
|
||||
|
||||
| 类型 | 平台 | 链接 | 命令 | 活跃维护 |
|
||||
|----------|------------|---------------------|------------------------------|------------------|
|
||||
| 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: |
|
||||
| 类型 | 平台 | 链接 | 命令 | 活跃维护 |
|
||||
|----------|--------------------|---------------------|------------------------------|------------------|
|
||||
| AUR | (Linux) Arch Linux | [sing-box][aur] ᴬᵁᴿ | `? -S sing-box` | :material-check: |
|
||||
| nixpkgs | (Linux) NixOS | [sing-box][nixpkgs] | `nix-env -iA nixos.sing-box` | :material-check: |
|
||||
| Homebrew | macOS / Linux | [sing-box][brew] | `brew install sing-box` | :material-check: |
|
||||
| Alpine | (Linux) Alpine | [sing-box][alpine] | `apk add sing-box` | :material-alert: |
|
||||
|
||||
=== ":material-apple: macOS"
|
||||
|
||||
| 类型 | 平台 | 链接 | 命令 | 活跃维护 |
|
||||
|----------|-------|------------------|-------------------------|------------------|
|
||||
| Homebrew | macOS | [sing-box][brew] | `brew install sing-box` | :material-check: |
|
||||
| 类型 | 平台 | 链接 | 命令 | 活跃维护 |
|
||||
|----------|---------------|------------------|-------------------------|------------------|
|
||||
| Homebrew | macOS / Linux | [sing-box][brew] | `brew install sing-box` | :material-check: |
|
||||
|
||||
=== ":material-microsoft-windows: Windows"
|
||||
|
||||
@@ -55,12 +55,6 @@ icon: material/package
|
||||
|--------|---------|--------------------|--------------------|------------------|
|
||||
| Termux | Android | [sing-box][termux] | `pkg add sing-box` | :material-check: |
|
||||
|
||||
=== ":material-freebsd: FreeBSD"
|
||||
|
||||
| 类型 | 平台 | 链接 | 命令 | 活跃维护 |
|
||||
|------------|---------|-------------------|------------------------|------------------|
|
||||
| FreshPorts | FreeBSD | [sing-box][ports] | `pkg install sing-box` | :material-alert: |
|
||||
|
||||
## :material-book-multiple: 服务管理
|
||||
|
||||
对于带有 [systemd][systemd] 的 Linux 系统,通常安装已经包含 sing-box 服务,
|
||||
@@ -83,6 +77,8 @@ icon: material/package
|
||||
|
||||
[nixpkgs]: https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/tools/networking/sing-box/default.nix
|
||||
|
||||
[termux]: https://github.com/termux/termux-packages/tree/master/packages/sing-box
|
||||
|
||||
[brew]: https://formulae.brew.sh/formula/sing-box
|
||||
|
||||
[choco]: https://chocolatey.org/packages/sing-box
|
||||
@@ -91,8 +87,4 @@ icon: material/package
|
||||
|
||||
[winget]: https://github.com/microsoft/winget-pkgs/tree/master/manifests/s/SagerNet/sing-box
|
||||
|
||||
[termux]: https://github.com/termux/termux-packages/tree/master/packages/sing-box
|
||||
|
||||
[ports]: https://www.freshports.org/net/sing-box
|
||||
|
||||
[systemd]: https://systemd.io/
|
||||
[systemd]: https://systemd.io/
|
||||
@@ -322,7 +322,20 @@ flowchart TB
|
||||
"server": "google"
|
||||
},
|
||||
{
|
||||
"geosite": "geolocation-cn",
|
||||
"type": "logical",
|
||||
"mode": "and",
|
||||
"rules": [
|
||||
{
|
||||
"geosite": "geolocation-!cn",
|
||||
"invert": true
|
||||
},
|
||||
{
|
||||
"geosite": [
|
||||
"cn",
|
||||
"category-companies@cn"
|
||||
],
|
||||
}
|
||||
],
|
||||
"server": "local"
|
||||
}
|
||||
]
|
||||
@@ -364,7 +377,20 @@ flowchart TB
|
||||
"server": "google"
|
||||
},
|
||||
{
|
||||
"rule_set": "geosite-geolocation-cn",
|
||||
"type": "logical",
|
||||
"mode": "and",
|
||||
"rules": [
|
||||
{
|
||||
"rule_set": "geosite-geolocation-!cn",
|
||||
"invert": true
|
||||
},
|
||||
{
|
||||
"rule_set": [
|
||||
"geosite-cn",
|
||||
"geosite-category-companies@cn"
|
||||
]
|
||||
}
|
||||
],
|
||||
"server": "local"
|
||||
}
|
||||
]
|
||||
@@ -373,9 +399,21 @@ flowchart TB
|
||||
"rule_set": [
|
||||
{
|
||||
"type": "remote",
|
||||
"tag": "geosite-geolocation-cn",
|
||||
"tag": "geosite-cn",
|
||||
"format": "binary",
|
||||
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-geolocation-cn.srs"
|
||||
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-cn.srs"
|
||||
},
|
||||
{
|
||||
"type": "remote",
|
||||
"tag": "geosite-geolocation-!cn",
|
||||
"format": "binary",
|
||||
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-geolocation-!cn.srs"
|
||||
},
|
||||
{
|
||||
"type": "remote",
|
||||
"tag": "geosite-category-companies@cn",
|
||||
"format": "binary",
|
||||
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-category-companies@cn.srs"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -441,7 +479,21 @@ flowchart TB
|
||||
"outbound": "block"
|
||||
},
|
||||
{
|
||||
"geosite": "geolocation-cn",
|
||||
"type": "logical",
|
||||
"mode": "and",
|
||||
"rules": [
|
||||
{
|
||||
"geosite": "geolocation-!cn",
|
||||
"invert": true
|
||||
},
|
||||
{
|
||||
"geosite": [
|
||||
"cn",
|
||||
"category-companies@cn"
|
||||
],
|
||||
"geoip": "cn"
|
||||
}
|
||||
],
|
||||
"outbound": "direct"
|
||||
}
|
||||
]
|
||||
@@ -508,9 +560,20 @@ flowchart TB
|
||||
"outbound": "block"
|
||||
},
|
||||
{
|
||||
"rule_set": [
|
||||
"geoip-cn",
|
||||
"geosite-geolocation-cn"
|
||||
"type": "logical",
|
||||
"mode": "and",
|
||||
"rules": [
|
||||
{
|
||||
"rule_set": "geosite-geolocation-!cn",
|
||||
"invert": true
|
||||
},
|
||||
{
|
||||
"rule_set": [
|
||||
"geoip-cn",
|
||||
"geosite-cn",
|
||||
"geosite-category-companies@cn"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outbound": "direct"
|
||||
}
|
||||
@@ -524,9 +587,21 @@ flowchart TB
|
||||
},
|
||||
{
|
||||
"type": "remote",
|
||||
"tag": "geosite-geolocation-cn",
|
||||
"tag": "geosite-cn",
|
||||
"format": "binary",
|
||||
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-geolocation-cn.srs"
|
||||
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-cn.srs"
|
||||
},
|
||||
{
|
||||
"type": "remote",
|
||||
"tag": "geosite-geolocation-!cn",
|
||||
"format": "binary",
|
||||
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-geolocation-!cn.srs"
|
||||
},
|
||||
{
|
||||
"type": "remote",
|
||||
"tag": "geosite-category-companies@cn",
|
||||
"format": "binary",
|
||||
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-category-companies@cn.srs"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@ icon: material/arrange-bring-forward
|
||||
|
||||
## 1.8.0
|
||||
|
||||
!!! warning "Unstable"
|
||||
|
||||
This version is still under development, and the following migration guide may be changed in the future.
|
||||
|
||||
### :material-close-box: Migrate cache file from Clash API to independent options
|
||||
|
||||
!!! info "References"
|
||||
|
||||
@@ -4,6 +4,10 @@ icon: material/arrange-bring-forward
|
||||
|
||||
## 1.8.0
|
||||
|
||||
!!! warning "不稳定的"
|
||||
|
||||
该版本仍在开发中,迁移指南可能将在未来更改。
|
||||
|
||||
### :material-close-box: 将缓存文件从 Clash API 迁移到独立选项
|
||||
|
||||
!!! info "参考"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package trafficontrol
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/netip"
|
||||
"time"
|
||||
@@ -9,7 +10,6 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
|
||||
@@ -93,11 +93,13 @@ func (s *CommandServer) listenUNIX() error {
|
||||
if err != nil {
|
||||
return E.Cause(err, "listen ", sockPath)
|
||||
}
|
||||
err = os.Chown(sockPath, sUserID, sGroupID)
|
||||
if err != nil {
|
||||
listener.Close()
|
||||
os.Remove(sockPath)
|
||||
return E.Cause(err, "chown")
|
||||
if sUserID > 0 {
|
||||
err = os.Chown(sockPath, sUserID, sGroupID)
|
||||
if err != nil {
|
||||
listener.Close()
|
||||
os.Remove(sockPath)
|
||||
return E.Cause(err, "chown")
|
||||
}
|
||||
}
|
||||
s.listener = listener
|
||||
go s.loopConnection(listener)
|
||||
|
||||
@@ -20,11 +20,13 @@ func RedirectStderr(path string) error {
|
||||
return err
|
||||
}
|
||||
if runtime.GOOS != "android" {
|
||||
err = outputFile.Chown(sUserID, sGroupID)
|
||||
if err != nil {
|
||||
outputFile.Close()
|
||||
os.Remove(outputFile.Name())
|
||||
return err
|
||||
if sUserID > 0 {
|
||||
err = outputFile.Chown(sUserID, sGroupID)
|
||||
if err != nil {
|
||||
outputFile.Close()
|
||||
os.Remove(outputFile.Name())
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
err = unix.Dup2(int(outputFile.Fd()), int(os.Stderr.Fd()))
|
||||
|
||||
@@ -97,17 +97,6 @@ func (m *platformDefaultInterfaceMonitor) UnregisterCallback(element *list.Eleme
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32) {
|
||||
if interfaceName == "" || interfaceIndex32 == -1 {
|
||||
m.defaultInterfaceName = ""
|
||||
m.defaultInterfaceIndex = -1
|
||||
m.access.Lock()
|
||||
callbacks := m.callbacks.Array()
|
||||
m.access.Unlock()
|
||||
for _, callback := range callbacks {
|
||||
callback(tun.EventNoRoute)
|
||||
}
|
||||
return
|
||||
}
|
||||
var err error
|
||||
if m.iif.UsePlatformInterfaceGetter() {
|
||||
err = m.updateInterfacesPlatform()
|
||||
@@ -121,6 +110,28 @@ func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName s
|
||||
m.logger.Error(E.Cause(err, "update interfaces"))
|
||||
}
|
||||
interfaceIndex := int(interfaceIndex32)
|
||||
if interfaceName == "" {
|
||||
for _, netIf := range m.networkAddresses {
|
||||
if netIf.interfaceIndex == interfaceIndex {
|
||||
interfaceName = netIf.interfaceName
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if interfaceIndex == -1 {
|
||||
for _, netIf := range m.networkAddresses {
|
||||
if netIf.interfaceName == interfaceName {
|
||||
interfaceIndex = netIf.interfaceIndex
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if interfaceName == "" {
|
||||
m.logger.Error(E.New("invalid interface name for ", interfaceIndex))
|
||||
return
|
||||
} else if interfaceIndex == -1 {
|
||||
m.logger.Error(E.New("invalid interface index for ", interfaceName))
|
||||
return
|
||||
}
|
||||
if m.defaultInterfaceName == interfaceName && m.defaultInterfaceIndex == interfaceIndex {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func serviceErrorPath() string {
|
||||
return filepath.Join(sWorkingPath, "network_extension_error")
|
||||
}
|
||||
|
||||
func ClearServiceError() {
|
||||
os.Remove(serviceErrorPath())
|
||||
}
|
||||
|
||||
func ReadServiceError() (string, error) {
|
||||
data, err := os.ReadFile(serviceErrorPath())
|
||||
if err == nil {
|
||||
os.Remove(serviceErrorPath())
|
||||
}
|
||||
return string(data), err
|
||||
}
|
||||
|
||||
func WriteServiceError(message string) error {
|
||||
errorFile, err := os.Create(serviceErrorPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
errorFile.WriteString(message)
|
||||
errorFile.Chown(sUserID, sGroupID)
|
||||
return errorFile.Close()
|
||||
}
|
||||
@@ -25,8 +25,6 @@ func Setup(basePath string, workingPath string, tempPath string, isTVOS bool) {
|
||||
sUserID = os.Getuid()
|
||||
sGroupID = os.Getgid()
|
||||
sTVOS = isTVOS
|
||||
os.MkdirAll(sWorkingPath, 0o777)
|
||||
os.MkdirAll(sTempPath, 0o777)
|
||||
}
|
||||
|
||||
func SetupWithUsername(basePath string, workingPath string, tempPath string, username string) error {
|
||||
@@ -39,10 +37,6 @@ func SetupWithUsername(basePath string, workingPath string, tempPath string, use
|
||||
}
|
||||
sUserID, _ = strconv.Atoi(sUser.Uid)
|
||||
sGroupID, _ = strconv.Atoi(sUser.Gid)
|
||||
os.MkdirAll(sWorkingPath, 0o777)
|
||||
os.MkdirAll(sTempPath, 0o777)
|
||||
os.Chown(sWorkingPath, sUserID, sGroupID)
|
||||
os.Chown(sTempPath, sUserID, sGroupID)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
36
go.mod
36
go.mod
@@ -5,10 +5,10 @@ go 1.20
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385
|
||||
github.com/caddyserver/certmagic v0.20.0
|
||||
github.com/cloudflare/circl v1.3.7
|
||||
github.com/cloudflare/circl v1.3.6
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/go-chi/chi/v5 v5.0.11
|
||||
github.com/go-chi/chi/v5 v5.0.10
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/gofrs/uuid/v5 v5.0.0
|
||||
@@ -17,23 +17,23 @@ require (
|
||||
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.58
|
||||
github.com/miekg/dns v1.1.57
|
||||
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.1
|
||||
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e
|
||||
github.com/sagernet/quic-go v0.40.1
|
||||
github.com/sagernet/quic-go v0.40.0
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.3.0
|
||||
github.com/sagernet/sing-dns v0.1.12
|
||||
github.com/sagernet/sing-mux v0.2.0
|
||||
github.com/sagernet/sing-quic v0.1.8
|
||||
github.com/sagernet/sing v0.3.0-beta.6
|
||||
github.com/sagernet/sing-dns v0.1.12-beta.1
|
||||
github.com/sagernet/sing-mux v0.1.6-beta.1
|
||||
github.com/sagernet/sing-quic v0.1.6-beta.1
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.6-beta.1
|
||||
github.com/sagernet/sing-shadowtls v0.1.4
|
||||
github.com/sagernet/sing-tun v0.2.1
|
||||
github.com/sagernet/sing-tun v0.2.0-beta.4
|
||||
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
|
||||
@@ -44,12 +44,12 @@ require (
|
||||
github.com/stretchr/testify v1.8.4
|
||||
go.uber.org/zap v1.26.0
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.18.0
|
||||
golang.org/x/net v0.20.0
|
||||
golang.org/x/sys v0.16.0
|
||||
golang.org/x/crypto v0.16.0
|
||||
golang.org/x/net v0.19.0
|
||||
golang.org/x/sys v0.15.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||
google.golang.org/grpc v1.60.1
|
||||
google.golang.org/protobuf v1.32.0
|
||||
google.golang.org/grpc v1.59.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
howett.net/plist v1.0.1
|
||||
)
|
||||
|
||||
@@ -86,12 +86,12 @@ require (
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
|
||||
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 // indirect
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.17.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect
|
||||
golang.org/x/tools v0.16.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.2.1 // indirect
|
||||
|
||||
76
go.sum
76
go.sum
@@ -6,8 +6,8 @@ github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sx
|
||||
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/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
|
||||
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
||||
@@ -19,8 +19,8 @@ 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.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA=
|
||||
github.com/go-chi/chi/v5 v5.0.11/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=
|
||||
@@ -74,8 +74,8 @@ github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczG
|
||||
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.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
|
||||
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
|
||||
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/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=
|
||||
@@ -104,27 +104,27 @@ github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e h1:DOkjByVeAR56dks
|
||||
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.1 h1:qLeTIJR0d0JWRmDWo346nLsVN6EWihd1kalJYPEd0TM=
|
||||
github.com/sagernet/quic-go v0.40.1/go.mod h1:CcKTpzTAISxrM4PA5M20/wYuz9Tj6Tx4DwGbNl9UQrU=
|
||||
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.3.0 h1:PIDVFZHnQAAYRL1UYqNM+0k5s8f/tb1lUW6UDcQiOc8=
|
||||
github.com/sagernet/sing v0.3.0/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
|
||||
github.com/sagernet/sing-dns v0.1.12 h1:1HqZ+ln+Rezx/aJMStaS0d7oPeX2EobSV1NT537kyj4=
|
||||
github.com/sagernet/sing-dns v0.1.12/go.mod h1:rx/DTOisneQpCgNQ4jbFU/JNEtnz0lYcHXenlVzpjEU=
|
||||
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.1.8 h1:G4iBXAKIII+uTzd55oZ/9cAQswGjlvHh/0yKMQioDS0=
|
||||
github.com/sagernet/sing-quic v0.1.8/go.mod h1:2w7DZXtf4MPjIGpovA3+vpI6bvOf1n1f9cQ1E2qQJSg=
|
||||
github.com/sagernet/sing v0.3.0-beta.6 h1:MvCJO2sBKr+k/jt6WfJ/YbohDujEnlaGqA4YQ3/Vh4U=
|
||||
github.com/sagernet/sing v0.3.0-beta.6/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
|
||||
github.com/sagernet/sing-dns v0.1.12-beta.1 h1:5J3VxSmz3ezVXYS0m8jce8jakbTiP8xiH2g2HGYjNR4=
|
||||
github.com/sagernet/sing-dns v0.1.12-beta.1/go.mod h1:zJ/YjnYB61SYE+ubMcMqVdpaSvsyQ2iShQGO3vuLvvE=
|
||||
github.com/sagernet/sing-mux v0.1.6-beta.1 h1:ADs1TgiMfA628Y2qfv21tEvePDZjBRRYddwtNFZiwe8=
|
||||
github.com/sagernet/sing-mux v0.1.6-beta.1/go.mod h1:WWtRmrwCDgb+g+7Da6o62I9WiMNB0a3w6BJhEpNQlNA=
|
||||
github.com/sagernet/sing-quic v0.1.6-beta.1 h1:OhNk0jxp7yZSvG/2Pg/gRLrhApqM1gFLB8LG2S4wSCc=
|
||||
github.com/sagernet/sing-quic v0.1.6-beta.1/go.mod h1:+OLsIVPmgHzCqcF3lGBVngc7ItYM6v3T0rn6Qtd4T1g=
|
||||
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-shadowsocks2 v0.1.6-beta.1 h1:0vbf4XA6QybVXhUqlmbceYWYpH96s4RRt6J+K+Xtr0A=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.6-beta.1/go.mod h1:H7p/X9U15oLBpdAHPk+n3GMEuJiimImj1B3GfwY2igE=
|
||||
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.2.1 h1:xZ/MkQbAX4yuOXq/s1pfhWa0lK1Ld7t4gOpSUbRl1Rs=
|
||||
github.com/sagernet/sing-tun v0.2.1/go.mod h1:w1HTPPEL575m+Ob3GOIWaTs3ZrTdgLIwoldce/p3WPU=
|
||||
github.com/sagernet/sing-tun v0.2.0-beta.4 h1:KcblUxYmG9/Qyn38AP1uFf/a/fh1kmFrybmF0eYlfrQ=
|
||||
github.com/sagernet/sing-tun v0.2.0-beta.4/go.mod h1:bV5YMmTun6g8AavxZDLu9S69MLVlu3Y9isBr3z0ujW4=
|
||||
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=
|
||||
@@ -168,17 +168,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.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
|
||||
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-20231214170342-aacd6d4b4611 h1:qCEDpW1G+vcj3Y7Fy52pEM1AWm3abj8WimGYejI3SC4=
|
||||
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
||||
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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
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.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
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=
|
||||
@@ -188,10 +188,10 @@ 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.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.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.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
@@ -199,19 +199,19 @@ 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.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
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-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-20231002182017-d307bd883b97 h1:6GQBEOdGkX6MMTLT9V+TjtIRZCw9VPD5Z+yHY9wMgS0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY=
|
||||
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
|
||||
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
|
||||
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.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
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/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
@@ -48,13 +47,13 @@ func NewDirect(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
inbound.overrideOption = 3
|
||||
inbound.overrideDestination = M.Socksaddr{Port: options.OverridePort}
|
||||
}
|
||||
var udpTimeout time.Duration
|
||||
var udpTimeout int64
|
||||
if options.UDPTimeout != 0 {
|
||||
udpTimeout = time.Duration(options.UDPTimeout)
|
||||
udpTimeout = options.UDPTimeout
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
udpTimeout = int64(C.UDPTimeout.Seconds())
|
||||
}
|
||||
inbound.udpNat = udpnat.New[netip.AddrPort](int64(udpTimeout.Seconds()), adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
inbound.udpNat = udpnat.New[netip.AddrPort](udpTimeout, adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
inbound.connHandler = inbound
|
||||
inbound.packetHandler = inbound
|
||||
inbound.packetUpstream = inbound.udpNat
|
||||
|
||||
@@ -5,7 +5,6 @@ package inbound
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/humanize"
|
||||
@@ -67,12 +66,6 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
} else {
|
||||
receiveBps = uint64(options.DownMbps) * hysteria.MbpsToBps
|
||||
}
|
||||
var udpTimeout time.Duration
|
||||
if options.UDPTimeout != 0 {
|
||||
udpTimeout = time.Duration(options.UDPTimeout)
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
}
|
||||
service, err := hysteria.NewService[int](hysteria.ServiceOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
@@ -80,7 +73,6 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
ReceiveBPS: receiveBps,
|
||||
XPlusPassword: options.Obfs,
|
||||
TLSConfig: tlsConfig,
|
||||
UDPTimeout: udpTimeout,
|
||||
Handler: adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, nil),
|
||||
|
||||
// Legacy options
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
@@ -88,12 +87,6 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context
|
||||
},
|
||||
tlsConfig: tlsConfig,
|
||||
}
|
||||
var udpTimeout time.Duration
|
||||
if options.UDPTimeout != 0 {
|
||||
udpTimeout = time.Duration(options.UDPTimeout)
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
}
|
||||
service, err := hysteria2.NewService[int](hysteria2.ServiceOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
@@ -103,7 +96,6 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context
|
||||
SalamanderPassword: salamanderPassword,
|
||||
TLSConfig: tlsConfig,
|
||||
IgnoreClientBandwidth: options.IgnoreClientBandwidth,
|
||||
UDPTimeout: udpTimeout,
|
||||
Handler: adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, nil),
|
||||
MasqueradeHandler: masqueradeHandler,
|
||||
})
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/mux"
|
||||
@@ -66,19 +65,19 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var udpTimeout time.Duration
|
||||
var udpTimeout int64
|
||||
if options.UDPTimeout != 0 {
|
||||
udpTimeout = time.Duration(options.UDPTimeout)
|
||||
udpTimeout = options.UDPTimeout
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
udpTimeout = int64(C.UDPTimeout.Seconds())
|
||||
}
|
||||
switch {
|
||||
case options.Method == shadowsocks.MethodNone:
|
||||
inbound.service = shadowsocks.NewNoneService(int64(udpTimeout.Seconds()), inbound.upstreamContextHandler())
|
||||
inbound.service = shadowsocks.NewNoneService(options.UDPTimeout, inbound.upstreamContextHandler())
|
||||
case common.Contains(shadowaead.List, options.Method):
|
||||
inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, int64(udpTimeout.Seconds()), inbound.upstreamContextHandler())
|
||||
inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, udpTimeout, inbound.upstreamContextHandler())
|
||||
case common.Contains(shadowaead_2022.List, options.Method):
|
||||
inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, int64(udpTimeout.Seconds()), inbound.upstreamContextHandler(), ntp.TimeFuncFromContext(ctx))
|
||||
inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, udpTimeout, inbound.upstreamContextHandler(), ntp.TimeFuncFromContext(ctx))
|
||||
default:
|
||||
err = E.New("unsupported method: ", options.Method)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/mux"
|
||||
@@ -54,25 +53,25 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var udpTimeout time.Duration
|
||||
var udpTimeout int64
|
||||
if options.UDPTimeout != 0 {
|
||||
udpTimeout = time.Duration(options.UDPTimeout)
|
||||
udpTimeout = options.UDPTimeout
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
udpTimeout = int64(C.UDPTimeout.Seconds())
|
||||
}
|
||||
var service shadowsocks.MultiService[int]
|
||||
if common.Contains(shadowaead_2022.List, options.Method) {
|
||||
service, err = shadowaead_2022.NewMultiServiceWithPassword[int](
|
||||
options.Method,
|
||||
options.Password,
|
||||
int64(udpTimeout.Seconds()),
|
||||
udpTimeout,
|
||||
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound),
|
||||
ntp.TimeFuncFromContext(ctx),
|
||||
)
|
||||
} else if common.Contains(shadowaead.List, options.Method) {
|
||||
service, err = shadowaead.NewMultiService[int](
|
||||
options.Method,
|
||||
int64(udpTimeout.Seconds()),
|
||||
udpTimeout,
|
||||
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
} else {
|
||||
return nil, E.New("unsupported method: " + options.Method)
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/mux"
|
||||
@@ -51,16 +50,16 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var udpTimeout time.Duration
|
||||
var udpTimeout int64
|
||||
if options.UDPTimeout != 0 {
|
||||
udpTimeout = time.Duration(options.UDPTimeout)
|
||||
udpTimeout = options.UDPTimeout
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
udpTimeout = int64(C.UDPTimeout.Seconds())
|
||||
}
|
||||
service, err := shadowaead_2022.NewRelayServiceWithPassword[int](
|
||||
options.Method,
|
||||
options.Password,
|
||||
int64(udpTimeout.Seconds()),
|
||||
udpTimeout,
|
||||
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound),
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/redir"
|
||||
@@ -38,15 +37,15 @@ func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
}
|
||||
var udpTimeout time.Duration
|
||||
var udpTimeout int64
|
||||
if options.UDPTimeout != 0 {
|
||||
udpTimeout = time.Duration(options.UDPTimeout)
|
||||
udpTimeout = options.UDPTimeout
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
udpTimeout = int64(C.UDPTimeout.Seconds())
|
||||
}
|
||||
tproxy.connHandler = tproxy
|
||||
tproxy.oobPacketHandler = tproxy
|
||||
tproxy.udpNat = udpnat.New[netip.AddrPort](int64(udpTimeout.Seconds()), tproxy.upstreamContextHandler())
|
||||
tproxy.udpNat = udpnat.New[netip.AddrPort](udpTimeout, tproxy.upstreamContextHandler())
|
||||
tproxy.packetUpstream = tproxy.udpNat
|
||||
return tproxy
|
||||
}
|
||||
|
||||
@@ -52,12 +52,6 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
||||
},
|
||||
tlsConfig: tlsConfig,
|
||||
}
|
||||
var udpTimeout time.Duration
|
||||
if options.UDPTimeout != 0 {
|
||||
udpTimeout = time.Duration(options.UDPTimeout)
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
}
|
||||
service, err := tuic.NewService[int](tuic.ServiceOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
@@ -66,7 +60,6 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
||||
AuthTimeout: time.Duration(options.AuthTimeout),
|
||||
ZeroRTTHandshake: options.ZeroRTTHandshake,
|
||||
Heartbeat: time.Duration(options.Heartbeat),
|
||||
UDPTimeout: udpTimeout,
|
||||
Handler: adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, nil),
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
@@ -44,11 +43,11 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
||||
if tunMTU == 0 {
|
||||
tunMTU = 9000
|
||||
}
|
||||
var udpTimeout time.Duration
|
||||
var udpTimeout int64
|
||||
if options.UDPTimeout != 0 {
|
||||
udpTimeout = time.Duration(options.UDPTimeout)
|
||||
udpTimeout = options.UDPTimeout
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
udpTimeout = int64(C.UDPTimeout.Seconds())
|
||||
}
|
||||
includeUID := uidToRange(options.IncludeUID)
|
||||
if len(options.IncludeUIDRange) > 0 {
|
||||
@@ -95,7 +94,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
||||
TableIndex: 2022,
|
||||
},
|
||||
endpointIndependentNat: options.EndpointIndependentNat,
|
||||
udpTimeout: int64(udpTimeout.Seconds()),
|
||||
udpTimeout: udpTimeout,
|
||||
stack: options.Stack,
|
||||
platformInterface: platformInterface,
|
||||
platformOptions: common.PtrValueOrDefault(options.Platform),
|
||||
|
||||
@@ -11,14 +11,12 @@ theme:
|
||||
logo: assets/icon.svg
|
||||
favicon: assets/icon.svg
|
||||
palette:
|
||||
- media: "(prefers-color-scheme: light)"
|
||||
scheme: default
|
||||
- scheme: default
|
||||
primary: white
|
||||
toggle:
|
||||
icon: material/brightness-7
|
||||
name: Switch to dark mode
|
||||
- media: "(prefers-color-scheme: dark)"
|
||||
scheme: slate
|
||||
- scheme: slate
|
||||
primary: black
|
||||
toggle:
|
||||
icon: material/brightness-4
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/sagernet/sing-box/common/humanize"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
)
|
||||
|
||||
type DebugOptions struct {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
@@ -107,35 +105,19 @@ type InboundOptions struct {
|
||||
}
|
||||
|
||||
type ListenOptions struct {
|
||||
Listen *ListenAddress `json:"listen,omitempty"`
|
||||
ListenPort uint16 `json:"listen_port,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
||||
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
||||
UDPFragmentDefault bool `json:"-"`
|
||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
||||
ProxyProtocol bool `json:"proxy_protocol,omitempty"`
|
||||
ProxyProtocolAcceptNoHeader bool `json:"proxy_protocol_accept_no_header,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
Listen *ListenAddress `json:"listen,omitempty"`
|
||||
ListenPort uint16 `json:"listen_port,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
||||
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
||||
UDPFragmentDefault bool `json:"-"`
|
||||
UDPTimeout int64 `json:"udp_timeout,omitempty"`
|
||||
ProxyProtocol bool `json:"proxy_protocol,omitempty"`
|
||||
ProxyProtocolAcceptNoHeader bool `json:"proxy_protocol_accept_no_header,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
InboundOptions
|
||||
}
|
||||
|
||||
type UDPTimeoutCompat Duration
|
||||
|
||||
func (c UDPTimeoutCompat) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal((time.Duration)(c).String())
|
||||
}
|
||||
|
||||
func (c *UDPTimeoutCompat) UnmarshalJSON(data []byte) error {
|
||||
var valueNumber int64
|
||||
err := json.Unmarshal(data, &valueNumber)
|
||||
if err == nil {
|
||||
*c = UDPTimeoutCompat(time.Second * time.Duration(valueNumber))
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(data, (*Duration)(c))
|
||||
}
|
||||
|
||||
type ListenOptionsWrapper interface {
|
||||
TakeListenOptions() ListenOptions
|
||||
ReplaceListenOptions(options ListenOptions)
|
||||
|
||||
@@ -24,7 +24,7 @@ type TunInboundOptions struct {
|
||||
IncludePackage Listable[string] `json:"include_package,omitempty"`
|
||||
ExcludePackage Listable[string] `json:"exclude_package,omitempty"`
|
||||
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
|
||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
||||
UDPTimeout int64 `json:"udp_timeout,omitempty"`
|
||||
Stack string `json:"stack,omitempty"`
|
||||
Platform *TunPlatformOptions `json:"platform,omitempty"`
|
||||
InboundOptions
|
||||
|
||||
@@ -30,7 +30,6 @@ type Direct struct {
|
||||
fallbackDelay time.Duration
|
||||
overrideOption int
|
||||
overrideDestination M.Socksaddr
|
||||
loopBack *loopBackDetector
|
||||
}
|
||||
|
||||
func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) {
|
||||
@@ -51,7 +50,6 @@ 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(),
|
||||
}
|
||||
if options.ProxyProtocol != 0 {
|
||||
return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
|
||||
@@ -90,11 +88,7 @@ func (h *Direct) DialContext(ctx context.Context, network string, destination M.
|
||||
case N.NetworkUDP:
|
||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
}
|
||||
conn, err := h.dialer.DialContext(ctx, network, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return h.loopBack.NewConn(conn), nil
|
||||
return h.dialer.DialContext(ctx, network, destination)
|
||||
}
|
||||
|
||||
func (h *Direct) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) {
|
||||
@@ -148,7 +142,6 @@ func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn = h.loopBack.NewPacketConn(bufio.NewPacketConn(conn))
|
||||
if originDestination != destination {
|
||||
conn = bufio.NewNATPacketConn(bufio.NewPacketConn(conn), destination, originDestination)
|
||||
}
|
||||
@@ -156,15 +149,9 @@ 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()) {
|
||||
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()) {
|
||||
return E.New("reject loopback packet connection to ", metadata.Destination)
|
||||
}
|
||||
return NewPacketConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
package outbound
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
type loopBackDetector struct {
|
||||
connAccess sync.RWMutex
|
||||
packetConnAccess sync.RWMutex
|
||||
connMap map[netip.AddrPort]bool
|
||||
packetConnMap map[netip.AddrPort]bool
|
||||
}
|
||||
|
||||
func newLoopBackDetector() *loopBackDetector {
|
||||
return &loopBackDetector{
|
||||
connMap: make(map[netip.AddrPort]bool),
|
||||
packetConnMap: make(map[netip.AddrPort]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *loopBackDetector) NewConn(conn net.Conn) net.Conn {
|
||||
connAddr := M.AddrPortFromNet(conn.LocalAddr())
|
||||
if !connAddr.IsValid() {
|
||||
return conn
|
||||
}
|
||||
if udpConn, isUDPConn := conn.(abstractUDPConn); isUDPConn {
|
||||
l.packetConnAccess.Lock()
|
||||
l.packetConnMap[connAddr] = true
|
||||
l.packetConnAccess.Unlock()
|
||||
return &loopBackDetectUDPWrapper{abstractUDPConn: udpConn, detector: l, connAddr: connAddr}
|
||||
} else {
|
||||
l.connAccess.Lock()
|
||||
l.connMap[connAddr] = true
|
||||
l.connAccess.Unlock()
|
||||
return &loopBackDetectWrapper{Conn: conn, detector: l, connAddr: connAddr}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *loopBackDetector) NewPacketConn(conn N.NetPacketConn) N.NetPacketConn {
|
||||
connAddr := M.AddrPortFromNet(conn.LocalAddr())
|
||||
if !connAddr.IsValid() {
|
||||
return conn
|
||||
}
|
||||
l.packetConnAccess.Lock()
|
||||
l.packetConnMap[connAddr] = true
|
||||
l.packetConnAccess.Unlock()
|
||||
return &loopBackDetectPacketWrapper{NetPacketConn: conn, detector: l, connAddr: connAddr}
|
||||
}
|
||||
|
||||
func (l *loopBackDetector) CheckConn(connAddr netip.AddrPort) bool {
|
||||
l.connAccess.RLock()
|
||||
defer l.connAccess.RUnlock()
|
||||
return l.connMap[connAddr]
|
||||
}
|
||||
|
||||
func (l *loopBackDetector) CheckPacketConn(connAddr netip.AddrPort) bool {
|
||||
l.packetConnAccess.RLock()
|
||||
defer l.packetConnAccess.RUnlock()
|
||||
return l.packetConnMap[connAddr]
|
||||
}
|
||||
|
||||
type loopBackDetectWrapper struct {
|
||||
net.Conn
|
||||
detector *loopBackDetector
|
||||
connAddr netip.AddrPort
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
func (w *loopBackDetectWrapper) Close() error {
|
||||
w.closeOnce.Do(func() {
|
||||
w.detector.connAccess.Lock()
|
||||
delete(w.detector.connMap, w.connAddr)
|
||||
w.detector.connAccess.Unlock()
|
||||
})
|
||||
return w.Conn.Close()
|
||||
}
|
||||
|
||||
func (w *loopBackDetectWrapper) ReaderReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *loopBackDetectWrapper) WriterReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *loopBackDetectWrapper) Upstream() any {
|
||||
return w.Conn
|
||||
}
|
||||
|
||||
type loopBackDetectPacketWrapper struct {
|
||||
N.NetPacketConn
|
||||
detector *loopBackDetector
|
||||
connAddr netip.AddrPort
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
func (w *loopBackDetectPacketWrapper) Close() error {
|
||||
w.closeOnce.Do(func() {
|
||||
w.detector.packetConnAccess.Lock()
|
||||
delete(w.detector.packetConnMap, w.connAddr)
|
||||
w.detector.packetConnAccess.Unlock()
|
||||
})
|
||||
return w.NetPacketConn.Close()
|
||||
}
|
||||
|
||||
func (w *loopBackDetectPacketWrapper) ReaderReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *loopBackDetectPacketWrapper) WriterReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *loopBackDetectPacketWrapper) Upstream() any {
|
||||
return w.NetPacketConn
|
||||
}
|
||||
|
||||
type abstractUDPConn interface {
|
||||
net.Conn
|
||||
net.PacketConn
|
||||
}
|
||||
|
||||
type loopBackDetectUDPWrapper struct {
|
||||
abstractUDPConn
|
||||
detector *loopBackDetector
|
||||
connAddr netip.AddrPort
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
func (w *loopBackDetectUDPWrapper) Close() error {
|
||||
w.closeOnce.Do(func() {
|
||||
w.detector.packetConnAccess.Lock()
|
||||
delete(w.detector.packetConnMap, w.connAddr)
|
||||
w.detector.packetConnAccess.Unlock()
|
||||
})
|
||||
return w.abstractUDPConn.Close()
|
||||
}
|
||||
|
||||
func (w *loopBackDetectUDPWrapper) ReaderReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *loopBackDetectUDPWrapper) WriterReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *loopBackDetectUDPWrapper) Upstream() any {
|
||||
return w.abstractUDPConn
|
||||
}
|
||||
@@ -241,8 +241,7 @@ func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWa
|
||||
return err
|
||||
}
|
||||
timeout.Update()
|
||||
response = truncateDNSMessage(response, 512) // TODO: add an option to custom UDP buffer size
|
||||
responseBuffer := buf.NewSize(dns.FixedPacketSize)
|
||||
responseBuffer := buf.NewPacket()
|
||||
responseBuffer.Resize(1024, 0)
|
||||
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
||||
if err != nil {
|
||||
@@ -264,21 +263,3 @@ func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWa
|
||||
})
|
||||
return group.Run(fastClose)
|
||||
}
|
||||
|
||||
func truncateDNSMessage(response *mDNS.Msg, maxLen int) *mDNS.Msg {
|
||||
responseLen := response.Len()
|
||||
if responseLen <= maxLen {
|
||||
return response
|
||||
}
|
||||
response = response.Copy()
|
||||
for len(response.Answer) > 0 && responseLen > maxLen {
|
||||
response.Answer = response.Answer[:len(response.Answer)-1]
|
||||
response.Truncated = true
|
||||
responseLen = response.Len()
|
||||
}
|
||||
if responseLen > maxLen {
|
||||
response.Ns = nil
|
||||
response.Extra = nil
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
outbound := &URLTest{
|
||||
myOutboundAdapter: myOutboundAdapter{
|
||||
protocol: C.TypeURLTest,
|
||||
network: []string{N.NetworkTCP, N.NetworkUDP},
|
||||
router: router,
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
@@ -64,6 +63,13 @@ func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
func (s *URLTest) Network() []string {
|
||||
if s.group == nil {
|
||||
return []string{N.NetworkTCP, N.NetworkUDP}
|
||||
}
|
||||
return s.group.Select(N.NetworkTCP).Network()
|
||||
}
|
||||
|
||||
func (s *URLTest) Start() error {
|
||||
outbounds := make([]adapter.Outbound, 0, len(s.tags))
|
||||
for i, tag := range s.tags {
|
||||
@@ -103,12 +109,7 @@ func (s *URLTest) Close() error {
|
||||
}
|
||||
|
||||
func (s *URLTest) Now() string {
|
||||
if s.group.selectedOutboundTCP != nil {
|
||||
return s.group.selectedOutboundTCP.Tag()
|
||||
} else if s.group.selectedOutboundUDP != nil {
|
||||
return s.group.selectedOutboundUDP.Tag()
|
||||
}
|
||||
return ""
|
||||
return s.group.Select(N.NetworkTCP).Tag()
|
||||
}
|
||||
|
||||
func (s *URLTest) All() []string {
|
||||
@@ -125,21 +126,7 @@ func (s *URLTest) CheckOutbounds() {
|
||||
|
||||
func (s *URLTest) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
s.group.Touch()
|
||||
var outbound adapter.Outbound
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkTCP:
|
||||
outbound = s.group.selectedOutboundTCP
|
||||
case N.NetworkUDP:
|
||||
outbound = s.group.selectedOutboundUDP
|
||||
default:
|
||||
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
||||
}
|
||||
if outbound == nil {
|
||||
outbound, _ = s.group.Select(network)
|
||||
}
|
||||
if outbound == nil {
|
||||
return nil, E.New("missing supported outbound")
|
||||
}
|
||||
outbound := s.group.Select(network)
|
||||
conn, err := outbound.DialContext(ctx, network, destination)
|
||||
if err == nil {
|
||||
return s.group.interruptGroup.NewConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil
|
||||
@@ -151,13 +138,7 @@ func (s *URLTest) DialContext(ctx context.Context, network string, destination M
|
||||
|
||||
func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
s.group.Touch()
|
||||
outbound := s.group.selectedOutboundUDP
|
||||
if outbound == nil {
|
||||
outbound, _ = s.group.Select(N.NetworkUDP)
|
||||
}
|
||||
if outbound == nil {
|
||||
return nil, E.New("missing supported outbound")
|
||||
}
|
||||
outbound := s.group.Select(N.NetworkUDP)
|
||||
conn, err := outbound.ListenPacket(ctx, destination)
|
||||
if err == nil {
|
||||
return s.group.interruptGroup.NewPacketConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil
|
||||
@@ -285,7 +266,7 @@ func (g *URLTestGroup) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) Select(network string) (adapter.Outbound, bool) {
|
||||
func (g *URLTestGroup) Select(network string) adapter.Outbound {
|
||||
var minDelay uint16
|
||||
var minTime time.Time
|
||||
var minOutbound adapter.Outbound
|
||||
@@ -308,11 +289,11 @@ func (g *URLTestGroup) Select(network string) (adapter.Outbound, bool) {
|
||||
if !common.Contains(detour.Network(), network) {
|
||||
continue
|
||||
}
|
||||
return detour, false
|
||||
minOutbound = detour
|
||||
break
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
return minOutbound, true
|
||||
return minOutbound
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) loopCheck() {
|
||||
@@ -396,12 +377,14 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) performUpdateCheck() {
|
||||
outbound := g.Select(N.NetworkTCP)
|
||||
var updated bool
|
||||
if outbound, exists := g.Select(N.NetworkTCP); outbound != nil && (g.selectedOutboundTCP == nil || (exists && outbound != g.selectedOutboundTCP)) {
|
||||
if outbound != g.selectedOutboundTCP {
|
||||
g.selectedOutboundTCP = outbound
|
||||
updated = true
|
||||
}
|
||||
if outbound, exists := g.Select(N.NetworkUDP); outbound != nil && (g.selectedOutboundUDP == nil || (exists && outbound != g.selectedOutboundUDP)) {
|
||||
outbound = g.Select(N.NetworkUDP)
|
||||
if outbound != g.selectedOutboundUDP {
|
||||
g.selectedOutboundUDP = outbound
|
||||
updated = true
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[Unit]
|
||||
Description=sing-box service
|
||||
Documentation=https://sing-box.sagernet.org
|
||||
After=network.target nss-lookup.target network-online.target
|
||||
After=network.target nss-lookup.target
|
||||
|
||||
[Service]
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[Unit]
|
||||
Description=sing-box service
|
||||
Documentation=https://sing-box.sagernet.org
|
||||
After=network.target nss-lookup.target network-online.target
|
||||
After=network.target nss-lookup.target
|
||||
|
||||
[Service]
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[Unit]
|
||||
Description=sing-box service
|
||||
Documentation=https://sing-box.sagernet.org
|
||||
After=network.target nss-lookup.target network-online.target
|
||||
After=network.target nss-lookup.target
|
||||
|
||||
[Service]
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
|
||||
@@ -629,7 +629,7 @@ func (r *Router) Close() error {
|
||||
}
|
||||
if r.geoIPReader != nil {
|
||||
monitor.Start("close geoip reader")
|
||||
err = E.Append(err, r.geoIPReader.Close(), func(err error) error {
|
||||
err = E.Append(err, common.Close(r.geoIPReader), func(err error) error {
|
||||
return E.Cause(err, "close geoip reader")
|
||||
})
|
||||
monitor.Finish()
|
||||
|
||||
@@ -72,7 +72,7 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.sourcePortItems) > 0 && !metadata.SourcePortMatch {
|
||||
if len(r.sourcePortItems) > 0 && !metadata.SourceAddressMatch {
|
||||
for _, item := range r.sourcePortItems {
|
||||
if item.Match(metadata) {
|
||||
metadata.SourcePortMatch = true
|
||||
@@ -81,7 +81,7 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.destinationAddressItems) > 0 && !metadata.DestinationAddressMatch {
|
||||
if len(r.destinationAddressItems) > 0 && !metadata.SourceAddressMatch {
|
||||
for _, item := range r.destinationAddressItems {
|
||||
if item.Match(metadata) {
|
||||
metadata.DestinationAddressMatch = true
|
||||
@@ -90,7 +90,7 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.destinationPortItems) > 0 && !metadata.DestinationPortMatch {
|
||||
if len(r.destinationPortItems) > 0 && !metadata.SourceAddressMatch {
|
||||
for _, item := range r.destinationPortItems {
|
||||
if item.Match(metadata) {
|
||||
metadata.DestinationPortMatch = true
|
||||
|
||||
@@ -115,7 +115,7 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
||||
if len(options.SourceIPCIDR) > 0 {
|
||||
item, err := NewIPCIDRItem(true, options.SourceIPCIDR)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "source_ip_cidr")
|
||||
return nil, E.Cause(err, "source_ipcidr")
|
||||
}
|
||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
|
||||
@@ -114,7 +114,7 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
if len(options.SourceIPCIDR) > 0 {
|
||||
item, err := NewIPCIDRItem(true, options.SourceIPCIDR)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "source_ip_cidr")
|
||||
return nil, E.Cause(err, "source_ipcidr")
|
||||
}
|
||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
|
||||
@@ -66,7 +66,7 @@ func NewDefaultHeadlessRule(router adapter.Router, options option.DefaultHeadles
|
||||
if len(options.SourceIPCIDR) > 0 {
|
||||
item, err := NewIPCIDRItem(true, options.SourceIPCIDR)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "source_ip_cidr")
|
||||
return nil, E.Cause(err, "source_ipcidr")
|
||||
}
|
||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
|
||||
@@ -35,9 +35,9 @@ func NewIPCIDRItem(isSource bool, prefixStrings []string) (*IPCIDRItem, error) {
|
||||
}
|
||||
var description string
|
||||
if isSource {
|
||||
description = "source_ip_cidr="
|
||||
description = "source_ipcidr="
|
||||
} else {
|
||||
description = "ip_cidr="
|
||||
description = "ipcidr="
|
||||
}
|
||||
if dLen := len(prefixStrings); dLen == 1 {
|
||||
description += prefixStrings[0]
|
||||
@@ -60,9 +60,9 @@ func NewIPCIDRItem(isSource bool, prefixStrings []string) (*IPCIDRItem, error) {
|
||||
func NewRawIPCIDRItem(isSource bool, ipSet *netipx.IPSet) *IPCIDRItem {
|
||||
var description string
|
||||
if isSource {
|
||||
description = "source_ip_cidr="
|
||||
description = "source_ipcidr="
|
||||
} else {
|
||||
description = "ip_cidr="
|
||||
description = "ipcidr="
|
||||
}
|
||||
description += "<binary>"
|
||||
return &IPCIDRItem{
|
||||
|
||||
@@ -30,11 +30,11 @@ func NewDomainItem(domains []string, domainSuffixes []string) *DomainItem {
|
||||
description += " "
|
||||
}
|
||||
if dsLen == 1 {
|
||||
description += "domain_suffix=" + domainSuffixes[0]
|
||||
description += "domainSuffix=" + domainSuffixes[0]
|
||||
} else if dsLen > 3 {
|
||||
description += "domain_suffix=[" + strings.Join(domainSuffixes[:3], " ") + "...]"
|
||||
description += "domainSuffix=[" + strings.Join(domainSuffixes[:3], " ") + "...]"
|
||||
} else {
|
||||
description += "domain_suffix=[" + strings.Join(domainSuffixes, " ") + "]"
|
||||
description += "domainSuffix=[" + strings.Join(domainSuffixes, " ") + "]"
|
||||
}
|
||||
}
|
||||
return &DomainItem{
|
||||
|
||||
@@ -20,23 +20,22 @@ type LocalRuleSet struct {
|
||||
}
|
||||
|
||||
func NewLocalRuleSet(router adapter.Router, options option.RuleSet) (*LocalRuleSet, error) {
|
||||
setFile, err := os.Open(options.LocalOptions.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var plainRuleSet option.PlainRuleSet
|
||||
switch options.Format {
|
||||
case C.RuleSetFormatSource, "":
|
||||
content, err := os.ReadFile(options.LocalOptions.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
compat, err := json.UnmarshalExtended[option.PlainRuleSetCompat](content)
|
||||
var compat option.PlainRuleSetCompat
|
||||
decoder := json.NewDecoder(json.NewCommentFilter(setFile))
|
||||
decoder.DisallowUnknownFields()
|
||||
err = decoder.Decode(&compat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plainRuleSet = compat.Upgrade()
|
||||
case C.RuleSetFormatBinary:
|
||||
setFile, err := os.Open(options.LocalOptions.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plainRuleSet, err = srs.Read(setFile, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -45,7 +44,6 @@ func NewLocalRuleSet(router adapter.Router, options option.RuleSet) (*LocalRuleS
|
||||
return nil, E.New("unknown rule set format: ", options.Format)
|
||||
}
|
||||
rules := make([]adapter.HeadlessRule, len(plainRuleSet.Rules))
|
||||
var err error
|
||||
for i, ruleOptions := range plainRuleSet.Rules {
|
||||
rules[i], err = NewHeadlessRule(router, ruleOptions)
|
||||
if err != nil {
|
||||
|
||||
@@ -128,7 +128,9 @@ func (s *RemoteRuleSet) loadBytes(content []byte) error {
|
||||
switch s.options.Format {
|
||||
case C.RuleSetFormatSource:
|
||||
var compat option.PlainRuleSetCompat
|
||||
compat, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content)
|
||||
decoder := json.NewDecoder(json.NewCommentFilter(bytes.NewReader(content)))
|
||||
decoder.DisallowUnknownFields()
|
||||
err = decoder.Decode(&compat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -75,9 +75,6 @@ func testShadowsocksMux(t *testing.T, options option.OutboundMultiplexOptions) {
|
||||
},
|
||||
Method: method,
|
||||
Password: password,
|
||||
Multiplex: &option.InboundMultiplexOptions{
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -2,16 +2,19 @@ package v2raygrpclite
|
||||
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/baderror"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
@@ -27,6 +30,7 @@ type GunConn struct {
|
||||
create chan struct{}
|
||||
err error
|
||||
readRemaining int
|
||||
writeAccess sync.Mutex
|
||||
}
|
||||
|
||||
func newGunConn(reader io.Reader, writer io.Writer, flusher http.Flusher) *GunConn {
|
||||
@@ -96,22 +100,19 @@ func (c *GunConn) read(b []byte) (n int, err error) {
|
||||
}
|
||||
|
||||
func (c *GunConn) Write(b []byte) (n int, err error) {
|
||||
varLen := rw.UVariantLen(uint64(len(b)))
|
||||
buffer := buf.NewSize(6 + varLen + len(b))
|
||||
header := buffer.Extend(6 + varLen)
|
||||
header[0] = 0x00
|
||||
binary.BigEndian.PutUint32(header[1:5], uint32(1+varLen+len(b)))
|
||||
header[5] = 0x0A
|
||||
binary.PutUvarint(header[6:], uint64(len(b)))
|
||||
common.Must1(buffer.Write(b))
|
||||
_, err = c.writer.Write(buffer.Bytes())
|
||||
if err != nil {
|
||||
return 0, baderror.WrapH2(err)
|
||||
}
|
||||
if c.flusher != nil {
|
||||
protobufHeader := [1 + binary.MaxVarintLen64]byte{0x0A}
|
||||
varuintLen := binary.PutUvarint(protobufHeader[1:], uint64(len(b)))
|
||||
grpcHeader := buf.Get(5)
|
||||
grpcPayloadLen := uint32(1 + varuintLen + len(b))
|
||||
binary.BigEndian.PutUint32(grpcHeader[1:5], grpcPayloadLen)
|
||||
c.writeAccess.Lock()
|
||||
_, err = bufio.Copy(c.writer, io.MultiReader(bytes.NewReader(grpcHeader), bytes.NewReader(protobufHeader[:varuintLen+1]), bytes.NewReader(b)))
|
||||
c.writeAccess.Unlock()
|
||||
buf.Put(grpcHeader)
|
||||
if err == nil && c.flusher != nil {
|
||||
c.flusher.Flush()
|
||||
}
|
||||
return len(b), nil
|
||||
return len(b), baderror.WrapH2(err)
|
||||
}
|
||||
|
||||
func (c *GunConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
@@ -119,18 +120,16 @@ func (c *GunConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
dataLen := buffer.Len()
|
||||
varLen := rw.UVariantLen(uint64(dataLen))
|
||||
header := buffer.ExtendHeader(6 + varLen)
|
||||
_ = header[6]
|
||||
header[0] = 0x00
|
||||
binary.BigEndian.PutUint32(header[1:5], uint32(1+varLen+dataLen))
|
||||
header[5] = 0x0A
|
||||
binary.PutUvarint(header[6:], uint64(dataLen))
|
||||
err := rw.WriteBytes(c.writer, buffer.Bytes())
|
||||
if err != nil {
|
||||
return baderror.WrapH2(err)
|
||||
}
|
||||
if c.flusher != nil {
|
||||
if err == nil && c.flusher != nil {
|
||||
c.flusher.Flush()
|
||||
}
|
||||
return nil
|
||||
return baderror.WrapH2(err)
|
||||
}
|
||||
|
||||
func (c *GunConn) FrontHeadroom() int {
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -29,7 +28,7 @@ type Client struct {
|
||||
serverAddr M.Socksaddr
|
||||
transport http.RoundTripper
|
||||
http2 bool
|
||||
requestURL url.URL
|
||||
url *url.URL
|
||||
host []string
|
||||
method string
|
||||
headers http.Header
|
||||
@@ -59,35 +58,33 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
||||
},
|
||||
}
|
||||
}
|
||||
if options.Method == "" {
|
||||
options.Method = http.MethodPut
|
||||
}
|
||||
var requestURL url.URL
|
||||
if tlsConfig == nil {
|
||||
requestURL.Scheme = "http"
|
||||
} else {
|
||||
requestURL.Scheme = "https"
|
||||
}
|
||||
requestURL.Host = serverAddr.String()
|
||||
requestURL.Path = options.Path
|
||||
err := sHTTP.URLSetPath(&requestURL, options.Path)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse path")
|
||||
}
|
||||
if !strings.HasPrefix(requestURL.Path, "/") {
|
||||
requestURL.Path = "/" + requestURL.Path
|
||||
}
|
||||
return &Client{
|
||||
client := &Client{
|
||||
ctx: ctx,
|
||||
dialer: dialer,
|
||||
serverAddr: serverAddr,
|
||||
requestURL: requestURL,
|
||||
host: options.Host,
|
||||
method: options.Method,
|
||||
headers: options.Headers.Build(),
|
||||
transport: transport,
|
||||
http2: tlsConfig != nil,
|
||||
}, nil
|
||||
}
|
||||
if client.method == "" {
|
||||
client.method = "PUT"
|
||||
}
|
||||
var uri url.URL
|
||||
if tlsConfig == nil {
|
||||
uri.Scheme = "http"
|
||||
} else {
|
||||
uri.Scheme = "https"
|
||||
}
|
||||
uri.Host = serverAddr.String()
|
||||
uri.Path = options.Path
|
||||
err := sHTTP.URLSetPath(&uri, options.Path)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse path")
|
||||
}
|
||||
client.url = &uri
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
@@ -106,7 +103,7 @@ func (c *Client) dialHTTP(ctx context.Context) (net.Conn, error) {
|
||||
|
||||
request := &http.Request{
|
||||
Method: c.method,
|
||||
URL: &c.requestURL,
|
||||
URL: c.url,
|
||||
Header: c.headers.Clone(),
|
||||
}
|
||||
switch hostLen := len(c.host); hostLen {
|
||||
@@ -126,7 +123,7 @@ func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) {
|
||||
request := &http.Request{
|
||||
Method: c.method,
|
||||
Body: pipeInReader,
|
||||
URL: &c.requestURL,
|
||||
URL: c.url,
|
||||
Header: c.headers.Clone(),
|
||||
}
|
||||
request = request.WithContext(ctx)
|
||||
|
||||
@@ -65,7 +65,7 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
s.invalidRequest(writer, request, http.StatusBadRequest, E.New("bad host: ", host))
|
||||
return
|
||||
}
|
||||
if request.URL.Path != s.path {
|
||||
if !strings.HasPrefix(request.URL.Path, s.path) {
|
||||
s.invalidRequest(writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -55,10 +55,15 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
||||
if !strings.HasPrefix(requestURL.Path, "/") {
|
||||
requestURL.Path = "/" + requestURL.Path
|
||||
}
|
||||
headers := options.Headers.Build()
|
||||
if host := headers.Get("Host"); host != "" {
|
||||
headers.Del("Host")
|
||||
requestURL.Host = host
|
||||
headers := make(http.Header)
|
||||
for key, value := range options.Headers {
|
||||
headers[key] = value
|
||||
if key == "Host" {
|
||||
if len(value) > 1 {
|
||||
return nil, E.New("multiple Host headers")
|
||||
}
|
||||
requestURL.Host = value[0]
|
||||
}
|
||||
}
|
||||
if headers.Get("User-Agent") == "" {
|
||||
headers.Set("User-Agent", "Go-http-client/1.1")
|
||||
|
||||
@@ -33,7 +33,6 @@ type Server struct {
|
||||
path string
|
||||
maxEarlyData uint32
|
||||
earlyDataHeaderName string
|
||||
upgrader ws.HTTPUpgrader
|
||||
}
|
||||
|
||||
func NewServer(ctx context.Context, options option.V2RayWebsocketOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) {
|
||||
@@ -44,10 +43,6 @@ func NewServer(ctx context.Context, options option.V2RayWebsocketOptions, tlsCon
|
||||
path: options.Path,
|
||||
maxEarlyData: options.MaxEarlyData,
|
||||
earlyDataHeaderName: options.EarlyDataHeaderName,
|
||||
upgrader: ws.HTTPUpgrader{
|
||||
Timeout: C.TCPTimeout,
|
||||
Header: options.Headers.Build(),
|
||||
},
|
||||
}
|
||||
if !strings.HasPrefix(server.path, "/") {
|
||||
server.path = "/" + server.path
|
||||
@@ -84,10 +79,6 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if request.URL.Path != s.path {
|
||||
s.invalidRequest(writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||
return
|
||||
}
|
||||
earlyDataStr := request.Header.Get(s.earlyDataHeaderName)
|
||||
if earlyDataStr != "" {
|
||||
earlyData, err = base64.RawURLEncoding.DecodeString(earlyDataStr)
|
||||
|
||||
@@ -24,7 +24,7 @@ var tlsRegistry []func(conn net.Conn) (loaded bool, netConn net.Conn, reflectTyp
|
||||
|
||||
func init() {
|
||||
tlsRegistry = append(tlsRegistry, func(conn net.Conn) (loaded bool, netConn net.Conn, reflectType reflect.Type, reflectPointer uintptr) {
|
||||
tlsConn, loaded := common.Cast[*tls.Conn](conn)
|
||||
tlsConn, loaded := conn.(*tls.Conn)
|
||||
if !loaded {
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user