comparison src/ch/ethz/ssh2/channel/ChannelManager.java @ 342:175c7d68f3c4

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