Question

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()
Was it helpful?

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top