zxcu zxcu - 5 months ago 32
Python Question

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()

Answer

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