- changed a few methods to be private

- changed basic constructor so that module could be used on its own aside from command-line
This commit is contained in:
Brian Martin 2015-07-08 09:33:38 -04:00
commit 1c5a84b739
2 changed files with 69 additions and 15 deletions

2
requirements.txt Normal file
View file

@ -0,0 +1,2 @@
netifaces==0.10.4
requests==2.7.0

82
traceroute.py Normal file → Executable file
View file

@ -15,10 +15,12 @@ import sys
import urllib import urllib
import urllib2 import urllib2
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
import requests
USER_AGENT = "traceroute/1.0 (+https://github.com/ayeowch/traceroute)" USER_AGENT = "traceroute/1.0 (+https://github.com/ayeowch/traceroute)"
class Traceroute(object): class Traceroute(object):
""" """
Multi-source traceroute instance. Multi-source traceroute instance.
@ -33,13 +35,23 @@ class Traceroute(object):
sources = json.loads(json_file.replace("_IP_ADDRESS_", ip_address)) sources = json.loads(json_file.replace("_IP_ADDRESS_", ip_address))
self.source = sources[country] self.source = sources[country]
self.country = country self.country = country
if self.country == 'LO':
self.local_mode = True
self.pub_ip = self._lookup_public_ip()
else:
self.local_mode = False
self.tmp_dir = tmp_dir self.tmp_dir = tmp_dir
self.no_geo = no_geo self.no_geo = no_geo
self.timeout = timeout self.timeout = timeout
self.debug = debug self.debug = debug
self.locations = {} self.locations = {}
self.hops = []
def traceroute(self): self._initialize()
def _initialize(self):
""" """
Instead of running the actual traceroute command, we will fetch Instead of running the actual traceroute command, we will fetch
standard traceroute results from several publicly available webpages standard traceroute results from several publicly available webpages
@ -62,16 +74,42 @@ class Traceroute(object):
traceroute = open(filepath, "r").read() traceroute = open(filepath, "r").read()
# hop_num, hosts # hop_num, hosts
hops = self.get_hops(traceroute) hops = self._get_hops(traceroute)
# hop_num, hostname, ip_address, rtt # hop_num, hostname, ip_address, rtt
hops = self.get_formatted_hops(hops) self.hops = self._get_formatted_hops()
if not self.no_geo: if not self.no_geo:
# hop_num, hostname, ip_address, rtt, latitude, longitude # hop_num, hostname, ip_address, rtt, latitude, longitude
hops = self.get_geocoded_hops(hops) self.hops = self._get_geocoded_hops()
self.hops = map(lambda h: {h.pop("hop_num") : h}, self.hops)
def get_report(self):
report_structure = {}
report_structure['hops'] = self.hops
if self.local_mode:
report_structure['pub_ip'] = self.pub_ip
return report_structure
def _lookup_public_ip(self):
#TODO Don't forget to put timeout from above here....
response = requests.get('https://api.ipify.org?format=json')
if response.status_code == 200:
ip_data = response.json()
if 'ip' not in ip_data.keys():
return 'Unable to determine IP'
else:
return ip_data['ip']
else:
return 'Unable to determine IP'
return hops
def get_traceroute_output(self): def get_traceroute_output(self):
""" """
@ -94,12 +132,11 @@ class Traceroute(object):
traceroute = re.findall(pattern, content)[0].strip() traceroute = re.findall(pattern, content)[0].strip()
return (status_code, traceroute) return (status_code, traceroute)
def get_hops(self, traceroute): def _get_hops(self, traceroute):
""" """
Returns hops from traceroute output in an array of dicts each Returns hops from traceroute output in an array of dicts each
with hop number and the associated hosts data. with hop number and the associated hosts data.
""" """
hops = []
regex = r'^(?P<hop_num>\d+)(?P<hosts>.*?)$' regex = r'^(?P<hop_num>\d+)(?P<hosts>.*?)$'
lines = traceroute.split("\n") lines = traceroute.split("\n")
for line in lines: for line in lines:
@ -112,17 +149,16 @@ class Traceroute(object):
except AttributeError: except AttributeError:
continue continue
self.print_debug(hop) self.print_debug(hop)
hops.append(hop) self.hops.append(hop)
return hops
def get_formatted_hops(self, hops): def _get_formatted_hops(self):
""" """
Hosts data from get_hops() is represented in a single string. Hosts data from get_hops() is represented in a single string.
We use this function to better represent the hosts data in a dict. We use this function to better represent the hosts data in a dict.
""" """
formatted_hops = [] formatted_hops = []
regex = r'(?P<h>[\w.-]+) \((?P<i>[\d.]+)\) (?P<r>\d{1,4}.\d{1,4} ms)' regex = r'(?P<h>[\w.-]+) \((?P<i>[\d.]+)\) (?P<r>\d{1,4}.\d{1,4} ms)'
for hop in hops: for hop in self.hops:
hop_num = int(hop['hop_num'].strip()) hop_num = int(hop['hop_num'].strip())
hosts = hop['hosts'].replace(" ", " ").strip() hosts = hop['hosts'].replace(" ", " ").strip()
# Using re.findall(), we split the hosts, then for each host, # Using re.findall(), we split the hosts, then for each host,
@ -139,13 +175,14 @@ class Traceroute(object):
formatted_hops.append(hop_context) formatted_hops.append(hop_context)
return formatted_hops return formatted_hops
def get_geocoded_hops(self, hops):
def _get_geocoded_hops(self):
""" """
Returns hops from get_formatted_hops() with geolocation information Returns hops from get_formatted_hops() with geolocation information
for each hop. for each hop.
""" """
geocoded_hops = [] geocoded_hops = []
for hop in hops: for hop in self.hops:
ip_address = hop['ip_address'] ip_address = hop['ip_address']
location = None location = None
if ip_address in self.locations: if ip_address in self.locations:
@ -287,6 +324,9 @@ def main():
options, _ = cmdparser.parse_args() options, _ = cmdparser.parse_args()
json_file = open(options.json_file, "r").read() json_file = open(options.json_file, "r").read()
sources = json.loads(json_file.replace("_IP_ADDRESS_", options.ip_address)) sources = json.loads(json_file.replace("_IP_ADDRESS_", options.ip_address))
# Get Hope info using Traceroute Object
traceroute = Traceroute(ip_address=options.ip_address, traceroute = Traceroute(ip_address=options.ip_address,
source=sources[options.country], source=sources[options.country],
country=options.country, country=options.country,
@ -294,8 +334,20 @@ def main():
no_geo=options.no_geo, no_geo=options.no_geo,
timeout=options.timeout, timeout=options.timeout,
debug=options.debug) debug=options.debug)
hops = traceroute.traceroute() """
print(json.dumps(hops, indent=4)) Pseudo-Code
report = traceroute.get_report()
print(json.dumps(report, indent=4)
"""
report = traceroute.get_report()
print(json.dumps(report, indent=4))
return 0 return 0