mirror of
https://github.com/henrygd/beszel.git
synced 2026-09-26 11:27:47 +02:00
100 lines
2.9 KiB
Go
100 lines
2.9 KiB
Go
//go:build windows
|
|
|
|
package wifi
|
|
|
|
import (
|
|
"context"
|
|
"unsafe"
|
|
|
|
"github.com/henrygd/beszel/internal/entities/system"
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
var wlan = windows.NewLazySystemDLL("wlanapi.dll")
|
|
var wlanOpen = wlan.NewProc("WlanOpenHandle")
|
|
var wlanClose = wlan.NewProc("WlanCloseHandle")
|
|
var wlanEnum = wlan.NewProc("WlanEnumInterfaces")
|
|
var wlanQuery = wlan.NewProc("WlanQueryInterface")
|
|
var wlanFree = wlan.NewProc("WlanFreeMemory")
|
|
|
|
type wlanInterface struct {
|
|
GUID windows.GUID
|
|
Description [256]uint16
|
|
State uint32
|
|
}
|
|
|
|
type wlanConnection struct {
|
|
State uint32
|
|
Mode uint32
|
|
Profile [256]uint16
|
|
SSIDLength uint32
|
|
SSID [32]byte
|
|
// Only the prefix through DOT11_SSID is read.
|
|
}
|
|
|
|
func collect(ctx context.Context) map[string]system.WiFi {
|
|
result := make(map[string]system.WiFi)
|
|
for _, proc := range []*windows.LazyProc{wlanOpen, wlanClose, wlanEnum, wlanQuery, wlanFree} {
|
|
if proc.Find() != nil {
|
|
return result
|
|
}
|
|
}
|
|
var handle windows.Handle
|
|
var version uint32
|
|
if rc, _, _ := wlanOpen.Call(2, 0, uintptr(unsafe.Pointer(&version)), uintptr(unsafe.Pointer(&handle))); rc != 0 {
|
|
return result
|
|
}
|
|
defer wlanClose.Call(uintptr(handle), 0)
|
|
var list unsafe.Pointer
|
|
if rc, _, _ := wlanEnum.Call(uintptr(handle), 0, uintptr(unsafe.Pointer(&list))); rc != 0 || list == nil {
|
|
return result
|
|
}
|
|
defer wlanFree.Call(uintptr(list))
|
|
count := *(*uint32)(list)
|
|
if count > 1024 {
|
|
return result
|
|
}
|
|
interfaces := unsafe.Slice((*wlanInterface)(unsafe.Add(list, 8)), int(count))
|
|
for _, iface := range interfaces {
|
|
if ctx.Err() != nil {
|
|
break
|
|
}
|
|
if iface.State != 1 {
|
|
continue
|
|
} // wlan_interface_state_connected
|
|
reading := system.WiFi{}
|
|
// SSID access may be denied by location privacy policy. Association comes
|
|
// from the interface state, so missing SSID does not suppress valid RSSI.
|
|
if data, size := queryWLAN(handle, &iface.GUID, 7); data != nil {
|
|
if size >= uint32(unsafe.Sizeof(wlanConnection{})) {
|
|
connection := (*wlanConnection)(data)
|
|
if connection.State == 1 && connection.SSIDLength <= 32 {
|
|
reading.SSID = validSSID(string(connection.SSID[:connection.SSIDLength]))
|
|
}
|
|
}
|
|
wlanFree.Call(uintptr(data))
|
|
}
|
|
// Native RSSI LONG, not the quality percentage in association attributes.
|
|
if data, size := queryWLAN(handle, &iface.GUID, 0x10000102); data != nil {
|
|
if size >= 4 {
|
|
signal := float64(*(*int32)(data))
|
|
if signal >= -150 && signal < 0 {
|
|
reading.Signal = &signal
|
|
}
|
|
}
|
|
wlanFree.Call(uintptr(data))
|
|
}
|
|
result[iface.GUID.String()] = reading
|
|
}
|
|
return result
|
|
}
|
|
|
|
func queryWLAN(handle windows.Handle, guid *windows.GUID, opcode uintptr) (unsafe.Pointer, uint32) {
|
|
var data unsafe.Pointer
|
|
var size uint32
|
|
if rc, _, _ := wlanQuery.Call(uintptr(handle), uintptr(unsafe.Pointer(guid)), opcode, 0, uintptr(unsafe.Pointer(&size)), uintptr(unsafe.Pointer(&data)), 0); rc != 0 {
|
|
return nil, 0
|
|
}
|
|
return data, size
|
|
}
|