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

This commit is contained in:
psy 2013-10-03 16:01:19 -07:00
parent 1c8a3f799e
commit a6c3115ca6
6 changed files with 172 additions and 72 deletions

View File

@ -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

View File

@ -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

View File

@ -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"
-------------------

183
main.py
View File

@ -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.")
# 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"
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):
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:
if self.url[0].startswith('file://'):
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"
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) 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()

View File

@ -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")

View File

@ -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('<travel>','').replace('</travel>','').split('<')[0]
hop_tag = dom.getElementsByTagName('hop')[0].toxml()
hop_data = hop_tag.replace('<hop>','').replace('</hop>','')
host_tag = dom.getElementsByTagName('host')[0].toxml()
host_data = host_tag.replace('<host>','').replace('</host>','')
hop_ip_tag = dom.getElementsByTagName('hop_ip')[0].toxml()
hop_ip_data = hop_ip_tag.replace('<hop_ip>','').replace('</hop_ip>','')
longitude_tag = dom.getElementsByTagName('longitude')[0].toxml()
longitude_data = longitude_tag.replace('<longitude>','').replace('</longitude>','')
latitude_tag = dom.getElementsByTagName('latitude')[0].toxml()
latitude_data = hop_tag.replace('<latitude>','').replace('</latitude>','')
city_tag = dom.getElementsByTagName('city')[0].toxml()
city_data = city_tag.replace('<city>','').replace('</city>','')
country_tag = dom.getElementsByTagName('country')[0].toxml()
country_data = country_tag.replace('<country>','').replace('</country>','')
server_name_tag = dom.getElementsByTagName('server_name')[0].toxml()
server_name_data = server_name_tag.replace('<server_name>','').replace('</server_name>','')
asn_tag = dom.getElementsByTagName('asn')[0].toxml()
asn_data = asn_tag.replace('<asn>','').replace('</asn>','')
timestamp_tag = dom.getElementsByTagName('timestamp')[0].toxml()
timestamp_data = timestamp_tag.replace('<timestamp>','').replace('</timestamp>','')
country_code_tag = dom.getElementsByTagName('country_code')[0].toxml()
country_code_data = country_code_tag.replace('<country_code>','').replace('</country_code>','')
meta_tag = dom.getElementsByTagName('meta')[0].toxml()
meta_data = meta_tag.replace('<meta>','').replace('</meta>','')
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