diff --git a/ping.py b/ping.py index 73400fa..f276b6d 100644 --- a/ping.py +++ b/ping.py @@ -1,103 +1,29 @@ -#!/usr/bin/env python - -""" - A pure python ping implementation using raw socket. - - - Note that ICMP messages can only be sent from processes running as root. - - - Derived from ping.c distributed in Linux's netkit. That code is - copyright (c) 1989 by The Regents of the University of California. - That code is in turn derived from code written by Mike Muuss of the - US Army Ballistic Research Laboratory in December, 1983 and - placed in the public domain. They have my thanks. - - Bugs are naturally mine. I'd be glad to hear about them. There are - certainly word - size dependenceies here. - - Copyright (c) Matthew Dixon Cowles, . - Distributable under the terms of the GNU General Public License - version 2. Provided with no warranties of any sort. - - Original Version from Matthew Dixon Cowles: - -> ftp://ftp.visi.com/users/mdc/ping.py - - Rewrite by Jens Diemer: - -> http://www.python-forum.de/post-69122.html#69122 - - - Revision history - ~~~~~~~~~~~~~~~~ - - March 11, 2010 - changes by Samuel Stauffer: - - replaced time.clock with default_timer which is set to - time.clock on windows and time.time on other systems. - - May 30, 2007 - little rewrite by Jens Diemer: - - change socket asterisk import to a normal import - - replace time.time() with time.clock() - - delete "return None" (or change to "return" only) - - in checksum() rename "str" to "source_string" - - November 22, 1997 - Initial hack. Doesn't do much, but rather than try to guess - what features I (or others) will want in the future, I've only - put in what I need now. - - December 16, 1997 - For some reason, the checksum bytes are in the wrong order when - this is run under Solaris 2.X for SPARC but it works right under - Linux x86. Since I don't know just what's wrong, I'll swap the - bytes always and then do an htons(). - - December 4, 2000 - Changed the struct.pack() calls to pack the checksum and ID as - unsigned. My thanks to Jerome Poincheval for the fix. - - Januari 27, 2015 - Changed receive response to not accept ICMP request messages. - It was possible to receive the very request that was sent. - - Last commit info: - ~~~~~~~~~~~~~~~~~ - $LastChangedDate: $ - $Rev: $ - $Author: $ -""" - - -import os, sys, socket, struct, select, time - -if sys.platform == "win32": - # On Windows, the best timer is time.clock() - default_timer = time.clock +import os, sys, socket, struct, select, time, base64 + +if sys.platform.startswith('win'): + default_timer = time.perf_counter() +elif sys.platform.startswith('linux'): + default_timer = time.perf_counter() +if sys.platform.startswith('darwin'): + #default_timer = time.clock() + default_timer = time.perf_counter() else: - # On most other platforms the best timer is time.time() - default_timer = time.time + default_timer = time.perf_counter() -# From /usr/include/linux/icmp.h; your milage may vary. ICMP_ECHO_REQUEST = 8 # Seems to be the same on Solaris. - 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 - """ sum = 0 - countTo = (len(source_string)/2)*2 + count_to = (len(source_string)//2)*2 count = 0 - while count> 16) + (sum & 0xffff) @@ -110,54 +36,58 @@ def checksum(source_string): return answer - -def receive_one_ping(my_socket, ID, timeout): +def recieve_one_ping(my_socket, ID, timeout): """ receive the ping from the socket. """ - timeLeft = timeout + time_left = timeout while True: - startedSelect = default_timer() - whatReady = select.select([my_socket], [], [], timeLeft) - howLongInSelect = (default_timer() - startedSelect) - if whatReady[0] == []: # Timeout + started_select = default_timer + what_ready = select.select([my_socket], [], [], time_left) + how_long_in_select = (default_timer - started_select) # removed default_timer() + if what_ready[0] == []: return - timeReceived = default_timer() - recPacket, addr = my_socket.recvfrom(1024) - icmpHeader = recPacket[20:28] - type, code, checksum, packetID, sequence = struct.unpack( - "bbHHh", icmpHeader + time_recieved = default_timer + rec_packet, addr = my_socket.recvfrom(1024) # changed recv_from to recvfrom + icmp_header = rec_packet[20:28] + type, code, checksum, packet_id, sequence = struct.unpack( + 'bbHHh', icmp_header ) - # Filters out the echo request itself. - # This can be tested by pinging 127.0.0.1 + # Filters out the echo request itself. + # This can be tested by pinging 127.0.0.1 # You'll see your own request - if type != 8 and packetID == ID: - bytesInDouble = struct.calcsize("d") - timeSent = struct.unpack("d", recPacket[28:28 + bytesInDouble])[0] - return timeReceived - timeSent + if type != 8 and packet_id == ID: + bytes_in_double = struct.calcsize('d') + time_sent = struct.unpack('d', rec_packet[28:28 + bytes_in_double])[0] + return time_recieved - time_sent - timeLeft = timeLeft - howLongInSelect - if timeLeft <= 0: + time_left = time_left - how_long_in_select + if time_left <= 0: return - def send_one_ping(my_socket, dest_addr, ID): """ Send one ping to the given >dest_addr<. """ - dest_addr = socket.gethostbyname(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" - data = struct.pack("d", default_timer()) + data + bytes_in_double = struct.calcsize('d') + data = (192 - bytes_in_double) * 'Q' + data = struct.pack('d', default_timer) + bytes(data.encode('utf-8')) + # base64.b64encode(bytes(data.encode('utf-8'))) # Calculate the checksum on the data and the dummy header. + + # data = base64.b64decode(data) works half the time or fails with incorrect padding + # + + data = decode_base64(data) my_checksum = checksum(header + data) # Now that we have the right checksum, we put that in. It's just easier @@ -168,7 +98,6 @@ def send_one_ping(my_socket, dest_addr, ID): packet = header + data my_socket.sendto(packet, (dest_addr, 1)) # Don't know about the 1 - def do_one(dest_addr, timeout): """ Returns either the delay (in seconds) or none on timeout. @@ -176,7 +105,7 @@ def do_one(dest_addr, timeout): icmp = socket.getprotobyname("icmp") try: my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp) - except socket.error, (errno, msg): + except socket.error (errno, msg): # removed ',' from excep, socket.error, (errno, msg) if errno == 1: # Operation not permitted msg = msg + ( @@ -189,32 +118,38 @@ def do_one(dest_addr, timeout): my_ID = os.getpid() & 0xFFFF send_one_ping(my_socket, dest_addr, my_ID) - delay = receive_one_ping(my_socket, my_ID, timeout) - + delay = recieve_one_ping(my_socket, my_ID, timeout) my_socket.close() return delay - 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 xrange(count): - print "ping %s..." % dest_addr, + + for i in range(count): # changed xrange() to range() + print("ping %s..." % dest_addr,) # removed , maybe take out end='' try: delay = do_one(dest_addr, timeout) - except socket.gaierror, e: - print "failed. (socket error: '%s')" % e[1] + + except socket.gaierror as e: + print("failed. (socket error: '%s')" % e) # e[1] break + if delay == None: - print "failed. (timeout within %ssec.)" % timeout + print("failed. (timeout within %ssec.)" % timeout) else: delay = delay * 1000 - print "get ping in %0.4fms" % delay + print("get ping in %0.4fms" % delay) print +def decode_base64(your_data): + padding = len(your_data) % 4 + if padding != 0: + your_data += b'='* (4 - padding) + return base64.b64decode(your_data) if __name__ == '__main__': verbose_ping("heise.de") diff --git a/ping_py_changes.txt b/ping_py_changes.txt new file mode 100644 index 0000000..408409a --- /dev/null +++ b/ping_py_changes.txt @@ -0,0 +1,35 @@ +Added the following changes: + +changed if sys.platform() to sys.platform.startswith() at lines 3, 6, and 9 + +Added two extra if statements line 6, and 9 for detecting linux and mac + +removed () from default_timer at lines 46, 48, 52 and 82 + +Changes time.clock() which is depreciated to time.perf_counter() as lines 3 , 6 +9, and 14 + +Changed count_to = (len(source_string) / 2) * 2 to +count_to = (len(source_string) // 2) * 2 at line 20 + +Changed ord(ord(source_string[count + 1]) * 256 + ord(source_string[count])) to +(source_string[count + 1]) * 256 + source_string[count] at line 23 + + +Renamed variables into more readable format. + + +Changed except socket.error, (errno, msg): to except socket.error (errno, msg) +at line 104 + +Changed except socket.gaierror, e: to except socket.gaierror as e: at line 130 + +Changed xrange() to range at line 125 because xrange was changed to range in +python 3+ + +Changed print("ping %s..." % dest_addr,) to print("ping %s..." % dest_addr) +at line 126 + +added padding function + +added encoding at line 83 and decoding at line 90