comparison src/ch/ethz/ssh2/channel/ChannelManager.java @ 273:91a31873c42a ganymed

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