-
Notifications
You must be signed in to change notification settings - Fork 0
/
CustClientListParser.py
executable file
·71 lines (66 loc) · 3.07 KB
/
CustClientListParser.py
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
#!/usr/bin/python3
##############################################################################
# CustClientListParser.py
# -----------------------
#
# ASUS Routers - at least those running asuswrt-merlin firmware - store the
# mappings a user makes to the client list in a file nominally stored in
# /jffs/nvram/custom_clientlist.
# This (essentially) maps MAC addresses to "friendy names". There's a couple
# of other integer fields too but I can't find documentation on what they're for.
#
# The format of this file is a *weird* angle-bracket delimiter. This script
# (Class) provides a way to turn this into standard Python Dict objects, by
# default using MAC address as the key.
#
# Internal format of the clientlist is similar to a repetition of:
# <Name>MacAddress>Int1>Int2>>
# (noting that "<" and ">" are literals not representative)
#
# Note that the FIRST record isn't started with "<" but all others appear to be.
# this is thus used as the record delimiter
###############################################################################
from os.path import isfile
class CustClientListParser(object):
"""Parse an ASUS custom_clientlist string into a Dict with MAC as key"""
def __init__(self, rawclientdata):
"""Load the datastring and parse into records"""
self.mactoname = {}
self.rawdata = rawclientdata
#We may be passed "rawclientdata" as either a filename or the contents.
#There's no foolproof way to detect which is which but this is close:
if isfile(rawclientdata):
with open(rawclientdata,"r") as x:
self.rawdata=x.read()
else :
self.rawdata = rawclientdata
self._parseclientdata()
def _parseclientdata(self):
"""Actually perform the work of parsing data into a KVP Dictionary"""
kvp = self.rawdata.split("<") # Split on "record delimiter"
if len(kvp)<2 :
raise TypeError("Passed incorrect ASUS custom_clientlist data")
for rec in kvp:
if len(rec)==0 :
continue #Ignore blank records
fields = rec.split(">")
if len(fields) < 2 :
raise TypeError(f"Invalid record in custom_clientlist data: {rec}")
#if fields[0][:1] != "<" :
# raise ValueError(f"Invalid NAME field in record {rec} ({fields[0]})")
if len(fields[1]) != 17 :
raise ValueError(f"Invalid MAC field in record {rec} ({fields[1]})")
self.mactoname[fields[1]] = fields[0]
def getMappings(self):
"""Return mapping dict from parsed data"""
return self.mactoname
if __name__ == "__main__":
print("This is a TEST PROGRAM")
print("(you probably don't want to be here)")
from sys import argv
if len(argv) == 2 and isfile(argv[1]):
namemappings = CustClientListParser(argv[1]).getMappings()
for mac in namemappings:
print(f"{mac} -> {namemappings[mac]}")
else :
print("When called directly, supply the path to an ASUS Router custom_clientlist file on the command line")