-
Notifications
You must be signed in to change notification settings - Fork 114
Expand file tree
/
Copy pathdiscover.py
More file actions
executable file
·176 lines (138 loc) · 5.18 KB
/
Copy pathdiscover.py
File metadata and controls
executable file
·176 lines (138 loc) · 5.18 KB
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
173
174
175
176
#!/usr/bin/env python3
"""A mini protocol for two hosts to discover each other on a LAN,
for peer-to-peer networking.
Alternate between broadcasting and listening for other machines
that might be broadcasting, with a random timeout so eventually
one will be broadcasting at the same time the other is listening.
"""
import socket
import time
import random
BROADCAST_PORT = 28999
REPLY_PORT = BROADCAST_PORT
def get_my_ip():
addrs = socket.gethostbyname_ex(socket.gethostname())[2]
if not addrs:
return None
for ip in addrs:
# Skip localhost, 127.0.0.1
if ip.startswith('127'):
continue
return ip
# if we get here, all addrs are localhost.
return addrs[0]
MY_IP = get_my_ip()
MY_IP_BYTES = MY_IP.encode()
MESSAGE_BYTES = b'discovery||%s' % MY_IP_BYTES
other_host = None
def broadcast():
"""Send out a broadcast on BROADCAST_PORT,
then listen for replies on REPLY_PORT.
Return True and save details of the other machine if a valid reply
was seen, else return False.
"""
global other_host
broadcast_socket = socket.socket(socket.AF_INET,
socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# broadcast mode
broadcast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
# Set a timeout so the broadcast doesn't last forever
# broadcast_socket.settimeout(random.randint(5, 10))
broadcast_socket.sendto(MESSAGE_BYTES, ('<broadcast>', BROADCAST_PORT))
# Does the socket need to be shut down afterward?
# broadcast_socket.shutdown(socket.SHUT_RDWR)
# broadcast_socket.close()
print("message broadcast, listening for responses on port", REPLY_PORT)
#
# See if there are any responses
#
response_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
response_socket.settimeout(random.randint(5, 10))
# bind the socket to a public host, and a well-known port
# This sometimes raises OSError: [Errno 98] Address already in use
response_socket.bind((MY_IP, REPLY_PORT))
response_socket.listen(1)
try:
(conn, address) = response_socket.accept()
conn.settimeout(random.randint(5, 10))
# data, addr = response_socket.recvfrom(1024)
data = conn.recv(1024)
except TimeoutError:
print("Timeout, didn't get any responses")
return False
print("Got a response! '%s'" % (data))
try:
proto, clientip = data.split(b'||')
except ValueError:
print("Bad protocol:", data)
return False
other_host = { 'ip': clientip.decode() }
return True
# response_socket.shutdown(socket.SHUT_RDWR)
# response_socket.close()
def listen_for_broadcasters():
"""Listen for broadcasts on BROADCAST_PORT.
If one is seen, reply on REPLY_PORT, save the details of the other
machine and return True.
Otherwise, return False.
"""
global other_host
# Listen on the BROADCAST_PORT
client_socket = socket.socket(socket.AF_INET,
socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# Receive broadcasts
client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
client_socket.settimeout(random.randint(5, 10))
client_socket.bind(("", BROADCAST_PORT))
try:
data, addr = client_socket.recvfrom(1024)
print("received: '%s' from %s" % (data, addr))
except TimeoutError:
print("Timed out, returning")
return False
# The server sends something like: b'discovery||192.168.1.2'
if not data.startswith(b'discovery||'):
print("Bad protocol,", data)
return False
try:
proto, serverip = data.split(b'||')
except:
print("Bad protocol:", data)
return False
# Reply to the server, using the REPLY_PORT
print("Sending a reply on port", REPLY_PORT)
responding_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
responding_socket.settimeout(10)
# now connect to the web server on port 80 - the normal http port
try:
responding_socket.connect((serverip, REPLY_PORT))
responding_socket.send(MESSAGE_BYTES)
print("sent a client response:", MESSAGE_BYTES)
other_host = { 'ip': serverip.decode() }
return True
except TimeoutError:
print("Timed out trying to send a response")
return False
if __name__ == '__main__':
import sys
if len(sys.argv) > 1:
while True:
if sys.argv[1] == 'broadcast':
broadcast()
elif sys.argv[1] == 'listen':
listen_for_broadcasters()
# By default, alternate between broadcasting
# and listening for another node that's broadcasting
while True:
print("\nBroadcasting")
if broadcast():
print("broadcast_ip() returned True!")
break
# time.sleep(random.randint(0, 10))
print("\nNow listening for broadcasters")
if listen_for_broadcasters():
print("listen_for_broadcasters() returned True!")
break
# time.sleep(random.randint(0, 10))
print("Woohoo, some communication happened")
print("Remote host is %s" % (other_host['ip']))