Sometimes, you may need to create a local port forwarder that will redirect all traffic from a local port to a particular remote host. This might be useful to enable proxy users to browse a certain site while preventing them from browsing some others.
Let us create a local port forwarding script that will redirect all traffic received at port 8800 to the Google home page (http://www.google.com). We can pass the local and remote host as well as port number to this script. For the sake of simplicity, let's only specify the local port number as we are aware that the web server runs on port 80.
Listing 3.1 shows a port forwarding example, as follows:
#!/usr/bin/env python # Python Network Programming Cookbook -- Chapter – 3 # This program is optimized for Python 2.7. # It may run on any other version with/without modifications. import argparse LOCAL_SERVER_HOST = 'localhost' REMOTE_SERVER_HOST = 'www.google.com' BUFSIZE = 4096 import asyncore import socket
First, we define the PortForwarder
class:
class PortForwarder(asyncore.dispatcher): def __init__(self, ip, port, remoteip,remoteport,backlog=5): asyncore.dispatcher.__init__(self) self.remoteip=remoteip self.remoteport=remoteport self.create_socket(socket.AF_INET,socket.SOCK_STREAM) self.set_reuse_addr() self.bind((ip,port)) self.listen(backlog) def handle_accept(self): conn, addr = self.accept() print "Connected to:",addr Sender(Receiver(conn),self.remoteip,self.remoteport)
Now, we need to specify the Receiver
and Sender
classes, as follows:
class Receiver(asyncore.dispatcher): def __init__(self,conn): asyncore.dispatcher.__init__(self,conn) self.from_remote_buffer='' self.to_remote_buffer='' self.sender=None def handle_connect(self): pass def handle_read(self): read = self.recv(BUFSIZE) self.from_remote_buffer += read def writable(self): return (len(self.to_remote_buffer) > 0) def handle_write(self): sent = self.send(self.to_remote_buffer) self.to_remote_buffer = self.to_remote_buffer[sent:] def handle_close(self): self.close() if self.sender: self.sender.close() class Sender(asyncore.dispatcher): def __init__(self, receiver, remoteaddr,remoteport): asyncore.dispatcher.__init__(self) self.receiver=receiver receiver.sender=self self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect((remoteaddr, remoteport)) def handle_connect(self): pass def handle_read(self): read = self.recv(BUFSIZE) self.receiver.to_remote_buffer += read def writable(self): return (len(self.receiver.from_remote_buffer) > 0) def handle_write(self): sent = self.send(self.receiver.from_remote_buffer) self.receiver.from_remote_buffer = self.receiver.from_remote_buffer[sent:] def handle_close(self): self.close() self.receiver.close() if __name__ == "__main__": parser = argparse.ArgumentParser(description='Port forwarding example') parser.add_argument('--local-host', action="store", dest="local_host", default=LOCAL_SERVER_HOST) parser.add_argument('--local-port', action="store", dest="local_port", type=int, required=True) parser.add_argument('--remote-host', action="store", dest="remote_host", default=REMOTE_SERVER_HOST) parser.add_argument('--remote-port', action="store", dest="remote_port", type=int, default=80) given_args = parser.parse_args() local_host, remote_host = given_args.local_host, given_args.remote_host local_port, remote_port = given_args.local_port, given_args.remote_port print "Starting port forwarding local %s:%s => remote %s:%s" % (local_host, local_port, remote_host, remote_port) PortForwarder(local_host, local_port, remote_host, remote_port) asyncore.loop()
If you run this script, it will show the following output:
$ python 3_1_port_forwarding.py --local-port=8800 Starting port forwarding local localhost:8800 => remote www.google.com:80
Now, open your browser and visit http://localhost:8800
. This will take you to the Google home page and the script will print something similar to the following command:
Connected to: ('127.0.0.1', 38557)
The following screenshot shows the forwarding a local port to a remote host:
We created a port forwarding class, PortForwarder subclassed
, from asyncore.dispatcher
, which wraps around the socket object. It provides a few additional helpful functions when certain events occur, for example, when the connection is successful or a client is connected to a server socket. You have the choice of overriding the set of methods defined in this class. In our case, we only override the
handle_accept()
method.
Two other classes have been derived from asyncore.dispatcher
. The
Receiver
class handles the incoming client requests and the Sender
class takes this Receiver
instance and processes the sent data to the clients. As you can see, these two classes override the handle_read()
, handle_write()
, and writeable()
methods to facilitate the bi-directional communication between the remote host and local client.
In summary, the PortForwarder
class takes the incoming client request in a local socket and passes this to the Sender
class instance, which in turn uses the Receiver
class instance to initiate a bi-directional communication with a remote server in the specified port.
3.144.121.45