]> Joshua Wise's Git repositories - dumload.git/blame - src/com/jcraft/jsch/Channel.java
GPLv3
[dumload.git] / src / com / jcraft / jsch / Channel.java
CommitLineData
0763e16d
JW
1/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
2/*
3Copyright (c) 2002-2010 ymnk, JCraft,Inc. All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
18THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
19INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
21INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
22INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
24OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*/
29
30package com.jcraft.jsch;
31
32import java.io.PipedInputStream;
33import java.io.PipedOutputStream;
34import java.io.InputStream;
35import java.io.OutputStream;
36import java.io.IOException;
37
38
39public abstract class Channel implements Runnable{
40
41 static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION= 91;
42 static final int SSH_MSG_CHANNEL_OPEN_FAILURE= 92;
43 static final int SSH_MSG_CHANNEL_WINDOW_ADJUST= 93;
44
45 static final int SSH_OPEN_ADMINISTRATIVELY_PROHIBITED= 1;
46 static final int SSH_OPEN_CONNECT_FAILED= 2;
47 static final int SSH_OPEN_UNKNOWN_CHANNEL_TYPE= 3;
48 static final int SSH_OPEN_RESOURCE_SHORTAGE= 4;
49
50 static int index=0;
51 private static java.util.Vector pool=new java.util.Vector();
52 static Channel getChannel(String type){
53 if(type.equals("session")){
54 return new ChannelSession();
55 }
56 if(type.equals("shell")){
57 return new ChannelShell();
58 }
59 if(type.equals("exec")){
60 return new ChannelExec();
61 }
62 if(type.equals("x11")){
63 return new ChannelX11();
64 }
65 if(type.equals("auth-agent@openssh.com")){
66 return new ChannelAgentForwarding();
67 }
68 if(type.equals("direct-tcpip")){
69 return new ChannelDirectTCPIP();
70 }
71 if(type.equals("forwarded-tcpip")){
72 return new ChannelForwardedTCPIP();
73 }
74 if(type.equals("sftp")){
75 return new ChannelSftp();
76 }
77 if(type.equals("subsystem")){
78 return new ChannelSubsystem();
79 }
80 return null;
81 }
82 static Channel getChannel(int id, Session session){
83 synchronized(pool){
84 for(int i=0; i<pool.size(); i++){
85 Channel c=(Channel)(pool.elementAt(i));
86 if(c.id==id && c.session==session) return c;
87 }
88 }
89 return null;
90 }
91 static void del(Channel c){
92 synchronized(pool){
93 pool.removeElement(c);
94 }
95 }
96
97 int id;
98 int recipient=-1;
99 byte[] type=Util.str2byte("foo");
100 int lwsize_max=0x100000;
101//int lwsize_max=0x20000; // 32*1024*4
102 int lwsize=lwsize_max; // local initial window size
103 int lmpsize=0x4000; // local maximum packet size
104//int lmpsize=0x8000; // local maximum packet size
105
106 long rwsize=0; // remote initial window size
107 int rmpsize=0; // remote maximum packet size
108
109 IO io=null;
110 Thread thread=null;
111
112 boolean eof_local=false;
113 boolean eof_remote=false;
114
115 boolean close=false;
116 boolean connected=false;
117
118 int exitstatus=-1;
119
120 int reply=0;
121 int connectTimeout=0;
122
123 private Session session;
124
125 int notifyme=0;
126
127 Channel(){
128 synchronized(pool){
129 id=index++;
130 pool.addElement(this);
131 }
132 }
133 void setRecipient(int foo){
134 this.recipient=foo;
135 }
136 int getRecipient(){
137 return recipient;
138 }
139
140 void init() throws JSchException {
141 }
142
143 public void connect() throws JSchException{
144 connect(0);
145 }
146
147 public void connect(int connectTimeout) throws JSchException{
148 Session _session=getSession();
149 if(!_session.isConnected()){
150 throw new JSchException("session is down");
151 }
152 this.connectTimeout=connectTimeout;
153 try{
154 Buffer buf=new Buffer(100);
155 Packet packet=new Packet(buf);
156 // send
157 // byte SSH_MSG_CHANNEL_OPEN(90)
158 // string channel type //
159 // uint32 sender channel // 0
160 // uint32 initial window size // 0x100000(65536)
161 // uint32 maxmum packet size // 0x4000(16384)
162 packet.reset();
163 buf.putByte((byte)90);
164 buf.putString(this.type);
165 buf.putInt(this.id);
166 buf.putInt(this.lwsize);
167 buf.putInt(this.lmpsize);
168 _session.write(packet);
169 int retry=1000;
170 long start=System.currentTimeMillis();
171 long timeout=connectTimeout;
172 while(this.getRecipient()==-1 &&
173 _session.isConnected() &&
174 retry>0){
175 if(timeout>0L){
176 if((System.currentTimeMillis()-start)>timeout){
177 retry=0;
178 continue;
179 }
180 }
181 try{Thread.sleep(50);}catch(Exception ee){}
182 retry--;
183 }
184 if(!_session.isConnected()){
185 throw new JSchException("session is down");
186 }
187 if(retry==0){
188 throw new JSchException("channel is not opened.");
189 }
190
191 /*
192 * At the failure in opening the channel on the sshd,
193 * 'SSH_MSG_CHANNEL_OPEN_FAILURE' will be sent from sshd and it will
194 * be processed in Session#run().
195 */
196 if(this.isClosed()){
197 throw new JSchException("channel is not opened.");
198 }
199 connected=true;
200 start();
201 }
202 catch(Exception e){
203 connected=false;
204 disconnect();
205 if(e instanceof JSchException)
206 throw (JSchException)e;
207 throw new JSchException(e.toString(), e);
208 }
209 }
210
211 public void setXForwarding(boolean foo){
212 }
213
214 public void start() throws JSchException{}
215
216 public boolean isEOF() {return eof_remote;}
217
218 void getData(Buffer buf){
219 setRecipient(buf.getInt());
220 setRemoteWindowSize(buf.getUInt());
221 setRemotePacketSize(buf.getInt());
222 }
223
224 public void setInputStream(InputStream in){
225 io.setInputStream(in, false);
226 }
227 public void setInputStream(InputStream in, boolean dontclose){
228 io.setInputStream(in, dontclose);
229 }
230 public void setOutputStream(OutputStream out){
231 io.setOutputStream(out, false);
232 }
233 public void setOutputStream(OutputStream out, boolean dontclose){
234 io.setOutputStream(out, dontclose);
235 }
236 public void setExtOutputStream(OutputStream out){
237 io.setExtOutputStream(out, false);
238 }
239 public void setExtOutputStream(OutputStream out, boolean dontclose){
240 io.setExtOutputStream(out, dontclose);
241 }
242 public InputStream getInputStream() throws IOException {
243 PipedInputStream in=
244 new MyPipedInputStream(
245 32*1024 // this value should be customizable.
246 );
247 io.setOutputStream(new PassiveOutputStream(in), false);
248 return in;
249 }
250 public InputStream getExtInputStream() throws IOException {
251 PipedInputStream in=
252 new MyPipedInputStream(
253 32*1024 // this value should be customizable.
254 );
255 io.setExtOutputStream(new PassiveOutputStream(in), false);
256 return in;
257 }
258 public OutputStream getOutputStream() throws IOException {
259 /*
260 PipedOutputStream out=new PipedOutputStream();
261 io.setInputStream(new PassiveInputStream(out
262 , 32*1024
263 ), false);
264 return out;
265 */
266
267 final Channel channel=this;
268 OutputStream out=new OutputStream(){
269 private int dataLen=0;
270 private Buffer buffer=null;
271 private Packet packet=null;
272 private boolean closed=false;
273 private synchronized void init() throws java.io.IOException{
274 buffer=new Buffer(rmpsize);
275 packet=new Packet(buffer);
276
277 byte[] _buf=buffer.buffer;
278 if(_buf.length-(14+0)-32-20<=0){
279 buffer=null;
280 packet=null;
281 throw new IOException("failed to initialize the channel.");
282 }
283
284 }
285 byte[] b=new byte[1];
286 public void write(int w) throws java.io.IOException{
287 b[0]=(byte)w;
288 write(b, 0, 1);
289 }
290 public void write(byte[] buf, int s, int l) throws java.io.IOException{
291 if(packet==null){
292 init();
293 }
294
295 if(closed){
296 throw new java.io.IOException("Already closed");
297 }
298
299 byte[] _buf=buffer.buffer;
300 int _bufl=_buf.length;
301 while(l>0){
302 int _l=l;
303 if(l>_bufl-(14+dataLen)-32-20){
304 _l=_bufl-(14+dataLen)-32-20;
305 }
306
307 if(_l<=0){
308 flush();
309 continue;
310 }
311
312 System.arraycopy(buf, s, _buf, 14+dataLen, _l);
313 dataLen+=_l;
314 s+=_l;
315 l-=_l;
316 }
317 }
318
319 public void flush() throws java.io.IOException{
320 if(closed){
321 throw new java.io.IOException("Already closed");
322 }
323 if(dataLen==0)
324 return;
325 packet.reset();
326 buffer.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
327 buffer.putInt(recipient);
328 buffer.putInt(dataLen);
329 buffer.skip(dataLen);
330 try{
331 int foo=dataLen;
332 dataLen=0;
333 getSession().write(packet, channel, foo);
334 }
335 catch(Exception e){
336 close();
337 throw new java.io.IOException(e.toString());
338 }
339
340 }
341 public void close() throws java.io.IOException{
342 if(packet==null){
343 try{
344 init();
345 }
346 catch(java.io.IOException e){
347 // close should be finished silently.
348 return;
349 }
350 }
351 if(closed){
352 return;
353 }
354 if(dataLen>0){
355 flush();
356 }
357 channel.eof();
358 closed=true;
359 }
360 };
361 return out;
362 }
363
364 class MyPipedInputStream extends PipedInputStream{
365 MyPipedInputStream() throws IOException{ super(); }
366 MyPipedInputStream(int size) throws IOException{
367 super();
368 buffer=new byte[size];
369 }
370 MyPipedInputStream(PipedOutputStream out) throws IOException{ super(out); }
371 MyPipedInputStream(PipedOutputStream out, int size) throws IOException{
372 super(out);
373 buffer=new byte[size];
374 }
375 }
376 void setLocalWindowSizeMax(int foo){ this.lwsize_max=foo; }
377 void setLocalWindowSize(int foo){ this.lwsize=foo; }
378 void setLocalPacketSize(int foo){ this.lmpsize=foo; }
379 synchronized void setRemoteWindowSize(long foo){ this.rwsize=foo; }
380 synchronized void addRemoteWindowSize(int foo){
381 this.rwsize+=foo;
382 if(notifyme>0)
383 notifyAll();
384 }
385 void setRemotePacketSize(int foo){ this.rmpsize=foo; }
386
387 public void run(){
388 }
389
390 void write(byte[] foo) throws IOException {
391 write(foo, 0, foo.length);
392 }
393 void write(byte[] foo, int s, int l) throws IOException {
394 try{
395 io.put(foo, s, l);
396 }catch(NullPointerException e){}
397 }
398 void write_ext(byte[] foo, int s, int l) throws IOException {
399 try{
400 io.put_ext(foo, s, l);
401 }catch(NullPointerException e){}
402 }
403
404 void eof_remote(){
405 eof_remote=true;
406 try{
407 io.out_close();
408 }
409 catch(NullPointerException e){}
410 }
411
412 void eof(){
413 if(eof_local)return;
414 eof_local=true;
415
416 try{
417 Buffer buf=new Buffer(100);
418 Packet packet=new Packet(buf);
419 packet.reset();
420 buf.putByte((byte)Session.SSH_MSG_CHANNEL_EOF);
421 buf.putInt(getRecipient());
422 synchronized(this){
423 if(!close)
424 getSession().write(packet);
425 }
426 }
427 catch(Exception e){
428 //System.err.println("Channel.eof");
429 //e.printStackTrace();
430 }
431 /*
432 if(!isConnected()){ disconnect(); }
433 */
434 }
435
436 /*
437 http://www1.ietf.org/internet-drafts/draft-ietf-secsh-connect-24.txt
438
4395.3 Closing a Channel
440 When a party will no longer send more data to a channel, it SHOULD
441 send SSH_MSG_CHANNEL_EOF.
442
443 byte SSH_MSG_CHANNEL_EOF
444 uint32 recipient_channel
445
446 No explicit response is sent to this message. However, the
447 application may send EOF to whatever is at the other end of the
448 channel. Note that the channel remains open after this message, and
449 more data may still be sent in the other direction. This message
450 does not consume window space and can be sent even if no window space
451 is available.
452
453 When either party wishes to terminate the channel, it sends
454 SSH_MSG_CHANNEL_CLOSE. Upon receiving this message, a party MUST
455 send back a SSH_MSG_CHANNEL_CLOSE unless it has already sent this
456 message for the channel. The channel is considered closed for a
457 party when it has both sent and received SSH_MSG_CHANNEL_CLOSE, and
458 the party may then reuse the channel number. A party MAY send
459 SSH_MSG_CHANNEL_CLOSE without having sent or received
460 SSH_MSG_CHANNEL_EOF.
461
462 byte SSH_MSG_CHANNEL_CLOSE
463 uint32 recipient_channel
464
465 This message does not consume window space and can be sent even if no
466 window space is available.
467
468 It is recommended that any data sent before this message is delivered
469 to the actual destination, if possible.
470 */
471
472 void close(){
473 if(close)return;
474 close=true;
475 eof_local=eof_remote=true;
476
477 try{
478 Buffer buf=new Buffer(100);
479 Packet packet=new Packet(buf);
480 packet.reset();
481 buf.putByte((byte)Session.SSH_MSG_CHANNEL_CLOSE);
482 buf.putInt(getRecipient());
483 synchronized(this){
484 getSession().write(packet);
485 }
486 }
487 catch(Exception e){
488 //e.printStackTrace();
489 }
490 }
491 public boolean isClosed(){
492 return close;
493 }
494 static void disconnect(Session session){
495 Channel[] channels=null;
496 int count=0;
497 synchronized(pool){
498 channels=new Channel[pool.size()];
499 for(int i=0; i<pool.size(); i++){
500 try{
501 Channel c=((Channel)(pool.elementAt(i)));
502 if(c.session==session){
503 channels[count++]=c;
504 }
505 }
506 catch(Exception e){
507 }
508 }
509 }
510 for(int i=0; i<count; i++){
511 channels[i].disconnect();
512 }
513 }
514
515 public void disconnect(){
516 //System.err.println(this+":disconnect "+io+" "+connected);
517 //Thread.dumpStack();
518
519 try{
520
521 synchronized(this){
522 if(!connected){
523 return;
524 }
525 connected=false;
526 }
527
528 close();
529
530 eof_remote=eof_local=true;
531
532 thread=null;
533
534 try{
535 if(io!=null){
536 io.close();
537 }
538 }
539 catch(Exception e){
540 //e.printStackTrace();
541 }
542 // io=null;
543 }
544 finally{
545 Channel.del(this);
546 }
547 }
548
549 public boolean isConnected(){
550 Session _session=this.session;
551 if(_session!=null){
552 return _session.isConnected() && connected;
553 }
554 return false;
555 }
556
557 public void sendSignal(String signal) throws Exception {
558 RequestSignal request=new RequestSignal();
559 request.setSignal(signal);
560 request.request(getSession(), this);
561 }
562
563// public String toString(){
564// return "Channel: type="+new String(type)+",id="+id+",recipient="+recipient+",window_size="+window_size+",packet_size="+packet_size;
565// }
566
567/*
568 class OutputThread extends Thread{
569 Channel c;
570 OutputThread(Channel c){ this.c=c;}
571 public void run(){c.output_thread();}
572 }
573*/
574
575 class PassiveInputStream extends MyPipedInputStream{
576 PipedOutputStream out;
577 PassiveInputStream(PipedOutputStream out, int size) throws IOException{
578 super(out, size);
579 this.out=out;
580 }
581 PassiveInputStream(PipedOutputStream out) throws IOException{
582 super(out);
583 this.out=out;
584 }
585 public void close() throws IOException{
586 if(out!=null){
587 this.out.close();
588 }
589 out=null;
590 }
591 }
592 class PassiveOutputStream extends PipedOutputStream{
593 PassiveOutputStream(PipedInputStream in) throws IOException{
594 super(in);
595 }
596 }
597
598 void setExitStatus(int status){ exitstatus=status; }
599 public int getExitStatus(){ return exitstatus; }
600
601 void setSession(Session session){
602 this.session=session;
603 }
604
605 public Session getSession() throws JSchException{
606 Session _session=session;
607 if(_session==null){
608 throw new JSchException("session is not available");
609 }
610 return _session;
611 }
612 public int getId(){ return id; }
613
614 protected void sendOpenConfirmation() throws Exception{
615 Buffer buf=new Buffer(100);
616 Packet packet=new Packet(buf);
617 packet.reset();
618 buf.putByte((byte)SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
619 buf.putInt(getRecipient());
620 buf.putInt(id);
621 buf.putInt(lwsize);
622 buf.putInt(lmpsize);
623 getSession().write(packet);
624 }
625
626 protected void sendOpenFailure(int reasoncode){
627 try{
628 Buffer buf=new Buffer(100);
629 Packet packet=new Packet(buf);
630 packet.reset();
631 buf.putByte((byte)SSH_MSG_CHANNEL_OPEN_FAILURE);
632 buf.putInt(getRecipient());
633 buf.putInt(reasoncode);
634 buf.putString(Util.str2byte("open failed"));
635 buf.putString(Util.empty);
636 getSession().write(packet);
637 }
638 catch(Exception e){
639 }
640 }
641}
This page took 0.077101 seconds and 4 git commands to generate.