001: /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
002: /*
003: Copyright (c) 2002-2008 ymnk, JCraft,Inc. All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without
006: modification, are permitted provided that the following conditions are met:
007:
008: 1. Redistributions of source code must retain the above copyright notice,
009: this list of conditions and the following disclaimer.
010:
011: 2. Redistributions in binary form must reproduce the above copyright
012: notice, this list of conditions and the following disclaimer in
013: the documentation and/or other materials provided with the distribution.
014:
015: 3. The names of the authors may not be used to endorse or promote products
016: derived from this software without specific prior written permission.
017:
018: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
019: INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
020: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
021: INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
022: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
023: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
024: OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
027: EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: */
029:
030: package com.jcraft.jsch;
031:
032: import java.io.PipedInputStream;
033: import java.io.PipedOutputStream;
034: import java.io.InputStream;
035: import java.io.OutputStream;
036: import java.io.IOException;
037:
038: public abstract class Channel implements Runnable {
039:
040: static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91;
041: static final int SSH_MSG_CHANNEL_OPEN_FAILURE = 92;
042: static final int SSH_MSG_CHANNEL_WINDOW_ADJUST = 93;
043:
044: static final int SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1;
045: static final int SSH_OPEN_CONNECT_FAILED = 2;
046: static final int SSH_OPEN_UNKNOWN_CHANNEL_TYPE = 3;
047: static final int SSH_OPEN_RESOURCE_SHORTAGE = 4;
048:
049: static int index = 0;
050: private static java.util.Vector pool = new java.util.Vector();
051:
052: static Channel getChannel(String type) {
053: if (type.equals("session")) {
054: return new ChannelSession();
055: }
056: if (type.equals("shell")) {
057: return new ChannelShell();
058: }
059: if (type.equals("exec")) {
060: return new ChannelExec();
061: }
062: if (type.equals("x11")) {
063: return new ChannelX11();
064: }
065: if (type.equals("auth-agent@openssh.com")) {
066: return new ChannelAgentForwarding();
067: }
068: if (type.equals("direct-tcpip")) {
069: return new ChannelDirectTCPIP();
070: }
071: if (type.equals("forwarded-tcpip")) {
072: return new ChannelForwardedTCPIP();
073: }
074: if (type.equals("sftp")) {
075: return new ChannelSftp();
076: }
077: if (type.equals("subsystem")) {
078: return new ChannelSubsystem();
079: }
080: return null;
081: }
082:
083: static Channel getChannel(int id, Session session) {
084: synchronized (pool) {
085: for (int i = 0; i < pool.size(); i++) {
086: Channel c = (Channel) (pool.elementAt(i));
087: if (c.id == id && c.session == session)
088: return c;
089: }
090: }
091: return null;
092: }
093:
094: static void del(Channel c) {
095: synchronized (pool) {
096: pool.removeElement(c);
097: }
098: }
099:
100: int id;
101: int recipient = -1;
102: byte[] type = "foo".getBytes();
103: int lwsize_max = 0x100000;
104: //int lwsize_max=0x20000; // 32*1024*4
105: int lwsize = lwsize_max; // local initial window size
106: int lmpsize = 0x4000; // local maximum packet size
107: //int lmpsize=0x8000; // local maximum packet size
108:
109: int rwsize = 0; // remote initial window size
110: int rmpsize = 0; // remote maximum packet size
111:
112: IO io = null;
113: Thread thread = null;
114:
115: boolean eof_local = false;
116: boolean eof_remote = false;
117:
118: boolean close = false;
119: boolean connected = false;
120:
121: int exitstatus = -1;
122:
123: int reply = 0;
124: int connectTimeout = 0;
125:
126: Session session;
127:
128: int notifyme = 0;
129:
130: Channel() {
131: synchronized (pool) {
132: id = index++;
133: pool.addElement(this );
134: }
135: }
136:
137: void setRecipient(int foo) {
138: this .recipient = foo;
139: }
140:
141: int getRecipient() {
142: return recipient;
143: }
144:
145: void init() {
146: }
147:
148: public void connect() throws JSchException {
149: connect(0);
150: }
151:
152: public void connect(int connectTimeout) throws JSchException {
153: if (!session.isConnected()) {
154: throw new JSchException("session is down");
155: }
156: this .connectTimeout = connectTimeout;
157: try {
158: Buffer buf = new Buffer(100);
159: Packet packet = new Packet(buf);
160: // send
161: // byte SSH_MSG_CHANNEL_OPEN(90)
162: // string channel type //
163: // uint32 sender channel // 0
164: // uint32 initial window size // 0x100000(65536)
165: // uint32 maxmum packet size // 0x4000(16384)
166: packet.reset();
167: buf.putByte((byte) 90);
168: buf.putString(this .type);
169: buf.putInt(this .id);
170: buf.putInt(this .lwsize);
171: buf.putInt(this .lmpsize);
172: session.write(packet);
173: int retry = 1000;
174: long start = System.currentTimeMillis();
175: long timeout = connectTimeout;
176: while (this .getRecipient() == -1 && session.isConnected()
177: && retry > 0) {
178: if (timeout > 0L) {
179: if ((System.currentTimeMillis() - start) > timeout) {
180: retry = 0;
181: continue;
182: }
183: }
184: try {
185: Thread.sleep(50);
186: } catch (Exception ee) {
187: }
188: retry--;
189: }
190: if (!session.isConnected()) {
191: throw new JSchException("session is down");
192: }
193: if (retry == 0) {
194: throw new JSchException("channel is not opened.");
195: }
196:
197: /*
198: * At the failure in opening the channel on the sshd,
199: * 'SSH_MSG_CHANNEL_OPEN_FAILURE' will be sent from sshd and it will
200: * be processed in Session#run().
201: */
202: if (this .isClosed()) {
203: throw new JSchException("channel is not opened.");
204: }
205: connected = true;
206: start();
207: } catch (Exception e) {
208: connected = false;
209: if (e instanceof JSchException)
210: throw (JSchException) e;
211: throw new JSchException(e.toString());
212: }
213: }
214:
215: public void setXForwarding(boolean foo) {
216: }
217:
218: public void start() throws JSchException {
219: }
220:
221: public boolean isEOF() {
222: return eof_remote;
223: }
224:
225: void getData(Buffer buf) {
226: setRecipient(buf.getInt());
227: setRemoteWindowSize(buf.getInt());
228: setRemotePacketSize(buf.getInt());
229: }
230:
231: public void setInputStream(InputStream in) {
232: io.setInputStream(in, false);
233: }
234:
235: public void setInputStream(InputStream in, boolean dontclose) {
236: io.setInputStream(in, dontclose);
237: }
238:
239: public void setOutputStream(OutputStream out) {
240: io.setOutputStream(out, false);
241: }
242:
243: public void setOutputStream(OutputStream out, boolean dontclose) {
244: io.setOutputStream(out, dontclose);
245: }
246:
247: public void setExtOutputStream(OutputStream out) {
248: io.setExtOutputStream(out, false);
249: }
250:
251: public void setExtOutputStream(OutputStream out, boolean dontclose) {
252: io.setExtOutputStream(out, dontclose);
253: }
254:
255: public InputStream getInputStream() throws IOException {
256: PipedInputStream in = new MyPipedInputStream(32 * 1024 // this value should be customizable.
257: );
258: io.setOutputStream(new PassiveOutputStream(in), false);
259: return in;
260: }
261:
262: public InputStream getExtInputStream() throws IOException {
263: PipedInputStream in = new MyPipedInputStream(32 * 1024 // this value should be customizable.
264: );
265: io.setExtOutputStream(new PassiveOutputStream(in), false);
266: return in;
267: }
268:
269: public OutputStream getOutputStream() throws IOException {
270: /*
271: PipedOutputStream out=new PipedOutputStream();
272: io.setInputStream(new PassiveInputStream(out
273: , 32*1024
274: ), false);
275: return out;
276: */
277:
278: final Channel channel = this ;
279: OutputStream out = new OutputStream() {
280: private int dataLen = 0;
281: private Buffer buffer = null;
282: private Packet packet = null;
283: private boolean closed = false;
284:
285: private synchronized void init() throws java.io.IOException {
286: buffer = new Buffer(rmpsize);
287: packet = new Packet(buffer);
288:
289: byte[] _buf = buffer.buffer;
290: if (_buf.length - (14 + 0) - 32 - 20 <= 0) {
291: buffer = null;
292: packet = null;
293: throw new IOException(
294: "failed to initialize the channel.");
295: }
296:
297: }
298:
299: byte[] b = new byte[1];
300:
301: public void write(int w) throws java.io.IOException {
302: b[0] = (byte) w;
303: write(b, 0, 1);
304: }
305:
306: public void write(byte[] buf, int s, int l)
307: throws java.io.IOException {
308: if (packet == null) {
309: init();
310: }
311:
312: if (closed) {
313: throw new java.io.IOException("Already closed");
314: }
315:
316: byte[] _buf = buffer.buffer;
317: int _bufl = _buf.length;
318: while (l > 0) {
319: int _l = l;
320: if (l > _bufl - (14 + dataLen) - 32 - 20) {
321: _l = _bufl - (14 + dataLen) - 32 - 20;
322: }
323:
324: if (_l <= 0) {
325: flush();
326: continue;
327: }
328:
329: System.arraycopy(buf, s, _buf, 14 + dataLen, _l);
330: dataLen += _l;
331: s += _l;
332: l -= _l;
333: }
334: }
335:
336: public void flush() throws java.io.IOException {
337: if (closed) {
338: throw new java.io.IOException("Already closed");
339: }
340: if (dataLen == 0)
341: return;
342: packet.reset();
343: buffer.putByte((byte) Session.SSH_MSG_CHANNEL_DATA);
344: buffer.putInt(recipient);
345: buffer.putInt(dataLen);
346: buffer.skip(dataLen);
347: try {
348: int foo = dataLen;
349: dataLen = 0;
350: session.write(packet, channel, foo);
351: } catch (Exception e) {
352: close();
353: throw new java.io.IOException(e.toString());
354: }
355:
356: }
357:
358: public void close() throws java.io.IOException {
359: if (packet == null) {
360: try {
361: init();
362: } catch (java.io.IOException e) {
363: // close should be finished silently.
364: return;
365: }
366: }
367: if (closed) {
368: return;
369: }
370: if (dataLen > 0) {
371: flush();
372: }
373: channel.eof();
374: closed = true;
375: }
376: };
377: return out;
378: }
379:
380: class MyPipedInputStream extends PipedInputStream {
381: MyPipedInputStream() throws IOException {
382: super ();
383: }
384:
385: MyPipedInputStream(int size) throws IOException {
386: super ();
387: buffer = new byte[size];
388: }
389:
390: MyPipedInputStream(PipedOutputStream out) throws IOException {
391: super (out);
392: }
393:
394: MyPipedInputStream(PipedOutputStream out, int size)
395: throws IOException {
396: super (out);
397: buffer = new byte[size];
398: }
399: }
400:
401: void setLocalWindowSizeMax(int foo) {
402: this .lwsize_max = foo;
403: }
404:
405: void setLocalWindowSize(int foo) {
406: this .lwsize = foo;
407: }
408:
409: void setLocalPacketSize(int foo) {
410: this .lmpsize = foo;
411: }
412:
413: synchronized void setRemoteWindowSize(int foo) {
414: this .rwsize = foo;
415: }
416:
417: synchronized void addRemoteWindowSize(int foo) {
418: this .rwsize += foo;
419: if (notifyme > 0)
420: notifyAll();
421: }
422:
423: void setRemotePacketSize(int foo) {
424: this .rmpsize = foo;
425: }
426:
427: public void run() {
428: }
429:
430: void write(byte[] foo) throws IOException {
431: write(foo, 0, foo.length);
432: }
433:
434: void write(byte[] foo, int s, int l) throws IOException {
435: try {
436: // if(io.out!=null)
437: io.put(foo, s, l);
438: } catch (NullPointerException e) {
439: }
440: }
441:
442: void write_ext(byte[] foo, int s, int l) throws IOException {
443: try {
444: // if(io.out_ext!=null)
445: io.put_ext(foo, s, l);
446: } catch (NullPointerException e) {
447: }
448: }
449:
450: void eof_remote() {
451: eof_remote = true;
452: try {
453: if (io.out != null) {
454: io.out.close();
455: io.out = null;
456: }
457: } catch (NullPointerException e) {
458: } catch (IOException e) {
459: }
460: }
461:
462: void eof() {
463: //System.err.println("EOF!!!! "+this);
464: //Thread.dumpStack();
465: if (close)
466: return;
467: if (eof_local)
468: return;
469: eof_local = true;
470: //close=eof;
471: try {
472: Buffer buf = new Buffer(100);
473: Packet packet = new Packet(buf);
474: packet.reset();
475: buf.putByte((byte) Session.SSH_MSG_CHANNEL_EOF);
476: buf.putInt(getRecipient());
477: session.write(packet);
478: } catch (Exception e) {
479: //System.err.println("Channel.eof");
480: //e.printStackTrace();
481: }
482: /*
483: if(!isConnected()){ disconnect(); }
484: */
485: }
486:
487: /*
488: http://www1.ietf.org/internet-drafts/draft-ietf-secsh-connect-24.txt
489:
490: 5.3 Closing a Channel
491: When a party will no longer send more data to a channel, it SHOULD
492: send SSH_MSG_CHANNEL_EOF.
493:
494: byte SSH_MSG_CHANNEL_EOF
495: uint32 recipient_channel
496:
497: No explicit response is sent to this message. However, the
498: application may send EOF to whatever is at the other end of the
499: channel. Note that the channel remains open after this message, and
500: more data may still be sent in the other direction. This message
501: does not consume window space and can be sent even if no window space
502: is available.
503:
504: When either party wishes to terminate the channel, it sends
505: SSH_MSG_CHANNEL_CLOSE. Upon receiving this message, a party MUST
506: send back a SSH_MSG_CHANNEL_CLOSE unless it has already sent this
507: message for the channel. The channel is considered closed for a
508: party when it has both sent and received SSH_MSG_CHANNEL_CLOSE, and
509: the party may then reuse the channel number. A party MAY send
510: SSH_MSG_CHANNEL_CLOSE without having sent or received
511: SSH_MSG_CHANNEL_EOF.
512:
513: byte SSH_MSG_CHANNEL_CLOSE
514: uint32 recipient_channel
515:
516: This message does not consume window space and can be sent even if no
517: window space is available.
518:
519: It is recommended that any data sent before this message is delivered
520: to the actual destination, if possible.
521: */
522:
523: void close() {
524: //System.err.println("close!!!!");
525: if (close)
526: return;
527: close = true;
528:
529: eof_local = eof_remote = true;
530:
531: try {
532: Buffer buf = new Buffer(100);
533: Packet packet = new Packet(buf);
534: packet.reset();
535: buf.putByte((byte) Session.SSH_MSG_CHANNEL_CLOSE);
536: buf.putInt(getRecipient());
537: session.write(packet);
538: } catch (Exception e) {
539: //e.printStackTrace();
540: }
541: }
542:
543: public boolean isClosed() {
544: return close;
545: }
546:
547: static void disconnect(Session session) {
548: Channel[] channels = null;
549: int count = 0;
550: synchronized (pool) {
551: channels = new Channel[pool.size()];
552: for (int i = 0; i < pool.size(); i++) {
553: try {
554: Channel c = ((Channel) (pool.elementAt(i)));
555: if (c.session == session) {
556: channels[count++] = c;
557: }
558: } catch (Exception e) {
559: }
560: }
561: }
562: for (int i = 0; i < count; i++) {
563: channels[i].disconnect();
564: }
565: }
566:
567: public void disconnect() {
568: //System.err.println(this+":disconnect "+io+" "+connected);
569: //Thread.dumpStack();
570:
571: synchronized (this ) {
572: if (!connected) {
573: return;
574: }
575: connected = false;
576: }
577:
578: close();
579:
580: eof_remote = eof_local = true;
581:
582: thread = null;
583:
584: try {
585: if (io != null) {
586: io.close();
587: }
588: } catch (Exception e) {
589: //e.printStackTrace();
590: }
591: io = null;
592: Channel.del(this );
593: }
594:
595: public boolean isConnected() {
596: if (this .session != null) {
597: return session.isConnected() && connected;
598: }
599: return false;
600: }
601:
602: public void sendSignal(String foo) throws Exception {
603: RequestSignal request = new RequestSignal();
604: request.setSignal(foo);
605: request.request(session, this );
606: }
607:
608: // public String toString(){
609: // return "Channel: type="+new String(type)+",id="+id+",recipient="+recipient+",window_size="+window_size+",packet_size="+packet_size;
610: // }
611:
612: /*
613: class OutputThread extends Thread{
614: Channel c;
615: OutputThread(Channel c){ this.c=c;}
616: public void run(){c.output_thread();}
617: }
618: */
619:
620: class PassiveInputStream extends MyPipedInputStream {
621: PipedOutputStream out;
622:
623: PassiveInputStream(PipedOutputStream out, int size)
624: throws IOException {
625: super (out, size);
626: this .out = out;
627: }
628:
629: PassiveInputStream(PipedOutputStream out) throws IOException {
630: super (out);
631: this .out = out;
632: }
633:
634: public void close() throws IOException {
635: if (out != null) {
636: this .out.close();
637: }
638: out = null;
639: }
640: }
641:
642: class PassiveOutputStream extends PipedOutputStream {
643: PassiveOutputStream(PipedInputStream in) throws IOException {
644: super (in);
645: }
646: }
647:
648: void setExitStatus(int foo) {
649: exitstatus = foo;
650: }
651:
652: public int getExitStatus() {
653: return exitstatus;
654: }
655:
656: void setSession(Session session) {
657: this .session = session;
658: }
659:
660: public Session getSession() {
661: return session;
662: }
663:
664: public int getId() {
665: return id;
666: }
667:
668: //public int getRecipientId(){ return getRecipient(); }
669:
670: protected void sendOpenConfirmation() throws Exception {
671: Buffer buf = new Buffer(100);
672: Packet packet = new Packet(buf);
673: packet.reset();
674: buf.putByte((byte) SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
675: buf.putInt(getRecipient());
676: buf.putInt(id);
677: buf.putInt(lwsize);
678: buf.putInt(lmpsize);
679: session.write(packet);
680: }
681:
682: protected void sendOpenFailure(int reasoncode) {
683: try {
684: Buffer buf = new Buffer(100);
685: Packet packet = new Packet(buf);
686: packet.reset();
687: buf.putByte((byte) SSH_MSG_CHANNEL_OPEN_FAILURE);
688: buf.putInt(getRecipient());
689: buf.putInt(reasoncode);
690: buf.putString("open failed".getBytes());
691: buf.putString("".getBytes());
692: session.write(packet);
693: } catch (Exception e) {
694: }
695: }
696: }
|