~jk0/nova/xs-ipv6

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/doc/conch/howto/conch_client.html

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE html  PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
 
2
  <head>
 
3
<title>Twisted Documentation: Writing a client with Twisted.Conch</title>
 
4
<link href="stylesheet.css" rel="stylesheet" type="text/css"/>
 
5
  </head>
 
6
 
 
7
  <body bgcolor="white">
 
8
    <h1 class="title">Writing a client with Twisted.Conch</h1>
 
9
    <div class="toc"><ol><li><a href="#auto0">Introduction</a></li><li><a href="#auto1">Writing a client</a></li><li><a href="#auto2">The Transport</a></li><li><a href="#auto3">The Authorization Client</a></li><li><a href="#auto4">The Connection</a></li><li><a href="#auto5">The Channel</a></li><li><a href="#auto6">The main() function</a></li></ol></div>
 
10
    <div class="content">
 
11
    <span/>
 
12
 
 
13
    <h2>Introduction<a name="auto0"/></h2>
 
14
 
 
15
<p>In the original days of computing, rsh/rlogin were used to connect to
 
16
remote computers and execute commands. These commands had the problem
 
17
that the passwords and commands were sent in the clear. To solve this
 
18
problem, the SSH protocol was created. Twisted.Conch implements the
 
19
second version of this protocol.</p>
 
20
 
 
21
    <h2>Writing a client<a name="auto1"/></h2>
 
22
 
 
23
<p>Writing a client with Conch involves sub-classing 4 classes: <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.conch.ssh.transport.SSHClientTransport.html" title="twisted.conch.ssh.transport.SSHClientTransport">twisted.conch.ssh.transport.SSHClientTransport</a></code>, <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.conch.ssh.userauth.SSHUserAuthClient.html" title="twisted.conch.ssh.userauth.SSHUserAuthClient">twisted.conch.ssh.userauth.SSHUserAuthClient</a></code>, <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.conch.ssh.connection.SSHConnection.html" title="twisted.conch.ssh.connection.SSHConnection">twisted.conch.ssh.connection.SSHConnection</a></code>, and <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.conch.ssh.channel.SSHChannel.html" title="twisted.conch.ssh.channel.SSHChannel">twisted.conch.ssh.channel.SSHChannel</a></code>. We'll start out
 
24
with <code class="python">SSHClientTransport</code> because it's the base 
 
25
of the client.</p>
 
26
 
 
27
<h2>The Transport<a name="auto2"/></h2>
 
28
 
 
29
<pre class="python"><p class="py-linenumber"> 1
 
30
 2
 
31
 3
 
32
 4
 
33
 5
 
34
 6
 
35
 7
 
36
 8
 
37
 9
 
38
10
 
39
11
 
40
12
 
41
13
 
42
14
 
43
</p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">conch</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">error</span>
 
44
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">conch</span>.<span class="py-src-variable">ssh</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">transport</span>
 
45
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">defer</span>
 
46
 
 
47
<span class="py-src-keyword">class</span> <span class="py-src-identifier">ClientTransport</span>(<span class="py-src-parameter">transport</span>.<span class="py-src-parameter">SSHClientTransport</span>):
 
48
 
 
49
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">verifyHostKey</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">pubKey</span>, <span class="py-src-parameter">fingerprint</span>):
 
50
        <span class="py-src-keyword">if</span> <span class="py-src-variable">fingerprint</span> != <span class="py-src-string">'b1:94:6a:c9:24:92:d2:34:7c:62:35:b4:d2:61:11:84'</span>:
 
51
            <span class="py-src-keyword">return</span> <span class="py-src-variable">defer</span>.<span class="py-src-variable">fail</span>(<span class="py-src-variable">error</span>.<span class="py-src-variable">ConchError</span>(<span class="py-src-string">'bad key'</span>))
 
52
        <span class="py-src-keyword">else</span>:
 
53
            <span class="py-src-keyword">return</span> <span class="py-src-variable">defer</span>.<span class="py-src-variable">succeed</span>(<span class="py-src-number">1</span>)
 
54
 
 
55
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">connectionSecure</span>(<span class="py-src-parameter">self</span>):
 
56
        <span class="py-src-variable">self</span>.<span class="py-src-variable">requestService</span>(<span class="py-src-variable">ClientUserAuth</span>(<span class="py-src-string">'user'</span>, <span class="py-src-variable">ClientConnection</span>()))
 
57
</pre>
 
58
 
 
59
<p>See how easy it is? <code class="python">SSHClientTransport</code>
 
60
handles the negotiation of encryption and the verification of keys
 
61
for you. The one security element that you as a client writer need to
 
62
implement is <code class="python">verifyHostKey()</code>. This method
 
63
is called with two strings: the public key sent by the server and its
 
64
fingerprint. You should verify the host key the server sends, either
 
65
by checking against a hard-coded value as in the example, or by asking
 
66
the user. <code class="python">verifyHostKey</code> returns a <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.internet.defer.Deferred.html" title="twisted.internet.defer.Deferred">twisted.internet.defer.Deferred</a></code> which gets a callback
 
67
if the host key is valid, or an errback if it is not. Note that in the
 
68
above, replace 'user' with the username you're attempting to ssh with,
 
69
for instance a call to <code class="python">os.getlogin()</code> for the
 
70
current user.</p>
 
71
 
 
72
<p>The second method you need to implement is <code class="python">connectionSecure()</code>. It is called when the
 
73
encryption is set up and other services can be run. The example requests
 
74
that the <code class="python">ClientUserAuth</code> service be started.
 
75
This service will be discussed next.</p>
 
76
 
 
77
<h2>The Authorization Client<a name="auto3"/></h2>
 
78
 
 
79
<pre class="python"><p class="py-linenumber"> 1
 
80
 2
 
81
 3
 
82
 4
 
83
 5
 
84
 6
 
85
 7
 
86
 8
 
87
 9
 
88
10
 
89
11
 
90
12
 
91
13
 
92
14
 
93
15
 
94
16
 
95
17
 
96
18
 
97
19
 
98
20
 
99
21
 
100
22
 
101
23
 
102
24
 
103
25
 
104
26
 
105
27
 
106
28
 
107
29
 
108
30
 
109
31
 
110
32
 
111
</p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">conch</span>.<span class="py-src-variable">ssh</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">keys</span>, <span class="py-src-variable">userauth</span>
 
112
 
 
113
<span class="py-src-comment"># these are the public/private keys from test_conch</span>
 
114
 
 
115
<span class="py-src-variable">publicKey</span> = <span class="py-src-string">'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAGEArzJx8OYOnJmzf4tfBEvLi8DVPrJ3\
 
116
/c9k2I/Az64fxjHf9imyRJbixtQhlH9lfNjUIx+4LmrJH5QNRsFporcHDKOTwTTYLh5KmRpslkYHR\
 
117
ivcJSkbh/C+BR3utDS555mV'</span>
 
118
 
 
119
<span class="py-src-variable">privateKey</span> = <span class="py-src-string">&quot;&quot;&quot;-----BEGIN RSA PRIVATE KEY-----
 
120
MIIByAIBAAJhAK8ycfDmDpyZs3+LXwRLy4vA1T6yd/3PZNiPwM+uH8Yx3/YpskSW
 
121
4sbUIZR/ZXzY1CMfuC5qyR+UDUbBaaK3Bwyjk8E02C4eSpkabJZGB0Yr3CUpG4fw
 
122
vgUd7rQ0ueeZlQIBIwJgbh+1VZfr7WftK5lu7MHtqE1S1vPWZQYE3+VUn8yJADyb
 
123
Z4fsZaCrzW9lkIqXkE3GIY+ojdhZhkO1gbG0118sIgphwSWKRxK0mvh6ERxKqIt1
 
124
xJEJO74EykXZV4oNJ8sjAjEA3J9r2ZghVhGN6V8DnQrTk24Td0E8hU8AcP0FVP+8
 
125
PQm/g/aXf2QQkQT+omdHVEJrAjEAy0pL0EBH6EVS98evDCBtQw22OZT52qXlAwZ2
 
126
gyTriKFVoqjeEjt3SZKKqXHSApP/AjBLpF99zcJJZRq2abgYlf9lv1chkrWqDHUu
 
127
DZttmYJeEfiFBBavVYIF1dOlZT0G8jMCMBc7sOSZodFnAiryP+Qg9otSBjJ3bQML
 
128
pSTqy7c3a2AScC/YyOwkDaICHnnD3XyjMwIxALRzl0tQEKMXs6hH8ToUdlLROCrP
 
129
EhQ0wahUTCk1gKA4uPD6TMTChavbh4K63OvbKg==
 
130
-----END RSA PRIVATE KEY-----&quot;&quot;&quot;</span>
 
131
 
 
132
<span class="py-src-keyword">class</span> <span class="py-src-identifier">ClientUserAuth</span>(<span class="py-src-parameter">userauth</span>.<span class="py-src-parameter">SSHUserAuthClient</span>):
 
133
 
 
134
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">getPassword</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">prompt</span> = <span class="py-src-parameter">None</span>):
 
135
        <span class="py-src-keyword">return</span> 
 
136
        <span class="py-src-comment"># this says we won't do password authentication</span>
 
137
 
 
138
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">getPublicKey</span>(<span class="py-src-parameter">self</span>):
 
139
        <span class="py-src-keyword">return</span> <span class="py-src-variable">keys</span>.<span class="py-src-variable">getPublicKeyString</span>(<span class="py-src-variable">data</span> = <span class="py-src-variable">publicKey</span>)
 
140
 
 
141
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">getPrivateKey</span>(<span class="py-src-parameter">self</span>):
 
142
        <span class="py-src-keyword">return</span> <span class="py-src-variable">defer</span>.<span class="py-src-variable">succeed</span>(<span class="py-src-variable">keys</span>.<span class="py-src-variable">getPrivateKeyObject</span>(<span class="py-src-variable">data</span> = <span class="py-src-variable">privateKey</span>))
 
143
</pre>
 
144
 
 
145
<p>Again, fairly simple. The <code class="python">SSHUserAuthClient</code> takes care of most
 
146
of the work, but the actual authentication data needs to be
 
147
supplied. <code class="python">getPassword()</code> asks for a
 
148
password, <code class="python">getPublicKey()</code> and <code class="python">getPrivateKey()</code> get public and private keys,
 
149
respectively. <code class="python">getPassword()</code> returns
 
150
a <code class="python">Deferred</code> that is called back with
 
151
the password to use. <code class="python">getPublicKey()</code>
 
152
returns the SSH key data for the public key to use. <code class="python">keys.getPublicKeyString()</code> will take
 
153
keys in OpenSSH and LSH format, and convert them to the
 
154
required format. <code class="python">getPrivateKey()</code>
 
155
returns a <code class="python">Deferred</code> which is
 
156
called back with the key object (as used in PyCrypto) for
 
157
the private key. <code class="python">getPassword()</code>
 
158
and <code class="python">getPrivateKey()</code> return <code class="python">Deferreds</code> because they may need to ask the user
 
159
for input.</p>
 
160
 
 
161
<p>Once the authentication is complete, <code class="python">SSHUserAuthClient</code> takes care of starting the code
 
162
<code class="python">SSHConnection</code> object given to it. Next, we'll
 
163
look at how to use the <code class="python">SSHConnection</code></p>
 
164
 
 
165
<h2>The Connection<a name="auto4"/></h2>
 
166
 
 
167
<pre class="python"><p class="py-linenumber">1
 
168
2
 
169
3
 
170
4
 
171
5
 
172
6
 
173
</p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">conch</span>.<span class="py-src-variable">ssh</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">connection</span>
 
174
 
 
175
<span class="py-src-keyword">class</span> <span class="py-src-identifier">ClientConnection</span>(<span class="py-src-parameter">connection</span>.<span class="py-src-parameter">SSHConnection</span>):
 
176
 
 
177
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">serviceStarted</span>(<span class="py-src-parameter">self</span>):
 
178
        <span class="py-src-variable">self</span>.<span class="py-src-variable">openChannel</span>(<span class="py-src-variable">CatChannel</span>(<span class="py-src-variable">conn</span> = <span class="py-src-variable">self</span>))
 
179
</pre>
 
180
 
 
181
<p><code class="python">SSHConnection</code> is the easiest,
 
182
as it's only responsible for starting the channels. It has
 
183
other methods, those will be examined when we look at <code class="python">SSHChannel</code>.</p>
 
184
 
 
185
<h2>The Channel<a name="auto5"/></h2>
 
186
 
 
187
<pre class="python"><p class="py-linenumber"> 1
 
188
 2
 
189
 3
 
190
 4
 
191
 5
 
192
 6
 
193
 7
 
194
 8
 
195
 9
 
196
10
 
197
11
 
198
12
 
199
13
 
200
14
 
201
15
 
202
16
 
203
17
 
204
18
 
205
19
 
206
20
 
207
21
 
208
22
 
209
</p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">conch</span>.<span class="py-src-variable">ssh</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">channel</span>, <span class="py-src-variable">common</span>
 
210
 
 
211
<span class="py-src-keyword">class</span> <span class="py-src-identifier">CatChannel</span>(<span class="py-src-parameter">channel</span>.<span class="py-src-parameter">SSHChannel</span>):
 
212
 
 
213
    <span class="py-src-variable">name</span> = <span class="py-src-string">'session'</span>
 
214
 
 
215
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">channelOpen</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">data</span>):
 
216
        <span class="py-src-variable">d</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">conn</span>.<span class="py-src-variable">sendRequest</span>(<span class="py-src-variable">self</span>, <span class="py-src-string">'exec'</span>, <span class="py-src-variable">common</span>.<span class="py-src-variable">NS</span>(<span class="py-src-string">'cat'</span>),
 
217
                                  <span class="py-src-variable">wantReply</span> = <span class="py-src-number">1</span>)
 
218
        <span class="py-src-variable">d</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">_cbSendRequest</span>)
 
219
        <span class="py-src-variable">self</span>.<span class="py-src-variable">catData</span> = <span class="py-src-string">''</span>
 
220
 
 
221
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">_cbSendRequest</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">ignored</span>):
 
222
        <span class="py-src-variable">self</span>.<span class="py-src-variable">write</span>(<span class="py-src-string">'This data will be echoed back to us by &quot;cat.&quot;\r\n'</span>)
 
223
        <span class="py-src-variable">self</span>.<span class="py-src-variable">conn</span>.<span class="py-src-variable">sendEOF</span>(<span class="py-src-variable">self</span>)
 
224
        <span class="py-src-variable">self</span>.<span class="py-src-variable">loseConnection</span>()
 
225
 
 
226
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">dataReceived</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">data</span>):
 
227
        <span class="py-src-variable">self</span>.<span class="py-src-variable">catData</span> += <span class="py-src-variable">data</span>
 
228
 
 
229
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">closed</span>(<span class="py-src-parameter">self</span>):
 
230
        <span class="py-src-keyword">print</span> <span class="py-src-string">'We got this from &quot;cat&quot;:'</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">catData</span>
 
231
</pre>
 
232
 
 
233
<p>Now that we've spent all this time getting the server and
 
234
client connected, here is where that work pays off. <code class="python">SSHChannel</code> is the interface between you and the
 
235
other side. This particular channel opens a session and plays with the
 
236
'cat' program, but your channel can implement anything, so long as the
 
237
server supports it.</p>
 
238
 
 
239
<p>The <code class="python">channelOpen()</code> method is
 
240
where everything gets started. It gets passed a chunk of data;
 
241
however, this chunk is usually nothing and can be ignored.
 
242
Our <code class="python">channelOpen()</code> initializes our
 
243
channel, and sends a request to the other side, using the
 
244
<code class="python">sendRequest()</code> method of the <code class="python">SSHConnection</code> object. Requests are used to send
 
245
events to the other side. We pass the method self so that it knows to
 
246
send the request for this channel. The 2nd argument of 'exec' tells the
 
247
server that we want to execute a command. The third argument is the data
 
248
that accompanies the request. <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/common.NS.html" title="common.NS">common.NS</a></code> encodes
 
249
the data as a length-prefixed string, which is how the server expects
 
250
the data. We also say that we want a reply saying that the process has a
 
251
been started. <code class="python">sendRequest()</code> then returns a
 
252
<code class="python">Deferred</code> which we add a callback for.</p>
 
253
 
 
254
<p>Once the callback fires, we send the data. <code class="python">SSHChannel</code> supports the <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/
 
255
.html" title="
 
256
">
 
257
twisted.internet.interface.Transport</a></code> interface, so
 
258
it can be given to Protocols to run them over the secure
 
259
connection. In our case, we just write the data directly. <code class="python">sendEOF()</code> does not follow the interface,
 
260
but Conch uses it to tell the other side that we will write no
 
261
more data. <code class="python">loseConnection()</code> shuts
 
262
down our side of the connection, but we will still receive data
 
263
through <code class="python">dataReceived()</code>. The <code class="python">closed()</code> method is called when both sides of the
 
264
connection are closed, and we use it to display the data we received
 
265
(which should be the same as the data we sent.)</p>
 
266
 
 
267
<p>Finally, let's actually invoke the code we've set up.</p>
 
268
 
 
269
<h2>The main() function<a name="auto6"/></h2>
 
270
<pre class="python"><p class="py-linenumber"> 1
 
271
 2
 
272
 3
 
273
 4
 
274
 5
 
275
 6
 
276
 7
 
277
 8
 
278
 9
 
279
10
 
280
</p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">protocol</span>, <span class="py-src-variable">reactor</span>
 
281
 
 
282
<span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
 
283
    <span class="py-src-variable">factory</span> = <span class="py-src-variable">protocol</span>.<span class="py-src-variable">ClientFactory</span>()
 
284
    <span class="py-src-variable">factory</span>.<span class="py-src-variable">protocol</span> = <span class="py-src-variable">ClientTransport</span>
 
285
    <span class="py-src-variable">reactor</span>.<span class="py-src-variable">connectTCP</span>(<span class="py-src-string">'localhost'</span>, <span class="py-src-number">22</span>, <span class="py-src-variable">factory</span>)
 
286
    <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
 
287
 
 
288
<span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">&quot;__main__&quot;</span>:
 
289
    <span class="py-src-variable">main</span>()
 
290
</pre>
 
291
 
 
292
<P>We call <code class="python">connectTCP()</code> to connect to
 
293
localhost, port 22 (the standard port for ssh), and pass it an instance
 
294
of <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.internet.protocol.ClientFactory.html" title="twisted.internet.protocol.ClientFactory">twisted.internet.protocol.ClientFactory</a></code>.
 
295
This instance has the attribute <code class="python">protocol</code>
 
296
set to our earlier <code class="python">ClientTransport</code>
 
297
class. Note that the protocol attribute is set to the class <code class="python">ClientTransport</code>, not an instance of
 
298
<code class="python">ClientTransport</code>! When the <code class="python">connectTCP</code> call completes, the protocol will be
 
299
called to create a <code class="python">ClientTransport()</code> object
 
300
- this then invokes all our previous work.</P>
 
301
 
 
302
<P>It's worth noting that in the example <code class="python">main()</code> 
 
303
routine, the <code class="python">reactor.run()</code> call never returns. 
 
304
If you want to make the program exit, call 
 
305
<code class="python">reactor.stop()</code> in the earlier 
 
306
<code class="python">closed()</code> method.</P>
 
307
 
 
308
<P>If you wish to observe the interactions in more detail, adding a call
 
309
to <code class="python">log.startLogging(sys.stdout, setStdout=0)</code>
 
310
before the <code class="python">reactor.run()</code> call will send all
 
311
logging to stdout.</P>
 
312
 
 
313
</div>
 
314
 
 
315
    <p><a href="index.html">Index</a></p>
 
316
    <span class="version">Version: 10.0.0</span>
 
317
  </body>
 
318
</html>
 
 
b'\\ No newline at end of file'