zxcu zxcu - 1 month ago 19
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

Comments