-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathping.py
305 lines (240 loc) · 7.88 KB
/
ping.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
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
#!/usr/bin/env python
"""
Description
ping.py - check a remote host for reachability
Usage
python ping.py host port
Output
#$># sudo python ping.py 192.168.1.99 8888
ping 192.168.1.99 port 8888
- icmp is reachable
- tcp is NOT reachable
- udp is reachable
Note
- Require administrator/sudo privileges
- Use GPLv2 code from https://github.com/samuel/python-ping/blob/master/ping.py
- works with python 2.7 and 3.6
Author
David Laperriere <[email protected]>
"""
import os
import sys
import socket
import struct
import select
import time
__version_info__ = (1, 0)
__version__ = '.'.join(map(str, __version_info__))
__author__ = "David Laperriere [email protected]"
ICMP_ECHO_REQUEST = 8
TIMEOUT = 5
if sys.platform == "win32":
# On Windows, the best timer is time.clock()
default_timer = time.clock
else:
# On most other platforms the best timer is time.time()
default_timer = time.time
# Python version compat
if sys.version_info[0] <= 2:
Py3 = False
elif sys.version_info[0] >= 3:
Py3 = True
# methods
def icmp(host, timeout=TIMEOUT):
"""
Send/receive ICMP packet to host
"""
delay = None
try:
delay = do_one(host, timeout)
except socket.gaierror as e:
print(" - icmp is NOT reachable (socket error: '%s')" % e)
if delay is None:
print(" - icmp is NOT reachable (timeout within %ssec.)" % timeout)
else:
delay = delay * 1000
# print "get ping in %0.4fms" % delay
print(" - icmp is reachable")
def tcp(host, port, timeout=TIMEOUT):
"""
Open/close a TCP connection to host:port
"""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
s.settimeout(timeout)
result = s.connect_ex((host, port))
if result == 0:
print(" - tcp is reachable")
else:
print(" - tcp is NOT reachable")
s.close()
def udp(host, port, timeout=TIMEOUT):
"""
Send UDP echo ping at host:port
The script echo.py can be used as a server
"""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.settimeout(timeout)
result = s.connect_ex((host, port))
if Py3:
s.send(bytes("PING", "ascii"))
else:
s.send('PING')
try:
reply, address = s.recvfrom(1024)
# print " Received '"+ str(reply) + "' from " + str(address)
result = 1
except socket.error as serror:
result = -1
# print "\t"+str(serror)
if result >= 0:
print(" - udp is reachable ")
else:
print(" - udp is NOT reachable ")
s.close()
# icmp methods from
# https://github.com/samuel/python-ping/blob/master/ping.py
# GPLv2
def checksum(source_string):
"""
I'm not too confident that this is right but testing seems
to suggest that it gives the same answers as in_cksum in ping.c
"""
csum = 0
countTo = (len(source_string) // 2) * 2
count = 0
while count < countTo:
if Py3:
thisVal = source_string[count + 1] * 256 + source_string[count]
else:
thisVal = ord(source_string[count + 1]) * \
256 + ord(source_string[count])
csum = csum + thisVal
csum = csum & 0xffffffff # Necessary?
count = count + 2
if countTo < len(source_string):
csum = csum + ord(source_string[len(source_string) - 1])
csum = csum & 0xffffffff # Necessary?
csum = (csum >> 16) + (csum & 0xffff)
csum = csum + (csum >> 16)
answer = ~csum
answer = answer & 0xffff
# Swap bytes. Bugger me if I know why.
answer = answer >> 8 | (answer << 8 & 0xff00)
return answer
# from https://github.com/samuel/python-ping/blob/master/ping.py
def receive_one_ping(my_socket, ID, timeout):
"""
receive the ping from the socket.
"""
timeLeft = timeout
while True:
startedSelect = default_timer()
whatReady = select.select([my_socket], [], [], timeLeft)
howLongInSelect = (default_timer() - startedSelect)
if whatReady[0] == []: # Timeout
return
timeReceived = default_timer()
recPacket, addr = my_socket.recvfrom(1024)
icmpHeader = recPacket[20:28]
itype, code, checksum, packetID, isequence = struct.unpack(
"bbHHh", icmpHeader
)
# Filters out the echo request itself.
# This can be tested by pinging 127.0.0.1
# You'll see your own request
if itype != 8 and packetID == ID:
bytesInDouble = struct.calcsize("d")
timeSent = struct.unpack("d", recPacket[28:28 + bytesInDouble])[0]
return timeReceived - timeSent
timeLeft = timeLeft - howLongInSelect
if timeLeft <= 0:
return
# from https://github.com/samuel/python-ping/blob/master/ping.py
def send_one_ping(my_socket, dest_addr, ID):
"""
Send one ping to the given >dest_addr<.
"""
dest_addr = socket.gethostbyname(dest_addr)
# Header is type (8), code (8), checksum (16), id (16), sequence (16)
my_checksum = 0
# Make a dummy heder with a 0 checksum.
header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, my_checksum, ID, 1)
bytesInDouble = struct.calcsize("d")
data = (192 - bytesInDouble) * "Q"
if Py3:
data = struct.pack("d", default_timer()) + bytes(data, "ascii")
else:
data = struct.pack("d", default_timer()) + data
# Calculate the checksum on the data and the dummy header.
my_checksum = checksum(header + data)
# Now that we have the right checksum, we put that in. It's just easier
# to make up a new header than to stuff it into the dummy.
header = struct.pack(
"bbHHh", ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), ID, 1
)
packet = header + data
my_socket.sendto(packet, (dest_addr, 1)) # Don't know about the 1
# from https://github.com/samuel/python-ping/blob/master/ping.py
def do_one(dest_addr, timeout):
"""
Returns either the delay (in seconds) or none on timeout.
"""
icmp = socket.getprotobyname("icmp")
try:
my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)
except socket.error as serror:
(errno, msg) = serror.args
if errno == 1:
# Operation not permitted
msg = msg + (
" - Note that ICMP messages can only be sent from processes"
" running as root."
)
raise socket.error(msg)
raise # raise the original error
my_ID = os.getpid() & 0xFFFF
send_one_ping(my_socket, dest_addr, my_ID)
delay = receive_one_ping(my_socket, my_ID, timeout)
my_socket.close()
return delay
# from https://github.com/samuel/python-ping/blob/master/ping.py
def verbose_ping(dest_addr, timeout=2, count=4):
"""
Send >count< ping to >dest_addr< with the given >timeout< and display
the result.
"""
for i in range(count):
print("ping %s..." % dest_addr)
try:
delay = do_one(dest_addr, timeout)
except socket.gaierror as e:
print("failed. (socket error: '%s')" % e[1])
break
if delay is None:
print("failed. (timeout within %ssec.)" % timeout)
else:
delay = delay * 1000
print("get ping in %0.4fms" % delay)
print()
# main
def main():
""" Main check parameters & ping host with icmp/tcp/udp """
if len(sys.argv) == 2:
host = sys.argv[1]
port = 80
elif len(sys.argv) == 3:
host = sys.argv[1]
port = int(sys.argv[2])
else:
print("Usage: python ping.py host port")
sys.exit(0)
print("ping " + host + " port " + str(port))
try:
icmp(host)
tcp(host, port)
udp(host, port)
except socket.error as msg:
print('Connection failed : ' + str(msg))
sys.exit(-1)
if __name__ == "__main__":
main()