added --timeout to specify max. time to download page, default = 120 seconds
This commit is contained in:
parent
21dd17aa98
commit
5ff3ffb620
2 changed files with 67 additions and 12 deletions
16
README.md
16
README.md
|
|
@ -18,6 +18,22 @@ Try the following from your Python interpreter:
|
||||||
|
|
||||||
You can also run the script directly by passing in the --ip_address option:
|
You can also run the script directly by passing in the --ip_address option:
|
||||||
|
|
||||||
|
$ python traceroute.py --help
|
||||||
|
Usage: traceroute.py --ip_address=IP_ADDRESS
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--version show program's version number and exit
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
-i IP_ADDRESS, --ip_address=IP_ADDRESS
|
||||||
|
IP address of destination host (default: 8.8.8.8)
|
||||||
|
-t TMP_DIR, --tmp_dir=TMP_DIR
|
||||||
|
Temporary directory to store downloaded traceroute
|
||||||
|
results (default: /tmp)
|
||||||
|
-n, --no_geo No geolocation data (default: False)
|
||||||
|
-s TIMEOUT, --timeout=TIMEOUT
|
||||||
|
Timeout in seconds for all downloads (default: 120)
|
||||||
|
-d, --debug Show debug output (default: False)
|
||||||
|
|
||||||
$ python traceroute.py --ip_address=8.8.8.8
|
$ python traceroute.py --ip_address=8.8.8.8
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,15 @@ for each hop for a specified host from geographically distant source(s).
|
||||||
|
|
||||||
__author__ = 'Dazzlepod (info@dazzlepod.com)'
|
__author__ = 'Dazzlepod (info@dazzlepod.com)'
|
||||||
__copyright__ = 'Copyright (c) 2013 Dazzlepod'
|
__copyright__ = 'Copyright (c) 2013 Dazzlepod'
|
||||||
__version__ = '$Revision: #12 $'
|
__version__ = '$Revision: #13 $'
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import optparse
|
import optparse
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import signal
|
||||||
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import urllib
|
import urllib
|
||||||
import urllib2
|
import urllib2
|
||||||
|
|
@ -24,11 +26,12 @@ from subprocess import Popen, PIPE
|
||||||
|
|
||||||
class Traceroute(object):
|
class Traceroute(object):
|
||||||
"""Traceroute instance."""
|
"""Traceroute instance."""
|
||||||
def __init__(self, ip_address='8.8.8.8', tmp_dir='/tmp', no_geo=False, debug=False):
|
def __init__(self, ip_address='8.8.8.8', tmp_dir='/tmp', no_geo=False, timeout=120, debug=False):
|
||||||
super(Traceroute, self).__init__()
|
super(Traceroute, self).__init__()
|
||||||
self.ip_address = ip_address
|
self.ip_address = ip_address
|
||||||
self.tmp_dir = tmp_dir
|
self.tmp_dir = tmp_dir
|
||||||
self.no_geo = no_geo
|
self.no_geo = no_geo
|
||||||
|
self.timeout = timeout
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
# cache geocoded IP addresses during the lifetime of this instance
|
# cache geocoded IP addresses during the lifetime of this instance
|
||||||
self.locations = {}
|
self.locations = {}
|
||||||
|
|
@ -65,10 +68,16 @@ class Traceroute(object):
|
||||||
|
|
||||||
# Example from traceroute.org
|
# Example from traceroute.org
|
||||||
url = "http://www.net.princeton.edu/cgi-bin/traceroute.pl"
|
url = "http://www.net.princeton.edu/cgi-bin/traceroute.pl"
|
||||||
(status_code, response) = self.urlopen(url, context = {'target': self.ip_address})
|
(status_code, content) = self.urlopen(url, context = {'target': self.ip_address})
|
||||||
|
content = content.strip()
|
||||||
|
|
||||||
pattern = re.compile(r'<pre.*?>(?P<traceroute>.*?)</pre>', re.DOTALL|re.IGNORECASE)
|
pattern = re.compile(r'<pre.*?>(?P<traceroute>.*?)</pre>', re.DOTALL|re.IGNORECASE)
|
||||||
traceroute = re.findall(pattern, response)[0].strip()
|
try:
|
||||||
|
traceroute = re.findall(pattern, content)[0].strip()
|
||||||
|
except IndexError:
|
||||||
|
# Manually append closing </pre> for partially downloaded page
|
||||||
|
content = '%s</pre>' % content
|
||||||
|
traceroute = re.findall(pattern, content)[0].strip()
|
||||||
|
|
||||||
return (status_code, traceroute)
|
return (status_code, traceroute)
|
||||||
|
|
||||||
|
|
@ -166,19 +175,18 @@ class Traceroute(object):
|
||||||
|
|
||||||
def urlopen(self, url, context=None):
|
def urlopen(self, url, context=None):
|
||||||
"""Perform HTTP GET/POST on the specified URL and return the resultant
|
"""Perform HTTP GET/POST on the specified URL and return the resultant
|
||||||
status code and response.
|
status code and response."""
|
||||||
"""
|
|
||||||
status_code = 200
|
status_code = 200
|
||||||
|
|
||||||
request = urllib2.Request(url = url)
|
request = urllib2.Request(url = url)
|
||||||
|
|
||||||
if context:
|
if context:
|
||||||
data = urllib.urlencode(context)
|
data = urllib.urlencode(context)
|
||||||
request.add_data(data)
|
request.add_data(data)
|
||||||
|
|
||||||
response = ''
|
content = ''
|
||||||
try:
|
try:
|
||||||
response = urllib2.urlopen(request).read()
|
response = urllib2.urlopen(request)
|
||||||
|
self.print_debug("url = %s\nheader = %s" % (response.geturl(), response.info()))
|
||||||
|
content = self.chunked_read(response)
|
||||||
except urllib2.HTTPError, e:
|
except urllib2.HTTPError, e:
|
||||||
status_code = e.code
|
status_code = e.code
|
||||||
except urllib2.URLError:
|
except urllib2.URLError:
|
||||||
|
|
@ -190,7 +198,37 @@ class Traceroute(object):
|
||||||
self.urlopen_count = 1
|
self.urlopen_count = 1
|
||||||
self.print_debug("[%d] url = %s, status_code = %d" % (self.urlopen_count, url, status_code))
|
self.print_debug("[%d] url = %s, status_code = %d" % (self.urlopen_count, url, status_code))
|
||||||
|
|
||||||
return (status_code, response)
|
return (status_code, content)
|
||||||
|
|
||||||
|
def chunked_read(self, response):
|
||||||
|
"""Read page response in chunks. A signal handler is attached to abort
|
||||||
|
reading after the set timeout.
|
||||||
|
Chunk size = 1KB, max. page size = 1MB
|
||||||
|
"""
|
||||||
|
content = ''
|
||||||
|
max_bytes = 1 * 1024 * 1024
|
||||||
|
completed_bytes = 0
|
||||||
|
bytes_per_read = 1024
|
||||||
|
|
||||||
|
try:
|
||||||
|
signal.signal(signal.SIGALRM, self.signal_handler)
|
||||||
|
signal.alarm(self.timeout)
|
||||||
|
while completed_bytes <= max_bytes:
|
||||||
|
data = response.read(bytes_per_read)
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
content += data
|
||||||
|
completed_bytes += bytes_per_read
|
||||||
|
self.print_debug("completed_bytes = %d, %s" % (completed_bytes, data))
|
||||||
|
signal.alarm(0)
|
||||||
|
except Exception, e:
|
||||||
|
self.print_debug("%s" % str(e))
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
def signal_handler(self, signum, frame):
|
||||||
|
"""Signal handler that simply raises an exception when triggered."""
|
||||||
|
raise Exception("Caught signal %d" % signum)
|
||||||
|
|
||||||
def print_debug(self, msg):
|
def print_debug(self, msg):
|
||||||
"""Print debug message to standard output."""
|
"""Print debug message to standard output."""
|
||||||
|
|
@ -204,12 +242,13 @@ def main():
|
||||||
cmdparser.add_option("-i", "--ip_address", type="string", default="8.8.8.8", help="IP address of destination host (default: 8.8.8.8)")
|
cmdparser.add_option("-i", "--ip_address", type="string", default="8.8.8.8", help="IP address of destination host (default: 8.8.8.8)")
|
||||||
cmdparser.add_option("-t", "--tmp_dir", type="string", default="/tmp", help="Temporary directory to store downloaded traceroute results (default: /tmp)")
|
cmdparser.add_option("-t", "--tmp_dir", type="string", default="/tmp", help="Temporary directory to store downloaded traceroute results (default: /tmp)")
|
||||||
cmdparser.add_option("-n", "--no_geo", action="store_true", default=False, help="No geolocation data (default: False)")
|
cmdparser.add_option("-n", "--no_geo", action="store_true", default=False, help="No geolocation data (default: False)")
|
||||||
|
cmdparser.add_option("-s", "--timeout", type="int", default=120, help="Timeout in seconds for all downloads (default: 120)")
|
||||||
cmdparser.add_option("-d", "--debug", action="store_true", default=False, help="Show debug output (default: False)")
|
cmdparser.add_option("-d", "--debug", action="store_true", default=False, help="Show debug output (default: False)")
|
||||||
|
|
||||||
(options, args) = cmdparser.parse_args()
|
(options, args) = cmdparser.parse_args()
|
||||||
|
|
||||||
if options.ip_address:
|
if options.ip_address:
|
||||||
traceroute = Traceroute(ip_address=options.ip_address, tmp_dir=options.tmp_dir, no_geo=options.no_geo, debug=options.debug)
|
traceroute = Traceroute(ip_address=options.ip_address, tmp_dir=options.tmp_dir, no_geo=options.no_geo, timeout=options.timeout, debug=options.debug)
|
||||||
hops = traceroute.traceroute()
|
hops = traceroute.traceroute()
|
||||||
hops = json.dumps(hops, indent=4)
|
hops = json.dumps(hops, indent=4)
|
||||||
print hops
|
print hops
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue