-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhostapd_ctrl.py
113 lines (98 loc) · 3.94 KB
/
hostapd_ctrl.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
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
import os
import re
import socket
import time
class HostapdCtrl:
def __init__(self, ifname):
# Arbitrary local socket name we bind to; this is needed because when
# we ATTACH to the hostapd control socket, it saves this name so it
# can send back events.
self._sockaddr_local = "/tmp/hostapd_ctrl.%d" % (os.getpid(), )
try:
os.unlink(self._sockaddr_local)
except:
pass # it wasn't there, okay
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
self.sock.bind(self._sockaddr_local)
self.sock.setblocking(0)
# hostapd control socket. Connect to this to send commands to, and get
# responses and events from, hostapd. If the network interface used by
# hostapd changes, this must change. (It may be better to just pick
# the first socket in the directory.)
self._sockaddr_remote = "/var/run/hostapd/%s" % (ifname, )
# Attach to hostapd control socket.
#
# When hostapd gets the ATTACH command, it saves the address of the socket
# that sent it (_sockaddr_local), then any asynchronous events that show
# up (like pin-needed) get sent there. There can be more than one client
# attached at the same time; hostapd keeps a list.
#
# Returns:
# True if attached
# False if not attached (timeout or error)
def attach(self, timeout_s):
self.sock.settimeout(0.1)
while True:
try:
self.sock.sendto("ATTACH", self._sockaddr_remote)
except:
# control socket probably not there
pass
else:
pkt = self.sock.recv(256)
# On success, we get "OK\n" (3 chars)
if pkt and len(pkt) == 3 and pkt.find("OK") != -1:
return True
timeout_s -= 1.0
if timeout_s < 0.0:
return False
time.sleep(1.0)
# end while True
# Receive message from hostapd.
def recv(self):
return self.sock.recv(256)
# Parse message from hostapd.
#
# The PIN request packet from hostapd looks something like this (with no
# newline; one space separates the two lines as shown here):
#
# <3>WPS-PIN-NEEDED 7d4c3eba-141a-54bc-9345-7bc0d6c27f04 00:15:6d:85:ce:b7
# [My Solo|3D Robotics|Solo|Solo-1|S-1234567890|0-00000000-0]
#
# The UUID 7d4c3eba-141a-54bc-9345-7bc0d6c27f04 is generated by Solo. It
# is based on the MAC address somehow, but since we don't know how it is
# generated (or whether it will be the same over time), it is only used
# where necessary.
#
# The MAC 00:15:6d:85:ce:b7 comes from Solo's WiFi card.
#
# Everything in the [brackets] comes from Solo's wpa_supplicant.conf file:
# [device_name|manufacturer|model_name|model_number|serial_number|device_type]
#
# Returns:
# 9-tuple for WPS-PIN-NEEDED
# ("WPS-PIN-NEEDED", uuid, mac, device_name, manufacturer,
# model_name, model_number, serial_number, device_type)
# 1-tuple for other message types
# ("MESSAGE-TYPE", )
# 1-tuple if message can't be parsed containing entire packet
def parse(self, pkt):
regexp = "<3>WPS-PIN-NEEDED ([0-9a-fA-F\-]+) ([0-9a-fA-F:]+) \[(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\]"
m = re.match(regexp, pkt)
if m:
fields = m.groups()
if len(fields) == 8:
return ("WPS-PIN-NEEDED", ) + fields
else:
return (pkt, )
# add other messages here as needed
# default: just parse out the message type
regexp = "<[0-9]+>(.+?) "
m = re.match(regexp, pkt)
if m:
return (m.group(1), )
return (pkt, )
# Send pin reply
def send_pin(self, uuid, pin):
pin_reply = "WPS_PIN %s %d" % (uuid, pin)
self.sock.sendto(pin_reply, self._sockaddr_remote)