• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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 
ChannelManager(TransportManager tm)59 	public ChannelManager(TransportManager tm)
60 	{
61 		this.tm = tm;
62 		tm.registerMessageHandler(this, 80, 100);
63 	}
64 
getChannel(int id)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 
removeChannel(int id)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 
addChannel(Channel c)93 	private int addChannel(Channel c)
94 	{
95 		synchronized (channels)
96 		{
97 			channels.add(c);
98 			return nextLocalChannel++;
99 		}
100 	}
101 
waitUntilChannelOpen(Channel c)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 
waitForGlobalSuccessOrFailure()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 
waitForChannelSuccessOrFailure(Channel c)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 
registerX11Cookie(String hexFakeCookie, X11ServerData data)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 
unRegisterX11Cookie(String hexFakeCookie, boolean killChannels)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 
checkX11Cookie(String hexFakeCookie)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 
closeAllChannels()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 
closeChannel(Channel c, String reason, boolean force)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 
sendEOF(Channel c)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 
sendOpenConfirmation(Channel c)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 
sendData(Channel c, byte[] buffer, int pos, int len)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 
requestGlobalForward(String bindAddress, int bindPort, String targetAddress, int targetPort)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 
requestCancelGlobalForward(int bindPort)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 
registerThread(IChannelWorkerThread thr)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 
openDirectTCPIPChannel(String host_to_connect, int port_to_connect, String originator_IP_address, int originator_port)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 
openSessionChannel()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 
requestPTY(Channel c, String term, int term_width_characters, int term_height_characters, int term_width_pixels, int term_height_pixels, byte[] terminal_modes)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 
requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol, String x11AuthenticationCookie, int x11ScreenNumber)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 
requestSubSystem(Channel c, String subSystemName)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 
requestExecCommand(Channel c, String cmd)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 	 */
requestExecCommand(Channel c, String cmd, String charsetName)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 
requestShell(Channel c)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 
msgChannelExtendedData(byte[] msg, int msglen)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 	 */
waitForCondition(Channel c, long timeout, int condition_mask)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 
getAvailable(Channel c, boolean extended)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 
getChannelData(Channel c, boolean extended, byte[] target, int off, int len)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 
msgChannelData(byte[] msg, int msglen)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 
msgChannelWindowAdjust(byte[] msg, int msglen)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 
msgChannelOpen(byte[] msg, int msglen)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 
msgChannelRequest(byte[] msg, int msglen)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 
msgChannelEOF(byte[] msg, int msglen)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 
msgChannelClose(byte[] msg, int msglen)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 
msgChannelSuccess(byte[] msg, int msglen)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 
msgChannelFailure(byte[] msg, int msglen)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 
msgChannelOpenConfirmation(byte[] msg, int msglen)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 
msgChannelOpenFailure(byte[] msg, int msglen)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 
msgGlobalRequest(byte[] msg, int msglen)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 
msgGlobalSuccess()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 
msgGlobalFailure()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 
handleMessage(byte[] msg, int msglen)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