zhangzengfei
2023-10-18 daac628c64069633787588372dea22499ac35396
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package iwlist
 
import (
    "fmt"
    "os/exec"
    "regexp"
    "strconv"
    "strings"
    "sync"
)
 
var (
    newCellRegexp = regexp.MustCompile(`^Cell\s+(?P<cell_number>.+)\s+-\s+Address:\s(?P<mac>.+)$`)
    regxp         [7]*regexp.Regexp
)
 
type Cell struct {
    CellNumber     string  `json:"cell_number"`
    MAC            string  `json:"mac"`
    ESSID          string  `json:"essid"`
    Mode           string  `json:"mode"`
    Frequency      float32 `json:"frequency"`
    FrequencyUnits string  `json:"frequency_units"`
    Channel        int     `json:"channel"`
    EncryptionKey  bool    `json:"encryption_key"`
    Encryption     string  `json:"encryption"`
    SignalQuality  int     `json:"signal_quality"`
    SignalTotal    int     `json:"signal_total"`
    SignalLevel    int     `json:"signal_level"`
    Connected      bool    `json:"connected"`
}
 
func init() {
    // precompile regexp
    regxp = [7]*regexp.Regexp{
        regexp.MustCompile(`^ESSID:\"(?P<essid>.*)\"$`),
        regexp.MustCompile(`^Mode:(?P<mode>.+)$`),
        regexp.MustCompile(`^Frequency:(?P<frequency>[\d.]+) (?P<frequency_units>.+) \(Channel (?P<channel>\d+)\)$`),
        regexp.MustCompile(`^Encryption key:(?P<encryption_key>.+)$`),
        regexp.MustCompile(`^IE:\ WPA\ Version\ (?P<wpa>.+)$`),
        regexp.MustCompile(`^IE:\ IEEE\ 802\.11i/WPA2\ Version\ (?P<wpa2>)$`),
        regexp.MustCompile(`^Quality=(?P<signal_quality>\d+)/(?P<signal_total>\d+)\s+Signal level=(?P<signal_level>.+) d.+$`),
    }
}
 
func Scan(interfaceName string) ([]Cell, error) {
    // execute iwlist for scanning wireless networks
    // 博通网卡不加sudo扫描不到网络
    cmdStr := fmt.Sprintf("sudo iwlist %s scan", interfaceName)
    cmd := exec.Command("/bin/bash", "-c", cmdStr)
    out, err := cmd.CombinedOutput()
    if err != nil {
        return nil, err
    }
 
    wifiList, err := parse(string(out))
    if err != nil {
        return wifiList, err
    }
 
    // 获取当前连接的wifi
    // 使用iwconfig 获取当前接口的连接状态, 过滤出连接的wifi名称
    // 忽略了Mode的判断,连接后应该是Managed
    //wlan0     IEEE 802.11AC  ESSID:"Basic"  Nickname:"<WIFI@REALTEK>"
    //          Mode:Managed  Frequency:5.22 GHz  Access Point: 82:45:58:18:17:E6
    //          Bit Rate:200 Mb/s   Sensitivity:0/0
    //          Retry:off   RTS thr:off   Fragment thr:off
    //          Power Management:off
    //          Link Quality=75/100  Signal level=-53 dBm  Noise level=0 dBm
    //          Rx invalid nwid:0  Rx invalid crypt:0  Rx invalid frag:0
    //          Tx excessive retries:0  Invalid misc:0   Missed beacon:0
 
    reg, _ := regexp.Compile(`ESSID:\"(\w+)\".*\n\s+Mode:(\w+).*Point:\s?(\S+)`)
    cmd = exec.Command("iwconfig", interfaceName)
    out, _ = cmd.CombinedOutput()
 
    res := reg.FindStringSubmatch(string(out))
    if len(res) == 4 {
        for i, v := range wifiList {
            if v.MAC == res[3] {
                wifiList[i].Connected = true
                wifiList[0], wifiList[i] = wifiList[i], wifiList[0]
            }
        }
    }
 
    return wifiList, nil
}
 
func parse(input string) (cells []Cell, err error) {
    lines := strings.Split(input, "\n")
 
    var cell *Cell
    var wg sync.WaitGroup
    var m sync.Mutex
    for _, line := range lines {
        line = strings.TrimSpace(line)
 
        // check new cell value
        if cellValues := newCellRegexp.FindStringSubmatch(line); len(cellValues) > 0 {
            cells = append(cells, Cell{
                CellNumber: cellValues[1],
                MAC:        cellValues[2],
            })
            cell = &cells[len(cells)-1]
 
            continue
        }
 
        // compare lines to regexps
        wg.Add(len(regxp))
        for _, reg := range regxp {
            go compare(line, &wg, &m, cell, reg)
        }
        wg.Wait()
    }
 
    return
}
 
func compare(line string, wg *sync.WaitGroup, m *sync.Mutex, cell *Cell, reg *regexp.Regexp) {
    defer wg.Done()
 
    if values := reg.FindStringSubmatch(line); len(values) > 0 {
        keys := reg.SubexpNames()
 
        m.Lock()
 
        for i := 1; i < len(keys); i++ {
            switch keys[i] {
            case "essid":
                cell.ESSID = values[i]
            case "mode":
                cell.Mode = values[i]
            case "frequency":
                if frequency, err := strconv.ParseFloat(values[i], 32); err == nil {
                    cell.Frequency = float32(frequency)
                }
            case "frequency_units":
                cell.FrequencyUnits = values[i]
            case "channel":
                if channel, err := strconv.ParseInt(values[i], 10, 32); err == nil {
                    cell.Channel = int(channel)
                }
            case "encryption_key":
                if cell.EncryptionKey = values[i] == "on"; cell.EncryptionKey {
                    cell.Encryption = "wep"
                } else {
                    cell.Encryption = "off"
                }
            case "wpa":
                cell.Encryption = "wpa"
            case "wpa2":
                cell.Encryption = "wpa2"
            case "signal_quality":
                if quality, err := strconv.ParseInt(values[i], 10, 32); err == nil {
                    cell.SignalQuality = int(quality)
                }
            case "signal_total":
                if total, err := strconv.ParseInt(values[i], 10, 32); err == nil {
                    cell.SignalTotal = int(total)
                }
            case "signal_level":
                if level, err := strconv.ParseInt(values[i], 10, 32); err == nil {
                    cell.SignalLevel = int(level)
                }
            }
        }
 
        m.Unlock()
    }
}