mirror of
https://github.com/taigrr/golang-wpasupplicant
synced 2025-01-18 04:43:18 -08:00
Initial interface to wpa_supplicant scan results
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.
This commit is contained in:
commit
8a251ea4ef
187
unixgram.go
Normal file
187
unixgram.go
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
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
|
||||||
|
}
|
78
wpasupplicant.go
Normal file
78
wpasupplicant.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package wpasupplicant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cipher int
|
||||||
|
|
||||||
|
const (
|
||||||
|
CIPHER_NONE Cipher = 1 << iota
|
||||||
|
WEP40
|
||||||
|
WEP104
|
||||||
|
TKIP
|
||||||
|
CCMP
|
||||||
|
AES_128_CMAC
|
||||||
|
GCMP
|
||||||
|
SMS4
|
||||||
|
GCMP_256
|
||||||
|
CCMP_256
|
||||||
|
_
|
||||||
|
BIP_GMAC_128
|
||||||
|
BIP_GMAC_256
|
||||||
|
BIP_CMAC_256
|
||||||
|
GTK_NOT_USED
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeyMgmt int
|
||||||
|
|
||||||
|
const (
|
||||||
|
IEEE8021X KeyMgmt = 1 << iota
|
||||||
|
PSK
|
||||||
|
KEY_MGMT_NONE
|
||||||
|
IEEE8021X_NO_WPA
|
||||||
|
WPA_NONE
|
||||||
|
FT_IEEE8021X
|
||||||
|
FT_PSK
|
||||||
|
IEEE8021X_SHA256
|
||||||
|
PSK_SHA256
|
||||||
|
WPS
|
||||||
|
SAE
|
||||||
|
FT_SAE
|
||||||
|
WAPI_PSK
|
||||||
|
WAPI_CERT
|
||||||
|
CCKM
|
||||||
|
OSEN
|
||||||
|
IEEE8021X_SUITE_B
|
||||||
|
IEEE8021X_SUITE_B_192
|
||||||
|
)
|
||||||
|
|
||||||
|
type Algorithm int
|
||||||
|
|
||||||
|
type ScanResult interface {
|
||||||
|
BSSID() net.HardwareAddr
|
||||||
|
SSID() string
|
||||||
|
Frequency() int
|
||||||
|
RSSI() int
|
||||||
|
Flags() []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type scanResult struct {
|
||||||
|
bssid net.HardwareAddr
|
||||||
|
ssid string
|
||||||
|
frequency int
|
||||||
|
rssi int
|
||||||
|
flags []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *scanResult) BSSID() net.HardwareAddr { return r.bssid }
|
||||||
|
func (r *scanResult) SSID() string { return r.ssid }
|
||||||
|
func (r *scanResult) Frequency() int { return r.frequency }
|
||||||
|
func (r *scanResult) RSSI() int { return r.rssi }
|
||||||
|
func (r *scanResult) Flags() []string { return r.flags }
|
||||||
|
|
||||||
|
type Conn interface {
|
||||||
|
Ping() error
|
||||||
|
|
||||||
|
ScanResults() ([]ScanResult, error)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user