Commit | Line | Data |
---|---|---|
094ee3a2 | 1 | #!/usr/bin/python3 |
3fba9787 | 2 | |
37ab4cdc | 3 | from hippotat import * |
aa663282 | 4 | |
e2d41dc1 IJ |
5 | import os |
6 | ||
e2d41dc1 | 7 | import twisted.internet |
e2d41dc1 | 8 | from twisted.web.server import NOT_DONE_YET |
e2d41dc1 | 9 | |
5da7763e IJ |
10 | #import twisted.web.server import Site |
11 | #from twisted.web.resource import Resource | |
3fba9787 | 12 | |
c4b6d990 IJ |
13 | import syslog |
14 | ||
b0cfbfce | 15 | clients = { } |
3fba9787 | 16 | |
5da7763e IJ |
17 | #---------- "router" ---------- |
18 | ||
ec0c4d95 IJ |
19 | def route(packet, saddr, daddr): |
20 | print('TRACE ', saddr, daddr, packet) | |
5da7763e IJ |
21 | try: client = clients[daddr] |
22 | except KeyError: dclient = None | |
23 | if dclient is not None: | |
24 | dclient.queue_outbound(packet) | |
88487243 | 25 | elif daddr == c.server or daddr not in c.network: |
ec0c4d95 | 26 | print('TRACE INBOUND ', saddr, daddr, packet) |
5da7763e | 27 | queue_inbound(packet) |
e2d41dc1 | 28 | elif daddr == relay: |
5da7763e IJ |
29 | log_discard(packet, saddr, daddr, 'relay') |
30 | else: | |
31 | log_discard(packet, saddr, daddr, 'no client') | |
32 | ||
5da7763e | 33 | #---------- client ---------- |
c4b6d990 | 34 | |
ec88b1f1 | 35 | class Client(): |
88487243 | 36 | def __init__(self, ip, cs, pw): |
ec88b1f1 IJ |
37 | # instance data members |
38 | self._ip = ip | |
39 | self._cs = cs | |
88487243 | 40 | self.pw = pw |
0ac316c8 | 41 | self._rq = collections.deque() # requests |
650a3251 | 42 | # self._pq = PacketQueue(...) |
c4b6d990 IJ |
43 | # plus from config: |
44 | # .max_batch_down | |
45 | # .max_queue_time | |
46 | # .max_request_time | |
650a3251 | 47 | # .target_requests_outstanding |
88487243 IJ |
48 | |
49 | if ip not in c.network: | |
50 | raise ValueError('client %s not in network' % ip) | |
51 | ||
650a3251 IJ |
52 | for k in ('max_batch_down','max_queue_time','max_request_time', |
53 | 'target_requests_outstanding'): | |
ec88b1f1 | 54 | req = cfg.getint(cs, k) |
094ee3a2 | 55 | limit = cfg.getint('limits',k) |
c4b6d990 | 56 | self.__dict__[k] = min(req, limit) |
ca732796 | 57 | |
650a3251 | 58 | self._pq = PacketQueue(self.max_queue_time) |
c4b6d990 | 59 | |
88487243 IJ |
60 | if ip in clients: |
61 | raise ValueError('multiple client cfg sections for %s' % ip) | |
62 | clients[ip] = self | |
63 | ||
64 | def process_arriving_data(self, d): | |
65 | for packet in slip.decode(d): | |
66 | (saddr, daddr) = packet_addrs(packet) | |
67 | if saddr != self._ip: | |
68 | raise ValueError('wrong source address %s' % saddr) | |
69 | route(packet, saddr, daddr) | |
70 | ||
71 | def _req_cancel(self, request): | |
72 | request.finish() | |
73 | ||
74 | def _req_error(self, err, request): | |
75 | self._req_cancel(request) | |
76 | ||
77 | def queue_outbound(self, packet): | |
78 | self._pq.append(packet) | |
ca732796 | 79 | self._check_outbound() |
88487243 IJ |
80 | |
81 | def http_request(self, request): | |
82 | request.setHeader('Content-Type','application/octet-stream') | |
83 | reactor.callLater(self.max_request_time, self._req_cancel, request) | |
84 | request.notifyFinish().addErrback(self._req_error, request) | |
85 | self._rq.append(request) | |
86 | self._check_outbound() | |
87 | ||
88 | def _check_outbound(self): | |
89 | while True: | |
90 | try: request = self._rq[0] | |
91 | except IndexError: request = None | |
92 | if request and request.finished: | |
93 | self._rq.popleft() | |
94 | continue | |
95 | ||
96 | if not self._pq.nonempty(): | |
97 | # no packets, oh well | |
98 | continue | |
99 | ||
100 | if request is None: | |
101 | # no request | |
102 | break | |
103 | ||
104 | # request, and also some non-expired packets | |
7b07f0b5 IJ |
105 | self._pq.process((lambda: request.sentLength), |
106 | request.write, | |
107 | self.max_batch_down) | |
0ac316c8 | 108 | |
88487243 IJ |
109 | assert(request.sentLength) |
110 | self._rq.popLeft() | |
111 | request.finish() | |
112 | # round again, looking for more to do | |
0ac316c8 | 113 | |
88487243 IJ |
114 | while len(self._rq) > self.target_requests_outstanding: |
115 | request = self._rq.popleft() | |
116 | request.finish() | |
650a3251 | 117 | |
5da7763e | 118 | class IphttpResource(twisted.web.resource.Resource): |
c1e4910b | 119 | isLeaf = True |
5da7763e IJ |
120 | def render_POST(self, request): |
121 | # find client, update config, etc. | |
1672ded0 IJ |
122 | metadata = request.args['m'] |
123 | (ci_s, pw, tro) = metadata.split(b'\n')[0:3] | |
1321ad5f | 124 | ci = ipaddr(ci_s) |
1672ded0 IJ |
125 | cl = clients[ci] |
126 | if pw != cl.pw: raise ValueError('bad password') | |
127 | ||
128 | if pw != cl.target_requests_outstanding: | |
129 | raise ... | |
5da7763e IJ |
130 | |
131 | try: d = request.args['d'] | |
132 | except KeyError: d = '' | |
133 | ||
1672ded0 IJ |
134 | cl.process_arriving_data(d) |
135 | cl.new_request(request) | |
5da7763e | 136 | |
8e279651 | 137 | def render_GET(self, request): |
040ff511 | 138 | return b'<html><body>hippotat</body></html>' |
8e279651 | 139 | |
5da7763e IJ |
140 | def start_http(): |
141 | resource = IphttpResource() | |
b11c6e7a | 142 | site = twisted.web.server.Site(resource) |
88487243 IJ |
143 | for sa in c.saddrs: |
144 | ep = sa.make_endpoint() | |
b11c6e7a | 145 | crash_on_defer(ep.listen(site)) |
5da7763e IJ |
146 | |
147 | #---------- config and setup ---------- | |
148 | ||
3fba9787 | 149 | def process_cfg(): |
87a7c0c7 | 150 | process_cfg_common_always() |
88487243 IJ |
151 | process_cfg_server() |
152 | process_cfg_network() | |
e75e9c17 IJ |
153 | |
154 | try: | |
87a7c0c7 | 155 | c.relay = cfg.get('virtual','relay') |
e2d41dc1 | 156 | except NoOptionError: |
87a7c0c7 | 157 | for search in c.network.hosts(): |
88487243 | 158 | if search == c.server: continue |
87a7c0c7 | 159 | c.relay = search |
e75e9c17 | 160 | break |
3fba9787 | 161 | |
88487243 IJ |
162 | process_cfg_saddrs() |
163 | process_cfg_clients(Client) | |
164 | ||
165 | process_cfg_ipif('server', | |
166 | (('local','server'), | |
034284c3 | 167 | ('peer', 'relay'), |
88487243 | 168 | ('rnets','network'))) |
5bae5ba3 | 169 | |
1321ad5f | 170 | common_startup() |
87a7c0c7 IJ |
171 | process_cfg() |
172 | start_ipif(c.ipif_command, route) | |
173 | start_http() | |
ae7c7784 | 174 | common_run() |