/examples/portforward.py

https://bitbucket.org/prologic/circuits/ · Python · 191 lines · 91 code · 50 blank · 50 comment · 7 complexity · 3a644eb6d40366006ca7dfa0961242d9 MD5 · raw file

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