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