0001: package ch.ethz.ssh2.channel;
0002:
0003: import java.io.IOException;
0004: import java.util.HashMap;
0005: import java.util.Vector;
0006:
0007: import ch.ethz.ssh2.ChannelCondition;
0008: import ch.ethz.ssh2.log.Logger;
0009: import ch.ethz.ssh2.packets.PacketChannelOpenConfirmation;
0010: import ch.ethz.ssh2.packets.PacketChannelOpenFailure;
0011: import ch.ethz.ssh2.packets.PacketGlobalCancelForwardRequest;
0012: import ch.ethz.ssh2.packets.PacketGlobalForwardRequest;
0013: import ch.ethz.ssh2.packets.PacketOpenDirectTCPIPChannel;
0014: import ch.ethz.ssh2.packets.PacketOpenSessionChannel;
0015: import ch.ethz.ssh2.packets.PacketSessionExecCommand;
0016: import ch.ethz.ssh2.packets.PacketSessionPtyRequest;
0017: import ch.ethz.ssh2.packets.PacketSessionStartShell;
0018: import ch.ethz.ssh2.packets.PacketSessionSubsystemRequest;
0019: import ch.ethz.ssh2.packets.PacketSessionX11Request;
0020: import ch.ethz.ssh2.packets.Packets;
0021: import ch.ethz.ssh2.packets.TypesReader;
0022: import ch.ethz.ssh2.transport.MessageHandler;
0023: import ch.ethz.ssh2.transport.TransportManager;
0024:
0025: /**
0026: * ChannelManager. Please read the comments in Channel.java.
0027: * <p>
0028: * Besides the crypto part, this is the core of the library.
0029: *
0030: * @author Christian Plattner, plattner@inf.ethz.ch
0031: * @version $Id: ChannelManager.java,v 1.15 2006/08/11 12:24:01 cplattne Exp $
0032: */
0033: public class ChannelManager implements MessageHandler {
0034: private static final Logger log = Logger
0035: .getLogger(ChannelManager.class);
0036:
0037: private HashMap x11_magic_cookies = new HashMap();
0038:
0039: private TransportManager tm;
0040:
0041: private Vector channels = new Vector();
0042: private int nextLocalChannel = 100;
0043: private boolean shutdown = false;
0044: private int globalSuccessCounter = 0;
0045: private int globalFailedCounter = 0;
0046:
0047: private HashMap remoteForwardings = new HashMap();
0048:
0049: private Vector listenerThreads = new Vector();
0050:
0051: private boolean listenerThreadsAllowed = true;
0052:
0053: public ChannelManager(TransportManager tm) {
0054: this .tm = tm;
0055: tm.registerMessageHandler(this , 80, 100);
0056: }
0057:
0058: private Channel getChannel(int id) {
0059: synchronized (channels) {
0060: for (int i = 0; i < channels.size(); i++) {
0061: Channel c = (Channel) channels.elementAt(i);
0062: if (c.localID == id)
0063: return c;
0064: }
0065: }
0066: return null;
0067: }
0068:
0069: private void removeChannel(int id) {
0070: synchronized (channels) {
0071: for (int i = 0; i < channels.size(); i++) {
0072: Channel c = (Channel) channels.elementAt(i);
0073: if (c.localID == id) {
0074: channels.removeElementAt(i);
0075: break;
0076: }
0077: }
0078: }
0079: }
0080:
0081: private int addChannel(Channel c) {
0082: synchronized (channels) {
0083: channels.addElement(c);
0084: return nextLocalChannel++;
0085: }
0086: }
0087:
0088: private void waitUntilChannelOpen(Channel c) throws IOException {
0089: synchronized (c) {
0090: while (c.state == Channel.STATE_OPENING) {
0091: try {
0092: c.wait();
0093: } catch (InterruptedException ignore) {
0094: }
0095: }
0096:
0097: if (c.state != Channel.STATE_OPEN) {
0098: removeChannel(c.localID);
0099:
0100: String detail = c.getReasonClosed();
0101:
0102: if (detail == null)
0103: detail = "state: " + c.state;
0104:
0105: throw new IOException("Could not open channel ("
0106: + detail + ")");
0107: }
0108: }
0109: }
0110:
0111: private final void waitForGlobalSuccessOrFailure()
0112: throws IOException {
0113: synchronized (channels) {
0114: while ((globalSuccessCounter == 0)
0115: && (globalFailedCounter == 0)) {
0116: if (shutdown) {
0117: throw new IOException(
0118: "The connection is being shutdown");
0119: }
0120:
0121: try {
0122: channels.wait();
0123: } catch (InterruptedException ignore) {
0124: }
0125: }
0126:
0127: if (globalFailedCounter != 0) {
0128: throw new IOException(
0129: "The server denied the request (did you enable port forwarding?)");
0130: }
0131:
0132: if (globalSuccessCounter == 0) {
0133: throw new IOException("Illegal state.");
0134: }
0135:
0136: }
0137: }
0138:
0139: private final void waitForChannelSuccessOrFailure(Channel c)
0140: throws IOException {
0141: synchronized (c) {
0142: while ((c.successCounter == 0) && (c.failedCounter == 0)) {
0143: if (c.state != Channel.STATE_OPEN) {
0144: String detail = c.getReasonClosed();
0145:
0146: if (detail == null)
0147: detail = "state: " + c.state;
0148:
0149: throw new IOException(
0150: "This SSH2 channel is not open (" + detail
0151: + ")");
0152: }
0153:
0154: try {
0155: c.wait();
0156: } catch (InterruptedException ignore) {
0157: }
0158: }
0159:
0160: if (c.failedCounter != 0) {
0161: throw new IOException("The server denied the request.");
0162: }
0163: }
0164: }
0165:
0166: public void registerX11Cookie(String hexFakeCookie,
0167: X11ServerData data) {
0168: synchronized (x11_magic_cookies) {
0169: x11_magic_cookies.put(hexFakeCookie, data);
0170: }
0171: }
0172:
0173: public void unRegisterX11Cookie(String hexFakeCookie,
0174: boolean killChannels) {
0175: if (hexFakeCookie == null)
0176: throw new IllegalStateException(
0177: "hexFakeCookie may not be null");
0178:
0179: synchronized (x11_magic_cookies) {
0180: x11_magic_cookies.remove(hexFakeCookie);
0181: }
0182:
0183: if (killChannels == false)
0184: return;
0185:
0186: if (log.isEnabled())
0187: log
0188: .log(50,
0189: "Closing all X11 channels for the given fake cookie");
0190:
0191: Vector channel_copy;
0192:
0193: synchronized (channels) {
0194: channel_copy = (Vector) channels.clone();
0195: }
0196:
0197: for (int i = 0; i < channel_copy.size(); i++) {
0198: Channel c = (Channel) channel_copy.elementAt(i);
0199:
0200: synchronized (c) {
0201: if (hexFakeCookie.equals(c.hexX11FakeCookie) == false)
0202: continue;
0203: }
0204:
0205: try {
0206: closeChannel(
0207: c,
0208: "Closing X11 channel since the corresponding session is closing",
0209: true);
0210: } catch (IOException e) {
0211: }
0212: }
0213: }
0214:
0215: public X11ServerData checkX11Cookie(String hexFakeCookie) {
0216: synchronized (x11_magic_cookies) {
0217: if (hexFakeCookie != null)
0218: return (X11ServerData) x11_magic_cookies
0219: .get(hexFakeCookie);
0220: }
0221: return null;
0222: }
0223:
0224: public void closeAllChannels() {
0225: if (log.isEnabled())
0226: log.log(50, "Closing all channels");
0227:
0228: Vector channel_copy;
0229:
0230: synchronized (channels) {
0231: channel_copy = (Vector) channels.clone();
0232: }
0233:
0234: for (int i = 0; i < channel_copy.size(); i++) {
0235: Channel c = (Channel) channel_copy.elementAt(i);
0236: try {
0237: closeChannel(c, "Closing all channels", true);
0238: } catch (IOException e) {
0239: }
0240: }
0241: }
0242:
0243: public void closeChannel(Channel c, String reason, boolean force)
0244: throws IOException {
0245: byte msg[] = new byte[5];
0246:
0247: synchronized (c) {
0248: if (force) {
0249: c.state = Channel.STATE_CLOSED;
0250: c.EOF = true;
0251: }
0252:
0253: c.setReasonClosed(reason);
0254:
0255: msg[0] = Packets.SSH_MSG_CHANNEL_CLOSE;
0256: msg[1] = (byte) (c.remoteID >> 24);
0257: msg[2] = (byte) (c.remoteID >> 16);
0258: msg[3] = (byte) (c.remoteID >> 8);
0259: msg[4] = (byte) (c.remoteID);
0260:
0261: c.notifyAll();
0262: }
0263:
0264: synchronized (c.channelSendLock) {
0265: if (c.closeMessageSent == true)
0266: return;
0267: tm.sendMessage(msg);
0268: c.closeMessageSent = true;
0269: }
0270:
0271: if (log.isEnabled())
0272: log.log(50, "Sent SSH_MSG_CHANNEL_CLOSE (channel "
0273: + c.localID + ")");
0274: }
0275:
0276: public void sendEOF(Channel c) throws IOException {
0277: byte[] msg = new byte[5];
0278:
0279: synchronized (c) {
0280: if (c.state != Channel.STATE_OPEN)
0281: return;
0282:
0283: msg[0] = Packets.SSH_MSG_CHANNEL_EOF;
0284: msg[1] = (byte) (c.remoteID >> 24);
0285: msg[2] = (byte) (c.remoteID >> 16);
0286: msg[3] = (byte) (c.remoteID >> 8);
0287: msg[4] = (byte) (c.remoteID);
0288: }
0289:
0290: synchronized (c.channelSendLock) {
0291: if (c.closeMessageSent == true)
0292: return;
0293: tm.sendMessage(msg);
0294: }
0295:
0296: if (log.isEnabled())
0297: log.log(50, "Sent EOF (Channel " + c.localID + "/"
0298: + c.remoteID + ")");
0299: }
0300:
0301: public void sendOpenConfirmation(Channel c) throws IOException {
0302: PacketChannelOpenConfirmation pcoc = null;
0303:
0304: synchronized (c) {
0305: if (c.state != Channel.STATE_OPENING)
0306: return;
0307:
0308: c.state = Channel.STATE_OPEN;
0309:
0310: pcoc = new PacketChannelOpenConfirmation(c.remoteID,
0311: c.localID, c.localWindow, c.localMaxPacketSize);
0312: }
0313:
0314: synchronized (c.channelSendLock) {
0315: if (c.closeMessageSent == true)
0316: return;
0317: tm.sendMessage(pcoc.getPayload());
0318: }
0319: }
0320:
0321: public void sendData(Channel c, byte[] buffer, int pos, int len)
0322: throws IOException {
0323: while (len > 0) {
0324: int this len = 0;
0325: byte[] msg;
0326:
0327: synchronized (c) {
0328: while (true) {
0329: if (c.state == Channel.STATE_CLOSED)
0330: throw new IOException(
0331: "SSH channel is closed. ("
0332: + c.getReasonClosed() + ")");
0333:
0334: if (c.state != Channel.STATE_OPEN)
0335: throw new IOException(
0336: "SSH channel in strange state. ("
0337: + c.state + ")");
0338:
0339: if (c.remoteWindow != 0)
0340: break;
0341:
0342: try {
0343: c.wait();
0344: } catch (InterruptedException ignore) {
0345: }
0346: }
0347:
0348: /* len > 0, no sign extension can happen when comparing */
0349:
0350: this len = (c.remoteWindow >= len) ? len
0351: : (int) c.remoteWindow;
0352:
0353: int estimatedMaxDataLen = c.remoteMaxPacketSize
0354: - (tm.getPacketOverheadEstimate() + 9);
0355:
0356: /* The worst case scenario =) a true bottleneck */
0357:
0358: if (estimatedMaxDataLen <= 0) {
0359: estimatedMaxDataLen = 1;
0360: }
0361:
0362: if (this len > estimatedMaxDataLen)
0363: this len = estimatedMaxDataLen;
0364:
0365: c.remoteWindow -= this len;
0366:
0367: msg = new byte[1 + 8 + this len];
0368:
0369: msg[0] = Packets.SSH_MSG_CHANNEL_DATA;
0370: msg[1] = (byte) (c.remoteID >> 24);
0371: msg[2] = (byte) (c.remoteID >> 16);
0372: msg[3] = (byte) (c.remoteID >> 8);
0373: msg[4] = (byte) (c.remoteID);
0374: msg[5] = (byte) (this len >> 24);
0375: msg[6] = (byte) (this len >> 16);
0376: msg[7] = (byte) (this len >> 8);
0377: msg[8] = (byte) (this len);
0378:
0379: System.arraycopy(buffer, pos, msg, 9, this len);
0380: }
0381:
0382: synchronized (c.channelSendLock) {
0383: if (c.closeMessageSent == true)
0384: throw new IOException("SSH channel is closed. ("
0385: + c.getReasonClosed() + ")");
0386:
0387: tm.sendMessage(msg);
0388: }
0389:
0390: pos += this len;
0391: len -= this len;
0392: }
0393: }
0394:
0395: public int requestGlobalForward(String bindAddress, int bindPort,
0396: String targetAddress, int targetPort) throws IOException {
0397: RemoteForwardingData rfd = new RemoteForwardingData();
0398:
0399: rfd.bindAddress = bindAddress;
0400: rfd.bindPort = bindPort;
0401: rfd.targetAddress = targetAddress;
0402: rfd.targetPort = targetPort;
0403:
0404: synchronized (remoteForwardings) {
0405: Integer key = new Integer(bindPort);
0406:
0407: if (remoteForwardings.get(key) != null) {
0408: throw new IOException(
0409: "There is already a forwarding for remote port "
0410: + bindPort);
0411: }
0412:
0413: remoteForwardings.put(key, rfd);
0414: }
0415:
0416: synchronized (channels) {
0417: globalSuccessCounter = globalFailedCounter = 0;
0418: }
0419:
0420: PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(
0421: true, bindAddress, bindPort);
0422: tm.sendMessage(pgf.getPayload());
0423:
0424: if (log.isEnabled())
0425: log.log(50, "Requesting a remote forwarding ('"
0426: + bindAddress + "', " + bindPort + ")");
0427:
0428: try {
0429: waitForGlobalSuccessOrFailure();
0430: } catch (IOException e) {
0431: synchronized (remoteForwardings) {
0432: remoteForwardings.remove(rfd);
0433: }
0434: throw e;
0435: }
0436:
0437: return bindPort;
0438: }
0439:
0440: public void requestCancelGlobalForward(int bindPort)
0441: throws IOException {
0442: RemoteForwardingData rfd = null;
0443:
0444: synchronized (remoteForwardings) {
0445: rfd = (RemoteForwardingData) remoteForwardings
0446: .get(new Integer(bindPort));
0447:
0448: if (rfd == null)
0449: throw new IOException(
0450: "Sorry, there is no known remote forwarding for remote port "
0451: + bindPort);
0452: }
0453:
0454: synchronized (channels) {
0455: globalSuccessCounter = globalFailedCounter = 0;
0456: }
0457:
0458: PacketGlobalCancelForwardRequest pgcf = new PacketGlobalCancelForwardRequest(
0459: true, rfd.bindAddress, rfd.bindPort);
0460: tm.sendMessage(pgcf.getPayload());
0461:
0462: if (log.isEnabled())
0463: log.log(50, "Requesting cancelation of remote forward ('"
0464: + rfd.bindAddress + "', " + rfd.bindPort + ")");
0465:
0466: waitForGlobalSuccessOrFailure();
0467:
0468: /* Only now we are sure that no more forwarded connections will arrive */
0469:
0470: synchronized (remoteForwardings) {
0471: remoteForwardings.remove(rfd);
0472: }
0473: }
0474:
0475: public void registerThread(IChannelWorkerThread thr)
0476: throws IOException {
0477: synchronized (listenerThreads) {
0478: if (listenerThreadsAllowed == false)
0479: throw new IOException(
0480: "Too late, this connection is closed.");
0481: listenerThreads.addElement(thr);
0482: }
0483: }
0484:
0485: public Channel openDirectTCPIPChannel(String host_to_connect,
0486: int port_to_connect, String originator_IP_address,
0487: int originator_port) throws IOException {
0488: Channel c = new Channel(this );
0489:
0490: synchronized (c) {
0491: c.localID = addChannel(c);
0492: // end of synchronized block forces writing out to main memory
0493: }
0494:
0495: PacketOpenDirectTCPIPChannel dtc = new PacketOpenDirectTCPIPChannel(
0496: c.localID, c.localWindow, c.localMaxPacketSize,
0497: host_to_connect, port_to_connect,
0498: originator_IP_address, originator_port);
0499:
0500: tm.sendMessage(dtc.getPayload());
0501:
0502: waitUntilChannelOpen(c);
0503:
0504: return c;
0505: }
0506:
0507: public Channel openSessionChannel() throws IOException {
0508: Channel c = new Channel(this );
0509:
0510: synchronized (c) {
0511: c.localID = addChannel(c);
0512: // end of synchronized block forces the writing out to main memory
0513: }
0514:
0515: if (log.isEnabled())
0516: log.log(50, "Sending SSH_MSG_CHANNEL_OPEN (Channel "
0517: + c.localID + ")");
0518:
0519: PacketOpenSessionChannel smo = new PacketOpenSessionChannel(
0520: c.localID, c.localWindow, c.localMaxPacketSize);
0521: tm.sendMessage(smo.getPayload());
0522:
0523: waitUntilChannelOpen(c);
0524:
0525: return c;
0526: }
0527:
0528: public void requestPTY(Channel c, String term,
0529: int term_width_characters, int term_height_characters,
0530: int term_width_pixels, int term_height_pixels,
0531: byte[] terminal_modes) throws IOException {
0532: PacketSessionPtyRequest spr;
0533:
0534: synchronized (c) {
0535: if (c.state != Channel.STATE_OPEN)
0536: throw new IOException(
0537: "Cannot request PTY on this channel ("
0538: + c.getReasonClosed() + ")");
0539:
0540: spr = new PacketSessionPtyRequest(c.remoteID, true, term,
0541: term_width_characters, term_height_characters,
0542: term_width_pixels, term_height_pixels,
0543: terminal_modes);
0544:
0545: c.successCounter = c.failedCounter = 0;
0546: }
0547:
0548: synchronized (c.channelSendLock) {
0549: if (c.closeMessageSent)
0550: throw new IOException(
0551: "Cannot request PTY on this channel ("
0552: + c.getReasonClosed() + ")");
0553: tm.sendMessage(spr.getPayload());
0554: }
0555:
0556: try {
0557: waitForChannelSuccessOrFailure(c);
0558: } catch (IOException e) {
0559: throw (IOException) new IOException("PTY request failed")
0560: .initCause(e);
0561: }
0562: }
0563:
0564: public void requestX11(Channel c, boolean singleConnection,
0565: String x11AuthenticationProtocol,
0566: String x11AuthenticationCookie, int x11ScreenNumber)
0567: throws IOException {
0568: PacketSessionX11Request psr;
0569:
0570: synchronized (c) {
0571: if (c.state != Channel.STATE_OPEN)
0572: throw new IOException(
0573: "Cannot request X11 on this channel ("
0574: + c.getReasonClosed() + ")");
0575:
0576: psr = new PacketSessionX11Request(c.remoteID, true,
0577: singleConnection, x11AuthenticationProtocol,
0578: x11AuthenticationCookie, x11ScreenNumber);
0579:
0580: c.successCounter = c.failedCounter = 0;
0581: }
0582:
0583: synchronized (c.channelSendLock) {
0584: if (c.closeMessageSent)
0585: throw new IOException(
0586: "Cannot request X11 on this channel ("
0587: + c.getReasonClosed() + ")");
0588: tm.sendMessage(psr.getPayload());
0589: }
0590:
0591: if (log.isEnabled())
0592: log.log(50, "Requesting X11 forwarding (Channel "
0593: + c.localID + "/" + c.remoteID + ")");
0594:
0595: try {
0596: waitForChannelSuccessOrFailure(c);
0597: } catch (IOException e) {
0598: throw (IOException) new IOException(
0599: "The X11 request failed.").initCause(e);
0600: }
0601: }
0602:
0603: public void requestSubSystem(Channel c, String subSystemName)
0604: throws IOException {
0605: PacketSessionSubsystemRequest ssr;
0606:
0607: synchronized (c) {
0608: if (c.state != Channel.STATE_OPEN)
0609: throw new IOException(
0610: "Cannot request subsystem on this channel ("
0611: + c.getReasonClosed() + ")");
0612:
0613: ssr = new PacketSessionSubsystemRequest(c.remoteID, true,
0614: subSystemName);
0615:
0616: c.successCounter = c.failedCounter = 0;
0617: }
0618:
0619: synchronized (c.channelSendLock) {
0620: if (c.closeMessageSent)
0621: throw new IOException(
0622: "Cannot request subsystem on this channel ("
0623: + c.getReasonClosed() + ")");
0624: tm.sendMessage(ssr.getPayload());
0625: }
0626:
0627: try {
0628: waitForChannelSuccessOrFailure(c);
0629: } catch (IOException e) {
0630: throw (IOException) new IOException(
0631: "The subsystem request failed.").initCause(e);
0632: }
0633: }
0634:
0635: public void requestExecCommand(Channel c, String cmd)
0636: throws IOException {
0637: PacketSessionExecCommand sm;
0638:
0639: synchronized (c) {
0640: if (c.state != Channel.STATE_OPEN)
0641: throw new IOException(
0642: "Cannot execute command on this channel ("
0643: + c.getReasonClosed() + ")");
0644:
0645: sm = new PacketSessionExecCommand(c.remoteID, true, cmd);
0646:
0647: c.successCounter = c.failedCounter = 0;
0648: }
0649:
0650: synchronized (c.channelSendLock) {
0651: if (c.closeMessageSent)
0652: throw new IOException(
0653: "Cannot execute command on this channel ("
0654: + c.getReasonClosed() + ")");
0655: tm.sendMessage(sm.getPayload());
0656: }
0657:
0658: if (log.isEnabled())
0659: log.log(50, "Executing command (channel " + c.localID
0660: + ", '" + cmd + "')");
0661:
0662: try {
0663: waitForChannelSuccessOrFailure(c);
0664: } catch (IOException e) {
0665: throw (IOException) new IOException(
0666: "The execute request failed.").initCause(e);
0667: }
0668: }
0669:
0670: public void requestShell(Channel c) throws IOException {
0671: PacketSessionStartShell sm;
0672:
0673: synchronized (c) {
0674: if (c.state != Channel.STATE_OPEN)
0675: throw new IOException(
0676: "Cannot start shell on this channel ("
0677: + c.getReasonClosed() + ")");
0678:
0679: sm = new PacketSessionStartShell(c.remoteID, true);
0680:
0681: c.successCounter = c.failedCounter = 0;
0682: }
0683:
0684: synchronized (c.channelSendLock) {
0685: if (c.closeMessageSent)
0686: throw new IOException(
0687: "Cannot start shell on this channel ("
0688: + c.getReasonClosed() + ")");
0689: tm.sendMessage(sm.getPayload());
0690: }
0691:
0692: try {
0693: waitForChannelSuccessOrFailure(c);
0694: } catch (IOException e) {
0695: throw (IOException) new IOException(
0696: "The shell request failed.").initCause(e);
0697: }
0698: }
0699:
0700: public void msgChannelExtendedData(byte[] msg, int msglen)
0701: throws IOException {
0702: if (msglen <= 13)
0703: throw new IOException(
0704: "SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size ("
0705: + msglen + ")");
0706:
0707: int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16)
0708: | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
0709: int dataType = ((msg[5] & 0xff) << 24)
0710: | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8)
0711: | (msg[8] & 0xff);
0712: int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16)
0713: | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff);
0714:
0715: Channel c = getChannel(id);
0716:
0717: if (c == null)
0718: throw new IOException(
0719: "Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel "
0720: + id);
0721:
0722: if (dataType != Packets.SSH_EXTENDED_DATA_STDERR)
0723: throw new IOException(
0724: "SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type ("
0725: + dataType + ")");
0726:
0727: if (len != (msglen - 13))
0728: throw new IOException(
0729: "SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated "
0730: + (msglen - 13) + ", got " + len + ")");
0731:
0732: if (log.isEnabled())
0733: log.log(80, "Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel "
0734: + id + ", " + len + ")");
0735:
0736: synchronized (c) {
0737: if (c.state == Channel.STATE_CLOSED)
0738: return; // ignore
0739:
0740: if (c.state != Channel.STATE_OPEN)
0741: throw new IOException(
0742: "Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state ("
0743: + c.state + ")");
0744:
0745: if (c.localWindow < len)
0746: throw new IOException(
0747: "Remote sent too much data, does not fit into window.");
0748:
0749: c.localWindow -= len;
0750:
0751: System.arraycopy(msg, 13, c.stderrBuffer, c.stderrWritepos,
0752: len);
0753: c.stderrWritepos += len;
0754:
0755: c.notifyAll();
0756: }
0757: }
0758:
0759: /**
0760: * Wait until for a condition.
0761: *
0762: * @param c
0763: * Channel
0764: * @param timeout
0765: * in ms, 0 means no timeout.
0766: * @param condition_mask
0767: * minimum event mask
0768: * @return all current events
0769: *
0770: */
0771: public int waitForCondition(Channel c, long timeout,
0772: int condition_mask) {
0773: long end_time = 0;
0774: boolean end_time_set = false;
0775:
0776: synchronized (c) {
0777: while (true) {
0778: int current_cond = 0;
0779:
0780: int stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
0781: int stderrAvail = c.stderrWritepos - c.stderrReadpos;
0782:
0783: if (stdoutAvail > 0)
0784: current_cond = current_cond
0785: | ChannelCondition.STDOUT_DATA;
0786:
0787: if (stderrAvail > 0)
0788: current_cond = current_cond
0789: | ChannelCondition.STDERR_DATA;
0790:
0791: if (c.EOF)
0792: current_cond = current_cond | ChannelCondition.EOF;
0793:
0794: if (c.getExitStatus() != null)
0795: current_cond = current_cond
0796: | ChannelCondition.EXIT_STATUS;
0797:
0798: if (c.getExitSignal() != null)
0799: current_cond = current_cond
0800: | ChannelCondition.EXIT_SIGNAL;
0801:
0802: if (c.state == Channel.STATE_CLOSED)
0803: return current_cond | ChannelCondition.CLOSED
0804: | ChannelCondition.EOF;
0805:
0806: if ((current_cond & condition_mask) != 0)
0807: return current_cond;
0808:
0809: if (timeout > 0) {
0810: if (!end_time_set) {
0811: end_time = System.currentTimeMillis() + timeout;
0812: end_time_set = true;
0813: } else {
0814: timeout = end_time - System.currentTimeMillis();
0815:
0816: if (timeout <= 0)
0817: return current_cond
0818: | ChannelCondition.TIMEOUT;
0819: }
0820: }
0821:
0822: try {
0823: if (timeout > 0)
0824: c.wait(timeout);
0825: else
0826: c.wait();
0827: } catch (InterruptedException e) {
0828: }
0829: }
0830: }
0831: }
0832:
0833: public int getAvailable(Channel c, boolean extended)
0834: throws IOException {
0835: synchronized (c) {
0836: int avail;
0837:
0838: if (extended)
0839: avail = c.stderrWritepos - c.stderrReadpos;
0840: else
0841: avail = c.stdoutWritepos - c.stdoutReadpos;
0842:
0843: return ((avail > 0) ? avail : (c.EOF ? -1 : 0));
0844: }
0845: }
0846:
0847: public int getChannelData(Channel c, boolean extended,
0848: byte[] target, int off, int len) throws IOException {
0849: int copylen = 0;
0850: int increment = 0;
0851: int remoteID = 0;
0852: int localID = 0;
0853:
0854: synchronized (c) {
0855: int stdoutAvail = 0;
0856: int stderrAvail = 0;
0857:
0858: while (true) {
0859: /*
0860: * Data available? We have to return remaining data even if the
0861: * channel is already closed.
0862: */
0863:
0864: stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
0865: stderrAvail = c.stderrWritepos - c.stderrReadpos;
0866:
0867: if ((!extended) && (stdoutAvail != 0))
0868: break;
0869:
0870: if ((extended) && (stderrAvail != 0))
0871: break;
0872:
0873: /* Do not wait if more data will never arrive (EOF or CLOSED) */
0874:
0875: if ((c.EOF) || (c.state != Channel.STATE_OPEN))
0876: return -1;
0877:
0878: try {
0879: c.wait();
0880: } catch (InterruptedException ignore) {
0881: }
0882: }
0883:
0884: /* OK, there is some data. Return it. */
0885:
0886: if (!extended) {
0887: copylen = (stdoutAvail > len) ? len : stdoutAvail;
0888: System.arraycopy(c.stdoutBuffer, c.stdoutReadpos,
0889: target, off, copylen);
0890: c.stdoutReadpos += copylen;
0891:
0892: if (c.stdoutReadpos != c.stdoutWritepos)
0893:
0894: System.arraycopy(c.stdoutBuffer, c.stdoutReadpos,
0895: c.stdoutBuffer, 0, c.stdoutWritepos
0896: - c.stdoutReadpos);
0897:
0898: c.stdoutWritepos -= c.stdoutReadpos;
0899: c.stdoutReadpos = 0;
0900: } else {
0901: copylen = (stderrAvail > len) ? len : stderrAvail;
0902: System.arraycopy(c.stderrBuffer, c.stderrReadpos,
0903: target, off, copylen);
0904: c.stderrReadpos += copylen;
0905:
0906: if (c.stderrReadpos != c.stderrWritepos)
0907:
0908: System.arraycopy(c.stderrBuffer, c.stderrReadpos,
0909: c.stderrBuffer, 0, c.stderrWritepos
0910: - c.stderrReadpos);
0911:
0912: c.stderrWritepos -= c.stderrReadpos;
0913: c.stderrReadpos = 0;
0914: }
0915:
0916: if (c.state != Channel.STATE_OPEN)
0917: return copylen;
0918:
0919: if (c.localWindow < ((Channel.CHANNEL_BUFFER_SIZE + 1) / 2)) {
0920: int minFreeSpace = Math.min(Channel.CHANNEL_BUFFER_SIZE
0921: - c.stdoutWritepos, Channel.CHANNEL_BUFFER_SIZE
0922: - c.stderrWritepos);
0923:
0924: increment = minFreeSpace - c.localWindow;
0925: c.localWindow = minFreeSpace;
0926: }
0927:
0928: remoteID = c.remoteID; /* read while holding the lock */
0929: localID = c.localID; /* read while holding the lock */
0930: }
0931:
0932: /*
0933: * If a consumer reads stdout and stdin in parallel, we may end up with
0934: * sending two msgWindowAdjust messages. Luckily, it
0935: * does not matter in which order they arrive at the server.
0936: */
0937:
0938: if (increment > 0) {
0939: if (log.isEnabled())
0940: log.log(80,
0941: "Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel "
0942: + localID + ", " + increment + ")");
0943:
0944: synchronized (c.channelSendLock) {
0945: byte[] msg = c.msgWindowAdjust;
0946:
0947: msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
0948: msg[1] = (byte) (remoteID >> 24);
0949: msg[2] = (byte) (remoteID >> 16);
0950: msg[3] = (byte) (remoteID >> 8);
0951: msg[4] = (byte) (remoteID);
0952: msg[5] = (byte) (increment >> 24);
0953: msg[6] = (byte) (increment >> 16);
0954: msg[7] = (byte) (increment >> 8);
0955: msg[8] = (byte) (increment);
0956:
0957: if (c.closeMessageSent == false)
0958: tm.sendMessage(msg);
0959: }
0960: }
0961:
0962: return copylen;
0963: }
0964:
0965: public void msgChannelData(byte[] msg, int msglen)
0966: throws IOException {
0967: if (msglen <= 9)
0968: throw new IOException(
0969: "SSH_MSG_CHANNEL_DATA message has wrong size ("
0970: + msglen + ")");
0971:
0972: int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16)
0973: | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
0974: int len = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16)
0975: | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
0976:
0977: Channel c = getChannel(id);
0978:
0979: if (c == null)
0980: throw new IOException(
0981: "Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel "
0982: + id);
0983:
0984: if (len != (msglen - 9))
0985: throw new IOException(
0986: "SSH_MSG_CHANNEL_DATA message has wrong len (calculated "
0987: + (msglen - 9) + ", got " + len + ")");
0988:
0989: if (log.isEnabled())
0990: log.log(80, "Got SSH_MSG_CHANNEL_DATA (channel " + id
0991: + ", " + len + ")");
0992:
0993: synchronized (c) {
0994: if (c.state == Channel.STATE_CLOSED)
0995: return; // ignore
0996:
0997: if (c.state != Channel.STATE_OPEN)
0998: throw new IOException(
0999: "Got SSH_MSG_CHANNEL_DATA, but channel is not in correct state ("
1000: + c.state + ")");
1001:
1002: if (c.localWindow < len)
1003: throw new IOException(
1004: "Remote sent too much data, does not fit into window.");
1005:
1006: c.localWindow -= len;
1007:
1008: System.arraycopy(msg, 9, c.stdoutBuffer, c.stdoutWritepos,
1009: len);
1010: c.stdoutWritepos += len;
1011:
1012: c.notifyAll();
1013: }
1014: }
1015:
1016: public void msgChannelWindowAdjust(byte[] msg, int msglen)
1017: throws IOException {
1018: if (msglen != 9)
1019: throw new IOException(
1020: "SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size ("
1021: + msglen + ")");
1022:
1023: int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16)
1024: | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1025: int windowChange = ((msg[5] & 0xff) << 24)
1026: | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8)
1027: | (msg[8] & 0xff);
1028:
1029: Channel c = getChannel(id);
1030:
1031: if (c == null)
1032: throw new IOException(
1033: "Unexpected SSH_MSG_CHANNEL_WINDOW_ADJUST message for non-existent channel "
1034: + id);
1035:
1036: synchronized (c) {
1037: final long huge = 0xFFFFffffL; /* 2^32 - 1 */
1038:
1039: c.remoteWindow += (windowChange & huge); /* avoid sign extension */
1040:
1041: /* TODO - is this a good heuristic? */
1042:
1043: if ((c.remoteWindow > huge))
1044: c.remoteWindow = huge;
1045:
1046: c.notifyAll();
1047: }
1048:
1049: if (log.isEnabled())
1050: log.log(80, "Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel "
1051: + id + ", " + windowChange + ")");
1052: }
1053:
1054: public void msgChannelOpen(byte[] msg, int msglen)
1055: throws IOException {
1056: TypesReader tr = new TypesReader(msg, 0, msglen);
1057:
1058: tr.readByte(); // skip packet type
1059: String channelType = tr.readString();
1060: int remoteID = tr.readUINT32(); /* sender channel */
1061: int remoteWindow = tr.readUINT32(); /* initial window size */
1062: int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */
1063:
1064: if ("x11".equals(channelType)) {
1065: synchronized (x11_magic_cookies) {
1066: /* If we did not request X11 forwarding, then simply ignore this bogus request. */
1067:
1068: if (x11_magic_cookies.size() == 0) {
1069: PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(
1070: remoteID,
1071: Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
1072: "X11 forwarding not activated", "");
1073:
1074: tm.sendAsynchronousMessage(pcof.getPayload());
1075:
1076: if (log.isEnabled())
1077: log.log(20,
1078: "Unexpected X11 request, denying it!");
1079:
1080: return;
1081: }
1082: }
1083:
1084: String remoteOriginatorAddress = tr.readString();
1085: int remoteOriginatorPort = tr.readUINT32();
1086:
1087: Channel c = new Channel(this );
1088:
1089: synchronized (c) {
1090: c.remoteID = remoteID;
1091: c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */
1092: c.remoteMaxPacketSize = remoteMaxPacketSize;
1093: c.localID = addChannel(c);
1094: }
1095:
1096: /*
1097: * The open confirmation message will be sent from another thread
1098: */
1099:
1100: RemoteX11AcceptThread rxat = new RemoteX11AcceptThread(c,
1101: remoteOriginatorAddress, remoteOriginatorPort);
1102: rxat.setDaemon(true);
1103: rxat.start();
1104:
1105: return;
1106: }
1107:
1108: if ("forwarded-tcpip".equals(channelType)) {
1109: String remoteConnectedAddress = tr.readString(); /* address that was connected */
1110: int remoteConnectedPort = tr.readUINT32(); /* port that was connected */
1111: String remoteOriginatorAddress = tr.readString(); /* originator IP address */
1112: int remoteOriginatorPort = tr.readUINT32(); /* originator port */
1113:
1114: RemoteForwardingData rfd = null;
1115:
1116: synchronized (remoteForwardings) {
1117: rfd = (RemoteForwardingData) remoteForwardings
1118: .get(new Integer(remoteConnectedPort));
1119: }
1120:
1121: if (rfd == null) {
1122: PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(
1123: remoteID,
1124: Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
1125: "No thanks, unknown port in forwarded-tcpip request",
1126: "");
1127:
1128: /* Always try to be polite. */
1129:
1130: tm.sendAsynchronousMessage(pcof.getPayload());
1131:
1132: if (log.isEnabled())
1133: log
1134: .log(20,
1135: "Unexpected forwarded-tcpip request, denying it!");
1136:
1137: return;
1138: }
1139:
1140: Channel c = new Channel(this );
1141:
1142: synchronized (c) {
1143: c.remoteID = remoteID;
1144: c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
1145: c.remoteMaxPacketSize = remoteMaxPacketSize;
1146: c.localID = addChannel(c);
1147: }
1148:
1149: /*
1150: * The open confirmation message will be sent from another thread.
1151: */
1152:
1153: RemoteAcceptThread rat = new RemoteAcceptThread(c,
1154: remoteConnectedAddress, remoteConnectedPort,
1155: remoteOriginatorAddress, remoteOriginatorPort,
1156: rfd.targetAddress, rfd.targetPort);
1157:
1158: rat.setDaemon(true);
1159: rat.start();
1160:
1161: return;
1162: }
1163:
1164: /* Tell the server that we have no idea what it is talking about */
1165:
1166: PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(
1167: remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
1168: "Unknown channel type", "");
1169:
1170: tm.sendAsynchronousMessage(pcof.getPayload());
1171:
1172: if (log.isEnabled())
1173: log.log(20,
1174: "The peer tried to open an unsupported channel type ("
1175: + channelType + ")");
1176: }
1177:
1178: public void msgChannelRequest(byte[] msg, int msglen)
1179: throws IOException {
1180: TypesReader tr = new TypesReader(msg, 0, msglen);
1181:
1182: tr.readByte(); // skip packet type
1183: int id = tr.readUINT32();
1184:
1185: Channel c = getChannel(id);
1186:
1187: if (c == null)
1188: throw new IOException(
1189: "Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel "
1190: + id);
1191:
1192: String type = tr.readString("US-ASCII");
1193: boolean wantReply = tr.readBoolean();
1194:
1195: if (log.isEnabled())
1196: log.log(80, "Got SSH_MSG_CHANNEL_REQUEST (channel " + id
1197: + ", '" + type + "')");
1198:
1199: if (type.equals("exit-status")) {
1200: if (wantReply != false)
1201: throw new IOException(
1202: "Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true");
1203:
1204: int exit_status = tr.readUINT32();
1205:
1206: if (tr.remain() != 0)
1207: throw new IOException(
1208: "Badly formatted SSH_MSG_CHANNEL_REQUEST message");
1209:
1210: synchronized (c) {
1211: c.exit_status = new Integer(exit_status);
1212: c.notifyAll();
1213: }
1214:
1215: if (log.isEnabled())
1216: log.log(50, "Got EXIT STATUS (channel " + id
1217: + ", status " + exit_status + ")");
1218:
1219: return;
1220: }
1221:
1222: if (type.equals("exit-signal")) {
1223: if (wantReply != false)
1224: throw new IOException(
1225: "Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true");
1226:
1227: String signame = tr.readString("US-ASCII");
1228: tr.readBoolean();
1229: tr.readString();
1230: tr.readString();
1231:
1232: if (tr.remain() != 0)
1233: throw new IOException(
1234: "Badly formatted SSH_MSG_CHANNEL_REQUEST message");
1235:
1236: synchronized (c) {
1237: c.exit_signal = signame;
1238: c.notifyAll();
1239: }
1240:
1241: if (log.isEnabled())
1242: log.log(50, "Got EXIT SIGNAL (channel " + id
1243: + ", signal " + signame + ")");
1244:
1245: return;
1246: }
1247:
1248: /* We simply ignore unknown channel requests, however, if the server wants a reply,
1249: * then we signal that we have no idea what it is about.
1250: */
1251:
1252: if (wantReply) {
1253: byte[] reply = new byte[5];
1254:
1255: reply[0] = Packets.SSH_MSG_CHANNEL_FAILURE;
1256: reply[1] = (byte) (c.remoteID >> 24);
1257: reply[2] = (byte) (c.remoteID >> 16);
1258: reply[3] = (byte) (c.remoteID >> 8);
1259: reply[4] = (byte) (c.remoteID);
1260:
1261: tm.sendAsynchronousMessage(reply);
1262: }
1263:
1264: if (log.isEnabled())
1265: log.log(50, "Channel request '" + type
1266: + "' is not known, ignoring it");
1267: }
1268:
1269: public void msgChannelEOF(byte[] msg, int msglen)
1270: throws IOException {
1271: if (msglen != 5)
1272: throw new IOException(
1273: "SSH_MSG_CHANNEL_EOF message has wrong size ("
1274: + msglen + ")");
1275:
1276: int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16)
1277: | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1278:
1279: Channel c = getChannel(id);
1280:
1281: if (c == null)
1282: throw new IOException(
1283: "Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel "
1284: + id);
1285:
1286: synchronized (c) {
1287: c.EOF = true;
1288: c.notifyAll();
1289: }
1290:
1291: if (log.isEnabled())
1292: log.log(50, "Got SSH_MSG_CHANNEL_EOF (channel " + id + ")");
1293: }
1294:
1295: public void msgChannelClose(byte[] msg, int msglen)
1296: throws IOException {
1297: if (msglen != 5)
1298: throw new IOException(
1299: "SSH_MSG_CHANNEL_CLOSE message has wrong size ("
1300: + msglen + ")");
1301:
1302: int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16)
1303: | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1304:
1305: Channel c = getChannel(id);
1306:
1307: if (c == null)
1308: throw new IOException(
1309: "Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel "
1310: + id);
1311:
1312: synchronized (c) {
1313: c.EOF = true;
1314: c.state = Channel.STATE_CLOSED;
1315: c.setReasonClosed("Close requested by remote");
1316: c.closeMessageRecv = true;
1317:
1318: removeChannel(c.localID);
1319:
1320: c.notifyAll();
1321: }
1322:
1323: if (log.isEnabled())
1324: log.log(50, "Got SSH_MSG_CHANNEL_CLOSE (channel " + id
1325: + ")");
1326: }
1327:
1328: public void msgChannelSuccess(byte[] msg, int msglen)
1329: throws IOException {
1330: if (msglen != 5)
1331: throw new IOException(
1332: "SSH_MSG_CHANNEL_SUCCESS message has wrong size ("
1333: + msglen + ")");
1334:
1335: int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16)
1336: | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1337:
1338: Channel c = getChannel(id);
1339:
1340: if (c == null)
1341: throw new IOException(
1342: "Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel "
1343: + id);
1344:
1345: synchronized (c) {
1346: c.successCounter++;
1347: c.notifyAll();
1348: }
1349:
1350: if (log.isEnabled())
1351: log.log(80, "Got SSH_MSG_CHANNEL_SUCCESS (channel " + id
1352: + ")");
1353: }
1354:
1355: public void msgChannelFailure(byte[] msg, int msglen)
1356: throws IOException {
1357: if (msglen != 5)
1358: throw new IOException(
1359: "SSH_MSG_CHANNEL_FAILURE message has wrong size ("
1360: + msglen + ")");
1361:
1362: int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16)
1363: | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1364:
1365: Channel c = getChannel(id);
1366:
1367: if (c == null)
1368: throw new IOException(
1369: "Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel "
1370: + id);
1371:
1372: synchronized (c) {
1373: c.failedCounter++;
1374: c.notifyAll();
1375: }
1376:
1377: if (log.isEnabled())
1378: log.log(50, "Got SSH_MSG_CHANNEL_FAILURE (channel " + id
1379: + ")");
1380: }
1381:
1382: public void msgChannelOpenConfirmation(byte[] msg, int msglen)
1383: throws IOException {
1384: PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(
1385: msg, 0, msglen);
1386:
1387: Channel c = getChannel(sm.recipientChannelID);
1388:
1389: if (c == null)
1390: throw new IOException(
1391: "Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel "
1392: + sm.recipientChannelID);
1393:
1394: synchronized (c) {
1395: if (c.state != Channel.STATE_OPENING)
1396: throw new IOException(
1397: "Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel "
1398: + sm.recipientChannelID);
1399:
1400: c.remoteID = sm.senderChannelID;
1401: c.remoteWindow = sm.initialWindowSize & 0xFFFFffffL; /* convert UINT32 to long */
1402: c.remoteMaxPacketSize = sm.maxPacketSize;
1403: c.state = Channel.STATE_OPEN;
1404: c.notifyAll();
1405: }
1406:
1407: if (log.isEnabled())
1408: log.log(50,
1409: "Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel "
1410: + sm.recipientChannelID + " / remote: "
1411: + sm.senderChannelID + ")");
1412: }
1413:
1414: public void msgChannelOpenFailure(byte[] msg, int msglen)
1415: throws IOException {
1416: if (msglen < 5)
1417: throw new IOException(
1418: "SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size ("
1419: + msglen + ")");
1420:
1421: TypesReader tr = new TypesReader(msg, 0, msglen);
1422:
1423: tr.readByte(); // skip packet type
1424: int id = tr.readUINT32(); /* sender channel */
1425:
1426: Channel c = getChannel(id);
1427:
1428: if (c == null)
1429: throw new IOException(
1430: "Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel "
1431: + id);
1432:
1433: int reasonCode = tr.readUINT32();
1434: String description = tr.readString("UTF-8");
1435:
1436: String reasonCodeSymbolicName = null;
1437:
1438: switch (reasonCode) {
1439: case 1:
1440: reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED";
1441: break;
1442: case 2:
1443: reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED";
1444: break;
1445: case 3:
1446: reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE";
1447: break;
1448: case 4:
1449: reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE";
1450: break;
1451: default:
1452: reasonCodeSymbolicName = "UNKNOWN REASON CODE ("
1453: + reasonCode + ")";
1454: }
1455:
1456: StringBuffer descriptionBuffer = new StringBuffer();
1457: descriptionBuffer.append(description);
1458:
1459: for (int i = 0; i < descriptionBuffer.length(); i++) {
1460: char cc = descriptionBuffer.charAt(i);
1461:
1462: if ((cc >= 32) && (cc <= 126))
1463: continue;
1464: descriptionBuffer.setCharAt(i, '\uFFFD');
1465: }
1466:
1467: synchronized (c) {
1468: c.EOF = true;
1469: c.state = Channel.STATE_CLOSED;
1470: c
1471: .setReasonClosed("The server refused to open the channel ("
1472: + reasonCodeSymbolicName
1473: + ", '"
1474: + descriptionBuffer.toString() + "')");
1475: c.notifyAll();
1476: }
1477:
1478: if (log.isEnabled())
1479: log.log(50, "Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel "
1480: + id + ")");
1481: }
1482:
1483: public void msgGlobalRequest(byte[] msg, int msglen)
1484: throws IOException {
1485: /* Currently we do not support any kind of global request */
1486:
1487: TypesReader tr = new TypesReader(msg, 0, msglen);
1488:
1489: tr.readByte(); // skip packet type
1490: String requestName = tr.readString();
1491: boolean wantReply = tr.readBoolean();
1492:
1493: if (wantReply) {
1494: byte[] reply_failure = new byte[1];
1495: reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE;
1496:
1497: tm.sendAsynchronousMessage(reply_failure);
1498: }
1499:
1500: /* We do not clean up the requestName String - that is OK for debug */
1501:
1502: if (log.isEnabled())
1503: log.log(80, "Got SSH_MSG_GLOBAL_REQUEST (" + requestName
1504: + ")");
1505: }
1506:
1507: public void msgGlobalSuccess() throws IOException {
1508: synchronized (channels) {
1509: globalSuccessCounter++;
1510: channels.notifyAll();
1511: }
1512:
1513: if (log.isEnabled())
1514: log.log(80, "Got SSH_MSG_REQUEST_SUCCESS");
1515: }
1516:
1517: public void msgGlobalFailure() throws IOException {
1518: synchronized (channels) {
1519: globalFailedCounter++;
1520: channels.notifyAll();
1521: }
1522:
1523: if (log.isEnabled())
1524: log.log(80, "Got SSH_MSG_REQUEST_FAILURE");
1525: }
1526:
1527: public void handleMessage(byte[] msg, int msglen)
1528: throws IOException {
1529: if (msg == null) {
1530: if (log.isEnabled())
1531: log.log(50, "HandleMessage: got shutdown");
1532:
1533: synchronized (listenerThreads) {
1534: for (int i = 0; i < listenerThreads.size(); i++) {
1535: IChannelWorkerThread lat = (IChannelWorkerThread) listenerThreads
1536: .elementAt(i);
1537: lat.stopWorking();
1538: }
1539: listenerThreadsAllowed = false;
1540: }
1541:
1542: synchronized (channels) {
1543: shutdown = true;
1544:
1545: for (int i = 0; i < channels.size(); i++) {
1546: Channel c = (Channel) channels.elementAt(i);
1547: synchronized (c) {
1548: c.EOF = true;
1549: c.state = Channel.STATE_CLOSED;
1550: c
1551: .setReasonClosed("The connection is being shutdown");
1552: c.closeMessageRecv = true; /*
1553: * You never know, perhaps
1554: * we are waiting for a
1555: * pending close message
1556: * from the server...
1557: */
1558: c.notifyAll();
1559: }
1560: }
1561: /* Works with J2ME */
1562: channels.setSize(0);
1563: channels.trimToSize();
1564: channels.notifyAll(); /* Notify global response waiters */
1565: return;
1566: }
1567: }
1568:
1569: switch (msg[0]) {
1570: case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
1571: msgChannelOpenConfirmation(msg, msglen);
1572: break;
1573: case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST:
1574: msgChannelWindowAdjust(msg, msglen);
1575: break;
1576: case Packets.SSH_MSG_CHANNEL_DATA:
1577: msgChannelData(msg, msglen);
1578: break;
1579: case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA:
1580: msgChannelExtendedData(msg, msglen);
1581: break;
1582: case Packets.SSH_MSG_CHANNEL_REQUEST:
1583: msgChannelRequest(msg, msglen);
1584: break;
1585: case Packets.SSH_MSG_CHANNEL_EOF:
1586: msgChannelEOF(msg, msglen);
1587: break;
1588: case Packets.SSH_MSG_CHANNEL_OPEN:
1589: msgChannelOpen(msg, msglen);
1590: break;
1591: case Packets.SSH_MSG_CHANNEL_CLOSE:
1592: msgChannelClose(msg, msglen);
1593: break;
1594: case Packets.SSH_MSG_CHANNEL_SUCCESS:
1595: msgChannelSuccess(msg, msglen);
1596: break;
1597: case Packets.SSH_MSG_CHANNEL_FAILURE:
1598: msgChannelFailure(msg, msglen);
1599: break;
1600: case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE:
1601: msgChannelOpenFailure(msg, msglen);
1602: break;
1603: case Packets.SSH_MSG_GLOBAL_REQUEST:
1604: msgGlobalRequest(msg, msglen);
1605: break;
1606: case Packets.SSH_MSG_REQUEST_SUCCESS:
1607: msgGlobalSuccess();
1608: break;
1609: case Packets.SSH_MSG_REQUEST_FAILURE:
1610: msgGlobalFailure();
1611: break;
1612: default:
1613: throw new IOException(
1614: "Cannot handle unknown channel message "
1615: + (msg[0] & 0xff));
1616: }
1617: }
1618: }
|