1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
7 This module contains the implementation of the TCP forwarding, which allows
8 clients and servers to forward arbitrary TCP data across the connection.
10 Maintainer: Paul Swartz
15 from twisted.internet import protocol, reactor
16 from twisted.python import log
18 import common, channel
20 class SSHListenForwardingFactory(protocol.Factory):
21 def __init__(self, connection, hostport, klass):
22 self.conn = connection
23 self.hostport = hostport # tuple
26 def buildProtocol(self, addr):
27 channel = self.klass(conn = self.conn)
28 client = SSHForwardingClient(channel)
29 channel.client = client
30 addrTuple = (addr.host, addr.port)
31 channelOpenData = packOpen_direct_tcpip(self.hostport, addrTuple)
32 self.conn.openChannel(channel, channelOpenData)
35 class SSHListenForwardingChannel(channel.SSHChannel):
37 def channelOpen(self, specificData):
38 log.msg('opened forwarding channel %s' % self.id)
39 if len(self.client.buf)>1:
40 b = self.client.buf[1:]
44 def openFailed(self, reason):
47 def dataReceived(self, data):
48 self.client.transport.write(data)
50 def eofReceived(self):
51 self.client.transport.loseConnection()
54 if hasattr(self, 'client'):
55 log.msg('closing local forwarding channel %s' % self.id)
56 self.client.transport.loseConnection()
59 class SSHListenClientForwardingChannel(SSHListenForwardingChannel):
63 class SSHListenServerForwardingChannel(SSHListenForwardingChannel):
65 name = 'forwarded-tcpip'
67 class SSHConnectForwardingChannel(channel.SSHChannel):
69 def __init__(self, hostport, *args, **kw):
70 channel.SSHChannel.__init__(self, *args, **kw)
71 self.hostport = hostport
75 def channelOpen(self, specificData):
76 cc = protocol.ClientCreator(reactor, SSHForwardingClient, self)
77 log.msg("connecting to %s:%i" % self.hostport)
78 cc.connectTCP(*self.hostport).addCallbacks(self._setClient, self._close)
80 def _setClient(self, client):
82 log.msg("connected to %s:%i" % self.hostport)
84 self.client.transport.write(self.clientBuf)
86 if self.client.buf[1:]:
87 self.write(self.client.buf[1:])
90 def _close(self, reason):
91 log.msg("failed to connect: %s" % reason)
94 def dataReceived(self, data):
96 self.client.transport.write(data)
98 self.clientBuf += data
102 log.msg('closed remote forwarding channel %s' % self.id)
103 if self.client.channel:
104 self.loseConnection()
105 self.client.transport.loseConnection()
108 def openConnectForwardingClient(remoteWindow, remoteMaxPacket, data, avatar):
109 remoteHP, origHP = unpackOpen_direct_tcpip(data)
110 return SSHConnectForwardingChannel(remoteHP,
111 remoteWindow=remoteWindow,
112 remoteMaxPacket=remoteMaxPacket,
115 class SSHForwardingClient(protocol.Protocol):
117 def __init__(self, channel):
118 self.channel = channel
121 def dataReceived(self, data):
125 self.channel.write(data)
127 def connectionLost(self, reason):
129 self.channel.loseConnection()
133 def packOpen_direct_tcpip((connHost, connPort), (origHost, origPort)):
134 """Pack the data suitable for sending in a CHANNEL_OPEN packet.
136 conn = common.NS(connHost) + struct.pack('>L', connPort)
137 orig = common.NS(origHost) + struct.pack('>L', origPort)
140 packOpen_forwarded_tcpip = packOpen_direct_tcpip
142 def unpackOpen_direct_tcpip(data):
143 """Unpack the data to a usable format.
145 connHost, rest = common.getNS(data)
146 connPort = int(struct.unpack('>L', rest[:4])[0])
147 origHost, rest = common.getNS(rest[4:])
148 origPort = int(struct.unpack('>L', rest[:4])[0])
149 return (connHost, connPort), (origHost, origPort)
151 unpackOpen_forwarded_tcpip = unpackOpen_direct_tcpip
153 def packGlobal_tcpip_forward((host, port)):
154 return common.NS(host) + struct.pack('>L', port)
156 def unpackGlobal_tcpip_forward(data):
157 host, rest = common.getNS(data)
158 port = int(struct.unpack('>L', rest[:4])[0])
161 """This is how the data -> eof -> close stuff /should/ work.
163 debug3: channel 1: waiting for connection
164 debug1: channel 1: connected
165 debug1: channel 1: read<=0 rfd 7 len 0
166 debug1: channel 1: read failed
167 debug1: channel 1: close_read
168 debug1: channel 1: input open -> drain
169 debug1: channel 1: ibuf empty
170 debug1: channel 1: send eof
171 debug1: channel 1: input drain -> closed
172 debug1: channel 1: rcvd eof
173 debug1: channel 1: output open -> drain
174 debug1: channel 1: obuf empty
175 debug1: channel 1: close_write
176 debug1: channel 1: output drain -> closed
177 debug1: channel 1: rcvd close
178 debug3: channel 1: will not send data after close
179 debug1: channel 1: send close
180 debug1: channel 1: is dead