Browse Source

server: add GET /providers (optional ?format=csv)

Returns a list of providers from the connected VPN peers.
Helge Jung 9 years ago
parent
commit
936f6690a6
2 changed files with 76 additions and 0 deletions
  1. 1 0
      .gitignore
  2. 75 0
      ffstatus/server.py

+ 1 - 0
.gitignore

@@ -7,3 +7,4 @@ storage.dat
 # dummy scripts + datafiles
 alfred*json
 batadv-vis*
+GeoIPISP.dat

+ 75 - 0
ffstatus/server.py

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