~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/conch/ssh/forwarding.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-01-17 14:52:35 UTC
  • mfrom: (1.1.5 upstream) (2.1.2 etch)
  • Revision ID: james.westby@ubuntu.com-20070117145235-btmig6qfmqfen0om
Tags: 2.5.0-0ubuntu1
New upstream version, compatible with python2.5.

Show diffs side-by-side

added added

removed removed

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