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