diff --git a/doc/CHANGELOG b/doc/CHANGELOG
index b64185f..805eb2c 100644
--- a/doc/CHANGELOG
+++ b/doc/CHANGELOG
@@ -2,9 +2,19 @@
Changelog: Border-Check
==============================
+
+=================
+October 4, 2013:
+=================
+
+- added feature to import traces from XML template
+- fixed some LFT subprocess errors on Unix based systems
+- more clean non-debug mode
+- added 'localhost' filter to target parser
+
=================
September 27, 2013:
=================
-- Public release: Border Check v0.1
+- public release: Border Check v0.1
diff --git a/doc/INSTALL b/doc/INSTALL
index 41d0c02..bac6fd3 100644
--- a/doc/INSTALL
+++ b/doc/INSTALL
@@ -30,7 +30,11 @@ BC runs on OSx and Unix systems. It requires the following libraries/dependencie
sudo apt-get install python-pip
- and then run pip install lxml and pip install pygeoip
+ and then run:
+
+ pip install lxml
+
+ pip install pygeoip
- biplist:
https://pypi.python.org/pypi/biplist/0.5
diff --git a/doc/README b/doc/README
index e2da879..df79439 100644
--- a/doc/README
+++ b/doc/README
@@ -22,6 +22,7 @@ Options:
-h, --help show this help message and exit
-d, --debug debug mode
--xml=EXPORT_XML export traces to xml (ex: --xml foo.xml)
+ --load=IMPORT_XML import traces to show (ex: --load bar.xml)
--bh=BROWSER_HISTORY set browser's history path
-b BROWSER set browser manually: F = Firefox / C = Chrome / S = Safari / Ch = Chromium
@@ -64,4 +65,8 @@ $ python bc --bh "Library/Safari/History.plist"
$ python bc --bh "Library/Safari/History.plist"
-------------------
+* Import 'traces' from xml:
+
+$ python bc --load "mytravel.xml"
+-------------------
diff --git a/main.py b/main.py
index c769f36..fb8aaeb 100755
--- a/main.py
+++ b/main.py
@@ -23,9 +23,6 @@ from webserver import BorderCheckWebserver
from xml_exporter import xml_reporting
import webbrowser
-# set to emit debug messages about errors (0 = off).
-DEBUG = 1
-
class bc(object):
"""
BC main Class
@@ -96,17 +93,21 @@ class bc(object):
try:
return func(*args)
except Exception as e:
- print("\n[Error] - Something wrong fetching urls. Aborting..."), "\n"
- if DEBUG:
+ if not options.debug:
+ print("[Error] - Something wrong happens!. Try to run again with --debug option to see more detailed information about the error on a Traceback output."), "\n"
+ else:
+ print("[Error] - Something wrong happens!. You have the reason at the end of the Traceback. If you don't understand what's happen, try to contact with project contributors."), "\n"
+ if options.debug == 1:
traceback.print_exc()
- sys.exit(2)
+ print "" # \n after traceback ouput
+ sys.exit(2)
def check_root(self):
"""
Check root permissions
"""
if not os.geteuid()==0:
- sys.exit("\nOnly root can run this script...\n")
+ sys.exit("Warning: Only root can launch traceroutes. (Try: 'sudo ./bc')\n")
def check_browser(self):
"""
@@ -384,7 +385,10 @@ class bc(object):
print "Can't get Safari version information, you'll have to look it up manually \n"
else:
print "Version:", self.browser_version
- print "History:", self.browser_history_path, "\n"
+ if self.options.import_xml: # history not needed on xml importing
+ pass
+ else:
+ print "History:", self.browser_history_path, "\n"
def getURL(self):
"""
@@ -429,6 +433,9 @@ class bc(object):
"""
Run an LFT
"""
+ # LFT needs root
+ root = self.try_running(self.check_root, "\nInternal error checking root permissions.")
+
#try:
if self.operating_system == 'darwin':
try:
@@ -438,19 +445,19 @@ class bc(object):
self.content = a.stdout.read()
if self.operating_system == 'linux':
- if self.method == '-e':
+ if self.method == '-e': # tcp probes
self.method = '-E'
try:
- self.content = subprocess.check_output(['lft', '-S', '-n', self.destination_ip])
+ self.content = subprocess.check_output(['lft', '-S', '-n', self.method, self.destination_ip])
# support for older python versions (<2.75) that don't support subprocess.check_output
except:
- a = subprocess.Popen(['lft', '-S', '-n', self.destination_ip], stdout=subprocess.PIPE)
+ a = subprocess.Popen(['lft', '-S', '-n', self.method, self.destination_ip], stdout=subprocess.PIPE)
self.content = a.stdout.read()
self.attempts += 1
if self.options.debug == True:
print "Tracing:", self.destination_ip, "with method:", self.method, 'attempt:', self.attempts, '\n'
self.lft_parse()
-
+
def lft_parse(self):
"""
Parse the lft to see if it produced any results, if not, run another LFT using a different method
@@ -524,7 +531,7 @@ class bc(object):
for ip in line:
if re.match(r'\d{1,4}\.\dms$', ip):
self.timestamp = ip.replace('ms', '')
- if re.match(r'^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$', ip) or re.match(r'^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$', ip) or re.match(r'^192.168\.\d{1,3}\.\d{1,3}$', ip) or re.match(r'^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3}$', ip):
+ if re.match(r'^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$', ip) or re.match(r'^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$', ip) or re.match(r'^192.168\.\d{1,3}\.\d{1,3}$', ip) or re.match(r'^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3}$', ip) or re.match('localhost', ip):
pass
else:
if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$",ip):
@@ -639,6 +646,45 @@ class bc(object):
os.remove('GeoIPASNum.gz')
print "Database: GeoIPASNum \n"
+ def importXML(self):
+ """
+ Import travels data directly from XML file (no root needed) and launch a web browser on a thread with a map showing them.
+ """
+ try:
+ xml_results = xml_reporting(self)
+ xml_imported = xml_results.read_xml_results() # read xml directly from file
+ except:
+ print("[Error] - Something wrong importing data from XML file. Aborting..."), "\n"
+ sys.exit(2)
+
+ # Set the maxmind geo databases
+ self.geoip = pygeoip.GeoIP('GeoLiteCity.dat')
+ self.geoasn = pygeoip.GeoIP('GeoIPASNum.dat')
+ match_ip = xml_imported[0].strip('http://').strip(':8080')
+ #regex for filtering local network IPs
+ if re.match(r'^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$', match_ip) or re.match(r'^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$', match_ip) or re.match(r'^192.168\.\d{1,3}\.\d{1,3}$', match_ip) or re.match(r'^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3}$', match_ip) or match_ip.startswith('file://') or match_ip.startswith('localhost'):
+ print '='*45 + "\n", "Target:\n" + '='*45 + "\n"
+ print "URL:", self.options.import_xml, "\n"
+ print "Warning: This target is not valid!.\n"
+ sys.exit(2)
+ else:
+ if xml_imported[0].startswith('file://'):
+ print '='*45 + "\n", "Target:\n" + '='*45 + "\n"
+ print "URL:", self.options.import_xml, "\n"
+ print "Warning: This target is not valid!.\n"
+ sys.exit(2)
+ else:
+ print '='*45 + "\n", "Target:\n" + '='*45 + "\n"
+ print "URL:", self.options.import_xml, "\n"
+ print "Host:", xml_imported[0], "\n"
+ os.system('cp -r ' + self.options.import_xml + ' data.xml') # copy XML data provided by user to data.xml template
+ # start web mode (on a different thread)
+ try:
+ webbrowser.open('http://127.0.0.1:8080', new=1)
+ BorderCheckWebserver(self)
+ except (KeyboardInterrupt, SystemExit):
+ sys.exit()
+
def run(self, opts=None):
"""
Run BorderCheck
@@ -653,77 +699,78 @@ class bc(object):
print('='*75)
print(str(p.version))
print('='*75)
- # root checker
- root = self.try_running(self.check_root, "\nInternal error checking root permissions.")
# extract browser type and path
browser = self.try_running(self.check_browser, "\nInternal error checking browser files path.")
# extract url
url = self.try_running(self.getURL, "\nInternal error getting urls from browser's database.")
# set geoip database
geo = self.try_running(self.getGEO, "\nInternal error setting geoIP database.")
- # run traceroutes
- match_ip = self.url[0].strip('http://').strip(':8080')
- #regex for filtering local network IPs
- if re.match(r'^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$', match_ip) or re.match(r'^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$', match_ip) or re.match(r'^192.168\.\d{1,3}\.\d{1,3}$', match_ip) or re.match(r'^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3}$', match_ip) or match_ip.startswith('file://'):
- print '='*45 + "\n", "Target:\n" + '='*45 + "\n"
- print "URL:", self.url[0], "\n"
- print "Warning: This target is not valid!.\n"
- pass
+ # read from XML or run traceroutes + stay latent mode
+ if options.import_xml:
+ import_xml = self.try_running(self.importXML, "\nInternal error importing XML data from file.")
else:
- if self.url[0].startswith('file://'):
+ match_ip = self.url[0].strip('http://').strip(':8080')
+ #regex for filtering local network IPs
+ if re.match(r'^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$', match_ip) or re.match(r'^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$', match_ip) or re.match(r'^192.168\.\d{1,3}\.\d{1,3}$', match_ip) or re.match(r'^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3}$', match_ip) or match_ip.startswith('file://') or match_ip.startswith('localhost'):
print '='*45 + "\n", "Target:\n" + '='*45 + "\n"
print "URL:", self.url[0], "\n"
print "Warning: This target is not valid!.\n"
pass
else:
- traces = self.try_running(self.traces, "\nInternal error tracerouting.")
+ if self.url[0].startswith('file://'):
+ print '='*45 + "\n", "Target:\n" + '='*45 + "\n"
+ print "URL:", self.url[0], "\n"
+ print "Warning: This target is not valid!.\n"
+ pass
+ else:
+ traces = self.try_running(self.traces, "\nInternal error tracerouting.")
# start web mode (on a different thread)
- try:
- t = threading.Thread(target=BorderCheckWebserver, args=(self, ))
- t.daemon = True
- t.start()
- time.sleep(2)
- except (KeyboardInterrupt, SystemExit):
- t.join()
- sys.exit()
- # open same browser of history access on a new tab
try:
- webbrowser.open('http://127.0.0.1:8080', new=1)
- except:
- print "Error: Browser is not responding correctly.\n"
+ t = threading.Thread(target=BorderCheckWebserver, args=(self, ))
+ t.daemon = True
+ t.start()
+ time.sleep(2)
+ except (KeyboardInterrupt, SystemExit):
+ t.join()
+ sys.exit()
+ # open same browser of history access on a new tab
+ try:
+ webbrowser.open('http://127.0.0.1:8080', new=1)
+ except:
+ print "Error: Browser is not responding correctly.\n"
- print('='*75)
- print(str(p.version))
- print('='*75 + "\n")
- print "Status: Waiting for new urls ...\n"
- print "Type 'Control+C' to exit.\n"
- # stay latent waiting for new urls
- while True:
- url = urlparse(self.getURL()).netloc
- #url = url.replace('www.','')
- try:
- match_ip = url.strip('http://').strip(':8080')
- except:
- print '='*45 + "\n", "Target:\n" + '='*45 + "\n"
- print "URL:", self.url[0], "\n"
- pass
- if url != self.old_url:
- if re.match(r'^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$', match_ip) or re.match(r'^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$', match_ip) or re.match(r'^192.168\.\d{1,3}\.\d{1,3}$', match_ip) or re.match(r'^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3}$', match_ip):
+ print('='*75)
+ print(str(p.version))
+ print('='*75 + "\n")
+ print "Status: Waiting for new urls ...\n"
+ print "Type 'Control+C' to exit.\n"
+ # stay latent waiting for new urls
+ while True:
+ url = urlparse(self.getURL()).netloc
+ #url = url.replace('www.','')
+ try:
+ match_ip = url.strip('http://').strip(':8080')
+ except:
+ print '='*45 + "\n", "Target:\n" + '='*45 + "\n"
+ print "URL:", self.url[0], "\n"
pass
- else:
- if self.url[0].startswith('file://'):
+ if url != self.old_url:
+ if re.match(r'^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$', match_ip) or re.match(r'^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$', match_ip) or re.match(r'^192.168\.\d{1,3}\.\d{1,3}$', match_ip) or re.match(r'^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3}$', match_ip) or match_ip.startswith('localhost'):
pass
else:
- if os.path.exists('data.xml'): # removing xml data to has a new map each time that bc is launched
- os.remove('data.xml')
- open('data.xml', 'w') # starting a new xml data container in write mode
- traces = self.try_running(self.traces, "\nInternal error tracerouting.")
- # open same browser of history access on a new tab
- # try:
- # webbrowser.open('http://127.0.0.1:8080', new=2) # open on same tab?
- # except:
- # print "Error: Browser is not responding correctly.\n"
- time.sleep(5) # free process time or goodbye :-)
+ if self.url[0].startswith('file://'):
+ pass
+ else:
+ if os.path.exists('data.xml'): # removing xml data to has a new map each time that bc is launched
+ os.remove('data.xml')
+ open('data.xml', 'w') # starting a new xml data container in write mode
+ traces = self.try_running(self.traces, "\nInternal error tracerouting.")
+ # open same browser of history access on a new tab
+ # try:
+ # webbrowser.open('http://127.0.0.1:8080', new=2) # open on same tab?
+ # except:
+ # print "Error: Browser is not responding correctly.\n"
+ time.sleep(5) # free process time or goodbye :-)
if __name__ == "__main__":
app = bc()
diff --git a/options.py b/options.py
index 34a7d25..d544609 100644
--- a/options.py
+++ b/options.py
@@ -15,6 +15,7 @@ class BCOptions(optparse.OptionParser):
self.add_option("-d", "--debug", action="store_true", dest="debug", help="debug mode")
self.add_option("--xml", action="store", dest="export_xml", help="export traces to xml (ex: --xml foo.xml)")
+ self.add_option("--load", action="store", dest="import_xml", help="import traces to show (ex: --load bar.xml)")
self.add_option("--bh", action="store", dest="browser_history", help="set browser's history path")
self.add_option("-b", action="store", dest="browser", help="set browser manually: F = Firefox / C = Chrome / S = Safari / Ch = Chromium")
#self.add_option("--proxy", action="store", dest="proxy", help="set proxy server")
diff --git a/xml_exporter.py b/xml_exporter.py
index 3358fc7..8146498 100755
--- a/xml_exporter.py
+++ b/xml_exporter.py
@@ -4,8 +4,6 @@
BC (Border-Check) is a tool to retrieve info of traceroute tests over website navigation routes.
GPLv3 - 2013 by psy (epsylon@riseup.net)
"""
-import xml.etree.ElementTree as ET
-
class xml_reporting(object):
"""
Print results from a traceroute in an XML fashion
@@ -15,6 +13,7 @@ class xml_reporting(object):
self.instance = bc
def print_xml_results(self, filename):
+ import xml.etree.ElementTree as ET
root = ET.Element("travel")
i = 1
for i in self.instance.result_list:
@@ -48,3 +47,37 @@ class xml_reporting(object):
tree = ET.ElementTree(root)
tree.write(filename)
+ def read_xml_results(self):
+ from xml.dom.minidom import parseString
+ file = open(self.instance.options.import_xml,'r')
+ data = file.read()
+ file.close()
+ dom = parseString(data)
+ travel_tag = dom.getElementsByTagName('travel')[0].toxml()
+ travel_data = travel_tag.replace('','').replace('','').split('<')[0]
+ hop_tag = dom.getElementsByTagName('hop')[0].toxml()
+ hop_data = hop_tag.replace('','').replace('','')
+ host_tag = dom.getElementsByTagName('host')[0].toxml()
+ host_data = host_tag.replace('','').replace('','')
+ hop_ip_tag = dom.getElementsByTagName('hop_ip')[0].toxml()
+ hop_ip_data = hop_ip_tag.replace('','').replace('','')
+ longitude_tag = dom.getElementsByTagName('longitude')[0].toxml()
+ longitude_data = longitude_tag.replace('','').replace('','')
+ latitude_tag = dom.getElementsByTagName('latitude')[0].toxml()
+ latitude_data = hop_tag.replace('','').replace('','')
+ city_tag = dom.getElementsByTagName('city')[0].toxml()
+ city_data = city_tag.replace('','').replace('','')
+ country_tag = dom.getElementsByTagName('country')[0].toxml()
+ country_data = country_tag.replace('','').replace('','')
+ server_name_tag = dom.getElementsByTagName('server_name')[0].toxml()
+ server_name_data = server_name_tag.replace('','').replace('','')
+ asn_tag = dom.getElementsByTagName('asn')[0].toxml()
+ asn_data = asn_tag.replace('','').replace('','')
+ timestamp_tag = dom.getElementsByTagName('timestamp')[0].toxml()
+ timestamp_data = timestamp_tag.replace('','').replace('','')
+ country_code_tag = dom.getElementsByTagName('country_code')[0].toxml()
+ country_code_data = country_code_tag.replace('','').replace('','')
+ meta_tag = dom.getElementsByTagName('meta')[0].toxml()
+ meta_data = meta_tag.replace('','').replace('','')
+
+ return travel_data, hop_data, hop_ip_data, longitude_data, latitude_data, city_data, country_data, server_name_data, asn_data, timestamp_data, country_code_data, meta_data