yokmp

Ping for Python3.x

Oct 11th, 2011
1,227
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.11 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # coding: utf-8
  3.  
  4. """
  5.    A pure python ping implementation using raw sockets.
  6.  
  7.    Note that ICMP messages can only be sent from processes running as root
  8.    (in Windows, you must run this script as 'Administrator').
  9.  
  10.    Original Version from Matthew Dixon Cowles:
  11.      -> ftp://ftp.visi.com/users/mdc/ping.py
  12.  
  13.    Rewrite by Jens Diemer:
  14.      -> http://www.python-forum.de/post-69122.html#69122
  15.  
  16.    Rewrite by George Notaras:
  17.      -> http://www.g-loaded.eu/2009/10/30/python-ping/
  18.  
  19.    Enhancements by Martin Falatic:
  20.      -> http://www.falatic.com/index.php/39/pinging-with-python
  21.  
  22.    Edited by yokmp:
  23.     I've done this because some lines doesn't work in Python3.2 and i needed it
  24.     a bit more interactive. See the last lines for detail.
  25.  
  26.    ===========================================================================
  27. """
  28.  
  29.  
  30. import os, sys, socket, struct, select, time, signal
  31.  
  32.  
  33. # ICMP parameters
  34.  
  35. ICMP_ECHOREPLY = 0 # Echo reply (per RFC792)
  36. ICMP_ECHO = 8 # Echo request (per RFC792)
  37. ICMP_MAX_RECV = 2048 # Max size of incoming buffer
  38.  
  39. MAX_SLEEP = 1000
  40.  
  41. class MyStats:
  42.     thisIP = "0.0.0.0"
  43.     pktsSent = 0
  44.     pktsRcvd = 0
  45.     minTime = 999999999
  46.     maxTime = 0
  47.     totTime = 0
  48.     fracLoss = 1.0
  49.  
  50. myStats = MyStats # Used globally
  51.  
  52.  
  53. def checksum(source_string):
  54.     """
  55.    A port of the functionality of in_cksum() from ping.c
  56.    Ideally this would act on the string as a series of 16-bit ints (host
  57.    packed), but this works.
  58.    Network data is big-endian, hosts are typically little-endian
  59.    """
  60.     countTo = (int(len(source_string) / 2)) * 2
  61.     sum = 0
  62.     count = 0
  63.  
  64.     # Handle bytes in pairs (decoding as short ints)
  65.     loByte = 0
  66.     hiByte = 0
  67.     while count < countTo:
  68.         if (sys.byteorder == "little"):
  69.             loByte = source_string[count]
  70.             hiByte = source_string[count + 1]
  71.         else:
  72.             loByte = source_string[count + 1]
  73.             hiByte = source_string[count]
  74.         sum = sum + (hiByte * 256 + loByte)
  75.         count += 2
  76.  
  77.     # Handle last byte if applicable (odd-number of bytes)
  78.     # Endianness should be irrelevant in this case
  79.     if countTo < len(source_string): # Check for odd length
  80.         loByte = source_string[len(source_string) - 1]
  81.         sum += loByte
  82.  
  83.     sum &= 0xffffffff # Truncate sum to 32 bits (a variance from ping.c, which
  84.                       # uses signed ints, but overflow is unlikely in ping)
  85.  
  86.     sum = (sum >> 16) + (sum & 0xffff)    # Add high 16 bits to low 16 bits
  87.     sum += (sum >> 16)                    # Add carry from above (if any)
  88.     answer = ~sum & 0xffff                # Invert and truncate to 16 bits
  89.     answer = socket.htons(answer)
  90.  
  91.     return answer
  92.  
  93.  
  94. def do_one(destIP, timeout, mySeqNumber, numDataBytes):
  95.     """
  96.    Returns either the delay (in ms) or None on timeout.
  97.    """
  98.     global myStats
  99.  
  100.     delay = None
  101.  
  102.     try: # One could use UDP here, but it's obscure
  103.         mySocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname("icmp"))
  104.     except (socket.error, (errno, msg)):
  105.         if errno == 1:
  106.             # Operation not permitted - Add more information to traceback
  107.             etype, evalue, etb = sys.exc_info()
  108.             evalue = etype(
  109.                 "%s - Note that ICMP messages can only be sent from processes running as root." % evalue
  110.             )
  111.             raise (etype, evalue, etb)
  112.  
  113.         print("failed. (socket error: '%s')" % msg)
  114.         raise # raise the original error
  115.  
  116.     my_ID = os.getpid() & 0xFFFF
  117.  
  118.     sentTime = send_one_ping(mySocket, destIP, my_ID, mySeqNumber, numDataBytes)
  119.     if sentTime == None:
  120.         mySocket.close()
  121.         return delay
  122.  
  123.     myStats.pktsSent += 1;
  124.  
  125.     recvTime, dataSize, iphSrcIP, icmpSeqNumber, iphTTL = receive_one_ping(mySocket, my_ID, timeout)
  126.  
  127.     mySocket.close()
  128.  
  129.     if recvTime:
  130.         delay = (recvTime - sentTime) * 1000
  131.         print("%d bytes from %s: icmp_seq=%d ttl=%d time=%d ms" % (
  132.             dataSize, socket.inet_ntoa(struct.pack("!I", iphSrcIP)), icmpSeqNumber, iphTTL, delay)
  133.         )
  134.         myStats.pktsRcvd += 1;
  135.         myStats.totTime += delay
  136.         if myStats.minTime > delay:
  137.             myStats.minTime = delay
  138.         if myStats.maxTime < delay:
  139.             myStats.maxTime = delay
  140.     else:
  141.         delay = None
  142.         print("Request timed out.")
  143.  
  144.     return delay
  145.  
  146.  
  147. def send_one_ping(mySocket, destIP, myID, mySeqNumber, numDataBytes):
  148.     """
  149.    Send one ping to the given >destIP<.
  150.    """
  151.     destIP = socket.gethostbyname(destIP)
  152.  
  153.     # Header is type (8), code (8), checksum (16), id (16), sequence (16)
  154.     myChecksum = 0
  155.  
  156.     # Make a dummy heder with a 0 checksum.
  157.     header = struct.pack(
  158.         "!BBHHH", ICMP_ECHO, 0, myChecksum, myID, mySeqNumber
  159.     )
  160.  
  161.     padBytes = []
  162.     startVal = 0x42
  163.     for i in range(startVal, startVal + (numDataBytes)):
  164.         padBytes += [(i & 0xff)]  # Keep chars in the 0-255 range
  165.     data = bytes(padBytes)
  166.  
  167.     # Calculate the checksum on the data and the dummy header.
  168.     myChecksum = checksum(header + data) # Checksum is in network order
  169.  
  170.     # Now that we have the right checksum, we put that in. It's just easier
  171.     # to make up a new header than to stuff it into the dummy.
  172.     header = struct.pack(
  173.         "!BBHHH", ICMP_ECHO, 0, myChecksum, myID, mySeqNumber
  174.     )
  175.  
  176.     packet = header + data
  177.  
  178.     sendTime = time.time()
  179.  
  180.     try:
  181.         mySocket.sendto(packet, (destIP, 1)) # Port number is irrelevant for ICMP
  182.     except socket.error as e:
  183.         print("General failure (%s)" % (e.args[1]))
  184.         return
  185.  
  186.     return sendTime
  187.  
  188.  
  189. def receive_one_ping(mySocket, myID, timeout):
  190.     """
  191.    Receive the ping from the socket. Timeout = in ms
  192.    """
  193.     timeLeft = timeout / 1000
  194.  
  195.     while True: # Loop while waiting for packet or timeout
  196.         startedSelect = time.time()
  197.         whatReady = select.select([mySocket], [], [], timeLeft)
  198.         howLongInSelect = (time.time() - startedSelect)
  199.         if whatReady[0] == []: # Timeout
  200.             return None, 0, 0, 0, 0
  201.  
  202.         timeReceived = time.time()
  203.  
  204.         recPacket, addr = mySocket.recvfrom(ICMP_MAX_RECV)
  205.  
  206.         ipHeader = recPacket[:20]
  207.         iphVersion, iphTypeOfSvc, iphLength, \
  208.         iphID, iphFlags, iphTTL, iphProtocol, \
  209.         iphChecksum, iphSrcIP, iphDestIP = struct.unpack(
  210.             "!BBHHHBBHII", ipHeader
  211.         )
  212.  
  213.         icmpHeader = recPacket[20:28]
  214.         icmpType, icmpCode, icmpChecksum, \
  215.         icmpPacketID, icmpSeqNumber = struct.unpack(
  216.             "!BBHHH", icmpHeader
  217.         )
  218.  
  219.         if icmpPacketID == myID: # Our packet
  220.             dataSize = len(recPacket) - 28
  221.             return timeReceived, dataSize, iphSrcIP, icmpSeqNumber, iphTTL
  222.  
  223.         timeLeft = timeLeft - howLongInSelect
  224.         if timeLeft <= 0:
  225.             return None, 0, 0, 0, 0
  226.  
  227.  
  228. def dump_stats():
  229.     """
  230.    Show stats when pings are done
  231.    """
  232.     global myStats
  233.  
  234.     print("\n----%s PYTHON PING Statistics----" % (myStats.thisIP))
  235.  
  236.     if myStats.pktsSent > 0:
  237.         myStats.fracLoss = (myStats.pktsSent - myStats.pktsRcvd) / myStats.pktsSent
  238.  
  239.     print("%d packets transmitted, %d packets received, %0.1f%% packet loss" % (
  240.         myStats.pktsSent, myStats.pktsRcvd, 100.0 * myStats.fracLoss
  241.     ))
  242.  
  243.     if myStats.pktsRcvd > 0:
  244.         print("round-trip (ms)  min/avg/max = %d/%0.1f/%d" % (
  245.             myStats.minTime, myStats.totTime / myStats.pktsRcvd, myStats.maxTime
  246.         ))
  247.  
  248.     print()
  249.     return
  250.  
  251.  
  252. def signal_handler(signum, frame):
  253.     """
  254.    Handle exit via signals
  255.    """
  256.     dump_stats()
  257.     print("\n(Terminated with signal %d)\n" % (signum))
  258.     sys.exit(0)
  259.  
  260.  
  261. def pyng(hostname, timeout=1000, count=3, numDataBytes=55):
  262.     """
  263.    Send >count< ping to >destIP< with the given >timeout< and display
  264.    the result.
  265.    """
  266.     global myStats
  267.  
  268.     signal.signal(signal.SIGINT, signal_handler)   # Handle Ctrl-C
  269.     if hasattr(signal, "SIGBREAK"):
  270.         # Handle Ctrl-Break e.g. under Windows
  271.         signal.signal(signal.SIGBREAK, signal_handler)
  272.  
  273.     myStats = MyStats() # Reset the stats
  274.  
  275.     mySeqNumber = 0 # Starting value
  276.  
  277.     try:
  278.         destIP = socket.gethostbyname(hostname)
  279.         print("\nPYTHON-PING %s (%s): %d data bytes" % (hostname, destIP, numDataBytes))
  280.     except socket.gaierror as e:
  281.         print("\nPYTHON-PING: Unknown host: %s (%s)" % (hostname, e.args[1]))
  282.         print()
  283.         return
  284.  
  285.     myStats.thisIP = destIP
  286.  
  287.     for i in range(count):
  288.         delay = do_one(destIP, timeout, mySeqNumber, numDataBytes)
  289.  
  290.         if delay == None:
  291.             delay = 0
  292.  
  293.         mySeqNumber += 1
  294.  
  295.         # Pause for the remainder of the MAX_SLEEP period (if applicable)
  296.         if (MAX_SLEEP > delay):
  297.             time.sleep((MAX_SLEEP - delay) / 1000)
  298.  
  299.     dump_stats()
  300.     global interactive
  301.     if (interactive==True):
  302.         interactive=False
  303.         input("Press any key ")
  304.  
  305. if __name__ == '__main__':
  306.     # lets be a bit more interactive:
  307.  
  308.     pyngthis=input("IP or Hostname: ")
  309.     interactive=True
  310.     pyng(pyngthis)
  311.  
  312.  
  313.  
  314.  
  315.  
Advertisement
Add Comment
Please, Sign In to add comment