forwarding.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. This module contains the implementation of the TCP forwarding, which allows
  5. clients and servers to forward arbitrary TCP data across the connection.
  6. Maintainer: Paul Swartz
  7. """
  8. import struct
  9. from twisted.conch.ssh import channel, common
  10. from twisted.internet import protocol, reactor
  11. from twisted.internet.endpoints import HostnameEndpoint, connectProtocol
  12. class SSHListenForwardingFactory(protocol.Factory):
  13. def __init__(self, connection, hostport, klass):
  14. self.conn = connection
  15. self.hostport = hostport # tuple
  16. self.klass = klass
  17. def buildProtocol(self, addr):
  18. channel = self.klass(conn=self.conn)
  19. client = SSHForwardingClient(channel)
  20. channel.client = client
  21. addrTuple = (addr.host, addr.port)
  22. channelOpenData = packOpen_direct_tcpip(self.hostport, addrTuple)
  23. self.conn.openChannel(channel, channelOpenData)
  24. return client
  25. class SSHListenForwardingChannel(channel.SSHChannel):
  26. def channelOpen(self, specificData):
  27. self._log.info("opened forwarding channel {id}", id=self.id)
  28. if len(self.client.buf) > 1:
  29. b = self.client.buf[1:]
  30. self.write(b)
  31. self.client.buf = b""
  32. def openFailed(self, reason):
  33. self.closed()
  34. def dataReceived(self, data):
  35. self.client.transport.write(data)
  36. def eofReceived(self):
  37. self.client.transport.loseConnection()
  38. def closed(self):
  39. if hasattr(self, "client"):
  40. self._log.info("closing local forwarding channel {id}", id=self.id)
  41. self.client.transport.loseConnection()
  42. del self.client
  43. class SSHListenClientForwardingChannel(SSHListenForwardingChannel):
  44. name = b"direct-tcpip"
  45. class SSHListenServerForwardingChannel(SSHListenForwardingChannel):
  46. name = b"forwarded-tcpip"
  47. class SSHConnectForwardingChannel(channel.SSHChannel):
  48. """
  49. Channel used for handling server side forwarding request.
  50. It acts as a client for the remote forwarding destination.
  51. @ivar hostport: C{(host, port)} requested by client as forwarding
  52. destination.
  53. @type hostport: L{tuple} or a C{sequence}
  54. @ivar client: Protocol connected to the forwarding destination.
  55. @type client: L{protocol.Protocol}
  56. @ivar clientBuf: Data received while forwarding channel is not yet
  57. connected.
  58. @type clientBuf: L{bytes}
  59. @var _reactor: Reactor used for TCP connections.
  60. @type _reactor: A reactor.
  61. @ivar _channelOpenDeferred: Deferred used in testing to check the
  62. result of C{channelOpen}.
  63. @type _channelOpenDeferred: L{twisted.internet.defer.Deferred}
  64. """
  65. _reactor = reactor
  66. def __init__(self, hostport, *args, **kw):
  67. channel.SSHChannel.__init__(self, *args, **kw)
  68. self.hostport = hostport
  69. self.client = None
  70. self.clientBuf = b""
  71. def channelOpen(self, specificData):
  72. """
  73. See: L{channel.SSHChannel}
  74. """
  75. self._log.info(
  76. "connecting to {host}:{port}", host=self.hostport[0], port=self.hostport[1]
  77. )
  78. ep = HostnameEndpoint(self._reactor, self.hostport[0], self.hostport[1])
  79. d = connectProtocol(ep, SSHForwardingClient(self))
  80. d.addCallbacks(self._setClient, self._close)
  81. self._channelOpenDeferred = d
  82. def _setClient(self, client):
  83. """
  84. Called when the connection was established to the forwarding
  85. destination.
  86. @param client: Client protocol connected to the forwarding destination.
  87. @type client: L{protocol.Protocol}
  88. """
  89. self.client = client
  90. self._log.info(
  91. "connected to {host}:{port}", host=self.hostport[0], port=self.hostport[1]
  92. )
  93. if self.clientBuf:
  94. self.client.transport.write(self.clientBuf)
  95. self.clientBuf = None
  96. if self.client.buf[1:]:
  97. self.write(self.client.buf[1:])
  98. self.client.buf = b""
  99. def _close(self, reason):
  100. """
  101. Called when failed to connect to the forwarding destination.
  102. @param reason: Reason why connection failed.
  103. @type reason: L{twisted.python.failure.Failure}
  104. """
  105. self._log.error(
  106. "failed to connect to {host}:{port}: {reason}",
  107. host=self.hostport[0],
  108. port=self.hostport[1],
  109. reason=reason,
  110. )
  111. self.loseConnection()
  112. def dataReceived(self, data):
  113. """
  114. See: L{channel.SSHChannel}
  115. """
  116. if self.client:
  117. self.client.transport.write(data)
  118. else:
  119. self.clientBuf += data
  120. def closed(self):
  121. """
  122. See: L{channel.SSHChannel}
  123. """
  124. if self.client:
  125. self._log.info("closed remote forwarding channel {id}", id=self.id)
  126. if self.client.channel:
  127. self.loseConnection()
  128. self.client.transport.loseConnection()
  129. del self.client
  130. def openConnectForwardingClient(remoteWindow, remoteMaxPacket, data, avatar):
  131. remoteHP, origHP = unpackOpen_direct_tcpip(data)
  132. return SSHConnectForwardingChannel(
  133. remoteHP,
  134. remoteWindow=remoteWindow,
  135. remoteMaxPacket=remoteMaxPacket,
  136. avatar=avatar,
  137. )
  138. class SSHForwardingClient(protocol.Protocol):
  139. def __init__(self, channel):
  140. self.channel = channel
  141. self.buf = b"\000"
  142. def dataReceived(self, data):
  143. if self.buf:
  144. self.buf += data
  145. else:
  146. self.channel.write(data)
  147. def connectionLost(self, reason):
  148. if self.channel:
  149. self.channel.loseConnection()
  150. self.channel = None
  151. def packOpen_direct_tcpip(destination, source):
  152. """
  153. Pack the data suitable for sending in a CHANNEL_OPEN packet.
  154. @type destination: L{tuple}
  155. @param destination: A tuple of the (host, port) of the destination host.
  156. @type source: L{tuple}
  157. @param source: A tuple of the (host, port) of the source host.
  158. """
  159. (connHost, connPort) = destination
  160. (origHost, origPort) = source
  161. if isinstance(connHost, str):
  162. connHost = connHost.encode("utf-8")
  163. if isinstance(origHost, str):
  164. origHost = origHost.encode("utf-8")
  165. conn = common.NS(connHost) + struct.pack(">L", connPort)
  166. orig = common.NS(origHost) + struct.pack(">L", origPort)
  167. return conn + orig
  168. packOpen_forwarded_tcpip = packOpen_direct_tcpip
  169. def unpackOpen_direct_tcpip(data):
  170. """Unpack the data to a usable format."""
  171. connHost, rest = common.getNS(data)
  172. if isinstance(connHost, bytes):
  173. connHost = connHost.decode("utf-8")
  174. connPort = int(struct.unpack(">L", rest[:4])[0])
  175. origHost, rest = common.getNS(rest[4:])
  176. if isinstance(origHost, bytes):
  177. origHost = origHost.decode("utf-8")
  178. origPort = int(struct.unpack(">L", rest[:4])[0])
  179. return (connHost, connPort), (origHost, origPort)
  180. unpackOpen_forwarded_tcpip = unpackOpen_direct_tcpip
  181. def packGlobal_tcpip_forward(peer):
  182. """
  183. Pack the data for tcpip forwarding.
  184. @param peer: A tuple of the (host, port) .
  185. @type peer: L{tuple}
  186. """
  187. (host, port) = peer
  188. return common.NS(host) + struct.pack(">L", port)
  189. def unpackGlobal_tcpip_forward(data):
  190. host, rest = common.getNS(data)
  191. if isinstance(host, bytes):
  192. host = host.decode("utf-8")
  193. port = int(struct.unpack(">L", rest[:4])[0])
  194. return host, port
  195. """This is how the data -> eof -> close stuff /should/ work.
  196. debug3: channel 1: waiting for connection
  197. debug1: channel 1: connected
  198. debug1: channel 1: read<=0 rfd 7 len 0
  199. debug1: channel 1: read failed
  200. debug1: channel 1: close_read
  201. debug1: channel 1: input open -> drain
  202. debug1: channel 1: ibuf empty
  203. debug1: channel 1: send eof
  204. debug1: channel 1: input drain -> closed
  205. debug1: channel 1: rcvd eof
  206. debug1: channel 1: output open -> drain
  207. debug1: channel 1: obuf empty
  208. debug1: channel 1: close_write
  209. debug1: channel 1: output drain -> closed
  210. debug1: channel 1: rcvd close
  211. debug3: channel 1: will not send data after close
  212. debug1: channel 1: send close
  213. debug1: channel 1: is dead
  214. """