Esempio n. 1
0
 def send(self, channel, cmd, data):
     assert isinstance(data, bytes)
     assert len(data) <= 65535
     p = struct.pack('!ccHHH', b('S'), b('S'), channel, cmd, len(data)) \
         + data
     self.outbuf.append(p)
     debug2(' > channel=%d cmd=%s len=%d (fullness=%d)' %
            (channel, cmd_to_name.get(cmd,
                                      hex(cmd)), len(data), self.fullness))
     self.fullness += len(data)
Esempio n. 2
0
 def hostwatch_ready(sock):
     assert(hw.pid)
     content = hw.sock.recv(4096)
     if content:
         lines = (hw.leftover + content).split(b('\n'))
         if lines[-1]:
             # no terminating newline: entry isn't complete yet!
             hw.leftover = lines.pop()
             lines.append(b(''))
         else:
             hw.leftover = b('')
         mux.send(0, ssnet.CMD_HOST_LIST, b('\n').join(lines))
     else:
         raise Fatal('hostwatch process died')
Esempio n. 3
0
 def __init__(self, rfile, wfile):
     Handler.__init__(self, [rfile, wfile])
     self.rfile = rfile
     self.wfile = wfile
     self.new_channel = self.got_dns_req = self.got_routes = None
     self.got_udp_open = self.got_udp_data = self.got_udp_close = None
     self.got_host_req = self.got_host_list = None
     self.channels = {}
     self.chani = 0
     self.want = 0
     self.inbuf = b('')
     self.outbuf = []
     self.fullness = 0
     self.too_full = False
     self.send(0, CMD_PING, b('chicken'))
Esempio n. 4
0
 def fill(self):
     if self.buf:
         return
     rb = self.uread()
     if rb:
         self.buf.append(rb)
     if rb == b(''):  # empty string means EOF; None means temporarily empty
         self.noread()
Esempio n. 5
0
 def handle(self):
     self.fill()
     # log('inbuf is: (%d,%d) %r'
     #     % (self.want, len(self.inbuf), self.inbuf))
     while 1:
         if len(self.inbuf) >= (self.want or HDR_LEN):
             (s1, s2, channel, cmd, datalen) = \
                 struct.unpack('!ccHHH', self.inbuf[:HDR_LEN])
             assert (s1 == b('S'))
             assert (s2 == b('S'))
             self.want = datalen + HDR_LEN
         if self.want and len(self.inbuf) >= self.want:
             data = self.inbuf[HDR_LEN:self.want]
             self.inbuf = self.inbuf[self.want:]
             self.want = 0
             self.got_packet(channel, cmd, data)
         else:
             break
Esempio n. 6
0
 def callback(self, sock):
     try:
         data, peer = sock.recvfrom(4096)
     except socket.error:
         _, e = sys.exc_info()[:2]
         log('UDP recv from %r port %d: %s' % (peer[0], peer[1], e))
         return
     debug2('UDP response: %d bytes' % len(data))
     hdr = b("%s,%r," % (peer[0], peer[1]))
     self.mux.send(self.chan, ssnet.CMD_UDP_DATA, hdr + data)
Esempio n. 7
0
 def uread(self):
     if self.connect_to:
         return None  # still connecting
     if self.shut_read:
         return
     self.rsock.setblocking(False)
     try:
         return _nb_clean(os.read, self.rsock.fileno(), 65536)
     except OSError:
         _, e = sys.exc_info()[:2]
         self.seterr('uread: %s' % e)
         return b('')  # unexpected error... we'll call it EOF
Esempio n. 8
0
 def udp_req(channel, cmd, data):
     debug2('Incoming UDP request channel=%d, cmd=%d' %
            (channel, cmd))
     if cmd == ssnet.CMD_UDP_DATA:
         (dstip, dstport, data) = data.split(b(','), 2)
         dstport = int(dstport)
         debug2('is incoming UDP data. %r %d.' % (dstip, dstport))
         h = udphandlers[channel]
         h.send((dstip, dstport), data)
     elif cmd == ssnet.CMD_UDP_CLOSE:
         debug2('is incoming UDP close')
         h = udphandlers[channel]
         h.ok = False
         del mux.channels[channel]
Esempio n. 9
0
 def fill(self):
     try:
         os.set_blocking(self.rfile.fileno(), False)
     except AttributeError:
         # python < 3.5
         flags = fcntl.fcntl(self.rfile.fileno(), fcntl.F_GETFL)
         flags |= os.O_NONBLOCK
         flags = fcntl.fcntl(self.rfile.fileno(), fcntl.F_SETFL, flags)
     try:
         # If LATENCY_BUFFER_SIZE is inappropriately large, we will
         # get a MemoryError here. Read no more than 1MiB.
         read = _nb_clean(os.read, self.rfile.fileno(),
                          min(1048576, LATENCY_BUFFER_SIZE))
     except OSError:
         _, e = sys.exc_info()[:2]
         raise Fatal('other end: %r' % e)
     # log('<<< %r' % b)
     if read == b(''):  # EOF
         self.ok = False
     if read:
         self.inbuf += read
Esempio n. 10
0
def main(latency_control, latency_buffer_size, auto_hosts, to_nameserver,
         auto_nets):
    try:
        helpers.logprefix = ' s: '
        debug1('Starting server with Python version %s'
               % platform.python_version())

        debug1('latency control setting = %r' % latency_control)
        if latency_buffer_size:
            import tshuttle.ssnet as ssnet
            ssnet.LATENCY_BUFFER_SIZE = latency_buffer_size

        # synchronization header
        sys.stdout.write('\0\0SSHUTTLE0001')
        sys.stdout.flush()

        handlers = []
        mux = Mux(sys.stdin, sys.stdout)
        handlers.append(mux)

        debug1('auto-nets:' + str(auto_nets))
        if auto_nets:
            routes = list(list_routes())
            debug1('available routes:')
            for r in routes:
                debug1('  %d/%s/%d' % r)
        else:
            routes = []

        routepkt = ''
        for r in routes:
            routepkt += '%d,%s,%d\n' % r
        mux.send(0, ssnet.CMD_ROUTES, b(routepkt))

        hw = Hostwatch()
        hw.leftover = b('')

        def hostwatch_ready(sock):
            assert(hw.pid)
            content = hw.sock.recv(4096)
            if content:
                lines = (hw.leftover + content).split(b('\n'))
                if lines[-1]:
                    # no terminating newline: entry isn't complete yet!
                    hw.leftover = lines.pop()
                    lines.append(b(''))
                else:
                    hw.leftover = b('')
                mux.send(0, ssnet.CMD_HOST_LIST, b('\n').join(lines))
            else:
                raise Fatal('hostwatch process died')

        def got_host_req(data):
            if not hw.pid:
                (hw.pid, hw.sock) = start_hostwatch(
                        data.decode("ASCII").strip().split(), auto_hosts)
                handlers.append(Handler(socks=[hw.sock],
                                        callback=hostwatch_ready))
        mux.got_host_req = got_host_req

        def new_channel(channel, data):
            (family, dstip, dstport) = data.decode("ASCII").split(',', 2)
            family = int(family)
            # AF_INET is the same constant on Linux and BSD but AF_INET6
            # is different. As the client and server can be running on
            # different platforms we can not just set the socket family
            # to what comes in the wire.
            if family != socket.AF_INET:
                family = socket.AF_INET6
            dstport = int(dstport)
            outwrap = ssnet.connect_dst(family, dstip, dstport)
            handlers.append(Proxy(MuxWrapper(mux, channel), outwrap))
        mux.new_channel = new_channel

        dnshandlers = {}

        def dns_req(channel, data):
            debug2('Incoming DNS request channel=%d.' % channel)
            h = DnsProxy(mux, channel, data, to_nameserver)
            handlers.append(h)
            dnshandlers[channel] = h
        mux.got_dns_req = dns_req

        udphandlers = {}

        def udp_req(channel, cmd, data):
            debug2('Incoming UDP request channel=%d, cmd=%d' %
                   (channel, cmd))
            if cmd == ssnet.CMD_UDP_DATA:
                (dstip, dstport, data) = data.split(b(','), 2)
                dstport = int(dstport)
                debug2('is incoming UDP data. %r %d.' % (dstip, dstport))
                h = udphandlers[channel]
                h.send((dstip, dstport), data)
            elif cmd == ssnet.CMD_UDP_CLOSE:
                debug2('is incoming UDP close')
                h = udphandlers[channel]
                h.ok = False
                del mux.channels[channel]

        def udp_open(channel, data):
            debug2('Incoming UDP open.')
            family = int(data)
            mux.channels[channel] = lambda cmd, data: udp_req(channel, cmd,
                                                              data)
            if channel in udphandlers:
                raise Fatal('UDP connection channel %d already open' %
                            channel)
            else:
                h = UdpProxy(mux, channel, family)
                handlers.append(h)
                udphandlers[channel] = h
        mux.got_udp_open = udp_open

        while mux.ok:
            if hw.pid:
                assert(hw.pid > 0)
                (rpid, rv) = os.waitpid(hw.pid, os.WNOHANG)
                if rpid:
                    raise Fatal(
                        'hostwatch exited unexpectedly: code 0x%04x' % rv)

            ssnet.runonce(handlers, mux)
            if latency_control:
                mux.check_fullness()

            if dnshandlers:
                now = time.time()
                remove = []
                for channel, h in dnshandlers.items():
                    if h.timeout < now or not h.ok:
                        debug3('expiring dnsreqs channel=%d' % channel)
                        remove.append(channel)
                        h.ok = False
                for channel in remove:
                    del dnshandlers[channel]
            if udphandlers:
                remove = []
                for channel, h in udphandlers.items():
                    if not h.ok:
                        debug3('expiring UDP channel=%d' % channel)
                        remove.append(channel)
                        h.ok = False
                for channel in remove:
                    del udphandlers[channel]

    except Fatal as e:
        log('fatal: %s' % e)
        sys.exit(99)
Esempio n. 11
0
 def uread(self):
     if self.shut_read:
         return b('')  # EOF
     else:
         return None  # no data available right now
Esempio n. 12
0
 def nowrite(self):
     if not self.shut_write:
         self.mux.send(self.channel, CMD_TCP_EOF, b(''))
         self.setnowrite()
Esempio n. 13
0
 def noread(self):
     if not self.shut_read:
         self.mux.send(self.channel, CMD_TCP_STOP_SENDING, b(''))
         self.setnoread()
Esempio n. 14
0
 def check_fullness(self):
     if self.fullness > LATENCY_BUFFER_SIZE:
         if not self.too_full:
             self.send(0, CMD_PING, b('rttest'))
         self.too_full = True