Recipe13.13.Forwarding and Redirecting Network Ports


Recipe 13.13. Forwarding and Redirecting Network Ports

Credit: Simon Foster

Problem

You need to forward a network port to another host (forwarding), possibly to a different port number (redirecting).

Solution

Classes using the tHReading and socket modules can provide port forwarding and redirecting:

import sys, socket, time, threading LOGGING = True loglock = threading.Lock( ) def log(s, *a):     if LOGGING:         loglock.acquire( )         try:             print '%s:%s' % (time.ctime( ), (s % a))             sys.stdout.flush( )         finally:             loglock.release( ) class PipeThread(threading.Thread):     pipes = [  ]     pipeslock = threading.Lock( )     def _ _init_ _(self, source, sink):         Thread._ _init_ _(self)         self.source = source         self.sink = sink         log('Creating new pipe thread %s ( %s -> %s )',              self, source.getpeername( ), sink.getpeername( ))         self.pipeslock.acquire( )         try: self.pipes.append(self)         finally: self.pipeslock.release( )         self.pipeslock.acquire( )         try: pipes_now = len(self.pipes)         finally: self.pipeslock.release( )         log('%s pipes now active', pipes_now)     def run(self):         while True:             try:                 data = self.source.recv(1024)                 if not data: break                 self.sink.send(data)             except:                 break         log('%s terminating', self)         self.pipeslock.acquire( )         try: self.pipes.remove(self)         finally: self.pipeslock.release( )         self.pipeslock.acquire( )         try: pipes_left = len(self.pipes)         finally: self.pipeslock.release( )         log('%s pipes still active', pipes_left) class Pinhole(threading.Thread):     def _ _init_ _(self, port, newhost, newport):         Thread._ _init_ _(self)         log('Redirecting: localhost:%s -> %s:%s', port, newhost, newport)         self.newhost = newhost         self.newport = newport         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)         self.sock.bind(('', port))         self.sock.listen(5)     def run(self):         while True:             newsock, address = self.sock.accept( )             log('Creating new session for %s:%s', *address)             fwd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)             fwd.connect((self.newhost, self.newport))             PipeThread(newsock, fwd).start( )             PipeThread(fwd, newsock).start( )

A short ending to this pinhole.py module, with the usual guard to run this part only when pinhole is run as a main script rather than imported, lets us offer this recipe's functionality as a command-line script:

if _ _name_ _ == '_ _main_ _':     print 'Starting Pinhole port forwarder/redirector'     import sys     # get the arguments, give help in case of errors     try:         port = int(sys.argv[1])         newhost = sys.argv[2]         try: newport = int(sys.argv[3])         except IndexError: newport = port     except (ValueError, IndexError):         print 'Usage: %s port newhost [newport]' % sys.argv[0]         sys.exit(1)     # start operations     sys.stdout = open('pinhole.log', 'w')     Pinhole(port, newhost, newport).start( )

Discussion

Port forwarding and redirecting can often come in handy when you're operating a network, even a small one. Applications or other services, possibly not under your control, may be hardwired to connect to servers on certain addresses or ports; by interposing a forwarder and redirector, you can send such applications' connection requests onto any other host and/or port that suits you better.

The code in this recipe supplies two classes that liberally use threading to provide this functionality and a small "main script" at the end, with the usual if _ _name_ _ = = '_ _main_ _' guard, to deliver this functionality as a command-line script. For once, the small "main script" is not just for demonstration and testing purposes but is actually quite useful on its own. For example:

# python pinhole.py 80 webserver

forwards all incoming HTTP sessions on standard port 80 to host webserver;

# python pinhole.py 23 localhost 2323

redirects all incoming telnet sessions on standard port 23 to port 2323 on this same host (since localhost is the conventional hostname for "this host" in all TCP/IP implementations).

See Also

Documentation for the standard library modules socket and threading in the Library Reference and Python in a Nutshell.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net