]> Joshua Wise's Git repositories - dumload.git/blob - src/com/jcraft/jsch/Channel.java
Initial commit.
[dumload.git] / src / com / jcraft / jsch / Channel.java
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.PipedInputStream;
33 import java.io.PipedOutputStream;
34 import java.io.InputStream;
35 import java.io.OutputStream;
36 import java.io.IOException;
37
38
39 public 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
439 5.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.051519 seconds and 4 git commands to generate.