--- /dev/null
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import ping, socket
+import os, time
+
+# (G)2013 xChaos, Arachne Labs http://arachne.cz + SPOJE.NET http://spoje.net
+
+hosts = "/etc/hosts"
+timeout = 1500 #timeout in ms
+interval = 200 #ping interval in ms
+attempts = 10
+
+tld = ".czf"
+domain = ".brevnov.czf"
+smokeping_prefix = "Klienti"
+smpater_prefix = "Backbone"
+smokeping_babble_length = 3
+smpater_babble_length = 2
+smokeping_html = "/var/www/html/web/sites/sysifos/hosts-ping/index.html"
+smpater_html = "/var/www/html/web/sites/sysifos/hosts-ping/backbone.html"
+smokeping_url = "http://sisyfos.brevnov.czf/cgi-bin/smokeping.cgi?filter=%s&target=%s"
+smpater_url = "http://tartarus.brevnov.czf/cgi-bin/smokeping.cgi?filter=%s&target=%s"
+table_head = """
+<table class="decorated last">
+<caption>hosts ping (%s)</caption><thead><tr>
+<th style="text-align: right;">#</th>
+<th>hostname</th>
+<th style="text-align: right;">received</th>
+<th style="text-align: right;">avg</th>
+<th style="text-align: right;">best</th>
+<th style="text-align: right;">worst</th>
+</tr></thead><tbody>
+"""
+table_end = """
+</tbody></table>
+<br />
+<p>Page generated by (G)2013 xChaos hosts-ping version 0.1-a</p>
+"""
+
+def try_to_ping(host):
+ sum = 0.0
+ best = None
+ worst = None
+ loss = 0
+
+ for i in range(0, attempts):
+ try:
+ delay = ping.Ping(host, timeout = timeout).do() #timeout in ms
+ time.sleep(interval/1000)
+
+ if delay:
+ sum += delay
+
+ if not best or best > delay:
+ best = delay
+
+ if not worst or worst < delay:
+ worst = delay
+
+ else:
+ loss += 1
+
+ except socket.error, e:
+ loss += 1
+
+ return (sum/attempts, best, worst, loss)
+
+
+def smokenam_style(hostname, prefix, babble_length):
+
+ if not tld in hostname:
+ hostname += domain
+
+ babble = hostname.split('.')
+ return '.'.join([prefix,] + [a_tooth for a_tooth in reversed(babble)][1:babble_length] + ['-'.join(babble),])
+
+
+def append_host(html, host, base_url, counter):
+ style = {'right': 'text-align: right;'}
+ columns = ('loss','avg','best','worst')
+ red_treshold = (0, 100, 50, 200)
+ green_treshold = (0, 2, 1, 10)
+
+ for kolikaty, column in enumerate(columns):
+ style[column] = style['right']
+
+ if not host[column]:
+ host[column] = 0 #don't want it to be "None" type
+
+ if host[column] > red_treshold[kolikaty]:
+ style[column] += ' color: red;'
+ elif host[column] < green_treshold[kolikaty]:
+ style[column] += ' color: green;'
+
+ received = attempts-host['loss']
+ html.write( ('<tr class="%s"><td style="%s">%d</td><td><a href="%s" target="_blank" class="blue">%s</a></td><td style="%s">%d/%d</td>' + "\n")
+ % (('even', 'odd')[counter % 2], style['right'], counter, base_url % (host['name'], host['smokename']), host['name'], style['loss'], received, attempts))
+
+ if host['avg'] and host['best'] and host['worst']:
+ html.write( ('<td style="%s">%1.2f</td><td style="%s">%1.2f</td><td style="%s">%1.2f</td></tr>' + "\n")
+ % (style['avg'], host['avg'], style['best'], host['best'], style['worst'], host['worst']))
+ else:
+ html.write(3*('<td style="%s">-</td>' % style['loss']) + "\n")
+
+# main program
+
+smokeping = []
+smpater = []
+
+for radek in open(hosts):
+ if radek[0] != '#':
+ is_smokeping = 'smokeping' in radek and not 'hidden' in radek
+ is_smpater = 'smpater' in radek
+ if is_smokeping or is_smpater:
+ slovo = radek.split("\t")
+ host = { 'ip': slovo[0], 'name': slovo[1].split(' ')[0] }
+ (host['avg'], host['best'], host['worst'], host['loss']) = try_to_ping(host['ip'])
+
+ if is_smokeping:
+ host['smokename'] = smokenam_style(host['name'], smokeping_prefix, smokeping_babble_length)
+ smokeping.append(host)
+ else:
+ host['smokename'] = smokenam_style(host['name'], smpater_prefix, smpater_babble_length)
+ smpater.append(host)
+
+# smokeping
+
+html = open(smokeping_html, 'w')
+html.write("<h1>Smokeping - klientská zařízení</h1>");
+html.write(table_head % time.ctime());
+
+for kolikaty, host in enumerate(sorted(smokeping, key = lambda host: -host['loss']*attempts*timeout-host['avg'])):
+ append_host(html, host, smokeping_url, kolikaty+1)
+
+html.write(table_end);
+html.close();
+
+# smpater
+
+html = open(smpater_html, 'w')
+html.write("<h1>Smokeping - páteřní routery</h1>");
+html.write(table_head % time.ctime());
+
+for kolikaty, host in enumerate(sorted(smpater, key = lambda host: -host['loss']*attempts*timeout-host['avg'])):
+ append_host(html, host, smpater_url, kolikaty+1)
+
+html.write(table_end);
+html.close();
--- /dev/null
+#!/usr/bin/env python
+# coding: utf-8
+
+"""
+ A pure python ping implementation using raw sockets.
+
+ Note that ICMP messages can only be send from processes running as root
+ (in Windows, you must run this script as 'Administrator').
+
+ Bugs are naturally mine. I'd be glad to hear about them. There are
+ certainly word - size dependencies here.
+
+ :homepage: https://github.com/jedie/python-ping/
+ :copyleft: 1989-2011 by the python-ping team, see AUTHORS for more details.
+ :license: GNU GPL v2, see LICENSE for more details.
+"""
+
+
+import array
+import os
+import select
+import signal
+import socket
+import struct
+import sys
+import time
+
+
+if sys.platform.startswith("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
+
+
+# ICMP parameters
+ICMP_ECHOREPLY = 0 # Echo reply (per RFC792)
+ICMP_ECHO = 8 # Echo request (per RFC792)
+ICMP_MAX_RECV = 2048 # Max size of incoming buffer
+
+MAX_SLEEP = 1000
+
+
+def calculate_checksum(source_string):
+ """
+ A port of the functionality of in_cksum() from ping.c
+ Ideally this would act on the string as a series of 16-bit ints (host
+ packed), but this works.
+ Network data is big-endian, hosts are typically little-endian
+ """
+ if len(source_string)%2:
+ source_string += "\x00"
+ converted = array.array("H", source_string)
+ if sys.byteorder == "big":
+ converted.byteswap()
+ val = sum(converted)
+
+ val &= 0xffffffff # Truncate val to 32 bits (a variance from ping.c, which
+ # uses signed ints, but overflow is unlikely in ping)
+
+ val = (val >> 16) + (val & 0xffff) # Add high 16 bits to low 16 bits
+ val += (val >> 16) # Add carry from above (if any)
+ answer = ~val & 0xffff # Invert and truncate to 16 bits
+ answer = socket.htons(answer)
+
+ return answer
+
+
+def is_valid_ip4_address(addr):
+ parts = addr.split(".")
+ if not len(parts) == 4:
+ return False
+ for part in parts:
+ try:
+ number = int(part)
+ except ValueError:
+ return False
+ if number > 255:
+ return False
+ return True
+
+def to_ip(addr):
+ if is_valid_ip4_address(addr):
+ return addr
+ return socket.gethostbyname(addr)
+
+
+class Ping(object):
+ def __init__(self, destination, timeout=1000, packet_size=55, own_id=None):
+ self.destination = destination
+ self.timeout = timeout
+ self.packet_size = packet_size
+ if own_id is None:
+ self.own_id = os.getpid() & 0xFFFF
+ else:
+ self.own_id = own_id
+
+ try:
+ # FIXME: Use destination only for display this line here? see: https://github.com/jedie/python-ping/issues/3
+ self.dest_ip = to_ip(self.destination)
+ except socket.gaierror as e:
+ self.print_unknown_host(e)
+# else:
+# self.print_start()
+
+ self.seq_number = 0
+ self.send_count = 0
+ self.receive_count = 0
+ self.min_time = 999999999
+ self.max_time = 0.0
+ self.total_time = 0.0
+
+ #--------------------------------------------------------------------------
+
+ def print_start(self):
+ print("\nPYTHON-PING %s (%s): %d data bytes" % (self.destination, self.dest_ip, self.packet_size))
+
+ def print_unknown_host(self, e):
+ print("\nPYTHON-PING: Unknown host: %s (%s)\n" % (self.destination, e.args[1]))
+ sys.exit(-1)
+
+ def print_success(self, delay, ip, packet_size, ip_header, icmp_header):
+ if ip == self.destination:
+ from_info = ip
+ else:
+ from_info = "%s (%s)" % (self.destination, ip)
+
+ print("%d bytes from %s: icmp_seq=%d ttl=%d time=%.1f ms" % (
+ packet_size, from_info, icmp_header["seq_number"], ip_header["ttl"], delay)
+ )
+ #print("IP header: %r" % ip_header)
+ #print("ICMP header: %r" % icmp_header)
+
+ def print_failed(self):
+ print("Request timed out.")
+
+ def print_exit(self):
+ print("\n----%s PYTHON PING Statistics----" % (self.destination))
+
+ lost_count = self.send_count - self.receive_count
+ #print("%i packets lost" % lost_count)
+ lost_rate = float(lost_count) / self.send_count * 100.0
+
+ print("%d packets transmitted, %d packets received, %0.1f%% packet loss" % (
+ self.send_count, self.receive_count, lost_rate
+ ))
+
+ if self.receive_count > 0:
+ print("round-trip (ms) min/avg/max = %0.3f/%0.3f/%0.3f" % (
+ self.min_time, self.total_time / self.receive_count, self.max_time
+ ))
+
+ print("")
+
+ #--------------------------------------------------------------------------
+
+ def signal_handler(self, signum, frame):
+ """
+ Handle print_exit via signals
+ """
+ self.print_exit()
+ print("\n(Terminated with signal %d)\n" % (signum))
+ sys.exit(0)
+
+ def setup_signal_handler(self):
+ signal.signal(signal.SIGINT, self.signal_handler) # Handle Ctrl-C
+ if hasattr(signal, "SIGBREAK"):
+ # Handle Ctrl-Break e.g. under Windows
+ signal.signal(signal.SIGBREAK, self.signal_handler)
+
+ #--------------------------------------------------------------------------
+
+ def header2dict(self, names, struct_format, data):
+ """ unpack the raw received IP and ICMP header informations to a dict """
+ unpacked_data = struct.unpack(struct_format, data)
+ return dict(zip(names, unpacked_data))
+
+ #--------------------------------------------------------------------------
+
+ def run(self, count=None, deadline=None):
+ """
+ send and receive pings in a loop. Stop if count or until deadline.
+ """
+ self.setup_signal_handler()
+
+ while True:
+ delay = self.do()
+
+ self.seq_number += 1
+ if count and self.seq_number >= count:
+ break
+ if deadline and self.total_time >= deadline:
+ break
+
+ if delay == None:
+ delay = 0
+
+ # Pause for the remainder of the MAX_SLEEP period (if applicable)
+ if (MAX_SLEEP > delay):
+ time.sleep((MAX_SLEEP - delay) / 1000.0)
+
+ self.print_exit()
+
+ def do(self):
+ """
+ Send one ICMP ECHO_REQUEST and receive the response until self.timeout
+ """
+ try: # One could use UDP here, but it's obscure
+ current_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname("icmp"))
+ except socket.error, (errno, msg):
+ if errno == 1:
+ # Operation not permitted - Add more information to traceback
+ etype, evalue, etb = sys.exc_info()
+ evalue = etype(
+ "%s - Note that ICMP messages can only be send from processes running as root." % evalue
+ )
+ raise etype, evalue, etb
+ raise # raise the original error
+
+ send_time = self.send_one_ping(current_socket)
+ if send_time == None:
+ return
+ self.send_count += 1
+
+ receive_time, packet_size, ip, ip_header, icmp_header = self.receive_one_ping(current_socket)
+ current_socket.close()
+
+ if receive_time:
+ self.receive_count += 1
+ delay = (receive_time - send_time) * 1000.0
+ self.total_time += delay
+ if self.min_time > delay:
+ self.min_time = delay
+ if self.max_time < delay:
+ self.max_time = delay
+
+# self.print_success(delay, ip, packet_size, ip_header, icmp_header)
+ return delay
+# else:
+# self.print_failed()
+
+ def send_one_ping(self, current_socket):
+ """
+ Send one ICMP ECHO_REQUEST
+ """
+ # Header is type (8), code (8), checksum (16), id (16), sequence (16)
+ checksum = 0
+
+ # Make a dummy header with a 0 checksum.
+ header = struct.pack(
+ "!BBHHH", ICMP_ECHO, 0, checksum, self.own_id, self.seq_number
+ )
+
+ padBytes = []
+ startVal = 0x42
+ for i in range(startVal, startVal + (self.packet_size)):
+ padBytes += [(i & 0xff)] # Keep chars in the 0-255 range
+ data = bytes(padBytes)
+
+ # Calculate the checksum on the data and the dummy header.
+ checksum = calculate_checksum(header + data) # Checksum is in network order
+
+ # 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, 0, checksum, self.own_id, self.seq_number
+ )
+
+ packet = header + data
+
+ send_time = default_timer()
+
+ try:
+ current_socket.sendto(packet, (self.destination, 1)) # Port number is irrelevant for ICMP
+ except socket.error as e:
+ print("General failure (%s)" % (e.args[1]))
+ current_socket.close()
+ return
+
+ return send_time
+
+ def receive_one_ping(self, current_socket):
+ """
+ Receive the ping from the socket. timeout = in ms
+ """
+ timeout = self.timeout / 1000.0
+
+ while True: # Loop while waiting for packet or timeout
+ select_start = default_timer()
+ inputready, outputready, exceptready = select.select([current_socket], [], [], timeout)
+ select_duration = (default_timer() - select_start)
+ if inputready == []: # timeout
+ return None, 0, 0, 0, 0
+
+ receive_time = default_timer()
+
+ packet_data, address = current_socket.recvfrom(ICMP_MAX_RECV)
+
+ icmp_header = self.header2dict(
+ names=[
+ "type", "code", "checksum",
+ "packet_id", "seq_number"
+ ],
+ struct_format="!BBHHH",
+ data=packet_data[20:28]
+ )
+
+ if icmp_header["packet_id"] == self.own_id: # Our packet
+ ip_header = self.header2dict(
+ names=[
+ "version", "type", "length",
+ "id", "flags", "ttl", "protocol",
+ "checksum", "src_ip", "dest_ip"
+ ],
+ struct_format="!BBHHHBBHII",
+ data=packet_data[:20]
+ )
+ packet_size = len(packet_data) - 28
+ ip = socket.inet_ntoa(struct.pack("!I", ip_header["src_ip"]))
+ # XXX: Why not ip = address[0] ???
+ return receive_time, packet_size, ip, ip_header, icmp_header
+
+ timeout = timeout - select_duration
+ if timeout <= 0:
+ return None, 0, 0, 0, 0
+
+
+def verbose_ping(hostname, timeout=1000, count=3, packet_size=55):
+ p = Ping(hostname, timeout, packet_size)
+ p.run(count)
+
+
+if __name__ == '__main__':
+ # FIXME: Add a real CLI
+ if len(sys.argv) == 1:
+ print "DEMO"
+
+ # These should work:
+ verbose_ping("heise.de")
+ verbose_ping("google.com")
+
+ # Inconsistent on Windows w/ ActivePython (Python 3.2 resolves correctly
+ # to the local host, but 2.7 tries to resolve to the local *gateway*)
+ verbose_ping("localhost")
+
+ # Should fail with 'getaddrinfo print_failed':
+ verbose_ping("foobar_url.foobar")
+
+ # Should fail (timeout), but it depends on the local network:
+ verbose_ping("192.168.255.254")
+
+ # Should fails with 'The requested address is not valid in its context':
+ verbose_ping("0.0.0.0")
+ elif len(sys.argv) == 2:
+ verbose_ping(sys.argv[1])
+ else:
+ print "Error: call ./ping.py domain.tld"