PageRenderTime 54ms CodeModel.GetById 26ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 1ms

/examples/portforward.py

https://bitbucket.org/prologic/circuits/
Python | 191 lines | 168 code | 10 blank | 13 comment | 0 complexity | 3a644eb6d40366006ca7dfa0961242d9 MD5 | raw file
  1#!/usr/bin/env python
  2
  3"""A Port Forwarding Example
  4
  5This example demonstrates slightly more complex features and behaviors
  6implementing a TCP/UDP Port Forwarder of network traffic. This can be used
  7as a simple tool to forward traffic from one port to another.
  8
  9Example:
 10
 11    ./portforward.py 0.0.0.0:2222 127.0.0.1:22
 12
 13This example also has support for daemonizing the process into the background.
 14"""
 15
 16
 17from uuid import uuid4 as uuid
 18from optparse import OptionParser
 19
 20from circuits.app import Daemon
 21from circuits import handler, Component, Debugger
 22from circuits.net.events import close, Connect, write
 23from circuits.net.sockets import TCPClient, TCPServer
 24
 25__version__ = "0.2"
 26
 27USAGE = "%prog [options] <srcaddr:srcport> <destaddr:destport>"
 28VERSION = "%prog v" + __version__
 29
 30
 31def parse_options():
 32    parser = OptionParser(usage=USAGE, version=VERSION)
 33
 34    parser.add_option(
 35        "-d", "--daemon",
 36        action="store_true", default=False, dest="daemon",
 37        help="Enable daemon mode (fork into the background)"
 38    )
 39
 40    parser.add_option(
 41        "", "--debug",
 42        action="store_true", default=False, dest="debug",
 43        help="Enable debug mode (verbose event output)"
 44    )
 45
 46    opts, args = parser.parse_args()
 47
 48    if len(args) < 2:
 49        parser.print_help()
 50        raise SystemExit(1)
 51
 52    return opts, args
 53
 54
 55def _on_target_disconnected(self, event):
 56    """Disconnected Event Handler
 57
 58    This unbound function will be later added as an event handler to a
 59    dynamically created and registered client instance and used to process
 60    Disconnected events of a connected client.
 61    """
 62
 63    channel = event.channels[0]
 64    sock = self._sockets[channel]
 65
 66    self.fire(close(sock), "source")
 67
 68    del self._sockets[channel]
 69    del self._clients[sock]
 70
 71
 72def _on_target_ready(self, component):
 73    """Ready Event Handler
 74
 75    This unbound function will be later added as an event handler to a
 76    dynamically created and registered client instance and used to process
 77    Ready events of a registered client.
 78    """
 79
 80    self.fire(connect(*self._target, secure=self._secure), component.channel)
 81
 82
 83def _on_target_read(self, event, data):
 84    """Read Event Handler
 85
 86    This unbound function will be later added as an event handler to a
 87    dynamically created and registered client instance and used to process
 88    Read events of a connected client.
 89    """
 90
 91    sock = self._sockets[event.channels[0]]
 92    self.fire(write(sock, data), "source")
 93
 94
 95class PortForwarder(Component):
 96
 97    def init(self, source, target, secure=False):
 98        self._source = source
 99        self._target = target
100        self._secure = secure
101
102        self._clients = dict()
103        self._sockets = dict()
104
105        # Setup our components and register them.
106        server = TCPServer(self._source, secure=self._secure, channel="source")
107        server.register(self)
108
109    @handler("connect", channel="source")
110    def _on_source_connect(self, sock, host, port):
111        """Explicitly defined connect Event Handler
112
113        This evens is triggered by the underlying TCPServer Component when
114        a new client connection has been made.
115
116        Here we dynamically create a Client instance, registere it and add
117        custom event handlers to handle the events of the newly created
118        client. The client is registered with a unique channel per connection.
119        """
120
121        bind = 0
122        channel = uuid()
123
124        client = TCPClient(bind, channel=channel)
125        client.register(self)
126
127        self.addHandler(
128            handler("disconnected", channel=channel)(_on_target_disconnected)
129        )
130
131        self.addHandler(
132            handler("ready", channel=channel)(_on_target_ready)
133        )
134
135        self.addHandler(
136            handler("read", channel=channel)(_on_target_read)
137        )
138
139        self._clients[sock] = client
140        self._sockets[client.channel] = sock
141
142    @handler("read", channel="source")
143    def _on_source_read(self, sock, data):
144        """Explicitly defined Read Event Handler
145
146        This evens is triggered by the underlying TCPServer Component when
147        a connected client has some data ready to be processed.
148
149        Here we simply fire a cooresponding write event to the cooresponding
150        matching client which we lookup using the socket object as the key
151        to determinte the unique id.
152        """
153
154        client = self._clients[sock]
155        self.fire(write(data), client.channel)
156
157
158def sanitize(s):
159    if ":" in s:
160        address, port = s.split(":")
161        port = int(port)
162        return address, port
163    return s
164
165
166def main():
167    opts, args = parse_options()
168
169    source = sanitize(args[0])
170    target = sanitize(args[1])
171
172    if type(source) is not tuple:
173        print("ERROR: source address must specify port (address:port)")
174        raise SystemExit(-1)
175
176    if type(target) is not tuple:
177        print("ERROR: target address must specify port (address:port)")
178        raise SystemExit(-1)
179
180    system = PortForwarder(source, target)
181
182    if opts.daemon:
183        Daemon("portforward.pid").register(system)
184
185    if opts.debug:
186        Debugger().register(system)
187
188    system.run()
189
190if __name__ == "__main__":
191    main()