mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-12 01:57:18 +10:00
Compare commits
77 Commits
v1.12.0-al
...
dev-test-m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1189d88048 | ||
|
|
82bc416985 | ||
|
|
276584be09 | ||
|
|
5d8f43cc6b | ||
|
|
ef92ed6795 | ||
|
|
e257b19bb3 | ||
|
|
e0e7a9ee96 | ||
|
|
170d22c4c5 | ||
|
|
b0a36b9fac | ||
|
|
079c491064 | ||
|
|
6794db201e | ||
|
|
a36080c51f | ||
|
|
324073f5dd | ||
|
|
ece54e8f2b | ||
|
|
566b97d5e5 | ||
|
|
d698e23a4d | ||
|
|
af1db56132 | ||
|
|
7a22d507b1 | ||
|
|
e9fb5faef6 | ||
|
|
7d6adf6933 | ||
|
|
f47803ca36 | ||
|
|
19b388fee4 | ||
|
|
d533f3e5c6 | ||
|
|
6435808a44 | ||
|
|
4975c3acac | ||
|
|
5c6fcc82ff | ||
|
|
652ea40c08 | ||
|
|
3488877fb9 | ||
|
|
a1547b337c | ||
|
|
d9dc7da43d | ||
|
|
a2fc5a04ce | ||
|
|
1a3c9ec2a4 | ||
|
|
3bbb0f4439 | ||
|
|
f982c02c34 | ||
|
|
ea77932276 | ||
|
|
3ef7cb1333 | ||
|
|
7035d803fc | ||
|
|
462587696b | ||
|
|
e4e0744da5 | ||
|
|
0c261c205b | ||
|
|
6249f076c6 | ||
|
|
9559d7b375 | ||
|
|
a160309182 | ||
|
|
16c3c682f8 | ||
|
|
f49536012e | ||
|
|
773cb3dcec | ||
|
|
8aaaa06d92 | ||
|
|
ce0ae1fa29 | ||
|
|
cf8889ba5d | ||
|
|
dbe8239ed9 | ||
|
|
28f937e13e | ||
|
|
a3c44f7160 | ||
|
|
469faff07c | ||
|
|
067ea0dccb | ||
|
|
e515c91bd2 | ||
|
|
2c648b7fbe | ||
|
|
b3ea0bd833 | ||
|
|
db5ec3cdfc | ||
|
|
9aca54d039 | ||
|
|
d55d5009c2 | ||
|
|
4f3ee61104 | ||
|
|
96eb98c00a | ||
|
|
68ce9577c6 | ||
|
|
3ae036e997 | ||
|
|
5da2d1d470 | ||
|
|
8e2baf40f1 | ||
|
|
c24c40dfee | ||
|
|
32e52ce1ed | ||
|
|
ed46438359 | ||
|
|
0b5490d5a3 | ||
|
|
2d73ef511d | ||
|
|
63e6c85f6f | ||
|
|
8946a6d2d0 | ||
|
|
d3132645fb | ||
|
|
373f158fe0 | ||
|
|
ce36835fab | ||
|
|
619fa671d7 |
3
.github/goreleaser/README.md
vendored
Normal file
3
.github/goreleaser/README.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# goreleaser
|
||||
|
||||
I'm sorry for this, but I can't afford to renew for now because the subscription is too expensive for an open source project.
|
||||
BIN
.github/goreleaser/ca.crt
vendored
Normal file
BIN
.github/goreleaser/ca.crt
vendored
Normal file
Binary file not shown.
87
.github/goreleaser/config.json
vendored
Normal file
87
.github/goreleaser/config.json
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "tls",
|
||||
"server": "8.8.8.8"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "tun",
|
||||
"address": [
|
||||
"172.19.0.1/30",
|
||||
"fdfe:dcba:9876::1/126"
|
||||
],
|
||||
"auto_route": true,
|
||||
"auto_redirect": true
|
||||
}
|
||||
],
|
||||
"certificate": {
|
||||
"tls_decryption": {
|
||||
"enabled": true,
|
||||
"key_pair_p12": "MIIKYQIBAzCCChEGCSqGSIb3DQEHAaCCCgIEggn+MIIJ+jCCBGgGCSqGSIb3DQEHBqCCBFkwggRVAgEAMIIETgYJKoZIhvcNAQcBMF0GCSqGSIb3DQEFDTBQMC8GCSqGSIb3DQEFDDAiBBBxLjkB6wrMHpRNPnq8KUnXAgIIADAKBggqhkiG9w0CCTAdBglghkgBZQMEASoEEHFou8IR0ZPb9O4NaLDC5LKAggPgL/7EoJRMEx5ZDVm2ZUQRuGyjS+lMB4JDZiykYfvfzMtQ2LZ+aO90rLxYFh4uBpbu+mmA0WDF/HU3GbE0nyY9beo0RAh0/u2Ak2kkfDSntRPVTl5zNBrT9hEtH9oSlN7tok9SMhWEJlsoIRhGinJwsDnDbXcIqkIj/oqtXlSJc6gA7CYf6AJRrVjP1Wtk80GMrMfYNvQw9bich5fs4biddf0xtR13YFV80rCPb+HtTT4KYa7Rzo5qR/cNHsMP/3v5BT2UszpaSIokPoW8ta1RWcQNXuH3OHjG4GMjg88w6xtyudIKrTyP0BTRfIJ2S2EtsWGHU2Gmr/MUY0a7abbtG+LVdSCRTgDoNeiY4C7lkQEOpefoZHWa3+jeGu17812YZHxfCZuhFy33rZgqngWRN1cdxoAbhozChtKmn0Uhdox7jqUw5M/Sj4DWHm0RNB8Ffvf39i/zvlfORzljIiwAKiB26FwpcKKRfx7rrjx4xRLkTLWl0DnJKxOcVz/oXSjglpHJvUSMgbpzXEHHQ7+d+K/WTnoj+dONifxiWBt1hQA8qoPiQceYWGY37oeWvGZI/Qv3ZSO5Mm/yVAuAFyOzJdpW5aC3Kq3gwNVbKNeeV5fWDtvP2K9XcgZFv8OqpNnvLmaL+iWHTPg5wYGvf0iWPr8NVU6OQpSZCOTodwOGfcpQ2YlCnkBgkjkJFLNuM4mi1U9kyTZWAYyZ6zVort0eezJcBoQGBBV2/GkFmwDNa9Q8mT8S7QTf7ZqAtyMnM9rBch7zIscBk6swG/KhgFRtUmDLpY6tpMb6vHHueu4duaUvIXvdjgTe4oE2Ou36VZ1+dC+RswmGCMwFlHqsZiIfU26SDiC3G9wH0iIg6th3LrDJYYD57l5Ps0pVjS7RAYYzu1lA2d2wGEFBJ3UEpJp257Wv2I6foeoTYXSX/XM1JUuFv1516qSqwPk4a1E6N6J+d+iWvM7BBcwakMG1XSUT4zhHrBzPPxXCCBeJHTcOoiaqXwaqsBBButSxViysvGZcBbyAxZNtmXCDh33a760XF4tb1f0mb2jW13CMARGOeubM3Z21eoc16tFkoKSD3wlzT2VlxVuUIgBT+wx4GOWgldngn2aXWInOkaEFdwABBLh5egxNBAI2tzirk6ijpRCq+gquTbEhxIwJavCfdYc0lqMevsEiZxqjoZHEf1EoId/rd3TEdclRf5OzLjbSbDICtFI82S5A+wDMXltVmB+Rw1mBZZUhvbUUOC7ARQ0pkE8DfwgPviFo2z8/i++3Mb02D57V1Mz1k6PB56QzlEOTJrmaBXSQs7U8Aiuln6CA+McwggWKBgkqhkiG9w0BBwGgggV7BIIFdzCCBXMwggVvBgsqhkiG9w0BDAoBAqCCBTcwggUzMF0GCSqGSIb3DQEFDTBQMC8GCSqGSIb3DQEFDDAiBBCl+G6epsuiNjP2afUFOwazAgIIADAKBggqhkiG9w0CCTAdBglghkgBZQMEASoEEG2FLRo+Ud+dbzCVbrer71YEggTQ81fiT0+gLnYWZpNq0MV/kPma4P+sws4wRd5CVG5rCMwmmr3JUCVk66uYLZTBXqHJ0qy3CPE2K1siImQJNS3DMD1q9WVCLPFEPLbO1ycsV73AOMc2UNJMkY7AgGCMpK+u/afMewsnAk/fmwjTw5qOm21TeesahwVvIMb3pQrkFu8FSIWK9IPRX7VCiYSa/KajiFKi0/lWEk9/LJEfikqGOB3FWYQkrV4jhhh+SNMm5LATgNgZ3FyhleruJZup0PN25W2IrpjcEBr9gHVU6gsCyB4PTTrVfopLq7goDWnOQeeAa4Y98QN6nT0EyqkfKU678/JeLz0gW8zijgdqzLwwucLg6cGE379d/2igE7/SJO8qa/JAjD3RDe88N97ysKW7vOOvIH6DnmkgQc8Cq/KKOyVlrDNx65YEft1oqVE3L5IfnmHT5ycbzyMJpdB6uL6OT9KqVLB2bHWDH47XfI8I8z56mzmKSXrWGm93beYV8u908Rokj82LHGEf9th6ttBZykWZgS+hQjc3jIU8xpa2/7mpPVFBCTiphBtp3+fCEVKmnubiiwe28Lw+xEvX8oAEGXhi5fNIGrAXvMk/rgpoh44wQwET6WnyiO8Ad8hOxvPtwgGD0m0FNFlv+yIGzY1PZeevquLKEwtvllo/A3g0OUbeGC2qC5s8VGkv11FRQPdUnOV2oXvosAWqxh6SnVrG8xbxc5L2xjJuUH8b70ne4iXzcfXo5FubtLuuJ6WNFWO9UasmvKaMqFZDlMK8FMcNTq6X0m8ilRZf056C3FDQAMxIa9mKyWebm3+4+LfxjgWo1dxvXR5HnMpzCbcoz/TIbSiUzSTaihxpzMi7Cvkc/JqPTTSkqjR+jLw7tOZucP8VtpQmQvqg2fd1hBgqam37qVC45D2765/V5v74+gtn5nc6HrGOEwpLlqcy5kojrjhQkNUkS7x5vg1KOFP/9uoC67qRFaGH5EM0XAdTVAyt1gn+StXVCXsNKvX93BPaNwL7we/zYZPpERFHaVD9R1Fw2Bz0+RzcdNQqP9yiq3mmmGNZHS0KSAKP3cmA3pwt9gPjpt/L1VNFgkVti2/YIDF37c3yuU9ZBI6kA7LhkcH5j0APr1ppS+Zxw6UKhsZDSGySqPyz3C2k4wy+R2+8mO1dN9haRW0smWWnziHWh8OFGhG+ghvc2HiX1tg2dTrByIFr9wixs4Kn9wDg0Qc1mS0+2+KacO8todl3jVYsLhcSTt5d2b/ZHuyAx4UPFtWPPF1vFdRMnf1jq83q/OEcTSfqkiEpEzs6NXpDEy1E0neq+LVHXi37IHzTGjjIvBnE2KZUoUdiFYitfDoUQpdhSpWKZTsmpqVXi/b7TZ20scvt5Qb6nfEWNds7hyGhnzAGQIV64xaDhKDB1p3QpDYxsJHvAGC1Yj9CY5w+sYpOjsfUo0qKeaFmu0fWX44s388GjZbid92/UvIxN9Lt/jri2xq+XPjJR194hc2ITUDrZvaaqeZ4odH4HXUC7FMqL6NVeX6MIv4g2QQkrt9DO35LokztOQCeuaA4rOZiM7mR3JJZIXf2jFNwElU0bvUJY2eYcQwTSQPXBzMz0AvhXxJiOFx3IQHHl55j4KMpab/NNbHChDUWJ7ptLX0/x8R3scJjCqwxJTAjBgkqhkiG9w0BCRUxFgQU6W6dFe0wnwEJqyaK7H5cUfUvxzQwRzAvMAsGCWCGSAFlAwQCAQQghQoDpo4gS46c+xoCeAykL69ZRT3zYrgNkgvL6s2UIVsEEHkNJl2cMvSFmZ8gi+cS/vQCAggA",
|
||||
"key_pair_p12_password": "D173A3D9"
|
||||
}
|
||||
},
|
||||
"mitm": {
|
||||
"enabled": true,
|
||||
"http2_enabled": true
|
||||
},
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"action": "sniff"
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
"mode": "or",
|
||||
"rules": [
|
||||
{
|
||||
"network": "udp",
|
||||
"port": 53
|
||||
},
|
||||
{
|
||||
"protocol": "dns"
|
||||
}
|
||||
],
|
||||
"action": "hijack-dns"
|
||||
},
|
||||
{
|
||||
"ip_is_private": true,
|
||||
"outbound": "direct"
|
||||
},
|
||||
{
|
||||
"action": "resolve"
|
||||
},
|
||||
{
|
||||
"domain": "goreleaser.com",
|
||||
"action": "route-options",
|
||||
"mitm": {
|
||||
"enabled": true,
|
||||
"surge_map_local": [
|
||||
"^https://goreleaser\\.com/static/latest-pro data-type=text data=\"(update check disabled)\""
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"domain": "api.gumroad.com",
|
||||
"action": "route-options",
|
||||
"mitm": {
|
||||
"enabled": true,
|
||||
"surge_map_local": [
|
||||
"^https://api\\.gumroad\\.com/v2/licenses/verify data-type=file data=.github/goreleaser/response.json header=\"Content-Type:application/json\""
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
11
.github/goreleaser/configure.sh
vendored
Executable file
11
.github/goreleaser/configure.sh
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
release/local/install_minimal.sh
|
||||
sudo cp .github/goreleaser/config.json /usr/local/etc/sing-box/config.json
|
||||
sudo mkdir -p /var/lib/sing-box/.github/goreleaser
|
||||
sudo cp .github/goreleaser/response.json /var/lib/sing-box/.github/goreleaser/response.json
|
||||
go run -v ./cmd/sing-box tools install-ca .github/goreleaser/ca.crt
|
||||
sudo systemctl start sing-box
|
||||
sleep 5
|
||||
12
.github/goreleaser/response.json
vendored
Normal file
12
.github/goreleaser/response.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"success": true,
|
||||
"purchase": {
|
||||
"license_key": "fake-key",
|
||||
"subscription_id": "fake-id",
|
||||
"product_id": "7ev6hHL7RZc753daE5bRNw==",
|
||||
"product_permalink": "https:\/\/beckersoft.gumroad.com\/l\/goreleaser",
|
||||
"seller_id": "A2wDalJj66fJdFU_jwy_oA==",
|
||||
"short_product_id": "CadfZ",
|
||||
"permalink": "goreleaser"
|
||||
}
|
||||
}
|
||||
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -155,7 +155,7 @@ jobs:
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser-pro
|
||||
version: 2.5.1
|
||||
version: '~> v2'
|
||||
install-only: true
|
||||
- name: Extract signing key
|
||||
run: |-
|
||||
@@ -551,7 +551,7 @@ jobs:
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser-pro
|
||||
version: 2.5.1
|
||||
version: '~> v2'
|
||||
install-only: true
|
||||
- name: Cache ghr
|
||||
uses: actions/cache@v4
|
||||
|
||||
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser-pro
|
||||
version: latest
|
||||
version: '~> v2'
|
||||
args: release -f .goreleaser.fury.yaml --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
/.idea/
|
||||
/vendor/
|
||||
/*.json
|
||||
/*.js
|
||||
/*.srs
|
||||
/*.db
|
||||
/site/
|
||||
|
||||
@@ -31,6 +31,7 @@ run:
|
||||
- with_reality_server
|
||||
- with_acme
|
||||
- with_clash_api
|
||||
- with_script
|
||||
|
||||
issues:
|
||||
exclude-dirs:
|
||||
|
||||
@@ -21,6 +21,7 @@ builds:
|
||||
- with_acme
|
||||
- with_clash_api
|
||||
- with_tailscale
|
||||
- with_script
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GOTOOLCHAIN=local
|
||||
@@ -51,6 +52,7 @@ builds:
|
||||
- with_acme
|
||||
- with_clash_api
|
||||
- with_tailscale
|
||||
- with_script
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GOROOT={{ .Env.GOPATH }}/go_legacy
|
||||
@@ -97,10 +99,12 @@ archives:
|
||||
builds:
|
||||
- main
|
||||
- android
|
||||
format: tar.gz
|
||||
formats:
|
||||
- tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
formats:
|
||||
- zip
|
||||
wrap_in_directory: true
|
||||
files:
|
||||
- LICENSE
|
||||
|
||||
6
Makefile
6
Makefile
@@ -1,7 +1,7 @@
|
||||
NAME = sing-box
|
||||
COMMIT = $(shell git rev-parse --short HEAD)
|
||||
TAGS_GO120 = with_gvisor,with_dhcp,with_wireguard,with_reality_server,with_clash_api,with_quic,with_utls
|
||||
TAGS_GO123 = with_tailscale
|
||||
TAGS_GO123 = with_tailscale,with_script
|
||||
TAGS ?= $(TAGS_GO120),$(TAGS_GO123)
|
||||
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_utls,with_reality_server
|
||||
|
||||
@@ -233,8 +233,8 @@ lib:
|
||||
go run ./cmd/internal/build_libbox -target ios
|
||||
|
||||
lib_install:
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.4
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.4
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.5
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.5
|
||||
|
||||
docs:
|
||||
venv/bin/mkdocs serve
|
||||
|
||||
@@ -10,6 +10,9 @@ import (
|
||||
type CertificateStore interface {
|
||||
LifecycleService
|
||||
Pool() *x509.CertPool
|
||||
TLSDecryptionEnabled() bool
|
||||
TLSDecryptionCertificate() *x509.Certificate
|
||||
TLSDecryptionPrivateKey() any
|
||||
}
|
||||
|
||||
func RootPoolFromContext(ctx context.Context) *x509.CertPool {
|
||||
|
||||
@@ -45,10 +45,10 @@ type RDRCStore interface {
|
||||
}
|
||||
|
||||
type DNSTransport interface {
|
||||
Lifecycle
|
||||
Type() string
|
||||
Tag() string
|
||||
Dependencies() []string
|
||||
Reset()
|
||||
Exchange(ctx context.Context, message *dns.Msg) (*dns.Msg, error)
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,10 @@ type CacheFile interface {
|
||||
StoreGroupExpand(group string, expand bool) error
|
||||
LoadRuleSet(tag string) *SavedBinary
|
||||
SaveRuleSet(tag string, set *SavedBinary) error
|
||||
LoadScript(tag string) *SavedBinary
|
||||
SaveScript(tag string, script *SavedBinary) error
|
||||
SurgePersistentStoreRead(key string) string
|
||||
SurgePersistentStoreWrite(key string, value string) error
|
||||
}
|
||||
|
||||
type SavedBinary struct {
|
||||
|
||||
@@ -2,6 +2,8 @@ package adapter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
@@ -53,10 +55,13 @@ type InboundContext struct {
|
||||
|
||||
// sniffer
|
||||
|
||||
Protocol string
|
||||
Domain string
|
||||
Client string
|
||||
SniffContext any
|
||||
Protocol string
|
||||
Domain string
|
||||
Client string
|
||||
SniffContext any
|
||||
PacketSniffError error
|
||||
HTTPRequest *http.Request
|
||||
ClientHello *tls.ClientHelloInfo
|
||||
|
||||
// cache
|
||||
|
||||
@@ -73,6 +78,7 @@ type InboundContext struct {
|
||||
UDPTimeout time.Duration
|
||||
TLSFragment bool
|
||||
TLSFragmentFallbackDelay time.Duration
|
||||
MITM *option.MITMRouteOptions
|
||||
|
||||
NetworkStrategy *C.NetworkStrategy
|
||||
NetworkType []C.InterfaceType
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package adapter
|
||||
|
||||
import E "github.com/sagernet/sing/common/exceptions"
|
||||
import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type StartStage uint8
|
||||
|
||||
@@ -45,6 +47,9 @@ type LifecycleService interface {
|
||||
|
||||
func Start(stage StartStage, services ...Lifecycle) error {
|
||||
for _, service := range services {
|
||||
if service == nil {
|
||||
continue
|
||||
}
|
||||
err := service.Start(stage)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
13
adapter/mitm.go
Normal file
13
adapter/mitm.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package adapter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
type MITMEngine interface {
|
||||
Lifecycle
|
||||
NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
||||
}
|
||||
54
adapter/script.go
Normal file
54
adapter/script.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package adapter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ScriptManager interface {
|
||||
Lifecycle
|
||||
Scripts() []Script
|
||||
Script(name string) (Script, bool)
|
||||
SurgeCache() *SurgeInMemoryCache
|
||||
}
|
||||
|
||||
type SurgeInMemoryCache struct {
|
||||
sync.RWMutex
|
||||
Data map[string]string
|
||||
}
|
||||
|
||||
type Script interface {
|
||||
Type() string
|
||||
Tag() string
|
||||
StartContext(ctx context.Context, startContext *HTTPStartContext) error
|
||||
PostStart() error
|
||||
Close() error
|
||||
}
|
||||
|
||||
type SurgeScript interface {
|
||||
Script
|
||||
ExecuteGeneric(ctx context.Context, scriptType string, timeout time.Duration, arguments []string) error
|
||||
ExecuteHTTPRequest(ctx context.Context, timeout time.Duration, request *http.Request, body []byte, binaryBody bool, arguments []string) (*HTTPRequestScriptResult, error)
|
||||
ExecuteHTTPResponse(ctx context.Context, timeout time.Duration, request *http.Request, response *http.Response, body []byte, binaryBody bool, arguments []string) (*HTTPResponseScriptResult, error)
|
||||
}
|
||||
|
||||
type HTTPRequestScriptResult struct {
|
||||
URL string
|
||||
Headers http.Header
|
||||
Body []byte
|
||||
Response *HTTPRequestScriptResponse
|
||||
}
|
||||
|
||||
type HTTPRequestScriptResponse struct {
|
||||
Status int
|
||||
Headers http.Header
|
||||
Body []byte
|
||||
}
|
||||
|
||||
type HTTPResponseScriptResult struct {
|
||||
Status int
|
||||
Headers http.Header
|
||||
Body []byte
|
||||
}
|
||||
60
box.go
60
box.go
@@ -23,9 +23,11 @@ import (
|
||||
"github.com/sagernet/sing-box/experimental/cachefile"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/mitm"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/protocol/direct"
|
||||
"github.com/sagernet/sing-box/route"
|
||||
"github.com/sagernet/sing-box/script"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
@@ -48,6 +50,8 @@ type Box struct {
|
||||
dnsRouter *dns.Router
|
||||
connection *route.ConnectionManager
|
||||
router *route.Router
|
||||
script *script.Manager
|
||||
mitm adapter.MITMEngine //*mitm.Engine
|
||||
services []adapter.LifecycleService
|
||||
done chan struct{}
|
||||
}
|
||||
@@ -143,18 +147,12 @@ func New(options Options) (*Box, error) {
|
||||
}
|
||||
|
||||
var services []adapter.LifecycleService
|
||||
certificateOptions := common.PtrValueOrDefault(options.Certificate)
|
||||
if C.IsAndroid || certificateOptions.Store != "" && certificateOptions.Store != C.CertificateStoreSystem ||
|
||||
len(certificateOptions.Certificate) > 0 ||
|
||||
len(certificateOptions.CertificatePath) > 0 ||
|
||||
len(certificateOptions.CertificateDirectoryPath) > 0 {
|
||||
certificateStore, err := certificate.NewStore(ctx, logFactory.NewLogger("certificate"), certificateOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
service.MustRegister[adapter.CertificateStore](ctx, certificateStore)
|
||||
services = append(services, certificateStore)
|
||||
certificateStore, err := certificate.NewStore(ctx, logFactory.NewLogger("certificate"), common.PtrValueOrDefault(options.Certificate))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
service.MustRegister[adapter.CertificateStore](ctx, certificateStore)
|
||||
services = append(services, certificateStore)
|
||||
|
||||
routeOptions := common.PtrValueOrDefault(options.Route)
|
||||
dnsOptions := common.PtrValueOrDefault(options.DNS)
|
||||
@@ -173,7 +171,7 @@ func New(options Options) (*Box, error) {
|
||||
return nil, E.Cause(err, "initialize network manager")
|
||||
}
|
||||
service.MustRegister[adapter.NetworkManager](ctx, networkManager)
|
||||
connectionManager := route.NewConnectionManager(logFactory.NewLogger("connection"))
|
||||
connectionManager := route.NewConnectionManager(ctx, logFactory.NewLogger("connection"))
|
||||
service.MustRegister[adapter.ConnectionManager](ctx, connectionManager)
|
||||
router := route.NewRouter(ctx, logFactory, routeOptions, dnsOptions)
|
||||
service.MustRegister[adapter.Router](ctx, router)
|
||||
@@ -181,8 +179,8 @@ func New(options Options) (*Box, error) {
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize router")
|
||||
}
|
||||
ntpOptions := common.PtrValueOrDefault(options.NTP)
|
||||
var timeService *tls.TimeServiceWrapper
|
||||
ntpOptions := common.PtrValueOrDefault(options.NTP)
|
||||
if ntpOptions.Enabled {
|
||||
timeService = new(tls.TimeServiceWrapper)
|
||||
service.MustRegister[ntp.TimeService](ctx, timeService)
|
||||
@@ -216,8 +214,15 @@ func New(options Options) (*Box, error) {
|
||||
} else {
|
||||
tag = F.ToString(i)
|
||||
}
|
||||
endpointCtx := ctx
|
||||
if tag != "" {
|
||||
// TODO: remove this
|
||||
endpointCtx = adapter.WithContext(endpointCtx, &adapter.InboundContext{
|
||||
Outbound: tag,
|
||||
})
|
||||
}
|
||||
err = endpointManager.Create(
|
||||
ctx,
|
||||
endpointCtx,
|
||||
router,
|
||||
logFactory.NewLogger(F.ToString("endpoint/", endpointOptions.Type, "[", tag, "]")),
|
||||
tag,
|
||||
@@ -289,6 +294,11 @@ func New(options Options) (*Box, error) {
|
||||
"local",
|
||||
option.LocalDNSServerOptions{},
|
||||
)))
|
||||
scriptManager, err := script.NewManager(ctx, logFactory, options.Scripts)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize script manager")
|
||||
}
|
||||
service.MustRegister[adapter.ScriptManager](ctx, scriptManager)
|
||||
if platformInterface != nil {
|
||||
err = platformInterface.Initialize(networkManager)
|
||||
if err != nil {
|
||||
@@ -338,6 +348,16 @@ func New(options Options) (*Box, error) {
|
||||
timeService.TimeService = ntpService
|
||||
services = append(services, adapter.NewLifecycleService(ntpService, "ntp service"))
|
||||
}
|
||||
mitmOptions := common.PtrValueOrDefault(options.MITM)
|
||||
var mitmEngine adapter.MITMEngine
|
||||
if mitmOptions.Enabled {
|
||||
engine, err := mitm.NewEngine(ctx, logFactory.NewLogger("mitm"), mitmOptions)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create MITM engine")
|
||||
}
|
||||
service.MustRegister[adapter.MITMEngine](ctx, engine)
|
||||
mitmEngine = engine
|
||||
}
|
||||
return &Box{
|
||||
network: networkManager,
|
||||
endpoint: endpointManager,
|
||||
@@ -347,6 +367,8 @@ func New(options Options) (*Box, error) {
|
||||
dnsRouter: dnsRouter,
|
||||
connection: connectionManager,
|
||||
router: router,
|
||||
script: scriptManager,
|
||||
mitm: mitmEngine,
|
||||
createdAt: createdAt,
|
||||
logFactory: logFactory,
|
||||
logger: logFactory.Logger(),
|
||||
@@ -405,11 +427,11 @@ func (s *Box) preStart() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(adapter.StartStateInitialize, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint)
|
||||
err = adapter.Start(adapter.StartStateInitialize, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.script, s.mitm, s.outbound, s.inbound, s.endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(adapter.StartStateStart, s.outbound, s.dnsTransport, s.dnsRouter, s.network, s.connection, s.router)
|
||||
err = adapter.Start(adapter.StartStateStart, s.outbound, s.dnsTransport, s.dnsRouter, s.network, s.connection, s.router, s.script, s.mitm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -433,7 +455,7 @@ func (s *Box) start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(adapter.StartStatePostStart, s.outbound, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.inbound, s.endpoint)
|
||||
err = adapter.Start(adapter.StartStatePostStart, s.outbound, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.script, s.mitm, s.inbound, s.endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -441,7 +463,7 @@ func (s *Box) start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(adapter.StartStateStarted, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint)
|
||||
err = adapter.Start(adapter.StartStateStarted, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.script, s.mitm, s.outbound, s.inbound, s.endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -460,7 +482,7 @@ func (s *Box) Close() error {
|
||||
close(s.done)
|
||||
}
|
||||
err := common.Close(
|
||||
s.inbound, s.outbound, s.endpoint, s.router, s.connection, s.dnsRouter, s.dnsTransport, s.network,
|
||||
s.inbound, s.outbound, s.endpoint, s.mitm, s.script, s.router, s.connection, s.dnsRouter, s.dnsTransport, s.network,
|
||||
)
|
||||
for _, lifecycleService := range s.services {
|
||||
err = E.Append(err, lifecycleService.Close(), func(err error) error {
|
||||
|
||||
Submodule clients/android updated: 0576fd75a6...aefe3c0290
Submodule clients/apple updated: a828bb3a93...ae5818ee5a
@@ -45,6 +45,7 @@ var (
|
||||
debugFlags []string
|
||||
sharedTags []string
|
||||
iosTags []string
|
||||
memcTags []string
|
||||
debugTags []string
|
||||
)
|
||||
|
||||
@@ -58,8 +59,9 @@ func init() {
|
||||
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid=")
|
||||
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag)
|
||||
|
||||
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api", "with_tailscale")
|
||||
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api", "with_script")
|
||||
iosTags = append(iosTags, "with_dhcp", "with_low_memory", "with_conntrack")
|
||||
memcTags = append(memcTags, "with_tailscale")
|
||||
debugTags = append(debugTags, "debug")
|
||||
}
|
||||
|
||||
@@ -99,18 +101,19 @@ func buildAndroid() {
|
||||
"-javapkg=io.nekohasekai",
|
||||
"-libname=box",
|
||||
}
|
||||
|
||||
if !debugEnabled {
|
||||
args = append(args, sharedFlags...)
|
||||
} else {
|
||||
args = append(args, debugFlags...)
|
||||
}
|
||||
|
||||
args = append(args, "-tags")
|
||||
if !debugEnabled {
|
||||
args = append(args, strings.Join(sharedTags, ","))
|
||||
} else {
|
||||
args = append(args, strings.Join(append(sharedTags, debugTags...), ","))
|
||||
tags := append(sharedTags, memcTags...)
|
||||
if debugEnabled {
|
||||
tags = append(tags, debugTags...)
|
||||
}
|
||||
|
||||
args = append(args, "-tags", strings.Join(tags, ","))
|
||||
args = append(args, "./experimental/libbox")
|
||||
|
||||
command := exec.Command(build_shared.GoBinPath+"/gomobile", args...)
|
||||
@@ -148,7 +151,9 @@ func buildApple() {
|
||||
"-v",
|
||||
"-target", bindTarget,
|
||||
"-libname=box",
|
||||
"-tags-macos=" + strings.Join(memcTags, ","),
|
||||
}
|
||||
|
||||
if !debugEnabled {
|
||||
args = append(args, sharedFlags...)
|
||||
} else {
|
||||
@@ -156,12 +161,11 @@ func buildApple() {
|
||||
}
|
||||
|
||||
tags := append(sharedTags, iosTags...)
|
||||
args = append(args, "-tags")
|
||||
if !debugEnabled {
|
||||
args = append(args, strings.Join(tags, ","))
|
||||
} else {
|
||||
args = append(args, strings.Join(append(tags, debugTags...), ","))
|
||||
if debugEnabled {
|
||||
tags = append(tags, debugTags...)
|
||||
}
|
||||
|
||||
args = append(args, "-tags", strings.Join(tags, ","))
|
||||
args = append(args, "./experimental/libbox")
|
||||
|
||||
command := exec.Command(build_shared.GoBinPath+"/gomobile", args...)
|
||||
|
||||
121
cmd/sing-box/cmd_generate_ca.go
Normal file
121
cmd/sing-box/cmd_generate_ca.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"software.sslmate.com/src/go-pkcs12"
|
||||
)
|
||||
|
||||
var (
|
||||
flagGenerateCAName string
|
||||
flagGenerateCAPKCS12Password string
|
||||
flagGenerateOutput string
|
||||
)
|
||||
|
||||
var commandGenerateCAKeyPair = &cobra.Command{
|
||||
Use: "ca-keypair",
|
||||
Short: "Generate CA key pair",
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := generateCAKeyPair()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
commandGenerateCAKeyPair.Flags().StringVarP(&flagGenerateCAName, "name", "n", "", "Set custom CA name")
|
||||
commandGenerateCAKeyPair.Flags().StringVarP(&flagGenerateCAPKCS12Password, "p12-password", "p", "", "Set custom PKCS12 password")
|
||||
commandGenerateCAKeyPair.Flags().StringVarP(&flagGenerateOutput, "output", "o", ".", "Set output directory")
|
||||
commandGenerate.AddCommand(commandGenerateCAKeyPair)
|
||||
}
|
||||
|
||||
func generateCAKeyPair() error {
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
spkiASN1, err := x509.MarshalPKIXPublicKey(privateKey.Public())
|
||||
var spki struct {
|
||||
Algorithm pkix.AlgorithmIdentifier
|
||||
SubjectPublicKey asn1.BitString
|
||||
}
|
||||
_, err = asn1.Unmarshal(spkiASN1, &spki)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
skid := sha1.Sum(spki.SubjectPublicKey.Bytes)
|
||||
var caName string
|
||||
if flagGenerateCAName != "" {
|
||||
caName = flagGenerateCAName
|
||||
} else {
|
||||
caName = "sing-box Generated CA " + strings.ToUpper(hex.EncodeToString(skid[:4]))
|
||||
}
|
||||
caTpl := &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{caName},
|
||||
CommonName: caName,
|
||||
},
|
||||
SubjectKeyId: skid[:],
|
||||
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||
NotBefore: time.Now(),
|
||||
KeyUsage: x509.KeyUsageCertSign,
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
MaxPathLenZero: true,
|
||||
}
|
||||
publicDer, err := x509.CreateCertificate(rand.Reader, caTpl, caTpl, privateKey.Public(), privateKey)
|
||||
var caPassword string
|
||||
if flagGenerateCAPKCS12Password != "" {
|
||||
caPassword = flagGenerateCAPKCS12Password
|
||||
} else {
|
||||
caPassword = strings.ToUpper(hex.EncodeToString(skid[:4]))
|
||||
}
|
||||
caTpl.Raw = publicDer
|
||||
p12Bytes, err := pkcs12.Modern.Encode(privateKey, caTpl, nil, caPassword)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
privateDer, err := x509.MarshalPKCS8PrivateKey(privateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.WriteFile(filepath.Join(flagGenerateOutput, caName+".pem"), pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: publicDer}), 0o644)
|
||||
os.WriteFile(filepath.Join(flagGenerateOutput, caName+".private.pem"), pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privateDer}), 0o644)
|
||||
os.WriteFile(filepath.Join(flagGenerateOutput, caName+".crt"), publicDer, 0o644)
|
||||
os.WriteFile(filepath.Join(flagGenerateOutput, caName+".p12"), p12Bytes, 0o644)
|
||||
var tlsDecryptionOptions option.TLSDecryptionOptions
|
||||
tlsDecryptionOptions.Enabled = true
|
||||
tlsDecryptionOptions.KeyPair = base64.StdEncoding.EncodeToString(p12Bytes)
|
||||
tlsDecryptionOptions.KeyPairPassword = caPassword
|
||||
var certificateOptions option.CertificateOptions
|
||||
certificateOptions.TLSDecryption = &tlsDecryptionOptions
|
||||
encoder := json.NewEncoder(os.Stdout)
|
||||
encoder.SetIndent("", " ")
|
||||
return encoder.Encode(certificateOptions)
|
||||
}
|
||||
@@ -1,13 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -19,36 +12,5 @@ var commandTools = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
commandTools.PersistentFlags().StringVarP(&commandToolsFlagOutbound, "outbound", "o", "", "Use specified tag instead of default outbound")
|
||||
mainCommand.AddCommand(commandTools)
|
||||
}
|
||||
|
||||
func createPreStartedClient() (*box.Box, error) {
|
||||
options, err := readConfigAndMerge()
|
||||
if err != nil {
|
||||
if !(errors.Is(err, os.ErrNotExist) && len(configDirectories) == 0 && len(configPaths) == 1) || configPaths[0] != "config.json" {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
instance, err := box.New(box.Options{Context: globalCtx, Options: options})
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create service")
|
||||
}
|
||||
err = instance.PreStart()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "start service")
|
||||
}
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
func createDialer(instance *box.Box, outboundTag string) (N.Dialer, error) {
|
||||
if outboundTag == "" {
|
||||
return instance.Outbound().Default(), nil
|
||||
} else {
|
||||
outbound, loaded := instance.Outbound().Outbound(outboundTag)
|
||||
if !loaded {
|
||||
return nil, E.New("outbound not found: ", outboundTag)
|
||||
}
|
||||
return outbound, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var commandConnectFlagNetwork string
|
||||
|
||||
var commandConnect = &cobra.Command{
|
||||
Use: "connect <address>",
|
||||
Short: "Connect to an address",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := connect(args[0])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
commandConnect.Flags().StringVarP(&commandConnectFlagNetwork, "network", "n", "tcp", "network type")
|
||||
commandTools.AddCommand(commandConnect)
|
||||
}
|
||||
|
||||
func connect(address string) error {
|
||||
switch N.NetworkName(commandConnectFlagNetwork) {
|
||||
case N.NetworkTCP, N.NetworkUDP:
|
||||
default:
|
||||
return E.Cause(N.ErrUnknownNetwork, commandConnectFlagNetwork)
|
||||
}
|
||||
instance, err := createPreStartedClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer instance.Close()
|
||||
dialer, err := createDialer(instance, commandToolsFlagOutbound)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn, err := dialer.DialContext(context.Background(), commandConnectFlagNetwork, M.ParseSocksaddr(address))
|
||||
if err != nil {
|
||||
return E.Cause(err, "connect to server")
|
||||
}
|
||||
var group task.Group
|
||||
group.Append("upload", func(ctx context.Context) error {
|
||||
return common.Error(bufio.Copy(conn, os.Stdin))
|
||||
})
|
||||
group.Append("download", func(ctx context.Context) error {
|
||||
return common.Error(bufio.Copy(os.Stdout, conn))
|
||||
})
|
||||
group.Cleanup(func() {
|
||||
conn.Close()
|
||||
})
|
||||
err = group.Run(context.Background())
|
||||
if E.IsClosed(err) {
|
||||
log.Info(err)
|
||||
} else {
|
||||
log.Error(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var commandFetch = &cobra.Command{
|
||||
Use: "fetch",
|
||||
Short: "Fetch an URL",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := fetch(args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
commandTools.AddCommand(commandFetch)
|
||||
}
|
||||
|
||||
var (
|
||||
httpClient *http.Client
|
||||
http3Client *http.Client
|
||||
)
|
||||
|
||||
func fetch(args []string) error {
|
||||
instance, err := createPreStartedClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer instance.Close()
|
||||
httpClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
dialer, err := createDialer(instance, commandToolsFlagOutbound)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
|
||||
},
|
||||
ForceAttemptHTTP2: true,
|
||||
},
|
||||
}
|
||||
defer httpClient.CloseIdleConnections()
|
||||
if C.WithQUIC {
|
||||
err = initializeHTTP3Client(instance)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer http3Client.CloseIdleConnections()
|
||||
}
|
||||
for _, urlString := range args {
|
||||
var parsedURL *url.URL
|
||||
parsedURL, err = url.Parse(urlString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch parsedURL.Scheme {
|
||||
case "":
|
||||
parsedURL.Scheme = "http"
|
||||
fallthrough
|
||||
case "http", "https":
|
||||
err = fetchHTTP(httpClient, parsedURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "http3":
|
||||
if !C.WithQUIC {
|
||||
return C.ErrQUICNotIncluded
|
||||
}
|
||||
parsedURL.Scheme = "https"
|
||||
err = fetchHTTP(http3Client, parsedURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return E.New("unsupported scheme: ", parsedURL.Scheme)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func fetchHTTP(httpClient *http.Client, parsedURL *url.URL) error {
|
||||
request, err := http.NewRequest("GET", parsedURL.String(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request.Header.Add("User-Agent", "curl/7.88.0")
|
||||
response, err := httpClient.Do(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
_, err = bufio.Copy(os.Stdout, response.Body)
|
||||
if errors.Is(err, io.EOF) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
//go:build with_quic
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
|
||||
"github.com/sagernet/quic-go"
|
||||
"github.com/sagernet/quic-go/http3"
|
||||
box "github.com/sagernet/sing-box"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func initializeHTTP3Client(instance *box.Box) error {
|
||||
dialer, err := createDialer(instance, commandToolsFlagOutbound)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
http3Client = &http.Client{
|
||||
Transport: &http3.Transport{
|
||||
Dial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
||||
destination := M.ParseSocksaddr(addr)
|
||||
udpConn, dErr := dialer.DialContext(ctx, N.NetworkUDP, destination)
|
||||
if dErr != nil {
|
||||
return nil, dErr
|
||||
}
|
||||
return quic.DialEarly(ctx, bufio.NewUnbindPacketConn(udpConn), udpConn.RemoteAddr(), tlsCfg, cfg)
|
||||
},
|
||||
},
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
//go:build !with_quic
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
box "github.com/sagernet/sing-box"
|
||||
)
|
||||
|
||||
func initializeHTTP3Client(instance *box.Box) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func fetchHTTP3(parsedURL *url.URL) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
108
cmd/sing-box/cmd_tools_install_ca.go
Normal file
108
cmd/sing-box/cmd_tools_install_ca.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/shell"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var commandInstallCACertificate = &cobra.Command{
|
||||
Use: "install-ca <path to certificate>",
|
||||
Short: "Install CA certificate to system",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := installCACertificate(args[0])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
commandTools.AddCommand(commandInstallCACertificate)
|
||||
}
|
||||
|
||||
func installCACertificate(path string) error {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
return shell.Exec("powershell", "-Command", "Import-Certificate -FilePath \""+path+"\" -CertStoreLocation Cert:\\LocalMachine\\Root").Attach().Run()
|
||||
case "darwin":
|
||||
return shell.Exec("sudo", "security", "add-trusted-cert", "-d", "-r", "trustRoot", "-k", "/Library/Keychains/System.keychain", path).Attach().Run()
|
||||
case "linux":
|
||||
updateCertPath, updateCertPathNotFoundErr := exec.LookPath("update-ca-certificates")
|
||||
if updateCertPathNotFoundErr == nil {
|
||||
publicDer, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll("/usr/local/share/ca-certificates", 0o755)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrPermission) {
|
||||
log.Info("Try running with sudo")
|
||||
return shell.Exec("sudo", os.Args...).Attach().Run()
|
||||
}
|
||||
return err
|
||||
}
|
||||
fileName := filepath.Base(updateCertPath)
|
||||
if !strings.HasSuffix(fileName, ".crt") {
|
||||
fileName = fileName + ".crt"
|
||||
}
|
||||
filePath, _ := filepath.Abs(filepath.Join("/usr/local/share/ca-certificates", fileName))
|
||||
err = os.WriteFile(filePath, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: publicDer}), 0o644)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrPermission) {
|
||||
log.Info("Try running with sudo")
|
||||
return shell.Exec("sudo", os.Args...).Attach().Run()
|
||||
}
|
||||
return err
|
||||
}
|
||||
log.Info("certificate written to " + filePath + "\n")
|
||||
err = shell.Exec(updateCertPath).Attach().Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("certificate installed")
|
||||
return nil
|
||||
}
|
||||
updateTrustPath, updateTrustPathNotFoundErr := exec.LookPath("update-ca-trust")
|
||||
if updateTrustPathNotFoundErr == nil {
|
||||
publicDer, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileName := filepath.Base(updateTrustPath)
|
||||
fileExt := filepath.Ext(path)
|
||||
if fileExt != "" {
|
||||
fileName = fileName[:len(fileName)-len(fileExt)]
|
||||
}
|
||||
filePath, _ := filepath.Abs(filepath.Join("/etc/pki/ca-trust/source/anchors/", fileName+".pem"))
|
||||
err = os.WriteFile(filePath, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: publicDer}), 0o644)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrPermission) {
|
||||
log.Info("Try running with sudo")
|
||||
return shell.Exec("sudo", os.Args...).Attach().Run()
|
||||
}
|
||||
return err
|
||||
}
|
||||
log.Info("certificate written to " + filePath + "\n")
|
||||
err = shell.Exec(updateTrustPath, "extract").Attach().Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("certificate installed")
|
||||
}
|
||||
return E.New("update-ca-certificates or update-ca-trust not found")
|
||||
default:
|
||||
return E.New("unsupported operating system: ", runtime.GOOS)
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/ntp"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -39,20 +40,11 @@ func init() {
|
||||
}
|
||||
|
||||
func syncTime() error {
|
||||
instance, err := createPreStartedClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dialer, err := createDialer(instance, commandToolsFlagOutbound)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer instance.Close()
|
||||
serverAddress := M.ParseSocksaddr(commandSyncTimeFlagServer)
|
||||
if serverAddress.Port == 0 {
|
||||
serverAddress.Port = 123
|
||||
}
|
||||
response, err := ntp.Exchange(context.Background(), dialer, serverAddress)
|
||||
response, err := ntp.Exchange(context.Background(), N.SystemDialer, serverAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package certificate
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -16,6 +17,8 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
"github.com/sagernet/sing/service"
|
||||
|
||||
"software.sslmate.com/src/go-pkcs12"
|
||||
)
|
||||
|
||||
var _ adapter.CertificateStore = (*Store)(nil)
|
||||
@@ -27,6 +30,9 @@ type Store struct {
|
||||
certificatePaths []string
|
||||
certificateDirectoryPaths []string
|
||||
watcher *fswatch.Watcher
|
||||
tlsDecryptionEnabled bool
|
||||
tlsDecryptionPrivateKey any
|
||||
tlsDecryptionCertificate *x509.Certificate
|
||||
}
|
||||
|
||||
func NewStore(ctx context.Context, logger logger.Logger, options option.CertificateOptions) (*Store, error) {
|
||||
@@ -90,6 +96,19 @@ func NewStore(ctx context.Context, logger logger.Logger, options option.Certific
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initializing certificate store")
|
||||
}
|
||||
if options.TLSDecryption != nil && options.TLSDecryption.Enabled {
|
||||
pfxBytes, err := base64.StdEncoding.DecodeString(options.TLSDecryption.KeyPair)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "decode key pair base64 bytes")
|
||||
}
|
||||
privateKey, certificate, err := pkcs12.Decode(pfxBytes, options.TLSDecryption.KeyPairPassword)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "decode key pair")
|
||||
}
|
||||
store.tlsDecryptionEnabled = true
|
||||
store.tlsDecryptionPrivateKey = privateKey
|
||||
store.tlsDecryptionCertificate = certificate
|
||||
}
|
||||
return store, nil
|
||||
}
|
||||
|
||||
@@ -183,3 +202,15 @@ func isSameDirSymlink(f fs.DirEntry, dir string) bool {
|
||||
target, err := os.Readlink(filepath.Join(dir, f.Name()))
|
||||
return err == nil && !strings.Contains(target, "/")
|
||||
}
|
||||
|
||||
func (s *Store) TLSDecryptionEnabled() bool {
|
||||
return s.tlsDecryptionEnabled
|
||||
}
|
||||
|
||||
func (s *Store) TLSDecryptionCertificate() *x509.Certificate {
|
||||
return s.tlsDecryptionCertificate
|
||||
}
|
||||
|
||||
func (s *Store) TLSDecryptionPrivateKey() any {
|
||||
return s.tlsDecryptionPrivateKey
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/conntrack"
|
||||
"github.com/sagernet/sing-box/common/listener"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -35,6 +36,7 @@ type DefaultDialer struct {
|
||||
udpListener net.ListenConfig
|
||||
udpAddr4 string
|
||||
udpAddr6 string
|
||||
netns string
|
||||
networkManager adapter.NetworkManager
|
||||
networkStrategy *C.NetworkStrategy
|
||||
defaultNetworkStrategy bool
|
||||
@@ -198,6 +200,7 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
||||
udpListener: listener,
|
||||
udpAddr4: udpAddr4,
|
||||
udpAddr6: udpAddr6,
|
||||
netns: options.NetNs,
|
||||
networkManager: networkManager,
|
||||
networkStrategy: networkStrategy,
|
||||
defaultNetworkStrategy: defaultNetworkStrategy,
|
||||
@@ -210,21 +213,25 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
||||
func (d *DefaultDialer) DialContext(ctx context.Context, network string, address M.Socksaddr) (net.Conn, error) {
|
||||
if !address.IsValid() {
|
||||
return nil, E.New("invalid address")
|
||||
} else if address.IsFqdn() {
|
||||
return nil, E.New("domain not resolved")
|
||||
}
|
||||
if d.networkStrategy == nil {
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkUDP:
|
||||
if !address.IsIPv6() {
|
||||
return trackConn(d.udpDialer4.DialContext(ctx, network, address.String()))
|
||||
} else {
|
||||
return trackConn(d.udpDialer6.DialContext(ctx, network, address.String()))
|
||||
return trackConn(listener.ListenNetworkNamespace[net.Conn](d.netns, func() (net.Conn, error) {
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkUDP:
|
||||
if !address.IsIPv6() {
|
||||
return d.udpDialer4.DialContext(ctx, network, address.String())
|
||||
} else {
|
||||
return d.udpDialer6.DialContext(ctx, network, address.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
if !address.IsIPv6() {
|
||||
return trackConn(DialSlowContext(&d.dialer4, ctx, network, address))
|
||||
} else {
|
||||
return trackConn(DialSlowContext(&d.dialer6, ctx, network, address))
|
||||
}
|
||||
if !address.IsIPv6() {
|
||||
return DialSlowContext(&d.dialer4, ctx, network, address)
|
||||
} else {
|
||||
return DialSlowContext(&d.dialer6, ctx, network, address)
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
return d.DialParallelInterface(ctx, network, address, d.networkStrategy, d.networkType, d.fallbackNetworkType, d.networkFallbackDelay)
|
||||
}
|
||||
@@ -280,13 +287,15 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
|
||||
|
||||
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
if d.networkStrategy == nil {
|
||||
if destination.IsIPv6() {
|
||||
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6))
|
||||
} else if destination.IsIPv4() && !destination.Addr.IsUnspecified() {
|
||||
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP+"4", d.udpAddr4))
|
||||
} else {
|
||||
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4))
|
||||
}
|
||||
return trackPacketConn(listener.ListenNetworkNamespace[net.PacketConn](d.netns, func() (net.PacketConn, error) {
|
||||
if destination.IsIPv6() {
|
||||
return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6)
|
||||
} else if destination.IsIPv4() && !destination.Addr.IsUnspecified() {
|
||||
return d.udpListener.ListenPacket(ctx, N.NetworkUDP+"4", d.udpAddr4)
|
||||
} else {
|
||||
return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4)
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
return d.ListenSerialInterfacePacket(ctx, destination, d.networkStrategy, d.networkType, d.fallbackNetworkType, d.networkFallbackDelay)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ func (d *DefaultDialer) dialParallelInterface(ctx context.Context, dialer net.Di
|
||||
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
||||
return nil, false, E.New("no available network interface")
|
||||
}
|
||||
defaultInterface := d.networkManager.InterfaceMonitor().DefaultInterface()
|
||||
if fallbackDelay == 0 {
|
||||
fallbackDelay = N.DefaultFallbackDelay
|
||||
}
|
||||
@@ -31,7 +32,9 @@ func (d *DefaultDialer) dialParallelInterface(ctx context.Context, dialer net.Di
|
||||
results := make(chan dialResult) // unbuffered
|
||||
startRacer := func(ctx context.Context, primary bool, iif adapter.NetworkInterface) {
|
||||
perNetDialer := dialer
|
||||
perNetDialer.Control = control.Append(perNetDialer.Control, control.BindToInterface(nil, iif.Name, iif.Index))
|
||||
if defaultInterface == nil || iif.Index != defaultInterface.Index {
|
||||
perNetDialer.Control = control.Append(perNetDialer.Control, control.BindToInterface(nil, iif.Name, iif.Index))
|
||||
}
|
||||
conn, err := perNetDialer.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
select {
|
||||
@@ -89,6 +92,7 @@ func (d *DefaultDialer) dialParallelInterfaceFastFallback(ctx context.Context, d
|
||||
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
||||
return nil, false, E.New("no available network interface")
|
||||
}
|
||||
defaultInterface := d.networkManager.InterfaceMonitor().DefaultInterface()
|
||||
if fallbackDelay == 0 {
|
||||
fallbackDelay = N.DefaultFallbackDelay
|
||||
}
|
||||
@@ -103,7 +107,9 @@ func (d *DefaultDialer) dialParallelInterfaceFastFallback(ctx context.Context, d
|
||||
results := make(chan dialResult) // unbuffered
|
||||
startRacer := func(ctx context.Context, primary bool, iif adapter.NetworkInterface) {
|
||||
perNetDialer := dialer
|
||||
perNetDialer.Control = control.Append(perNetDialer.Control, control.BindToInterface(nil, iif.Name, iif.Index))
|
||||
if defaultInterface == nil || iif.Index != defaultInterface.Index {
|
||||
perNetDialer.Control = control.Append(perNetDialer.Control, control.BindToInterface(nil, iif.Name, iif.Index))
|
||||
}
|
||||
conn, err := perNetDialer.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
select {
|
||||
@@ -149,10 +155,13 @@ func (d *DefaultDialer) listenSerialInterfacePacket(ctx context.Context, listene
|
||||
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
||||
return nil, E.New("no available network interface")
|
||||
}
|
||||
defaultInterface := d.networkManager.InterfaceMonitor().DefaultInterface()
|
||||
var errors []error
|
||||
for _, primaryInterface := range primaryInterfaces {
|
||||
perNetListener := listener
|
||||
perNetListener.Control = control.Append(perNetListener.Control, control.BindToInterface(nil, primaryInterface.Name, primaryInterface.Index))
|
||||
if defaultInterface == nil || primaryInterface.Index != defaultInterface.Index {
|
||||
perNetListener.Control = control.Append(perNetListener.Control, control.BindToInterface(nil, primaryInterface.Name, primaryInterface.Index))
|
||||
}
|
||||
conn, err := perNetListener.ListenPacket(ctx, network, addr)
|
||||
if err == nil {
|
||||
return conn, nil
|
||||
@@ -161,7 +170,9 @@ func (d *DefaultDialer) listenSerialInterfacePacket(ctx context.Context, listene
|
||||
}
|
||||
for _, fallbackInterface := range fallbackInterfaces {
|
||||
perNetListener := listener
|
||||
perNetListener.Control = control.Append(perNetListener.Control, control.BindToInterface(nil, fallbackInterface.Name, fallbackInterface.Index))
|
||||
if defaultInterface == nil || fallbackInterface.Index != defaultInterface.Index {
|
||||
perNetListener.Control = control.Append(perNetListener.Control, control.BindToInterface(nil, fallbackInterface.Name, fallbackInterface.Index))
|
||||
}
|
||||
conn, err := perNetListener.ListenPacket(ctx, network, addr)
|
||||
if err == nil {
|
||||
return conn, nil
|
||||
|
||||
@@ -6,14 +6,20 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
type DirectDialer interface {
|
||||
IsEmpty() bool
|
||||
}
|
||||
|
||||
type DetourDialer struct {
|
||||
outboundManager adapter.OutboundManager
|
||||
detour string
|
||||
directResolver bool
|
||||
dialer N.Dialer
|
||||
initOnce sync.Once
|
||||
initErr error
|
||||
@@ -23,9 +29,12 @@ func NewDetour(outboundManager adapter.OutboundManager, detour string) N.Dialer
|
||||
return &DetourDialer{outboundManager: outboundManager, detour: detour}
|
||||
}
|
||||
|
||||
func (d *DetourDialer) Start() error {
|
||||
_, err := d.Dialer()
|
||||
return err
|
||||
func InitializeDetour(dialer N.Dialer) error {
|
||||
detourDialer, isDetour := common.Cast[*DetourDialer](dialer)
|
||||
if !isDetour {
|
||||
return nil
|
||||
}
|
||||
return common.Error(detourDialer.Dialer())
|
||||
}
|
||||
|
||||
func (d *DetourDialer) Dialer() (N.Dialer, error) {
|
||||
@@ -34,11 +43,18 @@ func (d *DetourDialer) Dialer() (N.Dialer, error) {
|
||||
}
|
||||
|
||||
func (d *DetourDialer) init() {
|
||||
var loaded bool
|
||||
d.dialer, loaded = d.outboundManager.Outbound(d.detour)
|
||||
dialer, loaded := d.outboundManager.Outbound(d.detour)
|
||||
if !loaded {
|
||||
d.initErr = E.New("outbound detour not found: ", d.detour)
|
||||
return
|
||||
}
|
||||
if directDialer, isDirect := dialer.(DirectDialer); isDirect {
|
||||
if directDialer.IsEmpty() {
|
||||
d.initErr = E.New("detour to an empty direct outbound makes no sense")
|
||||
return
|
||||
}
|
||||
}
|
||||
d.dialer = dialer
|
||||
}
|
||||
|
||||
func (d *DetourDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
|
||||
@@ -52,7 +52,7 @@ func NewWithOptions(options Options) (N.Dialer, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if options.RemoteIsDomain && (dialOptions.Detour == "" || options.ResolverOnDetour) {
|
||||
if options.RemoteIsDomain && (dialOptions.Detour == "" || options.ResolverOnDetour || dialOptions.DomainResolver != nil && dialOptions.DomainResolver.Server != "") {
|
||||
networkManager := service.FromContext[adapter.NetworkManager](options.Context)
|
||||
dnsTransport := service.FromContext[adapter.DNSTransportManager](options.Context)
|
||||
var defaultOptions adapter.NetworkOptions
|
||||
@@ -104,7 +104,12 @@ func NewWithOptions(options Options) (N.Dialer, error) {
|
||||
} else if options.NewDialer {
|
||||
return nil, E.New("missing domain resolver for domain server address")
|
||||
} else {
|
||||
deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver)
|
||||
transports := dnsTransport.Transports()
|
||||
if len(transports) < 2 {
|
||||
dnsQueryOptions.Transport = dnsTransport.Default()
|
||||
} else {
|
||||
deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver)
|
||||
}
|
||||
}
|
||||
dialer = NewResolveDialer(
|
||||
options.Context,
|
||||
|
||||
@@ -44,6 +44,20 @@ type resolveDialer struct {
|
||||
}
|
||||
|
||||
func NewResolveDialer(ctx context.Context, dialer N.Dialer, parallel bool, server string, queryOptions adapter.DNSQueryOptions, fallbackDelay time.Duration) ResolveDialer {
|
||||
if parallelDialer, isParallel := dialer.(ParallelInterfaceDialer); isParallel {
|
||||
return &resolveParallelNetworkDialer{
|
||||
resolveDialer{
|
||||
transport: service.FromContext[adapter.DNSTransportManager](ctx),
|
||||
router: service.FromContext[adapter.DNSRouter](ctx),
|
||||
dialer: dialer,
|
||||
parallel: parallel,
|
||||
server: server,
|
||||
queryOptions: queryOptions,
|
||||
fallbackDelay: fallbackDelay,
|
||||
},
|
||||
parallelDialer,
|
||||
}
|
||||
}
|
||||
return &resolveDialer{
|
||||
transport: service.FromContext[adapter.DNSTransportManager](ctx),
|
||||
router: service.FromContext[adapter.DNSRouter](ctx),
|
||||
@@ -60,21 +74,6 @@ type resolveParallelNetworkDialer struct {
|
||||
dialer ParallelInterfaceDialer
|
||||
}
|
||||
|
||||
func NewResolveParallelInterfaceDialer(ctx context.Context, dialer ParallelInterfaceDialer, parallel bool, server string, queryOptions adapter.DNSQueryOptions, fallbackDelay time.Duration) ParallelInterfaceResolveDialer {
|
||||
return &resolveParallelNetworkDialer{
|
||||
resolveDialer{
|
||||
transport: service.FromContext[adapter.DNSTransportManager](ctx),
|
||||
router: service.FromContext[adapter.DNSRouter](ctx),
|
||||
dialer: dialer,
|
||||
parallel: parallel,
|
||||
server: server,
|
||||
queryOptions: queryOptions,
|
||||
fallbackDelay: fallbackDelay,
|
||||
},
|
||||
dialer,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *resolveDialer) initialize() error {
|
||||
d.initOnce.Do(d.initServer)
|
||||
return d.initErr
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -14,6 +16,8 @@ import (
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/vishvananda/netns"
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
@@ -135,3 +139,30 @@ func (l *Listener) UDPConn() *net.UDPConn {
|
||||
func (l *Listener) ListenOptions() option.ListenOptions {
|
||||
return l.listenOptions
|
||||
}
|
||||
|
||||
func ListenNetworkNamespace[T any](nameOrPath string, block func() (T, error)) (T, error) {
|
||||
if nameOrPath != "" {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
currentNs, err := netns.Get()
|
||||
if err != nil {
|
||||
return common.DefaultValue[T](), E.Cause(err, "get current netns")
|
||||
}
|
||||
defer netns.Set(currentNs)
|
||||
var targetNs netns.NsHandle
|
||||
if strings.HasPrefix(nameOrPath, "/") {
|
||||
targetNs, err = netns.GetFromPath(nameOrPath)
|
||||
} else {
|
||||
targetNs, err = netns.GetFromName(nameOrPath)
|
||||
}
|
||||
if err != nil {
|
||||
return common.DefaultValue[T](), E.Cause(err, "get netns ", nameOrPath)
|
||||
}
|
||||
defer targetNs.Close()
|
||||
err = netns.Set(targetNs)
|
||||
if err != nil {
|
||||
return common.DefaultValue[T](), E.Cause(err, "set netns to ", nameOrPath)
|
||||
}
|
||||
}
|
||||
return block()
|
||||
}
|
||||
|
||||
@@ -16,9 +16,12 @@ import (
|
||||
)
|
||||
|
||||
func (l *Listener) ListenTCP() (net.Listener, error) {
|
||||
//nolint:staticcheck
|
||||
if l.listenOptions.ProxyProtocol || l.listenOptions.ProxyProtocolAcceptNoHeader {
|
||||
return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
|
||||
}
|
||||
var err error
|
||||
bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(netip.AddrFrom4([4]byte{127, 0, 0, 1})), l.listenOptions.ListenPort)
|
||||
var tcpListener net.Listener
|
||||
var listenConfig net.ListenConfig
|
||||
if l.listenOptions.TCPKeepAlive >= 0 {
|
||||
keepIdle := time.Duration(l.listenOptions.TCPKeepAlive)
|
||||
@@ -37,20 +40,19 @@ func (l *Listener) ListenTCP() (net.Listener, error) {
|
||||
}
|
||||
setMultiPathTCP(&listenConfig)
|
||||
}
|
||||
if l.listenOptions.TCPFastOpen {
|
||||
var tfoConfig tfo.ListenConfig
|
||||
tfoConfig.ListenConfig = listenConfig
|
||||
tcpListener, err = tfoConfig.Listen(l.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
|
||||
} else {
|
||||
tcpListener, err = listenConfig.Listen(l.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
|
||||
}
|
||||
if err == nil {
|
||||
l.logger.Info("tcp server started at ", tcpListener.Addr())
|
||||
}
|
||||
//nolint:staticcheck
|
||||
if l.listenOptions.ProxyProtocol || l.listenOptions.ProxyProtocolAcceptNoHeader {
|
||||
return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
|
||||
tcpListener, err := ListenNetworkNamespace[net.Listener](l.listenOptions.NetNs, func() (net.Listener, error) {
|
||||
if l.listenOptions.TCPFastOpen {
|
||||
var tfoConfig tfo.ListenConfig
|
||||
tfoConfig.ListenConfig = listenConfig
|
||||
return tfoConfig.Listen(l.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
|
||||
} else {
|
||||
return listenConfig.Listen(l.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.logger.Info("tcp server started at ", tcpListener.Addr())
|
||||
l.tcpListener = tcpListener
|
||||
return tcpListener, err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package listener
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
@@ -24,7 +25,9 @@ func (l *Listener) ListenUDP() (net.PacketConn, error) {
|
||||
if !udpFragment {
|
||||
lc.Control = control.Append(lc.Control, control.DisableUDPFragment())
|
||||
}
|
||||
udpConn, err := lc.ListenPacket(l.ctx, M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.String())
|
||||
udpConn, err := ListenNetworkNamespace[net.PacketConn](l.listenOptions.NetNs, func() (net.PacketConn, error) {
|
||||
return lc.ListenPacket(l.ctx, M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.String())
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -34,6 +37,13 @@ func (l *Listener) ListenUDP() (net.PacketConn, error) {
|
||||
return udpConn, err
|
||||
}
|
||||
|
||||
func (l *Listener) ListenPacket(ctx context.Context, network string, address string) (net.PacketConn, error) {
|
||||
return ListenNetworkNamespace[net.PacketConn](l.listenOptions.NetNs, func() (net.PacketConn, error) {
|
||||
var listenConfig net.ListenConfig
|
||||
return listenConfig.ListenPacket(ctx, network, address)
|
||||
})
|
||||
}
|
||||
|
||||
func (l *Listener) UDPAddr() M.Socksaddr {
|
||||
return l.udpAddr
|
||||
}
|
||||
|
||||
@@ -18,5 +18,6 @@ func HTTPHost(_ context.Context, metadata *adapter.InboundContext, reader io.Rea
|
||||
}
|
||||
metadata.Protocol = C.ProtocolHTTP
|
||||
metadata.Domain = M.ParseSocksaddr(request.Host).AddrString()
|
||||
metadata.HTTPRequest = request
|
||||
return nil
|
||||
}
|
||||
|
||||
58
common/sniff/ntp.go
Normal file
58
common/sniff/ntp.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package sniff
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
)
|
||||
|
||||
func NTP(ctx context.Context, metadata *adapter.InboundContext, packet []byte) error {
|
||||
// NTP packets must be at least 48 bytes long (standard NTP header size).
|
||||
pLen := len(packet)
|
||||
if pLen < 48 {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
// Check the LI (Leap Indicator) and Version Number (VN) in the first byte.
|
||||
// We'll primarily focus on ensuring the version is valid for NTP.
|
||||
// Many NTP versions are used, but let's check for generally accepted ones (3 & 4 for IPv4, plus potential extensions/customizations)
|
||||
firstByte := packet[0]
|
||||
li := (firstByte >> 6) & 0x03 // Extract LI
|
||||
vn := (firstByte >> 3) & 0x07 // Extract VN
|
||||
mode := firstByte & 0x07 // Extract Mode
|
||||
|
||||
// Leap Indicator should be a valid value (0-3).
|
||||
if li > 3 {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
// Version Check (common NTP versions are 3 and 4)
|
||||
if vn != 3 && vn != 4 {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
// Check the Mode field for a client request (Mode 3). This validates it *is* a request.
|
||||
if mode != 3 {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
// Check Root Delay and Root Dispersion. While not strictly *required* for a request,
|
||||
// we can check if they appear to be reasonable values (not excessively large).
|
||||
rootDelay := binary.BigEndian.Uint32(packet[4:8])
|
||||
rootDispersion := binary.BigEndian.Uint32(packet[8:12])
|
||||
|
||||
// Check for unreasonably large root delay and dispersion. NTP RFC specifies max values of approximately 16 seconds.
|
||||
// Convert to milliseconds for easy comparison. Each unit is 1/2^16 seconds.
|
||||
if float64(rootDelay)/65536.0 > 16.0 {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
if float64(rootDispersion)/65536.0 > 16.0 {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
metadata.Protocol = C.ProtocolNTP
|
||||
|
||||
return nil
|
||||
}
|
||||
33
common/sniff/ntp_test.go
Normal file
33
common/sniff/ntp_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package sniff_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/sniff"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSniffNTP(t *testing.T) {
|
||||
t.Parallel()
|
||||
packet, err := hex.DecodeString("1b0006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
|
||||
require.NoError(t, err)
|
||||
var metadata adapter.InboundContext
|
||||
err = sniff.NTP(context.Background(), &metadata, packet)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, metadata.Protocol, C.ProtocolNTP)
|
||||
}
|
||||
|
||||
func TestSniffNTPFailed(t *testing.T) {
|
||||
t.Parallel()
|
||||
packet, err := hex.DecodeString("400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
|
||||
require.NoError(t, err)
|
||||
var metadata adapter.InboundContext
|
||||
err = sniff.NTP(context.Background(), &metadata, packet)
|
||||
require.ErrorIs(t, err, os.ErrInvalid)
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
@@ -34,7 +35,7 @@ func Skip(metadata *adapter.InboundContext) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func PeekStream(ctx context.Context, metadata *adapter.InboundContext, conn net.Conn, buffer *buf.Buffer, timeout time.Duration, sniffers ...StreamSniffer) error {
|
||||
func PeekStream(ctx context.Context, metadata *adapter.InboundContext, conn net.Conn, buffers []*buf.Buffer, buffer *buf.Buffer, timeout time.Duration, sniffers ...StreamSniffer) error {
|
||||
if timeout == 0 {
|
||||
timeout = C.ReadPayloadTimeout
|
||||
}
|
||||
@@ -55,7 +56,10 @@ func PeekStream(ctx context.Context, metadata *adapter.InboundContext, conn net.
|
||||
}
|
||||
errors = nil
|
||||
for _, sniffer := range sniffers {
|
||||
err = sniffer(ctx, metadata, bytes.NewReader(buffer.Bytes()))
|
||||
reader := io.MultiReader(common.Map(append(buffers, buffer), func(it *buf.Buffer) io.Reader {
|
||||
return bytes.NewReader(it.Bytes())
|
||||
})...)
|
||||
err = sniffer(ctx, metadata, reader)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ func TLSClientHello(ctx context.Context, metadata *adapter.InboundContext, reade
|
||||
if clientHello != nil {
|
||||
metadata.Protocol = C.ProtocolTLS
|
||||
metadata.Domain = clientHello.ServerName
|
||||
metadata.ClientHello = clientHello
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
|
||||
@@ -8,7 +8,10 @@ import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
)
|
||||
|
||||
func GenerateKeyPair(parent *x509.Certificate, parentKey any, timeFunc func() time.Time, serverName string) (*tls.Certificate, error) {
|
||||
@@ -35,17 +38,30 @@ func GenerateCertificate(parent *x509.Certificate, parentKey any, timeFunc func(
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
NotBefore: timeFunc().Add(time.Hour * -1),
|
||||
NotAfter: expire,
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
Subject: pkix.Name{
|
||||
CommonName: serverName,
|
||||
},
|
||||
DNSNames: []string{serverName},
|
||||
var template *x509.Certificate
|
||||
if serverAddress := M.ParseAddr(serverName); serverAddress.IsValid() {
|
||||
template = &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
IPAddresses: []net.IP{serverAddress.AsSlice()},
|
||||
NotBefore: timeFunc().Add(time.Hour * -1),
|
||||
NotAfter: expire,
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
} else {
|
||||
template = &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
NotBefore: timeFunc().Add(time.Hour * -1),
|
||||
NotAfter: expire,
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
Subject: pkix.Name{
|
||||
CommonName: serverName,
|
||||
},
|
||||
DNSNames: []string{serverName},
|
||||
}
|
||||
}
|
||||
if parent == nil {
|
||||
parent = template
|
||||
|
||||
@@ -15,19 +15,19 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
DNSTypeLegacy = "legacy"
|
||||
DNSTypeUDP = "udp"
|
||||
DNSTypeTCP = "tcp"
|
||||
DNSTypeTLS = "tls"
|
||||
DNSTypeHTTPS = "https"
|
||||
DNSTypeQUIC = "quic"
|
||||
DNSTypeHTTP3 = "h3"
|
||||
DNSTypeHosts = "hosts"
|
||||
DNSTypeLocal = "local"
|
||||
DNSTypePreDefined = "predefined"
|
||||
DNSTypeFakeIP = "fakeip"
|
||||
DNSTypeDHCP = "dhcp"
|
||||
DNSTypeTailscale = "tailscale"
|
||||
DNSTypeLegacy = "legacy"
|
||||
DNSTypeLegacyRcode = "legacy_rcode"
|
||||
DNSTypeUDP = "udp"
|
||||
DNSTypeTCP = "tcp"
|
||||
DNSTypeTLS = "tls"
|
||||
DNSTypeHTTPS = "https"
|
||||
DNSTypeQUIC = "quic"
|
||||
DNSTypeHTTP3 = "h3"
|
||||
DNSTypeLocal = "local"
|
||||
DNSTypeHosts = "hosts"
|
||||
DNSTypeFakeIP = "fakeip"
|
||||
DNSTypeDHCP = "dhcp"
|
||||
DNSTypeTailscale = "tailscale"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -78,6 +78,8 @@ func ProxyDisplayName(proxyType string) string {
|
||||
return "TUIC"
|
||||
case TypeHysteria2:
|
||||
return "Hysteria2"
|
||||
case TypeAnyTLS:
|
||||
return "AnyTLS"
|
||||
case TypeSelector:
|
||||
return "Selector"
|
||||
case TypeURLTest:
|
||||
|
||||
@@ -33,6 +33,7 @@ const (
|
||||
RuleActionTypeHijackDNS = "hijack-dns"
|
||||
RuleActionTypeSniff = "sniff"
|
||||
RuleActionTypeResolve = "resolve"
|
||||
RuleActionTypePredefined = "predefined"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
7
constant/script.go
Normal file
7
constant/script.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
ScriptTypeSurge = "surge"
|
||||
ScriptSourceTypeLocal = "local"
|
||||
ScriptSourceTypeRemote = "remote"
|
||||
)
|
||||
@@ -537,7 +537,7 @@ func FixedResponse(id uint16, question dns.Question, addresses []netip.Addr, tim
|
||||
Question: []dns.Question{question},
|
||||
}
|
||||
for _, address := range addresses {
|
||||
if address.Is4() {
|
||||
if address.Is4() && question.Qtype == dns.TypeA {
|
||||
response.Answer = append(response.Answer, &dns.A{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: question.Name,
|
||||
@@ -547,7 +547,7 @@ func FixedResponse(id uint16, question dns.Question, addresses []netip.Addr, tim
|
||||
},
|
||||
A: address.AsSlice(),
|
||||
})
|
||||
} else {
|
||||
} else if address.Is6() && question.Qtype == dns.TypeAAAA {
|
||||
response.Answer = append(response.Answer, &dns.AAAA{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: question.Name,
|
||||
|
||||
@@ -190,6 +190,8 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int,
|
||||
}
|
||||
case *R.RuleActionReject:
|
||||
return nil, currentRule, currentRuleIndex
|
||||
case *R.RuleActionPredefined:
|
||||
return nil, currentRule, currentRuleIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,6 +262,8 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg, options adapte
|
||||
case C.RuleActionRejectMethodDrop:
|
||||
return nil, tun.ErrDrop
|
||||
}
|
||||
case *R.RuleActionPredefined:
|
||||
return action.Response(message), nil
|
||||
}
|
||||
}
|
||||
var responseCheck func(responseAddrs []netip.Addr) bool
|
||||
@@ -366,7 +370,8 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ
|
||||
ruleIndex = -1
|
||||
for {
|
||||
dnsCtx := adapter.OverrideContext(ctx)
|
||||
transport, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex, true, &options)
|
||||
dnsOptions := options
|
||||
transport, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex, true, &dnsOptions)
|
||||
if rule != nil {
|
||||
switch action := rule.Action().(type) {
|
||||
case *R.RuleActionReject:
|
||||
@@ -376,6 +381,20 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ
|
||||
case C.RuleActionRejectMethodDrop:
|
||||
return nil, tun.ErrDrop
|
||||
}
|
||||
case *R.RuleActionPredefined:
|
||||
if action.Rcode != mDNS.RcodeSuccess {
|
||||
err = RcodeError(action.Rcode)
|
||||
} else {
|
||||
for _, answer := range action.Answer {
|
||||
switch record := answer.(type) {
|
||||
case *mDNS.A:
|
||||
responseAddrs = append(responseAddrs, M.AddrFromIP(record.A))
|
||||
case *mDNS.AAAA:
|
||||
responseAddrs = append(responseAddrs, M.AddrFromIP(record.AAAA))
|
||||
}
|
||||
}
|
||||
}
|
||||
goto response
|
||||
}
|
||||
}
|
||||
var responseCheck func(responseAddrs []netip.Addr) bool
|
||||
@@ -385,16 +404,17 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ
|
||||
return rule.MatchAddressLimit(metadata)
|
||||
}
|
||||
}
|
||||
if options.Strategy == C.DomainStrategyAsIS {
|
||||
options.Strategy = r.defaultDomainStrategy
|
||||
if dnsOptions.Strategy == C.DomainStrategyAsIS {
|
||||
dnsOptions.Strategy = r.defaultDomainStrategy
|
||||
}
|
||||
responseAddrs, err = r.client.Lookup(dnsCtx, transport, domain, options, responseCheck)
|
||||
responseAddrs, err = r.client.Lookup(dnsCtx, transport, domain, dnsOptions, responseCheck)
|
||||
if responseCheck == nil || err == nil {
|
||||
break
|
||||
}
|
||||
printResult()
|
||||
}
|
||||
}
|
||||
response:
|
||||
printResult()
|
||||
if len(responseAddrs) > 0 {
|
||||
r.logger.InfoContext(ctx, "lookup succeed for ", domain, ": ", strings.Join(F.MapToString(responseAddrs), " "))
|
||||
@@ -429,6 +449,6 @@ func (r *Router) LookupReverseMapping(ip netip.Addr) (string, bool) {
|
||||
func (r *Router) ResetNetwork() {
|
||||
r.ClearCache()
|
||||
for _, transport := range r.transport.Transports() {
|
||||
transport.Reset()
|
||||
transport.Close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ func (t *Transport) Start(stage adapter.StartStage) error {
|
||||
|
||||
func (t *Transport) Close() error {
|
||||
for _, transport := range t.transports {
|
||||
transport.Reset()
|
||||
transport.Close()
|
||||
}
|
||||
if t.interfaceCallback != nil {
|
||||
t.networkManager.InterfaceMonitor().UnregisterCallback(t.interfaceCallback)
|
||||
@@ -89,12 +89,6 @@ func (t *Transport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Reset() {
|
||||
for _, transport := range t.transports {
|
||||
transport.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
err := t.fetchServers()
|
||||
if err != nil {
|
||||
@@ -252,7 +246,7 @@ func (t *Transport) recreateServers(iface *control.Interface, serverAddrs []M.So
|
||||
transports = append(transports, transport.NewUDPRaw(t.logger, t.TransportAdapter, serverDialer, serverAddr))
|
||||
}
|
||||
for _, transport := range t.transports {
|
||||
transport.Reset()
|
||||
transport.Close()
|
||||
}
|
||||
t.transports = transports
|
||||
return nil
|
||||
|
||||
@@ -2,12 +2,15 @@ package hosts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/service/filemanager"
|
||||
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
@@ -20,31 +23,49 @@ var _ adapter.DNSTransport = (*Transport)(nil)
|
||||
|
||||
type Transport struct {
|
||||
dns.TransportAdapter
|
||||
files []*File
|
||||
files []*File
|
||||
predefined map[string][]netip.Addr
|
||||
}
|
||||
|
||||
func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, options option.HostsDNSServerOptions) (adapter.DNSTransport, error) {
|
||||
var files []*File
|
||||
var (
|
||||
files []*File
|
||||
predefined = make(map[string][]netip.Addr)
|
||||
)
|
||||
if len(options.Path) == 0 {
|
||||
files = append(files, NewFile(DefaultPath))
|
||||
} else {
|
||||
for _, path := range options.Path {
|
||||
files = append(files, NewFile(path))
|
||||
files = append(files, NewFile(filemanager.BasePath(ctx, os.ExpandEnv(path))))
|
||||
}
|
||||
}
|
||||
if options.Predefined != nil {
|
||||
for _, entry := range options.Predefined.Entries() {
|
||||
predefined[mDNS.CanonicalName(entry.Key)] = entry.Value
|
||||
}
|
||||
}
|
||||
return &Transport{
|
||||
TransportAdapter: dns.NewTransportAdapter(C.DNSTypeHosts, tag, nil),
|
||||
files: files,
|
||||
predefined: predefined,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *Transport) Reset() {
|
||||
func (t *Transport) Start(stage adapter.StartStage) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
question := message.Question[0]
|
||||
domain := dns.FqdnToDomain(question.Name)
|
||||
domain := mDNS.CanonicalName(question.Name)
|
||||
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
||||
if addresses, ok := t.predefined[domain]; ok {
|
||||
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil
|
||||
}
|
||||
for _, file := range t.files {
|
||||
addresses := file.Lookup(domain)
|
||||
if len(addresses) > 0 {
|
||||
|
||||
@@ -34,7 +34,7 @@ func (f *File) Lookup(name string) []netip.Addr {
|
||||
f.access.Lock()
|
||||
defer f.access.Unlock()
|
||||
f.update()
|
||||
return f.byName[name]
|
||||
return f.byName[dns.CanonicalName(name)]
|
||||
}
|
||||
|
||||
func (f *File) update() {
|
||||
|
||||
@@ -11,6 +11,6 @@ import (
|
||||
|
||||
func TestHosts(t *testing.T) {
|
||||
t.Parallel()
|
||||
require.Equal(t, []netip.Addr{netip.AddrFrom4([4]byte{127, 0, 0, 1}), netip.IPv6Loopback()}, hosts.NewFile("testdata/hosts").Lookup("localhost."))
|
||||
require.NotEmpty(t, hosts.NewFile(hosts.DefaultPath).Lookup("localhost."))
|
||||
require.Equal(t, []netip.Addr{netip.AddrFrom4([4]byte{127, 0, 0, 1}), netip.IPv6Loopback()}, hosts.NewFile("testdata/hosts").Lookup("localhost"))
|
||||
require.NotEmpty(t, hosts.NewFile(hosts.DefaultPath).Lookup("localhost"))
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
@@ -91,7 +92,7 @@ func NewHTTPS(ctx context.Context, logger log.ContextLogger, tag string, options
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serverAddr := options.ServerOptions.Build()
|
||||
serverAddr := options.DNSServerAddressOptions.Build()
|
||||
if serverAddr.Port == 0 {
|
||||
serverAddr.Port = 443
|
||||
}
|
||||
@@ -149,9 +150,17 @@ func NewHTTPSRaw(
|
||||
}
|
||||
}
|
||||
|
||||
func (t *HTTPSTransport) Reset() {
|
||||
func (t *HTTPSTransport) Start(stage adapter.StartStage) error {
|
||||
if stage != adapter.StartStateStart {
|
||||
return nil
|
||||
}
|
||||
return dialer.InitializeDetour(t.dialer)
|
||||
}
|
||||
|
||||
func (t *HTTPSTransport) Close() error {
|
||||
t.transport.CloseIdleConnections()
|
||||
t.transport = t.transport.Clone()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *HTTPSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
|
||||
@@ -40,7 +40,12 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *Transport) Reset() {
|
||||
func (t *Transport) Start(stage adapter.StartStage) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
@@ -139,6 +144,9 @@ func (t *Transport) tryOneName(ctx context.Context, config *dnsConfig, fqdn stri
|
||||
}
|
||||
|
||||
func (t *Transport) exchangeOne(ctx context.Context, server M.Socksaddr, question mDNS.Question, timeout time.Duration, useTCP, ad bool) (*mDNS.Msg, error) {
|
||||
if server.Port == 0 {
|
||||
server.Port = 53
|
||||
}
|
||||
var networks []string
|
||||
if useTCP {
|
||||
networks = []string{N.NetworkTCP}
|
||||
|
||||
@@ -58,8 +58,12 @@ func (f *FallbackTransport) Start(stage adapter.StartStage) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FallbackTransport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FallbackTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
if f.fallback {
|
||||
if !f.fallback {
|
||||
return f.DNSTransport.Exchange(ctx, message)
|
||||
}
|
||||
question := message.Question[0]
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
var _ adapter.DNSTransport = (*PredefinedTransport)(nil)
|
||||
|
||||
func RegisterPredefined(registry *dns.TransportRegistry) {
|
||||
dns.RegisterTransport[option.PredefinedDNSServerOptions](registry, C.DNSTypePreDefined, NewPredefined)
|
||||
}
|
||||
|
||||
type PredefinedTransport struct {
|
||||
dns.TransportAdapter
|
||||
responses []*predefinedResponse
|
||||
}
|
||||
|
||||
type predefinedResponse struct {
|
||||
questions []mDNS.Question
|
||||
answer *mDNS.Msg
|
||||
}
|
||||
|
||||
func NewPredefined(ctx context.Context, logger log.ContextLogger, tag string, options option.PredefinedDNSServerOptions) (adapter.DNSTransport, error) {
|
||||
var responses []*predefinedResponse
|
||||
for _, response := range options.Responses {
|
||||
questions, msg, err := response.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
responses = append(responses, &predefinedResponse{
|
||||
questions: questions,
|
||||
answer: msg,
|
||||
})
|
||||
}
|
||||
if len(responses) == 0 {
|
||||
return nil, E.New("empty predefined responses")
|
||||
}
|
||||
return &PredefinedTransport{
|
||||
TransportAdapter: dns.NewTransportAdapter(C.DNSTypePreDefined, tag, nil),
|
||||
responses: responses,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *PredefinedTransport) Reset() {
|
||||
}
|
||||
|
||||
func (t *PredefinedTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
for _, response := range t.responses {
|
||||
for _, question := range response.questions {
|
||||
if func() bool {
|
||||
if question.Name == "" && question.Qtype == mDNS.TypeNone {
|
||||
return true
|
||||
} else if question.Name == "" {
|
||||
return common.Any(message.Question, func(it mDNS.Question) bool {
|
||||
return it.Qtype == question.Qtype
|
||||
})
|
||||
} else if question.Qtype == mDNS.TypeNone {
|
||||
return common.Any(message.Question, func(it mDNS.Question) bool {
|
||||
return it.Name == question.Name
|
||||
})
|
||||
} else {
|
||||
return common.Contains(message.Question, question)
|
||||
}
|
||||
}() {
|
||||
copyAnswer := *response.answer
|
||||
copyAnswer.Id = message.Id
|
||||
copyAnswer.Question = message.Question
|
||||
return ©Answer, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, dns.RcodeNameError
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
sHTTP "github.com/sagernet/sing/protocol/http"
|
||||
|
||||
@@ -89,7 +88,7 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serverAddr := options.ServerOptions.Build()
|
||||
serverAddr := options.DNSServerAddressOptions.Build()
|
||||
if serverAddr.Port == 0 {
|
||||
serverAddr.Port = 443
|
||||
}
|
||||
@@ -101,8 +100,7 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options
|
||||
headers: headers,
|
||||
transport: &http3.Transport{
|
||||
Dial: func(ctx context.Context, addr string, tlsCfg *tls.STDConfig, cfg *quic.Config) (quic.EarlyConnection, error) {
|
||||
destinationAddr := M.ParseSocksaddr(addr)
|
||||
conn, dialErr := transportDialer.DialContext(ctx, N.NetworkUDP, destinationAddr)
|
||||
conn, dialErr := transportDialer.DialContext(ctx, N.NetworkUDP, serverAddr)
|
||||
if dialErr != nil {
|
||||
return nil, dialErr
|
||||
}
|
||||
@@ -113,8 +111,12 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *HTTP3Transport) Reset() {
|
||||
t.transport.Close()
|
||||
func (t *HTTP3Transport) Start(stage adapter.StartStage) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *HTTP3Transport) Close() error {
|
||||
return t.transport.Close()
|
||||
}
|
||||
|
||||
func (t *HTTP3Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
|
||||
@@ -54,7 +54,7 @@ func NewQUIC(ctx context.Context, logger log.ContextLogger, tag string, options
|
||||
if len(tlsConfig.NextProtos()) == 0 {
|
||||
tlsConfig.SetNextProtos([]string{"doq"})
|
||||
}
|
||||
serverAddr := options.ServerOptions.Build()
|
||||
serverAddr := options.DNSServerAddressOptions.Build()
|
||||
if serverAddr.Port == 0 {
|
||||
serverAddr.Port = 853
|
||||
}
|
||||
@@ -68,13 +68,18 @@ func NewQUIC(ctx context.Context, logger log.ContextLogger, tag string, options
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *Transport) Reset() {
|
||||
func (t *Transport) Start(stage adapter.StartStage) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Close() error {
|
||||
t.access.Lock()
|
||||
defer t.access.Unlock()
|
||||
connection := t.connection
|
||||
if connection != nil {
|
||||
connection.CloseWithError(0, "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
@@ -35,7 +36,7 @@ func NewTCP(ctx context.Context, logger log.ContextLogger, tag string, options o
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serverAddr := options.ServerOptions.Build()
|
||||
serverAddr := options.DNSServerAddressOptions.Build()
|
||||
if serverAddr.Port == 0 {
|
||||
serverAddr.Port = 53
|
||||
}
|
||||
@@ -46,7 +47,15 @@ func NewTCP(ctx context.Context, logger log.ContextLogger, tag string, options o
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *TCPTransport) Reset() {
|
||||
func (t *TCPTransport) Start(stage adapter.StartStage) error {
|
||||
if stage != adapter.StartStateStart {
|
||||
return nil
|
||||
}
|
||||
return dialer.InitializeDetour(t.dialer)
|
||||
}
|
||||
|
||||
func (t *TCPTransport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TCPTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
@@ -52,7 +53,7 @@ func NewTLS(ctx context.Context, logger log.ContextLogger, tag string, options o
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serverAddr := options.ServerOptions.Build()
|
||||
serverAddr := options.DNSServerAddressOptions.Build()
|
||||
if serverAddr.Port == 0 {
|
||||
serverAddr.Port = 853
|
||||
}
|
||||
@@ -65,13 +66,21 @@ func NewTLS(ctx context.Context, logger log.ContextLogger, tag string, options o
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *TLSTransport) Reset() {
|
||||
func (t *TLSTransport) Start(stage adapter.StartStage) error {
|
||||
if stage != adapter.StartStateStart {
|
||||
return nil
|
||||
}
|
||||
return dialer.InitializeDetour(t.dialer)
|
||||
}
|
||||
|
||||
func (t *TLSTransport) Close() error {
|
||||
t.access.Lock()
|
||||
defer t.access.Unlock()
|
||||
for connection := t.connections.Front(); connection != nil; connection = connection.Next() {
|
||||
connection.Value.Close()
|
||||
}
|
||||
t.connections.Init()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TLSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
@@ -42,7 +43,7 @@ func NewUDP(ctx context.Context, logger log.ContextLogger, tag string, options o
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serverAddr := options.ServerOptions.Build()
|
||||
serverAddr := options.DNSServerAddressOptions.Build()
|
||||
if serverAddr.Port == 0 {
|
||||
serverAddr.Port = 53
|
||||
}
|
||||
@@ -64,11 +65,19 @@ func NewUDPRaw(logger logger.ContextLogger, adapter dns.TransportAdapter, dialer
|
||||
}
|
||||
}
|
||||
|
||||
func (t *UDPTransport) Reset() {
|
||||
func (t *UDPTransport) Start(stage adapter.StartStage) error {
|
||||
if stage != adapter.StartStateStart {
|
||||
return nil
|
||||
}
|
||||
return dialer.InitializeDetour(t.dialer)
|
||||
}
|
||||
|
||||
func (t *UDPTransport) Close() error {
|
||||
t.access.Lock()
|
||||
defer t.access.Unlock()
|
||||
close(t.done)
|
||||
t.done = make(chan struct{})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *UDPTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
@@ -110,13 +119,6 @@ func (t *UDPTransport) exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M
|
||||
conn.access.Lock()
|
||||
delete(conn.callbacks, messageId)
|
||||
conn.access.Unlock()
|
||||
callback.access.Lock()
|
||||
select {
|
||||
case <-callback.done:
|
||||
default:
|
||||
close(callback.done)
|
||||
}
|
||||
callback.access.Unlock()
|
||||
}()
|
||||
rawMessage, err := exMessage.PackBuffer(buffer.FreeBytes())
|
||||
if err != nil {
|
||||
|
||||
@@ -225,7 +225,7 @@ func (m *TransportManager) Remove(tag string) error {
|
||||
}
|
||||
}
|
||||
if started {
|
||||
transport.Reset()
|
||||
transport.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,10 +2,54 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.12.0-alpha.12
|
||||
#### 1.12.0-beta.1
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.12.0-alpha.18
|
||||
|
||||
* Add wildcard SNI support for ShadowTLS inbound **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
See [ShadowTLS](/configuration/inbound/shadowtls/#wildcard_sni).
|
||||
|
||||
#### 1.12.0-alpha.17
|
||||
|
||||
* Add NTP sniffer **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
See [Protocol Sniff](/configuration/route/sniff/).
|
||||
|
||||
#### 1.12.0-alpha.16
|
||||
|
||||
* Update `domain_resolver` behavior **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
`route.default_domain_resolver` or `outbound.domain_resolver` is now optional when only one DNS server is configured.
|
||||
|
||||
See [Dial Fields](/configuration/shared/dial/#domain_resolver).
|
||||
|
||||
### 1.11.5
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
_We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we violated the rules (TestFlight users are not affected)._
|
||||
|
||||
#### 1.12.0-alpha.13
|
||||
|
||||
* Move `predefined` DNS server to DNS rule action **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
See [DNS Rule Action](/configuration/dns/rule_action/#predefined).
|
||||
|
||||
### 1.11.4
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@@ -7,6 +7,10 @@ icon: material/apple
|
||||
SFI/SFM/SFT allows users to manage and run local or remote sing-box configuration files, and provides
|
||||
platform-specific function implementation, such as TUN transparent proxy implementation.
|
||||
|
||||
!!! failure ""
|
||||
|
||||
We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we violated the rules (TestFlight users are not affected).
|
||||
|
||||
## :material-graph: Requirements
|
||||
|
||||
* iOS 15.0+ / macOS 13.0+ / Apple tvOS 17.0+
|
||||
|
||||
@@ -4,6 +4,7 @@ icon: material/alert-decagram
|
||||
|
||||
!!! quote "Changes in sing-box 1.12.0"
|
||||
|
||||
:material-plus: [ip_accept_any](#ip_accept_any)
|
||||
:material-delete-clock: [outbound](#outbound)
|
||||
|
||||
!!! quote "Changes in sing-box 1.11.0"
|
||||
@@ -77,15 +78,6 @@ icon: material/alert-decagram
|
||||
"domain_regex": [
|
||||
"^stun\\..+"
|
||||
],
|
||||
"geosite": [
|
||||
"cn"
|
||||
],
|
||||
"source_geoip": [
|
||||
"private"
|
||||
],
|
||||
"geoip": [
|
||||
"cn"
|
||||
],
|
||||
"source_ip_cidr": [
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
@@ -96,6 +88,7 @@ icon: material/alert-decagram
|
||||
"192.168.0.1"
|
||||
],
|
||||
"ip_is_private": false,
|
||||
"ip_accept_any": false,
|
||||
"source_port": [
|
||||
12345
|
||||
],
|
||||
@@ -147,8 +140,6 @@ icon: material/alert-decagram
|
||||
"geoip-cn",
|
||||
"geosite-cn"
|
||||
],
|
||||
// deprecated
|
||||
"rule_set_ipcidr_match_source": false,
|
||||
"rule_set_ip_cidr_match_source": false,
|
||||
"rule_set_ip_cidr_accept_empty": false,
|
||||
"invert": false,
|
||||
@@ -156,7 +147,20 @@ icon: material/alert-decagram
|
||||
"direct"
|
||||
],
|
||||
"action": "route",
|
||||
"server": "local"
|
||||
"server": "local",
|
||||
|
||||
// Deprecated
|
||||
|
||||
"rule_set_ipcidr_match_source": false,
|
||||
"geosite": [
|
||||
"cn"
|
||||
],
|
||||
"source_geoip": [
|
||||
"private"
|
||||
],
|
||||
"geoip": [
|
||||
"cn"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
@@ -451,7 +455,9 @@ Only takes effect for address requests (A/AAAA/HTTPS). When the query results do
|
||||
|
||||
#### geoip
|
||||
|
||||
!!! question "Since sing-box 1.9.0"
|
||||
!!! failure "Removed in sing-box 1.12.0"
|
||||
|
||||
GeoIP is deprecated in sing-box 1.8.0 and removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
|
||||
Match GeoIP with query response.
|
||||
|
||||
@@ -473,6 +479,12 @@ Match private IP with query response.
|
||||
|
||||
Make `ip_cidr` rules in rule-sets accept empty query response.
|
||||
|
||||
#### ip_accept_any
|
||||
|
||||
!!! question "Since sing-box 1.12.0"
|
||||
|
||||
Match any IP with query response.
|
||||
|
||||
### Logical Fields
|
||||
|
||||
#### type
|
||||
|
||||
@@ -4,6 +4,7 @@ icon: material/alert-decagram
|
||||
|
||||
!!! quote "sing-box 1.12.0 中的更改"
|
||||
|
||||
:material-plus: [ip_accept_any](#ip_accept_any)
|
||||
:material-delete-clock: [outbound](#outbound)
|
||||
|
||||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
@@ -77,15 +78,6 @@ icon: material/alert-decagram
|
||||
"domain_regex": [
|
||||
"^stun\\..+"
|
||||
],
|
||||
"geosite": [
|
||||
"cn"
|
||||
],
|
||||
"source_geoip": [
|
||||
"private"
|
||||
],
|
||||
"geoip": [
|
||||
"cn"
|
||||
],
|
||||
"source_ip_cidr": [
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
@@ -96,6 +88,7 @@ icon: material/alert-decagram
|
||||
"192.168.0.1"
|
||||
],
|
||||
"ip_is_private": false,
|
||||
"ip_accept_any": false,
|
||||
"source_port": [
|
||||
12345
|
||||
],
|
||||
@@ -147,8 +140,6 @@ icon: material/alert-decagram
|
||||
"geoip-cn",
|
||||
"geosite-cn"
|
||||
],
|
||||
// 已弃用
|
||||
"rule_set_ipcidr_match_source": false,
|
||||
"rule_set_ip_cidr_match_source": false,
|
||||
"rule_set_ip_cidr_accept_empty": false,
|
||||
"invert": false,
|
||||
@@ -156,7 +147,19 @@ icon: material/alert-decagram
|
||||
"direct"
|
||||
],
|
||||
"action": "route",
|
||||
"server": "local"
|
||||
"server": "local",
|
||||
|
||||
// 已弃用
|
||||
"rule_set_ipcidr_match_source": false,
|
||||
"geosite": [
|
||||
"cn"
|
||||
],
|
||||
"source_geoip": [
|
||||
"private"
|
||||
],
|
||||
"geoip": [
|
||||
"cn"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
@@ -232,17 +235,17 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
#### geosite
|
||||
|
||||
!!! failure "已在 sing-box 1.8.0 废弃"
|
||||
!!! failure "已在 sing-box 1.12.0 中被移除"
|
||||
|
||||
Geosite 已废弃且可能在不久的将来移除,参阅 [迁移指南](/zh/migration/#geosite)。
|
||||
GeoSite 已在 sing-box 1.8.0 废弃且在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/zh/migration/#geosite)。
|
||||
|
||||
匹配 Geosite。
|
||||
|
||||
#### source_geoip
|
||||
|
||||
!!! failure "已在 sing-box 1.8.0 废弃"
|
||||
!!! failure "已在 sing-box 1.12.0 中被移除"
|
||||
|
||||
GeoIP 已废弃且可能在不久的将来移除,参阅 [迁移指南](/zh/migration/#geoip)。
|
||||
GeoIP 已在 sing-box 1.8.0 废弃且在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/zh/migration/#geoip)。
|
||||
|
||||
匹配源 GeoIP。
|
||||
|
||||
@@ -451,7 +454,10 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
||||
|
||||
#### geoip
|
||||
|
||||
!!! question "自 sing-box 1.9.0 起"
|
||||
!!! failure "已在 sing-box 1.12.0 中被移除"
|
||||
|
||||
GeoIP 已在 sing-box 1.8.0 废弃且在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/zh/migration/#geoip)。
|
||||
|
||||
|
||||
与查询响应匹配 GeoIP。
|
||||
|
||||
@@ -467,6 +473,12 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
||||
|
||||
与查询响应匹配非公开 IP。
|
||||
|
||||
#### ip_accept_any
|
||||
|
||||
!!! question "自 sing-box 1.12.0 起"
|
||||
|
||||
匹配任意 IP。
|
||||
|
||||
#### rule_set_ip_cidr_accept_empty
|
||||
|
||||
!!! question "自 sing-box 1.10.0 起"
|
||||
|
||||
@@ -4,7 +4,8 @@ icon: material/new-box
|
||||
|
||||
!!! quote "Changes in sing-box 1.12.0"
|
||||
|
||||
:material-plus: [strategy](#strategy)
|
||||
:material-plus: [strategy](#strategy)
|
||||
:material-plus: [predefined](#predefined)
|
||||
|
||||
!!! question "Since sing-box 1.11.0"
|
||||
|
||||
@@ -31,6 +32,8 @@ Tag of target server.
|
||||
|
||||
#### strategy
|
||||
|
||||
!!! question "Since sing-box 1.12.0"
|
||||
|
||||
Set domain strategy for this query.
|
||||
|
||||
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
|
||||
@@ -69,7 +72,7 @@ Will overrides `dns.client_subnet`.
|
||||
```json
|
||||
{
|
||||
"action": "reject",
|
||||
"method": "default", // default
|
||||
"method": "",
|
||||
"no_drop": false
|
||||
}
|
||||
```
|
||||
@@ -81,8 +84,61 @@ Will overrides `dns.client_subnet`.
|
||||
- `default`: Reply with NXDOMAIN.
|
||||
- `drop`: Drop the request.
|
||||
|
||||
`default` will be used by default.
|
||||
|
||||
#### no_drop
|
||||
|
||||
If not enabled, `method` will be temporarily overwritten to `drop` after 50 triggers in 30s.
|
||||
|
||||
Not available when `method` is set to drop.
|
||||
|
||||
### predefined
|
||||
|
||||
!!! question "Since sing-box 1.12.0"
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "predefined",
|
||||
"rcode": "",
|
||||
"answer": [],
|
||||
"ns": [],
|
||||
"extra": []
|
||||
}
|
||||
```
|
||||
|
||||
`predefined` responds with predefined DNS records.
|
||||
|
||||
#### rcode
|
||||
|
||||
The response code.
|
||||
|
||||
| Value | Value in the legacy rcode server | Description |
|
||||
|------------|----------------------------------|-----------------|
|
||||
| `NOERROR` | `success` | Ok |
|
||||
| `FORMERR` | `format_error` | Bad request |
|
||||
| `SERVFAIL` | `server_failure` | Server failure |
|
||||
| `NXDOMAIN` | `name_error` | Not found |
|
||||
| `NOTIMP` | `not_implemented` | Not implemented |
|
||||
| `REFUSED` | `refused` | Refused |
|
||||
|
||||
`NOERROR` will be used by default.
|
||||
|
||||
#### answer
|
||||
|
||||
List of text DNS record to respond as answers.
|
||||
|
||||
Examples:
|
||||
|
||||
| Record Type | Example |
|
||||
|-------------|-------------------------------|
|
||||
| `A` | `localhost. IN A 127.0.0.1` |
|
||||
| `AAAA` | `localhost. IN AAAA ::1` |
|
||||
| `TXT` | `localhost. IN TXT \"Hello\"` |
|
||||
|
||||
#### ns
|
||||
|
||||
List of text DNS record to respond as name servers.
|
||||
|
||||
#### extra
|
||||
|
||||
List of text DNS record to respond as extra records.
|
||||
|
||||
@@ -4,7 +4,8 @@ icon: material/new-box
|
||||
|
||||
!!! quote "sing-box 1.12.0 中的更改"
|
||||
|
||||
:material-plus: [strategy](#strategy)
|
||||
:material-plus: [strategy](#strategy)
|
||||
:material-plus: [predefined](#predefined)
|
||||
|
||||
!!! question "自 sing-box 1.11.0 起"
|
||||
|
||||
@@ -12,9 +13,8 @@ icon: material/new-box
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route", // 默认
|
||||
"action": "route", // 默认
|
||||
"server": "",
|
||||
|
||||
"strategy": "",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": null,
|
||||
@@ -32,6 +32,8 @@ icon: material/new-box
|
||||
|
||||
#### strategy
|
||||
|
||||
!!! question "自 sing-box 1.12.0 起"
|
||||
|
||||
为此查询设置域名策略。
|
||||
|
||||
可选项:`prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`。
|
||||
@@ -70,7 +72,7 @@ icon: material/new-box
|
||||
```json
|
||||
{
|
||||
"action": "reject",
|
||||
"method": "default", // default
|
||||
"method": "",
|
||||
"no_drop": false
|
||||
}
|
||||
```
|
||||
@@ -82,8 +84,61 @@ icon: material/new-box
|
||||
- `default`: 返回 NXDOMAIN。
|
||||
- `drop`: 丢弃请求。
|
||||
|
||||
默认使用 `defualt`。
|
||||
|
||||
#### no_drop
|
||||
|
||||
如果未启用,则 30 秒内触发 50 次后,`method` 将被暂时覆盖为 `drop`。
|
||||
|
||||
当 `method` 设为 `drop` 时不可用。
|
||||
|
||||
### predefined
|
||||
|
||||
!!! question "自 sing-box 1.12.0 起"
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "predefined",
|
||||
"rcode": "",
|
||||
"answer": [],
|
||||
"ns": [],
|
||||
"extra": []
|
||||
}
|
||||
```
|
||||
|
||||
`predefined` 以预定义的 DNS 记录响应。
|
||||
|
||||
#### rcode
|
||||
|
||||
响应码。
|
||||
|
||||
| 值 | 旧 rcode DNS 服务器中的值 | 描述 |
|
||||
|------------|--------------------|-----------------|
|
||||
| `NOERROR` | `success` | Ok |
|
||||
| `FORMERR` | `format_error` | Bad request |
|
||||
| `SERVFAIL` | `server_failure` | Server failure |
|
||||
| `NXDOMAIN` | `name_error` | Not found |
|
||||
| `NOTIMP` | `not_implemented` | Not implemented |
|
||||
| `REFUSED` | `refused` | Refused |
|
||||
|
||||
默认使用 `NOERROR`。
|
||||
|
||||
#### answer
|
||||
|
||||
用于作为回答响应的文本 DNS 记录列表。
|
||||
|
||||
例子:
|
||||
|
||||
| 记录类型 | 例子 |
|
||||
|--------|-------------------------------|
|
||||
| `A` | `localhost. IN A 127.0.0.1` |
|
||||
| `AAAA` | `localhost. IN AAAA ::1` |
|
||||
| `TXT` | `localhost. IN TXT \"Hello\"` |
|
||||
|
||||
#### ns
|
||||
|
||||
用于作为名称服务器响应的文本 DNS 记录列表。
|
||||
|
||||
#### extra
|
||||
|
||||
用于作为额外记录响应的文本 DNS 记录列表。
|
||||
|
||||
96
docs/configuration/dns/server/hosts.md
Normal file
96
docs/configuration/dns/server/hosts.md
Normal file
@@ -0,0 +1,96 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! question "Since sing-box 1.12.0"
|
||||
|
||||
# Hosts
|
||||
|
||||
### Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "hosts",
|
||||
"tag": "",
|
||||
|
||||
"path": [],
|
||||
"predefined": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
!!! note ""
|
||||
|
||||
You can ignore the JSON Array [] tag when the content is only one item
|
||||
|
||||
### Fields
|
||||
|
||||
#### path
|
||||
|
||||
List of paths to hosts files.
|
||||
|
||||
`/etc/hosts` is used by default.
|
||||
|
||||
`C:\Windows\System32\Drivers\etc\hosts` is used by default on Windows.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
// "path": "/etc/hosts"
|
||||
|
||||
"path": [
|
||||
"/etc/hosts",
|
||||
"$HOME/.hosts"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### predefined
|
||||
|
||||
Predefined hosts.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"predefined": {
|
||||
"www.google.com": "127.0.0.1",
|
||||
"localhost": [
|
||||
"127.0.0.1",
|
||||
"::1"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
=== "Use hosts if available"
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
...
|
||||
},
|
||||
{
|
||||
"type": "hosts",
|
||||
"tag": "hosts"
|
||||
}
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"ip_accept_any": true,
|
||||
"server": "hosts"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,93 +0,0 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! question "Since sing-box 1.12.0"
|
||||
|
||||
# Predefined
|
||||
|
||||
### Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "predefined",
|
||||
"tag": "",
|
||||
"responses": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Fields
|
||||
|
||||
#### responses
|
||||
|
||||
==Required==
|
||||
|
||||
List of [Response](#response-structure).
|
||||
|
||||
### Response Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"query": [],
|
||||
"query_type": [],
|
||||
"rcode": "",
|
||||
"answer": [],
|
||||
"ns": [],
|
||||
"extra": []
|
||||
}
|
||||
```
|
||||
|
||||
!!! note ""
|
||||
|
||||
You can ignore the JSON Array [] tag when the content is only one item
|
||||
|
||||
### Response Fields
|
||||
|
||||
#### query
|
||||
|
||||
List of domain name to match.
|
||||
|
||||
#### query_type
|
||||
|
||||
List of query type to match.
|
||||
|
||||
#### rcode
|
||||
|
||||
The response code.
|
||||
|
||||
| Value | Value in the legacy rcode server | Description |
|
||||
|------------|----------------------------------|-----------------|
|
||||
| `NOERROR` | `success` | Ok |
|
||||
| `FORMERR` | `format_error` | Bad request |
|
||||
| `SERVFAIL` | `server_failure` | Server failure |
|
||||
| `NXDOMAIN` | `name_error` | Not found |
|
||||
| `NOTIMP` | `not_implemented` | Not implemented |
|
||||
| `REFUSED` | `refused` | Refused |
|
||||
|
||||
`NOERROR` will be used by default.
|
||||
|
||||
#### answer
|
||||
|
||||
List of text DNS record to respond as answers.
|
||||
|
||||
Examples:
|
||||
|
||||
| Record Type | Example |
|
||||
|-------------|-------------------------------|
|
||||
| `A` | `localhost. IN A 127.0.0.1` |
|
||||
| `AAAA` | `localhost. IN AAAA ::1` |
|
||||
| `TXT` | `localhost. IN TXT \"Hello\"` |
|
||||
|
||||
#### ns
|
||||
|
||||
List of text DNS record to respond as name servers.
|
||||
|
||||
#### extra
|
||||
|
||||
List of text DNS record to respond as extra records.
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.12.0"
|
||||
|
||||
:material-plus: [wildcard_sni](#wildcard_sni)
|
||||
|
||||
### Structure
|
||||
|
||||
```json
|
||||
@@ -29,7 +37,8 @@
|
||||
... // Dial Fields
|
||||
}
|
||||
},
|
||||
"strict_mode": false
|
||||
"strict_mode": false,
|
||||
"wildcard_sni": ""
|
||||
}
|
||||
```
|
||||
|
||||
@@ -55,7 +64,6 @@ ShadowTLS password.
|
||||
|
||||
Only available in the ShadowTLS protocol 2.
|
||||
|
||||
|
||||
#### users
|
||||
|
||||
ShadowTLS users.
|
||||
@@ -66,6 +74,8 @@ Only available in the ShadowTLS protocol 3.
|
||||
|
||||
==Required==
|
||||
|
||||
When `wildcard_sni` is configured to `all`, the server address is optional.
|
||||
|
||||
Handshake server address and [Dial Fields](/configuration/shared/dial/).
|
||||
|
||||
#### handshake_for_server_name
|
||||
@@ -79,3 +89,19 @@ Only available in the ShadowTLS protocol 2/3.
|
||||
ShadowTLS strict mode.
|
||||
|
||||
Only available in the ShadowTLS protocol 3.
|
||||
|
||||
#### wildcard_sni
|
||||
|
||||
!!! question "Since sing-box 1.12.0"
|
||||
|
||||
ShadowTLS wildcard SNI mode.
|
||||
|
||||
Available values are:
|
||||
|
||||
* `off`: (default) Disabled.
|
||||
* `authed`: Authenticated connections will have their destination overwritten to `(servername):443`
|
||||
* `all`: All connections will have their destination overwritten to `(servername):443`
|
||||
|
||||
Additionally, connections matching `handshake_for_server_name` are not affected.
|
||||
|
||||
Only available in the ShadowTLS protocol 3.
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.12.0 中的更改"
|
||||
|
||||
:material-plus: [wildcard_sni](#wildcard_sni)
|
||||
|
||||
### 结构
|
||||
|
||||
```json
|
||||
@@ -29,7 +37,8 @@
|
||||
... // 拨号字段
|
||||
}
|
||||
},
|
||||
"strict_mode": false
|
||||
"strict_mode": false,
|
||||
"wildcard_sni": ""
|
||||
}
|
||||
```
|
||||
|
||||
@@ -80,3 +89,19 @@ ShadowTLS 用户。
|
||||
ShadowTLS 严格模式。
|
||||
|
||||
仅在 ShadowTLS 协议版本 3 中可用。
|
||||
|
||||
#### wildcard_sni
|
||||
|
||||
!!! question "自 sing-box 1.12.0 起"
|
||||
|
||||
ShadowTLS 通配符 SNI 模式。
|
||||
|
||||
可用值:
|
||||
|
||||
* `off`:(默认)禁用。
|
||||
* `authed`:已认证的连接的目标将被重写为 `(servername):443`。
|
||||
* `all`:所有连接的目标将被重写为 `(servername):443`。
|
||||
|
||||
此外,匹配 `handshake_for_server_name` 的连接不受影响。
|
||||
|
||||
仅在 ShadowTLS 协议 3 中可用。
|
||||
|
||||
@@ -22,6 +22,7 @@ If enabled in the inbound, the protocol and domain name (if present) of by the c
|
||||
| UDP | `dtls` | / | / |
|
||||
| TCP | `ssh` | / | SSH Client Name |
|
||||
| TCP | `rdp` | / | / |
|
||||
| UDP | `ntp` | / | / |
|
||||
|
||||
| QUIC Client | Type |
|
||||
|:------------------------:|:----------:|
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
| UDP | `dtls` | / | / |
|
||||
| TCP | `ssh` | / | SSH 客户端名称 |
|
||||
| TCP | `rdp` | / | / |
|
||||
| UDP | `ntp` | / | / |
|
||||
|
||||
| QUIC 客户端 | 类型 |
|
||||
|:------------------------:|:----------:|
|
||||
|
||||
@@ -5,7 +5,8 @@ icon: material/new-box
|
||||
!!! quote "Changes in sing-box 1.12.0"
|
||||
|
||||
:material-plus: [domain_resolver](#domain_resolver)
|
||||
:material-delete-clock: [domain_strategy](#domain_strategy)
|
||||
:material-delete-clock: [domain_strategy](#domain_strategy)
|
||||
:material-plus: [netns](#netns)
|
||||
|
||||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
@@ -18,24 +19,25 @@ icon: material/new-box
|
||||
|
||||
```json
|
||||
{
|
||||
"detour": "upstream-out",
|
||||
"bind_interface": "en0",
|
||||
"inet4_bind_address": "0.0.0.0",
|
||||
"inet6_bind_address": "::",
|
||||
"routing_mark": 1234,
|
||||
"detour": "",
|
||||
"bind_interface": "",
|
||||
"inet4_bind_address": "",
|
||||
"inet6_bind_address": "",
|
||||
"routing_mark": 0,
|
||||
"reuse_addr": false,
|
||||
"connect_timeout": "5s",
|
||||
"connect_timeout": "",
|
||||
"tcp_fast_open": false,
|
||||
"tcp_multi_path": false,
|
||||
"udp_fragment": false,
|
||||
"netns": "",
|
||||
"domain_resolver": "", // or {}
|
||||
"network_strategy": "default",
|
||||
"network_strategy": "",
|
||||
"network_type": [],
|
||||
"fallback_network_type": [],
|
||||
"fallback_delay": "300ms",
|
||||
"fallback_delay": "",
|
||||
|
||||
// Deprecated
|
||||
"domain_strategy": "prefer_ipv6"
|
||||
"domain_strategy": ""
|
||||
}
|
||||
```
|
||||
|
||||
@@ -75,6 +77,15 @@ Set netfilter routing mark.
|
||||
|
||||
Reuse listener address.
|
||||
|
||||
#### connect_timeout
|
||||
|
||||
Connect timeout, in golang's Duration format.
|
||||
|
||||
A duration string is a possibly signed sequence of
|
||||
decimal numbers, each with optional fraction and a unit suffix,
|
||||
such as "300ms", "-1.5h" or "2h45m".
|
||||
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
|
||||
#### tcp_fast_open
|
||||
|
||||
Enable TCP Fast Open.
|
||||
@@ -91,14 +102,15 @@ Enable TCP Multi Path.
|
||||
|
||||
Enable UDP fragmentation.
|
||||
|
||||
#### connect_timeout
|
||||
#### netns
|
||||
|
||||
Connect timeout, in golang's Duration format.
|
||||
!!! question "Since sing-box 1.12.0"
|
||||
|
||||
A duration string is a possibly signed sequence of
|
||||
decimal numbers, each with optional fraction and a unit suffix,
|
||||
such as "300ms", "-1.5h" or "2h45m".
|
||||
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
!!! quote ""
|
||||
|
||||
Only supported on Linux.
|
||||
|
||||
Set network namespace, name or path.
|
||||
|
||||
#### domain_resolver
|
||||
|
||||
@@ -106,6 +118,10 @@ Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
|
||||
`outbound` DNS rule items are deprecated and will be removed in sing-box 1.14.0, so this item will be required for outbound/endpoints using domain name in server address since sing-box 1.14.0.
|
||||
|
||||
!!! info ""
|
||||
|
||||
`domain_resolver` or `route.default_domain_resolver` is optional when only one DNS server is configured.
|
||||
|
||||
Set domain resolver to use for resolving domain names.
|
||||
|
||||
This option uses the same format as the [route DNS rule action](/configuration/dns/rule_action/#route) without the `action` field.
|
||||
|
||||
@@ -5,7 +5,8 @@ icon: material/new-box
|
||||
!!! quote "sing-box 1.12.0 中的更改"
|
||||
|
||||
:material-plus: [domain_resolver](#domain_resolver)
|
||||
:material-delete-clock: [domain_strategy](#domain_strategy)
|
||||
:material-delete-clock: [domain_strategy](#domain_strategy)
|
||||
:material-plus: [netns](#netns)
|
||||
|
||||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
|
||||
@@ -18,25 +19,26 @@ icon: material/new-box
|
||||
|
||||
```json
|
||||
{
|
||||
"detour": "upstream-out",
|
||||
"bind_interface": "en0",
|
||||
"inet4_bind_address": "0.0.0.0",
|
||||
"inet6_bind_address": "::",
|
||||
"routing_mark": 1234,
|
||||
"detour": "",
|
||||
"bind_interface": "",
|
||||
"inet4_bind_address": "",
|
||||
"inet6_bind_address": "",
|
||||
"routing_mark": 0,
|
||||
"reuse_addr": false,
|
||||
"connect_timeout": "5s",
|
||||
"connect_timeout": "",
|
||||
"tcp_fast_open": false,
|
||||
"tcp_multi_path": false,
|
||||
"udp_fragment": false,
|
||||
"netns": "",
|
||||
"domain_resolver": "", // 或 {}
|
||||
"network_strategy": "",
|
||||
"network_type": [],
|
||||
"fallback_network_type": [],
|
||||
"fallback_delay": "300ms",
|
||||
"fallback_delay": "",
|
||||
|
||||
// 废弃的
|
||||
|
||||
"domain_strategy": "prefer_ipv6"
|
||||
"domain_strategy": ""
|
||||
}
|
||||
```
|
||||
|
||||
@@ -76,6 +78,13 @@ icon: material/new-box
|
||||
|
||||
重用监听地址。
|
||||
|
||||
#### connect_timeout
|
||||
|
||||
连接超时,采用 golang 的 Duration 格式。
|
||||
|
||||
持续时间字符串是一个可能有符号的序列十进制数,每个都有可选的分数和单位后缀, 例如 "300ms"、"-1.5h" 或 "2h45m"。
|
||||
有效时间单位为 "ns"、"us"(或 "µs")、"ms"、"s"、"m"、"h"。
|
||||
|
||||
#### tcp_fast_open
|
||||
|
||||
启用 TCP Fast Open。
|
||||
@@ -92,12 +101,15 @@ icon: material/new-box
|
||||
|
||||
启用 UDP 分段。
|
||||
|
||||
#### connect_timeout
|
||||
#### netns
|
||||
|
||||
连接超时,采用 golang 的 Duration 格式。
|
||||
!!! question "自 sing-box 1.12.0 起"
|
||||
|
||||
持续时间字符串是一个可能有符号的序列十进制数,每个都有可选的分数和单位后缀, 例如 "300ms"、"-1.5h" 或 "2h45m"。
|
||||
有效时间单位为 "ns"、"us"(或 "µs")、"ms"、"s"、"m"、"h"。
|
||||
!!! quote ""
|
||||
|
||||
仅支持 Linux。
|
||||
|
||||
设置网络命名空间,名称或路径。
|
||||
|
||||
#### domain_resolver
|
||||
|
||||
@@ -105,6 +117,10 @@ icon: material/new-box
|
||||
|
||||
`outbound` DNS 规则项已弃用,且将在 sing-box 1.14.0 中被移除。因此,从 sing-box 1.14.0 版本开始,所有在服务器地址中使用域名的出站/端点均需配置此项。
|
||||
|
||||
!!! info ""
|
||||
|
||||
当只有一个 DNS 服务器已配置时,`domain_resolver` 或 `route.default_domain_resolver` 是可选的。
|
||||
|
||||
用于设置解析域名的域名解析器。
|
||||
|
||||
此选项的格式与 [路由 DNS 规则动作](/configuration/dns/rule_action/#route) 相同,但不包含 `action` 字段。
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
---
|
||||
icon: material/delete-clock
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.12.0"
|
||||
|
||||
:material-plus: [netns](#netns)
|
||||
|
||||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
:material-delete-clock: [sniff](#sniff)
|
||||
@@ -14,17 +18,18 @@ icon: material/delete-clock
|
||||
|
||||
```json
|
||||
{
|
||||
"listen": "::",
|
||||
"listen_port": 5353,
|
||||
"listen": "",
|
||||
"listen_port": 0,
|
||||
"tcp_fast_open": false,
|
||||
"tcp_multi_path": false,
|
||||
"udp_fragment": false,
|
||||
"udp_timeout": "5m",
|
||||
"detour": "another-in",
|
||||
"udp_timeout": "",
|
||||
"netns": "",
|
||||
"detour": "",
|
||||
"sniff": false,
|
||||
"sniff_override_destination": false,
|
||||
"sniff_timeout": "300ms",
|
||||
"domain_strategy": "prefer_ipv6",
|
||||
"sniff_timeout": "",
|
||||
"domain_strategy": "",
|
||||
"udp_disable_domain_unmapping": false
|
||||
}
|
||||
```
|
||||
@@ -72,6 +77,16 @@ UDP NAT expiration time.
|
||||
|
||||
`5m` will be used by default.
|
||||
|
||||
#### netns
|
||||
|
||||
!!! question "Since sing-box 1.12.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported on Linux.
|
||||
|
||||
Set network namespace, name or path.
|
||||
|
||||
#### detour
|
||||
|
||||
If set, connections will be forwarded to the specified inbound.
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
---
|
||||
icon: material/delete-clock
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.12.0"
|
||||
|
||||
:material-plus: [netns](#netns)
|
||||
|
||||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
|
||||
:material-delete-clock: [sniff](#sniff)
|
||||
@@ -14,17 +18,18 @@ icon: material/delete-clock
|
||||
|
||||
```json
|
||||
{
|
||||
"listen": "::",
|
||||
"listen_port": 5353,
|
||||
"listen": "",
|
||||
"listen_port": 0,
|
||||
"tcp_fast_open": false,
|
||||
"tcp_multi_path": false,
|
||||
"udp_fragment": false,
|
||||
"udp_timeout": "5m",
|
||||
"detour": "another-in",
|
||||
"udp_timeout": "",
|
||||
"netns": "",
|
||||
"detour": "",
|
||||
"sniff": false,
|
||||
"sniff_override_destination": false,
|
||||
"sniff_timeout": "300ms",
|
||||
"domain_strategy": "prefer_ipv6",
|
||||
"sniff_timeout": "",
|
||||
"domain_strategy": "",
|
||||
"udp_disable_domain_unmapping": false
|
||||
}
|
||||
```
|
||||
@@ -73,6 +78,16 @@ UDP NAT 过期时间。
|
||||
|
||||
默认使用 `5m`。
|
||||
|
||||
#### netns
|
||||
|
||||
!!! question "自 sing-box 1.12.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅支持 Linux。
|
||||
|
||||
设置网络命名空间,名称或路径。
|
||||
|
||||
#### detour
|
||||
|
||||
如果设置,连接将被转发到指定的入站。
|
||||
|
||||
@@ -31,12 +31,11 @@ The protocol version, `1` or `2`.
|
||||
|
||||
### Application support
|
||||
|
||||
| Project | UoT v1 | UoT v2 |
|
||||
|--------------|----------------------|-------------------------------------------------------------------------------------------------------------------|
|
||||
| sing-box | v0 (2022/08/11) | v1.2-beta9 |
|
||||
| Xray-core | v1.5.7 (2022/06/05) | [f57ec13](https://github.com/XTLS/Xray-core/commit/f57ec1388084df041a2289bacab14e446bf1b357) (Not released) |
|
||||
| Clash.Meta | v1.12.0 (2022/07/02) | [8cb67b6](https://github.com/MetaCubeX/Clash.Meta/commit/8cb67b6480649edfa45dcc9ac89ce0789651e8b3) (Not released) |
|
||||
| Shadowrocket | v2.2.12 (2022/08/13) | / |
|
||||
| Project | UoT v1 | UoT v2 |
|
||||
|--------------|----------------------|----------------------|
|
||||
| sing-box | v0 (2022/08/11) | v1.2-beta9 |
|
||||
| Clash.Meta | v1.12.0 (2022/07/02) | v1.14.3 (2023/03/31) |
|
||||
| Shadowrocket | v2.2.12 (2022/08/13) | / |
|
||||
|
||||
### Protocol details
|
||||
|
||||
@@ -50,7 +49,13 @@ The client requests the magic address to the upper layer proxy protocol to indic
|
||||
|------|----------|-------|--------|----------|
|
||||
| u8 | variable | u16be | u16be | variable |
|
||||
|
||||
**ATYP / address / port**: Uses the SOCKS address format.
|
||||
**ATYP / address / port**: Uses the SOCKS address format, but with different address types:
|
||||
|
||||
| ATYP | Address type |
|
||||
|--------|--------------|
|
||||
| `0x00` | IPv4 Address |
|
||||
| `0x01` | IPv6 Address |
|
||||
| `0x02` | Domain Name |
|
||||
|
||||
#### Protocol version 2
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ icon: material/package
|
||||
|
||||
=== ":material-linux: Linux"
|
||||
|
||||
| 类型 | 平台 | 链接 | 命令 |
|
||||
| 类型 | 平台 | 命令 | 链接 |
|
||||
|----------|---------------|------------------------------|---------------------------------------------------------------------------------------------------------------|
|
||||
| AUR | Arch Linux | `? -S sing-box` | [][aur] |
|
||||
| nixpkgs | NixOS | `nix-env -iA nixos.sing-box` | [][nixpkgs] |
|
||||
@@ -61,13 +61,13 @@ icon: material/package
|
||||
|
||||
=== ":material-apple: macOS"
|
||||
|
||||
| 类型 | 平台 | 链接 | 命令 |
|
||||
| 类型 | 平台 | 命令 | 链接 |
|
||||
|----------|-------|-------------------------|------------------------------------------------------------------------------------------------|
|
||||
| Homebrew | macOS | `brew install sing-box` | [][brew] |
|
||||
|
||||
=== ":material-microsoft-windows: Windows"
|
||||
|
||||
| 类型 | 平台 | 链接 | 命令 |
|
||||
| 类型 | 平台 | 命令 | 链接 |
|
||||
|------------|---------|---------------------------|-----------------------------------------------------------------------------------------------------|
|
||||
| Scoop | Windows | `scoop install sing-box` | [][scoop] |
|
||||
| Chocolatey | Windows | `choco install sing-box` | [][choco] |
|
||||
@@ -75,13 +75,13 @@ icon: material/package
|
||||
|
||||
=== ":material-android: Android"
|
||||
|
||||
| 类型 | 平台 | 链接 | 命令 |
|
||||
| 类型 | 平台 | 命令 | 链接 |
|
||||
|--------|---------|--------------------|----------------------------------------------------------------------------------------------|
|
||||
| Termux | Android | `pkg add sing-box` | [][termux] |
|
||||
|
||||
=== ":material-freebsd: FreeBSD"
|
||||
|
||||
| 类型 | 平台 | 链接 | 命令 |
|
||||
| 类型 | 平台 | 命令 | 链接 |
|
||||
|------------|---------|------------------------|--------------------------------------------------------------------------------------------|
|
||||
| FreshPorts | FreeBSD | `pkg install sing-box` | [][ports] |
|
||||
|
||||
|
||||
@@ -567,7 +567,7 @@ The legacy outbound DNS rules are deprecated and can be replaced by new domain r
|
||||
"server_port": 2080,
|
||||
"domain_resolver": {
|
||||
"server": "local",
|
||||
"rewrite_tll": 60,
|
||||
"rewrite_ttl": 60,
|
||||
"client_subnet": "1.1.1.1"
|
||||
},
|
||||
// or "domain_resolver": "local",
|
||||
@@ -579,7 +579,7 @@ The legacy outbound DNS rules are deprecated and can be replaced by new domain r
|
||||
"route": {
|
||||
"default_domain_resolver": {
|
||||
"server": "local",
|
||||
"rewrite_tll": 60,
|
||||
"rewrite_ttl": 60,
|
||||
"client_subnet": "1.1.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,13 +565,21 @@ DNS 服务器已经重构。
|
||||
"type": "socks",
|
||||
"server": "example.org",
|
||||
"server_port": 2080,
|
||||
"domain_resolver": "local",
|
||||
"domain_resolver": {
|
||||
"server": "local",
|
||||
"rewrite_ttl": 60,
|
||||
"client_subnet": "1.1.1.1"
|
||||
},
|
||||
// 或 "domain_resolver": "local",
|
||||
}
|
||||
],
|
||||
|
||||
// 或
|
||||
|
||||
"route": {
|
||||
"default_domain_resolver": {
|
||||
"server": "local",
|
||||
"rewrite_tll": 60,
|
||||
"rewrite_ttl": 60,
|
||||
"client_subnet": "1.1.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,12 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
bucketSelected = []byte("selected")
|
||||
bucketExpand = []byte("group_expand")
|
||||
bucketMode = []byte("clash_mode")
|
||||
bucketRuleSet = []byte("rule_set")
|
||||
bucketSelected = []byte("selected")
|
||||
bucketExpand = []byte("group_expand")
|
||||
bucketMode = []byte("clash_mode")
|
||||
bucketRuleSet = []byte("rule_set")
|
||||
bucketScript = []byte("script")
|
||||
bucketSgPersistentStore = []byte("sg_persistent_store")
|
||||
|
||||
bucketNameList = []string{
|
||||
string(bucketSelected),
|
||||
@@ -316,3 +318,70 @@ func (c *CacheFile) SaveRuleSet(tag string, set *adapter.SavedBinary) error {
|
||||
return bucket.Put([]byte(tag), setBinary)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CacheFile) LoadScript(tag string) *adapter.SavedBinary {
|
||||
var savedSet adapter.SavedBinary
|
||||
err := c.DB.View(func(t *bbolt.Tx) error {
|
||||
bucket := c.bucket(t, bucketScript)
|
||||
if bucket == nil {
|
||||
return os.ErrNotExist
|
||||
}
|
||||
scriptBinary := bucket.Get([]byte(tag))
|
||||
if len(scriptBinary) == 0 {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
return savedSet.UnmarshalBinary(scriptBinary)
|
||||
})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &savedSet
|
||||
}
|
||||
|
||||
func (c *CacheFile) SaveScript(tag string, set *adapter.SavedBinary) error {
|
||||
return c.DB.Batch(func(t *bbolt.Tx) error {
|
||||
bucket, err := c.createBucket(t, bucketScript)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scriptBinary, err := set.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return bucket.Put([]byte(tag), scriptBinary)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CacheFile) SurgePersistentStoreRead(key string) string {
|
||||
var value string
|
||||
_ = c.DB.View(func(t *bbolt.Tx) error {
|
||||
bucket := c.bucket(t, bucketSgPersistentStore)
|
||||
if bucket == nil {
|
||||
return nil
|
||||
}
|
||||
valueBinary := bucket.Get([]byte(key))
|
||||
if len(valueBinary) > 0 {
|
||||
value = string(valueBinary)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return value
|
||||
}
|
||||
|
||||
func (c *CacheFile) SurgePersistentStoreWrite(key string, value string) error {
|
||||
return c.DB.Batch(func(t *bbolt.Tx) error {
|
||||
if value != "" {
|
||||
bucket, err := c.createBucket(t, bucketSgPersistentStore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return bucket.Put([]byte(key), []byte(value))
|
||||
} else {
|
||||
bucket := c.bucket(t, bucketSgPersistentStore)
|
||||
if bucket == nil {
|
||||
return nil
|
||||
}
|
||||
return bucket.Delete([]byte(key))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
73
experimental/clashapi/mitm.go
Normal file
73
experimental/clashapi/mitm.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package clashapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/service"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
func mitmRouter(ctx context.Context) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/mobileconfig", getMobileConfig(ctx))
|
||||
r.Get("/certificate", getCertificate(ctx))
|
||||
return r
|
||||
}
|
||||
|
||||
func getMobileConfig(ctx context.Context) http.HandlerFunc {
|
||||
return func(writer http.ResponseWriter, request *http.Request) {
|
||||
store := service.FromContext[adapter.CertificateStore](ctx)
|
||||
if !store.TLSDecryptionEnabled() {
|
||||
http.NotFound(writer, request)
|
||||
render.PlainText(writer, request, "TLS decryption not enabled")
|
||||
return
|
||||
}
|
||||
certificate := store.TLSDecryptionCertificate()
|
||||
writer.Header().Set("Content-Type", "application/x-apple-aspen-config")
|
||||
uuidGen := common.Must1(uuid.NewV4()).String()
|
||||
mobileConfig := map[string]interface{}{
|
||||
"PayloadContent": []interface{}{
|
||||
map[string]interface{}{
|
||||
"PayloadCertificateFileName": "Certificates.cer",
|
||||
"PayloadContent": certificate.Raw,
|
||||
"PayloadDescription": "Adds a root certificate",
|
||||
"PayloadDisplayName": certificate.Subject.CommonName,
|
||||
"PayloadIdentifier": "com.apple.security.root." + uuidGen,
|
||||
"PayloadType": "com.apple.security.root",
|
||||
"PayloadUUID": uuidGen,
|
||||
"PayloadVersion": 1,
|
||||
},
|
||||
},
|
||||
"PayloadDisplayName": certificate.Subject.CommonName,
|
||||
"PayloadIdentifier": "io.nekohasekai.sfa.ca.profile." + uuidGen,
|
||||
"PayloadRemovalDisallowed": false,
|
||||
"PayloadType": "Configuration",
|
||||
"PayloadUUID": uuidGen,
|
||||
"PayloadVersion": 1,
|
||||
}
|
||||
encoder := plist.NewEncoder(writer)
|
||||
encoder.Indent("\t")
|
||||
encoder.Encode(mobileConfig)
|
||||
}
|
||||
}
|
||||
|
||||
func getCertificate(ctx context.Context) http.HandlerFunc {
|
||||
return func(writer http.ResponseWriter, request *http.Request) {
|
||||
store := service.FromContext[adapter.CertificateStore](ctx)
|
||||
if !store.TLSDecryptionEnabled() {
|
||||
http.NotFound(writer, request)
|
||||
render.PlainText(writer, request, "TLS decryption not enabled")
|
||||
return
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/x-x509-ca-cert")
|
||||
writer.Header().Set("Content-Disposition", "attachment; filename=Certificate.crt")
|
||||
writer.Write(store.TLSDecryptionCertificate().Raw)
|
||||
}
|
||||
}
|
||||
@@ -124,6 +124,7 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op
|
||||
r.Mount("/profile", profileRouter())
|
||||
r.Mount("/cache", cacheRouter(ctx))
|
||||
r.Mount("/dns", dnsRouter(s.dnsRouter))
|
||||
r.Mount("/mitm", mitmRouter(ctx))
|
||||
|
||||
s.setupMetaAPI(r)
|
||||
})
|
||||
|
||||
@@ -77,15 +77,15 @@ func (s *Server) downloadExternalUI() error {
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return E.New("download external ui failed: ", response.Status)
|
||||
}
|
||||
err = s.downloadZIP(filepath.Base(downloadURL), response.Body, s.externalUI)
|
||||
err = s.downloadZIP(response.Body, s.externalUI)
|
||||
if err != nil {
|
||||
removeAllInDirectory(s.externalUI)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Server) downloadZIP(name string, body io.Reader, output string) error {
|
||||
tempFile, err := filemanager.CreateTemp(s.ctx, name)
|
||||
func (s *Server) downloadZIP(body io.Reader, output string) error {
|
||||
tempFile, err := filemanager.CreateTemp(s.ctx, "external-ui.zip")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -38,7 +38,12 @@ func newPlatformTransport(iif LocalDNSTransport, tag string, options option.Loca
|
||||
}
|
||||
}
|
||||
|
||||
func (p *platformTransport) Reset() {
|
||||
func (p *platformTransport) Start(stage adapter.StartStage) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *platformTransport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *platformTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
|
||||
@@ -56,7 +56,12 @@ func (m *platformDefaultInterfaceMonitor) UnregisterCallback(element *list.Eleme
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32, isExpensive bool, isConstrained bool) {
|
||||
if sFixAndroidStack {
|
||||
go m.updateDefaultInterface(interfaceName, interfaceIndex32, isExpensive, isConstrained)
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
m.updateDefaultInterface(interfaceName, interfaceIndex32, isExpensive, isConstrained)
|
||||
close(done)
|
||||
}()
|
||||
<-done
|
||||
} else {
|
||||
m.updateDefaultInterface(interfaceName, interfaceIndex32, isExpensive, isConstrained)
|
||||
}
|
||||
|
||||
@@ -32,4 +32,9 @@ type Notification struct {
|
||||
Subtitle string
|
||||
Body string
|
||||
OpenURL string
|
||||
Clipboard string
|
||||
MediaURL string
|
||||
MediaData []byte
|
||||
MediaType string
|
||||
Timeout int
|
||||
}
|
||||
|
||||
49
go.mod
49
go.mod
@@ -3,10 +3,12 @@ module github.com/sagernet/sing-box
|
||||
go 1.23.1
|
||||
|
||||
require (
|
||||
github.com/anytls/sing-anytls v0.0.3
|
||||
github.com/adhocore/gronx v1.19.5
|
||||
github.com/anytls/sing-anytls v0.0.6
|
||||
github.com/caddyserver/certmagic v0.21.7
|
||||
github.com/cloudflare/circl v1.6.0
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/dop251/goja v0.0.0-20250125213203-5ef83b82af17
|
||||
github.com/go-chi/chi/v5 v5.2.1
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/gofrs/uuid/v5 v5.3.1
|
||||
@@ -26,25 +28,26 @@ require (
|
||||
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
|
||||
github.com/sagernet/quic-go v0.49.0-beta.1
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.6.2-0.20250210072154-8dff604468ff
|
||||
github.com/sagernet/sing v0.6.4-0.20250319121229-11d8838dc56d
|
||||
github.com/sagernet/sing-mux v0.3.1
|
||||
github.com/sagernet/sing-quic v0.4.1-beta.1
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||
github.com/sagernet/sing-shadowtls v0.2.0
|
||||
github.com/sagernet/sing-tun v0.6.1
|
||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056
|
||||
github.com/sagernet/sing-tun v0.6.2-0.20250319123703-35b5747b44ec
|
||||
github.com/sagernet/sing-vmess v0.2.0
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||
github.com/sagernet/tailscale v1.79.0-mod.1
|
||||
github.com/sagernet/tailscale v1.80.3-mod.0
|
||||
github.com/sagernet/utls v1.6.7
|
||||
github.com/sagernet/wireguard-go v0.0.1-beta.5
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/vishvananda/netns v0.0.4
|
||||
go.uber.org/zap v1.27.0
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.33.0
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8
|
||||
golang.org/x/mod v0.23.0
|
||||
golang.org/x/net v0.35.0
|
||||
golang.org/x/sys v0.30.0
|
||||
@@ -52,6 +55,7 @@ require (
|
||||
google.golang.org/grpc v1.70.0
|
||||
google.golang.org/protobuf v1.36.5
|
||||
howett.net/plist v1.0.1
|
||||
software.sslmate.com/src/go-pkcs12 v0.4.0
|
||||
)
|
||||
|
||||
//replace github.com/sagernet/sing => ../sing
|
||||
@@ -71,12 +75,14 @@ require (
|
||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
|
||||
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect
|
||||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/gaissmai/bart v0.11.1 // indirect
|
||||
github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect
|
||||
github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect
|
||||
@@ -85,26 +91,25 @@ require (
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
|
||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/csrf v1.7.2 // indirect
|
||||
github.com/gorilla/csrf v1.7.3-0.20250123201450-9dd6af1f6d30 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
||||
github.com/illarion/gonotify/v2 v2.0.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect
|
||||
github.com/jsimonetti/rtnetlink v1.4.0 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect
|
||||
github.com/libdns/libdns v0.2.2 // indirect
|
||||
github.com/mdlayher/genetlink v1.3.2 // indirect
|
||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect
|
||||
github.com/mdlayher/sdnotify v1.0.0 // indirect
|
||||
github.com/mdlayher/socket v0.5.1 // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.17.2 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.9.7 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus-community/pro-bing v0.4.0 // indirect
|
||||
@@ -120,26 +125,22 @@ require (
|
||||
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect
|
||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
|
||||
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect
|
||||
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 // indirect
|
||||
github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 // indirect
|
||||
github.com/tcnksm/go-httpstat v0.2.0 // indirect
|
||||
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect
|
||||
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect
|
||||
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/term v0.29.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
golang.org/x/tools v0.29.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.3.0 // indirect
|
||||
)
|
||||
|
||||
//replace github.com/sagernet/sing => ../sing
|
||||
|
||||
106
go.sum
106
go.sum
@@ -1,5 +1,9 @@
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/adhocore/gronx v1.19.5 h1:cwIG4nT1v9DvadxtHBe6MzE+FZ1JDvAUC45U2fl4eSQ=
|
||||
github.com/adhocore/gronx v1.19.5/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A=
|
||||
@@ -8,8 +12,8 @@ github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7V
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/anytls/sing-anytls v0.0.3 h1:JAQpETCUiZ1LFAPnO8vyJsdxypvKB4zinXwy9KiqWeQ=
|
||||
github.com/anytls/sing-anytls v0.0.3/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8=
|
||||
github.com/anytls/sing-anytls v0.0.6 h1:UatIjl/OvzWQGXQ1I2bAIkabL9WtihW0fA7G+DXGBUg=
|
||||
github.com/anytls/sing-anytls v0.0.6/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8=
|
||||
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
|
||||
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg=
|
||||
@@ -30,6 +34,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
|
||||
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
||||
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk=
|
||||
@@ -38,10 +43,14 @@ github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbY
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
|
||||
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yezR0y7jJ5pceLHthLaYf4bA5T14B6q39S4q2Q=
|
||||
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A=
|
||||
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
|
||||
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dop251/goja v0.0.0-20250125213203-5ef83b82af17 h1:spJaibPy2sZNwo6Q0HjBVufq7hBUj5jNFOKRoogCBow=
|
||||
github.com/dop251/goja v0.0.0-20250125213203-5ef83b82af17/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
|
||||
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/gaissmai/bart v0.11.1 h1:5Uv5XwsaFBRo4E5VBcb9TzY8B7zxFf+U7isDxqOrRfc=
|
||||
github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg=
|
||||
github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I=
|
||||
@@ -50,16 +59,18 @@ github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
|
||||
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg=
|
||||
github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA=
|
||||
github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288 h1:KbX3Z3CgiYlbaavUq3Cj9/MjpO+88S7/AGXzynVDv84=
|
||||
github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288/go.mod h1:BWmvoE1Xia34f3l/ibJweyhrT+aROb/FQ6d+37F0e2s=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
@@ -83,12 +94,12 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 h1:wG8RYIyctLhdFk6Vl1yPGtSRtwGpVkWyZww1OCil2MI=
|
||||
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4=
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
|
||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI=
|
||||
github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk=
|
||||
github.com/gorilla/csrf v1.7.3-0.20250123201450-9dd6af1f6d30 h1:fiJdrgVBkjZ5B1HJ2WQwNOaXB+QyYcNXTA3t1XYLz0M=
|
||||
github.com/gorilla/csrf v1.7.3-0.20250123201450-9dd6af1f6d30/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk=
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
||||
@@ -102,9 +113,6 @@ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf
|
||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
|
||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8=
|
||||
github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I=
|
||||
github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
@@ -124,8 +132,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/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
|
||||
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
|
||||
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
||||
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 h1:A1Cq6Ysb0GM0tpKMbdCXCIfBclan4oHk1Jb+Hrejirg=
|
||||
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42/go.mod h1:BB4YCPDOzfy7FniQ/lxuYQ3dgmM2cZumHbK8RpTjN2o=
|
||||
github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ3c=
|
||||
github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE=
|
||||
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
|
||||
@@ -140,13 +148,12 @@ github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g=
|
||||
github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
|
||||
github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE=
|
||||
github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY=
|
||||
github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss=
|
||||
github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0=
|
||||
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
|
||||
github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4=
|
||||
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
|
||||
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -182,8 +189,8 @@ github.com/sagernet/quic-go v0.49.0-beta.1/go.mod h1:uesWD1Ihrldq1M3XtjuEvIUqi8W
|
||||
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.6.2-0.20250210072154-8dff604468ff h1:5UGghwx8cI14qFa0ienrLekAYfhdKAiWvJUkY7rHmsI=
|
||||
github.com/sagernet/sing v0.6.2-0.20250210072154-8dff604468ff/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing v0.6.4-0.20250319121229-11d8838dc56d h1:8GJnvXlOBdgCa0spumUzPbMamkEbud4sfNTd8+1YaEg=
|
||||
github.com/sagernet/sing v0.6.4-0.20250319121229-11d8838dc56d/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI=
|
||||
github.com/sagernet/sing-mux v0.3.1/go.mod h1:Mkdz8LnDstthz0HWuA/5foncnDIdcNN5KZ6AdJX+x78=
|
||||
github.com/sagernet/sing-quic v0.4.1-beta.1 h1:V2VfMckT3EQR3ZdfSzJgZZDsvfZZH42QAZpnOnHKa0s=
|
||||
@@ -192,16 +199,16 @@ github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegE
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.2.0 h1:cLKe4OAOFwuhmAIuPLj//CIL7Q9js+pIDardhJ+/osk=
|
||||
github.com/sagernet/sing-shadowtls v0.2.0/go.mod h1:agU+Fw5X+xnWVyRHyFthoZCX3MfWKCFPm4JUf+1oaxo=
|
||||
github.com/sagernet/sing-tun v0.6.1 h1:4l0+gnEKcGjlWfUVTD+W0BRApqIny/lU2ZliurE+VMo=
|
||||
github.com/sagernet/sing-tun v0.6.1/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056 h1:GFNJQAHhSXqAfxAw1wDG/QWbdpGH5Na3k8qUynqWnEA=
|
||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056/go.mod h1:HyacBPIFiKihJQR8LQp56FM4hBtd/7MZXnRxxQIOPsc=
|
||||
github.com/sagernet/sing-tun v0.6.2-0.20250319123703-35b5747b44ec h1:9/OYGb9qDmUFIhqd3S+3eni62EKRQR1rSmRH18baA/M=
|
||||
github.com/sagernet/sing-tun v0.6.2-0.20250319123703-35b5747b44ec/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
||||
github.com/sagernet/sing-vmess v0.2.0 h1:pCMGUXN2k7RpikQV65/rtXtDHzb190foTfF9IGTMZrI=
|
||||
github.com/sagernet/sing-vmess v0.2.0/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
|
||||
github.com/sagernet/tailscale v1.79.0-mod.1 h1:wIuAH7VqBYJNk0h2+bTyk4F0OlSqHvyLDCBrD3i+XNI=
|
||||
github.com/sagernet/tailscale v1.79.0-mod.1/go.mod h1:RKY5WjYLj3JJ7VO/8ZCw8eAFa4+kWU6A1Ftdk84uB14=
|
||||
github.com/sagernet/tailscale v1.80.3-mod.0 h1:oHIdivbR/yxoiA9d3a2rRlhYn2shY9XVF35Rr8jW508=
|
||||
github.com/sagernet/tailscale v1.80.3-mod.0/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI=
|
||||
github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8=
|
||||
github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM=
|
||||
github.com/sagernet/wireguard-go v0.0.1-beta.5 h1:aBEsxJUMEONwOZqKPIkuAcv4zJV5p6XlzEN04CF0FXc=
|
||||
@@ -213,6 +220,7 @@ github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3k
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
@@ -228,16 +236,14 @@ github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29X
|
||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
|
||||
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU=
|
||||
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
|
||||
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 h1:Gz0rz40FvFVLTBk/K8UNAenb36EbDSnh+q7Z9ldcC8w=
|
||||
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4/go.mod h1:phI29ccmHQBc+wvroosENp1IF9195449VDnFDhJ4rJU=
|
||||
github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 h1:tdUdyPqJ0C97SJfjB9tW6EylTtreyee9C44de+UBG0g=
|
||||
github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ=
|
||||
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+yfntqhI3oAu9i27nEojcQ4NuBQOo5ZFA=
|
||||
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc/go.mod h1:f93CXfllFsO9ZQVq+Zocb1Gp4G5Fz0b0rXHLOzt/Djc=
|
||||
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 h1:UBPHPtv8+nEAy2PD8RyAhOYvau1ek0HDJqLS/Pysi14=
|
||||
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ=
|
||||
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
|
||||
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
|
||||
github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0=
|
||||
github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8=
|
||||
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e h1:BA9O3BmlTmpjbvajAwzWx4Wo2TRVdpPXZEeemGQcajw=
|
||||
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
|
||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
@@ -267,17 +273,17 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
|
||||
go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
|
||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8=
|
||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
|
||||
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 h1:Tl++JLUCe4sxGu8cTpDzRLd3tN7US4hOxG5YpKCzkek=
|
||||
go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
|
||||
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
|
||||
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
|
||||
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
@@ -291,10 +297,8 @@ golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/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.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
@@ -306,11 +310,11 @@ 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.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.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.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
||||
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -329,6 +333,8 @@ google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojt
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -107,7 +107,6 @@ func DNSTransportRegistry() *dns.TransportRegistry {
|
||||
transport.RegisterUDP(registry)
|
||||
transport.RegisterTLS(registry)
|
||||
transport.RegisterHTTPS(registry)
|
||||
transport.RegisterPredefined(registry)
|
||||
hosts.RegisterTransport(registry)
|
||||
local.RegisterTransport(registry)
|
||||
fakeip.RegisterTransport(registry)
|
||||
|
||||
@@ -20,12 +20,16 @@ type ID struct {
|
||||
}
|
||||
|
||||
func ContextWithNewID(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, (*idKey)(nil), ID{
|
||||
return ContextWithID(ctx, ID{
|
||||
ID: rand.Uint32(),
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
func ContextWithID(ctx context.Context, id ID) context.Context {
|
||||
return context.WithValue(ctx, (*idKey)(nil), id)
|
||||
}
|
||||
|
||||
func IDFromContext(ctx context.Context) (ID, bool) {
|
||||
id, loaded := ctx.Value((*idKey)(nil)).(ID)
|
||||
return id, loaded
|
||||
|
||||
@@ -10,6 +10,10 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultTimeFormat = "-0700 2006-01-02 15:04:05"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Context context.Context
|
||||
Options option.LogOptions
|
||||
@@ -47,7 +51,7 @@ func New(options Options) (Factory, error) {
|
||||
DisableColors: logOptions.DisableColor || logFilePath != "",
|
||||
DisableTimestamp: !logOptions.Timestamp && logFilePath != "",
|
||||
FullTimestamp: logOptions.Timestamp,
|
||||
TimestampFormat: "-0700 2006-01-02 15:04:05",
|
||||
TimestampFormat: DefaultTimeFormat,
|
||||
}
|
||||
factory := NewDefaultFactory(
|
||||
options.Context,
|
||||
|
||||
11
mitm/constants.go
Normal file
11
mitm/constants.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package mitm
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
)
|
||||
|
||||
var surgeTinyGif = common.OnceValue(func() []byte {
|
||||
return common.Must1(base64.StdEncoding.DecodeString("R0lGODlhAQABAAAAACH5BAEAAAAALAAAAAABAAEAAAIBAAA="))
|
||||
})
|
||||
1099
mitm/engine.go
Normal file
1099
mitm/engine.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -84,13 +84,13 @@ nav:
|
||||
- configuration/dns/server/index.md
|
||||
- Legacy: configuration/dns/server/legacy.md
|
||||
- Local: configuration/dns/server/local.md
|
||||
- Hosts: configuration/dns/server/hosts.md
|
||||
- TCP: configuration/dns/server/tcp.md
|
||||
- UDP: configuration/dns/server/udp.md
|
||||
- TLS: configuration/dns/server/tls.md
|
||||
- QUIC: configuration/dns/server/quic.md
|
||||
- HTTPS: configuration/dns/server/https.md
|
||||
- HTTP3: configuration/dns/server/http3.md
|
||||
- Predefined: configuration/dns/server/predefined.md
|
||||
- DHCP: configuration/dns/server/dhcp.md
|
||||
- FakeIP: configuration/dns/server/fakeip.md
|
||||
- Tailscale: configuration/dns/server/tailscale.md
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user