Search code examples
pythontcptwistedtunnel

Twisted tcp tunnel/bridge/relay


I'd like to know how to write TCP tunnel/relay/bridge/proxy (you name it) using twisted.

I did some research in google, twisted doc/forum etc. etc but couldn't find anwser.

I already done it in pure python using socket, threading and select.

Here is code:

#!/usr/bin/env python

import socket
import sys
import select
import threading
import logging
import time


class Client(threading.Thread):
    def __init__(self, client, address, id_number, dst_ip, dst_port):
        self.log = logging.getLogger(__name__+'.client-%s' % id_number)
        self.running = False
        self.cl_soc = client
        self.cl_adr = address
        self.my_id = id_number
        self.dst_ip = dst_ip
        self.dst_port = dst_port
        threading.Thread.__init__(self)

    def stop(self):
        self.running = 0

    def run(self):
        try:
            self.dst_soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.dst_soc.connect((self.dst_ip,self.dst_port))
        except:
            self.log.error('Can\'t connect to %s:%s' % (self.dst_ip, self.dst_port))
        else:
            self.running = True
            self.log.info('Bridge %s <-> %s created' % (
                '%s:%s' % self.dst_soc.getpeername(), '%s:%s' % self.cl_adr))

        try:
            while self.running:
                iRdy = select.select([self.cl_soc, self.dst_soc],[],[], 1)[0]

                if self.cl_soc in iRdy:
                    buf = self.cl_soc.recv(4096)
                    if not buf:
                        info = 'Ended connection: client'
                        self.running = False
                    else:
                        self.dst_soc.send(buf)

                if self.dst_soc in iRdy:
                    buf = self.dst_soc.recv(4096)
                    if not buf:
                        info = 'Ended connection: destination'
                        self.running = False
                    else:
                        self.cl_soc.send(buf)


        except:
            self.log.error('Sth bad happend', exc_info=True)
            self.running = False

        self.log.debug('Closing sockets')
        try:
            self.dst_soc.close()
        except:
            pass

        try:
            self.cl_soc.close()
        except:
            pass


class Server(threading.Thread):
    def __init__(self, l_port=25565, d_ip='127.0.0.1', d_port=None):
        self.log = logging.getLogger(__name__+'.server-%s:%s' % (d_ip,d_port))
        threading.Thread.__init__(self)
        self.d_ip = d_ip
        if d_port == None:
            self.d_port = l_port
        else:
            self.d_port = d_port

        self.port = l_port
        binding = 1
        wait = 30
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.s.setsockopt(socket.SOL_SOCKET,  socket.SO_REUSEADDR,  1)
        while binding:
            try:
                self.s.bind(('',self.port))
            except:
                self.log.warning('Cant bind. Will wait %s sec' % wait)
                time.sleep(wait)
            else:
                binding = 0
                self.log.info('Server ready for connections - port %s' % d_port)

    def run(self):
        self.s.listen(5)
        input = [self.s, sys.stdin]
        running = 1
        self.cl_threads = []
        id_nr = 0

        while running:
            iRdy = select.select(input, [], [],1)[0]
            if self.s in iRdy:
                c_soc, c_adr = self.s.accept()
                c = Client(c_soc, c_adr, id_nr, self.d_ip, self.d_port)
                c.start()
                self.cl_threads.append(c)
                id_nr += 1

            if sys.stdin in iRdy:
                junk = sys.stdin.readline()
                print junk
                running = 0

        try:
            self.s.close()
        except:
            pass


        for c in self.cl_threads:
            c.stop()
            c.join(5)
            del c

        self.cl_threads = None

        self.log.info('Closing server')


if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG)
    s = Server(1424, '192.168.1.6', 1424)
    s.run()

Solution

  • this is actually, already built into twisted, from the command line you can type:

    $ twistd --nodaemon portforward --port 1424 --host 192.168.1.6
    

    to get the exact behavior you seem to be looking for.

    If you'd like to roll your own, you can still use all of the bits, in twisted.protocols.portforward