|
@@ -8,6 +8,7 @@ import cgi
|
|
|
from storage import Storage
|
|
|
import json
|
|
|
import logging
|
|
|
+import pygeoip
|
|
|
import re
|
|
|
import socket
|
|
|
from SocketServer import ThreadingMixIn
|
|
@@ -57,6 +58,11 @@ class BatcaveHttpRequestHandler(BaseHTTPRequestHandler):
|
|
|
self.respond_vpn(query)
|
|
|
return
|
|
|
|
|
|
+ # /providers
|
|
|
+ if path == 'providers':
|
|
|
+ self.respond_providers(query)
|
|
|
+ return
|
|
|
+
|
|
|
# /node/<id>.json - node's data
|
|
|
# /node/<id>/field - return specific field from node's data
|
|
|
m = re.match(r'node/([a-f0-9]{12})(?P<cmd>\.json|/[a-zA-Z0-9_\-]+)$', path)
|
|
@@ -367,6 +373,75 @@ class BatcaveHttpRequestHandler(BaseHTTPRequestHandler):
|
|
|
self.wfile.write('</body>')
|
|
|
self.wfile.write('</html>')
|
|
|
|
|
|
+ def respond_providers(self, query):
|
|
|
+ """Return a summary of providers."""
|
|
|
+
|
|
|
+ vpn = self.server.storage.data[self.DATAKEY_VPN]
|
|
|
+ outputformat = query['format'].lower() if 'format' in query else 'html'
|
|
|
+
|
|
|
+ geo = None
|
|
|
+ try:
|
|
|
+ geo = pygeoip.GeoIP('GeoIPISP.dat')
|
|
|
+ except:
|
|
|
+ self.return_error(500, 'The GeoIP-ISP database file could not be opened.')
|
|
|
+ return
|
|
|
+
|
|
|
+ isps = {}
|
|
|
+ for key in vpn:
|
|
|
+ item = vpn[key]
|
|
|
+ if not 'active' in item: continue
|
|
|
+
|
|
|
+ ips = set()
|
|
|
+ for gw in item['active']:
|
|
|
+ if 'remote' in item['active'][gw]:
|
|
|
+ ips.add(item['active'][gw]['remote'])
|
|
|
+
|
|
|
+ if len(ips) == 0: continue
|
|
|
+ if len(ips) > 1:
|
|
|
+ self.logger.warn('VPN key \'{0}\' has {1} active ips. Possible cause is an in-progress re-dialin.'.format(key, len(ips)))
|
|
|
+
|
|
|
+ item_isps = set()
|
|
|
+ for ip in ips:
|
|
|
+ try:
|
|
|
+ isp = geo.org_by_addr(ip)
|
|
|
+ item_isps.add(isp)
|
|
|
+ except Exception as err:
|
|
|
+ self.logger.debug('Failed to resolve ISP for \'{0}\': {1}'.format(ip, str(err)))
|
|
|
+
|
|
|
+ if len(item_isps) == 0:
|
|
|
+ item_isps.add('unknown')
|
|
|
+
|
|
|
+ elif len(item_isps) > 1:
|
|
|
+ self.logger.warn('VPN key \'{0}\' has {1} active IPs which resolved to {2} ISPs: \'{3}\''.format(key, len(ips), len(item_isps), '\', \''.join(item_isps)))
|
|
|
+
|
|
|
+ for isp in item_isps:
|
|
|
+ if not isp in isps: isps[isp] = 0
|
|
|
+ isps[isp] += 1
|
|
|
+
|
|
|
+ isps_sum = sum([isps[x] for x in isps])
|
|
|
+
|
|
|
+ if outputformat == 'csv':
|
|
|
+ self.send_headers('text/csv')
|
|
|
+
|
|
|
+ self.wfile.write('Count;Name\n')
|
|
|
+ for isp in isps:
|
|
|
+ self.wfile.write('{0};"{1}"\n'.format(isps[isp], isp))
|
|
|
+
|
|
|
+ elif outputformat == 'html':
|
|
|
+ self.send_headers()
|
|
|
+
|
|
|
+ self.wfile.write('<!DOCTYPE html><html><head><title>BATCAVE - PROVIDERS</title></head><body>\n')
|
|
|
+ self.wfile.write('<table border="2"><thead><tr><th>Count</th><th>Percentage</th><th>Name</th></tr></thead><tbody>\n')
|
|
|
+
|
|
|
+ for isp in isps:
|
|
|
+ self.wfile.write('<tr><td>{0}</td><td>{1:.1f}%</td><td>{2}</td></tr>\n'.format(isps[isp], isps[isp]*100.0/isps_sum, isp))
|
|
|
+
|
|
|
+ self.wfile.write('</tbody></table>\n')
|
|
|
+ self.wfile.write('</body></html>')
|
|
|
+
|
|
|
+ else:
|
|
|
+ self.send_error(400, 'Unknown output format.')
|
|
|
+
|
|
|
class ApiServer(ThreadingMixIn, HTTPServer):
|
|
|
def __init__(self, endpoint, storage):
|
|
|
if ':' in endpoint[0]: self.address_family = socket.AF_INET6
|