comparison src/com/trilead/ssh2/channel/ChannelManager.java @ 0:0ce5cc452d02

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