Compare commits

..

11 Commits

Author SHA1 Message Date
世界
47736b27ba refactor: Modular inbounds 2024-11-03 20:24:41 +08:00
世界
0b2c7ec35c refactor: Modular outbounds 2024-11-03 19:56:44 +08:00
世界
e537c56b6b Implement dns-hijack 2024-11-03 19:47:17 +08:00
世界
b456aff4ac Implement resolve(server) 2024-11-03 19:47:15 +08:00
世界
4da652ff02 Implement TCP and ICMP rejects 2024-11-03 19:47:11 +08:00
世界
262727ec6c Crazy sekai overturns the small pond 2024-11-03 19:47:09 +08:00
世界
81dc9e7698 documentation: Bump version 2024-11-02 21:40:38 +08:00
世界
d876077281 Update dependencies 2024-11-02 21:40:28 +08:00
世界
0df81c297b Update quic-go to v0.48.0 2024-11-02 21:40:28 +08:00
世界
b460484e43 Fix "Fix metadata context" 2024-11-02 21:37:04 +08:00
世界
0e511791e8 platform: Add openURL event 2024-11-02 17:13:13 +08:00
117 changed files with 1313 additions and 2425 deletions

View File

@@ -57,8 +57,6 @@ type InboundContext struct {
// Deprecated // Deprecated
InboundOptions option.InboundOptions InboundOptions option.InboundOptions
UDPDisableDomainUnmapping bool UDPDisableDomainUnmapping bool
UDPConnect bool
DNSServer string DNSServer string
DestinationAddresses []netip.Addr DestinationAddresses []netip.Addr

View File

@@ -20,7 +20,6 @@ import (
) )
func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error { func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error {
defer conn.Close()
ctx = adapter.WithContext(ctx, &metadata) ctx = adapter.WithContext(ctx, &metadata)
var outConn net.Conn var outConn net.Conn
var err error var err error
@@ -41,7 +40,6 @@ 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 { 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) ctx = adapter.WithContext(ctx, &metadata)
var outConn net.Conn var outConn net.Conn
var err error var err error
@@ -69,49 +67,29 @@ 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 { func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error {
defer conn.Close()
ctx = adapter.WithContext(ctx, &metadata) ctx = adapter.WithContext(ctx, &metadata)
var ( var outConn net.PacketConn
outPacketConn net.PacketConn var destinationAddress netip.Addr
outConn net.Conn var err error
destinationAddress netip.Addr
err error
)
if metadata.UDPConnect {
if len(metadata.DestinationAddresses) > 0 { if len(metadata.DestinationAddresses) > 0 {
outConn, err = N.DialSerial(ctx, this, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses) outConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses)
} else { } else {
outConn, err = this.DialContext(ctx, N.NetworkUDP, metadata.Destination) outConn, err = this.ListenPacket(ctx, metadata.Destination)
} }
if err != nil { if err != nil {
return N.ReportHandshakeFailure(conn, err) return N.ReportHandshakeFailure(conn, err)
} }
outPacketConn = bufio.NewUnbindPacketConn(outConn) err = N.ReportPacketConnHandshakeSuccess(conn, outConn)
connRemoteAddr := M.AddrFromNet(outConn.RemoteAddr())
if connRemoteAddr != metadata.Destination.Addr {
destinationAddress = connRemoteAddr
}
} else {
if len(metadata.DestinationAddresses) > 0 {
outPacketConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses)
} else {
outPacketConn, err = this.ListenPacket(ctx, metadata.Destination)
}
if err != nil { if err != nil {
return N.ReportHandshakeFailure(conn, err) outConn.Close()
}
}
err = N.ReportPacketConnHandshakeSuccess(conn, outPacketConn)
if err != nil {
outPacketConn.Close()
return err return err
} }
if destinationAddress.IsValid() { if destinationAddress.IsValid() {
if metadata.Destination.IsFqdn() { if metadata.Destination.IsFqdn() {
if metadata.UDPDisableDomainUnmapping { if metadata.UDPDisableDomainUnmapping {
outPacketConn = bufio.NewUnidirectionalNATPacketConn(bufio.NewPacketConn(outPacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination) outConn = bufio.NewUnidirectionalNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
} else { } else {
outPacketConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outPacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination) outConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
} }
} }
if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded { if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded {
@@ -126,63 +104,37 @@ func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn,
case C.ProtocolDNS: case C.ProtocolDNS:
ctx, conn = canceler.NewPacketConn(ctx, conn, C.DNSTimeout) ctx, conn = canceler.NewPacketConn(ctx, conn, C.DNSTimeout)
} }
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outPacketConn)) return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outConn))
} }
func NewDirectPacketConnection(ctx context.Context, router adapter.Router, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext, domainStrategy dns.DomainStrategy) error { 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) ctx = adapter.WithContext(ctx, &metadata)
var ( var outConn net.PacketConn
outPacketConn net.PacketConn var destinationAddress netip.Addr
outConn net.Conn var err error
destinationAddress netip.Addr
err error
)
if metadata.UDPConnect {
if len(metadata.DestinationAddresses) > 0 { if len(metadata.DestinationAddresses) > 0 {
outConn, err = N.DialSerial(ctx, this, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses) outConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses)
} else if metadata.Destination.IsFqdn() { } else if metadata.Destination.IsFqdn() {
var destinationAddresses []netip.Addr var destinationAddresses []netip.Addr
destinationAddresses, err = router.Lookup(ctx, metadata.Destination.Fqdn, domainStrategy) destinationAddresses, err = router.Lookup(ctx, metadata.Destination.Fqdn, domainStrategy)
if err != nil { if err != nil {
return N.ReportHandshakeFailure(conn, err) return N.ReportHandshakeFailure(conn, err)
} }
outConn, err = N.DialSerial(ctx, this, N.NetworkUDP, metadata.Destination, destinationAddresses) outConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, destinationAddresses)
} else { } else {
outConn, err = this.DialContext(ctx, N.NetworkUDP, metadata.Destination) outConn, err = this.ListenPacket(ctx, metadata.Destination)
} }
if err != nil { if err != nil {
return N.ReportHandshakeFailure(conn, err) return N.ReportHandshakeFailure(conn, err)
} }
connRemoteAddr := M.AddrFromNet(outConn.RemoteAddr()) err = N.ReportPacketConnHandshakeSuccess(conn, outConn)
if connRemoteAddr != metadata.Destination.Addr {
destinationAddress = connRemoteAddr
}
} else {
if len(metadata.DestinationAddresses) > 0 {
outPacketConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses)
} else if metadata.Destination.IsFqdn() {
var destinationAddresses []netip.Addr
destinationAddresses, err = router.Lookup(ctx, metadata.Destination.Fqdn, domainStrategy)
if err != nil { if err != nil {
return N.ReportHandshakeFailure(conn, err) outConn.Close()
}
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 {
outPacketConn.Close()
return err return err
} }
if destinationAddress.IsValid() { if destinationAddress.IsValid() {
if metadata.Destination.IsFqdn() { if metadata.Destination.IsFqdn() {
outPacketConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outPacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination) outConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination)
} }
if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded { if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded {
natConn.UpdateDestination(destinationAddress) natConn.UpdateDestination(destinationAddress)
@@ -196,7 +148,7 @@ func NewDirectPacketConnection(ctx context.Context, router adapter.Router, this
case C.ProtocolDNS: case C.ProtocolDNS:
ctx, conn = canceler.NewPacketConn(ctx, conn, C.DNSTimeout) ctx, conn = canceler.NewPacketConn(ctx, conn, C.DNSTimeout)
} }
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outPacketConn)) return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outConn))
} }
func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) error { func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) error {

View File

@@ -58,7 +58,7 @@ func FindSDK() {
} }
func findNDK() bool { func findNDK() bool {
const fixedVersion = "26.3.11579264" const fixedVersion = "27.2.12479018"
const versionFile = "source.properties" const versionFile = "source.properties"
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.IsFile(filepath.Join(fixedPath, versionFile)) { if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.IsFile(filepath.Join(fixedPath, versionFile)) {
androidNDKPath = fixedPath androidNDKPath = fixedPath

View File

@@ -68,6 +68,6 @@ func preRun(cmd *cobra.Command, args []string) {
if len(configPaths) == 0 && len(configDirectories) == 0 { if len(configPaths) == 0 && len(configDirectories) == 0 {
configPaths = append(configPaths, "config.json") configPaths = append(configPaths, "config.json")
} }
globalCtx = service.ContextWith(globalCtx, deprecated.NewStderrManager(log.StdLogger())) globalCtx = service.ContextWith(globalCtx, deprecated.NewEnvManager(log.StdLogger()))
globalCtx = box.Context(globalCtx, include.InboundRegistry(), include.OutboundRegistry()) globalCtx = box.Context(globalCtx, include.InboundRegistry(), include.OutboundRegistry())
} }

View File

@@ -30,7 +30,7 @@ func check() error {
if err != nil { if err != nil {
return err return err
} }
ctx, cancel := context.WithCancel(globalCtx) ctx, cancel := context.WithCancel(context.Background())
instance, err := box.New(box.Options{ instance, err := box.New(box.Options{
Context: ctx, Context: ctx,
Options: options, Options: options,

View File

@@ -2,6 +2,7 @@ package main
import ( import (
"bytes" "bytes"
"context"
"os" "os"
"path/filepath" "path/filepath"
@@ -38,7 +39,7 @@ func format() error {
return err return err
} }
for _, optionsEntry := range optionsList { for _, optionsEntry := range optionsList {
optionsEntry.options, err = badjson.Omitempty(globalCtx, optionsEntry.options) optionsEntry.options, err = badjson.Omitempty(context.TODO(), optionsEntry.options)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -3,7 +3,6 @@ package dialer
import ( import (
"context" "context"
"net" "net"
"net/netip"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -103,7 +102,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDi
udpAddr4 string udpAddr4 string
) )
if options.Inet4BindAddress != nil { if options.Inet4BindAddress != nil {
bindAddr := options.Inet4BindAddress.Build(netip.IPv4Unspecified()) bindAddr := options.Inet4BindAddress.Build()
dialer4.LocalAddr = &net.TCPAddr{IP: bindAddr.AsSlice()} dialer4.LocalAddr = &net.TCPAddr{IP: bindAddr.AsSlice()}
udpDialer4.LocalAddr = &net.UDPAddr{IP: bindAddr.AsSlice()} udpDialer4.LocalAddr = &net.UDPAddr{IP: bindAddr.AsSlice()}
udpAddr4 = M.SocksaddrFrom(bindAddr, 0).String() udpAddr4 = M.SocksaddrFrom(bindAddr, 0).String()
@@ -114,7 +113,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDi
udpAddr6 string udpAddr6 string
) )
if options.Inet6BindAddress != nil { if options.Inet6BindAddress != nil {
bindAddr := options.Inet6BindAddress.Build(netip.IPv6Unspecified()) bindAddr := options.Inet6BindAddress.Build()
dialer6.LocalAddr = &net.TCPAddr{IP: bindAddr.AsSlice()} dialer6.LocalAddr = &net.TCPAddr{IP: bindAddr.AsSlice()}
udpDialer6.LocalAddr = &net.UDPAddr{IP: bindAddr.AsSlice()} udpDialer6.LocalAddr = &net.UDPAddr{IP: bindAddr.AsSlice()}
udpAddr6 = M.SocksaddrFrom(bindAddr, 0).String() udpAddr6 = M.SocksaddrFrom(bindAddr, 0).String()

View File

@@ -3,7 +3,6 @@ package listener
import ( import (
"context" "context"
"net" "net"
"net/netip"
"sync/atomic" "sync/atomic"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -93,7 +92,7 @@ func (l *Listener) Start() error {
if l.setSystemProxy { if l.setSystemProxy {
listenPort := M.SocksaddrFromNet(l.tcpListener.Addr()).Port listenPort := M.SocksaddrFromNet(l.tcpListener.Addr()).Port
var listenAddrString string var listenAddrString string
listenAddr := l.listenOptions.Listen.Build(netip.IPv4Unspecified()) listenAddr := l.listenOptions.Listen.Build()
if listenAddr.IsUnspecified() { if listenAddr.IsUnspecified() {
listenAddrString = "127.0.0.1" listenAddrString = "127.0.0.1"
} else { } else {

View File

@@ -2,7 +2,6 @@ package listener
import ( import (
"net" "net"
"net/netip"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -17,7 +16,7 @@ import (
func (l *Listener) ListenTCP() (net.Listener, error) { func (l *Listener) ListenTCP() (net.Listener, error) {
var err error var err error
bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(netip.AddrFrom4([4]byte{127, 0, 0, 1})), l.listenOptions.ListenPort) bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(), l.listenOptions.ListenPort)
var tcpListener net.Listener var tcpListener net.Listener
var listenConfig net.ListenConfig var listenConfig net.ListenConfig
if l.listenOptions.TCPKeepAlive >= 0 { if l.listenOptions.TCPKeepAlive >= 0 {

View File

@@ -2,7 +2,6 @@ package listener
import ( import (
"net" "net"
"net/netip"
"os" "os"
"time" "time"
@@ -14,7 +13,7 @@ import (
) )
func (l *Listener) ListenUDP() (net.PacketConn, error) { func (l *Listener) ListenUDP() (net.PacketConn, error) {
bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(netip.AddrFrom4([4]byte{127, 0, 0, 1})), l.listenOptions.ListenPort) bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(), l.listenOptions.ListenPort)
var lc net.ListenConfig var lc net.ListenConfig
var udpFragment bool var udpFragment bool
if l.listenOptions.UDPFragment != nil { if l.listenOptions.UDPFragment != nil {
@@ -49,9 +48,9 @@ func (l *Listener) loopUDPIn() {
if !l.threadUnsafePacketWriter { if !l.threadUnsafePacketWriter {
buffer = buf.NewPacket() buffer = buf.NewPacket()
defer buffer.Release() defer buffer.Release()
}
buffer.IncRef() buffer.IncRef()
defer buffer.DecRef() defer buffer.DecRef()
}
if l.oobPacketHandler != nil { if l.oobPacketHandler != nil {
oob := make([]byte, 1024) oob := make([]byte, 1024)
for { for {

View File

@@ -25,8 +25,7 @@ const (
const ( const (
RuleActionTypeRoute = "route" RuleActionTypeRoute = "route"
RuleActionTypeRouteOptions = "route-options" RuleActionTypeReturn = "return"
RuleActionTypeDirect = "direct"
RuleActionTypeReject = "reject" RuleActionTypeReject = "reject"
RuleActionTypeHijackDNS = "hijack-dns" RuleActionTypeHijackDNS = "hijack-dns"
RuleActionTypeSniff = "sniff" RuleActionTypeSniff = "sniff"
@@ -35,5 +34,9 @@ const (
const ( const (
RuleActionRejectMethodDefault = "default" RuleActionRejectMethodDefault = "default"
RuleActionRejectMethodReset = "reset"
RuleActionRejectMethodNetworkUnreachable = "network-unreachable"
RuleActionRejectMethodHostUnreachable = "host-unreachable"
RuleActionRejectMethodPortUnreachable = "port-unreachable"
RuleActionRejectMethodDrop = "drop" RuleActionRejectMethodDrop = "drop"
) )

View File

@@ -2,45 +2,10 @@
icon: material/alert-decagram icon: material/alert-decagram
--- ---
#### 1.11.0-alpha.8 #### 1.11.0-alpha.5
* Fixes and improvements * Fixes and improvements
#### 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 #### 1.11.0-alpha.2
* Add warnings for usage of deprecated features * Add warnings for usage of deprecated features

View File

@@ -1,3 +1,7 @@
---
icon: material/new-box
---
!!! quote "Changes in sing-box 1.9.0" !!! quote "Changes in sing-box 1.9.0"
:material-plus: [client_subnet](#client_subnet) :material-plus: [client_subnet](#client_subnet)

View File

@@ -1,3 +1,7 @@
---
icon: material/new-box
---
!!! quote "sing-box 1.9.0 中的更改" !!! quote "sing-box 1.9.0 中的更改"
:material-plus: [client_subnet](#client_subnet) :material-plus: [client_subnet](#client_subnet)

View File

@@ -2,14 +2,6 @@
icon: material/new-box 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" !!! quote "Changes in sing-box 1.10.0"
:material-delete-clock: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source) :material-delete-clock: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source)
@@ -143,15 +135,19 @@ icon: material/new-box
"outbound": [ "outbound": [
"direct" "direct"
], ],
"action": "route", "server": "local",
"server": "local" "disable_cache": false,
"rewrite_ttl": 100,
"client_subnet": "127.0.0.1/24"
}, },
{ {
"type": "logical", "type": "logical",
"mode": "and", "mode": "and",
"rules": [], "rules": [],
"action": "route", "server": "local",
"server": "local" "disable_cache": false,
"rewrite_ttl": 100,
"client_subnet": "127.0.0.1/24"
} }
] ]
} }
@@ -222,7 +218,7 @@ Match domain using regular expression.
!!! failure "Deprecated in sing-box 1.8.0" !!! failure "Deprecated in sing-box 1.8.0"
Geosite is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geosite-to-rule-sets). Geosite is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geosite-to-rule-sets).
Match geosite. Match geosite.
@@ -230,7 +226,7 @@ Match geosite.
!!! failure "Deprecated in sing-box 1.8.0" !!! failure "Deprecated in sing-box 1.8.0"
GeoIP is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geoip-to-rule-sets). GeoIP is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geoip-to-rule-sets).
Match source geoip. Match source geoip.
@@ -358,35 +354,29 @@ Match outbound.
`any` can be used as a value to match any outbound. `any` can be used as a value to match any outbound.
#### action #### server
==Required== ==Required==
See [DNS Rule Actions](../rule_action/) for details. Tag of the target dns server.
#### server
!!! failure "Deprecated in sing-box 1.11.0"
Moved to [DNS Rule Action](../rule_action#route).
#### disable_cache #### disable_cache
!!! failure "Deprecated in sing-box 1.11.0" Disable cache and save cache in this query.
Moved to [DNS Rule Action](../rule_action#route).
#### rewrite_ttl #### rewrite_ttl
!!! failure "Deprecated in sing-box 1.11.0" Rewrite TTL in DNS responses.
Moved to [DNS Rule Action](../rule_action#route).
#### client_subnet #### client_subnet
!!! failure "Deprecated in sing-box 1.11.0" !!! question "Since sing-box 1.9.0"
Moved to [DNS Rule Action](../rule_action#route). Append a `edns0-subnet` OPT extra record with the specified IP prefix to every query by default.
If value is an IP address instead of prefix, `/32` or `/128` will be appended automatically.
Will overrides `dns.client_subnet` and `servers.[].client_subnet`.
### Address Filter Fields ### Address Filter Fields

View File

@@ -1,85 +0,0 @@
---
icon: material/new-box
---
# DNS Rule Action
!!! question "Since sing-box 1.11.0"
### route
```json
{
"action": "route", // default
"server": "",
// for compatibility
"disable_cache": false,
"rewrite_ttl": 0,
"client_subnet": null
}
```
`route` inherits the classic rule behavior of routing DNS requests to the specified server.
#### server
==Required==
Tag of target server.
#### disable_cache/rewrite_ttl/client_subnet
!!! failure "Deprecated in sing-box 1.11.0"
Legacy route options is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-legacy-dns-route-options-to-rule-actions).
### route-options
```json
{
"action": "route-options",
"disable_cache": false,
"rewrite_ttl": null,
"client_subnet": null
}
```
#### disable_cache
Disable cache and save cache in this query.
#### rewrite_ttl
Rewrite TTL in DNS responses.
#### client_subnet
Append a `edns0-subnet` OPT extra record with the specified IP prefix to every query by default.
If value is an IP address instead of prefix, `/32` or `/128` will be appended automatically.
Will overrides `dns.client_subnet` and `servers.[].client_subnet`.
### reject
```json
{
"action": "reject",
"method": "default", // default
"no_drop": false
}
```
`reject` reject DNS requests.
#### method
- `default`: Reply with NXDOMAIN.
- `drop`: Drop the request.
#### no_drop
If not enabled, `method` will be temporarily overwritten to `drop` after 50 triggers in 30s.
Not available when `method` is set to drop.

View File

@@ -1,86 +0,0 @@
---
icon: material/new-box
---
# DNS 规则动作
!!! question "自 sing-box 1.11.0 起"
### route
```json
{
"action": "route", // 默认
"server": "",
// 兼容性
"disable_cache": false,
"rewrite_ttl": 0,
"client_subnet": null
}
```
`route` 继承了将 DNS 请求 路由到指定服务器的经典规则动作。
#### server
==必填==
目标 DNS 服务器的标签。
#### disable_cache/rewrite_ttl/client_subnet
!!! failure "自 sing-box 1.11.0 起"
旧的路由选项已弃用,且将在 sing-box 1.12.0 中移除,参阅 [迁移指南](/migration/#migrate-legacy-dns-route-options-to-rule-actions).
### route-options
```json
{
"action": "route-options",
"disable_cache": false,
"rewrite_ttl": null,
"client_subnet": null
}
```
#### disable_cache
在此查询中禁用缓存。
#### rewrite_ttl
重写 DNS 回应中的 TTL。
#### client_subnet
默认情况下,将带有指定 IP 前缀的 `edns0-subnet` OPT 附加记录附加到每个查询。
如果值是 IP 地址而不是前缀,则会自动附加 `/32``/128`
将覆盖 `dns.client_subnet``servers.[].client_subnet`
### reject
```json
{
"action": "reject",
"method": "default", // default
"no_drop": false
}
```
`reject` 拒绝 DNS 请求。
#### method
- `default`: 返回 NXDOMAIN。
- `drop`: 丢弃请求。
#### no_drop
如果未启用,则 30 秒内触发 50 次后,`method` 将被暂时覆盖为 `drop`
`method` 设为 `drop` 时不可用。

View File

@@ -1,3 +1,7 @@
---
icon: material/new-box
---
!!! quote "Changes in sing-box 1.9.0" !!! quote "Changes in sing-box 1.9.0"
:material-plus: [client_subnet](#client_subnet) :material-plus: [client_subnet](#client_subnet)

View File

@@ -1,3 +1,7 @@
---
icon: material/new-box
---
!!! quote "sing-box 1.9.0 中的更改" !!! quote "sing-box 1.9.0 中的更改"
:material-plus: [client_subnet](#client_subnet) :material-plus: [client_subnet](#client_subnet)

View File

@@ -1,3 +1,7 @@
---
icon: material/new-box
---
!!! question "Since sing-box 1.8.0" !!! question "Since sing-box 1.8.0"
!!! quote "Changes in sing-box 1.9.0" !!! quote "Changes in sing-box 1.9.0"

View File

@@ -1,3 +1,7 @@
---
icon: material/new-box
---
!!! question "自 sing-box 1.8.0 起" !!! question "自 sing-box 1.8.0 起"
!!! quote "sing-box 1.9.0 中的更改" !!! quote "sing-box 1.9.0 中的更改"

View File

@@ -1,14 +1,8 @@
--- `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 ### Structure
```json F ```json
{ {
"type": "block", "type": "block",
"tag": "block" "tag": "block"

View File

@@ -1,11 +1,3 @@
---
icon: material/delete-clock
---
!!! failure "已在 sing-box 1.11.0 废弃"
旧的特殊出站已被弃用,且将在 sing-box 1.13.0 中被移除,参阅 [迁移指南](/migration/#migrate-legacy-special-outbounds-to-rule-actions).
`block` 出站关闭所有传入请求。 `block` 出站关闭所有传入请求。
### 结构 ### 结构

View File

@@ -1,11 +1,3 @@
---
icon: material/delete-clock
---
!!! failure "Deprecated in sing-box 1.11.0"
Legacy special outbounds are deprecated and will be removed in sing-box 1.13.0, check [Migration](/migration/#migrate-legacy-special-outbounds-to-rule-actions).
`dns` outbound is a internal DNS server. `dns` outbound is a internal DNS server.
### Structure ### Structure

View File

@@ -1,11 +1,3 @@
---
icon: material/delete-clock
---
!!! failure "已在 sing-box 1.11.0 废弃"
旧的特殊出站已被弃用,且将在 sing-box 1.13.0 中被移除, 参阅 [迁移指南](/migration/#migrate-legacy-special-outbounds-to-rule-actions).
`dns` 出站是一个内部 DNS 服务器。 `dns` 出站是一个内部 DNS 服务器。
### 结构 ### 结构

View File

@@ -4,7 +4,7 @@ icon: material/delete-clock
!!! failure "Deprecated in sing-box 1.8.0" !!! failure "Deprecated in sing-box 1.8.0"
GeoIP is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geoip-to-rule-sets). GeoIP is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geoip-to-rule-sets).
### Structure ### Structure

View File

@@ -4,7 +4,7 @@ icon: material/delete-clock
!!! failure "已在 sing-box 1.8.0 废弃" !!! failure "已在 sing-box 1.8.0 废弃"
GeoIP 已废弃且将在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/zh/migration/#geoip)。 GeoIP 已废弃且可能在不久的将来移除,参阅 [迁移指南](/zh/migration/#geoip)。
### 结构 ### 结构

View File

@@ -4,7 +4,7 @@ icon: material/delete-clock
!!! failure "Deprecated in sing-box 1.8.0" !!! failure "Deprecated in sing-box 1.8.0"
Geosite is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geosite-to-rule-sets). Geosite is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geosite-to-rule-sets).
### Structure ### Structure

View File

@@ -4,7 +4,7 @@ icon: material/delete-clock
!!! failure "已在 sing-box 1.8.0 废弃" !!! failure "已在 sing-box 1.8.0 废弃"
Geosite 已废弃且将在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/zh/migration/#geosite)。 Geosite 已废弃且可能在不久的将来移除,参阅 [迁移指南](/zh/migration/#geosite)。
### 结构 ### 结构

View File

@@ -1,12 +1,7 @@
--- ---
icon: material/new-box icon: material/alert-decagram
--- ---
!!! quote "Changes in sing-box 1.11.0"
:material-plus: [action](#action)
:material-alert: [outbound](#outbound)
!!! quote "Changes in sing-box 1.10.0" !!! quote "Changes in sing-box 1.10.0"
:material-plus: [client](#client) :material-plus: [client](#client)
@@ -134,7 +129,6 @@ icon: material/new-box
"rule_set_ipcidr_match_source": false, "rule_set_ipcidr_match_source": false,
"rule_set_ip_cidr_match_source": false, "rule_set_ip_cidr_match_source": false,
"invert": false, "invert": false,
"action": "route",
"outbound": "direct" "outbound": "direct"
}, },
{ {
@@ -142,7 +136,6 @@ icon: material/new-box
"mode": "and", "mode": "and",
"rules": [], "rules": [],
"invert": false, "invert": false,
"action": "route",
"outbound": "direct" "outbound": "direct"
} }
] ]
@@ -216,7 +209,7 @@ Match domain using regular expression.
!!! failure "Deprecated in sing-box 1.8.0" !!! failure "Deprecated in sing-box 1.8.0"
Geosite is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geosite-to-rule-sets). Geosite is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geosite-to-rule-sets).
Match geosite. Match geosite.
@@ -224,7 +217,7 @@ Match geosite.
!!! failure "Deprecated in sing-box 1.8.0" !!! failure "Deprecated in sing-box 1.8.0"
GeoIP is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geoip-to-rule-sets). GeoIP is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geoip-to-rule-sets).
Match source geoip. Match source geoip.
@@ -232,7 +225,7 @@ Match source geoip.
!!! failure "Deprecated in sing-box 1.8.0" !!! failure "Deprecated in sing-box 1.8.0"
GeoIP is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geoip-to-rule-sets). GeoIP is deprecated and may be removed in the future, check [Migration](/migration/#migrate-geoip-to-rule-sets).
Match geoip. Match geoip.
@@ -364,17 +357,11 @@ Make `ip_cidr` in rule-sets match the source IP.
Invert match result. Invert match result.
#### action #### outbound
==Required== ==Required==
See [Rule Actions](../rule_action/) for details. Tag of the target outbound.
#### outbound
!!! failure "Deprecated in sing-box 1.11.0"
Moved to [Rule Action](../rule_action#route).
### Logical Fields ### Logical Fields

View File

@@ -1,12 +1,7 @@
--- ---
icon: material/new-box icon: material/alert-decagram
--- ---
!!! quote "sing-box 1.11.0 中的更改"
:material-plus: [action](#action)
:material-alert: [outbound](#outbound)
!!! quote "sing-box 1.10.0 中的更改" !!! quote "sing-box 1.10.0 中的更改"
:material-plus: [client](#client) :material-plus: [client](#client)
@@ -132,7 +127,6 @@ icon: material/new-box
"rule_set_ipcidr_match_source": false, "rule_set_ipcidr_match_source": false,
"rule_set_ip_cidr_match_source": false, "rule_set_ip_cidr_match_source": false,
"invert": false, "invert": false,
"action": "route",
"outbound": "direct" "outbound": "direct"
}, },
{ {
@@ -140,7 +134,6 @@ icon: material/new-box
"mode": "and", "mode": "and",
"rules": [], "rules": [],
"invert": false, "invert": false,
"action": "route",
"outbound": "direct" "outbound": "direct"
} }
] ]
@@ -362,17 +355,11 @@ icon: material/new-box
反选匹配结果。 反选匹配结果。
#### action #### outbound
==必填== ==必填==
参阅 [规则行动](../rule_action/) 目标出站的标签
#### outbound
!!! failure "已在 sing-box 1.11.0 废弃"
已移动到 [规则行动](../rule_action#route).
### 逻辑字段 ### 逻辑字段

View File

@@ -1,139 +0,0 @@
---
icon: material/new-box
---
# Rule Action
!!! question "Since sing-box 1.11.0"
## Final actions
### route
```json
{
"action": "route", // default
"outbound": ""
}
```
`route` inherits the classic rule behavior of routing connection to the specified outbound.
#### outbound
==Required==
Tag of target outbound.
### route-options
```json
{
"action": "route-options",
"udp_disable_domain_unmapping": false,
"udp_connect": false
}
```
`route-options` set options for routing.
#### udp_disable_domain_unmapping
If enabled, for UDP proxy requests addressed to a domain,
the original packet address will be sent in the response instead of the mapped domain.
This option is used for compatibility with clients that
do not support receiving UDP packets with domain addresses, such as Surge.
#### udp_connect
If enabled, attempts to connect UDP connection to the destination instead of listen.
### reject
```json
{
"action": "reject",
"method": "default", // default
"no_drop": false
}
```
`reject` reject connections
The specified method is used for reject tun connections if `sniff` action has not been performed yet.
For non-tun connections and already established connections, will just be closed.
#### method
- `default`: Reply with TCP RST for TCP connections, and ICMP port unreachable for UDP packets.
- `drop`: Drop packets.
#### no_drop
If not enabled, `method` will be temporarily overwritten to `drop` after 50 triggers in 30s.
Not available when `method` is set to drop.
### hijack-dns
```json
{
"action": "hijack-dns"
}
```
`hijack-dns` hijack DNS requests to the sing-box DNS module.
## Non-final actions
### sniff
```json
{
"action": "sniff",
"sniffer": [],
"timeout": ""
}
```
`sniff` performs protocol sniffing on connections.
For deprecated `inbound.sniff` options, it is considered to `sniff()` performed before routing.
#### sniffer
Enabled sniffers.
All sniffers enabled by default.
Available protocol values an be found on in [Protocol Sniff](../sniff/)
#### timeout
Timeout for sniffing.
`300ms` is used by default.
### resolve
```json
{
"action": "resolve",
"strategy": "",
"server": ""
}
```
`resolve` resolve request destination from domain to IP addresses.
#### strategy
DNS resolution strategy, available values are: `prefer_ipv4`, `prefer_ipv6`, `ipv4_only`, `ipv6_only`.
`dns.strategy` will be used by default.
#### server
Specifies DNS server tag to use instead of selecting through DNS routing.

View File

@@ -1,136 +0,0 @@
---
icon: material/new-box
---
# 规则动作
!!! question "自 sing-box 1.11.0 起"
## 最终动作
### route
```json
{
"action": "route", // 默认
"outbound": "",
"udp_disable_domain_unmapping": false
}
```
`route` 继承了将连接路由到指定出站的经典规则动作。
#### outbound
==必填==
目标出站的标签。
### route-options
```json
{
"action": "route-options",
"udp_disable_domain_unmapping": false,
"udp_connect": false
}
```
#### udp_disable_domain_unmapping
如果启用,对于地址为域的 UDP 代理请求,将在响应中发送原始包地址而不是映射的域。
此选项用于兼容不支持接收带有域地址的 UDP 包的客户端,如 Surge。
#### udp_connect
如果启用,将尝试将 UDP 连接 connect 到目标而不是 listen。
### reject
```json
{
"action": "reject",
"method": "default", // 默认
"no_drop": false
}
```
`reject` 拒绝连接。
如果尚未执行 `sniff` 操作,则将使用指定方法拒绝 tun 连接。
对于非 tun 连接和已建立的连接,将直接关闭。
#### method
- `default`: 对于 TCP 连接回复 RST对于 UDP 包回复 ICMP 端口不可达。
- `drop`: 丢弃数据包。
#### no_drop
如果未启用,则 30 秒内触发 50 次后,`method` 将被暂时覆盖为 `drop`
`method` 设为 `drop` 时不可用。
### hijack-dns
```json
{
"action": "hijack-dns"
}
```
`hijack-dns` 劫持 DNS 请求至 sing-box DNS 模块。
## 非最终动作
### sniff
```json
{
"action": "sniff",
"sniffer": [],
"timeout": ""
}
```
`sniff` 对连接执行协议嗅探。
对于已弃用的 `inbound.sniff` 选项,被视为在路由之前执行的 `sniff`
#### sniffer
启用的探测器。
默认启用所有探测器。
可用的协议值可以在 [协议嗅探](../sniff/) 中找到。
#### timeout
探测超时时间。
默认使用 300ms。
### resolve
```json
{
"action": "resolve",
"strategy": "",
"server": ""
}
```
`resolve` 将请求的目标从域名解析为 IP 地址。
#### strategy
DNS 解析策略,可用值有:`prefer_ipv4``prefer_ipv6``ipv4_only``ipv6_only`
默认使用 `dns.strategy`
#### server
指定要使用的 DNS 服务器的标签,而不是通过 DNS 路由进行选择。

View File

@@ -1,15 +1,3 @@
---
icon: material/delete-clock
---
!!! quote "Changes in sing-box 1.11.0"
:material-delete-clock: [sniff](#sniff)
:material-delete-clock: [sniff_override_destination](#sniff_override_destination)
:material-delete-clock: [sniff_timeout](#sniff_timeout)
:material-delete-clock: [domain_strategy](#domain_strategy)
:material-delete-clock: [udp_disable_domain_unmapping](#udp_disable_domain_unmapping)
### Structure ### Structure
```json ```json
@@ -80,40 +68,24 @@ Requires target inbound support, see [Injectable](/configuration/inbound/#fields
#### sniff #### 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. Enable sniffing.
See [Protocol Sniff](/configuration/route/sniff/) for details. See [Protocol Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination #### 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. Override the connection destination address with the sniffed domain.
If the domain name is invalid (like tor), this will not work. If the domain name is invalid (like tor), this will not work.
#### sniff_timeout #### 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. Timeout for sniffing.
`300ms` is used by default. 300ms is used by default.
#### domain_strategy #### 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`. One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the requested domain name will be resolved to IP before routing. If set, the requested domain name will be resolved to IP before routing.
@@ -122,10 +94,6 @@ If `sniff_override_destination` is in effect, its value will be taken as a fallb
#### udp_disable_domain_unmapping #### 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, 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. the original packet address will be sent in the response instead of the mapped domain.

View File

@@ -1,15 +1,3 @@
---
icon: material/delete-clock
---
!!! quote "sing-box 1.11.0 中的更改"
:material-delete-clock: [sniff](#sniff)
:material-delete-clock: [sniff_override_destination](#sniff_override_destination)
:material-delete-clock: [sniff_timeout](#sniff_timeout)
:material-delete-clock: [domain_strategy](#domain_strategy)
:material-delete-clock: [udp_disable_domain_unmapping](#udp_disable_domain_unmapping)
### 结构 ### 结构
```json ```json
@@ -81,40 +69,24 @@ UDP NAT 过期时间,以秒为单位。
#### sniff #### sniff
!!! failure "已在 sing-box 1.11.0 废弃"
入站字段已废弃且将在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/migration/#migrate-legacy-inbound-fields-to-rule-actions).
启用协议探测。 启用协议探测。
参阅 [协议探测](/zh/configuration/route/sniff/) 参阅 [协议探测](/zh/configuration/route/sniff/)
#### sniff_override_destination #### sniff_override_destination
!!! failure "已在 sing-box 1.11.0 废弃"
入站字段已废弃且将在 sing-box 1.12.0 中被移除。
用探测出的域名覆盖连接目标地址。 用探测出的域名覆盖连接目标地址。
如果域名无效(如 Tor将不生效。 如果域名无效(如 Tor将不生效。
#### sniff_timeout #### sniff_timeout
!!! failure "已在 sing-box 1.11.0 废弃"
入站字段已废弃且将在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/migration/#migrate-legacy-inbound-fields-to-rule-actions).
探测超时时间。 探测超时时间。
默认使用 300ms。 默认使用 300ms。
#### domain_strategy #### 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` 可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`
如果设置,请求的域名将在路由之前解析为 IP。 如果设置,请求的域名将在路由之前解析为 IP。
@@ -123,10 +95,6 @@ UDP NAT 过期时间,以秒为单位。
#### udp_disable_domain_unmapping #### 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 代理请求,将在响应中发送原始包地址而不是映射的域。
此选项用于兼容不支持接收带有域地址的 UDP 包的客户端,如 Surge。 此选项用于兼容不支持接收带有域地址的 UDP 包的客户端,如 Surge。

View File

@@ -4,32 +4,6 @@ icon: material/delete-alert
# Deprecated Feature List # 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 ## 1.10.0
#### TUN address fields are merged #### TUN address fields are merged
@@ -38,7 +12,7 @@ Old fields will be removed in sing-box 1.12.0.
`inet4_route_address` and `inet6_route_address` are merged into `route_address`, `inet4_route_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`. `inet4_route_exclude_address` and `inet6_route_exclude_address` are merged into `route_exclude_address`.
Old fields will be removed in sing-box 1.12.0. Old fields are deprecated and will be removed in sing-box 1.11.0.
#### Match source rule items are renamed #### Match source rule items are renamed
@@ -58,7 +32,7 @@ check [Migration](/migration/#migrate-cache-file-from-clash-api-to-independent-o
#### GeoIP #### GeoIP
GeoIP is deprecated and will be removed in sing-box 1.12.0. GeoIP is deprecated and may be removed in the future.
The maxmind GeoIP National Database, as an IP classification database, The maxmind GeoIP National Database, as an IP classification database,
is not entirely suitable for traffic bypassing, is not entirely suitable for traffic bypassing,
@@ -69,7 +43,7 @@ check [Migration](/migration/#migrate-geoip-to-rule-sets).
#### Geosite #### Geosite
Geosite is deprecated and will be removed in sing-box 1.12.0. Geosite is deprecated and may be removed in the future.
Geosite, the `domain-list-community` project maintained by V2Ray as an early traffic bypassing solution, 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. suffers from a number of problems, including lack of maintenance, inaccurate rules, and difficult management.

View File

@@ -4,29 +4,6 @@ icon: material/delete-alert
# 废弃功能列表 # 废弃功能列表
## 1.11.0
#### 旧的特殊出站
旧的特殊出站(`block` / `dns`)已废弃且可以通过规则动作替代,
参阅 [迁移指南](/migration/#migrate-legacy-special-outbounds-to-rule-actions)。
旧字段将在 sing-box 1.13.0 中被移除。
#### 旧的入站字段
旧的入站字段(`inbound.<sniff/domain_strategy/...>`)已废弃且可以通过规则动作替代,
参阅 [迁移指南](/migration/#migrate-legacy-inbound-fields-to-rule-actions)。
旧字段将在 sing-box 1.13.0 中被移除。
#### 旧的 DNS 路由参数
旧的 DNS 路由参数(`disable_cache``rewrite_ttl``client_subnet`)已废弃且可以通过规则动作替代,
参阅 [迁移指南](/migration/#migrate-legacy-dns-route-options-to-rule-actions)。
旧字段将在 sing-box 1.12.0 中被移除。
## 1.10.0 ## 1.10.0
#### Match source 规则项已重命名 #### Match source 规则项已重命名
@@ -40,7 +17,7 @@ icon: material/delete-alert
`inet4_route_address``inet6_route_address` 已合并为 `route_address` `inet4_route_address``inet6_route_address` 已合并为 `route_address`
`inet4_route_exclude_address``inet6_route_exclude_address` 已合并为 `route_exclude_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 的支持 #### 移除对 go1.18 和 go1.19 的支持
@@ -55,7 +32,7 @@ Clash API 中的 `cache_file` 及相关功能已废弃且已迁移到独立的 `
#### GeoIP #### GeoIP
GeoIP 已废弃且将在 sing-box 1.12.0 中被移除。 GeoIP 已废弃且可能在不久的将来移除。
maxmind GeoIP 国家数据库作为 IP 分类数据库,不完全适合流量绕过, maxmind GeoIP 国家数据库作为 IP 分类数据库,不完全适合流量绕过,
且现有的实现均存在内存使用大与管理困难的问题。 且现有的实现均存在内存使用大与管理困难的问题。
@@ -65,7 +42,7 @@ sing-box 1.8.0 引入了[规则集](/configuration/rule-set/)
#### Geosite #### Geosite
Geosite 已废弃且将在 sing-box 1.12.0 中被移除。 Geosite 已废弃且可能在不久的将来移除。
Geosite即由 V2Ray 维护的 domain-list-community 项目,作为早期流量绕过解决方案, Geosite即由 V2Ray 维护的 domain-list-community 项目,作为早期流量绕过解决方案,
存在着包括缺少维护、规则不准确和管理困难内的大量问题。 存在着包括缺少维护、规则不准确和管理困难内的大量问题。

View File

@@ -2,212 +2,6 @@
icon: material/arrange-bring-forward 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 ## 1.10.0
### TUN address fields are merged ### TUN address fields are merged
@@ -216,6 +10,8 @@ Legacy DNS route options are deprecated and can be replaced by rule actions.
`inet4_route_address` and `inet6_route_address` are merged into `route_address`, `inet4_route_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`. `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" !!! info "References"
[TUN](/configuration/inbound/tun/) [TUN](/configuration/inbound/tun/)

View File

@@ -2,212 +2,6 @@
icon: material/arrange-bring-forward 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 ## 1.10.0
### TUN 地址字段已合并 ### TUN 地址字段已合并

View File

@@ -78,36 +78,9 @@ var OptionTUNAddressX = Note{
MigrationLink: "https://sing-box.sagernet.org/migration/#tun-address-fields-are-merged", 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{ var Options = []Note{
OptionBadMatchSource, OptionBadMatchSource,
OptionGEOIP, OptionGEOIP,
OptionGEOSITE, OptionGEOSITE,
OptionTUNAddressX, OptionTUNAddressX,
OptionSpecialOutbounds,
OptionInboundOptions,
OptionLegacyDNSRouteOptions,
} }

View File

@@ -7,23 +7,15 @@ import (
"github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/logger"
) )
type stderrManager struct { type envManager struct {
logger logger.Logger logger logger.Logger
reported map[string]bool
} }
func NewStderrManager(logger logger.Logger) Manager { func NewEnvManager(logger logger.Logger) Manager {
return &stderrManager{ return &envManager{logger: logger}
logger: logger,
reported: make(map[string]bool),
}
} }
func (f *stderrManager) ReportDeprecated(feature Note) { func (f *envManager) ReportDeprecated(feature Note) {
if f.reported[feature.Name] {
return
}
f.reported[feature.Name] = true
if !feature.Impending() { if !feature.Impending() {
f.logger.Warn(feature.MessageWithLink()) f.logger.Warn(feature.MessageWithLink())
return return

View File

@@ -20,6 +20,7 @@ type CommandClient struct {
type CommandClientOptions struct { type CommandClientOptions struct {
Command int32 Command int32
StatusInterval int64 StatusInterval int64
IsMainClient bool
} }
type CommandClientHandler interface { type CommandClientHandler interface {
@@ -28,6 +29,7 @@ type CommandClientHandler interface {
ClearLogs() ClearLogs()
WriteLogs(messageList StringIterator) WriteLogs(messageList StringIterator)
WriteStatus(message *StatusMessage) WriteStatus(message *StatusMessage)
OpenURL(url string)
WriteGroups(message OutboundGroupIterator) WriteGroups(message OutboundGroupIterator)
InitializeClashMode(modeList StringIterator, currentMode string) InitializeClashMode(modeList StringIterator, currentMode string)
UpdateClashMode(newMode string) UpdateClashMode(newMode string)
@@ -91,9 +93,13 @@ func (c *CommandClient) Connect() error {
c.handler.Connected() c.handler.Connected()
go c.handleLogConn(conn) go c.handleLogConn(conn)
case CommandStatus: 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) err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
if err != nil { if err != nil {
return E.Cause(err, "write interval") return E.Cause(err, "write header")
} }
c.handler.Connected() c.handler.Connected()
go c.handleStatusConn(conn) go c.handleStatusConn(conn)

View File

@@ -0,0 +1,40 @@
package libbox
import (
"encoding/binary"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/varbin"
)
type myEvent interface {
writeTo(writer varbin.Writer)
}
func readEvent(reader varbin.Reader) (myEvent, error) {
eventType, err := reader.ReadByte()
if err != nil {
return nil, err
}
switch eventType {
case eventTypeEmpty:
return nil, nil
case eventTypeOpenURL:
url, err := varbin.ReadValue[string](reader, binary.BigEndian)
if err != nil {
return nil, err
}
return &eventOpenURL{URL: url}, nil
default:
return nil, E.New("unknown event type: ", eventType)
}
}
type eventOpenURL struct {
URL string
}
func (e *eventOpenURL) writeTo(writer varbin.Writer) {
writer.WriteByte(eventTypeOpenURL)
varbin.Write(writer, binary.BigEndian, e.URL)
}

View File

@@ -33,6 +33,7 @@ type CommandServer struct {
urlTestUpdate chan struct{} urlTestUpdate chan struct{}
modeUpdate chan struct{} modeUpdate chan struct{}
logReset chan struct{} logReset chan struct{}
events chan myEvent
closedConnections []Connection closedConnections []Connection
} }
@@ -52,6 +53,7 @@ func NewCommandServer(handler CommandServerHandler, maxLines int32) *CommandServ
urlTestUpdate: make(chan struct{}, 1), urlTestUpdate: make(chan struct{}, 1),
modeUpdate: make(chan struct{}, 1), modeUpdate: make(chan struct{}, 1),
logReset: make(chan struct{}, 1), logReset: make(chan struct{}, 1),
events: make(chan myEvent, 8),
} }
server.observer = observable.NewObserver[string](server.subscriber, 64) server.observer = observable.NewObserver[string](server.subscriber, 64)
return server return server
@@ -61,6 +63,12 @@ func (s *CommandServer) SetService(newService *BoxService) {
if newService != nil { if newService != nil {
service.PtrFromContext[urltest.HistoryStorage](newService.ctx).SetHook(s.urlTestUpdate) service.PtrFromContext[urltest.HistoryStorage](newService.ctx).SetHook(s.urlTestUpdate)
newService.instance.Router().ClashServer().(*clashapi.Server).SetModeUpdateHook(s.modeUpdate) 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.service = newService
s.notifyURLTestUpdate() s.notifyURLTestUpdate()

View File

@@ -1,6 +1,7 @@
package libbox package libbox
import ( import (
std_bufio "bufio"
"encoding/binary" "encoding/binary"
"net" "net"
"runtime" "runtime"
@@ -9,9 +10,15 @@ import (
"github.com/sagernet/sing-box/common/conntrack" "github.com/sagernet/sing-box/common/conntrack"
"github.com/sagernet/sing-box/experimental/clashapi" "github.com/sagernet/sing-box/experimental/clashapi"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/memory" "github.com/sagernet/sing/common/memory"
) )
const (
eventTypeEmpty byte = iota
eventTypeOpenURL
)
type StatusMessage struct { type StatusMessage struct {
Memory int64 Memory int64
Goroutines int32 Goroutines int32
@@ -44,19 +51,44 @@ func (s *CommandServer) readStatus() StatusMessage {
} }
func (s *CommandServer) handleStatusConn(conn net.Conn) error { 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 var interval int64
err := binary.Read(conn, binary.BigEndian, &interval) err = binary.Read(conn, binary.BigEndian, &interval)
if err != nil { if err != nil {
return E.Cause(err, "read interval") return E.Cause(err, "read interval")
} }
ticker := time.NewTicker(time.Duration(interval)) ticker := time.NewTicker(time.Duration(interval))
defer ticker.Stop() defer ticker.Stop()
ctx := connKeepAlive(conn) 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()
}
}
} else {
for { for {
err = binary.Write(conn, binary.BigEndian, s.readStatus()) err = binary.Write(conn, binary.BigEndian, s.readStatus())
if err != nil { if err != nil {
return err return err
} }
writer.Flush()
select { select {
case <-ctx.Done(): case <-ctx.Done():
return ctx.Err() return ctx.Err()
@@ -64,11 +96,28 @@ func (s *CommandServer) handleStatusConn(conn net.Conn) error {
} }
} }
} }
}
func (c *CommandClient) handleStatusConn(conn net.Conn) { func (c *CommandClient) handleStatusConn(conn net.Conn) {
reader := std_bufio.NewReader(conn)
for { 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 var message StatusMessage
err := binary.Read(conn, binary.BigEndian, &message) err := binary.Read(reader, binary.BigEndian, &message)
if err != nil { if err != nil {
c.handler.Disconnected(err.Error()) c.handler.Disconnected(err.Error())
return return

View File

@@ -30,12 +30,11 @@ func parseConfig(ctx context.Context, configContent string) (option.Options, err
} }
func CheckConfig(configContent string) error { func CheckConfig(configContent string) error {
ctx := box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry()) options, err := parseConfig(box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry()), configContent)
options, err := parseConfig(ctx, configContent)
if err != nil { if err != nil {
return err return err
} }
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
ctx = service.ContextWith[platform.Interface](ctx, (*platformInterfaceStub)(nil)) ctx = service.ContextWith[platform.Interface](ctx, (*platformInterfaceStub)(nil))
instance, err := box.New(box.Options{ instance, err := box.New(box.Options{
@@ -58,7 +57,7 @@ func (s *platformInterfaceStub) UsePlatformAutoDetectInterfaceControl() bool {
return true return true
} }
func (s *platformInterfaceStub) AutoDetectInterfaceControl(fd int) error { func (s *platformInterfaceStub) AutoDetectInterfaceControl() control.Func {
return nil return nil
} }
@@ -138,8 +137,7 @@ func (s *interfaceMonitorStub) RegisterCallback(callback tun.DefaultInterfaceUpd
func (s *interfaceMonitorStub) UnregisterCallback(element *list.Element[tun.DefaultInterfaceUpdateCallback]) { func (s *interfaceMonitorStub) UnregisterCallback(element *list.Element[tun.DefaultInterfaceUpdateCallback]) {
} }
func (s *platformInterfaceStub) SendNotification(notification *platform.Notification) error { func (s *platformInterfaceStub) OpenURL(url string) {
return nil
} }
func FormatConfig(configContent string) (string, error) { func FormatConfig(configContent string) (string, error) {

View File

@@ -4,28 +4,27 @@ import (
"sync" "sync"
"github.com/sagernet/sing-box/experimental/deprecated" "github.com/sagernet/sing-box/experimental/deprecated"
"github.com/sagernet/sing/common"
) )
var _ deprecated.Manager = (*deprecatedManager)(nil) var _ deprecated.Manager = (*deprecatedManager)(nil)
type deprecatedManager struct { type deprecatedManager struct {
access sync.Mutex access sync.Mutex
notes []deprecated.Note features []deprecated.Note
} }
func (m *deprecatedManager) ReportDeprecated(feature deprecated.Note) { func (m *deprecatedManager) ReportDeprecated(feature deprecated.Note) {
m.access.Lock() m.access.Lock()
defer m.access.Unlock() defer m.access.Unlock()
m.notes = common.Uniq(append(m.notes, feature)) m.features = append(m.features, feature)
} }
func (m *deprecatedManager) Get() []deprecated.Note { func (m *deprecatedManager) Get() []deprecated.Note {
m.access.Lock() m.access.Lock()
defer m.access.Unlock() defer m.access.Unlock()
notes := m.notes features := m.features
m.notes = nil m.features = nil
return notes return features
} }
var _ = deprecated.Note(DeprecatedNote{}) var _ = deprecated.Note(DeprecatedNote{})

View File

@@ -1,30 +0,0 @@
package libbox
import (
"net"
"syscall"
)
// copied from net.linkFlags
func linkFlags(rawFlags uint32) net.Flags {
var f net.Flags
if rawFlags&syscall.IFF_UP != 0 {
f |= net.FlagUp
}
if rawFlags&syscall.IFF_RUNNING != 0 {
f |= net.FlagRunning
}
if rawFlags&syscall.IFF_BROADCAST != 0 {
f |= net.FlagBroadcast
}
if rawFlags&syscall.IFF_LOOPBACK != 0 {
f |= net.FlagLoopback
}
if rawFlags&syscall.IFF_POINTOPOINT != 0 {
f |= net.FlagPointToPoint
}
if rawFlags&syscall.IFF_MULTICAST != 0 {
f |= net.FlagMulticast
}
return f
}

View File

@@ -1,11 +0,0 @@
//go:build !linux
package libbox
import (
"net"
)
func linkFlags(rawFlags uint32) net.Flags {
panic("stub!")
}

View File

@@ -22,7 +22,6 @@ type PlatformInterface interface {
IncludeAllNetworks() bool IncludeAllNetworks() bool
ReadWIFIState() *WIFIState ReadWIFIState() *WIFIState
ClearDNSCache() ClearDNSCache()
SendNotification(notification *Notification) error
} }
type TunInterface interface { type TunInterface interface {
@@ -39,7 +38,6 @@ type NetworkInterface struct {
MTU int32 MTU int32
Name string Name string
Addresses StringIterator Addresses StringIterator
Flags int32
} }
type WIFIState struct { type WIFIState struct {
@@ -56,16 +54,6 @@ type NetworkInterfaceIterator interface {
HasNext() bool HasNext() bool
} }
type Notification struct {
Identifier string
TypeName string
TypeID int32
Title string
Subtitle string
Body string
OpenURL string
}
type OnDemandRule interface { type OnDemandRule interface {
Target() int32 Target() int32
DNSSearchDomainMatch() StringIterator DNSSearchDomainMatch() StringIterator

View File

@@ -14,7 +14,7 @@ import (
type Interface interface { type Interface interface {
Initialize(ctx context.Context, router adapter.Router) error Initialize(ctx context.Context, router adapter.Router) error
UsePlatformAutoDetectInterfaceControl() bool UsePlatformAutoDetectInterfaceControl() bool
AutoDetectInterfaceControl(fd int) error AutoDetectInterfaceControl() control.Func
OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
UsePlatformDefaultInterfaceMonitor() bool UsePlatformDefaultInterfaceMonitor() bool
CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor
@@ -25,15 +25,5 @@ type Interface interface {
ClearDNSCache() ClearDNSCache()
ReadWIFIState() adapter.WIFIState ReadWIFIState() adapter.WIFIState
process.Searcher process.Searcher
SendNotification(notification *Notification) error OpenURL(url string)
}
type Notification struct {
Identifier string
TypeName string
TypeID int32
Title string
Subtitle string
Body string
OpenURL string
} }

View File

@@ -35,24 +35,24 @@ type BoxService struct {
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
instance *box.Box instance *box.Box
platformInterface *platformInterfaceWrapper
pauseManager pause.Manager pauseManager pause.Manager
urlTestHistoryStorage *urltest.HistoryStorage urlTestHistoryStorage *urltest.HistoryStorage
servicePauseFields servicePauseFields
} }
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) { func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
ctx := box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry()) 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) options, err := parseConfig(ctx, configContent)
if err != nil { if err != nil {
return nil, err return nil, err
} }
runtimeDebug.FreeOSMemory() runtimeDebug.FreeOSMemory()
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
urlTestHistoryStorage := urltest.NewHistoryStorage() urlTestHistoryStorage := urltest.NewHistoryStorage()
ctx = service.ContextWithPtr(ctx, urlTestHistoryStorage) ctx = service.ContextWithPtr(ctx, urlTestHistoryStorage)
ctx = service.ContextWith[deprecated.Manager](ctx, new(deprecatedManager))
platformWrapper := &platformInterfaceWrapper{iif: platformInterface, useProcFS: platformInterface.UseProcFS()} platformWrapper := &platformInterfaceWrapper{iif: platformInterface, useProcFS: platformInterface.UseProcFS()}
ctx = service.ContextWith[platform.Interface](ctx, platformWrapper) ctx = service.ContextWith[platform.Interface](ctx, platformWrapper)
instance, err := box.New(box.Options{ instance, err := box.New(box.Options{
@@ -69,6 +69,7 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
instance: instance, instance: instance,
platformInterface: platformWrapper,
urlTestHistoryStorage: urlTestHistoryStorage, urlTestHistoryStorage: urlTestHistoryStorage,
pauseManager: service.FromContext[pause.Manager](ctx), pauseManager: service.FromContext[pause.Manager](ctx),
}, nil }, nil
@@ -107,6 +108,7 @@ type platformInterfaceWrapper struct {
iif PlatformInterface iif PlatformInterface
useProcFS bool useProcFS bool
router adapter.Router router adapter.Router
openURLFunc func(url string)
} }
func (w *platformInterfaceWrapper) Initialize(ctx context.Context, router adapter.Router) error { func (w *platformInterfaceWrapper) Initialize(ctx context.Context, router adapter.Router) error {
@@ -118,8 +120,12 @@ func (w *platformInterfaceWrapper) UsePlatformAutoDetectInterfaceControl() bool
return w.iif.UsePlatformAutoDetectInterfaceControl() return w.iif.UsePlatformAutoDetectInterfaceControl()
} }
func (w *platformInterfaceWrapper) AutoDetectInterfaceControl(fd int) error { 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)) return w.iif.AutoDetectInterfaceControl(int32(fd))
})
}
} }
func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) { func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
@@ -177,7 +183,6 @@ func (w *platformInterfaceWrapper) Interfaces() ([]control.Interface, error) {
MTU: int(netInterface.MTU), MTU: int(netInterface.MTU),
Name: netInterface.Name, Name: netInterface.Name,
Addresses: common.Map(iteratorToArray[string](netInterface.Addresses), netip.MustParsePrefix), Addresses: common.Map(iteratorToArray[string](netInterface.Addresses), netip.MustParsePrefix),
Flags: linkFlags(uint32(netInterface.Flags)),
}) })
} }
return interfaces, nil return interfaces, nil
@@ -238,6 +243,8 @@ func (w *platformInterfaceWrapper) WriteMessage(level log.Level, message string)
w.iif.WriteLog(message) w.iif.WriteLog(message)
} }
func (w *platformInterfaceWrapper) SendNotification(notification *platform.Notification) error { func (w *platformInterfaceWrapper) OpenURL(url string) {
return w.iif.SendNotification((*Notification)(notification)) if w.openURLFunc != nil {
w.openURLFunc(url)
}
} }

View File

@@ -23,6 +23,7 @@ var (
func init() { func init() {
debug.SetPanicOnFault(true) debug.SetPanicOnFault(true)
debug.SetTraceback("all")
} }
func Setup(basePath string, workingPath string, tempPath string, isTVOS bool) { func Setup(basePath string, workingPath string, tempPath string, isTVOS bool) {

12
go.mod
View File

@@ -23,16 +23,16 @@ require (
github.com/sagernet/fswatch v0.1.1 github.com/sagernet/fswatch v0.1.1
github.com/sagernet/gomobile v0.1.4 github.com/sagernet/gomobile v0.1.4
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3 github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3
github.com/sagernet/quic-go v0.48.1-beta.1 github.com/sagernet/quic-go v0.48.0-beta.1
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.6.0-alpha.1 github.com/sagernet/sing v0.5.0-rc.4.0.20241101160402-8452992a6369
github.com/sagernet/sing-dns v0.3.1-0.20241105104342-1914f319ddab 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-mux v0.2.1-0.20241020175909-fe6153f7a9ec
github.com/sagernet/sing-quic v0.3.0-rc.2 github.com/sagernet/sing-quic v0.3.0-rc.1
github.com/sagernet/sing-shadowsocks v0.2.7 github.com/sagernet/sing-shadowsocks v0.2.7
github.com/sagernet/sing-shadowsocks2 v0.2.0 github.com/sagernet/sing-shadowsocks2 v0.2.0
github.com/sagernet/sing-shadowtls v0.1.4 github.com/sagernet/sing-shadowtls v0.1.4
github.com/sagernet/sing-tun v0.5.0-alpha.1 github.com/sagernet/sing-tun v0.4.0-rc.4.0.20241023054150-3b5b396d06f7
github.com/sagernet/sing-vmess v0.1.12 github.com/sagernet/sing-vmess v0.1.12
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
github.com/sagernet/utls v1.6.7 github.com/sagernet/utls v1.6.7
@@ -53,8 +53,6 @@ require (
howett.net/plist v1.0.1 howett.net/plist v1.0.1
) )
//replace github.com/sagernet/sing => ../sing
require ( require (
github.com/ajg/form v1.5.1 // indirect github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.6 // indirect github.com/andybalholm/brotli v1.0.6 // indirect

20
go.sum
View File

@@ -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/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 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
github.com/sagernet/quic-go v0.48.1-beta.1 h1:ElPaV5yzlXIKZpqFMAcUGax6vddi3zt4AEpT94Z0vwo= github.com/sagernet/quic-go v0.48.0-beta.1 h1:86hQZrmuoARI3BpDRkQaP0iAVpywA4YsRrzJPYuPKWg=
github.com/sagernet/quic-go v0.48.1-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k= github.com/sagernet/quic-go v0.48.0-beta.1/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 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= 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.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
github.com/sagernet/sing v0.6.0-alpha.1 h1:dFbrjRWOTTrkezGWcJmpC9AwDU09nigSVhEibHQxKV4= github.com/sagernet/sing v0.5.0-rc.4.0.20241101160402-8452992a6369 h1:gfiUYWslwKM7OtvG37PV0iIDCWcacJSEUS2h29rpYac=
github.com/sagernet/sing v0.6.0-alpha.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= 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.1-0.20241105104342-1914f319ddab h1:djP4EY/KM5T62xscormLflVi7eDlHv6p7md1FHMSArE= github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241023053951-feb6d5403f2a h1:jpAlbmZxc1LymZrmJacsvHI57Wito5xy8qASZJMWoOQ=
github.com/sagernet/sing-dns v0.3.1-0.20241105104342-1914f319ddab/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M= 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 h1:6Fd/VsEsw9qIjaGi1IBTZSb4b4v5JYtNcoiBtGsQC48=
github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec/go.mod h1:RSwqqHwbtTOX3vs6ms8vMtBGH/0ZNyLm/uwt6TlmR84= github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec/go.mod h1:RSwqqHwbtTOX3vs6ms8vMtBGH/0ZNyLm/uwt6TlmR84=
github.com/sagernet/sing-quic v0.3.0-rc.2 h1:7vcC4bdS1GBJzHZhfmJiH0CfzQ4mYLUW51Z2RNHcGwc= github.com/sagernet/sing-quic v0.3.0-rc.1 h1:SlzL1yfEAKJyRduub8vzOVtbyTLAX7RZEEBZxO5utts=
github.com/sagernet/sing-quic v0.3.0-rc.2/go.mod h1:3UOq51WVqzra7eCgod7t4hqnTaOiZzFUci9avMrtOqs= github.com/sagernet/sing-quic v0.3.0-rc.1/go.mod h1:uX+aUHA0fgIN6U3WViseDpSdTQriViZ7qz0Wbsf1mNQ=
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8= github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE= 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 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= 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 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
github.com/sagernet/sing-tun v0.5.0-alpha.1 h1:Na5uwSXizchIXDqeo1hwcs1O9+ekx5MjT74NwVh0mXc= github.com/sagernet/sing-tun v0.4.0-rc.4.0.20241023054150-3b5b396d06f7 h1:wWfRBSP8v0Gc9yUeMgoKCiG+LIs/+bYLWWwVVYSbGFI=
github.com/sagernet/sing-tun v0.5.0-alpha.1/go.mod h1:I1exfctioZX1nDm+RtU72bvy17bExvIjsVZVst8Ck7M= github.com/sagernet/sing-tun v0.4.0-rc.4.0.20241023054150-3b5b396d06f7/go.mod h1:2v1L3BQKzoOpGuKMwC6pcs/5/Xb5PBqzqL6Lq88IoS8=
github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg= github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg=
github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I= 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 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=

View File

@@ -82,7 +82,6 @@ nav:
- configuration/dns/index.md - configuration/dns/index.md
- DNS Server: configuration/dns/server.md - DNS Server: configuration/dns/server.md
- DNS Rule: configuration/dns/rule.md - DNS Rule: configuration/dns/rule.md
- DNS Rule Action: configuration/dns/rule_action.md
- FakeIP: configuration/dns/fakeip.md - FakeIP: configuration/dns/fakeip.md
- NTP: - NTP:
- configuration/ntp/index.md - configuration/ntp/index.md
@@ -91,7 +90,6 @@ nav:
- GeoIP: configuration/route/geoip.md - GeoIP: configuration/route/geoip.md
- Geosite: configuration/route/geosite.md - Geosite: configuration/route/geosite.md
- Route Rule: configuration/route/rule.md - Route Rule: configuration/route/rule.md
- Rule Action: configuration/route/rule_action.md
- Protocol Sniff: configuration/route/sniff.md - Protocol Sniff: configuration/route/sniff.md
- Rule Set: - Rule Set:
- configuration/rule-set/index.md - configuration/rule-set/index.md
@@ -220,11 +218,9 @@ plugins:
Log: 日志 Log: 日志
DNS Server: DNS 服务器 DNS Server: DNS 服务器
DNS Rule: DNS 规则 DNS Rule: DNS 规则
DNS Rule Action: DNS 规则动作
Route: 路由 Route: 路由
Route Rule: 路由规则 Route Rule: 路由规则
Rule Action: 规则动作
Protocol Sniff: 协议探测 Protocol Sniff: 协议探测
Rule Set: 规则集 Rule Set: 规则集

View File

@@ -1,10 +1,6 @@
package option package option
import ( import "net/netip"
"net/netip"
"github.com/sagernet/sing/common/json/badoption"
)
type DNSOptions struct { type DNSOptions struct {
Servers []DNSServerOptions `json:"servers,omitempty"` Servers []DNSServerOptions `json:"servers,omitempty"`
@@ -20,10 +16,10 @@ type DNSServerOptions struct {
Address string `json:"address"` Address string `json:"address"`
AddressResolver string `json:"address_resolver,omitempty"` AddressResolver string `json:"address_resolver,omitempty"`
AddressStrategy DomainStrategy `json:"address_strategy,omitempty"` AddressStrategy DomainStrategy `json:"address_strategy,omitempty"`
AddressFallbackDelay badoption.Duration `json:"address_fallback_delay,omitempty"` AddressFallbackDelay Duration `json:"address_fallback_delay,omitempty"`
Strategy DomainStrategy `json:"strategy,omitempty"` Strategy DomainStrategy `json:"strategy,omitempty"`
Detour string `json:"detour,omitempty"` Detour string `json:"detour,omitempty"`
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"` ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
} }
type DNSClientOptions struct { type DNSClientOptions struct {
@@ -31,7 +27,7 @@ type DNSClientOptions struct {
DisableCache bool `json:"disable_cache,omitempty"` DisableCache bool `json:"disable_cache,omitempty"`
DisableExpire bool `json:"disable_expire,omitempty"` DisableExpire bool `json:"disable_expire,omitempty"`
IndependentCache bool `json:"independent_cache,omitempty"` IndependentCache bool `json:"independent_cache,omitempty"`
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"` ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
} }
type DNSFakeIPOptions struct { type DNSFakeIPOptions struct {

View File

@@ -1,7 +1,5 @@
package option package option
import "github.com/sagernet/sing/common/json/badoption"
type ExperimentalOptions struct { type ExperimentalOptions struct {
CacheFile *CacheFileOptions `json:"cache_file,omitempty"` CacheFile *CacheFileOptions `json:"cache_file,omitempty"`
ClashAPI *ClashAPIOptions `json:"clash_api,omitempty"` ClashAPI *ClashAPIOptions `json:"clash_api,omitempty"`
@@ -15,7 +13,7 @@ type CacheFileOptions struct {
CacheID string `json:"cache_id,omitempty"` CacheID string `json:"cache_id,omitempty"`
StoreFakeIP bool `json:"store_fakeip,omitempty"` StoreFakeIP bool `json:"store_fakeip,omitempty"`
StoreRDRC bool `json:"store_rdrc,omitempty"` StoreRDRC bool `json:"store_rdrc,omitempty"`
RDRCTimeout badoption.Duration `json:"rdrc_timeout,omitempty"` RDRCTimeout Duration `json:"rdrc_timeout,omitempty"`
} }
type ClashAPIOptions struct { type ClashAPIOptions struct {
@@ -26,7 +24,7 @@ type ClashAPIOptions struct {
Secret string `json:"secret,omitempty"` Secret string `json:"secret,omitempty"`
DefaultMode string `json:"default_mode,omitempty"` DefaultMode string `json:"default_mode,omitempty"`
ModeList []string `json:"-"` ModeList []string `json:"-"`
AccessControlAllowOrigin badoption.Listable[string] `json:"access_control_allow_origin,omitempty"` AccessControlAllowOrigin Listable[string] `json:"access_control_allow_origin,omitempty"`
AccessControlAllowPrivateNetwork bool `json:"access_control_allow_private_network,omitempty"` AccessControlAllowPrivateNetwork bool `json:"access_control_allow_private_network,omitempty"`
// Deprecated: migrated to global cache file // Deprecated: migrated to global cache file

View File

@@ -1,7 +1,5 @@
package option package option
import "github.com/sagernet/sing/common/json/badoption"
type SelectorOutboundOptions struct { type SelectorOutboundOptions struct {
Outbounds []string `json:"outbounds"` Outbounds []string `json:"outbounds"`
Default string `json:"default,omitempty"` Default string `json:"default,omitempty"`
@@ -11,8 +9,8 @@ type SelectorOutboundOptions struct {
type URLTestOutboundOptions struct { type URLTestOutboundOptions struct {
Outbounds []string `json:"outbounds"` Outbounds []string `json:"outbounds"`
URL string `json:"url,omitempty"` URL string `json:"url,omitempty"`
Interval badoption.Duration `json:"interval,omitempty"` Interval Duration `json:"interval,omitempty"`
Tolerance uint16 `json:"tolerance,omitempty"` Tolerance uint16 `json:"tolerance,omitempty"`
IdleTimeout badoption.Duration `json:"idle_timeout,omitempty"` IdleTimeout Duration `json:"idle_timeout,omitempty"`
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"` InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
} }

View File

@@ -7,7 +7,6 @@ import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json" "github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badjson" "github.com/sagernet/sing/common/json/badjson"
"github.com/sagernet/sing/common/json/badoption"
"github.com/sagernet/sing/service" "github.com/sagernet/sing/service"
) )
@@ -34,7 +33,7 @@ func (h *Inbound) UnmarshalJSONContext(ctx context.Context, content []byte) erro
} }
registry := service.FromContext[InboundOptionsRegistry](ctx) registry := service.FromContext[InboundOptionsRegistry](ctx)
if registry == nil { if registry == nil {
return E.New("missing Inbound fields registry in context") return E.New("missing inbound options registry in context")
} }
options, loaded := registry.CreateOptions(h.Type) options, loaded := registry.CreateOptions(h.Type)
if !loaded { if !loaded {
@@ -52,17 +51,17 @@ func (h *Inbound) UnmarshalJSONContext(ctx context.Context, content []byte) erro
type InboundOptions struct { type InboundOptions struct {
SniffEnabled bool `json:"sniff,omitempty"` SniffEnabled bool `json:"sniff,omitempty"`
SniffOverrideDestination bool `json:"sniff_override_destination,omitempty"` SniffOverrideDestination bool `json:"sniff_override_destination,omitempty"`
SniffTimeout badoption.Duration `json:"sniff_timeout,omitempty"` SniffTimeout Duration `json:"sniff_timeout,omitempty"`
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"` DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"` UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
Detour string `json:"detour,omitempty"` Detour string `json:"detour,omitempty"`
} }
type ListenOptions struct { type ListenOptions struct {
Listen *badoption.Addr `json:"listen,omitempty"` Listen *ListenAddress `json:"listen,omitempty"`
ListenPort uint16 `json:"listen_port,omitempty"` ListenPort uint16 `json:"listen_port,omitempty"`
TCPKeepAlive badoption.Duration `json:"tcp_keep_alive,omitempty"` TCPKeepAlive Duration `json:"tcp_keep_alive,omitempty"`
TCPKeepAliveInterval badoption.Duration `json:"tcp_keep_alive_interval,omitempty"` TCPKeepAliveInterval Duration `json:"tcp_keep_alive_interval,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"` TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
TCPMultiPath bool `json:"tcp_multi_path,omitempty"` TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
UDPFragment *bool `json:"udp_fragment,omitempty"` UDPFragment *bool `json:"udp_fragment,omitempty"`
@@ -76,7 +75,7 @@ type ListenOptions struct {
InboundOptions InboundOptions
} }
type UDPTimeoutCompat badoption.Duration type UDPTimeoutCompat Duration
func (c UDPTimeoutCompat) MarshalJSON() ([]byte, error) { func (c UDPTimeoutCompat) MarshalJSON() ([]byte, error) {
return json.Marshal((time.Duration)(c).String()) return json.Marshal((time.Duration)(c).String())
@@ -89,7 +88,7 @@ func (c *UDPTimeoutCompat) UnmarshalJSON(data []byte) error {
*c = UDPTimeoutCompat(time.Second * time.Duration(valueNumber)) *c = UDPTimeoutCompat(time.Second * time.Duration(valueNumber))
return nil return nil
} }
return json.Unmarshal(data, (*badoption.Duration)(c)) return json.Unmarshal(data, (*Duration)(c))
} }
type ListenOptionsWrapper interface { type ListenOptionsWrapper interface {

View File

@@ -1,10 +1,8 @@
package option package option
import "github.com/sagernet/sing/common/json/badoption"
type NTPOptions struct { type NTPOptions struct {
Enabled bool `json:"enabled,omitempty"` Enabled bool `json:"enabled,omitempty"`
Interval badoption.Duration `json:"interval,omitempty"` Interval Duration `json:"interval,omitempty"`
WriteToSystem bool `json:"write_to_system,omitempty"` WriteToSystem bool `json:"write_to_system,omitempty"`
ServerOptions ServerOptions
DialerOptions DialerOptions

View File

@@ -19,9 +19,9 @@ type _Options struct {
Experimental *ExperimentalOptions `json:"experimental,omitempty"` Experimental *ExperimentalOptions `json:"experimental,omitempty"`
// Deprecated: use Inbounds instead // Deprecated: use Inbounds instead
LegacyInbounds []LegacyInbound `json:"-"` LegacyInbounds []LegacyInbound `json:"inbound,omitempty"`
// Deprecated: use Outbounds instead // Deprecated: use Outbounds instead
LegacyOutbounds []LegacyOutbound `json:"-"` LegacyOutbounds []LegacyOutbound `json:"_"`
} }
type Options _Options type Options _Options

View File

@@ -3,12 +3,9 @@ package option
import ( import (
"context" "context"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental/deprecated"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json" "github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badjson" "github.com/sagernet/sing/common/json/badjson"
"github.com/sagernet/sing/common/json/badoption"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/service" "github.com/sagernet/sing/service"
) )
@@ -38,10 +35,6 @@ func (h *Outbound) UnmarshalJSONContext(ctx context.Context, content []byte) err
if registry == nil { if registry == nil {
return E.New("missing outbound options registry in context") 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) options, loaded := registry.CreateOptions(h.Type)
if !loaded { if !loaded {
return E.New("unknown outbound type: ", h.Type) return E.New("unknown outbound type: ", h.Type)
@@ -50,11 +43,6 @@ func (h *Outbound) UnmarshalJSONContext(ctx context.Context, content []byte) err
if err != nil { if err != nil {
return err return err
} }
if listenWrapper, isListen := options.(ListenOptionsWrapper); isListen {
if listenWrapper.TakeListenOptions().InboundOptions != (InboundOptions{}) {
deprecated.Report(ctx, deprecated.OptionInboundOptions)
}
}
h.Options = options h.Options = options
return nil return nil
} }
@@ -67,18 +55,18 @@ type DialerOptionsWrapper interface {
type DialerOptions struct { type DialerOptions struct {
Detour string `json:"detour,omitempty"` Detour string `json:"detour,omitempty"`
BindInterface string `json:"bind_interface,omitempty"` BindInterface string `json:"bind_interface,omitempty"`
Inet4BindAddress *badoption.Addr `json:"inet4_bind_address,omitempty"` Inet4BindAddress *ListenAddress `json:"inet4_bind_address,omitempty"`
Inet6BindAddress *badoption.Addr `json:"inet6_bind_address,omitempty"` Inet6BindAddress *ListenAddress `json:"inet6_bind_address,omitempty"`
ProtectPath string `json:"protect_path,omitempty"` ProtectPath string `json:"protect_path,omitempty"`
RoutingMark uint32 `json:"routing_mark,omitempty"` RoutingMark uint32 `json:"routing_mark,omitempty"`
ReuseAddr bool `json:"reuse_addr,omitempty"` ReuseAddr bool `json:"reuse_addr,omitempty"`
ConnectTimeout badoption.Duration `json:"connect_timeout,omitempty"` ConnectTimeout Duration `json:"connect_timeout,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"` TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
TCPMultiPath bool `json:"tcp_multi_path,omitempty"` TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
UDPFragment *bool `json:"udp_fragment,omitempty"` UDPFragment *bool `json:"udp_fragment,omitempty"`
UDPFragmentDefault bool `json:"-"` UDPFragmentDefault bool `json:"-"`
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"` DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
FallbackDelay badoption.Duration `json:"fallback_delay,omitempty"` FallbackDelay Duration `json:"fallback_delay,omitempty"`
IsWireGuardListener bool `json:"-"` IsWireGuardListener bool `json:"-"`
} }

View File

@@ -3,7 +3,6 @@ package option
import ( import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json" "github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badoption"
) )
type OnDemandOptions struct { type OnDemandOptions struct {
@@ -13,10 +12,10 @@ type OnDemandOptions struct {
type OnDemandRule struct { type OnDemandRule struct {
Action *OnDemandRuleAction `json:"action,omitempty"` Action *OnDemandRuleAction `json:"action,omitempty"`
DNSSearchDomainMatch badoption.Listable[string] `json:"dns_search_domain_match,omitempty"` DNSSearchDomainMatch Listable[string] `json:"dns_search_domain_match,omitempty"`
DNSServerAddressMatch badoption.Listable[string] `json:"dns_server_address_match,omitempty"` DNSServerAddressMatch Listable[string] `json:"dns_server_address_match,omitempty"`
InterfaceTypeMatch *OnDemandRuleInterfaceType `json:"interface_type_match,omitempty"` InterfaceTypeMatch *OnDemandRuleInterfaceType `json:"interface_type_match,omitempty"`
SSIDMatch badoption.Listable[string] `json:"ssid_match,omitempty"` SSIDMatch Listable[string] `json:"ssid_match,omitempty"`
ProbeURL string `json:"probe_url,omitempty"` ProbeURL string `json:"probe_url,omitempty"`
} }

View File

@@ -8,7 +8,6 @@ import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json" "github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badjson" "github.com/sagernet/sing/common/json/badjson"
"github.com/sagernet/sing/common/json/badoption"
) )
type _Rule struct { type _Rule struct {
@@ -67,37 +66,37 @@ func (r Rule) IsValid() bool {
} }
type RawDefaultRule struct { type RawDefaultRule struct {
Inbound badoption.Listable[string] `json:"inbound,omitempty"` Inbound Listable[string] `json:"inbound,omitempty"`
IPVersion int `json:"ip_version,omitempty"` IPVersion int `json:"ip_version,omitempty"`
Network badoption.Listable[string] `json:"network,omitempty"` Network Listable[string] `json:"network,omitempty"`
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"` AuthUser Listable[string] `json:"auth_user,omitempty"`
Protocol badoption.Listable[string] `json:"protocol,omitempty"` Protocol Listable[string] `json:"protocol,omitempty"`
Client badoption.Listable[string] `json:"client,omitempty"` Client Listable[string] `json:"client,omitempty"`
Domain badoption.Listable[string] `json:"domain,omitempty"` Domain Listable[string] `json:"domain,omitempty"`
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"` DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"` DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"` DomainRegex Listable[string] `json:"domain_regex,omitempty"`
Geosite badoption.Listable[string] `json:"geosite,omitempty"` Geosite Listable[string] `json:"geosite,omitempty"`
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"` SourceGeoIP Listable[string] `json:"source_geoip,omitempty"`
GeoIP badoption.Listable[string] `json:"geoip,omitempty"` GeoIP Listable[string] `json:"geoip,omitempty"`
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"` SourceIPCIDR Listable[string] `json:"source_ip_cidr,omitempty"`
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"` SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"` IPCIDR Listable[string] `json:"ip_cidr,omitempty"`
IPIsPrivate bool `json:"ip_is_private,omitempty"` IPIsPrivate bool `json:"ip_is_private,omitempty"`
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"` SourcePort Listable[uint16] `json:"source_port,omitempty"`
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"` SourcePortRange Listable[string] `json:"source_port_range,omitempty"`
Port badoption.Listable[uint16] `json:"port,omitempty"` Port Listable[uint16] `json:"port,omitempty"`
PortRange badoption.Listable[string] `json:"port_range,omitempty"` PortRange Listable[string] `json:"port_range,omitempty"`
ProcessName badoption.Listable[string] `json:"process_name,omitempty"` ProcessName Listable[string] `json:"process_name,omitempty"`
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"` ProcessPath Listable[string] `json:"process_path,omitempty"`
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"` ProcessPathRegex Listable[string] `json:"process_path_regex,omitempty"`
PackageName badoption.Listable[string] `json:"package_name,omitempty"` PackageName Listable[string] `json:"package_name,omitempty"`
User badoption.Listable[string] `json:"user,omitempty"` User Listable[string] `json:"user,omitempty"`
UserID badoption.Listable[int32] `json:"user_id,omitempty"` UserID Listable[int32] `json:"user_id,omitempty"`
ClashMode string `json:"clash_mode,omitempty"` ClashMode string `json:"clash_mode,omitempty"`
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"` WIFISSID Listable[string] `json:"wifi_ssid,omitempty"`
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"` WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"`
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"` RuleSet Listable[string] `json:"rule_set,omitempty"`
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"` RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
Invert bool `json:"invert,omitempty"` Invert bool `json:"invert,omitempty"`
@@ -110,7 +109,7 @@ type DefaultRule struct {
RuleAction RuleAction
} }
func (r DefaultRule) MarshalJSON() ([]byte, error) { func (r *DefaultRule) MarshalJSON() ([]byte, error) {
return badjson.MarshallObjects(r.RawDefaultRule, r.RuleAction) return badjson.MarshallObjects(r.RawDefaultRule, r.RuleAction)
} }
@@ -129,27 +128,27 @@ func (r *DefaultRule) IsValid() bool {
return !reflect.DeepEqual(r, defaultValue) return !reflect.DeepEqual(r, defaultValue)
} }
type RawLogicalRule struct { type _LogicalRule struct {
Mode string `json:"mode"` Mode string `json:"mode"`
Rules []Rule `json:"rules,omitempty"` Rules []Rule `json:"rules,omitempty"`
Invert bool `json:"invert,omitempty"` Invert bool `json:"invert,omitempty"`
} }
type LogicalRule struct { type LogicalRule struct {
RawLogicalRule _LogicalRule
RuleAction RuleAction
} }
func (r LogicalRule) MarshalJSON() ([]byte, error) { func (r *LogicalRule) MarshalJSON() ([]byte, error) {
return badjson.MarshallObjects(r.RawLogicalRule, r.RuleAction) return badjson.MarshallObjects(r._LogicalRule, r.RuleAction)
} }
func (r *LogicalRule) UnmarshalJSON(data []byte) error { func (r *LogicalRule) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, &r.RawLogicalRule) err := json.Unmarshal(data, &r._LogicalRule)
if err != nil { if err != nil {
return err return err
} }
return badjson.UnmarshallExcluded(data, &r.RawLogicalRule, &r.RuleAction) return badjson.UnmarshallExcluded(data, &r._LogicalRule, &r.RuleAction)
} }
func (r *LogicalRule) IsValid() bool { func (r *LogicalRule) IsValid() bool {

View File

@@ -1,25 +1,15 @@
package option package option
import ( import (
"context"
"fmt"
"net/netip"
"time"
C "github.com/sagernet/sing-box/constant" 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" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json" "github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badjson" "github.com/sagernet/sing/common/json/badjson"
"github.com/sagernet/sing/common/json/badoption"
) )
type _RuleAction struct { type _RuleAction struct {
Action string `json:"action,omitempty"` Action string `json:"action,omitempty"`
RouteOptions RouteActionOptions `json:"-"` RouteOptions RouteActionOptions `json:"-"`
RouteOptionsOptions RouteOptionsActionOptions `json:"-"`
DirectOptions DirectActionOptions `json:"-"`
RejectOptions RejectActionOptions `json:"-"` RejectOptions RejectActionOptions `json:"-"`
SniffOptions RouteActionSniff `json:"-"` SniffOptions RouteActionSniff `json:"-"`
ResolveOptions RouteActionResolve `json:"-"` ResolveOptions RouteActionResolve `json:"-"`
@@ -28,18 +18,13 @@ type _RuleAction struct {
type RuleAction _RuleAction type RuleAction _RuleAction
func (r RuleAction) MarshalJSON() ([]byte, error) { func (r RuleAction) MarshalJSON() ([]byte, error) {
if r.Action == "" {
return json.Marshal(struct{}{})
}
var v any var v any
switch r.Action { switch r.Action {
case C.RuleActionTypeRoute: case C.RuleActionTypeRoute:
r.Action = "" r.Action = ""
v = r.RouteOptions v = r.RouteOptions
case C.RuleActionTypeRouteOptions: case C.RuleActionTypeReturn:
v = r.RouteOptionsOptions v = nil
case C.RuleActionTypeDirect:
v = r.DirectOptions
case C.RuleActionTypeReject: case C.RuleActionTypeReject:
v = r.RejectOptions v = r.RejectOptions
case C.RuleActionTypeHijackDNS: case C.RuleActionTypeHijackDNS:
@@ -67,10 +52,8 @@ func (r *RuleAction) UnmarshalJSON(data []byte) error {
case "", C.RuleActionTypeRoute: case "", C.RuleActionTypeRoute:
r.Action = C.RuleActionTypeRoute r.Action = C.RuleActionTypeRoute
v = &r.RouteOptions v = &r.RouteOptions
case C.RuleActionTypeRouteOptions: case C.RuleActionTypeReturn:
v = &r.RouteOptionsOptions v = nil
case C.RuleActionTypeDirect:
v = &r.DirectOptions
case C.RuleActionTypeReject: case C.RuleActionTypeReject:
v = &r.RejectOptions v = &r.RejectOptions
case C.RuleActionTypeHijackDNS: case C.RuleActionTypeHijackDNS:
@@ -92,32 +75,33 @@ func (r *RuleAction) UnmarshalJSON(data []byte) error {
type _DNSRuleAction struct { type _DNSRuleAction struct {
Action string `json:"action,omitempty"` Action string `json:"action,omitempty"`
RouteOptions DNSRouteActionOptions `json:"-"` RouteOptions DNSRouteActionOptions `json:"-"`
RouteOptionsOptions DNSRouteOptionsActionOptions `json:"-"`
RejectOptions RejectActionOptions `json:"-"` RejectOptions RejectActionOptions `json:"-"`
SniffOptions RouteActionSniff `json:"-"`
ResolveOptions RouteActionResolve `json:"-"`
} }
type DNSRuleAction _DNSRuleAction type DNSRuleAction _DNSRuleAction
func (r DNSRuleAction) MarshalJSON() ([]byte, error) { func (r DNSRuleAction) MarshalJSON() ([]byte, error) {
if r.Action == "" {
return json.Marshal(struct{}{})
}
var v any var v any
switch r.Action { switch r.Action {
case C.RuleActionTypeRoute: case C.RuleActionTypeRoute:
r.Action = "" r.Action = ""
v = r.RouteOptions v = r.RouteOptions
case C.RuleActionTypeRouteOptions: case C.RuleActionTypeReturn:
v = r.RouteOptionsOptions v = nil
case C.RuleActionTypeReject: case C.RuleActionTypeReject:
v = r.RejectOptions v = r.RejectOptions
default: default:
return nil, E.New("unknown DNS rule action: " + r.Action) return nil, E.New("unknown DNS rule action: " + r.Action)
} }
if v == nil {
return badjson.MarshallObjects((_DNSRuleAction)(r))
}
return badjson.MarshallObjects((_DNSRuleAction)(r), v) return badjson.MarshallObjects((_DNSRuleAction)(r), v)
} }
func (r *DNSRuleAction) UnmarshalJSONContext(ctx context.Context, data []byte) error { func (r *DNSRuleAction) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, (*_DNSRuleAction)(r)) err := json.Unmarshal(data, (*_DNSRuleAction)(r))
if err != nil { if err != nil {
return err return err
@@ -127,146 +111,34 @@ func (r *DNSRuleAction) UnmarshalJSONContext(ctx context.Context, data []byte) e
case "", C.RuleActionTypeRoute: case "", C.RuleActionTypeRoute:
r.Action = C.RuleActionTypeRoute r.Action = C.RuleActionTypeRoute
v = &r.RouteOptions v = &r.RouteOptions
case C.RuleActionTypeRouteOptions: case C.RuleActionTypeReturn:
v = &r.RouteOptionsOptions v = nil
case C.RuleActionTypeReject: case C.RuleActionTypeReject:
v = &r.RejectOptions v = &r.RejectOptions
default: default:
return E.New("unknown DNS rule action: " + r.Action) return E.New("unknown DNS rule action: " + r.Action)
} }
return badjson.UnmarshallExcludedContext(ctx, data, (*_DNSRuleAction)(r), v) if v == nil {
// check unknown fields
return json.UnmarshalDisallowUnknownFields(data, &_DNSRuleAction{})
}
return badjson.UnmarshallExcluded(data, (*_DNSRuleAction)(r), v)
} }
type _RouteActionOptions struct { type RouteActionOptions struct {
Outbound string `json:"outbound,omitempty"` Outbound string `json:"outbound"`
}
type RouteActionOptions _RouteActionOptions
func (r *RouteActionOptions) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, (*_RouteActionOptions)(r))
if err != nil {
return err
}
return nil
}
type _RouteOptionsActionOptions struct {
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"` UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
UDPConnect bool `json:"udp_connect,omitempty"`
} }
type RouteOptionsActionOptions _RouteOptionsActionOptions type DNSRouteActionOptions struct {
Server string `json:"server"`
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"` DisableCache bool `json:"disable_cache,omitempty"`
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"` RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"` ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
}
type DNSRouteOptionsActionOptions _DNSRouteOptionsActionOptions
func (r *DNSRouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, (*_DNSRouteOptionsActionOptions)(r))
if err != nil {
return err
}
if *r == (DNSRouteOptionsActionOptions{}) {
return E.New("empty DNS route option action")
}
return nil
}
type _DirectActionOptions DialerOptions
type DirectActionOptions _DirectActionOptions
func (d DirectActionOptions) Descriptions() []string {
var descriptions []string
if d.BindInterface != "" {
descriptions = append(descriptions, "bind_interface="+d.BindInterface)
}
if d.Inet4BindAddress != nil {
descriptions = append(descriptions, "inet4_bind_address="+d.Inet4BindAddress.Build(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 { type _RejectActionOptions struct {
Method string `json:"method,omitempty"` Method string `json:"method,omitempty"`
NoDrop bool `json:"no_drop,omitempty"`
} }
type RejectActionOptions _RejectActionOptions type RejectActionOptions _RejectActionOptions
@@ -279,19 +151,20 @@ func (r *RejectActionOptions) UnmarshalJSON(bytes []byte) error {
switch r.Method { switch r.Method {
case "", C.RuleActionRejectMethodDefault: case "", C.RuleActionRejectMethodDefault:
r.Method = C.RuleActionRejectMethodDefault r.Method = C.RuleActionRejectMethodDefault
case C.RuleActionRejectMethodDrop: case C.RuleActionRejectMethodReset,
C.RuleActionRejectMethodNetworkUnreachable,
C.RuleActionRejectMethodHostUnreachable,
C.RuleActionRejectMethodPortUnreachable,
C.RuleActionRejectMethodDrop:
default: default:
return E.New("unknown reject method: " + r.Method) 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 return nil
} }
type RouteActionSniff struct { type RouteActionSniff struct {
Sniffer badoption.Listable[string] `json:"sniffer,omitempty"` Sniffer Listable[string] `json:"sniffer,omitempty"`
Timeout badoption.Duration `json:"timeout,omitempty"` Timeout Duration `json:"timeout,omitempty"`
} }
type RouteActionResolve struct { type RouteActionResolve struct {

View File

@@ -1,7 +1,6 @@
package option package option
import ( import (
"context"
"reflect" "reflect"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
@@ -9,7 +8,6 @@ import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json" "github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badjson" "github.com/sagernet/sing/common/json/badjson"
"github.com/sagernet/sing/common/json/badoption"
) )
type _DNSRule struct { type _DNSRule struct {
@@ -34,7 +32,7 @@ func (r DNSRule) MarshalJSON() ([]byte, error) {
return badjson.MarshallObjects((_DNSRule)(r), v) return badjson.MarshallObjects((_DNSRule)(r), v)
} }
func (r *DNSRule) UnmarshalJSONContext(ctx context.Context, bytes []byte) error { func (r *DNSRule) UnmarshalJSON(bytes []byte) error {
err := json.Unmarshal(bytes, (*_DNSRule)(r)) err := json.Unmarshal(bytes, (*_DNSRule)(r))
if err != nil { if err != nil {
return err return err
@@ -49,7 +47,7 @@ func (r *DNSRule) UnmarshalJSONContext(ctx context.Context, bytes []byte) error
default: default:
return E.New("unknown rule type: " + r.Type) return E.New("unknown rule type: " + r.Type)
} }
err = badjson.UnmarshallExcludedContext(ctx, bytes, (*_DNSRule)(r), v) err = badjson.UnmarshallExcluded(bytes, (*_DNSRule)(r), v)
if err != nil { if err != nil {
return err return err
} }
@@ -68,38 +66,38 @@ func (r DNSRule) IsValid() bool {
} }
type RawDefaultDNSRule struct { type RawDefaultDNSRule struct {
Inbound badoption.Listable[string] `json:"inbound,omitempty"` Inbound Listable[string] `json:"inbound,omitempty"`
IPVersion int `json:"ip_version,omitempty"` IPVersion int `json:"ip_version,omitempty"`
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"` QueryType Listable[DNSQueryType] `json:"query_type,omitempty"`
Network badoption.Listable[string] `json:"network,omitempty"` Network Listable[string] `json:"network,omitempty"`
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"` AuthUser Listable[string] `json:"auth_user,omitempty"`
Protocol badoption.Listable[string] `json:"protocol,omitempty"` Protocol Listable[string] `json:"protocol,omitempty"`
Domain badoption.Listable[string] `json:"domain,omitempty"` Domain Listable[string] `json:"domain,omitempty"`
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"` DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"` DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"` DomainRegex Listable[string] `json:"domain_regex,omitempty"`
Geosite badoption.Listable[string] `json:"geosite,omitempty"` Geosite Listable[string] `json:"geosite,omitempty"`
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"` SourceGeoIP Listable[string] `json:"source_geoip,omitempty"`
GeoIP badoption.Listable[string] `json:"geoip,omitempty"` GeoIP Listable[string] `json:"geoip,omitempty"`
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"` IPCIDR Listable[string] `json:"ip_cidr,omitempty"`
IPIsPrivate bool `json:"ip_is_private,omitempty"` IPIsPrivate bool `json:"ip_is_private,omitempty"`
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"` SourceIPCIDR Listable[string] `json:"source_ip_cidr,omitempty"`
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"` SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"` SourcePort Listable[uint16] `json:"source_port,omitempty"`
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"` SourcePortRange Listable[string] `json:"source_port_range,omitempty"`
Port badoption.Listable[uint16] `json:"port,omitempty"` Port Listable[uint16] `json:"port,omitempty"`
PortRange badoption.Listable[string] `json:"port_range,omitempty"` PortRange Listable[string] `json:"port_range,omitempty"`
ProcessName badoption.Listable[string] `json:"process_name,omitempty"` ProcessName Listable[string] `json:"process_name,omitempty"`
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"` ProcessPath Listable[string] `json:"process_path,omitempty"`
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"` ProcessPathRegex Listable[string] `json:"process_path_regex,omitempty"`
PackageName badoption.Listable[string] `json:"package_name,omitempty"` PackageName Listable[string] `json:"package_name,omitempty"`
User badoption.Listable[string] `json:"user,omitempty"` User Listable[string] `json:"user,omitempty"`
UserID badoption.Listable[int32] `json:"user_id,omitempty"` UserID Listable[int32] `json:"user_id,omitempty"`
Outbound badoption.Listable[string] `json:"outbound,omitempty"` Outbound Listable[string] `json:"outbound,omitempty"`
ClashMode string `json:"clash_mode,omitempty"` ClashMode string `json:"clash_mode,omitempty"`
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"` WIFISSID Listable[string] `json:"wifi_ssid,omitempty"`
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"` WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"`
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"` RuleSet Listable[string] `json:"rule_set,omitempty"`
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"` RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"` RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"`
Invert bool `json:"invert,omitempty"` Invert bool `json:"invert,omitempty"`
@@ -113,46 +111,46 @@ type DefaultDNSRule struct {
DNSRuleAction DNSRuleAction
} }
func (r DefaultDNSRule) MarshalJSON() ([]byte, error) { func (r *DefaultDNSRule) MarshalJSON() ([]byte, error) {
return badjson.MarshallObjects(r.RawDefaultDNSRule, r.DNSRuleAction) return badjson.MarshallObjects(r.RawDefaultDNSRule, r.DNSRuleAction)
} }
func (r *DefaultDNSRule) UnmarshalJSONContext(ctx context.Context, data []byte) error { func (r *DefaultDNSRule) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, &r.RawDefaultDNSRule) err := json.Unmarshal(data, &r.RawDefaultDNSRule)
if err != nil { if err != nil {
return err return err
} }
return badjson.UnmarshallExcludedContext(ctx, data, &r.RawDefaultDNSRule, &r.DNSRuleAction) return badjson.UnmarshallExcluded(data, &r.RawDefaultDNSRule, &r.DNSRuleAction)
} }
func (r DefaultDNSRule) IsValid() bool { func (r *DefaultDNSRule) IsValid() bool {
var defaultValue DefaultDNSRule var defaultValue DefaultDNSRule
defaultValue.Invert = r.Invert defaultValue.Invert = r.Invert
defaultValue.DNSRuleAction = r.DNSRuleAction defaultValue.DNSRuleAction = r.DNSRuleAction
return !reflect.DeepEqual(r, defaultValue) return !reflect.DeepEqual(r, defaultValue)
} }
type RawLogicalDNSRule struct { type _LogicalDNSRule struct {
Mode string `json:"mode"` Mode string `json:"mode"`
Rules []DNSRule `json:"rules,omitempty"` Rules []DNSRule `json:"rules,omitempty"`
Invert bool `json:"invert,omitempty"` Invert bool `json:"invert,omitempty"`
} }
type LogicalDNSRule struct { type LogicalDNSRule struct {
RawLogicalDNSRule _LogicalDNSRule
DNSRuleAction DNSRuleAction
} }
func (r *LogicalDNSRule) MarshalJSON() ([]byte, error) { func (r *LogicalDNSRule) MarshalJSON() ([]byte, error) {
return badjson.MarshallObjects(r.RawLogicalDNSRule, r.DNSRuleAction) return badjson.MarshallObjects(r._LogicalDNSRule, r.DNSRuleAction)
} }
func (r *LogicalDNSRule) UnmarshalJSONContext(ctx context.Context, data []byte) error { func (r *LogicalDNSRule) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, &r.RawLogicalDNSRule) err := json.Unmarshal(data, &r._LogicalDNSRule)
if err != nil { if err != nil {
return err return err
} }
return badjson.UnmarshallExcludedContext(ctx, data, &r.RawLogicalDNSRule, &r.DNSRuleAction) return badjson.UnmarshallExcluded(data, &r._LogicalDNSRule, &r.DNSRuleAction)
} }
func (r *LogicalDNSRule) IsValid() bool { func (r *LogicalDNSRule) IsValid() bool {

View File

@@ -10,7 +10,6 @@ import (
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/json" "github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badjson" "github.com/sagernet/sing/common/json/badjson"
"github.com/sagernet/sing/common/json/badoption"
"go4.org/netipx" "go4.org/netipx"
) )
@@ -87,7 +86,7 @@ type LocalRuleSet struct {
type RemoteRuleSet struct { type RemoteRuleSet struct {
URL string `json:"url"` URL string `json:"url"`
DownloadDetour string `json:"download_detour,omitempty"` DownloadDetour string `json:"download_detour,omitempty"`
UpdateInterval badoption.Duration `json:"update_interval,omitempty"` UpdateInterval Duration `json:"update_interval,omitempty"`
} }
type _HeadlessRule struct { type _HeadlessRule struct {
@@ -146,31 +145,31 @@ func (r HeadlessRule) IsValid() bool {
} }
type DefaultHeadlessRule struct { type DefaultHeadlessRule struct {
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"` QueryType Listable[DNSQueryType] `json:"query_type,omitempty"`
Network badoption.Listable[string] `json:"network,omitempty"` Network Listable[string] `json:"network,omitempty"`
Domain badoption.Listable[string] `json:"domain,omitempty"` Domain Listable[string] `json:"domain,omitempty"`
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"` DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"` DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"` DomainRegex Listable[string] `json:"domain_regex,omitempty"`
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"` SourceIPCIDR Listable[string] `json:"source_ip_cidr,omitempty"`
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"` IPCIDR Listable[string] `json:"ip_cidr,omitempty"`
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"` SourcePort Listable[uint16] `json:"source_port,omitempty"`
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"` SourcePortRange Listable[string] `json:"source_port_range,omitempty"`
Port badoption.Listable[uint16] `json:"port,omitempty"` Port Listable[uint16] `json:"port,omitempty"`
PortRange badoption.Listable[string] `json:"port_range,omitempty"` PortRange Listable[string] `json:"port_range,omitempty"`
ProcessName badoption.Listable[string] `json:"process_name,omitempty"` ProcessName Listable[string] `json:"process_name,omitempty"`
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"` ProcessPath Listable[string] `json:"process_path,omitempty"`
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"` ProcessPathRegex Listable[string] `json:"process_path_regex,omitempty"`
PackageName badoption.Listable[string] `json:"package_name,omitempty"` PackageName Listable[string] `json:"package_name,omitempty"`
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"` WIFISSID Listable[string] `json:"wifi_ssid,omitempty"`
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"` WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"`
Invert bool `json:"invert,omitempty"` Invert bool `json:"invert,omitempty"`
DomainMatcher *domain.Matcher `json:"-"` DomainMatcher *domain.Matcher `json:"-"`
SourceIPSet *netipx.IPSet `json:"-"` SourceIPSet *netipx.IPSet `json:"-"`
IPSet *netipx.IPSet `json:"-"` IPSet *netipx.IPSet `json:"-"`
AdGuardDomain badoption.Listable[string] `json:"-"` AdGuardDomain Listable[string] `json:"-"`
AdGuardDomainMatcher *domain.AdGuardMatcher `json:"-"` AdGuardDomainMatcher *domain.AdGuardMatcher `json:"-"`
} }

View File

@@ -1,9 +1,6 @@
package option package option
import ( import "github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/json/badoption"
)
type SocksInboundOptions struct { type SocksInboundOptions struct {
ListenOptions ListenOptions
@@ -34,5 +31,5 @@ type HTTPOutboundOptions struct {
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`
OutboundTLSOptionsContainer OutboundTLSOptionsContainer
Path string `json:"path,omitempty"` Path string `json:"path,omitempty"`
Headers badoption.HTTPHeader `json:"headers,omitempty"` Headers HTTPHeader `json:"headers,omitempty"`
} }

View File

@@ -1,16 +1,14 @@
package option package option
import "github.com/sagernet/sing/common/json/badoption"
type SSHOutboundOptions struct { type SSHOutboundOptions struct {
DialerOptions DialerOptions
ServerOptions ServerOptions
User string `json:"user,omitempty"` User string `json:"user,omitempty"`
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`
PrivateKey badoption.Listable[string] `json:"private_key,omitempty"` PrivateKey Listable[string] `json:"private_key,omitempty"`
PrivateKeyPath string `json:"private_key_path,omitempty"` PrivateKeyPath string `json:"private_key_path,omitempty"`
PrivateKeyPassphrase string `json:"private_key_passphrase,omitempty"` PrivateKeyPassphrase string `json:"private_key_passphrase,omitempty"`
HostKey badoption.Listable[string] `json:"host_key,omitempty"` HostKey Listable[string] `json:"host_key,omitempty"`
HostKeyAlgorithms badoption.Listable[string] `json:"host_key_algorithms,omitempty"` HostKeyAlgorithms Listable[string] `json:"host_key_algorithms,omitempty"`
ClientVersion string `json:"client_version,omitempty"` ClientVersion string `json:"client_version,omitempty"`
} }

226
option/time_unit.go Normal file
View File

@@ -0,0 +1,226 @@
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)
}

View File

@@ -1,18 +1,16 @@
package option package option
import "github.com/sagernet/sing/common/json/badoption"
type InboundTLSOptions struct { type InboundTLSOptions struct {
Enabled bool `json:"enabled,omitempty"` Enabled bool `json:"enabled,omitempty"`
ServerName string `json:"server_name,omitempty"` ServerName string `json:"server_name,omitempty"`
Insecure bool `json:"insecure,omitempty"` Insecure bool `json:"insecure,omitempty"`
ALPN badoption.Listable[string] `json:"alpn,omitempty"` ALPN Listable[string] `json:"alpn,omitempty"`
MinVersion string `json:"min_version,omitempty"` MinVersion string `json:"min_version,omitempty"`
MaxVersion string `json:"max_version,omitempty"` MaxVersion string `json:"max_version,omitempty"`
CipherSuites badoption.Listable[string] `json:"cipher_suites,omitempty"` CipherSuites Listable[string] `json:"cipher_suites,omitempty"`
Certificate badoption.Listable[string] `json:"certificate,omitempty"` Certificate Listable[string] `json:"certificate,omitempty"`
CertificatePath string `json:"certificate_path,omitempty"` CertificatePath string `json:"certificate_path,omitempty"`
Key badoption.Listable[string] `json:"key,omitempty"` Key Listable[string] `json:"key,omitempty"`
KeyPath string `json:"key_path,omitempty"` KeyPath string `json:"key_path,omitempty"`
ACME *InboundACMEOptions `json:"acme,omitempty"` ACME *InboundACMEOptions `json:"acme,omitempty"`
ECH *InboundECHOptions `json:"ech,omitempty"` ECH *InboundECHOptions `json:"ech,omitempty"`
@@ -41,11 +39,11 @@ type OutboundTLSOptions struct {
DisableSNI bool `json:"disable_sni,omitempty"` DisableSNI bool `json:"disable_sni,omitempty"`
ServerName string `json:"server_name,omitempty"` ServerName string `json:"server_name,omitempty"`
Insecure bool `json:"insecure,omitempty"` Insecure bool `json:"insecure,omitempty"`
ALPN badoption.Listable[string] `json:"alpn,omitempty"` ALPN Listable[string] `json:"alpn,omitempty"`
MinVersion string `json:"min_version,omitempty"` MinVersion string `json:"min_version,omitempty"`
MaxVersion string `json:"max_version,omitempty"` MaxVersion string `json:"max_version,omitempty"`
CipherSuites badoption.Listable[string] `json:"cipher_suites,omitempty"` CipherSuites Listable[string] `json:"cipher_suites,omitempty"`
Certificate badoption.Listable[string] `json:"certificate,omitempty"` Certificate Listable[string] `json:"certificate,omitempty"`
CertificatePath string `json:"certificate_path,omitempty"` CertificatePath string `json:"certificate_path,omitempty"`
ECH *OutboundECHOptions `json:"ech,omitempty"` ECH *OutboundECHOptions `json:"ech,omitempty"`
UTLS *OutboundUTLSOptions `json:"utls,omitempty"` UTLS *OutboundUTLSOptions `json:"utls,omitempty"`
@@ -73,8 +71,8 @@ type InboundRealityOptions struct {
Enabled bool `json:"enabled,omitempty"` Enabled bool `json:"enabled,omitempty"`
Handshake InboundRealityHandshakeOptions `json:"handshake,omitempty"` Handshake InboundRealityHandshakeOptions `json:"handshake,omitempty"`
PrivateKey string `json:"private_key,omitempty"` PrivateKey string `json:"private_key,omitempty"`
ShortID badoption.Listable[string] `json:"short_id,omitempty"` ShortID Listable[string] `json:"short_id,omitempty"`
MaxTimeDifference badoption.Duration `json:"max_time_difference,omitempty"` MaxTimeDifference Duration `json:"max_time_difference,omitempty"`
} }
type InboundRealityHandshakeOptions struct { type InboundRealityHandshakeOptions struct {
@@ -86,7 +84,7 @@ type InboundECHOptions struct {
Enabled bool `json:"enabled,omitempty"` Enabled bool `json:"enabled,omitempty"`
PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"` PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"`
DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"` DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"`
Key badoption.Listable[string] `json:"key,omitempty"` Key Listable[string] `json:"key,omitempty"`
KeyPath string `json:"key_path,omitempty"` KeyPath string `json:"key_path,omitempty"`
} }
@@ -94,7 +92,7 @@ type OutboundECHOptions struct {
Enabled bool `json:"enabled,omitempty"` Enabled bool `json:"enabled,omitempty"`
PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"` PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"`
DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"` DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"`
Config badoption.Listable[string] `json:"config,omitempty"` Config Listable[string] `json:"config,omitempty"`
ConfigPath string `json:"config_path,omitempty"` ConfigPath string `json:"config_path,omitempty"`
} }

View File

@@ -5,11 +5,10 @@ import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json" "github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badjson" "github.com/sagernet/sing/common/json/badjson"
"github.com/sagernet/sing/common/json/badoption"
) )
type InboundACMEOptions struct { type InboundACMEOptions struct {
Domain badoption.Listable[string] `json:"domain,omitempty"` Domain Listable[string] `json:"domain,omitempty"`
DataDirectory string `json:"data_directory,omitempty"` DataDirectory string `json:"data_directory,omitempty"`
DefaultServerName string `json:"default_server_name,omitempty"` DefaultServerName string `json:"default_server_name,omitempty"`
Email string `json:"email,omitempty"` Email string `json:"email,omitempty"`

View File

@@ -1,14 +1,12 @@
package option package option
import "github.com/sagernet/sing/common/json/badoption"
type TUICInboundOptions struct { type TUICInboundOptions struct {
ListenOptions ListenOptions
Users []TUICUser `json:"users,omitempty"` Users []TUICUser `json:"users,omitempty"`
CongestionControl string `json:"congestion_control,omitempty"` CongestionControl string `json:"congestion_control,omitempty"`
AuthTimeout badoption.Duration `json:"auth_timeout,omitempty"` AuthTimeout Duration `json:"auth_timeout,omitempty"`
ZeroRTTHandshake bool `json:"zero_rtt_handshake,omitempty"` ZeroRTTHandshake bool `json:"zero_rtt_handshake,omitempty"`
Heartbeat badoption.Duration `json:"heartbeat,omitempty"` Heartbeat Duration `json:"heartbeat,omitempty"`
InboundTLSOptionsContainer InboundTLSOptionsContainer
} }
@@ -27,7 +25,7 @@ type TUICOutboundOptions struct {
UDPRelayMode string `json:"udp_relay_mode,omitempty"` UDPRelayMode string `json:"udp_relay_mode,omitempty"`
UDPOverStream bool `json:"udp_over_stream,omitempty"` UDPOverStream bool `json:"udp_over_stream,omitempty"`
ZeroRTTHandshake bool `json:"zero_rtt_handshake,omitempty"` ZeroRTTHandshake bool `json:"zero_rtt_handshake,omitempty"`
Heartbeat badoption.Duration `json:"heartbeat,omitempty"` Heartbeat Duration `json:"heartbeat,omitempty"`
Network NetworkList `json:"network,omitempty"` Network NetworkList `json:"network,omitempty"`
OutboundTLSOptionsContainer OutboundTLSOptionsContainer
} }

View File

@@ -7,14 +7,13 @@ import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/json" "github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badoption"
) )
type TunInboundOptions struct { type TunInboundOptions struct {
InterfaceName string `json:"interface_name,omitempty"` InterfaceName string `json:"interface_name,omitempty"`
MTU uint32 `json:"mtu,omitempty"` MTU uint32 `json:"mtu,omitempty"`
GSO bool `json:"gso,omitempty"` GSO bool `json:"gso,omitempty"`
Address badoption.Listable[netip.Prefix] `json:"address,omitempty"` Address Listable[netip.Prefix] `json:"address,omitempty"`
AutoRoute bool `json:"auto_route,omitempty"` AutoRoute bool `json:"auto_route,omitempty"`
IPRoute2TableIndex int `json:"iproute2_table_index,omitempty"` IPRoute2TableIndex int `json:"iproute2_table_index,omitempty"`
IPRoute2RuleIndex int `json:"iproute2_rule_index,omitempty"` IPRoute2RuleIndex int `json:"iproute2_rule_index,omitempty"`
@@ -22,19 +21,19 @@ type TunInboundOptions struct {
AutoRedirectInputMark FwMark `json:"auto_redirect_input_mark,omitempty"` AutoRedirectInputMark FwMark `json:"auto_redirect_input_mark,omitempty"`
AutoRedirectOutputMark FwMark `json:"auto_redirect_output_mark,omitempty"` AutoRedirectOutputMark FwMark `json:"auto_redirect_output_mark,omitempty"`
StrictRoute bool `json:"strict_route,omitempty"` StrictRoute bool `json:"strict_route,omitempty"`
RouteAddress badoption.Listable[netip.Prefix] `json:"route_address,omitempty"` RouteAddress Listable[netip.Prefix] `json:"route_address,omitempty"`
RouteAddressSet badoption.Listable[string] `json:"route_address_set,omitempty"` RouteAddressSet Listable[string] `json:"route_address_set,omitempty"`
RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"route_exclude_address,omitempty"` RouteExcludeAddress Listable[netip.Prefix] `json:"route_exclude_address,omitempty"`
RouteExcludeAddressSet badoption.Listable[string] `json:"route_exclude_address_set,omitempty"` RouteExcludeAddressSet Listable[string] `json:"route_exclude_address_set,omitempty"`
IncludeInterface badoption.Listable[string] `json:"include_interface,omitempty"` IncludeInterface Listable[string] `json:"include_interface,omitempty"`
ExcludeInterface badoption.Listable[string] `json:"exclude_interface,omitempty"` ExcludeInterface Listable[string] `json:"exclude_interface,omitempty"`
IncludeUID badoption.Listable[uint32] `json:"include_uid,omitempty"` IncludeUID Listable[uint32] `json:"include_uid,omitempty"`
IncludeUIDRange badoption.Listable[string] `json:"include_uid_range,omitempty"` IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"`
ExcludeUID badoption.Listable[uint32] `json:"exclude_uid,omitempty"` ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"`
ExcludeUIDRange badoption.Listable[string] `json:"exclude_uid_range,omitempty"` ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"`
IncludeAndroidUser badoption.Listable[int] `json:"include_android_user,omitempty"` IncludeAndroidUser Listable[int] `json:"include_android_user,omitempty"`
IncludePackage badoption.Listable[string] `json:"include_package,omitempty"` IncludePackage Listable[string] `json:"include_package,omitempty"`
ExcludePackage badoption.Listable[string] `json:"exclude_package,omitempty"` ExcludePackage Listable[string] `json:"exclude_package,omitempty"`
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"` EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"` UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
Stack string `json:"stack,omitempty"` Stack string `json:"stack,omitempty"`
@@ -42,17 +41,17 @@ type TunInboundOptions struct {
InboundOptions InboundOptions
// Deprecated: merged to Address // Deprecated: merged to Address
Inet4Address badoption.Listable[netip.Prefix] `json:"inet4_address,omitempty"` Inet4Address Listable[netip.Prefix] `json:"inet4_address,omitempty"`
// Deprecated: merged to Address // Deprecated: merged to Address
Inet6Address badoption.Listable[netip.Prefix] `json:"inet6_address,omitempty"` Inet6Address Listable[netip.Prefix] `json:"inet6_address,omitempty"`
// Deprecated: merged to RouteAddress // Deprecated: merged to RouteAddress
Inet4RouteAddress badoption.Listable[netip.Prefix] `json:"inet4_route_address,omitempty"` Inet4RouteAddress Listable[netip.Prefix] `json:"inet4_route_address,omitempty"`
// Deprecated: merged to RouteAddress // Deprecated: merged to RouteAddress
Inet6RouteAddress badoption.Listable[netip.Prefix] `json:"inet6_route_address,omitempty"` Inet6RouteAddress Listable[netip.Prefix] `json:"inet6_route_address,omitempty"`
// Deprecated: merged to RouteExcludeAddress // Deprecated: merged to RouteExcludeAddress
Inet4RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"inet4_route_exclude_address,omitempty"` Inet4RouteExcludeAddress Listable[netip.Prefix] `json:"inet4_route_exclude_address,omitempty"`
// Deprecated: merged to RouteExcludeAddress // Deprecated: merged to RouteExcludeAddress
Inet6RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"inet6_route_exclude_address,omitempty"` Inet6RouteExcludeAddress Listable[netip.Prefix] `json:"inet6_route_exclude_address,omitempty"`
} }
type FwMark uint32 type FwMark uint32

View File

@@ -1,7 +1,5 @@
package option package option
import "github.com/sagernet/sing/common/json/badoption"
type TunPlatformOptions struct { type TunPlatformOptions struct {
HTTPProxy *HTTPProxyOptions `json:"http_proxy,omitempty"` HTTPProxy *HTTPProxyOptions `json:"http_proxy,omitempty"`
} }
@@ -9,6 +7,6 @@ type TunPlatformOptions struct {
type HTTPProxyOptions struct { type HTTPProxyOptions struct {
Enabled bool `json:"enabled,omitempty"` Enabled bool `json:"enabled,omitempty"`
ServerOptions ServerOptions
BypassDomain badoption.Listable[string] `json:"bypass_domain,omitempty"` BypassDomain Listable[string] `json:"bypass_domain,omitempty"`
MatchDomain badoption.Listable[string] `json:"match_domain,omitempty"` MatchDomain Listable[string] `json:"match_domain,omitempty"`
} }

View File

@@ -1,7 +1,10 @@
package option package option
import ( import (
"net/http"
"net/netip"
"strings" "strings"
"time"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
@@ -12,6 +15,79 @@ import (
mDNS "github.com/miekg/dns" 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 type NetworkList string
func (v *NetworkList) UnmarshalJSON(content []byte) error { func (v *NetworkList) UnmarshalJSON(content []byte) error {
@@ -44,6 +120,30 @@ func (v NetworkList) Build() []string {
return strings.Split(string(v), "\n") 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 type DomainStrategy dns.DomainStrategy
func (s DomainStrategy) String() string { func (s DomainStrategy) String() string {
@@ -106,6 +206,26 @@ func (s *DomainStrategy) UnmarshalJSON(bytes []byte) error {
return nil 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 type DNSQueryType uint16
func (t DNSQueryType) String() string { func (t DNSQueryType) String() string {
@@ -150,3 +270,15 @@ func DNSQueryTypeToString(queryType uint16) string {
} }
return F.ToString(queryType) 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
}

View File

@@ -5,7 +5,6 @@ import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json" "github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badjson" "github.com/sagernet/sing/common/json/badjson"
"github.com/sagernet/sing/common/json/badoption"
) )
type _V2RayTransportOptions struct { type _V2RayTransportOptions struct {
@@ -68,17 +67,17 @@ func (o *V2RayTransportOptions) UnmarshalJSON(bytes []byte) error {
} }
type V2RayHTTPOptions struct { type V2RayHTTPOptions struct {
Host badoption.Listable[string] `json:"host,omitempty"` Host Listable[string] `json:"host,omitempty"`
Path string `json:"path,omitempty"` Path string `json:"path,omitempty"`
Method string `json:"method,omitempty"` Method string `json:"method,omitempty"`
Headers badoption.HTTPHeader `json:"headers,omitempty"` Headers HTTPHeader `json:"headers,omitempty"`
IdleTimeout badoption.Duration `json:"idle_timeout,omitempty"` IdleTimeout Duration `json:"idle_timeout,omitempty"`
PingTimeout badoption.Duration `json:"ping_timeout,omitempty"` PingTimeout Duration `json:"ping_timeout,omitempty"`
} }
type V2RayWebsocketOptions struct { type V2RayWebsocketOptions struct {
Path string `json:"path,omitempty"` Path string `json:"path,omitempty"`
Headers badoption.HTTPHeader `json:"headers,omitempty"` Headers HTTPHeader `json:"headers,omitempty"`
MaxEarlyData uint32 `json:"max_early_data,omitempty"` MaxEarlyData uint32 `json:"max_early_data,omitempty"`
EarlyDataHeaderName string `json:"early_data_header_name,omitempty"` EarlyDataHeaderName string `json:"early_data_header_name,omitempty"`
} }
@@ -87,8 +86,8 @@ type V2RayQUICOptions struct{}
type V2RayGRPCOptions struct { type V2RayGRPCOptions struct {
ServiceName string `json:"service_name,omitempty"` ServiceName string `json:"service_name,omitempty"`
IdleTimeout badoption.Duration `json:"idle_timeout,omitempty"` IdleTimeout Duration `json:"idle_timeout,omitempty"`
PingTimeout badoption.Duration `json:"ping_timeout,omitempty"` PingTimeout Duration `json:"ping_timeout,omitempty"`
PermitWithoutStream bool `json:"permit_without_stream,omitempty"` PermitWithoutStream bool `json:"permit_without_stream,omitempty"`
ForceLite bool `json:"-"` // for test ForceLite bool `json:"-"` // for test
} }
@@ -96,5 +95,5 @@ type V2RayGRPCOptions struct {
type V2RayHTTPUpgradeOptions struct { type V2RayHTTPUpgradeOptions struct {
Host string `json:"host,omitempty"` Host string `json:"host,omitempty"`
Path string `json:"path,omitempty"` Path string `json:"path,omitempty"`
Headers badoption.HTTPHeader `json:"headers,omitempty"` Headers HTTPHeader `json:"headers,omitempty"`
} }

View File

@@ -1,17 +1,13 @@
package option package option
import ( import "net/netip"
"net/netip"
"github.com/sagernet/sing/common/json/badoption"
)
type WireGuardOutboundOptions struct { type WireGuardOutboundOptions struct {
DialerOptions DialerOptions
SystemInterface bool `json:"system_interface,omitempty"` SystemInterface bool `json:"system_interface,omitempty"`
GSO bool `json:"gso,omitempty"` GSO bool `json:"gso,omitempty"`
InterfaceName string `json:"interface_name,omitempty"` InterfaceName string `json:"interface_name,omitempty"`
LocalAddress badoption.Listable[netip.Prefix] `json:"local_address"` LocalAddress Listable[netip.Prefix] `json:"local_address"`
PrivateKey string `json:"private_key"` PrivateKey string `json:"private_key"`
Peers []WireGuardPeer `json:"peers,omitempty"` Peers []WireGuardPeer `json:"peers,omitempty"`
ServerOptions ServerOptions
@@ -27,6 +23,6 @@ type WireGuardPeer struct {
ServerOptions ServerOptions
PublicKey string `json:"public_key,omitempty"` PublicKey string `json:"public_key,omitempty"`
PreSharedKey string `json:"pre_shared_key,omitempty"` PreSharedKey string `json:"pre_shared_key,omitempty"`
AllowedIPs badoption.Listable[string] `json:"allowed_ips,omitempty"` AllowedIPs Listable[string] `json:"allowed_ips,omitempty"`
Reserved []uint8 `json:"reserved,omitempty"` Reserved []uint8 `json:"reserved,omitempty"`
} }

View File

@@ -43,7 +43,6 @@ func HandleStreamDNSRequest(ctx context.Context, router adapter.Router, conn net
go func() error { go func() error {
response, err := router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message) response, err := router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message)
if err != nil { if err != nil {
conn.Close()
return err return err
} }
responseBuffer := buf.NewPacket() responseBuffer := buf.NewPacket()

View File

@@ -27,8 +27,8 @@ var _ adapter.TCPInjectableInbound = (*Inbound)(nil)
type Inbound struct { type Inbound struct {
inbound.Adapter inbound.Adapter
router adapter.ConnectionRouterEx
logger logger.ContextLogger logger logger.ContextLogger
router adapter.ConnectionRouterEx
listener *listener.Listener listener *listener.Listener
authenticator *auth.Authenticator authenticator *auth.Authenticator
} }
@@ -37,7 +37,6 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
inbound := &Inbound{ inbound := &Inbound{
Adapter: inbound.NewAdapter(C.TypeSOCKS, tag), Adapter: inbound.NewAdapter(C.TypeSOCKS, tag),
router: uot.NewRouter(router, logger), router: uot.NewRouter(router, logger),
logger: logger,
authenticator: auth.NewAuthenticator(options.Users), authenticator: auth.NewAuthenticator(options.Users),
} }
inbound.listener = listener.New(listener.Options{ inbound.listener = listener.New(listener.Options{

View File

@@ -21,7 +21,6 @@ import (
"github.com/sagernet/sing-tun" "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json/badoption"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/ranges" "github.com/sagernet/sing/common/ranges"
@@ -258,7 +257,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
return inbound, nil return inbound, nil
} }
func uidToRange(uidList badoption.Listable[uint32]) []ranges.Range[uint32] { func uidToRange(uidList option.Listable[uint32]) []ranges.Range[uint32] {
return common.Map(uidList, func(uid uint32) ranges.Range[uint32] { return common.Map(uidList, func(uid uint32) ranges.Range[uint32] {
return ranges.NewSingle(uid) return ranges.NewSingle(uid)
}) })
@@ -344,25 +343,19 @@ func (t *Inbound) Start() error {
if err != nil { if err != nil {
return err return err
} }
monitor.Start("initiating tun stack")
err = tunStack.Start()
monitor.Finish()
t.tunStack = tunStack t.tunStack = tunStack
if err != nil {
return err
}
t.logger.Info("started at ", t.tunOptions.Name) t.logger.Info("started at ", t.tunOptions.Name)
return nil return nil
} }
func (t *Inbound) PostStart() error { func (t *Inbound) PostStart() error {
monitor := taskmonitor.New(t.logger, C.StartTimeout) 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 { if t.autoRedirect != nil {
t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet) t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet)
for _, routeRuleSet := range t.routeRuleSet { for _, routeRuleSet := range t.routeRuleSet {

View File

@@ -8,6 +8,7 @@ import (
"os" "os"
"os/user" "os/user"
"strings" "strings"
"syscall"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -76,7 +77,7 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
metadata.Network = N.NetworkTCP metadata.Network = N.NetworkTCP
switch metadata.Destination.Fqdn { switch metadata.Destination.Fqdn {
case mux.Destination.Fqdn: case mux.Destination.Fqdn:
return E.New("global multiplex is deprecated since sing-box v1.7.0, enable multiplex in Inbound fields instead.") return E.New("global multiplex is deprecated since sing-box v1.7.0, enable multiplex in inbound options instead.")
case vmess.MuxDestination.Fqdn: case vmess.MuxDestination.Fqdn:
return E.New("global multiplex (v2ray legacy) not supported since sing-box v1.7.0.") return E.New("global multiplex (v2ray legacy) not supported since sing-box v1.7.0.")
case uot.MagicAddress: case uot.MagicAddress:
@@ -91,33 +92,22 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
if err != nil { if err != nil {
return err return err
} }
var ( var selectedOutbound adapter.Outbound
// selectedOutbound adapter.Outbound var selectReturn bool
selectedDialer N.Dialer
selectedTag string
selectedDescription string
)
if selectedRule != nil { if selectedRule != nil {
switch action := selectedRule.Action().(type) { switch action := selectedRule.Action().(type) {
case *rule.RuleActionRoute: case *rule.RuleActionRoute:
selectedOutbound, loaded := r.Outbound(action.Outbound) var loaded bool
selectedOutbound, loaded = r.Outbound(action.Outbound)
if !loaded { if !loaded {
buf.ReleaseMulti(buffers) buf.ReleaseMulti(buffers)
return E.New("outbound not found: ", action.Outbound) return E.New("outbound not found: ", action.Outbound)
} }
if !common.Contains(selectedOutbound.Network(), N.NetworkTCP) { case *rule.RuleActionReturn:
buf.ReleaseMulti(buffers) selectReturn = true
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: case *rule.RuleActionReject:
buf.ReleaseMulti(buffers) buf.ReleaseMulti(buffers)
N.CloseOnHandshakeFailure(conn, onClose, action.Error(ctx)) N.CloseOnHandshakeFailure(conn, onClose, action.Error())
return nil return nil
case *rule.RuleActionHijackDNS: case *rule.RuleActionHijackDNS:
for _, buffer := range buffers { for _, buffer := range buffers {
@@ -127,16 +117,17 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
return nil return nil
} }
} }
if selectedRule == nil { if selectedRule == nil || selectReturn {
if r.defaultOutboundForConnection == nil { if r.defaultOutboundForConnection == nil {
buf.ReleaseMulti(buffers) buf.ReleaseMulti(buffers)
return E.New("missing default outbound with TCP support") return E.New("missing default outbound with TCP support")
} }
selectedDialer = r.defaultOutboundForConnection selectedOutbound = r.defaultOutboundForConnection
selectedTag = r.defaultOutboundForConnection.Tag() }
selectedDescription = F.ToString("outbound/", r.defaultOutboundForConnection.Type(), "[", r.defaultOutboundForConnection.Tag(), "]") if !common.Contains(selectedOutbound.Network(), N.NetworkTCP) {
buf.ReleaseMulti(buffers)
return E.New("TCP is not supported by outbound: ", selectedOutbound.Tag())
} }
for _, buffer := range buffers { for _, buffer := range buffers {
conn = bufio.NewCachedConn(conn, buffer) conn = bufio.NewCachedConn(conn, buffer)
} }
@@ -147,10 +138,10 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
} }
if r.v2rayServer != nil { if r.v2rayServer != nil {
if statsService := r.v2rayServer.StatsService(); statsService != nil { if statsService := r.v2rayServer.StatsService(); statsService != nil {
conn = statsService.RoutedConnection(metadata.Inbound, selectedTag, metadata.User, conn) conn = statsService.RoutedConnection(metadata.Inbound, selectedOutbound.Tag(), metadata.User, conn)
} }
} }
legacyOutbound, isLegacy := selectedDialer.(adapter.ConnectionHandler) legacyOutbound, isLegacy := selectedOutbound.(adapter.ConnectionHandler)
if isLegacy { if isLegacy {
err = legacyOutbound.NewConnection(ctx, conn, metadata) err = legacyOutbound.NewConnection(ctx, conn, metadata)
if err != nil { if err != nil {
@@ -158,7 +149,7 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
if onClose != nil { if onClose != nil {
onClose(err) onClose(err)
} }
return E.Cause(err, selectedDescription) return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
} else { } else {
if onClose != nil { if onClose != nil {
onClose(nil) onClose(nil)
@@ -167,13 +158,13 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
return nil return nil
} }
// TODO // TODO
err = outbound.NewConnection(ctx, selectedDialer, conn, metadata) err = outbound.NewConnection(ctx, selectedOutbound, conn, metadata)
if err != nil { if err != nil {
conn.Close() conn.Close()
if onClose != nil { if onClose != nil {
onClose(err) onClose(err)
} }
return E.Cause(err, selectedDescription) return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
} else { } else {
if onClose != nil { if onClose != nil {
onClose(nil) onClose(nil)
@@ -245,33 +236,23 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
if err != nil { if err != nil {
return err return err
} }
var ( var selectedOutbound adapter.Outbound
selectedDialer N.Dialer
selectedTag string
selectedDescription string
)
var selectReturn bool var selectReturn bool
if selectedRule != nil { if selectedRule != nil {
switch action := selectedRule.Action().(type) { switch action := selectedRule.Action().(type) {
case *rule.RuleActionRoute: case *rule.RuleActionRoute:
selectedOutbound, loaded := r.Outbound(action.Outbound) var loaded bool
selectedOutbound, loaded = r.Outbound(action.Outbound)
if !loaded { if !loaded {
N.ReleaseMultiPacketBuffer(packetBuffers) N.ReleaseMultiPacketBuffer(packetBuffers)
return E.New("outbound not found: ", action.Outbound) return E.New("outbound not found: ", action.Outbound)
} }
if !common.Contains(selectedOutbound.Network(), N.NetworkUDP) { metadata.UDPDisableDomainUnmapping = action.UDPDisableDomainUnmapping
N.ReleaseMultiPacketBuffer(packetBuffers) case *rule.RuleActionReturn:
return E.New("UDP is not supported by outbound: ", selectedOutbound.Tag()) selectReturn = true
}
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: case *rule.RuleActionReject:
N.ReleaseMultiPacketBuffer(packetBuffers) N.ReleaseMultiPacketBuffer(packetBuffers)
N.CloseOnHandshakeFailure(conn, onClose, action.Error(ctx)) N.CloseOnHandshakeFailure(conn, onClose, syscall.ECONNREFUSED)
return nil return nil
case *rule.RuleActionHijackDNS: case *rule.RuleActionHijackDNS:
r.hijackDNSPacket(ctx, conn, packetBuffers, metadata) r.hijackDNSPacket(ctx, conn, packetBuffers, metadata)
@@ -283,9 +264,11 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
N.ReleaseMultiPacketBuffer(packetBuffers) N.ReleaseMultiPacketBuffer(packetBuffers)
return E.New("missing default outbound with UDP support") return E.New("missing default outbound with UDP support")
} }
selectedDialer = r.defaultOutboundForPacketConnection selectedOutbound = r.defaultOutboundForPacketConnection
selectedTag = r.defaultOutboundForPacketConnection.Tag() }
selectedDescription = F.ToString("outbound/", r.defaultOutboundForPacketConnection.Type(), "[", r.defaultOutboundForPacketConnection.Tag(), "]") if !common.Contains(selectedOutbound.Network(), N.NetworkUDP) {
N.ReleaseMultiPacketBuffer(packetBuffers)
return E.New("UDP is not supported by outbound: ", selectedOutbound.Tag())
} }
for _, buffer := range packetBuffers { for _, buffer := range packetBuffers {
conn = bufio.NewCachedPacketConn(conn, buffer.Buffer, buffer.Destination) conn = bufio.NewCachedPacketConn(conn, buffer.Buffer, buffer.Destination)
@@ -298,26 +281,26 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
} }
if r.v2rayServer != nil { if r.v2rayServer != nil {
if statsService := r.v2rayServer.StatsService(); statsService != nil { if statsService := r.v2rayServer.StatsService(); statsService != nil {
conn = statsService.RoutedPacketConnection(metadata.Inbound, selectedTag, metadata.User, conn) conn = statsService.RoutedPacketConnection(metadata.Inbound, selectedOutbound.Tag(), metadata.User, conn)
} }
} }
if metadata.FakeIP { if metadata.FakeIP {
conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination) conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination)
} }
legacyOutbound, isLegacy := selectedDialer.(adapter.PacketConnectionHandler) legacyOutbound, isLegacy := selectedOutbound.(adapter.PacketConnectionHandler)
if isLegacy { if isLegacy {
err = legacyOutbound.NewPacketConnection(ctx, conn, metadata) err = legacyOutbound.NewPacketConnection(ctx, conn, metadata)
N.CloseOnHandshakeFailure(conn, onClose, err) N.CloseOnHandshakeFailure(conn, onClose, err)
if err != nil { if err != nil {
return E.Cause(err, selectedDescription) return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
} }
return nil return nil
} }
// TODO // TODO
err = outbound.NewPacketConnection(ctx, selectedDialer, conn, metadata) err = outbound.NewPacketConnection(ctx, selectedOutbound, conn, metadata)
N.CloseOnHandshakeFailure(conn, onClose, err) N.CloseOnHandshakeFailure(conn, onClose, err)
if err != nil { if err != nil {
return E.Cause(err, selectedDescription) return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]")
} }
return nil return nil
} }
@@ -334,7 +317,7 @@ func (r *Router) PreMatch(metadata adapter.InboundContext) error {
if !isReject { if !isReject {
return nil return nil
} }
return rejectAction.Error(nil) return rejectAction.Error()
} }
func (r *Router) matchRule( func (r *Router) matchRule(
@@ -462,7 +445,7 @@ match:
} }
} else { } else {
switch currentRule.Action().Type() { switch currentRule.Action().Type() {
case C.RuleActionTypeReject: case C.RuleActionTypeReject, C.RuleActionTypeResolve:
ruleDescription := currentRule.String() ruleDescription := currentRule.String()
if ruleDescription != "" { if ruleDescription != "" {
r.logger.DebugContext(ctx, "pre-match[", currentRuleIndex, "] ", currentRule, " => ", currentRule.Action()) r.logger.DebugContext(ctx, "pre-match[", currentRuleIndex, "] ", currentRule, " => ", currentRule.Action())
@@ -472,9 +455,6 @@ match:
} }
} }
switch action := currentRule.Action().(type) { switch action := currentRule.Action().(type) {
case *rule.RuleActionRouteOptions:
metadata.UDPDisableDomainUnmapping = action.UDPDisableDomainUnmapping
metadata.UDPConnect = action.UDPConnect
case *rule.RuleActionSniff: case *rule.RuleActionSniff:
if !preMatch { if !preMatch {
newBuffer, newPacketBuffers, newErr := r.actionSniff(ctx, metadata, action, inputConn, inputPacketConn) newBuffer, newPacketBuffers, newErr := r.actionSniff(ctx, metadata, action, inputConn, inputPacketConn)

View File

@@ -8,10 +8,8 @@ import (
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
R "github.com/sagernet/sing-box/route/rule" R "github.com/sagernet/sing-box/route/rule"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
tun "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/cache" "github.com/sagernet/sing/common/cache"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
@@ -50,63 +48,38 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int,
if ruleIndex != -1 { if ruleIndex != -1 {
dnsRules = dnsRules[ruleIndex+1:] dnsRules = dnsRules[ruleIndex+1:]
} }
for currentRuleIndex, currentRule := range dnsRules { for currentRuleIndex, rule := range dnsRules {
if currentRule.WithAddressLimit() && !isAddressQuery { if rule.WithAddressLimit() && !isAddressQuery {
continue continue
} }
metadata.ResetRuleCache() metadata.ResetRuleCache()
if currentRule.Match(metadata) { if rule.Match(metadata) {
displayRuleIndex := currentRuleIndex displayRuleIndex := currentRuleIndex
if displayRuleIndex != -1 { if displayRuleIndex != -1 {
displayRuleIndex += displayRuleIndex + 1 displayRuleIndex += displayRuleIndex + 1
} }
ruleDescription := currentRule.String() if routeAction, isRoute := rule.Action().(*R.RuleActionDNSRoute); isRoute {
if ruleDescription != "" { transport, loaded := r.transportMap[routeAction.Server]
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 { if !loaded {
r.dnsLogger.ErrorContext(ctx, "transport not found: ", action.Server) r.dnsLogger.ErrorContext(ctx, "transport not found: ", routeAction.Server)
continue continue
} }
_, isFakeIP := transport.(adapter.FakeIPTransport) _, isFakeIP := transport.(adapter.FakeIPTransport)
if isFakeIP && !allowFakeIP { if isFakeIP && !allowFakeIP {
continue continue
} }
if isFakeIP || action.DisableCache { options.DisableCache = isFakeIP || routeAction.DisableCache
options.DisableCache = true options.RewriteTTL = routeAction.RewriteTTL
} options.ClientSubnet = routeAction.ClientSubnet
if action.RewriteTTL != nil {
options.RewriteTTL = action.RewriteTTL
}
if action.ClientSubnet.IsValid() {
options.ClientSubnet = action.ClientSubnet
}
if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded { if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded {
options.Strategy = domainStrategy options.Strategy = domainStrategy
} else { } else {
options.Strategy = r.defaultDomainStrategy options.Strategy = r.defaultDomainStrategy
} }
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action()) r.dnsLogger.DebugContext(ctx, "match[", displayRuleIndex, "] ", rule.String(), " => ", rule.Action())
return transport, options, currentRule, currentRuleIndex return transport, options, rule, currentRuleIndex
case *R.RuleActionDNSRouteOptions: } else {
if action.DisableCache { return nil, options, rule, currentRuleIndex
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
} }
} }
} }
@@ -154,17 +127,6 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
dnsCtx := adapter.OverrideContext(ctx) dnsCtx := adapter.OverrideContext(ctx)
var addressLimit bool var addressLimit bool
transport, options, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message)) 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() { if rule != nil && rule.WithAddressLimit() {
addressLimit = true addressLimit = true
response, err = r.dnsClient.ExchangeWithResponseCheck(dnsCtx, transport, message, options, func(response *mDNS.Msg) bool { response, err = r.dnsClient.ExchangeWithResponseCheck(dnsCtx, transport, message, options, func(response *mDNS.Msg) bool {
@@ -276,17 +238,6 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
if strategy != dns.DomainStrategyAsIS { if strategy != dns.DomainStrategyAsIS {
options.Strategy = strategy 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() { if rule != nil && rule.WithAddressLimit() {
addressLimit = true addressLimit = true
responseAddrs, err = r.dnsClient.LookupWithResponseCheck(dnsCtx, transport, domain, options, func(responseAddrs []netip.Addr) bool { responseAddrs, err = r.dnsClient.LookupWithResponseCheck(dnsCtx, transport, domain, options, func(responseAddrs []netip.Addr) bool {

View File

@@ -8,7 +8,6 @@ import (
"os" "os"
"runtime" "runtime"
"strings" "strings"
"syscall"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -205,19 +204,12 @@ func NewRouter(
} else { } else {
detour = dialer.NewDetour(router, server.Detour) detour = dialer.NewDetour(router, server.Detour)
} }
var serverProtocol string
switch server.Address { switch server.Address {
case "local": case "local":
serverProtocol = "local"
default: default:
serverURL, _ := url.Parse(server.Address) serverURL, _ := url.Parse(server.Address)
var serverAddress string var serverAddress string
if serverURL != nil { if serverURL != nil {
if serverURL.Scheme == "" {
serverProtocol = "udp"
} else {
serverProtocol = serverURL.Scheme
}
serverAddress = serverURL.Hostname() serverAddress = serverURL.Hostname()
} }
if serverAddress == "" { if serverAddress == "" {
@@ -239,16 +231,13 @@ func NewRouter(
} }
var clientSubnet netip.Prefix var clientSubnet netip.Prefix
if server.ClientSubnet != nil { if server.ClientSubnet != nil {
clientSubnet = netip.Prefix(common.PtrValueOrDefault(server.ClientSubnet)) clientSubnet = server.ClientSubnet.Build()
} else if dnsOptions.ClientSubnet != nil { } else if dnsOptions.ClientSubnet != nil {
clientSubnet = netip.Prefix(common.PtrValueOrDefault(dnsOptions.ClientSubnet)) clientSubnet = dnsOptions.ClientSubnet.Build()
}
if serverProtocol == "" {
serverProtocol = "transport"
} }
transport, err := dns.CreateTransport(dns.TransportOptions{ transport, err := dns.CreateTransport(dns.TransportOptions{
Context: ctx, Context: ctx,
Logger: logFactory.NewLogger(F.ToString("dns/", serverProtocol, "[", tag, "]")), Logger: logFactory.NewLogger(F.ToString("dns/transport[", tag, "]")),
Name: tag, Name: tag,
Dialer: detour, Dialer: detour,
Address: server.Address, Address: server.Address,
@@ -833,11 +822,7 @@ func (r *Router) AutoDetectInterface() bool {
func (r *Router) AutoDetectInterfaceFunc() control.Func { func (r *Router) AutoDetectInterfaceFunc() control.Func {
if r.platformInterface != nil && r.platformInterface.UsePlatformAutoDetectInterfaceControl() { if r.platformInterface != nil && r.platformInterface.UsePlatformAutoDetectInterfaceControl() {
return func(network, address string, conn syscall.RawConn) error { return r.platformInterface.AutoDetectInterfaceControl()
return control.Raw(conn, func(fd uintptr) error {
return r.platformInterface.AutoDetectInterfaceControl(int(fd))
})
}
} else { } else {
if r.interfaceMonitor == nil { if r.interfaceMonitor == nil {
return nil return nil

View File

@@ -1,63 +1,34 @@
package rule package rule
import ( import (
"context"
"net/netip" "net/netip"
"os"
"strings" "strings"
"sync"
"syscall" "syscall"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/common/sniff" "github.com/sagernet/sing-box/common/sniff"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
"github.com/sagernet/sing-tun" "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/logger"
N "github.com/sagernet/sing/common/network"
) )
func NewRuleAction(router adapter.Router, logger logger.ContextLogger, action option.RuleAction) (adapter.RuleAction, error) { func NewRuleAction(action option.RuleAction) (adapter.RuleAction, error) {
switch action.Action { switch action.Action {
case C.RuleActionTypeRoute: case C.RuleActionTypeRoute:
return &RuleActionRoute{ return &RuleActionRoute{
Outbound: action.RouteOptions.Outbound, Outbound: action.RouteOptions.Outbound,
UDPDisableDomainUnmapping: action.RouteOptions.UDPDisableDomainUnmapping,
}, nil }, nil
case C.RuleActionTypeRouteOptions: case C.RuleActionTypeReturn:
return &RuleActionRouteOptions{ return &RuleActionReturn{}, nil
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.RuleActionTypeReject: case C.RuleActionTypeReject:
return &RuleActionReject{ return &RuleActionReject{
Method: action.RejectOptions.Method, Method: action.RejectOptions.Method,
NoDrop: action.RejectOptions.NoDrop,
logger: logger,
}, nil }, nil
case C.RuleActionTypeHijackDNS: case C.RuleActionTypeHijackDNS:
return &RuleActionHijackDNS{}, nil return &RuleActionHijackDNS{}, nil
@@ -77,26 +48,20 @@ func NewRuleAction(router adapter.Router, logger logger.ContextLogger, action op
} }
} }
func NewDNSRuleAction(logger logger.ContextLogger, action option.DNSRuleAction) adapter.RuleAction { func NewDNSRuleAction(action option.DNSRuleAction) adapter.RuleAction {
switch action.Action { switch action.Action {
case C.RuleActionTypeRoute: case C.RuleActionTypeRoute:
return &RuleActionDNSRoute{ return &RuleActionDNSRoute{
Server: action.RouteOptions.Server, Server: action.RouteOptions.Server,
DisableCache: action.RouteOptions.DisableCache, DisableCache: action.RouteOptions.DisableCache,
RewriteTTL: action.RouteOptions.RewriteTTL, RewriteTTL: action.RouteOptions.RewriteTTL,
ClientSubnet: netip.Prefix(common.PtrValueOrDefault(action.RouteOptions.ClientSubnet)), ClientSubnet: action.RouteOptions.ClientSubnet.Build(),
}
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: case C.RuleActionTypeReject:
return &RuleActionReject{ return &RuleActionReject{
Method: action.RejectOptions.Method, Method: action.RejectOptions.Method,
NoDrop: action.RejectOptions.NoDrop,
logger: logger,
} }
default: default:
panic(F.ToString("unknown rule action: ", action.Action)) panic(F.ToString("unknown rule action: ", action.Action))
@@ -105,6 +70,7 @@ func NewDNSRuleAction(logger logger.ContextLogger, action option.DNSRuleAction)
type RuleActionRoute struct { type RuleActionRoute struct {
Outbound string Outbound string
UDPDisableDomainUnmapping bool
} }
func (r *RuleActionRoute) Type() string { func (r *RuleActionRoute) Type() string {
@@ -115,26 +81,6 @@ func (r *RuleActionRoute) String() string {
return F.ToString("route(", r.Outbound, ")") 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 { type RuleActionDNSRoute struct {
Server string Server string
DisableCache bool DisableCache bool
@@ -150,49 +96,18 @@ func (r *RuleActionDNSRoute) String() string {
return F.ToString("route(", r.Server, ")") return F.ToString("route(", r.Server, ")")
} }
type RuleActionDNSRouteOptions struct { type RuleActionReturn struct{}
DisableCache bool
RewriteTTL *uint32 func (r *RuleActionReturn) Type() string {
ClientSubnet netip.Prefix return C.RuleActionTypeReturn
} }
func (r *RuleActionDNSRouteOptions) Type() string { func (r *RuleActionReturn) String() string {
return C.RuleActionTypeRouteOptions return "return"
}
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 { type RuleActionReject struct {
Method string Method string
NoDrop bool
logger logger.ContextLogger
dropAccess sync.Mutex
dropCounter []time.Time
} }
func (r *RuleActionReject) Type() string { func (r *RuleActionReject) Type() string {
@@ -206,30 +121,21 @@ func (r *RuleActionReject) String() string {
return F.ToString("reject(", r.Method, ")") return F.ToString("reject(", r.Method, ")")
} }
func (r *RuleActionReject) Error(ctx context.Context) error { func (r *RuleActionReject) Error() error {
var returnErr error
switch r.Method { switch r.Method {
case C.RuleActionRejectMethodDefault: case C.RuleActionRejectMethodReset:
returnErr = syscall.ECONNREFUSED return os.ErrClosed
case C.RuleActionRejectMethodNetworkUnreachable:
return syscall.ENETUNREACH
case C.RuleActionRejectMethodHostUnreachable:
return syscall.EHOSTUNREACH
case C.RuleActionRejectMethodDefault, C.RuleActionRejectMethodPortUnreachable:
return syscall.ECONNREFUSED
case C.RuleActionRejectMethodDrop: case C.RuleActionRejectMethodDrop:
return tun.ErrDrop return tun.ErrDrop
default: default:
panic(F.ToString("unknown reject method: ", r.Method)) 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{} type RuleActionHijackDNS struct{}

View File

@@ -52,7 +52,7 @@ type RuleItem interface {
} }
func NewDefaultRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.DefaultRule) (*DefaultRule, error) { func NewDefaultRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.DefaultRule) (*DefaultRule, error) {
action, err := NewRuleAction(router, logger, options.RuleAction) action, err := NewRuleAction(options.RuleAction)
if err != nil { if err != nil {
return nil, E.Cause(err, "action") 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) { func NewLogicalRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.LogicalRule) (*LogicalRule, error) {
action, err := NewRuleAction(router, logger, options.RuleAction) action, err := NewRuleAction(options.RuleAction)
if err != nil { if err != nil {
return nil, E.Cause(err, "action") return nil, E.Cause(err, "action")
} }

View File

@@ -51,7 +51,7 @@ func NewDefaultDNSRule(ctx context.Context, router adapter.Router, logger log.Co
rule := &DefaultDNSRule{ rule := &DefaultDNSRule{
abstractDefaultRule: abstractDefaultRule{ abstractDefaultRule: abstractDefaultRule{
invert: options.Invert, invert: options.Invert,
action: NewDNSRuleAction(logger, options.DNSRuleAction), action: NewDNSRuleAction(options.DNSRuleAction),
}, },
} }
if len(options.Inbound) > 0 { if len(options.Inbound) > 0 {
@@ -287,7 +287,7 @@ func NewLogicalDNSRule(ctx context.Context, router adapter.Router, logger log.Co
abstractLogicalRule: abstractLogicalRule{ abstractLogicalRule: abstractLogicalRule{
rules: make([]adapter.HeadlessRule, len(options.Rules)), rules: make([]adapter.HeadlessRule, len(options.Rules)),
invert: options.Invert, invert: options.Invert,
action: NewDNSRuleAction(logger, options.DNSRuleAction), action: NewDNSRuleAction(options.DNSRuleAction),
}, },
} }
switch options.Mode { switch options.Mode {

View File

@@ -7,8 +7,6 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-shadowsocks/shadowaead_2022" "github.com/sagernet/sing-shadowsocks/shadowaead_2022"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/json/badoption"
"github.com/gofrs/uuid/v5" "github.com/gofrs/uuid/v5"
) )
@@ -17,13 +15,13 @@ func TestBrutalShadowsocks(t *testing.T) {
method := shadowaead_2022.List[0] method := shadowaead_2022.List[0]
password := mkBase64(t, 16) password := mkBase64(t, 16)
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
Tag: "mixed-in", Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -32,7 +30,7 @@ func TestBrutalShadowsocks(t *testing.T) {
Type: C.TypeShadowsocks, Type: C.TypeShadowsocks,
ShadowsocksOptions: option.ShadowsocksInboundOptions{ ShadowsocksOptions: option.ShadowsocksInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
}, },
Method: method, Method: method,
@@ -102,13 +100,13 @@ func TestBrutalTrojan(t *testing.T) {
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org") _, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
password := mkBase64(t, 16) password := mkBase64(t, 16)
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
Tag: "mixed-in", Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -117,7 +115,7 @@ func TestBrutalTrojan(t *testing.T) {
Type: C.TypeTrojan, Type: C.TypeTrojan,
TrojanOptions: option.TrojanInboundOptions{ TrojanOptions: option.TrojanInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
}, },
Users: []option.TrojanUser{{Password: password}}, Users: []option.TrojanUser{{Password: password}},
@@ -199,13 +197,13 @@ func TestBrutalTrojan(t *testing.T) {
func TestBrutalVMess(t *testing.T) { func TestBrutalVMess(t *testing.T) {
user, _ := uuid.NewV4() user, _ := uuid.NewV4()
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
Tag: "mixed-in", Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -214,7 +212,7 @@ func TestBrutalVMess(t *testing.T) {
Type: C.TypeVMess, Type: C.TypeVMess,
VMessOptions: option.VMessInboundOptions{ VMessOptions: option.VMessInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
}, },
Users: []option.VMessUser{{UUID: user.String()}}, Users: []option.VMessUser{{UUID: user.String()}},
@@ -281,13 +279,13 @@ func TestBrutalVMess(t *testing.T) {
func TestBrutalVLESS(t *testing.T) { func TestBrutalVLESS(t *testing.T) {
user, _ := uuid.NewV4() user, _ := uuid.NewV4()
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
Tag: "mixed-in", Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -296,7 +294,7 @@ func TestBrutalVLESS(t *testing.T) {
Type: C.TypeVLESS, Type: C.TypeVLESS,
VLESSOptions: option.VLESSInboundOptions{ VLESSOptions: option.VLESSInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
}, },
Users: []option.VLESSUser{{UUID: user.String()}}, Users: []option.VLESSUser{{UUID: user.String()}},

View File

@@ -6,20 +6,18 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "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. // Since this is a feature one-off added by outsiders, I won't address these anymore.
func _TestProxyProtocol(t *testing.T) { func _TestProxyProtocol(t *testing.T) {
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
Tag: "mixed-in", Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -28,7 +26,7 @@ func _TestProxyProtocol(t *testing.T) {
Type: C.TypeDirect, Type: C.TypeDirect,
DirectOptions: option.DirectInboundOptions{ DirectOptions: option.DirectInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
ProxyProtocol: true, ProxyProtocol: true,
}, },

View File

@@ -7,8 +7,6 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/json/badoption"
"github.com/gofrs/uuid/v5" "github.com/gofrs/uuid/v5"
) )
@@ -16,13 +14,13 @@ import (
func TestTUICDomainUDP(t *testing.T) { func TestTUICDomainUDP(t *testing.T) {
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org") _, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
Tag: "mixed-in", Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -31,7 +29,7 @@ func TestTUICDomainUDP(t *testing.T) {
Type: C.TypeTUIC, Type: C.TypeTUIC,
TUICOptions: option.TUICInboundOptions{ TUICOptions: option.TUICInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
InboundOptions: option.InboundOptions{ InboundOptions: option.InboundOptions{
DomainStrategy: option.DomainStrategy(dns.DomainStrategyUseIPv6), DomainStrategy: option.DomainStrategy(dns.DomainStrategyUseIPv6),

View File

@@ -8,7 +8,6 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/json/badoption"
"github.com/gofrs/uuid/v5" "github.com/gofrs/uuid/v5"
) )
@@ -17,13 +16,13 @@ func TestECH(t *testing.T) {
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org") _, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false)) echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false))
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
Tag: "mixed-in", Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -32,7 +31,7 @@ func TestECH(t *testing.T) {
Type: C.TypeTrojan, Type: C.TypeTrojan,
TrojanOptions: option.TrojanInboundOptions{ TrojanOptions: option.TrojanInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
}, },
Users: []option.TrojanUser{ Users: []option.TrojanUser{
@@ -110,13 +109,13 @@ func TestECHQUIC(t *testing.T) {
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org") _, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false)) echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false))
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
Tag: "mixed-in", Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -125,7 +124,7 @@ func TestECHQUIC(t *testing.T) {
Type: C.TypeTUIC, Type: C.TypeTUIC,
TUICOptions: option.TUICInboundOptions{ TUICOptions: option.TUICInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
}, },
Users: []option.TUICUser{{ Users: []option.TUICUser{{
@@ -200,13 +199,13 @@ func TestECHHysteria2(t *testing.T) {
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org") _, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false)) echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false))
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
Tag: "mixed-in", Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -215,7 +214,7 @@ func TestECHHysteria2(t *testing.T) {
Type: C.TypeHysteria2, Type: C.TypeHysteria2,
Hysteria2Options: option.Hysteria2InboundOptions{ Hysteria2Options: option.Hysteria2InboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
}, },
Users: []option.Hysteria2User{{ Users: []option.Hysteria2User{{

View File

@@ -12,10 +12,10 @@ require (
github.com/docker/docker v27.3.1+incompatible github.com/docker/docker v27.3.1+incompatible
github.com/docker/go-connections v0.5.0 github.com/docker/go-connections v0.5.0
github.com/gofrs/uuid/v5 v5.3.0 github.com/gofrs/uuid/v5 v5.3.0
github.com/sagernet/quic-go v0.48.1-beta.1 github.com/sagernet/quic-go v0.48.0-beta.1
github.com/sagernet/sing v0.5.1-0.20241107131656-6e1285b5d82f github.com/sagernet/sing v0.5.0-rc.4.0.20241022031908-cd17884118cb
github.com/sagernet/sing-dns v0.3.1-0.20241105104342-1914f319ddab github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241021154031-a59e0fbba3ce
github.com/sagernet/sing-quic v0.3.0-rc.2 github.com/sagernet/sing-quic v0.3.0-rc.1
github.com/sagernet/sing-shadowsocks v0.2.7 github.com/sagernet/sing-shadowsocks v0.2.7
github.com/sagernet/sing-shadowsocks2 v0.2.0 github.com/sagernet/sing-shadowsocks2 v0.2.0
github.com/spyzhov/ajson v0.9.4 github.com/spyzhov/ajson v0.9.4
@@ -25,11 +25,13 @@ require (
) )
require ( require (
berty.tech/go-libtor v1.0.385 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/andybalholm/brotli v1.0.6 // indirect github.com/andybalholm/brotli v1.0.6 // indirect
github.com/caddyserver/certmagic v0.20.0 // indirect github.com/caddyserver/certmagic v0.20.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect github.com/cloudflare/circl v1.3.7 // indirect
github.com/containerd/log v0.1.0 // 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/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.5.0 // indirect github.com/distribution/reference v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect
@@ -40,6 +42,8 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // 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/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/gogo/protobuf v1.3.2 // indirect
github.com/google/btree v1.1.3 // indirect github.com/google/btree v1.1.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-cmp v0.6.0 // indirect
@@ -61,6 +65,7 @@ require (
github.com/moby/term v0.5.0 // indirect github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect github.com/morikuni/aec v1.0.0 // indirect
github.com/onsi/ginkgo/v2 v2.9.7 // 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/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/oschwald/maxminddb-golang v1.12.0 // indirect github.com/oschwald/maxminddb-golang v1.12.0 // indirect
@@ -76,10 +81,13 @@ require (
github.com/sagernet/nftables v0.3.0-beta.4 // indirect github.com/sagernet/nftables v0.3.0-beta.4 // indirect
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // 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-mux v0.2.1-0.20241020175909-fe6153f7a9ec // indirect
github.com/sagernet/sing-tun v0.4.0-rc.5.0.20241107062822-5a91eb99c90f // 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-vmess v0.1.12 // indirect github.com/sagernet/sing-vmess v0.1.12 // indirect
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
github.com/sagernet/utls v1.6.7 // 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/vishvananda/netns v0.0.4 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect github.com/zeebo/blake3 v0.2.3 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect

View File

@@ -1,3 +1,5 @@
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 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= 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= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
@@ -12,6 +14,9 @@ github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vc
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= 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 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= 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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -38,6 +43,10 @@ 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-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 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 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 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@@ -98,6 +107,8 @@ 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/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 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= 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 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
@@ -124,37 +135,45 @@ 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/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 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
github.com/sagernet/quic-go v0.48.1-beta.1 h1:ElPaV5yzlXIKZpqFMAcUGax6vddi3zt4AEpT94Z0vwo= github.com/sagernet/quic-go v0.48.0-beta.1 h1:86hQZrmuoARI3BpDRkQaP0iAVpywA4YsRrzJPYuPKWg=
github.com/sagernet/quic-go v0.48.1-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k= github.com/sagernet/quic-go v0.48.0-beta.1/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 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= 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.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
github.com/sagernet/sing v0.5.1-0.20241107131656-6e1285b5d82f h1:A6+OeV5P1mok0eEEbLh4PidymZ6VZnTZ2uHwfapXgdU= github.com/sagernet/sing v0.5.0-rc.4.0.20241022031908-cd17884118cb h1:3IhGq2UmcbQfAcuqyE8RYKFapqEEa3eItS/MrZr+5l8=
github.com/sagernet/sing v0.5.1-0.20241107131656-6e1285b5d82f/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= 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.1-0.20241105104342-1914f319ddab h1:djP4EY/KM5T62xscormLflVi7eDlHv6p7md1FHMSArE= github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241021154031-a59e0fbba3ce h1:OfpxE5qnXMyU/9LtNgX4M7bGP11lJx4s+KZ3Sijb0HE=
github.com/sagernet/sing-dns v0.3.1-0.20241105104342-1914f319ddab/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M= github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241021154031-a59e0fbba3ce/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 h1:6Fd/VsEsw9qIjaGi1IBTZSb4b4v5JYtNcoiBtGsQC48=
github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec/go.mod h1:RSwqqHwbtTOX3vs6ms8vMtBGH/0ZNyLm/uwt6TlmR84= github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec/go.mod h1:RSwqqHwbtTOX3vs6ms8vMtBGH/0ZNyLm/uwt6TlmR84=
github.com/sagernet/sing-quic v0.3.0-rc.2 h1:7vcC4bdS1GBJzHZhfmJiH0CfzQ4mYLUW51Z2RNHcGwc= github.com/sagernet/sing-quic v0.3.0-rc.1 h1:SlzL1yfEAKJyRduub8vzOVtbyTLAX7RZEEBZxO5utts=
github.com/sagernet/sing-quic v0.3.0-rc.2/go.mod h1:3UOq51WVqzra7eCgod7t4hqnTaOiZzFUci9avMrtOqs= github.com/sagernet/sing-quic v0.3.0-rc.1/go.mod h1:uX+aUHA0fgIN6U3WViseDpSdTQriViZ7qz0Wbsf1mNQ=
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8= github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE= 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 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
github.com/sagernet/sing-tun v0.4.0-rc.5.0.20241107062822-5a91eb99c90f h1:gQwTgN/E4oHe3VlseD3/RhPs866cWcTsPG4dP6a8f8o= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
github.com/sagernet/sing-tun v0.4.0-rc.5.0.20241107062822-5a91eb99c90f/go.mod h1:Ehs5mZ3T8tTgV3H1Tx4Va5ixvyKjTAUPJ3G11dq7B/g= 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-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg= 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/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 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= 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 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8=
github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM= 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 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 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 h1:MVibcTCgO7DY4IlskdqIlCmDOsUOZ9P7oKj8ifdcf84=
github.com/spyzhov/ajson v0.9.4/go.mod h1:a6oSw0MMb7Z5aD2tPoPO+jq11ETKgXUr2XktHdT8Wt8= 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/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.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 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
@@ -192,8 +211,10 @@ 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 h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= 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-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-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-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 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
@@ -206,6 +227,8 @@ 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-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-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-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 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= 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= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -214,15 +237,23 @@ 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 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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-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-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-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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.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.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 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.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.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 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=

View File

@@ -6,19 +6,17 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/json/badoption"
) )
func TestHTTPSelf(t *testing.T) { func TestHTTPSelf(t *testing.T) {
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
Tag: "mixed-in", Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -27,7 +25,7 @@ func TestHTTPSelf(t *testing.T) {
Type: C.TypeMixed, Type: C.TypeMixed,
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
}, },
}, },

View File

@@ -7,8 +7,6 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-quic/hysteria2" "github.com/sagernet/sing-quic/hysteria2"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/json/badoption"
) )
func TestHysteria2Self(t *testing.T) { func TestHysteria2Self(t *testing.T) {
@@ -30,13 +28,13 @@ func testHysteria2Self(t *testing.T, salamanderPassword string) {
} }
} }
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
Tag: "mixed-in", Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -45,7 +43,7 @@ func testHysteria2Self(t *testing.T, salamanderPassword string) {
Type: C.TypeHysteria2, Type: C.TypeHysteria2,
Hysteria2Options: option.Hysteria2InboundOptions{ Hysteria2Options: option.Hysteria2InboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
}, },
UpMbps: 100, UpMbps: 100,
@@ -117,12 +115,12 @@ func testHysteria2Self(t *testing.T, salamanderPassword string) {
func TestHysteria2Inbound(t *testing.T) { func TestHysteria2Inbound(t *testing.T) {
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org") caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeHysteria2, Type: C.TypeHysteria2,
Hysteria2Options: option.Hysteria2InboundOptions{ Hysteria2Options: option.Hysteria2InboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
}, },
Obfs: &option.Hysteria2Obfs{ Obfs: &option.Hysteria2Obfs{
@@ -169,12 +167,12 @@ func TestHysteria2Outbound(t *testing.T) {
}, },
}) })
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },

View File

@@ -6,20 +6,18 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/json/badoption"
) )
func TestHysteriaSelf(t *testing.T) { func TestHysteriaSelf(t *testing.T) {
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org") _, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
Tag: "mixed-in", Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -28,7 +26,7 @@ func TestHysteriaSelf(t *testing.T) {
Type: C.TypeHysteria, Type: C.TypeHysteria,
HysteriaOptions: option.HysteriaInboundOptions{ HysteriaOptions: option.HysteriaInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
}, },
UpMbps: 100, UpMbps: 100,
@@ -100,12 +98,12 @@ func TestHysteriaSelf(t *testing.T) {
func TestHysteriaInbound(t *testing.T) { func TestHysteriaInbound(t *testing.T) {
caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org") caPem, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeHysteria, Type: C.TypeHysteria,
HysteriaOptions: option.HysteriaInboundOptions{ HysteriaOptions: option.HysteriaInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
}, },
UpMbps: 100, UpMbps: 100,
@@ -151,12 +149,12 @@ func TestHysteriaOutbound(t *testing.T) {
}, },
}) })
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },

View File

@@ -7,21 +7,19 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-shadowsocks/shadowaead_2022" "github.com/sagernet/sing-shadowsocks/shadowaead_2022"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/json/badoption"
) )
func TestChainedInbound(t *testing.T) { func TestChainedInbound(t *testing.T) {
method := shadowaead_2022.List[0] method := shadowaead_2022.List[0]
password := mkBase64(t, 16) password := mkBase64(t, 16)
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
Tag: "mixed-in", Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -30,12 +28,10 @@ func TestChainedInbound(t *testing.T) {
Type: C.TypeShadowsocks, Type: C.TypeShadowsocks,
ShadowsocksOptions: option.ShadowsocksInboundOptions{ ShadowsocksOptions: option.ShadowsocksInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
InboundOptions: option.InboundOptions{
Detour: "detour", Detour: "detour",
}, },
},
Method: method, Method: method,
Password: password, Password: password,
}, },
@@ -45,7 +41,7 @@ func TestChainedInbound(t *testing.T) {
Tag: "detour", Tag: "detour",
ShadowsocksOptions: option.ShadowsocksInboundOptions{ ShadowsocksOptions: option.ShadowsocksInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: otherPort, ListenPort: otherPort,
}, },
Method: method, Method: method,

View File

@@ -7,8 +7,6 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/json/badoption"
"github.com/spyzhov/ajson" "github.com/spyzhov/ajson"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -39,12 +37,12 @@ func TestMuxCoolServer(t *testing.T) {
}) })
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeVMess, Type: C.TypeVMess,
VMessOptions: option.VMessInboundOptions{ VMessOptions: option.VMessInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
}, },
Users: []option.VMessUser{ Users: []option.VMessUser{
@@ -83,12 +81,12 @@ func TestMuxCoolClient(t *testing.T) {
}) })
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -114,13 +112,13 @@ func TestMuxCoolClient(t *testing.T) {
func TestMuxCoolSelf(t *testing.T) { func TestMuxCoolSelf(t *testing.T) {
user := newUUID() user := newUUID()
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
Tag: "mixed-in", Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -129,7 +127,7 @@ func TestMuxCoolSelf(t *testing.T) {
Type: C.TypeVMess, Type: C.TypeVMess,
VMessOptions: option.VMessInboundOptions{ VMessOptions: option.VMessInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
}, },
Users: []option.VMessUser{ Users: []option.VMessUser{

View File

@@ -7,8 +7,6 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-shadowsocks/shadowaead_2022" "github.com/sagernet/sing-shadowsocks/shadowaead_2022"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/json/badoption"
"github.com/gofrs/uuid/v5" "github.com/gofrs/uuid/v5"
) )
@@ -57,13 +55,13 @@ func testShadowsocksMux(t *testing.T, options option.OutboundMultiplexOptions) {
method := shadowaead_2022.List[0] method := shadowaead_2022.List[0]
password := mkBase64(t, 16) password := mkBase64(t, 16)
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
Tag: "mixed-in", Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -72,7 +70,7 @@ func testShadowsocksMux(t *testing.T, options option.OutboundMultiplexOptions) {
Type: C.TypeShadowsocks, Type: C.TypeShadowsocks,
ShadowsocksOptions: option.ShadowsocksInboundOptions{ ShadowsocksOptions: option.ShadowsocksInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
}, },
Method: method, Method: method,
@@ -127,13 +125,13 @@ func testShadowsocksMux(t *testing.T, options option.OutboundMultiplexOptions) {
func testVMessMux(t *testing.T, options option.OutboundMultiplexOptions) { func testVMessMux(t *testing.T, options option.OutboundMultiplexOptions) {
user, _ := uuid.NewV4() user, _ := uuid.NewV4()
startInstance(t, option.Options{ startInstance(t, option.Options{
LegacyInbounds: []option.LegacyInbound{ Inbounds: []option.LegacyInbound{
{ {
Type: C.TypeMixed, Type: C.TypeMixed,
Tag: "mixed-in", Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{ MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort, ListenPort: clientPort,
}, },
}, },
@@ -142,7 +140,7 @@ func testVMessMux(t *testing.T, options option.OutboundMultiplexOptions) {
Type: C.TypeVMess, Type: C.TypeVMess,
VMessOptions: option.VMessInboundOptions{ VMessOptions: option.VMessInboundOptions{
ListenOptions: option.ListenOptions{ ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort, ListenPort: serverPort,
}, },
Users: []option.VMessUser{ Users: []option.VMessUser{

Some files were not shown because too many files have changed in this diff Show More