1 /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
3 Copyright (c) 2002-2010 ymnk, JCraft,Inc. All rights reserved.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
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.
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.
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.
30 package com.jcraft.jsch;
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;
39 public abstract class Channel implements Runnable{
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;
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;
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();
56 if(type.equals("shell")){
57 return new ChannelShell();
59 if(type.equals("exec")){
60 return new ChannelExec();
62 if(type.equals("x11")){
63 return new ChannelX11();
65 if(type.equals("auth-agent@openssh.com")){
66 return new ChannelAgentForwarding();
68 if(type.equals("direct-tcpip")){
69 return new ChannelDirectTCPIP();
71 if(type.equals("forwarded-tcpip")){
72 return new ChannelForwardedTCPIP();
74 if(type.equals("sftp")){
75 return new ChannelSftp();
77 if(type.equals("subsystem")){
78 return new ChannelSubsystem();
82 static Channel getChannel(int id, Session session){
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;
91 static void del(Channel c){
93 pool.removeElement(c);
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
106 long rwsize=0; // remote initial window size
107 int rmpsize=0; // remote maximum packet size
112 boolean eof_local=false;
113 boolean eof_remote=false;
116 boolean connected=false;
121 int connectTimeout=0;
123 private Session session;
130 pool.addElement(this);
133 void setRecipient(int foo){
140 void init() throws JSchException {
143 public void connect() throws JSchException{
147 public void connect(int connectTimeout) throws JSchException{
148 Session _session=getSession();
149 if(!_session.isConnected()){
150 throw new JSchException("session is down");
152 this.connectTimeout=connectTimeout;
154 Buffer buf=new Buffer(100);
155 Packet packet=new Packet(buf);
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)
163 buf.putByte((byte)90);
164 buf.putString(this.type);
166 buf.putInt(this.lwsize);
167 buf.putInt(this.lmpsize);
168 _session.write(packet);
170 long start=System.currentTimeMillis();
171 long timeout=connectTimeout;
172 while(this.getRecipient()==-1 &&
173 _session.isConnected() &&
176 if((System.currentTimeMillis()-start)>timeout){
181 try{Thread.sleep(50);}catch(Exception ee){}
184 if(!_session.isConnected()){
185 throw new JSchException("session is down");
188 throw new JSchException("channel is not opened.");
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().
197 throw new JSchException("channel is not opened.");
205 if(e instanceof JSchException)
206 throw (JSchException)e;
207 throw new JSchException(e.toString(), e);
211 public void setXForwarding(boolean foo){
214 public void start() throws JSchException{}
216 public boolean isEOF() {return eof_remote;}
218 void getData(Buffer buf){
219 setRecipient(buf.getInt());
220 setRemoteWindowSize(buf.getUInt());
221 setRemotePacketSize(buf.getInt());
224 public void setInputStream(InputStream in){
225 io.setInputStream(in, false);
227 public void setInputStream(InputStream in, boolean dontclose){
228 io.setInputStream(in, dontclose);
230 public void setOutputStream(OutputStream out){
231 io.setOutputStream(out, false);
233 public void setOutputStream(OutputStream out, boolean dontclose){
234 io.setOutputStream(out, dontclose);
236 public void setExtOutputStream(OutputStream out){
237 io.setExtOutputStream(out, false);
239 public void setExtOutputStream(OutputStream out, boolean dontclose){
240 io.setExtOutputStream(out, dontclose);
242 public InputStream getInputStream() throws IOException {
244 new MyPipedInputStream(
245 32*1024 // this value should be customizable.
247 io.setOutputStream(new PassiveOutputStream(in), false);
250 public InputStream getExtInputStream() throws IOException {
252 new MyPipedInputStream(
253 32*1024 // this value should be customizable.
255 io.setExtOutputStream(new PassiveOutputStream(in), false);
258 public OutputStream getOutputStream() throws IOException {
260 PipedOutputStream out=new PipedOutputStream();
261 io.setInputStream(new PassiveInputStream(out
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);
277 byte[] _buf=buffer.buffer;
278 if(_buf.length-(14+0)-32-20<=0){
281 throw new IOException("failed to initialize the channel.");
285 byte[] b=new byte[1];
286 public void write(int w) throws java.io.IOException{
290 public void write(byte[] buf, int s, int l) throws java.io.IOException{
296 throw new java.io.IOException("Already closed");
299 byte[] _buf=buffer.buffer;
300 int _bufl=_buf.length;
303 if(l>_bufl-(14+dataLen)-32-20){
304 _l=_bufl-(14+dataLen)-32-20;
312 System.arraycopy(buf, s, _buf, 14+dataLen, _l);
319 public void flush() throws java.io.IOException{
321 throw new java.io.IOException("Already closed");
326 buffer.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
327 buffer.putInt(recipient);
328 buffer.putInt(dataLen);
329 buffer.skip(dataLen);
333 getSession().write(packet, channel, foo);
337 throw new java.io.IOException(e.toString());
341 public void close() throws java.io.IOException{
346 catch(java.io.IOException e){
347 // close should be finished silently.
364 class MyPipedInputStream extends PipedInputStream{
365 MyPipedInputStream() throws IOException{ super(); }
366 MyPipedInputStream(int size) throws IOException{
368 buffer=new byte[size];
370 MyPipedInputStream(PipedOutputStream out) throws IOException{ super(out); }
371 MyPipedInputStream(PipedOutputStream out, int size) throws IOException{
373 buffer=new byte[size];
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){
385 void setRemotePacketSize(int foo){ this.rmpsize=foo; }
390 void write(byte[] foo) throws IOException {
391 write(foo, 0, foo.length);
393 void write(byte[] foo, int s, int l) throws IOException {
396 }catch(NullPointerException e){}
398 void write_ext(byte[] foo, int s, int l) throws IOException {
400 io.put_ext(foo, s, l);
401 }catch(NullPointerException e){}
409 catch(NullPointerException e){}
417 Buffer buf=new Buffer(100);
418 Packet packet=new Packet(buf);
420 buf.putByte((byte)Session.SSH_MSG_CHANNEL_EOF);
421 buf.putInt(getRecipient());
424 getSession().write(packet);
428 //System.err.println("Channel.eof");
429 //e.printStackTrace();
432 if(!isConnected()){ disconnect(); }
437 http://www1.ietf.org/internet-drafts/draft-ietf-secsh-connect-24.txt
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.
443 byte SSH_MSG_CHANNEL_EOF
444 uint32 recipient_channel
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
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
462 byte SSH_MSG_CHANNEL_CLOSE
463 uint32 recipient_channel
465 This message does not consume window space and can be sent even if no
466 window space is available.
468 It is recommended that any data sent before this message is delivered
469 to the actual destination, if possible.
475 eof_local=eof_remote=true;
478 Buffer buf=new Buffer(100);
479 Packet packet=new Packet(buf);
481 buf.putByte((byte)Session.SSH_MSG_CHANNEL_CLOSE);
482 buf.putInt(getRecipient());
484 getSession().write(packet);
488 //e.printStackTrace();
491 public boolean isClosed(){
494 static void disconnect(Session session){
495 Channel[] channels=null;
498 channels=new Channel[pool.size()];
499 for(int i=0; i<pool.size(); i++){
501 Channel c=((Channel)(pool.elementAt(i)));
502 if(c.session==session){
510 for(int i=0; i<count; i++){
511 channels[i].disconnect();
515 public void disconnect(){
516 //System.err.println(this+":disconnect "+io+" "+connected);
517 //Thread.dumpStack();
530 eof_remote=eof_local=true;
540 //e.printStackTrace();
549 public boolean isConnected(){
550 Session _session=this.session;
552 return _session.isConnected() && connected;
557 public void sendSignal(String signal) throws Exception {
558 RequestSignal request=new RequestSignal();
559 request.setSignal(signal);
560 request.request(getSession(), this);
563 // public String toString(){
564 // return "Channel: type="+new String(type)+",id="+id+",recipient="+recipient+",window_size="+window_size+",packet_size="+packet_size;
568 class OutputThread extends Thread{
570 OutputThread(Channel c){ this.c=c;}
571 public void run(){c.output_thread();}
575 class PassiveInputStream extends MyPipedInputStream{
576 PipedOutputStream out;
577 PassiveInputStream(PipedOutputStream out, int size) throws IOException{
581 PassiveInputStream(PipedOutputStream out) throws IOException{
585 public void close() throws IOException{
592 class PassiveOutputStream extends PipedOutputStream{
593 PassiveOutputStream(PipedInputStream in) throws IOException{
598 void setExitStatus(int status){ exitstatus=status; }
599 public int getExitStatus(){ return exitstatus; }
601 void setSession(Session session){
602 this.session=session;
605 public Session getSession() throws JSchException{
606 Session _session=session;
608 throw new JSchException("session is not available");
612 public int getId(){ return id; }
614 protected void sendOpenConfirmation() throws Exception{
615 Buffer buf=new Buffer(100);
616 Packet packet=new Packet(buf);
618 buf.putByte((byte)SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
619 buf.putInt(getRecipient());
623 getSession().write(packet);
626 protected void sendOpenFailure(int reasoncode){
628 Buffer buf=new Buffer(100);
629 Packet packet=new Packet(buf);
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);