Initial import to Tizen
[profile/ivi/python-twisted.git] / doc / conch / howto / conch_client.html
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/12.1.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/12.1.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/12.1.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/12.1.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/12.1.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">Key</span>.<span class="py-src-variable">fromString</span>(<span class="py-src-variable">data</span> = <span class="py-src-variable">publicKey</span>).<span class="py-src-variable">blob</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">Key</span>.<span class="py-src-variable">fromString</span>(<span class="py-src-variable">data</span> = <span class="py-src-variable">privateKey</span>).<span class="py-src-variable">keyObject</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.Key.fromString()</code> will take
153 a key in OpenSSH or LSH format as a string, and convert it to the
154 required format. Alternatively, <code class="python">keys.Key.fromFile()</code> can be used instead, which
155 will take the filename of a key in OpenSSH and LSH format, and 
156 convert it to the required format. 
157 <code class="python">getPrivateKey()</code>
158 returns a <code class="python">Deferred</code> which is
159 called back with the key object (as used in PyCrypto) for
160 the private key. <code class="python">getPassword()</code>
161 and <code class="python">getPrivateKey()</code> return <code class="python">Deferreds</code> because they may need to ask the user
162 for input.</p>
163
164 <p>Once the authentication is complete, <code class="python">SSHUserAuthClient</code> takes care of starting the code 
165 <code class="python">SSHConnection</code> object given to it. Next, we'll
166 look at how to use the <code class="python">SSHConnection</code></p>
167
168 <h2>The Connection<a name="auto4"/></h2>
169
170 <pre class="python"><p class="py-linenumber">1
171 2
172 3
173 4
174 5
175 6
176 </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>
177
178 <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>):
179
180     <span class="py-src-keyword">def</span> <span class="py-src-identifier">serviceStarted</span>(<span class="py-src-parameter">self</span>):
181         <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>))
182 </pre>
183
184 <p><code class="python">SSHConnection</code> is the easiest,
185 as it's only responsible for starting the channels. It has
186 other methods, those will be examined when we look at <code class="python">SSHChannel</code>.</p>
187
188 <h2>The Channel<a name="auto5"/></h2>
189
190 <pre class="python"><p class="py-linenumber"> 1
191  2
192  3
193  4
194  5
195  6
196  7
197  8
198  9
199 10
200 11
201 12
202 13
203 14
204 15
205 16
206 17
207 18
208 19
209 20
210 21
211 22
212 </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>
213
214 <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>):
215
216     <span class="py-src-variable">name</span> = <span class="py-src-string">'session'</span>
217
218     <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>):
219         <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>),
220                                   <span class="py-src-variable">wantReply</span> = <span class="py-src-number">1</span>)
221         <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>)
222         <span class="py-src-variable">self</span>.<span class="py-src-variable">catData</span> = <span class="py-src-string">''</span>
223
224     <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>):
225         <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>)
226         <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>)
227         <span class="py-src-variable">self</span>.<span class="py-src-variable">loseConnection</span>()
228
229     <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>):
230         <span class="py-src-variable">self</span>.<span class="py-src-variable">catData</span> += <span class="py-src-variable">data</span>
231
232     <span class="py-src-keyword">def</span> <span class="py-src-identifier">closed</span>(<span class="py-src-parameter">self</span>):
233         <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>
234 </pre>
235
236 <p>Now that we've spent all this time getting the server and
237 client connected, here is where that work pays off. <code class="python">SSHChannel</code> is the interface between you and the
238 other side. This particular channel opens a session and plays with the
239 'cat' program, but your channel can implement anything, so long as the
240 server supports it.</p>
241
242 <p>The <code class="python">channelOpen()</code> method is
243 where everything gets started. It gets passed a chunk of data;
244 however, this chunk is usually nothing and can be ignored.
245 Our <code class="python">channelOpen()</code> initializes our
246 channel, and sends a request to the other side, using the 
247 <code class="python">sendRequest()</code> method of the <code class="python">SSHConnection</code> object. Requests are used to send
248 events to the other side. We pass the method self so that it knows to
249 send the request for this channel. The 2nd argument of 'exec' tells the
250 server that we want to execute a command. The third argument is the data
251 that accompanies the request. 
252 <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.conch.ssh.common.NS.html" title="twisted.conch.ssh.common.NS">common.NS</a></code> encodes
253 the data as a length-prefixed string, which is how the server expects
254 the data. We also say that we want a reply saying that the process has a
255 been started. <code class="python">sendRequest()</code> then returns a 
256 <code class="python">Deferred</code> which we add a callback for.</p>
257
258 <p>Once the callback fires, we send the data. <code class="python">SSHChannel</code> supports the 
259 <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.interfaces.ITransport.html" title="twisted.internet.interfaces.ITransport">twisted.internet.interfaces.ITransport</a></code> 
260 interface, so
261 it can be given to Protocols to run them over the secure
262 connection. In our case, we just write the data directly. <code class="python">sendEOF()</code> does not follow the interface,
263 but Conch uses it to tell the other side that we will write no
264 more data. <code class="python">loseConnection()</code> shuts
265 down our side of the connection, but we will still receive data
266 through <code class="python">dataReceived()</code>. The <code class="python">closed()</code> method is called when both sides of the
267 connection are closed, and we use it to display the data we received
268 (which should be the same as the data we sent.)</p>
269
270 <p>Finally, let's actually invoke the code we've set up.</p>
271
272 <h2>The main() function<a name="auto6"/></h2>
273 <pre class="python"><p class="py-linenumber"> 1
274  2
275  3
276  4
277  5
278  6
279  7
280  8
281  9
282 10
283 </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>
284
285 <span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
286     <span class="py-src-variable">factory</span> = <span class="py-src-variable">protocol</span>.<span class="py-src-variable">ClientFactory</span>()
287     <span class="py-src-variable">factory</span>.<span class="py-src-variable">protocol</span> = <span class="py-src-variable">ClientTransport</span>
288     <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>)
289     <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
290
291 <span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">&quot;__main__&quot;</span>:
292     <span class="py-src-variable">main</span>()
293 </pre>
294
295 <P>We call <code class="python">connectTCP()</code> to connect to
296 localhost, port 22 (the standard port for ssh), and pass it an instance
297 of <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.protocol.ClientFactory.html" title="twisted.internet.protocol.ClientFactory">twisted.internet.protocol.ClientFactory</a></code>.
298 This instance has the attribute <code class="python">protocol</code>
299 set to our earlier <code class="python">ClientTransport</code>
300 class. Note that the protocol attribute is set to the class <code class="python">ClientTransport</code>, not an instance of 
301 <code class="python">ClientTransport</code>! When the <code class="python">connectTCP</code> call completes, the protocol will be
302 called to create a <code class="python">ClientTransport()</code> object
303 - this then invokes all our previous work.</P>
304
305 <P>It's worth noting that in the example <code class="python">main()</code> 
306 routine, the <code class="python">reactor.run()</code> call never returns. 
307 If you want to make the program exit, call 
308 <code class="python">reactor.stop()</code> in the earlier 
309 <code class="python">closed()</code> method.</P>
310
311 <P>If you wish to observe the interactions in more detail, adding a call
312 to <code class="python">log.startLogging(sys.stdout, setStdout=0)</code>
313 before the <code class="python">reactor.run()</code> call will send all
314 logging to stdout.</P>
315
316 </div>
317
318     <p><a href="index.html">Index</a></p>
319     <span class="version">Version: 12.1.0</span>
320   </body>
321 </html>