Home | History | Annotate | Download | only in channel
      1 /*
      2  * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
      3  * Please refer to the LICENSE.txt for licensing details.
      4  */
      5 
      6 package ch.ethz.ssh2.channel;
      7 
      8 import java.io.IOException;
      9 import java.util.HashMap;
     10 import java.util.List;
     11 import java.util.Vector;
     12 
     13 import ch.ethz.ssh2.ChannelCondition;
     14 import ch.ethz.ssh2.log.Logger;
     15 import ch.ethz.ssh2.packets.PacketChannelOpenConfirmation;
     16 import ch.ethz.ssh2.packets.PacketChannelOpenFailure;
     17 import ch.ethz.ssh2.packets.PacketGlobalCancelForwardRequest;
     18 import ch.ethz.ssh2.packets.PacketGlobalForwardRequest;
     19 import ch.ethz.ssh2.packets.PacketOpenDirectTCPIPChannel;
     20 import ch.ethz.ssh2.packets.PacketOpenSessionChannel;
     21 import ch.ethz.ssh2.packets.PacketSessionExecCommand;
     22 import ch.ethz.ssh2.packets.PacketSessionPtyRequest;
     23 import ch.ethz.ssh2.packets.PacketSessionStartShell;
     24 import ch.ethz.ssh2.packets.PacketSessionSubsystemRequest;
     25 import ch.ethz.ssh2.packets.PacketSessionX11Request;
     26 import ch.ethz.ssh2.packets.Packets;
     27 import ch.ethz.ssh2.packets.TypesReader;
     28 import ch.ethz.ssh2.transport.MessageHandler;
     29 import ch.ethz.ssh2.transport.TransportManager;
     30 
     31 /**
     32  * ChannelManager. Please read the comments in Channel.java.
     33  * <p/>
     34  * Besides the crypto part, this is the core of the library.
     35  *
     36  * @author Christian Plattner
     37  * @version $Id: ChannelManager.java 41 2011-06-02 10:36:41Z dkocher (at) sudo.ch $
     38  */
     39 public class ChannelManager implements MessageHandler
     40 {
     41 	private static final Logger log = Logger.getLogger(ChannelManager.class);
     42 
     43 	private final HashMap<String, X11ServerData> x11_magic_cookies = new HashMap<String, X11ServerData>();
     44 
     45 	private TransportManager tm;
     46 
     47 	private final List<Channel> channels = new Vector<Channel>();
     48 	private int nextLocalChannel = 100;
     49 	private boolean shutdown = false;
     50 	private int globalSuccessCounter = 0;
     51 	private int globalFailedCounter = 0;
     52 
     53 	private final HashMap<Integer, RemoteForwardingData> remoteForwardings = new HashMap<Integer, RemoteForwardingData>();
     54 
     55 	private final List<IChannelWorkerThread> listenerThreads = new Vector<IChannelWorkerThread>();
     56 
     57 	private boolean listenerThreadsAllowed = true;
     58 
     59 	public ChannelManager(TransportManager tm)
     60 	{
     61 		this.tm = tm;
     62 		tm.registerMessageHandler(this, 80, 100);
     63 	}
     64 
     65 	private Channel getChannel(int id)
     66 	{
     67 		synchronized (channels)
     68 		{
     69 			for (Channel c : channels)
     70 			{
     71 				if (c.localID == id)
     72 					return c;
     73 			}
     74 		}
     75 		return null;
     76 	}
     77 
     78 	private void removeChannel(int id)
     79 	{
     80 		synchronized (channels)
     81 		{
     82 			for (Channel c : channels)
     83 			{
     84 				if (c.localID == id)
     85 				{
     86 					channels.remove(c);
     87 					break;
     88 				}
     89 			}
     90 		}
     91 	}
     92 
     93 	private int addChannel(Channel c)
     94 	{
     95 		synchronized (channels)
     96 		{
     97 			channels.add(c);
     98 			return nextLocalChannel++;
     99 		}
    100 	}
    101 
    102 	private void waitUntilChannelOpen(Channel c) throws IOException
    103 	{
    104 		boolean wasInterrupted = false;
    105 
    106 		synchronized (c)
    107 		{
    108 			while (c.state == Channel.STATE_OPENING)
    109 			{
    110 				try
    111 				{
    112 					c.wait();
    113 				}
    114 				catch (InterruptedException ignore)
    115 				{
    116 					wasInterrupted = true;
    117 				}
    118 			}
    119 
    120 			if (c.state != Channel.STATE_OPEN)
    121 			{
    122 				removeChannel(c.localID);
    123 
    124 				String detail = c.getReasonClosed();
    125 
    126 				if (detail == null)
    127 					detail = "state: " + c.state;
    128 
    129 				throw new IOException("Could not open channel (" + detail + ")");
    130 			}
    131 		}
    132 
    133 		if (wasInterrupted)
    134 			Thread.currentThread().interrupt();
    135 	}
    136 
    137 	private void waitForGlobalSuccessOrFailure() throws IOException
    138 	{
    139 		boolean wasInterrupted = false;
    140 
    141 		try
    142 		{
    143 			synchronized (channels)
    144 			{
    145 				while ((globalSuccessCounter == 0) && (globalFailedCounter == 0))
    146 				{
    147 					if (shutdown)
    148 					{
    149 						throw new IOException("The connection is being shutdown");
    150 					}
    151 
    152 					try
    153 					{
    154 						channels.wait();
    155 					}
    156 					catch (InterruptedException ignore)
    157 					{
    158 						wasInterrupted = true;
    159 					}
    160 				}
    161 
    162 				if (globalFailedCounter != 0)
    163 				{
    164 					throw new IOException("The server denied the request (did you enable port forwarding?)");
    165 				}
    166 
    167 				if (globalSuccessCounter == 0)
    168 				{
    169 					throw new IOException("Illegal state.");
    170 				}
    171 			}
    172 		}
    173 		finally
    174 		{
    175 			if (wasInterrupted)
    176 				Thread.currentThread().interrupt();
    177 		}
    178 	}
    179 
    180 	private void waitForChannelSuccessOrFailure(Channel c) throws IOException
    181 	{
    182 		boolean wasInterrupted = false;
    183 
    184 		try
    185 		{
    186 			synchronized (c)
    187 			{
    188 				while ((c.successCounter == 0) && (c.failedCounter == 0))
    189 				{
    190 					if (c.state != Channel.STATE_OPEN)
    191 					{
    192 						String detail = c.getReasonClosed();
    193 
    194 						if (detail == null)
    195 							detail = "state: " + c.state;
    196 
    197 						throw new IOException("This SSH2 channel is not open (" + detail + ")");
    198 					}
    199 
    200 					try
    201 					{
    202 						c.wait();
    203 					}
    204 					catch (InterruptedException ignore)
    205 					{
    206 						wasInterrupted = true;
    207 					}
    208 				}
    209 
    210 				if (c.failedCounter != 0)
    211 				{
    212 					throw new IOException("The server denied the request.");
    213 				}
    214 			}
    215 		}
    216 		finally
    217 		{
    218 			if (wasInterrupted)
    219 				Thread.currentThread().interrupt();
    220 		}
    221 	}
    222 
    223 	public void registerX11Cookie(String hexFakeCookie, X11ServerData data)
    224 	{
    225 		synchronized (x11_magic_cookies)
    226 		{
    227 			x11_magic_cookies.put(hexFakeCookie, data);
    228 		}
    229 	}
    230 
    231 	public void unRegisterX11Cookie(String hexFakeCookie, boolean killChannels)
    232 	{
    233 		if (hexFakeCookie == null)
    234 			throw new IllegalStateException("hexFakeCookie may not be null");
    235 
    236 		synchronized (x11_magic_cookies)
    237 		{
    238 			x11_magic_cookies.remove(hexFakeCookie);
    239 		}
    240 
    241 		if (killChannels == false)
    242 			return;
    243 
    244 		log.debug("Closing all X11 channels for the given fake cookie");
    245 
    246 		List<Channel> channel_copy = new Vector<Channel>();
    247 
    248 		synchronized (channels)
    249 		{
    250 			channel_copy.addAll(channels);
    251 		}
    252 
    253 		for (Channel c : channel_copy)
    254 		{
    255 			synchronized (c)
    256 			{
    257 				if (hexFakeCookie.equals(c.hexX11FakeCookie) == false)
    258 					continue;
    259 			}
    260 
    261 			try
    262 			{
    263 				closeChannel(c, "Closing X11 channel since the corresponding session is closing", true);
    264 			}
    265 			catch (IOException ignored)
    266 			{
    267 			}
    268 		}
    269 	}
    270 
    271 	public X11ServerData checkX11Cookie(String hexFakeCookie)
    272 	{
    273 		synchronized (x11_magic_cookies)
    274 		{
    275 			if (hexFakeCookie != null)
    276 				return x11_magic_cookies.get(hexFakeCookie);
    277 		}
    278 		return null;
    279 	}
    280 
    281 	public void closeAllChannels()
    282 	{
    283 
    284 		log.debug("Closing all channels");
    285 
    286 		List<Channel> channel_copy = new Vector<Channel>();
    287 
    288 		synchronized (channels)
    289 		{
    290 			channel_copy.addAll(channels);
    291 		}
    292 
    293 		for (Channel c : channel_copy)
    294 		{
    295 			try
    296 			{
    297 				closeChannel(c, "Closing all channels", true);
    298 			}
    299 			catch (IOException ignored)
    300 			{
    301 			}
    302 		}
    303 	}
    304 
    305 	public void closeChannel(Channel c, String reason, boolean force) throws IOException
    306 	{
    307 		byte msg[] = new byte[5];
    308 
    309 		synchronized (c)
    310 		{
    311 			if (force)
    312 			{
    313 				c.state = Channel.STATE_CLOSED;
    314 				c.EOF = true;
    315 			}
    316 
    317 			c.setReasonClosed(reason);
    318 
    319 			msg[0] = Packets.SSH_MSG_CHANNEL_CLOSE;
    320 			msg[1] = (byte) (c.remoteID >> 24);
    321 			msg[2] = (byte) (c.remoteID >> 16);
    322 			msg[3] = (byte) (c.remoteID >> 8);
    323 			msg[4] = (byte) (c.remoteID);
    324 
    325 			c.notifyAll();
    326 		}
    327 
    328 		synchronized (c.channelSendLock)
    329 		{
    330 			if (c.closeMessageSent == true)
    331 				return;
    332 			tm.sendMessage(msg);
    333 			c.closeMessageSent = true;
    334 		}
    335 
    336 
    337 		log.debug("Sent SSH_MSG_CHANNEL_CLOSE (channel " + c.localID + ")");
    338 	}
    339 
    340 	public void sendEOF(Channel c) throws IOException
    341 	{
    342 		byte[] msg = new byte[5];
    343 
    344 		synchronized (c)
    345 		{
    346 			if (c.state != Channel.STATE_OPEN)
    347 				return;
    348 
    349 			msg[0] = Packets.SSH_MSG_CHANNEL_EOF;
    350 			msg[1] = (byte) (c.remoteID >> 24);
    351 			msg[2] = (byte) (c.remoteID >> 16);
    352 			msg[3] = (byte) (c.remoteID >> 8);
    353 			msg[4] = (byte) (c.remoteID);
    354 		}
    355 
    356 		synchronized (c.channelSendLock)
    357 		{
    358 			if (c.closeMessageSent == true)
    359 				return;
    360 			tm.sendMessage(msg);
    361 		}
    362 
    363 
    364 		log.debug("Sent EOF (Channel " + c.localID + "/" + c.remoteID + ")");
    365 	}
    366 
    367 	public void sendOpenConfirmation(Channel c) throws IOException
    368 	{
    369 		PacketChannelOpenConfirmation pcoc = null;
    370 
    371 		synchronized (c)
    372 		{
    373 			if (c.state != Channel.STATE_OPENING)
    374 				return;
    375 
    376 			c.state = Channel.STATE_OPEN;
    377 
    378 			pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID, c.localWindow, c.localMaxPacketSize);
    379 		}
    380 
    381 		synchronized (c.channelSendLock)
    382 		{
    383 			if (c.closeMessageSent == true)
    384 				return;
    385 			tm.sendMessage(pcoc.getPayload());
    386 		}
    387 	}
    388 
    389 	public void sendData(Channel c, byte[] buffer, int pos, int len) throws IOException
    390 	{
    391 		boolean wasInterrupted = false;
    392 
    393 		try
    394 		{
    395 			while (len > 0)
    396 			{
    397 				int thislen = 0;
    398 				byte[] msg;
    399 
    400 				synchronized (c)
    401 				{
    402 					while (true)
    403 					{
    404 						if (c.state == Channel.STATE_CLOSED)
    405 							throw new ChannelClosedException("SSH channel is closed. (" + c.getReasonClosed() + ")");
    406 
    407 						if (c.state != Channel.STATE_OPEN)
    408 							throw new ChannelClosedException("SSH channel in strange state. (" + c.state + ")");
    409 
    410 						if (c.remoteWindow != 0)
    411 							break;
    412 
    413 						try
    414 						{
    415 							c.wait();
    416 						}
    417 						catch (InterruptedException ignore)
    418 						{
    419 							wasInterrupted = true;
    420 						}
    421 					}
    422 
    423 					/* len > 0, no sign extension can happen when comparing */
    424 
    425 					thislen = (c.remoteWindow >= len) ? len : (int) c.remoteWindow;
    426 
    427 					int estimatedMaxDataLen = c.remoteMaxPacketSize - (tm.getPacketOverheadEstimate() + 9);
    428 
    429 					/* The worst case scenario =) a true bottleneck */
    430 
    431 					if (estimatedMaxDataLen <= 0)
    432 					{
    433 						estimatedMaxDataLen = 1;
    434 					}
    435 
    436 					if (thislen > estimatedMaxDataLen)
    437 						thislen = estimatedMaxDataLen;
    438 
    439 					c.remoteWindow -= thislen;
    440 
    441 					msg = new byte[1 + 8 + thislen];
    442 
    443 					msg[0] = Packets.SSH_MSG_CHANNEL_DATA;
    444 					msg[1] = (byte) (c.remoteID >> 24);
    445 					msg[2] = (byte) (c.remoteID >> 16);
    446 					msg[3] = (byte) (c.remoteID >> 8);
    447 					msg[4] = (byte) (c.remoteID);
    448 					msg[5] = (byte) (thislen >> 24);
    449 					msg[6] = (byte) (thislen >> 16);
    450 					msg[7] = (byte) (thislen >> 8);
    451 					msg[8] = (byte) (thislen);
    452 
    453 					System.arraycopy(buffer, pos, msg, 9, thislen);
    454 				}
    455 
    456 				synchronized (c.channelSendLock)
    457 				{
    458 					if (c.closeMessageSent == true)
    459 						throw new ChannelClosedException("SSH channel is closed. (" + c.getReasonClosed() + ")");
    460 
    461 					tm.sendMessage(msg);
    462 				}
    463 
    464 				pos += thislen;
    465 				len -= thislen;
    466 			}
    467 		}
    468 		finally
    469 		{
    470 			if (wasInterrupted)
    471 				Thread.currentThread().interrupt();
    472 		}
    473 	}
    474 
    475 	public int requestGlobalForward(String bindAddress, int bindPort, String targetAddress, int targetPort)
    476 			throws IOException
    477 	{
    478 		RemoteForwardingData rfd = new RemoteForwardingData();
    479 
    480 		rfd.bindAddress = bindAddress;
    481 		rfd.bindPort = bindPort;
    482 		rfd.targetAddress = targetAddress;
    483 		rfd.targetPort = targetPort;
    484 
    485 		synchronized (remoteForwardings)
    486 		{
    487 			Integer key = new Integer(bindPort);
    488 
    489 			if (remoteForwardings.get(key) != null)
    490 			{
    491 				throw new IOException("There is already a forwarding for remote port " + bindPort);
    492 			}
    493 
    494 			remoteForwardings.put(key, rfd);
    495 		}
    496 
    497 		synchronized (channels)
    498 		{
    499 			globalSuccessCounter = globalFailedCounter = 0;
    500 		}
    501 
    502 		PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort);
    503 		tm.sendMessage(pgf.getPayload());
    504 
    505 
    506 		log.debug("Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")");
    507 
    508 		try
    509 		{
    510 			waitForGlobalSuccessOrFailure();
    511 		}
    512 		catch (IOException e)
    513 		{
    514 			synchronized (remoteForwardings)
    515 			{
    516 				remoteForwardings.remove(rfd);
    517 			}
    518 			throw e;
    519 		}
    520 
    521 		return bindPort;
    522 	}
    523 
    524 	public void requestCancelGlobalForward(int bindPort) throws IOException
    525 	{
    526 		RemoteForwardingData rfd = null;
    527 
    528 		synchronized (remoteForwardings)
    529 		{
    530 			rfd = remoteForwardings.get(new Integer(bindPort));
    531 
    532 			if (rfd == null)
    533 				throw new IOException("Sorry, there is no known remote forwarding for remote port " + bindPort);
    534 		}
    535 
    536 		synchronized (channels)
    537 		{
    538 			globalSuccessCounter = globalFailedCounter = 0;
    539 		}
    540 
    541 		PacketGlobalCancelForwardRequest pgcf = new PacketGlobalCancelForwardRequest(true, rfd.bindAddress,
    542 				rfd.bindPort);
    543 		tm.sendMessage(pgcf.getPayload());
    544 
    545 
    546 		log.debug("Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")");
    547 
    548 		waitForGlobalSuccessOrFailure();
    549 
    550 		/* Only now we are sure that no more forwarded connections will arrive */
    551 
    552 		synchronized (remoteForwardings)
    553 		{
    554 			remoteForwardings.remove(rfd);
    555 		}
    556 	}
    557 
    558 	public void registerThread(IChannelWorkerThread thr) throws IOException
    559 	{
    560 		synchronized (listenerThreads)
    561 		{
    562 			if (listenerThreadsAllowed == false)
    563 				throw new IOException("Too late, this connection is closed.");
    564 			listenerThreads.add(thr);
    565 		}
    566 	}
    567 
    568 	public Channel openDirectTCPIPChannel(String host_to_connect, int port_to_connect, String originator_IP_address,
    569 										  int originator_port) throws IOException
    570 	{
    571 		Channel c = new Channel(this);
    572 
    573 		synchronized (c)
    574 		{
    575 			c.localID = addChannel(c);
    576 			// end of synchronized block forces writing out to main memory
    577 		}
    578 
    579 		PacketOpenDirectTCPIPChannel dtc = new PacketOpenDirectTCPIPChannel(c.localID, c.localWindow,
    580 				c.localMaxPacketSize, host_to_connect, port_to_connect, originator_IP_address, originator_port);
    581 
    582 		tm.sendMessage(dtc.getPayload());
    583 
    584 		waitUntilChannelOpen(c);
    585 
    586 		return c;
    587 	}
    588 
    589 	public Channel openSessionChannel() throws IOException
    590 	{
    591 		Channel c = new Channel(this);
    592 
    593 		synchronized (c)
    594 		{
    595 			c.localID = addChannel(c);
    596 			// end of synchronized block forces the writing out to main memory
    597 		}
    598 
    599 
    600 		log.debug("Sending SSH_MSG_CHANNEL_OPEN (Channel " + c.localID + ")");
    601 
    602 		PacketOpenSessionChannel smo = new PacketOpenSessionChannel(c.localID, c.localWindow, c.localMaxPacketSize);
    603 		tm.sendMessage(smo.getPayload());
    604 
    605 		waitUntilChannelOpen(c);
    606 
    607 		return c;
    608 	}
    609 
    610 	public void requestPTY(Channel c, String term, int term_width_characters, int term_height_characters,
    611 						   int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException
    612 	{
    613 		PacketSessionPtyRequest spr;
    614 
    615 		synchronized (c)
    616 		{
    617 			if (c.state != Channel.STATE_OPEN)
    618 				throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
    619 
    620 			spr = new PacketSessionPtyRequest(c.remoteID, true, term, term_width_characters, term_height_characters,
    621 					term_width_pixels, term_height_pixels, terminal_modes);
    622 
    623 			c.successCounter = c.failedCounter = 0;
    624 		}
    625 
    626 		synchronized (c.channelSendLock)
    627 		{
    628 			if (c.closeMessageSent)
    629 				throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
    630 			tm.sendMessage(spr.getPayload());
    631 		}
    632 
    633 		try
    634 		{
    635 			waitForChannelSuccessOrFailure(c);
    636 		}
    637 		catch (IOException e)
    638 		{
    639 			throw (IOException) new IOException("PTY request failed").initCause(e);
    640 		}
    641 	}
    642 
    643 	public void requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol,
    644 						   String x11AuthenticationCookie, int x11ScreenNumber) throws IOException
    645 	{
    646 		PacketSessionX11Request psr;
    647 
    648 		synchronized (c)
    649 		{
    650 			if (c.state != Channel.STATE_OPEN)
    651 				throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
    652 
    653 			psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol,
    654 					x11AuthenticationCookie, x11ScreenNumber);
    655 
    656 			c.successCounter = c.failedCounter = 0;
    657 		}
    658 
    659 		synchronized (c.channelSendLock)
    660 		{
    661 			if (c.closeMessageSent)
    662 				throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
    663 			tm.sendMessage(psr.getPayload());
    664 		}
    665 
    666 
    667 		log.debug("Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")");
    668 
    669 		try
    670 		{
    671 			waitForChannelSuccessOrFailure(c);
    672 		}
    673 		catch (IOException e)
    674 		{
    675 			throw (IOException) new IOException("The X11 request failed.").initCause(e);
    676 		}
    677 	}
    678 
    679 	public void requestSubSystem(Channel c, String subSystemName) throws IOException
    680 	{
    681 		PacketSessionSubsystemRequest ssr;
    682 
    683 		synchronized (c)
    684 		{
    685 			if (c.state != Channel.STATE_OPEN)
    686 				throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
    687 
    688 			ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName);
    689 
    690 			c.successCounter = c.failedCounter = 0;
    691 		}
    692 
    693 		synchronized (c.channelSendLock)
    694 		{
    695 			if (c.closeMessageSent)
    696 				throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
    697 			tm.sendMessage(ssr.getPayload());
    698 		}
    699 
    700 		try
    701 		{
    702 			waitForChannelSuccessOrFailure(c);
    703 		}
    704 		catch (IOException e)
    705 		{
    706 			throw (IOException) new IOException("The subsystem request failed.").initCause(e);
    707 		}
    708 	}
    709 
    710 	public void requestExecCommand(Channel c, String cmd) throws IOException
    711 	{
    712 		this.requestExecCommand(c, cmd, null);
    713 	}
    714 
    715 	/**
    716 	 * @param charsetName The charset used to convert between Java Unicode Strings and byte encodings
    717 	 */
    718 	public void requestExecCommand(Channel c, String cmd, String charsetName) throws IOException
    719 	{
    720 		PacketSessionExecCommand sm;
    721 
    722 		synchronized (c)
    723 		{
    724 			if (c.state != Channel.STATE_OPEN)
    725 				throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
    726 
    727 			sm = new PacketSessionExecCommand(c.remoteID, true, cmd);
    728 
    729 			c.successCounter = c.failedCounter = 0;
    730 		}
    731 
    732 		synchronized (c.channelSendLock)
    733 		{
    734 			if (c.closeMessageSent)
    735 				throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
    736 			tm.sendMessage(sm.getPayload(charsetName));
    737 		}
    738 
    739 
    740 		log.debug("Executing command (channel " + c.localID + ", '" + cmd + "')");
    741 
    742 		try
    743 		{
    744 			waitForChannelSuccessOrFailure(c);
    745 		}
    746 		catch (IOException e)
    747 		{
    748 			throw (IOException) new IOException("The execute request failed.").initCause(e);
    749 		}
    750 	}
    751 
    752 	public void requestShell(Channel c) throws IOException
    753 	{
    754 		PacketSessionStartShell sm;
    755 
    756 		synchronized (c)
    757 		{
    758 			if (c.state != Channel.STATE_OPEN)
    759 				throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
    760 
    761 			sm = new PacketSessionStartShell(c.remoteID, true);
    762 
    763 			c.successCounter = c.failedCounter = 0;
    764 		}
    765 
    766 		synchronized (c.channelSendLock)
    767 		{
    768 			if (c.closeMessageSent)
    769 				throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
    770 			tm.sendMessage(sm.getPayload());
    771 		}
    772 
    773 		try
    774 		{
    775 			waitForChannelSuccessOrFailure(c);
    776 		}
    777 		catch (IOException e)
    778 		{
    779 			throw (IOException) new IOException("The shell request failed.").initCause(e);
    780 		}
    781 	}
    782 
    783 	public void msgChannelExtendedData(byte[] msg, int msglen) throws IOException
    784 	{
    785 		if (msglen <= 13)
    786 			throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (" + msglen + ")");
    787 
    788 		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
    789 		int dataType = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
    790 		int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16) | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff);
    791 
    792 		Channel c = getChannel(id);
    793 
    794 		if (c == null)
    795 			throw new IOException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id);
    796 
    797 		if (dataType != Packets.SSH_EXTENDED_DATA_STDERR)
    798 			throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")");
    799 
    800 		if (len != (msglen - 13))
    801 			throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msglen - 13)
    802 					+ ", got " + len + ")");
    803 
    804 
    805 		log.debug("Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel " + id + ", " + len + ")");
    806 
    807 		synchronized (c)
    808 		{
    809 			if (c.state == Channel.STATE_CLOSED)
    810 				return; // ignore
    811 
    812 			if (c.state != Channel.STATE_OPEN)
    813 				throw new IOException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state ("
    814 						+ c.state + ")");
    815 
    816 			if (c.localWindow < len)
    817 				throw new IOException("Remote sent too much data, does not fit into window.");
    818 
    819 			c.localWindow -= len;
    820 
    821 			System.arraycopy(msg, 13, c.stderrBuffer, c.stderrWritepos, len);
    822 			c.stderrWritepos += len;
    823 
    824 			c.notifyAll();
    825 		}
    826 	}
    827 
    828 	/**
    829 	 * Wait until for a condition.
    830 	 *
    831 	 * @param c Channel
    832 	 * @param timeout in ms, 0 means no timeout.
    833 	 * @param condition_mask minimum event mask (at least one of the conditions must be fulfilled)
    834 	 * @return all current events
    835 	 */
    836 	public int waitForCondition(Channel c, long timeout, int condition_mask)
    837 	{
    838 		boolean wasInterrupted = false;
    839 
    840 		try
    841 		{
    842 			long end_time = 0;
    843 			boolean end_time_set = false;
    844 
    845 			synchronized (c)
    846 			{
    847 				while (true)
    848 				{
    849 					int current_cond = 0;
    850 
    851 					int stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
    852 					int stderrAvail = c.stderrWritepos - c.stderrReadpos;
    853 
    854 					if (stdoutAvail > 0)
    855 						current_cond = current_cond | ChannelCondition.STDOUT_DATA;
    856 
    857 					if (stderrAvail > 0)
    858 						current_cond = current_cond | ChannelCondition.STDERR_DATA;
    859 
    860 					if (c.EOF)
    861 						current_cond = current_cond | ChannelCondition.EOF;
    862 
    863 					if (c.getExitStatus() != null)
    864 						current_cond = current_cond | ChannelCondition.EXIT_STATUS;
    865 
    866 					if (c.getExitSignal() != null)
    867 						current_cond = current_cond | ChannelCondition.EXIT_SIGNAL;
    868 
    869 					if (c.state == Channel.STATE_CLOSED)
    870 						return current_cond | ChannelCondition.CLOSED | ChannelCondition.EOF;
    871 
    872 					if ((current_cond & condition_mask) != 0)
    873 						return current_cond;
    874 
    875 					if (timeout > 0)
    876 					{
    877 						if (!end_time_set)
    878 						{
    879 							end_time = System.currentTimeMillis() + timeout;
    880 							end_time_set = true;
    881 						}
    882 						else
    883 						{
    884 							timeout = end_time - System.currentTimeMillis();
    885 
    886 							if (timeout <= 0)
    887 								return current_cond | ChannelCondition.TIMEOUT;
    888 						}
    889 					}
    890 
    891 					try
    892 					{
    893 						if (timeout > 0)
    894 							c.wait(timeout);
    895 						else
    896 							c.wait();
    897 					}
    898 					catch (InterruptedException e)
    899 					{
    900 						wasInterrupted = true;
    901 					}
    902 				}
    903 			}
    904 		}
    905 		finally
    906 		{
    907 			if (wasInterrupted)
    908 				Thread.currentThread().interrupt();
    909 		}
    910 	}
    911 
    912 	public int getAvailable(Channel c, boolean extended) throws IOException
    913 	{
    914 		synchronized (c)
    915 		{
    916 			int avail;
    917 
    918 			if (extended)
    919 				avail = c.stderrWritepos - c.stderrReadpos;
    920 			else
    921 				avail = c.stdoutWritepos - c.stdoutReadpos;
    922 
    923 			return ((avail > 0) ? avail : (c.EOF ? -1 : 0));
    924 		}
    925 	}
    926 
    927 	public int getChannelData(Channel c, boolean extended, byte[] target, int off, int len) throws IOException
    928 	{
    929 		boolean wasInterrupted = false;
    930 
    931 		try
    932 		{
    933 			int copylen = 0;
    934 			int increment = 0;
    935 			int remoteID = 0;
    936 			int localID = 0;
    937 
    938 			synchronized (c)
    939 			{
    940 				int stdoutAvail = 0;
    941 				int stderrAvail = 0;
    942 
    943 				while (true)
    944 				{
    945 					/*
    946 					 * Data available? We have to return remaining data even if the
    947 					 * channel is already closed.
    948 					 */
    949 
    950 					stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
    951 					stderrAvail = c.stderrWritepos - c.stderrReadpos;
    952 
    953 					if ((!extended) && (stdoutAvail != 0))
    954 						break;
    955 
    956 					if ((extended) && (stderrAvail != 0))
    957 						break;
    958 
    959 					/* Do not wait if more data will never arrive (EOF or CLOSED) */
    960 
    961 					if ((c.EOF) || (c.state != Channel.STATE_OPEN))
    962 						return -1;
    963 
    964 					try
    965 					{
    966 						c.wait();
    967 					}
    968 					catch (InterruptedException ignore)
    969 					{
    970 						wasInterrupted = true;
    971 					}
    972 				}
    973 
    974 				/* OK, there is some data. Return it. */
    975 
    976 				if (!extended)
    977 				{
    978 					copylen = (stdoutAvail > len) ? len : stdoutAvail;
    979 					System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, target, off, copylen);
    980 					c.stdoutReadpos += copylen;
    981 
    982 					if (c.stdoutReadpos != c.stdoutWritepos)
    983 
    984 						System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, c.stdoutBuffer, 0, c.stdoutWritepos
    985 								- c.stdoutReadpos);
    986 
    987 					c.stdoutWritepos -= c.stdoutReadpos;
    988 					c.stdoutReadpos = 0;
    989 				}
    990 				else
    991 				{
    992 					copylen = (stderrAvail > len) ? len : stderrAvail;
    993 					System.arraycopy(c.stderrBuffer, c.stderrReadpos, target, off, copylen);
    994 					c.stderrReadpos += copylen;
    995 
    996 					if (c.stderrReadpos != c.stderrWritepos)
    997 
    998 						System.arraycopy(c.stderrBuffer, c.stderrReadpos, c.stderrBuffer, 0, c.stderrWritepos
    999 								- c.stderrReadpos);
   1000 
   1001 					c.stderrWritepos -= c.stderrReadpos;
   1002 					c.stderrReadpos = 0;
   1003 				}
   1004 
   1005 				if (c.state != Channel.STATE_OPEN)
   1006 					return copylen;
   1007 
   1008 				if (c.localWindow < ((Channel.CHANNEL_BUFFER_SIZE + 1) / 2))
   1009 				{
   1010 					int minFreeSpace = Math.min(Channel.CHANNEL_BUFFER_SIZE - c.stdoutWritepos,
   1011 							Channel.CHANNEL_BUFFER_SIZE - c.stderrWritepos);
   1012 
   1013 					increment = minFreeSpace - c.localWindow;
   1014 					c.localWindow = minFreeSpace;
   1015 				}
   1016 
   1017 				remoteID = c.remoteID; /* read while holding the lock */
   1018 				localID = c.localID; /* read while holding the lock */
   1019 			}
   1020 
   1021 			/*
   1022 			 * If a consumer reads stdout and stdin in parallel, we may end up with
   1023 			 * sending two msgWindowAdjust messages. Luckily, it
   1024 			 * does not matter in which order they arrive at the server.
   1025 			 */
   1026 
   1027 			if (increment > 0)
   1028 			{
   1029 
   1030 				log.debug("Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")");
   1031 
   1032 				synchronized (c.channelSendLock)
   1033 				{
   1034 					byte[] msg = c.msgWindowAdjust;
   1035 
   1036 					msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
   1037 					msg[1] = (byte) (remoteID >> 24);
   1038 					msg[2] = (byte) (remoteID >> 16);
   1039 					msg[3] = (byte) (remoteID >> 8);
   1040 					msg[4] = (byte) (remoteID);
   1041 					msg[5] = (byte) (increment >> 24);
   1042 					msg[6] = (byte) (increment >> 16);
   1043 					msg[7] = (byte) (increment >> 8);
   1044 					msg[8] = (byte) (increment);
   1045 
   1046 					if (c.closeMessageSent == false)
   1047 						tm.sendMessage(msg);
   1048 				}
   1049 			}
   1050 
   1051 			return copylen;
   1052 		}
   1053 		finally
   1054 		{
   1055 			if (wasInterrupted)
   1056 				Thread.currentThread().interrupt();
   1057 		}
   1058 
   1059 	}
   1060 
   1061 	public void msgChannelData(byte[] msg, int msglen) throws IOException
   1062 	{
   1063 		if (msglen <= 9)
   1064 			throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong size (" + msglen + ")");
   1065 
   1066 		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
   1067 		int len = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
   1068 
   1069 		Channel c = getChannel(id);
   1070 
   1071 		if (c == null)
   1072 			throw new IOException("Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel " + id);
   1073 
   1074 		if (len != (msglen - 9))
   1075 			throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong len (calculated " + (msglen - 9) + ", got "
   1076 					+ len + ")");
   1077 
   1078 
   1079 		log.debug("Got SSH_MSG_CHANNEL_DATA (channel " + id + ", " + len + ")");
   1080 
   1081 		synchronized (c)
   1082 		{
   1083 			if (c.state == Channel.STATE_CLOSED)
   1084 				return; // ignore
   1085 
   1086 			if (c.state != Channel.STATE_OPEN)
   1087 				throw new IOException("Got SSH_MSG_CHANNEL_DATA, but channel is not in correct state (" + c.state + ")");
   1088 
   1089 			if (c.localWindow < len)
   1090 				throw new IOException("Remote sent too much data, does not fit into window.");
   1091 
   1092 			c.localWindow -= len;
   1093 
   1094 			System.arraycopy(msg, 9, c.stdoutBuffer, c.stdoutWritepos, len);
   1095 			c.stdoutWritepos += len;
   1096 
   1097 			c.notifyAll();
   1098 		}
   1099 	}
   1100 
   1101 	public void msgChannelWindowAdjust(byte[] msg, int msglen) throws IOException
   1102 	{
   1103 		if (msglen != 9)
   1104 			throw new IOException("SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size (" + msglen + ")");
   1105 
   1106 		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
   1107 		int windowChange = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
   1108 
   1109 		Channel c = getChannel(id);
   1110 
   1111 		if (c == null)
   1112 			throw new IOException("Unexpected SSH_MSG_CHANNEL_WINDOW_ADJUST message for non-existent channel " + id);
   1113 
   1114 		synchronized (c)
   1115 		{
   1116 			final long huge = 0xFFFFffffL; /* 2^32 - 1 */
   1117 
   1118 			c.remoteWindow += (windowChange & huge); /* avoid sign extension */
   1119 
   1120 			/* TODO - is this a good heuristic? */
   1121 
   1122 			if ((c.remoteWindow > huge))
   1123 				c.remoteWindow = huge;
   1124 
   1125 			c.notifyAll();
   1126 		}
   1127 
   1128 
   1129 		log.debug("Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + id + ", " + windowChange + ")");
   1130 	}
   1131 
   1132 	public void msgChannelOpen(byte[] msg, int msglen) throws IOException
   1133 	{
   1134 		TypesReader tr = new TypesReader(msg, 0, msglen);
   1135 
   1136 		tr.readByte(); // skip packet type
   1137 		String channelType = tr.readString();
   1138 		int remoteID = tr.readUINT32(); /* sender channel */
   1139 		int remoteWindow = tr.readUINT32(); /* initial window size */
   1140 		int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */
   1141 
   1142 		if ("x11".equals(channelType))
   1143 		{
   1144 			synchronized (x11_magic_cookies)
   1145 			{
   1146 				/* If we did not request X11 forwarding, then simply ignore this bogus request. */
   1147 
   1148 				if (x11_magic_cookies.size() == 0)
   1149 				{
   1150 					PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
   1151 							Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "X11 forwarding not activated", "");
   1152 
   1153 					tm.sendAsynchronousMessage(pcof.getPayload());
   1154 
   1155 
   1156 					log.warning("Unexpected X11 request, denying it!");
   1157 
   1158 					return;
   1159 				}
   1160 			}
   1161 
   1162 			String remoteOriginatorAddress = tr.readString();
   1163 			int remoteOriginatorPort = tr.readUINT32();
   1164 
   1165 			Channel c = new Channel(this);
   1166 
   1167 			synchronized (c)
   1168 			{
   1169 				c.remoteID = remoteID;
   1170 				c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */
   1171 				c.remoteMaxPacketSize = remoteMaxPacketSize;
   1172 				c.localID = addChannel(c);
   1173 			}
   1174 
   1175 			/*
   1176 			 * The open confirmation message will be sent from another thread
   1177 			 */
   1178 
   1179 			RemoteX11AcceptThread rxat = new RemoteX11AcceptThread(c, remoteOriginatorAddress, remoteOriginatorPort);
   1180 			rxat.setDaemon(true);
   1181 			rxat.start();
   1182 
   1183 			return;
   1184 		}
   1185 
   1186 		if ("forwarded-tcpip".equals(channelType))
   1187 		{
   1188 			String remoteConnectedAddress = tr.readString(); /* address that was connected */
   1189 			int remoteConnectedPort = tr.readUINT32(); /* port that was connected */
   1190 			String remoteOriginatorAddress = tr.readString(); /* originator IP address */
   1191 			int remoteOriginatorPort = tr.readUINT32(); /* originator port */
   1192 
   1193 			RemoteForwardingData rfd = null;
   1194 
   1195 			synchronized (remoteForwardings)
   1196 			{
   1197 				rfd = remoteForwardings.get(new Integer(remoteConnectedPort));
   1198 			}
   1199 
   1200 			if (rfd == null)
   1201 			{
   1202 				PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
   1203 						Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
   1204 						"No thanks, unknown port in forwarded-tcpip request", "");
   1205 
   1206 				/* Always try to be polite. */
   1207 
   1208 				tm.sendAsynchronousMessage(pcof.getPayload());
   1209 
   1210 
   1211 				log.debug("Unexpected forwarded-tcpip request, denying it!");
   1212 
   1213 				return;
   1214 			}
   1215 
   1216 			Channel c = new Channel(this);
   1217 
   1218 			synchronized (c)
   1219 			{
   1220 				c.remoteID = remoteID;
   1221 				c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
   1222 				c.remoteMaxPacketSize = remoteMaxPacketSize;
   1223 				c.localID = addChannel(c);
   1224 			}
   1225 
   1226 			/*
   1227 			 * The open confirmation message will be sent from another thread.
   1228 			 */
   1229 
   1230 			RemoteAcceptThread rat = new RemoteAcceptThread(c, remoteConnectedAddress, remoteConnectedPort,
   1231 					remoteOriginatorAddress, remoteOriginatorPort, rfd.targetAddress, rfd.targetPort);
   1232 
   1233 			rat.setDaemon(true);
   1234 			rat.start();
   1235 
   1236 			return;
   1237 		}
   1238 
   1239 		/* Tell the server that we have no idea what it is talking about */
   1240 
   1241 		PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
   1242 				"Unknown channel type", "");
   1243 
   1244 		tm.sendAsynchronousMessage(pcof.getPayload());
   1245 
   1246 
   1247 		log.warning("The peer tried to open an unsupported channel type (" + channelType + ")");
   1248 	}
   1249 
   1250 	public void msgChannelRequest(byte[] msg, int msglen) throws IOException
   1251 	{
   1252 		TypesReader tr = new TypesReader(msg, 0, msglen);
   1253 
   1254 		tr.readByte(); // skip packet type
   1255 		int id = tr.readUINT32();
   1256 
   1257 		Channel c = getChannel(id);
   1258 
   1259 		if (c == null)
   1260 			throw new IOException("Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id);
   1261 
   1262 		String type = tr.readString("US-ASCII");
   1263 		boolean wantReply = tr.readBoolean();
   1264 
   1265 
   1266 		log.debug("Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')");
   1267 
   1268 		if (type.equals("exit-status"))
   1269 		{
   1270 			if (wantReply != false)
   1271 				throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true");
   1272 
   1273 			int exit_status = tr.readUINT32();
   1274 
   1275 			if (tr.remain() != 0)
   1276 				throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
   1277 
   1278 			synchronized (c)
   1279 			{
   1280 				c.exit_status = new Integer(exit_status);
   1281 				c.notifyAll();
   1282 			}
   1283 
   1284 
   1285 			log.debug("Got EXIT STATUS (channel " + id + ", status " + exit_status + ")");
   1286 
   1287 			return;
   1288 		}
   1289 
   1290 		if (type.equals("exit-signal"))
   1291 		{
   1292 			if (wantReply != false)
   1293 				throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true");
   1294 
   1295 			String signame = tr.readString("US-ASCII");
   1296 			tr.readBoolean();
   1297 			tr.readString();
   1298 			tr.readString();
   1299 
   1300 			if (tr.remain() != 0)
   1301 				throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
   1302 
   1303 			synchronized (c)
   1304 			{
   1305 				c.exit_signal = signame;
   1306 				c.notifyAll();
   1307 			}
   1308 
   1309 
   1310 			log.debug("Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")");
   1311 
   1312 			return;
   1313 		}
   1314 
   1315 		/* We simply ignore unknown channel requests, however, if the server wants a reply,
   1316 		 * then we signal that we have no idea what it is about.
   1317 		 */
   1318 
   1319 		if (wantReply)
   1320 		{
   1321 			byte[] reply = new byte[5];
   1322 
   1323 			reply[0] = Packets.SSH_MSG_CHANNEL_FAILURE;
   1324 			reply[1] = (byte) (c.remoteID >> 24);
   1325 			reply[2] = (byte) (c.remoteID >> 16);
   1326 			reply[3] = (byte) (c.remoteID >> 8);
   1327 			reply[4] = (byte) (c.remoteID);
   1328 
   1329 			tm.sendAsynchronousMessage(reply);
   1330 		}
   1331 
   1332 
   1333 		log.debug("Channel request '" + type + "' is not known, ignoring it");
   1334 	}
   1335 
   1336 	public void msgChannelEOF(byte[] msg, int msglen) throws IOException
   1337 	{
   1338 		if (msglen != 5)
   1339 			throw new IOException("SSH_MSG_CHANNEL_EOF message has wrong size (" + msglen + ")");
   1340 
   1341 		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
   1342 
   1343 		Channel c = getChannel(id);
   1344 
   1345 		if (c == null)
   1346 			throw new IOException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id);
   1347 
   1348 		synchronized (c)
   1349 		{
   1350 			c.EOF = true;
   1351 			c.notifyAll();
   1352 		}
   1353 
   1354 
   1355 		log.debug("Got SSH_MSG_CHANNEL_EOF (channel " + id + ")");
   1356 	}
   1357 
   1358 	public void msgChannelClose(byte[] msg, int msglen) throws IOException
   1359 	{
   1360 		if (msglen != 5)
   1361 			throw new IOException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msglen + ")");
   1362 
   1363 		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
   1364 
   1365 		Channel c = getChannel(id);
   1366 
   1367 		if (c == null)
   1368 			throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id);
   1369 
   1370 		synchronized (c)
   1371 		{
   1372 			c.EOF = true;
   1373 			c.state = Channel.STATE_CLOSED;
   1374 			c.setReasonClosed("Close requested by remote");
   1375 			c.closeMessageRecv = true;
   1376 
   1377 			removeChannel(c.localID);
   1378 
   1379 			c.notifyAll();
   1380 		}
   1381 
   1382 
   1383 		log.debug("Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")");
   1384 	}
   1385 
   1386 	public void msgChannelSuccess(byte[] msg, int msglen) throws IOException
   1387 	{
   1388 		if (msglen != 5)
   1389 			throw new IOException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msglen + ")");
   1390 
   1391 		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
   1392 
   1393 		Channel c = getChannel(id);
   1394 
   1395 		if (c == null)
   1396 			throw new IOException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id);
   1397 
   1398 		synchronized (c)
   1399 		{
   1400 			c.successCounter++;
   1401 			c.notifyAll();
   1402 		}
   1403 
   1404 
   1405 		log.debug("Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")");
   1406 	}
   1407 
   1408 	public void msgChannelFailure(byte[] msg, int msglen) throws IOException
   1409 	{
   1410 		if (msglen != 5)
   1411 			throw new IOException("SSH_MSG_CHANNEL_FAILURE message has wrong size (" + msglen + ")");
   1412 
   1413 		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
   1414 
   1415 		Channel c = getChannel(id);
   1416 
   1417 		if (c == null)
   1418 			throw new IOException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id);
   1419 
   1420 		synchronized (c)
   1421 		{
   1422 			c.failedCounter++;
   1423 			c.notifyAll();
   1424 		}
   1425 
   1426 
   1427 		log.debug("Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")");
   1428 	}
   1429 
   1430 	public void msgChannelOpenConfirmation(byte[] msg, int msglen) throws IOException
   1431 	{
   1432 		PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg, 0, msglen);
   1433 
   1434 		Channel c = getChannel(sm.recipientChannelID);
   1435 
   1436 		if (c == null)
   1437 			throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel "
   1438 					+ sm.recipientChannelID);
   1439 
   1440 		synchronized (c)
   1441 		{
   1442 			if (c.state != Channel.STATE_OPENING)
   1443 				throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel "
   1444 						+ sm.recipientChannelID);
   1445 
   1446 			c.remoteID = sm.senderChannelID;
   1447 			c.remoteWindow = sm.initialWindowSize & 0xFFFFffffL; /* convert UINT32 to long */
   1448 			c.remoteMaxPacketSize = sm.maxPacketSize;
   1449 			c.state = Channel.STATE_OPEN;
   1450 			c.notifyAll();
   1451 		}
   1452 
   1453 
   1454 		log.debug("Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.recipientChannelID + " / remote: "
   1455 				+ sm.senderChannelID + ")");
   1456 	}
   1457 
   1458 	public void msgChannelOpenFailure(byte[] msg, int msglen) throws IOException
   1459 	{
   1460 		if (msglen < 5)
   1461 			throw new IOException("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (" + msglen + ")");
   1462 
   1463 		TypesReader tr = new TypesReader(msg, 0, msglen);
   1464 
   1465 		tr.readByte(); // skip packet type
   1466 		int id = tr.readUINT32(); /* sender channel */
   1467 
   1468 		Channel c = getChannel(id);
   1469 
   1470 		if (c == null)
   1471 			throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id);
   1472 
   1473 		int reasonCode = tr.readUINT32();
   1474 		String description = tr.readString("UTF-8");
   1475 
   1476 		String reasonCodeSymbolicName = null;
   1477 
   1478 		switch (reasonCode)
   1479 		{
   1480 			case 1:
   1481 				reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED";
   1482 				break;
   1483 			case 2:
   1484 				reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED";
   1485 				break;
   1486 			case 3:
   1487 				reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE";
   1488 				break;
   1489 			case 4:
   1490 				reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE";
   1491 				break;
   1492 			default:
   1493 				reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")";
   1494 		}
   1495 
   1496 		StringBuilder descriptionBuffer = new StringBuilder();
   1497 		descriptionBuffer.append(description);
   1498 
   1499 		for (int i = 0; i < descriptionBuffer.length(); i++)
   1500 		{
   1501 			char cc = descriptionBuffer.charAt(i);
   1502 
   1503 			if ((cc >= 32) && (cc <= 126))
   1504 				continue;
   1505 			descriptionBuffer.setCharAt(i, '\uFFFD');
   1506 		}
   1507 
   1508 		synchronized (c)
   1509 		{
   1510 			c.EOF = true;
   1511 			c.state = Channel.STATE_CLOSED;
   1512 			c.setReasonClosed("The server refused to open the channel (" + reasonCodeSymbolicName + ", '"
   1513 					+ descriptionBuffer.toString() + "')");
   1514 			c.notifyAll();
   1515 		}
   1516 
   1517 
   1518 		log.debug("Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")");
   1519 	}
   1520 
   1521 	public void msgGlobalRequest(byte[] msg, int msglen) throws IOException
   1522 	{
   1523 		/* Currently we do not support any kind of global request */
   1524 
   1525 		TypesReader tr = new TypesReader(msg, 0, msglen);
   1526 
   1527 		tr.readByte(); // skip packet type
   1528 		String requestName = tr.readString();
   1529 		boolean wantReply = tr.readBoolean();
   1530 
   1531 		if (wantReply)
   1532 		{
   1533 			byte[] reply_failure = new byte[1];
   1534 			reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE;
   1535 
   1536 			tm.sendAsynchronousMessage(reply_failure);
   1537 		}
   1538 
   1539 		/* We do not clean up the requestName String - that is OK for debug */
   1540 
   1541 
   1542 		log.debug("Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")");
   1543 	}
   1544 
   1545 	public void msgGlobalSuccess() throws IOException
   1546 	{
   1547 		synchronized (channels)
   1548 		{
   1549 			globalSuccessCounter++;
   1550 			channels.notifyAll();
   1551 		}
   1552 
   1553 
   1554 		log.debug("Got SSH_MSG_REQUEST_SUCCESS");
   1555 	}
   1556 
   1557 	public void msgGlobalFailure() throws IOException
   1558 	{
   1559 		synchronized (channels)
   1560 		{
   1561 			globalFailedCounter++;
   1562 			channels.notifyAll();
   1563 		}
   1564 
   1565 
   1566 		log.debug("Got SSH_MSG_REQUEST_FAILURE");
   1567 	}
   1568 
   1569 	public void handleMessage(byte[] msg, int msglen) throws IOException
   1570 	{
   1571 		if (msg == null)
   1572 		{
   1573 
   1574 			log.debug("HandleMessage: got shutdown");
   1575 
   1576 			synchronized (listenerThreads)
   1577 			{
   1578 				for (IChannelWorkerThread lat : listenerThreads)
   1579 				{
   1580 					lat.stopWorking();
   1581 				}
   1582 				listenerThreadsAllowed = false;
   1583 			}
   1584 
   1585 			synchronized (channels)
   1586 			{
   1587 				shutdown = true;
   1588 
   1589 				for (Channel c : channels)
   1590 				{
   1591 					synchronized (c)
   1592 					{
   1593 						c.EOF = true;
   1594 						c.state = Channel.STATE_CLOSED;
   1595 						c.setReasonClosed("The connection is being shutdown");
   1596 						c.closeMessageRecv = true; /*
   1597 													* You never know, perhaps
   1598 													* we are waiting for a
   1599 													* pending close message
   1600 													* from the server...
   1601 													*/
   1602 						c.notifyAll();
   1603 					}
   1604 				}
   1605 
   1606 				channels.clear();
   1607 				channels.notifyAll(); /* Notify global response waiters */
   1608 				return;
   1609 			}
   1610 		}
   1611 
   1612 		switch (msg[0])
   1613 		{
   1614 			case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
   1615 				msgChannelOpenConfirmation(msg, msglen);
   1616 				break;
   1617 			case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST:
   1618 				msgChannelWindowAdjust(msg, msglen);
   1619 				break;
   1620 			case Packets.SSH_MSG_CHANNEL_DATA:
   1621 				msgChannelData(msg, msglen);
   1622 				break;
   1623 			case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA:
   1624 				msgChannelExtendedData(msg, msglen);
   1625 				break;
   1626 			case Packets.SSH_MSG_CHANNEL_REQUEST:
   1627 				msgChannelRequest(msg, msglen);
   1628 				break;
   1629 			case Packets.SSH_MSG_CHANNEL_EOF:
   1630 				msgChannelEOF(msg, msglen);
   1631 				break;
   1632 			case Packets.SSH_MSG_CHANNEL_OPEN:
   1633 				msgChannelOpen(msg, msglen);
   1634 				break;
   1635 			case Packets.SSH_MSG_CHANNEL_CLOSE:
   1636 				msgChannelClose(msg, msglen);
   1637 				break;
   1638 			case Packets.SSH_MSG_CHANNEL_SUCCESS:
   1639 				msgChannelSuccess(msg, msglen);
   1640 				break;
   1641 			case Packets.SSH_MSG_CHANNEL_FAILURE:
   1642 				msgChannelFailure(msg, msglen);
   1643 				break;
   1644 			case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE:
   1645 				msgChannelOpenFailure(msg, msglen);
   1646 				break;
   1647 			case Packets.SSH_MSG_GLOBAL_REQUEST:
   1648 				msgGlobalRequest(msg, msglen);
   1649 				break;
   1650 			case Packets.SSH_MSG_REQUEST_SUCCESS:
   1651 				msgGlobalSuccess();
   1652 				break;
   1653 			case Packets.SSH_MSG_REQUEST_FAILURE:
   1654 				msgGlobalFailure();
   1655 				break;
   1656 			default:
   1657 				throw new IOException("Cannot handle unknown channel message " + (msg[0] & 0xff));
   1658 		}
   1659 	}
   1660 }
   1661