wip
[hippotat] / server
CommitLineData
3fba9787
IJ
1#!/usr/bin/python2
2
3from twisted.web.server import Site
4from twisted.web.resource import Resource
5from twisted.web.server import NOT_DONE_YET
6from twisted.internet import reactor
7
8import ConfigParser
ec88b1f1 9import ipaddress
3fba9787 10
c4b6d990
IJ
11import syslog
12
3fba9787
IJ
13clients = { }
14
15def ipaddress(input):
16 try:
ec88b1f1 17 r = ipaddress.IPv4Address(input)
3fba9787 18 except AddressValueError:
ec88b1f1 19 r = ipaddress.IPv6Address(input)
3fba9787
IJ
20 return r
21
22def ipnetwork(input):
23 try:
ec88b1f1 24 r = ipaddress.IPv4Network(input)
3fba9787 25 except NetworkValueError:
ec88b1f1 26 r = ipaddress.IPv6Network(input)
3fba9787
IJ
27 return r
28
ec88b1f1
IJ
29defcfg = u'''
30[default]
31max_batch_down: 65536
32max_queue_time: 10
33max_request_time: 54
34
35[global]
36max_batch_down: 262144
37max_queue_time: 121
38max_request_time: 121
39'''
40
c4b6d990
IJ
41def route(packet. daddr):
42 try: client = clients[daddr]
43 except KeyError: dclient = None
44 if dclient is not None:
45 dclient.queue_outbound_data(packet)
46 else if daddr = server or daddr not in network:
47 queue_inbound_data(packet)
48 else:
49 syslog.syslog(syslog.LOG_DEBUG, 'no client for %s' % daddr)
50
ec88b1f1 51class Client():
c4b6d990 52 def __init__(self, ip, cs):
ec88b1f1
IJ
53 # instance data members
54 self._ip = ip
55 self._cs = cs
56 self.pw = cfg.get(cs, 'password')
c4b6d990
IJ
57 # plus from config:
58 # .max_batch_down
59 # .max_queue_time
60 # .max_request_time
ec88b1f1
IJ
61 for k in ('max_batch_down','max_queue_time','max_request_time'):
62 req = cfg.getint(cs, k)
63 limit = cfg.getint('global',k)
c4b6d990
IJ
64 self.__dict__[k] = min(req, limit)
65
66 def process_arriving_data(self, d):
67 for packet in slip_decode(d):
68 (saddr, daddr) = ip_64_addrs(packet)
69 if saddr != self._ip:
70 raise ValueError('wrong source address %s' % saddr)
71 route(packet, daddr)
ec88b1f1 72
c4b6d990
IJ
73 def _req_cancel(self, request):
74 request.finish()
75
76 def _req_error(self, err, request):
77 self._req_cancel(request)
78
79 def http_request(self, request):
80 request.setHeader('Content-Type','application/octet-stream')
81 reactor.callLater(self.max_request_time, self._req_cancel, request)
82 request.notifyFinish().addErrback(self._req_error, request)
ec88b1f1 83
3fba9787
IJ
84def process_cfg():
85 global network
86 global ourself
87
ec88b1f1 88 network = ipnetwork(cfg.get('virtual','network'))
3fba9787
IJ
89 try:
90 ourself = cfg.get('virtual','server')
91 except ConfigParser.NoOptionError:
92 ourself = network.hosts().next()
93
ec88b1f1
IJ
94 for cs in cfg.sections():
95 if not (':' in cs or '.' in cs): continue
96 ci = ipaddress(cs)
97 if ci not in network:
98 raise ValueError('client %s not in network' % ci)
99 if ci in clients:
100 raise ValueError('multiple client cfg sections for %s' % ci)
101 clients[ci] = Client(ci, cs)
3fba9787
IJ
102
103class FormPage(Resource):
104 def render_POST(self, request):
ec88b1f1
IJ
105 # find client, update config, etc.
106 ci = ipaddress(request.args['i'])
107 c = clients[ci]
108 pw = request.args['pw']
109 if pw != c.pw: raise ValueError('bad password')
110
111 # update config
112 for r, w in (('mbd', 'max_batch_down'),
113 ('mqt', 'max_queue_time'),
114 ('mrt', 'max_request_time')):
115 try: v = request.args[r]
116 except KeyError: continue
117 v = int(v)
c4b6d990 118 c.__dict__[w] = v
ec88b1f1
IJ
119
120 try: d = request.args['d']
121 except KeyError: d = ''
122
123 c.process_arriving_data(d)
c4b6d990 124 c.new_request(request)