a8290ce57f1a46ee6ab1e106b5a8e9f889db4cc6
[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("shell")))
1423                 {
1424                         if (tr.remain() != 0)
1425                                 throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
1426                         
1427                         Runnable run_after_sending_success = null;
1428                         ServerSessionCallback sscb = server_session.getServerSessionCallback();
1429
1430                         if (sscb != null)
1431                                 run_after_sending_success = sscb.requestShell(server_session);
1432
1433                         if (wantReply)
1434                         {
1435                                 if (run_after_sending_success != null)
1436                                 {
1437                                         tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
1438                                 }
1439                                 else
1440                                 {
1441                                         tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
1442                                 }
1443                         }
1444                         
1445                         if (run_after_sending_success != null)
1446                         {
1447                                 runAsync(run_after_sending_success);
1448                         }
1449                         
1450                         return;
1451                 }
1452                 
1453                 if ((server_session != null) && (type.equals("exec")))
1454                 {
1455                         String command = tr.readString();
1456                         
1457                         if (tr.remain() != 0)
1458                                 throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
1459                         
1460                         Runnable run_after_sending_success = null;
1461                         ServerSessionCallback sscb = server_session.getServerSessionCallback();
1462
1463                         if (sscb != null)
1464                                 run_after_sending_success = sscb.requestExec(server_session, command);
1465
1466                         if (wantReply)
1467                         {
1468                                 if (run_after_sending_success != null)
1469                                 {
1470                                         tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
1471                                 }
1472                                 else
1473                                 {
1474                                         tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
1475                                 }
1476                         }
1477                         
1478                         if (run_after_sending_success != null)
1479                         {
1480                                 runAsync(run_after_sending_success);
1481                         }
1482                         
1483                         return;
1484                 }
1485
1486                 /* We simply ignore unknown channel requests, however, if the server wants a reply,
1487                  * then we signal that we have no idea what it is about.
1488                  */
1489
1490                 if (wantReply)
1491                 {
1492                         tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
1493                 }
1494
1495                 log.debug("Channel request '" + type + "' is not known, ignoring it");
1496         }
1497
1498         public void msgChannelEOF(byte[] msg, int msglen) throws IOException
1499         {
1500                 if (msglen != 5)
1501                         throw new IOException("SSH_MSG_CHANNEL_EOF message has wrong size (" + msglen + ")");
1502
1503                 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1504
1505                 Channel c = getChannel(id);
1506
1507                 if (c == null)
1508                         throw new IOException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id);
1509
1510                 synchronized (c)
1511                 {
1512                         c.EOF = true;
1513                         c.notifyAll();
1514                 }
1515
1516                 log.debug("Got SSH_MSG_CHANNEL_EOF (channel " + id + ")");
1517         }
1518
1519         public void msgChannelClose(byte[] msg, int msglen) throws IOException
1520         {
1521                 if (msglen != 5)
1522                         throw new IOException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msglen + ")");
1523
1524                 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1525
1526                 Channel c = getChannel(id);
1527
1528                 if (c == null)
1529                         throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id);
1530
1531                 synchronized (c)
1532                 {
1533                         c.EOF = true;
1534                         c.state = Channel.STATE_CLOSED;
1535                         c.setReasonClosed("Close requested by remote");
1536                         c.closeMessageRecv = true;
1537
1538                         removeChannel(c.localID);
1539
1540                         c.notifyAll();
1541                 }
1542
1543                 log.debug("Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")");
1544         }
1545
1546         public void msgChannelSuccess(byte[] msg, int msglen) throws IOException
1547         {
1548                 if (msglen != 5)
1549                         throw new IOException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msglen + ")");
1550
1551                 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1552
1553                 Channel c = getChannel(id);
1554
1555                 if (c == null)
1556                         throw new IOException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id);
1557
1558                 synchronized (c)
1559                 {
1560                         c.successCounter++;
1561                         c.notifyAll();
1562                 }
1563
1564                 log.debug("Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")");
1565         }
1566
1567         public void msgChannelFailure(byte[] msg, int msglen) throws IOException
1568         {
1569                 if (msglen != 5)
1570                         throw new IOException("SSH_MSG_CHANNEL_FAILURE message has wrong size (" + msglen + ")");
1571
1572                 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1573
1574                 Channel c = getChannel(id);
1575
1576                 if (c == null)
1577                         throw new IOException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id);
1578
1579                 synchronized (c)
1580                 {
1581                         c.failedCounter++;
1582                         c.notifyAll();
1583                 }
1584
1585                 log.debug("Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")");
1586         }
1587
1588         public void msgChannelOpenConfirmation(byte[] msg, int msglen) throws IOException
1589         {
1590                 PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg, 0, msglen);
1591
1592                 Channel c = getChannel(sm.recipientChannelID);
1593
1594                 if (c == null)
1595                         throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel "
1596                                         + sm.recipientChannelID);
1597
1598                 synchronized (c)
1599                 {
1600                         if (c.state != Channel.STATE_OPENING)
1601                                 throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel "
1602                                                 + sm.recipientChannelID);
1603
1604                         c.remoteID = sm.senderChannelID;
1605                         c.remoteWindow = sm.initialWindowSize & 0xFFFFffffL; /* convert UINT32 to long */
1606                         c.remoteMaxPacketSize = sm.maxPacketSize;
1607                         c.state = Channel.STATE_OPEN;
1608                         c.notifyAll();
1609                 }
1610
1611                 log.debug("Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.recipientChannelID + " / remote: "
1612                                 + sm.senderChannelID + ")");
1613         }
1614
1615         public void msgChannelOpenFailure(byte[] msg, int msglen) throws IOException
1616         {
1617                 if (msglen < 5)
1618                         throw new IOException("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (" + msglen + ")");
1619
1620                 TypesReader tr = new TypesReader(msg, 0, msglen);
1621
1622                 tr.readByte(); // skip packet type
1623                 int id = tr.readUINT32(); /* sender channel */
1624
1625                 Channel c = getChannel(id);
1626
1627                 if (c == null)
1628                         throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id);
1629
1630                 int reasonCode = tr.readUINT32();
1631                 String description = tr.readString("UTF-8");
1632
1633                 String reasonCodeSymbolicName = null;
1634
1635                 switch (reasonCode)
1636                 {
1637                         case 1:
1638                                 reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED";
1639                                 break;
1640                         case 2:
1641                                 reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED";
1642                                 break;
1643                         case 3:
1644                                 reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE";
1645                                 break;
1646                         case 4:
1647                                 reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE";
1648                                 break;
1649                         default:
1650                                 reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")";
1651                 }
1652
1653                 StringBuilder descriptionBuffer = new StringBuilder();
1654                 descriptionBuffer.append(description);
1655
1656                 for (int i = 0; i < descriptionBuffer.length(); i++)
1657                 {
1658                         char cc = descriptionBuffer.charAt(i);
1659
1660                         if ((cc >= 32) && (cc <= 126))
1661                                 continue;
1662                         descriptionBuffer.setCharAt(i, '\uFFFD');
1663                 }
1664
1665                 synchronized (c)
1666                 {
1667                         c.EOF = true;
1668                         c.state = Channel.STATE_CLOSED;
1669                         c.setReasonClosed("The server refused to open the channel (" + reasonCodeSymbolicName + ", '"
1670                                         + descriptionBuffer.toString() + "')");
1671                         c.notifyAll();
1672                 }
1673
1674                 log.debug("Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")");
1675         }
1676
1677         public void msgGlobalRequest(byte[] msg, int msglen) throws IOException
1678         {
1679                 /* Currently we do not support any kind of global request */
1680
1681                 TypesReader tr = new TypesReader(msg, 0, msglen);
1682
1683                 tr.readByte(); // skip packet type
1684                 String requestName = tr.readString();
1685                 boolean wantReply = tr.readBoolean();
1686
1687                 if (wantReply)
1688                 {
1689                         byte[] reply_failure = new byte[1];
1690                         reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE;
1691
1692                         tm.sendAsynchronousMessage(reply_failure);
1693                 }
1694
1695                 /* We do not clean up the requestName String - that is OK for debug */
1696
1697                 log.debug("Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")");
1698         }
1699
1700         public void msgGlobalSuccess() throws IOException
1701         {
1702                 synchronized (channels)
1703                 {
1704                         globalSuccessCounter++;
1705                         channels.notifyAll();
1706                 }
1707
1708                 log.debug("Got SSH_MSG_REQUEST_SUCCESS");
1709         }
1710
1711         public void msgGlobalFailure() throws IOException
1712         {
1713                 synchronized (channels)
1714                 {
1715                         globalFailedCounter++;
1716                         channels.notifyAll();
1717                 }
1718
1719                 log.debug("Got SSH_MSG_REQUEST_FAILURE");
1720         }
1721
1722         public void handleMessage(byte[] msg, int msglen) throws IOException
1723         {
1724                 if (msg == null)
1725                 {
1726
1727                         log.debug("HandleMessage: got shutdown");
1728
1729                         synchronized (listenerThreads)
1730                         {
1731                                 for (IChannelWorkerThread lat : listenerThreads)
1732                                 {
1733                                         lat.stopWorking();
1734                                 }
1735                                 listenerThreadsAllowed = false;
1736                         }
1737
1738                         synchronized (channels)
1739                         {
1740                                 shutdown = true;
1741
1742                                 for (Channel c : channels)
1743                                 {
1744                                         synchronized (c)
1745                                         {
1746                                                 c.EOF = true;
1747                                                 c.state = Channel.STATE_CLOSED;
1748                                                 c.setReasonClosed("The connection is being shutdown");
1749                                                 c.closeMessageRecv = true; /*
1750                                                                                                         * You never know, perhaps
1751                                                                                                         * we are waiting for a
1752                                                                                                         * pending close message
1753                                                                                                         * from the server...
1754                                                                                                         */
1755                                                 c.notifyAll();
1756                                         }
1757                                 }
1758
1759                                 channels.clear();
1760                                 channels.notifyAll(); /* Notify global response waiters */
1761                                 return;
1762                         }
1763                 }
1764
1765                 switch (msg[0])
1766                 {
1767                         case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
1768                                 msgChannelOpenConfirmation(msg, msglen);
1769                                 break;
1770                         case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST:
1771                                 msgChannelWindowAdjust(msg, msglen);
1772                                 break;
1773                         case Packets.SSH_MSG_CHANNEL_DATA:
1774                                 msgChannelData(msg, msglen);
1775                                 break;
1776                         case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA:
1777                                 msgChannelExtendedData(msg, msglen);
1778                                 break;
1779                         case Packets.SSH_MSG_CHANNEL_REQUEST:
1780                                 msgChannelRequest(msg, msglen);
1781                                 break;
1782                         case Packets.SSH_MSG_CHANNEL_EOF:
1783                                 msgChannelEOF(msg, msglen);
1784                                 break;
1785                         case Packets.SSH_MSG_CHANNEL_OPEN:
1786                                 msgChannelOpen(msg, msglen);
1787                                 break;
1788                         case Packets.SSH_MSG_CHANNEL_CLOSE:
1789                                 msgChannelClose(msg, msglen);
1790                                 break;
1791                         case Packets.SSH_MSG_CHANNEL_SUCCESS:
1792                                 msgChannelSuccess(msg, msglen);
1793                                 break;
1794                         case Packets.SSH_MSG_CHANNEL_FAILURE:
1795                                 msgChannelFailure(msg, msglen);
1796                                 break;
1797                         case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE:
1798                                 msgChannelOpenFailure(msg, msglen);
1799                                 break;
1800                         case Packets.SSH_MSG_GLOBAL_REQUEST:
1801                                 msgGlobalRequest(msg, msglen);
1802                                 break;
1803                         case Packets.SSH_MSG_REQUEST_SUCCESS:
1804                                 msgGlobalSuccess();
1805                                 break;
1806                         case Packets.SSH_MSG_REQUEST_FAILURE:
1807                                 msgGlobalFailure();
1808                                 break;
1809                         default:
1810                                 throw new IOException("Cannot handle unknown channel message " + (msg[0] & 0xff));
1811                 }
1812         }
1813 }