diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..73f11b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.py[co] +*~ +*.egg-info +/dist +/build +.pydevproject +/.settings +/.venv* +/.idea diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..b1e1167 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,18 @@ +AUTHORS / CONTRIBUTORS (alphabetic order): + + * Cowles, Matthew Dixon -- ftp://ftp.visi.com/users/mdc/ping.py + * Diemer, Jens -- http://www.jensdiemer.de + * Falatic, Martin -- http://www.falatic.com + * Hallman, Chris -- http://cdhallman.blogspot.com + * Harvey, Dan -- https://github.com/danharvey + * incidence -- https://github.com/incidence + * jcborras -- https://github.com/jcborras + * Motl, Andreas -- https://github.com/amotl + * Notaras, George -- http://www.g-loaded.eu + * Poincheval, Jerome + * Sarkhel, Kunal -- https://github.com/techwizrd + * Stauffer, Samuel + * Tibold, Marko -- https://github.com/markotibold + * Toews, Ben --- http://btoe.ws + * Zach Ware + * zed -- https://github.com/zed diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 0000000..9efc285 --- /dev/null +++ b/CHANGES.rst @@ -0,0 +1,39 @@ +*************** +gping changelog +*************** + +In progress +=========== + +2016-11-21 0.2 +============== +- Be graceful to hostname resolution failures +- Record all failures (hostname resolution and timeouts) in ``self.failures`` +- Improve console output formatting +- Add shell command entrypoint ``gping`` to ``setup.py`` +- Add argument parsing for interactive use, e.g. ``--hostnames=www.example.net,mail.example.net`` +- Incorporate "Make it possible to ping without root access" using ``socket.SOCK_DGRAM`` instead of ``socket.SOCK_RAW`` + by Marko Tibold: https://github.com/markotibold/gping/commit/b75fa2d4 +- Incorporate "Allow binding to an interface", e.g. ``--bind=192.168.111.2`` + by Dan Harvey: https://github.com/danharvey/pyping/commit/cb848976 +- Non-interactive mode: Record all results in ``self.results`` +- Non-interactive mode: Make verbose output optional + +2012-08-21 0.1 +============== +- Fix setup.py deps +- Add install instructions to README +- Tag release ``gping-0.1`` + +2012-07-17 0.0 +============== +- Add main gping.py + +2012-07-12 0.0 +============== +- Change __main__ action. Fix id overflow issue. + +2012-07-10 0.0 +============== +- First commit. Clean out stuff. Fix setup.py. +- Add README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9cc1267 --- /dev/null +++ b/LICENSE @@ -0,0 +1,11 @@ +The original code 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. + +Copyright (c) Matthew Dixon Cowles, . +Distributable under the terms of the GNU General Public License +version 2. Provided with no warranties of any sort. + +See AUTHORS for complete list of authors and contributors. \ No newline at end of file diff --git a/README b/README deleted file mode 100644 index 1488af0..0000000 --- a/README +++ /dev/null @@ -1,2 +0,0 @@ -Originally from http://svn.pylucid.net/pylucid/CodeSnippets/ping.py -This version maintained at http://github.com/samuel/python-ping \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..da7e4e3 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# GPing # + +An asynchronous, event-driven, pure-python ping implementation using raw sockets based on gevent. +This is a fork of the python-ping project replacing its internal machinery with gevent. +The point is to have an event driven ping utility for doing a huge number of concurrent pings efficiently. + + +## Usage ## + +### Run on the commandline ### + + # Ping some hostnames + gping --hostnames=gnu.org,fsf.org,google.com,microsoft.com,googleusercontent.com,live.com,stackoverflow.com,141.1.1.1,8.8.8.8,192.168.123.1,192.168.999.1 + + # Bind to a specific IP address + gping --bind=192.168.111.2 --hostnames=google.com,stackoverflow.com,8.8.8.8 + +### Demo with 100 concurrent pings ### + + # Ping the top 100 domains + time python gping.py + +### Use as library ### + + from gping import GPing + + gp = GPing() + gp.send("127.0.0.1", test_callback) + gp.join() + +## Install ## + +### From Python Package Index ### +This *should* be easy on your average \*nix box, just type: + + sudo pip install gping + + +### From GitHub ### +This way of installing gping is suitable for hacking on it: + + git clone https://github.com/mastahyeti/gping.git + cd gping + virtualenv .venv27 + source .venv27/bin/activate + python setup.py develop + +## License and Credits ## + +The python-ping project was started by Matthew Dixon Cowles. + + - copyleft 1989-2016 by the python-ping team, see AUTHORS for more details. + - license: GNU GPL v2, see LICENSE for more details. + +It was forked and ported to `gevent` by Ben Toews. Since then, this program is now called appropriately *GPing*. + +He says: +> I have left the license and authors information intact, but this project seems to have a long history or forks and such +> so I am probably somehow pissing someone off or violating some license. Let me know if this is the case and I will be better. diff --git a/TODO.rst b/TODO.rst new file mode 100644 index 0000000..542918e --- /dev/null +++ b/TODO.rst @@ -0,0 +1,24 @@ +********** +gping todo +********** + +Tasks +===== +- [x] Add entrypoint ``gping`` and argument parsing +- [o] Add reading from stdin +- [o] Add collectd metrics gathering: https://pythonhosted.org/collectd/ + +Upstream changes +================ +- [x] Add "Make it possible to ping without root access.": https://github.com/markotibold/gping/commit/b75fa2d41ec10073f894676fc3f3ec6b46693c05 +- [o] Add "Allow binding to an interface": https://github.com/danharvey/pyping/commit/cb8489762d3171fb0b69c0fb9aaa8968d5e439b4 +- [o] Add statsd support: https://github.com/ChristianKniep/python-ping/commit/a3653526e5857f346e2a244988df3849574ba385 +- [o] Check "fix packet numbering bug": https://github.com/matesito/gping/commit/498c93500e35c8f5ad9c682df1e6c66fb5c5998a +- [o] Check improvements from Vijayananda Reddy: https://github.com/VijayanandaReddy/gping/commit/25ac7c8efb6cf394b6c36ed2ed5b6712116b6c53 +- [o] Check "For some reason, die was not working": https://github.com/biplav/gping/commit/377f0f4e85535fd3b6f812eb29d49ad8bf9a24fa +- [o] Check "Changes to harden gping.py for usecases like die, ping of devices, timeouts": https://github.com/biplav/gping/commit/6fb9008d4ae63f954f6d59cfbf4bb445964c1351 +- [o] Check features of leading non-gevent repositories (IPv6, etc.): + + - https://github.com/l4m3rx/python-ping + - https://github.com/tomwilkie/pyping + diff --git a/gping.py b/gping.py new file mode 100644 index 0000000..fdf4523 --- /dev/null +++ b/gping.py @@ -0,0 +1,333 @@ +""" + This part is a fork of the python-ping project that makes + things work with gevent. +""" + +import os +import struct +import sys +import time +from args import args + +import gevent +from gevent import socket +from gevent.pool import Pool +from gevent.event import Event + +# 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 + count_to = (len(source_string) / 2) * 2 + for count in xrange(0, count_to, 2): + this = ord(source_string[count + 1]) * 256 + ord(source_string[count]) + sum = sum + this + sum = sum & 0xffffffff # Necessary? + + if count_to < len(source_string): + sum = sum + ord(source_string[len(source_string) - 1]) + sum = sum & 0xffffffff # Necessary? + + sum = (sum >> 16) + (sum & 0xffff) + sum = sum + (sum >> 16) + answer = ~sum + answer = answer & 0xffff + + # Swap bytes. Bugger me if I know why. + answer = answer >> 8 | (answer << 8 & 0xff00) + + return answer + + +class GPing: + """ + This class, when instantiated will start listening for ICMP responses. + Then call its send method to send pings. Callbacks will be sent ping + details + """ + def __init__(self, timeout=2, max_outstanding=10, bind=None, callback=None): + """ + :timeout - amount of time a ICMP echo request can be outstanding + :max_outstanding - maximum number of outstanding ICMP echo requests without responses (limits traffic) + """ + self.timeout = timeout + self.max_outstanding = max_outstanding + self.bind = bind + self.callback = callback + + # id we will increment with each ping + self.id = 0 + + # Object to hold and keep track of all of our self.pings + self.pings = {} + + # Keep track of results + self.results = [] + + # Keep track of failures + self.failures = [] + + # event to file when we want to shut down + self.die_event = Event() + + # setup socket + icmp = socket.getprotobyname("icmp") + try: + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, icmp) + except socket.error, (errno, msg): + if errno == 1: + # Operation not permitted + msg = msg + ( + " - Note that ICMP messages can only be sent from processes" + " running as root." + ) + raise socket.error(msg) + raise # raise the original error + + # Bind the socket to the interface of given IP address + if self.bind: + self.socket.bind((self.bind, 0)) # Port number is irrelevant for ICMP + + self.receive_glet = gevent.spawn(self.__receive__) + self.processto_glet = gevent.spawn(self.__process_timeouts__) + + + def die(self): + """ + try to shut everything down gracefully + """ + print "shutting down" + self.die_event.set() + socket.cancel_wait() + gevent.joinall([self.receive_glet,self.processto_glet]) + + + def join(self): + """ + does a lot of nothing until self.pings is empty + """ + while len(self.pings): + gevent.sleep() + + + def send(self, dest_addr, callback=None, psize=64): + """ + Send a ICMP echo request. + :dest_addr - where to send it + :callback - what to call when we get a response + :psize - how much data to send with it + """ + + callback = callback or self.callback + + # make sure we dont have too many outstanding requests + while len(self.pings) >= self.max_outstanding: + gevent.sleep(0.01) + + # figure out our id + packet_id = self.id + + # increment our id, but wrap if we go over the max size for USHORT + self.id = (self.id + 1) % 2 ** 16 + + + # make a spot for this ping in self.pings + self.pings[packet_id] = {'sent':False,'success':False,'error':False,'dest_addr':dest_addr,'dest_ip':None,'callback':callback} + + # Resolve hostname + try: + dest_ip = socket.gethostbyname(dest_addr) + self.pings[packet_id]['dest_ip'] = dest_ip + except socket.gaierror as ex: + self.pings[packet_id]['error'] = True + self.pings[packet_id]['message'] = str(ex) + return + + + # Remove header size from packet size + psize = psize - 8 + + # 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, packet_id, 1) + bytes = struct.calcsize("d") + data = (psize - bytes) * "Q" + data = struct.pack("d", time.time()) + data + + # Calculate the checksum on the data and the dummy header. + my_checksum = checksum(header + data) + + # 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_REQUEST, 0, socket.htons(my_checksum), packet_id, 1 + ) + packet = header + data + # note the send_time for checking for timeouts + self.pings[packet_id]['send_time'] = time.time() + + # send the packet + self.socket.sendto(packet, (dest_ip, 1)) # Don't know about the 1 + + #mark the packet as sent + self.pings[packet_id]['sent'] = True + + def record_result(self, ping): + + # Keep track of ping results + self.results.append(ping) + + # Propagate individual callback + if 'callback' in ping and callable(ping['callback']): + ping['callback'](ping) + + def __process_timeouts__(self): + """ + check to see if any of our pings have timed out + """ + while not self.die_event.is_set(): + for i in self.pings: + + # Detect timeout + if self.pings[i]['sent'] and time.time() - self.pings[i]['send_time'] > self.timeout: + self.pings[i]['error'] = True + self.pings[i]['message'] = 'Timeout after {} seconds'.format(self.timeout) + + # Handle all failures + if self.pings[i]['error'] == True: + self.record_result(self.pings[i]) + self.failures.append(self.pings[i]) + del(self.pings[i]) + break + + gevent.sleep() + + + def __receive__(self): + """ + receive response packets + """ + while not self.die_event.is_set(): + # wait till we can recv + try: + socket.wait_read(self.socket.fileno()) + except socket.error, (errno,msg): + if errno == socket.EBADF: + print "interrupting wait_read" + return + # reraise original exceptions + print "re-throwing socket exception on wait_read()" + raise + + time_received = time.time() + received_packet, addr = self.socket.recvfrom(1024) + icmpHeader = received_packet[20:28] + type, code, checksum, packet_id, sequence = struct.unpack( + "bbHHh", icmpHeader + ) + + if packet_id in self.pings: + bytes_received = struct.calcsize("d") + time_sent = struct.unpack("d", received_packet[28:28 + bytes_received])[0] + self.pings[packet_id]['delay'] = time_received - time_sent + + # i'd call that a success + self.pings[packet_id]['success'] = True + + # Record the ping result + self.record_result(self.pings[packet_id]) + + # delete the ping + del(self.pings[packet_id]) + + gevent.sleep() + + def print_report(self): + + # Output header + print >>sys.stderr + template = '{ip:20s}{delay:15s}{hostname:40s}{message}' + header = template.format(hostname='Hostname', ip='IP', delay='Delay', message='Message') + print >>sys.stderr, header + + # Output results + for result in self.results: + message = self.format_ping_result(result) + print >>sys.stderr, message + + # Output failures + print >>sys.stderr + print >>sys.stderr, 'Failures:' + template = '{hostname:45}{message}' + for failure in self.failures: + message = template.format(hostname=failure['dest_addr'], message=failure.get('message', 'unknown error')) + print >>sys.stderr, message + + def format_ping_result(self, ping): + template = '{ip:20s}{delay:15s}{hostname:40s}{message}' + message = template.format( + hostname = ping['dest_addr'], + ip = ping['dest_ip'], + delay = ping['success'] and str(round(ping['delay'], 6)) or '', + message = 'message' in ping and ping['message'] or '' + ) + message = message.strip() + return message + + +def ping(hostnames, verbose=False, **options): + options = options or {} + gp = GPing(**options) + + for hostname in hostnames: + gp.send(hostname) + + gp.join() + + if verbose: + gp.print_report() + + return gp + + +def run(): + + """ + print 'Arguments passed in: ' + str(args.all) + print 'Flags detected: ' + str(args.flags) + print 'Files detected: ' + str(args.files) + print 'NOT files detected: ' + str(args.not_files) + print 'Grouped Arguments: ' + str(args.grouped) + print 'Assignments detected: ' + str(args.assignments) + """ + + # The --hostnames argument is obligatory + if '--hostnames' in args.assignments: + + # Compute list of hostnames from argument + hostnames_raw = args.assignments['--hostnames'].get(0) + hostnames = hostnames_raw.split(',') + + # Compute additional options from arguments + options = {} + if '--bind' in args.assignments: + options['bind'] = args.assignments['--bind'].get(0) + + ping(hostnames, verbose=True, **options) + + else: + raise ValueError('Please specify the --hostnames= argument for passing a list of comma-separated hostnames') + + +if __name__ == '__main__': + top_100_domains = ['google.com','facebook.com','youtube.com','yahoo.com','baidu.com','wikipedia.org','live.com','qq.com','twitter.com','amazon.com','linkedin.com','blogspot.com','google.co.in','taobao.com','sina.com.cn','yahoo.co.jp','msn.com','google.com.hk','wordpress.com','google.de','google.co.jp','google.co.uk','ebay.com','yandex.ru','163.com','google.fr','weibo.com','googleusercontent.com','bing.com','microsoft.com','google.com.br','babylon.com','soso.com','apple.com','mail.ru','t.co','tumblr.com','vk.com','google.ru','sohu.com','google.es','pinterest.com','google.it','craigslist.org','bbc.co.uk','livejasmin.com','tudou.com','paypal.com','blogger.com','xhamster.com','ask.com','youku.com','fc2.com','google.com.mx','xvideos.com','google.ca','imdb.com','flickr.com','go.com','tmall.com','avg.com','ifeng.com','hao123.com','zedo.com','conduit.com','google.co.id','pornhub.com','adobe.com','blogspot.in','odnoklassniki.ru','google.com.tr','cnn.com','aol.com','360buy.com','google.com.au','rakuten.co.jp','about.com','mediafire.com','alibaba.com','ebay.de','espn.go.com','wordpress.org','chinaz.com','google.pl','stackoverflow.com','netflix.com','ebay.co.uk','uol.com.br','amazon.de','ameblo.jp','adf.ly','godaddy.com','huffingtonpost.com','amazon.co.jp','cnet.com','globo.com','youporn.com','4shared.com','thepiratebay.se','renren.com'] + ping(top_100_domains, verbose=True) + diff --git a/ping.py b/ping.py deleted file mode 100644 index af41f1e..0000000 --- a/ping.py +++ /dev/null @@ -1,217 +0,0 @@ -#!/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. - - - 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 -else: - # On most other platforms the best timer is time.time() - default_timer = time.time - -# 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 = 0 - while count> 16) + (sum & 0xffff) - sum = sum + (sum >> 16) - answer = ~sum - answer = answer & 0xffff - - # Swap bytes. Bugger me if I know why. - answer = answer >> 8 | (answer << 8 & 0xff00) - - return answer - - -def receive_one_ping(my_socket, ID, timeout): - """ - receive the ping from the socket. - """ - timeLeft = timeout - while True: - startedSelect = default_timer() - whatReady = select.select([my_socket], [], [], timeLeft) - howLongInSelect = (default_timer() - startedSelect) - if whatReady[0] == []: # Timeout - return - - timeReceived = default_timer() - recPacket, addr = my_socket.recvfrom(1024) - icmpHeader = recPacket[20:28] - type, code, checksum, packetID, sequence = struct.unpack( - "bbHHh", icmpHeader - ) - if packetID == ID: - bytesInDouble = struct.calcsize("d") - timeSent = struct.unpack("d", recPacket[28:28 + bytesInDouble])[0] - return timeReceived - timeSent - - timeLeft = timeLeft - howLongInSelect - if timeLeft <= 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) - - # 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 - - # Calculate the checksum on the data and the dummy header. - my_checksum = checksum(header + data) - - # 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_REQUEST, 0, socket.htons(my_checksum), ID, 1 - ) - 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. - """ - icmp = socket.getprotobyname("icmp") - try: - my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp) - except socket.error, (errno, msg): - if errno == 1: - # Operation not permitted - msg = msg + ( - " - Note that ICMP messages can only be sent from processes" - " running as root." - ) - raise socket.error(msg) - raise # raise the original error - - my_ID = os.getpid() & 0xFFFF - - send_one_ping(my_socket, dest_addr, my_ID) - delay = receive_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, - try: - delay = do_one(dest_addr, timeout) - except socket.gaierror, e: - print "failed. (socket error: '%s')" % e[1] - break - - if delay == None: - print "failed. (timeout within %ssec.)" % timeout - else: - delay = delay * 1000 - print "get ping in %0.4fms" % delay - print - - -if __name__ == '__main__': - verbose_ping("heise.de") - verbose_ping("google.com") - verbose_ping("a-test-url-taht-is-not-available.com") - verbose_ping("192.168.1.1") diff --git a/ping_header_info.txt b/ping_header_info.txt new file mode 100644 index 0000000..371c729 --- /dev/null +++ b/ping_header_info.txt @@ -0,0 +1,50 @@ +IP header info from RFC791 + -> http://tools.ietf.org/html/rfc791) + +0 1 2 3 +0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +|Version| IHL |Type of Service| Total Length | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Identification |Flags| Fragment Offset | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Time to Live | Protocol | Header Checksum | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Source Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Destination Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Options | Padding | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +=========================================================================== +ICMP Echo / Echo Reply Message header info from RFC792 + -> http://tools.ietf.org/html/rfc792 + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identifier | Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Data ... + +-+-+-+-+- + +=========================================================================== +ICMP parameter info: + -> http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xml + +=========================================================================== +An example of ping's typical output: + +PING heise.de (193.99.144.80): 56 data bytes +64 bytes from 193.99.144.80: icmp_seq=0 ttl=240 time=127 ms +64 bytes from 193.99.144.80: icmp_seq=1 ttl=240 time=127 ms +64 bytes from 193.99.144.80: icmp_seq=2 ttl=240 time=126 ms +64 bytes from 193.99.144.80: icmp_seq=3 ttl=240 time=126 ms +64 bytes from 193.99.144.80: icmp_seq=4 ttl=240 time=127 ms + +----heise.de PING Statistics---- +5 packets transmitted, 5 packets received, 0.0% packet loss +round-trip (ms) min/avg/max/med = 126/127/127/127 \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..e8860b3 --- /dev/null +++ b/setup.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" + distutils setup + ~~~~~~~~~~~~~~~ + + :homepage: https://github.com/mastahyeti/gping/ + :copyleft: 1989-2011 by the python-ping team, see AUTHORS for more details. + :license: GNU GPL v2, see LICENSE for more details. +""" + +import os + +from setuptools import setup, find_packages, Command + +def get_authors(): + authors = [] + try: + f = file(os.path.join(PACKAGE_ROOT, "AUTHORS"), "r") + for line in f: + if not line.strip().startswith("*"): + continue + if "--" in line: + line = line.split("--", 1)[0] + authors.append(line.strip(" *\r\n")) + f.close() + authors.sort() + except Exception, err: + authors = "[Error: %s]" % err + return authors + + +setup( + name='gping', + version="0.2", + description='A gevent fork of python-ping.', + author=get_authors(), + maintainer="Ben Toews", + maintainer_email="mastahyeti@gmail.com", + url='https://github.com/mastahyeti/gping', + keywords="ping icmp network latency gevent", + py_modules=['gping'], + install_requires=['gevent', 'args'], + dependency_links=[ + 'https://github.com/kennethreitz/args/tarball/0a6d5eb#egg=args', + ], + entry_points={ + 'console_scripts': [ + 'gping = gping:run', + ], + }, +)