Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / conch / ssh / forwarding.py
1 # Copyright (c) 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: Paul Swartz
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 """