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
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 {
+2 -1
View File
@@ -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)
+6 -3
View File
@@ -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,
+4
View File
@@ -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 {
+61 -6
View File
@@ -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
+4
View File
@@ -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")
+4
View File
@@ -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 {
+1
View File
@@ -4,4 +4,5 @@ type SystemProxy interface {
IsEnabled() bool
Enable() error
Disable() error
Close() error
}
+6 -2
View File
@@ -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
}
+4 -2
View File
@@ -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 {
+4 -4
View File
@@ -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)
+4 -4
View File
@@ -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)
+4 -4
View File
@@ -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)
+12 -3
View File
@@ -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)
+2 -1
View File
@@ -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{}
}
+3 -3
View File
@@ -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 {
+39 -11
View File
@@ -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
+57 -9
View File
@@ -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()
}
+2 -1
View File
@@ -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{}
}
+1 -1
View File
@@ -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{}
}
+3 -3
View File
@@ -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)
}
}
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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{}
}
+2 -1
View File
@@ -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{}
+1 -1
View File
@@ -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()
}
+1 -1
View File
@@ -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()
+17 -9
View File
@@ -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() {
+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)
}
func (h *Outbound) InterfaceUpdated() {
func (h *Outbound) InterfaceUpdated(ctx context.Context) {
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)
}
func (h *Inbound) InterfaceUpdated() {
func (h *Inbound) InterfaceUpdated(ctx context.Context) {
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)
}
func (h *Outbound) InterfaceUpdated() {
func (h *Outbound) InterfaceUpdated(ctx context.Context) {
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)
}
func (h *Outbound) InterfaceUpdated() {
func (h *Outbound) InterfaceUpdated(ctx context.Context) {
h.client.Engine().CloseAllConnections()
}
+1 -1
View File
@@ -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()
}
+1 -1
View File
@@ -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()
}
+1 -1
View File
@@ -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()
}
+1 -1
View File
@@ -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()
}
+1 -1
View File
@@ -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()
}
+1 -1
View File
@@ -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)
}
+1 -1
View File
@@ -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
}
+1 -1
View File
@@ -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()
}
+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"))
}
+1 -1
View File
@@ -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()
+1 -1
View File
@@ -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()
}
+1 -1
View File
@@ -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()
}
+14 -1
View File
@@ -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"))
+98 -39
View File
@@ -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()
}
}
+3 -2
View File
@@ -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()