Channel.java

Go to the documentation of this file.
00001 /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
00002 /*
00003 Copyright (c) 2002-2011 ymnk, JCraft,Inc. All rights reserved.
00004 
00005 Redistribution and use in source and binary forms, with or without
00006 modification, are permitted provided that the following conditions are met:
00007 
00008   1. Redistributions of source code must retain the above copyright notice,
00009      this list of conditions and the following disclaimer.
00010 
00011   2. Redistributions in binary form must reproduce the above copyright 
00012      notice, this list of conditions and the following disclaimer in 
00013      the documentation and/or other materials provided with the distribution.
00014 
00015   3. The names of the authors may not be used to endorse or promote products
00016      derived from this software without specific prior written permission.
00017 
00018 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
00019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
00020 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
00021 INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
00022 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00023 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
00024 OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00025 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00026 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
00027 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00028 */
00029 
00030 package com.jcraft.jsch;
00031 
00032 import java.io.PipedInputStream;
00033 import java.io.PipedOutputStream;
00034 import java.io.InputStream;
00035 import java.io.OutputStream;
00036 import java.io.IOException;
00037 
00049 public abstract class Channel implements Runnable{
00050 
00051   static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION=      91;
00052   static final int SSH_MSG_CHANNEL_OPEN_FAILURE=           92;
00053   static final int SSH_MSG_CHANNEL_WINDOW_ADJUST=          93;
00054 
00055   static final int SSH_OPEN_ADMINISTRATIVELY_PROHIBITED=    1;
00056   static final int SSH_OPEN_CONNECT_FAILED=                 2;
00057   static final int SSH_OPEN_UNKNOWN_CHANNEL_TYPE=           3;
00058   static final int SSH_OPEN_RESOURCE_SHORTAGE=              4;
00059 
00060   static int index=0; 
00061   private static java.util.Vector pool=new java.util.Vector();
00067   static Channel getChannel(String type){
00068     if(type.equals("session")){
00069       return new ChannelSession();
00070     }
00071     if(type.equals("shell")){
00072       return new ChannelShell();
00073     }
00074     if(type.equals("exec")){
00075       return new ChannelExec();
00076     }
00077     if(type.equals("x11")){
00078       return new ChannelX11();
00079     }
00080     if(type.equals("auth-agent@openssh.com")){
00081       return new ChannelAgentForwarding();
00082     }
00083     if(type.equals("direct-tcpip")){
00084       return new ChannelDirectTCPIP();
00085     }
00086     if(type.equals("forwarded-tcpip")){
00087       return new ChannelForwardedTCPIP();
00088     }
00089     if(type.equals("sftp")){
00090       return new ChannelSftp();
00091     }
00092     if(type.equals("subsystem")){
00093       return new ChannelSubsystem();
00094     }
00095     return null;
00096   }
00097 
00103   static Channel getChannel(int id, Session session){
00104     synchronized(pool){
00105       for(int i=0; i<pool.size(); i++){
00106         Channel c=(Channel)(pool.elementAt(i));
00107         if(c.id==id && c.session==session) return c;
00108       }
00109     }
00110     return null;
00111   }
00112 
00116   static void del(Channel c){
00117     synchronized(pool){
00118       pool.removeElement(c);
00119     }
00120   }
00121 
00122   int id;
00123   volatile int recipient=-1;
00124   protected byte[] type=Util.str2byte("foo");
00125   volatile int lwsize_max=0x100000;
00126   volatile int lwsize=lwsize_max;     // local initial window size
00127   volatile int lmpsize=0x4000;     // local maximum packet size
00128 
00129   volatile long rwsize=0;         // remote initial window size
00130   volatile int rmpsize=0;        // remote maximum packet size
00131 
00132   IO io=null;    
00133   Thread thread=null;
00134 
00135   volatile boolean eof_local=false;
00136   volatile boolean eof_remote=false;
00137 
00138   volatile boolean close=false;
00139   volatile boolean connected=false;
00140   volatile boolean open_confirmation=false;
00141 
00142   volatile int exitstatus=-1;
00143 
00144   volatile int reply=0; 
00145   volatile int connectTimeout=0;
00146 
00147   private Session session;
00148 
00149   int notifyme=0; 
00150 
00151   Channel(){
00152     synchronized(pool){
00153       id=index++;
00154       pool.addElement(this);
00155     }
00156   }
00157   synchronized void setRecipient(int foo){
00158     this.recipient=foo;
00159     if(notifyme>0)
00160       notifyAll();
00161   }
00162   int getRecipient(){
00163     return recipient;
00164   }
00165 
00166   void init() throws JSchException {
00167   }
00168 
00174   public void connect() throws JSchException{
00175     connect(0);
00176   }
00177 
00191   public void connect(int connectTimeout) throws JSchException{
00192     Session _session=getSession();
00193     if(!_session.isConnected()){
00194       throw new JSchException("session is down");
00195     }
00196     this.connectTimeout=connectTimeout;
00197     try{
00198       Buffer buf=new Buffer(100);
00199       Packet packet=new Packet(buf);
00200       // send
00201       // byte   SSH_MSG_CHANNEL_OPEN(90)
00202       // string channel type         //
00203       // uint32 sender channel       // 0
00204       // uint32 initial window size  // 0x100000(65536)
00205       // uint32 maxmum packet size   // 0x4000(16384)
00206       packet.reset();
00207       buf.putByte((byte)90);
00208       buf.putString(this.type);
00209       buf.putInt(this.id);
00210       buf.putInt(this.lwsize);
00211       buf.putInt(this.lmpsize);
00212       _session.write(packet);
00213       int retry=10;
00214       long start=System.currentTimeMillis();
00215       long timeout=connectTimeout;
00216       if(timeout!=0L) retry = 1;
00217       synchronized(this){
00218         while(this.getRecipient()==-1 &&
00219               _session.isConnected() &&
00220                retry>0){
00221           if(timeout>0L){
00222             if((System.currentTimeMillis()-start)>timeout){
00223               retry=0;
00224               continue;
00225             }
00226           }
00227           try{
00228             long t = timeout==0L ? 5000L : timeout;
00229             this.notifyme=1;
00230             wait(t);
00231           }
00232           catch(java.lang.InterruptedException e){
00233           }
00234           finally{
00235             this.notifyme=0;
00236           }
00237           retry--;
00238         }
00239       }
00240       if(!_session.isConnected()){
00241     throw new JSchException("session is down");
00242       }
00243       if(this.getRecipient()==-1){  // timeout
00244         throw new JSchException("channel is not opened.");
00245       }
00246       if(this.open_confirmation==false){  // SSH_MSG_CHANNEL_OPEN_FAILURE
00247         throw new JSchException("channel is not opened.");
00248       }
00249 
00250       connected=true;
00251       start();
00252     }
00253     catch(Exception e){
00254       connected=false;
00255       disconnect();
00256       if(e instanceof JSchException) 
00257         throw (JSchException)e;
00258       throw new JSchException(e.toString(), e);
00259     }
00260   }
00261 
00269   public void setXForwarding(boolean foo){
00270   }
00271 
00280   public void start() throws JSchException{}
00281 
00287   public boolean isEOF() {return eof_remote;}
00288 
00289   void getData(Buffer buf){
00290     setRecipient(buf.getInt());
00291     setRemoteWindowSize(buf.getUInt());
00292     setRemotePacketSize(buf.getInt());
00293   }
00294 
00302   public void setInputStream(InputStream in){
00303     io.setInputStream(in, false);
00304   }
00313   public void setInputStream(InputStream in, boolean dontclose){
00314     io.setInputStream(in, dontclose);
00315   }
00324   public void setOutputStream(OutputStream out){
00325     io.setOutputStream(out, false);
00326   }
00336   public void setOutputStream(OutputStream out, boolean dontclose){
00337     io.setOutputStream(out, dontclose);
00338   }
00339 
00355   public void setExtOutputStream(OutputStream out){
00356     io.setExtOutputStream(out, false);
00357   }
00358 
00376   public void setExtOutputStream(OutputStream out, boolean dontclose){
00377     io.setExtOutputStream(out, dontclose);
00378   }
00379 
00388   public InputStream getInputStream() throws IOException {
00389     PipedInputStream in=
00390       new MyPipedInputStream(
00391                              32*1024  // this value should be customizable.
00392                              );
00393     io.setOutputStream(new PassiveOutputStream(in), false);
00394     return in;
00395   }
00396 
00406   public InputStream getExtInputStream() throws IOException {
00407     PipedInputStream in=
00408       new MyPipedInputStream(
00409                              32*1024  // this value should be customizable.
00410                              );
00411     io.setExtOutputStream(new PassiveOutputStream(in), false);
00412     return in;
00413   }
00414 
00424   public OutputStream getOutputStream() throws IOException {
00425     /*
00426     PipedOutputStream out=new PipedOutputStream();
00427     io.setInputStream(new PassiveInputStream(out
00428                                              , 32*1024
00429                                              ), false);
00430     return out;
00431     */
00432 
00433     final Channel channel=this;
00434     OutputStream out=new OutputStream(){
00435         private int dataLen=0;
00436         private Buffer buffer=null;
00437         private Packet packet=null;
00438         private boolean closed=false;
00439         private synchronized void init() throws java.io.IOException{
00440           buffer=new Buffer(rmpsize);
00441           packet=new Packet(buffer);
00442 
00443           byte[] _buf=buffer.buffer;
00444           if(_buf.length-(14+0)-Session.buffer_margin<=0){
00445             buffer=null;
00446             packet=null;
00447             throw new IOException("failed to initialize the channel.");
00448           }
00449 
00450         }
00451         byte[] b=new byte[1];
00452         public void write(int w) throws java.io.IOException{
00453           b[0]=(byte)w;
00454           write(b, 0, 1);
00455         }
00456         public void write(byte[] buf, int s, int l) throws java.io.IOException{
00457           if(packet==null){
00458             init();
00459           }
00460 
00461           if(closed){
00462             throw new java.io.IOException("Already closed");
00463           }
00464 
00465           byte[] _buf=buffer.buffer;
00466           int _bufl=_buf.length;
00467           while(l>0){
00468             int _l=l;
00469             if(l>_bufl-(14+dataLen)-Session.buffer_margin){
00470               _l=_bufl-(14+dataLen)-Session.buffer_margin;
00471             }
00472 
00473             if(_l<=0){
00474               flush();
00475               continue;
00476             }
00477 
00478             System.arraycopy(buf, s, _buf, 14+dataLen, _l);
00479             dataLen+=_l;
00480             s+=_l;
00481             l-=_l;
00482           }
00483         }
00484 
00485         public void flush() throws java.io.IOException{
00486           if(closed){
00487             throw new java.io.IOException("Already closed");
00488           }
00489           if(dataLen==0)
00490             return;
00491           packet.reset();
00492           buffer.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
00493           buffer.putInt(recipient);
00494           buffer.putInt(dataLen);
00495           buffer.skip(dataLen);
00496           try{
00497             int foo=dataLen;
00498             dataLen=0;
00499             getSession().write(packet, channel, foo);
00500           }
00501           catch(Exception e){
00502             close();
00503             throw new java.io.IOException(e.toString());
00504           }
00505 
00506         }
00507         public void close() throws java.io.IOException{
00508           if(packet==null){
00509             try{
00510               init();
00511             }
00512             catch(java.io.IOException e){
00513               // close should be finished silently.
00514               return;
00515             }
00516           }
00517           if(closed){
00518             return;
00519           }
00520           if(dataLen>0){
00521             flush();
00522           }
00523           channel.eof();
00524           closed=true;
00525         }
00526       };
00527     return out;
00528   }
00529 
00530   class MyPipedInputStream extends PipedInputStream{
00531     MyPipedInputStream() throws IOException{ super(); }
00532     MyPipedInputStream(int size) throws IOException{
00533       super();
00534       buffer=new byte[size];
00535     }
00536     MyPipedInputStream(PipedOutputStream out) throws IOException{ super(out); }
00537     MyPipedInputStream(PipedOutputStream out, int size) throws IOException{
00538       super(out);
00539       buffer=new byte[size];
00540     }
00541 
00542     /*
00543      * TODO: We should have our own Piped[I/O]Stream implementation.
00544      * Before accepting data, JDK's PipedInputStream will check the existence of
00545      * reader thread, and if it is not alive, the stream will be closed.
00546      * That behavior may cause the problem if multiple threads make access to it.
00547      */
00548     public synchronized void updateReadSide() throws IOException {
00549       if(available() != 0){ // not empty
00550         return;
00551       }
00552       in = 0;
00553       out = 0;
00554       buffer[in++] = 0;
00555       read();
00556     }
00557   }
00558   void setLocalWindowSizeMax(int foo){ this.lwsize_max=foo; }
00559   void setLocalWindowSize(int foo){ this.lwsize=foo; }
00560   void setLocalPacketSize(int foo){ this.lmpsize=foo; }
00561   synchronized void setRemoteWindowSize(long foo){ this.rwsize=foo; }
00562   synchronized void addRemoteWindowSize(int foo){ 
00563     this.rwsize+=foo; 
00564     if(notifyme>0)
00565       notifyAll();
00566   }
00567   void setRemotePacketSize(int foo){ this.rmpsize=foo; }
00568 
00574   public void run(){
00575   }
00576 
00577   void write(byte[] foo) throws IOException {
00578     write(foo, 0, foo.length);
00579   }
00580   void write(byte[] foo, int s, int l) throws IOException {
00581     try{
00582       io.put(foo, s, l);
00583     }catch(NullPointerException e){}
00584   }
00585   void write_ext(byte[] foo, int s, int l) throws IOException {
00586     try{
00587       io.put_ext(foo, s, l);
00588     }catch(NullPointerException e){}
00589   }
00590 
00597   void eof_remote(){
00598     eof_remote=true;
00599     try{
00600       io.out_close();
00601     }
00602     catch(NullPointerException e){}
00603   }
00604 
00605   void eof(){
00606     if(eof_local)return;
00607     eof_local=true;
00608 
00609     try{
00610       Buffer buf=new Buffer(100);
00611       Packet packet=new Packet(buf);
00612       packet.reset();
00613       buf.putByte((byte)Session.SSH_MSG_CHANNEL_EOF);
00614       buf.putInt(getRecipient());
00615       synchronized(this){
00616         if(!close)
00617           getSession().write(packet);
00618       }
00619     }
00620     catch(Exception e){
00621       //System.err.println("Channel.eof");
00622       //e.printStackTrace();
00623     }
00624     /*
00625     if(!isConnected()){ disconnect(); }
00626     */
00627   }
00628 
00629   /*  http://tools.ietf.org/html/rfc4254#section-5.3
00630 
00631 5.3.  Closing a Channel
00632 
00633    When a party will no longer send more data to a channel, it SHOULD
00634    send SSH_MSG_CHANNEL_EOF.
00635 
00636       byte      SSH_MSG_CHANNEL_EOF
00637       uint32    recipient channel
00638 
00639    No explicit response is sent to this message.  However, the
00640    application may send EOF to whatever is at the other end of the
00641    channel.  Note that the channel remains open after this message, and
00642    more data may still be sent in the other direction.  This message
00643    does not consume window space and can be sent even if no window space
00644    is available.
00645 
00646    When either party wishes to terminate the channel, it sends
00647    SSH_MSG_CHANNEL_CLOSE.  Upon receiving this message, a party MUST
00648    send back an SSH_MSG_CHANNEL_CLOSE unless it has already sent this
00649    message for the channel.  The channel is considered closed for a
00650    party when it has both sent and received SSH_MSG_CHANNEL_CLOSE, and
00651    the party may then reuse the channel number.  A party MAY send
00652    SSH_MSG_CHANNEL_CLOSE without having sent or received
00653    SSH_MSG_CHANNEL_EOF.
00654 
00655       byte      SSH_MSG_CHANNEL_CLOSE
00656       uint32    recipient channel
00657 
00658    This message does not consume window space and can be sent even if no
00659    window space is available.
00660 
00661    It is RECOMMENDED that all data sent before this message be delivered
00662    to the actual destination, if possible.
00663   */
00664 
00665   void close(){
00666     if(close)return;
00667     close=true;
00668     eof_local=eof_remote=true;
00669 
00670     try{
00671       Buffer buf=new Buffer(100);
00672       Packet packet=new Packet(buf);
00673       packet.reset();
00674       buf.putByte((byte)Session.SSH_MSG_CHANNEL_CLOSE);
00675       buf.putInt(getRecipient());
00676       synchronized(this){
00677         getSession().write(packet);
00678       }
00679     }
00680     catch(Exception e){
00681       //e.printStackTrace();
00682     }
00683   }
00688   public boolean isClosed(){
00689     return close;
00690   }
00691 
00695   static void disconnect(Session session){
00696     Channel[] channels=null;
00697     int count=0;
00698     synchronized(pool){
00699       channels=new Channel[pool.size()];
00700       for(int i=0; i<pool.size(); i++){
00701     try{
00702       Channel c=((Channel)(pool.elementAt(i)));
00703       if(c.session==session){
00704         channels[count++]=c;
00705       }
00706     }
00707     catch(Exception e){
00708     }
00709       } 
00710     }
00711     for(int i=0; i<count; i++){
00712       channels[i].disconnect();
00713     }
00714   }
00715 
00719   public void disconnect(){
00720     //System.err.println(this+":disconnect "+io+" "+connected);
00721     //Thread.dumpStack();
00722 
00723     try{
00724 
00725       synchronized(this){
00726         if(!connected){
00727           return;
00728         }
00729         connected=false;
00730       }
00731 
00732       close();
00733 
00734       eof_remote=eof_local=true;
00735 
00736       thread=null;
00737 
00738       try{
00739         if(io!=null){
00740           io.close();
00741         }
00742       }
00743       catch(Exception e){
00744         //e.printStackTrace();
00745       }
00746       // io=null;
00747     }
00748     finally{
00749       Channel.del(this);
00750     }
00751   }
00752 
00753 
00759   public boolean isConnected(){
00760     Session _session=this.session;
00761     if(_session!=null){
00762       return _session.isConnected() && connected;
00763     }
00764     return false;
00765   }
00766 
00775   public void sendSignal(String signal) throws Exception {
00776     RequestSignal request=new RequestSignal();
00777     request.setSignal(signal);
00778     request.request(getSession(), this);
00779   }
00780 
00781 //  public String toString(){
00782 //      return "Channel: type="+new String(type)+",id="+id+",recipient="+recipient+",window_size="+window_size+",packet_size="+packet_size;
00783 //  }
00784 
00785 /*
00786   class OutputThread extends Thread{
00787     Channel c;
00788     OutputThread(Channel c){ this.c=c;}
00789     public void run(){c.output_thread();}
00790   }
00791 */
00792 
00793   class PassiveInputStream extends MyPipedInputStream{
00794     PipedOutputStream out;
00795     PassiveInputStream(PipedOutputStream out, int size) throws IOException{
00796       super(out, size);
00797       this.out=out;
00798     }
00799     PassiveInputStream(PipedOutputStream out) throws IOException{
00800       super(out);
00801       this.out=out;
00802     }
00803     public void close() throws IOException{
00804       if(out!=null){
00805         this.out.close();
00806       }
00807       out=null;
00808     }
00809   }
00810   class PassiveOutputStream extends PipedOutputStream{
00811     PassiveOutputStream(PipedInputStream in) throws IOException{
00812       super(in);
00813     }
00814   }
00815 
00816   void setExitStatus(int status){ exitstatus=status; }
00830   public int getExitStatus(){ return exitstatus; }
00831 
00832   void setSession(Session session){
00833     this.session=session;
00834   }
00835 
00842   public Session getSession() throws JSchException{ 
00843     Session _session=session;
00844     if(_session==null){
00845       throw new JSchException("session is not available");
00846     }
00847     return _session;
00848   }
00849 
00853   public int getId(){ return id; }
00854 
00855 
00859   protected void sendOpenConfirmation() throws Exception{
00860     Buffer buf=new Buffer(100);
00861     Packet packet=new Packet(buf);
00862     packet.reset();
00863     buf.putByte((byte)SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
00864     buf.putInt(getRecipient());
00865     buf.putInt(id);
00866     buf.putInt(lwsize);
00867     buf.putInt(lmpsize);
00868     getSession().write(packet);
00869   }
00870 
00875   protected void sendOpenFailure(int reasoncode){
00876     try{
00877       Buffer buf=new Buffer(100);
00878       Packet packet=new Packet(buf);
00879       packet.reset();
00880       buf.putByte((byte)SSH_MSG_CHANNEL_OPEN_FAILURE);
00881       buf.putInt(getRecipient());
00882       buf.putInt(reasoncode);
00883       buf.putString(Util.str2byte("open failed"));
00884       buf.putString(Util.empty);
00885       getSession().write(packet);
00886     }
00887     catch(Exception e){
00888     }
00889   }
00890 }

Generated on 5 May 2015 for HPCVIEWER by  doxygen 1.6.1