mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-12 18:17:18 +10:00
Compare commits
11 Commits
v1.11.0-al
...
dev-next-w
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47736b27ba | ||
|
|
0b2c7ec35c | ||
|
|
e537c56b6b | ||
|
|
b456aff4ac | ||
|
|
4da652ff02 | ||
|
|
262727ec6c | ||
|
|
81dc9e7698 | ||
|
|
d876077281 | ||
|
|
0df81c297b | ||
|
|
b460484e43 | ||
|
|
0e511791e8 |
@@ -57,9 +57,7 @@ type InboundContext struct {
|
||||
// Deprecated
|
||||
InboundOptions option.InboundOptions
|
||||
UDPDisableDomainUnmapping bool
|
||||
UDPConnect bool
|
||||
|
||||
DNSServer string
|
||||
DNSServer string
|
||||
|
||||
DestinationAddresses []netip.Addr
|
||||
SourceGeoIPCode string
|
||||
|
||||
@@ -68,47 +68,28 @@ func NewDirectConnection(ctx context.Context, router adapter.Router, this N.Dial
|
||||
|
||||
func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
var (
|
||||
outPacketConn net.PacketConn
|
||||
outConn net.Conn
|
||||
destinationAddress netip.Addr
|
||||
err error
|
||||
)
|
||||
if metadata.UDPConnect {
|
||||
if len(metadata.DestinationAddresses) > 0 {
|
||||
outConn, err = N.DialSerial(ctx, this, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses)
|
||||
} else {
|
||||
outConn, err = this.DialContext(ctx, N.NetworkUDP, metadata.Destination)
|
||||
}
|
||||
if err != nil {
|
||||
return N.ReportHandshakeFailure(conn, err)
|
||||
}
|
||||
outPacketConn = bufio.NewUnbindPacketConn(outConn)
|
||||
connRemoteAddr := M.AddrFromNet(outConn.RemoteAddr())
|
||||
if connRemoteAddr != metadata.Destination.Addr {
|
||||
destinationAddress = connRemoteAddr
|
||||
}
|
||||
var outConn net.PacketConn
|
||||
var destinationAddress netip.Addr
|
||||
var err error
|
||||
if len(metadata.DestinationAddresses) > 0 {
|
||||
outConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses)
|
||||
} else {
|
||||
if len(metadata.DestinationAddresses) > 0 {
|
||||
outPacketConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses)
|
||||
} else {
|
||||
outPacketConn, err = this.ListenPacket(ctx, metadata.Destination)
|
||||
}
|
||||
if err != nil {
|
||||
return N.ReportHandshakeFailure(conn, err)
|
||||
}
|
||||
outConn, err = this.ListenPacket(ctx, metadata.Destination)
|
||||
}
|
||||
err = N.ReportPacketConnHandshakeSuccess(conn, outPacketConn)
|
||||
if err != nil {
|
||||
outPacketConn.Close()
|
||||
return N.ReportHandshakeFailure(conn, err)
|
||||
}
|
||||
err = N.ReportPacketConnHandshakeSuccess(conn, outConn)
|
||||
if err != nil {
|
||||
outConn.Close()
|
||||
return err
|
||||
}
|
||||
if destinationAddress.IsValid() {
|
||||
if metadata.Destination.IsFqdn() {
|
||||
if metadata.UDPDisableDomainUnmapping {
|
||||
outPacketConn = bufio.NewUnidirectionalNATPacketConn(bufio.NewPacketConn(outPacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
|
||||
outConn = bufio.NewUnidirectionalNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
|
||||
} else {
|
||||
outPacketConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outPacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
|
||||
outConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
|
||||
}
|
||||
}
|
||||
if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded {
|
||||
@@ -123,62 +104,37 @@ func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn,
|
||||
case C.ProtocolDNS:
|
||||
ctx, conn = canceler.NewPacketConn(ctx, conn, C.DNSTimeout)
|
||||
}
|
||||
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outPacketConn))
|
||||
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outConn))
|
||||
}
|
||||
|
||||
func NewDirectPacketConnection(ctx context.Context, router adapter.Router, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext, domainStrategy dns.DomainStrategy) error {
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
var (
|
||||
outPacketConn net.PacketConn
|
||||
outConn net.Conn
|
||||
destinationAddress netip.Addr
|
||||
err error
|
||||
)
|
||||
if metadata.UDPConnect {
|
||||
if len(metadata.DestinationAddresses) > 0 {
|
||||
outConn, err = N.DialSerial(ctx, this, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses)
|
||||
} else if metadata.Destination.IsFqdn() {
|
||||
var destinationAddresses []netip.Addr
|
||||
destinationAddresses, err = router.Lookup(ctx, metadata.Destination.Fqdn, domainStrategy)
|
||||
if err != nil {
|
||||
return N.ReportHandshakeFailure(conn, err)
|
||||
}
|
||||
outConn, err = N.DialSerial(ctx, this, N.NetworkUDP, metadata.Destination, destinationAddresses)
|
||||
} else {
|
||||
outConn, err = this.DialContext(ctx, N.NetworkUDP, metadata.Destination)
|
||||
}
|
||||
var outConn net.PacketConn
|
||||
var destinationAddress netip.Addr
|
||||
var err error
|
||||
if len(metadata.DestinationAddresses) > 0 {
|
||||
outConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses)
|
||||
} else if metadata.Destination.IsFqdn() {
|
||||
var destinationAddresses []netip.Addr
|
||||
destinationAddresses, err = router.Lookup(ctx, metadata.Destination.Fqdn, domainStrategy)
|
||||
if err != nil {
|
||||
return N.ReportHandshakeFailure(conn, err)
|
||||
}
|
||||
connRemoteAddr := M.AddrFromNet(outConn.RemoteAddr())
|
||||
if connRemoteAddr != metadata.Destination.Addr {
|
||||
destinationAddress = connRemoteAddr
|
||||
}
|
||||
outConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, destinationAddresses)
|
||||
} else {
|
||||
if len(metadata.DestinationAddresses) > 0 {
|
||||
outPacketConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses)
|
||||
} else if metadata.Destination.IsFqdn() {
|
||||
var destinationAddresses []netip.Addr
|
||||
destinationAddresses, err = router.Lookup(ctx, metadata.Destination.Fqdn, domainStrategy)
|
||||
if err != nil {
|
||||
return N.ReportHandshakeFailure(conn, err)
|
||||
}
|
||||
outPacketConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, destinationAddresses)
|
||||
} else {
|
||||
outPacketConn, err = this.ListenPacket(ctx, metadata.Destination)
|
||||
}
|
||||
if err != nil {
|
||||
return N.ReportHandshakeFailure(conn, err)
|
||||
}
|
||||
outConn, err = this.ListenPacket(ctx, metadata.Destination)
|
||||
}
|
||||
err = N.ReportPacketConnHandshakeSuccess(conn, outPacketConn)
|
||||
if err != nil {
|
||||
outPacketConn.Close()
|
||||
return N.ReportHandshakeFailure(conn, err)
|
||||
}
|
||||
err = N.ReportPacketConnHandshakeSuccess(conn, outConn)
|
||||
if err != nil {
|
||||
outConn.Close()
|
||||
return err
|
||||
}
|
||||
if destinationAddress.IsValid() {
|
||||
if metadata.Destination.IsFqdn() {
|
||||
outPacketConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outPacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
|
||||
outConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
|
||||
}
|
||||
if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded {
|
||||
natConn.UpdateDestination(destinationAddress)
|
||||
@@ -192,7 +148,7 @@ func NewDirectPacketConnection(ctx context.Context, router adapter.Router, this
|
||||
case C.ProtocolDNS:
|
||||
ctx, conn = canceler.NewPacketConn(ctx, conn, C.DNSTimeout)
|
||||
}
|
||||
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outPacketConn))
|
||||
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outConn))
|
||||
}
|
||||
|
||||
func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) error {
|
||||
|
||||
@@ -68,6 +68,6 @@ func preRun(cmd *cobra.Command, args []string) {
|
||||
if len(configPaths) == 0 && len(configDirectories) == 0 {
|
||||
configPaths = append(configPaths, "config.json")
|
||||
}
|
||||
globalCtx = service.ContextWith(globalCtx, deprecated.NewStderrManager(log.StdLogger()))
|
||||
globalCtx = service.ContextWith(globalCtx, deprecated.NewEnvManager(log.StdLogger()))
|
||||
globalCtx = box.Context(globalCtx, include.InboundRegistry(), include.OutboundRegistry())
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ func check() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx, cancel := context.WithCancel(globalCtx)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
instance, err := box.New(box.Options{
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
|
||||
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -38,7 +39,7 @@ func format() error {
|
||||
return err
|
||||
}
|
||||
for _, optionsEntry := range optionsList {
|
||||
optionsEntry.options, err = badjson.Omitempty(globalCtx, optionsEntry.options)
|
||||
optionsEntry.options, err = badjson.Omitempty(context.TODO(), optionsEntry.options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -24,16 +24,19 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
RuleActionTypeRoute = "route"
|
||||
RuleActionTypeRouteOptions = "route-options"
|
||||
RuleActionTypeDirect = "direct"
|
||||
RuleActionTypeReject = "reject"
|
||||
RuleActionTypeHijackDNS = "hijack-dns"
|
||||
RuleActionTypeSniff = "sniff"
|
||||
RuleActionTypeResolve = "resolve"
|
||||
RuleActionTypeRoute = "route"
|
||||
RuleActionTypeReturn = "return"
|
||||
RuleActionTypeReject = "reject"
|
||||
RuleActionTypeHijackDNS = "hijack-dns"
|
||||
RuleActionTypeSniff = "sniff"
|
||||
RuleActionTypeResolve = "resolve"
|
||||
)
|
||||
|
||||
const (
|
||||
RuleActionRejectMethodDefault = "default"
|
||||
RuleActionRejectMethodDrop = "drop"
|
||||
RuleActionRejectMethodDefault = "default"
|
||||
RuleActionRejectMethodReset = "reset"
|
||||
RuleActionRejectMethodNetworkUnreachable = "network-unreachable"
|
||||
RuleActionRejectMethodHostUnreachable = "host-unreachable"
|
||||
RuleActionRejectMethodPortUnreachable = "port-unreachable"
|
||||
RuleActionRejectMethodDrop = "drop"
|
||||
)
|
||||
|
||||
@@ -2,39 +2,8 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.11.0-alpha.7
|
||||
#### 1.11.0-alpha.5
|
||||
|
||||
* Introducing rule actions **1**
|
||||
|
||||
**1**:
|
||||
|
||||
New rule actions replace legacy inbound fields and special outbound fields,
|
||||
and can be used for pre-matching **2**.
|
||||
|
||||
See [Rule](/configuration/route/rule/),
|
||||
[Rule Action](/configuration/route/rule_action/),
|
||||
[DNS Rule](/configuration/dns/rule/) and
|
||||
[DNS Rule Action](/configuration/dns/rule_action/).
|
||||
|
||||
For migration, see
|
||||
[Migrate legacy special outbounds to rule actions](/migration/#migrate-legacy-special-outbounds-to-rule-actions),
|
||||
[Migrate legacy inbound fields to rule actions](/migration/#migrate-legacy-inbound-fields-to-rule-actions)
|
||||
and [Migrate legacy DNS route options to rule actions](/migration/#migrate-legacy-dns-route-options-to-rule-actions).
|
||||
|
||||
**2**:
|
||||
|
||||
Similar to Surge's pre-matching.
|
||||
|
||||
Specifically, the new rule actions allow you to reject connections with
|
||||
TCP RST (for TCP connections) and ICMP port unreachable (for UDP packets)
|
||||
before connection established to improve tun's compatibility.
|
||||
|
||||
See [Rule Action](/configuration/route/rule_action/).
|
||||
|
||||
#### 1.11.0-alpha.6
|
||||
|
||||
* Update quic-go to v0.48.1
|
||||
* Set gateway for tun correctly
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.11.0-alpha.2
|
||||
@@ -78,7 +47,7 @@ Important changes since 1.9:
|
||||
The new auto-redirect feature allows TUN to automatically
|
||||
configure connection redirection to improve proxy performance.
|
||||
|
||||
When auto-redirect is enabled, new route address set options will allow you to
|
||||
When auto-redirect is enabled, new route address set options will allow you to
|
||||
automatically configure destination IP CIDR rules from a specified rule set to the firewall.
|
||||
|
||||
Specified or unspecified destinations will bypass the sing-box routes to get better performance
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.9.0"
|
||||
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.9.0 中的更改"
|
||||
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
@@ -2,14 +2,6 @@
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
:material-plus: [action](#action)
|
||||
:material-alert: [server](#server)
|
||||
:material-alert: [disable_cache](#disable_cache)
|
||||
:material-alert: [rewrite_ttl](#rewrite_ttl)
|
||||
:material-alert: [client_subnet](#client_subnet)
|
||||
|
||||
!!! quote "Changes in sing-box 1.10.0"
|
||||
|
||||
:material-delete-clock: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source)
|
||||
@@ -22,7 +14,7 @@ icon: material/new-box
|
||||
:material-plus: [geoip](#geoip)
|
||||
:material-plus: [ip_cidr](#ip_cidr)
|
||||
:material-plus: [ip_is_private](#ip_is_private)
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
:material-plus: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source)
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
@@ -143,15 +135,19 @@ icon: material/new-box
|
||||
"outbound": [
|
||||
"direct"
|
||||
],
|
||||
"action": "route",
|
||||
"server": "local"
|
||||
"server": "local",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": 100,
|
||||
"client_subnet": "127.0.0.1/24"
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
"mode": "and",
|
||||
"rules": [],
|
||||
"action": "route",
|
||||
"server": "local"
|
||||
"server": "local",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": 100,
|
||||
"client_subnet": "127.0.0.1/24"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -222,7 +218,7 @@ Match domain using regular expression.
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.8.0"
|
||||
|
||||
Geosite is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
||||
Geosite is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
||||
|
||||
Match geosite.
|
||||
|
||||
@@ -230,7 +226,7 @@ Match geosite.
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.8.0"
|
||||
|
||||
GeoIP is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
GeoIP is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
|
||||
Match source geoip.
|
||||
|
||||
@@ -358,35 +354,29 @@ Match outbound.
|
||||
|
||||
`any` can be used as a value to match any outbound.
|
||||
|
||||
#### action
|
||||
#### server
|
||||
|
||||
==Required==
|
||||
|
||||
See [DNS Rule Actions](../rule_action/) for details.
|
||||
|
||||
#### server
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Moved to [DNS Rule Action](../rule_action#route).
|
||||
Tag of the target dns server.
|
||||
|
||||
#### disable_cache
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Moved to [DNS Rule Action](../rule_action#route).
|
||||
Disable cache and save cache in this query.
|
||||
|
||||
#### rewrite_ttl
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Moved to [DNS Rule Action](../rule_action#route).
|
||||
Rewrite TTL in DNS responses.
|
||||
|
||||
#### client_subnet
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
!!! question "Since sing-box 1.9.0"
|
||||
|
||||
Moved to [DNS Rule Action](../rule_action#route).
|
||||
Append a `edns0-subnet` OPT extra record with the specified IP prefix to every query by default.
|
||||
|
||||
If value is an IP address instead of prefix, `/32` or `/128` will be appended automatically.
|
||||
|
||||
Will overrides `dns.client_subnet` and `servers.[].client_subnet`.
|
||||
|
||||
### Address Filter Fields
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ icon: material/new-box
|
||||
:material-plus: [geoip](#geoip)
|
||||
:material-plus: [ip_cidr](#ip_cidr)
|
||||
:material-plus: [ip_is_private](#ip_is_private)
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
:material-plus: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source)
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
# DNS Rule Action
|
||||
|
||||
!!! question "Since sing-box 1.11.0"
|
||||
|
||||
### route
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route", // default
|
||||
"server": "",
|
||||
|
||||
// for compatibility
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": 0,
|
||||
"client_subnet": null
|
||||
}
|
||||
```
|
||||
|
||||
`route` inherits the classic rule behavior of routing DNS requests to the specified server.
|
||||
|
||||
#### server
|
||||
|
||||
==Required==
|
||||
|
||||
Tag of target server.
|
||||
|
||||
#### disable_cache/rewrite_ttl/client_subnet
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Legacy route options is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-legacy-dns-route-options-to-rule-actions).
|
||||
|
||||
### route-options
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route-options",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": null,
|
||||
"client_subnet": null
|
||||
}
|
||||
```
|
||||
|
||||
#### disable_cache
|
||||
|
||||
Disable cache and save cache in this query.
|
||||
|
||||
#### rewrite_ttl
|
||||
|
||||
Rewrite TTL in DNS responses.
|
||||
|
||||
#### client_subnet
|
||||
|
||||
Append a `edns0-subnet` OPT extra record with the specified IP prefix to every query by default.
|
||||
|
||||
If value is an IP address instead of prefix, `/32` or `/128` will be appended automatically.
|
||||
|
||||
Will overrides `dns.client_subnet` and `servers.[].client_subnet`.
|
||||
|
||||
### reject
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "reject",
|
||||
"method": "default", // default
|
||||
"no_drop": false
|
||||
}
|
||||
```
|
||||
|
||||
`reject` reject DNS requests.
|
||||
|
||||
#### method
|
||||
|
||||
- `default`: Reply with NXDOMAIN.
|
||||
- `drop`: Drop the request.
|
||||
|
||||
#### 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.
|
||||
@@ -1,86 +0,0 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
# DNS 规则动作
|
||||
|
||||
!!! question "自 sing-box 1.11.0 起"
|
||||
|
||||
### route
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route", // 默认
|
||||
"server": "",
|
||||
|
||||
// 兼容性
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": 0,
|
||||
"client_subnet": null
|
||||
}
|
||||
```
|
||||
|
||||
`route` 继承了将 DNS 请求 路由到指定服务器的经典规则动作。
|
||||
|
||||
#### server
|
||||
|
||||
==必填==
|
||||
|
||||
目标 DNS 服务器的标签。
|
||||
|
||||
#### disable_cache/rewrite_ttl/client_subnet
|
||||
|
||||
!!! failure "自 sing-box 1.11.0 起"
|
||||
|
||||
旧的路由选项已弃用,且将在 sing-box 1.12.0 中移除,参阅 [迁移指南](/migration/#migrate-legacy-dns-route-options-to-rule-actions).
|
||||
|
||||
### route-options
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route-options",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": null,
|
||||
"client_subnet": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### disable_cache
|
||||
|
||||
在此查询中禁用缓存。
|
||||
|
||||
#### rewrite_ttl
|
||||
|
||||
重写 DNS 回应中的 TTL。
|
||||
|
||||
#### client_subnet
|
||||
|
||||
默认情况下,将带有指定 IP 前缀的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||
|
||||
如果值是 IP 地址而不是前缀,则会自动附加 `/32` 或 `/128`。
|
||||
|
||||
将覆盖 `dns.client_subnet` 与 `servers.[].client_subnet`。
|
||||
|
||||
### reject
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "reject",
|
||||
"method": "default", // default
|
||||
"no_drop": false
|
||||
}
|
||||
```
|
||||
|
||||
`reject` 拒绝 DNS 请求。
|
||||
|
||||
#### method
|
||||
|
||||
- `default`: 返回 NXDOMAIN。
|
||||
- `drop`: 丢弃请求。
|
||||
|
||||
#### no_drop
|
||||
|
||||
如果未启用,则 30 秒内触发 50 次后,`method` 将被暂时覆盖为 `drop`。
|
||||
|
||||
当 `method` 设为 `drop` 时不可用。
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.9.0"
|
||||
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.9.0 中的更改"
|
||||
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
|
||||
!!! quote "Changes in sing-box 1.9.0"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! question "自 sing-box 1.8.0 起"
|
||||
|
||||
!!! quote "sing-box 1.9.0 中的更改"
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
---
|
||||
icon: material/delete-clock
|
||||
---
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Legacy special outbounds are deprecated and will be removed in sing-box 1.13.0, check [Migration](/migration/#migrate-legacy-special-outbounds-to-rule-actions).
|
||||
`block` outbound closes all incoming requests.
|
||||
|
||||
### Structure
|
||||
|
||||
```json F
|
||||
```json
|
||||
{
|
||||
"type": "block",
|
||||
"tag": "block"
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
---
|
||||
icon: material/delete-clock
|
||||
---
|
||||
|
||||
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||
|
||||
旧的特殊出站已被弃用,且将在 sing-box 1.13.0 中被移除,参阅 [迁移指南](/migration/#migrate-legacy-special-outbounds-to-rule-actions).
|
||||
|
||||
`block` 出站关闭所有传入请求。
|
||||
|
||||
### 结构
|
||||
@@ -19,4 +11,4 @@ icon: material/delete-clock
|
||||
|
||||
### 字段
|
||||
|
||||
无字段。
|
||||
无字段。
|
||||
@@ -1,11 +1,3 @@
|
||||
---
|
||||
icon: material/delete-clock
|
||||
---
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Legacy special outbounds are deprecated and will be removed in sing-box 1.13.0, check [Migration](/migration/#migrate-legacy-special-outbounds-to-rule-actions).
|
||||
|
||||
`dns` outbound is a internal DNS server.
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
---
|
||||
icon: material/delete-clock
|
||||
---
|
||||
|
||||
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||
|
||||
旧的特殊出站已被弃用,且将在 sing-box 1.13.0 中被移除, 参阅 [迁移指南](/migration/#migrate-legacy-special-outbounds-to-rule-actions).
|
||||
|
||||
`dns` 出站是一个内部 DNS 服务器。
|
||||
|
||||
### 结构
|
||||
|
||||
@@ -4,7 +4,7 @@ icon: material/delete-clock
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.8.0"
|
||||
|
||||
GeoIP is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
GeoIP is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
|
||||
### Structure
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ icon: material/delete-clock
|
||||
|
||||
!!! failure "已在 sing-box 1.8.0 废弃"
|
||||
|
||||
GeoIP 已废弃且将在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/zh/migration/#geoip)。
|
||||
GeoIP 已废弃且可能在不久的将来移除,参阅 [迁移指南](/zh/migration/#geoip)。
|
||||
|
||||
### 结构
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ icon: material/delete-clock
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.8.0"
|
||||
|
||||
Geosite is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
||||
Geosite is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
||||
|
||||
### Structure
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ icon: material/delete-clock
|
||||
|
||||
!!! failure "已在 sing-box 1.8.0 废弃"
|
||||
|
||||
Geosite 已废弃且将在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/zh/migration/#geosite)。
|
||||
Geosite 已废弃且可能在不久的将来移除,参阅 [迁移指南](/zh/migration/#geosite)。
|
||||
|
||||
### 结构
|
||||
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
:material-plus: [action](#action)
|
||||
:material-alert: [outbound](#outbound)
|
||||
|
||||
!!! quote "Changes in sing-box 1.10.0"
|
||||
|
||||
:material-plus: [client](#client)
|
||||
@@ -134,7 +129,6 @@ icon: material/new-box
|
||||
"rule_set_ipcidr_match_source": false,
|
||||
"rule_set_ip_cidr_match_source": false,
|
||||
"invert": false,
|
||||
"action": "route",
|
||||
"outbound": "direct"
|
||||
},
|
||||
{
|
||||
@@ -142,7 +136,6 @@ icon: material/new-box
|
||||
"mode": "and",
|
||||
"rules": [],
|
||||
"invert": false,
|
||||
"action": "route",
|
||||
"outbound": "direct"
|
||||
}
|
||||
]
|
||||
@@ -216,7 +209,7 @@ Match domain using regular expression.
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.8.0"
|
||||
|
||||
Geosite is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
||||
Geosite is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
||||
|
||||
Match geosite.
|
||||
|
||||
@@ -224,7 +217,7 @@ Match geosite.
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.8.0"
|
||||
|
||||
GeoIP is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
GeoIP is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
|
||||
Match source geoip.
|
||||
|
||||
@@ -232,7 +225,7 @@ Match source geoip.
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.8.0"
|
||||
|
||||
GeoIP is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
GeoIP is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
|
||||
Match geoip.
|
||||
|
||||
@@ -364,17 +357,11 @@ Make `ip_cidr` in rule-sets match the source IP.
|
||||
|
||||
Invert match result.
|
||||
|
||||
#### action
|
||||
#### outbound
|
||||
|
||||
==Required==
|
||||
|
||||
See [Rule Actions](../rule_action/) for details.
|
||||
|
||||
#### outbound
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Moved to [Rule Action](../rule_action#route).
|
||||
Tag of the target outbound.
|
||||
|
||||
### Logical Fields
|
||||
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
|
||||
:material-plus: [action](#action)
|
||||
:material-alert: [outbound](#outbound)
|
||||
|
||||
!!! quote "sing-box 1.10.0 中的更改"
|
||||
|
||||
:material-plus: [client](#client)
|
||||
@@ -132,7 +127,6 @@ icon: material/new-box
|
||||
"rule_set_ipcidr_match_source": false,
|
||||
"rule_set_ip_cidr_match_source": false,
|
||||
"invert": false,
|
||||
"action": "route",
|
||||
"outbound": "direct"
|
||||
},
|
||||
{
|
||||
@@ -140,7 +134,6 @@ icon: material/new-box
|
||||
"mode": "and",
|
||||
"rules": [],
|
||||
"invert": false,
|
||||
"action": "route",
|
||||
"outbound": "direct"
|
||||
}
|
||||
]
|
||||
@@ -362,17 +355,11 @@ icon: material/new-box
|
||||
|
||||
反选匹配结果。
|
||||
|
||||
#### action
|
||||
#### outbound
|
||||
|
||||
==必填==
|
||||
|
||||
参阅 [规则行动](../rule_action/)。
|
||||
|
||||
#### outbound
|
||||
|
||||
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||
|
||||
已移动到 [规则行动](../rule_action#route).
|
||||
目标出站的标签。
|
||||
|
||||
### 逻辑字段
|
||||
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
# Rule Action
|
||||
|
||||
!!! question "Since sing-box 1.11.0"
|
||||
|
||||
## Final actions
|
||||
|
||||
### route
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route", // default
|
||||
"outbound": ""
|
||||
}
|
||||
```
|
||||
|
||||
`route` inherits the classic rule behavior of routing connection to the specified outbound.
|
||||
|
||||
#### outbound
|
||||
|
||||
==Required==
|
||||
|
||||
Tag of target outbound.
|
||||
|
||||
### route-options
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route-options",
|
||||
"udp_disable_domain_unmapping": false,
|
||||
"udp_connect": false
|
||||
}
|
||||
```
|
||||
|
||||
`route-options` set options for routing.
|
||||
|
||||
#### udp_disable_domain_unmapping
|
||||
|
||||
If enabled, for UDP proxy requests addressed to a domain,
|
||||
the original packet address will be sent in the response instead of the mapped domain.
|
||||
|
||||
This option is used for compatibility with clients that
|
||||
do not support receiving UDP packets with domain addresses, such as Surge.
|
||||
|
||||
#### udp_connect
|
||||
|
||||
If enabled, attempts to connect UDP connection to the destination instead of listen.
|
||||
|
||||
### reject
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "reject",
|
||||
"method": "default", // default
|
||||
"no_drop": false
|
||||
}
|
||||
```
|
||||
|
||||
`reject` reject connections
|
||||
|
||||
The specified method is used for reject tun connections if `sniff` action has not been performed yet.
|
||||
|
||||
For non-tun connections and already established connections, will just be closed.
|
||||
|
||||
#### method
|
||||
|
||||
- `default`: Reply with TCP RST for TCP connections, and ICMP port unreachable for UDP packets.
|
||||
- `drop`: Drop packets.
|
||||
|
||||
#### 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.
|
||||
|
||||
### hijack-dns
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "hijack-dns"
|
||||
}
|
||||
```
|
||||
|
||||
`hijack-dns` hijack DNS requests to the sing-box DNS module.
|
||||
|
||||
## Non-final actions
|
||||
|
||||
### sniff
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "sniff",
|
||||
"sniffer": [],
|
||||
"timeout": ""
|
||||
}
|
||||
```
|
||||
|
||||
`sniff` performs protocol sniffing on connections.
|
||||
|
||||
For deprecated `inbound.sniff` options, it is considered to `sniff()` performed before routing.
|
||||
|
||||
#### sniffer
|
||||
|
||||
Enabled sniffers.
|
||||
|
||||
All sniffers enabled by default.
|
||||
|
||||
Available protocol values an be found on in [Protocol Sniff](../sniff/)
|
||||
|
||||
#### timeout
|
||||
|
||||
Timeout for sniffing.
|
||||
|
||||
`300ms` is used by default.
|
||||
|
||||
### resolve
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "resolve",
|
||||
"strategy": "",
|
||||
"server": ""
|
||||
}
|
||||
```
|
||||
|
||||
`resolve` resolve request destination from domain to IP addresses.
|
||||
|
||||
#### strategy
|
||||
|
||||
DNS resolution strategy, available values are: `prefer_ipv4`, `prefer_ipv6`, `ipv4_only`, `ipv6_only`.
|
||||
|
||||
`dns.strategy` will be used by default.
|
||||
|
||||
#### server
|
||||
|
||||
Specifies DNS server tag to use instead of selecting through DNS routing.
|
||||
@@ -1,136 +0,0 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
# 规则动作
|
||||
|
||||
!!! question "自 sing-box 1.11.0 起"
|
||||
|
||||
## 最终动作
|
||||
|
||||
### route
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route", // 默认
|
||||
"outbound": "",
|
||||
"udp_disable_domain_unmapping": false
|
||||
}
|
||||
```
|
||||
|
||||
`route` 继承了将连接路由到指定出站的经典规则动作。
|
||||
|
||||
#### outbound
|
||||
|
||||
==必填==
|
||||
|
||||
目标出站的标签。
|
||||
|
||||
### route-options
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route-options",
|
||||
"udp_disable_domain_unmapping": false,
|
||||
"udp_connect": false
|
||||
}
|
||||
```
|
||||
|
||||
#### udp_disable_domain_unmapping
|
||||
|
||||
如果启用,对于地址为域的 UDP 代理请求,将在响应中发送原始包地址而不是映射的域。
|
||||
|
||||
此选项用于兼容不支持接收带有域地址的 UDP 包的客户端,如 Surge。
|
||||
|
||||
#### udp_connect
|
||||
|
||||
如果启用,将尝试将 UDP 连接 connect 到目标而不是 listen。
|
||||
|
||||
### reject
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "reject",
|
||||
"method": "default", // 默认
|
||||
"no_drop": false
|
||||
}
|
||||
```
|
||||
|
||||
`reject` 拒绝连接。
|
||||
|
||||
如果尚未执行 `sniff` 操作,则将使用指定方法拒绝 tun 连接。
|
||||
|
||||
对于非 tun 连接和已建立的连接,将直接关闭。
|
||||
|
||||
#### method
|
||||
|
||||
- `default`: 对于 TCP 连接回复 RST,对于 UDP 包回复 ICMP 端口不可达。
|
||||
- `drop`: 丢弃数据包。
|
||||
|
||||
#### no_drop
|
||||
|
||||
如果未启用,则 30 秒内触发 50 次后,`method` 将被暂时覆盖为 `drop`。
|
||||
|
||||
当 `method` 设为 `drop` 时不可用。
|
||||
|
||||
### hijack-dns
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "hijack-dns"
|
||||
}
|
||||
```
|
||||
|
||||
`hijack-dns` 劫持 DNS 请求至 sing-box DNS 模块。
|
||||
|
||||
## 非最终动作
|
||||
|
||||
### sniff
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "sniff",
|
||||
"sniffer": [],
|
||||
"timeout": ""
|
||||
}
|
||||
```
|
||||
|
||||
`sniff` 对连接执行协议嗅探。
|
||||
|
||||
对于已弃用的 `inbound.sniff` 选项,被视为在路由之前执行的 `sniff`。
|
||||
|
||||
#### sniffer
|
||||
|
||||
启用的探测器。
|
||||
|
||||
默认启用所有探测器。
|
||||
|
||||
可用的协议值可以在 [协议嗅探](../sniff/) 中找到。
|
||||
|
||||
#### timeout
|
||||
|
||||
探测超时时间。
|
||||
|
||||
默认使用 300ms。
|
||||
|
||||
### resolve
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "resolve",
|
||||
"strategy": "",
|
||||
"server": ""
|
||||
}
|
||||
```
|
||||
|
||||
`resolve` 将请求的目标从域名解析为 IP 地址。
|
||||
|
||||
#### strategy
|
||||
|
||||
DNS 解析策略,可用值有:`prefer_ipv4`、`prefer_ipv6`、`ipv4_only`、`ipv6_only`。
|
||||
|
||||
默认使用 `dns.strategy`。
|
||||
|
||||
#### server
|
||||
|
||||
指定要使用的 DNS 服务器的标签,而不是通过 DNS 路由进行选择。
|
||||
@@ -1,15 +1,3 @@
|
||||
---
|
||||
icon: material/delete-clock
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
:material-delete-clock: [sniff](#sniff)
|
||||
:material-delete-clock: [sniff_override_destination](#sniff_override_destination)
|
||||
:material-delete-clock: [sniff_timeout](#sniff_timeout)
|
||||
:material-delete-clock: [domain_strategy](#domain_strategy)
|
||||
:material-delete-clock: [udp_disable_domain_unmapping](#udp_disable_domain_unmapping)
|
||||
|
||||
### Structure
|
||||
|
||||
```json
|
||||
@@ -80,40 +68,24 @@ Requires target inbound support, see [Injectable](/configuration/inbound/#fields
|
||||
|
||||
#### sniff
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Inbound fields are deprecated and will be removed in sing-box 1.13.0, check [Migration](/migration/#migrate-legacy-inbound-fields-to-rule-actions).
|
||||
|
||||
Enable sniffing.
|
||||
|
||||
See [Protocol Sniff](/configuration/route/sniff/) for details.
|
||||
|
||||
#### sniff_override_destination
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Inbound fields are deprecated and will be removed in sing-box 1.13.0.
|
||||
|
||||
Override the connection destination address with the sniffed domain.
|
||||
|
||||
If the domain name is invalid (like tor), this will not work.
|
||||
|
||||
#### sniff_timeout
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Inbound fields are deprecated and will be removed in sing-box 1.13.0, check [Migration](/migration/#migrate-legacy-inbound-fields-to-rule-actions).
|
||||
|
||||
Timeout for sniffing.
|
||||
|
||||
`300ms` is used by default.
|
||||
300ms is used by default.
|
||||
|
||||
#### domain_strategy
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Inbound fields are deprecated and will be removed in sing-box 1.13.0, check [Migration](/migration/#migrate-legacy-inbound-fields-to-rule-actions).
|
||||
|
||||
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
|
||||
|
||||
If set, the requested domain name will be resolved to IP before routing.
|
||||
@@ -122,10 +94,6 @@ If `sniff_override_destination` is in effect, its value will be taken as a fallb
|
||||
|
||||
#### udp_disable_domain_unmapping
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Inbound fields are deprecated and will be removed in sing-box 1.13.0, check [Migration](/migration/#migrate-legacy-inbound-fields-to-rule-actions).
|
||||
|
||||
If enabled, for UDP proxy requests addressed to a domain,
|
||||
the original packet address will be sent in the response instead of the mapped domain.
|
||||
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
---
|
||||
icon: material/delete-clock
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
|
||||
:material-delete-clock: [sniff](#sniff)
|
||||
:material-delete-clock: [sniff_override_destination](#sniff_override_destination)
|
||||
:material-delete-clock: [sniff_timeout](#sniff_timeout)
|
||||
:material-delete-clock: [domain_strategy](#domain_strategy)
|
||||
:material-delete-clock: [udp_disable_domain_unmapping](#udp_disable_domain_unmapping)
|
||||
|
||||
### 结构
|
||||
|
||||
```json
|
||||
@@ -81,40 +69,24 @@ UDP NAT 过期时间,以秒为单位。
|
||||
|
||||
#### sniff
|
||||
|
||||
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||
|
||||
入站字段已废弃且将在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/migration/#migrate-legacy-inbound-fields-to-rule-actions).
|
||||
|
||||
启用协议探测。
|
||||
|
||||
参阅 [协议探测](/zh/configuration/route/sniff/)
|
||||
|
||||
#### sniff_override_destination
|
||||
|
||||
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||
|
||||
入站字段已废弃且将在 sing-box 1.12.0 中被移除。
|
||||
|
||||
用探测出的域名覆盖连接目标地址。
|
||||
|
||||
如果域名无效(如 Tor),将不生效。
|
||||
|
||||
#### sniff_timeout
|
||||
|
||||
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||
|
||||
入站字段已废弃且将在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/migration/#migrate-legacy-inbound-fields-to-rule-actions).
|
||||
|
||||
探测超时时间。
|
||||
|
||||
默认使用 300ms。
|
||||
|
||||
#### domain_strategy
|
||||
|
||||
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||
|
||||
入站字段已废弃且将在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/migration/#migrate-legacy-inbound-fields-to-rule-actions).
|
||||
|
||||
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`。
|
||||
|
||||
如果设置,请求的域名将在路由之前解析为 IP。
|
||||
@@ -123,10 +95,6 @@ UDP NAT 过期时间,以秒为单位。
|
||||
|
||||
#### udp_disable_domain_unmapping
|
||||
|
||||
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||
|
||||
入站字段已废弃且将在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/migration/#migrate-legacy-inbound-fields-to-rule-actions).
|
||||
|
||||
如果启用,对于地址为域的 UDP 代理请求,将在响应中发送原始包地址而不是映射的域。
|
||||
|
||||
此选项用于兼容不支持接收带有域地址的 UDP 包的客户端,如 Surge。
|
||||
|
||||
@@ -4,32 +4,6 @@ icon: material/delete-alert
|
||||
|
||||
# Deprecated Feature List
|
||||
|
||||
## 1.11.0
|
||||
|
||||
#### Legacy special outbounds
|
||||
|
||||
Legacy special outbounds (`block` / `dns`) are deprecated
|
||||
and can be replaced by rule actions,
|
||||
check [Migration](../migration/#migrate-legacy-special-outbounds-to-rule-actions).
|
||||
|
||||
Old fields will be removed in sing-box 1.13.0.
|
||||
|
||||
#### Legacy inbound fields
|
||||
|
||||
Legacy inbound fields (`inbound.<sniff/domain_strategy/...>` are deprecated
|
||||
and can be replaced by rule actions,
|
||||
check [Migration](../migration/#migrate-legacy-inbound-fields-to-rule-actions).
|
||||
|
||||
Old fields will be removed in sing-box 1.13.0.
|
||||
|
||||
#### Legacy DNS route options
|
||||
|
||||
Legacy DNS route options (`disable_cache`, `rewrite_ttl`, `client_subnet`) are deprecated
|
||||
and can be replaced by rule actions,
|
||||
check [Migration](../migration/#migrate-legacy-dns-route-options-to-rule-actions).
|
||||
|
||||
Old fields will be removed in sing-box 1.12.0.
|
||||
|
||||
## 1.10.0
|
||||
|
||||
#### TUN address fields are merged
|
||||
@@ -38,7 +12,7 @@ Old fields will be removed in sing-box 1.12.0.
|
||||
`inet4_route_address` and `inet6_route_address` are merged into `route_address`,
|
||||
`inet4_route_exclude_address` and `inet6_route_exclude_address` are merged into `route_exclude_address`.
|
||||
|
||||
Old fields will be removed in sing-box 1.12.0.
|
||||
Old fields are deprecated and will be removed in sing-box 1.11.0.
|
||||
|
||||
#### Match source rule items are renamed
|
||||
|
||||
@@ -58,7 +32,7 @@ check [Migration](/migration/#migrate-cache-file-from-clash-api-to-independent-o
|
||||
|
||||
#### GeoIP
|
||||
|
||||
GeoIP is deprecated and will be removed in sing-box 1.12.0.
|
||||
GeoIP is deprecated and may be removed in the future.
|
||||
|
||||
The maxmind GeoIP National Database, as an IP classification database,
|
||||
is not entirely suitable for traffic bypassing,
|
||||
@@ -69,7 +43,7 @@ check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
|
||||
#### Geosite
|
||||
|
||||
Geosite is deprecated and will be removed in sing-box 1.12.0.
|
||||
Geosite is deprecated and may be removed in the future.
|
||||
|
||||
Geosite, the `domain-list-community` project maintained by V2Ray as an early traffic bypassing solution,
|
||||
suffers from a number of problems, including lack of maintenance, inaccurate rules, and difficult management.
|
||||
|
||||
@@ -4,29 +4,6 @@ icon: material/delete-alert
|
||||
|
||||
# 废弃功能列表
|
||||
|
||||
## 1.11.0
|
||||
|
||||
#### 旧的特殊出站
|
||||
|
||||
旧的特殊出站(`block` / `dns`)已废弃且可以通过规则动作替代,
|
||||
参阅 [迁移指南](/migration/#migrate-legacy-special-outbounds-to-rule-actions)。
|
||||
|
||||
旧字段将在 sing-box 1.13.0 中被移除。
|
||||
|
||||
#### 旧的入站字段
|
||||
|
||||
旧的入站字段(`inbound.<sniff/domain_strategy/...>`)已废弃且可以通过规则动作替代,
|
||||
参阅 [迁移指南](/migration/#migrate-legacy-inbound-fields-to-rule-actions)。
|
||||
|
||||
旧字段将在 sing-box 1.13.0 中被移除。
|
||||
|
||||
#### 旧的 DNS 路由参数
|
||||
|
||||
旧的 DNS 路由参数(`disable_cache`、`rewrite_ttl`、`client_subnet`)已废弃且可以通过规则动作替代,
|
||||
参阅 [迁移指南](/migration/#migrate-legacy-dns-route-options-to-rule-actions)。
|
||||
|
||||
旧字段将在 sing-box 1.12.0 中被移除。
|
||||
|
||||
## 1.10.0
|
||||
|
||||
#### Match source 规则项已重命名
|
||||
@@ -40,7 +17,7 @@ icon: material/delete-alert
|
||||
`inet4_route_address` 和 `inet6_route_address` 已合并为 `route_address`,
|
||||
`inet4_route_exclude_address` 和 `inet6_route_exclude_address` 已合并为 `route_exclude_address`。
|
||||
|
||||
旧字段将在 sing-box 1.11.0 中被移除。
|
||||
旧字段已废弃,且将在 sing-box 1.11.0 中被移除。
|
||||
|
||||
#### 移除对 go1.18 和 go1.19 的支持
|
||||
|
||||
@@ -55,7 +32,7 @@ Clash API 中的 `cache_file` 及相关功能已废弃且已迁移到独立的 `
|
||||
|
||||
#### GeoIP
|
||||
|
||||
GeoIP 已废弃且将在 sing-box 1.12.0 中被移除。
|
||||
GeoIP 已废弃且可能在不久的将来移除。
|
||||
|
||||
maxmind GeoIP 国家数据库作为 IP 分类数据库,不完全适合流量绕过,
|
||||
且现有的实现均存在内存使用大与管理困难的问题。
|
||||
@@ -65,7 +42,7 @@ sing-box 1.8.0 引入了[规则集](/configuration/rule-set/),
|
||||
|
||||
#### Geosite
|
||||
|
||||
Geosite 已废弃且将在 sing-box 1.12.0 中被移除。
|
||||
Geosite 已废弃且可能在不久的将来移除。
|
||||
|
||||
Geosite,即由 V2Ray 维护的 domain-list-community 项目,作为早期流量绕过解决方案,
|
||||
存在着包括缺少维护、规则不准确和管理困难内的大量问题。
|
||||
|
||||
@@ -2,212 +2,6 @@
|
||||
icon: material/arrange-bring-forward
|
||||
---
|
||||
|
||||
## 1.11.0
|
||||
|
||||
### Migrate legacy special outbounds to rule actions
|
||||
|
||||
Legacy special outbounds are deprecated and can be replaced by rule actions.
|
||||
|
||||
!!! info "References"
|
||||
|
||||
[Rule Action](/configuration/route/rule_action/) /
|
||||
[Block](/configuration/outbound/block/) /
|
||||
[DNS](/configuration/outbound/dns)
|
||||
|
||||
=== "Block"
|
||||
|
||||
=== ":material-card-remove: Deprecated"
|
||||
|
||||
```json
|
||||
{
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "block",
|
||||
"tag": "block"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
...,
|
||||
|
||||
"outbound": "block"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-card-multiple: New"
|
||||
|
||||
```json
|
||||
{
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
...,
|
||||
|
||||
"action": "reject"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "DNS"
|
||||
|
||||
=== ":material-card-remove: Deprecated"
|
||||
|
||||
```json
|
||||
{
|
||||
"inbound": [
|
||||
{
|
||||
...,
|
||||
|
||||
"sniff": true
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"tag": "dns",
|
||||
"type": "dns"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"protocol": "dns",
|
||||
"outbound": "dns"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-card-multiple: New"
|
||||
|
||||
```json
|
||||
{
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"action": "sniff"
|
||||
},
|
||||
{
|
||||
"protocol": "dns",
|
||||
"action": "dns-hijack"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Migrate legacy inbound fields to rule actions
|
||||
|
||||
Inbound fields are deprecated and can be replaced by rule actions.
|
||||
|
||||
!!! info "References"
|
||||
|
||||
[Listen Fields](/configuration/inbound/listen/) /
|
||||
[Rule](/configuration/route/rule/) /
|
||||
[Rule Action](/configuration/route/rule_action/) /
|
||||
[DNS Rule](/configuration/dns/rule/) /
|
||||
[DNS Rule Action](/configuration/dns/rule_action/)
|
||||
|
||||
=== ":material-card-remove: Deprecated"
|
||||
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"sniff": true,
|
||||
"sniff_timeout": "1s",
|
||||
"domain_strategy": "prefer_ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-card-multiple: New"
|
||||
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"tag": "in"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"inbound": "in",
|
||||
"action": "resolve",
|
||||
"strategy": "prefer_ipv4"
|
||||
},
|
||||
{
|
||||
"inbound": "in",
|
||||
"action": "sniff",
|
||||
"timeout": "1s"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Migrate legacy DNS route options to rule actions
|
||||
|
||||
Legacy DNS route options are deprecated and can be replaced by rule actions.
|
||||
|
||||
!!! info "References"
|
||||
|
||||
[DNS Rule](/configuration/dns/rule/) /
|
||||
[DNS Rule Action](/configuration/dns/rule_action/)
|
||||
|
||||
=== ":material-card-remove: Deprecated"
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"rules": [
|
||||
{
|
||||
...,
|
||||
|
||||
"server": "local",
|
||||
"disable_cache": true,
|
||||
"rewrite_ttl": 600,
|
||||
"client_subnet": "1.1.1.1/24"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-card-multiple: New"
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"rules": [
|
||||
{
|
||||
...,
|
||||
|
||||
"action": "route-options",
|
||||
"disable_cache": true,
|
||||
"rewrite_ttl": 600,
|
||||
"client_subnet": "1.1.1.1/24"
|
||||
},
|
||||
{
|
||||
...,
|
||||
|
||||
"server": "local"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 1.10.0
|
||||
|
||||
### TUN address fields are merged
|
||||
@@ -216,6 +10,8 @@ Legacy DNS route options are deprecated and can be replaced by rule actions.
|
||||
`inet4_route_address` and `inet6_route_address` are merged into `route_address`,
|
||||
`inet4_route_exclude_address` and `inet6_route_exclude_address` are merged into `route_exclude_address`.
|
||||
|
||||
Old fields are deprecated and will be removed in sing-box 1.11.0.
|
||||
|
||||
!!! info "References"
|
||||
|
||||
[TUN](/configuration/inbound/tun/)
|
||||
|
||||
@@ -2,212 +2,6 @@
|
||||
icon: material/arrange-bring-forward
|
||||
---
|
||||
|
||||
## 1.11.0
|
||||
|
||||
### 迁移旧的特殊出站到规则动作
|
||||
|
||||
旧的特殊出站已被弃用,且可以被规则动作替代。
|
||||
|
||||
!!! info "参考"
|
||||
|
||||
[规则动作](/zh/configuration/route/rule_action/) /
|
||||
[Block](/zh/configuration/outbound/block/) /
|
||||
[DNS](/zh/configuration/outbound/dns)
|
||||
|
||||
=== "Block"
|
||||
|
||||
=== ":material-card-remove: 弃用的"
|
||||
|
||||
```json
|
||||
{
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "block",
|
||||
"tag": "block"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
...,
|
||||
|
||||
"outbound": "block"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-card-multiple: 新的"
|
||||
|
||||
```json
|
||||
{
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
...,
|
||||
|
||||
"action": "reject"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "DNS"
|
||||
|
||||
=== ":material-card-remove: 弃用的"
|
||||
|
||||
```json
|
||||
{
|
||||
"inbound": [
|
||||
{
|
||||
...,
|
||||
|
||||
"sniff": true
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"tag": "dns",
|
||||
"type": "dns"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"protocol": "dns",
|
||||
"outbound": "dns"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-card-multiple: 新的"
|
||||
|
||||
```json
|
||||
{
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"action": "sniff"
|
||||
},
|
||||
{
|
||||
"protocol": "dns",
|
||||
"action": "dns-hijack"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 迁移旧的入站字段到规则动作
|
||||
|
||||
入站选项已被弃用,且可以被规则动作替代。
|
||||
|
||||
!!! info "参考"
|
||||
|
||||
[监听字段](/zh/configuration/shared/listen/) /
|
||||
[规则](/zh/configuration/route/rule/) /
|
||||
[规则动作](/zh/configuration/route/rule_action/) /
|
||||
[DNS 规则](/zh/configuration/dns/rule/) /
|
||||
[DNS 规则动作](/zh/configuration/dns/rule_action/)
|
||||
|
||||
=== ":material-card-remove: 弃用的"
|
||||
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"sniff": true,
|
||||
"sniff_timeout": "1s",
|
||||
"domain_strategy": "prefer_ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-card-multiple: New"
|
||||
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"tag": "in"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"inbound": "in",
|
||||
"action": "resolve",
|
||||
"strategy": "prefer_ipv4"
|
||||
},
|
||||
{
|
||||
"inbound": "in",
|
||||
"action": "sniff",
|
||||
"timeout": "1s"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 迁移旧的 DNS 路由选项到规则动作
|
||||
|
||||
旧的 DNS 路由选项已被弃用,且可以被规则动作替代。
|
||||
|
||||
!!! info "参考"
|
||||
|
||||
[DNS 规则](/zh/configuration/dns/rule/) /
|
||||
[DNS 规则动作](/zh/configuration/dns/rule_action/)
|
||||
|
||||
=== ":material-card-remove: 弃用的"
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"rules": [
|
||||
{
|
||||
...,
|
||||
|
||||
"server": "local",
|
||||
"disable_cache": true,
|
||||
"rewrite_ttl": 600,
|
||||
"client_subnet": "1.1.1.1/24"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-card-multiple: 新的"
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"rules": [
|
||||
{
|
||||
...,
|
||||
|
||||
"action": "route-options",
|
||||
"disable_cache": true,
|
||||
"rewrite_ttl": 600,
|
||||
"client_subnet": "1.1.1.1/24"
|
||||
},
|
||||
{
|
||||
...,
|
||||
|
||||
"server": "local"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 1.10.0
|
||||
|
||||
### TUN 地址字段已合并
|
||||
|
||||
@@ -78,36 +78,9 @@ var OptionTUNAddressX = Note{
|
||||
MigrationLink: "https://sing-box.sagernet.org/migration/#tun-address-fields-are-merged",
|
||||
}
|
||||
|
||||
var OptionSpecialOutbounds = Note{
|
||||
Name: "special-outbounds",
|
||||
Description: "legacy special outbounds",
|
||||
DeprecatedVersion: "1.11.0",
|
||||
ScheduledVersion: "1.13.0",
|
||||
MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-legacy-special-outbounds-to-rule-actions",
|
||||
}
|
||||
|
||||
var OptionInboundOptions = Note{
|
||||
Name: "inbound-options",
|
||||
Description: "legacy inbound fields",
|
||||
DeprecatedVersion: "1.11.0",
|
||||
ScheduledVersion: "1.13.0",
|
||||
MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-legacy-special-outbounds-to-rule-actions",
|
||||
}
|
||||
|
||||
var OptionLegacyDNSRouteOptions = Note{
|
||||
Name: "legacy-dns-route-options",
|
||||
Description: "legacy dns route options",
|
||||
DeprecatedVersion: "1.11.0",
|
||||
ScheduledVersion: "1.12.0",
|
||||
MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-legacy-dns-route-options-to-rule-actions",
|
||||
}
|
||||
|
||||
var Options = []Note{
|
||||
OptionBadMatchSource,
|
||||
OptionGEOIP,
|
||||
OptionGEOSITE,
|
||||
OptionTUNAddressX,
|
||||
OptionSpecialOutbounds,
|
||||
OptionInboundOptions,
|
||||
OptionLegacyDNSRouteOptions,
|
||||
}
|
||||
|
||||
@@ -7,23 +7,15 @@ import (
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
type stderrManager struct {
|
||||
logger logger.Logger
|
||||
reported map[string]bool
|
||||
type envManager struct {
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
func NewStderrManager(logger logger.Logger) Manager {
|
||||
return &stderrManager{
|
||||
logger: logger,
|
||||
reported: make(map[string]bool),
|
||||
}
|
||||
func NewEnvManager(logger logger.Logger) Manager {
|
||||
return &envManager{logger: logger}
|
||||
}
|
||||
|
||||
func (f *stderrManager) ReportDeprecated(feature Note) {
|
||||
if f.reported[feature.Name] {
|
||||
return
|
||||
}
|
||||
f.reported[feature.Name] = true
|
||||
func (f *envManager) ReportDeprecated(feature Note) {
|
||||
if !feature.Impending() {
|
||||
f.logger.Warn(feature.MessageWithLink())
|
||||
return
|
||||
@@ -2,7 +2,6 @@ package deprecated
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
@@ -14,7 +13,6 @@ type Manager interface {
|
||||
func Report(ctx context.Context, feature Note) {
|
||||
manager := service.FromContext[Manager](ctx)
|
||||
if manager == nil {
|
||||
debug.PrintStack()
|
||||
return
|
||||
}
|
||||
manager.ReportDeprecated(feature)
|
||||
|
||||
@@ -20,6 +20,7 @@ type CommandClient struct {
|
||||
type CommandClientOptions struct {
|
||||
Command int32
|
||||
StatusInterval int64
|
||||
IsMainClient bool
|
||||
}
|
||||
|
||||
type CommandClientHandler interface {
|
||||
@@ -28,6 +29,7 @@ type CommandClientHandler interface {
|
||||
ClearLogs()
|
||||
WriteLogs(messageList StringIterator)
|
||||
WriteStatus(message *StatusMessage)
|
||||
OpenURL(url string)
|
||||
WriteGroups(message OutboundGroupIterator)
|
||||
InitializeClashMode(modeList StringIterator, currentMode string)
|
||||
UpdateClashMode(newMode string)
|
||||
@@ -91,9 +93,13 @@ func (c *CommandClient) Connect() error {
|
||||
c.handler.Connected()
|
||||
go c.handleLogConn(conn)
|
||||
case CommandStatus:
|
||||
err = binary.Write(conn, binary.BigEndian, c.options.IsMainClient)
|
||||
if err != nil {
|
||||
return E.Cause(err, "write is main client")
|
||||
}
|
||||
err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
|
||||
if err != nil {
|
||||
return E.Cause(err, "write interval")
|
||||
return E.Cause(err, "write header")
|
||||
}
|
||||
c.handler.Connected()
|
||||
go c.handleStatusConn(conn)
|
||||
|
||||
40
experimental/libbox/command_event.go
Normal file
40
experimental/libbox/command_event.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
)
|
||||
|
||||
type myEvent interface {
|
||||
writeTo(writer varbin.Writer)
|
||||
}
|
||||
|
||||
func readEvent(reader varbin.Reader) (myEvent, error) {
|
||||
eventType, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch eventType {
|
||||
case eventTypeEmpty:
|
||||
return nil, nil
|
||||
case eventTypeOpenURL:
|
||||
url, err := varbin.ReadValue[string](reader, binary.BigEndian)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &eventOpenURL{URL: url}, nil
|
||||
default:
|
||||
return nil, E.New("unknown event type: ", eventType)
|
||||
}
|
||||
}
|
||||
|
||||
type eventOpenURL struct {
|
||||
URL string
|
||||
}
|
||||
|
||||
func (e *eventOpenURL) writeTo(writer varbin.Writer) {
|
||||
writer.WriteByte(eventTypeOpenURL)
|
||||
varbin.Write(writer, binary.BigEndian, e.URL)
|
||||
}
|
||||
@@ -33,6 +33,7 @@ type CommandServer struct {
|
||||
urlTestUpdate chan struct{}
|
||||
modeUpdate chan struct{}
|
||||
logReset chan struct{}
|
||||
events chan myEvent
|
||||
|
||||
closedConnections []Connection
|
||||
}
|
||||
@@ -52,6 +53,7 @@ func NewCommandServer(handler CommandServerHandler, maxLines int32) *CommandServ
|
||||
urlTestUpdate: make(chan struct{}, 1),
|
||||
modeUpdate: make(chan struct{}, 1),
|
||||
logReset: make(chan struct{}, 1),
|
||||
events: make(chan myEvent, 8),
|
||||
}
|
||||
server.observer = observable.NewObserver[string](server.subscriber, 64)
|
||||
return server
|
||||
@@ -61,6 +63,12 @@ func (s *CommandServer) SetService(newService *BoxService) {
|
||||
if newService != nil {
|
||||
service.PtrFromContext[urltest.HistoryStorage](newService.ctx).SetHook(s.urlTestUpdate)
|
||||
newService.instance.Router().ClashServer().(*clashapi.Server).SetModeUpdateHook(s.modeUpdate)
|
||||
newService.platformInterface.openURLFunc = func(url string) {
|
||||
select {
|
||||
case s.events <- &eventOpenURL{URL: url}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
s.service = newService
|
||||
s.notifyURLTestUpdate()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"runtime"
|
||||
@@ -9,9 +10,15 @@ import (
|
||||
"github.com/sagernet/sing-box/common/conntrack"
|
||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/memory"
|
||||
)
|
||||
|
||||
const (
|
||||
eventTypeEmpty byte = iota
|
||||
eventTypeOpenURL
|
||||
)
|
||||
|
||||
type StatusMessage struct {
|
||||
Memory int64
|
||||
Goroutines int32
|
||||
@@ -44,31 +51,73 @@ func (s *CommandServer) readStatus() StatusMessage {
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleStatusConn(conn net.Conn) error {
|
||||
var isMainClient bool
|
||||
err := binary.Read(conn, binary.BigEndian, &isMainClient)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read is main client")
|
||||
}
|
||||
var interval int64
|
||||
err := binary.Read(conn, binary.BigEndian, &interval)
|
||||
err = binary.Read(conn, binary.BigEndian, &interval)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read interval")
|
||||
}
|
||||
ticker := time.NewTicker(time.Duration(interval))
|
||||
defer ticker.Stop()
|
||||
ctx := connKeepAlive(conn)
|
||||
for {
|
||||
err = binary.Write(conn, binary.BigEndian, s.readStatus())
|
||||
if err != nil {
|
||||
return err
|
||||
writer := std_bufio.NewWriter(conn)
|
||||
if isMainClient {
|
||||
for {
|
||||
writer.WriteByte(eventTypeEmpty)
|
||||
err = binary.Write(conn, binary.BigEndian, s.readStatus())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writer.Flush()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-ticker.C:
|
||||
case event := <-s.events:
|
||||
event.writeTo(writer)
|
||||
writer.Flush()
|
||||
}
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-ticker.C:
|
||||
} else {
|
||||
for {
|
||||
err = binary.Write(conn, binary.BigEndian, s.readStatus())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writer.Flush()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-ticker.C:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommandClient) handleStatusConn(conn net.Conn) {
|
||||
reader := std_bufio.NewReader(conn)
|
||||
for {
|
||||
if c.options.IsMainClient {
|
||||
rawEvent, err := readEvent(reader)
|
||||
if err != nil {
|
||||
c.handler.Disconnected(err.Error())
|
||||
return
|
||||
}
|
||||
switch event := rawEvent.(type) {
|
||||
case *eventOpenURL:
|
||||
c.handler.OpenURL(event.URL)
|
||||
continue
|
||||
case nil:
|
||||
default:
|
||||
panic(F.ToString("unexpected event type: ", event))
|
||||
}
|
||||
}
|
||||
var message StatusMessage
|
||||
err := binary.Read(conn, binary.BigEndian, &message)
|
||||
err := binary.Read(reader, binary.BigEndian, &message)
|
||||
if err != nil {
|
||||
c.handler.Disconnected(err.Error())
|
||||
return
|
||||
|
||||
@@ -30,12 +30,11 @@ func parseConfig(ctx context.Context, configContent string) (option.Options, err
|
||||
}
|
||||
|
||||
func CheckConfig(configContent string) error {
|
||||
ctx := box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry())
|
||||
options, err := parseConfig(ctx, configContent)
|
||||
options, err := parseConfig(box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry()), configContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx = service.ContextWith[platform.Interface](ctx, (*platformInterfaceStub)(nil))
|
||||
instance, err := box.New(box.Options{
|
||||
@@ -58,7 +57,7 @@ func (s *platformInterfaceStub) UsePlatformAutoDetectInterfaceControl() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *platformInterfaceStub) AutoDetectInterfaceControl(fd int) error {
|
||||
func (s *platformInterfaceStub) AutoDetectInterfaceControl() control.Func {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -138,8 +137,7 @@ func (s *interfaceMonitorStub) RegisterCallback(callback tun.DefaultInterfaceUpd
|
||||
func (s *interfaceMonitorStub) UnregisterCallback(element *list.Element[tun.DefaultInterfaceUpdateCallback]) {
|
||||
}
|
||||
|
||||
func (s *platformInterfaceStub) SendNotification(notification *platform.Notification) error {
|
||||
return nil
|
||||
func (s *platformInterfaceStub) OpenURL(url string) {
|
||||
}
|
||||
|
||||
func FormatConfig(configContent string) (string, error) {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||
"github.com/sagernet/sing/common"
|
||||
)
|
||||
|
||||
var _ deprecated.Manager = (*deprecatedManager)(nil)
|
||||
@@ -17,7 +16,7 @@ type deprecatedManager struct {
|
||||
func (m *deprecatedManager) ReportDeprecated(feature deprecated.Note) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.features = common.Uniq(append(m.features, feature))
|
||||
m.features = append(m.features, feature)
|
||||
}
|
||||
|
||||
func (m *deprecatedManager) Get() []deprecated.Note {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// copied from net.linkFlags
|
||||
func linkFlags(rawFlags uint32) net.Flags {
|
||||
var f net.Flags
|
||||
if rawFlags&syscall.IFF_UP != 0 {
|
||||
f |= net.FlagUp
|
||||
}
|
||||
if rawFlags&syscall.IFF_RUNNING != 0 {
|
||||
f |= net.FlagRunning
|
||||
}
|
||||
if rawFlags&syscall.IFF_BROADCAST != 0 {
|
||||
f |= net.FlagBroadcast
|
||||
}
|
||||
if rawFlags&syscall.IFF_LOOPBACK != 0 {
|
||||
f |= net.FlagLoopback
|
||||
}
|
||||
if rawFlags&syscall.IFF_POINTOPOINT != 0 {
|
||||
f |= net.FlagPointToPoint
|
||||
}
|
||||
if rawFlags&syscall.IFF_MULTICAST != 0 {
|
||||
f |= net.FlagMulticast
|
||||
}
|
||||
return f
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
//go:build !linux
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func linkFlags(rawFlags uint32) net.Flags {
|
||||
panic("stub!")
|
||||
}
|
||||
@@ -22,7 +22,6 @@ type PlatformInterface interface {
|
||||
IncludeAllNetworks() bool
|
||||
ReadWIFIState() *WIFIState
|
||||
ClearDNSCache()
|
||||
SendNotification(notification *Notification) error
|
||||
}
|
||||
|
||||
type TunInterface interface {
|
||||
@@ -39,7 +38,6 @@ type NetworkInterface struct {
|
||||
MTU int32
|
||||
Name string
|
||||
Addresses StringIterator
|
||||
Flags int32
|
||||
}
|
||||
|
||||
type WIFIState struct {
|
||||
@@ -56,16 +54,6 @@ type NetworkInterfaceIterator interface {
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
type Notification struct {
|
||||
Identifier string
|
||||
TypeName string
|
||||
TypeID int32
|
||||
Title string
|
||||
Subtitle string
|
||||
Body string
|
||||
OpenURL string
|
||||
}
|
||||
|
||||
type OnDemandRule interface {
|
||||
Target() int32
|
||||
DNSSearchDomainMatch() StringIterator
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
type Interface interface {
|
||||
Initialize(ctx context.Context, router adapter.Router) error
|
||||
UsePlatformAutoDetectInterfaceControl() bool
|
||||
AutoDetectInterfaceControl(fd int) error
|
||||
AutoDetectInterfaceControl() control.Func
|
||||
OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
||||
UsePlatformDefaultInterfaceMonitor() bool
|
||||
CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor
|
||||
@@ -25,15 +25,5 @@ type Interface interface {
|
||||
ClearDNSCache()
|
||||
ReadWIFIState() adapter.WIFIState
|
||||
process.Searcher
|
||||
SendNotification(notification *Notification) error
|
||||
}
|
||||
|
||||
type Notification struct {
|
||||
Identifier string
|
||||
TypeName string
|
||||
TypeID int32
|
||||
Title string
|
||||
Subtitle string
|
||||
Body string
|
||||
OpenURL string
|
||||
OpenURL(url string)
|
||||
}
|
||||
|
||||
@@ -35,24 +35,24 @@ type BoxService struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
instance *box.Box
|
||||
platformInterface *platformInterfaceWrapper
|
||||
pauseManager pause.Manager
|
||||
urlTestHistoryStorage *urltest.HistoryStorage
|
||||
|
||||
servicePauseFields
|
||||
}
|
||||
|
||||
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
||||
ctx := box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry())
|
||||
ctx = service.ContextWith[deprecated.Manager](ctx, new(deprecatedManager))
|
||||
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
|
||||
options, err := parseConfig(ctx, configContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runtimeDebug.FreeOSMemory()
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
|
||||
urlTestHistoryStorage := urltest.NewHistoryStorage()
|
||||
ctx = service.ContextWithPtr(ctx, urlTestHistoryStorage)
|
||||
ctx = service.ContextWith[deprecated.Manager](ctx, new(deprecatedManager))
|
||||
platformWrapper := &platformInterfaceWrapper{iif: platformInterface, useProcFS: platformInterface.UseProcFS()}
|
||||
ctx = service.ContextWith[platform.Interface](ctx, platformWrapper)
|
||||
instance, err := box.New(box.Options{
|
||||
@@ -69,6 +69,7 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
instance: instance,
|
||||
platformInterface: platformWrapper,
|
||||
urlTestHistoryStorage: urlTestHistoryStorage,
|
||||
pauseManager: service.FromContext[pause.Manager](ctx),
|
||||
}, nil
|
||||
@@ -104,9 +105,10 @@ var (
|
||||
)
|
||||
|
||||
type platformInterfaceWrapper struct {
|
||||
iif PlatformInterface
|
||||
useProcFS bool
|
||||
router adapter.Router
|
||||
iif PlatformInterface
|
||||
useProcFS bool
|
||||
router adapter.Router
|
||||
openURLFunc func(url string)
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) Initialize(ctx context.Context, router adapter.Router) error {
|
||||
@@ -118,8 +120,12 @@ func (w *platformInterfaceWrapper) UsePlatformAutoDetectInterfaceControl() bool
|
||||
return w.iif.UsePlatformAutoDetectInterfaceControl()
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) AutoDetectInterfaceControl(fd int) error {
|
||||
return w.iif.AutoDetectInterfaceControl(int32(fd))
|
||||
func (w *platformInterfaceWrapper) AutoDetectInterfaceControl() control.Func {
|
||||
return func(network, address string, conn syscall.RawConn) error {
|
||||
return control.Raw(conn, func(fd uintptr) error {
|
||||
return w.iif.AutoDetectInterfaceControl(int32(fd))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
|
||||
@@ -177,7 +183,6 @@ func (w *platformInterfaceWrapper) Interfaces() ([]control.Interface, error) {
|
||||
MTU: int(netInterface.MTU),
|
||||
Name: netInterface.Name,
|
||||
Addresses: common.Map(iteratorToArray[string](netInterface.Addresses), netip.MustParsePrefix),
|
||||
Flags: linkFlags(uint32(netInterface.Flags)),
|
||||
})
|
||||
}
|
||||
return interfaces, nil
|
||||
@@ -238,6 +243,8 @@ func (w *platformInterfaceWrapper) WriteMessage(level log.Level, message string)
|
||||
w.iif.WriteLog(message)
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) SendNotification(notification *platform.Notification) error {
|
||||
return w.iif.SendNotification((*Notification)(notification))
|
||||
func (w *platformInterfaceWrapper) OpenURL(url string) {
|
||||
if w.openURLFunc != nil {
|
||||
w.openURLFunc(url)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ var (
|
||||
|
||||
func init() {
|
||||
debug.SetPanicOnFault(true)
|
||||
debug.SetTraceback("all")
|
||||
}
|
||||
|
||||
func Setup(basePath string, workingPath string, tempPath string, isTVOS bool) {
|
||||
|
||||
12
go.mod
12
go.mod
@@ -23,16 +23,16 @@ require (
|
||||
github.com/sagernet/fswatch v0.1.1
|
||||
github.com/sagernet/gomobile v0.1.4
|
||||
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3
|
||||
github.com/sagernet/quic-go v0.48.1-beta.1
|
||||
github.com/sagernet/quic-go v0.48.0-beta.1
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.5.1-0.20241105104305-c80c8f907c56
|
||||
github.com/sagernet/sing-dns v0.3.1-0.20241105104342-1914f319ddab
|
||||
github.com/sagernet/sing v0.5.0-rc.4.0.20241101160402-8452992a6369
|
||||
github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241023053951-feb6d5403f2a
|
||||
github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec
|
||||
github.com/sagernet/sing-quic v0.3.0-rc.2
|
||||
github.com/sagernet/sing-quic v0.3.0-rc.1
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||
github.com/sagernet/sing-shadowtls v0.1.4
|
||||
github.com/sagernet/sing-tun v0.4.0-rc.5.0.20241107062822-5a91eb99c90f
|
||||
github.com/sagernet/sing-tun v0.4.0-rc.4.0.20241023054150-3b5b396d06f7
|
||||
github.com/sagernet/sing-vmess v0.1.12
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||
github.com/sagernet/utls v1.6.7
|
||||
@@ -53,8 +53,6 @@ require (
|
||||
howett.net/plist v1.0.1
|
||||
)
|
||||
|
||||
//replace github.com/sagernet/sing => ../sing
|
||||
|
||||
require (
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||
|
||||
20
go.sum
20
go.sum
@@ -105,27 +105,27 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
||||
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
||||
github.com/sagernet/quic-go v0.48.1-beta.1 h1:ElPaV5yzlXIKZpqFMAcUGax6vddi3zt4AEpT94Z0vwo=
|
||||
github.com/sagernet/quic-go v0.48.1-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k=
|
||||
github.com/sagernet/quic-go v0.48.0-beta.1 h1:86hQZrmuoARI3BpDRkQaP0iAVpywA4YsRrzJPYuPKWg=
|
||||
github.com/sagernet/quic-go v0.48.0-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k=
|
||||
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.5.1-0.20241105104305-c80c8f907c56 h1:g+JCKxY8a+0L7GXjtS+t6uvJB3ibqKwyM/LJfFQM9/A=
|
||||
github.com/sagernet/sing v0.5.1-0.20241105104305-c80c8f907c56/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-dns v0.3.1-0.20241105104342-1914f319ddab h1:djP4EY/KM5T62xscormLflVi7eDlHv6p7md1FHMSArE=
|
||||
github.com/sagernet/sing-dns v0.3.1-0.20241105104342-1914f319ddab/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M=
|
||||
github.com/sagernet/sing v0.5.0-rc.4.0.20241101160402-8452992a6369 h1:gfiUYWslwKM7OtvG37PV0iIDCWcacJSEUS2h29rpYac=
|
||||
github.com/sagernet/sing v0.5.0-rc.4.0.20241101160402-8452992a6369/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241023053951-feb6d5403f2a h1:jpAlbmZxc1LymZrmJacsvHI57Wito5xy8qASZJMWoOQ=
|
||||
github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241023053951-feb6d5403f2a/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M=
|
||||
github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec h1:6Fd/VsEsw9qIjaGi1IBTZSb4b4v5JYtNcoiBtGsQC48=
|
||||
github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec/go.mod h1:RSwqqHwbtTOX3vs6ms8vMtBGH/0ZNyLm/uwt6TlmR84=
|
||||
github.com/sagernet/sing-quic v0.3.0-rc.2 h1:7vcC4bdS1GBJzHZhfmJiH0CfzQ4mYLUW51Z2RNHcGwc=
|
||||
github.com/sagernet/sing-quic v0.3.0-rc.2/go.mod h1:3UOq51WVqzra7eCgod7t4hqnTaOiZzFUci9avMrtOqs=
|
||||
github.com/sagernet/sing-quic v0.3.0-rc.1 h1:SlzL1yfEAKJyRduub8vzOVtbyTLAX7RZEEBZxO5utts=
|
||||
github.com/sagernet/sing-quic v0.3.0-rc.1/go.mod h1:uX+aUHA0fgIN6U3WViseDpSdTQriViZ7qz0Wbsf1mNQ=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||
github.com/sagernet/sing-tun v0.4.0-rc.5.0.20241107062822-5a91eb99c90f h1:gQwTgN/E4oHe3VlseD3/RhPs866cWcTsPG4dP6a8f8o=
|
||||
github.com/sagernet/sing-tun v0.4.0-rc.5.0.20241107062822-5a91eb99c90f/go.mod h1:Ehs5mZ3T8tTgV3H1Tx4Va5ixvyKjTAUPJ3G11dq7B/g=
|
||||
github.com/sagernet/sing-tun v0.4.0-rc.4.0.20241023054150-3b5b396d06f7 h1:wWfRBSP8v0Gc9yUeMgoKCiG+LIs/+bYLWWwVVYSbGFI=
|
||||
github.com/sagernet/sing-tun v0.4.0-rc.4.0.20241023054150-3b5b396d06f7/go.mod h1:2v1L3BQKzoOpGuKMwC6pcs/5/Xb5PBqzqL6Lq88IoS8=
|
||||
github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg=
|
||||
github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||
|
||||
@@ -82,7 +82,6 @@ nav:
|
||||
- configuration/dns/index.md
|
||||
- DNS Server: configuration/dns/server.md
|
||||
- DNS Rule: configuration/dns/rule.md
|
||||
- DNS Rule Action: configuration/dns/rule_action.md
|
||||
- FakeIP: configuration/dns/fakeip.md
|
||||
- NTP:
|
||||
- configuration/ntp/index.md
|
||||
@@ -91,7 +90,6 @@ nav:
|
||||
- GeoIP: configuration/route/geoip.md
|
||||
- Geosite: configuration/route/geosite.md
|
||||
- Route Rule: configuration/route/rule.md
|
||||
- Rule Action: configuration/route/rule_action.md
|
||||
- Protocol Sniff: configuration/route/sniff.md
|
||||
- Rule Set:
|
||||
- configuration/rule-set/index.md
|
||||
@@ -220,11 +218,9 @@ plugins:
|
||||
Log: 日志
|
||||
DNS Server: DNS 服务器
|
||||
DNS Rule: DNS 规则
|
||||
DNS Rule Action: DNS 规则动作
|
||||
|
||||
Route: 路由
|
||||
Route Rule: 路由规则
|
||||
Rule Action: 规则动作
|
||||
Protocol Sniff: 协议探测
|
||||
|
||||
Rule Set: 规则集
|
||||
|
||||
@@ -33,7 +33,7 @@ func (h *Inbound) UnmarshalJSONContext(ctx context.Context, content []byte) erro
|
||||
}
|
||||
registry := service.FromContext[InboundOptionsRegistry](ctx)
|
||||
if registry == nil {
|
||||
return E.New("missing Inbound fields registry in context")
|
||||
return E.New("missing inbound options registry in context")
|
||||
}
|
||||
options, loaded := registry.CreateOptions(h.Type)
|
||||
if !loaded {
|
||||
|
||||
@@ -19,9 +19,9 @@ type _Options struct {
|
||||
Experimental *ExperimentalOptions `json:"experimental,omitempty"`
|
||||
|
||||
// Deprecated: use Inbounds instead
|
||||
LegacyInbounds []LegacyInbound `json:"-"`
|
||||
LegacyInbounds []LegacyInbound `json:"inbound,omitempty"`
|
||||
// Deprecated: use Outbounds instead
|
||||
LegacyOutbounds []LegacyOutbound `json:"-"`
|
||||
LegacyOutbounds []LegacyOutbound `json:"_"`
|
||||
}
|
||||
|
||||
type Options _Options
|
||||
|
||||
@@ -3,8 +3,6 @@ package option
|
||||
import (
|
||||
"context"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
@@ -37,10 +35,6 @@ func (h *Outbound) UnmarshalJSONContext(ctx context.Context, content []byte) err
|
||||
if registry == nil {
|
||||
return E.New("missing outbound options registry in context")
|
||||
}
|
||||
switch h.Type {
|
||||
case C.TypeBlock, C.TypeDNS:
|
||||
deprecated.Report(ctx, deprecated.OptionSpecialOutbounds)
|
||||
}
|
||||
options, loaded := registry.CreateOptions(h.Type)
|
||||
if !loaded {
|
||||
return E.New("unknown outbound type: ", h.Type)
|
||||
@@ -49,11 +43,6 @@ func (h *Outbound) UnmarshalJSONContext(ctx context.Context, content []byte) err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if listenWrapper, isListen := options.(ListenOptionsWrapper); isListen {
|
||||
if listenWrapper.TakeListenOptions().InboundOptions != (InboundOptions{}) {
|
||||
deprecated.Report(ctx, deprecated.OptionInboundOptions)
|
||||
}
|
||||
}
|
||||
h.Options = options
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ type DefaultRule struct {
|
||||
RuleAction
|
||||
}
|
||||
|
||||
func (r DefaultRule) MarshalJSON() ([]byte, error) {
|
||||
func (r *DefaultRule) MarshalJSON() ([]byte, error) {
|
||||
return badjson.MarshallObjects(r.RawDefaultRule, r.RuleAction)
|
||||
}
|
||||
|
||||
@@ -128,27 +128,27 @@ func (r *DefaultRule) IsValid() bool {
|
||||
return !reflect.DeepEqual(r, defaultValue)
|
||||
}
|
||||
|
||||
type RawLogicalRule struct {
|
||||
type _LogicalRule struct {
|
||||
Mode string `json:"mode"`
|
||||
Rules []Rule `json:"rules,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
}
|
||||
|
||||
type LogicalRule struct {
|
||||
RawLogicalRule
|
||||
_LogicalRule
|
||||
RuleAction
|
||||
}
|
||||
|
||||
func (r LogicalRule) MarshalJSON() ([]byte, error) {
|
||||
return badjson.MarshallObjects(r.RawLogicalRule, r.RuleAction)
|
||||
func (r *LogicalRule) MarshalJSON() ([]byte, error) {
|
||||
return badjson.MarshallObjects(r._LogicalRule, r.RuleAction)
|
||||
}
|
||||
|
||||
func (r *LogicalRule) UnmarshalJSON(data []byte) error {
|
||||
err := json.Unmarshal(data, &r.RawLogicalRule)
|
||||
err := json.Unmarshal(data, &r._LogicalRule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return badjson.UnmarshallExcluded(data, &r.RawLogicalRule, &r.RuleAction)
|
||||
return badjson.UnmarshallExcluded(data, &r._LogicalRule, &r.RuleAction)
|
||||
}
|
||||
|
||||
func (r *LogicalRule) IsValid() bool {
|
||||
|
||||
@@ -1,26 +1,18 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||
dns "github.com/sagernet/sing-dns"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
)
|
||||
|
||||
type _RuleAction struct {
|
||||
Action string `json:"action,omitempty"`
|
||||
RouteOptions RouteActionOptions `json:"-"`
|
||||
RouteOptionsOptions RouteOptionsActionOptions `json:"-"`
|
||||
DirectOptions DirectActionOptions `json:"-"`
|
||||
RejectOptions RejectActionOptions `json:"-"`
|
||||
SniffOptions RouteActionSniff `json:"-"`
|
||||
ResolveOptions RouteActionResolve `json:"-"`
|
||||
Action string `json:"action,omitempty"`
|
||||
RouteOptions RouteActionOptions `json:"-"`
|
||||
RejectOptions RejectActionOptions `json:"-"`
|
||||
SniffOptions RouteActionSniff `json:"-"`
|
||||
ResolveOptions RouteActionResolve `json:"-"`
|
||||
}
|
||||
|
||||
type RuleAction _RuleAction
|
||||
@@ -31,10 +23,8 @@ func (r RuleAction) MarshalJSON() ([]byte, error) {
|
||||
case C.RuleActionTypeRoute:
|
||||
r.Action = ""
|
||||
v = r.RouteOptions
|
||||
case C.RuleActionTypeRouteOptions:
|
||||
v = r.RouteOptionsOptions
|
||||
case C.RuleActionTypeDirect:
|
||||
v = r.DirectOptions
|
||||
case C.RuleActionTypeReturn:
|
||||
v = nil
|
||||
case C.RuleActionTypeReject:
|
||||
v = r.RejectOptions
|
||||
case C.RuleActionTypeHijackDNS:
|
||||
@@ -62,10 +52,8 @@ func (r *RuleAction) UnmarshalJSON(data []byte) error {
|
||||
case "", C.RuleActionTypeRoute:
|
||||
r.Action = C.RuleActionTypeRoute
|
||||
v = &r.RouteOptions
|
||||
case C.RuleActionTypeRouteOptions:
|
||||
v = &r.RouteOptionsOptions
|
||||
case C.RuleActionTypeDirect:
|
||||
v = &r.DirectOptions
|
||||
case C.RuleActionTypeReturn:
|
||||
v = nil
|
||||
case C.RuleActionTypeReject:
|
||||
v = &r.RejectOptions
|
||||
case C.RuleActionTypeHijackDNS:
|
||||
@@ -85,10 +73,11 @@ func (r *RuleAction) UnmarshalJSON(data []byte) error {
|
||||
}
|
||||
|
||||
type _DNSRuleAction struct {
|
||||
Action string `json:"action,omitempty"`
|
||||
RouteOptions DNSRouteActionOptions `json:"-"`
|
||||
RouteOptionsOptions DNSRouteOptionsActionOptions `json:"-"`
|
||||
RejectOptions RejectActionOptions `json:"-"`
|
||||
Action string `json:"action,omitempty"`
|
||||
RouteOptions DNSRouteActionOptions `json:"-"`
|
||||
RejectOptions RejectActionOptions `json:"-"`
|
||||
SniffOptions RouteActionSniff `json:"-"`
|
||||
ResolveOptions RouteActionResolve `json:"-"`
|
||||
}
|
||||
|
||||
type DNSRuleAction _DNSRuleAction
|
||||
@@ -99,17 +88,20 @@ func (r DNSRuleAction) MarshalJSON() ([]byte, error) {
|
||||
case C.RuleActionTypeRoute:
|
||||
r.Action = ""
|
||||
v = r.RouteOptions
|
||||
case C.RuleActionTypeRouteOptions:
|
||||
v = r.RouteOptionsOptions
|
||||
case C.RuleActionTypeReturn:
|
||||
v = nil
|
||||
case C.RuleActionTypeReject:
|
||||
v = r.RejectOptions
|
||||
default:
|
||||
return nil, E.New("unknown DNS rule action: " + r.Action)
|
||||
}
|
||||
if v == nil {
|
||||
return badjson.MarshallObjects((_DNSRuleAction)(r))
|
||||
}
|
||||
return badjson.MarshallObjects((_DNSRuleAction)(r), v)
|
||||
}
|
||||
|
||||
func (r *DNSRuleAction) UnmarshalJSONContext(ctx context.Context, data []byte) error {
|
||||
func (r *DNSRuleAction) UnmarshalJSON(data []byte) error {
|
||||
err := json.Unmarshal(data, (*_DNSRuleAction)(r))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -119,146 +111,34 @@ func (r *DNSRuleAction) UnmarshalJSONContext(ctx context.Context, data []byte) e
|
||||
case "", C.RuleActionTypeRoute:
|
||||
r.Action = C.RuleActionTypeRoute
|
||||
v = &r.RouteOptions
|
||||
case C.RuleActionTypeRouteOptions:
|
||||
v = &r.RouteOptionsOptions
|
||||
case C.RuleActionTypeReturn:
|
||||
v = nil
|
||||
case C.RuleActionTypeReject:
|
||||
v = &r.RejectOptions
|
||||
default:
|
||||
return E.New("unknown DNS rule action: " + r.Action)
|
||||
}
|
||||
return badjson.UnmarshallExcludedContext(ctx, data, (*_DNSRuleAction)(r), v)
|
||||
}
|
||||
|
||||
type _RouteActionOptions struct {
|
||||
Outbound string `json:"outbound"`
|
||||
}
|
||||
|
||||
type RouteActionOptions _RouteActionOptions
|
||||
|
||||
func (r *RouteActionOptions) UnmarshalJSON(data []byte) error {
|
||||
err := json.Unmarshal(data, (*_RouteActionOptions)(r))
|
||||
if err != nil {
|
||||
return err
|
||||
if v == nil {
|
||||
// check unknown fields
|
||||
return json.UnmarshalDisallowUnknownFields(data, &_DNSRuleAction{})
|
||||
}
|
||||
return nil
|
||||
return badjson.UnmarshallExcluded(data, (*_DNSRuleAction)(r), v)
|
||||
}
|
||||
|
||||
type _RouteOptionsActionOptions struct {
|
||||
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
|
||||
UDPConnect bool `json:"udp_connect,omitempty"`
|
||||
type RouteActionOptions struct {
|
||||
Outbound string `json:"outbound"`
|
||||
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
|
||||
}
|
||||
|
||||
type RouteOptionsActionOptions _RouteOptionsActionOptions
|
||||
|
||||
func (r *RouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
|
||||
err := json.Unmarshal(data, (*_RouteOptionsActionOptions)(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *r == (RouteOptionsActionOptions{}) {
|
||||
return E.New("empty route option action")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type _DNSRouteActionOptions struct {
|
||||
Server string `json:"server"`
|
||||
// Deprecated: Use DNSRouteOptionsActionOptions instead.
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
// Deprecated: Use DNSRouteOptionsActionOptions instead.
|
||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||
// Deprecated: Use DNSRouteOptionsActionOptions instead.
|
||||
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
|
||||
}
|
||||
|
||||
type DNSRouteActionOptions _DNSRouteActionOptions
|
||||
|
||||
func (r *DNSRouteActionOptions) UnmarshalJSONContext(ctx context.Context, data []byte) error {
|
||||
err := json.Unmarshal(data, (*_DNSRouteActionOptions)(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r.DisableCache || r.RewriteTTL != nil || r.ClientSubnet != nil {
|
||||
deprecated.Report(ctx, deprecated.OptionLegacyDNSRouteOptions)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type _DNSRouteOptionsActionOptions struct {
|
||||
type DNSRouteActionOptions struct {
|
||||
Server string `json:"server"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
|
||||
}
|
||||
|
||||
type DNSRouteOptionsActionOptions _DNSRouteOptionsActionOptions
|
||||
|
||||
func (r *DNSRouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
|
||||
err := json.Unmarshal(data, (*_DNSRouteOptionsActionOptions)(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *r == (DNSRouteOptionsActionOptions{}) {
|
||||
return E.New("empty DNS route option action")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type _DirectActionOptions DialerOptions
|
||||
|
||||
type DirectActionOptions _DirectActionOptions
|
||||
|
||||
func (d DirectActionOptions) Descriptions() []string {
|
||||
var descriptions []string
|
||||
if d.BindInterface != "" {
|
||||
descriptions = append(descriptions, "bind_interface="+d.BindInterface)
|
||||
}
|
||||
if d.Inet4BindAddress != nil {
|
||||
descriptions = append(descriptions, "inet4_bind_address="+d.Inet4BindAddress.Build().String())
|
||||
}
|
||||
if d.Inet6BindAddress != nil {
|
||||
descriptions = append(descriptions, "inet6_bind_address="+d.Inet6BindAddress.Build().String())
|
||||
}
|
||||
if d.RoutingMark != 0 {
|
||||
descriptions = append(descriptions, "routing_mark="+fmt.Sprintf("0x%x", d.RoutingMark))
|
||||
}
|
||||
if d.ReuseAddr {
|
||||
descriptions = append(descriptions, "reuse_addr")
|
||||
}
|
||||
if d.ConnectTimeout != 0 {
|
||||
descriptions = append(descriptions, "connect_timeout="+time.Duration(d.ConnectTimeout).String())
|
||||
}
|
||||
if d.TCPFastOpen {
|
||||
descriptions = append(descriptions, "tcp_fast_open")
|
||||
}
|
||||
if d.TCPMultiPath {
|
||||
descriptions = append(descriptions, "tcp_multi_path")
|
||||
}
|
||||
if d.UDPFragment != nil {
|
||||
descriptions = append(descriptions, "udp_fragment="+fmt.Sprint(*d.UDPFragment))
|
||||
}
|
||||
if d.DomainStrategy != DomainStrategy(dns.DomainStrategyAsIS) {
|
||||
descriptions = append(descriptions, "domain_strategy="+d.DomainStrategy.String())
|
||||
}
|
||||
if d.FallbackDelay != 0 {
|
||||
descriptions = append(descriptions, "fallback_delay="+time.Duration(d.FallbackDelay).String())
|
||||
}
|
||||
return descriptions
|
||||
}
|
||||
|
||||
func (d *DirectActionOptions) UnmarshalJSON(data []byte) error {
|
||||
err := json.Unmarshal(data, (*_DirectActionOptions)(d))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.Detour != "" {
|
||||
return E.New("detour is not available in the current context")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type _RejectActionOptions struct {
|
||||
Method string `json:"method,omitempty"`
|
||||
NoDrop bool `json:"no_drop,omitempty"`
|
||||
}
|
||||
|
||||
type RejectActionOptions _RejectActionOptions
|
||||
@@ -271,13 +151,14 @@ func (r *RejectActionOptions) UnmarshalJSON(bytes []byte) error {
|
||||
switch r.Method {
|
||||
case "", C.RuleActionRejectMethodDefault:
|
||||
r.Method = C.RuleActionRejectMethodDefault
|
||||
case C.RuleActionRejectMethodDrop:
|
||||
case C.RuleActionRejectMethodReset,
|
||||
C.RuleActionRejectMethodNetworkUnreachable,
|
||||
C.RuleActionRejectMethodHostUnreachable,
|
||||
C.RuleActionRejectMethodPortUnreachable,
|
||||
C.RuleActionRejectMethodDrop:
|
||||
default:
|
||||
return E.New("unknown reject method: " + r.Method)
|
||||
}
|
||||
if r.Method == C.RuleActionRejectMethodDrop && r.NoDrop {
|
||||
return E.New("no_drop is not available in current context")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
@@ -33,7 +32,7 @@ func (r DNSRule) MarshalJSON() ([]byte, error) {
|
||||
return badjson.MarshallObjects((_DNSRule)(r), v)
|
||||
}
|
||||
|
||||
func (r *DNSRule) UnmarshalJSONContext(ctx context.Context, bytes []byte) error {
|
||||
func (r *DNSRule) UnmarshalJSON(bytes []byte) error {
|
||||
err := json.Unmarshal(bytes, (*_DNSRule)(r))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -48,7 +47,7 @@ func (r *DNSRule) UnmarshalJSONContext(ctx context.Context, bytes []byte) error
|
||||
default:
|
||||
return E.New("unknown rule type: " + r.Type)
|
||||
}
|
||||
err = badjson.UnmarshallExcludedContext(ctx, bytes, (*_DNSRule)(r), v)
|
||||
err = badjson.UnmarshallExcluded(bytes, (*_DNSRule)(r), v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -112,19 +111,19 @@ type DefaultDNSRule struct {
|
||||
DNSRuleAction
|
||||
}
|
||||
|
||||
func (r DefaultDNSRule) MarshalJSON() ([]byte, error) {
|
||||
func (r *DefaultDNSRule) MarshalJSON() ([]byte, error) {
|
||||
return badjson.MarshallObjects(r.RawDefaultDNSRule, r.DNSRuleAction)
|
||||
}
|
||||
|
||||
func (r *DefaultDNSRule) UnmarshalJSONContext(ctx context.Context, data []byte) error {
|
||||
func (r *DefaultDNSRule) UnmarshalJSON(data []byte) error {
|
||||
err := json.Unmarshal(data, &r.RawDefaultDNSRule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return badjson.UnmarshallExcludedContext(ctx, data, &r.RawDefaultDNSRule, &r.DNSRuleAction)
|
||||
return badjson.UnmarshallExcluded(data, &r.RawDefaultDNSRule, &r.DNSRuleAction)
|
||||
}
|
||||
|
||||
func (r DefaultDNSRule) IsValid() bool {
|
||||
func (r *DefaultDNSRule) IsValid() bool {
|
||||
var defaultValue DefaultDNSRule
|
||||
defaultValue.Invert = r.Invert
|
||||
defaultValue.DNSRuleAction = r.DNSRuleAction
|
||||
@@ -146,12 +145,12 @@ func (r *LogicalDNSRule) MarshalJSON() ([]byte, error) {
|
||||
return badjson.MarshallObjects(r._LogicalDNSRule, r.DNSRuleAction)
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) UnmarshalJSONContext(ctx context.Context, data []byte) error {
|
||||
func (r *LogicalDNSRule) UnmarshalJSON(data []byte) error {
|
||||
err := json.Unmarshal(data, &r._LogicalDNSRule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return badjson.UnmarshallExcludedContext(ctx, data, &r._LogicalDNSRule, &r.DNSRuleAction)
|
||||
return badjson.UnmarshallExcluded(data, &r._LogicalDNSRule, &r.DNSRuleAction)
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) IsValid() bool {
|
||||
|
||||
@@ -43,7 +43,6 @@ func HandleStreamDNSRequest(ctx context.Context, router adapter.Router, conn net
|
||||
go func() error {
|
||||
response, err := router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
responseBuffer := buf.NewPacket()
|
||||
|
||||
@@ -343,25 +343,19 @@ func (t *Inbound) Start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
monitor.Start("initiating tun stack")
|
||||
err = tunStack.Start()
|
||||
monitor.Finish()
|
||||
t.tunStack = tunStack
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.logger.Info("started at ", t.tunOptions.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Inbound) PostStart() error {
|
||||
monitor := taskmonitor.New(t.logger, C.StartTimeout)
|
||||
monitor.Start("starting tun stack")
|
||||
err := t.tunStack.Start()
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return E.Cause(err, "starting tun stack")
|
||||
}
|
||||
monitor.Start("starting tun interface")
|
||||
err = t.tunIf.Start()
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return E.Cause(err, "starting TUN interface")
|
||||
}
|
||||
if t.autoRedirect != nil {
|
||||
t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet)
|
||||
for _, routeRuleSet := range t.routeRuleSet {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"os/user"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -76,7 +77,7 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
metadata.Network = N.NetworkTCP
|
||||
switch metadata.Destination.Fqdn {
|
||||
case mux.Destination.Fqdn:
|
||||
return E.New("global multiplex is deprecated since sing-box v1.7.0, enable multiplex in Inbound fields instead.")
|
||||
return E.New("global multiplex is deprecated since sing-box v1.7.0, enable multiplex in inbound options instead.")
|
||||
case vmess.MuxDestination.Fqdn:
|
||||
return E.New("global multiplex (v2ray legacy) not supported since sing-box v1.7.0.")
|
||||
case uot.MagicAddress:
|
||||
@@ -91,33 +92,22 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
// selectedOutbound adapter.Outbound
|
||||
selectedDialer N.Dialer
|
||||
selectedTag string
|
||||
selectedDescription string
|
||||
)
|
||||
var selectedOutbound adapter.Outbound
|
||||
var selectReturn bool
|
||||
if selectedRule != nil {
|
||||
switch action := selectedRule.Action().(type) {
|
||||
case *rule.RuleActionRoute:
|
||||
selectedOutbound, loaded := r.Outbound(action.Outbound)
|
||||
var loaded bool
|
||||
selectedOutbound, loaded = r.Outbound(action.Outbound)
|
||||
if !loaded {
|
||||
buf.ReleaseMulti(buffers)
|
||||
return E.New("outbound not found: ", action.Outbound)
|
||||
}
|
||||
if !common.Contains(selectedOutbound.Network(), N.NetworkTCP) {
|
||||
buf.ReleaseMulti(buffers)
|
||||
return E.New("TCP is not supported by outbound: ", selectedOutbound.Tag())
|
||||
}
|
||||
selectedDialer = selectedOutbound
|
||||
selectedTag = selectedOutbound.Tag()
|
||||
selectedDescription = F.ToString("outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
|
||||
case *rule.RuleActionDirect:
|
||||
selectedDialer = action.Dialer
|
||||
selectedDescription = action.String()
|
||||
case *rule.RuleActionReturn:
|
||||
selectReturn = true
|
||||
case *rule.RuleActionReject:
|
||||
buf.ReleaseMulti(buffers)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, action.Error(ctx))
|
||||
N.CloseOnHandshakeFailure(conn, onClose, action.Error())
|
||||
return nil
|
||||
case *rule.RuleActionHijackDNS:
|
||||
for _, buffer := range buffers {
|
||||
@@ -127,16 +117,17 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if selectedRule == nil {
|
||||
if selectedRule == nil || selectReturn {
|
||||
if r.defaultOutboundForConnection == nil {
|
||||
buf.ReleaseMulti(buffers)
|
||||
return E.New("missing default outbound with TCP support")
|
||||
}
|
||||
selectedDialer = r.defaultOutboundForConnection
|
||||
selectedTag = r.defaultOutboundForConnection.Tag()
|
||||
selectedDescription = F.ToString("outbound/", r.defaultOutboundForConnection.Type(), "[", r.defaultOutboundForConnection.Tag(), "]")
|
||||
selectedOutbound = r.defaultOutboundForConnection
|
||||
}
|
||||
if !common.Contains(selectedOutbound.Network(), N.NetworkTCP) {
|
||||
buf.ReleaseMulti(buffers)
|
||||
return E.New("TCP is not supported by outbound: ", selectedOutbound.Tag())
|
||||
}
|
||||
|
||||
for _, buffer := range buffers {
|
||||
conn = bufio.NewCachedConn(conn, buffer)
|
||||
}
|
||||
@@ -147,10 +138,10 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
}
|
||||
if r.v2rayServer != nil {
|
||||
if statsService := r.v2rayServer.StatsService(); statsService != nil {
|
||||
conn = statsService.RoutedConnection(metadata.Inbound, selectedTag, metadata.User, conn)
|
||||
conn = statsService.RoutedConnection(metadata.Inbound, selectedOutbound.Tag(), metadata.User, conn)
|
||||
}
|
||||
}
|
||||
legacyOutbound, isLegacy := selectedDialer.(adapter.ConnectionHandler)
|
||||
legacyOutbound, isLegacy := selectedOutbound.(adapter.ConnectionHandler)
|
||||
if isLegacy {
|
||||
err = legacyOutbound.NewConnection(ctx, conn, metadata)
|
||||
if err != nil {
|
||||
@@ -158,7 +149,7 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
if onClose != nil {
|
||||
onClose(err)
|
||||
}
|
||||
return E.Cause(err, selectedDescription)
|
||||
return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
|
||||
} else {
|
||||
if onClose != nil {
|
||||
onClose(nil)
|
||||
@@ -167,13 +158,13 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
return nil
|
||||
}
|
||||
// TODO
|
||||
err = outbound.NewConnection(ctx, selectedDialer, conn, metadata)
|
||||
err = outbound.NewConnection(ctx, selectedOutbound, conn, metadata)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
if onClose != nil {
|
||||
onClose(err)
|
||||
}
|
||||
return E.Cause(err, selectedDescription)
|
||||
return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
|
||||
} else {
|
||||
if onClose != nil {
|
||||
onClose(nil)
|
||||
@@ -245,33 +236,23 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
selectedDialer N.Dialer
|
||||
selectedTag string
|
||||
selectedDescription string
|
||||
)
|
||||
var selectedOutbound adapter.Outbound
|
||||
var selectReturn bool
|
||||
if selectedRule != nil {
|
||||
switch action := selectedRule.Action().(type) {
|
||||
case *rule.RuleActionRoute:
|
||||
selectedOutbound, loaded := r.Outbound(action.Outbound)
|
||||
var loaded bool
|
||||
selectedOutbound, loaded = r.Outbound(action.Outbound)
|
||||
if !loaded {
|
||||
N.ReleaseMultiPacketBuffer(packetBuffers)
|
||||
return E.New("outbound not found: ", action.Outbound)
|
||||
}
|
||||
if !common.Contains(selectedOutbound.Network(), N.NetworkUDP) {
|
||||
N.ReleaseMultiPacketBuffer(packetBuffers)
|
||||
return E.New("UDP is not supported by outbound: ", selectedOutbound.Tag())
|
||||
}
|
||||
selectedDialer = selectedOutbound
|
||||
selectedTag = selectedOutbound.Tag()
|
||||
selectedDescription = F.ToString("outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
|
||||
case *rule.RuleActionDirect:
|
||||
selectedDialer = action.Dialer
|
||||
selectedDescription = action.String()
|
||||
metadata.UDPDisableDomainUnmapping = action.UDPDisableDomainUnmapping
|
||||
case *rule.RuleActionReturn:
|
||||
selectReturn = true
|
||||
case *rule.RuleActionReject:
|
||||
N.ReleaseMultiPacketBuffer(packetBuffers)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, action.Error(ctx))
|
||||
N.CloseOnHandshakeFailure(conn, onClose, syscall.ECONNREFUSED)
|
||||
return nil
|
||||
case *rule.RuleActionHijackDNS:
|
||||
r.hijackDNSPacket(ctx, conn, packetBuffers, metadata)
|
||||
@@ -283,9 +264,11 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
N.ReleaseMultiPacketBuffer(packetBuffers)
|
||||
return E.New("missing default outbound with UDP support")
|
||||
}
|
||||
selectedDialer = r.defaultOutboundForPacketConnection
|
||||
selectedTag = r.defaultOutboundForPacketConnection.Tag()
|
||||
selectedDescription = F.ToString("outbound/", r.defaultOutboundForPacketConnection.Type(), "[", r.defaultOutboundForPacketConnection.Tag(), "]")
|
||||
selectedOutbound = r.defaultOutboundForPacketConnection
|
||||
}
|
||||
if !common.Contains(selectedOutbound.Network(), N.NetworkUDP) {
|
||||
N.ReleaseMultiPacketBuffer(packetBuffers)
|
||||
return E.New("UDP is not supported by outbound: ", selectedOutbound.Tag())
|
||||
}
|
||||
for _, buffer := range packetBuffers {
|
||||
conn = bufio.NewCachedPacketConn(conn, buffer.Buffer, buffer.Destination)
|
||||
@@ -298,26 +281,26 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
}
|
||||
if r.v2rayServer != nil {
|
||||
if statsService := r.v2rayServer.StatsService(); statsService != nil {
|
||||
conn = statsService.RoutedPacketConnection(metadata.Inbound, selectedTag, metadata.User, conn)
|
||||
conn = statsService.RoutedPacketConnection(metadata.Inbound, selectedOutbound.Tag(), metadata.User, conn)
|
||||
}
|
||||
}
|
||||
if metadata.FakeIP {
|
||||
conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination)
|
||||
}
|
||||
legacyOutbound, isLegacy := selectedDialer.(adapter.PacketConnectionHandler)
|
||||
legacyOutbound, isLegacy := selectedOutbound.(adapter.PacketConnectionHandler)
|
||||
if isLegacy {
|
||||
err = legacyOutbound.NewPacketConnection(ctx, conn, metadata)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
return E.Cause(err, selectedDescription)
|
||||
return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// TODO
|
||||
err = outbound.NewPacketConnection(ctx, selectedDialer, conn, metadata)
|
||||
err = outbound.NewPacketConnection(ctx, selectedOutbound, conn, metadata)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
return E.Cause(err, selectedDescription)
|
||||
return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -334,7 +317,7 @@ func (r *Router) PreMatch(metadata adapter.InboundContext) error {
|
||||
if !isReject {
|
||||
return nil
|
||||
}
|
||||
return rejectAction.Error(nil)
|
||||
return rejectAction.Error()
|
||||
}
|
||||
|
||||
func (r *Router) matchRule(
|
||||
@@ -462,7 +445,7 @@ match:
|
||||
}
|
||||
} else {
|
||||
switch currentRule.Action().Type() {
|
||||
case C.RuleActionTypeReject:
|
||||
case C.RuleActionTypeReject, C.RuleActionTypeResolve:
|
||||
ruleDescription := currentRule.String()
|
||||
if ruleDescription != "" {
|
||||
r.logger.DebugContext(ctx, "pre-match[", currentRuleIndex, "] ", currentRule, " => ", currentRule.Action())
|
||||
@@ -472,9 +455,6 @@ match:
|
||||
}
|
||||
}
|
||||
switch action := currentRule.Action().(type) {
|
||||
case *rule.RuleActionRouteOptions:
|
||||
metadata.UDPDisableDomainUnmapping = action.UDPDisableDomainUnmapping
|
||||
metadata.UDPConnect = action.UDPConnect
|
||||
case *rule.RuleActionSniff:
|
||||
if !preMatch {
|
||||
newBuffer, newPacketBuffers, newErr := r.actionSniff(ctx, metadata, action, inputConn, inputPacketConn)
|
||||
|
||||
@@ -8,10 +8,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
R "github.com/sagernet/sing-box/route/rule"
|
||||
"github.com/sagernet/sing-dns"
|
||||
tun "github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common/cache"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
@@ -50,63 +48,38 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int,
|
||||
if ruleIndex != -1 {
|
||||
dnsRules = dnsRules[ruleIndex+1:]
|
||||
}
|
||||
for currentRuleIndex, currentRule := range dnsRules {
|
||||
if currentRule.WithAddressLimit() && !isAddressQuery {
|
||||
for currentRuleIndex, rule := range dnsRules {
|
||||
if rule.WithAddressLimit() && !isAddressQuery {
|
||||
continue
|
||||
}
|
||||
metadata.ResetRuleCache()
|
||||
if currentRule.Match(metadata) {
|
||||
if rule.Match(metadata) {
|
||||
displayRuleIndex := currentRuleIndex
|
||||
if displayRuleIndex != -1 {
|
||||
displayRuleIndex += displayRuleIndex + 1
|
||||
}
|
||||
ruleDescription := currentRule.String()
|
||||
if ruleDescription != "" {
|
||||
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] ", currentRule, " => ", currentRule.Action())
|
||||
} else {
|
||||
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
|
||||
}
|
||||
switch action := currentRule.Action().(type) {
|
||||
case *R.RuleActionDNSRoute:
|
||||
transport, loaded := r.transportMap[action.Server]
|
||||
if routeAction, isRoute := rule.Action().(*R.RuleActionDNSRoute); isRoute {
|
||||
transport, loaded := r.transportMap[routeAction.Server]
|
||||
if !loaded {
|
||||
r.dnsLogger.ErrorContext(ctx, "transport not found: ", action.Server)
|
||||
r.dnsLogger.ErrorContext(ctx, "transport not found: ", routeAction.Server)
|
||||
continue
|
||||
}
|
||||
_, isFakeIP := transport.(adapter.FakeIPTransport)
|
||||
if isFakeIP && !allowFakeIP {
|
||||
continue
|
||||
}
|
||||
if isFakeIP || action.DisableCache {
|
||||
options.DisableCache = true
|
||||
}
|
||||
if action.RewriteTTL != nil {
|
||||
options.RewriteTTL = action.RewriteTTL
|
||||
}
|
||||
if action.ClientSubnet.IsValid() {
|
||||
options.ClientSubnet = action.ClientSubnet
|
||||
}
|
||||
options.DisableCache = isFakeIP || routeAction.DisableCache
|
||||
options.RewriteTTL = routeAction.RewriteTTL
|
||||
options.ClientSubnet = routeAction.ClientSubnet
|
||||
if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded {
|
||||
options.Strategy = domainStrategy
|
||||
} else {
|
||||
options.Strategy = r.defaultDomainStrategy
|
||||
}
|
||||
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
|
||||
return transport, options, currentRule, currentRuleIndex
|
||||
case *R.RuleActionDNSRouteOptions:
|
||||
if action.DisableCache {
|
||||
options.DisableCache = true
|
||||
}
|
||||
if action.RewriteTTL != nil {
|
||||
options.RewriteTTL = action.RewriteTTL
|
||||
}
|
||||
if action.ClientSubnet.IsValid() {
|
||||
options.ClientSubnet = action.ClientSubnet
|
||||
}
|
||||
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
|
||||
case *R.RuleActionReject:
|
||||
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
|
||||
return nil, options, currentRule, currentRuleIndex
|
||||
r.dnsLogger.DebugContext(ctx, "match[", displayRuleIndex, "] ", rule.String(), " => ", rule.Action())
|
||||
return transport, options, rule, currentRuleIndex
|
||||
} else {
|
||||
return nil, options, rule, currentRuleIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,17 +127,6 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
||||
dnsCtx := adapter.OverrideContext(ctx)
|
||||
var addressLimit bool
|
||||
transport, options, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message))
|
||||
if rule != nil {
|
||||
switch action := rule.Action().(type) {
|
||||
case *R.RuleActionReject:
|
||||
switch action.Method {
|
||||
case C.RuleActionRejectMethodDefault:
|
||||
return dns.FixedResponse(message.Id, message.Question[0], nil, 0), nil
|
||||
case C.RuleActionRejectMethodDrop:
|
||||
return nil, tun.ErrDrop
|
||||
}
|
||||
}
|
||||
}
|
||||
if rule != nil && rule.WithAddressLimit() {
|
||||
addressLimit = true
|
||||
response, err = r.dnsClient.ExchangeWithResponseCheck(dnsCtx, transport, message, options, func(response *mDNS.Msg) bool {
|
||||
@@ -276,17 +238,6 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
|
||||
if strategy != dns.DomainStrategyAsIS {
|
||||
options.Strategy = strategy
|
||||
}
|
||||
if rule != nil {
|
||||
switch action := rule.Action().(type) {
|
||||
case *R.RuleActionReject:
|
||||
switch action.Method {
|
||||
case C.RuleActionRejectMethodDefault:
|
||||
return nil, nil
|
||||
case C.RuleActionRejectMethodDrop:
|
||||
return nil, tun.ErrDrop
|
||||
}
|
||||
}
|
||||
}
|
||||
if rule != nil && rule.WithAddressLimit() {
|
||||
addressLimit = true
|
||||
responseAddrs, err = r.dnsClient.LookupWithResponseCheck(dnsCtx, transport, domain, options, func(responseAddrs []netip.Addr) bool {
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -205,19 +204,12 @@ func NewRouter(
|
||||
} else {
|
||||
detour = dialer.NewDetour(router, server.Detour)
|
||||
}
|
||||
var serverProtocol string
|
||||
switch server.Address {
|
||||
case "local":
|
||||
serverProtocol = "local"
|
||||
default:
|
||||
serverURL, _ := url.Parse(server.Address)
|
||||
var serverAddress string
|
||||
if serverURL != nil {
|
||||
if serverURL.Scheme == "" {
|
||||
serverProtocol = "udp"
|
||||
} else {
|
||||
serverProtocol = serverURL.Scheme
|
||||
}
|
||||
serverAddress = serverURL.Hostname()
|
||||
}
|
||||
if serverAddress == "" {
|
||||
@@ -243,12 +235,9 @@ func NewRouter(
|
||||
} else if dnsOptions.ClientSubnet != nil {
|
||||
clientSubnet = dnsOptions.ClientSubnet.Build()
|
||||
}
|
||||
if serverProtocol == "" {
|
||||
serverProtocol = "transport"
|
||||
}
|
||||
transport, err := dns.CreateTransport(dns.TransportOptions{
|
||||
Context: ctx,
|
||||
Logger: logFactory.NewLogger(F.ToString("dns/", serverProtocol, "[", tag, "]")),
|
||||
Logger: logFactory.NewLogger(F.ToString("dns/transport[", tag, "]")),
|
||||
Name: tag,
|
||||
Dialer: detour,
|
||||
Address: server.Address,
|
||||
@@ -833,11 +822,7 @@ func (r *Router) AutoDetectInterface() bool {
|
||||
|
||||
func (r *Router) AutoDetectInterfaceFunc() control.Func {
|
||||
if r.platformInterface != nil && r.platformInterface.UsePlatformAutoDetectInterfaceControl() {
|
||||
return func(network, address string, conn syscall.RawConn) error {
|
||||
return control.Raw(conn, func(fd uintptr) error {
|
||||
return r.platformInterface.AutoDetectInterfaceControl(int(fd))
|
||||
})
|
||||
}
|
||||
return r.platformInterface.AutoDetectInterfaceControl()
|
||||
} else {
|
||||
if r.interfaceMonitor == nil {
|
||||
return nil
|
||||
|
||||
@@ -1,63 +1,34 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/sniff"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func NewRuleAction(router adapter.Router, logger logger.ContextLogger, action option.RuleAction) (adapter.RuleAction, error) {
|
||||
func NewRuleAction(action option.RuleAction) (adapter.RuleAction, error) {
|
||||
switch action.Action {
|
||||
case C.RuleActionTypeRoute:
|
||||
return &RuleActionRoute{
|
||||
Outbound: action.RouteOptions.Outbound,
|
||||
}, nil
|
||||
case C.RuleActionTypeRouteOptions:
|
||||
return &RuleActionRouteOptions{
|
||||
UDPDisableDomainUnmapping: action.RouteOptionsOptions.UDPDisableDomainUnmapping,
|
||||
UDPConnect: action.RouteOptionsOptions.UDPConnect,
|
||||
}, nil
|
||||
case C.RuleActionTypeDirect:
|
||||
directDialer, err := dialer.New(router, option.DialerOptions(action.DirectOptions))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var description string
|
||||
descriptions := action.DirectOptions.Descriptions()
|
||||
switch len(descriptions) {
|
||||
case 0:
|
||||
case 1:
|
||||
description = F.ToString("(", descriptions[0], ")")
|
||||
case 2:
|
||||
description = F.ToString("(", descriptions[0], ",", descriptions[1], ")")
|
||||
default:
|
||||
description = F.ToString("(", descriptions[0], ",", descriptions[1], ",...)")
|
||||
}
|
||||
return &RuleActionDirect{
|
||||
Dialer: directDialer,
|
||||
description: description,
|
||||
Outbound: action.RouteOptions.Outbound,
|
||||
UDPDisableDomainUnmapping: action.RouteOptions.UDPDisableDomainUnmapping,
|
||||
}, nil
|
||||
case C.RuleActionTypeReturn:
|
||||
return &RuleActionReturn{}, nil
|
||||
case C.RuleActionTypeReject:
|
||||
return &RuleActionReject{
|
||||
Method: action.RejectOptions.Method,
|
||||
NoDrop: action.RejectOptions.NoDrop,
|
||||
logger: logger,
|
||||
}, nil
|
||||
case C.RuleActionTypeHijackDNS:
|
||||
return &RuleActionHijackDNS{}, nil
|
||||
@@ -77,7 +48,7 @@ func NewRuleAction(router adapter.Router, logger logger.ContextLogger, action op
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSRuleAction(logger logger.ContextLogger, action option.DNSRuleAction) adapter.RuleAction {
|
||||
func NewDNSRuleAction(action option.DNSRuleAction) adapter.RuleAction {
|
||||
switch action.Action {
|
||||
case C.RuleActionTypeRoute:
|
||||
return &RuleActionDNSRoute{
|
||||
@@ -86,17 +57,11 @@ func NewDNSRuleAction(logger logger.ContextLogger, action option.DNSRuleAction)
|
||||
RewriteTTL: action.RouteOptions.RewriteTTL,
|
||||
ClientSubnet: action.RouteOptions.ClientSubnet.Build(),
|
||||
}
|
||||
case C.RuleActionTypeRouteOptions:
|
||||
return &RuleActionDNSRouteOptions{
|
||||
DisableCache: action.RouteOptionsOptions.DisableCache,
|
||||
RewriteTTL: action.RouteOptionsOptions.RewriteTTL,
|
||||
ClientSubnet: action.RouteOptionsOptions.ClientSubnet.Build(),
|
||||
}
|
||||
case C.RuleActionTypeReturn:
|
||||
return &RuleActionReturn{}
|
||||
case C.RuleActionTypeReject:
|
||||
return &RuleActionReject{
|
||||
Method: action.RejectOptions.Method,
|
||||
NoDrop: action.RejectOptions.NoDrop,
|
||||
logger: logger,
|
||||
}
|
||||
default:
|
||||
panic(F.ToString("unknown rule action: ", action.Action))
|
||||
@@ -104,7 +69,8 @@ func NewDNSRuleAction(logger logger.ContextLogger, action option.DNSRuleAction)
|
||||
}
|
||||
|
||||
type RuleActionRoute struct {
|
||||
Outbound string
|
||||
Outbound string
|
||||
UDPDisableDomainUnmapping bool
|
||||
}
|
||||
|
||||
func (r *RuleActionRoute) Type() string {
|
||||
@@ -115,26 +81,6 @@ func (r *RuleActionRoute) String() string {
|
||||
return F.ToString("route(", r.Outbound, ")")
|
||||
}
|
||||
|
||||
type RuleActionRouteOptions struct {
|
||||
UDPDisableDomainUnmapping bool
|
||||
UDPConnect bool
|
||||
}
|
||||
|
||||
func (r *RuleActionRouteOptions) Type() string {
|
||||
return C.RuleActionTypeRouteOptions
|
||||
}
|
||||
|
||||
func (r *RuleActionRouteOptions) String() string {
|
||||
var descriptions []string
|
||||
if r.UDPDisableDomainUnmapping {
|
||||
descriptions = append(descriptions, "udp-disable-domain-unmapping")
|
||||
}
|
||||
if r.UDPConnect {
|
||||
descriptions = append(descriptions, "udp-connect")
|
||||
}
|
||||
return F.ToString("route-options(", strings.Join(descriptions, ","), ")")
|
||||
}
|
||||
|
||||
type RuleActionDNSRoute struct {
|
||||
Server string
|
||||
DisableCache bool
|
||||
@@ -150,49 +96,18 @@ func (r *RuleActionDNSRoute) String() string {
|
||||
return F.ToString("route(", r.Server, ")")
|
||||
}
|
||||
|
||||
type RuleActionDNSRouteOptions struct {
|
||||
DisableCache bool
|
||||
RewriteTTL *uint32
|
||||
ClientSubnet netip.Prefix
|
||||
type RuleActionReturn struct{}
|
||||
|
||||
func (r *RuleActionReturn) Type() string {
|
||||
return C.RuleActionTypeReturn
|
||||
}
|
||||
|
||||
func (r *RuleActionDNSRouteOptions) Type() string {
|
||||
return C.RuleActionTypeRouteOptions
|
||||
}
|
||||
|
||||
func (r *RuleActionDNSRouteOptions) String() string {
|
||||
var descriptions []string
|
||||
if r.DisableCache {
|
||||
descriptions = append(descriptions, "disable-cache")
|
||||
}
|
||||
if r.RewriteTTL != nil {
|
||||
descriptions = append(descriptions, F.ToString("rewrite-ttl(", *r.RewriteTTL, ")"))
|
||||
}
|
||||
if r.ClientSubnet.IsValid() {
|
||||
descriptions = append(descriptions, F.ToString("client-subnet(", r.ClientSubnet, ")"))
|
||||
}
|
||||
return F.ToString("route-options(", strings.Join(descriptions, ","), ")")
|
||||
}
|
||||
|
||||
type RuleActionDirect struct {
|
||||
Dialer N.Dialer
|
||||
description string
|
||||
}
|
||||
|
||||
func (r *RuleActionDirect) Type() string {
|
||||
return C.RuleActionTypeDirect
|
||||
}
|
||||
|
||||
func (r *RuleActionDirect) String() string {
|
||||
return "direct" + r.description
|
||||
func (r *RuleActionReturn) String() string {
|
||||
return "return"
|
||||
}
|
||||
|
||||
type RuleActionReject struct {
|
||||
Method string
|
||||
NoDrop bool
|
||||
logger logger.ContextLogger
|
||||
dropAccess sync.Mutex
|
||||
dropCounter []time.Time
|
||||
Method string
|
||||
}
|
||||
|
||||
func (r *RuleActionReject) Type() string {
|
||||
@@ -206,30 +121,21 @@ func (r *RuleActionReject) String() string {
|
||||
return F.ToString("reject(", r.Method, ")")
|
||||
}
|
||||
|
||||
func (r *RuleActionReject) Error(ctx context.Context) error {
|
||||
var returnErr error
|
||||
func (r *RuleActionReject) Error() error {
|
||||
switch r.Method {
|
||||
case C.RuleActionRejectMethodDefault:
|
||||
returnErr = syscall.ECONNREFUSED
|
||||
case C.RuleActionRejectMethodReset:
|
||||
return os.ErrClosed
|
||||
case C.RuleActionRejectMethodNetworkUnreachable:
|
||||
return syscall.ENETUNREACH
|
||||
case C.RuleActionRejectMethodHostUnreachable:
|
||||
return syscall.EHOSTUNREACH
|
||||
case C.RuleActionRejectMethodDefault, C.RuleActionRejectMethodPortUnreachable:
|
||||
return syscall.ECONNREFUSED
|
||||
case C.RuleActionRejectMethodDrop:
|
||||
return tun.ErrDrop
|
||||
default:
|
||||
panic(F.ToString("unknown reject method: ", r.Method))
|
||||
}
|
||||
r.dropAccess.Lock()
|
||||
defer r.dropAccess.Unlock()
|
||||
timeNow := time.Now()
|
||||
r.dropCounter = common.Filter(r.dropCounter, func(t time.Time) bool {
|
||||
return timeNow.Sub(t) <= 30*time.Second
|
||||
})
|
||||
r.dropCounter = append(r.dropCounter, timeNow)
|
||||
if len(r.dropCounter) > 50 {
|
||||
if ctx != nil {
|
||||
r.logger.DebugContext(ctx, "dropped due to flooding")
|
||||
}
|
||||
return tun.ErrDrop
|
||||
}
|
||||
return returnErr
|
||||
}
|
||||
|
||||
type RuleActionHijackDNS struct{}
|
||||
|
||||
@@ -52,7 +52,7 @@ type RuleItem interface {
|
||||
}
|
||||
|
||||
func NewDefaultRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.DefaultRule) (*DefaultRule, error) {
|
||||
action, err := NewRuleAction(router, logger, options.RuleAction)
|
||||
action, err := NewRuleAction(options.RuleAction)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "action")
|
||||
}
|
||||
@@ -254,7 +254,7 @@ type LogicalRule struct {
|
||||
}
|
||||
|
||||
func NewLogicalRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.LogicalRule) (*LogicalRule, error) {
|
||||
action, err := NewRuleAction(router, logger, options.RuleAction)
|
||||
action, err := NewRuleAction(options.RuleAction)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "action")
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ func NewDefaultDNSRule(ctx context.Context, router adapter.Router, logger log.Co
|
||||
rule := &DefaultDNSRule{
|
||||
abstractDefaultRule: abstractDefaultRule{
|
||||
invert: options.Invert,
|
||||
action: NewDNSRuleAction(logger, options.DNSRuleAction),
|
||||
action: NewDNSRuleAction(options.DNSRuleAction),
|
||||
},
|
||||
}
|
||||
if len(options.Inbound) > 0 {
|
||||
@@ -287,7 +287,7 @@ func NewLogicalDNSRule(ctx context.Context, router adapter.Router, logger log.Co
|
||||
abstractLogicalRule: abstractLogicalRule{
|
||||
rules: make([]adapter.HeadlessRule, len(options.Rules)),
|
||||
invert: options.Invert,
|
||||
action: NewDNSRuleAction(logger, options.DNSRuleAction),
|
||||
action: NewDNSRuleAction(options.DNSRuleAction),
|
||||
},
|
||||
}
|
||||
switch options.Mode {
|
||||
|
||||
Reference in New Issue
Block a user