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