Advertisement
opexxx

ping.py

Aug 18th, 2014
378
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.77 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. """
  4.    A pure python ping implementation using raw socket.
  5.  
  6.  
  7.    Note that ICMP messages can only be sent from processes running as root.
  8.  
  9.  
  10.    Derived from ping.c distributed in Linux's netkit. That code is
  11.    copyright (c) 1989 by The Regents of the University of California.
  12.    That code is in turn derived from code written by Mike Muuss of the
  13.    US Army Ballistic Research Laboratory in December, 1983 and
  14.    placed in the public domain. They have my thanks.
  15.  
  16.    Bugs are naturally mine. I'd be glad to hear about them. There are
  17.    certainly word - size dependenceies here.
  18.  
  19.    Copyright (c) Matthew Dixon Cowles, <http://www.visi.com/~mdc/>.
  20.    Distributable under the terms of the GNU General Public License
  21.    version 2. Provided with no warranties of any sort.
  22.  
  23.    Original Version from Matthew Dixon Cowles:
  24.      -> ftp://ftp.visi.com/users/mdc/ping.py
  25.  
  26.    Rewrite by Jens Diemer:
  27.      -> http://www.python-forum.de/post-69122.html#69122
  28.  
  29.  
  30.    Revision history
  31.    ~~~~~~~~~~~~~~~~
  32.  
  33.    March 11, 2010
  34.    changes by Samuel Stauffer:
  35.    - replaced time.clock with default_timer which is set to
  36.      time.clock on windows and time.time on other systems.
  37.  
  38.    May 30, 2007
  39.    little rewrite by Jens Diemer:
  40.     -  change socket asterisk import to a normal import
  41.     -  replace time.time() with time.clock()
  42.     -  delete "return None" (or change to "return" only)
  43.     -  in checksum() rename "str" to "source_string"
  44.  
  45.    November 22, 1997
  46.    Initial hack. Doesn't do much, but rather than try to guess
  47.    what features I (or others) will want in the future, I've only
  48.    put in what I need now.
  49.  
  50.    December 16, 1997
  51.    For some reason, the checksum bytes are in the wrong order when
  52.    this is run under Solaris 2.X for SPARC but it works right under
  53.    Linux x86. Since I don't know just what's wrong, I'll swap the
  54.    bytes always and then do an htons().
  55.  
  56.    December 4, 2000
  57.    Changed the struct.pack() calls to pack the checksum and ID as
  58.    unsigned. My thanks to Jerome Poincheval for the fix.
  59.  
  60.  
  61.    Last commit info:
  62.    ~~~~~~~~~~~~~~~~~
  63.    $LastChangedDate: $
  64.    $Rev: $
  65.    $Author: $
  66. """
  67.  
  68.  
  69. import os, sys, socket, struct, select, time
  70.  
  71. if sys.platform == "win32":
  72.     # On Windows, the best timer is time.clock()
  73.     default_timer = time.clock
  74. else:
  75.     # On most other platforms the best timer is time.time()
  76.     default_timer = time.time
  77.  
  78. # From /usr/include/linux/icmp.h; your milage may vary.
  79. ICMP_ECHO_REQUEST = 8 # Seems to be the same on Solaris.
  80.  
  81.  
  82. def checksum(source_string):
  83.     """
  84.    I'm not too confident that this is right but testing seems
  85.    to suggest that it gives the same answers as in_cksum in ping.c
  86.    """
  87.     sum = 0
  88.     countTo = (len(source_string)/2)*2
  89.     count = 0
  90.     while count<countTo:
  91.         thisVal = ord(source_string[count + 1])*256 + ord(source_string[count])
  92.         sum = sum + thisVal
  93.         sum = sum & 0xffffffff # Necessary?
  94.         count = count + 2
  95.  
  96.     if countTo<len(source_string):
  97.         sum = sum + ord(source_string[len(source_string) - 1])
  98.         sum = sum & 0xffffffff # Necessary?
  99.  
  100.     sum = (sum >> 16)  +  (sum & 0xffff)
  101.     sum = sum + (sum >> 16)
  102.     answer = ~sum
  103.     answer = answer & 0xffff
  104.  
  105.     # Swap bytes. Bugger me if I know why.
  106.     answer = answer >> 8 | (answer << 8 & 0xff00)
  107.  
  108.     return answer
  109.  
  110.  
  111. def receive_one_ping(my_socket, ID, timeout):
  112.     """
  113.    receive the ping from the socket.
  114.    """
  115.     timeLeft = timeout
  116.     while True:
  117.         startedSelect = default_timer()
  118.         whatReady = select.select([my_socket], [], [], timeLeft)
  119.         howLongInSelect = (default_timer() - startedSelect)
  120.         if whatReady[0] == []: # Timeout
  121.             return
  122.  
  123.         timeReceived = default_timer()
  124.         recPacket, addr = my_socket.recvfrom(1024)
  125.         icmpHeader = recPacket[20:28]
  126.         type, code, checksum, packetID, sequence = struct.unpack(
  127.             "bbHHh", icmpHeader
  128.         )
  129.         if packetID == ID:
  130.             bytesInDouble = struct.calcsize("d")
  131.             timeSent = struct.unpack("d", recPacket[28:28 + bytesInDouble])[0]
  132.             return timeReceived - timeSent
  133.  
  134.         timeLeft = timeLeft - howLongInSelect
  135.         if timeLeft <= 0:
  136.             return
  137.  
  138.  
  139. def send_one_ping(my_socket, dest_addr, ID):
  140.     """
  141.    Send one ping to the given >dest_addr<.
  142.    """
  143.     dest_addr  =  socket.gethostbyname(dest_addr)
  144.  
  145.     # Header is type (8), code (8), checksum (16), id (16), sequence (16)
  146.     my_checksum = 0
  147.  
  148.     # Make a dummy heder with a 0 checksum.
  149.     header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, my_checksum, ID, 1)
  150.     bytesInDouble = struct.calcsize("d")
  151.     data = (192 - bytesInDouble) * "Q"
  152.     data = struct.pack("d", default_timer()) + data
  153.  
  154.     # Calculate the checksum on the data and the dummy header.
  155.     my_checksum = checksum(header + data)
  156.  
  157.     # Now that we have the right checksum, we put that in. It's just easier
  158.     # to make up a new header than to stuff it into the dummy.
  159.     header = struct.pack(
  160.         "bbHHh", ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), ID, 1
  161.     )
  162.     packet = header + data
  163.     my_socket.sendto(packet, (dest_addr, 1)) # Don't know about the 1
  164.  
  165.  
  166. def do_one(dest_addr, timeout):
  167.     """
  168.    Returns either the delay (in seconds) or none on timeout.
  169.    """
  170.     icmp = socket.getprotobyname("icmp")
  171.     try:
  172.         my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)
  173.     except socket.error, (errno, msg):
  174.         if errno == 1:
  175.             # Operation not permitted
  176.             msg = msg + (
  177.                 " - Note that ICMP messages can only be sent from processes"
  178.                 " running as root."
  179.             )
  180.             raise socket.error(msg)
  181.         raise # raise the original error
  182.  
  183.     my_ID = os.getpid() & 0xFFFF
  184.  
  185.     send_one_ping(my_socket, dest_addr, my_ID)
  186.     delay = receive_one_ping(my_socket, my_ID, timeout)
  187.  
  188.     my_socket.close()
  189.     return delay
  190.  
  191.  
  192. def verbose_ping(dest_addr, timeout = 2, count = 4):
  193.     """
  194.    Send >count< ping to >dest_addr< with the given >timeout< and display
  195.    the result.
  196.    """
  197.     for i in xrange(count):
  198.         print "ping %s..." % dest_addr,
  199.         try:
  200.             delay  =  do_one(dest_addr, timeout)
  201.         except socket.gaierror, e:
  202.             print "failed. (socket error: '%s')" % e[1]
  203.             break
  204.  
  205.         if delay  ==  None:
  206.             print "failed. (timeout within %ssec.)" % timeout
  207.         else:
  208.             delay  =  delay * 1000
  209.             print "get ping in %0.4fms" % delay
  210.     print
  211.  
  212.  
  213. if __name__ == '__main__':
  214.     verbose_ping("heise.de")
  215.     verbose_ping("google.com")
  216.     verbose_ping("a-test-url-taht-is-not-available.com")
  217.     verbose_ping("192.168.1.1")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement