generated from VLADIMIR/template
up
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
package cudy
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func parseClients(data []byte) ([]Client, error) {
|
||||
doc, err := html.Parse(strings.NewReader(string(data)))
|
||||
if err != nil {
|
||||
return nil, ErrRead
|
||||
}
|
||||
clients := []Client{}
|
||||
var table *html.Node
|
||||
walk(doc, func(n *html.Node) {
|
||||
if n.Data == "th" && nodeText(n) == "IP / MAC Address" {
|
||||
for parent := n.Parent; parent != nil; parent = parent.Parent {
|
||||
if parent.Data == "table" {
|
||||
table = parent
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if table == nil {
|
||||
return nil, ErrRead
|
||||
}
|
||||
invalid := false
|
||||
seen := map[string]bool{}
|
||||
walk(table, func(row *html.Node) {
|
||||
if row.Type != html.ElementNode {
|
||||
return
|
||||
}
|
||||
if row.Data != "tr" {
|
||||
return
|
||||
}
|
||||
hasData := false
|
||||
for child := row.FirstChild; child != nil; child = child.NextSibling {
|
||||
hasData = hasData || child.Data == "td"
|
||||
}
|
||||
if !hasData {
|
||||
return
|
||||
}
|
||||
if !strings.HasPrefix(attr(row, "id"), "cbi-table-") {
|
||||
invalid = true
|
||||
return
|
||||
}
|
||||
fields := map[string]*html.Node{}
|
||||
prefix := attr(row, "id") + "-"
|
||||
walk(row, func(n *html.Node) {
|
||||
if n.Data == "div" && strings.HasPrefix(attr(n, "id"), prefix) {
|
||||
fields[strings.TrimPrefix(attr(n, "id"), prefix)] = n
|
||||
}
|
||||
})
|
||||
var band string
|
||||
wired := false
|
||||
if hostname := fields["hostname"]; hostname != nil {
|
||||
walk(hostname, func(n *html.Node) {
|
||||
if n.Data == "span" {
|
||||
label := nodeText(n)
|
||||
wired = wired || label == "Ethernet"
|
||||
if label == "2.4G WiFi" {
|
||||
band = "2.4 GHz"
|
||||
}
|
||||
if label == "5G WiFi" {
|
||||
band = "5 GHz"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// shortcut: stock TR3000 2.5.27 DOM and two bands; update parser fixtures for other firmware.
|
||||
if band == "" {
|
||||
if !wired {
|
||||
invalid = true
|
||||
}
|
||||
return
|
||||
}
|
||||
client := Client{Band: band}
|
||||
if field := fields["ipmac"]; field != nil {
|
||||
walk(field, func(n *html.Node) {
|
||||
if n.Data != "p" || client.MAC != "" {
|
||||
return
|
||||
}
|
||||
for _, value := range strings.Fields(nodeText(n)) {
|
||||
if ip, e := netip.ParseAddr(value); e == nil {
|
||||
client.IP = ip.String()
|
||||
}
|
||||
if mac, e := net.ParseMAC(value); e == nil && len(mac) == 6 {
|
||||
client.MAC = strings.ToUpper(mac.String())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if field := fields["signal"]; field != nil {
|
||||
walk(field, func(n *html.Node) {
|
||||
if n.Data == "p" && client.Signal == nil {
|
||||
parts := strings.Fields(nodeText(n))
|
||||
if len(parts) == 2 && parts[1] == "dBm" {
|
||||
if value, e := strconv.Atoi(parts[0]); e == nil && value >= -120 && value <= 0 {
|
||||
client.Signal = &value
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if client.MAC == "" || seen[client.MAC] {
|
||||
invalid = true
|
||||
return
|
||||
}
|
||||
seen[client.MAC] = true
|
||||
clients = append(clients, client)
|
||||
})
|
||||
if invalid || len(clients) > 1024 {
|
||||
return nil, ErrRead
|
||||
}
|
||||
sort.Slice(clients, func(i, j int) bool { return clients[i].MAC < clients[j].MAC })
|
||||
return clients, nil
|
||||
}
|
||||
Reference in New Issue
Block a user