]>
Commit | Line | Data |
---|---|---|
1 | /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ | |
2 | /* | |
3 | Copyright (c) 2002-2010 ymnk, JCraft,Inc. All rights reserved. | |
4 | ||
5 | Redistribution and use in source and binary forms, with or without | |
6 | modification, are permitted provided that the following conditions are met: | |
7 | ||
8 | 1. Redistributions of source code must retain the above copyright notice, | |
9 | this list of conditions and the following disclaimer. | |
10 | ||
11 | 2. Redistributions in binary form must reproduce the above copyright | |
12 | notice, this list of conditions and the following disclaimer in | |
13 | the documentation and/or other materials provided with the distribution. | |
14 | ||
15 | 3. The names of the authors may not be used to endorse or promote products | |
16 | derived from this software without specific prior written permission. | |
17 | ||
18 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, | |
19 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
20 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, | |
21 | INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, | |
22 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, | |
24 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |
27 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | ||
30 | package com.jcraft.jsch; | |
31 | ||
32 | import java.io.*; | |
33 | import java.net.*; | |
34 | ||
35 | public class Session implements Runnable{ | |
36 | static private final String version="JSCH-0.1.43"; | |
37 | ||
38 | // http://ietf.org/internet-drafts/draft-ietf-secsh-assignednumbers-01.txt | |
39 | static final int SSH_MSG_DISCONNECT= 1; | |
40 | static final int SSH_MSG_IGNORE= 2; | |
41 | static final int SSH_MSG_UNIMPLEMENTED= 3; | |
42 | static final int SSH_MSG_DEBUG= 4; | |
43 | static final int SSH_MSG_SERVICE_REQUEST= 5; | |
44 | static final int SSH_MSG_SERVICE_ACCEPT= 6; | |
45 | static final int SSH_MSG_KEXINIT= 20; | |
46 | static final int SSH_MSG_NEWKEYS= 21; | |
47 | static final int SSH_MSG_KEXDH_INIT= 30; | |
48 | static final int SSH_MSG_KEXDH_REPLY= 31; | |
49 | static final int SSH_MSG_KEX_DH_GEX_GROUP= 31; | |
50 | static final int SSH_MSG_KEX_DH_GEX_INIT= 32; | |
51 | static final int SSH_MSG_KEX_DH_GEX_REPLY= 33; | |
52 | static final int SSH_MSG_KEX_DH_GEX_REQUEST= 34; | |
53 | static final int SSH_MSG_GLOBAL_REQUEST= 80; | |
54 | static final int SSH_MSG_REQUEST_SUCCESS= 81; | |
55 | static final int SSH_MSG_REQUEST_FAILURE= 82; | |
56 | static final int SSH_MSG_CHANNEL_OPEN= 90; | |
57 | static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION= 91; | |
58 | static final int SSH_MSG_CHANNEL_OPEN_FAILURE= 92; | |
59 | static final int SSH_MSG_CHANNEL_WINDOW_ADJUST= 93; | |
60 | static final int SSH_MSG_CHANNEL_DATA= 94; | |
61 | static final int SSH_MSG_CHANNEL_EXTENDED_DATA= 95; | |
62 | static final int SSH_MSG_CHANNEL_EOF= 96; | |
63 | static final int SSH_MSG_CHANNEL_CLOSE= 97; | |
64 | static final int SSH_MSG_CHANNEL_REQUEST= 98; | |
65 | static final int SSH_MSG_CHANNEL_SUCCESS= 99; | |
66 | static final int SSH_MSG_CHANNEL_FAILURE= 100; | |
67 | ||
68 | private static final int PACKET_MAX_SIZE = 256 * 1024; | |
69 | ||
70 | private byte[] V_S; // server version | |
71 | private byte[] V_C=Util.str2byte("SSH-2.0-"+version); // client version | |
72 | ||
73 | private byte[] I_C; // the payload of the client's SSH_MSG_KEXINIT | |
74 | private byte[] I_S; // the payload of the server's SSH_MSG_KEXINIT | |
75 | private byte[] K_S; // the host key | |
76 | ||
77 | private byte[] session_id; | |
78 | ||
79 | private byte[] IVc2s; | |
80 | private byte[] IVs2c; | |
81 | private byte[] Ec2s; | |
82 | private byte[] Es2c; | |
83 | private byte[] MACc2s; | |
84 | private byte[] MACs2c; | |
85 | ||
86 | private int seqi=0; | |
87 | private int seqo=0; | |
88 | ||
89 | String[] guess=null; | |
90 | private Cipher s2ccipher; | |
91 | private Cipher c2scipher; | |
92 | private MAC s2cmac; | |
93 | private MAC c2smac; | |
94 | //private byte[] mac_buf; | |
95 | private byte[] s2cmac_result1; | |
96 | private byte[] s2cmac_result2; | |
97 | ||
98 | private Compression deflater; | |
99 | private Compression inflater; | |
100 | ||
101 | private IO io; | |
102 | private Socket socket; | |
103 | private int timeout=0; | |
104 | ||
105 | private boolean isConnected=false; | |
106 | ||
107 | private boolean isAuthed=false; | |
108 | ||
109 | private Thread connectThread=null; | |
110 | private Object lock=new Object(); | |
111 | ||
112 | boolean x11_forwarding=false; | |
113 | boolean agent_forwarding=false; | |
114 | ||
115 | InputStream in=null; | |
116 | OutputStream out=null; | |
117 | ||
118 | static Random random; | |
119 | ||
120 | Buffer buf; | |
121 | Packet packet; | |
122 | ||
123 | SocketFactory socket_factory=null; | |
124 | ||
125 | private java.util.Hashtable config=null; | |
126 | ||
127 | private Proxy proxy=null; | |
128 | private UserInfo userinfo; | |
129 | ||
130 | private String hostKeyAlias=null; | |
131 | private int serverAliveInterval=0; | |
132 | private int serverAliveCountMax=1; | |
133 | ||
134 | protected boolean daemon_thread=false; | |
135 | ||
136 | private long kex_start_time=0L; | |
137 | ||
138 | String host="127.0.0.1"; | |
139 | int port=22; | |
140 | ||
141 | String username=null; | |
142 | byte[] password=null; | |
143 | ||
144 | JSch jsch; | |
145 | ||
146 | Session(JSch jsch) throws JSchException{ | |
147 | super(); | |
148 | this.jsch=jsch; | |
149 | buf=new Buffer(); | |
150 | packet=new Packet(buf); | |
151 | } | |
152 | ||
153 | public void connect() throws JSchException{ | |
154 | connect(timeout); | |
155 | } | |
156 | ||
157 | public void connect(int connectTimeout) throws JSchException{ | |
158 | if(isConnected){ | |
159 | throw new JSchException("session is already connected"); | |
160 | } | |
161 | ||
162 | io=new IO(); | |
163 | if(random==null){ | |
164 | try{ | |
165 | Class c=Class.forName(getConfig("random")); | |
166 | random=(Random)(c.newInstance()); | |
167 | } | |
168 | catch(Exception e){ | |
169 | throw new JSchException(e.toString(), e); | |
170 | } | |
171 | } | |
172 | Packet.setRandom(random); | |
173 | ||
174 | if(JSch.getLogger().isEnabled(Logger.INFO)){ | |
175 | JSch.getLogger().log(Logger.INFO, | |
176 | "Connecting to "+host+" port "+port); | |
177 | } | |
178 | ||
179 | try { | |
180 | int i, j; | |
181 | ||
182 | if(proxy==null){ | |
183 | InputStream in; | |
184 | OutputStream out; | |
185 | if(socket_factory==null){ | |
186 | socket=Util.createSocket(host, port, connectTimeout); | |
187 | in=socket.getInputStream(); | |
188 | out=socket.getOutputStream(); | |
189 | } | |
190 | else{ | |
191 | socket=socket_factory.createSocket(host, port); | |
192 | in=socket_factory.getInputStream(socket); | |
193 | out=socket_factory.getOutputStream(socket); | |
194 | } | |
195 | //if(timeout>0){ socket.setSoTimeout(timeout); } | |
196 | socket.setTcpNoDelay(true); | |
197 | io.setInputStream(in); | |
198 | io.setOutputStream(out); | |
199 | } | |
200 | else{ | |
201 | synchronized(proxy){ | |
202 | proxy.connect(socket_factory, host, port, connectTimeout); | |
203 | io.setInputStream(proxy.getInputStream()); | |
204 | io.setOutputStream(proxy.getOutputStream()); | |
205 | socket=proxy.getSocket(); | |
206 | } | |
207 | } | |
208 | ||
209 | if(connectTimeout>0 && socket!=null){ | |
210 | socket.setSoTimeout(connectTimeout); | |
211 | } | |
212 | ||
213 | isConnected=true; | |
214 | ||
215 | if(JSch.getLogger().isEnabled(Logger.INFO)){ | |
216 | JSch.getLogger().log(Logger.INFO, | |
217 | "Connection established"); | |
218 | } | |
219 | ||
220 | jsch.addSession(this); | |
221 | ||
222 | { | |
223 | // Some Cisco devices will miss to read '\n' if it is sent separately. | |
224 | byte[] foo=new byte[V_C.length+1]; | |
225 | System.arraycopy(V_C, 0, foo, 0, V_C.length); | |
226 | foo[foo.length-1]=(byte)'\n'; | |
227 | io.put(foo, 0, foo.length); | |
228 | } | |
229 | ||
230 | while(true){ | |
231 | i=0; | |
232 | j=0; | |
233 | while(i<buf.buffer.length){ | |
234 | j=io.getByte(); | |
235 | if(j<0)break; | |
236 | buf.buffer[i]=(byte)j; i++; | |
237 | if(j==10)break; | |
238 | } | |
239 | if(j<0){ | |
240 | throw new JSchException("connection is closed by foreign host"); | |
241 | } | |
242 | ||
243 | if(buf.buffer[i-1]==10){ // 0x0a | |
244 | i--; | |
245 | if(i>0 && buf.buffer[i-1]==13){ // 0x0d | |
246 | i--; | |
247 | } | |
248 | } | |
249 | ||
250 | if(i<=3 || | |
251 | ((i!=buf.buffer.length) && | |
252 | (buf.buffer[0]!='S'||buf.buffer[1]!='S'|| | |
253 | buf.buffer[2]!='H'||buf.buffer[3]!='-'))){ | |
254 | // It must not start with 'SSH-' | |
255 | //System.err.println(new String(buf.buffer, 0, i); | |
256 | continue; | |
257 | } | |
258 | ||
259 | if(i==buf.buffer.length || | |
260 | i<7 || // SSH-1.99 or SSH-2.0 | |
261 | (buf.buffer[4]=='1' && buf.buffer[6]!='9') // SSH-1.5 | |
262 | ){ | |
263 | throw new JSchException("invalid server's version string"); | |
264 | } | |
265 | break; | |
266 | } | |
267 | ||
268 | V_S=new byte[i]; System.arraycopy(buf.buffer, 0, V_S, 0, i); | |
269 | //System.err.println("V_S: ("+i+") ["+new String(V_S)+"]"); | |
270 | ||
271 | if(JSch.getLogger().isEnabled(Logger.INFO)){ | |
272 | JSch.getLogger().log(Logger.INFO, | |
273 | "Remote version string: "+Util.byte2str(V_S)); | |
274 | JSch.getLogger().log(Logger.INFO, | |
275 | "Local version string: "+Util.byte2str(V_C)); | |
276 | } | |
277 | ||
278 | send_kexinit(); | |
279 | ||
280 | buf=read(buf); | |
281 | if(buf.getCommand()!=SSH_MSG_KEXINIT){ | |
282 | in_kex=false; | |
283 | throw new JSchException("invalid protocol: "+buf.getCommand()); | |
284 | } | |
285 | ||
286 | if(JSch.getLogger().isEnabled(Logger.INFO)){ | |
287 | JSch.getLogger().log(Logger.INFO, | |
288 | "SSH_MSG_KEXINIT received"); | |
289 | } | |
290 | ||
291 | KeyExchange kex=receive_kexinit(buf); | |
292 | ||
293 | while(true){ | |
294 | buf=read(buf); | |
295 | if(kex.getState()==buf.getCommand()){ | |
296 | kex_start_time=System.currentTimeMillis(); | |
297 | boolean result=kex.next(buf); | |
298 | if(!result){ | |
299 | //System.err.println("verify: "+result); | |
300 | in_kex=false; | |
301 | throw new JSchException("verify: "+result); | |
302 | } | |
303 | } | |
304 | else{ | |
305 | in_kex=false; | |
306 | throw new JSchException("invalid protocol(kex): "+buf.getCommand()); | |
307 | } | |
308 | if(kex.getState()==KeyExchange.STATE_END){ | |
309 | break; | |
310 | } | |
311 | } | |
312 | ||
313 | try{ checkHost(host, port, kex); } | |
314 | catch(JSchException ee){ | |
315 | in_kex=false; | |
316 | throw ee; | |
317 | } | |
318 | ||
319 | send_newkeys(); | |
320 | ||
321 | // receive SSH_MSG_NEWKEYS(21) | |
322 | buf=read(buf); | |
323 | //System.err.println("read: 21 ? "+buf.getCommand()); | |
324 | if(buf.getCommand()==SSH_MSG_NEWKEYS){ | |
325 | ||
326 | if(JSch.getLogger().isEnabled(Logger.INFO)){ | |
327 | JSch.getLogger().log(Logger.INFO, | |
328 | "SSH_MSG_NEWKEYS received"); | |
329 | } | |
330 | ||
331 | receive_newkeys(buf, kex); | |
332 | } | |
333 | else{ | |
334 | in_kex=false; | |
335 | throw new JSchException("invalid protocol(newkyes): "+buf.getCommand()); | |
336 | } | |
337 | ||
338 | boolean auth=false; | |
339 | boolean auth_cancel=false; | |
340 | ||
341 | UserAuth ua=null; | |
342 | try{ | |
343 | Class c=Class.forName(getConfig("userauth.none")); | |
344 | ua=(UserAuth)(c.newInstance()); | |
345 | } | |
346 | catch(Exception e){ | |
347 | throw new JSchException(e.toString(), e); | |
348 | } | |
349 | ||
350 | auth=ua.start(this); | |
351 | ||
352 | String cmethods=getConfig("PreferredAuthentications"); | |
353 | String[] cmethoda=Util.split(cmethods, ","); | |
354 | ||
355 | String smethods=null; | |
356 | if(!auth){ | |
357 | smethods=((UserAuthNone)ua).getMethods(); | |
358 | if(smethods!=null){ | |
359 | smethods=smethods.toLowerCase(); | |
360 | } | |
361 | else{ | |
362 | // methods: publickey,password,keyboard-interactive | |
363 | //smethods="publickey,password,keyboard-interactive"; | |
364 | smethods=cmethods; | |
365 | } | |
366 | } | |
367 | ||
368 | String[] smethoda=Util.split(smethods, ","); | |
369 | ||
370 | int methodi=0; | |
371 | ||
372 | loop: | |
373 | while(true){ | |
374 | ||
375 | //System.err.println("methods: "+methods); | |
376 | ||
377 | while(!auth && | |
378 | cmethoda!=null && methodi<cmethoda.length){ | |
379 | ||
380 | String method=cmethoda[methodi++]; | |
381 | boolean acceptable=false; | |
382 | for(int k=0; k<smethoda.length; k++){ | |
383 | if(smethoda[k].equals(method)){ | |
384 | acceptable=true; | |
385 | break; | |
386 | } | |
387 | } | |
388 | if(!acceptable){ | |
389 | continue; | |
390 | } | |
391 | ||
392 | //System.err.println(" method: "+method); | |
393 | ||
394 | if(JSch.getLogger().isEnabled(Logger.INFO)){ | |
395 | String str="Authentications that can continue: "; | |
396 | for(int k=methodi-1; k<cmethoda.length; k++){ | |
397 | str+=cmethoda[k]; | |
398 | if(k+1<cmethoda.length) | |
399 | str+=","; | |
400 | } | |
401 | JSch.getLogger().log(Logger.INFO, | |
402 | str); | |
403 | JSch.getLogger().log(Logger.INFO, | |
404 | "Next authentication method: "+method); | |
405 | } | |
406 | ||
407 | ua=null; | |
408 | try{ | |
409 | Class c=null; | |
410 | if(getConfig("userauth."+method)!=null){ | |
411 | c=Class.forName(getConfig("userauth."+method)); | |
412 | ua=(UserAuth)(c.newInstance()); | |
413 | } | |
414 | } | |
415 | catch(Exception e){ | |
416 | if(JSch.getLogger().isEnabled(Logger.WARN)){ | |
417 | JSch.getLogger().log(Logger.WARN, | |
418 | "failed to load "+method+" method"); | |
419 | } | |
420 | } | |
421 | ||
422 | if(ua!=null){ | |
423 | auth_cancel=false; | |
424 | try{ | |
425 | auth=ua.start(this); | |
426 | if(auth && | |
427 | JSch.getLogger().isEnabled(Logger.INFO)){ | |
428 | JSch.getLogger().log(Logger.INFO, | |
429 | "Authentication succeeded ("+method+")."); | |
430 | } | |
431 | } | |
432 | catch(JSchAuthCancelException ee){ | |
433 | auth_cancel=true; | |
434 | } | |
435 | catch(JSchPartialAuthException ee){ | |
436 | String tmp = smethods; | |
437 | smethods=ee.getMethods(); | |
438 | smethoda=Util.split(smethods, ","); | |
439 | if(!tmp.equals(smethods)){ | |
440 | methodi=0; | |
441 | } | |
442 | //System.err.println("PartialAuth: "+methods); | |
443 | auth_cancel=false; | |
444 | continue loop; | |
445 | } | |
446 | catch(RuntimeException ee){ | |
447 | throw ee; | |
448 | } | |
449 | catch(Exception ee){ | |
450 | //System.err.println("ee: "+ee); // SSH_MSG_DISCONNECT: 2 Too many authentication failures | |
451 | break loop; | |
452 | } | |
453 | } | |
454 | } | |
455 | break; | |
456 | } | |
457 | ||
458 | if(!auth){ | |
459 | if(auth_cancel) | |
460 | throw new JSchException("Auth cancel"); | |
461 | throw new JSchException("Auth fail"); | |
462 | } | |
463 | ||
464 | if(connectTimeout>0 || timeout>0){ | |
465 | socket.setSoTimeout(timeout); | |
466 | } | |
467 | ||
468 | isAuthed=true; | |
469 | ||
470 | synchronized(lock){ | |
471 | if(isConnected){ | |
472 | connectThread=new Thread(this); | |
473 | connectThread.setName("Connect thread "+host+" session"); | |
474 | if(daemon_thread){ | |
475 | connectThread.setDaemon(daemon_thread); | |
476 | } | |
477 | connectThread.start(); | |
478 | } | |
479 | else{ | |
480 | // The session has been already down and | |
481 | // we don't have to start new thread. | |
482 | } | |
483 | } | |
484 | } | |
485 | catch(Exception e) { | |
486 | in_kex=false; | |
487 | if(isConnected){ | |
488 | try{ | |
489 | packet.reset(); | |
490 | buf.putByte((byte)SSH_MSG_DISCONNECT); | |
491 | buf.putInt(3); | |
492 | buf.putString(Util.str2byte(e.toString())); | |
493 | buf.putString(Util.str2byte("en")); | |
494 | write(packet); | |
495 | disconnect(); | |
496 | } | |
497 | catch(Exception ee){ | |
498 | } | |
499 | } | |
500 | isConnected=false; | |
501 | //e.printStackTrace(); | |
502 | if(e instanceof RuntimeException) throw (RuntimeException)e; | |
503 | if(e instanceof JSchException) throw (JSchException)e; | |
504 | throw new JSchException("Session.connect: "+e); | |
505 | } | |
506 | finally{ | |
507 | Util.bzero(this.password); | |
508 | this.password=null; | |
509 | } | |
510 | } | |
511 | ||
512 | private KeyExchange receive_kexinit(Buffer buf) throws Exception { | |
513 | int j=buf.getInt(); | |
514 | if(j!=buf.getLength()){ // packet was compressed and | |
515 | buf.getByte(); // j is the size of deflated packet. | |
516 | I_S=new byte[buf.index-5]; | |
517 | } | |
518 | else{ | |
519 | I_S=new byte[j-1-buf.getByte()]; | |
520 | } | |
521 | System.arraycopy(buf.buffer, buf.s, I_S, 0, I_S.length); | |
522 | ||
523 | if(!in_kex){ // We are in rekeying activated by the remote! | |
524 | send_kexinit(); | |
525 | } | |
526 | ||
527 | guess=KeyExchange.guess(I_S, I_C); | |
528 | if(guess==null){ | |
529 | throw new JSchException("Algorithm negotiation fail"); | |
530 | } | |
531 | ||
532 | if(!isAuthed && | |
533 | (guess[KeyExchange.PROPOSAL_ENC_ALGS_CTOS].equals("none") || | |
534 | (guess[KeyExchange.PROPOSAL_ENC_ALGS_STOC].equals("none")))){ | |
535 | throw new JSchException("NONE Cipher should not be chosen before authentification is successed."); | |
536 | } | |
537 | ||
538 | KeyExchange kex=null; | |
539 | try{ | |
540 | Class c=Class.forName(getConfig(guess[KeyExchange.PROPOSAL_KEX_ALGS])); | |
541 | kex=(KeyExchange)(c.newInstance()); | |
542 | } | |
543 | catch(Exception e){ | |
544 | throw new JSchException(e.toString(), e); | |
545 | } | |
546 | ||
547 | kex.init(this, V_S, V_C, I_S, I_C); | |
548 | return kex; | |
549 | } | |
550 | ||
551 | private boolean in_kex=false; | |
552 | public void rekey() throws Exception { | |
553 | send_kexinit(); | |
554 | } | |
555 | private void send_kexinit() throws Exception { | |
556 | if(in_kex) | |
557 | return; | |
558 | ||
559 | String cipherc2s=getConfig("cipher.c2s"); | |
560 | String ciphers2c=getConfig("cipher.s2c"); | |
561 | ||
562 | String[] not_available=checkCiphers(getConfig("CheckCiphers")); | |
563 | if(not_available!=null && not_available.length>0){ | |
564 | cipherc2s=Util.diffString(cipherc2s, not_available); | |
565 | ciphers2c=Util.diffString(ciphers2c, not_available); | |
566 | if(cipherc2s==null || ciphers2c==null){ | |
567 | throw new JSchException("There are not any available ciphers."); | |
568 | } | |
569 | } | |
570 | ||
571 | in_kex=true; | |
572 | kex_start_time=System.currentTimeMillis(); | |
573 | ||
574 | // byte SSH_MSG_KEXINIT(20) | |
575 | // byte[16] cookie (random bytes) | |
576 | // string kex_algorithms | |
577 | // string server_host_key_algorithms | |
578 | // string encryption_algorithms_client_to_server | |
579 | // string encryption_algorithms_server_to_client | |
580 | // string mac_algorithms_client_to_server | |
581 | // string mac_algorithms_server_to_client | |
582 | // string compression_algorithms_client_to_server | |
583 | // string compression_algorithms_server_to_client | |
584 | // string languages_client_to_server | |
585 | // string languages_server_to_client | |
586 | Buffer buf = new Buffer(); // send_kexinit may be invoked | |
587 | Packet packet = new Packet(buf); // by user thread. | |
588 | packet.reset(); | |
589 | buf.putByte((byte) SSH_MSG_KEXINIT); | |
590 | synchronized(random){ | |
591 | random.fill(buf.buffer, buf.index, 16); buf.skip(16); | |
592 | } | |
593 | buf.putString(Util.str2byte(getConfig("kex"))); | |
594 | buf.putString(Util.str2byte(getConfig("server_host_key"))); | |
595 | buf.putString(Util.str2byte(cipherc2s)); | |
596 | buf.putString(Util.str2byte(ciphers2c)); | |
597 | buf.putString(Util.str2byte(getConfig("mac.c2s"))); | |
598 | buf.putString(Util.str2byte(getConfig("mac.s2c"))); | |
599 | buf.putString(Util.str2byte(getConfig("compression.c2s"))); | |
600 | buf.putString(Util.str2byte(getConfig("compression.s2c"))); | |
601 | buf.putString(Util.str2byte(getConfig("lang.c2s"))); | |
602 | buf.putString(Util.str2byte(getConfig("lang.s2c"))); | |
603 | buf.putByte((byte)0); | |
604 | buf.putInt(0); | |
605 | ||
606 | buf.setOffSet(5); | |
607 | I_C=new byte[buf.getLength()]; | |
608 | buf.getByte(I_C); | |
609 | ||
610 | write(packet); | |
611 | ||
612 | if(JSch.getLogger().isEnabled(Logger.INFO)){ | |
613 | JSch.getLogger().log(Logger.INFO, | |
614 | "SSH_MSG_KEXINIT sent"); | |
615 | } | |
616 | } | |
617 | ||
618 | private void send_newkeys() throws Exception { | |
619 | // send SSH_MSG_NEWKEYS(21) | |
620 | packet.reset(); | |
621 | buf.putByte((byte)SSH_MSG_NEWKEYS); | |
622 | write(packet); | |
623 | ||
624 | if(JSch.getLogger().isEnabled(Logger.INFO)){ | |
625 | JSch.getLogger().log(Logger.INFO, | |
626 | "SSH_MSG_NEWKEYS sent"); | |
627 | } | |
628 | } | |
629 | ||
630 | private void checkHost(String chost, int port, KeyExchange kex) throws JSchException { | |
631 | String shkc=getConfig("StrictHostKeyChecking"); | |
632 | ||
633 | if(hostKeyAlias!=null){ | |
634 | chost=hostKeyAlias; | |
635 | } | |
636 | ||
637 | //System.err.println("shkc: "+shkc); | |
638 | ||
639 | byte[] K_S=kex.getHostKey(); | |
640 | String key_type=kex.getKeyType(); | |
641 | String key_fprint=kex.getFingerPrint(); | |
642 | ||
643 | if(hostKeyAlias==null && port!=22){ | |
644 | chost=("["+chost+"]:"+port); | |
645 | } | |
646 | ||
647 | // hostkey=new HostKey(chost, K_S); | |
648 | ||
649 | HostKeyRepository hkr=jsch.getHostKeyRepository(); | |
650 | int i=0; | |
651 | synchronized(hkr){ | |
652 | i=hkr.check(chost, K_S); | |
653 | } | |
654 | ||
655 | boolean insert=false; | |
656 | ||
657 | if((shkc.equals("ask") || shkc.equals("yes")) && | |
658 | i==HostKeyRepository.CHANGED){ | |
659 | String file=null; | |
660 | synchronized(hkr){ | |
661 | file=hkr.getKnownHostsRepositoryID(); | |
662 | } | |
663 | if(file==null){file="known_hosts";} | |
664 | ||
665 | boolean b=false; | |
666 | ||
667 | if(userinfo!=null){ | |
668 | String message= | |
669 | "WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!\n"+ | |
670 | "IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\n"+ | |
671 | "Someone could be eavesdropping on you right now (man-in-the-middle attack)!\n"+ | |
672 | "It is also possible that the "+key_type+" host key has just been changed.\n"+ | |
673 | "The fingerprint for the "+key_type+" key sent by the remote host is\n"+ | |
674 | key_fprint+".\n"+ | |
675 | "Please contact your system administrator.\n"+ | |
676 | "Add correct host key in "+file+" to get rid of this message."; | |
677 | ||
678 | if(shkc.equals("ask")){ | |
679 | b=userinfo.promptYesNo(message+ | |
680 | "\nDo you want to delete the old key and insert the new key?"); | |
681 | } | |
682 | else{ // shkc.equals("yes") | |
683 | userinfo.showMessage(message); | |
684 | } | |
685 | } | |
686 | ||
687 | if(!b){ | |
688 | throw new JSchException("HostKey has been changed: "+chost); | |
689 | } | |
690 | ||
691 | synchronized(hkr){ | |
692 | hkr.remove(chost, | |
693 | (key_type.equals("DSA") ? "ssh-dss" : "ssh-rsa"), | |
694 | null); | |
695 | insert=true; | |
696 | } | |
697 | } | |
698 | ||
699 | if((shkc.equals("ask") || shkc.equals("yes")) && | |
700 | (i!=HostKeyRepository.OK) && !insert){ | |
701 | if(shkc.equals("yes")){ | |
702 | throw new JSchException("reject HostKey: "+host); | |
703 | } | |
704 | //System.err.println("finger-print: "+key_fprint); | |
705 | if(userinfo!=null){ | |
706 | boolean foo=userinfo.promptYesNo( | |
707 | "The authenticity of host '"+host+"' can't be established.\n"+ | |
708 | key_type+" key fingerprint is "+key_fprint+".\n"+ | |
709 | "Are you sure you want to continue connecting?" | |
710 | ); | |
711 | if(!foo){ | |
712 | throw new JSchException("reject HostKey: "+host); | |
713 | } | |
714 | insert=true; | |
715 | } | |
716 | else{ | |
717 | if(i==HostKeyRepository.NOT_INCLUDED) | |
718 | throw new JSchException("UnknownHostKey: "+host+". "+key_type+" key fingerprint is "+key_fprint); | |
719 | else | |
720 | throw new JSchException("HostKey has been changed: "+host); | |
721 | } | |
722 | } | |
723 | ||
724 | if(shkc.equals("no") && | |
725 | HostKeyRepository.NOT_INCLUDED==i){ | |
726 | insert=true; | |
727 | } | |
728 | ||
729 | if(i==HostKeyRepository.OK && | |
730 | JSch.getLogger().isEnabled(Logger.INFO)){ | |
731 | JSch.getLogger().log(Logger.INFO, | |
732 | "Host '"+host+"' is known and mathces the "+key_type+" host key"); | |
733 | } | |
734 | ||
735 | if(insert && | |
736 | JSch.getLogger().isEnabled(Logger.WARN)){ | |
737 | JSch.getLogger().log(Logger.WARN, | |
738 | "Permanently added '"+host+"' ("+key_type+") to the list of known hosts."); | |
739 | } | |
740 | ||
741 | String hkh=getConfig("HashKnownHosts"); | |
742 | if(hkh.equals("yes") && (hkr instanceof KnownHosts)){ | |
743 | hostkey=((KnownHosts)hkr).createHashedHostKey(chost, K_S); | |
744 | } | |
745 | else{ | |
746 | hostkey=new HostKey(chost, K_S); | |
747 | } | |
748 | ||
749 | if(insert){ | |
750 | synchronized(hkr){ | |
751 | hkr.add(hostkey, userinfo); | |
752 | } | |
753 | ||
754 | } | |
755 | ||
756 | } | |
757 | ||
758 | //public void start(){ (new Thread(this)).start(); } | |
759 | ||
760 | public Channel openChannel(String type) throws JSchException{ | |
761 | if(!isConnected){ | |
762 | throw new JSchException("session is down"); | |
763 | } | |
764 | try{ | |
765 | Channel channel=Channel.getChannel(type); | |
766 | addChannel(channel); | |
767 | channel.init(); | |
768 | return channel; | |
769 | } | |
770 | catch(Exception e){ | |
771 | //e.printStackTrace(); | |
772 | } | |
773 | return null; | |
774 | } | |
775 | ||
776 | // encode will bin invoked in write with synchronization. | |
777 | public void encode(Packet packet) throws Exception{ | |
778 | //System.err.println("encode: "+packet.buffer.getCommand()); | |
779 | //System.err.println(" "+packet.buffer.index); | |
780 | //if(packet.buffer.getCommand()==96){ | |
781 | //Thread.dumpStack(); | |
782 | //} | |
783 | if(deflater!=null){ | |
784 | packet.buffer.index=deflater.compress(packet.buffer.buffer, | |
785 | 5, packet.buffer.index); | |
786 | } | |
787 | if(c2scipher!=null){ | |
788 | //packet.padding(c2scipher.getIVSize()); | |
789 | packet.padding(c2scipher_size); | |
790 | int pad=packet.buffer.buffer[4]; | |
791 | synchronized(random){ | |
792 | random.fill(packet.buffer.buffer, packet.buffer.index-pad, pad); | |
793 | } | |
794 | } | |
795 | else{ | |
796 | packet.padding(8); | |
797 | } | |
798 | ||
799 | if(c2smac!=null){ | |
800 | c2smac.update(seqo); | |
801 | c2smac.update(packet.buffer.buffer, 0, packet.buffer.index); | |
802 | c2smac.doFinal(packet.buffer.buffer, packet.buffer.index); | |
803 | } | |
804 | if(c2scipher!=null){ | |
805 | byte[] buf=packet.buffer.buffer; | |
806 | c2scipher.update(buf, 0, packet.buffer.index, buf, 0); | |
807 | } | |
808 | if(c2smac!=null){ | |
809 | packet.buffer.skip(c2smac.getBlockSize()); | |
810 | } | |
811 | } | |
812 | ||
813 | int[] uncompress_len=new int[1]; | |
814 | ||
815 | private int s2ccipher_size=8; | |
816 | private int c2scipher_size=8; | |
817 | public Buffer read(Buffer buf) throws Exception{ | |
818 | int j=0; | |
819 | while(true){ | |
820 | buf.reset(); | |
821 | io.getByte(buf.buffer, buf.index, s2ccipher_size); | |
822 | buf.index+=s2ccipher_size; | |
823 | if(s2ccipher!=null){ | |
824 | s2ccipher.update(buf.buffer, 0, s2ccipher_size, buf.buffer, 0); | |
825 | } | |
826 | j=((buf.buffer[0]<<24)&0xff000000)| | |
827 | ((buf.buffer[1]<<16)&0x00ff0000)| | |
828 | ((buf.buffer[2]<< 8)&0x0000ff00)| | |
829 | ((buf.buffer[3] )&0x000000ff); | |
830 | // RFC 4253 6.1. Maximum Packet Length | |
831 | if(j<5 || j>PACKET_MAX_SIZE){ | |
832 | start_discard(buf, s2ccipher, s2cmac, j, PACKET_MAX_SIZE); | |
833 | } | |
834 | int need = j+4-s2ccipher_size; | |
835 | //if(need<0){ | |
836 | // throw new IOException("invalid data"); | |
837 | //} | |
838 | if((buf.index+need)>buf.buffer.length){ | |
839 | byte[] foo=new byte[buf.index+need]; | |
840 | System.arraycopy(buf.buffer, 0, foo, 0, buf.index); | |
841 | buf.buffer=foo; | |
842 | } | |
843 | ||
844 | if((need%s2ccipher_size)!=0){ | |
845 | String message="Bad packet length "+need; | |
846 | if(JSch.getLogger().isEnabled(Logger.FATAL)){ | |
847 | JSch.getLogger().log(Logger.FATAL, message); | |
848 | } | |
849 | start_discard(buf, s2ccipher, s2cmac, j, PACKET_MAX_SIZE-s2ccipher_size); | |
850 | } | |
851 | ||
852 | if(need>0){ | |
853 | io.getByte(buf.buffer, buf.index, need); buf.index+=(need); | |
854 | if(s2ccipher!=null){ | |
855 | s2ccipher.update(buf.buffer, s2ccipher_size, need, buf.buffer, s2ccipher_size); | |
856 | } | |
857 | } | |
858 | ||
859 | if(s2cmac!=null){ | |
860 | s2cmac.update(seqi); | |
861 | s2cmac.update(buf.buffer, 0, buf.index); | |
862 | ||
863 | s2cmac.doFinal(s2cmac_result1, 0); | |
864 | io.getByte(s2cmac_result2, 0, s2cmac_result2.length); | |
865 | if(!java.util.Arrays.equals(s2cmac_result1, s2cmac_result2)){ | |
866 | if(need > PACKET_MAX_SIZE){ | |
867 | throw new IOException("MAC Error"); | |
868 | } | |
869 | start_discard(buf, s2ccipher, s2cmac, j, PACKET_MAX_SIZE-need); | |
870 | continue; | |
871 | } | |
872 | } | |
873 | ||
874 | seqi++; | |
875 | ||
876 | if(inflater!=null){ | |
877 | //inflater.uncompress(buf); | |
878 | int pad=buf.buffer[4]; | |
879 | uncompress_len[0]=buf.index-5-pad; | |
880 | byte[] foo=inflater.uncompress(buf.buffer, 5, uncompress_len); | |
881 | if(foo!=null){ | |
882 | buf.buffer=foo; | |
883 | buf.index=5+uncompress_len[0]; | |
884 | } | |
885 | else{ | |
886 | System.err.println("fail in inflater"); | |
887 | break; | |
888 | } | |
889 | } | |
890 | ||
891 | int type=buf.getCommand()&0xff; | |
892 | //System.err.println("read: "+type); | |
893 | if(type==SSH_MSG_DISCONNECT){ | |
894 | buf.rewind(); | |
895 | buf.getInt();buf.getShort(); | |
896 | int reason_code=buf.getInt(); | |
897 | byte[] description=buf.getString(); | |
898 | byte[] language_tag=buf.getString(); | |
899 | throw new JSchException("SSH_MSG_DISCONNECT: "+ | |
900 | reason_code+ | |
901 | " "+Util.byte2str(description)+ | |
902 | " "+Util.byte2str(language_tag)); | |
903 | //break; | |
904 | } | |
905 | else if(type==SSH_MSG_IGNORE){ | |
906 | } | |
907 | else if(type==SSH_MSG_UNIMPLEMENTED){ | |
908 | buf.rewind(); | |
909 | buf.getInt();buf.getShort(); | |
910 | int reason_id=buf.getInt(); | |
911 | if(JSch.getLogger().isEnabled(Logger.INFO)){ | |
912 | JSch.getLogger().log(Logger.INFO, | |
913 | "Received SSH_MSG_UNIMPLEMENTED for "+reason_id); | |
914 | } | |
915 | } | |
916 | else if(type==SSH_MSG_DEBUG){ | |
917 | buf.rewind(); | |
918 | buf.getInt();buf.getShort(); | |
919 | /* | |
920 | byte always_display=(byte)buf.getByte(); | |
921 | byte[] message=buf.getString(); | |
922 | byte[] language_tag=buf.getString(); | |
923 | System.err.println("SSH_MSG_DEBUG:"+ | |
924 | " "+Util.byte2str(message)+ | |
925 | " "+Util.byte2str(language_tag)); | |
926 | */ | |
927 | } | |
928 | else if(type==SSH_MSG_CHANNEL_WINDOW_ADJUST){ | |
929 | buf.rewind(); | |
930 | buf.getInt();buf.getShort(); | |
931 | Channel c=Channel.getChannel(buf.getInt(), this); | |
932 | if(c==null){ | |
933 | } | |
934 | else{ | |
935 | c.addRemoteWindowSize(buf.getInt()); | |
936 | } | |
937 | } | |
938 | else if(type==UserAuth.SSH_MSG_USERAUTH_SUCCESS){ | |
939 | isAuthed=true; | |
940 | if(inflater==null && deflater==null){ | |
941 | String method; | |
942 | method=guess[KeyExchange.PROPOSAL_COMP_ALGS_CTOS]; | |
943 | initDeflater(method); | |
944 | ||
945 | method=guess[KeyExchange.PROPOSAL_COMP_ALGS_STOC]; | |
946 | initInflater(method); | |
947 | } | |
948 | break; | |
949 | } | |
950 | else{ | |
951 | break; | |
952 | } | |
953 | } | |
954 | buf.rewind(); | |
955 | return buf; | |
956 | } | |
957 | ||
958 | private void start_discard(Buffer buf, Cipher cipher, MAC mac, | |
959 | int packet_length, int discard) throws JSchException, IOException{ | |
960 | MAC discard_mac = null; | |
961 | ||
962 | if(!cipher.isCBC()){ | |
963 | throw new JSchException("Packet corrupt"); | |
964 | } | |
965 | ||
966 | if(packet_length!=PACKET_MAX_SIZE && mac != null){ | |
967 | discard_mac = mac; | |
968 | } | |
969 | ||
970 | discard -= buf.index; | |
971 | ||
972 | while(discard>0){ | |
973 | buf.reset(); | |
974 | int len = discard>buf.buffer.length ? buf.buffer.length : discard; | |
975 | io.getByte(buf.buffer, 0, len); | |
976 | if(discard_mac!=null){ | |
977 | discard_mac.update(buf.buffer, 0, len); | |
978 | } | |
979 | discard -= len; | |
980 | } | |
981 | ||
982 | if(discard_mac!=null){ | |
983 | discard_mac.doFinal(buf.buffer, 0); | |
984 | } | |
985 | ||
986 | throw new JSchException("Packet corrupt"); | |
987 | } | |
988 | ||
989 | byte[] getSessionId(){ | |
990 | return session_id; | |
991 | } | |
992 | ||
993 | private void receive_newkeys(Buffer buf, KeyExchange kex) throws Exception { | |
994 | updateKeys(kex); | |
995 | in_kex=false; | |
996 | } | |
997 | private void updateKeys(KeyExchange kex) throws Exception{ | |
998 | byte[] K=kex.getK(); | |
999 | byte[] H=kex.getH(); | |
1000 | HASH hash=kex.getHash(); | |
1001 | ||
1002 | // String[] guess=kex.guess; | |
1003 | ||
1004 | if(session_id==null){ | |
1005 | session_id=new byte[H.length]; | |
1006 | System.arraycopy(H, 0, session_id, 0, H.length); | |
1007 | } | |
1008 | ||
1009 | /* | |
1010 | Initial IV client to server: HASH (K || H || "A" || session_id) | |
1011 | Initial IV server to client: HASH (K || H || "B" || session_id) | |
1012 | Encryption key client to server: HASH (K || H || "C" || session_id) | |
1013 | Encryption key server to client: HASH (K || H || "D" || session_id) | |
1014 | Integrity key client to server: HASH (K || H || "E" || session_id) | |
1015 | Integrity key server to client: HASH (K || H || "F" || session_id) | |
1016 | */ | |
1017 | ||
1018 | buf.reset(); | |
1019 | buf.putMPInt(K); | |
1020 | buf.putByte(H); | |
1021 | buf.putByte((byte)0x41); | |
1022 | buf.putByte(session_id); | |
1023 | hash.update(buf.buffer, 0, buf.index); | |
1024 | IVc2s=hash.digest(); | |
1025 | ||
1026 | int j=buf.index-session_id.length-1; | |
1027 | ||
1028 | buf.buffer[j]++; | |
1029 | hash.update(buf.buffer, 0, buf.index); | |
1030 | IVs2c=hash.digest(); | |
1031 | ||
1032 | buf.buffer[j]++; | |
1033 | hash.update(buf.buffer, 0, buf.index); | |
1034 | Ec2s=hash.digest(); | |
1035 | ||
1036 | buf.buffer[j]++; | |
1037 | hash.update(buf.buffer, 0, buf.index); | |
1038 | Es2c=hash.digest(); | |
1039 | ||
1040 | buf.buffer[j]++; | |
1041 | hash.update(buf.buffer, 0, buf.index); | |
1042 | MACc2s=hash.digest(); | |
1043 | ||
1044 | buf.buffer[j]++; | |
1045 | hash.update(buf.buffer, 0, buf.index); | |
1046 | MACs2c=hash.digest(); | |
1047 | ||
1048 | try{ | |
1049 | Class c; | |
1050 | String method; | |
1051 | ||
1052 | method=guess[KeyExchange.PROPOSAL_ENC_ALGS_STOC]; | |
1053 | c=Class.forName(getConfig(method)); | |
1054 | s2ccipher=(Cipher)(c.newInstance()); | |
1055 | while(s2ccipher.getBlockSize()>Es2c.length){ | |
1056 | buf.reset(); | |
1057 | buf.putMPInt(K); | |
1058 | buf.putByte(H); | |
1059 | buf.putByte(Es2c); | |
1060 | hash.update(buf.buffer, 0, buf.index); | |
1061 | byte[] foo=hash.digest(); | |
1062 | byte[] bar=new byte[Es2c.length+foo.length]; | |
1063 | System.arraycopy(Es2c, 0, bar, 0, Es2c.length); | |
1064 | System.arraycopy(foo, 0, bar, Es2c.length, foo.length); | |
1065 | Es2c=bar; | |
1066 | } | |
1067 | s2ccipher.init(Cipher.DECRYPT_MODE, Es2c, IVs2c); | |
1068 | s2ccipher_size=s2ccipher.getIVSize(); | |
1069 | ||
1070 | method=guess[KeyExchange.PROPOSAL_MAC_ALGS_STOC]; | |
1071 | c=Class.forName(getConfig(method)); | |
1072 | s2cmac=(MAC)(c.newInstance()); | |
1073 | s2cmac.init(MACs2c); | |
1074 | //mac_buf=new byte[s2cmac.getBlockSize()]; | |
1075 | s2cmac_result1=new byte[s2cmac.getBlockSize()]; | |
1076 | s2cmac_result2=new byte[s2cmac.getBlockSize()]; | |
1077 | ||
1078 | method=guess[KeyExchange.PROPOSAL_ENC_ALGS_CTOS]; | |
1079 | c=Class.forName(getConfig(method)); | |
1080 | c2scipher=(Cipher)(c.newInstance()); | |
1081 | while(c2scipher.getBlockSize()>Ec2s.length){ | |
1082 | buf.reset(); | |
1083 | buf.putMPInt(K); | |
1084 | buf.putByte(H); | |
1085 | buf.putByte(Ec2s); | |
1086 | hash.update(buf.buffer, 0, buf.index); | |
1087 | byte[] foo=hash.digest(); | |
1088 | byte[] bar=new byte[Ec2s.length+foo.length]; | |
1089 | System.arraycopy(Ec2s, 0, bar, 0, Ec2s.length); | |
1090 | System.arraycopy(foo, 0, bar, Ec2s.length, foo.length); | |
1091 | Ec2s=bar; | |
1092 | } | |
1093 | c2scipher.init(Cipher.ENCRYPT_MODE, Ec2s, IVc2s); | |
1094 | c2scipher_size=c2scipher.getIVSize(); | |
1095 | ||
1096 | method=guess[KeyExchange.PROPOSAL_MAC_ALGS_CTOS]; | |
1097 | c=Class.forName(getConfig(method)); | |
1098 | c2smac=(MAC)(c.newInstance()); | |
1099 | c2smac.init(MACc2s); | |
1100 | ||
1101 | method=guess[KeyExchange.PROPOSAL_COMP_ALGS_CTOS]; | |
1102 | initDeflater(method); | |
1103 | ||
1104 | method=guess[KeyExchange.PROPOSAL_COMP_ALGS_STOC]; | |
1105 | initInflater(method); | |
1106 | } | |
1107 | catch(Exception e){ | |
1108 | if(e instanceof JSchException) | |
1109 | throw e; | |
1110 | throw new JSchException(e.toString(), e); | |
1111 | //System.err.println("updatekeys: "+e); | |
1112 | } | |
1113 | } | |
1114 | ||
1115 | /*public*/ /*synchronized*/ void write(Packet packet, Channel c, int length) throws Exception{ | |
1116 | long t = getTimeout(); | |
1117 | while(true){ | |
1118 | if(in_kex){ | |
1119 | if(t>0L && (System.currentTimeMillis()-kex_start_time)>t){ | |
1120 | throw new JSchException("timeout in wating for rekeying process."); | |
1121 | } | |
1122 | try{Thread.sleep(10);} | |
1123 | catch(java.lang.InterruptedException e){}; | |
1124 | continue; | |
1125 | } | |
1126 | synchronized(c){ | |
1127 | if(c.rwsize>=length){ | |
1128 | c.rwsize-=length; | |
1129 | break; | |
1130 | } | |
1131 | } | |
1132 | if(c.close || !c.isConnected()){ | |
1133 | throw new IOException("channel is broken"); | |
1134 | } | |
1135 | ||
1136 | boolean sendit=false; | |
1137 | int s=0; | |
1138 | byte command=0; | |
1139 | int recipient=-1; | |
1140 | synchronized(c){ | |
1141 | if(c.rwsize>0){ | |
1142 | long len=c.rwsize; | |
1143 | if(len>length){ | |
1144 | len=length; | |
1145 | } | |
1146 | if(len!=length){ | |
1147 | s=packet.shift((int)len, (c2smac!=null ? c2smac.getBlockSize() : 0)); | |
1148 | } | |
1149 | command=packet.buffer.getCommand(); | |
1150 | recipient=c.getRecipient(); | |
1151 | length-=len; | |
1152 | c.rwsize-=len; | |
1153 | sendit=true; | |
1154 | } | |
1155 | } | |
1156 | if(sendit){ | |
1157 | _write(packet); | |
1158 | if(length==0){ | |
1159 | return; | |
1160 | } | |
1161 | packet.unshift(command, recipient, s, length); | |
1162 | } | |
1163 | ||
1164 | synchronized(c){ | |
1165 | if(in_kex){ | |
1166 | continue; | |
1167 | } | |
1168 | if(c.rwsize>=length){ | |
1169 | c.rwsize-=length; | |
1170 | break; | |
1171 | } | |
1172 | try{ | |
1173 | c.notifyme++; | |
1174 | c.wait(100); | |
1175 | } | |
1176 | catch(java.lang.InterruptedException e){ | |
1177 | } | |
1178 | finally{ | |
1179 | c.notifyme--; | |
1180 | } | |
1181 | } | |
1182 | ||
1183 | } | |
1184 | _write(packet); | |
1185 | } | |
1186 | ||
1187 | public void write(Packet packet) throws Exception{ | |
1188 | // System.err.println("in_kex="+in_kex+" "+(packet.buffer.getCommand())); | |
1189 | long t = getTimeout(); | |
1190 | while(in_kex){ | |
1191 | if(t>0L && (System.currentTimeMillis()-kex_start_time)>t){ | |
1192 | throw new JSchException("timeout in wating for rekeying process."); | |
1193 | } | |
1194 | byte command=packet.buffer.getCommand(); | |
1195 | //System.err.println("command: "+command); | |
1196 | if(command==SSH_MSG_KEXINIT || | |
1197 | command==SSH_MSG_NEWKEYS || | |
1198 | command==SSH_MSG_KEXDH_INIT || | |
1199 | command==SSH_MSG_KEXDH_REPLY || | |
1200 | command==SSH_MSG_KEX_DH_GEX_GROUP || | |
1201 | command==SSH_MSG_KEX_DH_GEX_INIT || | |
1202 | command==SSH_MSG_KEX_DH_GEX_REPLY || | |
1203 | command==SSH_MSG_KEX_DH_GEX_REQUEST || | |
1204 | command==SSH_MSG_DISCONNECT){ | |
1205 | break; | |
1206 | } | |
1207 | try{Thread.sleep(10);} | |
1208 | catch(java.lang.InterruptedException e){}; | |
1209 | } | |
1210 | _write(packet); | |
1211 | } | |
1212 | ||
1213 | private void _write(Packet packet) throws Exception{ | |
1214 | synchronized(lock){ | |
1215 | encode(packet); | |
1216 | if(io!=null){ | |
1217 | io.put(packet); | |
1218 | seqo++; | |
1219 | } | |
1220 | } | |
1221 | } | |
1222 | ||
1223 | Runnable thread; | |
1224 | public void run(){ | |
1225 | thread=this; | |
1226 | ||
1227 | byte[] foo; | |
1228 | Buffer buf=new Buffer(); | |
1229 | Packet packet=new Packet(buf); | |
1230 | int i=0; | |
1231 | Channel channel; | |
1232 | int[] start=new int[1]; | |
1233 | int[] length=new int[1]; | |
1234 | KeyExchange kex=null; | |
1235 | ||
1236 | int stimeout=0; | |
1237 | try{ | |
1238 | while(isConnected && | |
1239 | thread!=null){ | |
1240 | try{ | |
1241 | buf=read(buf); | |
1242 | stimeout=0; | |
1243 | } | |
1244 | catch(InterruptedIOException/*SocketTimeoutException*/ ee){ | |
1245 | if(!in_kex && stimeout<serverAliveCountMax){ | |
1246 | sendKeepAliveMsg(); | |
1247 | stimeout++; | |
1248 | continue; | |
1249 | } | |
1250 | throw ee; | |
1251 | } | |
1252 | ||
1253 | int msgType=buf.getCommand()&0xff; | |
1254 | ||
1255 | if(kex!=null && kex.getState()==msgType){ | |
1256 | kex_start_time=System.currentTimeMillis(); | |
1257 | boolean result=kex.next(buf); | |
1258 | if(!result){ | |
1259 | throw new JSchException("verify: "+result); | |
1260 | } | |
1261 | continue; | |
1262 | } | |
1263 | ||
1264 | switch(msgType){ | |
1265 | case SSH_MSG_KEXINIT: | |
1266 | //System.err.println("KEXINIT"); | |
1267 | kex=receive_kexinit(buf); | |
1268 | break; | |
1269 | ||
1270 | case SSH_MSG_NEWKEYS: | |
1271 | //System.err.println("NEWKEYS"); | |
1272 | send_newkeys(); | |
1273 | receive_newkeys(buf, kex); | |
1274 | kex=null; | |
1275 | break; | |
1276 | ||
1277 | case SSH_MSG_CHANNEL_DATA: | |
1278 | buf.getInt(); | |
1279 | buf.getByte(); | |
1280 | buf.getByte(); | |
1281 | i=buf.getInt(); | |
1282 | channel=Channel.getChannel(i, this); | |
1283 | foo=buf.getString(start, length); | |
1284 | if(channel==null){ | |
1285 | break; | |
1286 | } | |
1287 | ||
1288 | if(length[0]==0){ | |
1289 | break; | |
1290 | } | |
1291 | ||
1292 | try{ | |
1293 | channel.write(foo, start[0], length[0]); | |
1294 | } | |
1295 | catch(Exception e){ | |
1296 | //System.err.println(e); | |
1297 | try{channel.disconnect();}catch(Exception ee){} | |
1298 | break; | |
1299 | } | |
1300 | int len=length[0]; | |
1301 | channel.setLocalWindowSize(channel.lwsize-len); | |
1302 | if(channel.lwsize<channel.lwsize_max/2){ | |
1303 | packet.reset(); | |
1304 | buf.putByte((byte)SSH_MSG_CHANNEL_WINDOW_ADJUST); | |
1305 | buf.putInt(channel.getRecipient()); | |
1306 | buf.putInt(channel.lwsize_max-channel.lwsize); | |
1307 | write(packet); | |
1308 | channel.setLocalWindowSize(channel.lwsize_max); | |
1309 | } | |
1310 | break; | |
1311 | ||
1312 | case SSH_MSG_CHANNEL_EXTENDED_DATA: | |
1313 | buf.getInt(); | |
1314 | buf.getShort(); | |
1315 | i=buf.getInt(); | |
1316 | channel=Channel.getChannel(i, this); | |
1317 | buf.getInt(); // data_type_code == 1 | |
1318 | foo=buf.getString(start, length); | |
1319 | //System.err.println("stderr: "+new String(foo,start[0],length[0])); | |
1320 | if(channel==null){ | |
1321 | break; | |
1322 | } | |
1323 | ||
1324 | if(length[0]==0){ | |
1325 | break; | |
1326 | } | |
1327 | ||
1328 | channel.write_ext(foo, start[0], length[0]); | |
1329 | ||
1330 | len=length[0]; | |
1331 | channel.setLocalWindowSize(channel.lwsize-len); | |
1332 | if(channel.lwsize<channel.lwsize_max/2){ | |
1333 | packet.reset(); | |
1334 | buf.putByte((byte)SSH_MSG_CHANNEL_WINDOW_ADJUST); | |
1335 | buf.putInt(channel.getRecipient()); | |
1336 | buf.putInt(channel.lwsize_max-channel.lwsize); | |
1337 | write(packet); | |
1338 | channel.setLocalWindowSize(channel.lwsize_max); | |
1339 | } | |
1340 | break; | |
1341 | ||
1342 | case SSH_MSG_CHANNEL_WINDOW_ADJUST: | |
1343 | buf.getInt(); | |
1344 | buf.getShort(); | |
1345 | i=buf.getInt(); | |
1346 | channel=Channel.getChannel(i, this); | |
1347 | if(channel==null){ | |
1348 | break; | |
1349 | } | |
1350 | channel.addRemoteWindowSize(buf.getInt()); | |
1351 | break; | |
1352 | ||
1353 | case SSH_MSG_CHANNEL_EOF: | |
1354 | buf.getInt(); | |
1355 | buf.getShort(); | |
1356 | i=buf.getInt(); | |
1357 | channel=Channel.getChannel(i, this); | |
1358 | if(channel!=null){ | |
1359 | //channel.eof_remote=true; | |
1360 | //channel.eof(); | |
1361 | channel.eof_remote(); | |
1362 | } | |
1363 | /* | |
1364 | packet.reset(); | |
1365 | buf.putByte((byte)SSH_MSG_CHANNEL_EOF); | |
1366 | buf.putInt(channel.getRecipient()); | |
1367 | write(packet); | |
1368 | */ | |
1369 | break; | |
1370 | case SSH_MSG_CHANNEL_CLOSE: | |
1371 | buf.getInt(); | |
1372 | buf.getShort(); | |
1373 | i=buf.getInt(); | |
1374 | channel=Channel.getChannel(i, this); | |
1375 | if(channel!=null){ | |
1376 | // channel.close(); | |
1377 | channel.disconnect(); | |
1378 | } | |
1379 | /* | |
1380 | if(Channel.pool.size()==0){ | |
1381 | thread=null; | |
1382 | } | |
1383 | */ | |
1384 | break; | |
1385 | case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: | |
1386 | buf.getInt(); | |
1387 | buf.getShort(); | |
1388 | i=buf.getInt(); | |
1389 | channel=Channel.getChannel(i, this); | |
1390 | if(channel==null){ | |
1391 | //break; | |
1392 | } | |
1393 | int r=buf.getInt(); | |
1394 | long rws=buf.getUInt(); | |
1395 | int rps=buf.getInt(); | |
1396 | ||
1397 | channel.setRemoteWindowSize(rws); | |
1398 | channel.setRemotePacketSize(rps); | |
1399 | channel.setRecipient(r); | |
1400 | break; | |
1401 | case SSH_MSG_CHANNEL_OPEN_FAILURE: | |
1402 | buf.getInt(); | |
1403 | buf.getShort(); | |
1404 | i=buf.getInt(); | |
1405 | channel=Channel.getChannel(i, this); | |
1406 | if(channel==null){ | |
1407 | //break; | |
1408 | } | |
1409 | int reason_code=buf.getInt(); | |
1410 | //foo=buf.getString(); // additional textual information | |
1411 | //foo=buf.getString(); // language tag | |
1412 | channel.exitstatus=reason_code; | |
1413 | channel.close=true; | |
1414 | channel.eof_remote=true; | |
1415 | channel.setRecipient(0); | |
1416 | break; | |
1417 | case SSH_MSG_CHANNEL_REQUEST: | |
1418 | buf.getInt(); | |
1419 | buf.getShort(); | |
1420 | i=buf.getInt(); | |
1421 | foo=buf.getString(); | |
1422 | boolean reply=(buf.getByte()!=0); | |
1423 | channel=Channel.getChannel(i, this); | |
1424 | if(channel!=null){ | |
1425 | byte reply_type=(byte)SSH_MSG_CHANNEL_FAILURE; | |
1426 | if((Util.byte2str(foo)).equals("exit-status")){ | |
1427 | i=buf.getInt(); // exit-status | |
1428 | channel.setExitStatus(i); | |
1429 | reply_type=(byte)SSH_MSG_CHANNEL_SUCCESS; | |
1430 | } | |
1431 | if(reply){ | |
1432 | packet.reset(); | |
1433 | buf.putByte(reply_type); | |
1434 | buf.putInt(channel.getRecipient()); | |
1435 | write(packet); | |
1436 | } | |
1437 | } | |
1438 | else{ | |
1439 | } | |
1440 | break; | |
1441 | case SSH_MSG_CHANNEL_OPEN: | |
1442 | buf.getInt(); | |
1443 | buf.getShort(); | |
1444 | foo=buf.getString(); | |
1445 | String ctyp=Util.byte2str(foo); | |
1446 | if(!"forwarded-tcpip".equals(ctyp) && | |
1447 | !("x11".equals(ctyp) && x11_forwarding) && | |
1448 | !("auth-agent@openssh.com".equals(ctyp) && agent_forwarding)){ | |
1449 | //System.err.println("Session.run: CHANNEL OPEN "+ctyp); | |
1450 | //throw new IOException("Session.run: CHANNEL OPEN "+ctyp); | |
1451 | packet.reset(); | |
1452 | buf.putByte((byte)SSH_MSG_CHANNEL_OPEN_FAILURE); | |
1453 | buf.putInt(buf.getInt()); | |
1454 | buf.putInt(Channel.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED); | |
1455 | buf.putString(Util.empty); | |
1456 | buf.putString(Util.empty); | |
1457 | write(packet); | |
1458 | } | |
1459 | else{ | |
1460 | channel=Channel.getChannel(ctyp); | |
1461 | addChannel(channel); | |
1462 | channel.getData(buf); | |
1463 | channel.init(); | |
1464 | ||
1465 | Thread tmp=new Thread(channel); | |
1466 | tmp.setName("Channel "+ctyp+" "+host); | |
1467 | if(daemon_thread){ | |
1468 | tmp.setDaemon(daemon_thread); | |
1469 | } | |
1470 | tmp.start(); | |
1471 | break; | |
1472 | } | |
1473 | case SSH_MSG_CHANNEL_SUCCESS: | |
1474 | buf.getInt(); | |
1475 | buf.getShort(); | |
1476 | i=buf.getInt(); | |
1477 | channel=Channel.getChannel(i, this); | |
1478 | if(channel==null){ | |
1479 | break; | |
1480 | } | |
1481 | channel.reply=1; | |
1482 | break; | |
1483 | case SSH_MSG_CHANNEL_FAILURE: | |
1484 | buf.getInt(); | |
1485 | buf.getShort(); | |
1486 | i=buf.getInt(); | |
1487 | channel=Channel.getChannel(i, this); | |
1488 | if(channel==null){ | |
1489 | break; | |
1490 | } | |
1491 | channel.reply=0; | |
1492 | break; | |
1493 | case SSH_MSG_GLOBAL_REQUEST: | |
1494 | buf.getInt(); | |
1495 | buf.getShort(); | |
1496 | foo=buf.getString(); // request name | |
1497 | reply=(buf.getByte()!=0); | |
1498 | if(reply){ | |
1499 | packet.reset(); | |
1500 | buf.putByte((byte)SSH_MSG_REQUEST_FAILURE); | |
1501 | write(packet); | |
1502 | } | |
1503 | break; | |
1504 | case SSH_MSG_REQUEST_FAILURE: | |
1505 | case SSH_MSG_REQUEST_SUCCESS: | |
1506 | Thread t=grr.getThread(); | |
1507 | if(t!=null){ | |
1508 | grr.setReply(msgType==SSH_MSG_REQUEST_SUCCESS? 1 : 0); | |
1509 | t.interrupt(); | |
1510 | } | |
1511 | break; | |
1512 | default: | |
1513 | //System.err.println("Session.run: unsupported type "+msgType); | |
1514 | throw new IOException("Unknown SSH message type "+msgType); | |
1515 | } | |
1516 | } | |
1517 | } | |
1518 | catch(Exception e){ | |
1519 | in_kex=false; | |
1520 | if(JSch.getLogger().isEnabled(Logger.INFO)){ | |
1521 | JSch.getLogger().log(Logger.INFO, | |
1522 | "Caught an exception, leaving main loop due to " + e.getMessage()); | |
1523 | } | |
1524 | //System.err.println("# Session.run"); | |
1525 | //e.printStackTrace(); | |
1526 | } | |
1527 | try{ | |
1528 | disconnect(); | |
1529 | } | |
1530 | catch(NullPointerException e){ | |
1531 | //System.err.println("@1"); | |
1532 | //e.printStackTrace(); | |
1533 | } | |
1534 | catch(Exception e){ | |
1535 | //System.err.println("@2"); | |
1536 | //e.printStackTrace(); | |
1537 | } | |
1538 | isConnected=false; | |
1539 | } | |
1540 | ||
1541 | public void disconnect(){ | |
1542 | if(!isConnected) return; | |
1543 | //System.err.println(this+": disconnect"); | |
1544 | //Thread.dumpStack(); | |
1545 | if(JSch.getLogger().isEnabled(Logger.INFO)){ | |
1546 | JSch.getLogger().log(Logger.INFO, | |
1547 | "Disconnecting from "+host+" port "+port); | |
1548 | } | |
1549 | /* | |
1550 | for(int i=0; i<Channel.pool.size(); i++){ | |
1551 | try{ | |
1552 | Channel c=((Channel)(Channel.pool.elementAt(i))); | |
1553 | if(c.session==this) c.eof(); | |
1554 | } | |
1555 | catch(Exception e){ | |
1556 | } | |
1557 | } | |
1558 | */ | |
1559 | ||
1560 | Channel.disconnect(this); | |
1561 | ||
1562 | isConnected=false; | |
1563 | ||
1564 | PortWatcher.delPort(this); | |
1565 | ChannelForwardedTCPIP.delPort(this); | |
1566 | ||
1567 | synchronized(lock){ | |
1568 | if(connectThread!=null){ | |
1569 | Thread.yield(); | |
1570 | connectThread.interrupt(); | |
1571 | connectThread=null; | |
1572 | } | |
1573 | } | |
1574 | thread=null; | |
1575 | try{ | |
1576 | if(io!=null){ | |
1577 | if(io.in!=null) io.in.close(); | |
1578 | if(io.out!=null) io.out.close(); | |
1579 | if(io.out_ext!=null) io.out_ext.close(); | |
1580 | } | |
1581 | if(proxy==null){ | |
1582 | if(socket!=null) | |
1583 | socket.close(); | |
1584 | } | |
1585 | else{ | |
1586 | synchronized(proxy){ | |
1587 | proxy.close(); | |
1588 | } | |
1589 | proxy=null; | |
1590 | } | |
1591 | } | |
1592 | catch(Exception e){ | |
1593 | // e.printStackTrace(); | |
1594 | } | |
1595 | io=null; | |
1596 | socket=null; | |
1597 | // synchronized(jsch.pool){ | |
1598 | // jsch.pool.removeElement(this); | |
1599 | // } | |
1600 | ||
1601 | jsch.removeSession(this); | |
1602 | ||
1603 | //System.gc(); | |
1604 | } | |
1605 | ||
1606 | public int setPortForwardingL(int lport, String host, int rport) throws JSchException{ | |
1607 | return setPortForwardingL("127.0.0.1", lport, host, rport); | |
1608 | } | |
1609 | public int setPortForwardingL(String boundaddress, int lport, String host, int rport) throws JSchException{ | |
1610 | return setPortForwardingL(boundaddress, lport, host, rport, null); | |
1611 | } | |
1612 | public int setPortForwardingL(String boundaddress, int lport, String host, int rport, ServerSocketFactory ssf) throws JSchException{ | |
1613 | PortWatcher pw=PortWatcher.addPort(this, boundaddress, lport, host, rport, ssf); | |
1614 | Thread tmp=new Thread(pw); | |
1615 | tmp.setName("PortWatcher Thread for "+host); | |
1616 | if(daemon_thread){ | |
1617 | tmp.setDaemon(daemon_thread); | |
1618 | } | |
1619 | tmp.start(); | |
1620 | return pw.lport; | |
1621 | } | |
1622 | public void delPortForwardingL(int lport) throws JSchException{ | |
1623 | delPortForwardingL("127.0.0.1", lport); | |
1624 | } | |
1625 | public void delPortForwardingL(String boundaddress, int lport) throws JSchException{ | |
1626 | PortWatcher.delPort(this, boundaddress, lport); | |
1627 | } | |
1628 | public String[] getPortForwardingL() throws JSchException{ | |
1629 | return PortWatcher.getPortForwarding(this); | |
1630 | } | |
1631 | ||
1632 | public void setPortForwardingR(int rport, String host, int lport) throws JSchException{ | |
1633 | setPortForwardingR(null, rport, host, lport, (SocketFactory)null); | |
1634 | } | |
1635 | public void setPortForwardingR(String bind_address, int rport, String host, int lport) throws JSchException{ | |
1636 | setPortForwardingR(bind_address, rport, host, lport, (SocketFactory)null); | |
1637 | } | |
1638 | public void setPortForwardingR(int rport, String host, int lport, SocketFactory sf) throws JSchException{ | |
1639 | setPortForwardingR(null, rport, host, lport, sf); | |
1640 | } | |
1641 | public void setPortForwardingR(String bind_address, int rport, String host, int lport, SocketFactory sf) throws JSchException{ | |
1642 | ChannelForwardedTCPIP.addPort(this, bind_address, rport, host, lport, sf); | |
1643 | setPortForwarding(bind_address, rport); | |
1644 | } | |
1645 | ||
1646 | public void setPortForwardingR(int rport, String daemon) throws JSchException{ | |
1647 | setPortForwardingR(null, rport, daemon, null); | |
1648 | } | |
1649 | public void setPortForwardingR(int rport, String daemon, Object[] arg) throws JSchException{ | |
1650 | setPortForwardingR(null, rport, daemon, arg); | |
1651 | } | |
1652 | public void setPortForwardingR(String bind_address, int rport, String daemon, Object[] arg) throws JSchException{ | |
1653 | ChannelForwardedTCPIP.addPort(this, bind_address, rport, daemon, arg); | |
1654 | setPortForwarding(bind_address, rport); | |
1655 | } | |
1656 | ||
1657 | private class GlobalRequestReply{ | |
1658 | private Thread thread=null; | |
1659 | private int reply=-1; | |
1660 | void setThread(Thread thread){ | |
1661 | this.thread=thread; | |
1662 | this.reply=-1; | |
1663 | } | |
1664 | Thread getThread(){ return thread; } | |
1665 | void setReply(int reply){ this.reply=reply; } | |
1666 | int getReply(){ return this.reply; } | |
1667 | } | |
1668 | private GlobalRequestReply grr=new GlobalRequestReply(); | |
1669 | private void setPortForwarding(String bind_address, int rport) throws JSchException{ | |
1670 | synchronized(grr){ | |
1671 | Buffer buf=new Buffer(100); // ?? | |
1672 | Packet packet=new Packet(buf); | |
1673 | ||
1674 | String address_to_bind=ChannelForwardedTCPIP.normalize(bind_address); | |
1675 | ||
1676 | try{ | |
1677 | // byte SSH_MSG_GLOBAL_REQUEST 80 | |
1678 | // string "tcpip-forward" | |
1679 | // boolean want_reply | |
1680 | // string address_to_bind | |
1681 | // uint32 port number to bind | |
1682 | packet.reset(); | |
1683 | buf.putByte((byte) SSH_MSG_GLOBAL_REQUEST); | |
1684 | buf.putString(Util.str2byte("tcpip-forward")); | |
1685 | // buf.putByte((byte)0); | |
1686 | buf.putByte((byte)1); | |
1687 | buf.putString(Util.str2byte(address_to_bind)); | |
1688 | buf.putInt(rport); | |
1689 | write(packet); | |
1690 | } | |
1691 | catch(Exception e){ | |
1692 | if(e instanceof Throwable) | |
1693 | throw new JSchException(e.toString(), (Throwable)e); | |
1694 | throw new JSchException(e.toString()); | |
1695 | } | |
1696 | ||
1697 | grr.setThread(Thread.currentThread()); | |
1698 | try{ Thread.sleep(10000);} | |
1699 | catch(Exception e){ | |
1700 | } | |
1701 | int reply=grr.getReply(); | |
1702 | grr.setThread(null); | |
1703 | if(reply==0){ | |
1704 | throw new JSchException("remote port forwarding failed for listen port "+rport); | |
1705 | } | |
1706 | } | |
1707 | } | |
1708 | public void delPortForwardingR(int rport) throws JSchException{ | |
1709 | ChannelForwardedTCPIP.delPort(this, rport); | |
1710 | } | |
1711 | ||
1712 | private void initDeflater(String method) throws JSchException{ | |
1713 | if(method.equals("none")){ | |
1714 | deflater=null; | |
1715 | return; | |
1716 | } | |
1717 | String foo=getConfig(method); | |
1718 | if(foo!=null){ | |
1719 | if(method.equals("zlib") || | |
1720 | (isAuthed && method.equals("zlib@openssh.com"))){ | |
1721 | try{ | |
1722 | Class c=Class.forName(foo); | |
1723 | deflater=(Compression)(c.newInstance()); | |
1724 | int level=6; | |
1725 | try{ level=Integer.parseInt(getConfig("compression_level"));} | |
1726 | catch(Exception ee){ } | |
1727 | deflater.init(Compression.DEFLATER, level); | |
1728 | } | |
1729 | catch(Exception ee){ | |
1730 | throw new JSchException(ee.toString(), ee); | |
1731 | //System.err.println(foo+" isn't accessible."); | |
1732 | } | |
1733 | } | |
1734 | } | |
1735 | } | |
1736 | private void initInflater(String method) throws JSchException{ | |
1737 | if(method.equals("none")){ | |
1738 | inflater=null; | |
1739 | return; | |
1740 | } | |
1741 | String foo=getConfig(method); | |
1742 | if(foo!=null){ | |
1743 | if(method.equals("zlib") || | |
1744 | (isAuthed && method.equals("zlib@openssh.com"))){ | |
1745 | try{ | |
1746 | Class c=Class.forName(foo); | |
1747 | inflater=(Compression)(c.newInstance()); | |
1748 | inflater.init(Compression.INFLATER, 0); | |
1749 | } | |
1750 | catch(Exception ee){ | |
1751 | throw new JSchException(ee.toString(), ee); | |
1752 | //System.err.println(foo+" isn't accessible."); | |
1753 | } | |
1754 | } | |
1755 | } | |
1756 | } | |
1757 | ||
1758 | void addChannel(Channel channel){ | |
1759 | channel.setSession(this); | |
1760 | } | |
1761 | ||
1762 | public void setProxy(Proxy proxy){ this.proxy=proxy; } | |
1763 | public void setHost(String host){ this.host=host; } | |
1764 | public void setPort(int port){ this.port=port; } | |
1765 | void setUserName(String username){ this.username=username; } | |
1766 | public void setUserInfo(UserInfo userinfo){ this.userinfo=userinfo; } | |
1767 | public UserInfo getUserInfo(){ return userinfo; } | |
1768 | public void setInputStream(InputStream in){ this.in=in; } | |
1769 | public void setOutputStream(OutputStream out){ this.out=out; } | |
1770 | public void setX11Host(String host){ ChannelX11.setHost(host); } | |
1771 | public void setX11Port(int port){ ChannelX11.setPort(port); } | |
1772 | public void setX11Cookie(String cookie){ ChannelX11.setCookie(cookie); } | |
1773 | public void setPassword(String password){ | |
1774 | if(password!=null) | |
1775 | this.password=Util.str2byte(password); | |
1776 | } | |
1777 | public void setPassword(byte[] password){ | |
1778 | if(password!=null){ | |
1779 | this.password=new byte[password.length]; | |
1780 | System.arraycopy(password, 0, this.password, 0, password.length); | |
1781 | } | |
1782 | } | |
1783 | ||
1784 | public void setConfig(java.util.Properties newconf){ | |
1785 | setConfig((java.util.Hashtable)newconf); | |
1786 | } | |
1787 | ||
1788 | public void setConfig(java.util.Hashtable newconf){ | |
1789 | synchronized(lock){ | |
1790 | if(config==null) | |
1791 | config=new java.util.Hashtable(); | |
1792 | for(java.util.Enumeration e=newconf.keys() ; e.hasMoreElements() ;) { | |
1793 | String key=(String)(e.nextElement()); | |
1794 | config.put(key, (String)(newconf.get(key))); | |
1795 | } | |
1796 | } | |
1797 | } | |
1798 | ||
1799 | public void setConfig(String key, String value){ | |
1800 | synchronized(lock){ | |
1801 | if(config==null){ | |
1802 | config=new java.util.Hashtable(); | |
1803 | } | |
1804 | config.put(key, value); | |
1805 | } | |
1806 | } | |
1807 | ||
1808 | public String getConfig(String key){ | |
1809 | Object foo=null; | |
1810 | if(config!=null){ | |
1811 | foo=config.get(key); | |
1812 | if(foo instanceof String) return (String)foo; | |
1813 | } | |
1814 | foo=jsch.getConfig(key); | |
1815 | if(foo instanceof String) return (String)foo; | |
1816 | return null; | |
1817 | } | |
1818 | ||
1819 | public void setSocketFactory(SocketFactory sfactory){ | |
1820 | socket_factory=sfactory; | |
1821 | } | |
1822 | public boolean isConnected(){ return isConnected; } | |
1823 | public int getTimeout(){ return timeout; } | |
1824 | public void setTimeout(int timeout) throws JSchException { | |
1825 | if(socket==null){ | |
1826 | if(timeout<0){ | |
1827 | throw new JSchException("invalid timeout value"); | |
1828 | } | |
1829 | this.timeout=timeout; | |
1830 | return; | |
1831 | } | |
1832 | try{ | |
1833 | socket.setSoTimeout(timeout); | |
1834 | this.timeout=timeout; | |
1835 | } | |
1836 | catch(Exception e){ | |
1837 | if(e instanceof Throwable) | |
1838 | throw new JSchException(e.toString(), (Throwable)e); | |
1839 | throw new JSchException(e.toString()); | |
1840 | } | |
1841 | } | |
1842 | public String getServerVersion(){ | |
1843 | return Util.byte2str(V_S); | |
1844 | } | |
1845 | public String getClientVersion(){ | |
1846 | return Util.byte2str(V_C); | |
1847 | } | |
1848 | public void setClientVersion(String cv){ | |
1849 | V_C=Util.str2byte(cv); | |
1850 | } | |
1851 | ||
1852 | public void sendIgnore() throws Exception{ | |
1853 | Buffer buf=new Buffer(); | |
1854 | Packet packet=new Packet(buf); | |
1855 | packet.reset(); | |
1856 | buf.putByte((byte)SSH_MSG_IGNORE); | |
1857 | write(packet); | |
1858 | } | |
1859 | ||
1860 | private static final byte[] keepalivemsg=Util.str2byte("keepalive@jcraft.com"); | |
1861 | public void sendKeepAliveMsg() throws Exception{ | |
1862 | Buffer buf=new Buffer(); | |
1863 | Packet packet=new Packet(buf); | |
1864 | packet.reset(); | |
1865 | buf.putByte((byte)SSH_MSG_GLOBAL_REQUEST); | |
1866 | buf.putString(keepalivemsg); | |
1867 | buf.putByte((byte)1); | |
1868 | write(packet); | |
1869 | } | |
1870 | ||
1871 | private HostKey hostkey=null; | |
1872 | public HostKey getHostKey(){ return hostkey; } | |
1873 | public String getHost(){return host;} | |
1874 | public String getUserName(){return username;} | |
1875 | public int getPort(){return port;} | |
1876 | public void setHostKeyAlias(String hostKeyAlias){ | |
1877 | this.hostKeyAlias=hostKeyAlias; | |
1878 | } | |
1879 | public String getHostKeyAlias(){ | |
1880 | return hostKeyAlias; | |
1881 | } | |
1882 | ||
1883 | public void setServerAliveInterval(int interval) throws JSchException { | |
1884 | setTimeout(interval); | |
1885 | this.serverAliveInterval=interval; | |
1886 | } | |
1887 | public void setServerAliveCountMax(int count){ | |
1888 | this.serverAliveCountMax=count; | |
1889 | } | |
1890 | ||
1891 | public int getServerAliveInterval(){ | |
1892 | return this.serverAliveInterval; | |
1893 | } | |
1894 | public int getServerAliveCountMax(){ | |
1895 | return this.serverAliveCountMax; | |
1896 | } | |
1897 | ||
1898 | public void setDaemonThread(boolean enable){ | |
1899 | this.daemon_thread=enable; | |
1900 | } | |
1901 | ||
1902 | private String[] checkCiphers(String ciphers){ | |
1903 | if(ciphers==null || ciphers.length()==0) | |
1904 | return null; | |
1905 | ||
1906 | if(JSch.getLogger().isEnabled(Logger.INFO)){ | |
1907 | JSch.getLogger().log(Logger.INFO, | |
1908 | "CheckCiphers: "+ciphers); | |
1909 | } | |
1910 | ||
1911 | java.util.Vector result=new java.util.Vector(); | |
1912 | String[] _ciphers=Util.split(ciphers, ","); | |
1913 | for(int i=0; i<_ciphers.length; i++){ | |
1914 | if(!checkCipher(getConfig(_ciphers[i]))){ | |
1915 | result.addElement(_ciphers[i]); | |
1916 | } | |
1917 | } | |
1918 | if(result.size()==0) | |
1919 | return null; | |
1920 | String[] foo=new String[result.size()]; | |
1921 | System.arraycopy(result.toArray(), 0, foo, 0, result.size()); | |
1922 | ||
1923 | if(JSch.getLogger().isEnabled(Logger.INFO)){ | |
1924 | for(int i=0; i<foo.length; i++){ | |
1925 | JSch.getLogger().log(Logger.INFO, | |
1926 | foo[i]+" is not available."); | |
1927 | } | |
1928 | } | |
1929 | ||
1930 | return foo; | |
1931 | } | |
1932 | ||
1933 | static boolean checkCipher(String cipher){ | |
1934 | try{ | |
1935 | Class c=Class.forName(cipher); | |
1936 | Cipher _c=(Cipher)(c.newInstance()); | |
1937 | _c.init(Cipher.ENCRYPT_MODE, | |
1938 | new byte[_c.getBlockSize()], | |
1939 | new byte[_c.getIVSize()]); | |
1940 | return true; | |
1941 | } | |
1942 | catch(Exception e){ | |
1943 | return false; | |
1944 | } | |
1945 | } | |
1946 | } |