Handle network update callbacks in background

This commit is contained in:
世界
2026-08-30 17:41:46 +08:00
parent cfddedb3da
commit 6720a27109
46 changed files with 382 additions and 134 deletions
+4 -3
View File
@@ -1,6 +1,7 @@
package adapter package adapter
import ( import (
"context"
"encoding/hex" "encoding/hex"
"net" "net"
"net/netip" "net/netip"
@@ -32,8 +33,8 @@ type NetworkManager interface {
PackageManager() tun.PackageManager PackageManager() tun.PackageManager
NeedWIFIState() bool NeedWIFIState() bool
WIFIState() WIFIState WIFIState() WIFIState
UpdateWIFIState() UpdateWIFIState(ctx context.Context)
ResetNetwork() ResetNetwork(ctx context.Context)
} }
type NetworkOptions struct { type NetworkOptions struct {
@@ -48,7 +49,7 @@ type NetworkOptions struct {
} }
type InterfaceUpdateListener interface { type InterfaceUpdateListener interface {
InterfaceUpdated() InterfaceUpdated(ctx context.Context)
} }
type WIFIState struct { type WIFIState struct {
+2 -1
View File
@@ -1,6 +1,7 @@
package adapter package adapter
import ( import (
"context"
"net/netip" "net/netip"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@@ -29,7 +30,7 @@ type PlatformInterface interface {
ClearDNSCache() ClearDNSCache()
RequestPermissionForWIFIState() error RequestPermissionForWIFIState() error
ReadWIFIState() WIFIState ReadWIFIState(ctx context.Context) WIFIState
UsePlatformConnectionOwnerFinder() bool UsePlatformConnectionOwnerFinder() bool
FindConnectionOwner(request *FindConnectionOwnerRequest) (*ConnectionOwner, error) FindConnectionOwner(request *FindConnectionOwnerRequest) (*ConnectionOwner, error)
+6 -3
View File
@@ -113,7 +113,7 @@ func (l *Listener) Start() error {
} }
err = systemProxy.Enable() err = systemProxy.Enable()
if err != nil { if err != nil {
return E.Cause(err, "set system proxy") return E.Errors(E.Cause(err, "set system proxy"), systemProxy.Close())
} }
l.systemProxy = systemProxy l.systemProxy = systemProxy
} }
@@ -123,8 +123,11 @@ func (l *Listener) Start() error {
func (l *Listener) Close() error { func (l *Listener) Close() error {
l.shutdown.Store(true) l.shutdown.Store(true)
var err error var err error
if l.systemProxy != nil && l.systemProxy.IsEnabled() { if l.systemProxy != nil {
err = l.systemProxy.Disable() if l.systemProxy.IsEnabled() {
err = l.systemProxy.Disable()
}
err = E.Errors(err, l.systemProxy.Close())
} }
return E.Errors(err, common.Close( return E.Errors(err, common.Close(
l.tcpListener, l.tcpListener,
+4
View File
@@ -55,6 +55,10 @@ func (p *AndroidSystemProxy) Enable() error {
return nil return nil
} }
func (p *AndroidSystemProxy) Close() error {
return nil
}
func (p *AndroidSystemProxy) Disable() error { func (p *AndroidSystemProxy) Disable() error {
err := p.runAndroidShell("settings", "put", "global", "http_proxy", ":0") err := p.runAndroidShell("settings", "put", "global", "http_proxy", ":0")
if err != nil { if err != nil {
+61 -6
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"strconv" "strconv"
"strings" "strings"
"sync"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-tun" "github.com/sagernet/sing-tun"
@@ -16,11 +17,14 @@ import (
) )
type DarwinSystemProxy struct { type DarwinSystemProxy struct {
ctx context.Context
monitor tun.DefaultInterfaceMonitor monitor tun.DefaultInterfaceMonitor
interfaceName string interfaceName string
element *list.Element[tun.DefaultInterfaceUpdateCallback] element *list.Element[tun.DefaultInterfaceUpdateCallback]
serverAddr M.Socksaddr serverAddr M.Socksaddr
supportSOCKS bool supportSOCKS bool
access sync.Mutex
updateCancel context.CancelFunc
isEnabled bool isEnabled bool
} }
@@ -30,6 +34,7 @@ func NewSystemProxy(ctx context.Context, serverAddr M.Socksaddr, supportSOCKS bo
return nil, E.New("missing interface monitor") return nil, E.New("missing interface monitor")
} }
proxy := &DarwinSystemProxy{ proxy := &DarwinSystemProxy{
ctx: ctx,
monitor: interfaceMonitor, monitor: interfaceMonitor,
serverAddr: serverAddr, serverAddr: serverAddr,
supportSOCKS: supportSOCKS, supportSOCKS: supportSOCKS,
@@ -39,14 +44,36 @@ func NewSystemProxy(ctx context.Context, serverAddr M.Socksaddr, supportSOCKS bo
} }
func (p *DarwinSystemProxy) IsEnabled() bool { func (p *DarwinSystemProxy) IsEnabled() bool {
p.access.Lock()
defer p.access.Unlock()
return p.isEnabled return p.isEnabled
} }
func (p *DarwinSystemProxy) Enable() error { 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 { 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) interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName)
if err != nil { if err != nil {
return err return err
@@ -67,19 +94,35 @@ func (p *DarwinSystemProxy) Disable() error {
} }
func (p *DarwinSystemProxy) routeUpdate(defaultInterface *control.Interface, flags int) { func (p *DarwinSystemProxy) routeUpdate(defaultInterface *control.Interface, flags int) {
if !p.isEnabled || defaultInterface == nil { if defaultInterface == nil {
return 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() newInterface := p.monitor.DefaultInterface()
if p.interfaceName == newInterface.Name { if newInterface == nil || p.interfaceName == newInterface.Name {
return nil return nil
} }
if p.interfaceName != "" { if p.interfaceName != "" {
_ = p.Disable() _ = p.disableLocked()
} }
p.interfaceName = newInterface.Name p.interfaceName = newInterface.Name
interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName) interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName)
@@ -87,15 +130,27 @@ func (p *DarwinSystemProxy) update0() error {
return err return err
} }
if p.supportSOCKS { 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() err = shell.Exec("networksetup", "-setsocksfirewallproxy", interfaceDisplayName, p.serverAddr.AddrString(), strconv.Itoa(int(p.serverAddr.Port))).Attach().Run()
} }
if err != nil { if err != nil {
return err 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() err = shell.Exec("networksetup", "-setwebproxy", interfaceDisplayName, p.serverAddr.AddrString(), strconv.Itoa(int(p.serverAddr.Port))).Attach().Run()
if err != nil { if err != nil {
return err 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() err = shell.Exec("networksetup", "-setsecurewebproxy", interfaceDisplayName, p.serverAddr.AddrString(), strconv.Itoa(int(p.serverAddr.Port))).Attach().Run()
if err != nil { if err != nil {
return err return err
+4
View File
@@ -118,6 +118,10 @@ func (p *LinuxSystemProxy) Enable() error {
return nil return nil
} }
func (p *LinuxSystemProxy) Close() error {
return nil
}
func (p *LinuxSystemProxy) Disable() error { func (p *LinuxSystemProxy) Disable() error {
if p.hasGSettings { if p.hasGSettings {
err := p.execute("gsettings", "set", "org.gnome.system.proxy", "mode", "none") err := p.execute("gsettings", "set", "org.gnome.system.proxy", "mode", "none")
+4
View File
@@ -36,6 +36,10 @@ func (p *WindowsSystemProxy) Enable() error {
return nil return nil
} }
func (p *WindowsSystemProxy) Close() error {
return nil
}
func (p *WindowsSystemProxy) Disable() error { func (p *WindowsSystemProxy) Disable() error {
err := wininet.ClearSystemProxy() err := wininet.ClearSystemProxy()
if err != nil { if err != nil {
+1
View File
@@ -4,4 +4,5 @@ type SystemProxy interface {
IsEnabled() bool IsEnabled() bool
Enable() error Enable() error
Disable() error Disable() error
Close() error
} }
+6 -2
View File
@@ -1,9 +1,13 @@
package settings package settings
import "github.com/sagernet/sing-box/adapter" import (
"context"
"github.com/sagernet/sing-box/adapter"
)
type WIFIMonitor interface { type WIFIMonitor interface {
ReadWIFIState() adapter.WIFIState ReadWIFIState(ctx context.Context) adapter.WIFIState
Start() error Start() error
Close() error Close() error
} }
+4 -2
View File
@@ -1,6 +1,8 @@
package settings package settings
import ( import (
"context"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
E "github.com/sagernet/sing/common/exceptions" 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") return nil, E.Cause(E.Errors(errors...), "no supported WIFI manager found")
} }
func (m *LinuxWIFIMonitor) ReadWIFIState() adapter.WIFIState { func (m *LinuxWIFIMonitor) ReadWIFIState(ctx context.Context) adapter.WIFIState {
return m.monitor.ReadWIFIState() return m.monitor.ReadWIFIState(ctx)
} }
func (m *LinuxWIFIMonitor) Start() error { func (m *LinuxWIFIMonitor) Start() error {
+4 -4
View File
@@ -35,8 +35,8 @@ func newConnManMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
return &connmanMonitor{conn: conn, callback: callback}, nil return &connmanMonitor{conn: conn, callback: callback}, nil
} }
func (m *connmanMonitor) ReadWIFIState() adapter.WIFIState { func (m *connmanMonitor) ReadWIFIState(ctx context.Context) adapter.WIFIState {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel() defer cancel()
cmObj := m.conn.Object("net.connman", "/") cmObj := m.conn.Object("net.connman", "/")
@@ -120,7 +120,7 @@ func (m *connmanMonitor) Start() error {
return err return err
} }
state := m.ReadWIFIState() state := m.ReadWIFIState(ctx)
go m.monitorSignals(ctx, m.signalChan, state) go m.monitorSignals(ctx, m.signalChan, state)
m.callback(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"), // 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. // not just the member name. This differs from the D-Bus signal member in the match rule.
if signal.Name == "net.connman.Service.PropertyChanged" { if signal.Name == "net.connman.Service.PropertyChanged" {
state := m.ReadWIFIState() state := m.ReadWIFIState(ctx)
if state != lastState { if state != lastState {
lastState = state lastState = state
m.callback(state) m.callback(state)
+4 -4
View File
@@ -35,8 +35,8 @@ func newIWDMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
return &iwdMonitor{conn: conn, callback: callback}, nil return &iwdMonitor{conn: conn, callback: callback}, nil
} }
func (m *iwdMonitor) ReadWIFIState() adapter.WIFIState { func (m *iwdMonitor) ReadWIFIState(ctx context.Context) adapter.WIFIState {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel() defer cancel()
iwdObj := m.conn.Object("net.connman.iwd", "/") iwdObj := m.conn.Object("net.connman.iwd", "/")
@@ -144,7 +144,7 @@ func (m *iwdMonitor) Start() error {
return err return err
} }
state := m.ReadWIFIState() state := m.ReadWIFIState(ctx)
go m.monitorSignals(ctx, m.signalChan, state) go m.monitorSignals(ctx, m.signalChan, state)
m.callback(state) m.callback(state)
@@ -161,7 +161,7 @@ func (m *iwdMonitor) monitorSignals(ctx context.Context, signalChan chan *dbus.S
return return
} }
if signal.Name == "org.freedesktop.DBus.Properties.PropertiesChanged" { if signal.Name == "org.freedesktop.DBus.Properties.PropertiesChanged" {
state := m.ReadWIFIState() state := m.ReadWIFIState(ctx)
if state != lastState { if state != lastState {
lastState = state lastState = state
m.callback(state) m.callback(state)
+4 -4
View File
@@ -36,8 +36,8 @@ func newNetworkManagerMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, er
return &networkManagerMonitor{conn: conn, callback: callback}, nil return &networkManagerMonitor{conn: conn, callback: callback}, nil
} }
func (m *networkManagerMonitor) ReadWIFIState() adapter.WIFIState { func (m *networkManagerMonitor) ReadWIFIState(ctx context.Context) adapter.WIFIState {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel() defer cancel()
nmObj := m.conn.Object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager") nmObj := m.conn.Object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")
@@ -119,7 +119,7 @@ func (m *networkManagerMonitor) Start() error {
return err return err
} }
state := m.ReadWIFIState() state := m.ReadWIFIState(ctx)
go m.monitorSignals(ctx, m.signalChan, state) go m.monitorSignals(ctx, m.signalChan, state)
m.callback(state) m.callback(state)
@@ -136,7 +136,7 @@ func (m *networkManagerMonitor) monitorSignals(ctx context.Context, signalChan c
return return
} }
if signal.Name == "org.freedesktop.DBus.Properties.PropertiesChanged" { if signal.Name == "org.freedesktop.DBus.Properties.PropertiesChanged" {
state := m.ReadWIFIState() state := m.ReadWIFIState(ctx)
if state != lastState { if state != lastState {
lastState = state lastState = state
m.callback(state) m.callback(state)
+12 -3
View File
@@ -52,7 +52,7 @@ func newWpaSupplicantMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, err
return nil, os.ErrNotExist return nil, os.ErrNotExist
} }
func (m *wpaSupplicantMonitor) ReadWIFIState() adapter.WIFIState { func (m *wpaSupplicantMonitor) ReadWIFIState(ctx context.Context) adapter.WIFIState {
id := wpaSocketCounter.Add(1) id := wpaSocketCounter.Add(1)
localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-%d-%d", os.Getpid(), id), Net: "unixgram"} localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-%d-%d", os.Getpid(), id), Net: "unixgram"}
remoteAddr := &net.UnixAddr{Name: m.socketPath, Net: "unixgram"} remoteAddr := &net.UnixAddr{Name: m.socketPath, Net: "unixgram"}
@@ -63,6 +63,15 @@ func (m *wpaSupplicantMonitor) ReadWIFIState() adapter.WIFIState {
defer conn.Close() defer conn.Close()
conn.SetDeadline(time.Now().Add(3 * time.Second)) 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") status, err := m.sendCommand(conn, "STATUS")
if err != nil { if err != nil {
@@ -124,7 +133,7 @@ func (m *wpaSupplicantMonitor) Start() error {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
m.cancel = cancel m.cancel = cancel
state := m.ReadWIFIState() state := m.ReadWIFIState(ctx)
go m.monitorEvents(ctx, state) go m.monitorEvents(ctx, state)
m.callback(state) m.callback(state)
@@ -202,7 +211,7 @@ func (m *wpaSupplicantMonitor) monitorEvents(ctx context.Context, lastState adap
debounceTimer.Stop() debounceTimer.Stop()
} }
debounceTimer = time.AfterFunc(500*time.Millisecond, func() { debounceTimer = time.AfterFunc(500*time.Millisecond, func() {
state := m.ReadWIFIState() state := m.ReadWIFIState(ctx)
if state != lastState { if state != lastState {
lastState = state lastState = state
m.callback(state) m.callback(state)
+2 -1
View File
@@ -4,6 +4,7 @@
package settings package settings
import ( import (
"context"
"os" "os"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -15,7 +16,7 @@ func NewWIFIMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
return nil, os.ErrInvalid return nil, os.ErrInvalid
} }
func (m *stubWIFIMonitor) ReadWIFIState() adapter.WIFIState { func (m *stubWIFIMonitor) ReadWIFIState(ctx context.Context) adapter.WIFIState {
return adapter.WIFIState{} return adapter.WIFIState{}
} }
+3 -3
View File
@@ -45,7 +45,7 @@ func NewWIFIMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
}, nil }, nil
} }
func (m *windowsWIFIMonitor) ReadWIFIState() adapter.WIFIState { func (m *windowsWIFIMonitor) ReadWIFIState(ctx context.Context) adapter.WIFIState {
interfaces, err := winwlanapi.EnumInterfaces(m.handle) interfaces, err := winwlanapi.EnumInterfaces(m.handle)
if err != nil || len(interfaces) == 0 { if err != nil || len(interfaces) == 0 {
return adapter.WIFIState{} return adapter.WIFIState{}
@@ -92,7 +92,7 @@ func (m *windowsWIFIMonitor) Start() error {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
m.cancel = cancel m.cancel = cancel
m.lastState = m.ReadWIFIState() m.lastState = m.ReadWIFIState(ctx)
callbackFunc := func(data *winwlanapi.NotificationData, callbackContext uintptr) uintptr { callbackFunc := func(data *winwlanapi.NotificationData, callbackContext uintptr) uintptr {
if data.NotificationSource != winwlanapi.NotificationSourceACM { if data.NotificationSource != winwlanapi.NotificationSourceACM {
@@ -126,7 +126,7 @@ func (m *windowsWIFIMonitor) checkAndNotify() {
m.mutex.Lock() m.mutex.Lock()
defer m.mutex.Unlock() defer m.mutex.Unlock()
state := m.ReadWIFIState() state := m.ReadWIFIState(context.Background())
if state != m.lastState { if state != m.lastState {
m.lastState = state m.lastState = state
if m.callback != nil { if m.callback != nil {
+39 -11
View File
@@ -55,6 +55,8 @@ type Transport struct {
platformInterface adapter.PlatformInterface platformInterface adapter.PlatformInterface
interfaceName string interfaceName string
interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback] interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback]
updateAccess sync.Mutex
updateCancel context.CancelFunc
refreshAccess sync.Mutex refreshAccess sync.Mutex
savedState atomic.Pointer[transportState] savedState atomic.Pointer[transportState]
ndots int ndots int
@@ -126,6 +128,13 @@ func (t *Transport) Close() error {
if t.interfaceCallback != nil { if t.interfaceCallback != nil {
t.networkManager.InterfaceMonitor().UnregisterCallback(t.interfaceCallback) 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() t.refreshAccess.Lock()
defer t.refreshAccess.Unlock() defer t.refreshAccess.Unlock()
state := t.savedState.Swap(nil) state := t.savedState.Swap(nil)
@@ -243,7 +252,7 @@ func (t *Transport) fetch() error {
return nil return nil
} }
} }
return t.updateServersLocked() return t.updateServersLocked(t.ctx)
} }
func (t *Transport) startRefresh() { func (t *Transport) startRefresh() {
@@ -256,7 +265,7 @@ func (t *Transport) startRefresh() {
if state != nil && time.Since(state.updatedAt) < C.DHCPTTL { if state != nil && time.Since(state.updatedAt) < C.DHCPTTL {
return return
} }
err := t.updateServersLocked() err := t.updateServersLocked(t.ctx)
if err != nil { if err != nil {
if errors.Is(err, errInterfaceIsCellular) && t.optional { if errors.Is(err, errInterfaceIsCellular) && t.optional {
t.logger.Debug(E.Cause(err, "dhcp: refresh DNS servers")) 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() iface, err := t.fetchInterface()
if err != nil { if err != nil {
t.storeFailureLocked(err) t.storeFailureLocked(err)
return E.Cause(err, "prepare interface") return E.Cause(err, "prepare interface")
} }
t.logger.Info("dhcp: query DNS servers on ", iface.Name) 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) err = t.fetchServers0(fetchCtx, iface)
cancel() cancel()
if err != nil { if err != nil {
if ctx.Err() != nil {
return err
}
t.storeFailureLocked(err) t.storeFailureLocked(err)
return err return err
} }
@@ -331,16 +343,28 @@ func (t *Transport) storeFailureLocked(err error) {
} }
func (t *Transport) interfaceUpdated(defaultInterface *control.Interface, flags int) { func (t *Transport) interfaceUpdated(defaultInterface *control.Interface, flags int) {
t.refreshAccess.Lock() updateContext, updateCancel := context.WithCancel(t.ctx)
err := t.updateServersLocked() t.updateAccess.Lock()
t.refreshAccess.Unlock() previousCancel := t.updateCancel
if err != nil { 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 { if errors.Is(err, errInterfaceIsCellular) && t.optional {
t.logger.Debug(E.Cause(errInterfaceIsCellular, "dhcp: update DNS servers")) t.logger.Debug(E.Cause(errInterfaceIsCellular, "dhcp: update DNS servers"))
} else { } else {
t.logger.Error("dhcp: update DNS servers: ", err) t.logger.Error("dhcp: update DNS servers: ", err)
} }
} }()
} }
func (t *Transport) fetchServers0(ctx context.Context, iface *control.Interface) error { 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 err error
) )
for range 5 { 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) { if err == nil || !errors.Is(err, syscall.EADDRINUSE) {
break break
} }
time.Sleep(time.Second) select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(time.Second):
}
} }
if err != nil { if err != nil {
return err return err
+57 -9
View File
@@ -58,6 +58,10 @@ type DBusResolvedResolver struct {
interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback] interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback]
systemBus *dbus.Conn systemBus *dbus.Conn
savedServerSet atomic.Pointer[resolvedServerSet] savedServerSet atomic.Pointer[resolvedServerSet]
updateAccess sync.Mutex
updateCancel context.CancelFunc
updateRunAccess sync.Mutex
closed bool
closeOnce sync.Once closeOnce sync.Once
} }
@@ -95,7 +99,7 @@ func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (Reso
} }
func (t *DBusResolvedResolver) Start() error { func (t *DBusResolvedResolver) Start() error {
t.updateStatus() t.updateStatus(t.ctx)
t.interfaceCallback = t.interfaceMonitor.RegisterCallback(t.updateDefaultInterface) t.interfaceCallback = t.interfaceMonitor.RegisterCallback(t.updateDefaultInterface)
err := t.systemBus.BusObject().AddMatchSignal( err := t.systemBus.BusObject().AddMatchSignal(
"org.freedesktop.DBus", "org.freedesktop.DBus",
@@ -122,7 +126,17 @@ func (t *DBusResolvedResolver) Start() error {
func (t *DBusResolvedResolver) Close() error { func (t *DBusResolvedResolver) Close() error {
var closeErr error var closeErr error
t.closeOnce.Do(func() { 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) serverSet := t.savedServerSet.Swap(nil)
t.updateRunAccess.Unlock()
if serverSet != nil { if serverSet != nil {
closeErr = serverSet.Close() closeErr = serverSet.Close()
} }
@@ -174,7 +188,7 @@ func (t *DBusResolvedResolver) Exchange(ctx context.Context, message *mDNS.Msg)
if err == nil { if err == nil {
return response, nil return response, nil
} }
t.updateStatus() t.updateStatus(t.ctx)
refreshedServerSet := t.savedServerSet.Load() refreshedServerSet := t.savedServerSet.Load()
if refreshedServerSet == nil || refreshedServerSet == serverSet { if refreshedServerSet == nil || refreshedServerSet == serverSet {
return nil, err return nil, err
@@ -196,7 +210,7 @@ func (t *DBusResolvedResolver) ExchangeAsync(ctx context.Context, message *mDNS.
return return
} }
go func() { go func() {
t.updateStatus() t.updateStatus(t.ctx)
refreshedServerSet := t.savedServerSet.Load() refreshedServerSet := t.savedServerSet.Load()
if refreshedServerSet == nil || refreshedServerSet == serverSet { if refreshedServerSet == nil || refreshedServerSet == serverSet {
callback(nil, err) callback(nil, err)
@@ -240,18 +254,44 @@ func (t *DBusResolvedResolver) loopUpdateStatus() {
if !loaded || newOwner == "" { if !loaded || newOwner == "" {
continue continue
} }
t.updateStatus() t.postUpdateStatus()
case "org.freedesktop.DBus.Properties.PropertiesChanged": case "org.freedesktop.DBus.Properties.PropertiesChanged":
if !shouldUpdateResolvedServerSet(signal) { if !shouldUpdateResolvedServerSet(signal) {
continue continue
} }
t.updateStatus() t.postUpdateStatus()
} }
} }
} }
func (t *DBusResolvedResolver) updateStatus() { func (t *DBusResolvedResolver) postUpdateStatus() {
serverSet, err := t.checkResolved(context.Background()) 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) oldServerSet := t.savedServerSet.Swap(serverSet)
if oldServerSet != nil { if oldServerSet != nil {
_ = oldServerSet.Close() _ = oldServerSet.Close()
@@ -291,7 +331,7 @@ func (t *DBusResolvedResolver) exchangeServerSet(ctx context.Context, message *m
func (t *DBusResolvedResolver) checkResolved(ctx context.Context) (*resolvedServerSet, error) { func (t *DBusResolvedResolver) checkResolved(ctx context.Context) (*resolvedServerSet, error) {
dbusObject := t.systemBus.Object("org.freedesktop.resolve1", "/org/freedesktop/resolve1") 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 { if err != nil {
return nil, err return nil, err
} }
@@ -321,10 +361,18 @@ func (t *DBusResolvedResolver) checkResolved(ctx context.Context) (*resolvedServ
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = ctx.Err()
if err != nil {
return nil, err
}
linkDNSEx, err := loadResolvedLinkDNSEx(linkObject) linkDNSEx, err := loadResolvedLinkDNSEx(linkObject)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = ctx.Err()
if err != nil {
return nil, err
}
linkDNS, err := loadResolvedLinkDNS(linkObject) linkDNS, err := loadResolvedLinkDNS(linkObject)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -570,5 +618,5 @@ func shouldUpdateResolvedServerSet(signal *dbus.Signal) bool {
} }
func (t *DBusResolvedResolver) updateDefaultInterface(defaultInterface *control.Interface, flags int) { func (t *DBusResolvedResolver) updateDefaultInterface(defaultInterface *control.Interface, flags int) {
t.updateStatus() t.postUpdateStatus()
} }
+2 -1
View File
@@ -3,6 +3,7 @@
package main package main
import ( import (
"context"
"net/netip" "net/netip"
"os" "os"
"os/exec" "os/exec"
@@ -120,7 +121,7 @@ func (p *linuxPlatformInterface) RequestPermissionForWIFIState() error {
return nil return nil
} }
func (p *linuxPlatformInterface) ReadWIFIState() adapter.WIFIState { func (p *linuxPlatformInterface) ReadWIFIState(ctx context.Context) adapter.WIFIState {
return adapter.WIFIState{} return adapter.WIFIState{}
} }
+1 -1
View File
@@ -135,7 +135,7 @@ func (p *windowsPlatformInterface) RequestPermissionForWIFIState() error {
return nil return nil
} }
func (p *windowsPlatformInterface) ReadWIFIState() adapter.WIFIState { func (p *windowsPlatformInterface) ReadWIFIState(ctx context.Context) adapter.WIFIState {
return adapter.WIFIState{} return adapter.WIFIState{}
} }
+3 -3
View File
@@ -24,7 +24,7 @@ import (
func connectionRouter(ctx context.Context, network adapter.NetworkManager, trafficManager *trafficcontrol.Manager) http.Handler { func connectionRouter(ctx context.Context, network adapter.NetworkManager, trafficManager *trafficcontrol.Manager) http.Handler {
r := chi.NewRouter() r := chi.NewRouter()
r.Get("/", getConnections(ctx, trafficManager)) r.Get("/", getConnections(ctx, trafficManager))
r.Delete("/", closeAllConnections(network, trafficManager)) r.Delete("/", closeAllConnections(ctx, network, trafficManager))
r.Delete("/{id}", closeConnection(trafficManager)) r.Delete("/{id}", closeConnection(trafficManager))
return r 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) { return func(w http.ResponseWriter, r *http.Request) {
trafficManager.CloseAllConnections() trafficManager.CloseAllConnections()
network.ResetNetwork() network.ResetNetwork(ctx)
render.NoContent(w, r) render.NoContent(w, r)
} }
} }
+2 -2
View File
@@ -262,7 +262,7 @@ func (s *CommandServer) ResetNetwork() {
if instance == nil || instance.Box() == nil { if instance == nil || instance.Box() == nil {
return return
} }
instance.Box().Network().ResetNetwork() instance.Box().Network().ResetNetwork(context.Background())
} }
func (s *CommandServer) UpdateWIFIState() { func (s *CommandServer) UpdateWIFIState() {
@@ -270,7 +270,7 @@ func (s *CommandServer) UpdateWIFIState() {
if instance == nil || instance.Box() == nil { if instance == nil || instance.Box() == nil {
return return
} }
instance.Box().Network().UpdateWIFIState() instance.Box().Network().UpdateWIFIState(context.Background())
} }
type platformHandler CommandServer type platformHandler CommandServer
+1 -1
View File
@@ -127,7 +127,7 @@ func (s *platformInterfaceStub) UsePlatformWIFIMonitor() bool {
return false return false
} }
func (s *platformInterfaceStub) ReadWIFIState() adapter.WIFIState { func (s *platformInterfaceStub) ReadWIFIState(ctx context.Context) adapter.WIFIState {
return adapter.WIFIState{} return adapter.WIFIState{}
} }
+2 -1
View File
@@ -1,6 +1,7 @@
package libbox package libbox
import ( import (
"context"
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"errors" "errors"
@@ -175,7 +176,7 @@ func (w *platformInterfaceWrapper) UsePlatformWIFIMonitor() bool {
return true return true
} }
func (w *platformInterfaceWrapper) ReadWIFIState() adapter.WIFIState { func (w *platformInterfaceWrapper) ReadWIFIState(ctx context.Context) adapter.WIFIState {
wifiState := w.iif.ReadWIFIState() wifiState := w.iif.ReadWIFIState()
if wifiState == nil { if wifiState == nil {
return adapter.WIFIState{} return adapter.WIFIState{}
+1 -1
View File
@@ -77,7 +77,7 @@ func (i *Inbound) Start(stage adapter.StartStage) error {
return i.listener.Start() return i.listener.Start()
} }
func (i *Inbound) InterfaceUpdated() { func (i *Inbound) InterfaceUpdated(ctx context.Context) {
i.udpNat.Purge() i.udpNat.Purge()
} }
+1 -1
View File
@@ -121,7 +121,7 @@ func (h *Outbound) fetchMyAddresses() {
h.myAddresses.Store(myAddresses) h.myAddresses.Store(myAddresses)
} }
func (h *Outbound) InterfaceUpdated() { func (h *Outbound) InterfaceUpdated(ctx context.Context) {
h.fetchMyAddresses() h.fetchMyAddresses()
if h.icmpPort != nil { if h.icmpPort != nil {
h.icmpPort.Close() h.icmpPort.Close()
+17 -9
View File
@@ -45,6 +45,7 @@ type URLTest struct {
tolerance uint16 tolerance uint16
idleTimeout time.Duration idleTimeout time.Duration
group *URLTestGroup group *URLTestGroup
checkAccess sync.Mutex
interruptExternalConnections bool interruptExternalConnections bool
} }
@@ -114,14 +115,14 @@ func (s *URLTest) URLTest(ctx context.Context) (map[string]uint16, error) {
} }
func (s *URLTest) CheckOutbounds() { func (s *URLTest) CheckOutbounds() {
s.group.CheckOutbounds(true) s.group.CheckOutbounds(s.ctx, true)
} }
func (s *URLTest) PerformUpdateCheck() { func (s *URLTest) PerformUpdateCheck() {
s.group.performUpdateCheck() s.group.performUpdateCheck()
} }
func (s *URLTest) InterfaceUpdated() { func (s *URLTest) InterfaceUpdated(ctx context.Context) {
group := s.group group := s.group
if group == nil { if group == nil {
return return
@@ -129,7 +130,14 @@ func (s *URLTest) InterfaceUpdated() {
if group.pause.IsDevicePaused() || group.pause.IsNetworkPaused() { if group.pause.IsDevicePaused() || group.pause.IsNetworkPaused() {
return 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) { 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() defer g.access.Unlock()
g.started = true g.started = true
g.lastActive.Store(time.Now()) g.lastActive.Store(time.Now())
go g.CheckOutbounds(false) go g.CheckOutbounds(g.ctx, false)
} }
func (g *URLTestGroup) Touch() { 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{}) { func (g *URLTestGroup) loopCheck(ticker *time.Ticker, closeChan <-chan struct{}) {
if time.Since(g.lastActive.Load()) > g.interval { if time.Since(g.lastActive.Load()) > g.interval {
g.lastActive.Store(time.Now()) g.lastActive.Store(time.Now())
g.CheckOutbounds(false) g.CheckOutbounds(g.ctx, false)
} }
for { for {
select { select {
@@ -349,12 +357,12 @@ func (g *URLTestGroup) loopCheck(ticker *time.Ticker, closeChan <-chan struct{})
g.access.Unlock() g.access.Unlock()
return return
} }
g.CheckOutbounds(false) g.CheckOutbounds(g.ctx, false)
} }
} }
func (g *URLTestGroup) CheckOutbounds(force bool) { func (g *URLTestGroup) CheckOutbounds(ctx context.Context, force bool) {
_, _ = g.urlTest(g.ctx, force) _, _ = g.urlTest(ctx, force)
} }
func (g *URLTestGroup) URLTest(ctx context.Context) (map[string]uint16, error) { 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 continue
} }
b.Go(realTag, func() (any, error) { b.Go(realTag, func() (any, error) {
testCtx, cancel := context.WithTimeout(g.ctx, C.TCPTimeout) testCtx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
defer cancel() defer cancel()
testChan := make(chan urlTestResult, 1) testChan := make(chan urlTestResult, 1)
go func() { go func() {
+1 -1
View File
@@ -115,7 +115,7 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
return h.client.ListenPacket(ctx, destination) return h.client.ListenPacket(ctx, destination)
} }
func (h *Outbound) InterfaceUpdated() { func (h *Outbound) InterfaceUpdated(ctx context.Context) {
h.client.CloseWithError(E.New("network changed")) h.client.CloseWithError(E.New("network changed"))
} }
+1 -1
View File
@@ -281,7 +281,7 @@ func (h *Inbound) Start(stage adapter.StartStage) error {
return h.service.Start(packetConn) return h.service.Start(packetConn)
} }
func (h *Inbound) InterfaceUpdated() { func (h *Inbound) InterfaceUpdated(ctx context.Context) {
h.service.Reset() h.service.Reset()
} }
+1 -1
View File
@@ -203,7 +203,7 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
return h.client.ListenPacket(ctx) return h.client.ListenPacket(ctx)
} }
func (h *Outbound) InterfaceUpdated() { func (h *Outbound) InterfaceUpdated(ctx context.Context) {
h.client.CloseWithError(E.New("network changed")) h.client.CloseWithError(E.New("network changed"))
} }
+1 -1
View File
@@ -254,7 +254,7 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
return h.uotClient.ListenPacket(ctx, destination) return h.uotClient.ListenPacket(ctx, destination)
} }
func (h *Outbound) InterfaceUpdated() { func (h *Outbound) InterfaceUpdated(ctx context.Context) {
h.client.Engine().CloseAllConnections() h.client.Engine().CloseAllConnections()
} }
+1 -1
View File
@@ -486,7 +486,7 @@ func (e *Endpoint) Close() error {
return err return err
} }
func (e *Endpoint) InterfaceUpdated() { func (e *Endpoint) InterfaceUpdated(ctx context.Context) {
e.client.RestartSession() e.client.RestartSession()
} }
+1 -1
View File
@@ -658,7 +658,7 @@ func (c *ClientEndpoint) Close() error {
return err return err
} }
func (c *ClientEndpoint) InterfaceUpdated() { func (c *ClientEndpoint) InterfaceUpdated(ctx context.Context) {
c.client.RestartSession() c.client.RestartSession()
} }
+1 -1
View File
@@ -85,7 +85,7 @@ func (t *TProxy) Start(stage adapter.StartStage) error {
return err return err
} }
func (t *TProxy) InterfaceUpdated() { func (t *TProxy) InterfaceUpdated(ctx context.Context) {
t.udpNat.Purge() t.udpNat.Purge()
} }
+1 -1
View File
@@ -130,7 +130,7 @@ func (h *Outbound) MultiplexEnabled() bool {
return h.multiplexDialer != nil return h.multiplexDialer != nil
} }
func (h *Outbound) InterfaceUpdated() { func (h *Outbound) InterfaceUpdated(ctx context.Context) {
if h.multiplexDialer != nil { if h.multiplexDialer != nil {
h.multiplexDialer.Reset() h.multiplexDialer.Reset()
} }
+1 -1
View File
@@ -139,7 +139,7 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
return packetConn, nil return packetConn, nil
} }
func (h *Outbound) InterfaceUpdated() { func (h *Outbound) InterfaceUpdated(ctx context.Context) {
h.client.Reset() h.client.Reset()
} }
+1 -1
View File
@@ -206,7 +206,7 @@ func (s *Outbound) connect(ctx context.Context) (client *ssh.Client, err error)
return client, nil return client, nil
} }
func (s *Outbound) InterfaceUpdated() { func (s *Outbound) InterfaceUpdated(ctx context.Context) {
common.Close(s.clientConn) common.Close(s.clientConn)
} }
+1 -1
View File
@@ -740,7 +740,7 @@ func (t *Endpoint) Close() error {
return err return err
} }
func (t *Endpoint) InterfaceUpdated() { func (t *Endpoint) InterfaceUpdated(ctx context.Context) {
if !t.started.Load() { if !t.started.Load() {
return return
} }
+1 -1
View File
@@ -113,7 +113,7 @@ func (h *Outbound) MultiplexEnabled() bool {
return h.multiplexDialer != nil return h.multiplexDialer != nil
} }
func (h *Outbound) InterfaceUpdated() { func (h *Outbound) InterfaceUpdated(ctx context.Context) {
if h.transport != nil { if h.transport != nil {
h.transport.Close() h.transport.Close()
} }
+1 -1
View File
@@ -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")) _ = h.client.CloseWithError(E.New("network changed"))
} }
+1 -1
View File
@@ -505,7 +505,7 @@ func (t *Inbound) updateRouteAddressSet(it adapter.RuleSet) {
t.routeExcludeAddressSet = nil t.routeExcludeAddressSet = nil
} }
func (t *Inbound) InterfaceUpdated() { func (t *Inbound) InterfaceUpdated(ctx context.Context) {
tunStack := t.tunStack tunStack := t.tunStack
if tunStack != nil { if tunStack != nil {
tunStack.ResetNetwork() tunStack.ResetNetwork()
+1 -1
View File
@@ -133,7 +133,7 @@ func (h *Outbound) MultiplexEnabled() bool {
return h.multiplexDialer != nil return h.multiplexDialer != nil
} }
func (h *Outbound) InterfaceUpdated() { func (h *Outbound) InterfaceUpdated(ctx context.Context) {
if h.transport != nil { if h.transport != nil {
h.transport.Close() h.transport.Close()
} }
+1 -1
View File
@@ -111,7 +111,7 @@ func (h *Outbound) MultiplexEnabled() bool {
return h.multiplexDialer != nil return h.multiplexDialer != nil
} }
func (h *Outbound) InterfaceUpdated() { func (h *Outbound) InterfaceUpdated(ctx context.Context) {
if h.transport != nil { if h.transport != nil {
h.transport.Close() h.transport.Close()
} }
+14 -1
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"net" "net"
"net/netip" "net/netip"
"sync"
"sync/atomic" "sync/atomic"
"time" "time"
@@ -42,6 +43,7 @@ type Endpoint struct {
logger logger.ContextLogger logger logger.ContextLogger
localAddresses []netip.Prefix localAddresses []netip.Prefix
endpoint *wireguard.Endpoint endpoint *wireguard.Endpoint
bindAccess sync.Mutex
started atomic.Bool started atomic.Bool
} }
@@ -145,14 +147,25 @@ func (w *Endpoint) Start(stage adapter.StartStage) error {
} }
func (w *Endpoint) Close() error { func (w *Endpoint) Close() error {
w.bindAccess.Lock()
w.started.Store(false) w.started.Store(false)
w.bindAccess.Unlock()
return w.endpoint.Close() return w.endpoint.Close()
} }
func (w *Endpoint) InterfaceUpdated() { func (w *Endpoint) InterfaceUpdated(ctx context.Context) {
if !w.started.Load() { if !w.started.Load() {
return 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() err := w.endpoint.BindUpdate()
if err != nil { if err != nil {
w.logger.Error(E.Cause(err, "update bind")) w.logger.Error(E.Cause(err, "update bind"))
+98 -39
View File
@@ -34,32 +34,37 @@ import (
var _ adapter.NetworkManager = (*NetworkManager)(nil) var _ adapter.NetworkManager = (*NetworkManager)(nil)
type NetworkManager struct { type NetworkManager struct {
ctx context.Context ctx context.Context
logger logger.ContextLogger logger logger.ContextLogger
router adapter.Router router adapter.Router
interfaceFinder *control.DefaultInterfaceFinder interfaceFinder *control.DefaultInterfaceFinder
networkInterfaces common.TypedValue[[]adapter.NetworkInterface] networkInterfaces common.TypedValue[[]adapter.NetworkInterface]
autoDetectInterface bool autoDetectInterface bool
defaultOptions adapter.NetworkOptions defaultOptions adapter.NetworkOptions
autoRedirectOutputMark uint32 autoRedirectOutputMark uint32
networkMonitor tun.NetworkUpdateMonitor networkMonitor tun.NetworkUpdateMonitor
interfaceMonitor tun.DefaultInterfaceMonitor interfaceMonitor tun.DefaultInterfaceMonitor
packageManager tun.PackageManager packageManager tun.PackageManager
powerListener winpowrprof.EventListener powerListener winpowrprof.EventListener
pauseManager pause.Manager pauseManager pause.Manager
platformInterface adapter.PlatformInterface platformInterface adapter.PlatformInterface
connectionManager adapter.ConnectionManager connectionManager adapter.ConnectionManager
endpoint adapter.EndpointManager endpoint adapter.EndpointManager
inbound adapter.InboundManager inbound adapter.InboundManager
outbound adapter.OutboundManager outbound adapter.OutboundManager
needWIFIState bool needWIFIState bool
wifiMonitor settings.WIFIMonitor wifiMonitor settings.WIFIMonitor
wifiState adapter.WIFIState wifiState adapter.WIFIState
networkEnvironment uint64 networkEnvironment uint64
stateAccess sync.RWMutex stateAccess sync.RWMutex
environmentUpdateAccess sync.Mutex environmentUpdateAccess sync.Mutex
environmentUpdateTimer *time.Timer environmentUpdateTimer *time.Timer
started bool 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) { 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() monitor.Finish()
} }
r.interfaceUpdateAccess.Lock()
interfaceUpdateCancel := r.interfaceUpdateCancel
r.interfaceUpdateCancel = nil
r.interfaceUpdateAccess.Unlock()
if interfaceUpdateCancel != nil {
interfaceUpdateCancel()
}
r.cancelPowerUpdate()
if r.networkMonitor != nil { if r.networkMonitor != nil {
monitor.Start("close network monitor") monitor.Start("close network monitor")
err = E.Append(err, r.networkMonitor.Close(), func(err error) error { 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 var state adapter.WIFIState
if r.wifiMonitor != nil { if r.wifiMonitor != nil {
state = r.wifiMonitor.ReadWIFIState() state = r.wifiMonitor.ReadWIFIState(ctx)
} else if r.platformInterface != nil && r.platformInterface.UsePlatformWIFIMonitor() { } else if r.platformInterface != nil && r.platformInterface.UsePlatformWIFIMonitor() {
state = r.platformInterface.ReadWIFIState() state = r.platformInterface.ReadWIFIState(ctx)
} else { } else {
return return
} }
r.onWIFIStateChanged(state) r.onWIFIStateChanged(state)
} }
func (r *NetworkManager) ResetNetwork() { func (r *NetworkManager) ResetNetwork(ctx context.Context) {
if r.connectionManager != nil { if r.connectionManager != nil {
r.connectionManager.CloseAll() r.connectionManager.CloseAll()
} }
@@ -475,21 +488,21 @@ func (r *NetworkManager) ResetNetwork() {
for _, endpoint := range r.endpoint.Endpoints() { for _, endpoint := range r.endpoint.Endpoints() {
listener, isListener := endpoint.(adapter.InterfaceUpdateListener) listener, isListener := endpoint.(adapter.InterfaceUpdateListener)
if isListener { if isListener {
listener.InterfaceUpdated() listener.InterfaceUpdated(ctx)
} }
} }
for _, inbound := range r.inbound.Inbounds() { for _, inbound := range r.inbound.Inbounds() {
listener, isListener := inbound.(adapter.InterfaceUpdateListener) listener, isListener := inbound.(adapter.InterfaceUpdateListener)
if isListener { if isListener {
listener.InterfaceUpdated() listener.InterfaceUpdated(ctx)
} }
} }
for _, outbound := range r.outbound.Outbounds() { for _, outbound := range r.outbound.Outbounds() {
listener, isListener := outbound.(adapter.InterfaceUpdateListener) listener, isListener := outbound.(adapter.InterfaceUpdateListener)
if isListener { if isListener {
listener.InterfaceUpdated() listener.InterfaceUpdated(ctx)
} }
} }
@@ -502,8 +515,27 @@ func (r *NetworkManager) notifyInterfaceUpdate(defaultInterface *control.Interfa
r.logger.Error("missing default interface") r.logger.Error("missing default interface")
return return
} }
r.pauseManager.NetworkWake() 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 var options []string
options = append(options, F.ToString("index ", defaultInterface.Index)) options = append(options, F.ToString("index ", defaultInterface.Index))
if C.IsAndroid && r.platformInterface == nil { 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.logger.Info("updated default interface ", defaultInterface.Name, ", ", strings.Join(options, ", "))
r.UpdateWIFIState() r.UpdateWIFIState(ctx)
if ctx.Err() != nil {
return
}
r.updateNetworkEnvironment() r.updateNetworkEnvironment()
if ctx.Err() != nil {
return
}
if !r.started { if !r.started {
return return
} }
r.ResetNetwork() r.ResetNetwork(ctx)
} }
func (r *NetworkManager) notifyWindowsPowerEvent(event int) { func (r *NetworkManager) notifyWindowsPowerEvent(event int) {
switch event { switch event {
case winpowrprof.EVENT_SUSPEND: case winpowrprof.EVENT_SUSPEND:
r.pauseManager.DevicePause() r.pauseManager.DevicePause()
r.ResetNetwork() r.cancelPowerUpdate()
r.ResetNetwork(r.ctx)
case winpowrprof.EVENT_RESUME: case winpowrprof.EVENT_RESUME:
if !r.pauseManager.IsDevicePaused() { if !r.pauseManager.IsDevicePaused() {
return return
@@ -552,7 +590,28 @@ func (r *NetworkManager) notifyWindowsPowerEvent(event int) {
fallthrough fallthrough
case winpowrprof.EVENT_RESUME_AUTOMATIC: case winpowrprof.EVENT_RESUME_AUTOMATIC:
r.pauseManager.DeviceWake() 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()
} }
} }
+3 -2
View File
@@ -1,6 +1,7 @@
package oomkiller package oomkiller
import ( import (
"context"
runtimeDebug "runtime/debug" runtimeDebug "runtime/debug"
"sync" "sync"
"time" "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)) t.logger.Warn("memory growth rate critical (report only), usage: ", byteformats.FormatMemoryBytes(sample.usage), t.logDetails(sample))
} else { } else {
t.logger.Error("memory growth rate critical, usage: ", byteformats.FormatMemoryBytes(sample.usage), t.logDetails(sample), ", resetting network") 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 { } else {
if t.killerDisabled { if t.killerDisabled {
t.logger.Warn("memory threshold reached (report only), usage: ", byteformats.FormatMemoryBytes(sample.usage), t.logDetails(sample)) t.logger.Warn("memory threshold reached (report only), usage: ", byteformats.FormatMemoryBytes(sample.usage), t.logDetails(sample))
} else { } else {
t.logger.Error("memory threshold reached, usage: ", byteformats.FormatMemoryBytes(sample.usage), t.logDetails(sample), ", resetting network") 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() badCleanup()