2 * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
3 * Please refer to the LICENSE.txt for licensing details.
6 package ch.ethz.ssh2.channel;
8 import java.io.IOException;
9 import java.util.HashMap;
10 import java.util.List;
11 import java.util.Vector;
13 import ch.ethz.ssh2.ChannelCondition;
14 import ch.ethz.ssh2.PtySettings;
15 import ch.ethz.ssh2.ServerConnectionCallback;
16 import ch.ethz.ssh2.ServerSessionCallback;
17 import ch.ethz.ssh2.log.Logger;
18 import ch.ethz.ssh2.packets.PacketChannelFailure;
19 import ch.ethz.ssh2.packets.PacketChannelOpenConfirmation;
20 import ch.ethz.ssh2.packets.PacketChannelOpenFailure;
21 import ch.ethz.ssh2.packets.PacketChannelSuccess;
22 import ch.ethz.ssh2.packets.PacketGlobalCancelForwardRequest;
23 import ch.ethz.ssh2.packets.PacketGlobalForwardRequest;
24 import ch.ethz.ssh2.packets.PacketOpenDirectTCPIPChannel;
25 import ch.ethz.ssh2.packets.PacketOpenSessionChannel;
26 import ch.ethz.ssh2.packets.PacketSessionExecCommand;
27 import ch.ethz.ssh2.packets.PacketSessionPtyRequest;
28 import ch.ethz.ssh2.packets.PacketSessionStartShell;
29 import ch.ethz.ssh2.packets.PacketSessionSubsystemRequest;
30 import ch.ethz.ssh2.packets.PacketSessionX11Request;
31 import ch.ethz.ssh2.packets.Packets;
32 import ch.ethz.ssh2.packets.TypesReader;
33 import ch.ethz.ssh2.server.ServerConnectionState;
34 import ch.ethz.ssh2.transport.MessageHandler;
35 import ch.ethz.ssh2.transport.TransportManager;
38 * ChannelManager. Please read the comments in Channel.java.
40 * Besides the crypto part, this is the core of the library.
42 * @author Christian Plattner
43 * @version $Id: ChannelManager.java 48 2013-08-01 12:22:33Z cleondris@gmail.com $
45 public class ChannelManager implements MessageHandler
47 private static final Logger log = Logger.getLogger(ChannelManager.class);
49 private final ServerConnectionState server_state;
50 private final TransportManager tm;
52 private final HashMap<String, X11ServerData> x11_magic_cookies = new HashMap<String, X11ServerData>();
54 private final List<Channel> channels = new Vector<Channel>();
55 private int nextLocalChannel = 100;
56 private boolean shutdown = false;
57 private int globalSuccessCounter = 0;
58 private int globalFailedCounter = 0;
60 private final HashMap<Integer, RemoteForwardingData> remoteForwardings = new HashMap<Integer, RemoteForwardingData>();
62 private final List<IChannelWorkerThread> listenerThreads = new Vector<IChannelWorkerThread>();
64 private boolean listenerThreadsAllowed = true;
67 * Constructor for client-mode.
70 public ChannelManager(TransportManager tm)
72 this.server_state = null;
74 tm.registerMessageHandler(this, 80, 100);
78 * Constructor for server-mode.
81 public ChannelManager(ServerConnectionState state)
83 this.server_state = state;
85 tm.registerMessageHandler(this, 80, 100);
88 private Channel getChannel(int id)
90 synchronized (channels)
92 for (Channel c : channels)
101 private void removeChannel(int id)
103 synchronized (channels)
105 for (Channel c : channels)
116 private int addChannel(Channel c)
118 synchronized (channels)
121 return nextLocalChannel++;
125 private void waitUntilChannelOpen(Channel c) throws IOException
127 boolean wasInterrupted = false;
131 while (c.state == Channel.STATE_OPENING)
137 catch (InterruptedException ignore)
139 wasInterrupted = true;
143 if (c.state != Channel.STATE_OPEN)
145 removeChannel(c.localID);
147 String detail = c.getReasonClosed();
150 detail = "state: " + c.state;
152 throw new IOException("Could not open channel (" + detail + ")");
157 Thread.currentThread().interrupt();
160 private void waitForGlobalSuccessOrFailure() throws IOException
162 boolean wasInterrupted = false;
166 synchronized (channels)
168 while ((globalSuccessCounter == 0) && (globalFailedCounter == 0))
172 throw new IOException("The connection is being shutdown");
179 catch (InterruptedException ignore)
181 wasInterrupted = true;
185 if (globalFailedCounter != 0)
187 throw new IOException("The server denied the request (did you enable port forwarding?)");
190 if (globalSuccessCounter == 0)
192 throw new IOException("Illegal state.");
199 Thread.currentThread().interrupt();
203 private void waitForChannelSuccessOrFailure(Channel c) throws IOException
205 boolean wasInterrupted = false;
211 while ((c.successCounter == 0) && (c.failedCounter == 0))
213 if (c.state != Channel.STATE_OPEN)
215 String detail = c.getReasonClosed();
218 detail = "state: " + c.state;
220 throw new IOException("This SSH2 channel is not open (" + detail + ")");
227 catch (InterruptedException ignore)
229 wasInterrupted = true;
233 if (c.failedCounter != 0)
235 throw new IOException("The server denied the request.");
242 Thread.currentThread().interrupt();
246 public void registerX11Cookie(String hexFakeCookie, X11ServerData data)
248 synchronized (x11_magic_cookies)
250 x11_magic_cookies.put(hexFakeCookie, data);
254 public void unRegisterX11Cookie(String hexFakeCookie, boolean killChannels)
256 if (hexFakeCookie == null)
257 throw new IllegalStateException("hexFakeCookie may not be null");
259 synchronized (x11_magic_cookies)
261 x11_magic_cookies.remove(hexFakeCookie);
264 if (killChannels == false)
267 log.debug("Closing all X11 channels for the given fake cookie");
269 List<Channel> channel_copy = new Vector<Channel>();
271 synchronized (channels)
273 channel_copy.addAll(channels);
276 for (Channel c : channel_copy)
280 if (hexFakeCookie.equals(c.hexX11FakeCookie) == false)
286 closeChannel(c, "Closing X11 channel since the corresponding session is closing", true);
288 catch (IOException ignored)
294 public X11ServerData checkX11Cookie(String hexFakeCookie)
296 synchronized (x11_magic_cookies)
298 if (hexFakeCookie != null)
299 return x11_magic_cookies.get(hexFakeCookie);
304 public void closeAllChannels()
306 log.debug("Closing all channels");
308 List<Channel> channel_copy = new Vector<Channel>();
310 synchronized (channels)
312 channel_copy.addAll(channels);
315 for (Channel c : channel_copy)
319 closeChannel(c, "Closing all channels", true);
321 catch (IOException ignored)
327 public void closeChannel(Channel c, String reason, boolean force) throws IOException
329 byte msg[] = new byte[5];
335 c.state = Channel.STATE_CLOSED;
339 c.setReasonClosed(reason);
341 msg[0] = Packets.SSH_MSG_CHANNEL_CLOSE;
342 msg[1] = (byte) (c.remoteID >> 24);
343 msg[2] = (byte) (c.remoteID >> 16);
344 msg[3] = (byte) (c.remoteID >> 8);
345 msg[4] = (byte) (c.remoteID);
350 synchronized (c.channelSendLock)
352 if (c.closeMessageSent == true)
355 c.closeMessageSent = true;
358 log.debug("Sent SSH_MSG_CHANNEL_CLOSE (channel " + c.localID + ")");
361 public void sendEOF(Channel c) throws IOException
363 byte[] msg = new byte[5];
367 if (c.state != Channel.STATE_OPEN)
370 msg[0] = Packets.SSH_MSG_CHANNEL_EOF;
371 msg[1] = (byte) (c.remoteID >> 24);
372 msg[2] = (byte) (c.remoteID >> 16);
373 msg[3] = (byte) (c.remoteID >> 8);
374 msg[4] = (byte) (c.remoteID);
377 synchronized (c.channelSendLock)
379 if (c.closeMessageSent == true)
385 log.debug("Sent EOF (Channel " + c.localID + "/" + c.remoteID + ")");
388 public void sendOpenConfirmation(Channel c) throws IOException
390 PacketChannelOpenConfirmation pcoc = null;
394 if (c.state != Channel.STATE_OPENING)
397 c.state = Channel.STATE_OPEN;
399 pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID, c.localWindow, c.localMaxPacketSize);
402 synchronized (c.channelSendLock)
404 if (c.closeMessageSent == true)
406 tm.sendMessage(pcoc.getPayload());
410 public void sendData(Channel c, byte[] buffer, int pos, int len) throws IOException
412 boolean wasInterrupted = false;
425 if (c.state == Channel.STATE_CLOSED)
426 throw new ChannelClosedException("SSH channel is closed. (" + c.getReasonClosed() + ")");
428 if (c.state != Channel.STATE_OPEN)
429 throw new ChannelClosedException("SSH channel in strange state. (" + c.state + ")");
431 if (c.remoteWindow != 0)
438 catch (InterruptedException ignore)
440 wasInterrupted = true;
444 /* len > 0, no sign extension can happen when comparing */
446 thislen = (c.remoteWindow >= len) ? len : (int) c.remoteWindow;
448 int estimatedMaxDataLen = c.remoteMaxPacketSize - (tm.getPacketOverheadEstimate() + 9);
450 /* The worst case scenario =) a true bottleneck */
452 if (estimatedMaxDataLen <= 0)
454 estimatedMaxDataLen = 1;
457 if (thislen > estimatedMaxDataLen)
458 thislen = estimatedMaxDataLen;
460 c.remoteWindow -= thislen;
462 msg = new byte[1 + 8 + thislen];
464 msg[0] = Packets.SSH_MSG_CHANNEL_DATA;
465 msg[1] = (byte) (c.remoteID >> 24);
466 msg[2] = (byte) (c.remoteID >> 16);
467 msg[3] = (byte) (c.remoteID >> 8);
468 msg[4] = (byte) (c.remoteID);
469 msg[5] = (byte) (thislen >> 24);
470 msg[6] = (byte) (thislen >> 16);
471 msg[7] = (byte) (thislen >> 8);
472 msg[8] = (byte) (thislen);
474 System.arraycopy(buffer, pos, msg, 9, thislen);
477 synchronized (c.channelSendLock)
479 if (c.closeMessageSent == true)
480 throw new ChannelClosedException("SSH channel is closed. (" + c.getReasonClosed() + ")");
492 Thread.currentThread().interrupt();
496 public int requestGlobalForward(String bindAddress, int bindPort, String targetAddress, int targetPort)
499 RemoteForwardingData rfd = new RemoteForwardingData();
501 rfd.bindAddress = bindAddress;
502 rfd.bindPort = bindPort;
503 rfd.targetAddress = targetAddress;
504 rfd.targetPort = targetPort;
506 synchronized (remoteForwardings)
508 Integer key = new Integer(bindPort);
510 if (remoteForwardings.get(key) != null)
512 throw new IOException("There is already a forwarding for remote port " + bindPort);
515 remoteForwardings.put(key, rfd);
518 synchronized (channels)
520 globalSuccessCounter = globalFailedCounter = 0;
523 PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort);
524 tm.sendMessage(pgf.getPayload());
526 log.debug("Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")");
530 waitForGlobalSuccessOrFailure();
532 catch (IOException e)
534 synchronized (remoteForwardings)
536 remoteForwardings.remove(rfd);
544 public void requestCancelGlobalForward(int bindPort) throws IOException
546 RemoteForwardingData rfd = null;
548 synchronized (remoteForwardings)
550 rfd = remoteForwardings.get(new Integer(bindPort));
553 throw new IOException("Sorry, there is no known remote forwarding for remote port " + bindPort);
556 synchronized (channels)
558 globalSuccessCounter = globalFailedCounter = 0;
561 PacketGlobalCancelForwardRequest pgcf = new PacketGlobalCancelForwardRequest(true, rfd.bindAddress,
563 tm.sendMessage(pgcf.getPayload());
565 log.debug("Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")");
567 waitForGlobalSuccessOrFailure();
569 /* Only now we are sure that no more forwarded connections will arrive */
571 synchronized (remoteForwardings)
573 remoteForwardings.remove(rfd);
577 public void registerThread(IChannelWorkerThread thr) throws IOException
579 synchronized (listenerThreads)
581 if (listenerThreadsAllowed == false)
582 throw new IOException("Too late, this connection is closed.");
583 listenerThreads.add(thr);
587 public Channel openDirectTCPIPChannel(String host_to_connect, int port_to_connect, String originator_IP_address,
588 int originator_port) throws IOException
590 Channel c = new Channel(this);
594 c.localID = addChannel(c);
595 // end of synchronized block forces writing out to main memory
598 PacketOpenDirectTCPIPChannel dtc = new PacketOpenDirectTCPIPChannel(c.localID, c.localWindow,
599 c.localMaxPacketSize, host_to_connect, port_to_connect, originator_IP_address, originator_port);
601 tm.sendMessage(dtc.getPayload());
603 waitUntilChannelOpen(c);
608 public Channel openSessionChannel() throws IOException
610 Channel c = new Channel(this);
614 c.localID = addChannel(c);
615 // end of synchronized block forces the writing out to main memory
618 log.debug("Sending SSH_MSG_CHANNEL_OPEN (Channel " + c.localID + ")");
620 PacketOpenSessionChannel smo = new PacketOpenSessionChannel(c.localID, c.localWindow, c.localMaxPacketSize);
621 tm.sendMessage(smo.getPayload());
623 waitUntilChannelOpen(c);
628 public void requestPTY(Channel c, String term, int term_width_characters, int term_height_characters,
629 int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException
631 PacketSessionPtyRequest spr;
635 if (c.state != Channel.STATE_OPEN)
636 throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
638 spr = new PacketSessionPtyRequest(c.remoteID, true, term, term_width_characters, term_height_characters,
639 term_width_pixels, term_height_pixels, terminal_modes);
641 c.successCounter = c.failedCounter = 0;
644 synchronized (c.channelSendLock)
646 if (c.closeMessageSent)
647 throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
648 tm.sendMessage(spr.getPayload());
653 waitForChannelSuccessOrFailure(c);
655 catch (IOException e)
657 throw (IOException) new IOException("PTY request failed").initCause(e);
661 public void requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol,
662 String x11AuthenticationCookie, int x11ScreenNumber) throws IOException
664 PacketSessionX11Request psr;
668 if (c.state != Channel.STATE_OPEN)
669 throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
671 psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol,
672 x11AuthenticationCookie, x11ScreenNumber);
674 c.successCounter = c.failedCounter = 0;
677 synchronized (c.channelSendLock)
679 if (c.closeMessageSent)
680 throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
681 tm.sendMessage(psr.getPayload());
684 log.debug("Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")");
688 waitForChannelSuccessOrFailure(c);
690 catch (IOException e)
692 throw (IOException) new IOException("The X11 request failed.").initCause(e);
696 public void requestSubSystem(Channel c, String subSystemName) throws IOException
698 PacketSessionSubsystemRequest ssr;
702 if (c.state != Channel.STATE_OPEN)
703 throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
705 ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName);
707 c.successCounter = c.failedCounter = 0;
710 synchronized (c.channelSendLock)
712 if (c.closeMessageSent)
713 throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
714 tm.sendMessage(ssr.getPayload());
719 waitForChannelSuccessOrFailure(c);
721 catch (IOException e)
723 throw (IOException) new IOException("The subsystem request failed.").initCause(e);
727 public void requestExecCommand(Channel c, String cmd) throws IOException
729 this.requestExecCommand(c, cmd, null);
733 * @param charsetName The charset used to convert between Java Unicode Strings and byte encodings
735 public void requestExecCommand(Channel c, String cmd, String charsetName) throws IOException
737 PacketSessionExecCommand sm;
741 if (c.state != Channel.STATE_OPEN)
742 throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
744 sm = new PacketSessionExecCommand(c.remoteID, true, cmd);
746 c.successCounter = c.failedCounter = 0;
749 synchronized (c.channelSendLock)
751 if (c.closeMessageSent)
752 throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
753 tm.sendMessage(sm.getPayload(charsetName));
756 log.debug("Executing command (channel " + c.localID + ", '" + cmd + "')");
760 waitForChannelSuccessOrFailure(c);
762 catch (IOException e)
764 throw (IOException) new IOException("The execute request failed.").initCause(e);
768 public void requestShell(Channel c) throws IOException
770 PacketSessionStartShell sm;
774 if (c.state != Channel.STATE_OPEN)
775 throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
777 sm = new PacketSessionStartShell(c.remoteID, true);
779 c.successCounter = c.failedCounter = 0;
782 synchronized (c.channelSendLock)
784 if (c.closeMessageSent)
785 throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
786 tm.sendMessage(sm.getPayload());
791 waitForChannelSuccessOrFailure(c);
793 catch (IOException e)
795 throw (IOException) new IOException("The shell request failed.").initCause(e);
799 public void msgChannelExtendedData(byte[] msg, int msglen) throws IOException
802 throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (" + msglen + ")");
804 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
805 int dataType = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
806 int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16) | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff);
808 Channel c = getChannel(id);
811 throw new IOException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id);
813 if (dataType != Packets.SSH_EXTENDED_DATA_STDERR)
814 throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")");
816 if (len != (msglen - 13))
817 throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msglen - 13)
818 + ", got " + len + ")");
820 log.debug("Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel " + id + ", " + len + ")");
824 if (c.state == Channel.STATE_CLOSED)
827 if (c.state != Channel.STATE_OPEN)
828 throw new IOException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state ("
831 if (c.localWindow < len)
832 throw new IOException("Remote sent too much data, does not fit into window.");
834 c.localWindow -= len;
836 System.arraycopy(msg, 13, c.stderrBuffer, c.stderrWritepos, len);
837 c.stderrWritepos += len;
844 * Wait until for a condition.
847 * @param timeout in ms, 0 means no timeout.
848 * @param condition_mask minimum event mask (at least one of the conditions must be fulfilled)
849 * @return all current events
851 public int waitForCondition(Channel c, long timeout, int condition_mask)
853 boolean wasInterrupted = false;
858 boolean end_time_set = false;
864 int current_cond = 0;
866 int stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
867 int stderrAvail = c.stderrWritepos - c.stderrReadpos;
870 current_cond = current_cond | ChannelCondition.STDOUT_DATA;
873 current_cond = current_cond | ChannelCondition.STDERR_DATA;
876 current_cond = current_cond | ChannelCondition.EOF;
878 if (c.getExitStatus() != null)
879 current_cond = current_cond | ChannelCondition.EXIT_STATUS;
881 if (c.getExitSignal() != null)
882 current_cond = current_cond | ChannelCondition.EXIT_SIGNAL;
884 if (c.state == Channel.STATE_CLOSED)
885 return current_cond | ChannelCondition.CLOSED | ChannelCondition.EOF;
887 if ((current_cond & condition_mask) != 0)
894 end_time = System.currentTimeMillis() + timeout;
899 timeout = end_time - System.currentTimeMillis();
902 return current_cond | ChannelCondition.TIMEOUT;
913 catch (InterruptedException e)
915 wasInterrupted = true;
923 Thread.currentThread().interrupt();
927 public int getAvailable(Channel c, boolean extended) throws IOException
934 avail = c.stderrWritepos - c.stderrReadpos;
936 avail = c.stdoutWritepos - c.stdoutReadpos;
938 return ((avail > 0) ? avail : (c.EOF ? -1 : 0));
942 public int getChannelData(Channel c, boolean extended, byte[] target, int off, int len) throws IOException
944 boolean wasInterrupted = false;
961 * Data available? We have to return remaining data even if the
962 * channel is already closed.
965 stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
966 stderrAvail = c.stderrWritepos - c.stderrReadpos;
968 if ((!extended) && (stdoutAvail != 0))
971 if ((extended) && (stderrAvail != 0))
974 /* Do not wait if more data will never arrive (EOF or CLOSED) */
976 if ((c.EOF) || (c.state != Channel.STATE_OPEN))
983 catch (InterruptedException ignore)
985 wasInterrupted = true;
989 /* OK, there is some data. Return it. */
993 copylen = (stdoutAvail > len) ? len : stdoutAvail;
994 System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, target, off, copylen);
995 c.stdoutReadpos += copylen;
997 if (c.stdoutReadpos != c.stdoutWritepos)
999 System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, c.stdoutBuffer, 0, c.stdoutWritepos
1002 c.stdoutWritepos -= c.stdoutReadpos;
1003 c.stdoutReadpos = 0;
1007 copylen = (stderrAvail > len) ? len : stderrAvail;
1008 System.arraycopy(c.stderrBuffer, c.stderrReadpos, target, off, copylen);
1009 c.stderrReadpos += copylen;
1011 if (c.stderrReadpos != c.stderrWritepos)
1013 System.arraycopy(c.stderrBuffer, c.stderrReadpos, c.stderrBuffer, 0, c.stderrWritepos
1016 c.stderrWritepos -= c.stderrReadpos;
1017 c.stderrReadpos = 0;
1020 if (c.state != Channel.STATE_OPEN)
1023 if (c.localWindow < ((Channel.CHANNEL_BUFFER_SIZE + 1) / 2))
1025 int minFreeSpace = Math.min(Channel.CHANNEL_BUFFER_SIZE - c.stdoutWritepos,
1026 Channel.CHANNEL_BUFFER_SIZE - c.stderrWritepos);
1028 increment = minFreeSpace - c.localWindow;
1029 c.localWindow = minFreeSpace;
1032 remoteID = c.remoteID; /* read while holding the lock */
1033 localID = c.localID; /* read while holding the lock */
1037 * If a consumer reads stdout and stdin in parallel, we may end up with
1038 * sending two msgWindowAdjust messages. Luckily, it
1039 * does not matter in which order they arrive at the server.
1044 log.debug("Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")");
1046 synchronized (c.channelSendLock)
1048 byte[] msg = c.msgWindowAdjust;
1050 msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
1051 msg[1] = (byte) (remoteID >> 24);
1052 msg[2] = (byte) (remoteID >> 16);
1053 msg[3] = (byte) (remoteID >> 8);
1054 msg[4] = (byte) (remoteID);
1055 msg[5] = (byte) (increment >> 24);
1056 msg[6] = (byte) (increment >> 16);
1057 msg[7] = (byte) (increment >> 8);
1058 msg[8] = (byte) (increment);
1060 if (c.closeMessageSent == false)
1061 tm.sendMessage(msg);
1070 Thread.currentThread().interrupt();
1075 public void msgChannelData(byte[] msg, int msglen) throws IOException
1078 throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong size (" + msglen + ")");
1080 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1081 int len = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
1083 Channel c = getChannel(id);
1086 throw new IOException("Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel " + id);
1088 if (len != (msglen - 9))
1089 throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong len (calculated " + (msglen - 9) + ", got "
1092 log.debug("Got SSH_MSG_CHANNEL_DATA (channel " + id + ", " + len + ")");
1096 if (c.state == Channel.STATE_CLOSED)
1099 if (c.state != Channel.STATE_OPEN)
1100 throw new IOException("Got SSH_MSG_CHANNEL_DATA, but channel is not in correct state (" + c.state + ")");
1102 if (c.localWindow < len)
1103 throw new IOException("Remote sent too much data, does not fit into window.");
1105 c.localWindow -= len;
1107 System.arraycopy(msg, 9, c.stdoutBuffer, c.stdoutWritepos, len);
1108 c.stdoutWritepos += len;
1114 public void msgChannelWindowAdjust(byte[] msg, int msglen) throws IOException
1117 throw new IOException("SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size (" + msglen + ")");
1119 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1120 int windowChange = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
1122 Channel c = getChannel(id);
1125 throw new IOException("Unexpected SSH_MSG_CHANNEL_WINDOW_ADJUST message for non-existent channel " + id);
1129 final long huge = 0xFFFFffffL; /* 2^32 - 1 */
1131 c.remoteWindow += (windowChange & huge); /* avoid sign extension */
1133 /* TODO - is this a good heuristic? */
1135 if ((c.remoteWindow > huge))
1136 c.remoteWindow = huge;
1142 log.debug("Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + id + ", " + windowChange + ")");
1145 public void msgChannelOpen(byte[] msg, int msglen) throws IOException
1147 TypesReader tr = new TypesReader(msg, 0, msglen);
1149 tr.readByte(); // skip packet type
1150 String channelType = tr.readString();
1151 int remoteID = tr.readUINT32(); /* sender channel */
1152 int remoteWindow = tr.readUINT32(); /* initial window size */
1153 int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */
1155 if ("x11".equals(channelType))
1157 synchronized (x11_magic_cookies)
1159 /* If we did not request X11 forwarding, then simply ignore this bogus request. */
1161 if (x11_magic_cookies.size() == 0)
1163 PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
1164 Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "X11 forwarding not activated", "");
1166 tm.sendAsynchronousMessage(pcof.getPayload());
1168 log.warning("Unexpected X11 request, denying it!");
1174 String remoteOriginatorAddress = tr.readString();
1175 int remoteOriginatorPort = tr.readUINT32();
1177 Channel c = new Channel(this);
1181 c.remoteID = remoteID;
1182 c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */
1183 c.remoteMaxPacketSize = remoteMaxPacketSize;
1184 c.localID = addChannel(c);
1188 * The open confirmation message will be sent from another thread
1191 RemoteX11AcceptThread rxat = new RemoteX11AcceptThread(c, remoteOriginatorAddress, remoteOriginatorPort);
1192 rxat.setDaemon(true);
1198 if ("forwarded-tcpip".equals(channelType))
1200 String remoteConnectedAddress = tr.readString(); /* address that was connected */
1201 int remoteConnectedPort = tr.readUINT32(); /* port that was connected */
1202 String remoteOriginatorAddress = tr.readString(); /* originator IP address */
1203 int remoteOriginatorPort = tr.readUINT32(); /* originator port */
1205 RemoteForwardingData rfd = null;
1207 synchronized (remoteForwardings)
1209 rfd = remoteForwardings.get(new Integer(remoteConnectedPort));
1214 PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
1215 Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
1216 "No thanks, unknown port in forwarded-tcpip request", "");
1218 /* Always try to be polite. */
1220 tm.sendAsynchronousMessage(pcof.getPayload());
1222 log.debug("Unexpected forwarded-tcpip request, denying it!");
1227 Channel c = new Channel(this);
1231 c.remoteID = remoteID;
1232 c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
1233 c.remoteMaxPacketSize = remoteMaxPacketSize;
1234 c.localID = addChannel(c);
1238 * The open confirmation message will be sent from another thread.
1241 RemoteAcceptThread rat = new RemoteAcceptThread(c, remoteConnectedAddress, remoteConnectedPort,
1242 remoteOriginatorAddress, remoteOriginatorPort, rfd.targetAddress, rfd.targetPort);
1244 rat.setDaemon(true);
1250 if ((server_state != null) && ("session".equals(channelType)))
1252 ServerConnectionCallback cb = null;
1254 synchronized (server_state)
1256 cb = server_state.cb_conn;
1261 tm.sendAsynchronousMessage(new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
1262 "Sessions are currently not enabled", "en").getPayload());
1267 final Channel c = new Channel(this);
1271 c.remoteID = remoteID;
1272 c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
1273 c.remoteMaxPacketSize = remoteMaxPacketSize;
1274 c.localID = addChannel(c);
1275 c.state = Channel.STATE_OPEN;
1276 c.ss = new ServerSessionImpl(c);
1279 PacketChannelOpenConfirmation pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID,
1280 c.localWindow, c.localMaxPacketSize);
1282 tm.sendAsynchronousMessage(pcoc.getPayload());
1284 c.ss.sscb = cb.acceptSession(c.ss);
1289 /* Tell the server that we have no idea what it is talking about */
1291 PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
1292 "Unknown channel type", "");
1294 tm.sendAsynchronousMessage(pcof.getPayload());
1297 log.warning("The peer tried to open an unsupported channel type (" + channelType + ")");
1300 /* Starts the given runnable in a foreground (non-daemon) thread */
1301 private void runAsync(Runnable r)
1303 Thread t = new Thread(r);
1307 public void msgChannelRequest(byte[] msg, int msglen) throws IOException
1309 TypesReader tr = new TypesReader(msg, 0, msglen);
1311 tr.readByte(); // skip packet type
1312 int id = tr.readUINT32();
1314 Channel c = getChannel(id);
1317 throw new IOException("Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id);
1319 ServerSessionImpl server_session = null;
1321 if (server_state != null)
1325 server_session = c.ss;
1329 String type = tr.readString("US-ASCII");
1330 boolean wantReply = tr.readBoolean();
1332 log.debug("Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')");
1334 if (type.equals("exit-status"))
1336 if (wantReply != false)
1337 throw new IOException(
1338 "Badly formatted SSH_MSG_CHANNEL_REQUEST exit-status message, 'want reply' is true");
1340 int exit_status = tr.readUINT32();
1342 if (tr.remain() != 0)
1343 throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
1347 c.exit_status = new Integer(exit_status);
1351 log.debug("Got EXIT STATUS (channel " + id + ", status " + exit_status + ")");
1356 if ((server_state == null) && (type.equals("exit-signal")))
1358 if (wantReply != false)
1359 throw new IOException(
1360 "Badly formatted SSH_MSG_CHANNEL_REQUEST exit-signal message, 'want reply' is true");
1362 String signame = tr.readString("US-ASCII");
1367 if (tr.remain() != 0)
1368 throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
1372 c.exit_signal = signame;
1376 log.debug("Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")");
1381 if ((server_session != null) && (type.equals("pty-req")))
1383 PtySettings pty = new PtySettings();
1385 pty.term = tr.readString();
1386 pty.term_width_characters = tr.readUINT32();
1387 pty.term_height_characters = tr.readUINT32();
1388 pty.term_width_pixels = tr.readUINT32();
1389 pty.term_height_pixels = tr.readUINT32();
1390 pty.terminal_modes = tr.readByteString();
1392 if (tr.remain() != 0)
1393 throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
1395 Runnable run_after_sending_success = null;
1397 ServerSessionCallback sscb = server_session.getServerSessionCallback();
1400 run_after_sending_success = sscb.requestPtyReq(server_session, pty);
1404 if (run_after_sending_success != null)
1406 tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
1410 tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
1414 if (run_after_sending_success != null)
1416 runAsync(run_after_sending_success);
1422 if ((server_session != null) && (type.equals("subsystem")))
1424 String command = tr.readString();
1425 if (tr.remain() != 0)
1426 throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
1428 Runnable run_after_sending_success = null;
1429 ServerSessionCallback sscb = server_session.getServerSessionCallback();
1432 run_after_sending_success = sscb.requestSubsystem(server_session, command);
1436 if (run_after_sending_success != null)
1438 tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
1442 tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
1446 if (run_after_sending_success != null)
1448 runAsync(run_after_sending_success);
1454 if ((server_session != null) && (type.equals("shell")))
1456 if (tr.remain() != 0)
1457 throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
1459 Runnable run_after_sending_success = null;
1460 ServerSessionCallback sscb = server_session.getServerSessionCallback();
1463 run_after_sending_success = sscb.requestShell(server_session);
1467 if (run_after_sending_success != null)
1469 tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
1473 tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
1477 if (run_after_sending_success != null)
1479 runAsync(run_after_sending_success);
1485 if ((server_session != null) && (type.equals("exec")))
1487 String command = tr.readString();
1489 if (tr.remain() != 0)
1490 throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
1492 Runnable run_after_sending_success = null;
1493 ServerSessionCallback sscb = server_session.getServerSessionCallback();
1496 run_after_sending_success = sscb.requestExec(server_session, command);
1500 if (run_after_sending_success != null)
1502 tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
1506 tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
1510 if (run_after_sending_success != null)
1512 runAsync(run_after_sending_success);
1518 /* We simply ignore unknown channel requests, however, if the server wants a reply,
1519 * then we signal that we have no idea what it is about.
1524 tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
1527 log.debug("Channel request '" + type + "' is not known, ignoring it");
1530 public void msgChannelEOF(byte[] msg, int msglen) throws IOException
1533 throw new IOException("SSH_MSG_CHANNEL_EOF message has wrong size (" + msglen + ")");
1535 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1537 Channel c = getChannel(id);
1540 throw new IOException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id);
1548 log.debug("Got SSH_MSG_CHANNEL_EOF (channel " + id + ")");
1551 public void msgChannelClose(byte[] msg, int msglen) throws IOException
1554 throw new IOException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msglen + ")");
1556 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1558 Channel c = getChannel(id);
1561 throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id);
1566 c.state = Channel.STATE_CLOSED;
1567 c.setReasonClosed("Close requested by remote");
1568 c.closeMessageRecv = true;
1570 removeChannel(c.localID);
1575 log.debug("Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")");
1578 public void msgChannelSuccess(byte[] msg, int msglen) throws IOException
1581 throw new IOException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msglen + ")");
1583 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1585 Channel c = getChannel(id);
1588 throw new IOException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id);
1596 log.debug("Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")");
1599 public void msgChannelFailure(byte[] msg, int msglen) throws IOException
1602 throw new IOException("SSH_MSG_CHANNEL_FAILURE message has wrong size (" + msglen + ")");
1604 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1606 Channel c = getChannel(id);
1609 throw new IOException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id);
1617 log.debug("Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")");
1620 public void msgChannelOpenConfirmation(byte[] msg, int msglen) throws IOException
1622 PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg, 0, msglen);
1624 Channel c = getChannel(sm.recipientChannelID);
1627 throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel "
1628 + sm.recipientChannelID);
1632 if (c.state != Channel.STATE_OPENING)
1633 throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel "
1634 + sm.recipientChannelID);
1636 c.remoteID = sm.senderChannelID;
1637 c.remoteWindow = sm.initialWindowSize & 0xFFFFffffL; /* convert UINT32 to long */
1638 c.remoteMaxPacketSize = sm.maxPacketSize;
1639 c.state = Channel.STATE_OPEN;
1643 log.debug("Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.recipientChannelID + " / remote: "
1644 + sm.senderChannelID + ")");
1647 public void msgChannelOpenFailure(byte[] msg, int msglen) throws IOException
1650 throw new IOException("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (" + msglen + ")");
1652 TypesReader tr = new TypesReader(msg, 0, msglen);
1654 tr.readByte(); // skip packet type
1655 int id = tr.readUINT32(); /* sender channel */
1657 Channel c = getChannel(id);
1660 throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id);
1662 int reasonCode = tr.readUINT32();
1663 String description = tr.readString("UTF-8");
1665 String reasonCodeSymbolicName = null;
1670 reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED";
1673 reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED";
1676 reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE";
1679 reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE";
1682 reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")";
1685 StringBuilder descriptionBuffer = new StringBuilder();
1686 descriptionBuffer.append(description);
1688 for (int i = 0; i < descriptionBuffer.length(); i++)
1690 char cc = descriptionBuffer.charAt(i);
1692 if ((cc >= 32) && (cc <= 126))
1694 descriptionBuffer.setCharAt(i, '\uFFFD');
1700 c.state = Channel.STATE_CLOSED;
1701 c.setReasonClosed("The server refused to open the channel (" + reasonCodeSymbolicName + ", '"
1702 + descriptionBuffer.toString() + "')");
1706 log.debug("Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")");
1709 public void msgGlobalRequest(byte[] msg, int msglen) throws IOException
1711 /* Currently we do not support any kind of global request */
1713 TypesReader tr = new TypesReader(msg, 0, msglen);
1715 tr.readByte(); // skip packet type
1716 String requestName = tr.readString();
1717 boolean wantReply = tr.readBoolean();
1721 byte[] reply_failure = new byte[1];
1722 reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE;
1724 tm.sendAsynchronousMessage(reply_failure);
1727 /* We do not clean up the requestName String - that is OK for debug */
1729 log.debug("Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")");
1732 public void msgGlobalSuccess() throws IOException
1734 synchronized (channels)
1736 globalSuccessCounter++;
1737 channels.notifyAll();
1740 log.debug("Got SSH_MSG_REQUEST_SUCCESS");
1743 public void msgGlobalFailure() throws IOException
1745 synchronized (channels)
1747 globalFailedCounter++;
1748 channels.notifyAll();
1751 log.debug("Got SSH_MSG_REQUEST_FAILURE");
1754 public void handleMessage(byte[] msg, int msglen) throws IOException
1759 log.debug("HandleMessage: got shutdown");
1761 synchronized (listenerThreads)
1763 for (IChannelWorkerThread lat : listenerThreads)
1767 listenerThreadsAllowed = false;
1770 synchronized (channels)
1774 for (Channel c : channels)
1779 c.state = Channel.STATE_CLOSED;
1780 c.setReasonClosed("The connection is being shutdown");
1781 c.closeMessageRecv = true; /*
1782 * You never know, perhaps
1783 * we are waiting for a
1784 * pending close message
1785 * from the server...
1792 channels.notifyAll(); /* Notify global response waiters */
1799 case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
1800 msgChannelOpenConfirmation(msg, msglen);
1802 case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST:
1803 msgChannelWindowAdjust(msg, msglen);
1805 case Packets.SSH_MSG_CHANNEL_DATA:
1806 msgChannelData(msg, msglen);
1808 case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA:
1809 msgChannelExtendedData(msg, msglen);
1811 case Packets.SSH_MSG_CHANNEL_REQUEST:
1812 msgChannelRequest(msg, msglen);
1814 case Packets.SSH_MSG_CHANNEL_EOF:
1815 msgChannelEOF(msg, msglen);
1817 case Packets.SSH_MSG_CHANNEL_OPEN:
1818 msgChannelOpen(msg, msglen);
1820 case Packets.SSH_MSG_CHANNEL_CLOSE:
1821 msgChannelClose(msg, msglen);
1823 case Packets.SSH_MSG_CHANNEL_SUCCESS:
1824 msgChannelSuccess(msg, msglen);
1826 case Packets.SSH_MSG_CHANNEL_FAILURE:
1827 msgChannelFailure(msg, msglen);
1829 case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE:
1830 msgChannelOpenFailure(msg, msglen);
1832 case Packets.SSH_MSG_GLOBAL_REQUEST:
1833 msgGlobalRequest(msg, msglen);
1835 case Packets.SSH_MSG_REQUEST_SUCCESS:
1838 case Packets.SSH_MSG_REQUEST_FAILURE:
1842 throw new IOException("Cannot handle unknown channel message " + (msg[0] & 0xff));