SHARE
TWEET

Ping

Jervase Apr 1st, 2012 617 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import os,sys,socket,struct,select,time,binascii,logging
  2. import ping_reporter
  3.  
  4. ping_count = 0
  5. ping_bandwidth = 0
  6. log = ping_reporter.setup_log('Ping')
  7. server_list = ['www.google.com','wikipedia.org','www.whitehouse.gov','127.0.0.1']
  8.  
  9. def select_server(log,max_timeout=2):
  10.         server = ''
  11.         log.notice('selecting server')
  12.         maxw = len(max(server_list, key=len))
  13.         min_delay = max_timeout * 1000 # seconds -> ms
  14.         for x in server_list:
  15.                 delay = min_delay + 1
  16.                 try: delay = single_ping(x,max_timeout)
  17.                 finally:
  18.                         if delay == None: log.notice('%-*s: timed out'%(maxw,x))
  19.                         else:             log.notice('%-*s: %05.02fms'%(maxw,x,delay*1000))
  20.                         if delay != None and delay < min_delay:
  21.                                 min_delay = delay
  22.                                 server = x
  23.         log.info('selected server: %s (%.02fms)'%(server,min_delay*1000))
  24.         return server
  25.  
  26. def carry_add(a, b):
  27.         c = a + b
  28.         return (c & 0xFFFF) + (c >> 16)
  29.  
  30. def checksum(msg):
  31.         s = 0
  32.         if len(msg)%2: # pad with NULL
  33.                 msg = msg + '%c'%0
  34.         for i in range(0, len(msg)/2*2, 2):
  35.                 w = ord(msg[i]) + (ord(msg[i+1]) << 8)
  36.                 s = carry_add(s, w)
  37.         return ~s & 0xFFFF
  38.  
  39. def build_ping(ID, data):
  40.         log.trace('ping::build_ping: ID=%d, bytes=%d'%(ID,len(data)))
  41.         if ID == 0: raise Exception('Invalid BlockID (0): many servers will corrupt ID=0 ICMP messages')
  42.  
  43.         data = str(data) # string type, like the packed result
  44.  
  45.         # Header is type (8), code (8), checksum (16), id (16), sequence (16)
  46.         icmp_type      = 8 # ICMP_ECHO_REQUEST
  47.         icmp_code      = 0 # Can be anything, but reply MUST be 0
  48.         icmp_checksum  = 0 # 0 for initial checksum calculation
  49. #       icmp_id        = (ID >> 16) & 0xFFFF
  50. #       icmp_sequence  = (ID <<  0) & 0xFFFF
  51.         block_id       = ID # append id & seq for 4-byte identifier
  52.  
  53.         header = struct.pack("bbHI", icmp_type, icmp_code, icmp_checksum, block_id)
  54.         icmp_checksum = checksum(header+data)
  55.         header = struct.pack("bbHI", icmp_type, icmp_code, icmp_checksum, block_id)
  56.  
  57.         # Return built ICMP message
  58.         return header+data
  59.  
  60. def build_socket(RCVBUF=1024*1024):
  61. # By default, SO_RCVBUF is ~50k (kernel doubles to 114688), which only supports
  62. # ~1k blocks with <1ms timing. Raising this to 1m supports >16k blocks. Unfortunately,
  63. # raising it more does little because we can't read/process the events fast enough, so
  64. # the buffer pretty quickly fills, and then start dropping packets again.
  65.         log.trace('ping::build_socket')
  66.         icmp = socket.getprotobyname("icmp")
  67.         try:
  68.                 icmp_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)
  69.         except socket.error, (errno, msg):
  70.                 if errno == 1: # Operation not permitted
  71.                         msg = msg + (" (ICMP messages can only be sent from processes running as root)")
  72.                         raise socket.error(msg)
  73.                 raise # raise the original error
  74.         socket.SO_SNDBUFFORCE = 32
  75.         socket.SO_RCVBUFFORCE = 33
  76.         icmp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUFFORCE, RCVBUF)
  77.         icmp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUFFORCE, RCVBUF)
  78.         return icmp_socket
  79.  
  80. def time_ping(d_socket, d_addr, ID=1):
  81.         log.trace('ping::time_ping: server=%s ID=%d'%(d_addr,ID))
  82.         data = struct.pack("d",time.time())
  83.         return data_ping(d_socket, d_addr, ID, data)
  84.  
  85. def data_ping(d_socket, d_addr, ID, data):
  86.         log.trace('ping::data_ping: server=%s ID=%d bytes=%d'%(d_addr,ID,len(data)))
  87.         send_ping(d_socket, socket.gethostbyname(d_addr), ID, data)
  88.  
  89. def send_ping(d_socket, d_addr, ID, data):
  90.         log.trace('ping::send_ping: server=%s ID=%d bytes=%d'%(d_addr,ID,len(data)))
  91.         global ping_count, ping_bandwidth
  92.         packet = build_ping(ID,data)
  93.         d_socket.sendto(packet, (d_addr, 1))
  94.         if 1:
  95.                 ping_count = ping_count + 1
  96.                 ping_bandwidth = ping_bandwidth + len(packet)
  97.  
  98. def parse_ip(packet):
  99.         log.trace('ping::parse_ip: bytes=%d'%(len(packet)))
  100.         if len(packet) < 20: return None
  101.         (verlen,ID,flags,frag,ttl,protocol,csum,src,dst) = struct.unpack('!B3xH4BHII',packet[:20])
  102.         ip = dict(  version= verlen >> 4,
  103.                                 length=  4*(verlen & 0xF),
  104.                                 ID=      ID,
  105.                                 flags=   flags >> 5,
  106.                                 fragment=((flags & 0x1F)+frag),
  107.                                 ttl=     ttl,
  108.                                 protocol=protocol,
  109.                                 checksum=csum,
  110.                                 src=     src,
  111.                                 dst=     dst)
  112.         return ip
  113.  
  114. def parse_icmp(packet,validate):
  115.         log.trace('ping::parse_icmp: bytes=%d'%(len(packet)))
  116.         if len(packet) < 8: return None
  117.         (type, code, csum, block_id) = struct.unpack('bbHI', packet[:8])
  118.         log.debug('ping::parse_icmp: type=%d code=%d csum=%x ID=%d'%(type,code,csum,block_id))
  119.         icmp = dict(type=type,
  120.                                 code=code,
  121.                                 checksum=csum, # calculated big-endian
  122.                                 block_id=block_id)
  123.  
  124.         if validate:
  125.                 t_header = struct.pack('bbHI',type,code,0,block_id)
  126.                 t_csum = checksum(t_header+packet[8:])
  127.                 icmp['valid'] = (t_csum == csum)
  128.                
  129.         return icmp
  130.  
  131. def parse_ping(packet,validate=False):
  132.         log.trace('ping::parse_ping: bytes=%d validate=%s'%(len(packet),validate))
  133.         if len(packet) < 20+8+1: return None # require 1 block of data
  134.         ip = parse_ip(packet)
  135.         if not ip:                                return None
  136.         if ip['protocol'] != socket.IPPROTO_ICMP: return None # ICMP
  137.         if ip['version'] != socket.IPPROTO_IPIP:  return None # IPv4
  138.         if ip['length']+8+1 > len(packet):        return None # invalid ICMP header
  139.  
  140.         packet = packet[ip['length']:]
  141.         icmp = parse_icmp(packet,validate)
  142.         if not icmp:                              return None
  143.         if icmp['type'] != 0:                     return None # not an Echo Reply packet
  144.         if icmp['code'] != 0:                     return None # not a valid Echo Reply packet
  145.         if validate and icmp['valid'] != True:    return None # invalid ICMP checksum
  146.  
  147.         payload = packet[8:]
  148.         log.debug('ping::parse_ping: valid echo reply w/ ID=%d (%d bytes)'%(icmp['block_id'],len(payload)))
  149.         return dict(ip=ip,icmp=icmp,payload=payload)
  150.        
  151.  
  152. def recv_ping(d_socket, timeout, validate=False):
  153.         d_socket.settimeout(timeout)
  154.         try:
  155.                 data,addr = d_socket.recvfrom(2048)
  156.         except socket.timeout:
  157.                 return None
  158.         parsed = parse_ping(data,validate)
  159.         if not parsed: return None
  160.         parsed['ID']=parsed['icmp']['block_id']
  161.         parsed['address']=addr
  162.         parsed['raw']=data
  163.         log.debug('ping::recv_ping: ID=%d address=%s bytes=%d'%(parsed['ID'],addr,len(data)))
  164.         return parsed
  165.  
  166. def read_ping(d_socket, timeout):
  167.         start = time.time()
  168.         while time.time() - start < timeout:
  169.                 msg = recv_ping(d_socket,timeout)
  170.                 if msg: return msg
  171.         return None
  172.  
  173. def receive_ping(my_socket, ID, timeout):
  174.         timeLeft = timeout
  175.         while True:
  176.                 startedSelect = time.time()
  177.                 whatReady = select.select([my_socket], [], [], timeLeft)
  178.                 if whatReady[0] == []: # Timeout
  179.                         return
  180.        
  181.                 timeReceived = time.time()
  182.                 howLongInSelect = (timeReceived - startedSelect)
  183.                 recPacket, addr = my_socket.recvfrom(1024)
  184.                 icmpHeader = recPacket[20:28]
  185.                 type, code, checksum, packetID = struct.unpack("bbHI", icmpHeader)
  186.                 if packetID == ID:
  187.                         bytesInDouble = struct.calcsize("d")
  188.                         timeSent = struct.unpack("d", recPacket[28:28 + bytesInDouble])[0]
  189.                         return timeReceived - timeSent
  190.        
  191.                 timeLeft = timeLeft - howLongInSelect
  192.                 if timeLeft <= 0:
  193.                         return 0
  194.  
  195. def single_ping(dest_addr, timeout):
  196.         my_socket = build_socket()
  197.         my_ID = os.getpid() & 0xFFFF
  198.         time_ping(my_socket, dest_addr, my_ID)
  199.         delay = receive_ping(my_socket, my_ID, timeout)
  200.         my_socket.close()
  201.         return delay
  202.  
  203. def verbose_ping(dest_addr, timeout = 2, count = 4):
  204.         for i in xrange(count):
  205.                 log.info("ping %s..." % dest_addr,)
  206.                 try:
  207.                         delay  =  single_ping(dest_addr, timeout)
  208.                 except socket.gaierror, e:
  209.                         log.error("failed. (socket error: '%s')" % e[1])
  210.                         break
  211.  
  212.                 if delay  ==  None:
  213.                         log.info("failed. (timeout within %ssec.)" % timeout)
  214.                 else:
  215.                         delay  =  delay * 1000
  216.                         log.info("get ping in %0.4fms" % delay)
  217.         print
  218.  
  219.  
  220. import os, pwd, grp
  221.  
  222. if __name__ == '__main__':
  223.         ping_reporter.start_log(log)
  224.         server = select_server(log,2)
  225.        
  226.         if 1:
  227.                 verbose_ping(server)
  228.         else:
  229.                 s = build_socket()
  230.                 print 'sending 100 pings...'
  231.                 for x in range(1,100):
  232.                         data_ping(s,server,x,struct.pack('d',x))
  233.                         print 'ping cycled...'
  234.                         recv_ping(s,1)
  235.                 print '100 pings sent'
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top