Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources]

David Ascher, Alex Martelli, Anna Ravenscroft

نسخه متنی -صفحه : 394/ 273
نمايش فراداده

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.