diff --git a/adapter/network.go b/adapter/network.go index 846d4392..c3ae515a 100644 --- a/adapter/network.go +++ b/adapter/network.go @@ -1,6 +1,7 @@ package adapter import ( + "context" "encoding/hex" "net" "net/netip" @@ -32,8 +33,8 @@ type NetworkManager interface { PackageManager() tun.PackageManager NeedWIFIState() bool WIFIState() WIFIState - UpdateWIFIState() - ResetNetwork() + UpdateWIFIState(ctx context.Context) + ResetNetwork(ctx context.Context) } type NetworkOptions struct { @@ -48,7 +49,7 @@ type NetworkOptions struct { } type InterfaceUpdateListener interface { - InterfaceUpdated() + InterfaceUpdated(ctx context.Context) } type WIFIState struct { diff --git a/adapter/platform.go b/adapter/platform.go index b068b37b..44af9b6b 100644 --- a/adapter/platform.go +++ b/adapter/platform.go @@ -1,6 +1,7 @@ package adapter import ( + "context" "net/netip" "github.com/sagernet/sing-box/option" @@ -29,7 +30,7 @@ type PlatformInterface interface { ClearDNSCache() RequestPermissionForWIFIState() error - ReadWIFIState() WIFIState + ReadWIFIState(ctx context.Context) WIFIState UsePlatformConnectionOwnerFinder() bool FindConnectionOwner(request *FindConnectionOwnerRequest) (*ConnectionOwner, error) diff --git a/common/listener/listener.go b/common/listener/listener.go index 7b657c98..7a58015c 100644 --- a/common/listener/listener.go +++ b/common/listener/listener.go @@ -113,7 +113,7 @@ func (l *Listener) Start() error { } err = systemProxy.Enable() if err != nil { - return E.Cause(err, "set system proxy") + return E.Errors(E.Cause(err, "set system proxy"), systemProxy.Close()) } l.systemProxy = systemProxy } @@ -123,8 +123,11 @@ func (l *Listener) Start() error { func (l *Listener) Close() error { l.shutdown.Store(true) var err error - if l.systemProxy != nil && l.systemProxy.IsEnabled() { - err = l.systemProxy.Disable() + if l.systemProxy != nil { + if l.systemProxy.IsEnabled() { + err = l.systemProxy.Disable() + } + err = E.Errors(err, l.systemProxy.Close()) } return E.Errors(err, common.Close( l.tcpListener, diff --git a/common/settings/proxy_android.go b/common/settings/proxy_android.go index df89c32e..4bd04c37 100644 --- a/common/settings/proxy_android.go +++ b/common/settings/proxy_android.go @@ -55,6 +55,10 @@ func (p *AndroidSystemProxy) Enable() error { return nil } +func (p *AndroidSystemProxy) Close() error { + return nil +} + func (p *AndroidSystemProxy) Disable() error { err := p.runAndroidShell("settings", "put", "global", "http_proxy", ":0") if err != nil { diff --git a/common/settings/proxy_darwin.go b/common/settings/proxy_darwin.go index 51103d9c..8fdc6daa 100644 --- a/common/settings/proxy_darwin.go +++ b/common/settings/proxy_darwin.go @@ -4,6 +4,7 @@ import ( "context" "strconv" "strings" + "sync" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-tun" @@ -16,11 +17,14 @@ import ( ) type DarwinSystemProxy struct { + ctx context.Context monitor tun.DefaultInterfaceMonitor interfaceName string element *list.Element[tun.DefaultInterfaceUpdateCallback] serverAddr M.Socksaddr supportSOCKS bool + access sync.Mutex + updateCancel context.CancelFunc isEnabled bool } @@ -30,6 +34,7 @@ func NewSystemProxy(ctx context.Context, serverAddr M.Socksaddr, supportSOCKS bo return nil, E.New("missing interface monitor") } proxy := &DarwinSystemProxy{ + ctx: ctx, monitor: interfaceMonitor, serverAddr: serverAddr, supportSOCKS: supportSOCKS, @@ -39,14 +44,36 @@ func NewSystemProxy(ctx context.Context, serverAddr M.Socksaddr, supportSOCKS bo } func (p *DarwinSystemProxy) IsEnabled() bool { + p.access.Lock() + defer p.access.Unlock() return p.isEnabled } func (p *DarwinSystemProxy) Enable() error { - return p.update0() + p.access.Lock() + defer p.access.Unlock() + return p.updateLocked(p.ctx) } func (p *DarwinSystemProxy) Disable() error { + p.access.Lock() + defer p.access.Unlock() + return p.disableLocked() +} + +func (p *DarwinSystemProxy) Close() error { + p.access.Lock() + updateCancel := p.updateCancel + p.updateCancel = nil + p.access.Unlock() + if updateCancel != nil { + updateCancel() + } + p.monitor.UnregisterCallback(p.element) + return nil +} + +func (p *DarwinSystemProxy) disableLocked() error { interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName) if err != nil { return err @@ -67,19 +94,35 @@ func (p *DarwinSystemProxy) Disable() error { } func (p *DarwinSystemProxy) routeUpdate(defaultInterface *control.Interface, flags int) { - if !p.isEnabled || defaultInterface == nil { + if defaultInterface == nil { return } - _ = p.update0() + updateContext, updateCancel := context.WithCancel(p.ctx) + p.access.Lock() + previousCancel := p.updateCancel + p.updateCancel = updateCancel + p.access.Unlock() + if previousCancel != nil { + previousCancel() + } + go func() { + defer updateCancel() + p.access.Lock() + defer p.access.Unlock() + if !p.isEnabled || updateContext.Err() != nil { + return + } + _ = p.updateLocked(updateContext) + }() } -func (p *DarwinSystemProxy) update0() error { +func (p *DarwinSystemProxy) updateLocked(ctx context.Context) error { newInterface := p.monitor.DefaultInterface() - if p.interfaceName == newInterface.Name { + if newInterface == nil || p.interfaceName == newInterface.Name { return nil } if p.interfaceName != "" { - _ = p.Disable() + _ = p.disableLocked() } p.interfaceName = newInterface.Name interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName) @@ -87,15 +130,27 @@ func (p *DarwinSystemProxy) update0() error { return err } if p.supportSOCKS { + err = ctx.Err() + if err != nil { + return err + } err = shell.Exec("networksetup", "-setsocksfirewallproxy", interfaceDisplayName, p.serverAddr.AddrString(), strconv.Itoa(int(p.serverAddr.Port))).Attach().Run() } if err != nil { return err } + err = ctx.Err() + if err != nil { + return err + } err = shell.Exec("networksetup", "-setwebproxy", interfaceDisplayName, p.serverAddr.AddrString(), strconv.Itoa(int(p.serverAddr.Port))).Attach().Run() if err != nil { return err } + err = ctx.Err() + if err != nil { + return err + } err = shell.Exec("networksetup", "-setsecurewebproxy", interfaceDisplayName, p.serverAddr.AddrString(), strconv.Itoa(int(p.serverAddr.Port))).Attach().Run() if err != nil { return err diff --git a/common/settings/proxy_linux.go b/common/settings/proxy_linux.go index 5f594dcd..b3714872 100644 --- a/common/settings/proxy_linux.go +++ b/common/settings/proxy_linux.go @@ -118,6 +118,10 @@ func (p *LinuxSystemProxy) Enable() error { return nil } +func (p *LinuxSystemProxy) Close() error { + return nil +} + func (p *LinuxSystemProxy) Disable() error { if p.hasGSettings { err := p.execute("gsettings", "set", "org.gnome.system.proxy", "mode", "none") diff --git a/common/settings/proxy_windows.go b/common/settings/proxy_windows.go index bd6a5dd2..f2d00cbb 100644 --- a/common/settings/proxy_windows.go +++ b/common/settings/proxy_windows.go @@ -36,6 +36,10 @@ func (p *WindowsSystemProxy) Enable() error { return nil } +func (p *WindowsSystemProxy) Close() error { + return nil +} + func (p *WindowsSystemProxy) Disable() error { err := wininet.ClearSystemProxy() if err != nil { diff --git a/common/settings/system_proxy.go b/common/settings/system_proxy.go index 0635c6f6..a1bff642 100644 --- a/common/settings/system_proxy.go +++ b/common/settings/system_proxy.go @@ -4,4 +4,5 @@ type SystemProxy interface { IsEnabled() bool Enable() error Disable() error + Close() error } diff --git a/common/settings/wifi.go b/common/settings/wifi.go index 62bef706..07c13905 100644 --- a/common/settings/wifi.go +++ b/common/settings/wifi.go @@ -1,9 +1,13 @@ package settings -import "github.com/sagernet/sing-box/adapter" +import ( + "context" + + "github.com/sagernet/sing-box/adapter" +) type WIFIMonitor interface { - ReadWIFIState() adapter.WIFIState + ReadWIFIState(ctx context.Context) adapter.WIFIState Start() error Close() error } diff --git a/common/settings/wifi_linux.go b/common/settings/wifi_linux.go index 9deed3c8..8b5b3493 100644 --- a/common/settings/wifi_linux.go +++ b/common/settings/wifi_linux.go @@ -1,6 +1,8 @@ package settings import ( + "context" + "github.com/sagernet/sing-box/adapter" E "github.com/sagernet/sing/common/exceptions" ) @@ -27,8 +29,8 @@ func NewWIFIMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) { return nil, E.Cause(E.Errors(errors...), "no supported WIFI manager found") } -func (m *LinuxWIFIMonitor) ReadWIFIState() adapter.WIFIState { - return m.monitor.ReadWIFIState() +func (m *LinuxWIFIMonitor) ReadWIFIState(ctx context.Context) adapter.WIFIState { + return m.monitor.ReadWIFIState(ctx) } func (m *LinuxWIFIMonitor) Start() error { diff --git a/common/settings/wifi_linux_connman.go b/common/settings/wifi_linux_connman.go index 46f6ea17..d5478077 100644 --- a/common/settings/wifi_linux_connman.go +++ b/common/settings/wifi_linux_connman.go @@ -35,8 +35,8 @@ func newConnManMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) { return &connmanMonitor{conn: conn, callback: callback}, nil } -func (m *connmanMonitor) ReadWIFIState() adapter.WIFIState { - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) +func (m *connmanMonitor) ReadWIFIState(ctx context.Context) adapter.WIFIState { + ctx, cancel := context.WithTimeout(ctx, 3*time.Second) defer cancel() cmObj := m.conn.Object("net.connman", "/") @@ -120,7 +120,7 @@ func (m *connmanMonitor) Start() error { return err } - state := m.ReadWIFIState() + state := m.ReadWIFIState(ctx) go m.monitorSignals(ctx, m.signalChan, state) m.callback(state) @@ -139,7 +139,7 @@ func (m *connmanMonitor) monitorSignals(ctx context.Context, signalChan chan *db // godbus Signal.Name uses "interface.member" format (e.g. "net.connman.Service.PropertyChanged"), // not just the member name. This differs from the D-Bus signal member in the match rule. if signal.Name == "net.connman.Service.PropertyChanged" { - state := m.ReadWIFIState() + state := m.ReadWIFIState(ctx) if state != lastState { lastState = state m.callback(state) diff --git a/common/settings/wifi_linux_iwd.go b/common/settings/wifi_linux_iwd.go index 327f9c47..2a42735c 100644 --- a/common/settings/wifi_linux_iwd.go +++ b/common/settings/wifi_linux_iwd.go @@ -35,8 +35,8 @@ func newIWDMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) { return &iwdMonitor{conn: conn, callback: callback}, nil } -func (m *iwdMonitor) ReadWIFIState() adapter.WIFIState { - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) +func (m *iwdMonitor) ReadWIFIState(ctx context.Context) adapter.WIFIState { + ctx, cancel := context.WithTimeout(ctx, 3*time.Second) defer cancel() iwdObj := m.conn.Object("net.connman.iwd", "/") @@ -144,7 +144,7 @@ func (m *iwdMonitor) Start() error { return err } - state := m.ReadWIFIState() + state := m.ReadWIFIState(ctx) go m.monitorSignals(ctx, m.signalChan, state) m.callback(state) @@ -161,7 +161,7 @@ func (m *iwdMonitor) monitorSignals(ctx context.Context, signalChan chan *dbus.S return } if signal.Name == "org.freedesktop.DBus.Properties.PropertiesChanged" { - state := m.ReadWIFIState() + state := m.ReadWIFIState(ctx) if state != lastState { lastState = state m.callback(state) diff --git a/common/settings/wifi_linux_nm.go b/common/settings/wifi_linux_nm.go index 77d897d4..56cdf2c0 100644 --- a/common/settings/wifi_linux_nm.go +++ b/common/settings/wifi_linux_nm.go @@ -36,8 +36,8 @@ func newNetworkManagerMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, er return &networkManagerMonitor{conn: conn, callback: callback}, nil } -func (m *networkManagerMonitor) ReadWIFIState() adapter.WIFIState { - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) +func (m *networkManagerMonitor) ReadWIFIState(ctx context.Context) adapter.WIFIState { + ctx, cancel := context.WithTimeout(ctx, 3*time.Second) defer cancel() nmObj := m.conn.Object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager") @@ -119,7 +119,7 @@ func (m *networkManagerMonitor) Start() error { return err } - state := m.ReadWIFIState() + state := m.ReadWIFIState(ctx) go m.monitorSignals(ctx, m.signalChan, state) m.callback(state) @@ -136,7 +136,7 @@ func (m *networkManagerMonitor) monitorSignals(ctx context.Context, signalChan c return } if signal.Name == "org.freedesktop.DBus.Properties.PropertiesChanged" { - state := m.ReadWIFIState() + state := m.ReadWIFIState(ctx) if state != lastState { lastState = state m.callback(state) diff --git a/common/settings/wifi_linux_wpa.go b/common/settings/wifi_linux_wpa.go index 192c2f01..87d4e02f 100644 --- a/common/settings/wifi_linux_wpa.go +++ b/common/settings/wifi_linux_wpa.go @@ -52,7 +52,7 @@ func newWpaSupplicantMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, err return nil, os.ErrNotExist } -func (m *wpaSupplicantMonitor) ReadWIFIState() adapter.WIFIState { +func (m *wpaSupplicantMonitor) ReadWIFIState(ctx context.Context) adapter.WIFIState { id := wpaSocketCounter.Add(1) localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-%d-%d", os.Getpid(), id), Net: "unixgram"} remoteAddr := &net.UnixAddr{Name: m.socketPath, Net: "unixgram"} @@ -63,6 +63,15 @@ func (m *wpaSupplicantMonitor) ReadWIFIState() adapter.WIFIState { defer conn.Close() conn.SetDeadline(time.Now().Add(3 * time.Second)) + done := make(chan struct{}) + defer close(done) + go func() { + select { + case <-ctx.Done(): + conn.SetDeadline(time.Now()) + case <-done: + } + }() status, err := m.sendCommand(conn, "STATUS") if err != nil { @@ -124,7 +133,7 @@ func (m *wpaSupplicantMonitor) Start() error { ctx, cancel := context.WithCancel(context.Background()) m.cancel = cancel - state := m.ReadWIFIState() + state := m.ReadWIFIState(ctx) go m.monitorEvents(ctx, state) m.callback(state) @@ -202,7 +211,7 @@ func (m *wpaSupplicantMonitor) monitorEvents(ctx context.Context, lastState adap debounceTimer.Stop() } debounceTimer = time.AfterFunc(500*time.Millisecond, func() { - state := m.ReadWIFIState() + state := m.ReadWIFIState(ctx) if state != lastState { lastState = state m.callback(state) diff --git a/common/settings/wifi_stub.go b/common/settings/wifi_stub.go index 499212e4..3bc776c8 100644 --- a/common/settings/wifi_stub.go +++ b/common/settings/wifi_stub.go @@ -4,6 +4,7 @@ package settings import ( + "context" "os" "github.com/sagernet/sing-box/adapter" @@ -15,7 +16,7 @@ func NewWIFIMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) { return nil, os.ErrInvalid } -func (m *stubWIFIMonitor) ReadWIFIState() adapter.WIFIState { +func (m *stubWIFIMonitor) ReadWIFIState(ctx context.Context) adapter.WIFIState { return adapter.WIFIState{} } diff --git a/common/settings/wifi_windows.go b/common/settings/wifi_windows.go index 91b0d479..4ee92437 100644 --- a/common/settings/wifi_windows.go +++ b/common/settings/wifi_windows.go @@ -45,7 +45,7 @@ func NewWIFIMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) { }, nil } -func (m *windowsWIFIMonitor) ReadWIFIState() adapter.WIFIState { +func (m *windowsWIFIMonitor) ReadWIFIState(ctx context.Context) adapter.WIFIState { interfaces, err := winwlanapi.EnumInterfaces(m.handle) if err != nil || len(interfaces) == 0 { return adapter.WIFIState{} @@ -92,7 +92,7 @@ func (m *windowsWIFIMonitor) Start() error { ctx, cancel := context.WithCancel(context.Background()) m.cancel = cancel - m.lastState = m.ReadWIFIState() + m.lastState = m.ReadWIFIState(ctx) callbackFunc := func(data *winwlanapi.NotificationData, callbackContext uintptr) uintptr { if data.NotificationSource != winwlanapi.NotificationSourceACM { @@ -126,7 +126,7 @@ func (m *windowsWIFIMonitor) checkAndNotify() { m.mutex.Lock() defer m.mutex.Unlock() - state := m.ReadWIFIState() + state := m.ReadWIFIState(context.Background()) if state != m.lastState { m.lastState = state if m.callback != nil { diff --git a/dns/transport/dhcp/dhcp.go b/dns/transport/dhcp/dhcp.go index 113d308b..c1ab2ffa 100644 --- a/dns/transport/dhcp/dhcp.go +++ b/dns/transport/dhcp/dhcp.go @@ -55,6 +55,8 @@ type Transport struct { platformInterface adapter.PlatformInterface interfaceName string interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback] + updateAccess sync.Mutex + updateCancel context.CancelFunc refreshAccess sync.Mutex savedState atomic.Pointer[transportState] ndots int @@ -126,6 +128,13 @@ func (t *Transport) Close() error { if t.interfaceCallback != nil { t.networkManager.InterfaceMonitor().UnregisterCallback(t.interfaceCallback) } + t.updateAccess.Lock() + updateCancel := t.updateCancel + t.updateCancel = nil + t.updateAccess.Unlock() + if updateCancel != nil { + updateCancel() + } t.refreshAccess.Lock() defer t.refreshAccess.Unlock() state := t.savedState.Swap(nil) @@ -243,7 +252,7 @@ func (t *Transport) fetch() error { return nil } } - return t.updateServersLocked() + return t.updateServersLocked(t.ctx) } func (t *Transport) startRefresh() { @@ -256,7 +265,7 @@ func (t *Transport) startRefresh() { if state != nil && time.Since(state.updatedAt) < C.DHCPTTL { return } - err := t.updateServersLocked() + err := t.updateServersLocked(t.ctx) if err != nil { if errors.Is(err, errInterfaceIsCellular) && t.optional { t.logger.Debug(E.Cause(err, "dhcp: refresh DNS servers")) @@ -293,17 +302,20 @@ func (t *Transport) fetchInterface() (*control.Interface, error) { } } -func (t *Transport) updateServersLocked() error { +func (t *Transport) updateServersLocked(ctx context.Context) error { iface, err := t.fetchInterface() if err != nil { t.storeFailureLocked(err) return E.Cause(err, "prepare interface") } t.logger.Info("dhcp: query DNS servers on ", iface.Name) - fetchCtx, cancel := context.WithTimeout(t.ctx, C.DHCPTimeout) + fetchCtx, cancel := context.WithTimeout(ctx, C.DHCPTimeout) err = t.fetchServers0(fetchCtx, iface) cancel() if err != nil { + if ctx.Err() != nil { + return err + } t.storeFailureLocked(err) return err } @@ -331,16 +343,28 @@ func (t *Transport) storeFailureLocked(err error) { } func (t *Transport) interfaceUpdated(defaultInterface *control.Interface, flags int) { - t.refreshAccess.Lock() - err := t.updateServersLocked() - t.refreshAccess.Unlock() - if err != nil { + updateContext, updateCancel := context.WithCancel(t.ctx) + t.updateAccess.Lock() + previousCancel := t.updateCancel + t.updateCancel = updateCancel + t.updateAccess.Unlock() + if previousCancel != nil { + previousCancel() + } + go func() { + defer updateCancel() + t.refreshAccess.Lock() + err := t.updateServersLocked(updateContext) + t.refreshAccess.Unlock() + if err == nil || updateContext.Err() != nil { + return + } if errors.Is(err, errInterfaceIsCellular) && t.optional { t.logger.Debug(E.Cause(errInterfaceIsCellular, "dhcp: update DNS servers")) } else { t.logger.Error("dhcp: update DNS servers: ", err) } - } + }() } func (t *Transport) fetchServers0(ctx context.Context, iface *control.Interface) error { @@ -356,11 +380,15 @@ func (t *Transport) fetchServers0(ctx context.Context, iface *control.Interface) err error ) for range 5 { - packetConn, err = listener.ListenPacket(t.ctx, "udp4", listenAddr) + packetConn, err = listener.ListenPacket(ctx, "udp4", listenAddr) if err == nil || !errors.Is(err, syscall.EADDRINUSE) { break } - time.Sleep(time.Second) + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(time.Second): + } } if err != nil { return err diff --git a/dns/transport/local/local_resolved_linux.go b/dns/transport/local/local_resolved_linux.go index cbbd3f36..50d4abce 100644 --- a/dns/transport/local/local_resolved_linux.go +++ b/dns/transport/local/local_resolved_linux.go @@ -58,6 +58,10 @@ type DBusResolvedResolver struct { interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback] systemBus *dbus.Conn savedServerSet atomic.Pointer[resolvedServerSet] + updateAccess sync.Mutex + updateCancel context.CancelFunc + updateRunAccess sync.Mutex + closed bool closeOnce sync.Once } @@ -95,7 +99,7 @@ func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (Reso } func (t *DBusResolvedResolver) Start() error { - t.updateStatus() + t.updateStatus(t.ctx) t.interfaceCallback = t.interfaceMonitor.RegisterCallback(t.updateDefaultInterface) err := t.systemBus.BusObject().AddMatchSignal( "org.freedesktop.DBus", @@ -122,7 +126,17 @@ func (t *DBusResolvedResolver) Start() error { func (t *DBusResolvedResolver) Close() error { var closeErr error t.closeOnce.Do(func() { + t.updateAccess.Lock() + updateCancel := t.updateCancel + t.updateCancel = nil + t.updateAccess.Unlock() + if updateCancel != nil { + updateCancel() + } + t.updateRunAccess.Lock() + t.closed = true serverSet := t.savedServerSet.Swap(nil) + t.updateRunAccess.Unlock() if serverSet != nil { closeErr = serverSet.Close() } @@ -174,7 +188,7 @@ func (t *DBusResolvedResolver) Exchange(ctx context.Context, message *mDNS.Msg) if err == nil { return response, nil } - t.updateStatus() + t.updateStatus(t.ctx) refreshedServerSet := t.savedServerSet.Load() if refreshedServerSet == nil || refreshedServerSet == serverSet { return nil, err @@ -196,7 +210,7 @@ func (t *DBusResolvedResolver) ExchangeAsync(ctx context.Context, message *mDNS. return } go func() { - t.updateStatus() + t.updateStatus(t.ctx) refreshedServerSet := t.savedServerSet.Load() if refreshedServerSet == nil || refreshedServerSet == serverSet { callback(nil, err) @@ -240,18 +254,44 @@ func (t *DBusResolvedResolver) loopUpdateStatus() { if !loaded || newOwner == "" { continue } - t.updateStatus() + t.postUpdateStatus() case "org.freedesktop.DBus.Properties.PropertiesChanged": if !shouldUpdateResolvedServerSet(signal) { continue } - t.updateStatus() + t.postUpdateStatus() } } } -func (t *DBusResolvedResolver) updateStatus() { - serverSet, err := t.checkResolved(context.Background()) +func (t *DBusResolvedResolver) postUpdateStatus() { + updateContext, updateCancel := context.WithCancel(t.ctx) + t.updateAccess.Lock() + previousCancel := t.updateCancel + t.updateCancel = updateCancel + t.updateAccess.Unlock() + if previousCancel != nil { + previousCancel() + } + go func() { + defer updateCancel() + t.updateStatus(updateContext) + }() +} + +func (t *DBusResolvedResolver) updateStatus(ctx context.Context) { + t.updateRunAccess.Lock() + defer t.updateRunAccess.Unlock() + if t.closed || ctx.Err() != nil { + return + } + serverSet, err := t.checkResolved(ctx) + if t.closed || ctx.Err() != nil { + if serverSet != nil { + _ = serverSet.Close() + } + return + } oldServerSet := t.savedServerSet.Swap(serverSet) if oldServerSet != nil { _ = oldServerSet.Close() @@ -291,7 +331,7 @@ func (t *DBusResolvedResolver) exchangeServerSet(ctx context.Context, message *m func (t *DBusResolvedResolver) checkResolved(ctx context.Context) (*resolvedServerSet, error) { dbusObject := t.systemBus.Object("org.freedesktop.resolve1", "/org/freedesktop/resolve1") - err := dbusObject.Call("org.freedesktop.DBus.Peer.Ping", 0).Err + err := dbusObject.(*dbus.Object).CallWithContext(ctx, "org.freedesktop.DBus.Peer.Ping", 0).Err if err != nil { return nil, err } @@ -321,10 +361,18 @@ func (t *DBusResolvedResolver) checkResolved(ctx context.Context) (*resolvedServ if err != nil { return nil, err } + err = ctx.Err() + if err != nil { + return nil, err + } linkDNSEx, err := loadResolvedLinkDNSEx(linkObject) if err != nil { return nil, err } + err = ctx.Err() + if err != nil { + return nil, err + } linkDNS, err := loadResolvedLinkDNS(linkObject) if err != nil { return nil, err @@ -570,5 +618,5 @@ func shouldUpdateResolvedServerSet(signal *dbus.Signal) bool { } func (t *DBusResolvedResolver) updateDefaultInterface(defaultInterface *control.Interface, flags int) { - t.updateStatus() + t.postUpdateStatus() } diff --git a/experimental/boxdd/platform_linux.go b/experimental/boxdd/platform_linux.go index 60341979..c7d2ffb0 100644 --- a/experimental/boxdd/platform_linux.go +++ b/experimental/boxdd/platform_linux.go @@ -3,6 +3,7 @@ package main import ( + "context" "net/netip" "os" "os/exec" @@ -120,7 +121,7 @@ func (p *linuxPlatformInterface) RequestPermissionForWIFIState() error { return nil } -func (p *linuxPlatformInterface) ReadWIFIState() adapter.WIFIState { +func (p *linuxPlatformInterface) ReadWIFIState(ctx context.Context) adapter.WIFIState { return adapter.WIFIState{} } diff --git a/experimental/boxdd/platform_windows.go b/experimental/boxdd/platform_windows.go index 9d972218..173c2d50 100644 --- a/experimental/boxdd/platform_windows.go +++ b/experimental/boxdd/platform_windows.go @@ -135,7 +135,7 @@ func (p *windowsPlatformInterface) RequestPermissionForWIFIState() error { return nil } -func (p *windowsPlatformInterface) ReadWIFIState() adapter.WIFIState { +func (p *windowsPlatformInterface) ReadWIFIState(ctx context.Context) adapter.WIFIState { return adapter.WIFIState{} } diff --git a/experimental/clashapi/connections.go b/experimental/clashapi/connections.go index 4b6038f2..2cf0a714 100644 --- a/experimental/clashapi/connections.go +++ b/experimental/clashapi/connections.go @@ -24,7 +24,7 @@ import ( func connectionRouter(ctx context.Context, network adapter.NetworkManager, trafficManager *trafficcontrol.Manager) http.Handler { r := chi.NewRouter() r.Get("/", getConnections(ctx, trafficManager)) - r.Delete("/", closeAllConnections(network, trafficManager)) + r.Delete("/", closeAllConnections(ctx, network, trafficManager)) r.Delete("/{id}", closeConnection(trafficManager)) return r } @@ -170,10 +170,10 @@ func closeConnection(trafficManager *trafficcontrol.Manager) func(w http.Respons } } -func closeAllConnections(network adapter.NetworkManager, trafficManager *trafficcontrol.Manager) func(w http.ResponseWriter, r *http.Request) { +func closeAllConnections(ctx context.Context, network adapter.NetworkManager, trafficManager *trafficcontrol.Manager) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { trafficManager.CloseAllConnections() - network.ResetNetwork() + network.ResetNetwork(ctx) render.NoContent(w, r) } } diff --git a/experimental/libbox/command_server.go b/experimental/libbox/command_server.go index 312431b3..d34299ac 100644 --- a/experimental/libbox/command_server.go +++ b/experimental/libbox/command_server.go @@ -262,7 +262,7 @@ func (s *CommandServer) ResetNetwork() { if instance == nil || instance.Box() == nil { return } - instance.Box().Network().ResetNetwork() + instance.Box().Network().ResetNetwork(context.Background()) } func (s *CommandServer) UpdateWIFIState() { @@ -270,7 +270,7 @@ func (s *CommandServer) UpdateWIFIState() { if instance == nil || instance.Box() == nil { return } - instance.Box().Network().UpdateWIFIState() + instance.Box().Network().UpdateWIFIState(context.Background()) } type platformHandler CommandServer diff --git a/experimental/libbox/config.go b/experimental/libbox/config.go index 1459ceaa..08aa8ff0 100644 --- a/experimental/libbox/config.go +++ b/experimental/libbox/config.go @@ -127,7 +127,7 @@ func (s *platformInterfaceStub) UsePlatformWIFIMonitor() bool { return false } -func (s *platformInterfaceStub) ReadWIFIState() adapter.WIFIState { +func (s *platformInterfaceStub) ReadWIFIState(ctx context.Context) adapter.WIFIState { return adapter.WIFIState{} } diff --git a/experimental/libbox/service.go b/experimental/libbox/service.go index 1ae370fd..a53ca289 100644 --- a/experimental/libbox/service.go +++ b/experimental/libbox/service.go @@ -1,6 +1,7 @@ package libbox import ( + "context" "crypto/rand" "encoding/hex" "errors" @@ -175,7 +176,7 @@ func (w *platformInterfaceWrapper) UsePlatformWIFIMonitor() bool { return true } -func (w *platformInterfaceWrapper) ReadWIFIState() adapter.WIFIState { +func (w *platformInterfaceWrapper) ReadWIFIState(ctx context.Context) adapter.WIFIState { wifiState := w.iif.ReadWIFIState() if wifiState == nil { return adapter.WIFIState{} diff --git a/protocol/direct/inbound.go b/protocol/direct/inbound.go index 42135dc1..34ebbb15 100644 --- a/protocol/direct/inbound.go +++ b/protocol/direct/inbound.go @@ -77,7 +77,7 @@ func (i *Inbound) Start(stage adapter.StartStage) error { return i.listener.Start() } -func (i *Inbound) InterfaceUpdated() { +func (i *Inbound) InterfaceUpdated(ctx context.Context) { i.udpNat.Purge() } diff --git a/protocol/direct/outbound.go b/protocol/direct/outbound.go index 5b9196a3..1ca35af0 100644 --- a/protocol/direct/outbound.go +++ b/protocol/direct/outbound.go @@ -121,7 +121,7 @@ func (h *Outbound) fetchMyAddresses() { h.myAddresses.Store(myAddresses) } -func (h *Outbound) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated(ctx context.Context) { h.fetchMyAddresses() if h.icmpPort != nil { h.icmpPort.Close() diff --git a/protocol/group/urltest.go b/protocol/group/urltest.go index f6acb3d5..5dfa2440 100644 --- a/protocol/group/urltest.go +++ b/protocol/group/urltest.go @@ -45,6 +45,7 @@ type URLTest struct { tolerance uint16 idleTimeout time.Duration group *URLTestGroup + checkAccess sync.Mutex interruptExternalConnections bool } @@ -114,14 +115,14 @@ func (s *URLTest) URLTest(ctx context.Context) (map[string]uint16, error) { } func (s *URLTest) CheckOutbounds() { - s.group.CheckOutbounds(true) + s.group.CheckOutbounds(s.ctx, true) } func (s *URLTest) PerformUpdateCheck() { s.group.performUpdateCheck() } -func (s *URLTest) InterfaceUpdated() { +func (s *URLTest) InterfaceUpdated(ctx context.Context) { group := s.group if group == nil { return @@ -129,7 +130,14 @@ func (s *URLTest) InterfaceUpdated() { if group.pause.IsDevicePaused() || group.pause.IsNetworkPaused() { return } - go group.CheckOutbounds(true) + go func() { + s.checkAccess.Lock() + defer s.checkAccess.Unlock() + if ctx.Err() != nil { + return + } + group.CheckOutbounds(ctx, true) + }() } func (s *URLTest) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { @@ -250,7 +258,7 @@ func (g *URLTestGroup) PostStart() { defer g.access.Unlock() g.started = true g.lastActive.Store(time.Now()) - go g.CheckOutbounds(false) + go g.CheckOutbounds(g.ctx, false) } func (g *URLTestGroup) Touch() { @@ -330,7 +338,7 @@ func (g *URLTestGroup) Select(network string) (adapter.Outbound, bool) { func (g *URLTestGroup) loopCheck(ticker *time.Ticker, closeChan <-chan struct{}) { if time.Since(g.lastActive.Load()) > g.interval { g.lastActive.Store(time.Now()) - g.CheckOutbounds(false) + g.CheckOutbounds(g.ctx, false) } for { select { @@ -349,12 +357,12 @@ func (g *URLTestGroup) loopCheck(ticker *time.Ticker, closeChan <-chan struct{}) g.access.Unlock() return } - g.CheckOutbounds(false) + g.CheckOutbounds(g.ctx, false) } } -func (g *URLTestGroup) CheckOutbounds(force bool) { - _, _ = g.urlTest(g.ctx, force) +func (g *URLTestGroup) CheckOutbounds(ctx context.Context, force bool) { + _, _ = g.urlTest(ctx, force) } func (g *URLTestGroup) URLTest(ctx context.Context) (map[string]uint16, error) { @@ -391,7 +399,7 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint continue } b.Go(realTag, func() (any, error) { - testCtx, cancel := context.WithTimeout(g.ctx, C.TCPTimeout) + testCtx, cancel := context.WithTimeout(ctx, C.TCPTimeout) defer cancel() testChan := make(chan urlTestResult, 1) go func() { diff --git a/protocol/hysteria/outbound.go b/protocol/hysteria/outbound.go index bd6c5e4a..a9451086 100644 --- a/protocol/hysteria/outbound.go +++ b/protocol/hysteria/outbound.go @@ -115,7 +115,7 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n return h.client.ListenPacket(ctx, destination) } -func (h *Outbound) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated(ctx context.Context) { h.client.CloseWithError(E.New("network changed")) } diff --git a/protocol/hysteria2/inbound.go b/protocol/hysteria2/inbound.go index 88d1b8f1..ef9233f3 100644 --- a/protocol/hysteria2/inbound.go +++ b/protocol/hysteria2/inbound.go @@ -281,7 +281,7 @@ func (h *Inbound) Start(stage adapter.StartStage) error { return h.service.Start(packetConn) } -func (h *Inbound) InterfaceUpdated() { +func (h *Inbound) InterfaceUpdated(ctx context.Context) { h.service.Reset() } diff --git a/protocol/hysteria2/outbound.go b/protocol/hysteria2/outbound.go index 9bb7949a..6576bf9f 100644 --- a/protocol/hysteria2/outbound.go +++ b/protocol/hysteria2/outbound.go @@ -203,7 +203,7 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n return h.client.ListenPacket(ctx) } -func (h *Outbound) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated(ctx context.Context) { h.client.CloseWithError(E.New("network changed")) } diff --git a/protocol/naive/outbound.go b/protocol/naive/outbound.go index 1e753c33..98bf8d97 100644 --- a/protocol/naive/outbound.go +++ b/protocol/naive/outbound.go @@ -254,7 +254,7 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n return h.uotClient.ListenPacket(ctx, destination) } -func (h *Outbound) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated(ctx context.Context) { h.client.Engine().CloseAllConnections() } diff --git a/protocol/openconnect/client.go b/protocol/openconnect/client.go index 86161691..2021d623 100644 --- a/protocol/openconnect/client.go +++ b/protocol/openconnect/client.go @@ -486,7 +486,7 @@ func (e *Endpoint) Close() error { return err } -func (e *Endpoint) InterfaceUpdated() { +func (e *Endpoint) InterfaceUpdated(ctx context.Context) { e.client.RestartSession() } diff --git a/protocol/openvpn/client.go b/protocol/openvpn/client.go index 69d44258..1fa5906a 100644 --- a/protocol/openvpn/client.go +++ b/protocol/openvpn/client.go @@ -658,7 +658,7 @@ func (c *ClientEndpoint) Close() error { return err } -func (c *ClientEndpoint) InterfaceUpdated() { +func (c *ClientEndpoint) InterfaceUpdated(ctx context.Context) { c.client.RestartSession() } diff --git a/protocol/redirect/tproxy.go b/protocol/redirect/tproxy.go index 6ce7330c..ed2c6bd7 100644 --- a/protocol/redirect/tproxy.go +++ b/protocol/redirect/tproxy.go @@ -85,7 +85,7 @@ func (t *TProxy) Start(stage adapter.StartStage) error { return err } -func (t *TProxy) InterfaceUpdated() { +func (t *TProxy) InterfaceUpdated(ctx context.Context) { t.udpNat.Purge() } diff --git a/protocol/shadowsocks/outbound.go b/protocol/shadowsocks/outbound.go index ebf21f6a..10dd204d 100644 --- a/protocol/shadowsocks/outbound.go +++ b/protocol/shadowsocks/outbound.go @@ -130,7 +130,7 @@ func (h *Outbound) MultiplexEnabled() bool { return h.multiplexDialer != nil } -func (h *Outbound) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated(ctx context.Context) { if h.multiplexDialer != nil { h.multiplexDialer.Reset() } diff --git a/protocol/snell/outbound.go b/protocol/snell/outbound.go index c24a92c0..01160a5e 100644 --- a/protocol/snell/outbound.go +++ b/protocol/snell/outbound.go @@ -139,7 +139,7 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n return packetConn, nil } -func (h *Outbound) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated(ctx context.Context) { h.client.Reset() } diff --git a/protocol/ssh/outbound.go b/protocol/ssh/outbound.go index bb33a2cc..ec2ab27d 100644 --- a/protocol/ssh/outbound.go +++ b/protocol/ssh/outbound.go @@ -206,7 +206,7 @@ func (s *Outbound) connect(ctx context.Context) (client *ssh.Client, err error) return client, nil } -func (s *Outbound) InterfaceUpdated() { +func (s *Outbound) InterfaceUpdated(ctx context.Context) { common.Close(s.clientConn) } diff --git a/protocol/tailscale/endpoint.go b/protocol/tailscale/endpoint.go index fb4d491c..a4efb83b 100644 --- a/protocol/tailscale/endpoint.go +++ b/protocol/tailscale/endpoint.go @@ -740,7 +740,7 @@ func (t *Endpoint) Close() error { return err } -func (t *Endpoint) InterfaceUpdated() { +func (t *Endpoint) InterfaceUpdated(ctx context.Context) { if !t.started.Load() { return } diff --git a/protocol/trojan/outbound.go b/protocol/trojan/outbound.go index c25af9bb..9abe76d4 100644 --- a/protocol/trojan/outbound.go +++ b/protocol/trojan/outbound.go @@ -113,7 +113,7 @@ func (h *Outbound) MultiplexEnabled() bool { return h.multiplexDialer != nil } -func (h *Outbound) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated(ctx context.Context) { if h.transport != nil { h.transport.Close() } diff --git a/protocol/tuic/outbound.go b/protocol/tuic/outbound.go index 694c8451..7c8ce053 100644 --- a/protocol/tuic/outbound.go +++ b/protocol/tuic/outbound.go @@ -142,7 +142,7 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n } } -func (h *Outbound) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated(ctx context.Context) { _ = h.client.CloseWithError(E.New("network changed")) } diff --git a/protocol/tun/inbound.go b/protocol/tun/inbound.go index 9d3ae9e5..22172e50 100644 --- a/protocol/tun/inbound.go +++ b/protocol/tun/inbound.go @@ -505,7 +505,7 @@ func (t *Inbound) updateRouteAddressSet(it adapter.RuleSet) { t.routeExcludeAddressSet = nil } -func (t *Inbound) InterfaceUpdated() { +func (t *Inbound) InterfaceUpdated(ctx context.Context) { tunStack := t.tunStack if tunStack != nil { tunStack.ResetNetwork() diff --git a/protocol/vless/outbound.go b/protocol/vless/outbound.go index 007c78f3..1356a589 100644 --- a/protocol/vless/outbound.go +++ b/protocol/vless/outbound.go @@ -133,7 +133,7 @@ func (h *Outbound) MultiplexEnabled() bool { return h.multiplexDialer != nil } -func (h *Outbound) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated(ctx context.Context) { if h.transport != nil { h.transport.Close() } diff --git a/protocol/vmess/outbound.go b/protocol/vmess/outbound.go index 1baf3b35..7b16fc90 100644 --- a/protocol/vmess/outbound.go +++ b/protocol/vmess/outbound.go @@ -111,7 +111,7 @@ func (h *Outbound) MultiplexEnabled() bool { return h.multiplexDialer != nil } -func (h *Outbound) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated(ctx context.Context) { if h.transport != nil { h.transport.Close() } diff --git a/protocol/wireguard/endpoint.go b/protocol/wireguard/endpoint.go index 7c7a0105..1fe5fd2e 100644 --- a/protocol/wireguard/endpoint.go +++ b/protocol/wireguard/endpoint.go @@ -4,6 +4,7 @@ import ( "context" "net" "net/netip" + "sync" "sync/atomic" "time" @@ -42,6 +43,7 @@ type Endpoint struct { logger logger.ContextLogger localAddresses []netip.Prefix endpoint *wireguard.Endpoint + bindAccess sync.Mutex started atomic.Bool } @@ -145,14 +147,25 @@ func (w *Endpoint) Start(stage adapter.StartStage) error { } func (w *Endpoint) Close() error { + w.bindAccess.Lock() w.started.Store(false) + w.bindAccess.Unlock() return w.endpoint.Close() } -func (w *Endpoint) InterfaceUpdated() { +func (w *Endpoint) InterfaceUpdated(ctx context.Context) { if !w.started.Load() { return } + go w.updateBind(ctx) +} + +func (w *Endpoint) updateBind(ctx context.Context) { + w.bindAccess.Lock() + defer w.bindAccess.Unlock() + if ctx.Err() != nil || !w.started.Load() { + return + } err := w.endpoint.BindUpdate() if err != nil { w.logger.Error(E.Cause(err, "update bind")) diff --git a/route/network.go b/route/network.go index 2f4a49b9..07de8145 100644 --- a/route/network.go +++ b/route/network.go @@ -34,32 +34,37 @@ import ( var _ adapter.NetworkManager = (*NetworkManager)(nil) type NetworkManager struct { - ctx context.Context - logger logger.ContextLogger - router adapter.Router - interfaceFinder *control.DefaultInterfaceFinder - networkInterfaces common.TypedValue[[]adapter.NetworkInterface] - autoDetectInterface bool - defaultOptions adapter.NetworkOptions - autoRedirectOutputMark uint32 - networkMonitor tun.NetworkUpdateMonitor - interfaceMonitor tun.DefaultInterfaceMonitor - packageManager tun.PackageManager - powerListener winpowrprof.EventListener - pauseManager pause.Manager - platformInterface adapter.PlatformInterface - connectionManager adapter.ConnectionManager - endpoint adapter.EndpointManager - inbound adapter.InboundManager - outbound adapter.OutboundManager - needWIFIState bool - wifiMonitor settings.WIFIMonitor - wifiState adapter.WIFIState - networkEnvironment uint64 - stateAccess sync.RWMutex - environmentUpdateAccess sync.Mutex - environmentUpdateTimer *time.Timer - started bool + ctx context.Context + logger logger.ContextLogger + router adapter.Router + interfaceFinder *control.DefaultInterfaceFinder + networkInterfaces common.TypedValue[[]adapter.NetworkInterface] + autoDetectInterface bool + defaultOptions adapter.NetworkOptions + autoRedirectOutputMark uint32 + networkMonitor tun.NetworkUpdateMonitor + interfaceMonitor tun.DefaultInterfaceMonitor + packageManager tun.PackageManager + powerListener winpowrprof.EventListener + pauseManager pause.Manager + platformInterface adapter.PlatformInterface + connectionManager adapter.ConnectionManager + endpoint adapter.EndpointManager + inbound adapter.InboundManager + outbound adapter.OutboundManager + needWIFIState bool + wifiMonitor settings.WIFIMonitor + wifiState adapter.WIFIState + networkEnvironment uint64 + stateAccess sync.RWMutex + environmentUpdateAccess sync.Mutex + environmentUpdateTimer *time.Timer + interfaceUpdateAccess sync.Mutex + interfaceUpdateCancel context.CancelFunc + interfaceUpdateRunAccess sync.Mutex + powerUpdateAccess sync.Mutex + powerUpdateCancel context.CancelFunc + started bool } func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, options option.RouteOptions, dnsOptions option.DNSOptions) (*NetworkManager, error) { @@ -251,6 +256,14 @@ func (r *NetworkManager) Close() error { }) monitor.Finish() } + r.interfaceUpdateAccess.Lock() + interfaceUpdateCancel := r.interfaceUpdateCancel + r.interfaceUpdateCancel = nil + r.interfaceUpdateAccess.Unlock() + if interfaceUpdateCancel != nil { + interfaceUpdateCancel() + } + r.cancelPowerUpdate() if r.networkMonitor != nil { monitor.Start("close network monitor") err = E.Append(err, r.networkMonitor.Close(), func(err error) error { @@ -455,19 +468,19 @@ func (r *NetworkManager) onWIFIStateChanged(state adapter.WIFIState) { } } -func (r *NetworkManager) UpdateWIFIState() { +func (r *NetworkManager) UpdateWIFIState(ctx context.Context) { var state adapter.WIFIState if r.wifiMonitor != nil { - state = r.wifiMonitor.ReadWIFIState() + state = r.wifiMonitor.ReadWIFIState(ctx) } else if r.platformInterface != nil && r.platformInterface.UsePlatformWIFIMonitor() { - state = r.platformInterface.ReadWIFIState() + state = r.platformInterface.ReadWIFIState(ctx) } else { return } r.onWIFIStateChanged(state) } -func (r *NetworkManager) ResetNetwork() { +func (r *NetworkManager) ResetNetwork(ctx context.Context) { if r.connectionManager != nil { r.connectionManager.CloseAll() } @@ -475,21 +488,21 @@ func (r *NetworkManager) ResetNetwork() { for _, endpoint := range r.endpoint.Endpoints() { listener, isListener := endpoint.(adapter.InterfaceUpdateListener) if isListener { - listener.InterfaceUpdated() + listener.InterfaceUpdated(ctx) } } for _, inbound := range r.inbound.Inbounds() { listener, isListener := inbound.(adapter.InterfaceUpdateListener) if isListener { - listener.InterfaceUpdated() + listener.InterfaceUpdated(ctx) } } for _, outbound := range r.outbound.Outbounds() { listener, isListener := outbound.(adapter.InterfaceUpdateListener) if isListener { - listener.InterfaceUpdated() + listener.InterfaceUpdated(ctx) } } @@ -502,8 +515,27 @@ func (r *NetworkManager) notifyInterfaceUpdate(defaultInterface *control.Interfa r.logger.Error("missing default interface") return } - r.pauseManager.NetworkWake() + updateContext, updateCancel := context.WithCancel(r.ctx) + r.interfaceUpdateAccess.Lock() + previousCancel := r.interfaceUpdateCancel + r.interfaceUpdateCancel = updateCancel + r.interfaceUpdateAccess.Unlock() + if previousCancel != nil { + previousCancel() + } + go func() { + defer updateCancel() + r.updateInterface(updateContext, defaultInterface) + }() +} + +func (r *NetworkManager) updateInterface(ctx context.Context, defaultInterface *control.Interface) { + r.interfaceUpdateRunAccess.Lock() + defer r.interfaceUpdateRunAccess.Unlock() + if ctx.Err() != nil { + return + } var options []string options = append(options, F.ToString("index ", defaultInterface.Index)) if C.IsAndroid && r.platformInterface == nil { @@ -531,20 +563,26 @@ func (r *NetworkManager) notifyInterfaceUpdate(defaultInterface *control.Interfa } } r.logger.Info("updated default interface ", defaultInterface.Name, ", ", strings.Join(options, ", ")) - r.UpdateWIFIState() + r.UpdateWIFIState(ctx) + if ctx.Err() != nil { + return + } r.updateNetworkEnvironment() - + if ctx.Err() != nil { + return + } if !r.started { return } - r.ResetNetwork() + r.ResetNetwork(ctx) } func (r *NetworkManager) notifyWindowsPowerEvent(event int) { switch event { case winpowrprof.EVENT_SUSPEND: r.pauseManager.DevicePause() - r.ResetNetwork() + r.cancelPowerUpdate() + r.ResetNetwork(r.ctx) case winpowrprof.EVENT_RESUME: if !r.pauseManager.IsDevicePaused() { return @@ -552,7 +590,28 @@ func (r *NetworkManager) notifyWindowsPowerEvent(event int) { fallthrough case winpowrprof.EVENT_RESUME_AUTOMATIC: r.pauseManager.DeviceWake() - r.ResetNetwork() + updateContext, updateCancel := context.WithCancel(r.ctx) + r.powerUpdateAccess.Lock() + previousCancel := r.powerUpdateCancel + r.powerUpdateCancel = updateCancel + r.powerUpdateAccess.Unlock() + if previousCancel != nil { + previousCancel() + } + go func() { + defer updateCancel() + r.ResetNetwork(updateContext) + }() + } +} + +func (r *NetworkManager) cancelPowerUpdate() { + r.powerUpdateAccess.Lock() + previousCancel := r.powerUpdateCancel + r.powerUpdateCancel = nil + r.powerUpdateAccess.Unlock() + if previousCancel != nil { + previousCancel() } } diff --git a/service/oomkiller/timer.go b/service/oomkiller/timer.go index f8ab1475..1f38956f 100644 --- a/service/oomkiller/timer.go +++ b/service/oomkiller/timer.go @@ -1,6 +1,7 @@ package oomkiller import ( + "context" runtimeDebug "runtime/debug" "sync" "time" @@ -203,14 +204,14 @@ func (t *adaptiveTimer) poll() { t.logger.Warn("memory growth rate critical (report only), usage: ", byteformats.FormatMemoryBytes(sample.usage), t.logDetails(sample)) } else { t.logger.Error("memory growth rate critical, usage: ", byteformats.FormatMemoryBytes(sample.usage), t.logDetails(sample), ", resetting network") - t.network.ResetNetwork() + t.network.ResetNetwork(context.Background()) } } else { if t.killerDisabled { t.logger.Warn("memory threshold reached (report only), usage: ", byteformats.FormatMemoryBytes(sample.usage), t.logDetails(sample)) } else { t.logger.Error("memory threshold reached, usage: ", byteformats.FormatMemoryBytes(sample.usage), t.logDetails(sample), ", resetting network") - t.network.ResetNetwork() + t.network.ResetNetwork(context.Background()) } } badCleanup()