From f691bd5ce1be30583cc454c2df268dc208d6c5eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 4 Aug 2022 22:01:20 +0800 Subject: [PATCH] Add set system proxy support for macOS --- adapter/router.go | 6 +- box.go | 1 + common/dialer/default.go | 32 +++++----- common/settings/command.go | 6 ++ common/settings/proxy_android.go | 15 +++-- common/settings/proxy_darwin.go | 98 +++++++++++++++++++++++++++++ common/settings/proxy_linux.go | 59 ++++++++--------- common/settings/proxy_stub.go | 15 +++-- common/settings/proxy_windows.go | 15 +++-- docs/configuration/inbound/http.md | 2 +- docs/configuration/inbound/mixed.md | 2 +- docs/configuration/route/index.md | 6 +- go.mod | 4 +- go.sum | 8 +-- inbound/default.go | 14 +++-- route/iface.go | 22 ------- route/iface_stub.go | 17 ----- route/iface_tun.go | 16 ----- route/router.go | 49 ++++++++------- test/go.mod | 4 +- test/go.sum | 8 +-- 21 files changed, 225 insertions(+), 174 deletions(-) create mode 100644 common/settings/proxy_darwin.go delete mode 100644 route/iface.go delete mode 100644 route/iface_stub.go delete mode 100644 route/iface_tun.go diff --git a/adapter/router.go b/adapter/router.go index e6744822..91cf7023 100644 --- a/adapter/router.go +++ b/adapter/router.go @@ -7,6 +7,7 @@ import ( "github.com/sagernet/sing-box/common/geoip" "github.com/sagernet/sing-dns" + "github.com/sagernet/sing-tun" "github.com/sagernet/sing/common/control" N "github.com/sagernet/sing/common/network" @@ -33,10 +34,9 @@ type Router interface { InterfaceBindManager() control.BindManager DefaultInterface() string AutoDetectInterface() bool - AutoDetectInterfaceName() string - AutoDetectInterfaceIndex() int DefaultMark() int - + NetworkMonitor() tun.NetworkUpdateMonitor + InterfaceMonitor() tun.DefaultInterfaceMonitor Rules() []Rule SetTrafficController(controller TrafficController) } diff --git a/box.go b/box.go index af92d01c..0475e427 100644 --- a/box.go +++ b/box.go @@ -90,6 +90,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) { logFactory.NewLogger("dns"), common.PtrValueOrDefault(options.Route), common.PtrValueOrDefault(options.DNS), + options.Inbounds, ) if err != nil { return nil, E.Cause(err, "parse route options") diff --git a/common/dialer/default.go b/common/dialer/default.go index efe7d574..5c0fc772 100644 --- a/common/dialer/default.go +++ b/common/dialer/default.go @@ -61,27 +61,27 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia var listener net.ListenConfig if options.BindInterface != "" { warnBindInterfaceOnUnsupportedPlatform.Check() - dialer.Control = control.Append(dialer.Control, control.BindToInterface(router.InterfaceBindManager(), options.BindInterface)) - listener.Control = control.Append(listener.Control, control.BindToInterface(router.InterfaceBindManager(), options.BindInterface)) + bindFunc := control.BindToInterface(router.InterfaceBindManager(), options.BindInterface) + dialer.Control = control.Append(dialer.Control, bindFunc) + listener.Control = control.Append(listener.Control, bindFunc) } else if router.AutoDetectInterface() { if C.IsWindows { - dialer.Control = control.Append(dialer.Control, control.BindToInterfaceIndexFunc(func() int { - return router.AutoDetectInterfaceIndex() - })) - listener.Control = control.Append(listener.Control, control.BindToInterfaceIndexFunc(func() int { - return router.AutoDetectInterfaceIndex() - })) + bindFunc := control.BindToInterfaceIndexFunc(func() int { + return router.InterfaceMonitor().DefaultInterfaceIndex() + }) + dialer.Control = control.Append(dialer.Control, bindFunc) + listener.Control = control.Append(listener.Control, bindFunc) } else { - dialer.Control = control.Append(dialer.Control, control.BindToInterfaceFunc(router.InterfaceBindManager(), func() string { - return router.AutoDetectInterfaceName() - })) - listener.Control = control.Append(listener.Control, control.BindToInterfaceFunc(router.InterfaceBindManager(), func() string { - return router.AutoDetectInterfaceName() - })) + bindFunc := control.BindToInterfaceFunc(router.InterfaceBindManager(), func() string { + return router.InterfaceMonitor().DefaultInterfaceName() + }) + dialer.Control = control.Append(dialer.Control, bindFunc) + listener.Control = control.Append(listener.Control, bindFunc) } } else if router.DefaultInterface() != "" { - dialer.Control = control.Append(dialer.Control, control.BindToInterface(router.InterfaceBindManager(), router.DefaultInterface())) - listener.Control = control.Append(listener.Control, control.BindToInterface(router.InterfaceBindManager(), router.DefaultInterface())) + bindFunc := control.BindToInterface(router.InterfaceBindManager(), router.DefaultInterface()) + dialer.Control = control.Append(dialer.Control, bindFunc) + listener.Control = control.Append(listener.Control, bindFunc) } if options.RoutingMark != 0 { warnRoutingMarkOnUnsupportedPlatform.Check() diff --git a/common/settings/command.go b/common/settings/command.go index 485d0d89..0ba88ddb 100644 --- a/common/settings/command.go +++ b/common/settings/command.go @@ -13,3 +13,9 @@ func runCommand(name string, args ...string) error { command.Stderr = os.Stderr return command.Run() } + +func readCommand(name string, args ...string) ([]byte, error) { + command := exec.Command(name, args...) + command.Env = os.Environ() + return command.CombinedOutput() +} diff --git a/common/settings/proxy_android.go b/common/settings/proxy_android.go index 2efce080..16c3005f 100644 --- a/common/settings/proxy_android.go +++ b/common/settings/proxy_android.go @@ -4,6 +4,7 @@ import ( "os" "strings" + "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" F "github.com/sagernet/sing/common/format" ) @@ -30,10 +31,12 @@ func runAndroidShell(name string, args ...string) error { } } -func ClearSystemProxy() error { - return runAndroidShell("settings", "put", "global", "http_proxy", ":0") -} - -func SetSystemProxy(port uint16, mixed bool) error { - return runAndroidShell("settings", "put", "global", "http_proxy", F.ToString("127.0.0.1:", port)) +func SetSystemProxy(router adapter.Router, port uint16, isMixed bool) (func() error, error) { + err := runAndroidShell("settings", "put", "global", "http_proxy", F.ToString("127.0.0.1:", port)) + if err != nil { + return nil, err + } + return func() error { + return runAndroidShell("settings", "put", "global", "http_proxy", ":0") + }, nil } diff --git a/common/settings/proxy_darwin.go b/common/settings/proxy_darwin.go new file mode 100644 index 00000000..4b27e053 --- /dev/null +++ b/common/settings/proxy_darwin.go @@ -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 +} diff --git a/common/settings/proxy_linux.go b/common/settings/proxy_linux.go index d258667e..decfe443 100644 --- a/common/settings/proxy_linux.go +++ b/common/settings/proxy_linux.go @@ -7,7 +7,7 @@ import ( "os/exec" "strings" - "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" @@ -35,42 +35,33 @@ func runAsUser(name string, args ...string) error { } } -func ClearSystemProxy() error { - if hasGSettings { - return runAsUser("gsettings", "set", "org.gnome.system.proxy", "mode", "none") +func SetSystemProxy(router adapter.Router, port uint16, isMixed bool) (func() error, error) { + if !hasGSettings { + return nil, E.New("unsupported desktop environment") } - return nil -} - -func SetSystemProxy(port uint16, mixed bool) error { - if hasGSettings { - err := runAsUser("gsettings", "set", "org.gnome.system.proxy.http", "enabled", "true") - 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 - } + err := runAsUser("gsettings", "set", "org.gnome.system.proxy.http", "enabled", "true") + if err != nil { + return nil, err + } + if isMixed { + err = setGnomeProxy(port, "ftp", "http", "https", "socks") } 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 { diff --git a/common/settings/proxy_stub.go b/common/settings/proxy_stub.go index a0418837..08ed0186 100644 --- a/common/settings/proxy_stub.go +++ b/common/settings/proxy_stub.go @@ -1,14 +1,13 @@ -//go:build !windows && !linux +//go:build !(windows || linux || darwin) package settings -import "github.com/sagernet/sing-box/log" +import ( + "os" -func ClearSystemProxy() error { - return nil -} + "github.com/sagernet/sing-box/adapter" +) -func SetSystemProxy(port uint16, mixed bool) error { - log.Warn("set system proxy: unsupported operating system") - return nil +func SetSystemProxy(router adapter.Router, port uint16, isMixed bool) (func() error, error) { + return nil, os.ErrInvalid } diff --git a/common/settings/proxy_windows.go b/common/settings/proxy_windows.go index f5fbca14..267670bb 100644 --- a/common/settings/proxy_windows.go +++ b/common/settings/proxy_windows.go @@ -1,14 +1,17 @@ package settings import ( + "github.com/sagernet/sing-box/adapter" F "github.com/sagernet/sing/common/format" "github.com/sagernet/sing/common/wininet" ) -func ClearSystemProxy() error { - return wininet.ClearSystemProxy() -} - -func SetSystemProxy(port uint16, mixed bool) error { - return wininet.SetSystemProxy(F.ToString("http://127.0.0.1:", port), "local") +func SetSystemProxy(router adapter.Router, port uint16, isMixed bool) (func() error, error) { + err := wininet.SetSystemProxy(F.ToString("http://127.0.0.1:", port), "local") + if err != nil { + return nil, err + } + return func() error { + return wininet.ClearSystemProxy() + }, nil } diff --git a/docs/configuration/inbound/http.md b/docs/configuration/inbound/http.md index dd3b543b..bfc1a653 100644 --- a/docs/configuration/inbound/http.md +++ b/docs/configuration/inbound/http.md @@ -71,7 +71,7 @@ If `sniff_override_destination` is in effect, its value will be taken as a fallb !!! error "" - Windows only + Only supported on Linux, Android, Windows, and macOS. Automatically set system proxy configuration when start and clean up when stop. diff --git a/docs/configuration/inbound/mixed.md b/docs/configuration/inbound/mixed.md index 192434c3..95668bb6 100644 --- a/docs/configuration/inbound/mixed.md +++ b/docs/configuration/inbound/mixed.md @@ -71,7 +71,7 @@ If `sniff_override_destination` is in effect, its value will be taken as a fallb !!! error "" - Windows only + Only supported on Linux, Android, Windows, and macOS. Automatically set system proxy configuration when start and clean up when stop. diff --git a/docs/configuration/route/index.md b/docs/configuration/route/index.md index 31dd3928..5db96fd0 100644 --- a/docs/configuration/route/index.md +++ b/docs/configuration/route/index.md @@ -30,7 +30,7 @@ Default outbound tag. the first outbound will be used if empty. !!! 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. @@ -40,7 +40,7 @@ Takes no effect if `outbound.bind_interface` is set. !!! 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. @@ -50,7 +50,7 @@ Takes no effect if `auto_detect_interface` is set. !!! error "" - Linux only + Only supported on Linux. Set iptables routing mark by default. diff --git a/go.mod b/go.mod index 4539532b..d5a8968a 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/sagernet/sing v0.0.0-20220804023557-9c64b40e7050 github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91 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/spf13/cobra v1.5.0 github.com/stretchr/testify v1.8.0 @@ -24,7 +24,7 @@ require ( go.uber.org/atomic v1.9.0 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa 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 ( diff --git a/go.sum b/go.sum index 00e459b6..905c66cc 100644 --- a/go.sum +++ b/go.sum @@ -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-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-tun v0.0.0-20220803112223-a8fd6450d4ed h1:28qeqeuHLZEkzdcZjYwcCn8y4ckyKimaP+L4P25dqUo= -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 h1:har8hmVNhGxp14zLNAoGrgfzgxZQn0KTYJDfJudj0RU= +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/go.mod h1:bNXBqSWYaG3ePl6u0xQY5zneE+ZKa3683ZpuE8S1M1w= 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-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-20220731174439-a90be440212d h1:Sv5ogFZatcgIMMtBSTTAgMYsicp25MXBubjXNDKwm80= -golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704 h1:Y7NOhdqIOU8kYI7BxsgL38d0ot0raxvcW+EMQU2QrT4= +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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/inbound/default.go b/inbound/default.go index fb567e9d..010f6f76 100644 --- a/inbound/default.go +++ b/inbound/default.go @@ -40,7 +40,8 @@ type myInboundAdapter struct { // http mixed - setSystemProxy bool + setSystemProxy bool + clearSystemProxy func() error // internal @@ -60,10 +61,10 @@ func (a *myInboundAdapter) Tag() string { } func (a *myInboundAdapter) Start() error { + var err error bindAddr := M.SocksaddrFrom(netip.Addr(a.listenOptions.Listen), a.listenOptions.ListenPort) if common.Contains(a.network, N.NetworkTCP) { var tcpListener *net.TCPListener - var err error if !a.listenOptions.TCPFastOpen { tcpListener, err = net.ListenTCP(M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.TCPAddr()) } else { @@ -77,7 +78,8 @@ func (a *myInboundAdapter) Start() error { a.logger.Info("tcp server started at ", tcpListener.Addr()) } 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 { return err } @@ -101,7 +103,7 @@ func (a *myInboundAdapter) Start() error { a.logger.Info("udp server started at ", udpConn.LocalAddr()) } 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 { return E.Cause(err, "set system proxy") } @@ -111,8 +113,8 @@ func (a *myInboundAdapter) Start() error { func (a *myInboundAdapter) Close() error { var err error - if a.setSystemProxy { - err = settings.ClearSystemProxy() + if a.clearSystemProxy != nil { + err = a.clearSystemProxy() } return E.Errors(err, common.Close( common.PtrOrNil(a.tcpListener), diff --git a/route/iface.go b/route/iface.go deleted file mode 100644 index c3a5f619..00000000 --- a/route/iface.go +++ /dev/null @@ -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 -} diff --git a/route/iface_stub.go b/route/iface_stub.go deleted file mode 100644 index e139cd46..00000000 --- a/route/iface_stub.go +++ /dev/null @@ -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 -} diff --git a/route/iface_tun.go b/route/iface_tun.go deleted file mode 100644 index 2a318de6..00000000 --- a/route/iface_tun.go +++ /dev/null @@ -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) -} diff --git a/route/router.go b/route/router.go index 9a59b1a4..ce791616 100644 --- a/route/router.go +++ b/route/router.go @@ -25,6 +25,7 @@ import ( "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-dns" + "github.com/sagernet/sing-tun" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/bufio" @@ -83,16 +84,16 @@ type Router struct { transports []dns.Transport transportMap map[string]dns.Transport interfaceBindManager control.BindManager - networkMonitor NetworkUpdateMonitor autoDetectInterface bool defaultInterface string - interfaceMonitor DefaultInterfaceMonitor defaultMark int + networkMonitor tun.NetworkUpdateMonitor + interfaceMonitor tun.DefaultInterfaceMonitor trafficController adapter.TrafficController 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 != "" { warnDefaultInterfaceOnUnsupportedPlatform.Check() } @@ -231,8 +232,13 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont router.transports = transports router.transportMap = transportMap - if router.interfaceBindManager != nil || options.AutoDetectInterface { - networkMonitor, err := NewNetworkUpdateMonitor(router) + needInterfaceMonitor := options.AutoDetectInterface || + 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 { router.networkMonitor = networkMonitor 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 { - interfaceMonitor, err := NewDefaultInterfaceMonitor(router.networkMonitor, func() { - router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(), ", index ", router.interfaceMonitor.DefaultInterfaceIndex()) - }) + if router.networkMonitor != nil && needInterfaceMonitor { + interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor) if err != nil { 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 } + if hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess { searcher, err := process.NewSearcher(logger) if err != nil { @@ -648,20 +657,6 @@ func (r *Router) DefaultInterface() string { 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 { return r.defaultMark } @@ -670,6 +665,14 @@ func (r *Router) Rules() []adapter.Rule { 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) { r.trafficController = controller } diff --git a/test/go.mod b/test/go.mod index affbe9d7..a59711e6 100644 --- a/test/go.mod +++ b/test/go.mod @@ -54,7 +54,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // 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-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/sirupsen/logrus v1.8.1 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect @@ -62,7 +62,7 @@ require ( go.uber.org/atomic v1.9.0 // indirect golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // 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/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/tools v0.1.9 // indirect diff --git a/test/go.sum b/test/go.sum index 811e97fa..c66afcca 100644 --- a/test/go.sum +++ b/test/go.sum @@ -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-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-tun v0.0.0-20220803112223-a8fd6450d4ed h1:28qeqeuHLZEkzdcZjYwcCn8y4ckyKimaP+L4P25dqUo= -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 h1:har8hmVNhGxp14zLNAoGrgfzgxZQn0KTYJDfJudj0RU= +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/go.mod h1:bNXBqSWYaG3ePl6u0xQY5zneE+ZKa3683ZpuE8S1M1w= 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-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-20220731174439-a90be440212d h1:Sv5ogFZatcgIMMtBSTTAgMYsicp25MXBubjXNDKwm80= -golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704 h1:Y7NOhdqIOU8kYI7BxsgL38d0ot0raxvcW+EMQU2QrT4= +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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=