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