1/*
2 * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
3 * Please refer to the LICENSE.txt for licensing details.
4 */
5
6package ch.ethz.ssh2.channel;
7
8import java.io.IOException;
9import java.util.HashMap;
10import java.util.List;
11import java.util.Vector;
12
13import ch.ethz.ssh2.ChannelCondition;
14import ch.ethz.ssh2.log.Logger;
15import ch.ethz.ssh2.packets.PacketChannelOpenConfirmation;
16import ch.ethz.ssh2.packets.PacketChannelOpenFailure;
17import ch.ethz.ssh2.packets.PacketGlobalCancelForwardRequest;
18import ch.ethz.ssh2.packets.PacketGlobalForwardRequest;
19import ch.ethz.ssh2.packets.PacketOpenDirectTCPIPChannel;
20import ch.ethz.ssh2.packets.PacketOpenSessionChannel;
21import ch.ethz.ssh2.packets.PacketSessionExecCommand;
22import ch.ethz.ssh2.packets.PacketSessionPtyRequest;
23import ch.ethz.ssh2.packets.PacketSessionStartShell;
24import ch.ethz.ssh2.packets.PacketSessionSubsystemRequest;
25import ch.ethz.ssh2.packets.PacketSessionX11Request;
26import ch.ethz.ssh2.packets.Packets;
27import ch.ethz.ssh2.packets.TypesReader;
28import ch.ethz.ssh2.transport.MessageHandler;
29import 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 */
39public 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