Add set system proxy support for macOS

This commit is contained in:
世界 2022-08-04 22:01:20 +08:00
parent 64dbac8138
commit f691bd5ce1
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
21 changed files with 225 additions and 174 deletions

View file

@ -7,6 +7,7 @@ import (
"github.com/sagernet/sing-box/common/geoip" "github.com/sagernet/sing-box/common/geoip"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/control" "github.com/sagernet/sing/common/control"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@ -33,10 +34,9 @@ type Router interface {
InterfaceBindManager() control.BindManager InterfaceBindManager() control.BindManager
DefaultInterface() string DefaultInterface() string
AutoDetectInterface() bool AutoDetectInterface() bool
AutoDetectInterfaceName() string
AutoDetectInterfaceIndex() int
DefaultMark() int DefaultMark() int
NetworkMonitor() tun.NetworkUpdateMonitor
InterfaceMonitor() tun.DefaultInterfaceMonitor
Rules() []Rule Rules() []Rule
SetTrafficController(controller TrafficController) SetTrafficController(controller TrafficController)
} }

1
box.go
View file

@ -90,6 +90,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
logFactory.NewLogger("dns"), logFactory.NewLogger("dns"),
common.PtrValueOrDefault(options.Route), common.PtrValueOrDefault(options.Route),
common.PtrValueOrDefault(options.DNS), common.PtrValueOrDefault(options.DNS),
options.Inbounds,
) )
if err != nil { if err != nil {
return nil, E.Cause(err, "parse route options") return nil, E.Cause(err, "parse route options")

View file

@ -61,27 +61,27 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
var listener net.ListenConfig var listener net.ListenConfig
if options.BindInterface != "" { if options.BindInterface != "" {
warnBindInterfaceOnUnsupportedPlatform.Check() warnBindInterfaceOnUnsupportedPlatform.Check()
dialer.Control = control.Append(dialer.Control, control.BindToInterface(router.InterfaceBindManager(), options.BindInterface)) bindFunc := control.BindToInterface(router.InterfaceBindManager(), options.BindInterface)
listener.Control = control.Append(listener.Control, control.BindToInterface(router.InterfaceBindManager(), options.BindInterface)) dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
} else if router.AutoDetectInterface() { } else if router.AutoDetectInterface() {
if C.IsWindows { if C.IsWindows {
dialer.Control = control.Append(dialer.Control, control.BindToInterfaceIndexFunc(func() int { bindFunc := control.BindToInterfaceIndexFunc(func() int {
return router.AutoDetectInterfaceIndex() return router.InterfaceMonitor().DefaultInterfaceIndex()
})) })
listener.Control = control.Append(listener.Control, control.BindToInterfaceIndexFunc(func() int { dialer.Control = control.Append(dialer.Control, bindFunc)
return router.AutoDetectInterfaceIndex() listener.Control = control.Append(listener.Control, bindFunc)
}))
} else { } else {
dialer.Control = control.Append(dialer.Control, control.BindToInterfaceFunc(router.InterfaceBindManager(), func() string { bindFunc := control.BindToInterfaceFunc(router.InterfaceBindManager(), func() string {
return router.AutoDetectInterfaceName() return router.InterfaceMonitor().DefaultInterfaceName()
})) })
listener.Control = control.Append(listener.Control, control.BindToInterfaceFunc(router.InterfaceBindManager(), func() string { dialer.Control = control.Append(dialer.Control, bindFunc)
return router.AutoDetectInterfaceName() listener.Control = control.Append(listener.Control, bindFunc)
}))
} }
} else if router.DefaultInterface() != "" { } else if router.DefaultInterface() != "" {
dialer.Control = control.Append(dialer.Control, control.BindToInterface(router.InterfaceBindManager(), router.DefaultInterface())) bindFunc := control.BindToInterface(router.InterfaceBindManager(), router.DefaultInterface())
listener.Control = control.Append(listener.Control, control.BindToInterface(router.InterfaceBindManager(), router.DefaultInterface())) dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
} }
if options.RoutingMark != 0 { if options.RoutingMark != 0 {
warnRoutingMarkOnUnsupportedPlatform.Check() warnRoutingMarkOnUnsupportedPlatform.Check()

View file

@ -13,3 +13,9 @@ func runCommand(name string, args ...string) error {
command.Stderr = os.Stderr command.Stderr = os.Stderr
return command.Run() return command.Run()
} }
func readCommand(name string, args ...string) ([]byte, error) {
command := exec.Command(name, args...)
command.Env = os.Environ()
return command.CombinedOutput()
}

View file

@ -4,6 +4,7 @@ import (
"os" "os"
"strings" "strings"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
) )
@ -30,10 +31,12 @@ func runAndroidShell(name string, args ...string) error {
} }
} }
func ClearSystemProxy() error { func SetSystemProxy(router adapter.Router, port uint16, isMixed bool) (func() error, error) {
return runAndroidShell("settings", "put", "global", "http_proxy", ":0") err := runAndroidShell("settings", "put", "global", "http_proxy", F.ToString("127.0.0.1:", port))
} if err != nil {
return nil, err
func SetSystemProxy(port uint16, mixed bool) error { }
return runAndroidShell("settings", "put", "global", "http_proxy", F.ToString("127.0.0.1:", port)) return func() error {
return runAndroidShell("settings", "put", "global", "http_proxy", ":0")
}, nil
} }

View file

@ -0,0 +1,98 @@
package settings
import (
"strings"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-tun"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/x/list"
)
type systemProxy struct {
monitor tun.DefaultInterfaceMonitor
interfaceName string
element *list.Element[tun.DefaultInterfaceUpdateCallback]
port uint16
isMixed bool
}
func (p *systemProxy) update() error {
newInterfaceName := p.monitor.DefaultInterfaceName()
if p.interfaceName == newInterfaceName {
return nil
}
if p.interfaceName != "" {
_ = p.unset()
}
p.interfaceName = newInterfaceName
interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName)
if err != nil {
return err
}
if p.isMixed {
err = runCommand("networksetup", "-setsocksfirewallproxy", interfaceDisplayName, "127.0.0.1", F.ToString(p.port))
}
if err == nil {
err = runCommand("networksetup", "-setwebproxy", interfaceDisplayName, "127.0.0.1", F.ToString(p.port))
}
if err == nil {
err = runCommand("networksetup", "-setsecurewebproxy", interfaceDisplayName, "127.0.0.1", F.ToString(p.port))
}
return err
}
func (p *systemProxy) unset() error {
interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName)
if err != nil {
return err
}
if p.isMixed {
err = runCommand("networksetup", "-setsocksfirewallproxystate", interfaceDisplayName, "off")
}
if err == nil {
err = runCommand("networksetup", "-setwebproxystate", interfaceDisplayName, "off")
}
if err == nil {
err = runCommand("networksetup", "-setsecurewebproxystate", interfaceDisplayName, "off")
}
return err
}
func getInterfaceDisplayName(name string) (string, error) {
content, err := readCommand("networksetup", "-listallhardwareports")
if err != nil {
return "", err
}
for _, deviceSpan := range strings.Split(string(content), "Ethernet Address") {
if strings.Contains(deviceSpan, "Device: "+name) {
substr := "Hardware Port: "
deviceSpan = deviceSpan[strings.Index(deviceSpan, substr)+len(substr):]
deviceSpan = deviceSpan[:strings.Index(deviceSpan, "\n")]
return deviceSpan, nil
}
}
return "", E.New(name, " not found in networksetup -listallhardwareports")
}
func SetSystemProxy(router adapter.Router, port uint16, isMixed bool) (func() error, error) {
interfaceMonitor := router.InterfaceMonitor()
if interfaceMonitor == nil {
return nil, E.New("missing interface monitor")
}
proxy := &systemProxy{
monitor: interfaceMonitor,
port: port,
isMixed: isMixed,
}
err := proxy.update()
if err != nil {
return nil, err
}
proxy.element = interfaceMonitor.RegisterCallback(proxy.update)
return func() error {
interfaceMonitor.UnregisterCallback(proxy.element)
return proxy.unset()
}, nil
}

View file

@ -7,7 +7,7 @@ import (
"os/exec" "os/exec"
"strings" "strings"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing/common" "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"
@ -35,42 +35,33 @@ func runAsUser(name string, args ...string) error {
} }
} }
func ClearSystemProxy() error { func SetSystemProxy(router adapter.Router, port uint16, isMixed bool) (func() error, error) {
if hasGSettings { if !hasGSettings {
return runAsUser("gsettings", "set", "org.gnome.system.proxy", "mode", "none") return nil, E.New("unsupported desktop environment")
} }
return nil err := runAsUser("gsettings", "set", "org.gnome.system.proxy.http", "enabled", "true")
} if err != nil {
return nil, err
func SetSystemProxy(port uint16, mixed bool) error { }
if hasGSettings { if isMixed {
err := runAsUser("gsettings", "set", "org.gnome.system.proxy.http", "enabled", "true") err = setGnomeProxy(port, "ftp", "http", "https", "socks")
if err != nil {
return err
}
if mixed {
err = setGnomeProxy(port, "ftp", "http", "https", "socks")
if err != nil {
return err
}
} else {
err = setGnomeProxy(port, "http", "https")
if err != nil {
return err
}
}
err = runAsUser("gsettings", "set", "org.gnome.system.proxy", "use-same-proxy", F.ToString(mixed))
if err != nil {
return err
}
err = runAsUser("gsettings", "set", "org.gnome.system.proxy", "mode", "manual")
if err != nil {
return err
}
} else { } else {
log.Warn("set system proxy: unsupported desktop environment") err = setGnomeProxy(port, "http", "https")
} }
return nil if err != nil {
return nil, err
}
err = runAsUser("gsettings", "set", "org.gnome.system.proxy", "use-same-proxy", F.ToString(isMixed))
if err != nil {
return nil, err
}
err = runAsUser("gsettings", "set", "org.gnome.system.proxy", "mode", "manual")
if err != nil {
return nil, err
}
return func() error {
return runAsUser("gsettings", "set", "org.gnome.system.proxy", "mode", "none")
}, nil
} }
func setGnomeProxy(port uint16, proxyTypes ...string) error { func setGnomeProxy(port uint16, proxyTypes ...string) error {

View file

@ -1,14 +1,13 @@
//go:build !windows && !linux //go:build !(windows || linux || darwin)
package settings package settings
import "github.com/sagernet/sing-box/log" import (
"os"
func ClearSystemProxy() error { "github.com/sagernet/sing-box/adapter"
return nil )
}
func SetSystemProxy(port uint16, mixed bool) error { func SetSystemProxy(router adapter.Router, port uint16, isMixed bool) (func() error, error) {
log.Warn("set system proxy: unsupported operating system") return nil, os.ErrInvalid
return nil
} }

View file

@ -1,14 +1,17 @@
package settings package settings
import ( import (
"github.com/sagernet/sing-box/adapter"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/wininet" "github.com/sagernet/sing/common/wininet"
) )
func ClearSystemProxy() error { func SetSystemProxy(router adapter.Router, port uint16, isMixed bool) (func() error, error) {
return wininet.ClearSystemProxy() err := wininet.SetSystemProxy(F.ToString("http://127.0.0.1:", port), "local")
} if err != nil {
return nil, err
func SetSystemProxy(port uint16, mixed bool) error { }
return wininet.SetSystemProxy(F.ToString("http://127.0.0.1:", port), "local") return func() error {
return wininet.ClearSystemProxy()
}, nil
} }

View file

@ -71,7 +71,7 @@ If `sniff_override_destination` is in effect, its value will be taken as a fallb
!!! error "" !!! error ""
Windows only Only supported on Linux, Android, Windows, and macOS.
Automatically set system proxy configuration when start and clean up when stop. Automatically set system proxy configuration when start and clean up when stop.

View file

@ -71,7 +71,7 @@ If `sniff_override_destination` is in effect, its value will be taken as a fallb
!!! error "" !!! error ""
Windows only Only supported on Linux, Android, Windows, and macOS.
Automatically set system proxy configuration when start and clean up when stop. Automatically set system proxy configuration when start and clean up when stop.

View file

@ -30,7 +30,7 @@ Default outbound tag. the first outbound will be used if empty.
!!! error "" !!! error ""
Linux and Windows only Only supported on Linux and Windows.
Bind outbound connections to the default NIC by default to prevent routing loops under Tun. Bind outbound connections to the default NIC by default to prevent routing loops under Tun.
@ -40,7 +40,7 @@ Takes no effect if `outbound.bind_interface` is set.
!!! error "" !!! error ""
Linux and Windows only Only supported on Linux and Windows.
Bind outbound connections to the specified NIC by default to prevent routing loops under Tun. Bind outbound connections to the specified NIC by default to prevent routing loops under Tun.
@ -50,7 +50,7 @@ Takes no effect if `auto_detect_interface` is set.
!!! error "" !!! error ""
Linux only Only supported on Linux.
Set iptables routing mark by default. Set iptables routing mark by default.

4
go.mod
View file

@ -16,7 +16,7 @@ require (
github.com/sagernet/sing v0.0.0-20220804023557-9c64b40e7050 github.com/sagernet/sing v0.0.0-20220804023557-9c64b40e7050
github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91 github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91
github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1 github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1
github.com/sagernet/sing-tun v0.0.0-20220803112223-a8fd6450d4ed github.com/sagernet/sing-tun v0.0.0-20220804154459-7ee0d19103d2
github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2 github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2
github.com/spf13/cobra v1.5.0 github.com/spf13/cobra v1.5.0
github.com/stretchr/testify v1.8.0 github.com/stretchr/testify v1.8.0
@ -24,7 +24,7 @@ require (
go.uber.org/atomic v1.9.0 go.uber.org/atomic v1.9.0
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b
golang.org/x/sys v0.0.0-20220731174439-a90be440212d golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704
) )
require ( require (

8
go.sum
View file

@ -155,8 +155,8 @@ github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91 h1:jxt2PYixIkK2i
github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91/go.mod h1:T77zZdE2Cm6VqnFumrpwsq+kxYsbq+vWDhmjtdSl/oM= github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91/go.mod h1:T77zZdE2Cm6VqnFumrpwsq+kxYsbq+vWDhmjtdSl/oM=
github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1 h1:RYvOc69eSNMN0dwVugrDts41Nn7Ar/C/n/fvytvFcp4= github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1 h1:RYvOc69eSNMN0dwVugrDts41Nn7Ar/C/n/fvytvFcp4=
github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1/go.mod h1:NqZjiXszgVCMQ4gVDa2V+drhS8NMfGqUqDF86EacEFc= github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1/go.mod h1:NqZjiXszgVCMQ4gVDa2V+drhS8NMfGqUqDF86EacEFc=
github.com/sagernet/sing-tun v0.0.0-20220803112223-a8fd6450d4ed h1:28qeqeuHLZEkzdcZjYwcCn8y4ckyKimaP+L4P25dqUo= github.com/sagernet/sing-tun v0.0.0-20220804154459-7ee0d19103d2 h1:har8hmVNhGxp14zLNAoGrgfzgxZQn0KTYJDfJudj0RU=
github.com/sagernet/sing-tun v0.0.0-20220803112223-a8fd6450d4ed/go.mod h1:jNlPidQzZYkpmpQJ+sDN2YGrPsL4QImoqBpuauId9po= github.com/sagernet/sing-tun v0.0.0-20220804154459-7ee0d19103d2/go.mod h1:K1Hfxaa/1zsxZix3ats3k1TJftVwK0l4OoRnUjjhi0g=
github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2 h1:C8sc2MYiNx0O7uQ0nieJWq5qYeIHj20XHFWPlcgoQeY= github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2 h1:C8sc2MYiNx0O7uQ0nieJWq5qYeIHj20XHFWPlcgoQeY=
github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2/go.mod h1:bNXBqSWYaG3ePl6u0xQY5zneE+ZKa3683ZpuE8S1M1w= github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2/go.mod h1:bNXBqSWYaG3ePl6u0xQY5zneE+ZKa3683ZpuE8S1M1w=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
@ -279,8 +279,8 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d h1:Sv5ogFZatcgIMMtBSTTAgMYsicp25MXBubjXNDKwm80= golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704 h1:Y7NOhdqIOU8kYI7BxsgL38d0ot0raxvcW+EMQU2QrT4=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
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=

View file

@ -40,7 +40,8 @@ type myInboundAdapter struct {
// http mixed // http mixed
setSystemProxy bool setSystemProxy bool
clearSystemProxy func() error
// internal // internal
@ -60,10 +61,10 @@ func (a *myInboundAdapter) Tag() string {
} }
func (a *myInboundAdapter) Start() error { func (a *myInboundAdapter) Start() error {
var err error
bindAddr := M.SocksaddrFrom(netip.Addr(a.listenOptions.Listen), a.listenOptions.ListenPort) bindAddr := M.SocksaddrFrom(netip.Addr(a.listenOptions.Listen), a.listenOptions.ListenPort)
if common.Contains(a.network, N.NetworkTCP) { if common.Contains(a.network, N.NetworkTCP) {
var tcpListener *net.TCPListener var tcpListener *net.TCPListener
var err error
if !a.listenOptions.TCPFastOpen { if !a.listenOptions.TCPFastOpen {
tcpListener, err = net.ListenTCP(M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.TCPAddr()) tcpListener, err = net.ListenTCP(M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.TCPAddr())
} else { } else {
@ -77,7 +78,8 @@ func (a *myInboundAdapter) Start() error {
a.logger.Info("tcp server started at ", tcpListener.Addr()) a.logger.Info("tcp server started at ", tcpListener.Addr())
} }
if common.Contains(a.network, N.NetworkUDP) { if common.Contains(a.network, N.NetworkUDP) {
udpConn, err := net.ListenUDP(M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.UDPAddr()) var udpConn *net.UDPConn
udpConn, err = net.ListenUDP(M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.UDPAddr())
if err != nil { if err != nil {
return err return err
} }
@ -101,7 +103,7 @@ func (a *myInboundAdapter) Start() error {
a.logger.Info("udp server started at ", udpConn.LocalAddr()) a.logger.Info("udp server started at ", udpConn.LocalAddr())
} }
if a.setSystemProxy { if a.setSystemProxy {
err := settings.SetSystemProxy(M.SocksaddrFromNet(a.tcpListener.Addr()).Port, a.protocol == C.TypeMixed) a.clearSystemProxy, err = settings.SetSystemProxy(a.router, M.SocksaddrFromNet(a.tcpListener.Addr()).Port, a.protocol == C.TypeMixed)
if err != nil { if err != nil {
return E.Cause(err, "set system proxy") return E.Cause(err, "set system proxy")
} }
@ -111,8 +113,8 @@ func (a *myInboundAdapter) Start() error {
func (a *myInboundAdapter) Close() error { func (a *myInboundAdapter) Close() error {
var err error var err error
if a.setSystemProxy { if a.clearSystemProxy != nil {
err = settings.ClearSystemProxy() err = a.clearSystemProxy()
} }
return E.Errors(err, common.Close( return E.Errors(err, common.Close(
common.PtrOrNil(a.tcpListener), common.PtrOrNil(a.tcpListener),

View file

@ -1,22 +0,0 @@
package route
import "github.com/sagernet/sing/common/x/list"
type (
NetworkUpdateCallback = func() error
DefaultInterfaceUpdateCallback = func()
)
type NetworkUpdateMonitor interface {
Start() error
Close() error
RegisterCallback(callback NetworkUpdateCallback) *list.Element[NetworkUpdateCallback]
UnregisterCallback(element *list.Element[NetworkUpdateCallback])
}
type DefaultInterfaceMonitor interface {
Start() error
Close() error
DefaultInterfaceName() string
DefaultInterfaceIndex() int
}

View file

@ -1,17 +0,0 @@
//go:build !(linux || windows) || no_gvisor
package route
import (
"os"
E "github.com/sagernet/sing/common/exceptions"
)
func NewNetworkUpdateMonitor(errorHandler E.Handler) (NetworkUpdateMonitor, error) {
return nil, os.ErrInvalid
}
func NewDefaultInterfaceMonitor(networkMonitor NetworkUpdateMonitor, callback DefaultInterfaceUpdateCallback) (DefaultInterfaceMonitor, error) {
return nil, os.ErrInvalid
}

View file

@ -1,16 +0,0 @@
//go:build (linux || windows) && !no_gvisor
package route
import (
"github.com/sagernet/sing-tun"
E "github.com/sagernet/sing/common/exceptions"
)
func NewNetworkUpdateMonitor(errorHandler E.Handler) (NetworkUpdateMonitor, error) {
return tun.NewNetworkUpdateMonitor(errorHandler)
}
func NewDefaultInterfaceMonitor(networkMonitor NetworkUpdateMonitor, callback DefaultInterfaceUpdateCallback) (DefaultInterfaceMonitor, error) {
return tun.NewDefaultInterfaceMonitor(networkMonitor, callback)
}

View file

@ -25,6 +25,7 @@ import (
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"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/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
@ -83,16 +84,16 @@ type Router struct {
transports []dns.Transport transports []dns.Transport
transportMap map[string]dns.Transport transportMap map[string]dns.Transport
interfaceBindManager control.BindManager interfaceBindManager control.BindManager
networkMonitor NetworkUpdateMonitor
autoDetectInterface bool autoDetectInterface bool
defaultInterface string defaultInterface string
interfaceMonitor DefaultInterfaceMonitor
defaultMark int defaultMark int
networkMonitor tun.NetworkUpdateMonitor
interfaceMonitor tun.DefaultInterfaceMonitor
trafficController adapter.TrafficController trafficController adapter.TrafficController
processSearcher process.Searcher processSearcher process.Searcher
} }
func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.ContextLogger, options option.RouteOptions, dnsOptions option.DNSOptions) (*Router, error) { func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.ContextLogger, options option.RouteOptions, dnsOptions option.DNSOptions, inbounds []option.Inbound) (*Router, error) {
if options.DefaultInterface != "" { if options.DefaultInterface != "" {
warnDefaultInterfaceOnUnsupportedPlatform.Check() warnDefaultInterfaceOnUnsupportedPlatform.Check()
} }
@ -231,8 +232,13 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
router.transports = transports router.transports = transports
router.transportMap = transportMap router.transportMap = transportMap
if router.interfaceBindManager != nil || options.AutoDetectInterface { needInterfaceMonitor := options.AutoDetectInterface ||
networkMonitor, err := NewNetworkUpdateMonitor(router) C.IsDarwin && common.Any(inbounds, func(inbound option.Inbound) bool {
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy
})
if router.interfaceBindManager != nil || needInterfaceMonitor {
networkMonitor, err := tun.NewNetworkUpdateMonitor(router)
if err == nil { if err == nil {
router.networkMonitor = networkMonitor router.networkMonitor = networkMonitor
if router.interfaceBindManager != nil { if router.interfaceBindManager != nil {
@ -241,15 +247,18 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
} }
} }
if router.networkMonitor != nil && options.AutoDetectInterface { if router.networkMonitor != nil && needInterfaceMonitor {
interfaceMonitor, err := NewDefaultInterfaceMonitor(router.networkMonitor, func() { interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor)
router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(), ", index ", router.interfaceMonitor.DefaultInterfaceIndex())
})
if err != nil { if err != nil {
return nil, E.New("auto_detect_interface unsupported on current platform") return nil, E.New("auto_detect_interface unsupported on current platform")
} }
interfaceMonitor.RegisterCallback(func() error {
router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(), ", index ", router.interfaceMonitor.DefaultInterfaceIndex())
return nil
})
router.interfaceMonitor = interfaceMonitor router.interfaceMonitor = interfaceMonitor
} }
if hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess { if hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess {
searcher, err := process.NewSearcher(logger) searcher, err := process.NewSearcher(logger)
if err != nil { if err != nil {
@ -648,20 +657,6 @@ func (r *Router) DefaultInterface() string {
return r.defaultInterface return r.defaultInterface
} }
func (r *Router) AutoDetectInterfaceName() string {
if r.interfaceMonitor == nil {
return ""
}
return r.interfaceMonitor.DefaultInterfaceName()
}
func (r *Router) AutoDetectInterfaceIndex() int {
if r.interfaceMonitor == nil {
return -1
}
return r.interfaceMonitor.DefaultInterfaceIndex()
}
func (r *Router) DefaultMark() int { func (r *Router) DefaultMark() int {
return r.defaultMark return r.defaultMark
} }
@ -670,6 +665,14 @@ func (r *Router) Rules() []adapter.Rule {
return r.rules return r.rules
} }
func (r *Router) NetworkMonitor() tun.NetworkUpdateMonitor {
return r.networkMonitor
}
func (r *Router) InterfaceMonitor() tun.DefaultInterfaceMonitor {
return r.interfaceMonitor
}
func (r *Router) SetTrafficController(controller adapter.TrafficController) { func (r *Router) SetTrafficController(controller adapter.TrafficController) {
r.trafficController = controller r.trafficController = controller
} }

View file

@ -54,7 +54,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sagernet/netlink v0.0.0-20220803045538-bdac49abf805 // indirect github.com/sagernet/netlink v0.0.0-20220803045538-bdac49abf805 // indirect
github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91 // indirect github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91 // indirect
github.com/sagernet/sing-tun v0.0.0-20220803112223-a8fd6450d4ed // indirect github.com/sagernet/sing-tun v0.0.0-20220804154459-7ee0d19103d2 // indirect
github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2 // indirect github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect github.com/sirupsen/logrus v1.8.1 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
@ -62,7 +62,7 @@ require (
go.uber.org/atomic v1.9.0 // indirect go.uber.org/atomic v1.9.0 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/mod v0.5.1 // indirect golang.org/x/mod v0.5.1 // indirect
golang.org/x/sys v0.0.0-20220731174439-a90be440212d // indirect golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/tools v0.1.9 // indirect golang.org/x/tools v0.1.9 // indirect

View file

@ -180,8 +180,8 @@ github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91 h1:jxt2PYixIkK2i
github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91/go.mod h1:T77zZdE2Cm6VqnFumrpwsq+kxYsbq+vWDhmjtdSl/oM= github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91/go.mod h1:T77zZdE2Cm6VqnFumrpwsq+kxYsbq+vWDhmjtdSl/oM=
github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1 h1:RYvOc69eSNMN0dwVugrDts41Nn7Ar/C/n/fvytvFcp4= github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1 h1:RYvOc69eSNMN0dwVugrDts41Nn7Ar/C/n/fvytvFcp4=
github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1/go.mod h1:NqZjiXszgVCMQ4gVDa2V+drhS8NMfGqUqDF86EacEFc= github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1/go.mod h1:NqZjiXszgVCMQ4gVDa2V+drhS8NMfGqUqDF86EacEFc=
github.com/sagernet/sing-tun v0.0.0-20220803112223-a8fd6450d4ed h1:28qeqeuHLZEkzdcZjYwcCn8y4ckyKimaP+L4P25dqUo= github.com/sagernet/sing-tun v0.0.0-20220804154459-7ee0d19103d2 h1:har8hmVNhGxp14zLNAoGrgfzgxZQn0KTYJDfJudj0RU=
github.com/sagernet/sing-tun v0.0.0-20220803112223-a8fd6450d4ed/go.mod h1:jNlPidQzZYkpmpQJ+sDN2YGrPsL4QImoqBpuauId9po= github.com/sagernet/sing-tun v0.0.0-20220804154459-7ee0d19103d2/go.mod h1:K1Hfxaa/1zsxZix3ats3k1TJftVwK0l4OoRnUjjhi0g=
github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2 h1:C8sc2MYiNx0O7uQ0nieJWq5qYeIHj20XHFWPlcgoQeY= github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2 h1:C8sc2MYiNx0O7uQ0nieJWq5qYeIHj20XHFWPlcgoQeY=
github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2/go.mod h1:bNXBqSWYaG3ePl6u0xQY5zneE+ZKa3683ZpuE8S1M1w= github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2/go.mod h1:bNXBqSWYaG3ePl6u0xQY5zneE+ZKa3683ZpuE8S1M1w=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
@ -314,8 +314,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d h1:Sv5ogFZatcgIMMtBSTTAgMYsicp25MXBubjXNDKwm80= golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704 h1:Y7NOhdqIOU8kYI7BxsgL38d0ot0raxvcW+EMQU2QrT4=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
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=