[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/phpseclib/phpseclib/phpseclib/Net/ -> SSH2.php (source)

   1  <?php
   2  
   3  /**
   4   * Pure-PHP implementation of SSHv2.
   5   *
   6   * PHP version 5
   7   *
   8   * Here are some examples of how to use this library:
   9   * <code>
  10   * <?php
  11   *    include 'vendor/autoload.php';
  12   *
  13   *    $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
  14   *    if (!$ssh->login('username', 'password')) {
  15   *        exit('Login Failed');
  16   *    }
  17   *
  18   *    echo $ssh->exec('pwd');
  19   *    echo $ssh->exec('ls -la');
  20   * ?>
  21   * </code>
  22   *
  23   * <code>
  24   * <?php
  25   *    include 'vendor/autoload.php';
  26   *
  27   *    $key = new \phpseclib\Crypt\RSA();
  28   *    //$key->setPassword('whatever');
  29   *    $key->loadKey(file_get_contents('privatekey'));
  30   *
  31   *    $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
  32   *    if (!$ssh->login('username', $key)) {
  33   *        exit('Login Failed');
  34   *    }
  35   *
  36   *    echo $ssh->read('username@username:~$');
  37   *    $ssh->write("ls -la\n");
  38   *    echo $ssh->read('username@username:~$');
  39   * ?>
  40   * </code>
  41   *
  42   * @category  Net
  43   * @package   SSH2
  44   * @author    Jim Wigginton <terrafrost@php.net>
  45   * @copyright 2007 Jim Wigginton
  46   * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  47   * @link      http://phpseclib.sourceforge.net
  48   */
  49  
  50  namespace phpseclib\Net;
  51  
  52  use phpseclib\Crypt\Base;
  53  use phpseclib\Crypt\Blowfish;
  54  use phpseclib\Crypt\Hash;
  55  use phpseclib\Crypt\Random;
  56  use phpseclib\Crypt\RC4;
  57  use phpseclib\Crypt\Rijndael;
  58  use phpseclib\Crypt\RSA;
  59  use phpseclib\Crypt\TripleDES;
  60  use phpseclib\Crypt\Twofish;
  61  use phpseclib\Math\BigInteger; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
  62  use phpseclib\System\SSH\Agent;
  63  
  64  /**
  65   * Pure-PHP implementation of SSHv2.
  66   *
  67   * @package SSH2
  68   * @author  Jim Wigginton <terrafrost@php.net>
  69   * @access  public
  70   */
  71  class SSH2
  72  {
  73      /**#@+
  74       * Execution Bitmap Masks
  75       *
  76       * @see \phpseclib\Net\SSH2::bitmap
  77       * @access private
  78       */
  79      const MASK_CONSTRUCTOR   = 0x00000001;
  80      const MASK_CONNECTED     = 0x00000002;
  81      const MASK_LOGIN_REQ     = 0x00000004;
  82      const MASK_LOGIN         = 0x00000008;
  83      const MASK_SHELL         = 0x00000010;
  84      const MASK_WINDOW_ADJUST = 0x00000020;
  85      /**#@-*/
  86  
  87      /**#@+
  88       * Channel constants
  89       *
  90       * RFC4254 refers not to client and server channels but rather to sender and recipient channels.  we don't refer
  91       * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
  92       * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
  93       * recepient channel.  at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
  94       * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet:
  95       *     The 'recipient channel' is the channel number given in the original
  96       *     open request, and 'sender channel' is the channel number allocated by
  97       *     the other side.
  98       *
  99       * @see \phpseclib\Net\SSH2::_send_channel_packet()
 100       * @see \phpseclib\Net\SSH2::_get_channel_packet()
 101       * @access private
 102      */
 103      const CHANNEL_EXEC          = 1; // PuTTy uses 0x100
 104      const CHANNEL_SHELL         = 2;
 105      const CHANNEL_SUBSYSTEM     = 3;
 106      const CHANNEL_AGENT_FORWARD = 4;
 107      const CHANNEL_KEEP_ALIVE    = 5;
 108      /**#@-*/
 109  
 110      /**#@+
 111       * @access public
 112       * @see \phpseclib\Net\SSH2::getLog()
 113      */
 114      /**
 115       * Returns the message numbers
 116       */
 117      const LOG_SIMPLE = 1;
 118      /**
 119       * Returns the message content
 120       */
 121      const LOG_COMPLEX = 2;
 122      /**
 123       * Outputs the content real-time
 124       */
 125      const LOG_REALTIME = 3;
 126      /**
 127       * Dumps the content real-time to a file
 128       */
 129      const LOG_REALTIME_FILE = 4;
 130      /**
 131       * Make sure that the log never gets larger than this
 132       */
 133      const LOG_MAX_SIZE = 1048576; // 1024 * 1024
 134      /**#@-*/
 135  
 136      /**#@+
 137       * @access public
 138       * @see \phpseclib\Net\SSH2::read()
 139      */
 140      /**
 141       * Returns when a string matching $expect exactly is found
 142       */
 143      const READ_SIMPLE = 1;
 144      /**
 145       * Returns when a string matching the regular expression $expect is found
 146       */
 147      const READ_REGEX = 2;
 148      /**
 149       * Returns whenever a data packet is received.
 150       *
 151       * Some data packets may only contain a single character so it may be necessary
 152       * to call read() multiple times when using this option
 153       */
 154      const READ_NEXT = 3;
 155      /**#@-*/
 156  
 157      /**
 158       * The SSH identifier
 159       *
 160       * @var string
 161       * @access private
 162       */
 163      var $identifier;
 164  
 165      /**
 166       * The Socket Object
 167       *
 168       * @var object
 169       * @access private
 170       */
 171      var $fsock;
 172  
 173      /**
 174       * Execution Bitmap
 175       *
 176       * The bits that are set represent functions that have been called already.  This is used to determine
 177       * if a requisite function has been successfully executed.  If not, an error should be thrown.
 178       *
 179       * @var int
 180       * @access private
 181       */
 182      var $bitmap = 0;
 183  
 184      /**
 185       * Error information
 186       *
 187       * @see self::getErrors()
 188       * @see self::getLastError()
 189       * @var string
 190       * @access private
 191       */
 192      var $errors = array();
 193  
 194      /**
 195       * Server Identifier
 196       *
 197       * @see self::getServerIdentification()
 198       * @var array|false
 199       * @access private
 200       */
 201      var $server_identifier = false;
 202  
 203      /**
 204       * Key Exchange Algorithms
 205       *
 206       * @see self::getKexAlgorithims()
 207       * @var array|false
 208       * @access private
 209       */
 210      var $kex_algorithms = false;
 211  
 212      /**
 213       * Key Exchange Algorithm
 214       *
 215       * @see self::getMethodsNegotiated()
 216       * @var string|false
 217       * @access private
 218       */
 219      var $kex_algorithm = false;
 220  
 221      /**
 222       * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
 223       *
 224       * @see self::_key_exchange()
 225       * @var int
 226       * @access private
 227       */
 228      var $kex_dh_group_size_min = 1536;
 229  
 230      /**
 231       * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
 232       *
 233       * @see self::_key_exchange()
 234       * @var int
 235       * @access private
 236       */
 237      var $kex_dh_group_size_preferred = 2048;
 238  
 239      /**
 240       * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
 241       *
 242       * @see self::_key_exchange()
 243       * @var int
 244       * @access private
 245       */
 246      var $kex_dh_group_size_max = 4096;
 247  
 248      /**
 249       * Server Host Key Algorithms
 250       *
 251       * @see self::getServerHostKeyAlgorithms()
 252       * @var array|false
 253       * @access private
 254       */
 255      var $server_host_key_algorithms = false;
 256  
 257      /**
 258       * Encryption Algorithms: Client to Server
 259       *
 260       * @see self::getEncryptionAlgorithmsClient2Server()
 261       * @var array|false
 262       * @access private
 263       */
 264      var $encryption_algorithms_client_to_server = false;
 265  
 266      /**
 267       * Encryption Algorithms: Server to Client
 268       *
 269       * @see self::getEncryptionAlgorithmsServer2Client()
 270       * @var array|false
 271       * @access private
 272       */
 273      var $encryption_algorithms_server_to_client = false;
 274  
 275      /**
 276       * MAC Algorithms: Client to Server
 277       *
 278       * @see self::getMACAlgorithmsClient2Server()
 279       * @var array|false
 280       * @access private
 281       */
 282      var $mac_algorithms_client_to_server = false;
 283  
 284      /**
 285       * MAC Algorithms: Server to Client
 286       *
 287       * @see self::getMACAlgorithmsServer2Client()
 288       * @var array|false
 289       * @access private
 290       */
 291      var $mac_algorithms_server_to_client = false;
 292  
 293      /**
 294       * Compression Algorithms: Client to Server
 295       *
 296       * @see self::getCompressionAlgorithmsClient2Server()
 297       * @var array|false
 298       * @access private
 299       */
 300      var $compression_algorithms_client_to_server = false;
 301  
 302      /**
 303       * Compression Algorithms: Server to Client
 304       *
 305       * @see self::getCompressionAlgorithmsServer2Client()
 306       * @var array|false
 307       * @access private
 308       */
 309      var $compression_algorithms_server_to_client = false;
 310  
 311      /**
 312       * Languages: Server to Client
 313       *
 314       * @see self::getLanguagesServer2Client()
 315       * @var array|false
 316       * @access private
 317       */
 318      var $languages_server_to_client = false;
 319  
 320      /**
 321       * Languages: Client to Server
 322       *
 323       * @see self::getLanguagesClient2Server()
 324       * @var array|false
 325       * @access private
 326       */
 327      var $languages_client_to_server = false;
 328  
 329      /**
 330       * Preferred Algorithms
 331       *
 332       * @see self::setPreferredAlgorithms()
 333       * @var array
 334       * @access private
 335       */
 336      var $preferred = array();
 337  
 338      /**
 339       * Block Size for Server to Client Encryption
 340       *
 341       * "Note that the length of the concatenation of 'packet_length',
 342       *  'padding_length', 'payload', and 'random padding' MUST be a multiple
 343       *  of the cipher block size or 8, whichever is larger.  This constraint
 344       *  MUST be enforced, even when using stream ciphers."
 345       *
 346       *  -- http://tools.ietf.org/html/rfc4253#section-6
 347       *
 348       * @see self::__construct()
 349       * @see self::_send_binary_packet()
 350       * @var int
 351       * @access private
 352       */
 353      var $encrypt_block_size = 8;
 354  
 355      /**
 356       * Block Size for Client to Server Encryption
 357       *
 358       * @see self::__construct()
 359       * @see self::_get_binary_packet()
 360       * @var int
 361       * @access private
 362       */
 363      var $decrypt_block_size = 8;
 364  
 365      /**
 366       * Server to Client Encryption Object
 367       *
 368       * @see self::_get_binary_packet()
 369       * @var object
 370       * @access private
 371       */
 372      var $decrypt = false;
 373  
 374      /**
 375       * Client to Server Encryption Object
 376       *
 377       * @see self::_send_binary_packet()
 378       * @var object
 379       * @access private
 380       */
 381      var $encrypt = false;
 382  
 383      /**
 384       * Client to Server HMAC Object
 385       *
 386       * @see self::_send_binary_packet()
 387       * @var object
 388       * @access private
 389       */
 390      var $hmac_create = false;
 391  
 392      /**
 393       * Server to Client HMAC Object
 394       *
 395       * @see self::_get_binary_packet()
 396       * @var object
 397       * @access private
 398       */
 399      var $hmac_check = false;
 400  
 401      /**
 402       * Size of server to client HMAC
 403       *
 404       * We need to know how big the HMAC will be for the server to client direction so that we know how many bytes to read.
 405       * For the client to server side, the HMAC object will make the HMAC as long as it needs to be.  All we need to do is
 406       * append it.
 407       *
 408       * @see self::_get_binary_packet()
 409       * @var int
 410       * @access private
 411       */
 412      var $hmac_size = false;
 413  
 414      /**
 415       * Server Public Host Key
 416       *
 417       * @see self::getServerPublicHostKey()
 418       * @var string
 419       * @access private
 420       */
 421      var $server_public_host_key;
 422  
 423      /**
 424       * Session identifier
 425       *
 426       * "The exchange hash H from the first key exchange is additionally
 427       *  used as the session identifier, which is a unique identifier for
 428       *  this connection."
 429       *
 430       *  -- http://tools.ietf.org/html/rfc4253#section-7.2
 431       *
 432       * @see self::_key_exchange()
 433       * @var string
 434       * @access private
 435       */
 436      var $session_id = false;
 437  
 438      /**
 439       * Exchange hash
 440       *
 441       * The current exchange hash
 442       *
 443       * @see self::_key_exchange()
 444       * @var string
 445       * @access private
 446       */
 447      var $exchange_hash = false;
 448  
 449      /**
 450       * Message Numbers
 451       *
 452       * @see self::__construct()
 453       * @var array
 454       * @access private
 455       */
 456      var $message_numbers = array();
 457  
 458      /**
 459       * Disconnection Message 'reason codes' defined in RFC4253
 460       *
 461       * @see self::__construct()
 462       * @var array
 463       * @access private
 464       */
 465      var $disconnect_reasons = array();
 466  
 467      /**
 468       * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
 469       *
 470       * @see self::__construct()
 471       * @var array
 472       * @access private
 473       */
 474      var $channel_open_failure_reasons = array();
 475  
 476      /**
 477       * Terminal Modes
 478       *
 479       * @link http://tools.ietf.org/html/rfc4254#section-8
 480       * @see self::__construct()
 481       * @var array
 482       * @access private
 483       */
 484      var $terminal_modes = array();
 485  
 486      /**
 487       * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
 488       *
 489       * @link http://tools.ietf.org/html/rfc4254#section-5.2
 490       * @see self::__construct()
 491       * @var array
 492       * @access private
 493       */
 494      var $channel_extended_data_type_codes = array();
 495  
 496      /**
 497       * Send Sequence Number
 498       *
 499       * See 'Section 6.4.  Data Integrity' of rfc4253 for more info.
 500       *
 501       * @see self::_send_binary_packet()
 502       * @var int
 503       * @access private
 504       */
 505      var $send_seq_no = 0;
 506  
 507      /**
 508       * Get Sequence Number
 509       *
 510       * See 'Section 6.4.  Data Integrity' of rfc4253 for more info.
 511       *
 512       * @see self::_get_binary_packet()
 513       * @var int
 514       * @access private
 515       */
 516      var $get_seq_no = 0;
 517  
 518      /**
 519       * Server Channels
 520       *
 521       * Maps client channels to server channels
 522       *
 523       * @see self::_get_channel_packet()
 524       * @see self::exec()
 525       * @var array
 526       * @access private
 527       */
 528      var $server_channels = array();
 529  
 530      /**
 531       * Channel Buffers
 532       *
 533       * If a client requests a packet from one channel but receives two packets from another those packets should
 534       * be placed in a buffer
 535       *
 536       * @see self::_get_channel_packet()
 537       * @see self::exec()
 538       * @var array
 539       * @access private
 540       */
 541      var $channel_buffers = array();
 542  
 543      /**
 544       * Channel Status
 545       *
 546       * Contains the type of the last sent message
 547       *
 548       * @see self::_get_channel_packet()
 549       * @var array
 550       * @access private
 551       */
 552      var $channel_status = array();
 553  
 554      /**
 555       * Packet Size
 556       *
 557       * Maximum packet size indexed by channel
 558       *
 559       * @see self::_send_channel_packet()
 560       * @var array
 561       * @access private
 562       */
 563      var $packet_size_client_to_server = array();
 564  
 565      /**
 566       * Message Number Log
 567       *
 568       * @see self::getLog()
 569       * @var array
 570       * @access private
 571       */
 572      var $message_number_log = array();
 573  
 574      /**
 575       * Message Log
 576       *
 577       * @see self::getLog()
 578       * @var array
 579       * @access private
 580       */
 581      var $message_log = array();
 582  
 583      /**
 584       * The Window Size
 585       *
 586       * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB)
 587       *
 588       * @var int
 589       * @see self::_send_channel_packet()
 590       * @see self::exec()
 591       * @access private
 592       */
 593      var $window_size = 0x7FFFFFFF;
 594  
 595      /**
 596       * Window size, server to client
 597       *
 598       * Window size indexed by channel
 599       *
 600       * @see self::_send_channel_packet()
 601       * @var array
 602       * @access private
 603       */
 604      var $window_size_server_to_client = array();
 605  
 606      /**
 607       * Window size, client to server
 608       *
 609       * Window size indexed by channel
 610       *
 611       * @see self::_get_channel_packet()
 612       * @var array
 613       * @access private
 614       */
 615      var $window_size_client_to_server = array();
 616  
 617      /**
 618       * Server signature
 619       *
 620       * Verified against $this->session_id
 621       *
 622       * @see self::getServerPublicHostKey()
 623       * @var string
 624       * @access private
 625       */
 626      var $signature = '';
 627  
 628      /**
 629       * Server signature format
 630       *
 631       * ssh-rsa or ssh-dss.
 632       *
 633       * @see self::getServerPublicHostKey()
 634       * @var string
 635       * @access private
 636       */
 637      var $signature_format = '';
 638  
 639      /**
 640       * Interactive Buffer
 641       *
 642       * @see self::read()
 643       * @var array
 644       * @access private
 645       */
 646      var $interactiveBuffer = '';
 647  
 648      /**
 649       * Current log size
 650       *
 651       * Should never exceed self::LOG_MAX_SIZE
 652       *
 653       * @see self::_send_binary_packet()
 654       * @see self::_get_binary_packet()
 655       * @var int
 656       * @access private
 657       */
 658      var $log_size;
 659  
 660      /**
 661       * Timeout
 662       *
 663       * @see self::setTimeout()
 664       * @access private
 665       */
 666      var $timeout;
 667  
 668      /**
 669       * Current Timeout
 670       *
 671       * @see self::_get_channel_packet()
 672       * @access private
 673       */
 674      var $curTimeout;
 675  
 676      /**
 677       * Real-time log file pointer
 678       *
 679       * @see self::_append_log()
 680       * @var resource
 681       * @access private
 682       */
 683      var $realtime_log_file;
 684  
 685      /**
 686       * Real-time log file size
 687       *
 688       * @see self::_append_log()
 689       * @var int
 690       * @access private
 691       */
 692      var $realtime_log_size;
 693  
 694      /**
 695       * Has the signature been validated?
 696       *
 697       * @see self::getServerPublicHostKey()
 698       * @var bool
 699       * @access private
 700       */
 701      var $signature_validated = false;
 702  
 703      /**
 704       * Real-time log file wrap boolean
 705       *
 706       * @see self::_append_log()
 707       * @access private
 708       */
 709      var $realtime_log_wrap;
 710  
 711      /**
 712       * Flag to suppress stderr from output
 713       *
 714       * @see self::enableQuietMode()
 715       * @access private
 716       */
 717      var $quiet_mode = false;
 718  
 719      /**
 720       * Time of first network activity
 721       *
 722       * @var int
 723       * @access private
 724       */
 725      var $last_packet;
 726  
 727      /**
 728       * Exit status returned from ssh if any
 729       *
 730       * @var int
 731       * @access private
 732       */
 733      var $exit_status;
 734  
 735      /**
 736       * Flag to request a PTY when using exec()
 737       *
 738       * @var bool
 739       * @see self::enablePTY()
 740       * @access private
 741       */
 742      var $request_pty = false;
 743  
 744      /**
 745       * Flag set while exec() is running when using enablePTY()
 746       *
 747       * @var bool
 748       * @access private
 749       */
 750      var $in_request_pty_exec = false;
 751  
 752      /**
 753       * Flag set after startSubsystem() is called
 754       *
 755       * @var bool
 756       * @access private
 757       */
 758      var $in_subsystem;
 759  
 760      /**
 761       * Contents of stdError
 762       *
 763       * @var string
 764       * @access private
 765       */
 766      var $stdErrorLog;
 767  
 768      /**
 769       * The Last Interactive Response
 770       *
 771       * @see self::_keyboard_interactive_process()
 772       * @var string
 773       * @access private
 774       */
 775      var $last_interactive_response = '';
 776  
 777      /**
 778       * Keyboard Interactive Request / Responses
 779       *
 780       * @see self::_keyboard_interactive_process()
 781       * @var array
 782       * @access private
 783       */
 784      var $keyboard_requests_responses = array();
 785  
 786      /**
 787       * Banner Message
 788       *
 789       * Quoting from the RFC, "in some jurisdictions, sending a warning message before
 790       * authentication may be relevant for getting legal protection."
 791       *
 792       * @see self::_filter()
 793       * @see self::getBannerMessage()
 794       * @var string
 795       * @access private
 796       */
 797      var $banner_message = '';
 798  
 799      /**
 800       * Did read() timeout or return normally?
 801       *
 802       * @see self::isTimeout()
 803       * @var bool
 804       * @access private
 805       */
 806      var $is_timeout = false;
 807  
 808      /**
 809       * Log Boundary
 810       *
 811       * @see self::_format_log()
 812       * @var string
 813       * @access private
 814       */
 815      var $log_boundary = ':';
 816  
 817      /**
 818       * Log Long Width
 819       *
 820       * @see self::_format_log()
 821       * @var int
 822       * @access private
 823       */
 824      var $log_long_width = 65;
 825  
 826      /**
 827       * Log Short Width
 828       *
 829       * @see self::_format_log()
 830       * @var int
 831       * @access private
 832       */
 833      var $log_short_width = 16;
 834  
 835      /**
 836       * Hostname
 837       *
 838       * @see self::__construct()
 839       * @see self::_connect()
 840       * @var string
 841       * @access private
 842       */
 843      var $host;
 844  
 845      /**
 846       * Port Number
 847       *
 848       * @see self::__construct()
 849       * @see self::_connect()
 850       * @var int
 851       * @access private
 852       */
 853      var $port;
 854  
 855      /**
 856       * Number of columns for terminal window size
 857       *
 858       * @see self::getWindowColumns()
 859       * @see self::setWindowColumns()
 860       * @see self::setWindowSize()
 861       * @var int
 862       * @access private
 863       */
 864      var $windowColumns = 80;
 865  
 866      /**
 867       * Number of columns for terminal window size
 868       *
 869       * @see self::getWindowRows()
 870       * @see self::setWindowRows()
 871       * @see self::setWindowSize()
 872       * @var int
 873       * @access private
 874       */
 875      var $windowRows = 24;
 876  
 877      /**
 878       * Crypto Engine
 879       *
 880       * @see self::setCryptoEngine()
 881       * @see self::_key_exchange()
 882       * @var int
 883       * @access private
 884       */
 885      var $crypto_engine = false;
 886  
 887      /**
 888       * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario
 889       *
 890       * @var System_SSH_Agent
 891       * @access private
 892       */
 893      var $agent;
 894  
 895      /**
 896       * Send the identification string first?
 897       *
 898       * @var bool
 899       * @access private
 900       */
 901      var $send_id_string_first = true;
 902  
 903      /**
 904       * Send the key exchange initiation packet first?
 905       *
 906       * @var bool
 907       * @access private
 908       */
 909      var $send_kex_first = true;
 910  
 911      /**
 912       * Some versions of OpenSSH incorrectly calculate the key size
 913       *
 914       * @var bool
 915       * @access private
 916       */
 917      var $bad_key_size_fix = false;
 918  
 919      /**
 920       * Should we try to re-connect to re-establish keys?
 921       *
 922       * @var bool
 923       * @access private
 924       */
 925      var $retry_connect = false;
 926  
 927      /**
 928       * Binary Packet Buffer
 929       *
 930       * @var string|false
 931       * @access private
 932       */
 933      var $binary_packet_buffer = false;
 934  
 935      /**
 936       * Preferred Signature Format
 937       *
 938       * @var string|false
 939       * @access private
 940       */
 941      var $preferred_signature_format = false;
 942  
 943      /**
 944       * Authentication Credentials
 945       *
 946       * @var array
 947       * @access private
 948       */
 949      var $auth = array();
 950  
 951      /**
 952       * Default Constructor.
 953       *
 954       * $host can either be a string, representing the host, or a stream resource.
 955       *
 956       * @param mixed $host
 957       * @param int $port
 958       * @param int $timeout
 959       * @see self::login()
 960       * @return \phpseclib\Net\SSH2
 961       * @access public
 962       */
 963      function __construct($host, $port = 22, $timeout = 10)
 964      {
 965          $this->message_numbers = array(
 966              1 => 'NET_SSH2_MSG_DISCONNECT',
 967              2 => 'NET_SSH2_MSG_IGNORE',
 968              3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
 969              4 => 'NET_SSH2_MSG_DEBUG',
 970              5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
 971              6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
 972              20 => 'NET_SSH2_MSG_KEXINIT',
 973              21 => 'NET_SSH2_MSG_NEWKEYS',
 974              30 => 'NET_SSH2_MSG_KEXDH_INIT',
 975              31 => 'NET_SSH2_MSG_KEXDH_REPLY',
 976              50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
 977              51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
 978              52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
 979              53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
 980  
 981              80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
 982              81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
 983              82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
 984              90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
 985              91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
 986              92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
 987              93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
 988              94 => 'NET_SSH2_MSG_CHANNEL_DATA',
 989              95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
 990              96 => 'NET_SSH2_MSG_CHANNEL_EOF',
 991              97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
 992              98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
 993              99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
 994              100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
 995          );
 996          $this->disconnect_reasons = array(
 997              1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
 998              2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
 999              3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
1000              4 => 'NET_SSH2_DISCONNECT_RESERVED',
1001              5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
1002              6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
1003              7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
1004              8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
1005              9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
1006              10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
1007              11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
1008              12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
1009              13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
1010              14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
1011              15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
1012          );
1013          $this->channel_open_failure_reasons = array(
1014              1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
1015          );
1016          $this->terminal_modes = array(
1017              0 => 'NET_SSH2_TTY_OP_END'
1018          );
1019          $this->channel_extended_data_type_codes = array(
1020              1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
1021          );
1022  
1023          $this->_define_array(
1024              $this->message_numbers,
1025              $this->disconnect_reasons,
1026              $this->channel_open_failure_reasons,
1027              $this->terminal_modes,
1028              $this->channel_extended_data_type_codes,
1029              array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
1030              array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'),
1031              array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
1032                    61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'),
1033              // RFC 4419 - diffie-hellman-group-exchange-sha{1,256}
1034              array(30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD',
1035                    31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP',
1036                    32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT',
1037                    33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY',
1038                    34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'),
1039              // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org)
1040              array(30 => 'NET_SSH2_MSG_KEX_ECDH_INIT',
1041                    31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY')
1042          );
1043  
1044          if (is_resource($host)) {
1045              $this->fsock = $host;
1046              return;
1047          }
1048  
1049          if (is_string($host)) {
1050              $this->host = $host;
1051              $this->port = $port;
1052              $this->timeout = $timeout;
1053          }
1054      }
1055  
1056      /**
1057       * Set Crypto Engine Mode
1058       *
1059       * Possible $engine values:
1060       * CRYPT_MODE_INTERNAL, CRYPT_MODE_MCRYPT
1061       *
1062       * @param int $engine
1063       * @access public
1064       */
1065      function setCryptoEngine($engine)
1066      {
1067          $this->crypto_engine = $engine;
1068      }
1069  
1070      /**
1071       * Send Identification String First
1072       *
1073       * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
1074       * both sides MUST send an identification string". It does not say which side sends it first. In
1075       * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
1076       *
1077       * @access public
1078       */
1079      function sendIdentificationStringFirst()
1080      {
1081          $this->send_id_string_first = true;
1082      }
1083  
1084      /**
1085       * Send Identification String Last
1086       *
1087       * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
1088       * both sides MUST send an identification string". It does not say which side sends it first. In
1089       * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
1090       *
1091       * @access public
1092       */
1093      function sendIdentificationStringLast()
1094      {
1095          $this->send_id_string_first = false;
1096      }
1097  
1098      /**
1099       * Send SSH_MSG_KEXINIT First
1100       *
1101       * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
1102       * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
1103       * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
1104       *
1105       * @access public
1106       */
1107      function sendKEXINITFirst()
1108      {
1109          $this->send_kex_first = true;
1110      }
1111  
1112      /**
1113       * Send SSH_MSG_KEXINIT Last
1114       *
1115       * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
1116       * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
1117       * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
1118       *
1119       * @access public
1120       */
1121      function sendKEXINITLast()
1122      {
1123          $this->send_kex_first = false;
1124      }
1125  
1126      /**
1127       * Connect to an SSHv2 server
1128       *
1129       * @return bool
1130       * @access private
1131       */
1132      function _connect()
1133      {
1134          if ($this->bitmap & self::MASK_CONSTRUCTOR) {
1135              return false;
1136          }
1137  
1138          $this->bitmap |= self::MASK_CONSTRUCTOR;
1139  
1140          $this->curTimeout = $this->timeout;
1141  
1142          $this->last_packet = microtime(true);
1143  
1144          if (!is_resource($this->fsock)) {
1145              $start = microtime(true);
1146              // with stream_select a timeout of 0 means that no timeout takes place;
1147              // with fsockopen a timeout of 0 means that you instantly timeout
1148              // to resolve this incompatibility a timeout of 100,000 will be used for fsockopen if timeout is 0
1149              $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout == 0 ? 100000 : $this->curTimeout);
1150              if (!$this->fsock) {
1151                  $host = $this->host . ':' . $this->port;
1152                  user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"));
1153                  return false;
1154              }
1155              $elapsed = microtime(true) - $start;
1156  
1157              if ($this->curTimeout) {
1158                  $this->curTimeout-= $elapsed;
1159                  if ($this->curTimeout < 0) {
1160                      $this->is_timeout = true;
1161                      return false;
1162                  }
1163              }
1164          }
1165  
1166          $this->identifier = $this->_generate_identifier();
1167  
1168          if ($this->send_id_string_first) {
1169              fputs($this->fsock, $this->identifier . "\r\n");
1170          }
1171  
1172          /* According to the SSH2 specs,
1173  
1174            "The server MAY send other lines of data before sending the version
1175             string.  Each line SHOULD be terminated by a Carriage Return and Line
1176             Feed.  Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
1177             in ISO-10646 UTF-8 [RFC3629] (language is not specified).  Clients
1178             MUST be able to process such lines." */
1179          $data = '';
1180          while (!feof($this->fsock) && !preg_match('#(.*)^(SSH-(\d\.\d+).*)#ms', $data, $matches)) {
1181              $line = '';
1182              while (true) {
1183                  if ($this->curTimeout) {
1184                      if ($this->curTimeout < 0) {
1185                          $this->is_timeout = true;
1186                          return false;
1187                      }
1188                      $read = array($this->fsock);
1189                      $write = $except = null;
1190                      $start = microtime(true);
1191                      $sec = floor($this->curTimeout);
1192                      $usec = 1000000 * ($this->curTimeout - $sec);
1193                      // on windows this returns a "Warning: Invalid CRT parameters detected" error
1194                      // the !count() is done as a workaround for <https://bugs.php.net/42682>
1195                      if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
1196                          $this->is_timeout = true;
1197                          return false;
1198                      }
1199                      $elapsed = microtime(true) - $start;
1200                      $this->curTimeout-= $elapsed;
1201                  }
1202  
1203                  $temp = stream_get_line($this->fsock, 255, "\n");
1204                  if (strlen($temp) == 255) {
1205                      continue;
1206                  }
1207  
1208                  $line.= "$temp\n";
1209  
1210                  // quoting RFC4253, "Implementers who wish to maintain
1211                  // compatibility with older, undocumented versions of this protocol may
1212                  // want to process the identification string without expecting the
1213                  // presence of the carriage return character for reasons described in
1214                  // Section 5 of this document."
1215  
1216                  //if (substr($line, -2) == "\r\n") {
1217                  //    break;
1218                  //}
1219  
1220                  break;
1221              }
1222  
1223              $data.= $line;
1224          }
1225  
1226          if (feof($this->fsock)) {
1227              $this->bitmap = 0;
1228              user_error('Connection closed by server');
1229              return false;
1230          }
1231  
1232          $extra = $matches[1];
1233  
1234          if (defined('NET_SSH2_LOGGING')) {
1235              $this->_append_log('<-', $matches[0]);
1236              $this->_append_log('->', $this->identifier . "\r\n");
1237          }
1238  
1239          $this->server_identifier = trim($temp, "\r\n");
1240          if (strlen($extra)) {
1241              $this->errors[] = $data;
1242          }
1243  
1244          if (version_compare($matches[3], '1.99', '<')) {
1245              user_error("Cannot connect to SSH $matches[3] servers");
1246              return false;
1247          }
1248  
1249          if (!$this->send_id_string_first) {
1250              fputs($this->fsock, $this->identifier . "\r\n");
1251          }
1252  
1253          if (!$this->send_kex_first) {
1254              $response = $this->_get_binary_packet();
1255              if ($response === false) {
1256                  $this->bitmap = 0;
1257                  user_error('Connection closed by server');
1258                  return false;
1259              }
1260  
1261              if (!strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
1262                  user_error('Expected SSH_MSG_KEXINIT');
1263                  return false;
1264              }
1265  
1266              if (!$this->_key_exchange($response)) {
1267                  return false;
1268              }
1269          }
1270  
1271          if ($this->send_kex_first && !$this->_key_exchange()) {
1272              return false;
1273          }
1274  
1275          $this->bitmap|= self::MASK_CONNECTED;
1276  
1277          return true;
1278      }
1279  
1280      /**
1281       * Generates the SSH identifier
1282       *
1283       * You should overwrite this method in your own class if you want to use another identifier
1284       *
1285       * @access protected
1286       * @return string
1287       */
1288      function _generate_identifier()
1289      {
1290          $identifier = 'SSH-2.0-phpseclib_2.0';
1291  
1292          $ext = array();
1293          if (function_exists('sodium_crypto_box_publickey_from_secretkey')) {
1294              $ext[] = 'libsodium';
1295          }
1296  
1297          if (extension_loaded('openssl')) {
1298              $ext[] = 'openssl';
1299          } elseif (extension_loaded('mcrypt')) {
1300              $ext[] = 'mcrypt';
1301          }
1302  
1303          if (extension_loaded('gmp')) {
1304              $ext[] = 'gmp';
1305          } elseif (extension_loaded('bcmath')) {
1306              $ext[] = 'bcmath';
1307          }
1308  
1309          if (!empty($ext)) {
1310              $identifier .= ' (' . implode(', ', $ext) . ')';
1311          }
1312  
1313          return $identifier;
1314      }
1315  
1316      /**
1317       * Key Exchange
1318       *
1319       * @param string $kexinit_payload_server optional
1320       * @access private
1321       */
1322      function _key_exchange($kexinit_payload_server = false)
1323      {
1324          $preferred = $this->preferred;
1325  
1326          $kex_algorithms = isset($preferred['kex']) ?
1327              $preferred['kex'] :
1328              $this->getSupportedKEXAlgorithms();
1329          $server_host_key_algorithms = isset($preferred['hostkey']) ?
1330              $preferred['hostkey'] :
1331              $this->getSupportedHostKeyAlgorithms();
1332          $s2c_encryption_algorithms = isset($preferred['server_to_client']['crypt']) ?
1333              $preferred['server_to_client']['crypt'] :
1334              $this->getSupportedEncryptionAlgorithms();
1335          $c2s_encryption_algorithms = isset($preferred['client_to_server']['crypt']) ?
1336              $preferred['client_to_server']['crypt'] :
1337              $this->getSupportedEncryptionAlgorithms();
1338          $s2c_mac_algorithms = isset($preferred['server_to_client']['mac']) ?
1339              $preferred['server_to_client']['mac'] :
1340              $this->getSupportedMACAlgorithms();
1341          $c2s_mac_algorithms = isset($preferred['client_to_server']['mac']) ?
1342              $preferred['client_to_server']['mac'] :
1343              $this->getSupportedMACAlgorithms();
1344          $s2c_compression_algorithms = isset($preferred['server_to_client']['comp']) ?
1345              $preferred['server_to_client']['comp'] :
1346              $this->getSupportedCompressionAlgorithms();
1347          $c2s_compression_algorithms = isset($preferred['client_to_server']['comp']) ?
1348              $preferred['client_to_server']['comp'] :
1349              $this->getSupportedCompressionAlgorithms();
1350  
1351          // some SSH servers have buggy implementations of some of the above algorithms
1352          switch (true) {
1353              case $this->server_identifier == 'SSH-2.0-SSHD':
1354              case substr($this->server_identifier, 0, 13) == 'SSH-2.0-DLINK':
1355                  if (!isset($preferred['server_to_client']['mac'])) {
1356                      $s2c_mac_algorithms = array_values(array_diff(
1357                          $s2c_mac_algorithms,
1358                          array('hmac-sha1-96', 'hmac-md5-96')
1359                      ));
1360                  }
1361                  if (!isset($preferred['client_to_server']['mac'])) {
1362                      $c2s_mac_algorithms = array_values(array_diff(
1363                          $c2s_mac_algorithms,
1364                          array('hmac-sha1-96', 'hmac-md5-96')
1365                      ));
1366                  }
1367          }
1368  
1369          $str_kex_algorithms = implode(',', $kex_algorithms);
1370          $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
1371          $encryption_algorithms_server_to_client = implode(',', $s2c_encryption_algorithms);
1372          $encryption_algorithms_client_to_server = implode(',', $c2s_encryption_algorithms);
1373          $mac_algorithms_server_to_client = implode(',', $s2c_mac_algorithms);
1374          $mac_algorithms_client_to_server = implode(',', $c2s_mac_algorithms);
1375          $compression_algorithms_server_to_client = implode(',', $s2c_compression_algorithms);
1376          $compression_algorithms_client_to_server = implode(',', $c2s_compression_algorithms);
1377  
1378          $client_cookie = Random::string(16);
1379  
1380          $kexinit_payload_client = pack(
1381              'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
1382              NET_SSH2_MSG_KEXINIT,
1383              $client_cookie,
1384              strlen($str_kex_algorithms),
1385              $str_kex_algorithms,
1386              strlen($str_server_host_key_algorithms),
1387              $str_server_host_key_algorithms,
1388              strlen($encryption_algorithms_client_to_server),
1389              $encryption_algorithms_client_to_server,
1390              strlen($encryption_algorithms_server_to_client),
1391              $encryption_algorithms_server_to_client,
1392              strlen($mac_algorithms_client_to_server),
1393              $mac_algorithms_client_to_server,
1394              strlen($mac_algorithms_server_to_client),
1395              $mac_algorithms_server_to_client,
1396              strlen($compression_algorithms_client_to_server),
1397              $compression_algorithms_client_to_server,
1398              strlen($compression_algorithms_server_to_client),
1399              $compression_algorithms_server_to_client,
1400              0,
1401              '',
1402              0,
1403              '',
1404              0,
1405              0
1406          );
1407  
1408          if ($this->send_kex_first) {
1409              if (!$this->_send_binary_packet($kexinit_payload_client)) {
1410                  return false;
1411              }
1412  
1413              $kexinit_payload_server = $this->_get_binary_packet();
1414              if ($kexinit_payload_server === false) {
1415                  $this->bitmap = 0;
1416                  user_error('Connection closed by server');
1417                  return false;
1418              }
1419  
1420              if (!strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) {
1421                  user_error('Expected SSH_MSG_KEXINIT');
1422                  return false;
1423              }
1424          }
1425  
1426          $response = $kexinit_payload_server;
1427          $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
1428          $server_cookie = $this->_string_shift($response, 16);
1429  
1430          if (strlen($response) < 4) {
1431              return false;
1432          }
1433          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1434          $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
1435  
1436          if (strlen($response) < 4) {
1437              return false;
1438          }
1439          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1440          $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
1441  
1442          if (strlen($response) < 4) {
1443              return false;
1444          }
1445          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1446          $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
1447  
1448          if (strlen($response) < 4) {
1449              return false;
1450          }
1451          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1452          $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
1453  
1454          if (strlen($response) < 4) {
1455              return false;
1456          }
1457          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1458          $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
1459  
1460          if (strlen($response) < 4) {
1461              return false;
1462          }
1463          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1464          $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
1465  
1466          if (strlen($response) < 4) {
1467              return false;
1468          }
1469          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1470          $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
1471  
1472          if (strlen($response) < 4) {
1473              return false;
1474          }
1475          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1476          $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
1477  
1478          if (strlen($response) < 4) {
1479              return false;
1480          }
1481          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1482          $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
1483  
1484          if (strlen($response) < 4) {
1485              return false;
1486          }
1487          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1488          $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
1489  
1490          if (!strlen($response)) {
1491              return false;
1492          }
1493          extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
1494          $first_kex_packet_follows = $first_kex_packet_follows != 0;
1495  
1496          if (!$this->send_kex_first && !$this->_send_binary_packet($kexinit_payload_client)) {
1497              return false;
1498          }
1499  
1500          // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
1501          // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
1502          // diffie-hellman key exchange as fast as possible
1503          $decrypt = $this->_array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client);
1504          $decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt);
1505          if ($decryptKeyLength === null) {
1506              user_error('No compatible server to client encryption algorithms found');
1507              return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1508          }
1509  
1510          $encrypt = $this->_array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server);
1511          $encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt);
1512          if ($encryptKeyLength === null) {
1513              user_error('No compatible client to server encryption algorithms found');
1514              return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1515          }
1516  
1517          // through diffie-hellman key exchange a symmetric key is obtained
1518          $this->kex_algorithm = $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms);
1519          if ($kex_algorithm === false) {
1520              user_error('No compatible key exchange algorithms found');
1521              return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1522          }
1523  
1524          // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty.
1525          $exchange_hash_rfc4419 = '';
1526  
1527          if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
1528              $x = Random::string(32);
1529              $eBytes = sodium_crypto_box_publickey_from_secretkey($x);
1530              $clientKexInitMessage = NET_SSH2_MSG_KEX_ECDH_INIT;
1531              $serverKexReplyMessage = NET_SSH2_MSG_KEX_ECDH_REPLY;
1532              $kexHash = new Hash('sha256');
1533          } else {
1534              if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
1535                  $dh_group_sizes_packed = pack(
1536                      'NNN',
1537                      $this->kex_dh_group_size_min,
1538                      $this->kex_dh_group_size_preferred,
1539                      $this->kex_dh_group_size_max
1540                  );
1541                  $packet = pack(
1542                      'Ca*',
1543                      NET_SSH2_MSG_KEXDH_GEX_REQUEST,
1544                      $dh_group_sizes_packed
1545                  );
1546                  if (!$this->_send_binary_packet($packet)) {
1547                      return false;
1548                  }
1549  
1550                  $response = $this->_get_binary_packet();
1551                  if ($response === false) {
1552                      $this->bitmap = 0;
1553                      user_error('Connection closed by server');
1554                      return false;
1555                  }
1556                  extract(unpack('Ctype', $this->_string_shift($response, 1)));
1557                  if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) {
1558                      user_error('Expected SSH_MSG_KEX_DH_GEX_GROUP');
1559                      return false;
1560                  }
1561  
1562                  if (strlen($response) < 4) {
1563                      return false;
1564                  }
1565                  extract(unpack('NprimeLength', $this->_string_shift($response, 4)));
1566                  $primeBytes = $this->_string_shift($response, $primeLength);
1567                  $prime = new BigInteger($primeBytes, -256);
1568  
1569                  if (strlen($response) < 4) {
1570                      return false;
1571                  }
1572                  extract(unpack('NgLength', $this->_string_shift($response, 4)));
1573                  $gBytes = $this->_string_shift($response, $gLength);
1574                  $g = new BigInteger($gBytes, -256);
1575  
1576                  $exchange_hash_rfc4419 = pack(
1577                      'a*Na*Na*',
1578                      $dh_group_sizes_packed,
1579                      $primeLength,
1580                      $primeBytes,
1581                      $gLength,
1582                      $gBytes
1583                  );
1584  
1585                  $clientKexInitMessage = NET_SSH2_MSG_KEXDH_GEX_INIT;
1586                  $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_GEX_REPLY;
1587              } else {
1588                  switch ($kex_algorithm) {
1589                      // see http://tools.ietf.org/html/rfc2409#section-6.2 and
1590                      // http://tools.ietf.org/html/rfc2412, appendex E
1591                      case 'diffie-hellman-group1-sha1':
1592                          $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
1593                                  '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
1594                                  '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
1595                                  'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
1596                          break;
1597                      // see http://tools.ietf.org/html/rfc3526#section-3
1598                      case 'diffie-hellman-group14-sha1':
1599                          $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
1600                                  '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
1601                                  '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
1602                                  'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
1603                                  '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
1604                                  '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
1605                                  'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
1606                                  '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
1607                          break;
1608                  }
1609                  // For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1
1610                  // the generator field element is 2 (decimal) and the hash function is sha1.
1611                  $g = new BigInteger(2);
1612                  $prime = new BigInteger($prime, 16);
1613                  $clientKexInitMessage = NET_SSH2_MSG_KEXDH_INIT;
1614                  $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_REPLY;
1615              }
1616  
1617              switch ($kex_algorithm) {
1618                  case 'diffie-hellman-group-exchange-sha256':
1619                      $kexHash = new Hash('sha256');
1620                      break;
1621                  default:
1622                      $kexHash = new Hash('sha1');
1623              }
1624  
1625              /* To increase the speed of the key exchange, both client and server may
1626              reduce the size of their private exponents.  It should be at least
1627              twice as long as the key material that is generated from the shared
1628              secret.  For more details, see the paper by van Oorschot and Wiener
1629              [VAN-OORSCHOT].
1630  
1631              -- http://tools.ietf.org/html/rfc4419#section-6.2 */
1632              $one = new BigInteger(1);
1633              $keyLength = min($kexHash->getLength(), max($encryptKeyLength, $decryptKeyLength));
1634              $max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 * $keyLength
1635              $max = $max->subtract($one);
1636  
1637              $x = $one->random($one, $max);
1638              $e = $g->modPow($x, $prime);
1639  
1640              $eBytes = $e->toBytes(true);
1641          }
1642          $data = pack('CNa*', $clientKexInitMessage, strlen($eBytes), $eBytes);
1643  
1644          if (!$this->_send_binary_packet($data)) {
1645              $this->bitmap = 0;
1646              user_error('Connection closed by server');
1647              return false;
1648          }
1649  
1650          $response = $this->_get_binary_packet();
1651          if ($response === false) {
1652              $this->bitmap = 0;
1653              user_error('Connection closed by server');
1654              return false;
1655          }
1656          if (!strlen($response)) {
1657              return false;
1658          }
1659          extract(unpack('Ctype', $this->_string_shift($response, 1)));
1660  
1661          if ($type != $serverKexReplyMessage) {
1662              user_error('Expected SSH_MSG_KEXDH_REPLY');
1663              return false;
1664          }
1665  
1666          if (strlen($response) < 4) {
1667              return false;
1668          }
1669          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1670          $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);
1671  
1672          if (strlen($server_public_host_key) < 4) {
1673              return false;
1674          }
1675          $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
1676          $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);
1677  
1678          if (strlen($response) < 4) {
1679              return false;
1680          }
1681          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1682          $fBytes = $this->_string_shift($response, $temp['length']);
1683  
1684          if (strlen($response) < 4) {
1685              return false;
1686          }
1687          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1688          $this->signature = $this->_string_shift($response, $temp['length']);
1689  
1690          if (strlen($this->signature) < 4) {
1691              return false;
1692          }
1693          $temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
1694          $this->signature_format = $this->_string_shift($this->signature, $temp['length']);
1695  
1696          if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
1697              if (strlen($fBytes) !== 32) {
1698                  user_error('Received curve25519 public key of invalid length.');
1699                  return false;
1700              }
1701              $key = new BigInteger(sodium_crypto_scalarmult($x, $fBytes), 256);
1702              sodium_memzero($x);
1703          } else {
1704              $f = new BigInteger($fBytes, -256);
1705              $key = $f->modPow($x, $prime);
1706          }
1707          $keyBytes = $key->toBytes(true);
1708  
1709          $this->exchange_hash = pack(
1710              'Na*Na*Na*Na*Na*a*Na*Na*Na*',
1711              strlen($this->identifier),
1712              $this->identifier,
1713              strlen($this->server_identifier),
1714              $this->server_identifier,
1715              strlen($kexinit_payload_client),
1716              $kexinit_payload_client,
1717              strlen($kexinit_payload_server),
1718              $kexinit_payload_server,
1719              strlen($this->server_public_host_key),
1720              $this->server_public_host_key,
1721              $exchange_hash_rfc4419,
1722              strlen($eBytes),
1723              $eBytes,
1724              strlen($fBytes),
1725              $fBytes,
1726              strlen($keyBytes),
1727              $keyBytes
1728          );
1729  
1730          $this->exchange_hash = $kexHash->hash($this->exchange_hash);
1731  
1732          if ($this->session_id === false) {
1733              $this->session_id = $this->exchange_hash;
1734          }
1735  
1736          $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
1737          if ($server_host_key_algorithm === false) {
1738              user_error('No compatible server host key algorithms found');
1739              return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1740          }
1741  
1742          switch ($server_host_key_algorithm) {
1743              case 'ssh-dss':
1744                  $expected_key_format = 'ssh-dss';
1745                  break;
1746              //case 'rsa-sha2-256':
1747              //case 'rsa-sha2-512':
1748              //case 'ssh-rsa':
1749              default:
1750                  $expected_key_format = 'ssh-rsa';
1751          }
1752  
1753          if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) {
1754              switch (true) {
1755                  case $this->signature_format == $server_host_key_algorithm:
1756                  case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512':
1757                  case $this->signature_format != 'ssh-rsa':
1758                      user_error('Server Host Key Algorithm Mismatch');
1759                      return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1760              }
1761          }
1762  
1763          $packet = pack(
1764              'C',
1765              NET_SSH2_MSG_NEWKEYS
1766          );
1767  
1768          if (!$this->_send_binary_packet($packet)) {
1769              return false;
1770          }
1771  
1772          $response = $this->_get_binary_packet();
1773  
1774          if ($response === false) {
1775              $this->bitmap = 0;
1776              user_error('Connection closed by server');
1777              return false;
1778          }
1779  
1780          if (!strlen($response)) {
1781              return false;
1782          }
1783          extract(unpack('Ctype', $this->_string_shift($response, 1)));
1784  
1785          if ($type != NET_SSH2_MSG_NEWKEYS) {
1786              user_error('Expected SSH_MSG_NEWKEYS');
1787              return false;
1788          }
1789  
1790          $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
1791  
1792          $this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt);
1793          if ($this->encrypt) {
1794              if ($this->crypto_engine) {
1795                  $this->encrypt->setPreferredEngine($this->crypto_engine);
1796              }
1797              if ($this->encrypt->block_size) {
1798                  $this->encrypt_block_size = $this->encrypt->block_size;
1799              }
1800              $this->encrypt->enableContinuousBuffer();
1801              $this->encrypt->disablePadding();
1802  
1803              if ($this->encrypt->getBlockLength()) {
1804                  $this->encrypt_block_size = $this->encrypt->getBlockLength() >> 3;
1805              }
1806  
1807              $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id);
1808              while ($this->encrypt_block_size > strlen($iv)) {
1809                  $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
1810              }
1811              $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
1812  
1813              $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id);
1814              while ($encryptKeyLength > strlen($key)) {
1815                  $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
1816              }
1817              $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
1818  
1819              $this->encrypt->name = $decrypt;
1820          }
1821  
1822          $this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt);
1823          if ($this->decrypt) {
1824              if ($this->crypto_engine) {
1825                  $this->decrypt->setPreferredEngine($this->crypto_engine);
1826              }
1827              if ($this->decrypt->block_size) {
1828                  $this->decrypt_block_size = $this->decrypt->block_size;
1829              }
1830              $this->decrypt->enableContinuousBuffer();
1831              $this->decrypt->disablePadding();
1832  
1833              if ($this->decrypt->getBlockLength()) {
1834                  $this->decrypt_block_size = $this->decrypt->getBlockLength() >> 3;
1835              }
1836  
1837              $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id);
1838              while ($this->decrypt_block_size > strlen($iv)) {
1839                  $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
1840              }
1841              $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
1842  
1843              $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id);
1844              while ($decryptKeyLength > strlen($key)) {
1845                  $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
1846              }
1847              $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
1848  
1849              $this->decrypt->name = $decrypt;
1850          }
1851  
1852          /* The "arcfour128" algorithm is the RC4 cipher, as described in
1853             [SCHNEIER], using a 128-bit key.  The first 1536 bytes of keystream
1854             generated by the cipher MUST be discarded, and the first byte of the
1855             first encrypted packet MUST be encrypted using the 1537th byte of
1856             keystream.
1857  
1858             -- http://tools.ietf.org/html/rfc4345#section-4 */
1859          if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
1860              $this->encrypt->encrypt(str_repeat("\0", 1536));
1861          }
1862          if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
1863              $this->decrypt->decrypt(str_repeat("\0", 1536));
1864          }
1865  
1866          $mac_algorithm = $this->_array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server);
1867          if ($mac_algorithm === false) {
1868              user_error('No compatible client to server message authentication algorithms found');
1869              return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1870          }
1871  
1872          $createKeyLength = 0; // ie. $mac_algorithm == 'none'
1873          switch ($mac_algorithm) {
1874              case 'hmac-sha2-256':
1875                  $this->hmac_create = new Hash('sha256');
1876                  $createKeyLength = 32;
1877                  break;
1878              case 'hmac-sha1':
1879                  $this->hmac_create = new Hash('sha1');
1880                  $createKeyLength = 20;
1881                  break;
1882              case 'hmac-sha1-96':
1883                  $this->hmac_create = new Hash('sha1-96');
1884                  $createKeyLength = 20;
1885                  break;
1886              case 'hmac-md5':
1887                  $this->hmac_create = new Hash('md5');
1888                  $createKeyLength = 16;
1889                  break;
1890              case 'hmac-md5-96':
1891                  $this->hmac_create = new Hash('md5-96');
1892                  $createKeyLength = 16;
1893          }
1894          $this->hmac_create->name = $mac_algorithm;
1895  
1896          $mac_algorithm = $this->_array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client);
1897          if ($mac_algorithm === false) {
1898              user_error('No compatible server to client message authentication algorithms found');
1899              return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1900          }
1901  
1902          $checkKeyLength = 0;
1903          $this->hmac_size = 0;
1904          switch ($mac_algorithm) {
1905              case 'hmac-sha2-256':
1906                  $this->hmac_check = new Hash('sha256');
1907                  $checkKeyLength = 32;
1908                  $this->hmac_size = 32;
1909                  break;
1910              case 'hmac-sha1':
1911                  $this->hmac_check = new Hash('sha1');
1912                  $checkKeyLength = 20;
1913                  $this->hmac_size = 20;
1914                  break;
1915              case 'hmac-sha1-96':
1916                  $this->hmac_check = new Hash('sha1-96');
1917                  $checkKeyLength = 20;
1918                  $this->hmac_size = 12;
1919                  break;
1920              case 'hmac-md5':
1921                  $this->hmac_check = new Hash('md5');
1922                  $checkKeyLength = 16;
1923                  $this->hmac_size = 16;
1924                  break;
1925              case 'hmac-md5-96':
1926                  $this->hmac_check = new Hash('md5-96');
1927                  $checkKeyLength = 16;
1928                  $this->hmac_size = 12;
1929          }
1930          $this->hmac_check->name = $mac_algorithm;
1931  
1932          $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id);
1933          while ($createKeyLength > strlen($key)) {
1934              $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
1935          }
1936          $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
1937  
1938          $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id);
1939          while ($checkKeyLength > strlen($key)) {
1940              $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
1941          }
1942          $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
1943  
1944          $compression_algorithm = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
1945          if ($compression_algorithm === false) {
1946              user_error('No compatible client to server compression algorithms found');
1947              return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1948          }
1949          //$this->decompress = $compression_algorithm == 'zlib';
1950  
1951          $compression_algorithm = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_client_to_server);
1952          if ($compression_algorithm === false) {
1953              user_error('No compatible server to client compression algorithms found');
1954              return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1955          }
1956          //$this->compress = $compression_algorithm == 'zlib';
1957  
1958          return true;
1959      }
1960  
1961      /**
1962       * Maps an encryption algorithm name to the number of key bytes.
1963       *
1964       * @param string $algorithm Name of the encryption algorithm
1965       * @return int|null Number of bytes as an integer or null for unknown
1966       * @access private
1967       */
1968      function _encryption_algorithm_to_key_size($algorithm)
1969      {
1970          if ($this->bad_key_size_fix && $this->_bad_algorithm_candidate($algorithm)) {
1971              return 16;
1972          }
1973  
1974          switch ($algorithm) {
1975              case 'none':
1976                  return 0;
1977              case 'aes128-cbc':
1978              case 'aes128-ctr':
1979              case 'arcfour':
1980              case 'arcfour128':
1981              case 'blowfish-cbc':
1982              case 'blowfish-ctr':
1983              case 'twofish128-cbc':
1984              case 'twofish128-ctr':
1985                  return 16;
1986              case '3des-cbc':
1987              case '3des-ctr':
1988              case 'aes192-cbc':
1989              case 'aes192-ctr':
1990              case 'twofish192-cbc':
1991              case 'twofish192-ctr':
1992                  return 24;
1993              case 'aes256-cbc':
1994              case 'aes256-ctr':
1995              case 'arcfour256':
1996              case 'twofish-cbc':
1997              case 'twofish256-cbc':
1998              case 'twofish256-ctr':
1999                  return 32;
2000          }
2001          return null;
2002      }
2003  
2004      /**
2005       * Maps an encryption algorithm name to an instance of a subclass of
2006       * \phpseclib\Crypt\Base.
2007       *
2008       * @param string $algorithm Name of the encryption algorithm
2009       * @return mixed Instance of \phpseclib\Crypt\Base or null for unknown
2010       * @access private
2011       */
2012      function _encryption_algorithm_to_crypt_instance($algorithm)
2013      {
2014          switch ($algorithm) {
2015              case '3des-cbc':
2016                  return new TripleDES();
2017              case '3des-ctr':
2018                  return new TripleDES(Base::MODE_CTR);
2019              case 'aes256-cbc':
2020              case 'aes192-cbc':
2021              case 'aes128-cbc':
2022                  return new Rijndael();
2023              case 'aes256-ctr':
2024              case 'aes192-ctr':
2025              case 'aes128-ctr':
2026                  return new Rijndael(Base::MODE_CTR);
2027              case 'blowfish-cbc':
2028                  return new Blowfish();
2029              case 'blowfish-ctr':
2030                  return new Blowfish(Base::MODE_CTR);
2031              case 'twofish128-cbc':
2032              case 'twofish192-cbc':
2033              case 'twofish256-cbc':
2034              case 'twofish-cbc':
2035                  return new Twofish();
2036              case 'twofish128-ctr':
2037              case 'twofish192-ctr':
2038              case 'twofish256-ctr':
2039                  return new Twofish(Base::MODE_CTR);
2040              case 'arcfour':
2041              case 'arcfour128':
2042              case 'arcfour256':
2043                  return new RC4();
2044          }
2045          return null;
2046      }
2047  
2048      /**
2049       * Tests whether or not proposed algorithm has a potential for issues
2050       *
2051       * @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html
2052       * @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291
2053       * @param string $algorithm Name of the encryption algorithm
2054       * @return bool
2055       * @access private
2056       */
2057      function _bad_algorithm_candidate($algorithm)
2058      {
2059          switch ($algorithm) {
2060              case 'arcfour256':
2061              case 'aes192-ctr':
2062              case 'aes256-ctr':
2063                  return true;
2064          }
2065  
2066          return false;
2067      }
2068  
2069      /**
2070       * Login
2071       *
2072       * The $password parameter can be a plaintext password, a \phpseclib\Crypt\RSA object or an array
2073       *
2074       * @param string $username
2075       * @param mixed $password
2076       * @param mixed $...
2077       * @return bool
2078       * @see self::_login()
2079       * @access public
2080       */
2081      function login($username)
2082      {
2083          $args = func_get_args();
2084          $this->auth[] = $args;
2085          return call_user_func_array(array(&$this, '_login'), $args);
2086      }
2087  
2088      /**
2089       * Login Helper
2090       *
2091       * @param string $username
2092       * @param mixed $password
2093       * @param mixed $...
2094       * @return bool
2095       * @see self::_login_helper()
2096       * @access private
2097       */
2098      function _login($username)
2099      {
2100          if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
2101              if (!$this->_connect()) {
2102                  return false;
2103              }
2104          }
2105  
2106          $args = array_slice(func_get_args(), 1);
2107          if (empty($args)) {
2108              return $this->_login_helper($username);
2109          }
2110  
2111          foreach ($args as $arg) {
2112              if ($this->_login_helper($username, $arg)) {
2113                  return true;
2114              }
2115          }
2116          return false;
2117      }
2118  
2119      /**
2120       * Login Helper
2121       *
2122       * @param string $username
2123       * @param string $password
2124       * @return bool
2125       * @access private
2126       * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
2127       *           by sending dummy SSH_MSG_IGNORE messages.
2128       */
2129      function _login_helper($username, $password = null)
2130      {
2131          if (!($this->bitmap & self::MASK_CONNECTED)) {
2132              return false;
2133          }
2134  
2135          if (!($this->bitmap & self::MASK_LOGIN_REQ)) {
2136              $packet = pack(
2137                  'CNa*',
2138                  NET_SSH2_MSG_SERVICE_REQUEST,
2139                  strlen('ssh-userauth'),
2140                  'ssh-userauth'
2141              );
2142  
2143              if (!$this->_send_binary_packet($packet)) {
2144                  return false;
2145              }
2146  
2147              $response = $this->_get_binary_packet();
2148              if ($response === false) {
2149                  if ($this->retry_connect) {
2150                      $this->retry_connect = false;
2151                      if (!$this->_connect()) {
2152                          return false;
2153                      }
2154                      return $this->_login_helper($username, $password);
2155                  }
2156                  $this->bitmap = 0;
2157                  user_error('Connection closed by server');
2158                  return false;
2159              }
2160  
2161              if (strlen($response) < 4) {
2162                  return false;
2163              }
2164              extract(unpack('Ctype', $this->_string_shift($response, 1)));
2165  
2166              if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
2167                  user_error('Expected SSH_MSG_SERVICE_ACCEPT');
2168                  return false;
2169              }
2170              $this->bitmap |= self::MASK_LOGIN_REQ;
2171          }
2172  
2173          if (strlen($this->last_interactive_response)) {
2174              return !is_string($password) && !is_array($password) ? false : $this->_keyboard_interactive_process($password);
2175          }
2176  
2177          if ($password instanceof RSA) {
2178              return $this->_privatekey_login($username, $password);
2179          } elseif ($password instanceof Agent) {
2180              return $this->_ssh_agent_login($username, $password);
2181          }
2182  
2183          if (is_array($password)) {
2184              if ($this->_keyboard_interactive_login($username, $password)) {
2185                  $this->bitmap |= self::MASK_LOGIN;
2186                  return true;
2187              }
2188              return false;
2189          }
2190  
2191          if (!isset($password)) {
2192              $packet = pack(
2193                  'CNa*Na*Na*',
2194                  NET_SSH2_MSG_USERAUTH_REQUEST,
2195                  strlen($username),
2196                  $username,
2197                  strlen('ssh-connection'),
2198                  'ssh-connection',
2199                  strlen('none'),
2200                  'none'
2201              );
2202  
2203              if (!$this->_send_binary_packet($packet)) {
2204                  return false;
2205              }
2206  
2207              $response = $this->_get_binary_packet();
2208              if ($response === false) {
2209                  $this->bitmap = 0;
2210                  user_error('Connection closed by server');
2211                  return false;
2212              }
2213  
2214              if (!strlen($response)) {
2215                  return false;
2216              }
2217              extract(unpack('Ctype', $this->_string_shift($response, 1)));
2218  
2219              switch ($type) {
2220                  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2221                      $this->bitmap |= self::MASK_LOGIN;
2222                      return true;
2223                  //case NET_SSH2_MSG_USERAUTH_FAILURE:
2224                  default:
2225                      return false;
2226              }
2227          }
2228  
2229          $packet = pack(
2230              'CNa*Na*Na*CNa*',
2231              NET_SSH2_MSG_USERAUTH_REQUEST,
2232              strlen($username),
2233              $username,
2234              strlen('ssh-connection'),
2235              'ssh-connection',
2236              strlen('password'),
2237              'password',
2238              0,
2239              strlen($password),
2240              $password
2241          );
2242  
2243          // remove the username and password from the logged packet
2244          if (!defined('NET_SSH2_LOGGING')) {
2245              $logged = null;
2246          } else {
2247              $logged = pack(
2248                  'CNa*Na*Na*CNa*',
2249                  NET_SSH2_MSG_USERAUTH_REQUEST,
2250                  strlen('username'),
2251                  'username',
2252                  strlen('ssh-connection'),
2253                  'ssh-connection',
2254                  strlen('password'),
2255                  'password',
2256                  0,
2257                  strlen('password'),
2258                  'password'
2259              );
2260          }
2261  
2262          if (!$this->_send_binary_packet($packet, $logged)) {
2263              return false;
2264          }
2265  
2266          $response = $this->_get_binary_packet();
2267          if ($response === false) {
2268              $this->bitmap = 0;
2269              user_error('Connection closed by server');
2270              return false;
2271          }
2272  
2273          if (!strlen($response)) {
2274              return false;
2275          }
2276          extract(unpack('Ctype', $this->_string_shift($response, 1)));
2277  
2278          switch ($type) {
2279              case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
2280                  if (defined('NET_SSH2_LOGGING')) {
2281                      $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ';
2282                  }
2283                  if (strlen($response) < 4) {
2284                      return false;
2285                  }
2286                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2287                  $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $this->_string_shift($response, $length);
2288                  return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
2289              case NET_SSH2_MSG_USERAUTH_FAILURE:
2290                  // can we use keyboard-interactive authentication?  if not then either the login is bad or the server employees
2291                  // multi-factor authentication
2292                  if (strlen($response) < 4) {
2293                      return false;
2294                  }
2295                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2296                  $auth_methods = explode(',', $this->_string_shift($response, $length));
2297                  if (!strlen($response)) {
2298                      return false;
2299                  }
2300                  extract(unpack('Cpartial_success', $this->_string_shift($response, 1)));
2301                  $partial_success = $partial_success != 0;
2302  
2303                  if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) {
2304                      if ($this->_keyboard_interactive_login($username, $password)) {
2305                          $this->bitmap |= self::MASK_LOGIN;
2306                          return true;
2307                      }
2308                      return false;
2309                  }
2310                  return false;
2311              case NET_SSH2_MSG_USERAUTH_SUCCESS:
2312                  $this->bitmap |= self::MASK_LOGIN;
2313                  return true;
2314          }
2315  
2316          return false;
2317      }
2318  
2319      /**
2320       * Login via keyboard-interactive authentication
2321       *
2322       * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details.  This is not a full-featured keyboard-interactive authenticator.
2323       *
2324       * @param string $username
2325       * @param string $password
2326       * @return bool
2327       * @access private
2328       */
2329      function _keyboard_interactive_login($username, $password)
2330      {
2331          $packet = pack(
2332              'CNa*Na*Na*Na*Na*',
2333              NET_SSH2_MSG_USERAUTH_REQUEST,
2334              strlen($username),
2335              $username,
2336              strlen('ssh-connection'),
2337              'ssh-connection',
2338              strlen('keyboard-interactive'),
2339              'keyboard-interactive',
2340              0,
2341              '',
2342              0,
2343              ''
2344          );
2345  
2346          if (!$this->_send_binary_packet($packet)) {
2347              return false;
2348          }
2349  
2350          return $this->_keyboard_interactive_process($password);
2351      }
2352  
2353      /**
2354       * Handle the keyboard-interactive requests / responses.
2355       *
2356       * @param string $responses...
2357       * @return bool
2358       * @access private
2359       */
2360      function _keyboard_interactive_process()
2361      {
2362          $responses = func_get_args();
2363  
2364          if (strlen($this->last_interactive_response)) {
2365              $response = $this->last_interactive_response;
2366          } else {
2367              $orig = $response = $this->_get_binary_packet();
2368              if ($response === false) {
2369                  $this->bitmap = 0;
2370                  user_error('Connection closed by server');
2371                  return false;
2372              }
2373          }
2374  
2375          if (!strlen($response)) {
2376              return false;
2377          }
2378          extract(unpack('Ctype', $this->_string_shift($response, 1)));
2379  
2380          switch ($type) {
2381              case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
2382                  if (strlen($response) < 4) {
2383                      return false;
2384                  }
2385                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2386                  $this->_string_shift($response, $length); // name; may be empty
2387                  if (strlen($response) < 4) {
2388                      return false;
2389                  }
2390                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2391                  $this->_string_shift($response, $length); // instruction; may be empty
2392                  if (strlen($response) < 4) {
2393                      return false;
2394                  }
2395                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2396                  $this->_string_shift($response, $length); // language tag; may be empty
2397                  if (strlen($response) < 4) {
2398                      return false;
2399                  }
2400                  extract(unpack('Nnum_prompts', $this->_string_shift($response, 4)));
2401  
2402                  for ($i = 0; $i < count($responses); $i++) {
2403                      if (is_array($responses[$i])) {
2404                          foreach ($responses[$i] as $key => $value) {
2405                              $this->keyboard_requests_responses[$key] = $value;
2406                          }
2407                          unset($responses[$i]);
2408                      }
2409                  }
2410                  $responses = array_values($responses);
2411  
2412                  if (isset($this->keyboard_requests_responses)) {
2413                      for ($i = 0; $i < $num_prompts; $i++) {
2414                          if (strlen($response) < 4) {
2415                              return false;
2416                          }
2417                          extract(unpack('Nlength', $this->_string_shift($response, 4)));
2418                          // prompt - ie. "Password: "; must not be empty
2419                          $prompt = $this->_string_shift($response, $length);
2420                          //$echo = $this->_string_shift($response) != chr(0);
2421                          foreach ($this->keyboard_requests_responses as $key => $value) {
2422                              if (substr($prompt, 0, strlen($key)) == $key) {
2423                                  $responses[] = $value;
2424                                  break;
2425                              }
2426                          }
2427                      }
2428                  }
2429  
2430                  // see http://tools.ietf.org/html/rfc4256#section-3.2
2431                  if (strlen($this->last_interactive_response)) {
2432                      $this->last_interactive_response = '';
2433                  } elseif (defined('NET_SSH2_LOGGING')) {
2434                      $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
2435                          'UNKNOWN',
2436                          'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
2437                          $this->message_number_log[count($this->message_number_log) - 1]
2438                      );
2439                  }
2440  
2441                  if (!count($responses) && $num_prompts) {
2442                      $this->last_interactive_response = $orig;
2443                      return false;
2444                  }
2445  
2446                  /*
2447                     After obtaining the requested information from the user, the client
2448                     MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
2449                  */
2450                  // see http://tools.ietf.org/html/rfc4256#section-3.4
2451                  $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
2452                  for ($i = 0; $i < count($responses); $i++) {
2453                      $packet.= pack('Na*', strlen($responses[$i]), $responses[$i]);
2454                      $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer');
2455                  }
2456  
2457                  if (!$this->_send_binary_packet($packet, $logged)) {
2458                      return false;
2459                  }
2460  
2461                  if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) {
2462                      $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
2463                          'UNKNOWN',
2464                          'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE',
2465                          $this->message_number_log[count($this->message_number_log) - 1]
2466                      );
2467                  }
2468  
2469                  /*
2470                     After receiving the response, the server MUST send either an
2471                     SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another
2472                     SSH_MSG_USERAUTH_INFO_REQUEST message.
2473                  */
2474                  // maybe phpseclib should force close the connection after x request / responses?  unless something like that is done
2475                  // there could be an infinite loop of request / responses.
2476                  return $this->_keyboard_interactive_process();
2477              case NET_SSH2_MSG_USERAUTH_SUCCESS:
2478                  return true;
2479              case NET_SSH2_MSG_USERAUTH_FAILURE:
2480                  return false;
2481          }
2482  
2483          return false;
2484      }
2485  
2486      /**
2487       * Login with an ssh-agent provided key
2488       *
2489       * @param string $username
2490       * @param \phpseclib\System\SSH\Agent $agent
2491       * @return bool
2492       * @access private
2493       */
2494      function _ssh_agent_login($username, $agent)
2495      {
2496          $this->agent = $agent;
2497          $keys = $agent->requestIdentities();
2498          foreach ($keys as $key) {
2499              if ($this->_privatekey_login($username, $key)) {
2500                  return true;
2501              }
2502          }
2503  
2504          return false;
2505      }
2506  
2507      /**
2508       * Login with an RSA private key
2509       *
2510       * @param string $username
2511       * @param \phpseclib\Crypt\RSA $password
2512       * @return bool
2513       * @access private
2514       * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
2515       *           by sending dummy SSH_MSG_IGNORE messages.
2516       */
2517      function _privatekey_login($username, $privatekey)
2518      {
2519          // see http://tools.ietf.org/html/rfc4253#page-15
2520          $publickey = $privatekey->getPublicKey(RSA::PUBLIC_FORMAT_RAW);
2521          if ($publickey === false) {
2522              return false;
2523          }
2524  
2525          $publickey = array(
2526              'e' => $publickey['e']->toBytes(true),
2527              'n' => $publickey['n']->toBytes(true)
2528          );
2529          $publickey = pack(
2530              'Na*Na*Na*',
2531              strlen('ssh-rsa'),
2532              'ssh-rsa',
2533              strlen($publickey['e']),
2534              $publickey['e'],
2535              strlen($publickey['n']),
2536              $publickey['n']
2537          );
2538  
2539          switch ($this->signature_format) {
2540              case 'rsa-sha2-512':
2541                  $hash = 'sha512';
2542                  $signatureType = 'rsa-sha2-512';
2543                  break;
2544              case 'rsa-sha2-256':
2545                  $hash = 'sha256';
2546                  $signatureType = 'rsa-sha2-256';
2547                  break;
2548              //case 'ssh-rsa':
2549              default:
2550                  $hash = 'sha1';
2551                  $signatureType = 'ssh-rsa';
2552          }
2553  
2554          $part1 = pack(
2555              'CNa*Na*Na*',
2556              NET_SSH2_MSG_USERAUTH_REQUEST,
2557              strlen($username),
2558              $username,
2559              strlen('ssh-connection'),
2560              'ssh-connection',
2561              strlen('publickey'),
2562              'publickey'
2563          );
2564          $part2 = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($publickey), $publickey);
2565  
2566          $packet = $part1 . chr(0) . $part2;
2567          if (!$this->_send_binary_packet($packet)) {
2568              return false;
2569          }
2570  
2571          $response = $this->_get_binary_packet();
2572          if ($response === false) {
2573              $this->bitmap = 0;
2574              user_error('Connection closed by server');
2575              return false;
2576          }
2577  
2578          if (!strlen($response)) {
2579              return false;
2580          }
2581          extract(unpack('Ctype', $this->_string_shift($response, 1)));
2582  
2583          switch ($type) {
2584              case NET_SSH2_MSG_USERAUTH_FAILURE:
2585                  if (strlen($response) < 4) {
2586                      return false;
2587                  }
2588                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2589                  $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length);
2590                  return false;
2591              case NET_SSH2_MSG_USERAUTH_PK_OK:
2592                  // we'll just take it on faith that the public key blob and the public key algorithm name are as
2593                  // they should be
2594                  if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) {
2595                      $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
2596                          'UNKNOWN',
2597                          'NET_SSH2_MSG_USERAUTH_PK_OK',
2598                          $this->message_number_log[count($this->message_number_log) - 1]
2599                      );
2600                  }
2601          }
2602  
2603          $packet = $part1 . chr(1) . $part2;
2604          $privatekey->setSignatureMode(RSA::SIGNATURE_PKCS1);
2605          $privatekey->setHash($hash);
2606          $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
2607          $signature = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($signature), $signature);
2608          $packet.= pack('Na*', strlen($signature), $signature);
2609  
2610          if (!$this->_send_binary_packet($packet)) {
2611              return false;
2612          }
2613  
2614          $response = $this->_get_binary_packet();
2615          if ($response === false) {
2616              $this->bitmap = 0;
2617              user_error('Connection closed by server');
2618              return false;
2619          }
2620  
2621          if (!strlen($response)) {
2622              return false;
2623          }
2624          extract(unpack('Ctype', $this->_string_shift($response, 1)));
2625  
2626          switch ($type) {
2627              case NET_SSH2_MSG_USERAUTH_FAILURE:
2628                  // either the login is bad or the server employs multi-factor authentication
2629                  return false;
2630              case NET_SSH2_MSG_USERAUTH_SUCCESS:
2631                  $this->bitmap |= self::MASK_LOGIN;
2632                  return true;
2633          }
2634  
2635          return false;
2636      }
2637  
2638      /**
2639       * Set Timeout
2640       *
2641       * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely.  setTimeout() makes it so it'll timeout.
2642       * Setting $timeout to false or 0 will mean there is no timeout.
2643       *
2644       * @param mixed $timeout
2645       * @access public
2646       */
2647      function setTimeout($timeout)
2648      {
2649          $this->timeout = $this->curTimeout = $timeout;
2650      }
2651  
2652      /**
2653       * Get the output from stdError
2654       *
2655       * @access public
2656       */
2657      function getStdError()
2658      {
2659          return $this->stdErrorLog;
2660      }
2661  
2662      /**
2663       * Execute Command
2664       *
2665       * If $callback is set to false then \phpseclib\Net\SSH2::_get_channel_packet(self::CHANNEL_EXEC) will need to be called manually.
2666       * In all likelihood, this is not a feature you want to be taking advantage of.
2667       *
2668       * @param string $command
2669       * @param Callback $callback
2670       * @return string
2671       * @access public
2672       */
2673      function exec($command, $callback = null)
2674      {
2675          $this->curTimeout = $this->timeout;
2676          $this->is_timeout = false;
2677          $this->stdErrorLog = '';
2678  
2679          if (!$this->isAuthenticated()) {
2680              return false;
2681          }
2682  
2683          if ($this->in_request_pty_exec) {
2684              user_error('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.');
2685              return false;
2686          }
2687  
2688          // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
2689          // be adjusted".  0x7FFFFFFF is, at 2GB, the max size.  technically, it should probably be decremented, but,
2690          // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway.
2691          // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
2692          $this->window_size_server_to_client[self::CHANNEL_EXEC] = $this->window_size;
2693          // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
2694          // uses 0x4000, that's what will be used here, as well.
2695          $packet_size = 0x4000;
2696  
2697          $packet = pack(
2698              'CNa*N3',
2699              NET_SSH2_MSG_CHANNEL_OPEN,
2700              strlen('session'),
2701              'session',
2702              self::CHANNEL_EXEC,
2703              $this->window_size_server_to_client[self::CHANNEL_EXEC],
2704              $packet_size
2705          );
2706  
2707          if (!$this->_send_binary_packet($packet)) {
2708              return false;
2709          }
2710  
2711          $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
2712  
2713          $response = $this->_get_channel_packet(self::CHANNEL_EXEC);
2714          if ($response === false) {
2715              return false;
2716          }
2717  
2718          if ($this->request_pty === true) {
2719              $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
2720              $packet = pack(
2721                  'CNNa*CNa*N5a*',
2722                  NET_SSH2_MSG_CHANNEL_REQUEST,
2723                  $this->server_channels[self::CHANNEL_EXEC],
2724                  strlen('pty-req'),
2725                  'pty-req',
2726                  1,
2727                  strlen('vt100'),
2728                  'vt100',
2729                  $this->windowColumns,
2730                  $this->windowRows,
2731                  0,
2732                  0,
2733                  strlen($terminal_modes),
2734                  $terminal_modes
2735              );
2736  
2737              if (!$this->_send_binary_packet($packet)) {
2738                  return false;
2739              }
2740  
2741              $response = $this->_get_binary_packet();
2742              if ($response === false) {
2743                  $this->bitmap = 0;
2744                  user_error('Connection closed by server');
2745                  return false;
2746              }
2747  
2748              if (!strlen($response)) {
2749                  return false;
2750              }
2751              list(, $type) = unpack('C', $this->_string_shift($response, 1));
2752  
2753              switch ($type) {
2754                  case NET_SSH2_MSG_CHANNEL_SUCCESS:
2755                      break;
2756                  case NET_SSH2_MSG_CHANNEL_FAILURE:
2757                  default:
2758                      user_error('Unable to request pseudo-terminal');
2759                      return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2760              }
2761              $this->in_request_pty_exec = true;
2762          }
2763  
2764          // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
2765          // down.  the one place where it might be desirable is if you're doing something like \phpseclib\Net\SSH2::exec('ping localhost &').
2766          // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then
2767          // then immediately terminate.  without such a request exec() will loop indefinitely.  the ping process won't end but
2768          // neither will your script.
2769  
2770          // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
2771          // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
2772          // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA.  RFC4254#section-5.2 corroborates.
2773          $packet = pack(
2774              'CNNa*CNa*',
2775              NET_SSH2_MSG_CHANNEL_REQUEST,
2776              $this->server_channels[self::CHANNEL_EXEC],
2777              strlen('exec'),
2778              'exec',
2779              1,
2780              strlen($command),
2781              $command
2782          );
2783          if (!$this->_send_binary_packet($packet)) {
2784              return false;
2785          }
2786  
2787          $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
2788  
2789          $response = $this->_get_channel_packet(self::CHANNEL_EXEC);
2790          if ($response === false) {
2791              return false;
2792          }
2793  
2794          $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
2795  
2796          if ($callback === false || $this->in_request_pty_exec) {
2797              return true;
2798          }
2799  
2800          $output = '';
2801          while (true) {
2802              $temp = $this->_get_channel_packet(self::CHANNEL_EXEC);
2803              switch (true) {
2804                  case $temp === true:
2805                      return is_callable($callback) ? true : $output;
2806                  case $temp === false:
2807                      return false;
2808                  default:
2809                      if (is_callable($callback)) {
2810                          if (call_user_func($callback, $temp) === true) {
2811                              $this->_close_channel(self::CHANNEL_EXEC);
2812                              return true;
2813                          }
2814                      } else {
2815                          $output.= $temp;
2816                      }
2817              }
2818          }
2819      }
2820  
2821      /**
2822       * Creates an interactive shell
2823       *
2824       * @see self::read()
2825       * @see self::write()
2826       * @return bool
2827       * @access private
2828       */
2829      function _initShell()
2830      {
2831          if ($this->in_request_pty_exec === true) {
2832              return true;
2833          }
2834  
2835          $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size;
2836          $packet_size = 0x4000;
2837  
2838          $packet = pack(
2839              'CNa*N3',
2840              NET_SSH2_MSG_CHANNEL_OPEN,
2841              strlen('session'),
2842              'session',
2843              self::CHANNEL_SHELL,
2844              $this->window_size_server_to_client[self::CHANNEL_SHELL],
2845              $packet_size
2846          );
2847  
2848          if (!$this->_send_binary_packet($packet)) {
2849              return false;
2850          }
2851  
2852          $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
2853  
2854          $response = $this->_get_channel_packet(self::CHANNEL_SHELL);
2855          if ($response === false) {
2856              return false;
2857          }
2858  
2859          $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
2860          $packet = pack(
2861              'CNNa*CNa*N5a*',
2862              NET_SSH2_MSG_CHANNEL_REQUEST,
2863              $this->server_channels[self::CHANNEL_SHELL],
2864              strlen('pty-req'),
2865              'pty-req',
2866              1,
2867              strlen('vt100'),
2868              'vt100',
2869              $this->windowColumns,
2870              $this->windowRows,
2871              0,
2872              0,
2873              strlen($terminal_modes),
2874              $terminal_modes
2875          );
2876  
2877          if (!$this->_send_binary_packet($packet)) {
2878              return false;
2879          }
2880  
2881          $response = $this->_get_binary_packet();
2882          if ($response === false) {
2883              $this->bitmap = 0;
2884              user_error('Connection closed by server');
2885              return false;
2886          }
2887  
2888          if (!strlen($response)) {
2889              return false;
2890          }
2891          list(, $type) = unpack('C', $this->_string_shift($response, 1));
2892  
2893          switch ($type) {
2894              case NET_SSH2_MSG_CHANNEL_SUCCESS:
2895              // if a pty can't be opened maybe commands can still be executed
2896              case NET_SSH2_MSG_CHANNEL_FAILURE:
2897                  break;
2898              default:
2899                  user_error('Unable to request pseudo-terminal');
2900                  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2901          }
2902  
2903          $packet = pack(
2904              'CNNa*C',
2905              NET_SSH2_MSG_CHANNEL_REQUEST,
2906              $this->server_channels[self::CHANNEL_SHELL],
2907              strlen('shell'),
2908              'shell',
2909              1
2910          );
2911          if (!$this->_send_binary_packet($packet)) {
2912              return false;
2913          }
2914  
2915          $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
2916  
2917          $response = $this->_get_channel_packet(self::CHANNEL_SHELL);
2918          if ($response === false) {
2919              return false;
2920          }
2921  
2922          $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
2923  
2924          $this->bitmap |= self::MASK_SHELL;
2925  
2926          return true;
2927      }
2928  
2929      /**
2930       * Return the channel to be used with read() / write()
2931       *
2932       * @see self::read()
2933       * @see self::write()
2934       * @return int
2935       * @access public
2936       */
2937      function _get_interactive_channel()
2938      {
2939          switch (true) {
2940              case $this->in_subsystem:
2941                  return self::CHANNEL_SUBSYSTEM;
2942              case $this->in_request_pty_exec:
2943                  return self::CHANNEL_EXEC;
2944              default:
2945                  return self::CHANNEL_SHELL;
2946          }
2947      }
2948  
2949      /**
2950       * Return an available open channel
2951       *
2952       * @return int
2953       * @access public
2954       */
2955      function _get_open_channel()
2956      {
2957          $channel = self::CHANNEL_EXEC;
2958          do {
2959              if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) {
2960                  return $channel;
2961              }
2962          } while ($channel++ < self::CHANNEL_SUBSYSTEM);
2963  
2964          return false;
2965      }
2966  
2967      /**
2968       * Returns the output of an interactive shell
2969       *
2970       * Returns when there's a match for $expect, which can take the form of a string literal or,
2971       * if $mode == self::READ_REGEX, a regular expression.
2972       *
2973       * @see self::write()
2974       * @param string $expect
2975       * @param int $mode
2976       * @return string
2977       * @access public
2978       */
2979      function read($expect = '', $mode = self::READ_SIMPLE)
2980      {
2981          $this->curTimeout = $this->timeout;
2982          $this->is_timeout = false;
2983  
2984          if (!$this->isAuthenticated()) {
2985              user_error('Operation disallowed prior to login()');
2986              return false;
2987          }
2988  
2989          if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
2990              user_error('Unable to initiate an interactive shell session');
2991              return false;
2992          }
2993  
2994          $channel = $this->_get_interactive_channel();
2995  
2996          if ($mode == self::READ_NEXT) {
2997              return $this->_get_channel_packet($channel);
2998          }
2999  
3000          $match = $expect;
3001          while (true) {
3002              if ($mode == self::READ_REGEX) {
3003                  preg_match($expect, substr($this->interactiveBuffer, -1024), $matches);
3004                  $match = isset($matches[0]) ? $matches[0] : '';
3005              }
3006              $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
3007              if ($pos !== false) {
3008                  return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
3009              }
3010              $response = $this->_get_channel_packet($channel);
3011              if (is_bool($response)) {
3012                  $this->in_request_pty_exec = false;
3013                  return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false;
3014              }
3015  
3016              $this->interactiveBuffer.= $response;
3017          }
3018      }
3019  
3020      /**
3021       * Inputs a command into an interactive shell.
3022       *
3023       * @see self::read()
3024       * @param string $cmd
3025       * @return bool
3026       * @access public
3027       */
3028      function write($cmd)
3029      {
3030          if (!$this->isAuthenticated()) {
3031              user_error('Operation disallowed prior to login()');
3032              return false;
3033          }
3034  
3035          if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
3036              user_error('Unable to initiate an interactive shell session');
3037              return false;
3038          }
3039  
3040          return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd);
3041      }
3042  
3043      /**
3044       * Start a subsystem.
3045       *
3046       * Right now only one subsystem at a time is supported. To support multiple subsystem's stopSubsystem() could accept
3047       * a string that contained the name of the subsystem, but at that point, only one subsystem of each type could be opened.
3048       * To support multiple subsystem's of the same name maybe it'd be best if startSubsystem() generated a new channel id and
3049       * returns that and then that that was passed into stopSubsystem() but that'll be saved for a future date and implemented
3050       * if there's sufficient demand for such a feature.
3051       *
3052       * @see self::stopSubsystem()
3053       * @param string $subsystem
3054       * @return bool
3055       * @access public
3056       */
3057      function startSubsystem($subsystem)
3058      {
3059          $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size;
3060  
3061          $packet = pack(
3062              'CNa*N3',
3063              NET_SSH2_MSG_CHANNEL_OPEN,
3064              strlen('session'),
3065              'session',
3066              self::CHANNEL_SUBSYSTEM,
3067              $this->window_size,
3068              0x4000
3069          );
3070  
3071          if (!$this->_send_binary_packet($packet)) {
3072              return false;
3073          }
3074  
3075          $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN;
3076  
3077          $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM);
3078          if ($response === false) {
3079              return false;
3080          }
3081  
3082          $packet = pack(
3083              'CNNa*CNa*',
3084              NET_SSH2_MSG_CHANNEL_REQUEST,
3085              $this->server_channels[self::CHANNEL_SUBSYSTEM],
3086              strlen('subsystem'),
3087              'subsystem',
3088              1,
3089              strlen($subsystem),
3090              $subsystem
3091          );
3092          if (!$this->_send_binary_packet($packet)) {
3093              return false;
3094          }
3095  
3096          $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST;
3097  
3098          $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM);
3099  
3100          if ($response === false) {
3101              return false;
3102          }
3103  
3104          $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA;
3105  
3106          $this->bitmap |= self::MASK_SHELL;
3107          $this->in_subsystem = true;
3108  
3109          return true;
3110      }
3111  
3112      /**
3113       * Stops a subsystem.
3114       *
3115       * @see self::startSubsystem()
3116       * @return bool
3117       * @access public
3118       */
3119      function stopSubsystem()
3120      {
3121          $this->in_subsystem = false;
3122          $this->_close_channel(self::CHANNEL_SUBSYSTEM);
3123          return true;
3124      }
3125  
3126      /**
3127       * Closes a channel
3128       *
3129       * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call
3130       *
3131       * @access public
3132       */
3133      function reset()
3134      {
3135          $this->_close_channel($this->_get_interactive_channel());
3136      }
3137  
3138      /**
3139       * Is timeout?
3140       *
3141       * Did exec() or read() return because they timed out or because they encountered the end?
3142       *
3143       * @access public
3144       */
3145      function isTimeout()
3146      {
3147          return $this->is_timeout;
3148      }
3149  
3150      /**
3151       * Disconnect
3152       *
3153       * @access public
3154       */
3155      function disconnect()
3156      {
3157          $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3158          if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
3159              fclose($this->realtime_log_file);
3160          }
3161      }
3162  
3163      /**
3164       * Destructor.
3165       *
3166       * Will be called, automatically, if you're supporting just PHP5.  If you're supporting PHP4, you'll need to call
3167       * disconnect().
3168       *
3169       * @access public
3170       */
3171      function __destruct()
3172      {
3173          $this->disconnect();
3174      }
3175  
3176      /**
3177       * Is the connection still active?
3178       *
3179       * @return bool
3180       * @access public
3181       */
3182      function isConnected()
3183      {
3184          return (bool) ($this->bitmap & self::MASK_CONNECTED);
3185      }
3186  
3187      /**
3188       * Have you successfully been logged in?
3189       *
3190       * @return bool
3191       * @access public
3192       */
3193      function isAuthenticated()
3194      {
3195          return (bool) ($this->bitmap & self::MASK_LOGIN);
3196      }
3197  
3198      /**
3199       * Pings a server connection, or tries to reconnect if the connection has gone down
3200       *
3201       * Inspired by http://php.net/manual/en/mysqli.ping.php
3202       *
3203       * @return bool
3204       * @access public
3205       */
3206      function ping()
3207      {
3208          if (!$this->isAuthenticated()) {
3209              if (!empty($this->auth)) {
3210                  return $this->_reconnect();
3211              }
3212              return false;
3213          }
3214  
3215          $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size;
3216          $packet_size = 0x4000;
3217          $packet = pack(
3218              'CNa*N3',
3219              NET_SSH2_MSG_CHANNEL_OPEN,
3220              strlen('session'),
3221              'session',
3222              self::CHANNEL_KEEP_ALIVE,
3223              $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE],
3224              $packet_size
3225          );
3226  
3227          if (!@$this->_send_binary_packet($packet)) {
3228              return $this->_reconnect();
3229          }
3230  
3231          $this->channel_status[self::CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN;
3232  
3233          $response = @$this->_get_channel_packet(self::CHANNEL_KEEP_ALIVE);
3234          if ($response !== false) {
3235              $this->_close_channel(self::CHANNEL_KEEP_ALIVE);
3236              return true;
3237          }
3238  
3239          return $this->_reconnect();
3240      }
3241  
3242      /**
3243       * In situ reconnect method
3244       *
3245       * @return boolean
3246       * @access private
3247       */
3248      function _reconnect()
3249      {
3250          $this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST);
3251          $this->retry_connect = true;
3252          if (!$this->_connect()) {
3253              return false;
3254          }
3255          foreach ($this->auth as $auth) {
3256              $result = call_user_func_array(array(&$this, 'login'), $auth);
3257          }
3258          return $result;
3259      }
3260  
3261      /**
3262       * Resets a connection for re-use
3263       *
3264       * @param int $reason
3265       * @access private
3266       */
3267      function _reset_connection($reason)
3268      {
3269          $this->_disconnect($reason);
3270          $this->decrypt = $this->encrypt = false;
3271          $this->decrypt_block_size = $this->encrypt_block_size = 8;
3272          $this->hmac_check = $this->hmac_create = false;
3273          $this->hmac_size = false;
3274          $this->session_id = false;
3275          $this->retry_connect = true;
3276          $this->get_seq_no = $this->send_seq_no = 0;
3277      }
3278  
3279      /**
3280       * Gets Binary Packets
3281       *
3282       * See '6. Binary Packet Protocol' of rfc4253 for more info.
3283       *
3284       * @see self::_send_binary_packet()
3285       * @return string
3286       * @access private
3287       */
3288      function _get_binary_packet($skip_channel_filter = false)
3289      {
3290          if (!is_resource($this->fsock) || feof($this->fsock)) {
3291              $this->bitmap = 0;
3292              user_error('Connection closed prematurely');
3293              return false;
3294          }
3295  
3296          $start = microtime(true);
3297          $raw = stream_get_contents($this->fsock, $this->decrypt_block_size);
3298  
3299          if (!strlen($raw)) {
3300              return '';
3301          }
3302  
3303          if ($this->decrypt !== false) {
3304              $raw = $this->decrypt->decrypt($raw);
3305          }
3306          if ($raw === false) {
3307              user_error('Unable to decrypt content');
3308              return false;
3309          }
3310  
3311          if (strlen($raw) < 5) {
3312              return false;
3313          }
3314          extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));
3315  
3316          $remaining_length = $packet_length + 4 - $this->decrypt_block_size;
3317  
3318          // quoting <http://tools.ietf.org/html/rfc4253#section-6.1>,
3319          // "implementations SHOULD check that the packet length is reasonable"
3320          // PuTTY uses 0x9000 as the actual max packet size and so to shall we
3321          if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
3322              if (!$this->bad_key_size_fix && $this->_bad_algorithm_candidate($this->decrypt->name) && !($this->bitmap & SSH2::MASK_LOGIN)) {
3323                  $this->bad_key_size_fix = true;
3324                  $this->_reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
3325                  return false;
3326              }
3327              user_error('Invalid size');
3328              return false;
3329          }
3330  
3331          $buffer = '';
3332          while ($remaining_length > 0) {
3333              $temp = stream_get_contents($this->fsock, $remaining_length);
3334              if ($temp === false || feof($this->fsock)) {
3335                  $this->bitmap = 0;
3336                  user_error('Error reading from socket');
3337                  return false;
3338              }
3339              $buffer.= $temp;
3340              $remaining_length-= strlen($temp);
3341          }
3342  
3343          $stop = microtime(true);
3344          if (strlen($buffer)) {
3345              $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
3346          }
3347  
3348          $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1);
3349          $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty
3350  
3351          if ($this->hmac_check !== false) {
3352              $hmac = stream_get_contents($this->fsock, $this->hmac_size);
3353              if ($hmac === false || strlen($hmac) != $this->hmac_size) {
3354                  $this->bitmap = 0;
3355                  user_error('Error reading socket');
3356                  return false;
3357              } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
3358                  user_error('Invalid HMAC');
3359                  return false;
3360              }
3361          }
3362  
3363          //if ($this->decompress) {
3364          //    $payload = gzinflate(substr($payload, 2));
3365          //}
3366  
3367          $this->get_seq_no++;
3368  
3369          if (defined('NET_SSH2_LOGGING')) {
3370              $current = microtime(true);
3371              $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
3372              $message_number = '<- ' . $message_number .
3373                                ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
3374              $this->_append_log($message_number, $payload);
3375              $this->last_packet = $current;
3376          }
3377  
3378          return $this->_filter($payload, $skip_channel_filter);
3379      }
3380  
3381      /**
3382       * Filter Binary Packets
3383       *
3384       * Because some binary packets need to be ignored...
3385       *
3386       * @see self::_get_binary_packet()
3387       * @return string
3388       * @access private
3389       */
3390      function _filter($payload, $skip_channel_filter)
3391      {
3392          switch (ord($payload[0])) {
3393              case NET_SSH2_MSG_DISCONNECT:
3394                  $this->_string_shift($payload, 1);
3395                  if (strlen($payload) < 8) {
3396                      return false;
3397                  }
3398                  extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
3399                  $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . $this->_string_shift($payload, $length);
3400                  $this->bitmap = 0;
3401                  return false;
3402              case NET_SSH2_MSG_IGNORE:
3403                  $payload = $this->_get_binary_packet($skip_channel_filter);
3404                  break;
3405              case NET_SSH2_MSG_DEBUG:
3406                  $this->_string_shift($payload, 2);
3407                  if (strlen($payload) < 4) {
3408                      return false;
3409                  }
3410                  extract(unpack('Nlength', $this->_string_shift($payload, 4)));
3411                  $this->errors[] = 'SSH_MSG_DEBUG: ' . $this->_string_shift($payload, $length);
3412                  $payload = $this->_get_binary_packet($skip_channel_filter);
3413                  break;
3414              case NET_SSH2_MSG_UNIMPLEMENTED:
3415                  return false;
3416              case NET_SSH2_MSG_KEXINIT:
3417                  if ($this->session_id !== false) {
3418                      $this->send_kex_first = false;
3419                      if (!$this->_key_exchange($payload)) {
3420                          $this->bitmap = 0;
3421                          return false;
3422                      }
3423                      $payload = $this->_get_binary_packet($skip_channel_filter);
3424                  }
3425          }
3426  
3427          // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in
3428          if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
3429              $this->_string_shift($payload, 1);
3430              if (strlen($payload) < 4) {
3431                  return false;
3432              }
3433              extract(unpack('Nlength', $this->_string_shift($payload, 4)));
3434              $this->banner_message = $this->_string_shift($payload, $length);
3435              $payload = $this->_get_binary_packet();
3436          }
3437  
3438          // only called when we've already logged in
3439          if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) {
3440              switch (ord($payload[0])) {
3441                  case NET_SSH2_MSG_CHANNEL_DATA:
3442                  case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
3443                  case NET_SSH2_MSG_CHANNEL_REQUEST:
3444                  case NET_SSH2_MSG_CHANNEL_CLOSE:
3445                  case NET_SSH2_MSG_CHANNEL_EOF:
3446                      if (!$skip_channel_filter && !empty($this->server_channels)) {
3447                          $this->binary_packet_buffer = $payload;
3448                          $this->_get_channel_packet(true);
3449                          $payload = $this->_get_binary_packet();
3450                      }
3451                      break;
3452                  case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
3453                      if (strlen($payload) < 4) {
3454                          return false;
3455                      }
3456                      extract(unpack('Nlength', $this->_string_shift($payload, 4)));
3457                      $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . $this->_string_shift($payload, $length);
3458  
3459                      if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
3460                          return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3461                      }
3462  
3463                      $payload = $this->_get_binary_packet($skip_channel_filter);
3464                      break;
3465                  case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
3466                      $this->_string_shift($payload, 1);
3467                      if (strlen($payload) < 4) {
3468                          return false;
3469                      }
3470                      extract(unpack('Nlength', $this->_string_shift($payload, 4)));
3471                      $data = $this->_string_shift($payload, $length);
3472                      if (strlen($payload) < 4) {
3473                          return false;
3474                      }
3475                      extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
3476                      switch ($data) {
3477                          case 'auth-agent':
3478                          case 'auth-agent@openssh.com':
3479                              if (isset($this->agent)) {
3480                                  $new_channel = self::CHANNEL_AGENT_FORWARD;
3481  
3482                                  if (strlen($payload) < 8) {
3483                                      return false;
3484                                  }
3485                                  extract(unpack('Nremote_window_size', $this->_string_shift($payload, 4)));
3486                                  extract(unpack('Nremote_maximum_packet_size', $this->_string_shift($payload, 4)));
3487  
3488                                  $this->packet_size_client_to_server[$new_channel] = $remote_window_size;
3489                                  $this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size;
3490                                  $this->window_size_client_to_server[$new_channel] = $this->window_size;
3491  
3492                                  $packet_size = 0x4000;
3493  
3494                                  $packet = pack(
3495                                      'CN4',
3496                                      NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION,
3497                                      $server_channel,
3498                                      $new_channel,
3499                                      $packet_size,
3500                                      $packet_size
3501                                  );
3502  
3503                                  $this->server_channels[$new_channel] = $server_channel;
3504                                  $this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION;
3505                                  if (!$this->_send_binary_packet($packet)) {
3506                                      return false;
3507                                  }
3508                              }
3509                              break;
3510                          default:
3511                              $packet = pack(
3512                                  'CN3a*Na*',
3513                                  NET_SSH2_MSG_REQUEST_FAILURE,
3514                                  $server_channel,
3515                                  NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
3516                                  0,
3517                                  '',
3518                                  0,
3519                                  ''
3520                              );
3521  
3522                              if (!$this->_send_binary_packet($packet)) {
3523                                  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3524                              }
3525                      }
3526                      $payload = $this->_get_binary_packet($skip_channel_filter);
3527                      break;
3528                  case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
3529                      $this->_string_shift($payload, 1);
3530                      if (strlen($payload) < 8) {
3531                          return false;
3532                      }
3533                      extract(unpack('Nchannel', $this->_string_shift($payload, 4)));
3534                      extract(unpack('Nwindow_size', $this->_string_shift($payload, 4)));
3535                      $this->window_size_client_to_server[$channel]+= $window_size;
3536  
3537                      $payload = ($this->bitmap & self::MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet($skip_channel_filter);
3538              }
3539          }
3540  
3541          return $payload;
3542      }
3543  
3544      /**
3545       * Enable Quiet Mode
3546       *
3547       * Suppress stderr from output
3548       *
3549       * @access public
3550       */
3551      function enableQuietMode()
3552      {
3553          $this->quiet_mode = true;
3554      }
3555  
3556      /**
3557       * Disable Quiet Mode
3558       *
3559       * Show stderr in output
3560       *
3561       * @access public
3562       */
3563      function disableQuietMode()
3564      {
3565          $this->quiet_mode = false;
3566      }
3567  
3568      /**
3569       * Returns whether Quiet Mode is enabled or not
3570       *
3571       * @see self::enableQuietMode()
3572       * @see self::disableQuietMode()
3573       * @access public
3574       * @return bool
3575       */
3576      function isQuietModeEnabled()
3577      {
3578          return $this->quiet_mode;
3579      }
3580  
3581      /**
3582       * Enable request-pty when using exec()
3583       *
3584       * @access public
3585       */
3586      function enablePTY()
3587      {
3588          $this->request_pty = true;
3589      }
3590  
3591      /**
3592       * Disable request-pty when using exec()
3593       *
3594       * @access public
3595       */
3596      function disablePTY()
3597      {
3598          if ($this->in_request_pty_exec) {
3599              $this->_close_channel(self::CHANNEL_EXEC);
3600              $this->in_request_pty_exec = false;
3601          }
3602          $this->request_pty = false;
3603      }
3604  
3605      /**
3606       * Returns whether request-pty is enabled or not
3607       *
3608       * @see self::enablePTY()
3609       * @see self::disablePTY()
3610       * @access public
3611       * @return bool
3612       */
3613      function isPTYEnabled()
3614      {
3615          return $this->request_pty;
3616      }
3617  
3618      /**
3619       * Gets channel data
3620       *
3621       * Returns the data as a string if it's available and false if not.
3622       *
3623       * @param $client_channel
3624       * @return mixed
3625       * @access private
3626       */
3627      function _get_channel_packet($client_channel, $skip_extended = false)
3628      {
3629          if (!empty($this->channel_buffers[$client_channel])) {
3630              return array_shift($this->channel_buffers[$client_channel]);
3631          }
3632  
3633          while (true) {
3634              if ($this->binary_packet_buffer !== false) {
3635                  $response = $this->binary_packet_buffer;
3636                  $this->binary_packet_buffer = false;
3637              } else {
3638                  $read = array($this->fsock);
3639                  $write = $except = null;
3640  
3641                  if (!$this->curTimeout) {
3642                      @stream_select($read, $write, $except, null);
3643                  } else {
3644                      if ($this->curTimeout < 0) {
3645                          $this->is_timeout = true;
3646                          return true;
3647                      }
3648  
3649                      $read = array($this->fsock);
3650                      $write = $except = null;
3651  
3652                      $start = microtime(true);
3653                      $sec = floor($this->curTimeout);
3654                      $usec = 1000000 * ($this->curTimeout - $sec);
3655                      // on windows this returns a "Warning: Invalid CRT parameters detected" error
3656                      if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
3657                          $this->is_timeout = true;
3658                          if ($client_channel == self::CHANNEL_EXEC && !$this->request_pty) {
3659                              $this->_close_channel($client_channel);
3660                          }
3661                          return true;
3662                      }
3663                      $elapsed = microtime(true) - $start;
3664                      $this->curTimeout-= $elapsed;
3665                  }
3666  
3667                  $response = $this->_get_binary_packet(true);
3668                  if ($response === false) {
3669                      $this->bitmap = 0;
3670                      user_error('Connection closed by server');
3671                      return false;
3672                  }
3673              }
3674  
3675              if ($client_channel == -1 && $response === true) {
3676                  return true;
3677              }
3678              if (!strlen($response)) {
3679                  return false;
3680              }
3681              extract(unpack('Ctype', $this->_string_shift($response, 1)));
3682  
3683              if (strlen($response) < 4) {
3684                  return false;
3685              }
3686              if ($type == NET_SSH2_MSG_CHANNEL_OPEN) {
3687                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
3688              } else {
3689                  extract(unpack('Nchannel', $this->_string_shift($response, 4)));
3690              }
3691  
3692              // will not be setup yet on incoming channel open request
3693              if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) {
3694                  $this->window_size_server_to_client[$channel]-= strlen($response);
3695  
3696                  // resize the window, if appropriate
3697                  if ($this->window_size_server_to_client[$channel] < 0) {
3698                      $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_size);
3699                      if (!$this->_send_binary_packet($packet)) {
3700                          return false;
3701                      }
3702                      $this->window_size_server_to_client[$channel]+= $this->window_size;
3703                  }
3704  
3705                  switch ($type) {
3706                      case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
3707                          /*
3708                          if ($client_channel == self::CHANNEL_EXEC) {
3709                              $this->_send_channel_packet($client_channel, chr(0));
3710                          }
3711                          */
3712                          // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
3713                          if (strlen($response) < 8) {
3714                              return false;
3715                          }
3716                          extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
3717                          $data = $this->_string_shift($response, $length);
3718                          $this->stdErrorLog.= $data;
3719                          if ($skip_extended || $this->quiet_mode) {
3720                              continue 2;
3721                          }
3722                          if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) {
3723                              return $data;
3724                          }
3725                          if (!isset($this->channel_buffers[$channel])) {
3726                              $this->channel_buffers[$channel] = array();
3727                          }
3728                          $this->channel_buffers[$channel][] = $data;
3729  
3730                          continue 2;
3731                      case NET_SSH2_MSG_CHANNEL_REQUEST:
3732                          if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) {
3733                              continue 2;
3734                          }
3735                          if (strlen($response) < 4) {
3736                              return false;
3737                          }
3738                          extract(unpack('Nlength', $this->_string_shift($response, 4)));
3739                          $value = $this->_string_shift($response, $length);
3740                          switch ($value) {
3741                              case 'exit-signal':
3742                                  $this->_string_shift($response, 1);
3743                                  if (strlen($response) < 4) {
3744                                      return false;
3745                                  }
3746                                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
3747                                  $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length);
3748                                  $this->_string_shift($response, 1);
3749                                  if (strlen($response) < 4) {
3750                                      return false;
3751                                  }
3752                                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
3753                                  if ($length) {
3754                                      $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length);
3755                                  }
3756  
3757                                  $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
3758                                  $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
3759  
3760                                  $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF;
3761  
3762                                  continue 3;
3763                              case 'exit-status':
3764                                  if (strlen($response) < 5) {
3765                                      return false;
3766                                  }
3767                                  extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5)));
3768                                  $this->exit_status = $exit_status;
3769  
3770                                  // "The client MAY ignore these messages."
3771                                  // -- http://tools.ietf.org/html/rfc4254#section-6.10
3772  
3773                                  continue 3;
3774                              default:
3775                                  // "Some systems may not implement signals, in which case they SHOULD ignore this message."
3776                                  //  -- http://tools.ietf.org/html/rfc4254#section-6.9
3777                                  continue 3;
3778                          }
3779                  }
3780  
3781                  switch ($this->channel_status[$channel]) {
3782                      case NET_SSH2_MSG_CHANNEL_OPEN:
3783                          switch ($type) {
3784                              case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3785                                  if (strlen($response) < 4) {
3786                                      return false;
3787                                  }
3788                                  extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
3789                                  $this->server_channels[$channel] = $server_channel;
3790                                  if (strlen($response) < 4) {
3791                                      return false;
3792                                  }
3793                                  extract(unpack('Nwindow_size', $this->_string_shift($response, 4)));
3794                                  if ($window_size < 0) {
3795                                      $window_size&= 0x7FFFFFFF;
3796                                      $window_size+= 0x80000000;
3797                                  }
3798                                  $this->window_size_client_to_server[$channel] = $window_size;
3799                                  if (strlen($response) < 4) {
3800                                       return false;
3801                                  }
3802                                  $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
3803                                  $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server'];
3804                                  $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
3805                                  $this->_on_channel_open();
3806                                  return $result;
3807                              //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
3808                              default:
3809                                  user_error('Unable to open channel');
3810                                  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3811                          }
3812                          break;
3813                      case NET_SSH2_MSG_CHANNEL_REQUEST:
3814                          switch ($type) {
3815                              case NET_SSH2_MSG_CHANNEL_SUCCESS:
3816                                  return true;
3817                              case NET_SSH2_MSG_CHANNEL_FAILURE:
3818                                  return false;
3819                              default:
3820                                  user_error('Unable to fulfill channel request');
3821                                  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3822                          }
3823                      case NET_SSH2_MSG_CHANNEL_CLOSE:
3824                          return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended);
3825                  }
3826              }
3827  
3828              // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA
3829  
3830              switch ($type) {
3831                  case NET_SSH2_MSG_CHANNEL_DATA:
3832                      /*
3833                      if ($channel == self::CHANNEL_EXEC) {
3834                          // SCP requires null packets, such as this, be sent.  further, in the case of the ssh.com SSH server
3835                          // this actually seems to make things twice as fast.  more to the point, the message right after
3836                          // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
3837                          // in OpenSSH it slows things down but only by a couple thousandths of a second.
3838                          $this->_send_channel_packet($channel, chr(0));
3839                      }
3840                      */
3841                      if (strlen($response) < 4) {
3842                          return false;
3843                      }
3844                      extract(unpack('Nlength', $this->_string_shift($response, 4)));
3845                      $data = $this->_string_shift($response, $length);
3846  
3847                      if ($channel == self::CHANNEL_AGENT_FORWARD) {
3848                          $agent_response = $this->agent->_forward_data($data);
3849                          if (!is_bool($agent_response)) {
3850                              $this->_send_channel_packet($channel, $agent_response);
3851                          }
3852                          break;
3853                      }
3854  
3855                      if ($client_channel == $channel) {
3856                          return $data;
3857                      }
3858                      if (!isset($this->channel_buffers[$channel])) {
3859                          $this->channel_buffers[$channel] = array();
3860                      }
3861                      $this->channel_buffers[$channel][] = $data;
3862                      break;
3863                  case NET_SSH2_MSG_CHANNEL_CLOSE:
3864                      $this->curTimeout = 0;
3865  
3866                      if ($this->bitmap & self::MASK_SHELL) {
3867                          $this->bitmap&= ~self::MASK_SHELL;
3868                      }
3869                      if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
3870                          $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
3871                      }
3872  
3873                      $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
3874                      if ($client_channel == $channel) {
3875                          return true;
3876                      }
3877                  case NET_SSH2_MSG_CHANNEL_EOF:
3878                      break;
3879                  default:
3880                      user_error('Error reading channel data');
3881                      return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3882              }
3883          }
3884      }
3885  
3886      /**
3887       * Sends Binary Packets
3888       *
3889       * See '6. Binary Packet Protocol' of rfc4253 for more info.
3890       *
3891       * @param string $data
3892       * @param string $logged
3893       * @see self::_get_binary_packet()
3894       * @return bool
3895       * @access private
3896       */
3897      function _send_binary_packet($data, $logged = null)
3898      {
3899          if (!is_resource($this->fsock) || feof($this->fsock)) {
3900              $this->bitmap = 0;
3901              user_error('Connection closed prematurely');
3902              return false;
3903          }
3904  
3905          //if ($this->compress) {
3906          //    // the -4 removes the checksum:
3907          //    // http://php.net/function.gzcompress#57710
3908          //    $data = substr(gzcompress($data), 0, -4);
3909          //}
3910  
3911          // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
3912          $packet_length = strlen($data) + 9;
3913          // round up to the nearest $this->encrypt_block_size
3914          $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
3915          // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
3916          $padding_length = $packet_length - strlen($data) - 5;
3917          $padding = Random::string($padding_length);
3918  
3919          // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
3920          $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);
3921  
3922          $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : '';
3923          $this->send_seq_no++;
3924  
3925          if ($this->encrypt !== false) {
3926              $packet = $this->encrypt->encrypt($packet);
3927          }
3928  
3929          $packet.= $hmac;
3930  
3931          $start = microtime(true);
3932          $result = strlen($packet) == fputs($this->fsock, $packet);
3933          $stop = microtime(true);
3934  
3935          if (defined('NET_SSH2_LOGGING')) {
3936              $current = microtime(true);
3937              $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')';
3938              $message_number = '-> ' . $message_number .
3939                                ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
3940              $this->_append_log($message_number, isset($logged) ? $logged : $data);
3941              $this->last_packet = $current;
3942          }
3943  
3944          return $result;
3945      }
3946  
3947      /**
3948       * Logs data packets
3949       *
3950       * Makes sure that only the last 1MB worth of packets will be logged
3951       *
3952       * @param string $data
3953       * @access private
3954       */
3955      function _append_log($message_number, $message)
3956      {
3957          // remove the byte identifying the message type from all but the first two messages (ie. the identification strings)
3958          if (strlen($message_number) > 2) {
3959              $this->_string_shift($message);
3960          }
3961  
3962          switch (NET_SSH2_LOGGING) {
3963              // useful for benchmarks
3964              case self::LOG_SIMPLE:
3965                  $this->message_number_log[] = $message_number;
3966                  break;
3967              // the most useful log for SSH2
3968              case self::LOG_COMPLEX:
3969                  $this->message_number_log[] = $message_number;
3970                  $this->log_size+= strlen($message);
3971                  $this->message_log[] = $message;
3972                  while ($this->log_size > self::LOG_MAX_SIZE) {
3973                      $this->log_size-= strlen(array_shift($this->message_log));
3974                      array_shift($this->message_number_log);
3975                  }
3976                  break;
3977              // dump the output out realtime; packets may be interspersed with non packets,
3978              // passwords won't be filtered out and select other packets may not be correctly
3979              // identified
3980              case self::LOG_REALTIME:
3981                  switch (PHP_SAPI) {
3982                      case 'cli':
3983                          $start = $stop = "\r\n";
3984                          break;
3985                      default:
3986                          $start = '<pre>';
3987                          $stop = '</pre>';
3988                  }
3989                  echo $start . $this->_format_log(array($message), array($message_number)) . $stop;
3990                  @flush();
3991                  @ob_flush();
3992                  break;
3993              // basically the same thing as self::LOG_REALTIME with the caveat that self::LOG_REALTIME_FILE
3994              // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE.
3995              // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
3996              // at the beginning of the file
3997              case self::LOG_REALTIME_FILE:
3998                  if (!isset($this->realtime_log_file)) {
3999                      // PHP doesn't seem to like using constants in fopen()
4000                      $filename = self::LOG_REALTIME_FILENAME;
4001                      $fp = fopen($filename, 'w');
4002                      $this->realtime_log_file = $fp;
4003                  }
4004                  if (!is_resource($this->realtime_log_file)) {
4005                      break;
4006                  }
4007                  $entry = $this->_format_log(array($message), array($message_number));
4008                  if ($this->realtime_log_wrap) {
4009                      $temp = "<<< START >>>\r\n";
4010                      $entry.= $temp;
4011                      fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
4012                  }
4013                  $this->realtime_log_size+= strlen($entry);
4014                  if ($this->realtime_log_size > self::LOG_MAX_SIZE) {
4015                      fseek($this->realtime_log_file, 0);
4016                      $this->realtime_log_size = strlen($entry);
4017                      $this->realtime_log_wrap = true;
4018                  }
4019                  fputs($this->realtime_log_file, $entry);
4020          }
4021      }
4022  
4023      /**
4024       * Sends channel data
4025       *
4026       * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
4027       *
4028       * @param int $client_channel
4029       * @param string $data
4030       * @return bool
4031       * @access private
4032       */
4033      function _send_channel_packet($client_channel, $data)
4034      {
4035          while (strlen($data)) {
4036              if (!$this->window_size_client_to_server[$client_channel]) {
4037                  $this->bitmap^= self::MASK_WINDOW_ADJUST;
4038                  // using an invalid channel will let the buffers be built up for the valid channels
4039                  $this->_get_channel_packet(-1);
4040                  $this->bitmap^= self::MASK_WINDOW_ADJUST;
4041              }
4042  
4043              /* The maximum amount of data allowed is determined by the maximum
4044                 packet size for the channel, and the current window size, whichever
4045                 is smaller.
4046                   -- http://tools.ietf.org/html/rfc4254#section-5.2 */
4047              $max_size = min(
4048                  $this->packet_size_client_to_server[$client_channel],
4049                  $this->window_size_client_to_server[$client_channel]
4050              );
4051  
4052              $temp = $this->_string_shift($data, $max_size);
4053              $packet = pack(
4054                  'CN2a*',
4055                  NET_SSH2_MSG_CHANNEL_DATA,
4056                  $this->server_channels[$client_channel],
4057                  strlen($temp),
4058                  $temp
4059              );
4060              $this->window_size_client_to_server[$client_channel]-= strlen($temp);
4061              if (!$this->_send_binary_packet($packet)) {
4062                  return false;
4063              }
4064          }
4065  
4066          return true;
4067      }
4068  
4069      /**
4070       * Closes and flushes a channel
4071       *
4072       * \phpseclib\Net\SSH2 doesn't properly close most channels.  For exec() channels are normally closed by the server
4073       * and for SFTP channels are presumably closed when the client disconnects.  This functions is intended
4074       * for SCP more than anything.
4075       *
4076       * @param int $client_channel
4077       * @param bool $want_reply
4078       * @return bool
4079       * @access private
4080       */
4081      function _close_channel($client_channel, $want_reply = false)
4082      {
4083          // see http://tools.ietf.org/html/rfc4254#section-5.3
4084  
4085          $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
4086  
4087          if (!$want_reply) {
4088              $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
4089          }
4090  
4091          $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
4092  
4093          $this->curTimeout = 0;
4094  
4095          while (!is_bool($this->_get_channel_packet($client_channel))) {
4096          }
4097  
4098          if ($want_reply) {
4099              $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
4100          }
4101  
4102          if ($this->bitmap & self::MASK_SHELL) {
4103              $this->bitmap&= ~self::MASK_SHELL;
4104          }
4105      }
4106  
4107      /**
4108       * Disconnect
4109       *
4110       * @param int $reason
4111       * @return bool
4112       * @access private
4113       */
4114      function _disconnect($reason)
4115      {
4116          if ($this->bitmap & self::MASK_CONNECTED) {
4117              $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, '');
4118              $this->_send_binary_packet($data);
4119          }
4120  
4121          $this->bitmap = 0;
4122          if (is_resource($this->fsock) && get_resource_type($this->fsock) == 'stream') {
4123              fclose($this->fsock);
4124          }
4125  
4126          return false;
4127      }
4128  
4129      /**
4130       * String Shift
4131       *
4132       * Inspired by array_shift
4133       *
4134       * @param string $string
4135       * @param int $index
4136       * @return string
4137       * @access private
4138       */
4139      function _string_shift(&$string, $index = 1)
4140      {
4141          $substr = substr($string, 0, $index);
4142          $string = substr($string, $index);
4143          return $substr;
4144      }
4145  
4146      /**
4147       * Define Array
4148       *
4149       * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
4150       * named constants from it, using the value as the name of the constant and the index as the value of the constant.
4151       * If any of the constants that would be defined already exists, none of the constants will be defined.
4152       *
4153       * @param array $array
4154       * @access private
4155       */
4156      function _define_array()
4157      {
4158          $args = func_get_args();
4159          foreach ($args as $arg) {
4160              foreach ($arg as $key => $value) {
4161                  if (!defined($value)) {
4162                      define($value, $key);
4163                  } else {
4164                      break 2;
4165                  }
4166              }
4167          }
4168      }
4169  
4170      /**
4171       * Returns a log of the packets that have been sent and received.
4172       *
4173       * Returns a string if NET_SSH2_LOGGING == self::LOG_COMPLEX, an array if NET_SSH2_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING')
4174       *
4175       * @access public
4176       * @return array|false|string
4177       */
4178      function getLog()
4179      {
4180          if (!defined('NET_SSH2_LOGGING')) {
4181              return false;
4182          }
4183  
4184          switch (NET_SSH2_LOGGING) {
4185              case self::LOG_SIMPLE:
4186                  return $this->message_number_log;
4187              case self::LOG_COMPLEX:
4188                  $log = $this->_format_log($this->message_log, $this->message_number_log);
4189                  return PHP_SAPI == 'cli' ? $log : '<pre>' . $log . '</pre>';
4190              default:
4191                  return false;
4192          }
4193      }
4194  
4195      /**
4196       * Formats a log for printing
4197       *
4198       * @param array $message_log
4199       * @param array $message_number_log
4200       * @access private
4201       * @return string
4202       */
4203      function _format_log($message_log, $message_number_log)
4204      {
4205          $output = '';
4206          for ($i = 0; $i < count($message_log); $i++) {
4207              $output.= $message_number_log[$i] . "\r\n";
4208              $current_log = $message_log[$i];
4209              $j = 0;
4210              do {
4211                  if (strlen($current_log)) {
4212                      $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0  ';
4213                  }
4214                  $fragment = $this->_string_shift($current_log, $this->log_short_width);
4215                  $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary));
4216                  // replace non ASCII printable characters with dots
4217                  // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
4218                  // also replace < with a . since < messes up the output on web browsers
4219                  $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
4220                  $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
4221                  $j++;
4222              } while (strlen($current_log));
4223              $output.= "\r\n";
4224          }
4225  
4226          return $output;
4227      }
4228  
4229      /**
4230       * Helper function for _format_log
4231       *
4232       * For use with preg_replace_callback()
4233       *
4234       * @param array $matches
4235       * @access private
4236       * @return string
4237       */
4238      function _format_log_helper($matches)
4239      {
4240          return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT);
4241      }
4242  
4243      /**
4244       * Helper function for agent->_on_channel_open()
4245       *
4246       * Used when channels are created to inform agent
4247       * of said channel opening. Must be called after
4248       * channel open confirmation received
4249       *
4250       * @access private
4251       */
4252      function _on_channel_open()
4253      {
4254          if (isset($this->agent)) {
4255              $this->agent->_on_channel_open($this);
4256          }
4257      }
4258  
4259      /**
4260       * Returns the first value of the intersection of two arrays or false if
4261       * the intersection is empty. The order is defined by the first parameter.
4262       *
4263       * @param array $array1
4264       * @param array $array2
4265       * @return mixed False if intersection is empty, else intersected value.
4266       * @access private
4267       */
4268      function _array_intersect_first($array1, $array2)
4269      {
4270          foreach ($array1 as $value) {
4271              if (in_array($value, $array2)) {
4272                  return $value;
4273              }
4274          }
4275          return false;
4276      }
4277  
4278      /**
4279       * Returns all errors
4280       *
4281       * @return string[]
4282       * @access public
4283       */
4284      function getErrors()
4285      {
4286          return $this->errors;
4287      }
4288  
4289      /**
4290       * Returns the last error
4291       *
4292       * @return string
4293       * @access public
4294       */
4295      function getLastError()
4296      {
4297          $count = count($this->errors);
4298  
4299          if ($count > 0) {
4300              return $this->errors[$count - 1];
4301          }
4302      }
4303  
4304      /**
4305       * Return the server identification.
4306       *
4307       * @return string
4308       * @access public
4309       */
4310      function getServerIdentification()
4311      {
4312          $this->_connect();
4313  
4314          return $this->server_identifier;
4315      }
4316  
4317      /**
4318       * Return a list of the key exchange algorithms the server supports.
4319       *
4320       * @return array
4321       * @access public
4322       */
4323      function getKexAlgorithms()
4324      {
4325          $this->_connect();
4326  
4327          return $this->kex_algorithms;
4328      }
4329  
4330      /**
4331       * Return a list of the host key (public key) algorithms the server supports.
4332       *
4333       * @return array
4334       * @access public
4335       */
4336      function getServerHostKeyAlgorithms()
4337      {
4338          $this->_connect();
4339  
4340          return $this->server_host_key_algorithms;
4341      }
4342  
4343      /**
4344       * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client.
4345       *
4346       * @return array
4347       * @access public
4348       */
4349      function getEncryptionAlgorithmsClient2Server()
4350      {
4351          $this->_connect();
4352  
4353          return $this->encryption_algorithms_client_to_server;
4354      }
4355  
4356      /**
4357       * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client.
4358       *
4359       * @return array
4360       * @access public
4361       */
4362      function getEncryptionAlgorithmsServer2Client()
4363      {
4364          $this->_connect();
4365  
4366          return $this->encryption_algorithms_server_to_client;
4367      }
4368  
4369      /**
4370       * Return a list of the MAC algorithms the server supports, when receiving stuff from the client.
4371       *
4372       * @return array
4373       * @access public
4374       */
4375      function getMACAlgorithmsClient2Server()
4376      {
4377          $this->_connect();
4378  
4379          return $this->mac_algorithms_client_to_server;
4380      }
4381  
4382      /**
4383       * Return a list of the MAC algorithms the server supports, when sending stuff to the client.
4384       *
4385       * @return array
4386       * @access public
4387       */
4388      function getMACAlgorithmsServer2Client()
4389      {
4390          $this->_connect();
4391  
4392          return $this->mac_algorithms_server_to_client;
4393      }
4394  
4395      /**
4396       * Return a list of the compression algorithms the server supports, when receiving stuff from the client.
4397       *
4398       * @return array
4399       * @access public
4400       */
4401      function getCompressionAlgorithmsClient2Server()
4402      {
4403          $this->_connect();
4404  
4405          return $this->compression_algorithms_client_to_server;
4406      }
4407  
4408      /**
4409       * Return a list of the compression algorithms the server supports, when sending stuff to the client.
4410       *
4411       * @return array
4412       * @access public
4413       */
4414      function getCompressionAlgorithmsServer2Client()
4415      {
4416          $this->_connect();
4417  
4418          return $this->compression_algorithms_server_to_client;
4419      }
4420  
4421      /**
4422       * Return a list of the languages the server supports, when sending stuff to the client.
4423       *
4424       * @return array
4425       * @access public
4426       */
4427      function getLanguagesServer2Client()
4428      {
4429          $this->_connect();
4430  
4431          return $this->languages_server_to_client;
4432      }
4433  
4434      /**
4435       * Return a list of the languages the server supports, when receiving stuff from the client.
4436       *
4437       * @return array
4438       * @access public
4439       */
4440      function getLanguagesClient2Server()
4441      {
4442          $this->_connect();
4443  
4444          return $this->languages_client_to_server;
4445      }
4446  
4447      /**
4448       * Returns a list of algorithms the server supports
4449       *
4450       * @return array
4451       * @access public
4452       */
4453      public function getServerAlgorithms()
4454      {
4455          $this->_connect();
4456  
4457          return array(
4458              'kex' => $this->kex_algorithms,
4459              'hostkey' => $this->server_host_key_algorithms,
4460              'client_to_server' => array(
4461                  'crypt' => $this->encryption_algorithms_client_to_server,
4462                  'mac' => $this->mac_algorithms_client_to_server,
4463                  'comp' => $this->compression_algorithms_client_to_server,
4464                  'lang' => $this->languages_client_to_server
4465              ),
4466              'server_to_client' => array(
4467                  'crypt' => $this->encryption_algorithms_server_to_client,
4468                  'mac' => $this->mac_algorithms_server_to_client,
4469                  'comp' => $this->compression_algorithms_server_to_client,
4470                  'lang' => $this->languages_server_to_client
4471              )
4472          );
4473      }
4474  
4475      /**
4476       * Returns a list of KEX algorithms that phpseclib supports
4477       *
4478       * @return array
4479       * @access public
4480       */
4481      function getSupportedKEXAlgorithms()
4482      {
4483          $kex_algorithms = array(
4484              // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using
4485              // Curve25519. See doc/curve25519-sha256@libssh.org.txt in the
4486              // libssh repository for more information.
4487              'curve25519-sha256@libssh.org',
4488  
4489              'diffie-hellman-group-exchange-sha256',// RFC 4419
4490              'diffie-hellman-group-exchange-sha1',  // RFC 4419
4491  
4492              // Diffie-Hellman Key Agreement (DH) using integer modulo prime
4493              // groups.
4494              'diffie-hellman-group14-sha1', // REQUIRED
4495              'diffie-hellman-group1-sha1', // REQUIRED
4496          );
4497  
4498          if (!function_exists('sodium_crypto_box_publickey_from_secretkey')) {
4499              $kex_algorithms = array_diff(
4500                  $kex_algorithms,
4501                  array('curve25519-sha256@libssh.org')
4502              );
4503          }
4504  
4505          return $kex_algorithms;
4506      }
4507  
4508      /**
4509       * Returns a list of host key algorithms that phpseclib supports
4510       *
4511       * @return array
4512       * @access public
4513       */
4514      function getSupportedHostKeyAlgorithms()
4515      {
4516          return array(
4517              'rsa-sha2-256', // RFC 8332
4518              'rsa-sha2-512', // RFC 8332
4519              'ssh-rsa', // RECOMMENDED  sign   Raw RSA Key
4520              'ssh-dss'  // REQUIRED     sign   Raw DSS Key
4521          );
4522      }
4523  
4524      /**
4525       * Returns a list of symmetric key algorithms that phpseclib supports
4526       *
4527       * @return array
4528       * @access public
4529       */
4530      function getSupportedEncryptionAlgorithms()
4531      {
4532          $algos = array(
4533              // from <http://tools.ietf.org/html/rfc4345#section-4>:
4534              'arcfour256',
4535              'arcfour128',
4536  
4537              //'arcfour',      // OPTIONAL          the ARCFOUR stream cipher with a 128-bit key
4538  
4539              // CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>:
4540              'aes128-ctr',     // RECOMMENDED       AES (Rijndael) in SDCTR mode, with 128-bit key
4541              'aes192-ctr',     // RECOMMENDED       AES with 192-bit key
4542              'aes256-ctr',     // RECOMMENDED       AES with 256-bit key
4543  
4544              'twofish128-ctr', // OPTIONAL          Twofish in SDCTR mode, with 128-bit key
4545              'twofish192-ctr', // OPTIONAL          Twofish with 192-bit key
4546              'twofish256-ctr', // OPTIONAL          Twofish with 256-bit key
4547  
4548              'aes128-cbc',     // RECOMMENDED       AES with a 128-bit key
4549              'aes192-cbc',     // OPTIONAL          AES with a 192-bit key
4550              'aes256-cbc',     // OPTIONAL          AES in CBC mode, with a 256-bit key
4551  
4552              'twofish128-cbc', // OPTIONAL          Twofish with a 128-bit key
4553              'twofish192-cbc', // OPTIONAL          Twofish with a 192-bit key
4554              'twofish256-cbc',
4555              'twofish-cbc',    // OPTIONAL          alias for "twofish256-cbc"
4556                                //                   (this is being retained for historical reasons)
4557  
4558              'blowfish-ctr',   // OPTIONAL          Blowfish in SDCTR mode
4559  
4560              'blowfish-cbc',   // OPTIONAL          Blowfish in CBC mode
4561  
4562              '3des-ctr',       // RECOMMENDED       Three-key 3DES in SDCTR mode
4563  
4564              '3des-cbc',       // REQUIRED          three-key 3DES in CBC mode
4565  
4566               //'none'           // OPTIONAL          no encryption; NOT RECOMMENDED
4567          );
4568  
4569          $engines = array(
4570              Base::ENGINE_OPENSSL,
4571              Base::ENGINE_MCRYPT,
4572              Base::ENGINE_INTERNAL
4573          );
4574  
4575          $ciphers = array();
4576          foreach ($engines as $engine) {
4577              foreach ($algos as $algo) {
4578                  $obj = $this->_encryption_algorithm_to_crypt_instance($algo);
4579                  if ($obj instanceof Rijndael) {
4580                      $obj->setKeyLength(preg_replace('#[^\d]#', '', $algo));
4581                  }
4582                  switch ($algo) {
4583                      case 'arcfour128':
4584                      case 'arcfour256':
4585                          if ($engine == Base::ENGINE_INTERNAL) {
4586                              $algos = array_diff($algos, array($algo));
4587                              $ciphers[] = $algo;
4588                          } else {
4589                              continue 2;
4590                          }
4591                  }
4592                  if ($obj->isValidEngine($engine)) {
4593                      $algos = array_diff($algos, array($algo));
4594                      $ciphers[] = $algo;
4595                  }
4596              }
4597          }
4598  
4599          return $ciphers;
4600      }
4601  
4602      /**
4603       * Returns a list of MAC algorithms that phpseclib supports
4604       *
4605       * @return array
4606       * @access public
4607       */
4608      function getSupportedMACAlgorithms()
4609      {
4610          return array(
4611              // from <http://www.ietf.org/rfc/rfc6668.txt>:
4612              'hmac-sha2-256',// RECOMMENDED     HMAC-SHA256 (digest length = key length = 32)
4613  
4614              'hmac-sha1-96', // RECOMMENDED     first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
4615              'hmac-sha1',    // REQUIRED        HMAC-SHA1 (digest length = key length = 20)
4616              'hmac-md5-96',  // OPTIONAL        first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
4617              'hmac-md5',     // OPTIONAL        HMAC-MD5 (digest length = key length = 16)
4618              //'none'          // OPTIONAL        no MAC; NOT RECOMMENDED
4619          );
4620      }
4621  
4622      /**
4623       * Returns a list of compression algorithms that phpseclib supports
4624       *
4625       * @return array
4626       * @access public
4627       */
4628      function getSupportedCompressionAlgorithms()
4629      {
4630          return array(
4631              'none'   // REQUIRED        no compression
4632              //'zlib' // OPTIONAL        ZLIB (LZ77) compression
4633          );
4634      }
4635  
4636      /**
4637       * Return list of negotiated algorithms
4638       *
4639       * Uses the same format as https://www.php.net/ssh2-methods-negotiated
4640       *
4641       * @return array
4642       * @access public
4643       */
4644      function getAlgorithmsNegotiated()
4645      {
4646          $this->_connect();
4647  
4648          return array(
4649              'kex' => $this->kex_algorithm,
4650              'hostkey' => $this->signature_format,
4651              'client_to_server' => array(
4652                  'crypt' => $this->encrypt->name,
4653                  'mac' => $this->hmac_create->name,
4654                  'comp' => 'none',
4655              ),
4656              'server_to_client' => array(
4657                  'crypt' => $this->decrypt->name,
4658                  'mac' => $this->hmac_check->name,
4659                  'comp' => 'none',
4660              )
4661          );
4662      }
4663  
4664      /**
4665       * Accepts an associative array with up to four parameters as described at
4666       * <https://www.php.net/manual/en/function.ssh2-connect.php>
4667       *
4668       * @param array $methods
4669       * @access public
4670       */
4671      function setPreferredAlgorithms($methods)
4672      {
4673          $preferred = $methods;
4674  
4675          if (isset($preferred['kex'])) {
4676              $preferred['kex'] = array_intersect(
4677                  $preferred['kex'],
4678                  $this->getSupportedKEXAlgorithms()
4679              );
4680          }
4681  
4682          if (isset($preferred['hostkey'])) {
4683              $preferred['hostkey'] = array_intersect(
4684                  $preferred['hostkey'],
4685                  $this->getSupportedHostKeyAlgorithms()
4686              );
4687          }
4688  
4689          $keys = array('client_to_server', 'server_to_client');
4690          foreach ($keys as $key) {
4691              if (isset($preferred[$key])) {
4692                  $a = &$preferred[$key];
4693                  if (isset($a['crypt'])) {
4694                      $a['crypt'] = array_intersect(
4695                          $a['crypt'],
4696                          $this->getSupportedEncryptionAlgorithms()
4697                      );
4698                  }
4699                  if (isset($a['comp'])) {
4700                      $a['comp'] = array_intersect(
4701                          $a['comp'],
4702                          $this->getSupportedCompressionAlgorithms()
4703                      );
4704                  }
4705                  if (isset($a['mac'])) {
4706                      $a['mac'] = array_intersect(
4707                          $a['mac'],
4708                          $this->getSupportedMACAlgorithms()