Compare commits

..

No commits in common. "master" and "convert-to-distutils" have entirely different histories.

24 changed files with 336 additions and 13022 deletions

9
.gitignore vendored
View file

@ -1,7 +1,2 @@
*.py[co]
*.egg
*.egg-info
eggs
*.cfg
*.log
*.pyc
*.pyo

View file

@ -1,58 +1,42 @@
BIRD-LG
=======
This is a looking glass for the Internet Routing Daemon "Bird".
this is a looking glass for the Internet Routing Daemon "Bird"
Software is split in two parts:
- lgproxy.py:
It must be installed and started on all bird nodes. It act as a proxy to make traceroute and bird query on the node.
Access restriction to this web service can be done in file "lgproxy.cfg" (only IP address based restriction for now).
software is splited onto two parts:
- lg-proxy.py:
It must be install and started on all bird node. It act as a proxy to make traceroute and bird query on the node
Also the access restriction to this web service can be done is file "lg-proxy.cfg" (only ip based restriction for now)
- lg.py:
This is the frontend, a web based UI that request information to all lg-proxy.py nodes
The domain and the list of all bird node can be done
This is the frontend, a web based UI that request informations to all lgproxy.py nodes.
The domain and the list of all bird nodes can be done.
```
***************
+--> * lgproxy.py *
+--> * lg-proxy.py *
| ***************
|
******** ******************* | ***************
* USER * ----> * webserver/lg.py *--+--> * lgproxy.py *
* USER * ----> * webserver/lg.py *--+--> * lg-proxy.py *
******** ******************* | ***************
|
| ***************
+--> * lgproxy.py *
+--> * lg-proxy.py *
***************
```
Installation
------------
```
apt-get install python-memcache python-flask python-pydot python-dnspython libapache2-mod-wsgi
```
bird-lg depends on :
bird-lg depend on :
- python-flask >= 0.8
- python-dnspython
- python-pydot
Each services can be embedded in any webserver by following regular python-flask configuration.
Each service can by embeded in any webserver by follow regular python-flask configuration
You should copy the configuration files (`lgproxy.cfg.example` to `lgproxy.cfg`
and `lg.cfg.example` to `lg.cfg`) and edit them.
Only tested with bird 1.2.5
Should work with most Bird versions (from 1.2.x, tested up to 1.4.5).
source code under GPL 3.0, powered by Flask, jQuery and Bootstrap
Source code is under GPL 3.0, powered by Flask, jQuery and Bootstrap.
Copyright © 2012 Mehdi Abaakouk <sileht@sileht.net>
Copyright (c) 2012 Mehdi Abaakouk <sileht@sileht.net>

4
lg-proxy.cfg Normal file
View file

@ -0,0 +1,4 @@
ACCESS_LIST = ["91.224.149.206", "178.33.111.110"]
IPV4_SOURCE="91.224.148.1"
IPV6_SOURCE="2a01:6600:8000::1"

84
lg-proxy.py Normal file
View file

@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
# vim: ts=4
###
#
# Copyright (c) 2006 Mehdi Abaakouk
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#
###
import subprocess
from urllib import unquote
from bird import BirdSocket
from flask import Flask, request, abort
app = Flask(__name__)
app.config.from_pyfile('lg-proxy.cfg')
def check_accesslist():
if app.config["ACCESS_LIST"] and request.remote_addr not in app.config["ACCESS_LIST"]:
abort(401)
@app.route("/traceroute")
@app.route("/traceroute6")
def traceroute():
check_accesslist()
src = []
if request.path == '/traceroute6':
o = "-6"
if app.config.get("IPV6_SOURCE",""):
src = [ "-s", app.config.get("IPV6_SOURCE") ]
else:
o = "-4"
if app.config.get("IPV4_SOURCE",""):
src = [ "-s", app.config.get("IPV4_SOURCE") ]
query = request.args.get("q","")
query = unquote(query)
command = [ 'traceroute' , o ] + src + [ '-A', '-q1', '-N32', '-w1', '-m15', query ]
result = subprocess.Popen( command , stdout=subprocess.PIPE).communicate()[0].decode('utf-8', 'ignore').replace("\n","<br>")
return result
@app.route("/bird")
@app.route("/bird6")
def bird():
check_accesslist()
if request.path == "/bird": b = BirdSocket(file="/var/run/bird.ctl")
elif request.path == "/bird6": b = BirdSocket(file="/var/run/bird6.ctl")
else: return "No bird socket selected"
query = request.args.get("q","")
query = unquote(query)
status, result = b.cmd(query)
b.close()
# FIXME: use status
return result
if __name__ == "__main__":
app.debug = True
app.run("0.0.0.0")

20
lg.cfg Normal file
View file

@ -0,0 +1,20 @@
DOMAIN = "tetaneutral.net"
PROXY = {
"gw": 5000,
"h3": 5000,
}
# Used for bgpmap
ROUTER_IP = {
"gw" : [ "91.224.148.2", "2a01:6600:8000::175" ],
"h3" : [ "91.224.148.3", "2a01:6600:8000::131" ]
}
AS_NUMBER = {
"gw" : "197422",
"h3" : "197422"
}
SESSION_KEY = '\xd77\xf9\xfa\xc2\xb5\xcd\x85)`+H\x9d\xeeW\\%\xbe/\xbaT\x89\xe8\xa7'

View file

@ -1,35 +0,0 @@
# Configuration file for lg.py
# Copy to lg.cfg and edit to suit your needs.
DEBUG = False
LOG_FILE="/var/log/bird-lg/lg.log"
LOG_LEVEL="WARNING"
DOMAIN = "example.com"
# Which IP/port to listen to for client connections.
BIND_IP = "0.0.0.0"
BIND_PORT = 5000
# Which backend routers to connect to (here, router1.example.com:5000 and
# router2.example.com:5000). The routers must run lgproxy.py.
PROXY = {
"router1": 5000,
"router2": 5000,
}
# Used for bgpmap
ROUTER_IP = {
"router1" : [ "192.0.2.1", "2001:db8::1" ],
"router2" : [ "192.0.2.2", "2001:db8::2" ]
}
AS_NUMBER = {
"router1" : "64498",
"router2" : "65538"
}
#WHOIS_SERVER = "whois.foo.bar"
SESSION_KEY = '\xd77\xf9\xfa\xc2\xb5\xcd\x85)`+H\x9d\xeeW\\%\xbe/\xbaT\x89\xe8\xa7'

445
lg.py Normal file → Executable file
View file

@ -20,45 +20,20 @@
#
###
from collections import defaultdict
from logging.handlers import TimedRotatingFileHandler
from urllib import quote, unquote
from urllib2 import urlopen
import json
import logging
import memcache
import random
import re
import subprocess
import re
from urllib2 import urlopen
from urllib import quote, unquote
import json
import random
from toolbox import mask_is_valid, ipv6_is_valid, ipv4_is_valid, resolve, resolve_ptr, save_cache_pickle, load_cache_pickle, get_asname_from_whois, unescape
from toolbox import mask_is_valid, ipv6_is_valid, ipv4_is_valid, resolve, save_cache_pickle, load_cache_pickle
from dns.resolver import NXDOMAIN
from flask import Flask, render_template, jsonify, redirect, session, request, abort, Response, Markup
import pydot
from flask import Flask, render_template, jsonify, redirect, session, request, abort, Response
app = Flask(__name__)
app.config.from_pyfile('lg.cfg')
app.secret_key = app.config["SESSION_KEY"]
app.debug = app.config["DEBUG"]
file_handler = TimedRotatingFileHandler(filename=app.config["LOG_FILE"], when="midnight")
file_handler.setLevel(getattr(logging, app.config["LOG_LEVEL"].upper()))
app.logger.addHandler(file_handler)
memcache_server = app.config.get("MEMCACHE_SERVER", "127.0.0.1:11211")
memcache_expiration = int(app.config.get("MEMCACHE_EXPIRATION", "1296000")) # 15 days by default
mc = memcache.Client([memcache_server])
def get_asn_from_as(n):
asn_zone = app.config.get("ASN_ZONE", "asn.cymru.com")
try:
data = resolve("AS%s.%s" % (n, asn_zone), "TXT").replace("'", "").replace('"', '')
except:
return " " * 5
return [field.strip() for field in data.split("|")]
def add_links(text):
@ -71,18 +46,16 @@ def add_links(text):
ret_text = []
for line in text:
# Some heuristic to create link
if line.strip().startswith("BGP.as_path:") or line.strip().startswith("Neighbor AS:"):
ret_text.append(re.sub(r'(\d+)', r'<a href="/whois?q=\1" class="whois">\1</a>', line))
if line.strip().startswith("BGP.as_path:") or \
line.strip().startswith("Neighbor AS:"):
ret_text.append(re.sub(r'(\d+)', r'<a href="/whois/\1" class="whois">\1</a>', line))
else:
line = re.sub(r'([a-zA-Z0-9\-]*\.([a-zA-Z]{2,3}){1,2})(\s|$)', r'<a href="/whois?q=\1" class="whois">\1</a>\3', line)
line = re.sub(r'AS(\d+)', r'<a href="/whois?q=\1" class="whois">AS\1</a>', line)
line = re.sub(r'(\d+\.\d+\.\d+\.\d+)', r'<a href="/whois?q=\1" class="whois">\1</a>', line)
if len(request.path) >= 2:
hosts = "/".join(request.path.split("/")[2:])
else:
hosts = "/"
line = re.sub(r'([a-zA-Z0-9\-]*\.([a-zA-Z]{2,3}){1,2})(\s|$)', r'<a href="/whois/\1" class="whois">\1</a>\3', line)
line = re.sub(r'AS(\d+)', r'<a href="/whois/\1" class="whois">AS\1</a>', line)
line = re.sub(r'(\d+\.\d+\.\d+\.\d+)', r'<a href="/whois/\1" class="whois">\1</a>', line)
hosts = "/".join(request.path.split("/")[2:])
line = re.sub(r'\[(\w+)\s+((|\d\d\d\d-\d\d-\d\d\s)(|\d\d:)\d\d:\d\d|\w\w\w\d\d)', r'[<a href="/detail/%s?q=\1">\1</a> \2' % hosts, line)
line = re.sub(r'(^|\s+)(([a-f\d]{0,4}:){3,10}[a-f\d]{0,4})', r'\1<a href="/whois?q=\2" class="whois">\2</a>', line, re.I)
line = re.sub(r'(^|\s+)(([a-f\d]{0,4}:){3,10}[a-f\d]{0,4})', r'\1<a href="/whois/\2" class="whois">\2</a>', line, re.I)
ret_text.append(line)
return "\n".join(ret_text)
@ -110,10 +83,7 @@ def set_session(request_type, hosts, proto, request_args):
def whois_command(query):
server = []
if app.config.get("WHOIS_SERVER", ""):
server = ["-h", app.config.get("WHOIS_SERVER")]
return subprocess.Popen(['whois'] + server + [query], stdout=subprocess.PIPE).communicate()[0].decode('utf-8', 'ignore')
return subprocess.Popen(['whois', query], stdout=subprocess.PIPE).communicate()[0].decode('utf-8', 'ignore')
def bird_command(host, proto, query):
@ -122,9 +92,9 @@ def bird_command(host, proto, query):
def bird_proxy(host, proto, service, query):
"""Retreive data of a service from a running lgproxy on a remote node
"""Retreive data of a service from a running lg-proxy on a remote node
First and second arguments are the node and the port of the running lgproxy
First and second arguments are the node and the port of the running lg-proxy
Third argument is the service, can be "traceroute" or "bird"
Last argument, the query to pass to the service
@ -139,12 +109,10 @@ def bird_proxy(host, proto, service, query):
port = app.config["PROXY"].get(host, "")
if not port:
return False, 'Host "%s" invalid' % host
elif not path:
return False, 'Proto "%s" invalid' % proto
if not port or not path:
return False, "Host/Proto not allowed"
else:
url = 'http://{}:{}/{}?q={}'.format(app.config['ROUTER_IP'][host][0], port, path, quote(query))
url = "http://%s.%s:%d/%s?q=%s" % (host, app.config["DOMAIN"], port, path, quote(query))
try:
f = urlopen(url)
resultat = f.read()
@ -158,19 +126,18 @@ def bird_proxy(host, proto, service, query):
@app.context_processor
def inject_commands():
commands = [
("ping", "ping ..."),
("traceroute", "traceroute ..."),
("summary", "show protocols"),
("detail", "show protocols ... all"),
("prefix", "show route for ..."),
("prefix_detail", "show route for ... all"),
("prefix_bgpmap", "show route for ... (bgpmap)"),
("where", "show route where net ~ [ ... ]"),
("where_detail", "show route where net ~ [ ... ] all"),
("where_bgpmap", "show route where net ~ [ ... ] (bgpmap)"),
("adv", "show route ..."),
("adv_bgpmap", "show route ... (bgpmap)"),
]
("traceroute", "traceroute ..."),
("summary", "show protocols"),
("detail", "show protocols ... all"),
("prefix", "show route for ..."),
("prefix_detail", "show route for ... all"),
("prefix_bgpmap", "show route for ... (bgpmap)"),
("where", "show route where net ~ [ ... ]"),
("where_detail", "show route where net ~ [ ... ] all"),
("where_bgpmap", "show route where net ~ [ ... ] (bgpmap)"),
("adv", "show route ..."),
("adv_bgpmap", "show route ... (bgpmap)"),
]
commands_dict = {}
for id, text in commands:
commands_dict[id] = text
@ -188,33 +155,27 @@ def hello():
def error_page(text):
return render_template('error.html', errors=[text]), 500
return render_template('error.html', error=text), 500
@app.errorhandler(400)
def incorrect_request(e):
return render_template('error.html', warnings=["The server could not understand the request"]), 400
return render_template('error.html', warning="The server could not understand the request"), 400
@app.errorhandler(404)
def page_not_found(e):
return render_template('error.html', warnings=["The requested URL was not found on the server."]), 404
return render_template('error.html', warning="The requested URL was not found on the server."), 404
def get_query():
q = unquote(request.args.get('q', '').strip())
return q
@app.route("/whois")
def whois():
query = get_query()
if not query:
@app.route("/whois/<query>")
def whois(query):
if not query.strip():
abort(400)
try:
asnum = int(query)
query = "AS%d" % asnum
query = "as%d" % asnum
except:
m = re.match(r"[\w\d-]*\.(?P<domain>[\d\w-]+\.[\d\w-]+)$", query)
if m:
@ -225,54 +186,41 @@ def whois():
SUMMARY_UNWANTED_PROTOS = ["Kernel", "Static", "Device"]
SUMMARY_RE_MATCH = r"(?P<name>[\w_]+)\s+(?P<proto>\w+)\s+(?P<table>\w+)\s+(?P<state>\w+)\s+(?P<since>((|\d\d\d\d-\d\d-\d\d\s)(|\d\d:)\d\d:\d\d|\w\w\w\d\d))($|\s+(?P<info>.*))"
@app.route("/summary/<hosts>")
@app.route("/summary/<hosts>/<proto>")
def summary(hosts, proto="ipv4"):
set_session("summary", hosts, proto, "")
command = "show protocols"
summary = {}
errors = []
error = []
for host in hosts.split("+"):
ret, res = bird_command(host, proto, command)
res = res.split("\n")
if len(res) > 1:
data = []
for line in res[1:]:
line = line.strip()
if line and (line.split() + [""])[1] not in SUMMARY_UNWANTED_PROTOS:
m = re.match(SUMMARY_RE_MATCH, line)
if m:
data.append(m.groupdict())
else:
app.logger.warning("couldn't parse: %s", line)
if ret is False:
errors.append("%s" % res)
continue
summary[host] = data
else:
error.append("%s: bird command failed with error, %s" % (host, "\n".join(res)))
if len(res) <= 1:
errors.append("%s: bird command failed with error, %s" % (host, "\n".join(res)))
continue
data = []
for line in res[1:]:
line = line.strip()
if line and (line.split() + [""])[1] not in SUMMARY_UNWANTED_PROTOS:
split = line.split()
if len(split) >= 5:
props = dict()
props["name"] = split[0]
props["proto"] = split[1]
props["table"] = split[2]
props["state"] = split[3]
props["since"] = split[4]
props["info"] = ' '.join(split[5:]) if len(split) > 5 else ""
data.append(props)
else:
app.logger.warning("couldn't parse: %s", line)
summary[host] = data
return render_template('summary.html', summary=summary, command=command, errors=errors)
return render_template('summary.html', summary=summary, command=command, error="<br>".join(error))
@app.route("/detail/<hosts>/<proto>")
def detail(hosts, proto):
name = get_query()
name = request.args.get('q', '').strip()
if not name:
abort(400)
@ -280,60 +228,21 @@ def detail(hosts, proto):
command = "show protocols all %s" % name
detail = {}
errors = []
error = []
for host in hosts.split("+"):
ret, res = bird_command(host, proto, command)
res = res.split("\n")
if len(res) > 1:
detail[host] = {"status": res[1], "description": add_links(res[2:])}
else:
error.append("%s: bird command failed with error, %s" % (host, "\n".join(res)))
if ret is False:
errors.append("%s" % res)
continue
if len(res) <= 1:
errors.append("%s: bird command failed with error, %s" % (host, "\n".join(res)))
continue
detail[host] = {"status": res[1], "description": add_links(res[2:])}
return render_template('detail.html', detail=detail, command=command, errors=errors)
@app.route("/ping/<hosts>/<proto>")
def ping(hosts, proto):
q = get_query()
if not q:
abort(400)
set_session("ping", hosts, proto, q)
if proto == "ipv6" and not ipv6_is_valid(q):
try:
q = resolve(q, "AAAA")
except:
return error_page("%s is unresolvable or invalid for %s" % (q, proto))
if proto == "ipv4" and not ipv4_is_valid(q):
try:
q = resolve(q, "A")
except:
return error_page("%s is unresolvable or invalid for %s" % (q, proto))
errors = []
infos = {}
for host in hosts.split("+"):
status, resultat = bird_proxy(host, proto, "ping", q)
if status is False:
errors.append("%s" % resultat)
continue
infos[host] = add_links(resultat)
return render_template('ping.html', infos=infos, errors=errors)
return render_template('detail.html', detail=detail, command=command, error="<br>".join(error))
@app.route("/traceroute/<hosts>/<proto>")
def traceroute(hosts, proto):
q = get_query()
q = request.args.get('q', '').strip()
if not q:
abort(400)
@ -350,16 +259,11 @@ def traceroute(hosts, proto):
except:
return error_page("%s is unresolvable or invalid for %s" % (q, proto))
errors = []
infos = {}
for host in hosts.split("+"):
status, resultat = bird_proxy(host, proto, "traceroute", q)
if status is False:
errors.append("%s" % resultat)
continue
infos[host] = add_links(resultat)
return render_template('traceroute.html', infos=infos, errors=errors)
return render_template('traceroute.html', infos=infos)
@app.route("/adv/<hosts>/<proto>")
@ -402,21 +306,31 @@ def show_route_for_bgpmap(hosts, proto):
return show_route("prefix_bgpmap", hosts, proto)
ASNAME_CACHE_FILE = "/tmp/asname_cache.pickle"
ASNAME_CACHE = load_cache_pickle(ASNAME_CACHE_FILE, {})
def get_as_name(_as):
"""return a string that contain the as number following by the as name
It's the use whois database informations
# Warning, the server can be blacklisted from ripe is too many requests are done
"""
if not _as:
return "AS?????"
if not _as.isdigit():
return _as.strip()
name = get_asname_from_whois(whois_command('AS' + _as)).replace(" ","\r",1)
return "AS%s | %s" % (_as, name)
if _as not in ASNAME_CACHE:
whois_answer = whois_command("as%s" % _as)
as_name = re.search('(as-name|ASName): (.*)', whois_answer)
if as_name:
ASNAME_CACHE[_as] = as_name.group(2).strip()
else:
ASNAME_CACHE[_as] = _as
save_cache_pickle(ASNAME_CACHE_FILE, ASNAME_CACHE)
if ASNAME_CACHE[_as] == _as:
return "AS%s" % _as
else:
return "AS%s\r%s" % (_as, ASNAME_CACHE[_as])
def get_as_number_from_protocol_name(host, proto, protocol):
ret, res = bird_command(host, proto, "show protocols all %s" % protocol)
@ -431,26 +345,20 @@ def get_as_number_from_protocol_name(host, proto, protocol):
def show_bgpmap():
"""return a bgp map in a png file, from the json tree in q argument"""
data = get_query()
data = request.args.get('q', '').strip()
if not data:
abort(400)
data = json.loads(data)
data = json.loads(unquote(data))
graph = pydot.Dot('BGPMAP', graph_type='digraph')
nodes = {}
edges = {}
def escape(label):
label = label.replace("&", "&amp;")
label = label.replace(">", "&gt;")
label = label.replace("<", "&lt;")
return label
def add_node(_as, **kwargs):
if _as not in nodes:
kwargs["label"] = '<<TABLE CELLBORDER="0" BORDER="0" CELLPADDING="0" CELLSPACING="0"><TR><TD ALIGN="CENTER">' + escape(kwargs.get("label", get_as_name(_as))).replace("\r", "<BR/>") + "</TD></TR></TABLE>>"
kwargs["label"] = '<<TABLE CELLBORDER="0" BORDER="0" CELLPADDING="0" CELLSPACING="0"><TR><TD ALIGN="CENTER">' + kwargs.get("label", get_as_name(_as)).replace("\r","<BR/>") + "</TD></TR></TABLE>>"
nodes[_as] = pydot.Node(_as, style="filled", fontsize="10", **kwargs)
graph.add_node(nodes[_as])
return nodes[_as]
@ -466,19 +374,11 @@ def show_bgpmap():
edges[edge_tuple] = edge
elif "label" in kwargs and kwargs["label"]:
e = edges[edge_tuple]
label_without_star = kwargs["label"].replace("*", "")
labels = e.get_label().split("\r")
if "%s*" % label_without_star not in labels:
labels = [kwargs["label"]] + [l for l in labels if not l.startswith(label_without_star)]
labels = sorted(labels, cmp=lambda x, y: x.endswith("*") and -1 or 1)
label = escape("\r".join(labels))
e.set_label(label)
e.set_label(e.get_label() + "\r" + kwargs["label"])
return edges[edge_tuple]
for host, asmaps in data.iteritems():
add_node(host, label="%s\r%s" % (host.upper(), app.config["DOMAIN"].upper()), shape="box", fillcolor="#F5A9A9")
add_node(host, label= "%s\r%s" % (host.upper(), app.config["DOMAIN"].upper()), shape="box", fillcolor="#F5A9A9")
as_number = app.config["AS_NUMBER"].get(host, None)
if as_number:
@ -486,9 +386,9 @@ def show_bgpmap():
edge = add_edge(as_number, nodes[host])
edge.set_color("red")
edge.set_style("bold")
#colors = [ "#009e23", "#1a6ec1" , "#d05701", "#6f879f", "#939a0e", "#0e9a93", "#9a0e85", "#56d8e1" ]
# colors = [ "#009e23", "#1a6ec1" , "#d05701", "#6f879f", "#939a0e", "#0e9a93", "#9a0e85", "#56d8e1" ]
previous_as = None
hosts = data.keys()
for host, asmaps in data.iteritems():
first = True
@ -503,23 +403,18 @@ def show_bgpmap():
continue
if not hop:
if app.config.get('BIRD_HAS_FULL_VIEW', False):
hop = True
hop_label = ''
continue
elif _as not in hosts:
hop_label = _as
hop = True
if _as not in hosts:
hop_label = _as
if first:
hop_label = hop_label + "*"
continue
else:
hop_label = ""
add_node(_as, fillcolor=(first and "#F5A9A9" or "white"))
if hop_label:
edge = add_edge(nodes[previous_as], nodes[_as], label=hop_label, fontsize="7")
else:
edge = add_edge(nodes[previous_as], nodes[_as], fontsize="7")
edge = add_edge(nodes[previous_as], nodes[_as] , label=hop_label, fontsize="7")
hop_label = ""
@ -533,11 +428,10 @@ def show_bgpmap():
previous_as = _as
first = False
if previous_as:
node = add_node(previous_as)
node.set_shape("box")
node = add_node(previous_as)
node.set_shape("box")
# return Response("<pre>" + graph.create_dot() + "</pre>")
#return Response("<pre>" + graph.create_dot() + "</pre>")
return Response(graph.create_png(), mimetype='image/png')
@ -547,14 +441,10 @@ def build_as_tree_from_raw_bird_ouput(host, proto, text):
path = None
paths = []
net_dest = None
re_via = re.compile(r'(.*)via\s+([0-9a-fA-F:\.]+)\s+on.*\[(\w+)\s+')
re_unreachable = re.compile(r'(.*)unreachable\s+\[(\w+)\s+')
for line in text:
line = line.strip()
expr = re_via.search(line)
expr = re.search(r'(.*)via\s+([0-9a-fA-F:\.]+)\s+on.*\[(\w+)\s+', line)
if expr:
if path:
path.append(net_dest)
@ -574,26 +464,12 @@ def build_as_tree_from_raw_bird_ouput(host, proto, text):
break
else:
# ugly hack for good printing
path = [peer_protocol_name]
# path = ["%s\r%s" % (peer_protocol_name, get_as_name(get_as_number_from_protocol_name(host, proto, peer_protocol_name)))]
expr2 = re_unreachable.search(line)
if expr2:
if path:
path.append(net_dest)
paths.append(path)
path = None
if expr2.group(1).strip():
net_dest = expr2.group(1).strip()
path = [ peer_protocol_name ]
# path = ["%s\r%s" % (peer_protocol_name, get_as_name(get_as_number_from_protocol_name(host, proto, peer_protocol_name)))]
if line.startswith("BGP.as_path:"):
ASes = line.replace("BGP.as_path:", "").strip().split(" ")
if path:
path.extend(ASes)
else:
path = ASes
path.extend(line.replace("BGP.as_path:", "").strip().split(" "))
if path:
path.append(net_dest)
paths.append(path)
@ -601,95 +477,8 @@ def build_as_tree_from_raw_bird_ouput(host, proto, text):
return paths
def build_as_tree_from_full_view(host, proto, res):
re_chunk_start = re.compile(r'(.*)unreachable\s+\[(.*)\s+.*\s+from\s+(.*)\].*\(.*\)\s\[.*\]')
dest_subnet = None
raw = defaultdict(dict)
for line in res:
line = line.strip()
expr = re_chunk_start.search(line)
if expr:
# Beginning of the BGP reply chunk
if not dest_subnet:
dest_subnet = expr.group(1).strip()
router_tag = expr.group(2).strip()
router_ip = expr.group(3).strip()
try:
router_ip = resolve_ptr(router_ip)
except NXDOMAIN:
# If PTR record can't be found, IP will do too
pass
elif line.startswith('BGP.as_path:'):
# BGP AS path
line = line.replace('BGP.as_path:', '')
line = line.strip()
path = [router_tag, ]
for as_num in line.split(' '):
if as_num:
path.append(as_num)
path_tag = '+'.join(path[1:])
if path_tag not in raw:
raw[path_tag] = list()
raw[path_tag].append(dict(router_tag=router_tag, router_ip=router_ip, path=path))
elif line.startswith('BGP.community:'):
# BGP community
line = line.replace('BGP.community:', '')
line = line.strip()
raw[path_tag][-1]['community'] = line.split(' ')
elif line.startswith('BGP.cluster_list:'):
# BGP cluster size
line = line.replace('BGP.cluster_list:', '')
line = line.strip()
raw[path_tag][-1]['cluster_size'] = len(line.split(' '))
for path_tag in raw:
raw[path_tag] = iter(raw[path_tag])
result = defaultdict(list)
exhausted_tags = set()
existing_paths_num = len(raw)
if len(raw) > app.config.get('MAX_PATHS', 10):
max_paths = existing_paths_num
else:
max_paths = app.config.get('MAX_PATHS', 10)
path_count = 0
while path_count < max_paths:
for path_tag in sorted(raw, key=lambda x: x.count('+')):
if path_tag in exhausted_tags:
continue
try:
path = next(raw[path_tag])
except StopIteration:
exhausted_tags.add(path_tag)
continue
result[path['router_ip']].append(path['path'])
result[path['router_ip']][-1].append(dest_subnet)
path_count += 1
if path_count == max_paths:
break
if path_count == max_paths or len(exhausted_tags) == existing_paths_num:
break
return result
def show_route(request_type, hosts, proto):
expression = get_query()
expression = unquote(request.args.get('q', '')).strip()
if not expression:
abort(400)
@ -709,7 +498,7 @@ def show_route(request_type, hosts, proto):
command = "show route where net ~ [ " + expression + " ]" + all
else:
mask = ""
if len(expression.split("/")) == 2:
if len(expression.split("/")) > 1:
expression, mask = (expression.split("/"))
if not mask and proto == "ipv4":
@ -736,32 +525,26 @@ def show_route(request_type, hosts, proto):
command = "show route for " + expression + all
detail = {}
errors = []
error = []
for host in hosts.split("+"):
ret, res = bird_command(host, proto, command)
res = res.split("\n")
if ret is False:
errors.append("%s" % res)
continue
if len(res) <= 1:
errors.append("%s: bird command failed with error, %s" % (host, "\n".join(res)))
continue
if bgpmap:
if app.config.get('BIRD_HAS_FULL_VIEW', False):
detail = build_as_tree_from_full_view(host, proto, res)
else:
if len(res) > 1:
if bgpmap:
detail[host] = build_as_tree_from_raw_bird_ouput(host, proto, res)
else:
detail[host] = add_links(res)
else:
detail[host] = add_links(res)
error.append("%s: bird command failed with error, %s" % (host, "\n".join(res)))
if bgpmap:
detail = json.dumps(detail)
return render_template((bgpmap and 'bgpmap.html' or 'route.html'), detail=detail, command=command, expression=expression, errors=errors)
return render_template((bgpmap and 'bgpmap.html' or 'route.html'), detail=detail, command=command, expression=expression, error="<br>".join(error))
app.secret_key = app.config["SESSION_KEY"]
app.debug = True
if __name__ == "__main__":
app.run(app.config.get("BIND_IP", "0.0.0.0"), app.config.get("BIND_PORT", 5000))
app.run("0.0.0.0")

View file

@ -1,8 +1,5 @@
import sys
import os
sitepath = os.path.realpath(os.path.dirname(__file__))
sys.path.insert(0, sitepath)
sys.path.insert(0,"/var/www/lg2.tetaneutral.net/")
from lg import app as application

View file

@ -1,22 +0,0 @@
# Configuration file for lgproxy.py
# Copy to lgproxy.cfg and edit to suit your needs.
DEBUG = False
LOG_FILE = "/var/log/bird-lg/lgproxy.log"
LOG_LEVEL = "WARNING"
# Which IP/port to listen to for the frontend
BIND_IP = "0.0.0.0"
BIND_PORT = 5000
# Who can connect to the proxy to launch Bird commands
ACCESS_LIST = ["192.0.2.42", "2001:db8::42"]
# Location of Bird control sockets, example for Debian > wheezy
#BIRD_SOCKET = "/run/bird/bird.ctl"
#BIRD6_SOCKET = "/run/bird/bird6.ctl"
# Source IP for traceroute/traceroute6
IPV4_SOURCE = "192.0.2.1"
IPV6_SOURCE = "2001:db8::1"

View file

@ -1,158 +0,0 @@
# -*- coding: utf-8 -*-
# vim: ts=4
###
#
# Copyright (c) 2006 Mehdi Abaakouk
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#
###
import sys
import logging
from logging.handlers import TimedRotatingFileHandler
from logging import FileHandler
import subprocess
from urllib import unquote
from bird import BirdSocket
from flask import Flask, request, abort
app = Flask(__name__)
app.debug = app.config["DEBUG"]
app.config.from_pyfile('lgproxy.cfg')
file_handler = TimedRotatingFileHandler(filename=app.config["LOG_FILE"], when="midnight")
app.logger.setLevel(getattr(logging, app.config["LOG_LEVEL"].upper()))
app.logger.addHandler(file_handler)
@app.before_request
def access_log_before(*args, **kwargs):
app.logger.info("[%s] request %s, %s", request.remote_addr, request.url, "|".join(["%s:%s" % (k, v) for k, v in request.headers.items()]))
@app.after_request
def access_log_after(response, *args, **kwargs):
app.logger.info("[%s] reponse %s, %s", request.remote_addr, request.url, response.status_code)
return response
def check_accesslist():
acl = app.config["ACCESS_LIST"]
if acl and request.remote_addr not in acl:
app.logger.warning("Remote address not in ACCESS_LIST: %s", request.remote_addr)
abort(401)
def check_features():
features = app.config.get('FEATURES', [])
if features and request.endpoint not in features:
app.logger.warning("Requested endpoint not in FEATURES: %s", request.endpoint)
abort(401)
@app.route("/ping")
@app.route("/ping6")
def ping():
check_accesslist()
check_features()
src = []
if request.path == '/ping':
ping = 'ping'
if app.config.get("IPV4_SOURCE", ""):
src = ["-I", app.config.get("IPV4_SOURCE")]
else:
ping = 'ping6'
if app.config.get("IPV6_SOURCE", ""):
src = ["-I", app.config.get("IPV6_SOURCE")]
query = request.args.get("q", "")
query = unquote(query)
options = ['-c4', '-i1', '-w5']
command = [ping] + src + options + [query]
result = subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0].decode('utf-8', 'ignore').replace("\n", "<br>")
return result
@app.route("/traceroute")
@app.route("/traceroute6")
def traceroute():
check_accesslist()
check_features()
if sys.platform.startswith('freebsd') or sys.platform.startswith('netbsd') or sys.platform.startswith('openbsd'):
traceroute4 = ['traceroute']
traceroute6 = ['traceroute6']
else: # For Linux
traceroute4 = ['traceroute', '-4']
traceroute6 = ['traceroute', '-6']
src = []
if request.path == '/traceroute6':
traceroute = traceroute6
if app.config.get("IPV6_SOURCE", ""):
src = ["-s", app.config.get("IPV6_SOURCE")]
else:
traceroute = traceroute4
if app.config.get("IPV4_SOURCE", ""):
src = ["-s", app.config.get("IPV4_SOURCE")]
query = request.args.get("q", "")
query = unquote(query)
if sys.platform.startswith('freebsd') or sys.platform.startswith('netbsd'):
options = ['-a', '-q1', '-w1', '-m15']
elif sys.platform.startswith('openbsd'):
options = ['-A', '-q1', '-w1', '-m15']
else: # For Linux
options = ['-A', '-q1', '-N32', '-w1', '-m15']
command = traceroute + src + options + [query]
result = subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0].decode('utf-8', 'ignore').replace("\n", "<br>")
return result
@app.route("/bird")
@app.route("/bird6")
def bird():
check_accesslist()
check_features()
if request.path == "/bird":
b = BirdSocket(file=app.config.get('BIRD_SOCKET'))
elif request.path == "/bird6":
b = BirdSocket(file=app.config.get('BIRD6_SOCKET'))
else:
return "No bird socket selected"
query = request.args.get("q", "")
query = unquote(query)
status, result = b.cmd(query)
b.close()
# FIXME: use status
return result
if __name__ == "__main__":
app.logger.info("lgproxy start")
app.run(app.config.get("BIND_IP", "0.0.0.0"), app.config.get("BIND_PORT", 5000))

View file

@ -1,8 +0,0 @@
import sys
import os
sitepath = os.path.realpath(os.path.dirname(__file__))
sys.path.insert(0, sitepath)
from lgproxy import app as application

View file

@ -1,47 +0,0 @@
div.dataTables_length label {
float: left;
text-align: left;
}
div.dataTables_length select {
width: 75px;
}
div.dataTables_filter label {
float: right;
}
div.dataTables_info {
padding-top: 8px;
}
div.dataTables_paginate {
float: right;
margin: 0;
}
table.table {
clear: both;
margin-bottom: 6px !important;
}
table.table thead .sorting,
table.table thead .sorting_asc,
table.table thead .sorting_desc,
table.table thead .sorting_asc_disabled,
table.table thead .sorting_desc_disabled {
cursor: pointer;
*cursor: hand;
}
table.table thead .sorting { background: url('../img/sort_both.png') no-repeat center right; }
table.table thead .sorting_asc { background: url('../img/sort_asc.png') no-repeat center right; }
table.table thead .sorting_desc { background: url('../img/sort_desc.png') no-repeat center right; }
table.table thead .sorting_asc_disabled { background: url('../img/sort_asc_disabled.png') no-repeat center right; }
table.table thead .sorting_desc_disabled { background: url('../img/sort_desc_disabled.png') no-repeat center right; }
table.dataTable th:active {
outline: none;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

View file

@ -1,96 +0,0 @@
/* Default class modification */
$.extend( $.fn.dataTableExt.oStdClasses, {
"sWrapper": "dataTables_wrapper form-inline"
} );
/* API method to get paging information */
$.fn.dataTableExt.oApi.fnPagingInfo = function ( oSettings )
{
return {
"iStart": oSettings._iDisplayStart,
"iEnd": oSettings.fnDisplayEnd(),
"iLength": oSettings._iDisplayLength,
"iTotal": oSettings.fnRecordsTotal(),
"iFilteredTotal": oSettings.fnRecordsDisplay(),
"iPage": Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ),
"iTotalPages": Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength )
};
}
/* Bootstrap style pagination control */
$.extend( $.fn.dataTableExt.oPagination, {
"bootstrap": {
"fnInit": function( oSettings, nPaging, fnDraw ) {
var oLang = oSettings.oLanguage.oPaginate;
var fnClickHandler = function ( e ) {
e.preventDefault();
if ( oSettings.oApi._fnPageChange(oSettings, e.data.action) ) {
fnDraw( oSettings );
}
};
$(nPaging).addClass('pagination').append(
'<ul>'+
'<li class="prev disabled"><a href="#">&larr; '+oLang.sPrevious+'</a></li>'+
'<li class="next disabled"><a href="#">'+oLang.sNext+' &rarr; </a></li>'+
'</ul>'
);
var els = $('a', nPaging);
$(els[0]).bind( 'click.DT', { action: "previous" }, fnClickHandler );
$(els[1]).bind( 'click.DT', { action: "next" }, fnClickHandler );
},
"fnUpdate": function ( oSettings, fnDraw ) {
var iListLength = 5;
var oPaging = oSettings.oInstance.fnPagingInfo();
var an = oSettings.aanFeatures.p;
var i, j, sClass, iStart, iEnd, iHalf=Math.floor(iListLength/2);
if ( oPaging.iTotalPages < iListLength) {
iStart = 1;
iEnd = oPaging.iTotalPages;
}
else if ( oPaging.iPage <= iHalf ) {
iStart = 1;
iEnd = iListLength;
} else if ( oPaging.iPage >= (oPaging.iTotalPages-iHalf) ) {
iStart = oPaging.iTotalPages - iListLength + 1;
iEnd = oPaging.iTotalPages;
} else {
iStart = oPaging.iPage - iHalf + 1;
iEnd = iStart + iListLength - 1;
}
for ( i=0, iLen=an.length ; i<iLen ; i++ ) {
// Remove the middle elements
$('li:gt(0)', an[i]).filter(':not(:last)').remove();
// Add the new list items and their event handlers
for ( j=iStart ; j<=iEnd ; j++ ) {
sClass = (j==oPaging.iPage+1) ? 'class="active"' : '';
$('<li '+sClass+'><a href="#">'+j+'</a></li>')
.insertBefore( $('li:last', an[i])[0] )
.bind('click', function (e) {
e.preventDefault();
oSettings._iDisplayStart = (parseInt($('a', this).text(),10)-1) * oPaging.iLength;
fnDraw( oSettings );
} );
}
// Add / remove disabled classes from the static elements
if ( oPaging.iPage === 0 ) {
$('li:first', an[i]).addClass('disabled');
} else {
$('li:first', an[i]).removeClass('disabled');
}
if ( oPaging.iPage === oPaging.iTotalPages-1 || oPaging.iTotalPages === 0 ) {
$('li:last', an[i]).addClass('disabled');
} else {
$('li:last', an[i]).removeClass('disabled');
}
}
}
}
} );

File diff suppressed because it is too large Load diff

View file

@ -1,112 +1,99 @@
$(window).unload(function() {
$(".progress").show()
});
function change_url(loc) {
$(".progress").show(0, function() {
$(window).unload(function(){
$(".progress").show()
});
function change_url(loc){
$(".progress").show(0, function(){
document.location = loc;
});
}
function reload() {
function reload(){
loc = "/" + request_type + "/" + hosts + "/" + proto;
if (request_type != "summary") {
if(request_args != undefined && request_args != "") {
loc = loc + "?q=" + escape(request_args);
if (request_type != "summary" ){
if( request_args != undefined && request_args != ""){
loc = loc + "?q=" + request_args;
change_url(loc)
}
} else {
change_url(loc)
}
}
function update_view() {
function update_view(){
if (request_type == "summary")
$(".navbar-search").hide();
else
$(".navbar-search").show();
$(".navbar li").removeClass('active');
$(".proto a#"+proto).parent().addClass('active');
$(".hosts a[id='"+hosts+"']").parent().addClass('active');
$(".request_type a#"+request_type).parent().addClass('active');
command = $(".request_type a#"+request_type).text().split("...");
$(".request_type a:first").html(command[0] + '<b class="caret"></b>');
if (command[1] != undefined)
if (command[1] != undefined ) {
$(".navbar li:last").html("&nbsp;&nbsp;"+command[1]);
else
} else {
$(".navbar li:last").html("");
}
request_args = $(".request_args").val();
$(".request_args").focus();
$(".request_args").select();
}
$(function() {
$(".history a").click(function (event) {
event.preventDefault();
change_url(this.href)
});
$(".modal .modal-footer .btn").click(function() {
$(".modal").modal('hide');
});
$("a.whois").click(function (event) {
event.preventDefault();
link = $(this).attr('href');
$.getJSON(link, function(data) {
$(".modal h3").html(data.title);
$(".modal .modal-body > p").html(data.output);
$(".modal").modal('show');
$(function(){
$(".history a").click(function (event){
event.preventDefault();
change_url(this.href)
});
});
$(".history a").click(function () {
$(".history li").removeClass("active")
$(this).parent().addClass("active")
});
$(".hosts a").click(function() {
hosts = $(this).attr('id');
update_view();
reload();
});
$(".proto a").click(function() {
proto = $(this).attr('id');
update_view();
reload();
});
$(".request_type ul a").click(function() {
if (request_type.split("_")[0] != $(this).attr('id').split("_")[0]) {
request_args = ""
$(".request_args").val("");
}
request_type = $(this).attr('id');
update_view();
reload();
});
$("form").submit(function() {
update_view();
reload();
});
$('.request_args').val(request_args);
update_view();
t = $('.table-summary')
if (t)
t.dataTable({
"bPaginate": false,
$(".modal .modal-footer .btn").click(function(){
$(".modal").modal('hide');
});
$("a.whois").click(function (event){
event.preventDefault();
link = $(this).attr('href');
$.getJSON(link, function(data) {
$(".modal h3").html(data.title);
$(".modal .modal-body > p").html(data.output);
$(".modal").modal('show');
});
});
$(".history a").click(function (){
$(".history li").removeClass("active")
$(this).parent().addClass("active")
});
$(".hosts a").click(function(){
hosts = $(this).attr('id');
update_view();
reload();
});
$(".proto a").click(function(){
proto = $(this).attr('id');
update_view();
reload();
});
$(".request_type ul a").click(function(){
if ( request_type.split("_")[0] != $(this).attr('id').split("_")[0] ){
request_args = ""
$(".request_args").val("");
}
request_type = $(this).attr('id');
update_view();
reload();
});
$("form").submit(function(){
update_view();
reload();
});
$('.request_args').val(request_args);
update_view();
});

View file

@ -1,12 +1,11 @@
<!doctype html>
<html lang="en">
<title>{{config.DOMAIN|capitalize}} looking glass</title>
<title>Tetaneutral.net looking glass</title>
<head>
<meta charset="UTF-8">
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/bootstrap-responsive.min.css') }}">
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/docs.css') }}">
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/DT_bootstrap.css') }}">
</head>
<body>
<div class="navbar navbar-fixed-top">
@ -64,15 +63,11 @@
<div class="container-fluid">
<div class="row-fluid">
<div class="span8">
{% if warnings %}
<div class="alert alert-warning">
{% for warning in warnings %}{{warning}}<br />{% endfor %}
</div>
{% if warning %}
<div class="alert alert-warning">{{warning}}</div>
{% endif %}
{% if errors %}
<div class="alert alert-error">
{% for error in errors %}{{error}}<br />{% endfor %}
</div>
{% if error %}
<div class="alert alert-error">{{error}}</div>
{% endif %}
{% block body %}{% endblock %}
@ -116,11 +111,10 @@
</div>
<script type="text/javascript" src="{{url_for('static', filename='js/jquery.js') }}"></script>
<script type="text/javascript" src="{{url_for('static', filename='js/bootstrap.min.js') }}"></script>
<script type="text/javascript" src="{{url_for('static', filename='js/jquery.dataTables.js') }}"></script>
<script type="text/javascript" src="{{url_for('static', filename='js/DT_bootstrap.js') }}"></script>
<script type="text/javascript" src="{{url_for('static', filename='js/jquery-impromptu.3.2.min.js') }}"></script>
<script type="text/javascript">
request_type = "{{session.request_type}}";
request_args = "{{session.request_args|safe}}";
request_args = "{{session.request_args}}";
hosts = "{{session.hosts}}";
proto = "{{session.proto}}";
history_query = {{session.history|tojson|safe}};

View file

@ -1,10 +0,0 @@
{% extends "layout.html" %}
{% block body %}
{% for host in infos %}
<h3 id="ping_cmd_{{host}}">{{host}}/{{session.proto}}: ping {{session.request_args}}</h3><br />
{% if infos[host]|trim %}
<pre>{{infos[host]|trim|safe}}</pre>
{% endif %}
<br />
{% endfor %}
{% endblock %}

View file

@ -1,21 +1,14 @@
{% extends "layout.html" %}
{% block body %}
{% for host in summary %}
<h3 style="float:left">{{host}}: {{command}}</h3>
<table class="table table-striped table-bordered table-condensed table-summary">
<h3>{{host}}: {{command}}</h3><br />
<table class="table table-striped table-bordered table-condensed">
<thead>
<tr><th>Name</th><th>protocol</th><th>table</th><th>state</th><th>since</th><th>info</th></tr>
</thead>
<tbody>
{% for row in summary[host] %}
<tr class="{{ loop.cycle('odd', 'even') }}">
<td><a href="/detail/{{host}}/{{session.proto}}?q={{row.name}}">{{row.name}}</a></td>
<td>{{row.proto}}</td>
<td>{{row.table}}</td>
<td><span class="label label-{% if row.state == "up" %}success{% elif row.state == "down" %}default{% else %}important{% endif %}">{{row.state}}</span></td>
<td>{{row.since}}</td>
<td>{{row.info}}</td>
</tr>
<tr class="{{ loop.cycle('odd', 'even') }}"><td><a href="/detail/{{host}}/{{session.proto}}?q={{row.name}}">{{row.name}}</a></td><td>{{row.proto}}</td><td>{{row.table}}</td><td>{{row.state}}</td><td>{{row.since}}</td><td>{{row.info}}</td></tr>
{% else %}
<tr><td>{{summary[host].error}}</td><td></td><td></td><td></td><td></td><td></td></tr>
{% endfor %}

View file

@ -19,51 +19,22 @@
#
###
from dns import resolver, reversename
from dns import resolver
import socket
import pickle
import xml.parsers.expat
import re
from flask import Flask
resolv = resolver.Resolver()
resolv.timeout = 0.5
resolv.lifetime = 1
app = Flask(__name__)
app.config.from_pyfile('lg.cfg')
def resolve(n, q):
return str(resolv.query(n, q)[0])
def resolve_ptr(ip):
ptr = str(resolve(reversename.from_address(ip), 'PTR')).lower()
ptr = ptr.replace(app.config.get('ROUTER_NAME_REMOVE', ''), '')
return ptr
asname_regex = re.compile("(ASName|as-name):\s+(?P<name>\S+)")
def get_asname_from_whois(data):
r = asname_regex.search(data)
if not r:
return 'UNKNOWN-AS'
return r.groupdict()['name']
return str(resolver.query(n,q)[0])
def mask_is_valid(n):
if not n:
return True
try:
mask = int(n)
return (mask >= 1 and mask <= 128)
except:
return False
if not n:
return True
try:
mask = int(n)
return ( mask >= 1 and mask <= 128)
except:
return False
def ipv4_is_valid(n):
try:
@ -72,7 +43,6 @@ def ipv4_is_valid(n):
except socket.error:
return False
def ipv6_is_valid(n):
try:
socket.inet_pton(socket.AF_INET6, n)
@ -80,49 +50,20 @@ def ipv6_is_valid(n):
except socket.error:
return False
def save_cache_pickle(filename, data):
output = open(filename, 'wb')
pickle.dump(data, output)
output.close()
output = open(filename, 'wb')
pickle.dump(data, output)
output.close()
def load_cache_pickle(filename, default = None):
try:
pkl_file = open(filename, 'rb')
except IOError:
return default
try:
data = pickle.load(pkl_file)
except:
data = default
pkl_file.close()
return data
def load_cache_pickle(filename, default=None):
try:
pkl_file = open(filename, 'rb')
except IOError:
return default
try:
data = pickle.load(pkl_file)
except:
data = default
pkl_file.close()
return data
def unescape(s):
want_unicode = False
if isinstance(s, unicode):
s = s.encode("utf-8")
want_unicode = True
# the rest of this assumes that `s` is UTF-8
list = []
# create and initialize a parser object
p = xml.parsers.expat.ParserCreate("utf-8")
p.buffer_text = True
p.returns_unicode = want_unicode
p.CharacterDataHandler = list.append
# parse the data wrapped in a dummy element
# (needed so the "document" is well-formed)
p.Parse("<e>", 0)
p.Parse(s, 0)
p.Parse("</e>", 1)
# join the extracted strings and return
es = ""
if want_unicode:
es = u""
return es.join(list)