mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-13 02:27:19 +10:00
Compare commits
25 Commits
dev-next-w
...
v1.11.0-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61eff2c591 | ||
|
|
e4eacf32d8 | ||
|
|
903de67b4d | ||
|
|
6ec669cde5 | ||
|
|
8742761e11 | ||
|
|
322192a66b | ||
|
|
ec4bf64f45 | ||
|
|
4f99c669c9 | ||
|
|
0e5ba64f44 | ||
|
|
849e387d19 | ||
|
|
e45763d5ba | ||
|
|
5eb8522205 | ||
|
|
c2b833a228 | ||
|
|
7f65ab8166 | ||
|
|
84a102a6ef | ||
|
|
f1c76c4dde | ||
|
|
8df0aa5719 | ||
|
|
21faadb992 | ||
|
|
88099a304a | ||
|
|
f504fb0d46 | ||
|
|
1d517b6ca5 | ||
|
|
b702d0b67a | ||
|
|
a001e30d8b | ||
|
|
cdb93f0bb2 | ||
|
|
718cffea9a |
@@ -57,7 +57,9 @@ type InboundContext struct {
|
||||
// Deprecated
|
||||
InboundOptions option.InboundOptions
|
||||
UDPDisableDomainUnmapping bool
|
||||
DNSServer string
|
||||
UDPConnect bool
|
||||
|
||||
DNSServer string
|
||||
|
||||
DestinationAddresses []netip.Addr
|
||||
SourceGeoIPCode string
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
)
|
||||
|
||||
func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
defer conn.Close()
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
var outConn net.Conn
|
||||
var err error
|
||||
@@ -40,6 +41,7 @@ func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata a
|
||||
}
|
||||
|
||||
func NewDirectConnection(ctx context.Context, router adapter.Router, this N.Dialer, conn net.Conn, metadata adapter.InboundContext, domainStrategy dns.DomainStrategy) error {
|
||||
defer conn.Close()
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
var outConn net.Conn
|
||||
var err error
|
||||
@@ -67,29 +69,49 @@ 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 {
|
||||
defer conn.Close()
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
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)
|
||||
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
|
||||
}
|
||||
} else {
|
||||
outConn, err = this.ListenPacket(ctx, metadata.Destination)
|
||||
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)
|
||||
}
|
||||
}
|
||||
err = N.ReportPacketConnHandshakeSuccess(conn, outPacketConn)
|
||||
if err != nil {
|
||||
return N.ReportHandshakeFailure(conn, err)
|
||||
}
|
||||
err = N.ReportPacketConnHandshakeSuccess(conn, outConn)
|
||||
if err != nil {
|
||||
outConn.Close()
|
||||
outPacketConn.Close()
|
||||
return err
|
||||
}
|
||||
if destinationAddress.IsValid() {
|
||||
if metadata.Destination.IsFqdn() {
|
||||
if metadata.UDPDisableDomainUnmapping {
|
||||
outConn = bufio.NewUnidirectionalNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
|
||||
outPacketConn = bufio.NewUnidirectionalNATPacketConn(bufio.NewPacketConn(outPacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
|
||||
} else {
|
||||
outConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
|
||||
outPacketConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outPacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
|
||||
}
|
||||
}
|
||||
if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded {
|
||||
@@ -104,37 +126,63 @@ 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(outConn))
|
||||
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outPacketConn))
|
||||
}
|
||||
|
||||
func NewDirectPacketConnection(ctx context.Context, router adapter.Router, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext, domainStrategy dns.DomainStrategy) error {
|
||||
defer conn.Close()
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
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)
|
||||
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)
|
||||
}
|
||||
if err != nil {
|
||||
return N.ReportHandshakeFailure(conn, err)
|
||||
}
|
||||
outConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, destinationAddresses)
|
||||
connRemoteAddr := M.AddrFromNet(outConn.RemoteAddr())
|
||||
if connRemoteAddr != metadata.Destination.Addr {
|
||||
destinationAddress = connRemoteAddr
|
||||
}
|
||||
} else {
|
||||
outConn, err = this.ListenPacket(ctx, metadata.Destination)
|
||||
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)
|
||||
}
|
||||
}
|
||||
err = N.ReportPacketConnHandshakeSuccess(conn, outPacketConn)
|
||||
if err != nil {
|
||||
return N.ReportHandshakeFailure(conn, err)
|
||||
}
|
||||
err = N.ReportPacketConnHandshakeSuccess(conn, outConn)
|
||||
if err != nil {
|
||||
outConn.Close()
|
||||
outPacketConn.Close()
|
||||
return err
|
||||
}
|
||||
if destinationAddress.IsValid() {
|
||||
if metadata.Destination.IsFqdn() {
|
||||
outConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
|
||||
outPacketConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outPacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
|
||||
}
|
||||
if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded {
|
||||
natConn.UpdateDestination(destinationAddress)
|
||||
@@ -148,7 +196,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(outConn))
|
||||
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outPacketConn))
|
||||
}
|
||||
|
||||
func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) error {
|
||||
|
||||
@@ -58,7 +58,7 @@ func FindSDK() {
|
||||
}
|
||||
|
||||
func findNDK() bool {
|
||||
const fixedVersion = "27.2.12479018"
|
||||
const fixedVersion = "26.3.11579264"
|
||||
const versionFile = "source.properties"
|
||||
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.IsFile(filepath.Join(fixedPath, versionFile)) {
|
||||
androidNDKPath = fixedPath
|
||||
|
||||
@@ -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.NewEnvManager(log.StdLogger()))
|
||||
globalCtx = service.ContextWith(globalCtx, deprecated.NewStderrManager(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(context.Background())
|
||||
ctx, cancel := context.WithCancel(globalCtx)
|
||||
instance, err := box.New(box.Options{
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
|
||||
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -39,7 +38,7 @@ func format() error {
|
||||
return err
|
||||
}
|
||||
for _, optionsEntry := range optionsList {
|
||||
optionsEntry.options, err = badjson.Omitempty(context.TODO(), optionsEntry.options)
|
||||
optionsEntry.options, err = badjson.Omitempty(globalCtx, optionsEntry.options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package dialer
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -102,7 +103,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDi
|
||||
udpAddr4 string
|
||||
)
|
||||
if options.Inet4BindAddress != nil {
|
||||
bindAddr := options.Inet4BindAddress.Build()
|
||||
bindAddr := options.Inet4BindAddress.Build(netip.IPv4Unspecified())
|
||||
dialer4.LocalAddr = &net.TCPAddr{IP: bindAddr.AsSlice()}
|
||||
udpDialer4.LocalAddr = &net.UDPAddr{IP: bindAddr.AsSlice()}
|
||||
udpAddr4 = M.SocksaddrFrom(bindAddr, 0).String()
|
||||
@@ -113,7 +114,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDi
|
||||
udpAddr6 string
|
||||
)
|
||||
if options.Inet6BindAddress != nil {
|
||||
bindAddr := options.Inet6BindAddress.Build()
|
||||
bindAddr := options.Inet6BindAddress.Build(netip.IPv6Unspecified())
|
||||
dialer6.LocalAddr = &net.TCPAddr{IP: bindAddr.AsSlice()}
|
||||
udpDialer6.LocalAddr = &net.UDPAddr{IP: bindAddr.AsSlice()}
|
||||
udpAddr6 = M.SocksaddrFrom(bindAddr, 0).String()
|
||||
|
||||
@@ -3,6 +3,7 @@ package listener
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -92,7 +93,7 @@ func (l *Listener) Start() error {
|
||||
if l.setSystemProxy {
|
||||
listenPort := M.SocksaddrFromNet(l.tcpListener.Addr()).Port
|
||||
var listenAddrString string
|
||||
listenAddr := l.listenOptions.Listen.Build()
|
||||
listenAddr := l.listenOptions.Listen.Build(netip.IPv4Unspecified())
|
||||
if listenAddr.IsUnspecified() {
|
||||
listenAddrString = "127.0.0.1"
|
||||
} else {
|
||||
|
||||
@@ -2,6 +2,7 @@ package listener
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -16,7 +17,7 @@ import (
|
||||
|
||||
func (l *Listener) ListenTCP() (net.Listener, error) {
|
||||
var err error
|
||||
bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(), l.listenOptions.ListenPort)
|
||||
bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(netip.AddrFrom4([4]byte{127, 0, 0, 1})), l.listenOptions.ListenPort)
|
||||
var tcpListener net.Listener
|
||||
var listenConfig net.ListenConfig
|
||||
if l.listenOptions.TCPKeepAlive >= 0 {
|
||||
|
||||
@@ -2,6 +2,7 @@ package listener
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
@@ -13,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
func (l *Listener) ListenUDP() (net.PacketConn, error) {
|
||||
bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(), l.listenOptions.ListenPort)
|
||||
bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(netip.AddrFrom4([4]byte{127, 0, 0, 1})), l.listenOptions.ListenPort)
|
||||
var lc net.ListenConfig
|
||||
var udpFragment bool
|
||||
if l.listenOptions.UDPFragment != nil {
|
||||
@@ -48,9 +49,9 @@ func (l *Listener) loopUDPIn() {
|
||||
if !l.threadUnsafePacketWriter {
|
||||
buffer = buf.NewPacket()
|
||||
defer buffer.Release()
|
||||
buffer.IncRef()
|
||||
defer buffer.DecRef()
|
||||
}
|
||||
buffer.IncRef()
|
||||
defer buffer.DecRef()
|
||||
if l.oobPacketHandler != nil {
|
||||
oob := make([]byte, 1024)
|
||||
for {
|
||||
|
||||
@@ -24,19 +24,16 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
RuleActionTypeRoute = "route"
|
||||
RuleActionTypeReturn = "return"
|
||||
RuleActionTypeReject = "reject"
|
||||
RuleActionTypeHijackDNS = "hijack-dns"
|
||||
RuleActionTypeSniff = "sniff"
|
||||
RuleActionTypeResolve = "resolve"
|
||||
RuleActionTypeRoute = "route"
|
||||
RuleActionTypeRouteOptions = "route-options"
|
||||
RuleActionTypeDirect = "direct"
|
||||
RuleActionTypeReject = "reject"
|
||||
RuleActionTypeHijackDNS = "hijack-dns"
|
||||
RuleActionTypeSniff = "sniff"
|
||||
RuleActionTypeResolve = "resolve"
|
||||
)
|
||||
|
||||
const (
|
||||
RuleActionRejectMethodDefault = "default"
|
||||
RuleActionRejectMethodReset = "reset"
|
||||
RuleActionRejectMethodNetworkUnreachable = "network-unreachable"
|
||||
RuleActionRejectMethodHostUnreachable = "host-unreachable"
|
||||
RuleActionRejectMethodPortUnreachable = "port-unreachable"
|
||||
RuleActionRejectMethodDrop = "drop"
|
||||
RuleActionRejectMethodDefault = "default"
|
||||
RuleActionRejectMethodDrop = "drop"
|
||||
)
|
||||
|
||||
@@ -2,10 +2,56 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.11.0-alpha.5
|
||||
#### 1.11.0-alpha.10
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.11.0-alpha.9
|
||||
|
||||
* Improve tun compatibility **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
When `gvisor` tun stack is enabled, even if the request passes routing,
|
||||
if the outbound connection establishment fails,
|
||||
the connection still does not need to be established and a TCP RST is replied.
|
||||
|
||||
#### 1.11.0-alpha.7
|
||||
|
||||
* 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
|
||||
|
||||
* Add warnings for usage of deprecated features
|
||||
@@ -47,7 +93,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,7 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.9.0"
|
||||
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.9.0 中的更改"
|
||||
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
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)
|
||||
@@ -14,7 +22,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"
|
||||
@@ -135,19 +143,15 @@ icon: material/new-box
|
||||
"outbound": [
|
||||
"direct"
|
||||
],
|
||||
"server": "local",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": 100,
|
||||
"client_subnet": "127.0.0.1/24"
|
||||
"action": "route",
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
"mode": "and",
|
||||
"rules": [],
|
||||
"server": "local",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": 100,
|
||||
"client_subnet": "127.0.0.1/24"
|
||||
"action": "route",
|
||||
"server": "local"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -218,7 +222,7 @@ Match domain using regular expression.
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.8.0"
|
||||
|
||||
Geosite is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
||||
Geosite is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
||||
|
||||
Match geosite.
|
||||
|
||||
@@ -226,7 +230,7 @@ Match geosite.
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.8.0"
|
||||
|
||||
GeoIP is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
GeoIP is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
|
||||
Match source geoip.
|
||||
|
||||
@@ -354,29 +358,35 @@ Match outbound.
|
||||
|
||||
`any` can be used as a value to match any outbound.
|
||||
|
||||
#### server
|
||||
#### action
|
||||
|
||||
==Required==
|
||||
|
||||
Tag of the target dns server.
|
||||
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).
|
||||
|
||||
#### disable_cache
|
||||
|
||||
Disable cache and save cache in this query.
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Moved to [DNS Rule Action](../rule_action#route).
|
||||
|
||||
#### rewrite_ttl
|
||||
|
||||
Rewrite TTL in DNS responses.
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Moved to [DNS Rule Action](../rule_action#route).
|
||||
|
||||
#### client_subnet
|
||||
|
||||
!!! question "Since sing-box 1.9.0"
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Append a `edns0-subnet` OPT extra record with the specified IP prefix to every query by default.
|
||||
|
||||
If value is an IP address instead of prefix, `/32` or `/128` will be appended automatically.
|
||||
|
||||
Will overrides `dns.client_subnet` and `servers.[].client_subnet`.
|
||||
Moved to [DNS Rule Action](../rule_action#route).
|
||||
|
||||
### 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 中的更改"
|
||||
|
||||
85
docs/configuration/dns/rule_action.md
Normal file
85
docs/configuration/dns/rule_action.md
Normal file
@@ -0,0 +1,85 @@
|
||||
---
|
||||
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.
|
||||
86
docs/configuration/dns/rule_action.zh.md
Normal file
86
docs/configuration/dns/rule_action.zh.md
Normal file
@@ -0,0 +1,86 @@
|
||||
---
|
||||
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,7 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.9.0"
|
||||
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.9.0 中的更改"
|
||||
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
|
||||
!!! quote "Changes in sing-box 1.9.0"
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! question "自 sing-box 1.8.0 起"
|
||||
|
||||
!!! quote "sing-box 1.9.0 中的更改"
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
`block` outbound closes all incoming requests.
|
||||
---
|
||||
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).
|
||||
|
||||
### Structure
|
||||
|
||||
```json
|
||||
```json F
|
||||
{
|
||||
"type": "block",
|
||||
"tag": "block"
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
icon: material/delete-clock
|
||||
---
|
||||
|
||||
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||
|
||||
旧的特殊出站已被弃用,且将在 sing-box 1.13.0 中被移除,参阅 [迁移指南](/migration/#migrate-legacy-special-outbounds-to-rule-actions).
|
||||
|
||||
`block` 出站关闭所有传入请求。
|
||||
|
||||
### 结构
|
||||
@@ -11,4 +19,4 @@
|
||||
|
||||
### 字段
|
||||
|
||||
无字段。
|
||||
无字段。
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
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,3 +1,11 @@
|
||||
---
|
||||
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 may be removed in the future, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
GeoIP is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
|
||||
### Structure
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ icon: material/delete-clock
|
||||
|
||||
!!! failure "已在 sing-box 1.8.0 废弃"
|
||||
|
||||
GeoIP 已废弃且可能在不久的将来移除,参阅 [迁移指南](/zh/migration/#geoip)。
|
||||
GeoIP 已废弃且将在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/zh/migration/#geoip)。
|
||||
|
||||
### 结构
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ icon: material/delete-clock
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.8.0"
|
||||
|
||||
Geosite is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
||||
Geosite is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
||||
|
||||
### Structure
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ icon: material/delete-clock
|
||||
|
||||
!!! failure "已在 sing-box 1.8.0 废弃"
|
||||
|
||||
Geosite 已废弃且可能在不久的将来移除,参阅 [迁移指南](/zh/migration/#geosite)。
|
||||
Geosite 已废弃且将在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/zh/migration/#geosite)。
|
||||
|
||||
### 结构
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! 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)
|
||||
@@ -129,6 +134,7 @@ icon: material/alert-decagram
|
||||
"rule_set_ipcidr_match_source": false,
|
||||
"rule_set_ip_cidr_match_source": false,
|
||||
"invert": false,
|
||||
"action": "route",
|
||||
"outbound": "direct"
|
||||
},
|
||||
{
|
||||
@@ -136,6 +142,7 @@ icon: material/alert-decagram
|
||||
"mode": "and",
|
||||
"rules": [],
|
||||
"invert": false,
|
||||
"action": "route",
|
||||
"outbound": "direct"
|
||||
}
|
||||
]
|
||||
@@ -209,7 +216,7 @@ Match domain using regular expression.
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.8.0"
|
||||
|
||||
Geosite is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
||||
Geosite is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
||||
|
||||
Match geosite.
|
||||
|
||||
@@ -217,7 +224,7 @@ Match geosite.
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.8.0"
|
||||
|
||||
GeoIP is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
GeoIP is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
|
||||
Match source geoip.
|
||||
|
||||
@@ -225,7 +232,7 @@ Match source geoip.
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.8.0"
|
||||
|
||||
GeoIP is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
GeoIP is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
|
||||
Match geoip.
|
||||
|
||||
@@ -357,11 +364,17 @@ Make `ip_cidr` in rule-sets match the source IP.
|
||||
|
||||
Invert match result.
|
||||
|
||||
#### outbound
|
||||
#### action
|
||||
|
||||
==Required==
|
||||
|
||||
Tag of the target outbound.
|
||||
See [Rule Actions](../rule_action/) for details.
|
||||
|
||||
#### outbound
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Moved to [Rule Action](../rule_action#route).
|
||||
|
||||
### Logical Fields
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
|
||||
:material-plus: [action](#action)
|
||||
:material-alert: [outbound](#outbound)
|
||||
|
||||
!!! quote "sing-box 1.10.0 中的更改"
|
||||
|
||||
:material-plus: [client](#client)
|
||||
@@ -127,6 +132,7 @@ icon: material/alert-decagram
|
||||
"rule_set_ipcidr_match_source": false,
|
||||
"rule_set_ip_cidr_match_source": false,
|
||||
"invert": false,
|
||||
"action": "route",
|
||||
"outbound": "direct"
|
||||
},
|
||||
{
|
||||
@@ -134,6 +140,7 @@ icon: material/alert-decagram
|
||||
"mode": "and",
|
||||
"rules": [],
|
||||
"invert": false,
|
||||
"action": "route",
|
||||
"outbound": "direct"
|
||||
}
|
||||
]
|
||||
@@ -355,11 +362,17 @@ icon: material/alert-decagram
|
||||
|
||||
反选匹配结果。
|
||||
|
||||
#### outbound
|
||||
#### action
|
||||
|
||||
==必填==
|
||||
|
||||
目标出站的标签。
|
||||
参阅 [规则行动](../rule_action/)。
|
||||
|
||||
#### outbound
|
||||
|
||||
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||
|
||||
已移动到 [规则行动](../rule_action#route).
|
||||
|
||||
### 逻辑字段
|
||||
|
||||
|
||||
139
docs/configuration/route/rule_action.md
Normal file
139
docs/configuration/route/rule_action.md
Normal file
@@ -0,0 +1,139 @@
|
||||
---
|
||||
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.
|
||||
136
docs/configuration/route/rule_action.zh.md
Normal file
136
docs/configuration/route/rule_action.zh.md
Normal file
@@ -0,0 +1,136 @@
|
||||
---
|
||||
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,3 +1,15 @@
|
||||
---
|
||||
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
|
||||
@@ -68,24 +80,40 @@ 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.
|
||||
@@ -94,6 +122,10 @@ 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,3 +1,15 @@
|
||||
---
|
||||
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
|
||||
@@ -69,24 +81,40 @@ 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。
|
||||
@@ -95,6 +123,10 @@ 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,6 +4,32 @@ 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
|
||||
@@ -12,7 +38,7 @@ icon: material/delete-alert
|
||||
`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.
|
||||
Old fields will be removed in sing-box 1.12.0.
|
||||
|
||||
#### Match source rule items are renamed
|
||||
|
||||
@@ -32,7 +58,7 @@ check [Migration](/migration/#migrate-cache-file-from-clash-api-to-independent-o
|
||||
|
||||
#### GeoIP
|
||||
|
||||
GeoIP is deprecated and may be removed in the future.
|
||||
GeoIP is deprecated and will be removed in sing-box 1.12.0.
|
||||
|
||||
The maxmind GeoIP National Database, as an IP classification database,
|
||||
is not entirely suitable for traffic bypassing,
|
||||
@@ -43,7 +69,7 @@ check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
|
||||
#### Geosite
|
||||
|
||||
Geosite is deprecated and may be removed in the future.
|
||||
Geosite is deprecated and will be removed in sing-box 1.12.0.
|
||||
|
||||
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,6 +4,29 @@ 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 规则项已重命名
|
||||
@@ -17,7 +40,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 的支持
|
||||
|
||||
@@ -32,7 +55,7 @@ Clash API 中的 `cache_file` 及相关功能已废弃且已迁移到独立的 `
|
||||
|
||||
#### GeoIP
|
||||
|
||||
GeoIP 已废弃且可能在不久的将来移除。
|
||||
GeoIP 已废弃且将在 sing-box 1.12.0 中被移除。
|
||||
|
||||
maxmind GeoIP 国家数据库作为 IP 分类数据库,不完全适合流量绕过,
|
||||
且现有的实现均存在内存使用大与管理困难的问题。
|
||||
@@ -42,7 +65,7 @@ sing-box 1.8.0 引入了[规则集](/configuration/rule-set/),
|
||||
|
||||
#### Geosite
|
||||
|
||||
Geosite 已废弃且可能在不久的将来移除。
|
||||
Geosite 已废弃且将在 sing-box 1.12.0 中被移除。
|
||||
|
||||
Geosite,即由 V2Ray 维护的 domain-list-community 项目,作为早期流量绕过解决方案,
|
||||
存在着包括缺少维护、规则不准确和管理困难内的大量问题。
|
||||
|
||||
@@ -2,6 +2,212 @@
|
||||
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": "hijack-dns"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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
|
||||
@@ -10,8 +216,6 @@ icon: material/arrange-bring-forward
|
||||
`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,6 +2,212 @@
|
||||
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": "hijack-dns"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 迁移旧的入站字段到规则动作
|
||||
|
||||
入站选项已被弃用,且可以被规则动作替代。
|
||||
|
||||
!!! 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 地址字段已合并
|
||||
|
||||
@@ -24,10 +24,10 @@ func (n Note) Impending() bool {
|
||||
return false
|
||||
}
|
||||
versionMinor := semver.Compare(semver.MajorMinor("v"+C.Version), "v"+n.ScheduledVersion)
|
||||
if versionMinor < 0 {
|
||||
if semver.Prerelease("v"+C.Version) == "" && versionMinor > 0 {
|
||||
panic("invalid deprecated note: " + n.Name)
|
||||
}
|
||||
return versionMinor <= 1
|
||||
return versionMinor >= -1
|
||||
}
|
||||
|
||||
func (n Note) Message() string {
|
||||
@@ -78,9 +78,36 @@ 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,15 +7,23 @@ import (
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
type envManager struct {
|
||||
logger logger.Logger
|
||||
type stderrManager struct {
|
||||
logger logger.Logger
|
||||
reported map[string]bool
|
||||
}
|
||||
|
||||
func NewEnvManager(logger logger.Logger) Manager {
|
||||
return &envManager{logger: logger}
|
||||
func NewStderrManager(logger logger.Logger) Manager {
|
||||
return &stderrManager{
|
||||
logger: logger,
|
||||
reported: make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *envManager) ReportDeprecated(feature Note) {
|
||||
func (f *stderrManager) ReportDeprecated(feature Note) {
|
||||
if f.reported[feature.Name] {
|
||||
return
|
||||
}
|
||||
f.reported[feature.Name] = true
|
||||
if !feature.Impending() {
|
||||
f.logger.Warn(feature.MessageWithLink())
|
||||
return
|
||||
@@ -20,7 +20,6 @@ type CommandClient struct {
|
||||
type CommandClientOptions struct {
|
||||
Command int32
|
||||
StatusInterval int64
|
||||
IsMainClient bool
|
||||
}
|
||||
|
||||
type CommandClientHandler interface {
|
||||
@@ -29,7 +28,6 @@ type CommandClientHandler interface {
|
||||
ClearLogs()
|
||||
WriteLogs(messageList StringIterator)
|
||||
WriteStatus(message *StatusMessage)
|
||||
OpenURL(url string)
|
||||
WriteGroups(message OutboundGroupIterator)
|
||||
InitializeClashMode(modeList StringIterator, currentMode string)
|
||||
UpdateClashMode(newMode string)
|
||||
@@ -93,13 +91,9 @@ 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 header")
|
||||
return E.Cause(err, "write interval")
|
||||
}
|
||||
c.handler.Connected()
|
||||
go c.handleStatusConn(conn)
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
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,7 +33,6 @@ type CommandServer struct {
|
||||
urlTestUpdate chan struct{}
|
||||
modeUpdate chan struct{}
|
||||
logReset chan struct{}
|
||||
events chan myEvent
|
||||
|
||||
closedConnections []Connection
|
||||
}
|
||||
@@ -53,7 +52,6 @@ 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
|
||||
@@ -63,12 +61,6 @@ 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,7 +1,6 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"runtime"
|
||||
@@ -10,15 +9,9 @@ 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
|
||||
@@ -51,73 +44,31 @@ 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)
|
||||
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()
|
||||
}
|
||||
for {
|
||||
err = binary.Write(conn, binary.BigEndian, s.readStatus())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} 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:
|
||||
}
|
||||
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(reader, binary.BigEndian, &message)
|
||||
err := binary.Read(conn, binary.BigEndian, &message)
|
||||
if err != nil {
|
||||
c.handler.Disconnected(err.Error())
|
||||
return
|
||||
|
||||
@@ -30,11 +30,12 @@ func parseConfig(ctx context.Context, configContent string) (option.Options, err
|
||||
}
|
||||
|
||||
func CheckConfig(configContent string) error {
|
||||
options, err := parseConfig(box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry()), configContent)
|
||||
ctx := box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry())
|
||||
options, err := parseConfig(ctx, configContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
ctx = service.ContextWith[platform.Interface](ctx, (*platformInterfaceStub)(nil))
|
||||
instance, err := box.New(box.Options{
|
||||
@@ -57,7 +58,7 @@ func (s *platformInterfaceStub) UsePlatformAutoDetectInterfaceControl() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *platformInterfaceStub) AutoDetectInterfaceControl() control.Func {
|
||||
func (s *platformInterfaceStub) AutoDetectInterfaceControl(fd int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -137,7 +138,8 @@ func (s *interfaceMonitorStub) RegisterCallback(callback tun.DefaultInterfaceUpd
|
||||
func (s *interfaceMonitorStub) UnregisterCallback(element *list.Element[tun.DefaultInterfaceUpdateCallback]) {
|
||||
}
|
||||
|
||||
func (s *platformInterfaceStub) OpenURL(url string) {
|
||||
func (s *platformInterfaceStub) SendNotification(notification *platform.Notification) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func FormatConfig(configContent string) (string, error) {
|
||||
|
||||
@@ -4,27 +4,28 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||
"github.com/sagernet/sing/common"
|
||||
)
|
||||
|
||||
var _ deprecated.Manager = (*deprecatedManager)(nil)
|
||||
|
||||
type deprecatedManager struct {
|
||||
access sync.Mutex
|
||||
features []deprecated.Note
|
||||
access sync.Mutex
|
||||
notes []deprecated.Note
|
||||
}
|
||||
|
||||
func (m *deprecatedManager) ReportDeprecated(feature deprecated.Note) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.features = append(m.features, feature)
|
||||
m.notes = common.Uniq(append(m.notes, feature))
|
||||
}
|
||||
|
||||
func (m *deprecatedManager) Get() []deprecated.Note {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
features := m.features
|
||||
m.features = nil
|
||||
return features
|
||||
notes := m.notes
|
||||
m.notes = nil
|
||||
return notes
|
||||
}
|
||||
|
||||
var _ = deprecated.Note(DeprecatedNote{})
|
||||
|
||||
30
experimental/libbox/link_flags_linux.go
Normal file
30
experimental/libbox/link_flags_linux.go
Normal file
@@ -0,0 +1,30 @@
|
||||
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
|
||||
}
|
||||
11
experimental/libbox/link_flags_stub.go
Normal file
11
experimental/libbox/link_flags_stub.go
Normal file
@@ -0,0 +1,11 @@
|
||||
//go:build !linux
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func linkFlags(rawFlags uint32) net.Flags {
|
||||
panic("stub!")
|
||||
}
|
||||
@@ -22,6 +22,7 @@ type PlatformInterface interface {
|
||||
IncludeAllNetworks() bool
|
||||
ReadWIFIState() *WIFIState
|
||||
ClearDNSCache()
|
||||
SendNotification(notification *Notification) error
|
||||
}
|
||||
|
||||
type TunInterface interface {
|
||||
@@ -38,6 +39,7 @@ type NetworkInterface struct {
|
||||
MTU int32
|
||||
Name string
|
||||
Addresses StringIterator
|
||||
Flags int32
|
||||
}
|
||||
|
||||
type WIFIState struct {
|
||||
@@ -54,6 +56,16 @@ 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() control.Func
|
||||
AutoDetectInterfaceControl(fd int) error
|
||||
OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
||||
UsePlatformDefaultInterfaceMonitor() bool
|
||||
CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor
|
||||
@@ -25,5 +25,15 @@ type Interface interface {
|
||||
ClearDNSCache()
|
||||
ReadWIFIState() adapter.WIFIState
|
||||
process.Searcher
|
||||
OpenURL(url string)
|
||||
SendNotification(notification *Notification) error
|
||||
}
|
||||
|
||||
type Notification struct {
|
||||
Identifier string
|
||||
TypeName string
|
||||
TypeID int32
|
||||
Title string
|
||||
Subtitle string
|
||||
Body string
|
||||
OpenURL 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,7 +69,6 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
instance: instance,
|
||||
platformInterface: platformWrapper,
|
||||
urlTestHistoryStorage: urlTestHistoryStorage,
|
||||
pauseManager: service.FromContext[pause.Manager](ctx),
|
||||
}, nil
|
||||
@@ -105,10 +104,9 @@ var (
|
||||
)
|
||||
|
||||
type platformInterfaceWrapper struct {
|
||||
iif PlatformInterface
|
||||
useProcFS bool
|
||||
router adapter.Router
|
||||
openURLFunc func(url string)
|
||||
iif PlatformInterface
|
||||
useProcFS bool
|
||||
router adapter.Router
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) Initialize(ctx context.Context, router adapter.Router) error {
|
||||
@@ -120,12 +118,8 @@ func (w *platformInterfaceWrapper) UsePlatformAutoDetectInterfaceControl() bool
|
||||
return w.iif.UsePlatformAutoDetectInterfaceControl()
|
||||
}
|
||||
|
||||
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) AutoDetectInterfaceControl(fd int) error {
|
||||
return w.iif.AutoDetectInterfaceControl(int32(fd))
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
|
||||
@@ -183,6 +177,7 @@ 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
|
||||
@@ -243,8 +238,6 @@ func (w *platformInterfaceWrapper) WriteMessage(level log.Level, message string)
|
||||
w.iif.WriteLog(message)
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) OpenURL(url string) {
|
||||
if w.openURLFunc != nil {
|
||||
w.openURLFunc(url)
|
||||
}
|
||||
func (w *platformInterfaceWrapper) SendNotification(notification *platform.Notification) error {
|
||||
return w.iif.SendNotification((*Notification)(notification))
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ var (
|
||||
|
||||
func init() {
|
||||
debug.SetPanicOnFault(true)
|
||||
debug.SetTraceback("all")
|
||||
}
|
||||
|
||||
func Setup(basePath string, workingPath string, tempPath string, isTVOS bool) {
|
||||
|
||||
24
go.mod
24
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.0-beta.1
|
||||
github.com/sagernet/quic-go v0.48.1-beta.1
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
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.1
|
||||
github.com/sagernet/sing v0.6.0-alpha.3
|
||||
github.com/sagernet/sing-dns v0.4.0-alpha.1
|
||||
github.com/sagernet/sing-mux v0.3.0-alpha.1
|
||||
github.com/sagernet/sing-quic v0.3.0-rc.2
|
||||
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.4.0.20241023054150-3b5b396d06f7
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.3
|
||||
github.com/sagernet/sing-vmess v0.1.12
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||
github.com/sagernet/utls v1.6.7
|
||||
@@ -42,17 +42,19 @@ require (
|
||||
github.com/stretchr/testify v1.9.0
|
||||
go.uber.org/zap v1.27.0
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.28.0
|
||||
golang.org/x/crypto v0.29.0
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
||||
golang.org/x/mod v0.20.0
|
||||
golang.org/x/net v0.30.0
|
||||
golang.org/x/sys v0.26.0
|
||||
golang.org/x/net v0.31.0
|
||||
golang.org/x/sys v0.27.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||
google.golang.org/grpc v1.63.2
|
||||
google.golang.org/protobuf v1.33.0
|
||||
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
|
||||
@@ -86,8 +88,8 @@ require (
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
|
||||
46
go.sum
46
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.0-beta.1 h1:86hQZrmuoARI3BpDRkQaP0iAVpywA4YsRrzJPYuPKWg=
|
||||
github.com/sagernet/quic-go v0.48.0-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k=
|
||||
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/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.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.1 h1:SlzL1yfEAKJyRduub8vzOVtbyTLAX7RZEEBZxO5utts=
|
||||
github.com/sagernet/sing-quic v0.3.0-rc.1/go.mod h1:uX+aUHA0fgIN6U3WViseDpSdTQriViZ7qz0Wbsf1mNQ=
|
||||
github.com/sagernet/sing v0.6.0-alpha.3 h1:GLp9d6Gbt+Ioeplauuzojz1nY2J6moceVGYIOv/h5gA=
|
||||
github.com/sagernet/sing v0.6.0-alpha.3/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-dns v0.4.0-alpha.1 h1:2KlP8DeqtGkULFiZtvG2r7SuoJP6orANFzJwC5vDKvg=
|
||||
github.com/sagernet/sing-dns v0.4.0-alpha.1/go.mod h1:vgHATsm4wdymwpvBZPei8RY+546iGXS6hlWv2x6YKcM=
|
||||
github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg=
|
||||
github.com/sagernet/sing-mux v0.3.0-alpha.1/go.mod h1:FTcImmdfW38Lz7b+HQ+mxxOth1lz4ao8uEnz+MwIJQE=
|
||||
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-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.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-tun v0.6.0-alpha.3 h1:KddmFF9ZYC1n+HZzpAZ1StMC5v38ir6hH0PJ7uGpAGE=
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.3/go.mod h1:R/UaKB1oFIvAeMIH2btQCaDVsfCNomEjfYBSCSA94sw=
|
||||
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=
|
||||
@@ -163,18 +163,18 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -182,14 +182,14 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
||||
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
||||
@@ -82,6 +82,7 @@ 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
|
||||
@@ -90,6 +91,7 @@ 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
|
||||
@@ -218,9 +220,11 @@ plugins:
|
||||
Log: 日志
|
||||
DNS Server: DNS 服务器
|
||||
DNS Rule: DNS 规则
|
||||
DNS Rule Action: DNS 规则动作
|
||||
|
||||
Route: 路由
|
||||
Route Rule: 路由规则
|
||||
Rule Action: 规则动作
|
||||
Protocol Sniff: 协议探测
|
||||
|
||||
Rule Set: 规则集
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package option
|
||||
|
||||
import "net/netip"
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
type DNSOptions struct {
|
||||
Servers []DNSServerOptions `json:"servers,omitempty"`
|
||||
@@ -12,22 +16,22 @@ type DNSOptions struct {
|
||||
}
|
||||
|
||||
type DNSServerOptions struct {
|
||||
Tag string `json:"tag,omitempty"`
|
||||
Address string `json:"address"`
|
||||
AddressResolver string `json:"address_resolver,omitempty"`
|
||||
AddressStrategy DomainStrategy `json:"address_strategy,omitempty"`
|
||||
AddressFallbackDelay Duration `json:"address_fallback_delay,omitempty"`
|
||||
Strategy DomainStrategy `json:"strategy,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
|
||||
Tag string `json:"tag,omitempty"`
|
||||
Address string `json:"address"`
|
||||
AddressResolver string `json:"address_resolver,omitempty"`
|
||||
AddressStrategy DomainStrategy `json:"address_strategy,omitempty"`
|
||||
AddressFallbackDelay badoption.Duration `json:"address_fallback_delay,omitempty"`
|
||||
Strategy DomainStrategy `json:"strategy,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
|
||||
}
|
||||
|
||||
type DNSClientOptions struct {
|
||||
Strategy DomainStrategy `json:"strategy,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
DisableExpire bool `json:"disable_expire,omitempty"`
|
||||
IndependentCache bool `json:"independent_cache,omitempty"`
|
||||
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
|
||||
Strategy DomainStrategy `json:"strategy,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
DisableExpire bool `json:"disable_expire,omitempty"`
|
||||
IndependentCache bool `json:"independent_cache,omitempty"`
|
||||
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
|
||||
}
|
||||
|
||||
type DNSFakeIPOptions struct {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package option
|
||||
|
||||
import "github.com/sagernet/sing/common/json/badoption"
|
||||
|
||||
type ExperimentalOptions struct {
|
||||
CacheFile *CacheFileOptions `json:"cache_file,omitempty"`
|
||||
ClashAPI *ClashAPIOptions `json:"clash_api,omitempty"`
|
||||
@@ -8,24 +10,24 @@ type ExperimentalOptions struct {
|
||||
}
|
||||
|
||||
type CacheFileOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
CacheID string `json:"cache_id,omitempty"`
|
||||
StoreFakeIP bool `json:"store_fakeip,omitempty"`
|
||||
StoreRDRC bool `json:"store_rdrc,omitempty"`
|
||||
RDRCTimeout Duration `json:"rdrc_timeout,omitempty"`
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
CacheID string `json:"cache_id,omitempty"`
|
||||
StoreFakeIP bool `json:"store_fakeip,omitempty"`
|
||||
StoreRDRC bool `json:"store_rdrc,omitempty"`
|
||||
RDRCTimeout badoption.Duration `json:"rdrc_timeout,omitempty"`
|
||||
}
|
||||
|
||||
type ClashAPIOptions struct {
|
||||
ExternalController string `json:"external_controller,omitempty"`
|
||||
ExternalUI string `json:"external_ui,omitempty"`
|
||||
ExternalUIDownloadURL string `json:"external_ui_download_url,omitempty"`
|
||||
ExternalUIDownloadDetour string `json:"external_ui_download_detour,omitempty"`
|
||||
Secret string `json:"secret,omitempty"`
|
||||
DefaultMode string `json:"default_mode,omitempty"`
|
||||
ModeList []string `json:"-"`
|
||||
AccessControlAllowOrigin Listable[string] `json:"access_control_allow_origin,omitempty"`
|
||||
AccessControlAllowPrivateNetwork bool `json:"access_control_allow_private_network,omitempty"`
|
||||
ExternalController string `json:"external_controller,omitempty"`
|
||||
ExternalUI string `json:"external_ui,omitempty"`
|
||||
ExternalUIDownloadURL string `json:"external_ui_download_url,omitempty"`
|
||||
ExternalUIDownloadDetour string `json:"external_ui_download_detour,omitempty"`
|
||||
Secret string `json:"secret,omitempty"`
|
||||
DefaultMode string `json:"default_mode,omitempty"`
|
||||
ModeList []string `json:"-"`
|
||||
AccessControlAllowOrigin badoption.Listable[string] `json:"access_control_allow_origin,omitempty"`
|
||||
AccessControlAllowPrivateNetwork bool `json:"access_control_allow_private_network,omitempty"`
|
||||
|
||||
// Deprecated: migrated to global cache file
|
||||
CacheFile string `json:"cache_file,omitempty"`
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package option
|
||||
|
||||
import "github.com/sagernet/sing/common/json/badoption"
|
||||
|
||||
type SelectorOutboundOptions struct {
|
||||
Outbounds []string `json:"outbounds"`
|
||||
Default string `json:"default,omitempty"`
|
||||
@@ -7,10 +9,10 @@ type SelectorOutboundOptions struct {
|
||||
}
|
||||
|
||||
type URLTestOutboundOptions struct {
|
||||
Outbounds []string `json:"outbounds"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Interval Duration `json:"interval,omitempty"`
|
||||
Tolerance uint16 `json:"tolerance,omitempty"`
|
||||
IdleTimeout Duration `json:"idle_timeout,omitempty"`
|
||||
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
||||
Outbounds []string `json:"outbounds"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Interval badoption.Duration `json:"interval,omitempty"`
|
||||
Tolerance uint16 `json:"tolerance,omitempty"`
|
||||
IdleTimeout badoption.Duration `json:"idle_timeout,omitempty"`
|
||||
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
@@ -33,7 +34,7 @@ func (h *Inbound) UnmarshalJSONContext(ctx context.Context, content []byte) erro
|
||||
}
|
||||
registry := service.FromContext[InboundOptionsRegistry](ctx)
|
||||
if registry == nil {
|
||||
return E.New("missing inbound options registry in context")
|
||||
return E.New("missing Inbound fields registry in context")
|
||||
}
|
||||
options, loaded := registry.CreateOptions(h.Type)
|
||||
if !loaded {
|
||||
@@ -49,24 +50,24 @@ func (h *Inbound) UnmarshalJSONContext(ctx context.Context, content []byte) erro
|
||||
|
||||
// Deprecated: Use rule action instead
|
||||
type InboundOptions struct {
|
||||
SniffEnabled bool `json:"sniff,omitempty"`
|
||||
SniffOverrideDestination bool `json:"sniff_override_destination,omitempty"`
|
||||
SniffTimeout Duration `json:"sniff_timeout,omitempty"`
|
||||
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
||||
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
SniffEnabled bool `json:"sniff,omitempty"`
|
||||
SniffOverrideDestination bool `json:"sniff_override_destination,omitempty"`
|
||||
SniffTimeout badoption.Duration `json:"sniff_timeout,omitempty"`
|
||||
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
||||
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
}
|
||||
|
||||
type ListenOptions struct {
|
||||
Listen *ListenAddress `json:"listen,omitempty"`
|
||||
ListenPort uint16 `json:"listen_port,omitempty"`
|
||||
TCPKeepAlive Duration `json:"tcp_keep_alive,omitempty"`
|
||||
TCPKeepAliveInterval Duration `json:"tcp_keep_alive_interval,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
||||
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
||||
UDPFragmentDefault bool `json:"-"`
|
||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
||||
Listen *badoption.Addr `json:"listen,omitempty"`
|
||||
ListenPort uint16 `json:"listen_port,omitempty"`
|
||||
TCPKeepAlive badoption.Duration `json:"tcp_keep_alive,omitempty"`
|
||||
TCPKeepAliveInterval badoption.Duration `json:"tcp_keep_alive_interval,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
||||
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
||||
UDPFragmentDefault bool `json:"-"`
|
||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
||||
|
||||
// Deprecated: removed
|
||||
ProxyProtocol bool `json:"proxy_protocol,omitempty"`
|
||||
@@ -75,7 +76,7 @@ type ListenOptions struct {
|
||||
InboundOptions
|
||||
}
|
||||
|
||||
type UDPTimeoutCompat Duration
|
||||
type UDPTimeoutCompat badoption.Duration
|
||||
|
||||
func (c UDPTimeoutCompat) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal((time.Duration)(c).String())
|
||||
@@ -88,7 +89,7 @@ func (c *UDPTimeoutCompat) UnmarshalJSON(data []byte) error {
|
||||
*c = UDPTimeoutCompat(time.Second * time.Duration(valueNumber))
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(data, (*Duration)(c))
|
||||
return json.Unmarshal(data, (*badoption.Duration)(c))
|
||||
}
|
||||
|
||||
type ListenOptionsWrapper interface {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package option
|
||||
|
||||
import "github.com/sagernet/sing/common/json/badoption"
|
||||
|
||||
type NTPOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Interval Duration `json:"interval,omitempty"`
|
||||
WriteToSystem bool `json:"write_to_system,omitempty"`
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Interval badoption.Duration `json:"interval,omitempty"`
|
||||
WriteToSystem bool `json:"write_to_system,omitempty"`
|
||||
ServerOptions
|
||||
DialerOptions
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ type _Options struct {
|
||||
Experimental *ExperimentalOptions `json:"experimental,omitempty"`
|
||||
|
||||
// Deprecated: use Inbounds instead
|
||||
LegacyInbounds []LegacyInbound `json:"inbound,omitempty"`
|
||||
LegacyInbounds []LegacyInbound `json:"-"`
|
||||
// Deprecated: use Outbounds instead
|
||||
LegacyOutbounds []LegacyOutbound `json:"_"`
|
||||
LegacyOutbounds []LegacyOutbound `json:"-"`
|
||||
}
|
||||
|
||||
type Options _Options
|
||||
|
||||
@@ -3,9 +3,12 @@ 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"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
@@ -35,6 +38,10 @@ 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)
|
||||
@@ -43,6 +50,11 @@ 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
|
||||
}
|
||||
@@ -53,21 +65,21 @@ type DialerOptionsWrapper interface {
|
||||
}
|
||||
|
||||
type DialerOptions struct {
|
||||
Detour string `json:"detour,omitempty"`
|
||||
BindInterface string `json:"bind_interface,omitempty"`
|
||||
Inet4BindAddress *ListenAddress `json:"inet4_bind_address,omitempty"`
|
||||
Inet6BindAddress *ListenAddress `json:"inet6_bind_address,omitempty"`
|
||||
ProtectPath string `json:"protect_path,omitempty"`
|
||||
RoutingMark uint32 `json:"routing_mark,omitempty"`
|
||||
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
||||
ConnectTimeout Duration `json:"connect_timeout,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
||||
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
||||
UDPFragmentDefault bool `json:"-"`
|
||||
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
||||
FallbackDelay Duration `json:"fallback_delay,omitempty"`
|
||||
IsWireGuardListener bool `json:"-"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
BindInterface string `json:"bind_interface,omitempty"`
|
||||
Inet4BindAddress *badoption.Addr `json:"inet4_bind_address,omitempty"`
|
||||
Inet6BindAddress *badoption.Addr `json:"inet6_bind_address,omitempty"`
|
||||
ProtectPath string `json:"protect_path,omitempty"`
|
||||
RoutingMark uint32 `json:"routing_mark,omitempty"`
|
||||
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
||||
ConnectTimeout badoption.Duration `json:"connect_timeout,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
||||
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
||||
UDPFragmentDefault bool `json:"-"`
|
||||
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
||||
FallbackDelay badoption.Duration `json:"fallback_delay,omitempty"`
|
||||
IsWireGuardListener bool `json:"-"`
|
||||
}
|
||||
|
||||
func (o *DialerOptions) TakeDialerOptions() DialerOptions {
|
||||
|
||||
@@ -3,6 +3,7 @@ package option
|
||||
import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
type OnDemandOptions struct {
|
||||
@@ -12,10 +13,10 @@ type OnDemandOptions struct {
|
||||
|
||||
type OnDemandRule struct {
|
||||
Action *OnDemandRuleAction `json:"action,omitempty"`
|
||||
DNSSearchDomainMatch Listable[string] `json:"dns_search_domain_match,omitempty"`
|
||||
DNSServerAddressMatch Listable[string] `json:"dns_server_address_match,omitempty"`
|
||||
DNSSearchDomainMatch badoption.Listable[string] `json:"dns_search_domain_match,omitempty"`
|
||||
DNSServerAddressMatch badoption.Listable[string] `json:"dns_server_address_match,omitempty"`
|
||||
InterfaceTypeMatch *OnDemandRuleInterfaceType `json:"interface_type_match,omitempty"`
|
||||
SSIDMatch Listable[string] `json:"ssid_match,omitempty"`
|
||||
SSIDMatch badoption.Listable[string] `json:"ssid_match,omitempty"`
|
||||
ProbeURL string `json:"probe_url,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
type _Rule struct {
|
||||
@@ -66,39 +67,39 @@ func (r Rule) IsValid() bool {
|
||||
}
|
||||
|
||||
type RawDefaultRule struct {
|
||||
Inbound Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
Network Listable[string] `json:"network,omitempty"`
|
||||
AuthUser Listable[string] `json:"auth_user,omitempty"`
|
||||
Protocol Listable[string] `json:"protocol,omitempty"`
|
||||
Client Listable[string] `json:"client,omitempty"`
|
||||
Domain Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP Listable[string] `json:"source_geoip,omitempty"`
|
||||
GeoIP Listable[string] `json:"geoip,omitempty"`
|
||||
SourceIPCIDR Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||
IPCIDR Listable[string] `json:"ip_cidr,omitempty"`
|
||||
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||
SourcePort Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName Listable[string] `json:"package_name,omitempty"`
|
||||
User Listable[string] `json:"user,omitempty"`
|
||||
UserID Listable[int32] `json:"user_id,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
WIFISSID Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
RuleSet Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Inbound badoption.Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"`
|
||||
Protocol badoption.Listable[string] `json:"protocol,omitempty"`
|
||||
Client badoption.Listable[string] `json:"client,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite badoption.Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"`
|
||||
GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
User badoption.Listable[string] `json:"user,omitempty"`
|
||||
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
|
||||
// Deprecated: renamed to rule_set_ip_cidr_match_source
|
||||
Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
|
||||
@@ -109,7 +110,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 +129,27 @@ func (r *DefaultRule) IsValid() bool {
|
||||
return !reflect.DeepEqual(r, defaultValue)
|
||||
}
|
||||
|
||||
type _LogicalRule struct {
|
||||
type RawLogicalRule struct {
|
||||
Mode string `json:"mode"`
|
||||
Rules []Rule `json:"rules,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
}
|
||||
|
||||
type LogicalRule struct {
|
||||
_LogicalRule
|
||||
RawLogicalRule
|
||||
RuleAction
|
||||
}
|
||||
|
||||
func (r *LogicalRule) MarshalJSON() ([]byte, error) {
|
||||
return badjson.MarshallObjects(r._LogicalRule, r.RuleAction)
|
||||
func (r LogicalRule) MarshalJSON() ([]byte, error) {
|
||||
return badjson.MarshallObjects(r.RawLogicalRule, r.RuleAction)
|
||||
}
|
||||
|
||||
func (r *LogicalRule) UnmarshalJSON(data []byte) error {
|
||||
err := json.Unmarshal(data, &r._LogicalRule)
|
||||
err := json.Unmarshal(data, &r.RawLogicalRule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return badjson.UnmarshallExcluded(data, &r._LogicalRule, &r.RuleAction)
|
||||
return badjson.UnmarshallExcluded(data, &r.RawLogicalRule, &r.RuleAction)
|
||||
}
|
||||
|
||||
func (r *LogicalRule) IsValid() bool {
|
||||
|
||||
@@ -1,30 +1,45 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"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"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
type _RuleAction struct {
|
||||
Action string `json:"action,omitempty"`
|
||||
RouteOptions RouteActionOptions `json:"-"`
|
||||
RejectOptions RejectActionOptions `json:"-"`
|
||||
SniffOptions RouteActionSniff `json:"-"`
|
||||
ResolveOptions RouteActionResolve `json:"-"`
|
||||
Action string `json:"action,omitempty"`
|
||||
RouteOptions RouteActionOptions `json:"-"`
|
||||
RouteOptionsOptions RouteOptionsActionOptions `json:"-"`
|
||||
DirectOptions DirectActionOptions `json:"-"`
|
||||
RejectOptions RejectActionOptions `json:"-"`
|
||||
SniffOptions RouteActionSniff `json:"-"`
|
||||
ResolveOptions RouteActionResolve `json:"-"`
|
||||
}
|
||||
|
||||
type RuleAction _RuleAction
|
||||
|
||||
func (r RuleAction) MarshalJSON() ([]byte, error) {
|
||||
if r.Action == "" {
|
||||
return json.Marshal(struct{}{})
|
||||
}
|
||||
var v any
|
||||
switch r.Action {
|
||||
case C.RuleActionTypeRoute:
|
||||
r.Action = ""
|
||||
v = r.RouteOptions
|
||||
case C.RuleActionTypeReturn:
|
||||
v = nil
|
||||
case C.RuleActionTypeRouteOptions:
|
||||
v = r.RouteOptionsOptions
|
||||
case C.RuleActionTypeDirect:
|
||||
v = r.DirectOptions
|
||||
case C.RuleActionTypeReject:
|
||||
v = r.RejectOptions
|
||||
case C.RuleActionTypeHijackDNS:
|
||||
@@ -52,8 +67,10 @@ func (r *RuleAction) UnmarshalJSON(data []byte) error {
|
||||
case "", C.RuleActionTypeRoute:
|
||||
r.Action = C.RuleActionTypeRoute
|
||||
v = &r.RouteOptions
|
||||
case C.RuleActionTypeReturn:
|
||||
v = nil
|
||||
case C.RuleActionTypeRouteOptions:
|
||||
v = &r.RouteOptionsOptions
|
||||
case C.RuleActionTypeDirect:
|
||||
v = &r.DirectOptions
|
||||
case C.RuleActionTypeReject:
|
||||
v = &r.RejectOptions
|
||||
case C.RuleActionTypeHijackDNS:
|
||||
@@ -73,35 +90,34 @@ func (r *RuleAction) UnmarshalJSON(data []byte) error {
|
||||
}
|
||||
|
||||
type _DNSRuleAction struct {
|
||||
Action string `json:"action,omitempty"`
|
||||
RouteOptions DNSRouteActionOptions `json:"-"`
|
||||
RejectOptions RejectActionOptions `json:"-"`
|
||||
SniffOptions RouteActionSniff `json:"-"`
|
||||
ResolveOptions RouteActionResolve `json:"-"`
|
||||
Action string `json:"action,omitempty"`
|
||||
RouteOptions DNSRouteActionOptions `json:"-"`
|
||||
RouteOptionsOptions DNSRouteOptionsActionOptions `json:"-"`
|
||||
RejectOptions RejectActionOptions `json:"-"`
|
||||
}
|
||||
|
||||
type DNSRuleAction _DNSRuleAction
|
||||
|
||||
func (r DNSRuleAction) MarshalJSON() ([]byte, error) {
|
||||
if r.Action == "" {
|
||||
return json.Marshal(struct{}{})
|
||||
}
|
||||
var v any
|
||||
switch r.Action {
|
||||
case C.RuleActionTypeRoute:
|
||||
r.Action = ""
|
||||
v = r.RouteOptions
|
||||
case C.RuleActionTypeReturn:
|
||||
v = nil
|
||||
case C.RuleActionTypeRouteOptions:
|
||||
v = r.RouteOptionsOptions
|
||||
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) UnmarshalJSON(data []byte) error {
|
||||
func (r *DNSRuleAction) UnmarshalJSONContext(ctx context.Context, data []byte) error {
|
||||
err := json.Unmarshal(data, (*_DNSRuleAction)(r))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -111,34 +127,146 @@ func (r *DNSRuleAction) UnmarshalJSON(data []byte) error {
|
||||
case "", C.RuleActionTypeRoute:
|
||||
r.Action = C.RuleActionTypeRoute
|
||||
v = &r.RouteOptions
|
||||
case C.RuleActionTypeReturn:
|
||||
v = nil
|
||||
case C.RuleActionTypeRouteOptions:
|
||||
v = &r.RouteOptionsOptions
|
||||
case C.RuleActionTypeReject:
|
||||
v = &r.RejectOptions
|
||||
default:
|
||||
return E.New("unknown DNS rule action: " + r.Action)
|
||||
}
|
||||
if v == nil {
|
||||
// check unknown fields
|
||||
return json.UnmarshalDisallowUnknownFields(data, &_DNSRuleAction{})
|
||||
return badjson.UnmarshallExcludedContext(ctx, data, (*_DNSRuleAction)(r), v)
|
||||
}
|
||||
|
||||
type _RouteActionOptions struct {
|
||||
Outbound string `json:"outbound,omitempty"`
|
||||
}
|
||||
|
||||
type RouteActionOptions _RouteActionOptions
|
||||
|
||||
func (r *RouteActionOptions) UnmarshalJSON(data []byte) error {
|
||||
err := json.Unmarshal(data, (*_RouteActionOptions)(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return badjson.UnmarshallExcluded(data, (*_DNSRuleAction)(r), v)
|
||||
return nil
|
||||
}
|
||||
|
||||
type RouteActionOptions struct {
|
||||
Outbound string `json:"outbound"`
|
||||
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
|
||||
type _RouteOptionsActionOptions struct {
|
||||
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
|
||||
UDPConnect bool `json:"udp_connect,omitempty"`
|
||||
}
|
||||
|
||||
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 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,omitempty"`
|
||||
// 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 *badoption.Prefixable `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 {
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||
ClientSubnet *badoption.Prefixable `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(netip.IPv4Unspecified()).String())
|
||||
}
|
||||
if d.Inet6BindAddress != nil {
|
||||
descriptions = append(descriptions, "inet6_bind_address="+d.Inet6BindAddress.Build(netip.IPv6Unspecified()).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
|
||||
@@ -151,20 +279,19 @@ func (r *RejectActionOptions) UnmarshalJSON(bytes []byte) error {
|
||||
switch r.Method {
|
||||
case "", C.RuleActionRejectMethodDefault:
|
||||
r.Method = C.RuleActionRejectMethodDefault
|
||||
case C.RuleActionRejectMethodReset,
|
||||
C.RuleActionRejectMethodNetworkUnreachable,
|
||||
C.RuleActionRejectMethodHostUnreachable,
|
||||
C.RuleActionRejectMethodPortUnreachable,
|
||||
C.RuleActionRejectMethodDrop:
|
||||
case 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
|
||||
}
|
||||
|
||||
type RouteActionSniff struct {
|
||||
Sniffer Listable[string] `json:"sniffer,omitempty"`
|
||||
Timeout Duration `json:"timeout,omitempty"`
|
||||
Sniffer badoption.Listable[string] `json:"sniffer,omitempty"`
|
||||
Timeout badoption.Duration `json:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
type RouteActionResolve struct {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
@@ -8,6 +9,7 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
type _DNSRule struct {
|
||||
@@ -32,7 +34,7 @@ func (r DNSRule) MarshalJSON() ([]byte, error) {
|
||||
return badjson.MarshallObjects((_DNSRule)(r), v)
|
||||
}
|
||||
|
||||
func (r *DNSRule) UnmarshalJSON(bytes []byte) error {
|
||||
func (r *DNSRule) UnmarshalJSONContext(ctx context.Context, bytes []byte) error {
|
||||
err := json.Unmarshal(bytes, (*_DNSRule)(r))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -47,7 +49,7 @@ func (r *DNSRule) UnmarshalJSON(bytes []byte) error {
|
||||
default:
|
||||
return E.New("unknown rule type: " + r.Type)
|
||||
}
|
||||
err = badjson.UnmarshallExcluded(bytes, (*_DNSRule)(r), v)
|
||||
err = badjson.UnmarshallExcludedContext(ctx, bytes, (*_DNSRule)(r), v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -66,41 +68,41 @@ func (r DNSRule) IsValid() bool {
|
||||
}
|
||||
|
||||
type RawDefaultDNSRule struct {
|
||||
Inbound Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
QueryType Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network Listable[string] `json:"network,omitempty"`
|
||||
AuthUser Listable[string] `json:"auth_user,omitempty"`
|
||||
Protocol Listable[string] `json:"protocol,omitempty"`
|
||||
Domain Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP Listable[string] `json:"source_geoip,omitempty"`
|
||||
GeoIP Listable[string] `json:"geoip,omitempty"`
|
||||
IPCIDR Listable[string] `json:"ip_cidr,omitempty"`
|
||||
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||
SourceIPCIDR Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||
SourcePort Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName Listable[string] `json:"package_name,omitempty"`
|
||||
User Listable[string] `json:"user,omitempty"`
|
||||
UserID Listable[int32] `json:"user_id,omitempty"`
|
||||
Outbound Listable[string] `json:"outbound,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
WIFISSID Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
RuleSet Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Inbound badoption.Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"`
|
||||
Protocol badoption.Listable[string] `json:"protocol,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite badoption.Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"`
|
||||
GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
User badoption.Listable[string] `json:"user,omitempty"`
|
||||
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
|
||||
Outbound badoption.Listable[string] `json:"outbound,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
|
||||
// Deprecated: renamed to rule_set_ip_cidr_match_source
|
||||
Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
|
||||
@@ -111,46 +113,46 @@ 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) UnmarshalJSON(data []byte) error {
|
||||
func (r *DefaultDNSRule) UnmarshalJSONContext(ctx context.Context, data []byte) error {
|
||||
err := json.Unmarshal(data, &r.RawDefaultDNSRule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return badjson.UnmarshallExcluded(data, &r.RawDefaultDNSRule, &r.DNSRuleAction)
|
||||
return badjson.UnmarshallExcludedContext(ctx, 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
|
||||
return !reflect.DeepEqual(r, defaultValue)
|
||||
}
|
||||
|
||||
type _LogicalDNSRule struct {
|
||||
type RawLogicalDNSRule struct {
|
||||
Mode string `json:"mode"`
|
||||
Rules []DNSRule `json:"rules,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
}
|
||||
|
||||
type LogicalDNSRule struct {
|
||||
_LogicalDNSRule
|
||||
RawLogicalDNSRule
|
||||
DNSRuleAction
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) MarshalJSON() ([]byte, error) {
|
||||
return badjson.MarshallObjects(r._LogicalDNSRule, r.DNSRuleAction)
|
||||
return badjson.MarshallObjects(r.RawLogicalDNSRule, r.DNSRuleAction)
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) UnmarshalJSON(data []byte) error {
|
||||
err := json.Unmarshal(data, &r._LogicalDNSRule)
|
||||
func (r *LogicalDNSRule) UnmarshalJSONContext(ctx context.Context, data []byte) error {
|
||||
err := json.Unmarshal(data, &r.RawLogicalDNSRule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return badjson.UnmarshallExcluded(data, &r._LogicalDNSRule, &r.DNSRuleAction)
|
||||
return badjson.UnmarshallExcludedContext(ctx, data, &r.RawLogicalDNSRule, &r.DNSRuleAction)
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) IsValid() bool {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
|
||||
"go4.org/netipx"
|
||||
)
|
||||
@@ -84,9 +85,9 @@ type LocalRuleSet struct {
|
||||
}
|
||||
|
||||
type RemoteRuleSet struct {
|
||||
URL string `json:"url"`
|
||||
DownloadDetour string `json:"download_detour,omitempty"`
|
||||
UpdateInterval Duration `json:"update_interval,omitempty"`
|
||||
URL string `json:"url"`
|
||||
DownloadDetour string `json:"download_detour,omitempty"`
|
||||
UpdateInterval badoption.Duration `json:"update_interval,omitempty"`
|
||||
}
|
||||
|
||||
type _HeadlessRule struct {
|
||||
@@ -145,32 +146,32 @@ func (r HeadlessRule) IsValid() bool {
|
||||
}
|
||||
|
||||
type DefaultHeadlessRule struct {
|
||||
QueryType Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network Listable[string] `json:"network,omitempty"`
|
||||
Domain Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex Listable[string] `json:"domain_regex,omitempty"`
|
||||
SourceIPCIDR Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
IPCIDR Listable[string] `json:"ip_cidr,omitempty"`
|
||||
SourcePort Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName Listable[string] `json:"package_name,omitempty"`
|
||||
WIFISSID Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
|
||||
DomainMatcher *domain.Matcher `json:"-"`
|
||||
SourceIPSet *netipx.IPSet `json:"-"`
|
||||
IPSet *netipx.IPSet `json:"-"`
|
||||
|
||||
AdGuardDomain Listable[string] `json:"-"`
|
||||
AdGuardDomainMatcher *domain.AdGuardMatcher `json:"-"`
|
||||
AdGuardDomain badoption.Listable[string] `json:"-"`
|
||||
AdGuardDomainMatcher *domain.AdGuardMatcher `json:"-"`
|
||||
}
|
||||
|
||||
func (r DefaultHeadlessRule) IsValid() bool {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package option
|
||||
|
||||
import "github.com/sagernet/sing/common/auth"
|
||||
import (
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
type SocksInboundOptions struct {
|
||||
ListenOptions
|
||||
@@ -30,6 +33,6 @@ type HTTPOutboundOptions struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
OutboundTLSOptionsContainer
|
||||
Path string `json:"path,omitempty"`
|
||||
Headers HTTPHeader `json:"headers,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Headers badoption.HTTPHeader `json:"headers,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package option
|
||||
|
||||
import "github.com/sagernet/sing/common/json/badoption"
|
||||
|
||||
type SSHOutboundOptions struct {
|
||||
DialerOptions
|
||||
ServerOptions
|
||||
User string `json:"user,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
PrivateKey Listable[string] `json:"private_key,omitempty"`
|
||||
PrivateKeyPath string `json:"private_key_path,omitempty"`
|
||||
PrivateKeyPassphrase string `json:"private_key_passphrase,omitempty"`
|
||||
HostKey Listable[string] `json:"host_key,omitempty"`
|
||||
HostKeyAlgorithms Listable[string] `json:"host_key_algorithms,omitempty"`
|
||||
ClientVersion string `json:"client_version,omitempty"`
|
||||
User string `json:"user,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
PrivateKey badoption.Listable[string] `json:"private_key,omitempty"`
|
||||
PrivateKeyPath string `json:"private_key_path,omitempty"`
|
||||
PrivateKeyPassphrase string `json:"private_key_passphrase,omitempty"`
|
||||
HostKey badoption.Listable[string] `json:"host_key,omitempty"`
|
||||
HostKeyAlgorithms badoption.Listable[string] `json:"host_key_algorithms,omitempty"`
|
||||
ClientVersion string `json:"client_version,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1,226 +0,0 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
const durationDay = 24 * time.Hour
|
||||
|
||||
var unitMap = map[string]uint64{
|
||||
"ns": uint64(time.Nanosecond),
|
||||
"us": uint64(time.Microsecond),
|
||||
"µs": uint64(time.Microsecond), // U+00B5 = micro symbol
|
||||
"μs": uint64(time.Microsecond), // U+03BC = Greek letter mu
|
||||
"ms": uint64(time.Millisecond),
|
||||
"s": uint64(time.Second),
|
||||
"m": uint64(time.Minute),
|
||||
"h": uint64(time.Hour),
|
||||
"d": uint64(durationDay),
|
||||
}
|
||||
|
||||
// ParseDuration parses a duration string.
|
||||
// A duration string is a possibly signed sequence of
|
||||
// decimal numbers, each with optional fraction and a unit suffix,
|
||||
// such as "300ms", "-1.5h" or "2h45m".
|
||||
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
func ParseDuration(s string) (Duration, error) {
|
||||
// [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
|
||||
orig := s
|
||||
var d uint64
|
||||
neg := false
|
||||
|
||||
// Consume [-+]?
|
||||
if s != "" {
|
||||
c := s[0]
|
||||
if c == '-' || c == '+' {
|
||||
neg = c == '-'
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
// Special case: if all that is left is "0", this is zero.
|
||||
if s == "0" {
|
||||
return 0, nil
|
||||
}
|
||||
if s == "" {
|
||||
return 0, errors.New("time: invalid duration " + quote(orig))
|
||||
}
|
||||
for s != "" {
|
||||
var (
|
||||
v, f uint64 // integers before, after decimal point
|
||||
scale float64 = 1 // value = v + f/scale
|
||||
)
|
||||
|
||||
var err error
|
||||
|
||||
// The next character must be [0-9.]
|
||||
if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') {
|
||||
return 0, errors.New("time: invalid duration " + quote(orig))
|
||||
}
|
||||
// Consume [0-9]*
|
||||
pl := len(s)
|
||||
v, s, err = leadingInt(s)
|
||||
if err != nil {
|
||||
return 0, errors.New("time: invalid duration " + quote(orig))
|
||||
}
|
||||
pre := pl != len(s) // whether we consumed anything before a period
|
||||
|
||||
// Consume (\.[0-9]*)?
|
||||
post := false
|
||||
if s != "" && s[0] == '.' {
|
||||
s = s[1:]
|
||||
pl := len(s)
|
||||
f, scale, s = leadingFraction(s)
|
||||
post = pl != len(s)
|
||||
}
|
||||
if !pre && !post {
|
||||
// no digits (e.g. ".s" or "-.s")
|
||||
return 0, errors.New("time: invalid duration " + quote(orig))
|
||||
}
|
||||
|
||||
// Consume unit.
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if c == '.' || '0' <= c && c <= '9' {
|
||||
break
|
||||
}
|
||||
}
|
||||
if i == 0 {
|
||||
return 0, errors.New("time: missing unit in duration " + quote(orig))
|
||||
}
|
||||
u := s[:i]
|
||||
s = s[i:]
|
||||
unit, ok := unitMap[u]
|
||||
if !ok {
|
||||
return 0, errors.New("time: unknown unit " + quote(u) + " in duration " + quote(orig))
|
||||
}
|
||||
if v > 1<<63/unit {
|
||||
// overflow
|
||||
return 0, errors.New("time: invalid duration " + quote(orig))
|
||||
}
|
||||
v *= unit
|
||||
if f > 0 {
|
||||
// float64 is needed to be nanosecond accurate for fractions of hours.
|
||||
// v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit)
|
||||
v += uint64(float64(f) * (float64(unit) / scale))
|
||||
if v > 1<<63 {
|
||||
// overflow
|
||||
return 0, errors.New("time: invalid duration " + quote(orig))
|
||||
}
|
||||
}
|
||||
d += v
|
||||
if d > 1<<63 {
|
||||
return 0, errors.New("time: invalid duration " + quote(orig))
|
||||
}
|
||||
}
|
||||
if neg {
|
||||
return -Duration(d), nil
|
||||
}
|
||||
if d > 1<<63-1 {
|
||||
return 0, errors.New("time: invalid duration " + quote(orig))
|
||||
}
|
||||
return Duration(d), nil
|
||||
}
|
||||
|
||||
var errLeadingInt = errors.New("time: bad [0-9]*") // never printed
|
||||
|
||||
// leadingInt consumes the leading [0-9]* from s.
|
||||
func leadingInt[bytes []byte | string](s bytes) (x uint64, rem bytes, err error) {
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if c < '0' || c > '9' {
|
||||
break
|
||||
}
|
||||
if x > 1<<63/10 {
|
||||
// overflow
|
||||
return 0, rem, errLeadingInt
|
||||
}
|
||||
x = x*10 + uint64(c) - '0'
|
||||
if x > 1<<63 {
|
||||
// overflow
|
||||
return 0, rem, errLeadingInt
|
||||
}
|
||||
}
|
||||
return x, s[i:], nil
|
||||
}
|
||||
|
||||
// leadingFraction consumes the leading [0-9]* from s.
|
||||
// It is used only for fractions, so does not return an error on overflow,
|
||||
// it just stops accumulating precision.
|
||||
func leadingFraction(s string) (x uint64, scale float64, rem string) {
|
||||
i := 0
|
||||
scale = 1
|
||||
overflow := false
|
||||
for ; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if c < '0' || c > '9' {
|
||||
break
|
||||
}
|
||||
if overflow {
|
||||
continue
|
||||
}
|
||||
if x > (1<<63-1)/10 {
|
||||
// It's possible for overflow to give a positive number, so take care.
|
||||
overflow = true
|
||||
continue
|
||||
}
|
||||
y := x*10 + uint64(c) - '0'
|
||||
if y > 1<<63 {
|
||||
overflow = true
|
||||
continue
|
||||
}
|
||||
x = y
|
||||
scale *= 10
|
||||
}
|
||||
return x, scale, s[i:]
|
||||
}
|
||||
|
||||
// These are borrowed from unicode/utf8 and strconv and replicate behavior in
|
||||
// that package, since we can't take a dependency on either.
|
||||
const (
|
||||
lowerhex = "0123456789abcdef"
|
||||
runeSelf = 0x80
|
||||
runeError = '\uFFFD'
|
||||
)
|
||||
|
||||
func quote(s string) string {
|
||||
buf := make([]byte, 1, len(s)+2) // slice will be at least len(s) + quotes
|
||||
buf[0] = '"'
|
||||
for i, c := range s {
|
||||
if c >= runeSelf || c < ' ' {
|
||||
// This means you are asking us to parse a time.Duration or
|
||||
// time.Location with unprintable or non-ASCII characters in it.
|
||||
// We don't expect to hit this case very often. We could try to
|
||||
// reproduce strconv.Quote's behavior with full fidelity but
|
||||
// given how rarely we expect to hit these edge cases, speed and
|
||||
// conciseness are better.
|
||||
var width int
|
||||
if c == runeError {
|
||||
width = 1
|
||||
if i+2 < len(s) && s[i:i+3] == string(runeError) {
|
||||
width = 3
|
||||
}
|
||||
} else {
|
||||
width = len(string(c))
|
||||
}
|
||||
for j := 0; j < width; j++ {
|
||||
buf = append(buf, `\x`...)
|
||||
buf = append(buf, lowerhex[s[i+j]>>4])
|
||||
buf = append(buf, lowerhex[s[i+j]&0xF])
|
||||
}
|
||||
} else {
|
||||
if c == '"' || c == '\\' {
|
||||
buf = append(buf, '\\')
|
||||
}
|
||||
buf = append(buf, string(c)...)
|
||||
}
|
||||
}
|
||||
buf = append(buf, '"')
|
||||
return string(buf)
|
||||
}
|
||||
@@ -1,20 +1,22 @@
|
||||
package option
|
||||
|
||||
import "github.com/sagernet/sing/common/json/badoption"
|
||||
|
||||
type InboundTLSOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
ServerName string `json:"server_name,omitempty"`
|
||||
Insecure bool `json:"insecure,omitempty"`
|
||||
ALPN Listable[string] `json:"alpn,omitempty"`
|
||||
MinVersion string `json:"min_version,omitempty"`
|
||||
MaxVersion string `json:"max_version,omitempty"`
|
||||
CipherSuites Listable[string] `json:"cipher_suites,omitempty"`
|
||||
Certificate Listable[string] `json:"certificate,omitempty"`
|
||||
CertificatePath string `json:"certificate_path,omitempty"`
|
||||
Key Listable[string] `json:"key,omitempty"`
|
||||
KeyPath string `json:"key_path,omitempty"`
|
||||
ACME *InboundACMEOptions `json:"acme,omitempty"`
|
||||
ECH *InboundECHOptions `json:"ech,omitempty"`
|
||||
Reality *InboundRealityOptions `json:"reality,omitempty"`
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
ServerName string `json:"server_name,omitempty"`
|
||||
Insecure bool `json:"insecure,omitempty"`
|
||||
ALPN badoption.Listable[string] `json:"alpn,omitempty"`
|
||||
MinVersion string `json:"min_version,omitempty"`
|
||||
MaxVersion string `json:"max_version,omitempty"`
|
||||
CipherSuites badoption.Listable[string] `json:"cipher_suites,omitempty"`
|
||||
Certificate badoption.Listable[string] `json:"certificate,omitempty"`
|
||||
CertificatePath string `json:"certificate_path,omitempty"`
|
||||
Key badoption.Listable[string] `json:"key,omitempty"`
|
||||
KeyPath string `json:"key_path,omitempty"`
|
||||
ACME *InboundACMEOptions `json:"acme,omitempty"`
|
||||
ECH *InboundECHOptions `json:"ech,omitempty"`
|
||||
Reality *InboundRealityOptions `json:"reality,omitempty"`
|
||||
}
|
||||
|
||||
type InboundTLSOptionsContainer struct {
|
||||
@@ -35,19 +37,19 @@ func (o *InboundTLSOptionsContainer) ReplaceInboundTLSOptions(options *InboundTL
|
||||
}
|
||||
|
||||
type OutboundTLSOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
DisableSNI bool `json:"disable_sni,omitempty"`
|
||||
ServerName string `json:"server_name,omitempty"`
|
||||
Insecure bool `json:"insecure,omitempty"`
|
||||
ALPN Listable[string] `json:"alpn,omitempty"`
|
||||
MinVersion string `json:"min_version,omitempty"`
|
||||
MaxVersion string `json:"max_version,omitempty"`
|
||||
CipherSuites Listable[string] `json:"cipher_suites,omitempty"`
|
||||
Certificate Listable[string] `json:"certificate,omitempty"`
|
||||
CertificatePath string `json:"certificate_path,omitempty"`
|
||||
ECH *OutboundECHOptions `json:"ech,omitempty"`
|
||||
UTLS *OutboundUTLSOptions `json:"utls,omitempty"`
|
||||
Reality *OutboundRealityOptions `json:"reality,omitempty"`
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
DisableSNI bool `json:"disable_sni,omitempty"`
|
||||
ServerName string `json:"server_name,omitempty"`
|
||||
Insecure bool `json:"insecure,omitempty"`
|
||||
ALPN badoption.Listable[string] `json:"alpn,omitempty"`
|
||||
MinVersion string `json:"min_version,omitempty"`
|
||||
MaxVersion string `json:"max_version,omitempty"`
|
||||
CipherSuites badoption.Listable[string] `json:"cipher_suites,omitempty"`
|
||||
Certificate badoption.Listable[string] `json:"certificate,omitempty"`
|
||||
CertificatePath string `json:"certificate_path,omitempty"`
|
||||
ECH *OutboundECHOptions `json:"ech,omitempty"`
|
||||
UTLS *OutboundUTLSOptions `json:"utls,omitempty"`
|
||||
Reality *OutboundRealityOptions `json:"reality,omitempty"`
|
||||
}
|
||||
|
||||
type OutboundTLSOptionsContainer struct {
|
||||
@@ -71,8 +73,8 @@ type InboundRealityOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Handshake InboundRealityHandshakeOptions `json:"handshake,omitempty"`
|
||||
PrivateKey string `json:"private_key,omitempty"`
|
||||
ShortID Listable[string] `json:"short_id,omitempty"`
|
||||
MaxTimeDifference Duration `json:"max_time_difference,omitempty"`
|
||||
ShortID badoption.Listable[string] `json:"short_id,omitempty"`
|
||||
MaxTimeDifference badoption.Duration `json:"max_time_difference,omitempty"`
|
||||
}
|
||||
|
||||
type InboundRealityHandshakeOptions struct {
|
||||
@@ -81,19 +83,19 @@ type InboundRealityHandshakeOptions struct {
|
||||
}
|
||||
|
||||
type InboundECHOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"`
|
||||
DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"`
|
||||
Key Listable[string] `json:"key,omitempty"`
|
||||
KeyPath string `json:"key_path,omitempty"`
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"`
|
||||
DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"`
|
||||
Key badoption.Listable[string] `json:"key,omitempty"`
|
||||
KeyPath string `json:"key_path,omitempty"`
|
||||
}
|
||||
|
||||
type OutboundECHOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"`
|
||||
DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"`
|
||||
Config Listable[string] `json:"config,omitempty"`
|
||||
ConfigPath string `json:"config_path,omitempty"`
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"`
|
||||
DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"`
|
||||
Config badoption.Listable[string] `json:"config,omitempty"`
|
||||
ConfigPath string `json:"config_path,omitempty"`
|
||||
}
|
||||
|
||||
type OutboundUTLSOptions struct {
|
||||
|
||||
@@ -5,10 +5,11 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
type InboundACMEOptions struct {
|
||||
Domain Listable[string] `json:"domain,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DataDirectory string `json:"data_directory,omitempty"`
|
||||
DefaultServerName string `json:"default_server_name,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package option
|
||||
|
||||
import "github.com/sagernet/sing/common/json/badoption"
|
||||
|
||||
type TUICInboundOptions struct {
|
||||
ListenOptions
|
||||
Users []TUICUser `json:"users,omitempty"`
|
||||
CongestionControl string `json:"congestion_control,omitempty"`
|
||||
AuthTimeout Duration `json:"auth_timeout,omitempty"`
|
||||
ZeroRTTHandshake bool `json:"zero_rtt_handshake,omitempty"`
|
||||
Heartbeat Duration `json:"heartbeat,omitempty"`
|
||||
Users []TUICUser `json:"users,omitempty"`
|
||||
CongestionControl string `json:"congestion_control,omitempty"`
|
||||
AuthTimeout badoption.Duration `json:"auth_timeout,omitempty"`
|
||||
ZeroRTTHandshake bool `json:"zero_rtt_handshake,omitempty"`
|
||||
Heartbeat badoption.Duration `json:"heartbeat,omitempty"`
|
||||
InboundTLSOptionsContainer
|
||||
}
|
||||
|
||||
@@ -19,13 +21,13 @@ type TUICUser struct {
|
||||
type TUICOutboundOptions struct {
|
||||
DialerOptions
|
||||
ServerOptions
|
||||
UUID string `json:"uuid,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
CongestionControl string `json:"congestion_control,omitempty"`
|
||||
UDPRelayMode string `json:"udp_relay_mode,omitempty"`
|
||||
UDPOverStream bool `json:"udp_over_stream,omitempty"`
|
||||
ZeroRTTHandshake bool `json:"zero_rtt_handshake,omitempty"`
|
||||
Heartbeat Duration `json:"heartbeat,omitempty"`
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
UUID string `json:"uuid,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
CongestionControl string `json:"congestion_control,omitempty"`
|
||||
UDPRelayMode string `json:"udp_relay_mode,omitempty"`
|
||||
UDPOverStream bool `json:"udp_over_stream,omitempty"`
|
||||
ZeroRTTHandshake bool `json:"zero_rtt_handshake,omitempty"`
|
||||
Heartbeat badoption.Duration `json:"heartbeat,omitempty"`
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
OutboundTLSOptionsContainer
|
||||
}
|
||||
|
||||
@@ -7,51 +7,52 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
type TunInboundOptions struct {
|
||||
InterfaceName string `json:"interface_name,omitempty"`
|
||||
MTU uint32 `json:"mtu,omitempty"`
|
||||
GSO bool `json:"gso,omitempty"`
|
||||
Address Listable[netip.Prefix] `json:"address,omitempty"`
|
||||
AutoRoute bool `json:"auto_route,omitempty"`
|
||||
IPRoute2TableIndex int `json:"iproute2_table_index,omitempty"`
|
||||
IPRoute2RuleIndex int `json:"iproute2_rule_index,omitempty"`
|
||||
AutoRedirect bool `json:"auto_redirect,omitempty"`
|
||||
AutoRedirectInputMark FwMark `json:"auto_redirect_input_mark,omitempty"`
|
||||
AutoRedirectOutputMark FwMark `json:"auto_redirect_output_mark,omitempty"`
|
||||
StrictRoute bool `json:"strict_route,omitempty"`
|
||||
RouteAddress Listable[netip.Prefix] `json:"route_address,omitempty"`
|
||||
RouteAddressSet Listable[string] `json:"route_address_set,omitempty"`
|
||||
RouteExcludeAddress Listable[netip.Prefix] `json:"route_exclude_address,omitempty"`
|
||||
RouteExcludeAddressSet Listable[string] `json:"route_exclude_address_set,omitempty"`
|
||||
IncludeInterface Listable[string] `json:"include_interface,omitempty"`
|
||||
ExcludeInterface Listable[string] `json:"exclude_interface,omitempty"`
|
||||
IncludeUID Listable[uint32] `json:"include_uid,omitempty"`
|
||||
IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"`
|
||||
ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"`
|
||||
ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"`
|
||||
IncludeAndroidUser Listable[int] `json:"include_android_user,omitempty"`
|
||||
IncludePackage Listable[string] `json:"include_package,omitempty"`
|
||||
ExcludePackage Listable[string] `json:"exclude_package,omitempty"`
|
||||
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
|
||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
||||
Stack string `json:"stack,omitempty"`
|
||||
Platform *TunPlatformOptions `json:"platform,omitempty"`
|
||||
InterfaceName string `json:"interface_name,omitempty"`
|
||||
MTU uint32 `json:"mtu,omitempty"`
|
||||
GSO bool `json:"gso,omitempty"`
|
||||
Address badoption.Listable[netip.Prefix] `json:"address,omitempty"`
|
||||
AutoRoute bool `json:"auto_route,omitempty"`
|
||||
IPRoute2TableIndex int `json:"iproute2_table_index,omitempty"`
|
||||
IPRoute2RuleIndex int `json:"iproute2_rule_index,omitempty"`
|
||||
AutoRedirect bool `json:"auto_redirect,omitempty"`
|
||||
AutoRedirectInputMark FwMark `json:"auto_redirect_input_mark,omitempty"`
|
||||
AutoRedirectOutputMark FwMark `json:"auto_redirect_output_mark,omitempty"`
|
||||
StrictRoute bool `json:"strict_route,omitempty"`
|
||||
RouteAddress badoption.Listable[netip.Prefix] `json:"route_address,omitempty"`
|
||||
RouteAddressSet badoption.Listable[string] `json:"route_address_set,omitempty"`
|
||||
RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"route_exclude_address,omitempty"`
|
||||
RouteExcludeAddressSet badoption.Listable[string] `json:"route_exclude_address_set,omitempty"`
|
||||
IncludeInterface badoption.Listable[string] `json:"include_interface,omitempty"`
|
||||
ExcludeInterface badoption.Listable[string] `json:"exclude_interface,omitempty"`
|
||||
IncludeUID badoption.Listable[uint32] `json:"include_uid,omitempty"`
|
||||
IncludeUIDRange badoption.Listable[string] `json:"include_uid_range,omitempty"`
|
||||
ExcludeUID badoption.Listable[uint32] `json:"exclude_uid,omitempty"`
|
||||
ExcludeUIDRange badoption.Listable[string] `json:"exclude_uid_range,omitempty"`
|
||||
IncludeAndroidUser badoption.Listable[int] `json:"include_android_user,omitempty"`
|
||||
IncludePackage badoption.Listable[string] `json:"include_package,omitempty"`
|
||||
ExcludePackage badoption.Listable[string] `json:"exclude_package,omitempty"`
|
||||
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
|
||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
||||
Stack string `json:"stack,omitempty"`
|
||||
Platform *TunPlatformOptions `json:"platform,omitempty"`
|
||||
InboundOptions
|
||||
|
||||
// Deprecated: merged to Address
|
||||
Inet4Address Listable[netip.Prefix] `json:"inet4_address,omitempty"`
|
||||
Inet4Address badoption.Listable[netip.Prefix] `json:"inet4_address,omitempty"`
|
||||
// Deprecated: merged to Address
|
||||
Inet6Address Listable[netip.Prefix] `json:"inet6_address,omitempty"`
|
||||
Inet6Address badoption.Listable[netip.Prefix] `json:"inet6_address,omitempty"`
|
||||
// Deprecated: merged to RouteAddress
|
||||
Inet4RouteAddress Listable[netip.Prefix] `json:"inet4_route_address,omitempty"`
|
||||
Inet4RouteAddress badoption.Listable[netip.Prefix] `json:"inet4_route_address,omitempty"`
|
||||
// Deprecated: merged to RouteAddress
|
||||
Inet6RouteAddress Listable[netip.Prefix] `json:"inet6_route_address,omitempty"`
|
||||
Inet6RouteAddress badoption.Listable[netip.Prefix] `json:"inet6_route_address,omitempty"`
|
||||
// Deprecated: merged to RouteExcludeAddress
|
||||
Inet4RouteExcludeAddress Listable[netip.Prefix] `json:"inet4_route_exclude_address,omitempty"`
|
||||
Inet4RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"inet4_route_exclude_address,omitempty"`
|
||||
// Deprecated: merged to RouteExcludeAddress
|
||||
Inet6RouteExcludeAddress Listable[netip.Prefix] `json:"inet6_route_exclude_address,omitempty"`
|
||||
Inet6RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"inet6_route_exclude_address,omitempty"`
|
||||
}
|
||||
|
||||
type FwMark uint32
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package option
|
||||
|
||||
import "github.com/sagernet/sing/common/json/badoption"
|
||||
|
||||
type TunPlatformOptions struct {
|
||||
HTTPProxy *HTTPProxyOptions `json:"http_proxy,omitempty"`
|
||||
}
|
||||
@@ -7,6 +9,6 @@ type TunPlatformOptions struct {
|
||||
type HTTPProxyOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
ServerOptions
|
||||
BypassDomain Listable[string] `json:"bypass_domain,omitempty"`
|
||||
MatchDomain Listable[string] `json:"match_domain,omitempty"`
|
||||
BypassDomain badoption.Listable[string] `json:"bypass_domain,omitempty"`
|
||||
MatchDomain badoption.Listable[string] `json:"match_domain,omitempty"`
|
||||
}
|
||||
|
||||
132
option/types.go
132
option/types.go
@@ -1,10 +1,7 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-dns"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -15,79 +12,6 @@ import (
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type ListenAddress netip.Addr
|
||||
|
||||
func NewListenAddress(addr netip.Addr) *ListenAddress {
|
||||
address := ListenAddress(addr)
|
||||
return &address
|
||||
}
|
||||
|
||||
func (a ListenAddress) MarshalJSON() ([]byte, error) {
|
||||
addr := netip.Addr(a)
|
||||
if !addr.IsValid() {
|
||||
return nil, nil
|
||||
}
|
||||
return json.Marshal(addr.String())
|
||||
}
|
||||
|
||||
func (a *ListenAddress) UnmarshalJSON(content []byte) error {
|
||||
var value string
|
||||
err := json.Unmarshal(content, &value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
addr, err := netip.ParseAddr(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*a = ListenAddress(addr)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ListenAddress) Build() netip.Addr {
|
||||
if a == nil {
|
||||
return netip.AddrFrom4([4]byte{127, 0, 0, 1})
|
||||
}
|
||||
return (netip.Addr)(*a)
|
||||
}
|
||||
|
||||
type AddrPrefix netip.Prefix
|
||||
|
||||
func (a AddrPrefix) MarshalJSON() ([]byte, error) {
|
||||
prefix := netip.Prefix(a)
|
||||
if prefix.Bits() == prefix.Addr().BitLen() {
|
||||
return json.Marshal(prefix.Addr().String())
|
||||
} else {
|
||||
return json.Marshal(prefix.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AddrPrefix) UnmarshalJSON(content []byte) error {
|
||||
var value string
|
||||
err := json.Unmarshal(content, &value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prefix, prefixErr := netip.ParsePrefix(value)
|
||||
if prefixErr == nil {
|
||||
*a = AddrPrefix(prefix)
|
||||
return nil
|
||||
}
|
||||
addr, addrErr := netip.ParseAddr(value)
|
||||
if addrErr == nil {
|
||||
*a = AddrPrefix(netip.PrefixFrom(addr, addr.BitLen()))
|
||||
return nil
|
||||
}
|
||||
return prefixErr
|
||||
}
|
||||
|
||||
func (a *AddrPrefix) Build() netip.Prefix {
|
||||
if a == nil {
|
||||
return netip.Prefix{}
|
||||
}
|
||||
return netip.Prefix(*a)
|
||||
}
|
||||
|
||||
type NetworkList string
|
||||
|
||||
func (v *NetworkList) UnmarshalJSON(content []byte) error {
|
||||
@@ -120,30 +44,6 @@ func (v NetworkList) Build() []string {
|
||||
return strings.Split(string(v), "\n")
|
||||
}
|
||||
|
||||
type Listable[T any] []T
|
||||
|
||||
func (l Listable[T]) MarshalJSON() ([]byte, error) {
|
||||
arrayList := []T(l)
|
||||
if len(arrayList) == 1 {
|
||||
return json.Marshal(arrayList[0])
|
||||
}
|
||||
return json.Marshal(arrayList)
|
||||
}
|
||||
|
||||
func (l *Listable[T]) UnmarshalJSON(content []byte) error {
|
||||
err := json.UnmarshalDisallowUnknownFields(content, (*[]T)(l))
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
var singleItem T
|
||||
newError := json.UnmarshalDisallowUnknownFields(content, &singleItem)
|
||||
if newError != nil {
|
||||
return E.Errors(err, newError)
|
||||
}
|
||||
*l = []T{singleItem}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DomainStrategy dns.DomainStrategy
|
||||
|
||||
func (s DomainStrategy) String() string {
|
||||
@@ -206,26 +106,6 @@ func (s *DomainStrategy) UnmarshalJSON(bytes []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Duration time.Duration
|
||||
|
||||
func (d Duration) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal((time.Duration)(d).String())
|
||||
}
|
||||
|
||||
func (d *Duration) UnmarshalJSON(bytes []byte) error {
|
||||
var value string
|
||||
err := json.Unmarshal(bytes, &value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
duration, err := ParseDuration(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*d = Duration(duration)
|
||||
return nil
|
||||
}
|
||||
|
||||
type DNSQueryType uint16
|
||||
|
||||
func (t DNSQueryType) String() string {
|
||||
@@ -270,15 +150,3 @@ func DNSQueryTypeToString(queryType uint16) string {
|
||||
}
|
||||
return F.ToString(queryType)
|
||||
}
|
||||
|
||||
type HTTPHeader map[string]Listable[string]
|
||||
|
||||
func (h HTTPHeader) Build() http.Header {
|
||||
header := make(http.Header)
|
||||
for name, values := range h {
|
||||
for _, value := range values {
|
||||
header.Add(name, value)
|
||||
}
|
||||
}
|
||||
return header
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
type _V2RayTransportOptions struct {
|
||||
@@ -67,33 +68,33 @@ func (o *V2RayTransportOptions) UnmarshalJSON(bytes []byte) error {
|
||||
}
|
||||
|
||||
type V2RayHTTPOptions struct {
|
||||
Host Listable[string] `json:"host,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Method string `json:"method,omitempty"`
|
||||
Headers HTTPHeader `json:"headers,omitempty"`
|
||||
IdleTimeout Duration `json:"idle_timeout,omitempty"`
|
||||
PingTimeout Duration `json:"ping_timeout,omitempty"`
|
||||
Host badoption.Listable[string] `json:"host,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Method string `json:"method,omitempty"`
|
||||
Headers badoption.HTTPHeader `json:"headers,omitempty"`
|
||||
IdleTimeout badoption.Duration `json:"idle_timeout,omitempty"`
|
||||
PingTimeout badoption.Duration `json:"ping_timeout,omitempty"`
|
||||
}
|
||||
|
||||
type V2RayWebsocketOptions struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
Headers HTTPHeader `json:"headers,omitempty"`
|
||||
MaxEarlyData uint32 `json:"max_early_data,omitempty"`
|
||||
EarlyDataHeaderName string `json:"early_data_header_name,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Headers badoption.HTTPHeader `json:"headers,omitempty"`
|
||||
MaxEarlyData uint32 `json:"max_early_data,omitempty"`
|
||||
EarlyDataHeaderName string `json:"early_data_header_name,omitempty"`
|
||||
}
|
||||
|
||||
type V2RayQUICOptions struct{}
|
||||
|
||||
type V2RayGRPCOptions struct {
|
||||
ServiceName string `json:"service_name,omitempty"`
|
||||
IdleTimeout Duration `json:"idle_timeout,omitempty"`
|
||||
PingTimeout Duration `json:"ping_timeout,omitempty"`
|
||||
PermitWithoutStream bool `json:"permit_without_stream,omitempty"`
|
||||
ForceLite bool `json:"-"` // for test
|
||||
ServiceName string `json:"service_name,omitempty"`
|
||||
IdleTimeout badoption.Duration `json:"idle_timeout,omitempty"`
|
||||
PingTimeout badoption.Duration `json:"ping_timeout,omitempty"`
|
||||
PermitWithoutStream bool `json:"permit_without_stream,omitempty"`
|
||||
ForceLite bool `json:"-"` // for test
|
||||
}
|
||||
|
||||
type V2RayHTTPUpgradeOptions struct {
|
||||
Host string `json:"host,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Headers HTTPHeader `json:"headers,omitempty"`
|
||||
Host string `json:"host,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Headers badoption.HTTPHeader `json:"headers,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
package option
|
||||
|
||||
import "net/netip"
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
type WireGuardOutboundOptions struct {
|
||||
DialerOptions
|
||||
SystemInterface bool `json:"system_interface,omitempty"`
|
||||
GSO bool `json:"gso,omitempty"`
|
||||
InterfaceName string `json:"interface_name,omitempty"`
|
||||
LocalAddress Listable[netip.Prefix] `json:"local_address"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
Peers []WireGuardPeer `json:"peers,omitempty"`
|
||||
SystemInterface bool `json:"system_interface,omitempty"`
|
||||
GSO bool `json:"gso,omitempty"`
|
||||
InterfaceName string `json:"interface_name,omitempty"`
|
||||
LocalAddress badoption.Listable[netip.Prefix] `json:"local_address"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
Peers []WireGuardPeer `json:"peers,omitempty"`
|
||||
ServerOptions
|
||||
PeerPublicKey string `json:"peer_public_key"`
|
||||
PreSharedKey string `json:"pre_shared_key,omitempty"`
|
||||
@@ -21,8 +25,8 @@ type WireGuardOutboundOptions struct {
|
||||
|
||||
type WireGuardPeer struct {
|
||||
ServerOptions
|
||||
PublicKey string `json:"public_key,omitempty"`
|
||||
PreSharedKey string `json:"pre_shared_key,omitempty"`
|
||||
AllowedIPs Listable[string] `json:"allowed_ips,omitempty"`
|
||||
Reserved []uint8 `json:"reserved,omitempty"`
|
||||
PublicKey string `json:"public_key,omitempty"`
|
||||
PreSharedKey string `json:"pre_shared_key,omitempty"`
|
||||
AllowedIPs badoption.Listable[string] `json:"allowed_ips,omitempty"`
|
||||
Reserved []uint8 `json:"reserved,omitempty"`
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
}
|
||||
inbound.udpNat = udpnat.New(inbound, inbound.preparePacketConnection, udpTimeout)
|
||||
inbound.udpNat = udpnat.New(inbound, inbound.preparePacketConnection, udpTimeout, false)
|
||||
inbound.listener = listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
|
||||
@@ -43,6 +43,7 @@ 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()
|
||||
|
||||
@@ -116,12 +116,12 @@ func (n *Inbound) Start() error {
|
||||
|
||||
if common.Contains(n.network, N.NetworkUDP) {
|
||||
http3Server, err := ConfigureHTTP3ListenerFunc(n.listener, n, n.tlsConfig, n.logger)
|
||||
if len(n.network) > 1 {
|
||||
n.logger.Warn(E.Cause(err, "naive http3 disabled"))
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else {
|
||||
if err == nil {
|
||||
n.h3Server = http3Server
|
||||
} else if len(n.network) > 1 {
|
||||
n.logger.Warn(E.Cause(err, "naive http3 disabled"))
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
} else {
|
||||
udpTimeout = C.UDPTimeout
|
||||
}
|
||||
tproxy.udpNat = udpnat.New(tproxy, tproxy.preparePacketConnection, udpTimeout)
|
||||
tproxy.udpNat = udpnat.New(tproxy, tproxy.preparePacketConnection, udpTimeout, false)
|
||||
tproxy.listener = listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
|
||||
@@ -27,8 +27,8 @@ var _ adapter.TCPInjectableInbound = (*Inbound)(nil)
|
||||
|
||||
type Inbound struct {
|
||||
inbound.Adapter
|
||||
logger logger.ContextLogger
|
||||
router adapter.ConnectionRouterEx
|
||||
logger logger.ContextLogger
|
||||
listener *listener.Listener
|
||||
authenticator *auth.Authenticator
|
||||
}
|
||||
@@ -37,6 +37,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeSOCKS, tag),
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
authenticator: auth.NewAuthenticator(options.Users),
|
||||
}
|
||||
inbound.listener = listener.New(listener.Options{
|
||||
|
||||
@@ -48,6 +48,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
inbound := &Inbound{
|
||||
Adapter: inbound.NewAdapter(C.TypeTUIC, tag),
|
||||
router: uot.NewRouter(router, logger),
|
||||
logger: logger,
|
||||
listener: listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/ranges"
|
||||
@@ -257,7 +258,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func uidToRange(uidList option.Listable[uint32]) []ranges.Range[uint32] {
|
||||
func uidToRange(uidList badoption.Listable[uint32]) []ranges.Range[uint32] {
|
||||
return common.Map(uidList, func(uid uint32) ranges.Range[uint32] {
|
||||
return ranges.NewSingle(uid)
|
||||
})
|
||||
@@ -343,19 +344,25 @@ 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,7 +8,6 @@ import (
|
||||
"os"
|
||||
"os/user"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -77,7 +76,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 options instead.")
|
||||
return E.New("global multiplex is deprecated since sing-box v1.7.0, enable multiplex in Inbound fields instead.")
|
||||
case vmess.MuxDestination.Fqdn:
|
||||
return E.New("global multiplex (v2ray legacy) not supported since sing-box v1.7.0.")
|
||||
case uot.MagicAddress:
|
||||
@@ -92,22 +91,33 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var selectedOutbound adapter.Outbound
|
||||
var selectReturn bool
|
||||
var (
|
||||
// selectedOutbound adapter.Outbound
|
||||
selectedDialer N.Dialer
|
||||
selectedTag string
|
||||
selectedDescription string
|
||||
)
|
||||
if selectedRule != nil {
|
||||
switch action := selectedRule.Action().(type) {
|
||||
case *rule.RuleActionRoute:
|
||||
var loaded bool
|
||||
selectedOutbound, loaded = r.Outbound(action.Outbound)
|
||||
selectedOutbound, loaded := r.Outbound(action.Outbound)
|
||||
if !loaded {
|
||||
buf.ReleaseMulti(buffers)
|
||||
return E.New("outbound not found: ", action.Outbound)
|
||||
}
|
||||
case *rule.RuleActionReturn:
|
||||
selectReturn = true
|
||||
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.RuleActionReject:
|
||||
buf.ReleaseMulti(buffers)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, action.Error())
|
||||
N.CloseOnHandshakeFailure(conn, onClose, action.Error(ctx))
|
||||
return nil
|
||||
case *rule.RuleActionHijackDNS:
|
||||
for _, buffer := range buffers {
|
||||
@@ -117,17 +127,16 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if selectedRule == nil || selectReturn {
|
||||
if selectedRule == nil {
|
||||
if r.defaultOutboundForConnection == nil {
|
||||
buf.ReleaseMulti(buffers)
|
||||
return E.New("missing default outbound with TCP support")
|
||||
}
|
||||
selectedOutbound = r.defaultOutboundForConnection
|
||||
}
|
||||
if !common.Contains(selectedOutbound.Network(), N.NetworkTCP) {
|
||||
buf.ReleaseMulti(buffers)
|
||||
return E.New("TCP is not supported by outbound: ", selectedOutbound.Tag())
|
||||
selectedDialer = r.defaultOutboundForConnection
|
||||
selectedTag = r.defaultOutboundForConnection.Tag()
|
||||
selectedDescription = F.ToString("outbound/", r.defaultOutboundForConnection.Type(), "[", r.defaultOutboundForConnection.Tag(), "]")
|
||||
}
|
||||
|
||||
for _, buffer := range buffers {
|
||||
conn = bufio.NewCachedConn(conn, buffer)
|
||||
}
|
||||
@@ -138,10 +147,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, selectedOutbound.Tag(), metadata.User, conn)
|
||||
conn = statsService.RoutedConnection(metadata.Inbound, selectedTag, metadata.User, conn)
|
||||
}
|
||||
}
|
||||
legacyOutbound, isLegacy := selectedOutbound.(adapter.ConnectionHandler)
|
||||
legacyOutbound, isLegacy := selectedDialer.(adapter.ConnectionHandler)
|
||||
if isLegacy {
|
||||
err = legacyOutbound.NewConnection(ctx, conn, metadata)
|
||||
if err != nil {
|
||||
@@ -149,7 +158,7 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
if onClose != nil {
|
||||
onClose(err)
|
||||
}
|
||||
return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
|
||||
return E.Cause(err, selectedDescription)
|
||||
} else {
|
||||
if onClose != nil {
|
||||
onClose(nil)
|
||||
@@ -158,13 +167,13 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
return nil
|
||||
}
|
||||
// TODO
|
||||
err = outbound.NewConnection(ctx, selectedOutbound, conn, metadata)
|
||||
err = outbound.NewConnection(ctx, selectedDialer, conn, metadata)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
if onClose != nil {
|
||||
onClose(err)
|
||||
}
|
||||
return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
|
||||
return E.Cause(err, selectedDescription)
|
||||
} else {
|
||||
if onClose != nil {
|
||||
onClose(nil)
|
||||
@@ -236,23 +245,33 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var selectedOutbound adapter.Outbound
|
||||
var (
|
||||
selectedDialer N.Dialer
|
||||
selectedTag string
|
||||
selectedDescription string
|
||||
)
|
||||
var selectReturn bool
|
||||
if selectedRule != nil {
|
||||
switch action := selectedRule.Action().(type) {
|
||||
case *rule.RuleActionRoute:
|
||||
var loaded bool
|
||||
selectedOutbound, loaded = r.Outbound(action.Outbound)
|
||||
selectedOutbound, loaded := r.Outbound(action.Outbound)
|
||||
if !loaded {
|
||||
N.ReleaseMultiPacketBuffer(packetBuffers)
|
||||
return E.New("outbound not found: ", action.Outbound)
|
||||
}
|
||||
metadata.UDPDisableDomainUnmapping = action.UDPDisableDomainUnmapping
|
||||
case *rule.RuleActionReturn:
|
||||
selectReturn = true
|
||||
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()
|
||||
case *rule.RuleActionReject:
|
||||
N.ReleaseMultiPacketBuffer(packetBuffers)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, syscall.ECONNREFUSED)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, action.Error(ctx))
|
||||
return nil
|
||||
case *rule.RuleActionHijackDNS:
|
||||
r.hijackDNSPacket(ctx, conn, packetBuffers, metadata)
|
||||
@@ -264,11 +283,9 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
N.ReleaseMultiPacketBuffer(packetBuffers)
|
||||
return E.New("missing default outbound with UDP support")
|
||||
}
|
||||
selectedOutbound = r.defaultOutboundForPacketConnection
|
||||
}
|
||||
if !common.Contains(selectedOutbound.Network(), N.NetworkUDP) {
|
||||
N.ReleaseMultiPacketBuffer(packetBuffers)
|
||||
return E.New("UDP is not supported by outbound: ", selectedOutbound.Tag())
|
||||
selectedDialer = r.defaultOutboundForPacketConnection
|
||||
selectedTag = r.defaultOutboundForPacketConnection.Tag()
|
||||
selectedDescription = F.ToString("outbound/", r.defaultOutboundForPacketConnection.Type(), "[", r.defaultOutboundForPacketConnection.Tag(), "]")
|
||||
}
|
||||
for _, buffer := range packetBuffers {
|
||||
conn = bufio.NewCachedPacketConn(conn, buffer.Buffer, buffer.Destination)
|
||||
@@ -281,26 +298,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, selectedOutbound.Tag(), metadata.User, conn)
|
||||
conn = statsService.RoutedPacketConnection(metadata.Inbound, selectedTag, metadata.User, conn)
|
||||
}
|
||||
}
|
||||
if metadata.FakeIP {
|
||||
conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination)
|
||||
}
|
||||
legacyOutbound, isLegacy := selectedOutbound.(adapter.PacketConnectionHandler)
|
||||
legacyOutbound, isLegacy := selectedDialer.(adapter.PacketConnectionHandler)
|
||||
if isLegacy {
|
||||
err = legacyOutbound.NewPacketConnection(ctx, conn, metadata)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
|
||||
return E.Cause(err, selectedDescription)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// TODO
|
||||
err = outbound.NewPacketConnection(ctx, selectedOutbound, conn, metadata)
|
||||
err = outbound.NewPacketConnection(ctx, selectedDialer, conn, metadata)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
|
||||
return E.Cause(err, selectedDescription)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -317,7 +334,7 @@ func (r *Router) PreMatch(metadata adapter.InboundContext) error {
|
||||
if !isReject {
|
||||
return nil
|
||||
}
|
||||
return rejectAction.Error()
|
||||
return rejectAction.Error(nil)
|
||||
}
|
||||
|
||||
func (r *Router) matchRule(
|
||||
@@ -445,7 +462,7 @@ match:
|
||||
}
|
||||
} else {
|
||||
switch currentRule.Action().Type() {
|
||||
case C.RuleActionTypeReject, C.RuleActionTypeResolve:
|
||||
case C.RuleActionTypeReject:
|
||||
ruleDescription := currentRule.String()
|
||||
if ruleDescription != "" {
|
||||
r.logger.DebugContext(ctx, "pre-match[", currentRuleIndex, "] ", currentRule, " => ", currentRule.Action())
|
||||
@@ -455,6 +472,9 @@ 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,8 +8,10 @@ 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"
|
||||
@@ -48,38 +50,63 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int,
|
||||
if ruleIndex != -1 {
|
||||
dnsRules = dnsRules[ruleIndex+1:]
|
||||
}
|
||||
for currentRuleIndex, rule := range dnsRules {
|
||||
if rule.WithAddressLimit() && !isAddressQuery {
|
||||
for currentRuleIndex, currentRule := range dnsRules {
|
||||
if currentRule.WithAddressLimit() && !isAddressQuery {
|
||||
continue
|
||||
}
|
||||
metadata.ResetRuleCache()
|
||||
if rule.Match(metadata) {
|
||||
if currentRule.Match(metadata) {
|
||||
displayRuleIndex := currentRuleIndex
|
||||
if displayRuleIndex != -1 {
|
||||
displayRuleIndex += displayRuleIndex + 1
|
||||
}
|
||||
if routeAction, isRoute := rule.Action().(*R.RuleActionDNSRoute); isRoute {
|
||||
transport, loaded := r.transportMap[routeAction.Server]
|
||||
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 !loaded {
|
||||
r.dnsLogger.ErrorContext(ctx, "transport not found: ", routeAction.Server)
|
||||
r.dnsLogger.ErrorContext(ctx, "transport not found: ", action.Server)
|
||||
continue
|
||||
}
|
||||
_, isFakeIP := transport.(adapter.FakeIPTransport)
|
||||
if isFakeIP && !allowFakeIP {
|
||||
continue
|
||||
}
|
||||
options.DisableCache = isFakeIP || routeAction.DisableCache
|
||||
options.RewriteTTL = routeAction.RewriteTTL
|
||||
options.ClientSubnet = routeAction.ClientSubnet
|
||||
if isFakeIP || action.DisableCache {
|
||||
options.DisableCache = true
|
||||
}
|
||||
if action.RewriteTTL != nil {
|
||||
options.RewriteTTL = action.RewriteTTL
|
||||
}
|
||||
if action.ClientSubnet.IsValid() {
|
||||
options.ClientSubnet = action.ClientSubnet
|
||||
}
|
||||
if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded {
|
||||
options.Strategy = domainStrategy
|
||||
} else {
|
||||
options.Strategy = r.defaultDomainStrategy
|
||||
}
|
||||
r.dnsLogger.DebugContext(ctx, "match[", displayRuleIndex, "] ", rule.String(), " => ", rule.Action())
|
||||
return transport, options, rule, currentRuleIndex
|
||||
} else {
|
||||
return nil, options, rule, currentRuleIndex
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,6 +154,17 @@ 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 {
|
||||
@@ -238,6 +276,17 @@ 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,6 +8,7 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -204,12 +205,19 @@ 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 == "" {
|
||||
@@ -231,13 +239,16 @@ func NewRouter(
|
||||
}
|
||||
var clientSubnet netip.Prefix
|
||||
if server.ClientSubnet != nil {
|
||||
clientSubnet = server.ClientSubnet.Build()
|
||||
clientSubnet = netip.Prefix(common.PtrValueOrDefault(server.ClientSubnet))
|
||||
} else if dnsOptions.ClientSubnet != nil {
|
||||
clientSubnet = dnsOptions.ClientSubnet.Build()
|
||||
clientSubnet = netip.Prefix(common.PtrValueOrDefault(dnsOptions.ClientSubnet))
|
||||
}
|
||||
if serverProtocol == "" {
|
||||
serverProtocol = "transport"
|
||||
}
|
||||
transport, err := dns.CreateTransport(dns.TransportOptions{
|
||||
Context: ctx,
|
||||
Logger: logFactory.NewLogger(F.ToString("dns/transport[", tag, "]")),
|
||||
Logger: logFactory.NewLogger(F.ToString("dns/", serverProtocol, "[", tag, "]")),
|
||||
Name: tag,
|
||||
Dialer: detour,
|
||||
Address: server.Address,
|
||||
@@ -822,7 +833,11 @@ func (r *Router) AutoDetectInterface() bool {
|
||||
|
||||
func (r *Router) AutoDetectInterfaceFunc() control.Func {
|
||||
if r.platformInterface != nil && r.platformInterface.UsePlatformAutoDetectInterfaceControl() {
|
||||
return r.platformInterface.AutoDetectInterfaceControl()
|
||||
return func(network, address string, conn syscall.RawConn) error {
|
||||
return control.Raw(conn, func(fd uintptr) error {
|
||||
return r.platformInterface.AutoDetectInterfaceControl(int(fd))
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if r.interfaceMonitor == nil {
|
||||
return nil
|
||||
|
||||
@@ -1,34 +1,65 @@
|
||||
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(action option.RuleAction) (adapter.RuleAction, error) {
|
||||
func NewRuleAction(router adapter.Router, logger logger.ContextLogger, action option.RuleAction) (adapter.RuleAction, error) {
|
||||
switch action.Action {
|
||||
case "":
|
||||
return nil, nil
|
||||
case C.RuleActionTypeRoute:
|
||||
return &RuleActionRoute{
|
||||
Outbound: action.RouteOptions.Outbound,
|
||||
UDPDisableDomainUnmapping: action.RouteOptions.UDPDisableDomainUnmapping,
|
||||
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,
|
||||
}, 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
|
||||
@@ -48,20 +79,28 @@ func NewRuleAction(action option.RuleAction) (adapter.RuleAction, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSRuleAction(action option.DNSRuleAction) adapter.RuleAction {
|
||||
func NewDNSRuleAction(logger logger.ContextLogger, action option.DNSRuleAction) adapter.RuleAction {
|
||||
switch action.Action {
|
||||
case "":
|
||||
return nil
|
||||
case C.RuleActionTypeRoute:
|
||||
return &RuleActionDNSRoute{
|
||||
Server: action.RouteOptions.Server,
|
||||
DisableCache: action.RouteOptions.DisableCache,
|
||||
RewriteTTL: action.RouteOptions.RewriteTTL,
|
||||
ClientSubnet: action.RouteOptions.ClientSubnet.Build(),
|
||||
ClientSubnet: netip.Prefix(common.PtrValueOrDefault(action.RouteOptions.ClientSubnet)),
|
||||
}
|
||||
case C.RuleActionTypeRouteOptions:
|
||||
return &RuleActionDNSRouteOptions{
|
||||
DisableCache: action.RouteOptionsOptions.DisableCache,
|
||||
RewriteTTL: action.RouteOptionsOptions.RewriteTTL,
|
||||
ClientSubnet: netip.Prefix(common.PtrValueOrDefault(action.RouteOptionsOptions.ClientSubnet)),
|
||||
}
|
||||
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))
|
||||
@@ -69,8 +108,7 @@ func NewDNSRuleAction(action option.DNSRuleAction) adapter.RuleAction {
|
||||
}
|
||||
|
||||
type RuleActionRoute struct {
|
||||
Outbound string
|
||||
UDPDisableDomainUnmapping bool
|
||||
Outbound string
|
||||
}
|
||||
|
||||
func (r *RuleActionRoute) Type() string {
|
||||
@@ -81,6 +119,26 @@ 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
|
||||
@@ -96,18 +154,49 @@ func (r *RuleActionDNSRoute) String() string {
|
||||
return F.ToString("route(", r.Server, ")")
|
||||
}
|
||||
|
||||
type RuleActionReturn struct{}
|
||||
|
||||
func (r *RuleActionReturn) Type() string {
|
||||
return C.RuleActionTypeReturn
|
||||
type RuleActionDNSRouteOptions struct {
|
||||
DisableCache bool
|
||||
RewriteTTL *uint32
|
||||
ClientSubnet netip.Prefix
|
||||
}
|
||||
|
||||
func (r *RuleActionReturn) String() string {
|
||||
return "return"
|
||||
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
|
||||
}
|
||||
|
||||
type RuleActionReject struct {
|
||||
Method string
|
||||
Method string
|
||||
NoDrop bool
|
||||
logger logger.ContextLogger
|
||||
dropAccess sync.Mutex
|
||||
dropCounter []time.Time
|
||||
}
|
||||
|
||||
func (r *RuleActionReject) Type() string {
|
||||
@@ -121,21 +210,30 @@ func (r *RuleActionReject) String() string {
|
||||
return F.ToString("reject(", r.Method, ")")
|
||||
}
|
||||
|
||||
func (r *RuleActionReject) Error() error {
|
||||
func (r *RuleActionReject) Error(ctx context.Context) error {
|
||||
var returnErr error
|
||||
switch r.Method {
|
||||
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.RuleActionRejectMethodDefault:
|
||||
returnErr = 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(options.RuleAction)
|
||||
action, err := NewRuleAction(router, logger, 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(options.RuleAction)
|
||||
action, err := NewRuleAction(router, logger, 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(options.DNSRuleAction),
|
||||
action: NewDNSRuleAction(logger, 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(options.DNSRuleAction),
|
||||
action: NewDNSRuleAction(logger, options.DNSRuleAction),
|
||||
},
|
||||
}
|
||||
switch options.Mode {
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
)
|
||||
@@ -15,13 +17,13 @@ func TestBrutalShadowsocks(t *testing.T) {
|
||||
method := shadowaead_2022.List[0]
|
||||
password := mkBase64(t, 16)
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
LegacyInbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
@@ -30,7 +32,7 @@ func TestBrutalShadowsocks(t *testing.T) {
|
||||
Type: C.TypeShadowsocks,
|
||||
ShadowsocksOptions: option.ShadowsocksInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
Method: method,
|
||||
@@ -100,13 +102,13 @@ func TestBrutalTrojan(t *testing.T) {
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
password := mkBase64(t, 16)
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
LegacyInbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
@@ -115,7 +117,7 @@ func TestBrutalTrojan(t *testing.T) {
|
||||
Type: C.TypeTrojan,
|
||||
TrojanOptions: option.TrojanInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
Users: []option.TrojanUser{{Password: password}},
|
||||
@@ -197,13 +199,13 @@ func TestBrutalTrojan(t *testing.T) {
|
||||
func TestBrutalVMess(t *testing.T) {
|
||||
user, _ := uuid.NewV4()
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
LegacyInbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
@@ -212,7 +214,7 @@ func TestBrutalVMess(t *testing.T) {
|
||||
Type: C.TypeVMess,
|
||||
VMessOptions: option.VMessInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
Users: []option.VMessUser{{UUID: user.String()}},
|
||||
@@ -279,13 +281,13 @@ func TestBrutalVMess(t *testing.T) {
|
||||
func TestBrutalVLESS(t *testing.T) {
|
||||
user, _ := uuid.NewV4()
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
LegacyInbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
@@ -294,7 +296,7 @@ func TestBrutalVLESS(t *testing.T) {
|
||||
Type: C.TypeVLESS,
|
||||
VLESSOptions: option.VLESSInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
Users: []option.VLESSUser{{UUID: user.String()}},
|
||||
|
||||
@@ -6,18 +6,20 @@ import (
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
// Since this is a feature one-off added by outsiders, I won't address these anymore.
|
||||
func _TestProxyProtocol(t *testing.T) {
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
LegacyInbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
@@ -26,7 +28,7 @@ func _TestProxyProtocol(t *testing.T) {
|
||||
Type: C.TypeDirect,
|
||||
DirectOptions: option.DirectInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: serverPort,
|
||||
ProxyProtocol: true,
|
||||
},
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
)
|
||||
@@ -14,13 +16,13 @@ import (
|
||||
func TestTUICDomainUDP(t *testing.T) {
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
LegacyInbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
@@ -29,7 +31,7 @@ func TestTUICDomainUDP(t *testing.T) {
|
||||
Type: C.TypeTUIC,
|
||||
TUICOptions: option.TUICInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: serverPort,
|
||||
InboundOptions: option.InboundOptions{
|
||||
DomainStrategy: option.DomainStrategy(dns.DomainStrategyUseIPv6),
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
)
|
||||
@@ -16,13 +17,13 @@ func TestECH(t *testing.T) {
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false))
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
LegacyInbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
@@ -31,7 +32,7 @@ func TestECH(t *testing.T) {
|
||||
Type: C.TypeTrojan,
|
||||
TrojanOptions: option.TrojanInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
Users: []option.TrojanUser{
|
||||
@@ -109,13 +110,13 @@ func TestECHQUIC(t *testing.T) {
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false))
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
LegacyInbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
@@ -124,7 +125,7 @@ func TestECHQUIC(t *testing.T) {
|
||||
Type: C.TypeTUIC,
|
||||
TUICOptions: option.TUICInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
Users: []option.TUICUser{{
|
||||
@@ -199,13 +200,13 @@ func TestECHHysteria2(t *testing.T) {
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false))
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
LegacyInbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
@@ -214,7 +215,7 @@ func TestECHHysteria2(t *testing.T) {
|
||||
Type: C.TypeHysteria2,
|
||||
Hysteria2Options: option.Hysteria2InboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
Users: []option.Hysteria2User{{
|
||||
|
||||
18
test/go.mod
18
test/go.mod
@@ -12,10 +12,10 @@ require (
|
||||
github.com/docker/docker v27.3.1+incompatible
|
||||
github.com/docker/go-connections v0.5.0
|
||||
github.com/gofrs/uuid/v5 v5.3.0
|
||||
github.com/sagernet/quic-go v0.48.0-beta.1
|
||||
github.com/sagernet/sing v0.5.0-rc.4.0.20241022031908-cd17884118cb
|
||||
github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241021154031-a59e0fbba3ce
|
||||
github.com/sagernet/sing-quic v0.3.0-rc.1
|
||||
github.com/sagernet/quic-go v0.48.1-beta.1
|
||||
github.com/sagernet/sing v0.5.1-0.20241107131656-6e1285b5d82f
|
||||
github.com/sagernet/sing-dns v0.3.1-0.20241105104342-1914f319ddab
|
||||
github.com/sagernet/sing-quic v0.3.0-rc.2
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||
github.com/spyzhov/ajson v0.9.4
|
||||
@@ -25,13 +25,11 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||
github.com/caddyserver/certmagic v0.20.0 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/cretz/bine v0.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/distribution/reference v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
@@ -42,8 +40,6 @@ require (
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
@@ -65,7 +61,6 @@ require (
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.9.7 // indirect
|
||||
github.com/ooni/go-libtor v1.1.8 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
|
||||
@@ -81,13 +76,10 @@ require (
|
||||
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
|
||||
github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec // indirect
|
||||
github.com/sagernet/sing-shadowtls v0.1.4 // indirect
|
||||
github.com/sagernet/sing-tun v0.4.0-rc.4.0.20241021153919-9ae45181180d // indirect
|
||||
github.com/sagernet/sing-tun v0.4.0-rc.5.0.20241107062822-5a91eb99c90f // indirect
|
||||
github.com/sagernet/sing-vmess v0.1.12 // indirect
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
|
||||
github.com/sagernet/utls v1.6.7 // indirect
|
||||
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 // indirect
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
|
||||
|
||||
51
test/go.sum
51
test/go.sum
@@ -1,5 +1,3 @@
|
||||
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
|
||||
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
@@ -14,9 +12,6 @@ github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vc
|
||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
|
||||
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
||||
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -43,10 +38,6 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
|
||||
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
@@ -107,8 +98,6 @@ github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss=
|
||||
github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0=
|
||||
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
|
||||
github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4=
|
||||
github.com/ooni/go-libtor v1.1.8 h1:Wo3V3DVTxl5vZdxtQakqYP+DAHx7pPtAFSl1bnAa08w=
|
||||
github.com/ooni/go-libtor v1.1.8/go.mod h1:q1YyLwRD9GeMyeerVvwc0vJ2YgwDLTp2bdVcrh/JXyI=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||
@@ -135,45 +124,37 @@ 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.0-beta.1 h1:86hQZrmuoARI3BpDRkQaP0iAVpywA4YsRrzJPYuPKWg=
|
||||
github.com/sagernet/quic-go v0.48.0-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k=
|
||||
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/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.0-rc.4.0.20241022031908-cd17884118cb h1:3IhGq2UmcbQfAcuqyE8RYKFapqEEa3eItS/MrZr+5l8=
|
||||
github.com/sagernet/sing v0.5.0-rc.4.0.20241022031908-cd17884118cb/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241021154031-a59e0fbba3ce h1:OfpxE5qnXMyU/9LtNgX4M7bGP11lJx4s+KZ3Sijb0HE=
|
||||
github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241021154031-a59e0fbba3ce/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M=
|
||||
github.com/sagernet/sing v0.5.1-0.20241107131656-6e1285b5d82f h1:A6+OeV5P1mok0eEEbLh4PidymZ6VZnTZ2uHwfapXgdU=
|
||||
github.com/sagernet/sing v0.5.1-0.20241107131656-6e1285b5d82f/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-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.1 h1:SlzL1yfEAKJyRduub8vzOVtbyTLAX7RZEEBZxO5utts=
|
||||
github.com/sagernet/sing-quic v0.3.0-rc.1/go.mod h1:uX+aUHA0fgIN6U3WViseDpSdTQriViZ7qz0Wbsf1mNQ=
|
||||
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-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.4.0.20241021153919-9ae45181180d h1:zWcIQM3eAKJGzy7zhqkO7zm7ZI890OdR4vSwx2mevS0=
|
||||
github.com/sagernet/sing-tun v0.4.0-rc.4.0.20241021153919-9ae45181180d/go.mod h1:Xhv+Mz2nE7HZTwResni8EtTa7AMJz/S6uQLT5lV23M8=
|
||||
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-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=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
|
||||
github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8=
|
||||
github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 h1:R0OMYAScomNAVpTfbHFpxqJpvwuhxSRi+g6z7gZhABs=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8/go.mod h1:K4J7/npM+VAMUeUmTa2JaA02JmyheP0GpRBOUvn3ecc=
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spyzhov/ajson v0.9.4 h1:MVibcTCgO7DY4IlskdqIlCmDOsUOZ9P7oKj8ifdcf84=
|
||||
github.com/spyzhov/ajson v0.9.4/go.mod h1:a6oSw0MMb7Z5aD2tPoPO+jq11ETKgXUr2XktHdT8Wt8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||
@@ -211,10 +192,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
@@ -227,8 +206,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -237,23 +214,15 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
|
||||
@@ -6,17 +6,19 @@ import (
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
func TestHTTPSelf(t *testing.T) {
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.LegacyInbound{
|
||||
LegacyInbounds: []option.LegacyInbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
@@ -25,7 +27,7 @@ func TestHTTPSelf(t *testing.T) {
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user