7 import twisted
.internet
8 from twisted
.web
.server
import NOT_DONE_YET
10 #import twisted.web.server import Site
11 #from twisted.web.resource import Resource
17 #---------- "router" ----------
19 def route(packet
, saddr
, daddr
):
20 print('TRACE ', saddr
, daddr
, packet
)
21 try: dclient
= clients
[daddr
]
22 except KeyError: dclient
= None
23 if dclient
is not None:
24 dclient
.queue_outbound(packet
)
25 elif daddr
== c
.server
or daddr
not in c
.network
:
26 print('TRACE INBOUND ', saddr
, daddr
, packet
)
29 log_discard(packet
, saddr
, daddr
, 'relay')
31 log_discard(packet
, saddr
, daddr
, 'no client')
33 #---------- client ----------
36 def __init__(self
, ip
, cs
, pw
):
37 # instance data members
41 self
._rq
= collections
.deque() # requests
42 # self._pq = PacketQueue(...)
47 # .target_requests_outstanding
49 if ip
not in c
.network
:
50 raise ValueError('client %s not in network' % ip
)
52 for k
in ('max_batch_down','max_queue_time','max_request_time',
53 'target_requests_outstanding'):
54 req
= cfg
.getint(cs
, k
)
55 limit
= cfg
.getint('limits',k
)
56 self
.__dict__
[k
] = min(req
, limit
)
58 self
._pq
= PacketQueue(self
.max_queue_time
)
61 raise ValueError('multiple client cfg sections for %s' % ip
)
64 def process_arriving_data(self
, d
):
65 for packet
in slip
.decode(d
):
66 (saddr
, daddr
) = packet_addrs(packet
)
68 raise ValueError('wrong source address %s' % saddr
)
69 route(packet
, saddr
, daddr
)
71 def _req_cancel(self
, request
):
74 def _req_error(self
, err
, request
):
75 self
._req_cancel(request
)
77 def queue_outbound(self
, packet
):
78 self
._pq
.append(packet
)
79 self
._check_outbound()
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()
88 def _check_outbound(self
):
90 try: request
= self
._rq
[0]
91 except IndexError: request
= None
92 if request
and request
.finished
:
96 if not self
._pq
.nonempty():
104 # request, and also some non-expired packets
105 self
._pq
.process((lambda: request
.sentLength
),
109 assert(request
.sentLength
)
112 # round again, looking for more to do
114 while len(self
._rq
) > self
.target_requests_outstanding
:
115 request
= self
._rq
.popleft()
118 def process_request(request
):
119 # find client, update config, etc.
120 metadata
= request
.args
['m']
121 (ci_s
, pw
, tro
) = metadata
.split(b
'\n')[0:3]
124 if pw
!= cl
.pw
: raise ValueError('bad password')
126 if pw
!= cl
.target_requests_outstanding
:
129 try: d
= request
.args
['d']
130 except KeyError: d
= ''
132 cl
.process_arriving_data(d
)
133 cl
.new_request(request
)
135 class IphttpResource(twisted
.web
.resource
.Resource
):
137 def render_POST(self
, request
):
138 try: process_request(request
)
139 except Exception as e
:
140 request
.setHeader('Content-Type','text/plain; charset="utf-8"')
141 request
.setResponseCode(400)
142 return str(e
).encode('utf-8')
144 def render_GET(self
, request
):
145 return b
'<html><body>hippotat</body></html>'
148 resource
= IphttpResource()
149 site
= twisted
.web
.server
.Site(resource
)
151 ep
= sa
.make_endpoint()
152 crash_on_defer(ep
.listen(site
))
154 #---------- config and setup ----------
157 process_cfg_common_always()
159 process_cfg_network()
162 c
.relay
= cfg
.get('virtual','relay')
163 except NoOptionError
:
164 for search
in c
.network
.hosts():
165 if search
== c
.server
: continue
170 process_cfg_clients(Client
)
172 process_cfg_ipif('server',
175 ('rnets','network')))
179 start_ipif(c
.ipif_command
, route
)