1<?php
2
3/**
4 * Pure-PHP implementation of SSHv1.
5 *
6 * PHP version 5
7 *
8 * Here's a short example of how to use this library:
9 * <code>
10 * <?php
11 *    include 'vendor/autoload.php';
12 *
13 *    $ssh = new \phpseclib\Net\SSH1('www.domain.tld');
14 *    if (!$ssh->login('username', 'password')) {
15 *        exit('Login Failed');
16 *    }
17 *
18 *    echo $ssh->exec('ls -la');
19 * ?>
20 * </code>
21 *
22 * Here's another short example:
23 * <code>
24 * <?php
25 *    include 'vendor/autoload.php';
26 *
27 *    $ssh = new \phpseclib\Net\SSH1('www.domain.tld');
28 *    if (!$ssh->login('username', 'password')) {
29 *        exit('Login Failed');
30 *    }
31 *
32 *    echo $ssh->read('username@username:~$');
33 *    $ssh->write("ls -la\n");
34 *    echo $ssh->read('username@username:~$');
35 * ?>
36 * </code>
37 *
38 * More information on the SSHv1 specification can be found by reading
39 * {@link http://www.snailbook.com/docs/protocol-1.5.txt protocol-1.5.txt}.
40 *
41 * @category  Net
42 * @package   SSH1
43 * @author    Jim Wigginton <terrafrost@php.net>
44 * @copyright 2007 Jim Wigginton
45 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
46 * @link      http://phpseclib.sourceforge.net
47 */
48
49namespace phpseclib\Net;
50
51use phpseclib\Crypt\DES;
52use phpseclib\Crypt\Random;
53use phpseclib\Crypt\TripleDES;
54use phpseclib\Math\BigInteger;
55
56/**
57 * Pure-PHP implementation of SSHv1.
58 *
59 * @package SSH1
60 * @author  Jim Wigginton <terrafrost@php.net>
61 * @access  public
62 */
63class SSH1
64{
65    /**#@+
66     * Encryption Methods
67     *
68     * @see \phpseclib\Net\SSH1::getSupportedCiphers()
69     * @access public
70     */
71    /**
72     * No encryption
73     *
74     * Not supported.
75     */
76    const CIPHER_NONE = 0;
77    /**
78     * IDEA in CFB mode
79     *
80     * Not supported.
81     */
82    const CIPHER_IDEA = 1;
83    /**
84     * DES in CBC mode
85     */
86    const CIPHER_DES = 2;
87    /**
88     * Triple-DES in CBC mode
89     *
90     * All implementations are required to support this
91     */
92    const CIPHER_3DES = 3;
93    /**
94     * TRI's Simple Stream encryption CBC
95     *
96     * Not supported nor is it defined in the official SSH1 specs.  OpenSSH, however, does define it (see cipher.h),
97     * although it doesn't use it (see cipher.c)
98     */
99    const CIPHER_BROKEN_TSS = 4;
100    /**
101     * RC4
102     *
103     * Not supported.
104     *
105     * @internal According to the SSH1 specs:
106     *
107     *        "The first 16 bytes of the session key are used as the key for
108     *         the server to client direction.  The remaining 16 bytes are used
109     *         as the key for the client to server direction.  This gives
110     *         independent 128-bit keys for each direction."
111     *
112     *     This library currently only supports encryption when the same key is being used for both directions.  This is
113     *     because there's only one $crypto object.  Two could be added ($encrypt and $decrypt, perhaps).
114     */
115    const CIPHER_RC4 = 5;
116    /**
117     * Blowfish
118     *
119     * Not supported nor is it defined in the official SSH1 specs.  OpenSSH, however, defines it (see cipher.h) and
120     * uses it (see cipher.c)
121     */
122    const CIPHER_BLOWFISH = 6;
123    /**#@-*/
124
125    /**#@+
126     * Authentication Methods
127     *
128     * @see \phpseclib\Net\SSH1::getSupportedAuthentications()
129     * @access public
130    */
131    /**
132     * .rhosts or /etc/hosts.equiv
133     */
134    const AUTH_RHOSTS = 1;
135    /**
136     * pure RSA authentication
137     */
138    const AUTH_RSA = 2;
139    /**
140     * password authentication
141     *
142     * This is the only method that is supported by this library.
143     */
144    const AUTH_PASSWORD = 3;
145    /**
146     * .rhosts with RSA host authentication
147     */
148    const AUTH_RHOSTS_RSA = 4;
149    /**#@-*/
150
151    /**#@+
152     * Terminal Modes
153     *
154     * @link http://3sp.com/content/developer/maverick-net/docs/Maverick.SSH.PseudoTerminalModesMembers.html
155     * @access private
156    */
157    const TTY_OP_END = 0;
158    /**#@-*/
159
160    /**
161     * The Response Type
162     *
163     * @see \phpseclib\Net\SSH1::_get_binary_packet()
164     * @access private
165     */
166    const RESPONSE_TYPE = 1;
167
168    /**
169     * The Response Data
170     *
171     * @see \phpseclib\Net\SSH1::_get_binary_packet()
172     * @access private
173     */
174    const RESPONSE_DATA = 2;
175
176    /**#@+
177     * Execution Bitmap Masks
178     *
179     * @see \phpseclib\Net\SSH1::bitmap
180     * @access private
181    */
182    const MASK_CONSTRUCTOR = 0x00000001;
183    const MASK_CONNECTED   = 0x00000002;
184    const MASK_LOGIN       = 0x00000004;
185    const MASK_SHELL       = 0x00000008;
186    /**#@-*/
187
188    /**#@+
189     * @access public
190     * @see \phpseclib\Net\SSH1::getLog()
191    */
192    /**
193     * Returns the message numbers
194     */
195    const LOG_SIMPLE = 1;
196    /**
197     * Returns the message content
198     */
199    const LOG_COMPLEX = 2;
200    /**
201     * Outputs the content real-time
202     */
203    const LOG_REALTIME = 3;
204    /**
205     * Dumps the content real-time to a file
206     */
207    const LOG_REALTIME_FILE = 4;
208    /**#@-*/
209
210    /**#@+
211     * @access public
212     * @see \phpseclib\Net\SSH1::read()
213    */
214    /**
215     * Returns when a string matching $expect exactly is found
216     */
217    const READ_SIMPLE = 1;
218    /**
219     * Returns when a string matching the regular expression $expect is found
220     */
221    const READ_REGEX = 2;
222    /**#@-*/
223
224    /**
225     * The SSH identifier
226     *
227     * @var string
228     * @access private
229     */
230    var $identifier = 'SSH-1.5-phpseclib';
231
232    /**
233     * The Socket Object
234     *
235     * @var object
236     * @access private
237     */
238    var $fsock;
239
240    /**
241     * The cryptography object
242     *
243     * @var object
244     * @access private
245     */
246    var $crypto = false;
247
248    /**
249     * Execution Bitmap
250     *
251     * The bits that are set represent functions that have been called already.  This is used to determine
252     * if a requisite function has been successfully executed.  If not, an error should be thrown.
253     *
254     * @var int
255     * @access private
256     */
257    var $bitmap = 0;
258
259    /**
260     * The Server Key Public Exponent
261     *
262     * Logged for debug purposes
263     *
264     * @see self::getServerKeyPublicExponent()
265     * @var string
266     * @access private
267     */
268    var $server_key_public_exponent;
269
270    /**
271     * The Server Key Public Modulus
272     *
273     * Logged for debug purposes
274     *
275     * @see self::getServerKeyPublicModulus()
276     * @var string
277     * @access private
278     */
279    var $server_key_public_modulus;
280
281    /**
282     * The Host Key Public Exponent
283     *
284     * Logged for debug purposes
285     *
286     * @see self::getHostKeyPublicExponent()
287     * @var string
288     * @access private
289     */
290    var $host_key_public_exponent;
291
292    /**
293     * The Host Key Public Modulus
294     *
295     * Logged for debug purposes
296     *
297     * @see self::getHostKeyPublicModulus()
298     * @var string
299     * @access private
300     */
301    var $host_key_public_modulus;
302
303    /**
304     * Supported Ciphers
305     *
306     * Logged for debug purposes
307     *
308     * @see self::getSupportedCiphers()
309     * @var array
310     * @access private
311     */
312    var $supported_ciphers = array(
313        self::CIPHER_NONE       => 'No encryption',
314        self::CIPHER_IDEA       => 'IDEA in CFB mode',
315        self::CIPHER_DES        => 'DES in CBC mode',
316        self::CIPHER_3DES       => 'Triple-DES in CBC mode',
317        self::CIPHER_BROKEN_TSS => 'TRI\'s Simple Stream encryption CBC',
318        self::CIPHER_RC4        => 'RC4',
319        self::CIPHER_BLOWFISH   => 'Blowfish'
320    );
321
322    /**
323     * Supported Authentications
324     *
325     * Logged for debug purposes
326     *
327     * @see self::getSupportedAuthentications()
328     * @var array
329     * @access private
330     */
331    var $supported_authentications = array(
332        self::AUTH_RHOSTS     => '.rhosts or /etc/hosts.equiv',
333        self::AUTH_RSA        => 'pure RSA authentication',
334        self::AUTH_PASSWORD   => 'password authentication',
335        self::AUTH_RHOSTS_RSA => '.rhosts with RSA host authentication'
336    );
337
338    /**
339     * Server Identification
340     *
341     * @see self::getServerIdentification()
342     * @var string
343     * @access private
344     */
345    var $server_identification = '';
346
347    /**
348     * Protocol Flags
349     *
350     * @see self::__construct()
351     * @var array
352     * @access private
353     */
354    var $protocol_flags = array();
355
356    /**
357     * Protocol Flag Log
358     *
359     * @see self::getLog()
360     * @var array
361     * @access private
362     */
363    var $protocol_flag_log = array();
364
365    /**
366     * Message Log
367     *
368     * @see self::getLog()
369     * @var array
370     * @access private
371     */
372    var $message_log = array();
373
374    /**
375     * Real-time log file pointer
376     *
377     * @see self::_append_log()
378     * @var resource
379     * @access private
380     */
381    var $realtime_log_file;
382
383    /**
384     * Real-time log file size
385     *
386     * @see self::_append_log()
387     * @var int
388     * @access private
389     */
390    var $realtime_log_size;
391
392    /**
393     * Real-time log file wrap boolean
394     *
395     * @see self::_append_log()
396     * @var bool
397     * @access private
398     */
399    var $realtime_log_wrap;
400
401    /**
402     * Interactive Buffer
403     *
404     * @see self::read()
405     * @var array
406     * @access private
407     */
408    var $interactiveBuffer = '';
409
410    /**
411     * Timeout
412     *
413     * @see self::setTimeout()
414     * @access private
415     */
416    var $timeout;
417
418    /**
419     * Current Timeout
420     *
421     * @see self::_get_channel_packet()
422     * @access private
423     */
424    var $curTimeout;
425
426    /**
427     * Log Boundary
428     *
429     * @see self::_format_log()
430     * @access private
431     */
432    var $log_boundary = ':';
433
434    /**
435     * Log Long Width
436     *
437     * @see self::_format_log()
438     * @access private
439     */
440    var $log_long_width = 65;
441
442    /**
443     * Log Short Width
444     *
445     * @see self::_format_log()
446     * @access private
447     */
448    var $log_short_width = 16;
449
450    /**
451     * Hostname
452     *
453     * @see self::__construct()
454     * @see self::_connect()
455     * @var string
456     * @access private
457     */
458    var $host;
459
460    /**
461     * Port Number
462     *
463     * @see self::__construct()
464     * @see self::_connect()
465     * @var int
466     * @access private
467     */
468    var $port;
469
470    /**
471     * Timeout for initial connection
472     *
473     * Set by the constructor call. Calling setTimeout() is optional. If it's not called functions like
474     * exec() won't timeout unless some PHP setting forces it too. The timeout specified in the constructor,
475     * however, is non-optional. There will be a timeout, whether or not you set it. If you don't it'll be
476     * 10 seconds. It is used by fsockopen() in that function.
477     *
478     * @see self::__construct()
479     * @see self::_connect()
480     * @var int
481     * @access private
482     */
483    var $connectionTimeout;
484
485    /**
486     * Default cipher
487     *
488     * @see self::__construct()
489     * @see self::_connect()
490     * @var int
491     * @access private
492     */
493    var $cipher;
494
495    /**
496     * Default Constructor.
497     *
498     * Connects to an SSHv1 server
499     *
500     * @param string $host
501     * @param int $port
502     * @param int $timeout
503     * @param int $cipher
504     * @return \phpseclib\Net\SSH1
505     * @access public
506     */
507    function __construct($host, $port = 22, $timeout = 10, $cipher = self::CIPHER_3DES)
508    {
509        $this->protocol_flags = array(
510            1  => 'NET_SSH1_MSG_DISCONNECT',
511            2  => 'NET_SSH1_SMSG_PUBLIC_KEY',
512            3  => 'NET_SSH1_CMSG_SESSION_KEY',
513            4  => 'NET_SSH1_CMSG_USER',
514            9  => 'NET_SSH1_CMSG_AUTH_PASSWORD',
515            10 => 'NET_SSH1_CMSG_REQUEST_PTY',
516            12 => 'NET_SSH1_CMSG_EXEC_SHELL',
517            13 => 'NET_SSH1_CMSG_EXEC_CMD',
518            14 => 'NET_SSH1_SMSG_SUCCESS',
519            15 => 'NET_SSH1_SMSG_FAILURE',
520            16 => 'NET_SSH1_CMSG_STDIN_DATA',
521            17 => 'NET_SSH1_SMSG_STDOUT_DATA',
522            18 => 'NET_SSH1_SMSG_STDERR_DATA',
523            19 => 'NET_SSH1_CMSG_EOF',
524            20 => 'NET_SSH1_SMSG_EXITSTATUS',
525            33 => 'NET_SSH1_CMSG_EXIT_CONFIRMATION'
526        );
527
528        $this->_define_array($this->protocol_flags);
529
530        $this->host = $host;
531        $this->port = $port;
532        $this->connectionTimeout = $timeout;
533        $this->cipher = $cipher;
534    }
535
536    /**
537     * Connect to an SSHv1 server
538     *
539     * @return bool
540     * @access private
541     */
542    function _connect()
543    {
544        $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->connectionTimeout);
545        if (!$this->fsock) {
546            user_error(rtrim("Cannot connect to {$this->host}:{$this->port}. Error $errno. $errstr"));
547            return false;
548        }
549
550        $this->server_identification = $init_line = fgets($this->fsock, 255);
551
552        if (defined('NET_SSH1_LOGGING')) {
553            $this->_append_log('<-', $this->server_identification);
554            $this->_append_log('->', $this->identifier . "\r\n");
555        }
556
557        if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) {
558            user_error('Can only connect to SSH servers');
559            return false;
560        }
561        if ($parts[1][0] != 1) {
562            user_error("Cannot connect to SSH $parts[1] servers");
563            return false;
564        }
565
566        fputs($this->fsock, $this->identifier."\r\n");
567
568        $response = $this->_get_binary_packet();
569        if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) {
570            user_error('Expected SSH_SMSG_PUBLIC_KEY');
571            return false;
572        }
573
574        $anti_spoofing_cookie = $this->_string_shift($response[self::RESPONSE_DATA], 8);
575
576        $this->_string_shift($response[self::RESPONSE_DATA], 4);
577
578        if (strlen($response[self::RESPONSE_DATA]) < 2) {
579            return false;
580        }
581        $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
582        $server_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
583        $this->server_key_public_exponent = $server_key_public_exponent;
584
585        if (strlen($response[self::RESPONSE_DATA]) < 2) {
586            return false;
587        }
588        $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
589        $server_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
590
591        $this->server_key_public_modulus = $server_key_public_modulus;
592
593        $this->_string_shift($response[self::RESPONSE_DATA], 4);
594
595        if (strlen($response[self::RESPONSE_DATA]) < 2) {
596            return false;
597        }
598        $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
599        $host_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
600        $this->host_key_public_exponent = $host_key_public_exponent;
601
602        if (strlen($response[self::RESPONSE_DATA]) < 2) {
603            return false;
604        }
605        $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
606        $host_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
607
608        $this->host_key_public_modulus = $host_key_public_modulus;
609
610        $this->_string_shift($response[self::RESPONSE_DATA], 4);
611
612        // get a list of the supported ciphers
613        if (strlen($response[self::RESPONSE_DATA]) < 4) {
614            return false;
615        }
616        extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4)));
617
618        foreach ($this->supported_ciphers as $mask => $name) {
619            if (($supported_ciphers_mask & (1 << $mask)) == 0) {
620                unset($this->supported_ciphers[$mask]);
621            }
622        }
623
624        // get a list of the supported authentications
625        if (strlen($response[self::RESPONSE_DATA]) < 4) {
626            return false;
627        }
628        extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4)));
629        foreach ($this->supported_authentications as $mask => $name) {
630            if (($supported_authentications_mask & (1 << $mask)) == 0) {
631                unset($this->supported_authentications[$mask]);
632            }
633        }
634
635        $session_id = pack('H*', md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie));
636
637        $session_key = Random::string(32);
638        $double_encrypted_session_key = $session_key ^ str_pad($session_id, 32, chr(0));
639
640        if ($server_key_public_modulus->compare($host_key_public_modulus) < 0) {
641            $double_encrypted_session_key = $this->_rsa_crypt(
642                $double_encrypted_session_key,
643                array(
644                    $server_key_public_exponent,
645                    $server_key_public_modulus
646                )
647            );
648            $double_encrypted_session_key = $this->_rsa_crypt(
649                $double_encrypted_session_key,
650                array(
651                    $host_key_public_exponent,
652                    $host_key_public_modulus
653                )
654            );
655        } else {
656            $double_encrypted_session_key = $this->_rsa_crypt(
657                $double_encrypted_session_key,
658                array(
659                    $host_key_public_exponent,
660                    $host_key_public_modulus
661                )
662            );
663            $double_encrypted_session_key = $this->_rsa_crypt(
664                $double_encrypted_session_key,
665                array(
666                    $server_key_public_exponent,
667                    $server_key_public_modulus
668                )
669            );
670        }
671
672        $cipher = isset($this->supported_ciphers[$this->cipher]) ? $this->cipher : self::CIPHER_3DES;
673        $data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0);
674
675        if (!$this->_send_binary_packet($data)) {
676            user_error('Error sending SSH_CMSG_SESSION_KEY');
677            return false;
678        }
679
680        switch ($cipher) {
681            //case self::CIPHER_NONE:
682            //    $this->crypto = new \phpseclib\Crypt\Null();
683            //    break;
684            case self::CIPHER_DES:
685                $this->crypto = new DES();
686                $this->crypto->disablePadding();
687                $this->crypto->enableContinuousBuffer();
688                $this->crypto->setKey(substr($session_key, 0,  8));
689                break;
690            case self::CIPHER_3DES:
691                $this->crypto = new TripleDES(TripleDES::MODE_3CBC);
692                $this->crypto->disablePadding();
693                $this->crypto->enableContinuousBuffer();
694                $this->crypto->setKey(substr($session_key, 0, 24));
695                break;
696            //case self::CIPHER_RC4:
697            //    $this->crypto = new RC4();
698            //    $this->crypto->enableContinuousBuffer();
699            //    $this->crypto->setKey(substr($session_key, 0,  16));
700            //    break;
701        }
702
703        $response = $this->_get_binary_packet();
704
705        if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
706            user_error('Expected SSH_SMSG_SUCCESS');
707            return false;
708        }
709
710        $this->bitmap = self::MASK_CONNECTED;
711
712        return true;
713    }
714
715    /**
716     * Login
717     *
718     * @param string $username
719     * @param string $password
720     * @return bool
721     * @access public
722     */
723    function login($username, $password = '')
724    {
725        if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
726            $this->bitmap |= self::MASK_CONSTRUCTOR;
727            if (!$this->_connect()) {
728                return false;
729            }
730        }
731
732        if (!($this->bitmap & self::MASK_CONNECTED)) {
733            return false;
734        }
735
736        $data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username);
737
738        if (!$this->_send_binary_packet($data)) {
739            user_error('Error sending SSH_CMSG_USER');
740            return false;
741        }
742
743        $response = $this->_get_binary_packet();
744
745        if ($response === true) {
746            return false;
747        }
748        if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
749            $this->bitmap |= self::MASK_LOGIN;
750            return true;
751        } elseif ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) {
752            user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE');
753            return false;
754        }
755
756        $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password);
757
758        if (!$this->_send_binary_packet($data)) {
759            user_error('Error sending SSH_CMSG_AUTH_PASSWORD');
760            return false;
761        }
762
763        // remove the username and password from the last logged packet
764        if (defined('NET_SSH1_LOGGING') && NET_SSH1_LOGGING == self::LOG_COMPLEX) {
765            $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen('password'), 'password');
766            $this->message_log[count($this->message_log) - 1] = $data;
767        }
768
769        $response = $this->_get_binary_packet();
770
771        if ($response === true) {
772            return false;
773        }
774        if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
775            $this->bitmap |= self::MASK_LOGIN;
776            return true;
777        } elseif ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE) {
778            return false;
779        } else {
780            user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE');
781            return false;
782        }
783    }
784
785    /**
786     * Set Timeout
787     *
788     * $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.
789     * Setting $timeout to false or 0 will mean there is no timeout.
790     *
791     * @param mixed $timeout
792     */
793    function setTimeout($timeout)
794    {
795        $this->timeout = $this->curTimeout = $timeout;
796    }
797
798    /**
799     * Executes a command on a non-interactive shell, returns the output, and quits.
800     *
801     * An SSH1 server will close the connection after a command has been executed on a non-interactive shell.  SSH2
802     * servers don't, however, this isn't an SSH2 client.  The way this works, on the server, is by initiating a
803     * shell with the -s option, as discussed in the following links:
804     *
805     * {@link http://www.faqs.org/docs/bashman/bashref_65.html http://www.faqs.org/docs/bashman/bashref_65.html}
806     * {@link http://www.faqs.org/docs/bashman/bashref_62.html http://www.faqs.org/docs/bashman/bashref_62.html}
807     *
808     * To execute further commands, a new \phpseclib\Net\SSH1 object will need to be created.
809     *
810     * Returns false on failure and the output, otherwise.
811     *
812     * @see self::interactiveRead()
813     * @see self::interactiveWrite()
814     * @param string $cmd
815     * @param bool $block
816     * @return mixed
817     * @access public
818     */
819    function exec($cmd, $block = true)
820    {
821        if (!($this->bitmap & self::MASK_LOGIN)) {
822            user_error('Operation disallowed prior to login()');
823            return false;
824        }
825
826        $data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd);
827
828        if (!$this->_send_binary_packet($data)) {
829            user_error('Error sending SSH_CMSG_EXEC_CMD');
830            return false;
831        }
832
833        if (!$block) {
834            return true;
835        }
836
837        $output = '';
838        $response = $this->_get_binary_packet();
839
840        if ($response !== false) {
841            do {
842                $output.= substr($response[self::RESPONSE_DATA], 4);
843                $response = $this->_get_binary_packet();
844            } while (is_array($response) && $response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_EXITSTATUS);
845        }
846
847        $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION);
848
849        // i don't think it's really all that important if this packet gets sent or not.
850        $this->_send_binary_packet($data);
851
852        fclose($this->fsock);
853
854        // reset the execution bitmap - a new \phpseclib\Net\SSH1 object needs to be created.
855        $this->bitmap = 0;
856
857        return $output;
858    }
859
860    /**
861     * Creates an interactive shell
862     *
863     * @see self::interactiveRead()
864     * @see self::interactiveWrite()
865     * @return bool
866     * @access private
867     */
868    function _initShell()
869    {
870        // connect using the sample parameters in protocol-1.5.txt.
871        // according to wikipedia.org's entry on text terminals, "the fundamental type of application running on a text
872        // terminal is a command line interpreter or shell".  thus, opening a terminal session to run the shell.
873        $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, self::TTY_OP_END);
874
875        if (!$this->_send_binary_packet($data)) {
876            user_error('Error sending SSH_CMSG_REQUEST_PTY');
877            return false;
878        }
879
880        $response = $this->_get_binary_packet();
881
882        if ($response === true) {
883            return false;
884        }
885        if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
886            user_error('Expected SSH_SMSG_SUCCESS');
887            return false;
888        }
889
890        $data = pack('C', NET_SSH1_CMSG_EXEC_SHELL);
891
892        if (!$this->_send_binary_packet($data)) {
893            user_error('Error sending SSH_CMSG_EXEC_SHELL');
894            return false;
895        }
896
897        $this->bitmap |= self::MASK_SHELL;
898
899        //stream_set_blocking($this->fsock, 0);
900
901        return true;
902    }
903
904    /**
905     * Inputs a command into an interactive shell.
906     *
907     * @see self::interactiveWrite()
908     * @param string $cmd
909     * @return bool
910     * @access public
911     */
912    function write($cmd)
913    {
914        return $this->interactiveWrite($cmd);
915    }
916
917    /**
918     * Returns the output of an interactive shell when there's a match for $expect
919     *
920     * $expect can take the form of a string literal or, if $mode == self::READ_REGEX,
921     * a regular expression.
922     *
923     * @see self::write()
924     * @param string $expect
925     * @param int $mode
926     * @return bool
927     * @access public
928     */
929    function read($expect, $mode = self::READ_SIMPLE)
930    {
931        if (!($this->bitmap & self::MASK_LOGIN)) {
932            user_error('Operation disallowed prior to login()');
933            return false;
934        }
935
936        if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
937            user_error('Unable to initiate an interactive shell session');
938            return false;
939        }
940
941        $match = $expect;
942        while (true) {
943            if ($mode == self::READ_REGEX) {
944                preg_match($expect, $this->interactiveBuffer, $matches);
945                $match = isset($matches[0]) ? $matches[0] : '';
946            }
947            $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
948            if ($pos !== false) {
949                return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
950            }
951            $response = $this->_get_binary_packet();
952
953            if ($response === true) {
954                return $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer));
955            }
956            $this->interactiveBuffer.= substr($response[self::RESPONSE_DATA], 4);
957        }
958    }
959
960    /**
961     * Inputs a command into an interactive shell.
962     *
963     * @see self::interactiveRead()
964     * @param string $cmd
965     * @return bool
966     * @access public
967     */
968    function interactiveWrite($cmd)
969    {
970        if (!($this->bitmap & self::MASK_LOGIN)) {
971            user_error('Operation disallowed prior to login()');
972            return false;
973        }
974
975        if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
976            user_error('Unable to initiate an interactive shell session');
977            return false;
978        }
979
980        $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd);
981
982        if (!$this->_send_binary_packet($data)) {
983            user_error('Error sending SSH_CMSG_STDIN');
984            return false;
985        }
986
987        return true;
988    }
989
990    /**
991     * Returns the output of an interactive shell when no more output is available.
992     *
993     * Requires PHP 4.3.0 or later due to the use of the stream_select() function.  If you see stuff like
994     * "^[[00m", you're seeing ANSI escape codes.  According to
995     * {@link http://support.microsoft.com/kb/101875 How to Enable ANSI.SYS in a Command Window}, "Windows NT
996     * does not support ANSI escape sequences in Win32 Console applications", so if you're a Windows user,
997     * there's not going to be much recourse.
998     *
999     * @see self::interactiveRead()
1000     * @return string
1001     * @access public
1002     */
1003    function interactiveRead()
1004    {
1005        if (!($this->bitmap & self::MASK_LOGIN)) {
1006            user_error('Operation disallowed prior to login()');
1007            return false;
1008        }
1009
1010        if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
1011            user_error('Unable to initiate an interactive shell session');
1012            return false;
1013        }
1014
1015        $read = array($this->fsock);
1016        $write = $except = null;
1017        if (stream_select($read, $write, $except, 0)) {
1018            $response = $this->_get_binary_packet();
1019            return substr($response[self::RESPONSE_DATA], 4);
1020        } else {
1021            return '';
1022        }
1023    }
1024
1025    /**
1026     * Disconnect
1027     *
1028     * @access public
1029     */
1030    function disconnect()
1031    {
1032        $this->_disconnect();
1033    }
1034
1035    /**
1036     * Destructor.
1037     *
1038     * Will be called, automatically, if you're supporting just PHP5.  If you're supporting PHP4, you'll need to call
1039     * disconnect().
1040     *
1041     * @access public
1042     */
1043    function __destruct()
1044    {
1045        $this->_disconnect();
1046    }
1047
1048    /**
1049     * Disconnect
1050     *
1051     * @param string $msg
1052     * @access private
1053     */
1054    function _disconnect($msg = 'Client Quit')
1055    {
1056        if ($this->bitmap) {
1057            $data = pack('C', NET_SSH1_CMSG_EOF);
1058            $this->_send_binary_packet($data);
1059            /*
1060            $response = $this->_get_binary_packet();
1061            if ($response === true) {
1062                $response = array(self::RESPONSE_TYPE => -1);
1063            }
1064            switch ($response[self::RESPONSE_TYPE]) {
1065                case NET_SSH1_SMSG_EXITSTATUS:
1066                    $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION);
1067                    break;
1068                default:
1069                    $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg);
1070            }
1071            */
1072            $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg);
1073
1074            $this->_send_binary_packet($data);
1075            fclose($this->fsock);
1076            $this->bitmap = 0;
1077        }
1078    }
1079
1080    /**
1081     * Gets Binary Packets
1082     *
1083     * See 'The Binary Packet Protocol' of protocol-1.5.txt for more info.
1084     *
1085     * Also, this function could be improved upon by adding detection for the following exploit:
1086     * http://www.securiteam.com/securitynews/5LP042K3FY.html
1087     *
1088     * @see self::_send_binary_packet()
1089     * @return array
1090     * @access private
1091     */
1092    function _get_binary_packet()
1093    {
1094        if (feof($this->fsock)) {
1095            //user_error('connection closed prematurely');
1096            return false;
1097        }
1098
1099        if ($this->curTimeout) {
1100            $read = array($this->fsock);
1101            $write = $except = null;
1102
1103            $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
1104            $sec = floor($this->curTimeout);
1105            $usec = 1000000 * ($this->curTimeout - $sec);
1106            // on windows this returns a "Warning: Invalid CRT parameters detected" error
1107            if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
1108                //$this->_disconnect('Timeout');
1109                return true;
1110            }
1111            $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
1112            $this->curTimeout-= $elapsed;
1113        }
1114
1115        $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
1116        $data = fread($this->fsock, 4);
1117        if (strlen($data) < 4) {
1118            return false;
1119        }
1120        $temp = unpack('Nlength', $data);
1121
1122        $padding_length = 8 - ($temp['length'] & 7);
1123        $length = $temp['length'] + $padding_length;
1124        $raw = '';
1125
1126        while ($length > 0) {
1127            $temp = fread($this->fsock, $length);
1128            if (strlen($temp) != $length) {
1129                return false;
1130            }
1131            $raw.= $temp;
1132            $length-= strlen($temp);
1133        }
1134        $stop = strtok(microtime(), ' ') + strtok('');
1135
1136        if (strlen($raw) && $this->crypto !== false) {
1137            $raw = $this->crypto->decrypt($raw);
1138        }
1139
1140        $padding = substr($raw, 0, $padding_length);
1141        $type = $raw[$padding_length];
1142        $data = substr($raw, $padding_length + 1, -4);
1143
1144        if (strlen($raw) < 4) {
1145            return false;
1146        }
1147        $temp = unpack('Ncrc', substr($raw, -4));
1148
1149        //if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) {
1150        //    user_error('Bad CRC in packet from server');
1151        //    return false;
1152        //}
1153
1154        $type = ord($type);
1155
1156        if (defined('NET_SSH1_LOGGING')) {
1157            $temp = isset($this->protocol_flags[$type]) ? $this->protocol_flags[$type] : 'UNKNOWN';
1158            $temp = '<- ' . $temp .
1159                    ' (' . round($stop - $start, 4) . 's)';
1160            $this->_append_log($temp, $data);
1161        }
1162
1163        return array(
1164            self::RESPONSE_TYPE => $type,
1165            self::RESPONSE_DATA => $data
1166        );
1167    }
1168
1169    /**
1170     * Sends Binary Packets
1171     *
1172     * Returns true on success, false on failure.
1173     *
1174     * @see self::_get_binary_packet()
1175     * @param string $data
1176     * @return bool
1177     * @access private
1178     */
1179    function _send_binary_packet($data)
1180    {
1181        if (feof($this->fsock)) {
1182            //user_error('connection closed prematurely');
1183            return false;
1184        }
1185
1186        $length = strlen($data) + 4;
1187
1188        $padding = Random::string(8 - ($length & 7));
1189
1190        $orig = $data;
1191        $data = $padding . $data;
1192        $data.= pack('N', $this->_crc($data));
1193
1194        if ($this->crypto !== false) {
1195            $data = $this->crypto->encrypt($data);
1196        }
1197
1198        $packet = pack('Na*', $length, $data);
1199
1200        $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
1201        $result = strlen($packet) == fputs($this->fsock, $packet);
1202        $stop = strtok(microtime(), ' ') + strtok('');
1203
1204        if (defined('NET_SSH1_LOGGING')) {
1205            $temp = isset($this->protocol_flags[ord($orig[0])]) ? $this->protocol_flags[ord($orig[0])] : 'UNKNOWN';
1206            $temp = '-> ' . $temp .
1207                    ' (' . round($stop - $start, 4) . 's)';
1208            $this->_append_log($temp, $orig);
1209        }
1210
1211        return $result;
1212    }
1213
1214    /**
1215     * Cyclic Redundancy Check (CRC)
1216     *
1217     * PHP's crc32 function is implemented slightly differently than the one that SSH v1 uses, so
1218     * we've reimplemented it. A more detailed discussion of the differences can be found after
1219     * $crc_lookup_table's initialization.
1220     *
1221     * @see self::_get_binary_packet()
1222     * @see self::_send_binary_packet()
1223     * @param string $data
1224     * @return int
1225     * @access private
1226     */
1227    function _crc($data)
1228    {
1229        static $crc_lookup_table = array(
1230            0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
1231            0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
1232            0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
1233            0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
1234            0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
1235            0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
1236            0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
1237            0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
1238            0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
1239            0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
1240            0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
1241            0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
1242            0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
1243            0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
1244            0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
1245            0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
1246            0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
1247            0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
1248            0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
1249            0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
1250            0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
1251            0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
1252            0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
1253            0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
1254            0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
1255            0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
1256            0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
1257            0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
1258            0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
1259            0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
1260            0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
1261            0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
1262            0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
1263            0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
1264            0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
1265            0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
1266            0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
1267            0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
1268            0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
1269            0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
1270            0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
1271            0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
1272            0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
1273            0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
1274            0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
1275            0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
1276            0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
1277            0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
1278            0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
1279            0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
1280            0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
1281            0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
1282            0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
1283            0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
1284            0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
1285            0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
1286            0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
1287            0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
1288            0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
1289            0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
1290            0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
1291            0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
1292            0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
1293            0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
1294        );
1295
1296        // For this function to yield the same output as PHP's crc32 function, $crc would have to be
1297        // set to 0xFFFFFFFF, initially - not 0x00000000 as it currently is.
1298        $crc = 0x00000000;
1299        $length = strlen($data);
1300
1301        for ($i=0; $i<$length; $i++) {
1302            // We AND $crc >> 8 with 0x00FFFFFF because we want the eight newly added bits to all
1303            // be zero.  PHP, unfortunately, doesn't always do this.  0x80000000 >> 8, as an example,
1304            // yields 0xFF800000 - not 0x00800000.  The following link elaborates:
1305            // http://www.php.net/manual/en/language.operators.bitwise.php#57281
1306            $crc = (($crc >> 8) & 0x00FFFFFF) ^ $crc_lookup_table[($crc & 0xFF) ^ ord($data[$i])];
1307        }
1308
1309        // In addition to having to set $crc to 0xFFFFFFFF, initially, the return value must be XOR'd with
1310        // 0xFFFFFFFF for this function to return the same thing that PHP's crc32 function would.
1311        return $crc;
1312    }
1313
1314    /**
1315     * String Shift
1316     *
1317     * Inspired by array_shift
1318     *
1319     * @param string $string
1320     * @param int $index
1321     * @return string
1322     * @access private
1323     */
1324    function _string_shift(&$string, $index = 1)
1325    {
1326        $substr = substr($string, 0, $index);
1327        $string = substr($string, $index);
1328        return $substr;
1329    }
1330
1331    /**
1332     * RSA Encrypt
1333     *
1334     * Returns mod(pow($m, $e), $n), where $n should be the product of two (large) primes $p and $q and where $e
1335     * should be a number with the property that gcd($e, ($p - 1) * ($q - 1)) == 1.  Could just make anything that
1336     * calls this call modexp, instead, but I think this makes things clearer, maybe...
1337     *
1338     * @see self::__construct()
1339     * @param BigInteger $m
1340     * @param array $key
1341     * @return BigInteger
1342     * @access private
1343     */
1344    function _rsa_crypt($m, $key)
1345    {
1346        /*
1347        $rsa = new RSA();
1348        $rsa->loadKey($key, RSA::PUBLIC_FORMAT_RAW);
1349        $rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
1350        return $rsa->encrypt($m);
1351        */
1352
1353        // To quote from protocol-1.5.txt:
1354        // The most significant byte (which is only partial as the value must be
1355        // less than the public modulus, which is never a power of two) is zero.
1356        //
1357        // The next byte contains the value 2 (which stands for public-key
1358        // encrypted data in the PKCS standard [PKCS#1]).  Then, there are non-
1359        // zero random bytes to fill any unused space, a zero byte, and the data
1360        // to be encrypted in the least significant bytes, the last byte of the
1361        // data in the least significant byte.
1362
1363        // Presumably the part of PKCS#1 they're refering to is "Section 7.2.1 Encryption Operation",
1364        // under "7.2 RSAES-PKCS1-v1.5" and "7 Encryption schemes" of the following URL:
1365        // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
1366        $modulus = $key[1]->toBytes();
1367        $length = strlen($modulus) - strlen($m) - 3;
1368        $random = '';
1369        while (strlen($random) != $length) {
1370            $block = Random::string($length - strlen($random));
1371            $block = str_replace("\x00", '', $block);
1372            $random.= $block;
1373        }
1374        $temp = chr(0) . chr(2) . $random . chr(0) . $m;
1375
1376        $m = new BigInteger($temp, 256);
1377        $m = $m->modPow($key[0], $key[1]);
1378
1379        return $m->toBytes();
1380    }
1381
1382    /**
1383     * Define Array
1384     *
1385     * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
1386     * named constants from it, using the value as the name of the constant and the index as the value of the constant.
1387     * If any of the constants that would be defined already exists, none of the constants will be defined.
1388     *
1389     * @access private
1390     */
1391    function _define_array()
1392    {
1393        $args = func_get_args();
1394        foreach ($args as $arg) {
1395            foreach ($arg as $key => $value) {
1396                if (!defined($value)) {
1397                    define($value, $key);
1398                } else {
1399                    break 2;
1400                }
1401            }
1402        }
1403    }
1404
1405    /**
1406     * Returns a log of the packets that have been sent and received.
1407     *
1408     * Returns a string if NET_SSH1_LOGGING == self::LOG_COMPLEX, an array if NET_SSH1_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH1_LOGGING')
1409     *
1410     * @access public
1411     * @return array|false|string
1412     */
1413    function getLog()
1414    {
1415        if (!defined('NET_SSH1_LOGGING')) {
1416            return false;
1417        }
1418
1419        switch (NET_SSH1_LOGGING) {
1420            case self::LOG_SIMPLE:
1421                return $this->message_number_log;
1422                break;
1423            case self::LOG_COMPLEX:
1424                return $this->_format_log($this->message_log, $this->protocol_flags_log);
1425                break;
1426            default:
1427                return false;
1428        }
1429    }
1430
1431    /**
1432     * Formats a log for printing
1433     *
1434     * @param array $message_log
1435     * @param array $message_number_log
1436     * @access private
1437     * @return string
1438     */
1439    function _format_log($message_log, $message_number_log)
1440    {
1441        $output = '';
1442        for ($i = 0; $i < count($message_log); $i++) {
1443            $output.= $message_number_log[$i] . "\r\n";
1444            $current_log = $message_log[$i];
1445            $j = 0;
1446            do {
1447                if (strlen($current_log)) {
1448                    $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0  ';
1449                }
1450                $fragment = $this->_string_shift($current_log, $this->log_short_width);
1451                $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary));
1452                // replace non ASCII printable characters with dots
1453                // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
1454                // also replace < with a . since < messes up the output on web browsers
1455                $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
1456                $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
1457                $j++;
1458            } while (strlen($current_log));
1459            $output.= "\r\n";
1460        }
1461
1462        return $output;
1463    }
1464
1465    /**
1466     * Helper function for _format_log
1467     *
1468     * For use with preg_replace_callback()
1469     *
1470     * @param array $matches
1471     * @access private
1472     * @return string
1473     */
1474    function _format_log_helper($matches)
1475    {
1476        return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT);
1477    }
1478
1479    /**
1480     * Return the server key public exponent
1481     *
1482     * Returns, by default, the base-10 representation.  If $raw_output is set to true, returns, instead,
1483     * the raw bytes.  This behavior is similar to PHP's md5() function.
1484     *
1485     * @param bool $raw_output
1486     * @return string
1487     * @access public
1488     */
1489    function getServerKeyPublicExponent($raw_output = false)
1490    {
1491        return $raw_output ? $this->server_key_public_exponent->toBytes() : $this->server_key_public_exponent->toString();
1492    }
1493
1494    /**
1495     * Return the server key public modulus
1496     *
1497     * Returns, by default, the base-10 representation.  If $raw_output is set to true, returns, instead,
1498     * the raw bytes.  This behavior is similar to PHP's md5() function.
1499     *
1500     * @param bool $raw_output
1501     * @return string
1502     * @access public
1503     */
1504    function getServerKeyPublicModulus($raw_output = false)
1505    {
1506        return $raw_output ? $this->server_key_public_modulus->toBytes() : $this->server_key_public_modulus->toString();
1507    }
1508
1509    /**
1510     * Return the host key public exponent
1511     *
1512     * Returns, by default, the base-10 representation.  If $raw_output is set to true, returns, instead,
1513     * the raw bytes.  This behavior is similar to PHP's md5() function.
1514     *
1515     * @param bool $raw_output
1516     * @return string
1517     * @access public
1518     */
1519    function getHostKeyPublicExponent($raw_output = false)
1520    {
1521        return $raw_output ? $this->host_key_public_exponent->toBytes() : $this->host_key_public_exponent->toString();
1522    }
1523
1524    /**
1525     * Return the host key public modulus
1526     *
1527     * Returns, by default, the base-10 representation.  If $raw_output is set to true, returns, instead,
1528     * the raw bytes.  This behavior is similar to PHP's md5() function.
1529     *
1530     * @param bool $raw_output
1531     * @return string
1532     * @access public
1533     */
1534    function getHostKeyPublicModulus($raw_output = false)
1535    {
1536        return $raw_output ? $this->host_key_public_modulus->toBytes() : $this->host_key_public_modulus->toString();
1537    }
1538
1539    /**
1540     * Return a list of ciphers supported by SSH1 server.
1541     *
1542     * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output
1543     * is set to true, returns, instead, an array of constants.  ie. instead of array('Triple-DES in CBC mode'), you'll
1544     * get array(self::CIPHER_3DES).
1545     *
1546     * @param bool $raw_output
1547     * @return array
1548     * @access public
1549     */
1550    function getSupportedCiphers($raw_output = false)
1551    {
1552        return $raw_output ? array_keys($this->supported_ciphers) : array_values($this->supported_ciphers);
1553    }
1554
1555    /**
1556     * Return a list of authentications supported by SSH1 server.
1557     *
1558     * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output
1559     * is set to true, returns, instead, an array of constants.  ie. instead of array('password authentication'), you'll
1560     * get array(self::AUTH_PASSWORD).
1561     *
1562     * @param bool $raw_output
1563     * @return array
1564     * @access public
1565     */
1566    function getSupportedAuthentications($raw_output = false)
1567    {
1568        return $raw_output ? array_keys($this->supported_authentications) : array_values($this->supported_authentications);
1569    }
1570
1571    /**
1572     * Return the server identification.
1573     *
1574     * @return string
1575     * @access public
1576     */
1577    function getServerIdentification()
1578    {
1579        return rtrim($this->server_identification);
1580    }
1581
1582    /**
1583     * Logs data packets
1584     *
1585     * Makes sure that only the last 1MB worth of packets will be logged
1586     *
1587     * @param int $protocol_flags
1588     * @param string $message
1589     * @access private
1590     */
1591    function _append_log($protocol_flags, $message)
1592    {
1593        switch (NET_SSH1_LOGGING) {
1594            // useful for benchmarks
1595            case self::LOG_SIMPLE:
1596                $this->protocol_flags_log[] = $protocol_flags;
1597                break;
1598            // the most useful log for SSH1
1599            case self::LOG_COMPLEX:
1600                $this->protocol_flags_log[] = $protocol_flags;
1601                $this->_string_shift($message);
1602                $this->log_size+= strlen($message);
1603                $this->message_log[] = $message;
1604                while ($this->log_size > self::LOG_MAX_SIZE) {
1605                    $this->log_size-= strlen(array_shift($this->message_log));
1606                    array_shift($this->protocol_flags_log);
1607                }
1608                break;
1609            // dump the output out realtime; packets may be interspersed with non packets,
1610            // passwords won't be filtered out and select other packets may not be correctly
1611            // identified
1612            case self::LOG_REALTIME:
1613                echo "<pre>\r\n" . $this->_format_log(array($message), array($protocol_flags)) . "\r\n</pre>\r\n";
1614                @flush();
1615                @ob_flush();
1616                break;
1617            // basically the same thing as self::LOG_REALTIME with the caveat that self::LOG_REALTIME_FILE
1618            // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE.
1619            // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
1620            // at the beginning of the file
1621            case self::LOG_REALTIME_FILE:
1622                if (!isset($this->realtime_log_file)) {
1623                    // PHP doesn't seem to like using constants in fopen()
1624                    $filename = self::LOG_REALTIME_FILE;
1625                    $fp = fopen($filename, 'w');
1626                    $this->realtime_log_file = $fp;
1627                }
1628                if (!is_resource($this->realtime_log_file)) {
1629                    break;
1630                }
1631                $entry = $this->_format_log(array($message), array($protocol_flags));
1632                if ($this->realtime_log_wrap) {
1633                    $temp = "<<< START >>>\r\n";
1634                    $entry.= $temp;
1635                    fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
1636                }
1637                $this->realtime_log_size+= strlen($entry);
1638                if ($this->realtime_log_size > self::LOG_MAX_SIZE) {
1639                    fseek($this->realtime_log_file, 0);
1640                    $this->realtime_log_size = strlen($entry);
1641                    $this->realtime_log_wrap = true;
1642                }
1643                fputs($this->realtime_log_file, $entry);
1644        }
1645    }
1646}
1647