mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-13 20:28:32 +10:00
Compare commits
31 Commits
v1.8.0-rc.
...
v1.8.0-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e458398bb2 | ||
|
|
a5d324177b | ||
|
|
34d5441d56 | ||
|
|
9713dfb29c | ||
|
|
c0483a2b37 | ||
|
|
6fe582fcd7 | ||
|
|
081c45b9d7 | ||
|
|
4e02b9ea94 | ||
|
|
0b2023660b | ||
|
|
dee6fffa23 | ||
|
|
26d29761df | ||
|
|
4e4d220bbe | ||
|
|
d38336bec4 | ||
|
|
de4f167402 | ||
|
|
b9439b8fbd | ||
|
|
817be7af6e | ||
|
|
f57e98dbb0 | ||
|
|
47213f0616 | ||
|
|
2f4f4e18ee | ||
|
|
bc2d73b2d6 | ||
|
|
abd4d8b367 | ||
|
|
f998ccecde | ||
|
|
fc9804b20f | ||
|
|
d1ad342cb0 | ||
|
|
80cc0cd5db | ||
|
|
3041f34dfa | ||
|
|
3a9b787a6e | ||
|
|
bb70e8267e | ||
|
|
411f02ee4f | ||
|
|
340e74eed4 | ||
|
|
ad93b45021 |
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
|
||||
2
.github/workflows/debug.yml
vendored
2
.github/workflows/debug.yml
vendored
@@ -216,7 +216,7 @@ jobs:
|
||||
id: build
|
||||
run: make
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: sing-box-${{ matrix.name }}
|
||||
path: sing-box*
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -6,21 +6,27 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
type DetourDialer struct {
|
||||
router adapter.Router
|
||||
detour string
|
||||
dialer N.Dialer
|
||||
initOnce sync.Once
|
||||
initErr error
|
||||
router adapter.Router
|
||||
detour string
|
||||
allowNestedDirect bool
|
||||
dialer N.Dialer
|
||||
initOnce sync.Once
|
||||
initErr error
|
||||
}
|
||||
|
||||
func NewDetour(router adapter.Router, detour string) N.Dialer {
|
||||
return &DetourDialer{router: router, detour: detour}
|
||||
func NewDetour(router adapter.Router, detour string, allowNestedDirect bool) N.Dialer {
|
||||
return &DetourDialer{
|
||||
router: router,
|
||||
detour: detour,
|
||||
allowNestedDirect: allowNestedDirect,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DetourDialer) Start() error {
|
||||
@@ -30,10 +36,17 @@ func (d *DetourDialer) Start() error {
|
||||
|
||||
func (d *DetourDialer) Dialer() (N.Dialer, error) {
|
||||
d.initOnce.Do(func() {
|
||||
var loaded bool
|
||||
d.dialer, loaded = d.router.Outbound(d.detour)
|
||||
var (
|
||||
dialer adapter.Outbound
|
||||
loaded bool
|
||||
)
|
||||
dialer, loaded = d.router.Outbound(d.detour)
|
||||
if !loaded {
|
||||
d.initErr = E.New("outbound detour not found: ", d.detour)
|
||||
} else if !d.allowNestedDirect && dialer.Type() == C.TypeDirect {
|
||||
d.initErr = E.New("using a direct outbound as a detour is illegal")
|
||||
} else {
|
||||
d.dialer = dialer
|
||||
}
|
||||
})
|
||||
return d.dialer, d.initErr
|
||||
|
||||
@@ -23,7 +23,7 @@ func New(router adapter.Router, options option.DialerOptions) (N.Dialer, error)
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
dialer = NewDetour(router, options.Detour)
|
||||
dialer = NewDetour(router, options.Detour, false)
|
||||
}
|
||||
domainStrategy := dns.DomainStrategy(options.DomainStrategy)
|
||||
if domainStrategy != dns.DomainStrategyAsIS || options.Detour == "" {
|
||||
|
||||
@@ -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,36 +2,6 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.8.0-rc.8
|
||||
|
||||
* 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
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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/).
|
||||
|
||||
@@ -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 请求的额外标头。
|
||||
|
||||
如果设置,服务器将写入响应。
|
||||
默认服务器将写入响应。
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
26
go.mod
26
go.mod
@@ -8,7 +8,7 @@ require (
|
||||
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
|
||||
@@ -24,16 +24,16 @@ require (
|
||||
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-beta.2
|
||||
github.com/sagernet/quic-go v0.40.0
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.3.0-rc.7
|
||||
github.com/sagernet/sing-dns v0.1.12
|
||||
github.com/sagernet/sing-mux v0.1.7-rc.1
|
||||
github.com/sagernet/sing-quic v0.1.7-rc.2
|
||||
github.com/sagernet/sing v0.3.0-beta.7
|
||||
github.com/sagernet/sing-dns v0.1.12-beta.1
|
||||
github.com/sagernet/sing-mux v0.1.6-beta.3
|
||||
github.com/sagernet/sing-quic v0.1.6-beta.2
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.6-rc.1
|
||||
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.0-rc.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.17.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-20231226003508-02704c960a9b // 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.16.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // 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
|
||||
|
||||
52
go.sum
52
go.sum
@@ -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=
|
||||
@@ -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-beta.2 h1:USRwm36XuAFdcrmv4vDRD+YUOO08DfvLNruXThrVHZU=
|
||||
github.com/sagernet/quic-go v0.40.1-beta.2/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-rc.7 h1:FmnzFRYC6usVgWf112cUxiexwvL+iAurKmCL4Axa9+A=
|
||||
github.com/sagernet/sing v0.3.0-rc.7/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.1.7-rc.1 h1:4XlQSeIqKA6uaW1KGQA9wCG5zt9ajhvc/zJnnZV/n1c=
|
||||
github.com/sagernet/sing-mux v0.1.7-rc.1/go.mod h1:KK5zCbNujj5kn36G+wLFROOXyJhaaXLyaZWY2w7kBNQ=
|
||||
github.com/sagernet/sing-quic v0.1.7-rc.2 h1:rCWhtvzQwgkWbX4sVHYdNwzyPweoUPEgBCBatywHjMs=
|
||||
github.com/sagernet/sing-quic v0.1.7-rc.2/go.mod h1:IbKCPWXP13zd3cdu0rirtYjkMlquc5zWtc3avfSUGAw=
|
||||
github.com/sagernet/sing v0.3.0-beta.7 h1:AAGnMovsI1r8su731Yl6nDMwWw5XIXkmdlaVBIeL7uI=
|
||||
github.com/sagernet/sing v0.3.0-beta.7/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.3 h1:Bcd89O0zgDNs/q6MJBS0EpgRvry9dB0VJ2FAL9Q2Mp8=
|
||||
github.com/sagernet/sing-mux v0.1.6-beta.3/go.mod h1:WWtRmrwCDgb+g+7Da6o62I9WiMNB0a3w6BJhEpNQlNA=
|
||||
github.com/sagernet/sing-quic v0.1.6-beta.2 h1:u/tab4OB2IlEUxORJ0ncIWLemiJa2oQ/IonB1ysxHQo=
|
||||
github.com/sagernet/sing-quic v0.1.6-beta.2/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.1.6-rc.1 h1:E+8OyyVg0YfFNUmxMx9jYBEhjLYMQSAMzJrUmE934bo=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.6-rc.1/go.mod h1:wFkU7sKxyZADS/idtJqBhtc+QBf5iwX9nZO7ymcn6MM=
|
||||
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.0-rc.1 h1:CnlxRgrJKAMKYNuJOcKie6TjRz8wremEq1wndLup7cA=
|
||||
github.com/sagernet/sing-tun v0.2.0-rc.1/go.mod h1:hpbL9jNAbYT9G2EHCpCXVIgSrM/2Wgnrm/Hped+8zdY=
|
||||
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,10 +168,10 @@ 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.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4=
|
||||
golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
||||
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=
|
||||
@@ -204,14 +204,14 @@ 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=
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -122,18 +122,14 @@ type ListenOptions struct {
|
||||
|
||||
type UDPTimeoutCompat Duration
|
||||
|
||||
func (c UDPTimeoutCompat) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal((time.Duration)(c).String())
|
||||
}
|
||||
|
||||
func (c *UDPTimeoutCompat) UnmarshalJSON(data []byte) error {
|
||||
func (u *UDPTimeoutCompat) UnmarshalJSON(data []byte) error {
|
||||
var valueNumber int64
|
||||
err := json.Unmarshal(data, &valueNumber)
|
||||
if err == nil {
|
||||
*c = UDPTimeoutCompat(time.Second * time.Duration(valueNumber))
|
||||
*u = UDPTimeoutCompat(time.Second * time.Duration(valueNumber))
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(data, (*Duration)(c))
|
||||
return json.Unmarshal(data, (*Duration)(u))
|
||||
}
|
||||
|
||||
type ListenOptionsWrapper interface {
|
||||
|
||||
@@ -30,10 +30,14 @@ 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) {
|
||||
options.UDPFragmentDefault = true
|
||||
if options.Detour != "" {
|
||||
return nil, E.New("detour option for direct outbound is illegal")
|
||||
}
|
||||
outboundDialer, err := dialer.New(router, options.DialerOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -50,6 +54,7 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti
|
||||
domainStrategy: dns.DomainStrategy(options.DomainStrategy),
|
||||
fallbackDelay: time.Duration(options.FallbackDelay),
|
||||
dialer: outboundDialer,
|
||||
loopBack: newLoopBackDetector(),
|
||||
}
|
||||
if options.ProxyProtocol != 0 {
|
||||
return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
|
||||
@@ -88,7 +93,11 @@ func (h *Direct) DialContext(ctx context.Context, network string, destination M.
|
||||
case N.NetworkUDP:
|
||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
}
|
||||
return h.dialer.DialContext(ctx, network, destination)
|
||||
conn, err := h.dialer.DialContext(ctx, network, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return h.loopBack.NewConn(conn), nil
|
||||
}
|
||||
|
||||
func (h *Direct) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) {
|
||||
@@ -142,6 +151,7 @@ func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn = h.loopBack.NewPacketConn(conn)
|
||||
if originDestination != destination {
|
||||
conn = bufio.NewNATPacketConn(bufio.NewPacketConn(conn), destination, originDestination)
|
||||
}
|
||||
@@ -149,9 +159,15 @@ 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)
|
||||
}
|
||||
|
||||
153
outbound/direct_loopback_detect.go
Normal file
153
outbound/direct_loopback_detect.go
Normal file
@@ -0,0 +1,153 @@
|
||||
package outbound
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
)
|
||||
|
||||
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 net.PacketConn) net.PacketConn {
|
||||
connAddr := M.AddrPortFromNet(conn.LocalAddr())
|
||||
if !connAddr.IsValid() {
|
||||
return conn
|
||||
}
|
||||
l.packetConnAccess.Lock()
|
||||
l.packetConnMap[connAddr] = true
|
||||
l.packetConnAccess.Unlock()
|
||||
return &loopBackDetectPacketWrapper{PacketConn: 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 {
|
||||
net.PacketConn
|
||||
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.PacketConn.Close()
|
||||
}
|
||||
|
||||
func (w *loopBackDetectPacketWrapper) ReaderReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *loopBackDetectPacketWrapper) WriterReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *loopBackDetectPacketWrapper) Upstream() any {
|
||||
return w.PacketConn
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -126,9 +127,6 @@ func (s *URLTest) CheckOutbounds() {
|
||||
func (s *URLTest) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
s.group.Touch()
|
||||
outbound := s.group.Select(network)
|
||||
if outbound == nil {
|
||||
return nil, E.New("missing supported outbound")
|
||||
}
|
||||
conn, err := outbound.DialContext(ctx, network, destination)
|
||||
if err == nil {
|
||||
return s.group.interruptGroup.NewConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil
|
||||
@@ -141,9 +139,6 @@ 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.Select(N.NetworkUDP)
|
||||
if outbound == nil {
|
||||
return nil, E.New("missing supported outbound")
|
||||
}
|
||||
conn, err := outbound.ListenPacket(ctx, destination)
|
||||
if err == nil {
|
||||
return s.group.interruptGroup.NewPacketConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil
|
||||
@@ -384,12 +379,12 @@ 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 != nil && outbound != g.selectedOutboundTCP {
|
||||
if outbound != g.selectedOutboundTCP {
|
||||
g.selectedOutboundTCP = outbound
|
||||
updated = true
|
||||
}
|
||||
outbound = g.Select(N.NetworkUDP)
|
||||
if outbound != nil && outbound != g.selectedOutboundUDP {
|
||||
if outbound != g.selectedOutboundUDP {
|
||||
g.selectedOutboundUDP = outbound
|
||||
updated = true
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ func NewRouter(
|
||||
if server.Detour == "" {
|
||||
detour = dialer.NewRouter(router)
|
||||
} else {
|
||||
detour = dialer.NewDetour(router, server.Detour)
|
||||
detour = dialer.NewDetour(router, server.Detour, true)
|
||||
}
|
||||
switch server.Address {
|
||||
case "local":
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user