Subversion Repositories oidplus

Rev

Rev 846 | Rev 1042 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 846 Rev 874
Line 35... Line 35...
35
 *    $ssh->write("ls -la\n");
35
 *    $ssh->write("ls -la\n");
36
 *    echo $ssh->read('username@username:~$');
36
 *    echo $ssh->read('username@username:~$');
37
 * ?>
37
 * ?>
38
 * </code>
38
 * </code>
39
 *
39
 *
-
 
40
 * @category  Net
-
 
41
 * @package   SSH2
40
 * @author    Jim Wigginton <terrafrost@php.net>
42
 * @author    Jim Wigginton <terrafrost@php.net>
41
 * @copyright 2007 Jim Wigginton
43
 * @copyright 2007 Jim Wigginton
42
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
44
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
43
 * @link      http://phpseclib.sourceforge.net
45
 * @link      http://phpseclib.sourceforge.net
44
 */
46
 */
Line 58... Line 60...
58
use phpseclib3\Crypt\Hash;
60
use phpseclib3\Crypt\Hash;
59
use phpseclib3\Crypt\Random;
61
use phpseclib3\Crypt\Random;
60
use phpseclib3\Crypt\RC4;
62
use phpseclib3\Crypt\RC4;
61
use phpseclib3\Crypt\Rijndael;
63
use phpseclib3\Crypt\Rijndael;
62
use phpseclib3\Crypt\RSA;
64
use phpseclib3\Crypt\RSA;
63
use phpseclib3\Crypt\TripleDES;
65
use phpseclib3\Crypt\TripleDES; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
64
use phpseclib3\Crypt\Twofish;
66
use phpseclib3\Crypt\Twofish;
65
use phpseclib3\Exception\ConnectionClosedException;
67
use phpseclib3\Exception\ConnectionClosedException;
66
use phpseclib3\Exception\InsufficientSetupException;
68
use phpseclib3\Exception\InsufficientSetupException;
67
use phpseclib3\Exception\NoSupportedAlgorithmsException;
69
use phpseclib3\Exception\NoSupportedAlgorithmsException;
68
use phpseclib3\Exception\UnableToConnectException;
70
use phpseclib3\Exception\UnableToConnectException;
69
use phpseclib3\Exception\UnsupportedAlgorithmException;
71
use phpseclib3\Exception\UnsupportedAlgorithmException;
70
use phpseclib3\Exception\UnsupportedCurveException;
72
use phpseclib3\Exception\UnsupportedCurveException;
71
use phpseclib3\Math\BigInteger;
73
use phpseclib3\Math\BigInteger;
72
use phpseclib3\Net\SSH2\ChannelConnectionFailureReason;
-
 
73
use phpseclib3\Net\SSH2\DisconnectReason;
-
 
74
use phpseclib3\Net\SSH2\MessageType;
-
 
75
use phpseclib3\Net\SSH2\MessageTypeExtra;
-
 
76
use phpseclib3\Net\SSH2\TerminalMode;
-
 
77
use phpseclib3\System\SSH\Agent;
74
use phpseclib3\System\SSH\Agent;
78
 
75
 
79
/**
76
/**
80
 * Pure-PHP implementation of SSHv2.
77
 * Pure-PHP implementation of SSHv2.
81
 *
78
 *
-
 
79
 * @package SSH2
82
 * @author  Jim Wigginton <terrafrost@php.net>
80
 * @author  Jim Wigginton <terrafrost@php.net>
-
 
81
 * @access  public
83
 */
82
 */
84
class SSH2
83
class SSH2
85
{
84
{
86
    /**#@+
85
    /**#@+
87
     * Compression Types
86
     * Compression Types
88
     *
87
     *
-
 
88
     * @access private
89
     */
89
     */
90
    /**
90
    /**
91
     * No compression
91
     * No compression
92
     */
92
     */
93
    const NET_SSH2_COMPRESSION_NONE = 1;
93
    const NET_SSH2_COMPRESSION_NONE = 1;
Line 121... Line 121...
121
     *     open request, and 'sender channel' is the channel number allocated by
121
     *     open request, and 'sender channel' is the channel number allocated by
122
     *     the other side.
122
     *     the other side.
123
     *
123
     *
124
     * @see \phpseclib3\Net\SSH2::send_channel_packet()
124
     * @see \phpseclib3\Net\SSH2::send_channel_packet()
125
     * @see \phpseclib3\Net\SSH2::get_channel_packet()
125
     * @see \phpseclib3\Net\SSH2::get_channel_packet()
-
 
126
     * @access private
126
     */
127
     */
127
    const CHANNEL_EXEC          = 1; // PuTTy uses 0x100
128
    const CHANNEL_EXEC          = 1; // PuTTy uses 0x100
128
    const CHANNEL_SHELL         = 2;
129
    const CHANNEL_SHELL         = 2;
129
    const CHANNEL_SUBSYSTEM     = 3;
130
    const CHANNEL_SUBSYSTEM     = 3;
130
    const CHANNEL_AGENT_FORWARD = 4;
131
    const CHANNEL_AGENT_FORWARD = 4;
131
    const CHANNEL_KEEP_ALIVE    = 5;
132
    const CHANNEL_KEEP_ALIVE    = 5;
132
 
133
 
133
    /**
134
    /**
134
     * Returns the message numbers
135
     * Returns the message numbers
135
     *
136
     *
-
 
137
     * @access public
136
     * @see \phpseclib3\Net\SSH2::getLog()
138
     * @see \phpseclib3\Net\SSH2::getLog()
137
     */
139
     */
138
    const LOG_SIMPLE = 1;
140
    const LOG_SIMPLE = 1;
139
    /**
141
    /**
140
     * Returns the message content
142
     * Returns the message content
141
     *
143
     *
-
 
144
     * @access public
142
     * @see \phpseclib3\Net\SSH2::getLog()
145
     * @see \phpseclib3\Net\SSH2::getLog()
143
     */
146
     */
144
    const LOG_COMPLEX = 2;
147
    const LOG_COMPLEX = 2;
145
    /**
148
    /**
146
     * Outputs the content real-time
149
     * Outputs the content real-time
147
     *
150
     *
-
 
151
     * @access public
148
     * @see \phpseclib3\Net\SSH2::getLog()
152
     * @see \phpseclib3\Net\SSH2::getLog()
149
     */
153
     */
150
    const LOG_REALTIME = 3;
154
    const LOG_REALTIME = 3;
151
    /**
155
    /**
152
     * Dumps the content real-time to a file
156
     * Dumps the content real-time to a file
153
     *
157
     *
-
 
158
     * @access public
154
     * @see \phpseclib3\Net\SSH2::getLog()
159
     * @see \phpseclib3\Net\SSH2::getLog()
155
     */
160
     */
156
    const LOG_REALTIME_FILE = 4;
161
    const LOG_REALTIME_FILE = 4;
157
    /**
162
    /**
158
     * Make sure that the log never gets larger than this
163
     * Make sure that the log never gets larger than this
159
     *
164
     *
-
 
165
     * @access public
160
     * @see \phpseclib3\Net\SSH2::getLog()
166
     * @see \phpseclib3\Net\SSH2::getLog()
161
     */
167
     */
162
    const LOG_MAX_SIZE = 1048576; // 1024 * 1024
168
    const LOG_MAX_SIZE = 1048576; // 1024 * 1024
163
 
169
 
164
    /**
170
    /**
165
     * Returns when a string matching $expect exactly is found
171
     * Returns when a string matching $expect exactly is found
166
     *
172
     *
-
 
173
     * @access public
167
     * @see \phpseclib3\Net\SSH2::read()
174
     * @see \phpseclib3\Net\SSH2::read()
168
     */
175
     */
169
    const READ_SIMPLE = 1;
176
    const READ_SIMPLE = 1;
170
    /**
177
    /**
171
     * Returns when a string matching the regular expression $expect is found
178
     * Returns when a string matching the regular expression $expect is found
172
     *
179
     *
-
 
180
     * @access public
173
     * @see \phpseclib3\Net\SSH2::read()
181
     * @see \phpseclib3\Net\SSH2::read()
174
     */
182
     */
175
    const READ_REGEX = 2;
183
    const READ_REGEX = 2;
176
    /**
184
    /**
177
     * Returns whenever a data packet is received.
185
     * Returns whenever a data packet is received.
178
     *
186
     *
179
     * Some data packets may only contain a single character so it may be necessary
187
     * Some data packets may only contain a single character so it may be necessary
180
     * to call read() multiple times when using this option
188
     * to call read() multiple times when using this option
181
     *
189
     *
-
 
190
     * @access public
182
     * @see \phpseclib3\Net\SSH2::read()
191
     * @see \phpseclib3\Net\SSH2::read()
183
     */
192
     */
184
    const READ_NEXT = 3;
193
    const READ_NEXT = 3;
185
 
194
 
186
    /**
195
    /**
187
     * The SSH identifier
196
     * The SSH identifier
188
     *
197
     *
189
     * @var string
198
     * @var string
-
 
199
     * @access private
190
     */
200
     */
191
    private $identifier;
201
    private $identifier;
192
 
202
 
193
    /**
203
    /**
194
     * The Socket Object
204
     * The Socket Object
195
     *
205
     *
196
     * @var resource|closed-resource|null
206
     * @var resource|closed-resource|null
-
 
207
     * @access private
197
     */
208
     */
198
    public $fsock;
209
    public $fsock;
199
 
210
 
200
    /**
211
    /**
201
     * Execution Bitmap
212
     * Execution Bitmap
202
     *
213
     *
203
     * The bits that are set represent functions that have been called already.  This is used to determine
214
     * The bits that are set represent functions that have been called already.  This is used to determine
204
     * if a requisite function has been successfully executed.  If not, an error should be thrown.
215
     * if a requisite function has been successfully executed.  If not, an error should be thrown.
205
     *
216
     *
206
     * @var int
217
     * @var int
-
 
218
     * @access private
207
     */
219
     */
208
    protected $bitmap = 0;
220
    protected $bitmap = 0;
209
 
221
 
210
    /**
222
    /**
211
     * Error information
223
     * Error information
212
     *
224
     *
213
     * @see self::getErrors()
225
     * @see self::getErrors()
214
     * @see self::getLastError()
226
     * @see self::getLastError()
215
     * @var array
227
     * @var array
-
 
228
     * @access private
216
     */
229
     */
217
    private $errors = [];
230
    private $errors = [];
218
 
231
 
219
    /**
232
    /**
220
     * Server Identifier
233
     * Server Identifier
221
     *
234
     *
222
     * @see self::getServerIdentification()
235
     * @see self::getServerIdentification()
223
     * @var string|false
236
     * @var string|false
-
 
237
     * @access private
224
     */
238
     */
225
    protected $server_identifier = false;
239
    protected $server_identifier = false;
226
 
240
 
227
    /**
241
    /**
228
     * Key Exchange Algorithms
242
     * Key Exchange Algorithms
229
     *
243
     *
230
     * @see self::getKexAlgorithims()
244
     * @see self::getKexAlgorithims()
231
     * @var array|false
245
     * @var array|false
-
 
246
     * @access private
232
     */
247
     */
233
    private $kex_algorithms = false;
248
    private $kex_algorithms = false;
234
 
249
 
235
    /**
250
    /**
236
     * Key Exchange Algorithm
251
     * Key Exchange Algorithm
237
     *
252
     *
238
     * @see self::getMethodsNegotiated()
253
     * @see self::getMethodsNegotiated()
239
     * @var string|false
254
     * @var string|false
-
 
255
     * @access private
240
     */
256
     */
241
    private $kex_algorithm = false;
257
    private $kex_algorithm = false;
242
 
258
 
243
    /**
259
    /**
244
     * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
260
     * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
245
     *
261
     *
246
     * @see self::_key_exchange()
262
     * @see self::_key_exchange()
247
     * @var int
263
     * @var int
-
 
264
     * @access private
248
     */
265
     */
249
    private $kex_dh_group_size_min = 1536;
266
    private $kex_dh_group_size_min = 1536;
250
 
267
 
251
    /**
268
    /**
252
     * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
269
     * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
253
     *
270
     *
254
     * @see self::_key_exchange()
271
     * @see self::_key_exchange()
255
     * @var int
272
     * @var int
-
 
273
     * @access private
256
     */
274
     */
257
    private $kex_dh_group_size_preferred = 2048;
275
    private $kex_dh_group_size_preferred = 2048;
258
 
276
 
259
    /**
277
    /**
260
     * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
278
     * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
261
     *
279
     *
262
     * @see self::_key_exchange()
280
     * @see self::_key_exchange()
263
     * @var int
281
     * @var int
-
 
282
     * @access private
264
     */
283
     */
265
    private $kex_dh_group_size_max = 4096;
284
    private $kex_dh_group_size_max = 4096;
266
 
285
 
267
    /**
286
    /**
268
     * Server Host Key Algorithms
287
     * Server Host Key Algorithms
269
     *
288
     *
270
     * @see self::getServerHostKeyAlgorithms()
289
     * @see self::getServerHostKeyAlgorithms()
271
     * @var array|false
290
     * @var array|false
-
 
291
     * @access private
272
     */
292
     */
273
    private $server_host_key_algorithms = false;
293
    private $server_host_key_algorithms = false;
274
 
294
 
275
    /**
295
    /**
276
     * Encryption Algorithms: Client to Server
296
     * Encryption Algorithms: Client to Server
277
     *
297
     *
278
     * @see self::getEncryptionAlgorithmsClient2Server()
298
     * @see self::getEncryptionAlgorithmsClient2Server()
279
     * @var array|false
299
     * @var array|false
-
 
300
     * @access private
280
     */
301
     */
281
    private $encryption_algorithms_client_to_server = false;
302
    private $encryption_algorithms_client_to_server = false;
282
 
303
 
283
    /**
304
    /**
284
     * Encryption Algorithms: Server to Client
305
     * Encryption Algorithms: Server to Client
285
     *
306
     *
286
     * @see self::getEncryptionAlgorithmsServer2Client()
307
     * @see self::getEncryptionAlgorithmsServer2Client()
287
     * @var array|false
308
     * @var array|false
-
 
309
     * @access private
288
     */
310
     */
289
    private $encryption_algorithms_server_to_client = false;
311
    private $encryption_algorithms_server_to_client = false;
290
 
312
 
291
    /**
313
    /**
292
     * MAC Algorithms: Client to Server
314
     * MAC Algorithms: Client to Server
293
     *
315
     *
294
     * @see self::getMACAlgorithmsClient2Server()
316
     * @see self::getMACAlgorithmsClient2Server()
295
     * @var array|false
317
     * @var array|false
-
 
318
     * @access private
296
     */
319
     */
297
    private $mac_algorithms_client_to_server = false;
320
    private $mac_algorithms_client_to_server = false;
298
 
321
 
299
    /**
322
    /**
300
     * MAC Algorithms: Server to Client
323
     * MAC Algorithms: Server to Client
301
     *
324
     *
302
     * @see self::getMACAlgorithmsServer2Client()
325
     * @see self::getMACAlgorithmsServer2Client()
303
     * @var array|false
326
     * @var array|false
-
 
327
     * @access private
304
     */
328
     */
305
    private $mac_algorithms_server_to_client = false;
329
    private $mac_algorithms_server_to_client = false;
306
 
330
 
307
    /**
331
    /**
308
     * Compression Algorithms: Client to Server
332
     * Compression Algorithms: Client to Server
309
     *
333
     *
310
     * @see self::getCompressionAlgorithmsClient2Server()
334
     * @see self::getCompressionAlgorithmsClient2Server()
311
     * @var array|false
335
     * @var array|false
-
 
336
     * @access private
312
     */
337
     */
313
    private $compression_algorithms_client_to_server = false;
338
    private $compression_algorithms_client_to_server = false;
314
 
339
 
315
    /**
340
    /**
316
     * Compression Algorithms: Server to Client
341
     * Compression Algorithms: Server to Client
317
     *
342
     *
318
     * @see self::getCompressionAlgorithmsServer2Client()
343
     * @see self::getCompressionAlgorithmsServer2Client()
319
     * @var array|false
344
     * @var array|false
-
 
345
     * @access private
320
     */
346
     */
321
    private $compression_algorithms_server_to_client = false;
347
    private $compression_algorithms_server_to_client = false;
322
 
348
 
323
    /**
349
    /**
324
     * Languages: Server to Client
350
     * Languages: Server to Client
325
     *
351
     *
326
     * @see self::getLanguagesServer2Client()
352
     * @see self::getLanguagesServer2Client()
327
     * @var array|false
353
     * @var array|false
-
 
354
     * @access private
328
     */
355
     */
329
    private $languages_server_to_client = false;
356
    private $languages_server_to_client = false;
330
 
357
 
331
    /**
358
    /**
332
     * Languages: Client to Server
359
     * Languages: Client to Server
333
     *
360
     *
334
     * @see self::getLanguagesClient2Server()
361
     * @see self::getLanguagesClient2Server()
335
     * @var array|false
362
     * @var array|false
-
 
363
     * @access private
336
     */
364
     */
337
    private $languages_client_to_server = false;
365
    private $languages_client_to_server = false;
338
 
366
 
339
    /**
367
    /**
340
     * Preferred Algorithms
368
     * Preferred Algorithms
341
     *
369
     *
342
     * @see self::setPreferredAlgorithms()
370
     * @see self::setPreferredAlgorithms()
343
     * @var array
371
     * @var array
-
 
372
     * @access private
344
     */
373
     */
345
    private $preferred = [];
374
    private $preferred = [];
346
 
375
 
347
    /**
376
    /**
348
     * Block Size for Server to Client Encryption
377
     * Block Size for Server to Client Encryption
Line 355... Line 384...
355
     *  -- http://tools.ietf.org/html/rfc4253#section-6
384
     *  -- http://tools.ietf.org/html/rfc4253#section-6
356
     *
385
     *
357
     * @see self::__construct()
386
     * @see self::__construct()
358
     * @see self::_send_binary_packet()
387
     * @see self::_send_binary_packet()
359
     * @var int
388
     * @var int
-
 
389
     * @access private
360
     */
390
     */
361
    private $encrypt_block_size = 8;
391
    private $encrypt_block_size = 8;
362
 
392
 
363
    /**
393
    /**
364
     * Block Size for Client to Server Encryption
394
     * Block Size for Client to Server Encryption
365
     *
395
     *
366
     * @see self::__construct()
396
     * @see self::__construct()
367
     * @see self::_get_binary_packet()
397
     * @see self::_get_binary_packet()
368
     * @var int
398
     * @var int
-
 
399
     * @access private
369
     */
400
     */
370
    private $decrypt_block_size = 8;
401
    private $decrypt_block_size = 8;
371
 
402
 
372
    /**
403
    /**
373
     * Server to Client Encryption Object
404
     * Server to Client Encryption Object
374
     *
405
     *
375
     * @see self::_get_binary_packet()
406
     * @see self::_get_binary_packet()
376
     * @var SymmetricKey|false
407
     * @var SymmetricKey|false
-
 
408
     * @access private
377
     */
409
     */
378
    private $decrypt = false;
410
    private $decrypt = false;
379
 
411
 
380
    /**
412
    /**
381
     * Decryption Algorithm Name
413
     * Decryption Algorithm Name
382
     *
414
     *
383
     * @var string|null
415
     * @var string|null
-
 
416
     * @access private
384
     */
417
     */
385
    private $decryptName;
418
    private $decryptName;
386
 
419
 
387
    /**
420
    /**
388
     * Decryption Invocation Counter
421
     * Decryption Invocation Counter
389
     *
422
     *
390
     * Used by GCM
423
     * Used by GCM
391
     *
424
     *
392
     * @var string|null
425
     * @var string|null
-
 
426
     * @access private
393
     */
427
     */
394
    private $decryptInvocationCounter;
428
    private $decryptInvocationCounter;
395
 
429
 
396
    /**
430
    /**
397
     * Fixed Part of Nonce
431
     * Fixed Part of Nonce
398
     *
432
     *
399
     * Used by GCM
433
     * Used by GCM
400
     *
434
     *
401
     * @var string|null
435
     * @var string|null
-
 
436
     * @access private
402
     */
437
     */
403
    private $decryptFixedPart;
438
    private $decryptFixedPart;
404
 
439
 
405
    /**
440
    /**
406
     * Server to Client Length Encryption Object
441
     * Server to Client Length Encryption Object
407
     *
442
     *
408
     * @see self::_get_binary_packet()
443
     * @see self::_get_binary_packet()
409
     * @var object
444
     * @var object
-
 
445
     * @access private
410
     */
446
     */
411
    private $lengthDecrypt = false;
447
    private $lengthDecrypt = false;
412
 
448
 
413
    /**
449
    /**
414
     * Client to Server Encryption Object
450
     * Client to Server Encryption Object
415
     *
451
     *
416
     * @see self::_send_binary_packet()
452
     * @see self::_send_binary_packet()
417
     * @var SymmetricKey|false
453
     * @var SymmetricKey|false
-
 
454
     * @access private
418
     */
455
     */
419
    private $encrypt = false;
456
    private $encrypt = false;
420
 
457
 
421
    /**
458
    /**
422
     * Encryption Algorithm Name
459
     * Encryption Algorithm Name
423
     *
460
     *
424
     * @var string|null
461
     * @var string|null
-
 
462
     * @access private
425
     */
463
     */
426
    private $encryptName;
464
    private $encryptName;
427
 
465
 
428
    /**
466
    /**
429
     * Encryption Invocation Counter
467
     * Encryption Invocation Counter
430
     *
468
     *
431
     * Used by GCM
469
     * Used by GCM
432
     *
470
     *
433
     * @var string|null
471
     * @var string|null
-
 
472
     * @access private
434
     */
473
     */
435
    private $encryptInvocationCounter;
474
    private $encryptInvocationCounter;
436
 
475
 
437
    /**
476
    /**
438
     * Fixed Part of Nonce
477
     * Fixed Part of Nonce
439
     *
478
     *
440
     * Used by GCM
479
     * Used by GCM
441
     *
480
     *
442
     * @var string|null
481
     * @var string|null
-
 
482
     * @access private
443
     */
483
     */
444
    private $encryptFixedPart;
484
    private $encryptFixedPart;
445
 
485
 
446
    /**
486
    /**
447
     * Client to Server Length Encryption Object
487
     * Client to Server Length Encryption Object
448
     *
488
     *
449
     * @see self::_send_binary_packet()
489
     * @see self::_send_binary_packet()
450
     * @var object
490
     * @var object
-
 
491
     * @access private
451
     */
492
     */
452
    private $lengthEncrypt = false;
493
    private $lengthEncrypt = false;
453
 
494
 
454
    /**
495
    /**
455
     * Client to Server HMAC Object
496
     * Client to Server HMAC Object
456
     *
497
     *
457
     * @see self::_send_binary_packet()
498
     * @see self::_send_binary_packet()
458
     * @var object
499
     * @var object
-
 
500
     * @access private
459
     */
501
     */
460
    private $hmac_create = false;
502
    private $hmac_create = false;
461
 
503
 
462
    /**
504
    /**
463
     * Client to Server HMAC Name
505
     * Client to Server HMAC Name
464
     *
506
     *
465
     * @var string|false
507
     * @var string|false
-
 
508
     * @access private
466
     */
509
     */
467
    private $hmac_create_name;
510
    private $hmac_create_name;
468
 
511
 
469
    /**
512
    /**
470
     * Client to Server ETM
513
     * Client to Server ETM
471
     *
514
     *
472
     * @var int|false
515
     * @var int|false
-
 
516
     * @access private
473
     */
517
     */
474
    private $hmac_create_etm;
518
    private $hmac_create_etm;
475
 
519
 
476
    /**
520
    /**
477
     * Server to Client HMAC Object
521
     * Server to Client HMAC Object
478
     *
522
     *
479
     * @see self::_get_binary_packet()
523
     * @see self::_get_binary_packet()
480
     * @var object
524
     * @var object
-
 
525
     * @access private
481
     */
526
     */
482
    private $hmac_check = false;
527
    private $hmac_check = false;
483
 
528
 
484
    /**
529
    /**
485
     * Server to Client HMAC Name
530
     * Server to Client HMAC Name
486
     *
531
     *
487
     * @var string|false
532
     * @var string|false
-
 
533
     * @access private
488
     */
534
     */
489
    private $hmac_check_name;
535
    private $hmac_check_name;
490
 
536
 
491
    /**
537
    /**
492
     * Server to Client ETM
538
     * Server to Client ETM
493
     *
539
     *
494
     * @var int|false
540
     * @var int|false
-
 
541
     * @access private
495
     */
542
     */
496
    private $hmac_check_etm;
543
    private $hmac_check_etm;
497
 
544
 
498
    /**
545
    /**
499
     * Size of server to client HMAC
546
     * Size of server to client HMAC
Line 502... Line 549...
502
     * For the client to server side, the HMAC object will make the HMAC as long as it needs to be.  All we need to do is
549
     * For the client to server side, the HMAC object will make the HMAC as long as it needs to be.  All we need to do is
503
     * append it.
550
     * append it.
504
     *
551
     *
505
     * @see self::_get_binary_packet()
552
     * @see self::_get_binary_packet()
506
     * @var int
553
     * @var int
-
 
554
     * @access private
507
     */
555
     */
508
    private $hmac_size = false;
556
    private $hmac_size = false;
509
 
557
 
510
    /**
558
    /**
511
     * Server Public Host Key
559
     * Server Public Host Key
512
     *
560
     *
513
     * @see self::getServerPublicHostKey()
561
     * @see self::getServerPublicHostKey()
514
     * @var string
562
     * @var string
-
 
563
     * @access private
515
     */
564
     */
516
    private $server_public_host_key;
565
    private $server_public_host_key;
517
 
566
 
518
    /**
567
    /**
519
     * Session identifier
568
     * Session identifier
Line 524... Line 573...
524
     *
573
     *
525
     *  -- http://tools.ietf.org/html/rfc4253#section-7.2
574
     *  -- http://tools.ietf.org/html/rfc4253#section-7.2
526
     *
575
     *
527
     * @see self::_key_exchange()
576
     * @see self::_key_exchange()
528
     * @var string
577
     * @var string
-
 
578
     * @access private
529
     */
579
     */
530
    private $session_id = false;
580
    private $session_id = false;
531
 
581
 
532
    /**
582
    /**
533
     * Exchange hash
583
     * Exchange hash
534
     *
584
     *
535
     * The current exchange hash
585
     * The current exchange hash
536
     *
586
     *
537
     * @see self::_key_exchange()
587
     * @see self::_key_exchange()
538
     * @var string
588
     * @var string
-
 
589
     * @access private
539
     */
590
     */
540
    private $exchange_hash = false;
591
    private $exchange_hash = false;
541
 
592
 
542
    /**
593
    /**
-
 
594
     * Message Numbers
-
 
595
     *
-
 
596
     * @see self::__construct()
-
 
597
     * @var array
-
 
598
     * @access private
-
 
599
     */
-
 
600
    private $message_numbers = [];
-
 
601
 
-
 
602
    /**
-
 
603
     * Disconnection Message 'reason codes' defined in RFC4253
-
 
604
     *
-
 
605
     * @see self::__construct()
-
 
606
     * @var array
-
 
607
     * @access private
-
 
608
     */
-
 
609
    private $disconnect_reasons = [];
-
 
610
 
-
 
611
    /**
-
 
612
     * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
-
 
613
     *
-
 
614
     * @see self::__construct()
-
 
615
     * @var array
-
 
616
     * @access private
-
 
617
     */
-
 
618
    private $channel_open_failure_reasons = [];
-
 
619
 
-
 
620
    /**
-
 
621
     * Terminal Modes
-
 
622
     *
-
 
623
     * @link http://tools.ietf.org/html/rfc4254#section-8
-
 
624
     * @see self::__construct()
-
 
625
     * @var array
-
 
626
     * @access private
-
 
627
     */
-
 
628
    private $terminal_modes = [];
-
 
629
 
-
 
630
    /**
-
 
631
     * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
-
 
632
     *
-
 
633
     * @link http://tools.ietf.org/html/rfc4254#section-5.2
-
 
634
     * @see self::__construct()
-
 
635
     * @var array
-
 
636
     * @access private
-
 
637
     */
-
 
638
    private $channel_extended_data_type_codes = [];
-
 
639
 
-
 
640
    /**
543
     * Send Sequence Number
641
     * Send Sequence Number
544
     *
642
     *
545
     * See 'Section 6.4.  Data Integrity' of rfc4253 for more info.
643
     * See 'Section 6.4.  Data Integrity' of rfc4253 for more info.
546
     *
644
     *
547
     * @see self::_send_binary_packet()
645
     * @see self::_send_binary_packet()
548
     * @var int
646
     * @var int
-
 
647
     * @access private
549
     */
648
     */
550
    private $send_seq_no = 0;
649
    private $send_seq_no = 0;
551
 
650
 
552
    /**
651
    /**
553
     * Get Sequence Number
652
     * Get Sequence Number
554
     *
653
     *
555
     * See 'Section 6.4.  Data Integrity' of rfc4253 for more info.
654
     * See 'Section 6.4.  Data Integrity' of rfc4253 for more info.
556
     *
655
     *
557
     * @see self::_get_binary_packet()
656
     * @see self::_get_binary_packet()
558
     * @var int
657
     * @var int
-
 
658
     * @access private
559
     */
659
     */
560
    private $get_seq_no = 0;
660
    private $get_seq_no = 0;
561
 
661
 
562
    /**
662
    /**
563
     * Server Channels
663
     * Server Channels
Line 565... Line 665...
565
     * Maps client channels to server channels
665
     * Maps client channels to server channels
566
     *
666
     *
567
     * @see self::get_channel_packet()
667
     * @see self::get_channel_packet()
568
     * @see self::exec()
668
     * @see self::exec()
569
     * @var array
669
     * @var array
-
 
670
     * @access private
570
     */
671
     */
571
    protected $server_channels = [];
672
    protected $server_channels = [];
572
 
673
 
573
    /**
674
    /**
574
     * Channel Buffers
675
     * Channel Buffers
Line 577... Line 678...
577
     * be placed in a buffer
678
     * be placed in a buffer
578
     *
679
     *
579
     * @see self::get_channel_packet()
680
     * @see self::get_channel_packet()
580
     * @see self::exec()
681
     * @see self::exec()
581
     * @var array
682
     * @var array
-
 
683
     * @access private
582
     */
684
     */
583
    private $channel_buffers = [];
685
    private $channel_buffers = [];
584
 
686
 
585
    /**
687
    /**
586
     * Channel Status
688
     * Channel Status
587
     *
689
     *
588
     * Contains the type of the last sent message
690
     * Contains the type of the last sent message
589
     *
691
     *
590
     * @see self::get_channel_packet()
692
     * @see self::get_channel_packet()
591
     * @var array
693
     * @var array
-
 
694
     * @access private
592
     */
695
     */
593
    protected $channel_status = [];
696
    protected $channel_status = [];
594
 
697
 
595
    /**
698
    /**
596
     * Packet Size
699
     * Packet Size
597
     *
700
     *
598
     * Maximum packet size indexed by channel
701
     * Maximum packet size indexed by channel
599
     *
702
     *
600
     * @see self::send_channel_packet()
703
     * @see self::send_channel_packet()
601
     * @var array
704
     * @var array
-
 
705
     * @access private
602
     */
706
     */
603
    private $packet_size_client_to_server = [];
707
    private $packet_size_client_to_server = [];
604
 
708
 
605
    /**
709
    /**
606
     * Message Number Log
710
     * Message Number Log
607
     *
711
     *
608
     * @see self::getLog()
712
     * @see self::getLog()
609
     * @var array
713
     * @var array
-
 
714
     * @access private
610
     */
715
     */
611
    private $message_number_log = [];
716
    private $message_number_log = [];
612
 
717
 
613
    /**
718
    /**
614
     * Message Log
719
     * Message Log
615
     *
720
     *
616
     * @see self::getLog()
721
     * @see self::getLog()
617
     * @var array
722
     * @var array
-
 
723
     * @access private
618
     */
724
     */
619
    private $message_log = [];
725
    private $message_log = [];
620
 
726
 
621
    /**
727
    /**
622
     * The Window Size
728
     * The Window Size
Line 624... Line 730...
624
     * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB)
730
     * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB)
625
     *
731
     *
626
     * @var int
732
     * @var int
627
     * @see self::send_channel_packet()
733
     * @see self::send_channel_packet()
628
     * @see self::exec()
734
     * @see self::exec()
-
 
735
     * @access private
629
     */
736
     */
630
    protected $window_size = 0x7FFFFFFF;
737
    protected $window_size = 0x7FFFFFFF;
631
 
738
 
632
    /**
739
    /**
633
     * What we resize the window to
740
     * What we resize the window to
Line 637... Line 744...
637
     * we'll just do what PuTTY does
744
     * we'll just do what PuTTY does
638
     *
745
     *
639
     * @var int
746
     * @var int
640
     * @see self::_send_channel_packet()
747
     * @see self::_send_channel_packet()
641
     * @see self::exec()
748
     * @see self::exec()
-
 
749
     * @access private
642
     */
750
     */
643
    private $window_resize = 0x40000000;
751
    private $window_resize = 0x40000000;
644
 
752
 
645
    /**
753
    /**
646
     * Window size, server to client
754
     * Window size, server to client
647
     *
755
     *
648
     * Window size indexed by channel
756
     * Window size indexed by channel
649
     *
757
     *
650
     * @see self::send_channel_packet()
758
     * @see self::send_channel_packet()
651
     * @var array
759
     * @var array
-
 
760
     * @access private
652
     */
761
     */
653
    protected $window_size_server_to_client = [];
762
    protected $window_size_server_to_client = [];
654
 
763
 
655
    /**
764
    /**
656
     * Window size, client to server
765
     * Window size, client to server
657
     *
766
     *
658
     * Window size indexed by channel
767
     * Window size indexed by channel
659
     *
768
     *
660
     * @see self::get_channel_packet()
769
     * @see self::get_channel_packet()
661
     * @var array
770
     * @var array
-
 
771
     * @access private
662
     */
772
     */
663
    private $window_size_client_to_server = [];
773
    private $window_size_client_to_server = [];
664
 
774
 
665
    /**
775
    /**
666
     * Server signature
776
     * Server signature
667
     *
777
     *
668
     * Verified against $this->session_id
778
     * Verified against $this->session_id
669
     *
779
     *
670
     * @see self::getServerPublicHostKey()
780
     * @see self::getServerPublicHostKey()
671
     * @var string
781
     * @var string
-
 
782
     * @access private
672
     */
783
     */
673
    private $signature = '';
784
    private $signature = '';
674
 
785
 
675
    /**
786
    /**
676
     * Server signature format
787
     * Server signature format
677
     *
788
     *
678
     * ssh-rsa or ssh-dss.
789
     * ssh-rsa or ssh-dss.
679
     *
790
     *
680
     * @see self::getServerPublicHostKey()
791
     * @see self::getServerPublicHostKey()
681
     * @var string
792
     * @var string
-
 
793
     * @access private
682
     */
794
     */
683
    private $signature_format = '';
795
    private $signature_format = '';
684
 
796
 
685
    /**
797
    /**
686
     * Interactive Buffer
798
     * Interactive Buffer
687
     *
799
     *
688
     * @see self::read()
800
     * @see self::read()
689
     * @var string
801
     * @var string
-
 
802
     * @access private
690
     */
803
     */
691
    private $interactiveBuffer = '';
804
    private $interactiveBuffer = '';
692
 
805
 
693
    /**
806
    /**
694
     * Current log size
807
     * Current log size
Line 696... Line 809...
696
     * Should never exceed self::LOG_MAX_SIZE
809
     * Should never exceed self::LOG_MAX_SIZE
697
     *
810
     *
698
     * @see self::_send_binary_packet()
811
     * @see self::_send_binary_packet()
699
     * @see self::_get_binary_packet()
812
     * @see self::_get_binary_packet()
700
     * @var int
813
     * @var int
-
 
814
     * @access private
701
     */
815
     */
702
    private $log_size;
816
    private $log_size;
703
 
817
 
704
    /**
818
    /**
705
     * Timeout
819
     * Timeout
706
     *
820
     *
707
     * @see self::setTimeout()
821
     * @see self::setTimeout()
-
 
822
     * @access private
708
     */
823
     */
709
    protected $timeout;
824
    protected $timeout;
710
 
825
 
711
    /**
826
    /**
712
     * Current Timeout
827
     * Current Timeout
713
     *
828
     *
714
     * @see self::get_channel_packet()
829
     * @see self::get_channel_packet()
-
 
830
     * @access private
715
     */
831
     */
716
    protected $curTimeout;
832
    protected $curTimeout;
717
 
833
 
718
    /**
834
    /**
719
     * Keep Alive Interval
835
     * Keep Alive Interval
720
     *
836
     *
721
     * @see self::setKeepAlive()
837
     * @see self::setKeepAlive()
-
 
838
     * @access private
722
     */
839
     */
723
    private $keepAlive;
840
    private $keepAlive;
724
 
841
 
725
    /**
842
    /**
726
     * Real-time log file pointer
843
     * Real-time log file pointer
727
     *
844
     *
728
     * @see self::_append_log()
845
     * @see self::_append_log()
729
     * @var resource|closed-resource
846
     * @var resource|closed-resource
-
 
847
     * @access private
730
     */
848
     */
731
    private $realtime_log_file;
849
    private $realtime_log_file;
732
 
850
 
733
    /**
851
    /**
734
     * Real-time log file size
852
     * Real-time log file size
735
     *
853
     *
736
     * @see self::_append_log()
854
     * @see self::_append_log()
737
     * @var int
855
     * @var int
-
 
856
     * @access private
738
     */
857
     */
739
    private $realtime_log_size;
858
    private $realtime_log_size;
740
 
859
 
741
    /**
860
    /**
742
     * Has the signature been validated?
861
     * Has the signature been validated?
743
     *
862
     *
744
     * @see self::getServerPublicHostKey()
863
     * @see self::getServerPublicHostKey()
745
     * @var bool
864
     * @var bool
-
 
865
     * @access private
746
     */
866
     */
747
    private $signature_validated = false;
867
    private $signature_validated = false;
748
 
868
 
749
    /**
869
    /**
750
     * Real-time log file wrap boolean
870
     * Real-time log file wrap boolean
751
     *
871
     *
752
     * @see self::_append_log()
872
     * @see self::_append_log()
-
 
873
     * @access private
753
     */
874
     */
754
    private $realtime_log_wrap;
875
    private $realtime_log_wrap;
755
 
876
 
756
    /**
877
    /**
757
     * Flag to suppress stderr from output
878
     * Flag to suppress stderr from output
758
     *
879
     *
759
     * @see self::enableQuietMode()
880
     * @see self::enableQuietMode()
-
 
881
     * @access private
760
     */
882
     */
761
    private $quiet_mode = false;
883
    private $quiet_mode = false;
762
 
884
 
763
    /**
885
    /**
764
     * Time of first network activity
886
     * Time of first network activity
765
     *
887
     *
766
     * @var float
888
     * @var float
-
 
889
     * @access private
767
     */
890
     */
768
    private $last_packet;
891
    private $last_packet;
769
 
892
 
770
    /**
893
    /**
771
     * Exit status returned from ssh if any
894
     * Exit status returned from ssh if any
772
     *
895
     *
773
     * @var int
896
     * @var int
-
 
897
     * @access private
774
     */
898
     */
775
    private $exit_status;
899
    private $exit_status;
776
 
900
 
777
    /**
901
    /**
778
     * Flag to request a PTY when using exec()
902
     * Flag to request a PTY when using exec()
779
     *
903
     *
780
     * @var bool
904
     * @var bool
781
     * @see self::enablePTY()
905
     * @see self::enablePTY()
-
 
906
     * @access private
782
     */
907
     */
783
    private $request_pty = false;
908
    private $request_pty = false;
784
 
909
 
785
    /**
910
    /**
786
     * Flag set while exec() is running when using enablePTY()
911
     * Flag set while exec() is running when using enablePTY()
787
     *
912
     *
788
     * @var bool
913
     * @var bool
-
 
914
     * @access private
789
     */
915
     */
790
    private $in_request_pty_exec = false;
916
    private $in_request_pty_exec = false;
791
 
917
 
792
    /**
918
    /**
793
     * Flag set after startSubsystem() is called
919
     * Flag set after startSubsystem() is called
794
     *
920
     *
795
     * @var bool
921
     * @var bool
-
 
922
     * @access private
796
     */
923
     */
797
    private $in_subsystem;
924
    private $in_subsystem;
798
 
925
 
799
    /**
926
    /**
800
     * Contents of stdError
927
     * Contents of stdError
801
     *
928
     *
802
     * @var string
929
     * @var string
-
 
930
     * @access private
803
     */
931
     */
804
    private $stdErrorLog;
932
    private $stdErrorLog;
805
 
933
 
806
    /**
934
    /**
807
     * The Last Interactive Response
935
     * The Last Interactive Response
808
     *
936
     *
809
     * @see self::_keyboard_interactive_process()
937
     * @see self::_keyboard_interactive_process()
810
     * @var string
938
     * @var string
-
 
939
     * @access private
811
     */
940
     */
812
    private $last_interactive_response = '';
941
    private $last_interactive_response = '';
813
 
942
 
814
    /**
943
    /**
815
     * Keyboard Interactive Request / Responses
944
     * Keyboard Interactive Request / Responses
816
     *
945
     *
817
     * @see self::_keyboard_interactive_process()
946
     * @see self::_keyboard_interactive_process()
818
     * @var array
947
     * @var array
-
 
948
     * @access private
819
     */
949
     */
820
    private $keyboard_requests_responses = [];
950
    private $keyboard_requests_responses = [];
821
 
951
 
822
    /**
952
    /**
823
     * Banner Message
953
     * Banner Message
Line 826... Line 956...
826
     * authentication may be relevant for getting legal protection."
956
     * authentication may be relevant for getting legal protection."
827
     *
957
     *
828
     * @see self::_filter()
958
     * @see self::_filter()
829
     * @see self::getBannerMessage()
959
     * @see self::getBannerMessage()
830
     * @var string
960
     * @var string
-
 
961
     * @access private
831
     */
962
     */
832
    private $banner_message = '';
963
    private $banner_message = '';
833
 
964
 
834
    /**
965
    /**
835
     * Did read() timeout or return normally?
966
     * Did read() timeout or return normally?
836
     *
967
     *
837
     * @see self::isTimeout()
968
     * @see self::isTimeout()
838
     * @var bool
969
     * @var bool
-
 
970
     * @access private
839
     */
971
     */
840
    private $is_timeout = false;
972
    private $is_timeout = false;
841
 
973
 
842
    /**
974
    /**
843
     * Log Boundary
975
     * Log Boundary
844
     *
976
     *
845
     * @see self::_format_log()
977
     * @see self::_format_log()
846
     * @var string
978
     * @var string
-
 
979
     * @access private
847
     */
980
     */
848
    private $log_boundary = ':';
981
    private $log_boundary = ':';
849
 
982
 
850
    /**
983
    /**
851
     * Log Long Width
984
     * Log Long Width
852
     *
985
     *
853
     * @see self::_format_log()
986
     * @see self::_format_log()
854
     * @var int
987
     * @var int
-
 
988
     * @access private
855
     */
989
     */
856
    private $log_long_width = 65;
990
    private $log_long_width = 65;
857
 
991
 
858
    /**
992
    /**
859
     * Log Short Width
993
     * Log Short Width
860
     *
994
     *
861
     * @see self::_format_log()
995
     * @see self::_format_log()
862
     * @var int
996
     * @var int
-
 
997
     * @access private
863
     */
998
     */
864
    private $log_short_width = 16;
999
    private $log_short_width = 16;
865
 
1000
 
866
    /**
1001
    /**
867
     * Hostname
1002
     * Hostname
868
     *
1003
     *
869
     * @see self::__construct()
1004
     * @see self::__construct()
870
     * @see self::_connect()
1005
     * @see self::_connect()
871
     * @var string
1006
     * @var string
-
 
1007
     * @access private
872
     */
1008
     */
873
    private $host;
1009
    private $host;
874
 
1010
 
875
    /**
1011
    /**
876
     * Port Number
1012
     * Port Number
877
     *
1013
     *
878
     * @see self::__construct()
1014
     * @see self::__construct()
879
     * @see self::_connect()
1015
     * @see self::_connect()
880
     * @var int
1016
     * @var int
-
 
1017
     * @access private
881
     */
1018
     */
882
    private $port;
1019
    private $port;
883
 
1020
 
884
    /**
1021
    /**
885
     * Number of columns for terminal window size
1022
     * Number of columns for terminal window size
886
     *
1023
     *
887
     * @see self::getWindowColumns()
1024
     * @see self::getWindowColumns()
888
     * @see self::setWindowColumns()
1025
     * @see self::setWindowColumns()
889
     * @see self::setWindowSize()
1026
     * @see self::setWindowSize()
890
     * @var int
1027
     * @var int
-
 
1028
     * @access private
891
     */
1029
     */
892
    private $windowColumns = 80;
1030
    private $windowColumns = 80;
893
 
1031
 
894
    /**
1032
    /**
895
     * Number of columns for terminal window size
1033
     * Number of columns for terminal window size
896
     *
1034
     *
897
     * @see self::getWindowRows()
1035
     * @see self::getWindowRows()
898
     * @see self::setWindowRows()
1036
     * @see self::setWindowRows()
899
     * @see self::setWindowSize()
1037
     * @see self::setWindowSize()
900
     * @var int
1038
     * @var int
-
 
1039
     * @access private
901
     */
1040
     */
902
    private $windowRows = 24;
1041
    private $windowRows = 24;
903
 
1042
 
904
    /**
1043
    /**
905
     * Crypto Engine
1044
     * Crypto Engine
906
     *
1045
     *
907
     * @see self::setCryptoEngine()
1046
     * @see self::setCryptoEngine()
908
     * @see self::_key_exchange()
1047
     * @see self::_key_exchange()
909
     * @var int
1048
     * @var int
-
 
1049
     * @access private
910
     */
1050
     */
911
    private static $crypto_engine = false;
1051
    private static $crypto_engine = false;
912
 
1052
 
913
    /**
1053
    /**
914
     * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario
1054
     * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario
915
     *
1055
     *
916
     * @var Agent
1056
     * @var Agent
-
 
1057
     * @access private
917
     */
1058
     */
918
    private $agent;
1059
    private $agent;
919
 
1060
 
920
    /**
1061
    /**
921
     * Connection storage to replicates ssh2 extension functionality:
1062
     * Connection storage to replicates ssh2 extension functionality:
Line 927... Line 1068...
927
 
1068
 
928
    /**
1069
    /**
929
     * Send the identification string first?
1070
     * Send the identification string first?
930
     *
1071
     *
931
     * @var bool
1072
     * @var bool
-
 
1073
     * @access private
932
     */
1074
     */
933
    private $send_id_string_first = true;
1075
    private $send_id_string_first = true;
934
 
1076
 
935
    /**
1077
    /**
936
     * Send the key exchange initiation packet first?
1078
     * Send the key exchange initiation packet first?
937
     *
1079
     *
938
     * @var bool
1080
     * @var bool
-
 
1081
     * @access private
939
     */
1082
     */
940
    private $send_kex_first = true;
1083
    private $send_kex_first = true;
941
 
1084
 
942
    /**
1085
    /**
943
     * Some versions of OpenSSH incorrectly calculate the key size
1086
     * Some versions of OpenSSH incorrectly calculate the key size
944
     *
1087
     *
945
     * @var bool
1088
     * @var bool
-
 
1089
     * @access private
946
     */
1090
     */
947
    private $bad_key_size_fix = false;
1091
    private $bad_key_size_fix = false;
948
 
1092
 
949
    /**
1093
    /**
950
     * Should we try to re-connect to re-establish keys?
1094
     * Should we try to re-connect to re-establish keys?
951
     *
1095
     *
952
     * @var bool
1096
     * @var bool
-
 
1097
     * @access private
953
     */
1098
     */
954
    private $retry_connect = false;
1099
    private $retry_connect = false;
955
 
1100
 
956
    /**
1101
    /**
957
     * Binary Packet Buffer
1102
     * Binary Packet Buffer
958
     *
1103
     *
959
     * @var string|false
1104
     * @var string|false
-
 
1105
     * @access private
960
     */
1106
     */
961
    private $binary_packet_buffer = false;
1107
    private $binary_packet_buffer = false;
962
 
1108
 
963
    /**
1109
    /**
964
     * Preferred Signature Format
1110
     * Preferred Signature Format
965
     *
1111
     *
966
     * @var string|false
1112
     * @var string|false
-
 
1113
     * @access private
967
     */
1114
     */
968
    protected $preferred_signature_format = false;
1115
    protected $preferred_signature_format = false;
969
 
1116
 
970
    /**
1117
    /**
971
     * Authentication Credentials
1118
     * Authentication Credentials
972
     *
1119
     *
973
     * @var array
1120
     * @var array
-
 
1121
     * @access private
974
     */
1122
     */
975
    protected $auth = [];
1123
    protected $auth = [];
976
 
1124
 
977
    /**
1125
    /**
978
     * Terminal
1126
     * Terminal
979
     *
1127
     *
980
     * @var string
1128
     * @var string
-
 
1129
     * @access private
981
     */
1130
     */
982
    private $term = 'vt100';
1131
    private $term = 'vt100';
983
 
1132
 
984
    /**
1133
    /**
985
     * The authentication methods that may productively continue authentication.
1134
     * The authentication methods that may productively continue authentication.
986
     *
1135
     *
987
     * @see https://tools.ietf.org/html/rfc4252#section-5.1
1136
     * @see https://tools.ietf.org/html/rfc4252#section-5.1
988
     * @var array|null
1137
     * @var array|null
-
 
1138
     * @access private
989
     */
1139
     */
990
    private $auth_methods_to_continue = null;
1140
    private $auth_methods_to_continue = null;
991
 
1141
 
992
    /**
1142
    /**
993
     * Compression method
1143
     * Compression method
994
     *
1144
     *
995
     * @var int
1145
     * @var int
-
 
1146
     * @access private
996
     */
1147
     */
997
    private $compress = self::NET_SSH2_COMPRESSION_NONE;
1148
    private $compress = self::NET_SSH2_COMPRESSION_NONE;
998
 
1149
 
999
    /**
1150
    /**
1000
     * Decompression method
1151
     * Decompression method
1001
     *
1152
     *
1002
     * @var int
1153
     * @var int
-
 
1154
     * @access private
1003
     */
1155
     */
1004
    private $decompress = self::NET_SSH2_COMPRESSION_NONE;
1156
    private $decompress = self::NET_SSH2_COMPRESSION_NONE;
1005
 
1157
 
1006
    /**
1158
    /**
1007
     * Compression context
1159
     * Compression context
1008
     *
1160
     *
1009
     * @var resource|false|null
1161
     * @var resource|false|null
-
 
1162
     * @access private
1010
     */
1163
     */
1011
    private $compress_context;
1164
    private $compress_context;
1012
 
1165
 
1013
    /**
1166
    /**
1014
     * Decompression context
1167
     * Decompression context
1015
     *
1168
     *
1016
     * @var resource|object
1169
     * @var resource|object
-
 
1170
     * @access private
1017
     */
1171
     */
1018
    private $decompress_context;
1172
    private $decompress_context;
1019
 
1173
 
1020
    /**
1174
    /**
1021
     * Regenerate Compression Context
1175
     * Regenerate Compression Context
1022
     *
1176
     *
1023
     * @var bool
1177
     * @var bool
-
 
1178
     * @access private
1024
     */
1179
     */
1025
    private $regenerate_compression_context = false;
1180
    private $regenerate_compression_context = false;
1026
 
1181
 
1027
    /**
1182
    /**
1028
     * Regenerate Decompression Context
1183
     * Regenerate Decompression Context
1029
     *
1184
     *
1030
     * @var bool
1185
     * @var bool
-
 
1186
     * @access private
1031
     */
1187
     */
1032
    private $regenerate_decompression_context = false;
1188
    private $regenerate_decompression_context = false;
1033
 
1189
 
1034
    /**
1190
    /**
1035
     * Smart multi-factor authentication flag
1191
     * Smart multi-factor authentication flag
1036
     *
1192
     *
1037
     * @var bool
1193
     * @var bool
-
 
1194
     * @access private
1038
     */
1195
     */
1039
    private $smartMFA = true;
1196
    private $smartMFA = true;
1040
 
1197
 
1041
    /**
1198
    /**
1042
     * Default Constructor.
1199
     * Default Constructor.
Line 1045... Line 1202...
1045
     *
1202
     *
1046
     * @param mixed $host
1203
     * @param mixed $host
1047
     * @param int $port
1204
     * @param int $port
1048
     * @param int $timeout
1205
     * @param int $timeout
1049
     * @see self::login()
1206
     * @see self::login()
-
 
1207
     * @access public
1050
     */
1208
     */
1051
    public function __construct($host, $port = 22, $timeout = 10)
1209
    public function __construct($host, $port = 22, $timeout = 10)
1052
    {
1210
    {
-
 
1211
        $this->message_numbers = [
-
 
1212
            1 => 'NET_SSH2_MSG_DISCONNECT',
-
 
1213
            2 => 'NET_SSH2_MSG_IGNORE',
-
 
1214
            3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
-
 
1215
            4 => 'NET_SSH2_MSG_DEBUG',
-
 
1216
            5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
-
 
1217
            6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
-
 
1218
            20 => 'NET_SSH2_MSG_KEXINIT',
-
 
1219
            21 => 'NET_SSH2_MSG_NEWKEYS',
-
 
1220
            30 => 'NET_SSH2_MSG_KEXDH_INIT',
-
 
1221
            31 => 'NET_SSH2_MSG_KEXDH_REPLY',
-
 
1222
            50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
-
 
1223
            51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
-
 
1224
            52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
-
 
1225
            53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
-
 
1226
 
-
 
1227
            80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
-
 
1228
            81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
-
 
1229
            82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
-
 
1230
            90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
-
 
1231
            91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
-
 
1232
            92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
-
 
1233
            93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
-
 
1234
            94 => 'NET_SSH2_MSG_CHANNEL_DATA',
-
 
1235
            95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
-
 
1236
            96 => 'NET_SSH2_MSG_CHANNEL_EOF',
-
 
1237
            97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
-
 
1238
            98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
-
 
1239
            99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
-
 
1240
            100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
-
 
1241
        ];
-
 
1242
        $this->disconnect_reasons = [
-
 
1243
            1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
-
 
1244
            2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
-
 
1245
            3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
-
 
1246
            4 => 'NET_SSH2_DISCONNECT_RESERVED',
-
 
1247
            5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
-
 
1248
            6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
-
 
1249
            7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
-
 
1250
            8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
-
 
1251
            9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
-
 
1252
            10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
-
 
1253
            11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
-
 
1254
            12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
-
 
1255
            13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
-
 
1256
            14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
-
 
1257
            15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
-
 
1258
        ];
-
 
1259
        $this->channel_open_failure_reasons = [
-
 
1260
            1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
-
 
1261
        ];
-
 
1262
        $this->terminal_modes = [
-
 
1263
            0 => 'NET_SSH2_TTY_OP_END'
-
 
1264
        ];
-
 
1265
        $this->channel_extended_data_type_codes = [
-
 
1266
            1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
-
 
1267
        ];
-
 
1268
 
-
 
1269
        $this->define_array(
-
 
1270
            $this->message_numbers,
-
 
1271
            $this->disconnect_reasons,
-
 
1272
            $this->channel_open_failure_reasons,
-
 
1273
            $this->terminal_modes,
-
 
1274
            $this->channel_extended_data_type_codes,
-
 
1275
            [60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'],
-
 
1276
            [60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'],
-
 
1277
            [60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
-
 
1278
                  61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'],
-
 
1279
            // RFC 4419 - diffie-hellman-group-exchange-sha{1,256}
-
 
1280
            [30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD',
-
 
1281
                  31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP',
-
 
1282
                  32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT',
-
 
1283
                  33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY',
-
 
1284
                  34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'],
-
 
1285
            // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org)
-
 
1286
            [30 => 'NET_SSH2_MSG_KEX_ECDH_INIT',
-
 
1287
                  31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY']
-
 
1288
        );
-
 
1289
 
1053
        /**
1290
        /**
1054
         * Typehint is required due to a bug in Psalm: https://github.com/vimeo/psalm/issues/7508
1291
         * Typehint is required due to a bug in Psalm: https://github.com/vimeo/psalm/issues/7508
1055
         * @var \WeakReference<SSH2>|SSH2
1292
         * @var \WeakReference<SSH2>|SSH2
1056
         */
1293
         */
1057
        self::$connections[$this->getResourceId()] = class_exists('WeakReference')
1294
        self::$connections[$this->getResourceId()] = class_exists('WeakReference')
Line 1075... Line 1312...
1075
     *
1312
     *
1076
     * Possible $engine values:
1313
     * Possible $engine values:
1077
     * OpenSSL, mcrypt, Eval, PHP
1314
     * OpenSSL, mcrypt, Eval, PHP
1078
     *
1315
     *
1079
     * @param int $engine
1316
     * @param int $engine
-
 
1317
     * @access public
1080
     */
1318
     */
1081
    public static function setCryptoEngine($engine)
1319
    public static function setCryptoEngine($engine)
1082
    {
1320
    {
1083
        self::$crypto_engine = $engine;
1321
        self::$crypto_engine = $engine;
1084
    }
1322
    }
Line 1088... Line 1326...
1088
     *
1326
     *
1089
     * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
1327
     * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
1090
     * both sides MUST send an identification string". It does not say which side sends it first. In
1328
     * both sides MUST send an identification string". It does not say which side sends it first. In
1091
     * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
1329
     * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
1092
     *
1330
     *
-
 
1331
     * @access public
1093
     */
1332
     */
1094
    public function sendIdentificationStringFirst()
1333
    public function sendIdentificationStringFirst()
1095
    {
1334
    {
1096
        $this->send_id_string_first = true;
1335
        $this->send_id_string_first = true;
1097
    }
1336
    }
Line 1101... Line 1340...
1101
     *
1340
     *
1102
     * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
1341
     * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
1103
     * both sides MUST send an identification string". It does not say which side sends it first. In
1342
     * both sides MUST send an identification string". It does not say which side sends it first. In
1104
     * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
1343
     * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
1105
     *
1344
     *
-
 
1345
     * @access public
1106
     */
1346
     */
1107
    public function sendIdentificationStringLast()
1347
    public function sendIdentificationStringLast()
1108
    {
1348
    {
1109
        $this->send_id_string_first = false;
1349
        $this->send_id_string_first = false;
1110
    }
1350
    }
Line 1114... Line 1354...
1114
     *
1354
     *
1115
     * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
1355
     * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
1116
     * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
1356
     * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
1117
     * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
1357
     * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
1118
     *
1358
     *
-
 
1359
     * @access public
1119
     */
1360
     */
1120
    public function sendKEXINITFirst()
1361
    public function sendKEXINITFirst()
1121
    {
1362
    {
1122
        $this->send_kex_first = true;
1363
        $this->send_kex_first = true;
1123
    }
1364
    }
Line 1127... Line 1368...
1127
     *
1368
     *
1128
     * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
1369
     * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
1129
     * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
1370
     * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
1130
     * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
1371
     * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
1131
     *
1372
     *
-
 
1373
     * @access public
1132
     */
1374
     */
1133
    public function sendKEXINITLast()
1375
    public function sendKEXINITLast()
1134
    {
1376
    {
1135
        $this->send_kex_first = false;
1377
        $this->send_kex_first = false;
1136
    }
1378
    }
Line 1138... Line 1380...
1138
    /**
1380
    /**
1139
     * Connect to an SSHv2 server
1381
     * Connect to an SSHv2 server
1140
     *
1382
     *
1141
     * @throws \UnexpectedValueException on receipt of unexpected packets
1383
     * @throws \UnexpectedValueException on receipt of unexpected packets
1142
     * @throws \RuntimeException on other errors
1384
     * @throws \RuntimeException on other errors
-
 
1385
     * @access private
1143
     */
1386
     */
1144
    private function connect()
1387
    private function connect()
1145
    {
1388
    {
1146
        if ($this->bitmap & self::MASK_CONSTRUCTOR) {
1389
        if ($this->bitmap & self::MASK_CONSTRUCTOR) {
1147
            return;
1390
            return;
Line 1259... Line 1502...
1259
        }
1502
        }
1260
 
1503
 
1261
        if (!$this->send_kex_first) {
1504
        if (!$this->send_kex_first) {
1262
            $response = $this->get_binary_packet();
1505
            $response = $this->get_binary_packet();
1263
 
1506
 
1264
            if (is_bool($response) || !strlen($response) || ord($response[0]) != MessageType::KEXINIT) {
1507
            if (is_bool($response) || !strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
1265
                $this->bitmap = 0;
1508
                $this->bitmap = 0;
1266
                throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT');
1509
                throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT');
1267
            }
1510
            }
1268
 
1511
 
1269
            $this->key_exchange($response);
1512
            $this->key_exchange($response);
Line 1281... Line 1524...
1281
    /**
1524
    /**
1282
     * Generates the SSH identifier
1525
     * Generates the SSH identifier
1283
     *
1526
     *
1284
     * You should overwrite this method in your own class if you want to use another identifier
1527
     * You should overwrite this method in your own class if you want to use another identifier
1285
     *
1528
     *
-
 
1529
     * @access protected
1286
     * @return string
1530
     * @return string
1287
     */
1531
     */
1288
    private function generate_identifier()
1532
    private function generate_identifier()
1289
    {
1533
    {
1290
        $identifier = 'SSH-2.0-phpseclib_3.0';
1534
        $identifier = 'SSH-2.0-phpseclib_3.0';
Line 1319... Line 1563...
1319
     * @return bool
1563
     * @return bool
1320
     * @param string|bool $kexinit_payload_server optional
1564
     * @param string|bool $kexinit_payload_server optional
1321
     * @throws \UnexpectedValueException on receipt of unexpected packets
1565
     * @throws \UnexpectedValueException on receipt of unexpected packets
1322
     * @throws \RuntimeException on other errors
1566
     * @throws \RuntimeException on other errors
1323
     * @throws \phpseclib3\Exception\NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible
1567
     * @throws \phpseclib3\Exception\NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible
-
 
1568
     * @access private
1324
     */
1569
     */
1325
    private function key_exchange($kexinit_payload_server = false)
1570
    private function key_exchange($kexinit_payload_server = false)
1326
    {
1571
    {
1327
        $preferred = $this->preferred;
1572
        $preferred = $this->preferred;
1328
        $send_kex = true;
1573
        $send_kex = true;
Line 1370... Line 1615...
1370
                }
1615
                }
1371
        }
1616
        }
1372
 
1617
 
1373
        $client_cookie = Random::string(16);
1618
        $client_cookie = Random::string(16);
1374
 
1619
 
1375
        $kexinit_payload_client = pack('Ca*', MessageType::KEXINIT, $client_cookie);
1620
        $kexinit_payload_client = pack('Ca*', NET_SSH2_MSG_KEXINIT, $client_cookie);
1376
        $kexinit_payload_client .= Strings::packSSH2(
1621
        $kexinit_payload_client .= Strings::packSSH2(
1377
            'L10bN',
1622
            'L10bN',
1378
            $kex_algorithms,
1623
            $kex_algorithms,
1379
            $server_host_key_algorithms,
1624
            $server_host_key_algorithms,
1380
            $c2s_encryption_algorithms,
1625
            $c2s_encryption_algorithms,
Line 1395... Line 1640...
1395
            $kexinit_payload_server = $this->get_binary_packet();
1640
            $kexinit_payload_server = $this->get_binary_packet();
1396
 
1641
 
1397
            if (
1642
            if (
1398
                is_bool($kexinit_payload_server)
1643
                is_bool($kexinit_payload_server)
1399
                || !strlen($kexinit_payload_server)
1644
                || !strlen($kexinit_payload_server)
1400
                || ord($kexinit_payload_server[0]) != MessageType::KEXINIT
1645
                || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT
1401
            ) {
1646
            ) {
1402
                $this->disconnect_helper(DisconnectReason::PROTOCOL_ERROR);
1647
                $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR);
1403
                throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT');
1648
                throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT');
1404
            }
1649
            }
1405
 
1650
 
1406
            $send_kex = false;
1651
            $send_kex = false;
1407
        }
1652
        }
Line 1433... Line 1678...
1433
        // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
1678
        // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
1434
        // diffie-hellman key exchange as fast as possible
1679
        // diffie-hellman key exchange as fast as possible
1435
        $decrypt = self::array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client);
1680
        $decrypt = self::array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client);
1436
        $decryptKeyLength = $this->encryption_algorithm_to_key_size($decrypt);
1681
        $decryptKeyLength = $this->encryption_algorithm_to_key_size($decrypt);
1437
        if ($decryptKeyLength === null) {
1682
        if ($decryptKeyLength === null) {
1438
            $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
1683
            $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1439
            throw new NoSupportedAlgorithmsException('No compatible server to client encryption algorithms found');
1684
            throw new NoSupportedAlgorithmsException('No compatible server to client encryption algorithms found');
1440
        }
1685
        }
1441
 
1686
 
1442
        $encrypt = self::array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server);
1687
        $encrypt = self::array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server);
1443
        $encryptKeyLength = $this->encryption_algorithm_to_key_size($encrypt);
1688
        $encryptKeyLength = $this->encryption_algorithm_to_key_size($encrypt);
1444
        if ($encryptKeyLength === null) {
1689
        if ($encryptKeyLength === null) {
1445
            $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
1690
            $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1446
            throw new NoSupportedAlgorithmsException('No compatible client to server encryption algorithms found');
1691
            throw new NoSupportedAlgorithmsException('No compatible client to server encryption algorithms found');
1447
        }
1692
        }
1448
 
1693
 
1449
        // through diffie-hellman key exchange a symmetric key is obtained
1694
        // through diffie-hellman key exchange a symmetric key is obtained
1450
        $this->kex_algorithm = self::array_intersect_first($kex_algorithms, $this->kex_algorithms);
1695
        $this->kex_algorithm = self::array_intersect_first($kex_algorithms, $this->kex_algorithms);
1451
        if ($this->kex_algorithm === false) {
1696
        if ($this->kex_algorithm === false) {
1452
            $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
1697
            $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1453
            throw new NoSupportedAlgorithmsException('No compatible key exchange algorithms found');
1698
            throw new NoSupportedAlgorithmsException('No compatible key exchange algorithms found');
1454
        }
1699
        }
1455
 
1700
 
1456
        $server_host_key_algorithm = self::array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
1701
        $server_host_key_algorithm = self::array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
1457
        if ($server_host_key_algorithm === false) {
1702
        if ($server_host_key_algorithm === false) {
1458
            $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
1703
            $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1459
            throw new NoSupportedAlgorithmsException('No compatible server host key algorithms found');
1704
            throw new NoSupportedAlgorithmsException('No compatible server host key algorithms found');
1460
        }
1705
        }
1461
 
1706
 
1462
        $mac_algorithm_out = self::array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server);
1707
        $mac_algorithm_out = self::array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server);
1463
        if ($mac_algorithm_out === false) {
1708
        if ($mac_algorithm_out === false) {
1464
            $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
1709
            $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1465
            throw new NoSupportedAlgorithmsException('No compatible client to server message authentication algorithms found');
1710
            throw new NoSupportedAlgorithmsException('No compatible client to server message authentication algorithms found');
1466
        }
1711
        }
1467
 
1712
 
1468
        $mac_algorithm_in = self::array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client);
1713
        $mac_algorithm_in = self::array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client);
1469
        if ($mac_algorithm_in === false) {
1714
        if ($mac_algorithm_in === false) {
1470
            $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
1715
            $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1471
            throw new NoSupportedAlgorithmsException('No compatible server to client message authentication algorithms found');
1716
            throw new NoSupportedAlgorithmsException('No compatible server to client message authentication algorithms found');
1472
        }
1717
        }
1473
 
1718
 
1474
        $compression_map = [
1719
        $compression_map = [
1475
            'none' => self::NET_SSH2_COMPRESSION_NONE,
1720
            'none' => self::NET_SSH2_COMPRESSION_NONE,
Line 1477... Line 1722...
1477
            'zlib@openssh.com' => self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH
1722
            'zlib@openssh.com' => self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH
1478
        ];
1723
        ];
1479
 
1724
 
1480
        $compression_algorithm_in = self::array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client);
1725
        $compression_algorithm_in = self::array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client);
1481
        if ($compression_algorithm_in === false) {
1726
        if ($compression_algorithm_in === false) {
1482
            $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
1727
            $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1483
            throw new NoSupportedAlgorithmsException('No compatible server to client compression algorithms found');
1728
            throw new NoSupportedAlgorithmsException('No compatible server to client compression algorithms found');
1484
        }
1729
        }
1485
        $this->decompress = $compression_map[$compression_algorithm_in];
1730
        $this->decompress = $compression_map[$compression_algorithm_in];
1486
 
1731
 
1487
        $compression_algorithm_out = self::array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
1732
        $compression_algorithm_out = self::array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
1488
        if ($compression_algorithm_out === false) {
1733
        if ($compression_algorithm_out === false) {
1489
            $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
1734
            $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1490
            throw new NoSupportedAlgorithmsException('No compatible client to server compression algorithms found');
1735
            throw new NoSupportedAlgorithmsException('No compatible client to server compression algorithms found');
1491
        }
1736
        }
1492
        $this->compress = $compression_map[$compression_algorithm_out];
1737
        $this->compress = $compression_map[$compression_algorithm_out];
1493
 
1738
 
1494
        switch ($this->kex_algorithm) {
1739
        switch ($this->kex_algorithm) {
Line 1521... Line 1766...
1521
            $curve = strpos($this->kex_algorithm, 'curve25519-sha256') === 0 ?
1766
            $curve = strpos($this->kex_algorithm, 'curve25519-sha256') === 0 ?
1522
                'Curve25519' :
1767
                'Curve25519' :
1523
                substr($this->kex_algorithm, 10);
1768
                substr($this->kex_algorithm, 10);
1524
            $ourPrivate = EC::createKey($curve);
1769
            $ourPrivate = EC::createKey($curve);
1525
            $ourPublicBytes = $ourPrivate->getPublicKey()->getEncodedCoordinates();
1770
            $ourPublicBytes = $ourPrivate->getPublicKey()->getEncodedCoordinates();
1526
            $clientKexInitMessage = MessageTypeExtra::KEX_ECDH_INIT;
1771
            $clientKexInitMessage = 'NET_SSH2_MSG_KEX_ECDH_INIT';
1527
            $serverKexReplyMessage = MessageTypeExtra::KEX_ECDH_REPLY;
1772
            $serverKexReplyMessage = 'NET_SSH2_MSG_KEX_ECDH_REPLY';
1528
        } else {
1773
        } else {
1529
            if (strpos($this->kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
1774
            if (strpos($this->kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
1530
                $dh_group_sizes_packed = pack(
1775
                $dh_group_sizes_packed = pack(
1531
                    'NNN',
1776
                    'NNN',
1532
                    $this->kex_dh_group_size_min,
1777
                    $this->kex_dh_group_size_min,
1533
                    $this->kex_dh_group_size_preferred,
1778
                    $this->kex_dh_group_size_preferred,
1534
                    $this->kex_dh_group_size_max
1779
                    $this->kex_dh_group_size_max
1535
                );
1780
                );
1536
                $packet = pack(
1781
                $packet = pack(
1537
                    'Ca*',
1782
                    'Ca*',
1538
                    MessageTypeExtra::KEXDH_GEX_REQUEST,
1783
                    NET_SSH2_MSG_KEXDH_GEX_REQUEST,
1539
                    $dh_group_sizes_packed
1784
                    $dh_group_sizes_packed
1540
                );
1785
                );
1541
                $this->send_binary_packet($packet);
1786
                $this->send_binary_packet($packet);
1542
                $this->updateLogHistory('UNKNOWN (34)', 'SSH_MSG_KEXDH_GEX_REQUEST');
1787
                $this->updateLogHistory('UNKNOWN (34)', 'NET_SSH2_MSG_KEXDH_GEX_REQUEST');
1543
 
1788
 
1544
                $response = $this->get_binary_packet();
1789
                $response = $this->get_binary_packet();
1545
 
1790
 
1546
                list($type, $primeBytes, $gBytes) = Strings::unpackSSH2('Css', $response);
1791
                list($type, $primeBytes, $gBytes) = Strings::unpackSSH2('Css', $response);
1547
                if ($type != MessageTypeExtra::KEXDH_GEX_GROUP) {
1792
                if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) {
1548
                    $this->disconnect_helper(DisconnectReason::PROTOCOL_ERROR);
1793
                    $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR);
1549
                    throw new \UnexpectedValueException('Expected SSH_MSG_KEX_DH_GEX_GROUP');
1794
                    throw new \UnexpectedValueException('Expected SSH_MSG_KEX_DH_GEX_GROUP');
1550
                }
1795
                }
1551
                $this->updateLogHistory('UNKNOWN (31)', 'SSH_MSG_KEXDH_GEX_GROUP');
1796
                $this->updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEXDH_GEX_GROUP');
1552
                $prime = new BigInteger($primeBytes, -256);
1797
                $prime = new BigInteger($primeBytes, -256);
1553
                $g = new BigInteger($gBytes, -256);
1798
                $g = new BigInteger($gBytes, -256);
1554
 
1799
 
1555
                $exchange_hash_rfc4419 = $dh_group_sizes_packed . Strings::packSSH2(
1800
                $exchange_hash_rfc4419 = $dh_group_sizes_packed . Strings::packSSH2(
1556
                    'ss',
1801
                    'ss',
1557
                    $primeBytes,
1802
                    $primeBytes,
1558
                    $gBytes
1803
                    $gBytes
1559
                );
1804
                );
1560
 
1805
 
1561
                $params = DH::createParameters($prime, $g);
1806
                $params = DH::createParameters($prime, $g);
1562
                $clientKexInitMessage = MessageTypeExtra::KEXDH_GEX_INIT;
1807
                $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_GEX_INIT';
1563
                $serverKexReplyMessage = MessageTypeExtra::KEXDH_GEX_REPLY;
1808
                $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_GEX_REPLY';
1564
            } else {
1809
            } else {
1565
                $params = DH::createParameters($this->kex_algorithm);
1810
                $params = DH::createParameters($this->kex_algorithm);
1566
                $clientKexInitMessage = MessageType::KEXDH_INIT;
1811
                $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_INIT';
1567
                $serverKexReplyMessage = MessageType::KEXDH_REPLY;
1812
                $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_REPLY';
1568
            }
1813
            }
1569
 
1814
 
1570
            $keyLength = min($kexHash->getLengthInBytes(), max($encryptKeyLength, $decryptKeyLength));
1815
            $keyLength = min($kexHash->getLengthInBytes(), max($encryptKeyLength, $decryptKeyLength));
1571
 
1816
 
1572
            $ourPrivate = DH::createKey($params, 16 * $keyLength); // 2 * 8 * $keyLength
1817
            $ourPrivate = DH::createKey($params, 16 * $keyLength); // 2 * 8 * $keyLength
1573
            $ourPublic = $ourPrivate->getPublicKey()->toBigInteger();
1818
            $ourPublic = $ourPrivate->getPublicKey()->toBigInteger();
1574
            $ourPublicBytes = $ourPublic->toBytes(true);
1819
            $ourPublicBytes = $ourPublic->toBytes(true);
1575
        }
1820
        }
1576
 
1821
 
1577
        $data = pack('CNa*', $clientKexInitMessage, strlen($ourPublicBytes), $ourPublicBytes);
1822
        $data = pack('CNa*', constant($clientKexInitMessage), strlen($ourPublicBytes), $ourPublicBytes);
1578
 
1823
 
1579
        $this->send_binary_packet($data);
1824
        $this->send_binary_packet($data);
1580
 
1825
 
1581
        switch ($clientKexInitMessage) {
1826
        switch ($clientKexInitMessage) {
1582
            case MessageTypeExtra::KEX_ECDH_INIT:
1827
            case 'NET_SSH2_MSG_KEX_ECDH_INIT':
1583
                $this->updateLogHistory('SSH_MSG_KEXDH_INIT', 'SSH_MSG_KEX_ECDH_INIT');
1828
                $this->updateLogHistory('NET_SSH2_MSG_KEXDH_INIT', 'NET_SSH2_MSG_KEX_ECDH_INIT');
1584
                break;
1829
                break;
1585
            case MessageTypeExtra::KEXDH_GEX_INIT:
1830
            case 'NET_SSH2_MSG_KEXDH_GEX_INIT':
1586
                $this->updateLogHistory('UNKNOWN (32)', 'SSH_MSG_KEXDH_GEX_INIT');
1831
                $this->updateLogHistory('UNKNOWN (32)', 'NET_SSH2_MSG_KEXDH_GEX_INIT');
1587
        }
1832
        }
1588
 
1833
 
1589
        $response = $this->get_binary_packet();
1834
        $response = $this->get_binary_packet();
1590
 
1835
 
1591
        list(
1836
        list(
Line 1593... Line 1838...
1593
            $server_public_host_key,
1838
            $server_public_host_key,
1594
            $theirPublicBytes,
1839
            $theirPublicBytes,
1595
            $this->signature
1840
            $this->signature
1596
        ) = Strings::unpackSSH2('Csss', $response);
1841
        ) = Strings::unpackSSH2('Csss', $response);
1597
 
1842
 
1598
        if ($type != $serverKexReplyMessage) {
1843
        if ($type != constant($serverKexReplyMessage)) {
1599
            $this->disconnect_helper(DisconnectReason::PROTOCOL_ERROR);
1844
            $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR);
1600
            throw new \UnexpectedValueException("Expected $serverKexReplyMessage");
1845
            throw new \UnexpectedValueException("Expected $serverKexReplyMessage");
1601
        }
1846
        }
1602
        switch ($serverKexReplyMessage) {
1847
        switch ($serverKexReplyMessage) {
1603
            case MessageTypeExtra::KEX_ECDH_REPLY:
1848
            case 'NET_SSH2_MSG_KEX_ECDH_REPLY':
1604
                $this->updateLogHistory('SSH_MSG_KEXDH_REPLY', 'SSH_MSG_KEX_ECDH_REPLY');
1849
                $this->updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEX_ECDH_REPLY');
1605
                break;
1850
                break;
1606
            case MessageTypeExtra::KEXDH_GEX_REPLY:
1851
            case 'NET_SSH2_MSG_KEXDH_GEX_REPLY':
1607
                $this->updateLogHistory('UNKNOWN (33)', 'SSH_MSG_KEXDH_GEX_REPLY');
1852
                $this->updateLogHistory('UNKNOWN (33)', 'NET_SSH2_MSG_KEXDH_GEX_REPLY');
1608
        }
1853
        }
1609
 
1854
 
1610
        $this->server_public_host_key = $server_public_host_key;
1855
        $this->server_public_host_key = $server_public_host_key;
1611
        list($public_key_format) = Strings::unpackSSH2('s', $server_public_host_key);
1856
        list($public_key_format) = Strings::unpackSSH2('s', $server_public_host_key);
1612
        if (strlen($this->signature) < 4) {
1857
        if (strlen($this->signature) < 4) {
Line 1656... Line 1901...
1656
        if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) {
1901
        if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) {
1657
            switch (true) {
1902
            switch (true) {
1658
                case $this->signature_format == $server_host_key_algorithm:
1903
                case $this->signature_format == $server_host_key_algorithm:
1659
                case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512':
1904
                case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512':
1660
                case $this->signature_format != 'ssh-rsa':
1905
                case $this->signature_format != 'ssh-rsa':
1661
                    $this->disconnect_helper(DisconnectReason::HOST_KEY_NOT_VERIFIABLE);
1906
                    $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
1662
                    throw new \RuntimeException('Server Host Key Algorithm Mismatch (' . $this->signature_format . ' vs ' . $server_host_key_algorithm . ')');
1907
                    throw new \RuntimeException('Server Host Key Algorithm Mismatch (' . $this->signature_format . ' vs ' . $server_host_key_algorithm . ')');
1663
            }
1908
            }
1664
        }
1909
        }
1665
 
1910
 
1666
        $packet = pack('C', MessageType::NEWKEYS);
1911
        $packet = pack('C', NET_SSH2_MSG_NEWKEYS);
1667
        $this->send_binary_packet($packet);
1912
        $this->send_binary_packet($packet);
1668
 
1913
 
1669
        $response = $this->get_binary_packet();
1914
        $response = $this->get_binary_packet();
1670
 
1915
 
1671
        if ($response === false) {
1916
        if ($response === false) {
1672
            $this->disconnect_helper(DisconnectReason::CONNECTION_LOST);
1917
            $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST);
1673
            throw new ConnectionClosedException('Connection closed by server');
1918
            throw new ConnectionClosedException('Connection closed by server');
1674
        }
1919
        }
1675
 
1920
 
1676
        list($type) = Strings::unpackSSH2('C', $response);
1921
        list($type) = Strings::unpackSSH2('C', $response);
1677
        if ($type != MessageType::NEWKEYS) {
1922
        if ($type != NET_SSH2_MSG_NEWKEYS) {
1678
            $this->disconnect_helper(DisconnectReason::PROTOCOL_ERROR);
1923
            $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR);
1679
            throw new \UnexpectedValueException('Expected SSH_MSG_NEWKEYS');
1924
            throw new \UnexpectedValueException('Expected SSH_MSG_NEWKEYS');
1680
        }
1925
        }
1681
 
1926
 
1682
        $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
1927
        $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
1683
 
1928
 
Line 1834... Line 2079...
1834
    /**
2079
    /**
1835
     * Maps an encryption algorithm name to the number of key bytes.
2080
     * Maps an encryption algorithm name to the number of key bytes.
1836
     *
2081
     *
1837
     * @param string $algorithm Name of the encryption algorithm
2082
     * @param string $algorithm Name of the encryption algorithm
1838
     * @return int|null Number of bytes as an integer or null for unknown
2083
     * @return int|null Number of bytes as an integer or null for unknown
-
 
2084
     * @access private
1839
     */
2085
     */
1840
    private function encryption_algorithm_to_key_size($algorithm)
2086
    private function encryption_algorithm_to_key_size($algorithm)
1841
    {
2087
    {
1842
        if ($this->bad_key_size_fix && self::bad_algorithm_candidate($algorithm)) {
2088
        if ($this->bad_key_size_fix && self::bad_algorithm_candidate($algorithm)) {
1843
            return 16;
2089
            return 16;
Line 1881... Line 2127...
1881
     * Maps an encryption algorithm name to an instance of a subclass of
2127
     * Maps an encryption algorithm name to an instance of a subclass of
1882
     * \phpseclib3\Crypt\Common\SymmetricKey.
2128
     * \phpseclib3\Crypt\Common\SymmetricKey.
1883
     *
2129
     *
1884
     * @param string $algorithm Name of the encryption algorithm
2130
     * @param string $algorithm Name of the encryption algorithm
1885
     * @return SymmetricKey|null
2131
     * @return SymmetricKey|null
-
 
2132
     * @access private
1886
     */
2133
     */
1887
    private static function encryption_algorithm_to_crypt_instance($algorithm)
2134
    private static function encryption_algorithm_to_crypt_instance($algorithm)
1888
    {
2135
    {
1889
        switch ($algorithm) {
2136
        switch ($algorithm) {
1890
            case '3des-cbc':
2137
            case '3des-cbc':
Line 1929... Line 2176...
1929
     * Maps an encryption algorithm name to an instance of a subclass of
2176
     * Maps an encryption algorithm name to an instance of a subclass of
1930
     * \phpseclib3\Crypt\Hash.
2177
     * \phpseclib3\Crypt\Hash.
1931
     *
2178
     *
1932
     * @param string $algorithm Name of the encryption algorithm
2179
     * @param string $algorithm Name of the encryption algorithm
1933
     * @return array{Hash, int}|null
2180
     * @return array{Hash, int}|null
-
 
2181
     * @access private
1934
     */
2182
     */
1935
    private static function mac_algorithm_to_hash_instance($algorithm)
2183
    private static function mac_algorithm_to_hash_instance($algorithm)
1936
    {
2184
    {
1937
        switch ($algorithm) {
2185
        switch ($algorithm) {
1938
            case 'umac-64@openssh.com':
2186
            case 'umac-64@openssh.com':
Line 1964... Line 2212...
1964
     *
2212
     *
1965
     * @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html
2213
     * @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html
1966
     * @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291
2214
     * @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291
1967
     * @param string $algorithm Name of the encryption algorithm
2215
     * @param string $algorithm Name of the encryption algorithm
1968
     * @return bool
2216
     * @return bool
-
 
2217
     * @access private
1969
     */
2218
     */
1970
    private static function bad_algorithm_candidate($algorithm)
2219
    private static function bad_algorithm_candidate($algorithm)
1971
    {
2220
    {
1972
        switch ($algorithm) {
2221
        switch ($algorithm) {
1973
            case 'arcfour256':
2222
            case 'arcfour256':
Line 1986... Line 2235...
1986
     *
2235
     *
1987
     * @param string $username
2236
     * @param string $username
1988
     * @param string|AsymmetricKey|array[]|Agent|null ...$args
2237
     * @param string|AsymmetricKey|array[]|Agent|null ...$args
1989
     * @return bool
2238
     * @return bool
1990
     * @see self::_login()
2239
     * @see self::_login()
-
 
2240
     * @access public
1991
     */
2241
     */
1992
    public function login($username, ...$args)
2242
    public function login($username, ...$args)
1993
    {
2243
    {
1994
        $this->auth[] = func_get_args();
2244
        $this->auth[] = func_get_args();
1995
 
2245
 
Line 2011... Line 2261...
2011
     *
2261
     *
2012
     * @param string $username
2262
     * @param string $username
2013
     * @param string ...$args
2263
     * @param string ...$args
2014
     * @return bool
2264
     * @return bool
2015
     * @see self::_login_helper()
2265
     * @see self::_login_helper()
-
 
2266
     * @access private
2016
     */
2267
     */
2017
    protected function sublogin($username, ...$args)
2268
    protected function sublogin($username, ...$args)
2018
    {
2269
    {
2019
        if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
2270
        if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
2020
            $this->connect();
2271
            $this->connect();
Line 2108... Line 2359...
2108
     * @param string $username
2359
     * @param string $username
2109
     * @param string|AsymmetricKey|array[]|Agent|null ...$args
2360
     * @param string|AsymmetricKey|array[]|Agent|null ...$args
2110
     * @return bool
2361
     * @return bool
2111
     * @throws \UnexpectedValueException on receipt of unexpected packets
2362
     * @throws \UnexpectedValueException on receipt of unexpected packets
2112
     * @throws \RuntimeException on other errors
2363
     * @throws \RuntimeException on other errors
-
 
2364
     * @access private
2113
     */
2365
     */
2114
    private function login_helper($username, $password = null)
2366
    private function login_helper($username, $password = null)
2115
    {
2367
    {
2116
        if (!($this->bitmap & self::MASK_CONNECTED)) {
2368
        if (!($this->bitmap & self::MASK_CONNECTED)) {
2117
            return false;
2369
            return false;
2118
        }
2370
        }
2119
 
2371
 
2120
        if (!($this->bitmap & self::MASK_LOGIN_REQ)) {
2372
        if (!($this->bitmap & self::MASK_LOGIN_REQ)) {
2121
            $packet = Strings::packSSH2('Cs', MessageType::SERVICE_REQUEST, 'ssh-userauth');
2373
            $packet = Strings::packSSH2('Cs', NET_SSH2_MSG_SERVICE_REQUEST, 'ssh-userauth');
2122
            $this->send_binary_packet($packet);
2374
            $this->send_binary_packet($packet);
2123
 
2375
 
2124
            try {
2376
            try {
2125
                $response = $this->get_binary_packet();
2377
                $response = $this->get_binary_packet();
2126
            } catch (\Exception $e) {
2378
            } catch (\Exception $e) {
2127
                if ($this->retry_connect) {
2379
                if ($this->retry_connect) {
2128
                    $this->retry_connect = false;
2380
                    $this->retry_connect = false;
2129
                    $this->connect();
2381
                    $this->connect();
2130
                    return $this->login_helper($username, $password);
2382
                    return $this->login_helper($username, $password);
2131
                }
2383
                }
2132
                $this->disconnect_helper(DisconnectReason::CONNECTION_LOST);
2384
                $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST);
2133
                throw new ConnectionClosedException('Connection closed by server');
2385
                throw new ConnectionClosedException('Connection closed by server');
2134
            }
2386
            }
2135
 
2387
 
2136
            list($type, $service) = Strings::unpackSSH2('Cs', $response);
2388
            list($type, $service) = Strings::unpackSSH2('Cs', $response);
2137
            if ($type != MessageType::SERVICE_ACCEPT || $service != 'ssh-userauth') {
2389
            if ($type != NET_SSH2_MSG_SERVICE_ACCEPT || $service != 'ssh-userauth') {
2138
                $this->disconnect_helper(DisconnectReason::PROTOCOL_ERROR);
2390
                $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR);
2139
                throw new \UnexpectedValueException('Expected SSH_MSG_SERVICE_ACCEPT');
2391
                throw new \UnexpectedValueException('Expected SSH_MSG_SERVICE_ACCEPT');
2140
            }
2392
            }
2141
            $this->bitmap |= self::MASK_LOGIN_REQ;
2393
            $this->bitmap |= self::MASK_LOGIN_REQ;
2142
        }
2394
        }
2143
 
2395
 
Line 2162... Line 2414...
2162
        }
2414
        }
2163
 
2415
 
2164
        if (!isset($password)) {
2416
        if (!isset($password)) {
2165
            $packet = Strings::packSSH2(
2417
            $packet = Strings::packSSH2(
2166
                'Cs3',
2418
                'Cs3',
2167
                MessageType::USERAUTH_REQUEST,
2419
                NET_SSH2_MSG_USERAUTH_REQUEST,
2168
                $username,
2420
                $username,
2169
                'ssh-connection',
2421
                'ssh-connection',
2170
                'none'
2422
                'none'
2171
            );
2423
            );
2172
 
2424
 
Line 2174... Line 2426...
2174
 
2426
 
2175
            $response = $this->get_binary_packet();
2427
            $response = $this->get_binary_packet();
2176
 
2428
 
2177
            list($type) = Strings::unpackSSH2('C', $response);
2429
            list($type) = Strings::unpackSSH2('C', $response);
2178
            switch ($type) {
2430
            switch ($type) {
2179
                case MessageType::USERAUTH_SUCCESS:
2431
                case NET_SSH2_MSG_USERAUTH_SUCCESS:
2180
                    $this->bitmap |= self::MASK_LOGIN;
2432
                    $this->bitmap |= self::MASK_LOGIN;
2181
                    return true;
2433
                    return true;
2182
                case MessageType::USERAUTH_FAILURE:
2434
                case NET_SSH2_MSG_USERAUTH_FAILURE:
2183
                    list($auth_methods) = Strings::unpackSSH2('L', $response);
2435
                    list($auth_methods) = Strings::unpackSSH2('L', $response);
2184
                    $this->auth_methods_to_continue = $auth_methods;
2436
                    $this->auth_methods_to_continue = $auth_methods;
2185
                    // fall-through
2437
                    // fall-through
2186
                default:
2438
                default:
2187
                    return false;
2439
                    return false;
2188
            }
2440
            }
2189
        }
2441
        }
2190
 
2442
 
2191
        $packet = Strings::packSSH2(
2443
        $packet = Strings::packSSH2(
2192
            'Cs3bs',
2444
            'Cs3bs',
2193
            MessageType::USERAUTH_REQUEST,
2445
            NET_SSH2_MSG_USERAUTH_REQUEST,
2194
            $username,
2446
            $username,
2195
            'ssh-connection',
2447
            'ssh-connection',
2196
            'password',
2448
            'password',
2197
            false,
2449
            false,
2198
            $password
2450
            $password
Line 2202... Line 2454...
2202
        if (!defined('NET_SSH2_LOGGING')) {
2454
        if (!defined('NET_SSH2_LOGGING')) {
2203
            $logged = null;
2455
            $logged = null;
2204
        } else {
2456
        } else {
2205
            $logged = Strings::packSSH2(
2457
            $logged = Strings::packSSH2(
2206
                'Cs3bs',
2458
                'Cs3bs',
2207
                MessageType::USERAUTH_REQUEST,
2459
                NET_SSH2_MSG_USERAUTH_REQUEST,
2208
                $username,
2460
                $username,
2209
                'ssh-connection',
2461
                'ssh-connection',
2210
                'password',
2462
                'password',
2211
                false,
2463
                false,
2212
                'password'
2464
                'password'
Line 2214... Line 2466...
2214
        }
2466
        }
2215
 
2467
 
2216
        $this->send_binary_packet($packet, $logged);
2468
        $this->send_binary_packet($packet, $logged);
2217
 
2469
 
2218
        $response = $this->get_binary_packet();
2470
        $response = $this->get_binary_packet();
2219
        if ($response === false) {
-
 
2220
            return false;
-
 
2221
        }
2471
 
2222
        list($type) = Strings::unpackSSH2('C', $response);
2472
        list($type) = Strings::unpackSSH2('C', $response);
2223
        switch ($type) {
2473
        switch ($type) {
2224
            case MessageTypeExtra::USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
2474
            case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
2225
                $this->updateLogHistory('SSH_MSG_USERAUTH_INFO_REQUEST', 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ');
2475
                $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ');
2226
 
2476
 
2227
                list($message) = Strings::unpackSSH2('s', $response);
2477
                list($message) = Strings::unpackSSH2('s', $response);
2228
                $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $message;
2478
                $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $message;
2229
 
2479
 
2230
                return $this->disconnect_helper(DisconnectReason::AUTH_CANCELLED_BY_USER);
2480
                return $this->disconnect_helper(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
2231
            case MessageType::USERAUTH_FAILURE:
2481
            case NET_SSH2_MSG_USERAUTH_FAILURE:
2232
                // can we use keyboard-interactive authentication?  if not then either the login is bad or the server employees
2482
                // can we use keyboard-interactive authentication?  if not then either the login is bad or the server employees
2233
                // multi-factor authentication
2483
                // multi-factor authentication
2234
                list($auth_methods, $partial_success) = Strings::unpackSSH2('Lb', $response);
2484
                list($auth_methods, $partial_success) = Strings::unpackSSH2('Lb', $response);
2235
                $this->auth_methods_to_continue = $auth_methods;
2485
                $this->auth_methods_to_continue = $auth_methods;
2236
                if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) {
2486
                if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) {
Line 2239... Line 2489...
2239
                        return true;
2489
                        return true;
2240
                    }
2490
                    }
2241
                    return false;
2491
                    return false;
2242
                }
2492
                }
2243
                return false;
2493
                return false;
2244
            case MessageType::USERAUTH_SUCCESS:
2494
            case NET_SSH2_MSG_USERAUTH_SUCCESS:
2245
                $this->bitmap |= self::MASK_LOGIN;
2495
                $this->bitmap |= self::MASK_LOGIN;
2246
                return true;
2496
                return true;
2247
        }
2497
        }
2248
 
2498
 
2249
        return false;
2499
        return false;
Line 2255... Line 2505...
2255
     * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details.  This is not a full-featured keyboard-interactive authenticator.
2505
     * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details.  This is not a full-featured keyboard-interactive authenticator.
2256
     *
2506
     *
2257
     * @param string $username
2507
     * @param string $username
2258
     * @param string|array $password
2508
     * @param string|array $password
2259
     * @return bool
2509
     * @return bool
-
 
2510
     * @access private
2260
     */
2511
     */
2261
    private function keyboard_interactive_login($username, $password)
2512
    private function keyboard_interactive_login($username, $password)
2262
    {
2513
    {
2263
        $packet = Strings::packSSH2(
2514
        $packet = Strings::packSSH2(
2264
            'Cs5',
2515
            'Cs5',
2265
            MessageType::USERAUTH_REQUEST,
2516
            NET_SSH2_MSG_USERAUTH_REQUEST,
2266
            $username,
2517
            $username,
2267
            'ssh-connection',
2518
            'ssh-connection',
2268
            'keyboard-interactive',
2519
            'keyboard-interactive',
2269
            '', // language tag
2520
            '', // language tag
2270
            '' // submethods
2521
            '' // submethods
Line 2278... Line 2529...
2278
     * Handle the keyboard-interactive requests / responses.
2529
     * Handle the keyboard-interactive requests / responses.
2279
     *
2530
     *
2280
     * @param string|array ...$responses
2531
     * @param string|array ...$responses
2281
     * @return bool
2532
     * @return bool
2282
     * @throws \RuntimeException on connection error
2533
     * @throws \RuntimeException on connection error
-
 
2534
     * @access private
2283
     */
2535
     */
2284
    private function keyboard_interactive_process(...$responses)
2536
    private function keyboard_interactive_process(...$responses)
2285
    {
2537
    {
2286
        if (strlen($this->last_interactive_response)) {
2538
        if (strlen($this->last_interactive_response)) {
2287
            $response = $this->last_interactive_response;
2539
            $response = $this->last_interactive_response;
Line 2289... Line 2541...
2289
            $orig = $response = $this->get_binary_packet();
2541
            $orig = $response = $this->get_binary_packet();
2290
        }
2542
        }
2291
 
2543
 
2292
        list($type) = Strings::unpackSSH2('C', $response);
2544
        list($type) = Strings::unpackSSH2('C', $response);
2293
        switch ($type) {
2545
        switch ($type) {
2294
            case MessageType::USERAUTH_INFO_REQUEST:
2546
            case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
2295
                list(
2547
                list(
2296
                    , // name; may be empty
2548
                    , // name; may be empty
2297
                    , // instruction; may be empty
2549
                    , // instruction; may be empty
2298
                    , // language tag; may be empty
2550
                    , // language tag; may be empty
2299
                    $num_prompts
2551
                    $num_prompts
Line 2326... Line 2578...
2326
 
2578
 
2327
                // see http://tools.ietf.org/html/rfc4256#section-3.2
2579
                // see http://tools.ietf.org/html/rfc4256#section-3.2
2328
                if (strlen($this->last_interactive_response)) {
2580
                if (strlen($this->last_interactive_response)) {
2329
                    $this->last_interactive_response = '';
2581
                    $this->last_interactive_response = '';
2330
                } else {
2582
                } else {
2331
                    $this->updateLogHistory('UNKNOWN (60)', 'SSH_MSG_USERAUTH_INFO_REQUEST');
2583
                    $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST');
2332
                }
2584
                }
2333
 
2585
 
2334
                if (!count($responses) && $num_prompts) {
2586
                if (!count($responses) && $num_prompts) {
2335
                    $this->last_interactive_response = $orig;
2587
                    $this->last_interactive_response = $orig;
2336
                    return false;
2588
                    return false;
Line 2339... Line 2591...
2339
                /*
2591
                /*
2340
                   After obtaining the requested information from the user, the client
2592
                   After obtaining the requested information from the user, the client
2341
                   MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
2593
                   MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
2342
                */
2594
                */
2343
                // see http://tools.ietf.org/html/rfc4256#section-3.4
2595
                // see http://tools.ietf.org/html/rfc4256#section-3.4
2344
                $packet = $logged = pack('CN', MessageType::USERAUTH_INFO_RESPONSE, count($responses));
2596
                $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
2345
                for ($i = 0; $i < count($responses); $i++) {
2597
                for ($i = 0; $i < count($responses); $i++) {
2346
                    $packet .= Strings::packSSH2('s', $responses[$i]);
2598
                    $packet .= Strings::packSSH2('s', $responses[$i]);
2347
                    $logged .= Strings::packSSH2('s', 'dummy-answer');
2599
                    $logged .= Strings::packSSH2('s', 'dummy-answer');
2348
                }
2600
                }
2349
 
2601
 
2350
                $this->send_binary_packet($packet, $logged);
2602
                $this->send_binary_packet($packet, $logged);
2351
 
2603
 
2352
                $this->updateLogHistory('UNKNOWN (61)', 'SSH_MSG_USERAUTH_INFO_RESPONSE');
2604
                $this->updateLogHistory('UNKNOWN (61)', 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE');
2353
 
2605
 
2354
                /*
2606
                /*
2355
                   After receiving the response, the server MUST send either an
2607
                   After receiving the response, the server MUST send either an
2356
                   SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another
2608
                   SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another
2357
                   SSH_MSG_USERAUTH_INFO_REQUEST message.
2609
                   SSH_MSG_USERAUTH_INFO_REQUEST message.
2358
                */
2610
                */
2359
                // maybe phpseclib should force close the connection after x request / responses?  unless something like that is done
2611
                // maybe phpseclib should force close the connection after x request / responses?  unless something like that is done
2360
                // there could be an infinite loop of request / responses.
2612
                // there could be an infinite loop of request / responses.
2361
                return $this->keyboard_interactive_process();
2613
                return $this->keyboard_interactive_process();
2362
            case MessageType::USERAUTH_SUCCESS:
2614
            case NET_SSH2_MSG_USERAUTH_SUCCESS:
2363
                return true;
2615
                return true;
2364
            case MessageType::USERAUTH_FAILURE:
2616
            case NET_SSH2_MSG_USERAUTH_FAILURE:
2365
                list($auth_methods) = Strings::unpackSSH2('L', $response);
2617
                list($auth_methods) = Strings::unpackSSH2('L', $response);
2366
                $this->auth_methods_to_continue = $auth_methods;
2618
                $this->auth_methods_to_continue = $auth_methods;
2367
                return false;
2619
                return false;
2368
        }
2620
        }
2369
 
2621
 
Line 2374... Line 2626...
2374
     * Login with an ssh-agent provided key
2626
     * Login with an ssh-agent provided key
2375
     *
2627
     *
2376
     * @param string $username
2628
     * @param string $username
2377
     * @param \phpseclib3\System\SSH\Agent $agent
2629
     * @param \phpseclib3\System\SSH\Agent $agent
2378
     * @return bool
2630
     * @return bool
-
 
2631
     * @access private
2379
     */
2632
     */
2380
    private function ssh_agent_login($username, Agent $agent)
2633
    private function ssh_agent_login($username, Agent $agent)
2381
    {
2634
    {
2382
        $this->agent = $agent;
2635
        $this->agent = $agent;
2383
        $keys = $agent->requestIdentities();
2636
        $keys = $agent->requestIdentities();
Line 2398... Line 2651...
2398
     *
2651
     *
2399
     * @param string $username
2652
     * @param string $username
2400
     * @param \phpseclib3\Crypt\Common\PrivateKey $privatekey
2653
     * @param \phpseclib3\Crypt\Common\PrivateKey $privatekey
2401
     * @return bool
2654
     * @return bool
2402
     * @throws \RuntimeException on connection error
2655
     * @throws \RuntimeException on connection error
-
 
2656
     * @access private
2403
     */
2657
     */
2404
    private function privatekey_login($username, PrivateKey $privatekey)
2658
    private function privatekey_login($username, PrivateKey $privatekey)
2405
    {
2659
    {
2406
        $publickey = $privatekey->getPublicKey();
2660
        $publickey = $privatekey->getPublicKey();
2407
 
2661
 
Line 2462... Line 2716...
2462
 
2716
 
2463
        $publickeyStr = $publickey->toString('OpenSSH', ['binary' => true]);
2717
        $publickeyStr = $publickey->toString('OpenSSH', ['binary' => true]);
2464
 
2718
 
2465
        $part1 = Strings::packSSH2(
2719
        $part1 = Strings::packSSH2(
2466
            'Csss',
2720
            'Csss',
2467
            MessageType::USERAUTH_REQUEST,
2721
            NET_SSH2_MSG_USERAUTH_REQUEST,
2468
            $username,
2722
            $username,
2469
            'ssh-connection',
2723
            'ssh-connection',
2470
            'publickey'
2724
            'publickey'
2471
        );
2725
        );
2472
        $part2 = Strings::packSSH2('ss', $signatureType, $publickeyStr);
2726
        $part2 = Strings::packSSH2('ss', $signatureType, $publickeyStr);
Line 2476... Line 2730...
2476
 
2730
 
2477
        $response = $this->get_binary_packet();
2731
        $response = $this->get_binary_packet();
2478
 
2732
 
2479
        list($type) = Strings::unpackSSH2('C', $response);
2733
        list($type) = Strings::unpackSSH2('C', $response);
2480
        switch ($type) {
2734
        switch ($type) {
2481
            case MessageType::USERAUTH_FAILURE:
2735
            case NET_SSH2_MSG_USERAUTH_FAILURE:
2482
                list($auth_methods) = Strings::unpackSSH2('L', $response);
2736
                list($auth_methods) = Strings::unpackSSH2('L', $response);
2483
                $this->auth_methods_to_continue = $auth_methods;
2737
                $this->auth_methods_to_continue = $auth_methods;
2484
                $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE';
2738
                $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE';
2485
                return false;
2739
                return false;
2486
            case MessageTypeExtra::USERAUTH_PK_OK:
2740
            case NET_SSH2_MSG_USERAUTH_PK_OK:
2487
                // we'll just take it on faith that the public key blob and the public key algorithm name are as
2741
                // we'll just take it on faith that the public key blob and the public key algorithm name are as
2488
                // they should be
2742
                // they should be
2489
                $this->updateLogHistory('SSH_MSG_USERAUTH_INFO_REQUEST', 'SSH_MSG_USERAUTH_PK_OK');
2743
                $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK');
2490
                break;
2744
                break;
2491
            case MessageType::USERAUTH_SUCCESS:
2745
            case NET_SSH2_MSG_USERAUTH_SUCCESS:
2492
                $this->bitmap |= self::MASK_LOGIN;
2746
                $this->bitmap |= self::MASK_LOGIN;
2493
                return true;
2747
                return true;
2494
            default:
2748
            default:
2495
                $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
2749
                $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
2496
                throw new ConnectionClosedException('Unexpected response to publickey authentication pt 1');
2750
                throw new ConnectionClosedException('Unexpected response to publickey authentication pt 1');
2497
        }
2751
        }
2498
 
2752
 
2499
        $packet = $part1 . chr(1) . $part2;
2753
        $packet = $part1 . chr(1) . $part2;
2500
        $privatekey = $privatekey->withHash($hash);
2754
        $privatekey = $privatekey->withHash($hash);
Line 2508... Line 2762...
2508
 
2762
 
2509
        $response = $this->get_binary_packet();
2763
        $response = $this->get_binary_packet();
2510
 
2764
 
2511
        list($type) = Strings::unpackSSH2('C', $response);
2765
        list($type) = Strings::unpackSSH2('C', $response);
2512
        switch ($type) {
2766
        switch ($type) {
2513
            case MessageType::USERAUTH_FAILURE:
2767
            case NET_SSH2_MSG_USERAUTH_FAILURE:
2514
                // either the login is bad or the server employs multi-factor authentication
2768
                // either the login is bad or the server employs multi-factor authentication
2515
                list($auth_methods) = Strings::unpackSSH2('L', $response);
2769
                list($auth_methods) = Strings::unpackSSH2('L', $response);
2516
                $this->auth_methods_to_continue = $auth_methods;
2770
                $this->auth_methods_to_continue = $auth_methods;
2517
                return false;
2771
                return false;
2518
            case MessageType::USERAUTH_SUCCESS:
2772
            case NET_SSH2_MSG_USERAUTH_SUCCESS:
2519
                $this->bitmap |= self::MASK_LOGIN;
2773
                $this->bitmap |= self::MASK_LOGIN;
2520
                return true;
2774
                return true;
2521
        }
2775
        }
2522
 
2776
 
2523
        $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
2777
        $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
2524
        throw new ConnectionClosedException('Unexpected response to publickey authentication pt 2');
2778
        throw new ConnectionClosedException('Unexpected response to publickey authentication pt 2');
2525
    }
2779
    }
2526
 
2780
 
2527
    /**
2781
    /**
2528
     * Set Timeout
2782
     * Set Timeout
2529
     *
2783
     *
2530
     * $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.
2784
     * $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.
2531
     * Setting $timeout to false or 0 will mean there is no timeout.
2785
     * Setting $timeout to false or 0 will mean there is no timeout.
2532
     *
2786
     *
2533
     * @param mixed $timeout
2787
     * @param mixed $timeout
-
 
2788
     * @access public
2534
     */
2789
     */
2535
    public function setTimeout($timeout)
2790
    public function setTimeout($timeout)
2536
    {
2791
    {
2537
        $this->timeout = $this->curTimeout = $timeout;
2792
        $this->timeout = $this->curTimeout = $timeout;
2538
    }
2793
    }
Line 2541... Line 2796...
2541
     * Set Keep Alive
2796
     * Set Keep Alive
2542
     *
2797
     *
2543
     * Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive non-zero number.
2798
     * Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive non-zero number.
2544
     *
2799
     *
2545
     * @param int $interval
2800
     * @param int $interval
-
 
2801
     * @access public
2546
     */
2802
     */
2547
    public function setKeepAlive($interval)
2803
    public function setKeepAlive($interval)
2548
    {
2804
    {
2549
        $this->keepAlive = $interval;
2805
        $this->keepAlive = $interval;
2550
    }
2806
    }
2551
 
2807
 
2552
    /**
2808
    /**
2553
     * Get the output from stdError
2809
     * Get the output from stdError
2554
     *
2810
     *
-
 
2811
     * @access public
2555
     */
2812
     */
2556
    public function getStdError()
2813
    public function getStdError()
2557
    {
2814
    {
2558
        return $this->stdErrorLog;
2815
        return $this->stdErrorLog;
2559
    }
2816
    }
Line 2566... Line 2823...
2566
     *
2823
     *
2567
     * @param string $command
2824
     * @param string $command
2568
     * @return string|bool
2825
     * @return string|bool
2569
     * @psalm-return ($callback is callable ? bool : string|bool)
2826
     * @psalm-return ($callback is callable ? bool : string|bool)
2570
     * @throws \RuntimeException on connection error
2827
     * @throws \RuntimeException on connection error
-
 
2828
     * @access public
2571
     */
2829
     */
2572
    public function exec($command, callable $callback = null)
2830
    public function exec($command, callable $callback = null)
2573
    {
2831
    {
2574
        $this->curTimeout = $this->timeout;
2832
        $this->curTimeout = $this->timeout;
2575
        $this->is_timeout = false;
2833
        $this->is_timeout = false;
Line 2592... Line 2850...
2592
        // uses 0x4000, that's what will be used here, as well.
2850
        // uses 0x4000, that's what will be used here, as well.
2593
        $packet_size = 0x4000;
2851
        $packet_size = 0x4000;
2594
 
2852
 
2595
        $packet = Strings::packSSH2(
2853
        $packet = Strings::packSSH2(
2596
            'CsN3',
2854
            'CsN3',
2597
            MessageType::CHANNEL_OPEN,
2855
            NET_SSH2_MSG_CHANNEL_OPEN,
2598
            'session',
2856
            'session',
2599
            self::CHANNEL_EXEC,
2857
            self::CHANNEL_EXEC,
2600
            $this->window_size_server_to_client[self::CHANNEL_EXEC],
2858
            $this->window_size_server_to_client[self::CHANNEL_EXEC],
2601
            $packet_size
2859
            $packet_size
2602
        );
2860
        );
2603
        $this->send_binary_packet($packet);
2861
        $this->send_binary_packet($packet);
2604
 
2862
 
2605
        $this->channel_status[self::CHANNEL_EXEC] = MessageType::CHANNEL_OPEN;
2863
        $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
2606
 
2864
 
2607
        $this->get_channel_packet(self::CHANNEL_EXEC);
2865
        $this->get_channel_packet(self::CHANNEL_EXEC);
2608
 
2866
 
2609
        if ($this->request_pty === true) {
2867
        if ($this->request_pty === true) {
2610
            $terminal_modes = pack('C', TerminalMode::TTY_OP_END);
2868
            $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
2611
            $packet = Strings::packSSH2(
2869
            $packet = Strings::packSSH2(
2612
                'CNsCsN4s',
2870
                'CNsCsN4s',
2613
                MessageType::CHANNEL_REQUEST,
2871
                NET_SSH2_MSG_CHANNEL_REQUEST,
2614
                $this->server_channels[self::CHANNEL_EXEC],
2872
                $this->server_channels[self::CHANNEL_EXEC],
2615
                'pty-req',
2873
                'pty-req',
2616
                1,
2874
                1,
2617
                $this->term,
2875
                $this->term,
2618
                $this->windowColumns,
2876
                $this->windowColumns,
Line 2622... Line 2880...
2622
                $terminal_modes
2880
                $terminal_modes
2623
            );
2881
            );
2624
 
2882
 
2625
            $this->send_binary_packet($packet);
2883
            $this->send_binary_packet($packet);
2626
 
2884
 
2627
            $this->channel_status[self::CHANNEL_EXEC] = MessageType::CHANNEL_REQUEST;
2885
            $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
2628
            if (!$this->get_channel_packet(self::CHANNEL_EXEC)) {
2886
            if (!$this->get_channel_packet(self::CHANNEL_EXEC)) {
2629
                $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
2887
                $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
2630
                throw new \RuntimeException('Unable to request pseudo-terminal');
2888
                throw new \RuntimeException('Unable to request pseudo-terminal');
2631
            }
2889
            }
2632
 
2890
 
2633
            $this->in_request_pty_exec = true;
2891
            $this->in_request_pty_exec = true;
2634
        }
2892
        }
Line 2642... Line 2900...
2642
        // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
2900
        // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
2643
        // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
2901
        // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
2644
        // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA.  RFC4254#section-5.2 corroborates.
2902
        // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA.  RFC4254#section-5.2 corroborates.
2645
        $packet = Strings::packSSH2(
2903
        $packet = Strings::packSSH2(
2646
            'CNsCs',
2904
            'CNsCs',
2647
            MessageType::CHANNEL_REQUEST,
2905
            NET_SSH2_MSG_CHANNEL_REQUEST,
2648
            $this->server_channels[self::CHANNEL_EXEC],
2906
            $this->server_channels[self::CHANNEL_EXEC],
2649
            'exec',
2907
            'exec',
2650
            1,
2908
            1,
2651
            $command
2909
            $command
2652
        );
2910
        );
2653
        $this->send_binary_packet($packet);
2911
        $this->send_binary_packet($packet);
2654
 
2912
 
2655
        $this->channel_status[self::CHANNEL_EXEC] = MessageType::CHANNEL_REQUEST;
2913
        $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
2656
 
2914
 
2657
        if (!$this->get_channel_packet(self::CHANNEL_EXEC)) {
2915
        if (!$this->get_channel_packet(self::CHANNEL_EXEC)) {
2658
            return false;
2916
            return false;
2659
        }
2917
        }
2660
 
2918
 
2661
        $this->channel_status[self::CHANNEL_EXEC] = MessageType::CHANNEL_DATA;
2919
        $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
2662
 
2920
 
2663
        if ($callback === false || $this->in_request_pty_exec) {
2921
        if ($callback === false || $this->in_request_pty_exec) {
2664
            return true;
2922
            return true;
2665
        }
2923
        }
2666
 
2924
 
Line 2691... Line 2949...
2691
     * @see self::read()
2949
     * @see self::read()
2692
     * @see self::write()
2950
     * @see self::write()
2693
     * @return bool
2951
     * @return bool
2694
     * @throws \UnexpectedValueException on receipt of unexpected packets
2952
     * @throws \UnexpectedValueException on receipt of unexpected packets
2695
     * @throws \RuntimeException on other errors
2953
     * @throws \RuntimeException on other errors
-
 
2954
     * @access private
2696
     */
2955
     */
2697
    private function initShell()
2956
    private function initShell()
2698
    {
2957
    {
2699
        if ($this->in_request_pty_exec === true) {
2958
        if ($this->in_request_pty_exec === true) {
2700
            return true;
2959
            return true;
Line 2703... Line 2962...
2703
        $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size;
2962
        $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size;
2704
        $packet_size = 0x4000;
2963
        $packet_size = 0x4000;
2705
 
2964
 
2706
        $packet = Strings::packSSH2(
2965
        $packet = Strings::packSSH2(
2707
            'CsN3',
2966
            'CsN3',
2708
            MessageType::CHANNEL_OPEN,
2967
            NET_SSH2_MSG_CHANNEL_OPEN,
2709
            'session',
2968
            'session',
2710
            self::CHANNEL_SHELL,
2969
            self::CHANNEL_SHELL,
2711
            $this->window_size_server_to_client[self::CHANNEL_SHELL],
2970
            $this->window_size_server_to_client[self::CHANNEL_SHELL],
2712
            $packet_size
2971
            $packet_size
2713
        );
2972
        );
2714
 
2973
 
2715
        $this->send_binary_packet($packet);
2974
        $this->send_binary_packet($packet);
2716
 
2975
 
2717
        $this->channel_status[self::CHANNEL_SHELL] = MessageType::CHANNEL_OPEN;
2976
        $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
2718
 
2977
 
2719
        $this->get_channel_packet(self::CHANNEL_SHELL);
2978
        $this->get_channel_packet(self::CHANNEL_SHELL);
2720
 
2979
 
2721
        $terminal_modes = pack('C', TerminalMode::TTY_OP_END);
2980
        $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
2722
        $packet = Strings::packSSH2(
2981
        $packet = Strings::packSSH2(
2723
            'CNsbsN4s',
2982
            'CNsbsN4s',
2724
            MessageType::CHANNEL_REQUEST,
2983
            NET_SSH2_MSG_CHANNEL_REQUEST,
2725
            $this->server_channels[self::CHANNEL_SHELL],
2984
            $this->server_channels[self::CHANNEL_SHELL],
2726
            'pty-req',
2985
            'pty-req',
2727
            true, // want reply
2986
            true, // want reply
2728
            $this->term,
2987
            $this->term,
2729
            $this->windowColumns,
2988
            $this->windowColumns,
Line 2733... Line 2992...
2733
            $terminal_modes
2992
            $terminal_modes
2734
        );
2993
        );
2735
 
2994
 
2736
        $this->send_binary_packet($packet);
2995
        $this->send_binary_packet($packet);
2737
 
2996
 
2738
        $this->channel_status[self::CHANNEL_SHELL] = MessageType::CHANNEL_REQUEST;
2997
        $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
2739
 
2998
 
2740
        if (!$this->get_channel_packet(self::CHANNEL_SHELL)) {
2999
        if (!$this->get_channel_packet(self::CHANNEL_SHELL)) {
2741
            throw new \RuntimeException('Unable to request pty');
3000
            throw new \RuntimeException('Unable to request pty');
2742
        }
3001
        }
2743
 
3002
 
2744
        $packet = Strings::packSSH2(
3003
        $packet = Strings::packSSH2(
2745
            'CNsb',
3004
            'CNsb',
2746
            MessageType::CHANNEL_REQUEST,
3005
            NET_SSH2_MSG_CHANNEL_REQUEST,
2747
            $this->server_channels[self::CHANNEL_SHELL],
3006
            $this->server_channels[self::CHANNEL_SHELL],
2748
            'shell',
3007
            'shell',
2749
            true // want reply
3008
            true // want reply
2750
        );
3009
        );
2751
        $this->send_binary_packet($packet);
3010
        $this->send_binary_packet($packet);
Line 2753... Line 3012...
2753
        $response = $this->get_channel_packet(self::CHANNEL_SHELL);
3012
        $response = $this->get_channel_packet(self::CHANNEL_SHELL);
2754
        if ($response === false) {
3013
        if ($response === false) {
2755
            throw new \RuntimeException('Unable to request shell');
3014
            throw new \RuntimeException('Unable to request shell');
2756
        }
3015
        }
2757
 
3016
 
2758
        $this->channel_status[self::CHANNEL_SHELL] = MessageType::CHANNEL_DATA;
3017
        $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
2759
 
3018
 
2760
        $this->bitmap |= self::MASK_SHELL;
3019
        $this->bitmap |= self::MASK_SHELL;
2761
 
3020
 
2762
        return true;
3021
        return true;
2763
    }
3022
    }
Line 2766... Line 3025...
2766
     * Return the channel to be used with read() / write()
3025
     * Return the channel to be used with read() / write()
2767
     *
3026
     *
2768
     * @see self::read()
3027
     * @see self::read()
2769
     * @see self::write()
3028
     * @see self::write()
2770
     * @return int
3029
     * @return int
-
 
3030
     * @access public
2771
     */
3031
     */
2772
    private function get_interactive_channel()
3032
    private function get_interactive_channel()
2773
    {
3033
    {
2774
        switch (true) {
3034
        switch (true) {
2775
            case $this->in_subsystem:
3035
            case $this->in_subsystem:
Line 2783... Line 3043...
2783
 
3043
 
2784
    /**
3044
    /**
2785
     * Return an available open channel
3045
     * Return an available open channel
2786
     *
3046
     *
2787
     * @return int
3047
     * @return int
-
 
3048
     * @access public
2788
     */
3049
     */
2789
    private function get_open_channel()
3050
    private function get_open_channel()
2790
    {
3051
    {
2791
        $channel = self::CHANNEL_EXEC;
3052
        $channel = self::CHANNEL_EXEC;
2792
        do {
3053
        do {
2793
            if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == MessageType::CHANNEL_OPEN) {
3054
            if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) {
2794
                return $channel;
3055
                return $channel;
2795
            }
3056
            }
2796
        } while ($channel++ < self::CHANNEL_SUBSYSTEM);
3057
        } while ($channel++ < self::CHANNEL_SUBSYSTEM);
2797
 
3058
 
2798
        return false;
3059
        return false;
Line 2800... Line 3061...
2800
 
3061
 
2801
    /**
3062
    /**
2802
     * Request agent forwarding of remote server
3063
     * Request agent forwarding of remote server
2803
     *
3064
     *
2804
     * @return bool
3065
     * @return bool
-
 
3066
     * @access public
2805
     */
3067
     */
2806
    public function requestAgentForwarding()
3068
    public function requestAgentForwarding()
2807
    {
3069
    {
2808
        $request_channel = $this->get_open_channel();
3070
        $request_channel = $this->get_open_channel();
2809
        if ($request_channel === false) {
3071
        if ($request_channel === false) {
2810
            return false;
3072
            return false;
2811
        }
3073
        }
2812
 
3074
 
2813
        $packet = Strings::packSSH2(
3075
        $packet = Strings::packSSH2(
2814
            'CNsC',
3076
            'CNsC',
2815
            MessageType::CHANNEL_REQUEST,
3077
            NET_SSH2_MSG_CHANNEL_REQUEST,
2816
            $this->server_channels[$request_channel],
3078
            $this->server_channels[$request_channel],
2817
            'auth-agent-req@openssh.com',
3079
            'auth-agent-req@openssh.com',
2818
            1
3080
            1
2819
        );
3081
        );
2820
 
3082
 
2821
        $this->channel_status[$request_channel] = MessageType::CHANNEL_REQUEST;
3083
        $this->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_REQUEST;
2822
 
3084
 
2823
        $this->send_binary_packet($packet);
3085
        $this->send_binary_packet($packet);
2824
 
3086
 
2825
        if (!$this->get_channel_packet($request_channel)) {
3087
        if (!$this->get_channel_packet($request_channel)) {
2826
            return false;
3088
            return false;
2827
        }
3089
        }
2828
 
3090
 
2829
        $this->channel_status[$request_channel] = MessageType::CHANNEL_OPEN;
3091
        $this->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_OPEN;
2830
 
3092
 
2831
        return true;
3093
        return true;
2832
    }
3094
    }
2833
 
3095
 
2834
    /**
3096
    /**
Line 2840... Line 3102...
2840
     * @see self::write()
3102
     * @see self::write()
2841
     * @param string $expect
3103
     * @param string $expect
2842
     * @param int $mode
3104
     * @param int $mode
2843
     * @return string|bool|null
3105
     * @return string|bool|null
2844
     * @throws \RuntimeException on connection error
3106
     * @throws \RuntimeException on connection error
-
 
3107
     * @access public
2845
     */
3108
     */
2846
    public function read($expect = '', $mode = self::READ_SIMPLE)
3109
    public function read($expect = '', $mode = self::READ_SIMPLE)
2847
    {
3110
    {
2848
        $this->curTimeout = $this->timeout;
3111
        $this->curTimeout = $this->timeout;
2849
        $this->is_timeout = false;
3112
        $this->is_timeout = false;
Line 2913... Line 3176...
2913
     * if there's sufficient demand for such a feature.
3176
     * if there's sufficient demand for such a feature.
2914
     *
3177
     *
2915
     * @see self::stopSubsystem()
3178
     * @see self::stopSubsystem()
2916
     * @param string $subsystem
3179
     * @param string $subsystem
2917
     * @return bool
3180
     * @return bool
-
 
3181
     * @access public
2918
     */
3182
     */
2919
    public function startSubsystem($subsystem)
3183
    public function startSubsystem($subsystem)
2920
    {
3184
    {
2921
        $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size;
3185
        $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size;
2922
 
3186
 
2923
        $packet = Strings::packSSH2(
3187
        $packet = Strings::packSSH2(
2924
            'CsN3',
3188
            'CsN3',
2925
            MessageType::CHANNEL_OPEN,
3189
            NET_SSH2_MSG_CHANNEL_OPEN,
2926
            'session',
3190
            'session',
2927
            self::CHANNEL_SUBSYSTEM,
3191
            self::CHANNEL_SUBSYSTEM,
2928
            $this->window_size,
3192
            $this->window_size,
2929
            0x4000
3193
            0x4000
2930
        );
3194
        );
2931
 
3195
 
2932
        $this->send_binary_packet($packet);
3196
        $this->send_binary_packet($packet);
2933
 
3197
 
2934
        $this->channel_status[self::CHANNEL_SUBSYSTEM] = MessageType::CHANNEL_OPEN;
3198
        $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN;
2935
 
3199
 
2936
        $this->get_channel_packet(self::CHANNEL_SUBSYSTEM);
3200
        $this->get_channel_packet(self::CHANNEL_SUBSYSTEM);
2937
 
3201
 
2938
        $packet = Strings::packSSH2(
3202
        $packet = Strings::packSSH2(
2939
            'CNsCs',
3203
            'CNsCs',
2940
            MessageType::CHANNEL_REQUEST,
3204
            NET_SSH2_MSG_CHANNEL_REQUEST,
2941
            $this->server_channels[self::CHANNEL_SUBSYSTEM],
3205
            $this->server_channels[self::CHANNEL_SUBSYSTEM],
2942
            'subsystem',
3206
            'subsystem',
2943
            1,
3207
            1,
2944
            $subsystem
3208
            $subsystem
2945
        );
3209
        );
2946
        $this->send_binary_packet($packet);
3210
        $this->send_binary_packet($packet);
2947
 
3211
 
2948
        $this->channel_status[self::CHANNEL_SUBSYSTEM] = MessageType::CHANNEL_REQUEST;
3212
        $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST;
2949
 
3213
 
2950
        if (!$this->get_channel_packet(self::CHANNEL_SUBSYSTEM)) {
3214
        if (!$this->get_channel_packet(self::CHANNEL_SUBSYSTEM)) {
2951
            return false;
3215
            return false;
2952
        }
3216
        }
2953
 
3217
 
2954
        $this->channel_status[self::CHANNEL_SUBSYSTEM] = MessageType::CHANNEL_DATA;
3218
        $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA;
2955
 
3219
 
2956
        $this->bitmap |= self::MASK_SHELL;
3220
        $this->bitmap |= self::MASK_SHELL;
2957
        $this->in_subsystem = true;
3221
        $this->in_subsystem = true;
2958
 
3222
 
2959
        return true;
3223
        return true;
Line 2962... Line 3226...
2962
    /**
3226
    /**
2963
     * Stops a subsystem.
3227
     * Stops a subsystem.
2964
     *
3228
     *
2965
     * @see self::startSubsystem()
3229
     * @see self::startSubsystem()
2966
     * @return bool
3230
     * @return bool
-
 
3231
     * @access public
2967
     */
3232
     */
2968
    public function stopSubsystem()
3233
    public function stopSubsystem()
2969
    {
3234
    {
2970
        $this->in_subsystem = false;
3235
        $this->in_subsystem = false;
2971
        $this->close_channel(self::CHANNEL_SUBSYSTEM);
3236
        $this->close_channel(self::CHANNEL_SUBSYSTEM);
Line 2975... Line 3240...
2975
    /**
3240
    /**
2976
     * Closes a channel
3241
     * Closes a channel
2977
     *
3242
     *
2978
     * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call
3243
     * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call
2979
     *
3244
     *
-
 
3245
     * @access public
2980
     */
3246
     */
2981
    public function reset()
3247
    public function reset()
2982
    {
3248
    {
2983
        $this->close_channel($this->get_interactive_channel());
3249
        $this->close_channel($this->get_interactive_channel());
2984
    }
3250
    }
Line 2986... Line 3252...
2986
    /**
3252
    /**
2987
     * Is timeout?
3253
     * Is timeout?
2988
     *
3254
     *
2989
     * Did exec() or read() return because they timed out or because they encountered the end?
3255
     * Did exec() or read() return because they timed out or because they encountered the end?
2990
     *
3256
     *
-
 
3257
     * @access public
2991
     */
3258
     */
2992
    public function isTimeout()
3259
    public function isTimeout()
2993
    {
3260
    {
2994
        return $this->is_timeout;
3261
        return $this->is_timeout;
2995
    }
3262
    }
2996
 
3263
 
2997
    /**
3264
    /**
2998
     * Disconnect
3265
     * Disconnect
2999
     *
3266
     *
-
 
3267
     * @access public
3000
     */
3268
     */
3001
    public function disconnect()
3269
    public function disconnect()
3002
    {
3270
    {
3003
        $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
3271
        $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
3004
        if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
3272
        if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
3005
            fclose($this->realtime_log_file);
3273
            fclose($this->realtime_log_file);
3006
        }
3274
        }
3007
        unset(self::$connections[$this->getResourceId()]);
3275
        unset(self::$connections[$this->getResourceId()]);
3008
    }
3276
    }
Line 3011... Line 3279...
3011
     * Destructor.
3279
     * Destructor.
3012
     *
3280
     *
3013
     * Will be called, automatically, if you're supporting just PHP5.  If you're supporting PHP4, you'll need to call
3281
     * Will be called, automatically, if you're supporting just PHP5.  If you're supporting PHP4, you'll need to call
3014
     * disconnect().
3282
     * disconnect().
3015
     *
3283
     *
-
 
3284
     * @access public
3016
     */
3285
     */
3017
    public function __destruct()
3286
    public function __destruct()
3018
    {
3287
    {
3019
        $this->disconnect();
3288
        $this->disconnect();
3020
    }
3289
    }
3021
 
3290
 
3022
    /**
3291
    /**
3023
     * Is the connection still active?
3292
     * Is the connection still active?
3024
     *
3293
     *
3025
     * @return bool
3294
     * @return bool
-
 
3295
     * @access public
3026
     */
3296
     */
3027
    public function isConnected()
3297
    public function isConnected()
3028
    {
3298
    {
3029
        return (bool) ($this->bitmap & self::MASK_CONNECTED);
3299
        return (bool) ($this->bitmap & self::MASK_CONNECTED);
3030
    }
3300
    }
3031
 
3301
 
3032
    /**
3302
    /**
3033
     * Have you successfully been logged in?
3303
     * Have you successfully been logged in?
3034
     *
3304
     *
3035
     * @return bool
3305
     * @return bool
-
 
3306
     * @access public
3036
     */
3307
     */
3037
    public function isAuthenticated()
3308
    public function isAuthenticated()
3038
    {
3309
    {
3039
        return (bool) ($this->bitmap & self::MASK_LOGIN);
3310
        return (bool) ($this->bitmap & self::MASK_LOGIN);
3040
    }
3311
    }
Line 3057... Line 3328...
3057
 
3328
 
3058
        $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size;
3329
        $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size;
3059
        $packet_size = 0x4000;
3330
        $packet_size = 0x4000;
3060
        $packet = Strings::packSSH2(
3331
        $packet = Strings::packSSH2(
3061
            'CsN3',
3332
            'CsN3',
3062
            MessageType::CHANNEL_OPEN,
3333
            NET_SSH2_MSG_CHANNEL_OPEN,
3063
            'session',
3334
            'session',
3064
            self::CHANNEL_KEEP_ALIVE,
3335
            self::CHANNEL_KEEP_ALIVE,
3065
            $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE],
3336
            $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE],
3066
            $packet_size
3337
            $packet_size
3067
        );
3338
        );
3068
 
3339
 
3069
        try {
3340
        try {
3070
            $this->send_binary_packet($packet);
3341
            $this->send_binary_packet($packet);
3071
 
3342
 
3072
            $this->channel_status[self::CHANNEL_KEEP_ALIVE] = MessageType::CHANNEL_OPEN;
3343
            $this->channel_status[self::CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN;
3073
 
3344
 
3074
            $response = $this->get_channel_packet(self::CHANNEL_KEEP_ALIVE);
3345
            $response = $this->get_channel_packet(self::CHANNEL_KEEP_ALIVE);
3075
        } catch (\RuntimeException $e) {
3346
        } catch (\RuntimeException $e) {
3076
            return $this->reconnect();
3347
            return $this->reconnect();
3077
        }
3348
        }
Line 3085... Line 3356...
3085
     *
3356
     *
3086
     * @return boolean
3357
     * @return boolean
3087
     */
3358
     */
3088
    private function reconnect()
3359
    private function reconnect()
3089
    {
3360
    {
3090
        $this->reset_connection(DisconnectReason::CONNECTION_LOST);
3361
        $this->reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST);
3091
        $this->retry_connect = true;
3362
        $this->retry_connect = true;
3092
        $this->connect();
3363
        $this->connect();
3093
        foreach ($this->auth as $auth) {
3364
        foreach ($this->auth as $auth) {
3094
            $result = $this->login(...$auth);
3365
            $result = $this->login(...$auth);
3095
        }
3366
        }
Line 3098... Line 3369...
3098
 
3369
 
3099
    /**
3370
    /**
3100
     * Resets a connection for re-use
3371
     * Resets a connection for re-use
3101
     *
3372
     *
3102
     * @param int $reason
3373
     * @param int $reason
-
 
3374
     * @access private
3103
     */
3375
     */
3104
    protected function reset_connection($reason)
3376
    protected function reset_connection($reason)
3105
    {
3377
    {
3106
        $this->disconnect_helper($reason);
3378
        $this->disconnect_helper($reason);
3107
        $this->decrypt = $this->encrypt = false;
3379
        $this->decrypt = $this->encrypt = false;
Line 3119... Line 3391...
3119
     * See '6. Binary Packet Protocol' of rfc4253 for more info.
3391
     * See '6. Binary Packet Protocol' of rfc4253 for more info.
3120
     *
3392
     *
3121
     * @see self::_send_binary_packet()
3393
     * @see self::_send_binary_packet()
3122
     * @param bool $skip_channel_filter
3394
     * @param bool $skip_channel_filter
3123
     * @return bool|string
3395
     * @return bool|string
-
 
3396
     * @access private
3124
     */
3397
     */
3125
    private function get_binary_packet($skip_channel_filter = false)
3398
    private function get_binary_packet($skip_channel_filter = false)
3126
    {
3399
    {
3127
        if ($skip_channel_filter) {
3400
        if ($skip_channel_filter) {
3128
            if (!is_resource($this->fsock)) {
3401
            if (!is_resource($this->fsock)) {
Line 3134... Line 3407...
3134
            if (!$this->curTimeout) {
3407
            if (!$this->curTimeout) {
3135
                if ($this->keepAlive <= 0) {
3408
                if ($this->keepAlive <= 0) {
3136
                    @stream_select($read, $write, $except, null);
3409
                    @stream_select($read, $write, $except, null);
3137
                } else {
3410
                } else {
3138
                    if (!@stream_select($read, $write, $except, $this->keepAlive)) {
3411
                    if (!@stream_select($read, $write, $except, $this->keepAlive)) {
3139
                        $this->send_binary_packet(pack('CN', MessageType::IGNORE, 0));
3412
                        $this->send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0));
3140
                        return $this->get_binary_packet(true);
3413
                        return $this->get_binary_packet(true);
3141
                    }
3414
                    }
3142
                }
3415
                }
3143
            } else {
3416
            } else {
3144
                if ($this->curTimeout < 0) {
3417
                if ($this->curTimeout < 0) {
Line 3148... Line 3421...
3148
 
3421
 
3149
                $start = microtime(true);
3422
                $start = microtime(true);
3150
 
3423
 
3151
                if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) {
3424
                if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) {
3152
                    if (!@stream_select($read, $write, $except, $this->keepAlive)) {
3425
                    if (!@stream_select($read, $write, $except, $this->keepAlive)) {
3153
                        $this->send_binary_packet(pack('CN', MessageType::IGNORE, 0));
3426
                        $this->send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0));
3154
                        $elapsed = microtime(true) - $start;
3427
                        $elapsed = microtime(true) - $start;
3155
                        $this->curTimeout -= $elapsed;
3428
                        $this->curTimeout -= $elapsed;
3156
                        return $this->get_binary_packet(true);
3429
                        return $this->get_binary_packet(true);
3157
                    }
3430
                    }
3158
                    $elapsed = microtime(true) - $start;
3431
                    $elapsed = microtime(true) - $start;
Line 3286... Line 3559...
3286
        $padding = Strings::shift($raw, $padding_length); // should leave $raw empty
3559
        $padding = Strings::shift($raw, $padding_length); // should leave $raw empty
3287
 
3560
 
3288
        if ($this->hmac_check instanceof Hash) {
3561
        if ($this->hmac_check instanceof Hash) {
3289
            $hmac = stream_get_contents($this->fsock, $this->hmac_size);
3562
            $hmac = stream_get_contents($this->fsock, $this->hmac_size);
3290
            if ($hmac === false || strlen($hmac) != $this->hmac_size) {
3563
            if ($hmac === false || strlen($hmac) != $this->hmac_size) {
3291
                $this->disconnect_helper(DisconnectReason::MAC_ERROR);
3564
                $this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR);
3292
                throw new \RuntimeException('Error reading socket');
3565
                throw new \RuntimeException('Error reading socket');
3293
            }
3566
            }
3294
 
3567
 
3295
            $reconstructed = !$this->hmac_check_etm ?
3568
            $reconstructed = !$this->hmac_check_etm ?
3296
                pack('NCa*', $packet_length, $padding_length, $payload . $padding) :
3569
                pack('NCa*', $packet_length, $padding_length, $payload . $padding) :
3297
                $encrypted;
3570
                $encrypted;
3298
            if (($this->hmac_check->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') {
3571
            if (($this->hmac_check->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') {
3299
                $this->hmac_check->setNonce("\0\0\0\0" . pack('N', $this->get_seq_no));
3572
                $this->hmac_check->setNonce("\0\0\0\0" . pack('N', $this->get_seq_no));
3300
                if ($hmac != $this->hmac_check->hash($reconstructed)) {
3573
                if ($hmac != $this->hmac_check->hash($reconstructed)) {
3301
                    $this->disconnect_helper(DisconnectReason::MAC_ERROR);
3574
                    $this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR);
3302
                    throw new \RuntimeException('Invalid UMAC');
3575
                    throw new \RuntimeException('Invalid UMAC');
3303
                }
3576
                }
3304
            } else {
3577
            } else {
3305
                if ($hmac != $this->hmac_check->hash(pack('Na*', $this->get_seq_no, $reconstructed))) {
3578
                if ($hmac != $this->hmac_check->hash(pack('Na*', $this->get_seq_no, $reconstructed))) {
3306
                    $this->disconnect_helper(DisconnectReason::MAC_ERROR);
3579
                    $this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR);
3307
                    throw new \RuntimeException('Invalid HMAC');
3580
                    throw new \RuntimeException('Invalid HMAC');
3308
                }
3581
                }
3309
            }
3582
            }
3310
        }
3583
        }
3311
 
3584
 
Line 3348... Line 3621...
3348
 
3621
 
3349
        $this->get_seq_no++;
3622
        $this->get_seq_no++;
3350
 
3623
 
3351
        if (defined('NET_SSH2_LOGGING')) {
3624
        if (defined('NET_SSH2_LOGGING')) {
3352
            $current = microtime(true);
3625
            $current = microtime(true);
3353
            $message_number = sprintf(
-
 
3354
                '<- %s (since last: %s, network: %ss)',
-
 
3355
                ($constantName = MessageType::findConstantNameByValue($value = ord($payload[0])))
3626
            $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
3356
                    ? "SSH_MSG_$constantName"
3627
            $message_number = '<- ' . $message_number .
3357
                    : "UNKNOWN ($value)",
-
 
3358
                round($current - $this->last_packet, 4),
3628
                              ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
3359
                round($stop - $start, 4)
-
 
3360
            );
-
 
3361
            $this->append_log($message_number, $payload);
3629
            $this->append_log($message_number, $payload);
3362
            $this->last_packet = $current;
3630
            $this->last_packet = $current;
3363
        }
3631
        }
3364
 
3632
 
3365
        return $this->filter($payload, $skip_channel_filter);
3633
        return $this->filter($payload, $skip_channel_filter);
Line 3369... Line 3637...
3369
     * Read Remaining Bytes
3637
     * Read Remaining Bytes
3370
     *
3638
     *
3371
     * @see self::get_binary_packet()
3639
     * @see self::get_binary_packet()
3372
     * @param int $remaining_length
3640
     * @param int $remaining_length
3373
     * @return string
3641
     * @return string
-
 
3642
     * @access private
3374
     */
3643
     */
3375
    private function read_remaining_bytes($remaining_length)
3644
    private function read_remaining_bytes($remaining_length)
3376
    {
3645
    {
3377
        if (!$remaining_length) {
3646
        if (!$remaining_length) {
3378
            return '';
3647
            return '';
Line 3395... Line 3664...
3395
        // PuTTY uses 0x9000 as the actual max packet size and so to shall we
3664
        // PuTTY uses 0x9000 as the actual max packet size and so to shall we
3396
        // don't do this when GCM mode is used since GCM mode doesn't encrypt the length
3665
        // don't do this when GCM mode is used since GCM mode doesn't encrypt the length
3397
        if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
3666
        if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
3398
            if (!$this->bad_key_size_fix && self::bad_algorithm_candidate($this->decrypt ? $this->decryptName : '') && !($this->bitmap & SSH2::MASK_LOGIN)) {
3667
            if (!$this->bad_key_size_fix && self::bad_algorithm_candidate($this->decrypt ? $this->decryptName : '') && !($this->bitmap & SSH2::MASK_LOGIN)) {
3399
                $this->bad_key_size_fix = true;
3668
                $this->bad_key_size_fix = true;
3400
                $this->reset_connection(DisconnectReason::KEY_EXCHANGE_FAILED);
3669
                $this->reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
3401
                return false;
3670
                return false;
3402
            }
3671
            }
3403
            throw new \RuntimeException('Invalid size');
3672
            throw new \RuntimeException('Invalid size');
3404
        }
3673
        }
3405
 
3674
 
Line 3409... Line 3678...
3409
 
3678
 
3410
        $buffer = '';
3679
        $buffer = '';
3411
        while ($remaining_length > 0) {
3680
        while ($remaining_length > 0) {
3412
            $temp = stream_get_contents($this->fsock, $remaining_length);
3681
            $temp = stream_get_contents($this->fsock, $remaining_length);
3413
            if ($temp === false || feof($this->fsock)) {
3682
            if ($temp === false || feof($this->fsock)) {
3414
                $this->disconnect_helper(DisconnectReason::CONNECTION_LOST);
3683
                $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST);
3415
                throw new \RuntimeException('Error reading from socket');
3684
                throw new \RuntimeException('Error reading from socket');
3416
            }
3685
            }
3417
            $buffer .= $temp;
3686
            $buffer .= $temp;
3418
            $remaining_length -= strlen($temp);
3687
            $remaining_length -= strlen($temp);
3419
        }
3688
        }
Line 3428... Line 3697...
3428
     *
3697
     *
3429
     * @see self::_get_binary_packet()
3698
     * @see self::_get_binary_packet()
3430
     * @param string $payload
3699
     * @param string $payload
3431
     * @param bool $skip_channel_filter
3700
     * @param bool $skip_channel_filter
3432
     * @return string|bool
3701
     * @return string|bool
-
 
3702
     * @access private
3433
     */
3703
     */
3434
    private function filter($payload, $skip_channel_filter)
3704
    private function filter($payload, $skip_channel_filter)
3435
    {
3705
    {
3436
        switch (ord($payload[0])) {
3706
        switch (ord($payload[0])) {
3437
            case MessageType::DISCONNECT:
3707
            case NET_SSH2_MSG_DISCONNECT:
3438
                Strings::shift($payload, 1);
3708
                Strings::shift($payload, 1);
3439
                list($reason_code, $message) = Strings::unpackSSH2('Ns', $payload);
3709
                list($reason_code, $message) = Strings::unpackSSH2('Ns', $payload);
3440
                $this->errors[] = 'SSH_MSG_DISCONNECT: SSH_DISCONNECT_' . DisconnectReason::getConstantNameByValue($reason_code) . "\r\n$message";
3710
                $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n$message";
3441
                $this->bitmap = 0;
3711
                $this->bitmap = 0;
3442
                return false;
3712
                return false;
3443
            case MessageType::IGNORE:
3713
            case NET_SSH2_MSG_IGNORE:
3444
                $payload = $this->get_binary_packet($skip_channel_filter);
3714
                $payload = $this->get_binary_packet($skip_channel_filter);
3445
                break;
3715
                break;
3446
            case MessageType::DEBUG:
3716
            case NET_SSH2_MSG_DEBUG:
3447
                Strings::shift($payload, 2); // second byte is "always_display"
3717
                Strings::shift($payload, 2); // second byte is "always_display"
3448
                list($message) = Strings::unpackSSH2('s', $payload);
3718
                list($message) = Strings::unpackSSH2('s', $payload);
3449
                $this->errors[] = "SSH_MSG_DEBUG: $message";
3719
                $this->errors[] = "SSH_MSG_DEBUG: $message";
3450
                $payload = $this->get_binary_packet($skip_channel_filter);
3720
                $payload = $this->get_binary_packet($skip_channel_filter);
3451
                break;
3721
                break;
3452
            case MessageType::UNIMPLEMENTED:
3722
            case NET_SSH2_MSG_UNIMPLEMENTED:
3453
                return false;
3723
                return false;
3454
            case MessageType::KEXINIT:
3724
            case NET_SSH2_MSG_KEXINIT:
3455
                if ($this->session_id !== false) {
3725
                if ($this->session_id !== false) {
3456
                    if (!$this->key_exchange($payload)) {
3726
                    if (!$this->key_exchange($payload)) {
3457
                        $this->bitmap = 0;
3727
                        $this->bitmap = 0;
3458
                        return false;
3728
                        return false;
3459
                    }
3729
                    }
3460
                    $payload = $this->get_binary_packet($skip_channel_filter);
3730
                    $payload = $this->get_binary_packet($skip_channel_filter);
3461
                }
3731
                }
3462
        }
3732
        }
3463
 
3733
 
3464
        // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in
3734
        // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in
3465
        if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && !is_bool($payload) && ord($payload[0]) == MessageType::USERAUTH_BANNER) {
3735
        if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && !is_bool($payload) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
3466
            Strings::shift($payload, 1);
3736
            Strings::shift($payload, 1);
3467
            list($this->banner_message) = Strings::unpackSSH2('s', $payload);
3737
            list($this->banner_message) = Strings::unpackSSH2('s', $payload);
3468
            $payload = $this->get_binary_packet();
3738
            $payload = $this->get_binary_packet();
3469
        }
3739
        }
3470
 
3740
 
Line 3473... Line 3743...
3473
            if (is_bool($payload)) {
3743
            if (is_bool($payload)) {
3474
                return $payload;
3744
                return $payload;
3475
            }
3745
            }
3476
 
3746
 
3477
            switch (ord($payload[0])) {
3747
            switch (ord($payload[0])) {
3478
                case MessageType::CHANNEL_REQUEST:
3748
                case NET_SSH2_MSG_CHANNEL_REQUEST:
3479
                    if (strlen($payload) == 31) {
3749
                    if (strlen($payload) == 31) {
3480
                        extract(unpack('cpacket_type/Nchannel/Nlength', $payload));
3750
                        extract(unpack('cpacket_type/Nchannel/Nlength', $payload));
3481
                        if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) {
3751
                        if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) {
3482
                            if (ord(substr($payload, 9 + $length))) { // want reply
3752
                            if (ord(substr($payload, 9 + $length))) { // want reply
3483
                                $this->send_binary_packet(pack('CN', MessageType::CHANNEL_SUCCESS, $this->server_channels[$channel]));
3753
                                $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel]));
3484
                            }
3754
                            }
3485
                            $payload = $this->get_binary_packet($skip_channel_filter);
3755
                            $payload = $this->get_binary_packet($skip_channel_filter);
3486
                        }
3756
                        }
3487
                    }
3757
                    }
3488
                    break;
3758
                    break;
3489
                case MessageType::CHANNEL_DATA:
3759
                case NET_SSH2_MSG_CHANNEL_DATA:
3490
                case MessageType::CHANNEL_EXTENDED_DATA:
3760
                case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
3491
                case MessageType::CHANNEL_CLOSE:
3761
                case NET_SSH2_MSG_CHANNEL_CLOSE:
3492
                case MessageType::CHANNEL_EOF:
3762
                case NET_SSH2_MSG_CHANNEL_EOF:
3493
                    if (!$skip_channel_filter && !empty($this->server_channels)) {
3763
                    if (!$skip_channel_filter && !empty($this->server_channels)) {
3494
                        $this->binary_packet_buffer = $payload;
3764
                        $this->binary_packet_buffer = $payload;
3495
                        $this->get_channel_packet(true);
3765
                        $this->get_channel_packet(true);
3496
                        $payload = $this->get_binary_packet();
3766
                        $payload = $this->get_binary_packet();
3497
                    }
3767
                    }
3498
                    break;
3768
                    break;
3499
                case MessageType::GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
3769
                case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
3500
                    Strings::shift($payload, 1);
3770
                    Strings::shift($payload, 1);
3501
                    list($request_name) = Strings::unpackSSH2('s', $payload);
3771
                    list($request_name) = Strings::unpackSSH2('s', $payload);
3502
                    $this->errors[] = "SSH_MSG_GLOBAL_REQUEST: $request_name";
3772
                    $this->errors[] = "SSH_MSG_GLOBAL_REQUEST: $request_name";
3503
 
3773
 
3504
                    try {
3774
                    try {
3505
                        $this->send_binary_packet(pack('C', MessageType::REQUEST_FAILURE));
3775
                        $this->send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE));
3506
                    } catch (\RuntimeException $e) {
3776
                    } catch (\RuntimeException $e) {
3507
                        return $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
3777
                        return $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
3508
                    }
3778
                    }
3509
 
3779
 
3510
                    $payload = $this->get_binary_packet($skip_channel_filter);
3780
                    $payload = $this->get_binary_packet($skip_channel_filter);
3511
                    break;
3781
                    break;
3512
                case MessageType::CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
3782
                case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
3513
                    Strings::shift($payload, 1);
3783
                    Strings::shift($payload, 1);
3514
                    list($data, $server_channel) = Strings::unpackSSH2('sN', $payload);
3784
                    list($data, $server_channel) = Strings::unpackSSH2('sN', $payload);
3515
                    switch ($data) {
3785
                    switch ($data) {
3516
                        case 'auth-agent':
3786
                        case 'auth-agent':
3517
                        case 'auth-agent@openssh.com':
3787
                        case 'auth-agent@openssh.com':
Line 3529... Line 3799...
3529
 
3799
 
3530
                                $packet_size = 0x4000;
3800
                                $packet_size = 0x4000;
3531
 
3801
 
3532
                                $packet = pack(
3802
                                $packet = pack(
3533
                                    'CN4',
3803
                                    'CN4',
3534
                                    MessageType::CHANNEL_OPEN_CONFIRMATION,
3804
                                    NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION,
3535
                                    $server_channel,
3805
                                    $server_channel,
3536
                                    $new_channel,
3806
                                    $new_channel,
3537
                                    $packet_size,
3807
                                    $packet_size,
3538
                                    $packet_size
3808
                                    $packet_size
3539
                                );
3809
                                );
3540
 
3810
 
3541
                                $this->server_channels[$new_channel] = $server_channel;
3811
                                $this->server_channels[$new_channel] = $server_channel;
3542
                                $this->channel_status[$new_channel] = MessageType::CHANNEL_OPEN_CONFIRMATION;
3812
                                $this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION;
3543
                                $this->send_binary_packet($packet);
3813
                                $this->send_binary_packet($packet);
3544
                            }
3814
                            }
3545
                            break;
3815
                            break;
3546
                        default:
3816
                        default:
3547
                            $packet = Strings::packSSH2(
3817
                            $packet = Strings::packSSH2(
3548
                                'CN2ss',
3818
                                'CN2ss',
3549
                                MessageType::CHANNEL_OPEN_FAILURE,
3819
                                NET_SSH2_MSG_CHANNEL_OPEN_FAILURE,
3550
                                $server_channel,
3820
                                $server_channel,
3551
                                ChannelConnectionFailureReason::ADMINISTRATIVELY_PROHIBITED,
3821
                                NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
3552
                                '', // description
3822
                                '', // description
3553
                                '' // language tag
3823
                                '' // language tag
3554
                            );
3824
                            );
3555
 
3825
 
3556
                            try {
3826
                            try {
3557
                                $this->send_binary_packet($packet);
3827
                                $this->send_binary_packet($packet);
3558
                            } catch (\RuntimeException $e) {
3828
                            } catch (\RuntimeException $e) {
3559
                                return $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
3829
                                return $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
3560
                            }
3830
                            }
3561
                    }
3831
                    }
3562
 
3832
 
3563
                    $payload = $this->get_binary_packet($skip_channel_filter);
3833
                    $payload = $this->get_binary_packet($skip_channel_filter);
3564
                    break;
3834
                    break;
3565
                case MessageType::CHANNEL_WINDOW_ADJUST:
3835
                case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
3566
                    Strings::shift($payload, 1);
3836
                    Strings::shift($payload, 1);
3567
                    list($channel, $window_size) = Strings::unpackSSH2('NN', $payload);
3837
                    list($channel, $window_size) = Strings::unpackSSH2('NN', $payload);
3568
 
3838
 
3569
                    $this->window_size_client_to_server[$channel] += $window_size;
3839
                    $this->window_size_client_to_server[$channel] += $window_size;
3570
 
3840
 
Line 3578... Line 3848...
3578
    /**
3848
    /**
3579
     * Enable Quiet Mode
3849
     * Enable Quiet Mode
3580
     *
3850
     *
3581
     * Suppress stderr from output
3851
     * Suppress stderr from output
3582
     *
3852
     *
-
 
3853
     * @access public
3583
     */
3854
     */
3584
    public function enableQuietMode()
3855
    public function enableQuietMode()
3585
    {
3856
    {
3586
        $this->quiet_mode = true;
3857
        $this->quiet_mode = true;
3587
    }
3858
    }
Line 3589... Line 3860...
3589
    /**
3860
    /**
3590
     * Disable Quiet Mode
3861
     * Disable Quiet Mode
3591
     *
3862
     *
3592
     * Show stderr in output
3863
     * Show stderr in output
3593
     *
3864
     *
-
 
3865
     * @access public
3594
     */
3866
     */
3595
    public function disableQuietMode()
3867
    public function disableQuietMode()
3596
    {
3868
    {
3597
        $this->quiet_mode = false;
3869
        $this->quiet_mode = false;
3598
    }
3870
    }
Line 3600... Line 3872...
3600
    /**
3872
    /**
3601
     * Returns whether Quiet Mode is enabled or not
3873
     * Returns whether Quiet Mode is enabled or not
3602
     *
3874
     *
3603
     * @see self::enableQuietMode()
3875
     * @see self::enableQuietMode()
3604
     * @see self::disableQuietMode()
3876
     * @see self::disableQuietMode()
-
 
3877
     * @access public
3605
     * @return bool
3878
     * @return bool
3606
     */
3879
     */
3607
    public function isQuietModeEnabled()
3880
    public function isQuietModeEnabled()
3608
    {
3881
    {
3609
        return $this->quiet_mode;
3882
        return $this->quiet_mode;
3610
    }
3883
    }
3611
 
3884
 
3612
    /**
3885
    /**
3613
     * Enable request-pty when using exec()
3886
     * Enable request-pty when using exec()
3614
     *
3887
     *
-
 
3888
     * @access public
3615
     */
3889
     */
3616
    public function enablePTY()
3890
    public function enablePTY()
3617
    {
3891
    {
3618
        $this->request_pty = true;
3892
        $this->request_pty = true;
3619
    }
3893
    }
3620
 
3894
 
3621
    /**
3895
    /**
3622
     * Disable request-pty when using exec()
3896
     * Disable request-pty when using exec()
3623
     *
3897
     *
-
 
3898
     * @access public
3624
     */
3899
     */
3625
    public function disablePTY()
3900
    public function disablePTY()
3626
    {
3901
    {
3627
        if ($this->in_request_pty_exec) {
3902
        if ($this->in_request_pty_exec) {
3628
            $this->close_channel(self::CHANNEL_EXEC);
3903
            $this->close_channel(self::CHANNEL_EXEC);
Line 3634... Line 3909...
3634
    /**
3909
    /**
3635
     * Returns whether request-pty is enabled or not
3910
     * Returns whether request-pty is enabled or not
3636
     *
3911
     *
3637
     * @see self::enablePTY()
3912
     * @see self::enablePTY()
3638
     * @see self::disablePTY()
3913
     * @see self::disablePTY()
-
 
3914
     * @access public
3639
     * @return bool
3915
     * @return bool
3640
     */
3916
     */
3641
    public function isPTYEnabled()
3917
    public function isPTYEnabled()
3642
    {
3918
    {
3643
        return $this->request_pty;
3919
        return $this->request_pty;
Line 3659... Line 3935...
3659
     *
3935
     *
3660
     * @param int $client_channel
3936
     * @param int $client_channel
3661
     * @param bool $skip_extended
3937
     * @param bool $skip_extended
3662
     * @return mixed
3938
     * @return mixed
3663
     * @throws \RuntimeException on connection error
3939
     * @throws \RuntimeException on connection error
-
 
3940
     * @access private
3664
     */
3941
     */
3665
    protected function get_channel_packet($client_channel, $skip_extended = false)
3942
    protected function get_channel_packet($client_channel, $skip_extended = false)
3666
    {
3943
    {
3667
        if (!empty($this->channel_buffers[$client_channel])) {
3944
        if (!empty($this->channel_buffers[$client_channel])) {
3668
            switch ($this->channel_status[$client_channel]) {
3945
            switch ($this->channel_status[$client_channel]) {
3669
                case MessageType::CHANNEL_REQUEST:
3946
                case NET_SSH2_MSG_CHANNEL_REQUEST:
3670
                    foreach ($this->channel_buffers[$client_channel] as $i => $packet) {
3947
                    foreach ($this->channel_buffers[$client_channel] as $i => $packet) {
3671
                        switch (ord($packet[0])) {
3948
                        switch (ord($packet[0])) {
3672
                            case MessageType::CHANNEL_SUCCESS:
3949
                            case NET_SSH2_MSG_CHANNEL_SUCCESS:
3673
                            case MessageType::CHANNEL_FAILURE:
3950
                            case NET_SSH2_MSG_CHANNEL_FAILURE:
3674
                                unset($this->channel_buffers[$client_channel][$i]);
3951
                                unset($this->channel_buffers[$client_channel][$i]);
3675
                                return substr($packet, 1);
3952
                                return substr($packet, 1);
3676
                        }
3953
                        }
3677
                    }
3954
                    }
3678
                    break;
3955
                    break;
Line 3692... Line 3969...
3692
                        $this->close_channel($client_channel);
3969
                        $this->close_channel($client_channel);
3693
                    }
3970
                    }
3694
                    return true;
3971
                    return true;
3695
                }
3972
                }
3696
                if ($response === false) {
3973
                if ($response === false) {
3697
                    $this->disconnect_helper(DisconnectReason::CONNECTION_LOST);
3974
                    $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST);
3698
                    throw new ConnectionClosedException('Connection closed by server');
3975
                    throw new ConnectionClosedException('Connection closed by server');
3699
                }
3976
                }
3700
            }
3977
            }
3701
 
3978
 
3702
            if ($client_channel == -1 && $response === true) {
3979
            if ($client_channel == -1 && $response === true) {
Line 3710... Line 3987...
3710
 
3987
 
3711
                // resize the window, if appropriate
3988
                // resize the window, if appropriate
3712
                if ($this->window_size_server_to_client[$channel] < 0) {
3989
                if ($this->window_size_server_to_client[$channel] < 0) {
3713
                // PuTTY does something more analogous to the following:
3990
                // PuTTY does something more analogous to the following:
3714
                //if ($this->window_size_server_to_client[$channel] < 0x3FFFFFFF) {
3991
                //if ($this->window_size_server_to_client[$channel] < 0x3FFFFFFF) {
3715
                    $packet = pack('CNN', MessageType::CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_resize);
3992
                    $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_resize);
3716
                    $this->send_binary_packet($packet);
3993
                    $this->send_binary_packet($packet);
3717
                    $this->window_size_server_to_client[$channel] += $this->window_resize;
3994
                    $this->window_size_server_to_client[$channel] += $this->window_resize;
3718
                }
3995
                }
3719
 
3996
 
3720
                switch ($type) {
3997
                switch ($type) {
3721
                    case MessageType::CHANNEL_EXTENDED_DATA:
3998
                    case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
3722
                        /*
3999
                        /*
3723
                        if ($client_channel == self::CHANNEL_EXEC) {
4000
                        if ($client_channel == self::CHANNEL_EXEC) {
3724
                            $this->send_channel_packet($client_channel, chr(0));
4001
                            $this->send_channel_packet($client_channel, chr(0));
3725
                        }
4002
                        }
3726
                        */
4003
                        */
Line 3728... Line 4005...
3728
                        list($data_type_code, $data) = Strings::unpackSSH2('Ns', $response);
4005
                        list($data_type_code, $data) = Strings::unpackSSH2('Ns', $response);
3729
                        $this->stdErrorLog .= $data;
4006
                        $this->stdErrorLog .= $data;
3730
                        if ($skip_extended || $this->quiet_mode) {
4007
                        if ($skip_extended || $this->quiet_mode) {
3731
                            continue 2;
4008
                            continue 2;
3732
                        }
4009
                        }
3733
                        if ($client_channel == $channel && $this->channel_status[$channel] == MessageType::CHANNEL_DATA) {
4010
                        if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) {
3734
                            return $data;
4011
                            return $data;
3735
                        }
4012
                        }
3736
                        $this->channel_buffers[$channel][] = chr($type) . $data;
4013
                        $this->channel_buffers[$channel][] = chr($type) . $data;
3737
 
4014
 
3738
                        continue 2;
4015
                        continue 2;
3739
                    case MessageType::CHANNEL_REQUEST:
4016
                    case NET_SSH2_MSG_CHANNEL_REQUEST:
3740
                        if ($this->channel_status[$channel] == MessageType::CHANNEL_CLOSE) {
4017
                        if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) {
3741
                            continue 2;
4018
                            continue 2;
3742
                        }
4019
                        }
3743
                        list($value) = Strings::unpackSSH2('s', $response);
4020
                        list($value) = Strings::unpackSSH2('s', $response);
3744
                        switch ($value) {
4021
                        switch ($value) {
3745
                            case 'exit-signal':
4022
                            case 'exit-signal':
Line 3753... Line 4030...
3753
                                $this->errors[] = "SSH_MSG_CHANNEL_REQUEST (exit-signal): $signal_name";
4030
                                $this->errors[] = "SSH_MSG_CHANNEL_REQUEST (exit-signal): $signal_name";
3754
                                if (strlen($error_message)) {
4031
                                if (strlen($error_message)) {
3755
                                    $this->errors[count($this->errors) - 1] .= "\r\n$error_message";
4032
                                    $this->errors[count($this->errors) - 1] .= "\r\n$error_message";
3756
                                }
4033
                                }
3757
 
4034
 
3758
                                $this->send_binary_packet(pack('CN', MessageType::CHANNEL_EOF, $this->server_channels[$client_channel]));
4035
                                $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
3759
                                $this->send_binary_packet(pack('CN', MessageType::CHANNEL_CLOSE, $this->server_channels[$channel]));
4036
                                $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
3760
 
4037
 
3761
                                $this->channel_status[$channel] = MessageType::CHANNEL_EOF;
4038
                                $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF;
3762
 
4039
 
3763
                                continue 3;
4040
                                continue 3;
3764
                            case 'exit-status':
4041
                            case 'exit-status':
3765
                                list(, $this->exit_status) = Strings::unpackSSH2('CN', $response);
4042
                                list(, $this->exit_status) = Strings::unpackSSH2('CN', $response);
3766
 
4043
 
Line 3774... Line 4051...
3774
                                continue 3;
4051
                                continue 3;
3775
                        }
4052
                        }
3776
                }
4053
                }
3777
 
4054
 
3778
                switch ($this->channel_status[$channel]) {
4055
                switch ($this->channel_status[$channel]) {
3779
                    case MessageType::CHANNEL_OPEN:
4056
                    case NET_SSH2_MSG_CHANNEL_OPEN:
3780
                        switch ($type) {
4057
                        switch ($type) {
3781
                            case MessageType::CHANNEL_OPEN_CONFIRMATION:
4058
                            case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3782
                                list(
4059
                                list(
3783
                                    $this->server_channels[$channel],
4060
                                    $this->server_channels[$channel],
3784
                                    $window_size,
4061
                                    $window_size,
3785
                                    $this->packet_size_client_to_server[$channel]
4062
                                    $this->packet_size_client_to_server[$channel]
3786
                                ) = Strings::unpackSSH2('NNN', $response);
4063
                                ) = Strings::unpackSSH2('NNN', $response);
Line 3791... Line 4068...
3791
                                }
4068
                                }
3792
                                $this->window_size_client_to_server[$channel] = $window_size;
4069
                                $this->window_size_client_to_server[$channel] = $window_size;
3793
                                $result = $client_channel == $channel ? true : $this->get_channel_packet($client_channel, $skip_extended);
4070
                                $result = $client_channel == $channel ? true : $this->get_channel_packet($client_channel, $skip_extended);
3794
                                $this->on_channel_open();
4071
                                $this->on_channel_open();
3795
                                return $result;
4072
                                return $result;
3796
                            case MessageType::CHANNEL_OPEN_FAILURE:
4073
                            case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
3797
                                $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
4074
                                $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
3798
                                throw new \RuntimeException('Unable to open channel');
4075
                                throw new \RuntimeException('Unable to open channel');
3799
                            default:
4076
                            default:
3800
                                if ($client_channel == $channel) {
4077
                                if ($client_channel == $channel) {
3801
                                    $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
4078
                                    $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
3802
                                    throw new \RuntimeException('Unexpected response to open request');
4079
                                    throw new \RuntimeException('Unexpected response to open request');
3803
                                }
4080
                                }
3804
                                return $this->get_channel_packet($client_channel, $skip_extended);
4081
                                return $this->get_channel_packet($client_channel, $skip_extended);
3805
                        }
4082
                        }
3806
                        break;
4083
                        break;
3807
                    case MessageType::CHANNEL_REQUEST:
4084
                    case NET_SSH2_MSG_CHANNEL_REQUEST:
3808
                        switch ($type) {
4085
                        switch ($type) {
3809
                            case MessageType::CHANNEL_SUCCESS:
4086
                            case NET_SSH2_MSG_CHANNEL_SUCCESS:
3810
                                return true;
4087
                                return true;
3811
                            case MessageType::CHANNEL_FAILURE:
4088
                            case NET_SSH2_MSG_CHANNEL_FAILURE:
3812
                                return false;
4089
                                return false;
3813
                            case MessageType::CHANNEL_DATA:
4090
                            case NET_SSH2_MSG_CHANNEL_DATA:
3814
                                list($data) = Strings::unpackSSH2('s', $response);
4091
                                list($data) = Strings::unpackSSH2('s', $response);
3815
                                $this->channel_buffers[$channel][] = chr($type) . $data;
4092
                                $this->channel_buffers[$channel][] = chr($type) . $data;
3816
                                return $this->get_channel_packet($client_channel, $skip_extended);
4093
                                return $this->get_channel_packet($client_channel, $skip_extended);
3817
                            default:
4094
                            default:
3818
                                $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
4095
                                $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
3819
                                throw new \RuntimeException('Unable to fulfill channel request');
4096
                                throw new \RuntimeException('Unable to fulfill channel request');
3820
                        }
4097
                        }
3821
                    case MessageType::CHANNEL_CLOSE:
4098
                    case NET_SSH2_MSG_CHANNEL_CLOSE:
3822
                        return $type == MessageType::CHANNEL_CLOSE ? true : $this->get_channel_packet($client_channel, $skip_extended);
4099
                        return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->get_channel_packet($client_channel, $skip_extended);
3823
                }
4100
                }
3824
            }
4101
            }
3825
 
4102
 
3826
            // ie. $this->channel_status[$channel] == SSHMsg::CHANNEL_DATA
4103
            // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA
3827
 
4104
 
3828
            switch ($type) {
4105
            switch ($type) {
3829
                case MessageType::CHANNEL_DATA:
4106
                case NET_SSH2_MSG_CHANNEL_DATA:
3830
                    /*
4107
                    /*
3831
                    if ($channel == self::CHANNEL_EXEC) {
4108
                    if ($channel == self::CHANNEL_EXEC) {
3832
                        // SCP requires null packets, such as this, be sent.  further, in the case of the ssh.com SSH server
4109
                        // SCP requires null packets, such as this, be sent.  further, in the case of the ssh.com SSH server
3833
                        // this actually seems to make things twice as fast.  more to the point, the message right after
4110
                        // this actually seems to make things twice as fast.  more to the point, the message right after
3834
                        // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
4111
                        // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
Line 3849... Line 4126...
3849
                    if ($client_channel == $channel) {
4126
                    if ($client_channel == $channel) {
3850
                        return $data;
4127
                        return $data;
3851
                    }
4128
                    }
3852
                    $this->channel_buffers[$channel][] = chr($type) . $data;
4129
                    $this->channel_buffers[$channel][] = chr($type) . $data;
3853
                    break;
4130
                    break;
3854
                case MessageType::CHANNEL_CLOSE:
4131
                case NET_SSH2_MSG_CHANNEL_CLOSE:
3855
                    $this->curTimeout = 5;
4132
                    $this->curTimeout = 5;
3856
 
4133
 
3857
                    if ($this->bitmap & self::MASK_SHELL) {
4134
                    if ($this->bitmap & self::MASK_SHELL) {
3858
                        $this->bitmap &= ~self::MASK_SHELL;
4135
                        $this->bitmap &= ~self::MASK_SHELL;
3859
                    }
4136
                    }
3860
                    if ($this->channel_status[$channel] != MessageType::CHANNEL_EOF) {
4137
                    if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
3861
                        $this->send_binary_packet(pack('CN', MessageType::CHANNEL_CLOSE, $this->server_channels[$channel]));
4138
                        $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
3862
                    }
4139
                    }
3863
 
4140
 
3864
                    $this->channel_status[$channel] = MessageType::CHANNEL_CLOSE;
4141
                    $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
3865
                    if ($client_channel == $channel) {
4142
                    if ($client_channel == $channel) {
3866
                        return true;
4143
                        return true;
3867
                    }
4144
                    }
3868
                    // fall-through
4145
                    // fall-through
3869
                case MessageType::CHANNEL_EOF:
4146
                case NET_SSH2_MSG_CHANNEL_EOF:
3870
                    break;
4147
                    break;
3871
                default:
4148
                default:
3872
                    $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
4149
                    $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
3873
                    throw new \RuntimeException("Error reading channel data ($type)");
4150
                    throw new \RuntimeException("Error reading channel data ($type)");
3874
            }
4151
            }
3875
        }
4152
        }
3876
    }
4153
    }
3877
 
4154
 
Line 3882... Line 4159...
3882
     *
4159
     *
3883
     * @param string $data
4160
     * @param string $data
3884
     * @param string $logged
4161
     * @param string $logged
3885
     * @see self::_get_binary_packet()
4162
     * @see self::_get_binary_packet()
3886
     * @return void
4163
     * @return void
-
 
4164
     * @access private
3887
     */
4165
     */
3888
    protected function send_binary_packet($data, $logged = null)
4166
    protected function send_binary_packet($data, $logged = null)
3889
    {
4167
    {
3890
        if (!is_resource($this->fsock) || feof($this->fsock)) {
4168
        if (!is_resource($this->fsock) || feof($this->fsock)) {
3891
            $this->bitmap = 0;
4169
            $this->bitmap = 0;
Line 4006... Line 4284...
4006
        $sent = @fputs($this->fsock, $packet);
4284
        $sent = @fputs($this->fsock, $packet);
4007
        $stop = microtime(true);
4285
        $stop = microtime(true);
4008
 
4286
 
4009
        if (defined('NET_SSH2_LOGGING')) {
4287
        if (defined('NET_SSH2_LOGGING')) {
4010
            $current = microtime(true);
4288
            $current = microtime(true);
4011
            $message_number = sprintf(
-
 
4012
                '-> %s (since last: %s, network: %ss)',
-
 
4013
                ($constantName = MessageType::findConstantNameByValue($value = ord($logged[0]), false))
4289
            $message_number = isset($this->message_numbers[ord($logged[0])]) ? $this->message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')';
4014
                    ? "SSH_MSG_$constantName"
4290
            $message_number = '-> ' . $message_number .
4015
                    : "UNKNOWN ($value)",
-
 
4016
                round($current - $this->last_packet, 4),
4291
                              ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
4017
                round($stop - $start, 4)
-
 
4018
            );
-
 
4019
            $this->append_log($message_number, $logged);
4292
            $this->append_log($message_number, $logged);
4020
            $this->last_packet = $current;
4293
            $this->last_packet = $current;
4021
        }
4294
        }
4022
 
4295
 
4023
        if (strlen($packet) != $sent) {
4296
        if (strlen($packet) != $sent) {
Line 4031... Line 4304...
4031
     *
4304
     *
4032
     * Makes sure that only the last 1MB worth of packets will be logged
4305
     * Makes sure that only the last 1MB worth of packets will be logged
4033
     *
4306
     *
4034
     * @param string $message_number
4307
     * @param string $message_number
4035
     * @param string $message
4308
     * @param string $message
-
 
4309
     * @access private
4036
     */
4310
     */
4037
    private function append_log($message_number, $message)
4311
    private function append_log($message_number, $message)
4038
    {
4312
    {
4039
        if (!defined('NET_SSH2_LOGGING')) {
-
 
4040
            return;
-
 
4041
        }
-
 
4042
 
-
 
4043
        // remove the byte identifying the message type from all but the first two messages (ie. the identification strings)
4313
        // remove the byte identifying the message type from all but the first two messages (ie. the identification strings)
4044
        if (strlen($message_number) > 2) {
4314
        if (strlen($message_number) > 2) {
4045
            Strings::shift($message);
4315
            Strings::shift($message);
4046
        }
4316
        }
4047
 
4317
 
Line 4135... Line 4405...
4135
            );
4405
            );
4136
 
4406
 
4137
            $temp = Strings::shift($data, $max_size);
4407
            $temp = Strings::shift($data, $max_size);
4138
            $packet = Strings::packSSH2(
4408
            $packet = Strings::packSSH2(
4139
                'CNs',
4409
                'CNs',
4140
                MessageType::CHANNEL_DATA,
4410
                NET_SSH2_MSG_CHANNEL_DATA,
4141
                $this->server_channels[$client_channel],
4411
                $this->server_channels[$client_channel],
4142
                $temp
4412
                $temp
4143
            );
4413
            );
4144
            $this->window_size_client_to_server[$client_channel] -= strlen($temp);
4414
            $this->window_size_client_to_server[$client_channel] -= strlen($temp);
4145
            $this->send_binary_packet($packet);
4415
            $this->send_binary_packet($packet);
Line 4154... Line 4424...
4154
     * for SCP more than anything.
4424
     * for SCP more than anything.
4155
     *
4425
     *
4156
     * @param int $client_channel
4426
     * @param int $client_channel
4157
     * @param bool $want_reply
4427
     * @param bool $want_reply
4158
     * @return void
4428
     * @return void
-
 
4429
     * @access private
4159
     */
4430
     */
4160
    private function close_channel($client_channel, $want_reply = false)
4431
    private function close_channel($client_channel, $want_reply = false)
4161
    {
4432
    {
4162
        // see http://tools.ietf.org/html/rfc4254#section-5.3
4433
        // see http://tools.ietf.org/html/rfc4254#section-5.3
4163
 
4434
 
4164
        $this->send_binary_packet(pack('CN', MessageType::CHANNEL_EOF, $this->server_channels[$client_channel]));
4435
        $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
4165
 
4436
 
4166
        if (!$want_reply) {
4437
        if (!$want_reply) {
4167
            $this->send_binary_packet(pack('CN', MessageType::CHANNEL_CLOSE, $this->server_channels[$client_channel]));
4438
            $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
4168
        }
4439
        }
4169
 
4440
 
4170
        $this->channel_status[$client_channel] = MessageType::CHANNEL_CLOSE;
4441
        $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
4171
 
4442
 
4172
        $this->curTimeout = 5;
4443
        $this->curTimeout = 5;
4173
 
4444
 
4174
        while (!is_bool($this->get_channel_packet($client_channel))) {
4445
        while (!is_bool($this->get_channel_packet($client_channel))) {
4175
        }
4446
        }
Line 4177... Line 4448...
4177
        if ($this->is_timeout) {
4448
        if ($this->is_timeout) {
4178
            $this->disconnect();
4449
            $this->disconnect();
4179
        }
4450
        }
4180
 
4451
 
4181
        if ($want_reply) {
4452
        if ($want_reply) {
4182
            $this->send_binary_packet(pack('CN', MessageType::CHANNEL_CLOSE, $this->server_channels[$client_channel]));
4453
            $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
4183
        }
4454
        }
4184
 
4455
 
4185
        if ($this->bitmap & self::MASK_SHELL) {
4456
        if ($this->bitmap & self::MASK_SHELL) {
4186
            $this->bitmap &= ~self::MASK_SHELL;
4457
            $this->bitmap &= ~self::MASK_SHELL;
4187
        }
4458
        }
Line 4190... Line 4461...
4190
    /**
4461
    /**
4191
     * Disconnect
4462
     * Disconnect
4192
     *
4463
     *
4193
     * @param int $reason
4464
     * @param int $reason
4194
     * @return false
4465
     * @return false
-
 
4466
     * @access protected
4195
     */
4467
     */
4196
    protected function disconnect_helper($reason)
4468
    protected function disconnect_helper($reason)
4197
    {
4469
    {
4198
        if ($this->bitmap & self::MASK_CONNECTED) {
4470
        if ($this->bitmap & self::MASK_CONNECTED) {
4199
            $data = Strings::packSSH2('CNss', MessageType::DISCONNECT, $reason, '', '');
4471
            $data = Strings::packSSH2('CNss', NET_SSH2_MSG_DISCONNECT, $reason, '', '');
4200
            try {
4472
            try {
4201
                $this->send_binary_packet($data);
4473
                $this->send_binary_packet($data);
4202
            } catch (\Exception $e) {
4474
            } catch (\Exception $e) {
4203
            }
4475
            }
4204
        }
4476
        }
Line 4210... Line 4482...
4210
 
4482
 
4211
        return false;
4483
        return false;
4212
    }
4484
    }
4213
 
4485
 
4214
    /**
4486
    /**
-
 
4487
     * Define Array
-
 
4488
     *
-
 
4489
     * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
-
 
4490
     * named constants from it, using the value as the name of the constant and the index as the value of the constant.
-
 
4491
     * If any of the constants that would be defined already exists, none of the constants will be defined.
-
 
4492
     *
-
 
4493
     * @param mixed[] ...$args
-
 
4494
     * @access protected
-
 
4495
     */
-
 
4496
    protected function define_array(...$args)
-
 
4497
    {
-
 
4498
        foreach ($args as $arg) {
-
 
4499
            foreach ($arg as $key => $value) {
-
 
4500
                if (!defined($value)) {
-
 
4501
                    define($value, $key);
-
 
4502
                } else {
-
 
4503
                    break 2;
-
 
4504
                }
-
 
4505
            }
-
 
4506
        }
-
 
4507
    }
-
 
4508
 
-
 
4509
    /**
4215
     * Returns a log of the packets that have been sent and received.
4510
     * Returns a log of the packets that have been sent and received.
4216
     *
4511
     *
4217
     * Returns a string if NET_SSH2_LOGGING == self::LOG_COMPLEX, an array if NET_SSH2_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING')
4512
     * Returns a string if NET_SSH2_LOGGING == self::LOG_COMPLEX, an array if NET_SSH2_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING')
4218
     *
4513
     *
-
 
4514
     * @access public
4219
     * @return array|false|string
4515
     * @return array|false|string
4220
     */
4516
     */
4221
    public function getLog()
4517
    public function getLog()
4222
    {
4518
    {
4223
        if (!defined('NET_SSH2_LOGGING')) {
4519
        if (!defined('NET_SSH2_LOGGING')) {
Line 4238... Line 4534...
4238
    /**
4534
    /**
4239
     * Formats a log for printing
4535
     * Formats a log for printing
4240
     *
4536
     *
4241
     * @param array $message_log
4537
     * @param array $message_log
4242
     * @param array $message_number_log
4538
     * @param array $message_number_log
-
 
4539
     * @access private
4243
     * @return string
4540
     * @return string
4244
     */
4541
     */
4245
    protected function format_log($message_log, $message_number_log)
4542
    protected function format_log($message_log, $message_number_log)
4246
    {
4543
    {
4247
        $output = '';
4544
        $output = '';
Line 4275... Line 4572...
4275
     *
4572
     *
4276
     * Used when channels are created to inform agent
4573
     * Used when channels are created to inform agent
4277
     * of said channel opening. Must be called after
4574
     * of said channel opening. Must be called after
4278
     * channel open confirmation received
4575
     * channel open confirmation received
4279
     *
4576
     *
-
 
4577
     * @access private
4280
     */
4578
     */
4281
    private function on_channel_open()
4579
    private function on_channel_open()
4282
    {
4580
    {
4283
        if (isset($this->agent)) {
4581
        if (isset($this->agent)) {
4284
            $this->agent->registerChannelOpen($this);
4582
            $this->agent->registerChannelOpen($this);
Line 4290... Line 4588...
4290
     * the intersection is empty. The order is defined by the first parameter.
4588
     * the intersection is empty. The order is defined by the first parameter.
4291
     *
4589
     *
4292
     * @param array $array1
4590
     * @param array $array1
4293
     * @param array $array2
4591
     * @param array $array2
4294
     * @return mixed False if intersection is empty, else intersected value.
4592
     * @return mixed False if intersection is empty, else intersected value.
-
 
4593
     * @access private
4295
     */
4594
     */
4296
    private static function array_intersect_first($array1, $array2)
4595
    private static function array_intersect_first($array1, $array2)
4297
    {
4596
    {
4298
        foreach ($array1 as $value) {
4597
        foreach ($array1 as $value) {
4299
            if (in_array($value, $array2)) {
4598
            if (in_array($value, $array2)) {
Line 4305... Line 4604...
4305
 
4604
 
4306
    /**
4605
    /**
4307
     * Returns all errors
4606
     * Returns all errors
4308
     *
4607
     *
4309
     * @return string[]
4608
     * @return string[]
-
 
4609
     * @access public
4310
     */
4610
     */
4311
    public function getErrors()
4611
    public function getErrors()
4312
    {
4612
    {
4313
        return $this->errors;
4613
        return $this->errors;
4314
    }
4614
    }
4315
 
4615
 
4316
    /**
4616
    /**
4317
     * Returns the last error
4617
     * Returns the last error
4318
     *
4618
     *
4319
     * @return string
4619
     * @return string
-
 
4620
     * @access public
4320
     */
4621
     */
4321
    public function getLastError()
4622
    public function getLastError()
4322
    {
4623
    {
4323
        $count = count($this->errors);
4624
        $count = count($this->errors);
4324
 
4625
 
Line 4329... Line 4630...
4329
 
4630
 
4330
    /**
4631
    /**
4331
     * Return the server identification.
4632
     * Return the server identification.
4332
     *
4633
     *
4333
     * @return string|false
4634
     * @return string|false
-
 
4635
     * @access public
4334
     */
4636
     */
4335
    public function getServerIdentification()
4637
    public function getServerIdentification()
4336
    {
4638
    {
4337
        $this->connect();
4639
        $this->connect();
4338
 
4640
 
Line 4341... Line 4643...
4341
 
4643
 
4342
    /**
4644
    /**
4343
     * Returns a list of algorithms the server supports
4645
     * Returns a list of algorithms the server supports
4344
     *
4646
     *
4345
     * @return array
4647
     * @return array
-
 
4648
     * @access public
4346
     */
4649
     */
4347
    public function getServerAlgorithms()
4650
    public function getServerAlgorithms()
4348
    {
4651
    {
4349
        $this->connect();
4652
        $this->connect();
4350
 
4653
 
Line 4368... Line 4671...
4368
 
4671
 
4369
    /**
4672
    /**
4370
     * Returns a list of KEX algorithms that phpseclib supports
4673
     * Returns a list of KEX algorithms that phpseclib supports
4371
     *
4674
     *
4372
     * @return array
4675
     * @return array
-
 
4676
     * @access public
4373
     */
4677
     */
4374
    public static function getSupportedKEXAlgorithms()
4678
    public static function getSupportedKEXAlgorithms()
4375
    {
4679
    {
4376
        $kex_algorithms = [
4680
        $kex_algorithms = [
4377
            // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using
4681
            // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using
Line 4404... Line 4708...
4404
 
4708
 
4405
    /**
4709
    /**
4406
     * Returns a list of host key algorithms that phpseclib supports
4710
     * Returns a list of host key algorithms that phpseclib supports
4407
     *
4711
     *
4408
     * @return array
4712
     * @return array
-
 
4713
     * @access public
4409
     */
4714
     */
4410
    public static function getSupportedHostKeyAlgorithms()
4715
    public static function getSupportedHostKeyAlgorithms()
4411
    {
4716
    {
4412
        return [
4717
        return [
4413
            'ssh-ed25519', // https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-02
4718
            'ssh-ed25519', // https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-02
Line 4423... Line 4728...
4423
 
4728
 
4424
    /**
4729
    /**
4425
     * Returns a list of symmetric key algorithms that phpseclib supports
4730
     * Returns a list of symmetric key algorithms that phpseclib supports
4426
     *
4731
     *
4427
     * @return array
4732
     * @return array
-
 
4733
     * @access public
4428
     */
4734
     */
4429
    public static function getSupportedEncryptionAlgorithms()
4735
    public static function getSupportedEncryptionAlgorithms()
4430
    {
4736
    {
4431
        $algos = [
4737
        $algos = [
4432
            // from <https://tools.ietf.org/html/rfc5647>:
4738
            // from <https://tools.ietf.org/html/rfc5647>:
Line 4442... Line 4748...
4442
            // CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>:
4748
            // CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>:
4443
            'aes128-ctr',     // RECOMMENDED       AES (Rijndael) in SDCTR mode, with 128-bit key
4749
            'aes128-ctr',     // RECOMMENDED       AES (Rijndael) in SDCTR mode, with 128-bit key
4444
            'aes192-ctr',     // RECOMMENDED       AES with 192-bit key
4750
            'aes192-ctr',     // RECOMMENDED       AES with 192-bit key
4445
            'aes256-ctr',     // RECOMMENDED       AES with 256-bit key
4751
            'aes256-ctr',     // RECOMMENDED       AES with 256-bit key
4446
 
4752
 
4447
            // from <https://github.com/openssh/openssh-portable/blob/001aa55/PROTOCOL.chacha20poly1305>:
4753
            // from <https://git.io/fhxOl>:
4448
            // one of the big benefits of chacha20-poly1305 is speed. the problem is...
4754
            // one of the big benefits of chacha20-poly1305 is speed. the problem is...
4449
            // libsodium doesn't generate the poly1305 keys in the way ssh does and openssl's PHP bindings don't even
4755
            // libsodium doesn't generate the poly1305 keys in the way ssh does and openssl's PHP bindings don't even
4450
            // seem to support poly1305 currently. so even if libsodium or openssl are being used for the chacha20
4756
            // seem to support poly1305 currently. so even if libsodium or openssl are being used for the chacha20
4451
            // part, pure-PHP has to be used for the poly1305 part and that's gonna cause a big slow down.
4757
            // part, pure-PHP has to be used for the poly1305 part and that's gonna cause a big slow down.
4452
            // speed-wise it winds up being faster to use AES (when openssl or mcrypt are available) and some HMAC
4758
            // speed-wise it winds up being faster to use AES (when openssl or mcrypt are available) and some HMAC
Line 4527... Line 4833...
4527
 
4833
 
4528
    /**
4834
    /**
4529
     * Returns a list of MAC algorithms that phpseclib supports
4835
     * Returns a list of MAC algorithms that phpseclib supports
4530
     *
4836
     *
4531
     * @return array
4837
     * @return array
-
 
4838
     * @access public
4532
     */
4839
     */
4533
    public static function getSupportedMACAlgorithms()
4840
    public static function getSupportedMACAlgorithms()
4534
    {
4841
    {
4535
        return [
4842
        return [
4536
            'hmac-sha2-256-etm@openssh.com',
4843
            'hmac-sha2-256-etm@openssh.com',
Line 4557... Line 4864...
4557
 
4864
 
4558
    /**
4865
    /**
4559
     * Returns a list of compression algorithms that phpseclib supports
4866
     * Returns a list of compression algorithms that phpseclib supports
4560
     *
4867
     *
4561
     * @return array
4868
     * @return array
-
 
4869
     * @access public
4562
     */
4870
     */
4563
    public static function getSupportedCompressionAlgorithms()
4871
    public static function getSupportedCompressionAlgorithms()
4564
    {
4872
    {
4565
        $algos = ['none']; // REQUIRED        no compression
4873
        $algos = ['none']; // REQUIRED        no compression
4566
        if (function_exists('deflate_init')) {
4874
        if (function_exists('deflate_init')) {
Line 4574... Line 4882...
4574
     * Return list of negotiated algorithms
4882
     * Return list of negotiated algorithms
4575
     *
4883
     *
4576
     * Uses the same format as https://www.php.net/ssh2-methods-negotiated
4884
     * Uses the same format as https://www.php.net/ssh2-methods-negotiated
4577
     *
4885
     *
4578
     * @return array
4886
     * @return array
-
 
4887
     * @access public
4579
     */
4888
     */
4580
    public function getAlgorithmsNegotiated()
4889
    public function getAlgorithmsNegotiated()
4581
    {
4890
    {
4582
        $this->connect();
4891
        $this->connect();
4583
 
4892
 
Line 4605... Line 4914...
4605
 
4914
 
4606
    /**
4915
    /**
4607
     * Allows you to set the terminal
4916
     * Allows you to set the terminal
4608
     *
4917
     *
4609
     * @param string $term
4918
     * @param string $term
-
 
4919
     * @access public
4610
     */
4920
     */
4611
    public function setTerminal($term)
4921
    public function setTerminal($term)
4612
    {
4922
    {
4613
        $this->term = $term;
4923
        $this->term = $term;
4614
    }
4924
    }
Line 4616... Line 4926...
4616
    /**
4926
    /**
4617
     * Accepts an associative array with up to four parameters as described at
4927
     * Accepts an associative array with up to four parameters as described at
4618
     * <https://www.php.net/manual/en/function.ssh2-connect.php>
4928
     * <https://www.php.net/manual/en/function.ssh2-connect.php>
4619
     *
4929
     *
4620
     * @param array $methods
4930
     * @param array $methods
-
 
4931
     * @access public
4621
     */
4932
     */
4622
    public function setPreferredAlgorithms(array $methods)
4933
    public function setPreferredAlgorithms(array $methods)
4623
    {
4934
    {
4624
        $preferred = $methods;
4935
        $preferred = $methods;
4625
 
4936
 
Line 4702... Line 5013...
4702
     *
5013
     *
4703
     * Quoting from the RFC, "in some jurisdictions, sending a warning message before
5014
     * Quoting from the RFC, "in some jurisdictions, sending a warning message before
4704
     * authentication may be relevant for getting legal protection."
5015
     * authentication may be relevant for getting legal protection."
4705
     *
5016
     *
4706
     * @return string
5017
     * @return string
-
 
5018
     * @access public
4707
     */
5019
     */
4708
    public function getBannerMessage()
5020
    public function getBannerMessage()
4709
    {
5021
    {
4710
        return $this->banner_message;
5022
        return $this->banner_message;
4711
    }
5023
    }
Line 4717... Line 5029...
4717
     * is recommended.  Returns false if the server signature is not signed correctly with the public host key.
5029
     * is recommended.  Returns false if the server signature is not signed correctly with the public host key.
4718
     *
5030
     *
4719
     * @return string|false
5031
     * @return string|false
4720
     * @throws \RuntimeException on badly formatted keys
5032
     * @throws \RuntimeException on badly formatted keys
4721
     * @throws \phpseclib3\Exception\NoSupportedAlgorithmsException when the key isn't in a supported format
5033
     * @throws \phpseclib3\Exception\NoSupportedAlgorithmsException when the key isn't in a supported format
-
 
5034
     * @access public
4722
     */
5035
     */
4723
    public function getServerPublicHostKey()
5036
    public function getServerPublicHostKey()
4724
    {
5037
    {
4725
        if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
5038
        if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
4726
            $this->connect();
5039
            $this->connect();
Line 4786... Line 5099...
4786
                        $hash = 'sha1';
5099
                        $hash = 'sha1';
4787
                }
5100
                }
4788
                $key = $key->withHash($hash);
5101
                $key = $key->withHash($hash);
4789
                break;
5102
                break;
4790
            default:
5103
            default:
4791
                $this->disconnect_helper(DisconnectReason::HOST_KEY_NOT_VERIFIABLE);
5104
                $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
4792
                throw new NoSupportedAlgorithmsException('Unsupported signature format');
5105
                throw new NoSupportedAlgorithmsException('Unsupported signature format');
4793
        }
5106
        }
4794
 
5107
 
4795
        if (!$key->verify($this->exchange_hash, $signature)) {
5108
        if (!$key->verify($this->exchange_hash, $signature)) {
4796
            return $this->disconnect_helper(DisconnectReason::HOST_KEY_NOT_VERIFIABLE);
5109
            return $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
4797
        };
5110
        };
4798
 
5111
 
4799
        return $this->signature_format . ' ' . $server_public_host_key;
5112
        return $this->signature_format . ' ' . $server_public_host_key;
4800
    }
5113
    }
4801
 
5114
 
4802
    /**
5115
    /**
4803
     * Returns the exit status of an SSH command or false.
5116
     * Returns the exit status of an SSH command or false.
4804
     *
5117
     *
4805
     * @return false|int
5118
     * @return false|int
-
 
5119
     * @access public
4806
     */
5120
     */
4807
    public function getExitStatus()
5121
    public function getExitStatus()
4808
    {
5122
    {
4809
        if (is_null($this->exit_status)) {
5123
        if (is_null($this->exit_status)) {
4810
            return false;
5124
            return false;
Line 4814... Line 5128...
4814
 
5128
 
4815
    /**
5129
    /**
4816
     * Returns the number of columns for the terminal window size.
5130
     * Returns the number of columns for the terminal window size.
4817
     *
5131
     *
4818
     * @return int
5132
     * @return int
-
 
5133
     * @access public
4819
     */
5134
     */
4820
    public function getWindowColumns()
5135
    public function getWindowColumns()
4821
    {
5136
    {
4822
        return $this->windowColumns;
5137
        return $this->windowColumns;
4823
    }
5138
    }
4824
 
5139
 
4825
    /**
5140
    /**
4826
     * Returns the number of rows for the terminal window size.
5141
     * Returns the number of rows for the terminal window size.
4827
     *
5142
     *
4828
     * @return int
5143
     * @return int
-
 
5144
     * @access public
4829
     */
5145
     */
4830
    public function getWindowRows()
5146
    public function getWindowRows()
4831
    {
5147
    {
4832
        return $this->windowRows;
5148
        return $this->windowRows;
4833
    }
5149
    }
4834
 
5150
 
4835
    /**
5151
    /**
4836
     * Sets the number of columns for the terminal window size.
5152
     * Sets the number of columns for the terminal window size.
4837
     *
5153
     *
4838
     * @param int $value
5154
     * @param int $value
-
 
5155
     * @access public
4839
     */
5156
     */
4840
    public function setWindowColumns($value)
5157
    public function setWindowColumns($value)
4841
    {
5158
    {
4842
        $this->windowColumns = $value;
5159
        $this->windowColumns = $value;
4843
    }
5160
    }
4844
 
5161
 
4845
    /**
5162
    /**
4846
     * Sets the number of rows for the terminal window size.
5163
     * Sets the number of rows for the terminal window size.
4847
     *
5164
     *
4848
     * @param int $value
5165
     * @param int $value
-
 
5166
     * @access public
4849
     */
5167
     */
4850
    public function setWindowRows($value)
5168
    public function setWindowRows($value)
4851
    {
5169
    {
4852
        $this->windowRows = $value;
5170
        $this->windowRows = $value;
4853
    }
5171
    }
Line 4855... Line 5173...
4855
    /**
5173
    /**
4856
     * Sets the number of columns and rows for the terminal window size.
5174
     * Sets the number of columns and rows for the terminal window size.
4857
     *
5175
     *
4858
     * @param int $columns
5176
     * @param int $columns
4859
     * @param int $rows
5177
     * @param int $rows
-
 
5178
     * @access public
4860
     */
5179
     */
4861
    public function setWindowSize($columns = 80, $rows = 24)
5180
    public function setWindowSize($columns = 80, $rows = 24)
4862
    {
5181
    {
4863
        $this->windowColumns = $columns;
5182
        $this->windowColumns = $columns;
4864
        $this->windowRows = $rows;
5183
        $this->windowRows = $rows;
Line 4866... Line 5185...
4866
 
5185
 
4867
    /**
5186
    /**
4868
     * To String Magic Method
5187
     * To String Magic Method
4869
     *
5188
     *
4870
     * @return string
5189
     * @return string
-
 
5190
     * @access public
4871
     */
5191
     */
4872
    #[\ReturnTypeWillChange]
5192
    #[\ReturnTypeWillChange]
4873
    public function __toString()
5193
    public function __toString()
4874
    {
5194
    {
4875
        return $this->getResourceId();
5195
        return $this->getResourceId();
Line 4921... Line 5241...
4921
            $temp[$key] = $ref->get();
5241
            $temp[$key] = $ref->get();
4922
        }
5242
        }
4923
        return $temp;
5243
        return $temp;
4924
    }
5244
    }
4925
 
5245
 
4926
    /**
5246
    /*
4927
     * Update packet types in log history
5247
     * Update packet types in log history
4928
     *
5248
     *
4929
     * @param string $old
5249
     * @param string $old
4930
     * @param string $new
5250
     * @param string $new
-
 
5251
     * @access private
4931
     */
5252
     */
4932
    private function updateLogHistory($old, $new)
5253
    private function updateLogHistory($old, $new)
4933
    {
5254
    {
4934
        if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) {
5255
        if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) {
4935
            $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
5256
            $this->message_number_log[count($this->message_number_log) - 1] = str_replace(