#!/usr/bin/python3
+import sys
+import os
+
import twisted
+import twisted.internet
+import twisted.internet.endpoints
+from twisted.internet import reactor
+from twisted.web.server import NOT_DONE_YET
+from twisted.logger import LogLevel
+
+import ipaddress
+from ipaddress import AddressValueError
#import twisted.web.server import Site
#from twisted.web.resource import Resource
-from twisted.web.server import NOT_DONE_YET
-from twisted.internet import reactor
from optparse import OptionParser
from configparser import ConfigParser
from configparser import NoOptionError
-import ipaddress
import collections
clients = { }
-def ipaddress(input):
+def ipaddr(input):
try:
r = ipaddress.IPv4Address(input)
except AddressValueError:
# [relay]
[server]
-ipif = userv root ipif %(host),%(relay),%(mtu),slip %(network)
+ipif = userv root ipif %(host)s,%(relay)s,%(mtu)s,slip %(network)s
addrs = 127.0.0.1 ::1
port = 80
#---------- "router" ----------
-def route(packet. daddr):
+def route(packet, daddr):
try: client = clients[daddr]
except KeyError: dclient = None
if dclient is not None:
dclient.queue_outbound(packet)
- else if daddr = host or daddr not in network:
+ elif daddr == host or daddr not in network:
queue_inbound(packet)
- else if daddr = relay:
+ elif daddr == relay:
log_discard(packet, saddr, daddr, 'relay')
else:
log_discard(packet, saddr, daddr, 'no client')
self._buffer = b''
def connectionMade(self): pass
def outReceived(self, data):
- buffer += data
- packets = slip_decode(buffer)
- buffer = packets.pop()
+ #print('RECV ', repr(data))
+ self._buffer += data
+ packets = slip_decode(self._buffer)
+ self._buffer = packets.pop()
for packet in packets:
(saddr, daddr) = packet_addrs(packet)
route(packet, daddr)
global ipif
ipif = IpifProcessProtocol()
reactor.spawnProcess(ipif,
- '/bin/sh',['-c', ipif_command],
+ '/bin/sh',['sh','-xc', ipif_command],
childFDs={0:'w', 1:'r', 2:2})
def queue_inbound(packet):
ipif.transport.write(slip_encode(packet))
ipif.transport.write(slip_delimiter)
+#---------- SLIP handling ----------
+
+slip_end = b'\300'
+slip_esc = b'\333'
+slip_esc_end = b'\334'
+slip_esc_esc = b'\335'
+slip_delimiter = slip_end
+
+def slip_encode(packet):
+ return (packet
+ .replace(slip_esc, slip_esc + slip_esc_esc)
+ .replace(slip_end, slip_esc + slip_esc_end))
+
+def slip_decode(data):
+ print('DECODE ', repr(data))
+ out = []
+ for packet in data.split(slip_end):
+ pdata = b''
+ while True:
+ eix = packet.find(slip_esc)
+ if eix == -1:
+ pdata += packet
+ break
+ #print('ESC ', repr((pdata, packet, eix)))
+ pdata += packet[0 : eix]
+ ck = packet[eix+1]
+ if ck == slip_esc_esc: pdata += slip_esc
+ elif ck == slip_esc_end: pdata += slip_end
+ else: raise ValueError('invalid SLIP escape')
+ packet = packet[eix+2 : ]
+ out.append(pdata)
+ print('DECODED ', repr(out))
+ return out
+
+#---------- packet parsing ----------
+
+def packet_addrs(packet):
+ pass
+
#---------- client ----------
class Client():
# now request is an unfinished request, or None
try: (queuetime, packet) = self._pq[0]
- except: IndexError:
+ except IndexError:
# no packets, oh well
break
class IphttpResource(twisted.web.resource.Resource):
def render_POST(self, request):
# find client, update config, etc.
- ci = ipaddress(request.args['i'])
+ ci = ipaddr(request.args['i'])
c = clients[ci]
pw = request.args['pw']
if pw != c.pw: raise ValueError('bad password')
def start_http():
resource = IphttpResource()
sitefactory = twisted.web.server.Site(resource)
- for addrspec in cfg.get('server','addresses').split():
+ for addrspec in cfg.get('server','addrs').split():
try:
addr = ipaddress.IPv4Address(addrspec)
endpointfactory = twisted.internet.endpoints.TCP4ServerEndpoint
try:
host = cfg.get('virtual','host')
except NoOptionError:
- host = network.hosts().next()
+ host = next(network.hosts())
try:
relay = cfg.get('virtual','relay')
- except OptionError:
+ except NoOptionError:
for search in network.hosts():
- if search = host: continue
+ if search == host: continue
relay = search
break
for cs in cfg.sections():
if not (':' in cs or '.' in cs): continue
- ci = ipaddress(cs)
+ ci = ipaddr(cs)
if ci not in network:
raise ValueError('client %s not in network' % ci)
if ci in clients:
raise ValueError('multiple client cfg sections for %s' % ci)
clients[ci] = Client(ci, cs)
+ global mtu
+ mtu = cfg.get('virtual','mtu')
+
iic_vars = { }
for k in ('host','relay','mtu','network'):
iic_vars[k] = globals()[k]
ipif_command = cfg.get('server','ipif', vars=iic_vars)
+def crash_on_critical(event):
+ if event.get('log_level') >= LogLevel.critical:
+ print('crashing: ', twisted.logger.formatEvent(event), file=sys.stderr)
+ #print('crashing!', file=sys.stderr)
+ #os._exit(1)
+ try: reactor.stop()
+ except twisted.internet.error.ReactorNotRunning: pass
+
def startup():
+ global cfg
+
op = OptionParser()
op.add_option('-c', '--config', dest='configfile',
default='/etc/hippottd/server.conf')
(opts, args) = op.parse_args()
if len(args): op.error('no non-option arguments please')
+ twisted.logger.globalLogPublisher.addObserver(crash_on_critical)
+
cfg = ConfigParser()
cfg.read_string(defcfg)
- cfg.read_file(opts['configfile'])
+ cfg.read(opts.configfile)
process_cfg()
start_ipif()
start_http()
+
+startup()
+reactor.run()