|
@@ -9,6 +9,7 @@ import json
|
|
|
import logging
|
|
|
import re
|
|
|
from SocketServer import ThreadingMixIn
|
|
|
+import time
|
|
|
|
|
|
class BatcaveHttpRequestHandler(BaseHTTPRequestHandler):
|
|
|
def __init__(self, request, client_address, server):
|
|
@@ -39,6 +40,10 @@ class BatcaveHttpRequestHandler(BaseHTTPRequestHandler):
|
|
|
self.respond_list(query)
|
|
|
return
|
|
|
|
|
|
+ if path == 'vpn':
|
|
|
+ self.respond_vpn(query)
|
|
|
+ return
|
|
|
+
|
|
|
m = re.match(r'node/([a-f0-9]{12})(?P<cmd>\.json|/[a-zA-Z0-9_\-]+)$', path)
|
|
|
if m != None:
|
|
|
cmd = m.group('cmd')
|
|
@@ -149,6 +154,77 @@ class BatcaveHttpRequestHandler(BaseHTTPRequestHandler):
|
|
|
self.send_headers('text/plain' if isinstance(value, basestring) else 'text/json')
|
|
|
self.wfile.write(value if isinstance(value, basestring) else json.dumps(value))
|
|
|
|
|
|
+ def respond_vpn(self, query):
|
|
|
+ storage = self.server.storage
|
|
|
+ peername = query['peer'] if 'peer' in query else None
|
|
|
+ key = query['key'] if 'key' in query else None
|
|
|
+ action = query['action'] if 'action' in query else None
|
|
|
+ remote = query['remote'] if 'remote' in query else None
|
|
|
+ gw = query['gw'] if 'gw' in query else None
|
|
|
+
|
|
|
+ if action == 'list':
|
|
|
+ self.respond_vpnlist()
|
|
|
+ return
|
|
|
+
|
|
|
+ if action != 'establish' and action != 'disestablish':
|
|
|
+ self.logger.error('VPN: unknown action \'{0}\''.format(action))
|
|
|
+ self.send_error(400, 'Invalid action.')
|
|
|
+ return
|
|
|
+
|
|
|
+ for k,v in { 'peername': peername, 'key': key, 'remote': remote, 'gw': gw }.items():
|
|
|
+ if v is None or len(v.strip()) == 0:
|
|
|
+ self.logger.error('VPN {0}: no or empty {1}'.format(action, k))
|
|
|
+ self.send_error(400, 'Missing value for ' + str(k))
|
|
|
+ return
|
|
|
+
|
|
|
+ if key is None or re.match(r'^[a-fA-F0-9]+$', key) is None:
|
|
|
+ self.logger.error('VPN peer \'{0}\' {1}: bad key \'{2}\''.format(peername, action, key))
|
|
|
+ self.send_error(400, 'Bad key.')
|
|
|
+ return
|
|
|
+
|
|
|
+ if not 'vpn' in storage.data: storage.data['vpn'] = {}
|
|
|
+ if not key in storage.data['vpn']: storage.data['vpn'][key] = { 'active': {}, 'last': {} }
|
|
|
+ item = storage.data['vpn'][key]
|
|
|
+
|
|
|
+ if action == 'establish':
|
|
|
+ item['active'][gw] = { 'establish': time.time(), 'peer': peername, 'remote': remote }
|
|
|
+ elif action == 'disestablish':
|
|
|
+ active = {}
|
|
|
+ if gw in item['active']:
|
|
|
+ active = item['active'][gw]
|
|
|
+ del(item['active'][gw])
|
|
|
+ active['disestablish'] = time.time()
|
|
|
+ item['last'][gw] = active
|
|
|
+ else:
|
|
|
+ self.send_error(500, 'Unknown action not filtered (' + str(action) + ')')
|
|
|
+ return
|
|
|
+
|
|
|
+ self.send_headers('text/plain')
|
|
|
+ self.wfile.write('OK')
|
|
|
+
|
|
|
+ storage.save()
|
|
|
+
|
|
|
+ def respond_vpnlist(self):
|
|
|
+ storage = self.server.storage
|
|
|
+
|
|
|
+ self.send_headers()
|
|
|
+ self.wfile.write('<!DOCTYPE html>\n')
|
|
|
+ self.wfile.write('<html><head><title>BATCAVE - VPN LIST</title></head>\n')
|
|
|
+ self.wfile.write('<body>\n')
|
|
|
+ self.wfile.write('<table>\n<thead><tr><th>key</th><th>active</th><th>last</th></tr></thead>\n')
|
|
|
+
|
|
|
+ if 'vpn' in storage.data:
|
|
|
+ for key in storage.data['vpn']:
|
|
|
+ item = storage.data['vpn'][key]
|
|
|
+ self.wfile.write('<tr><td>' + str(key) + '</td>')
|
|
|
+ self.wfile.write('<td>' + json.dumps(item['active'] if 'active' in item else {}) + '</td>')
|
|
|
+ self.wfile.write('<td>' + json.dumps(item['last'] if 'last' in item else {}) + '</td>')
|
|
|
+ self.wfile.write('</tr>\n')
|
|
|
+
|
|
|
+ self.wfile.write('</table>\n')
|
|
|
+ self.wfile.write('</body>')
|
|
|
+ self.wfile.write('</html>')
|
|
|
+
|
|
|
class ApiServer(ThreadingMixIn, HTTPServer):
|
|
|
def __init__(self, endpoint, storage):
|
|
|
HTTPServer.__init__(self, endpoint, BatcaveHttpRequestHandler)
|