comparison app/src/main/java/ch/ethz/ssh2/channel/ChannelManager.java @ 438:d29cce60f393

migrate from Eclipse to Android Studio
author Carl Byington <carl@five-ten-sg.com>
date Thu, 03 Dec 2015 11:23:55 -0800
parents src/ch/ethz/ssh2/channel/ChannelManager.java@f6d26c5f878e
children
comparison
equal deleted inserted replaced
437:208b31032318 438:d29cce60f393
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
614 public void requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol,
615 String x11AuthenticationCookie, int x11ScreenNumber) throws IOException {
616 PacketSessionX11Request psr;
617
618 synchronized (c) {
619 if (c.state != Channel.STATE_OPEN) {
620 throw c.getReasonClosed();
621 }
622
623 psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol,
624 x11AuthenticationCookie, x11ScreenNumber);
625 c.successCounter = c.failedCounter = 0;
626 }
627
628 synchronized (c.channelSendLock) {
629 if (c.closeMessageSent) {
630 throw c.getReasonClosed();
631 }
632
633 tm.sendMessage(psr.getPayload());
634 }
635
636 log.debug("Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")");
637
638 try {
639 waitForChannelSuccessOrFailure(c);
640 }
641 catch (IOException e) {
642 throw new IOException("The X11 request failed.", e);
643 }
644 }
645
646 public void requestSubSystem(Channel c, String subSystemName) throws IOException {
647 PacketSessionSubsystemRequest ssr;
648
649 synchronized (c) {
650 if (c.state != Channel.STATE_OPEN) {
651 throw c.getReasonClosed();
652 }
653
654 ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName);
655 c.successCounter = c.failedCounter = 0;
656 }
657
658 synchronized (c.channelSendLock) {
659 if (c.closeMessageSent) {
660 throw c.getReasonClosed();
661 }
662
663 tm.sendMessage(ssr.getPayload());
664 }
665
666 try {
667 waitForChannelSuccessOrFailure(c);
668 }
669 catch (IOException e) {
670 throw new IOException("The subsystem request failed.", e);
671 }
672 }
673
674 public void requestExecCommand(Channel c, String cmd) throws IOException {
675 this.requestExecCommand(c, cmd, null);
676 }
677
678 /**
679 * @param charsetName The charset used to convert between Java Unicode Strings and byte encodings
680 */
681 public void requestExecCommand(Channel c, String cmd, String charsetName) throws IOException {
682 PacketSessionExecCommand sm;
683
684 synchronized (c) {
685 if (c.state != Channel.STATE_OPEN) {
686 throw c.getReasonClosed();
687 }
688
689 sm = new PacketSessionExecCommand(c.remoteID, true, cmd, charsetName);
690 c.successCounter = c.failedCounter = 0;
691 }
692
693 synchronized (c.channelSendLock) {
694 if (c.closeMessageSent) {
695 throw c.getReasonClosed();
696 }
697
698 tm.sendMessage(sm.getPayload());
699 }
700
701 log.debug("Executing command (channel " + c.localID + ", '" + cmd + "')");
702
703 try {
704 waitForChannelSuccessOrFailure(c);
705 }
706 catch (IOException e) {
707 throw new IOException("The execute request failed.", e);
708 }
709 }
710
711 public void requestShell(Channel c) throws IOException {
712 PacketSessionStartShell sm;
713
714 synchronized (c) {
715 if (c.state != Channel.STATE_OPEN) {
716 throw c.getReasonClosed();
717 }
718
719 sm = new PacketSessionStartShell(c.remoteID, true);
720 c.successCounter = c.failedCounter = 0;
721 }
722
723 synchronized (c.channelSendLock) {
724 if (c.closeMessageSent) {
725 throw c.getReasonClosed();
726 }
727
728 tm.sendMessage(sm.getPayload());
729 }
730
731 try {
732 waitForChannelSuccessOrFailure(c);
733 }
734 catch (IOException e) {
735 throw new IOException("The shell request failed.", e);
736 }
737 }
738
739 public void msgChannelExtendedData(byte[] msg) throws IOException {
740 if (msg.length <= 13) {
741 throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (%d)", msg.length));
742 }
743
744 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
745 int dataType = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
746 int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16) | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff);
747 Channel c = getChannel(id);
748
749 if (c == null) {
750 throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id);
751 }
752
753 if (dataType != Packets.SSH_EXTENDED_DATA_STDERR) {
754 throw new PacketFormatException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")");
755 }
756
757 if (len != (msg.length - 13)) {
758 throw new PacketFormatException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msg.length - 13)
759 + ", got " + len + ")");
760 }
761
762 log.debug("Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel " + id + ", " + len + ")");
763
764 synchronized (c) {
765 if (c.state == Channel.STATE_CLOSED) {
766 return; // ignore
767 }
768
769 if (c.state != Channel.STATE_OPEN) {
770 throw new PacketTypeException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state ("
771 + c.state + ")");
772 }
773
774 if (c.localWindow < len) {
775 throw new PacketFormatException("Remote sent too much data, does not fit into window.");
776 }
777
778 c.localWindow -= len;
779 System.arraycopy(msg, 13, c.stderrBuffer, c.stderrWritepos, len);
780 c.stderrWritepos += len;
781 c.notifyAll();
782 }
783 }
784
785 /**
786 * Wait until for a condition.
787 *
788 * @param c Channel
789 * @param timeout in ms, 0 means no timeout.
790 * @param condition_mask minimum event mask (at least one of the conditions must be fulfilled)
791 * @return all current events
792 */
793 public int waitForCondition(Channel c, long timeout, int condition_mask) throws IOException {
794 long end_time = 0;
795 boolean end_time_set = false;
796
797 synchronized (c) {
798 while (true) {
799 int current_cond = 0;
800 int stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
801 int stderrAvail = c.stderrWritepos - c.stderrReadpos;
802
803 if (stdoutAvail > 0) {
804 current_cond = current_cond | ChannelCondition.STDOUT_DATA;
805 }
806
807 if (stderrAvail > 0) {
808 current_cond = current_cond | ChannelCondition.STDERR_DATA;
809 }
810
811 if (c.EOF) {
812 current_cond = current_cond | ChannelCondition.EOF;
813 }
814
815 if (c.getExitStatus() != null) {
816 current_cond = current_cond | ChannelCondition.EXIT_STATUS;
817 }
818
819 if (c.getExitSignal() != null) {
820 current_cond = current_cond | ChannelCondition.EXIT_SIGNAL;
821 }
822
823 if (c.state == Channel.STATE_CLOSED) {
824 return current_cond | ChannelCondition.CLOSED | ChannelCondition.EOF;
825 }
826
827 if ((current_cond & condition_mask) != 0) {
828 return current_cond;
829 }
830
831 if (timeout > 0) {
832 if (!end_time_set) {
833 end_time = System.currentTimeMillis() + timeout;
834 end_time_set = true;
835 }
836 else {
837 timeout = end_time - System.currentTimeMillis();
838
839 if (timeout <= 0) {
840 return current_cond | ChannelCondition.TIMEOUT;
841 }
842 }
843 }
844
845 try {
846 if (timeout > 0) {
847 c.wait(timeout);
848 }
849 else {
850 c.wait();
851 }
852 }
853 catch (InterruptedException e) {
854 throw new InterruptedIOException(e.getMessage());
855 }
856 }
857 }
858 }
859
860 public int getAvailable(Channel c, boolean extended) throws IOException {
861 synchronized (c) {
862 int avail;
863
864 if (extended) {
865 avail = c.stderrWritepos - c.stderrReadpos;
866 }
867 else {
868 avail = c.stdoutWritepos - c.stdoutReadpos;
869 }
870
871 return ((avail > 0) ? avail : (c.EOF ? -1 : 0));
872 }
873 }
874
875 public int getChannelData(Channel c, boolean extended, byte[] target, int off, int len) throws IOException {
876 int copylen = 0;
877 int increment = 0;
878 int remoteID = 0;
879 int localID = 0;
880
881 synchronized (c) {
882 int stdoutAvail = 0;
883 int stderrAvail = 0;
884
885 while (true) {
886 /*
887 * Data available? We have to return remaining data even if the
888 * channel is already closed.
889 */
890 stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
891 stderrAvail = c.stderrWritepos - c.stderrReadpos;
892
893 if ((!extended) && (stdoutAvail != 0)) {
894 break;
895 }
896
897 if ((extended) && (stderrAvail != 0)) {
898 break;
899 }
900
901 /* Do not wait if more data will never arrive (EOF or CLOSED) */
902
903 if ((c.EOF) || (c.state != Channel.STATE_OPEN)) {
904 return -1;
905 }
906
907 try {
908 c.wait();
909 }
910 catch (InterruptedException e) {
911 throw new InterruptedIOException(e.getMessage());
912 }
913 }
914
915 /* OK, there is some data. Return it. */
916
917 if (!extended) {
918 copylen = (stdoutAvail > len) ? len : stdoutAvail;
919 System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, target, off, copylen);
920 c.stdoutReadpos += copylen;
921
922 if (c.stdoutReadpos != c.stdoutWritepos) {
923 System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, c.stdoutBuffer, 0, c.stdoutWritepos
924 - c.stdoutReadpos);
925 }
926
927 c.stdoutWritepos -= c.stdoutReadpos;
928 c.stdoutReadpos = 0;
929 }
930 else {
931 copylen = (stderrAvail > len) ? len : stderrAvail;
932 System.arraycopy(c.stderrBuffer, c.stderrReadpos, target, off, copylen);
933 c.stderrReadpos += copylen;
934
935 if (c.stderrReadpos != c.stderrWritepos) {
936 System.arraycopy(c.stderrBuffer, c.stderrReadpos, c.stderrBuffer, 0, c.stderrWritepos
937 - c.stderrReadpos);
938 }
939
940 c.stderrWritepos -= c.stderrReadpos;
941 c.stderrReadpos = 0;
942 }
943
944 if (c.state != Channel.STATE_OPEN) {
945 return copylen;
946 }
947
948 if (c.localWindow < ((Channel.CHANNEL_BUFFER_SIZE + 1) / 2)) {
949 int minFreeSpace = Math.min(Channel.CHANNEL_BUFFER_SIZE - c.stdoutWritepos,
950 Channel.CHANNEL_BUFFER_SIZE - c.stderrWritepos);
951 increment = minFreeSpace - c.localWindow;
952 c.localWindow = minFreeSpace;
953 }
954
955 remoteID = c.remoteID; /* read while holding the lock */
956 localID = c.localID; /* read while holding the lock */
957 }
958
959 /*
960 * If a consumer reads stdout and stdin in parallel, we may end up with
961 * sending two msgWindowAdjust messages. Luckily, it
962 * does not matter in which order they arrive at the server.
963 */
964
965 if (increment > 0) {
966 log.debug("Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")");
967
968 synchronized (c.channelSendLock) {
969 byte[] msg = c.msgWindowAdjust;
970 msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
971 msg[1] = (byte)(remoteID >> 24);
972 msg[2] = (byte)(remoteID >> 16);
973 msg[3] = (byte)(remoteID >> 8);
974 msg[4] = (byte)(remoteID);
975 msg[5] = (byte)(increment >> 24);
976 msg[6] = (byte)(increment >> 16);
977 msg[7] = (byte)(increment >> 8);
978 msg[8] = (byte)(increment);
979
980 if (!c.closeMessageSent) {
981 tm.sendMessage(msg);
982 }
983 }
984 }
985
986 return copylen;
987 }
988
989 public void msgChannelData(byte[] msg) throws IOException {
990 if (msg.length <= 9) {
991 throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_DATA message has wrong size (%d)", msg.length));
992 }
993
994 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
995 int len = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
996 Channel c = getChannel(id);
997
998 if (c == null) {
999 throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel " + id);
1000 }
1001
1002 if (len != (msg.length - 9)) {
1003 throw new PacketFormatException("SSH_MSG_CHANNEL_DATA message has wrong len (calculated " + (msg.length - 9) + ", got "
1004 + len + ")");
1005 }
1006
1007 log.debug("Got SSH_MSG_CHANNEL_DATA (channel " + id + ", " + len + ")");
1008
1009 synchronized (c) {
1010 if (c.state == Channel.STATE_CLOSED) {
1011 return; // ignore
1012 }
1013
1014 if (c.state != Channel.STATE_OPEN) {
1015 throw new PacketTypeException("Got SSH_MSG_CHANNEL_DATA, but channel is not in correct state (" + c.state + ")");
1016 }
1017
1018 if (c.localWindow < len) {
1019 throw new IOException("Remote sent too much data, does not fit into window.");
1020 }
1021
1022 c.localWindow -= len;
1023 System.arraycopy(msg, 9, c.stdoutBuffer, c.stdoutWritepos, len);
1024 c.stdoutWritepos += len;
1025 c.notifyAll();
1026 }
1027 }
1028
1029 public void msgChannelWindowAdjust(byte[] msg) throws IOException {
1030 if (msg.length != 9) {
1031 throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size (%d)", msg.length));
1032 }
1033
1034 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1035 int windowChange = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
1036 Channel c = getChannel(id);
1037
1038 if (c == null) {
1039 throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_WINDOW_ADJUST message for non-existent channel " + id);
1040 }
1041
1042 synchronized (c) {
1043 final long huge = 0xFFFFffffL; /* 2^32 - 1 */
1044 c.remoteWindow += (windowChange & huge); /* avoid sign extension */
1045
1046 /* TODO - is this a good heuristic? */
1047
1048 if ((c.remoteWindow > huge)) {
1049 c.remoteWindow = huge;
1050 }
1051
1052 c.notifyAll();
1053 }
1054
1055 log.debug("Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + id + ", " + windowChange + ")");
1056 }
1057
1058 public void msgChannelOpen(byte[] msg) throws IOException {
1059 TypesReader tr = new TypesReader(msg);
1060 tr.readByte(); // skip packet type
1061 String channelType = tr.readString();
1062 int remoteID = tr.readUINT32(); /* sender channel */
1063 int remoteWindow = tr.readUINT32(); /* initial window size */
1064 int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */
1065
1066 if ("x11".equals(channelType)) {
1067 synchronized (x11_magic_cookies) {
1068 /* If we did not request X11 forwarding, then simply ignore this bogus request. */
1069 if (x11_magic_cookies.size() == 0) {
1070 PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
1071 Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "X11 forwarding not activated", "");
1072 tm.sendAsynchronousMessage(pcof.getPayload());
1073 log.warning("Unexpected X11 request, denying it!");
1074 return;
1075 }
1076 }
1077
1078 String remoteOriginatorAddress = tr.readString();
1079 int remoteOriginatorPort = tr.readUINT32();
1080 Channel c = new Channel(this);
1081
1082 synchronized (c) {
1083 c.remoteID = remoteID;
1084 c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */
1085 c.remoteMaxPacketSize = remoteMaxPacketSize;
1086 c.localID = addChannel(c);
1087 }
1088
1089 /*
1090 * The open confirmation message will be sent from another thread
1091 */
1092 RemoteX11AcceptThread rxat = new RemoteX11AcceptThread(c, remoteOriginatorAddress, remoteOriginatorPort);
1093 rxat.setDaemon(true);
1094 rxat.start();
1095 return;
1096 }
1097
1098 if ("forwarded-tcpip".equals(channelType)) {
1099 String remoteConnectedAddress = tr.readString(); /* address that was connected */
1100 int remoteConnectedPort = tr.readUINT32(); /* port that was connected */
1101 String remoteOriginatorAddress = tr.readString(); /* originator IP address */
1102 int remoteOriginatorPort = tr.readUINT32(); /* originator port */
1103 RemoteForwardingData rfd;
1104
1105 synchronized (remoteForwardings) {
1106 rfd = remoteForwardings.get(remoteConnectedPort);
1107 }
1108
1109 if (rfd == null) {
1110 PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
1111 Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
1112 "No thanks, unknown port in forwarded-tcpip request", "");
1113 /* Always try to be polite. */
1114 tm.sendAsynchronousMessage(pcof.getPayload());
1115 log.debug("Unexpected forwarded-tcpip request, denying it!");
1116 return;
1117 }
1118
1119 Channel c = new Channel(this);
1120
1121 synchronized (c) {
1122 c.remoteID = remoteID;
1123 c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
1124 c.remoteMaxPacketSize = remoteMaxPacketSize;
1125 c.localID = addChannel(c);
1126 }
1127
1128 /*
1129 * The open confirmation message will be sent from another thread.
1130 */
1131 RemoteAcceptThread rat = new RemoteAcceptThread(c, remoteConnectedAddress, remoteConnectedPort,
1132 remoteOriginatorAddress, remoteOriginatorPort, rfd.targetAddress, rfd.targetPort);
1133 rat.setDaemon(true);
1134 rat.start();
1135 return;
1136 }
1137
1138 if ((server_state != null) && ("session".equals(channelType))) {
1139 ServerConnectionCallback cb;
1140
1141 synchronized (server_state) {
1142 cb = server_state.cb_conn;
1143 }
1144
1145 if (cb == null) {
1146 tm.sendAsynchronousMessage(new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
1147 "Sessions are currently not enabled", "en").getPayload());
1148 return;
1149 }
1150
1151 final Channel c = new Channel(this);
1152
1153 synchronized (c) {
1154 c.remoteID = remoteID;
1155 c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
1156 c.remoteMaxPacketSize = remoteMaxPacketSize;
1157 c.localID = addChannel(c);
1158 c.state = Channel.STATE_OPEN;
1159 c.ss = new ServerSessionImpl(c);
1160 }
1161
1162 PacketChannelOpenConfirmation pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID,
1163 c.localWindow, c.localMaxPacketSize);
1164 tm.sendAsynchronousMessage(pcoc.getPayload());
1165 c.ss.sscb = cb.acceptSession(c.ss);
1166 return;
1167 }
1168
1169 /* Tell the server that we have no idea what it is talking about */
1170 PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
1171 "Unknown channel type", "");
1172 tm.sendAsynchronousMessage(pcof.getPayload());
1173 log.warning("The peer tried to open an unsupported channel type (" + channelType + ")");
1174 }
1175
1176 /* Starts the given runnable in a foreground (non-daemon) thread */
1177 private void runAsync(Runnable r) {
1178 Thread t = new Thread(r);
1179 t.start();
1180 }
1181
1182 public void msgChannelRequest(byte[] msg) throws IOException {
1183 TypesReader tr = new TypesReader(msg);
1184 tr.readByte(); // skip packet type
1185 int id = tr.readUINT32();
1186 Channel c = getChannel(id);
1187
1188 if (c == null) {
1189 throw new IOException("Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id);
1190 }
1191
1192 ServerSessionImpl server_session = null;
1193
1194 if (server_state != null) {
1195 synchronized (c) {
1196 server_session = c.ss;
1197 }
1198 }
1199
1200 String type = tr.readString("US-ASCII");
1201 boolean wantReply = tr.readBoolean();
1202 log.debug("Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')");
1203
1204 if (type.equals("exit-status")) {
1205 if (wantReply) {
1206 throw new IOException(
1207 "Badly formatted SSH_MSG_CHANNEL_REQUEST exit-status message, 'want reply' is true");
1208 }
1209
1210 int exit_status = tr.readUINT32();
1211
1212 if (tr.remain() != 0) {
1213 throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
1214 }
1215
1216 synchronized (c) {
1217 c.exit_status = exit_status;
1218 c.notifyAll();
1219 }
1220
1221 log.debug("Got EXIT STATUS (channel " + id + ", status " + exit_status + ")");
1222 return;
1223 }
1224
1225 if ((server_state == null) && (type.equals("exit-signal"))) {
1226 if (wantReply) {
1227 throw new IOException(
1228 "Badly formatted SSH_MSG_CHANNEL_REQUEST exit-signal message, 'want reply' is true");
1229 }
1230
1231 String signame = tr.readString("US-ASCII");
1232 tr.readBoolean();
1233 tr.readString();
1234 tr.readString();
1235
1236 if (tr.remain() != 0) {
1237 throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
1238 }
1239
1240 synchronized (c) {
1241 c.exit_signal = signame;
1242 c.notifyAll();
1243 }
1244
1245 log.debug("Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")");
1246 return;
1247 }
1248
1249 if ((server_session != null) && (type.equals("pty-req"))) {
1250 PtySettings pty = new PtySettings();
1251 pty.term = tr.readString();
1252 pty.term_width_characters = tr.readUINT32();
1253 pty.term_height_characters = tr.readUINT32();
1254 pty.term_width_pixels = tr.readUINT32();
1255 pty.term_height_pixels = tr.readUINT32();
1256 pty.terminal_modes = tr.readByteString();
1257
1258 if (tr.remain() != 0) {
1259 throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
1260 }
1261
1262 Runnable run_after_sending_success = null;
1263 ServerSessionCallback sscb = server_session.getServerSessionCallback();
1264
1265 if (sscb != null) {
1266 run_after_sending_success = sscb.requestPtyReq(server_session, pty);
1267 }
1268
1269 if (wantReply) {
1270 if (run_after_sending_success != null) {
1271 tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
1272 }
1273 else {
1274 tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
1275 }
1276 }
1277
1278 if (run_after_sending_success != null) {
1279 runAsync(run_after_sending_success);
1280 }
1281
1282 return;
1283 }
1284
1285 if ((server_session != null) && (type.equals("shell"))) {
1286 if (tr.remain() != 0) {
1287 throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
1288 }
1289
1290 Runnable run_after_sending_success = null;
1291 ServerSessionCallback sscb = server_session.getServerSessionCallback();
1292
1293 if (sscb != null) {
1294 run_after_sending_success = sscb.requestShell(server_session);
1295 }
1296
1297 if (wantReply) {
1298 if (run_after_sending_success != null) {
1299 tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
1300 }
1301 else {
1302 tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
1303 }
1304 }
1305
1306 if (run_after_sending_success != null) {
1307 runAsync(run_after_sending_success);
1308 }
1309
1310 return;
1311 }
1312
1313 if ((server_session != null) && (type.equals("exec"))) {
1314 String command = tr.readString();
1315
1316 if (tr.remain() != 0) {
1317 throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
1318 }
1319
1320 Runnable run_after_sending_success = null;
1321 ServerSessionCallback sscb = server_session.getServerSessionCallback();
1322
1323 if (sscb != null) {
1324 run_after_sending_success = sscb.requestExec(server_session, command);
1325 }
1326
1327 if (wantReply) {
1328 if (run_after_sending_success != null) {
1329 tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
1330 }
1331 else {
1332 tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
1333 }
1334 }
1335
1336 if (run_after_sending_success != null) {
1337 runAsync(run_after_sending_success);
1338 }
1339
1340 return;
1341 }
1342
1343 /* We simply ignore unknown channel requests, however, if the server wants a reply,
1344 * then we signal that we have no idea what it is about.
1345 */
1346
1347 if (wantReply) {
1348 tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
1349 }
1350
1351 log.debug("Channel request '" + type + "' is not known, ignoring it");
1352 }
1353
1354 public void msgChannelEOF(byte[] msg) throws IOException {
1355 if (msg.length != 5) {
1356 throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_EOF message has wrong size (%d)", msg.length));
1357 }
1358
1359 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1360 Channel c = getChannel(id);
1361
1362 if (c == null) {
1363 throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id);
1364 }
1365
1366 synchronized (c) {
1367 c.EOF = true;
1368 c.notifyAll();
1369 }
1370
1371 log.debug("Got SSH_MSG_CHANNEL_EOF (channel " + id + ")");
1372 }
1373
1374 public void msgChannelClose(byte[] msg) throws IOException {
1375 if (msg.length != 5) {
1376 throw new PacketFormatException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msg.length + ")");
1377 }
1378
1379 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1380 Channel c = getChannel(id);
1381
1382 if (c == null) {
1383 throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id);
1384 }
1385
1386 synchronized (c) {
1387 c.EOF = true;
1388 c.state = Channel.STATE_CLOSED;
1389 c.setReasonClosed(new ChannelClosedException("Close requested by remote"));
1390 c.closeMessageRecv = true;
1391 removeChannel(c.localID);
1392 c.notifyAll();
1393 }
1394
1395 log.debug("Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")");
1396 }
1397
1398 public void msgChannelSuccess(byte[] msg) throws IOException {
1399 if (msg.length != 5) {
1400 throw new PacketFormatException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msg.length + ")");
1401 }
1402
1403 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1404 Channel c = getChannel(id);
1405
1406 if (c == null) {
1407 throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id);
1408 }
1409
1410 synchronized (c) {
1411 c.successCounter++;
1412 c.notifyAll();
1413 }
1414
1415 log.debug("Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")");
1416 }
1417
1418 public void msgChannelFailure(byte[] msg) throws IOException {
1419 if (msg.length != 5) {
1420 throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_FAILURE message has wrong size (%d)", msg.length));
1421 }
1422
1423 int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
1424 Channel c = getChannel(id);
1425
1426 if (c == null) {
1427 throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id);
1428 }
1429
1430 synchronized (c) {
1431 c.failedCounter++;
1432 c.notifyAll();
1433 }
1434
1435 log.debug("Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")");
1436 }
1437
1438 public void msgChannelOpenConfirmation(byte[] msg) throws IOException {
1439 PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg);
1440 Channel c = getChannel(sm.getRecipientChannelID());
1441
1442 if (c == null) {
1443 throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel "
1444 + sm.getRecipientChannelID());
1445 }
1446
1447 synchronized (c) {
1448 if (c.state != Channel.STATE_OPENING) {
1449 throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel "
1450 + sm.getRecipientChannelID());
1451 }
1452
1453 c.remoteID = sm.getSenderChannelID();
1454 c.remoteWindow = sm.getInitialWindowSize() & 0xFFFFffffL; /* convert UINT32 to long */
1455 c.remoteMaxPacketSize = sm.getMaxPacketSize();
1456 c.state = Channel.STATE_OPEN;
1457 c.notifyAll();
1458 }
1459
1460 log.debug("Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.getRecipientChannelID() + " / remote: "
1461 + sm.getSenderChannelID() + ")");
1462 }
1463
1464 public void msgChannelOpenFailure(byte[] msg) throws IOException {
1465 if (msg.length < 5) {
1466 throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (%d)", msg.length));
1467 }
1468
1469 TypesReader tr = new TypesReader(msg);
1470 tr.readByte(); // skip packet type
1471 int id = tr.readUINT32(); /* sender channel */
1472 Channel c = getChannel(id);
1473
1474 if (c == null) {
1475 throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id);
1476 }
1477
1478 int reasonCode = tr.readUINT32();
1479 String description = tr.readString("UTF-8");
1480 String reasonCodeSymbolicName;
1481
1482 switch (reasonCode) {
1483 case 1:
1484 reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED";
1485 break;
1486
1487 case 2:
1488 reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED";
1489 break;
1490
1491 case 3:
1492 reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE";
1493 break;
1494
1495 case 4:
1496 reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE";
1497 break;
1498
1499 default:
1500 reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")";
1501 }
1502
1503 StringBuilder descriptionBuffer = new StringBuilder();
1504 descriptionBuffer.append(description);
1505
1506 for (int i = 0; i < descriptionBuffer.length(); i++) {
1507 char cc = descriptionBuffer.charAt(i);
1508
1509 if ((cc >= 32) && (cc <= 126)) {
1510 continue;
1511 }
1512
1513 descriptionBuffer.setCharAt(i, '\uFFFD');
1514 }
1515
1516 synchronized (c) {
1517 c.EOF = true;
1518 c.state = Channel.STATE_CLOSED;
1519 c.setReasonClosed(new ChannelClosedException(String.format("The server refused to open the channel (%s, '%s')",
1520 reasonCodeSymbolicName, descriptionBuffer.toString())));
1521 c.notifyAll();
1522 }
1523
1524 log.debug("Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")");
1525 }
1526
1527 public void msgGlobalRequest(byte[] msg) throws IOException {
1528 /* Currently we do not support any kind of global request */
1529 TypesReader tr = new TypesReader(msg);
1530 tr.readByte(); // skip packet type
1531 String requestName = tr.readString();
1532 boolean wantReply = tr.readBoolean();
1533
1534 if (wantReply) {
1535 byte[] reply_failure = new byte[1];
1536 reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE;
1537 tm.sendAsynchronousMessage(reply_failure);
1538 }
1539
1540 /* We do not clean up the requestName String - that is OK for debug */
1541 log.debug("Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")");
1542 }
1543
1544 public void msgGlobalSuccess() throws IOException {
1545 synchronized (channels) {
1546 globalSuccessCounter++;
1547 channels.notifyAll();
1548 }
1549
1550 log.debug("Got SSH_MSG_REQUEST_SUCCESS");
1551 }
1552
1553 public void msgGlobalFailure() throws IOException {
1554 synchronized (channels) {
1555 globalFailedCounter++;
1556 channels.notifyAll();
1557 }
1558
1559 log.debug("Got SSH_MSG_REQUEST_FAILURE");
1560 }
1561
1562 public void handleFailure(final IOException failure) {
1563 log.debug("HandleMessage: got shutdown");
1564
1565 synchronized (listenerThreads) {
1566 for (IChannelWorkerThread lat : listenerThreads) {
1567 lat.stopWorking();
1568 }
1569
1570 listenerThreadsAllowed = false;
1571 }
1572
1573 synchronized (channels) {
1574 shutdown = true;
1575
1576 for (Channel c : channels) {
1577 synchronized (c) {
1578 c.EOF = true;
1579 c.state = Channel.STATE_CLOSED;
1580 c.setReasonClosed(failure);
1581 c.closeMessageRecv = true;
1582 c.notifyAll();
1583 }
1584 }
1585
1586 channels.clear();
1587 channels.notifyAll(); /* Notify global response waiters */
1588 }
1589 }
1590
1591 public void handleMessage(byte[] msg) throws IOException {
1592 switch (msg[0]) {
1593 case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
1594 msgChannelOpenConfirmation(msg);
1595 break;
1596
1597 case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST:
1598 msgChannelWindowAdjust(msg);
1599 break;
1600
1601 case Packets.SSH_MSG_CHANNEL_DATA:
1602 msgChannelData(msg);
1603 break;
1604
1605 case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA:
1606 msgChannelExtendedData(msg);
1607 break;
1608
1609 case Packets.SSH_MSG_CHANNEL_REQUEST:
1610 msgChannelRequest(msg);
1611 break;
1612
1613 case Packets.SSH_MSG_CHANNEL_EOF:
1614 msgChannelEOF(msg);
1615 break;
1616
1617 case Packets.SSH_MSG_CHANNEL_OPEN:
1618 msgChannelOpen(msg);
1619 break;
1620
1621 case Packets.SSH_MSG_CHANNEL_CLOSE:
1622 msgChannelClose(msg);
1623 break;
1624
1625 case Packets.SSH_MSG_CHANNEL_SUCCESS:
1626 msgChannelSuccess(msg);
1627 break;
1628
1629 case Packets.SSH_MSG_CHANNEL_FAILURE:
1630 msgChannelFailure(msg);
1631 break;
1632
1633 case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE:
1634 msgChannelOpenFailure(msg);
1635 break;
1636
1637 case Packets.SSH_MSG_GLOBAL_REQUEST:
1638 msgGlobalRequest(msg);
1639 break;
1640
1641 case Packets.SSH_MSG_REQUEST_SUCCESS:
1642 msgGlobalSuccess();
1643 break;
1644
1645 case Packets.SSH_MSG_REQUEST_FAILURE:
1646 msgGlobalFailure();
1647 break;
1648
1649 default:
1650 throw new PacketTypeException(msg[0]);
1651 }
1652 }
1653 }