mirror of
https://github.com/taigrr/golang-wpasupplicant
synced 2025-01-18 04:43:18 -08:00
This code connects to wpa_supplicant's control interface using a UNIX datagram socket. It can ping the daemon and fetch wifi network scan results. Documentation, tests, and additional functionality is forthcoming.
188 lines
3.4 KiB
Go
188 lines
3.4 KiB
Go
package wpasupplicant
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
)
|
|
|
|
type message struct {
|
|
priority int
|
|
data []byte
|
|
err error
|
|
}
|
|
|
|
type unixgramConn struct {
|
|
c *net.UnixConn
|
|
fd uintptr
|
|
solicited, unsolicited chan message
|
|
}
|
|
|
|
func Unixgram(ifName string) (Conn, error) {
|
|
var err error
|
|
uc := &unixgramConn{}
|
|
|
|
local, err := ioutil.TempFile("/tmp", "wpa_supplicant")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
os.Remove(local.Name())
|
|
|
|
uc.c, err = net.DialUnix("unixgram",
|
|
&net.UnixAddr{Name: local.Name(), Net: "unixgram"},
|
|
&net.UnixAddr{Name: path.Join("/run/wpa_supplicant", ifName), Net: "unixgram"})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
file, err := uc.c.File()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
uc.fd = file.Fd()
|
|
|
|
uc.solicited = make(chan message)
|
|
uc.unsolicited = make(chan message)
|
|
|
|
go uc.readLoop()
|
|
|
|
return uc, nil
|
|
}
|
|
|
|
func (uc *unixgramConn) readLoop() {
|
|
for {
|
|
n, _, err := syscall.Recvfrom(int(uc.fd), []byte{}, syscall.MSG_PEEK|syscall.MSG_TRUNC)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
buf := make([]byte, n)
|
|
_, err = uc.c.Read(buf[:])
|
|
if err != nil {
|
|
uc.solicited <- message{
|
|
err: err,
|
|
}
|
|
}
|
|
|
|
var p int
|
|
var c chan message
|
|
if len(buf) >= 3 && buf[0] == '<' && buf[2] == '>' {
|
|
switch buf[1] {
|
|
case '0', '1', '2', '3', '4':
|
|
c = uc.unsolicited
|
|
p, _ = strconv.Atoi(string(buf[1]))
|
|
buf = buf[3:]
|
|
default:
|
|
c = uc.solicited
|
|
p = 2
|
|
}
|
|
} else {
|
|
c = uc.solicited
|
|
p = 2
|
|
}
|
|
|
|
c <- message{
|
|
priority: p,
|
|
data: buf,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (uc *unixgramConn) cmd(cmd string) ([]byte, error) {
|
|
// TODO: block if any other commands are running
|
|
|
|
_, err := uc.c.Write([]byte(cmd))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
msg := <-uc.solicited
|
|
return msg.data, msg.err
|
|
}
|
|
|
|
func (uc *unixgramConn) Ping() error {
|
|
resp, err := uc.cmd("PING")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if bytes.Compare(resp, []byte("PONG\n")) == 0 {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("expected %q, got %q", "PONG", resp)
|
|
}
|
|
|
|
func (uc *unixgramConn) ScanResults() ([]ScanResult, error) {
|
|
resp, err := uc.cmd("SCAN_RESULTS")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s := bufio.NewScanner(bytes.NewBuffer(resp))
|
|
if !s.Scan() {
|
|
return nil, errors.New("failed to parse scan results")
|
|
}
|
|
var bssidCol, freqCol, rssiCol, flagsCol, ssidCol, maxCol int
|
|
for n, col := range strings.Split(s.Text(), " / ") {
|
|
switch col {
|
|
case "bssid":
|
|
bssidCol = n
|
|
case "frequency":
|
|
freqCol = n
|
|
case "signal level":
|
|
rssiCol = n
|
|
case "flags":
|
|
flagsCol = n
|
|
case "ssid":
|
|
ssidCol = n
|
|
}
|
|
maxCol = n
|
|
}
|
|
|
|
var res []ScanResult
|
|
for s.Scan() {
|
|
fields := strings.Split(s.Text(), "\t")
|
|
if len(fields) < maxCol {
|
|
continue // TODO: log error
|
|
}
|
|
|
|
bssid, err := net.ParseMAC(fields[bssidCol])
|
|
if err != nil {
|
|
continue // TODO: log error
|
|
}
|
|
|
|
freq, err := strconv.Atoi(fields[freqCol])
|
|
if err != nil {
|
|
continue // TODO: log error
|
|
}
|
|
|
|
rssi, err := strconv.Atoi(fields[rssiCol])
|
|
if err != nil {
|
|
continue // TODO: log error
|
|
}
|
|
|
|
var flags []string
|
|
if len(fields[flagsCol]) >= 2 && fields[flagsCol][0] != '[' && fields[flagsCol][len(fields[flagsCol])-1] != ']' {
|
|
flags = strings.Split(fields[flagsCol][1:len(fields[flagsCol])-2], "][")
|
|
}
|
|
|
|
res = append(res, &scanResult{
|
|
bssid: bssid,
|
|
frequency: freq,
|
|
rssi: rssi,
|
|
flags: flags,
|
|
ssid: fields[ssidCol],
|
|
})
|
|
}
|
|
|
|
return res, nil
|
|
}
|