File: portforward.py

package info (click to toggle)
circuits 3.2.3-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,980 kB
  • sloc: python: 17,583; javascript: 3,226; makefile: 100
file content (198 lines) | stat: -rwxr-xr-x 5,299 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#!/usr/bin/env python
"""
A Port Forwarding Example

This example demonstrates slightly more complex features and behaviors
implementing a TCP/UDP Port Forwarder of network traffic. This can be used
as a simple tool to forward traffic from one port to another.

Example:
-------
    ./portforward.py 0.0.0.0:2222 127.0.0.1:22

This example also has support for daemonizing the process into the background.

"""

from optparse import OptionParser
from uuid import uuid4 as uuid

from circuits import Component, Debugger, handler
from circuits.app import Daemon
from circuits.net.events import close, connect, write
from circuits.net.sockets import TCPClient, TCPServer


__version__ = '0.2'

USAGE = '%prog [options] <srcaddr:srcport> <destaddr:destport>'
VERSION = '%prog v' + __version__


def parse_options():
    parser = OptionParser(usage=USAGE, version=VERSION)

    parser.add_option(
        '-d',
        '--daemon',
        action='store_true',
        default=False,
        dest='daemon',
        help='Enable daemon mode (fork into the background)',
    )

    parser.add_option(
        '',
        '--debug',
        action='store_true',
        default=False,
        dest='debug',
        help='Enable debug mode (verbose event output)',
    )

    opts, args = parser.parse_args()

    if len(args) < 2:
        parser.print_help()
        raise SystemExit(1)

    return opts, args


def _on_target_disconnected(self, event):
    """
    Disconnected Event Handler

    This unbound function will be later added as an event handler to a
    dynamically created and registered client instance and used to process
    Disconnected events of a connected client.
    """
    channel = event.channels[0]
    sock = self._sockets[channel]

    self.fire(close(sock), 'source')

    del self._sockets[channel]
    del self._clients[sock]


def _on_target_ready(self, component):
    """
    Ready Event Handler

    This unbound function will be later added as an event handler to a
    dynamically created and registered client instance and used to process
    Ready events of a registered client.
    """
    self.fire(connect(*self._target, secure=self._secure), component.channel)


def _on_target_read(self, event, data):
    """
    Read Event Handler

    This unbound function will be later added as an event handler to a
    dynamically created and registered client instance and used to process
    Read events of a connected client.
    """
    sock = self._sockets[event.channels[0]]
    self.fire(write(sock, data), 'source')


class PortForwarder(Component):
    def init(self, source, target, secure=False):
        self._source = source
        self._target = target
        self._secure = secure

        self._clients = {}
        self._sockets = {}

        # Setup our components and register them.
        server = TCPServer(self._source, secure=self._secure, channel='source')
        server.register(self)

    @handler('connect', channel='source')
    def _on_source_connect(self, sock, host, port):
        """
        Explicitly defined connect Event Handler

        This evens is triggered by the underlying TCPServer Component when
        a new client connection has been made.

        Here we dynamically create a Client instance, registere it and add
        custom event handlers to handle the events of the newly created
        client. The client is registered with a unique channel per connection.
        """
        bind = 0
        channel = uuid()

        client = TCPClient(bind, channel=channel)
        client.register(self)

        self.addHandler(
            handler('disconnected', channel=channel)(_on_target_disconnected),
        )

        self.addHandler(
            handler('ready', channel=channel)(_on_target_ready),
        )

        self.addHandler(
            handler('read', channel=channel)(_on_target_read),
        )

        self._clients[sock] = client
        self._sockets[client.channel] = sock

    @handler('read', channel='source')
    def _on_source_read(self, sock, data):
        """
        Explicitly defined Read Event Handler

        This evens is triggered by the underlying TCPServer Component when
        a connected client has some data ready to be processed.

        Here we simply fire a cooresponding write event to the cooresponding
        matching client which we lookup using the socket object as the key
        to determinte the unique id.
        """
        client = self._clients[sock]
        self.fire(write(data), client.channel)


def sanitize(s):
    if ':' in s:
        address, port = s.split(':')
        port = int(port)
        return address, port
    return s


def main():
    opts, args = parse_options()

    source = sanitize(args[0])
    target = sanitize(args[1])

    if type(source) is not tuple:
        print('ERROR: source address must specify port (address:port)')
        raise SystemExit(-1)

    if type(target) is not tuple:
        print('ERROR: target address must specify port (address:port)')
        raise SystemExit(-1)

    system = PortForwarder(source, target)

    if opts.daemon:
        Daemon('portforward.pid').register(system)

    if opts.debug:
        Debugger().register(system)

    system.run()


if __name__ == '__main__':
    main()