1
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
2
# See LICENSE for details.
6
"""This module contains the implementation of the TCP forwarding, which allows
7
clients and servers to forward arbitrary TCP data across the connection.
9
This module is unstable.
11
Maintainer: U{Paul Swartz<mailto:z3p@twistedmatrix.com>}
16
from twisted.internet import protocol, reactor
17
from twisted.python import log
19
import common, channel
21
class SSHListenForwardingFactory(protocol.Factory):
22
def __init__(self, connection, hostport, klass):
23
self.conn = connection
24
self.hostport = hostport # tuple
27
def buildProtocol(self, addr):
28
channel = self.klass(conn = self.conn)
29
client = SSHForwardingClient(channel)
30
channel.client = client
31
addrTuple = (addr.host, addr.port)
32
channelOpenData = packOpen_direct_tcpip(self.hostport, addrTuple)
33
self.conn.openChannel(channel, channelOpenData)
36
class SSHListenForwardingChannel(channel.SSHChannel):
38
def channelOpen(self, specificData):
39
log.msg('opened forwarding channel %s' % self.id)
40
if len(self.client.buf)>1:
41
b = self.client.buf[1:]
45
def openFailed(self, reason):
48
def dataReceived(self, data):
49
self.client.transport.write(data)
51
def eofReceived(self):
52
self.client.transport.loseConnection()
55
if hasattr(self, 'client'):
56
log.msg('closing local forwarding channel %s' % self.id)
57
self.client.transport.loseConnection()
60
class SSHListenClientForwardingChannel(SSHListenForwardingChannel):
64
class SSHListenServerForwardingChannel(SSHListenForwardingChannel):
66
name = 'forwarded-tcpip'
68
class SSHConnectForwardingChannel(channel.SSHChannel):
70
def __init__(self, hostport, *args, **kw):
71
channel.SSHChannel.__init__(self, *args, **kw)
72
self.hostport = hostport
76
def channelOpen(self, specificData):
77
cc = protocol.ClientCreator(reactor, SSHForwardingClient, self)
78
log.msg("connecting to %s:%i" % self.hostport)
79
cc.connectTCP(*self.hostport).addCallbacks(self._setClient, self._close)
81
def _setClient(self, client):
83
log.msg("connected to %s:%i" % self.hostport)
85
self.client.transport.write(self.clientBuf)
87
if self.client.buf[1:]:
88
self.write(self.client.buf[1:])
91
def _close(self, reason):
92
log.msg("failed to connect: %s" % reason)
95
def dataReceived(self, data):
97
self.client.transport.write(data)
99
self.clientBuf += data
103
log.msg('closed remote forwarding channel %s' % self.id)
104
if self.client.channel:
105
self.loseConnection()
106
self.client.transport.loseConnection()
109
def openConnectForwardingClient(remoteWindow, remoteMaxPacket, data, avatar):
110
remoteHP, origHP = unpackOpen_direct_tcpip(data)
111
return SSHConnectForwardingChannel(remoteHP,
112
remoteWindow=remoteWindow,
113
remoteMaxPacket=remoteMaxPacket,
116
class SSHForwardingClient(protocol.Protocol):
118
def __init__(self, channel):
119
self.channel = channel
122
def dataReceived(self, data):
126
self.channel.write(data)
128
def connectionLost(self, reason):
130
self.channel.loseConnection()
134
def packOpen_direct_tcpip((connHost, connPort), (origHost, origPort)):
135
"""Pack the data suitable for sending in a CHANNEL_OPEN packet.
137
conn = common.NS(connHost) + struct.pack('>L', connPort)
138
orig = common.NS(origHost) + struct.pack('>L', origPort)
141
packOpen_forwarded_tcpip = packOpen_direct_tcpip
143
def unpackOpen_direct_tcpip(data):
144
"""Unpack the data to a usable format.
146
connHost, rest = common.getNS(data)
147
connPort = int(struct.unpack('>L', rest[:4])[0])
148
origHost, rest = common.getNS(rest[4:])
149
origPort = int(struct.unpack('>L', rest[:4])[0])
150
return (connHost, connPort), (origHost, origPort)
152
unpackOpen_forwarded_tcpip = unpackOpen_direct_tcpip
154
def packGlobal_tcpip_forward((host, port)):
155
return common.NS(host) + struct.pack('>L', port)
157
def unpackGlobal_tcpip_forward(data):
158
host, rest = common.getNS(data)
159
port = int(struct.unpack('>L', rest[:4])[0])
162
"""This is how the data -> eof -> close stuff /should/ work.
164
debug3: channel 1: waiting for connection
165
debug1: channel 1: connected
166
debug1: channel 1: read<=0 rfd 7 len 0
167
debug1: channel 1: read failed
168
debug1: channel 1: close_read
169
debug1: channel 1: input open -> drain
170
debug1: channel 1: ibuf empty
171
debug1: channel 1: send eof
172
debug1: channel 1: input drain -> closed
173
debug1: channel 1: rcvd eof
174
debug1: channel 1: output open -> drain
175
debug1: channel 1: obuf empty
176
debug1: channel 1: close_write
177
debug1: channel 1: output drain -> closed
178
debug1: channel 1: rcvd close
179
debug3: channel 1: will not send data after close
180
debug1: channel 1: send close
181
debug1: channel 1: is dead