mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-11 17:47:20 +10:00
145 lines
3.0 KiB
Go
145 lines
3.0 KiB
Go
//go:build windows
|
|
|
|
package settings
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
"syscall"
|
|
|
|
"github.com/sagernet/sing-box/adapter"
|
|
"github.com/sagernet/sing/common/winwlanapi"
|
|
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
type windowsWIFIMonitor struct {
|
|
handle windows.Handle
|
|
callback func(adapter.WIFIState)
|
|
cancel context.CancelFunc
|
|
lastState adapter.WIFIState
|
|
mutex sync.Mutex
|
|
}
|
|
|
|
func NewWIFIMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
|
handle, err := winwlanapi.OpenHandle()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
interfaces, err := winwlanapi.EnumInterfaces(handle)
|
|
if err != nil {
|
|
winwlanapi.CloseHandle(handle)
|
|
return nil, err
|
|
}
|
|
if len(interfaces) == 0 {
|
|
winwlanapi.CloseHandle(handle)
|
|
return nil, fmt.Errorf("no wireless interfaces found")
|
|
}
|
|
|
|
return &windowsWIFIMonitor{
|
|
handle: handle,
|
|
callback: callback,
|
|
}, nil
|
|
}
|
|
|
|
func (m *windowsWIFIMonitor) ReadWIFIState() adapter.WIFIState {
|
|
interfaces, err := winwlanapi.EnumInterfaces(m.handle)
|
|
if err != nil || len(interfaces) == 0 {
|
|
return adapter.WIFIState{}
|
|
}
|
|
|
|
for _, iface := range interfaces {
|
|
if iface.InterfaceState != winwlanapi.InterfaceStateConnected {
|
|
continue
|
|
}
|
|
|
|
guid := iface.InterfaceGUID
|
|
attrs, err := winwlanapi.QueryCurrentConnection(m.handle, &guid)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
ssidLength := attrs.AssociationAttributes.SSID.Length
|
|
if ssidLength == 0 || ssidLength > winwlanapi.Dot11SSIDMaxLength {
|
|
continue
|
|
}
|
|
|
|
ssid := string(attrs.AssociationAttributes.SSID.SSID[:ssidLength])
|
|
bssid := formatBSSID(attrs.AssociationAttributes.BSSID)
|
|
|
|
return adapter.WIFIState{
|
|
SSID: strings.TrimSpace(ssid),
|
|
BSSID: bssid,
|
|
}
|
|
}
|
|
|
|
return adapter.WIFIState{}
|
|
}
|
|
|
|
func formatBSSID(mac winwlanapi.Dot11MacAddress) string {
|
|
return fmt.Sprintf("%02X%02X%02X%02X%02X%02X",
|
|
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5])
|
|
}
|
|
|
|
func (m *windowsWIFIMonitor) Start() error {
|
|
if m.callback == nil {
|
|
return nil
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
m.cancel = cancel
|
|
|
|
m.lastState = m.ReadWIFIState()
|
|
|
|
callbackFunc := func(data *winwlanapi.NotificationData, callbackContext uintptr) uintptr {
|
|
if data.NotificationSource != winwlanapi.NotificationSourceACM {
|
|
return 0
|
|
}
|
|
switch data.NotificationCode {
|
|
case winwlanapi.NotificationACMConnectionComplete,
|
|
winwlanapi.NotificationACMDisconnected:
|
|
m.checkAndNotify()
|
|
}
|
|
return 0
|
|
}
|
|
|
|
callbackPointer := syscall.NewCallback(callbackFunc)
|
|
|
|
err := winwlanapi.RegisterNotification(m.handle, winwlanapi.NotificationSourceACM, callbackPointer, 0)
|
|
if err != nil {
|
|
cancel()
|
|
return err
|
|
}
|
|
|
|
go func() {
|
|
<-ctx.Done()
|
|
}()
|
|
|
|
m.callback(m.lastState)
|
|
return nil
|
|
}
|
|
|
|
func (m *windowsWIFIMonitor) checkAndNotify() {
|
|
m.mutex.Lock()
|
|
defer m.mutex.Unlock()
|
|
|
|
state := m.ReadWIFIState()
|
|
if state != m.lastState {
|
|
m.lastState = state
|
|
if m.callback != nil {
|
|
m.callback(state)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *windowsWIFIMonitor) Close() error {
|
|
if m.cancel != nil {
|
|
m.cancel()
|
|
}
|
|
winwlanapi.UnregisterNotification(m.handle)
|
|
return winwlanapi.CloseHandle(m.handle)
|
|
}
|