-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbeacon.py
148 lines (123 loc) · 4.39 KB
/
beacon.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
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
# -*- coding: utf-8 -*-
"""
Location is performed by UDB broadcasts.
@license: MIT License
"""
import socket
import errno
import select
import threading
import uuid
try:
import netifaces
HAS_NETIFACES = True
except ImportError:
HAS_NETIFACES = False
###########################################################################
###########################################################################
CLIENT_TIMEOUT = 2
SERVER_TIMEOUT = 0.2
MAX_INTERFACE_SERVERS = 5 #: maximum count of beacons on a single interface
UUID_LENGTH = 16
###########################################################################
###########################################################################
def get_broadcast_addresses():
"""
Retrieve broadcast addresses from network interfaces.
"""
addr_list = []
if HAS_NETIFACES:
for iface in netifaces.interfaces():
addresses = netifaces.ifaddresses(iface).get(netifaces.AF_INET)
if addresses is None:
continue
for address in addresses:
broadcast_addr = address.get("broadcast")
if broadcast_addr is None:
continue
addr_list.append(broadcast_addr)
return ["127.0.0.1", "255.255.255.255", "<broadcast>"] + addr_list
###################################################################
def _find_servers(port, key, wait_for_all):
"""
@return: list of resolved address
"""
addresses = get_broadcast_addresses()
bcast_socks = []
for bcast_addr in addresses:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
try:
sock.sendto(key, (bcast_addr, port))
bcast_socks.append(sock)
except socket.error, err:
if err.errno != errno.ENETUNREACH:
raise
servers = []
srv_uuids = set()
max_tries = len(bcast_socks) * MAX_INTERFACE_SERVERS
while max_tries > 0:
try:
rlist, dummy_wlist, dummy_xlist = select.select(bcast_socks, [], [],
CLIENT_TIMEOUT)
except socket.error:
break
if len(rlist) == 0:
# timeout, no respond
break
max_tries -= len(rlist)
try:
for bcast_sock in rlist:
message, (ip, dummy_port) = bcast_sock.recvfrom(UUID_LENGTH + len(key))
if len(message) < UUID_LENGTH:
continue
srv_uuid = message[:UUID_LENGTH]
srv_key = message[UUID_LENGTH:]
if (srv_key == key) and (srv_uuid not in srv_uuids):
servers.append(ip)
srv_uuids.add(srv_uuid)
if not wait_for_all:
max_tries = 0
break
except socket.error, e:
continue
return servers
###################################################################
def find_all_servers(port, key):
return _find_servers(port, key, True)
###################################################################
def find_server(port, key):
"""
Find first responding server.
@return: IP address or None if not found.
"""
servers = _find_servers(port, key, False)
if len(servers) == 0:
return None
else:
return servers[0]
###########################################################################
###########################################################################
class Beacon(threading.Thread):
def __init__(self, port, key):
threading.Thread.__init__(self)
self.port = port
self.key = key
self.quit = False
self.unique_id = uuid.uuid1().bytes
################################################
def run(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.settimeout(SERVER_TIMEOUT)
sock.bind(("", self.port))
while not self.quit:
try:
message, address = sock.recvfrom(len(self.key))
except socket.error as exc:
continue
if message != self.key:
# not my client
continue
sock.sendto(self.unique_id + self.key, address)
sock.close()