OLD | NEW |
| (Empty) |
1 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
2 # See LICENSE for details. | |
3 | |
4 # | |
5 | |
6 """ | |
7 This module contains the implementation of the TCP forwarding, which allows | |
8 clients and servers to forward arbitrary TCP data across the connection. | |
9 | |
10 Maintainer: U{Paul Swartz<mailto:z3p@twistedmatrix.com>} | |
11 """ | |
12 | |
13 import struct | |
14 | |
15 from twisted.internet import protocol, reactor | |
16 from twisted.python import log | |
17 | |
18 import common, channel | |
19 | |
20 class SSHListenForwardingFactory(protocol.Factory): | |
21 def __init__(self, connection, hostport, klass): | |
22 self.conn = connection | |
23 self.hostport = hostport # tuple | |
24 self.klass = klass | |
25 | |
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) | |
33 return client | |
34 | |
35 class SSHListenForwardingChannel(channel.SSHChannel): | |
36 | |
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:] | |
41 self.write(b) | |
42 self.client.buf = '' | |
43 | |
44 def openFailed(self, reason): | |
45 self.closed() | |
46 | |
47 def dataReceived(self, data): | |
48 self.client.transport.write(data) | |
49 | |
50 def eofReceived(self): | |
51 self.client.transport.loseConnection() | |
52 | |
53 def closed(self): | |
54 if hasattr(self, 'client'): | |
55 log.msg('closing local forwarding channel %s' % self.id) | |
56 self.client.transport.loseConnection() | |
57 del self.client | |
58 | |
59 class SSHListenClientForwardingChannel(SSHListenForwardingChannel): | |
60 | |
61 name = 'direct-tcpip' | |
62 | |
63 class SSHListenServerForwardingChannel(SSHListenForwardingChannel): | |
64 | |
65 name = 'forwarded-tcpip' | |
66 | |
67 class SSHConnectForwardingChannel(channel.SSHChannel): | |
68 | |
69 def __init__(self, hostport, *args, **kw): | |
70 channel.SSHChannel.__init__(self, *args, **kw) | |
71 self.hostport = hostport | |
72 self.client = None | |
73 self.clientBuf = '' | |
74 | |
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) | |
79 | |
80 def _setClient(self, client): | |
81 self.client = client | |
82 log.msg("connected to %s:%i" % self.hostport) | |
83 if self.clientBuf: | |
84 self.client.transport.write(self.clientBuf) | |
85 self.clientBuf = None | |
86 if self.client.buf[1:]: | |
87 self.write(self.client.buf[1:]) | |
88 self.client.buf = '' | |
89 | |
90 def _close(self, reason): | |
91 log.msg("failed to connect: %s" % reason) | |
92 self.loseConnection() | |
93 | |
94 def dataReceived(self, data): | |
95 if self.client: | |
96 self.client.transport.write(data) | |
97 else: | |
98 self.clientBuf += data | |
99 | |
100 def closed(self): | |
101 if self.client: | |
102 log.msg('closed remote forwarding channel %s' % self.id) | |
103 if self.client.channel: | |
104 self.loseConnection() | |
105 self.client.transport.loseConnection() | |
106 del self.client | |
107 | |
108 def openConnectForwardingClient(remoteWindow, remoteMaxPacket, data, avatar): | |
109 remoteHP, origHP = unpackOpen_direct_tcpip(data) | |
110 return SSHConnectForwardingChannel(remoteHP, | |
111 remoteWindow=remoteWindow, | |
112 remoteMaxPacket=remoteMaxPacket, | |
113 avatar=avatar) | |
114 | |
115 class SSHForwardingClient(protocol.Protocol): | |
116 | |
117 def __init__(self, channel): | |
118 self.channel = channel | |
119 self.buf = '\000' | |
120 | |
121 def dataReceived(self, data): | |
122 if self.buf: | |
123 self.buf += data | |
124 else: | |
125 self.channel.write(data) | |
126 | |
127 def connectionLost(self, reason): | |
128 if self.channel: | |
129 self.channel.loseConnection() | |
130 self.channel = None | |
131 | |
132 | |
133 def packOpen_direct_tcpip((connHost, connPort), (origHost, origPort)): | |
134 """Pack the data suitable for sending in a CHANNEL_OPEN packet. | |
135 """ | |
136 conn = common.NS(connHost) + struct.pack('>L', connPort) | |
137 orig = common.NS(origHost) + struct.pack('>L', origPort) | |
138 return conn + orig | |
139 | |
140 packOpen_forwarded_tcpip = packOpen_direct_tcpip | |
141 | |
142 def unpackOpen_direct_tcpip(data): | |
143 """Unpack the data to a usable format. | |
144 """ | |
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) | |
150 | |
151 unpackOpen_forwarded_tcpip = unpackOpen_direct_tcpip | |
152 | |
153 def packGlobal_tcpip_forward((host, port)): | |
154 return common.NS(host) + struct.pack('>L', port) | |
155 | |
156 def unpackGlobal_tcpip_forward(data): | |
157 host, rest = common.getNS(data) | |
158 port = int(struct.unpack('>L', rest[:4])[0]) | |
159 return host, port | |
160 | |
161 """This is how the data -> eof -> close stuff /should/ work. | |
162 | |
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 | |
181 """ | |
OLD | NEW |