mirror of
https://github.com/phpseclib/phpseclib.git
synced 2024-12-27 19:54:57 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
137b5dae42
@ -14,7 +14,7 @@ env:
|
||||
before_install: true
|
||||
|
||||
install:
|
||||
- wget http://ftp.gnu.org/gnu/parallel/parallel-20120522.tar.bz2
|
||||
- wget http://ftp.gnu.org/gnu/parallel/parallel-20170822.tar.bz2
|
||||
- tar -xvjf parallel*
|
||||
- cd parallel*
|
||||
- ./configure
|
||||
|
@ -8,6 +8,7 @@ AES, Blowfish, Twofish, SSH-1, SSH-2, SFTP, and X.509
|
||||
|
||||
* [Browse Git](https://github.com/phpseclib/phpseclib)
|
||||
* [Code Coverage Report](https://coverage.phpseclib.org/master/latest/)
|
||||
* Support phpseclib development by [![Becoming a patron](https://img.shields.io/badge/become-patron-brightgreen.svg)](https://www.patreon.com/phpseclib)
|
||||
|
||||
## Documentation
|
||||
|
||||
|
@ -51,9 +51,9 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"paragonie/constant_time_encoding": "^1|^2",
|
||||
"paragonie/constant_time_encoding": "^1",
|
||||
"paragonie/random_compat": "^1.4|^2.0",
|
||||
"php": ">=7.0"
|
||||
"php": ">=5.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"phing/phing": "~2.7",
|
||||
|
@ -27,6 +27,8 @@ use ParagonIE\ConstantTime\Base64;
|
||||
use phpseclib\File\ASN1\Element;
|
||||
use phpseclib\Math\BigInteger;
|
||||
use phpseclib\Common\Functions\Strings;
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
|
||||
/**
|
||||
* Pure-PHP ASN.1 Parser
|
||||
@ -739,7 +741,7 @@ abstract class ASN1
|
||||
if (isset($mapping['implicit'])) {
|
||||
$decoded['content'] = self::decodeTime($decoded['content'], $decoded['type']);
|
||||
}
|
||||
return @date(self::$format, $decoded['content']);
|
||||
return $decoded['content'] ? $decoded['content']->format(self::$format) : false;
|
||||
case self::TYPE_BIT_STRING:
|
||||
if (isset($mapping['mapping'])) {
|
||||
$offset = ord($decoded['content'][0]);
|
||||
@ -989,7 +991,8 @@ abstract class ASN1
|
||||
case self::TYPE_GENERALIZED_TIME:
|
||||
$format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y';
|
||||
$format.= 'mdHis';
|
||||
$value = @gmdate($format, strtotime($source)) . 'Z';
|
||||
$date = new DateTime($source, new DateTimeZone('GMT'));
|
||||
$value = $date->format($format) . 'Z';
|
||||
break;
|
||||
case self::TYPE_BIT_STRING:
|
||||
if (isset($mapping['mapping'])) {
|
||||
@ -1151,33 +1154,32 @@ abstract class ASN1
|
||||
http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
|
||||
http://www.obj-sys.com/asn1tutorial/node14.html */
|
||||
|
||||
$pattern = $tag == self::TYPE_UTC_TIME ?
|
||||
'#^(..)(..)(..)(..)(..)(..)?(.*)$#' :
|
||||
'#(....)(..)(..)(..)(..)(..).*([Z+-].*)$#';
|
||||
|
||||
preg_match($pattern, $content, $matches);
|
||||
|
||||
list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches;
|
||||
$format = 'YmdHis';
|
||||
|
||||
if ($tag == self::TYPE_UTC_TIME) {
|
||||
$year = $year >= 50 ? "19$year" : "20$year";
|
||||
}
|
||||
|
||||
if ($timezone == 'Z') {
|
||||
$mktime = 'gmmktime';
|
||||
$timezone = 0;
|
||||
} elseif (preg_match('#([+-])(\d\d)(\d\d)#', $timezone, $matches)) {
|
||||
$mktime = 'gmmktime';
|
||||
$timezone = 60 * $matches[3] + 3600 * $matches[2];
|
||||
if ($matches[1] == '-') {
|
||||
$timezone = -$timezone;
|
||||
// https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=28 says "the seconds
|
||||
// element shall always be present" but none-the-less I've seen X509 certs where it isn't and if the
|
||||
// browsers parse it phpseclib ought to too
|
||||
if (preg_match('#^(\d{10})(Z|[+-]\d{4})$#', $content, $matches)) {
|
||||
$content = $matches[1] . '00' . $matches[2];
|
||||
}
|
||||
} else {
|
||||
$mktime = 'mktime';
|
||||
$timezone = 0;
|
||||
$prefix = substr($content, 0, 2) >= 50 ? '19' : '20';
|
||||
$content = $prefix . $content;
|
||||
} elseif (strpos($content, '.') !== false) {
|
||||
$format.= '.u';
|
||||
}
|
||||
|
||||
return @$mktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year) + $timezone;
|
||||
if ($content[strlen($content) - 1] == 'Z') {
|
||||
$content = substr($content, 0, -1) . '+0000';
|
||||
}
|
||||
|
||||
if (strpos($content, '-') !== false || strpos($content, '+') !== false) {
|
||||
$format.= 'O';
|
||||
}
|
||||
|
||||
// error supression isn't necessary as of PHP 7.0:
|
||||
// http://php.net/manual/en/migration70.other-changes.php
|
||||
return @DateTime::createFromFormat($format, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -35,7 +35,8 @@ use phpseclib\Exception\UnsupportedAlgorithmException;
|
||||
use phpseclib\File\ASN1\Element;
|
||||
use phpseclib\Math\BigInteger;
|
||||
use phpseclib\File\ASN1\Maps;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
|
||||
/**
|
||||
* Pure-PHP X.509 Parser
|
||||
@ -1061,7 +1062,7 @@ class X509
|
||||
}
|
||||
|
||||
if (!isset($date)) {
|
||||
$date = time();
|
||||
$date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
|
||||
}
|
||||
|
||||
$notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore'];
|
||||
@ -1071,8 +1072,8 @@ class X509
|
||||
$notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime'];
|
||||
|
||||
switch (true) {
|
||||
case $date < @strtotime($notBefore):
|
||||
case $date > @strtotime($notAfter):
|
||||
case $date < new DateTime($notBefore, new DateTimeZone(@date_default_timezone_get())):
|
||||
case $date > new DateTime($notAfter, new DateTimeZone(@date_default_timezone_get())):
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2290,7 +2291,11 @@ class X509
|
||||
*/
|
||||
private function timeField($date)
|
||||
{
|
||||
$year = @gmdate("Y", @strtotime($date)); // the same way ASN1.php parses this
|
||||
if ($date instanceof Element) {
|
||||
return $date;
|
||||
}
|
||||
$dateObj = new DateTime($date, new DateTimeZone('GMT'));
|
||||
$year = $dateObj->format('Y'); // the same way ASN1.php parses this
|
||||
if ($year < 2050) {
|
||||
return ['utcTime' => $date];
|
||||
} else {
|
||||
@ -2355,8 +2360,12 @@ class X509
|
||||
return false;
|
||||
}
|
||||
|
||||
$startDate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O');
|
||||
$endDate = !empty($this->endDate) ? $this->endDate : @date('D, d M Y H:i:s O', strtotime('+1 year'));
|
||||
$startDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
|
||||
$startDate = !empty($this->startDate) ? $this->startDate : $startDate->format('D, d M Y H:i:s O');
|
||||
|
||||
$endDate = new DateTime('+1 year', new DateTimeZone(@date_default_timezone_get()));
|
||||
$endDate = !empty($this->endDate) ? $this->endDate : $endDate->format('D, d M Y H:i:s O');
|
||||
|
||||
/* "The serial number MUST be a positive integer"
|
||||
"Conforming CAs MUST NOT use serialNumber values longer than 20 octets."
|
||||
-- https://tools.ietf.org/html/rfc5280#section-4.1.2.2
|
||||
@ -2624,7 +2633,9 @@ class X509
|
||||
|
||||
$currentCert = isset($this->currentCert) ? $this->currentCert : null;
|
||||
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null;
|
||||
$thisUpdate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O');
|
||||
|
||||
$thisUpdate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
|
||||
$thisUpdate = !empty($this->startDate) ? $this->startDate : $thisUpdate->format('D, d M Y H:i:s O');
|
||||
|
||||
if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) {
|
||||
$this->currentCert = $crl->currentCert;
|
||||
@ -2777,7 +2788,11 @@ class X509
|
||||
*/
|
||||
public function setStartDate($date)
|
||||
{
|
||||
$this->startDate = @date('D, d M Y H:i:s O', @strtotime($date));
|
||||
if (!is_object($date) || !is_a($date, 'DateTime')) {
|
||||
$date = new DateTime($date);
|
||||
}
|
||||
|
||||
$this->startDate = $date->format('D, d M Y H:i:s O', new DateTimeZone(@date_default_timezone_get()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2800,7 +2815,11 @@ class X509
|
||||
$temp = chr(ASN1::TYPE_GENERALIZED_TIME) . ASN1::encodeLength(strlen($temp)) . $temp;
|
||||
$this->endDate = new Element($temp);
|
||||
} else {
|
||||
$this->endDate = @date('D, d M Y H:i:s O', @strtotime($date));
|
||||
if (!is_object($date) || !is_a($date, 'DateTime')) {
|
||||
$date = new DateTime($date);
|
||||
}
|
||||
|
||||
$this->endDate = $date->format('D, d M Y H:i:s O', new DateTimeZone(@date_default_timezone_get()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -3530,8 +3549,9 @@ class X509
|
||||
}
|
||||
|
||||
$i = count($rclist);
|
||||
$revocationDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
|
||||
$rclist[] = ['userCertificate' => $serial,
|
||||
'revocationDate' => $this->timeField(@date('D, d M Y H:i:s O'))];
|
||||
'revocationDate' => $this->timeField($revocationDate->format('D, d M Y H:i:s O'))];
|
||||
return $i;
|
||||
}
|
||||
|
||||
|
@ -1969,7 +1969,7 @@ class SFTP extends SSH2
|
||||
|
||||
if (isset($fp)) {
|
||||
$stat = fstat($fp);
|
||||
$size = $stat['size'];
|
||||
$size = !empty($stat) ? $stat['size'] : 0;
|
||||
|
||||
if ($local_start >= 0) {
|
||||
fseek($fp, $local_start);
|
||||
|
@ -103,10 +103,10 @@ class SSH2
|
||||
* @see \phpseclib\Net\SSH2::_get_channel_packet()
|
||||
* @access private
|
||||
*/
|
||||
const CHANNEL_EXEC = 0; // PuTTy uses 0x100
|
||||
const CHANNEL_SHELL = 1;
|
||||
const CHANNEL_SUBSYSTEM = 2;
|
||||
const CHANNEL_AGENT_FORWARD = 3;
|
||||
const CHANNEL_EXEC = 1; // PuTTy uses 0x100
|
||||
const CHANNEL_SHELL = 2;
|
||||
const CHANNEL_SUBSYSTEM = 3;
|
||||
const CHANNEL_AGENT_FORWARD = 4;
|
||||
/**#@-*/
|
||||
|
||||
/**#@+
|
||||
@ -897,6 +897,38 @@ class SSH2
|
||||
*/
|
||||
private $send_kex_first = true;
|
||||
|
||||
/**
|
||||
* Some versions of OpenSSH incorrectly calculate the key size
|
||||
*
|
||||
* @var bool
|
||||
* @access private
|
||||
*/
|
||||
private $bad_key_size_fix = false;
|
||||
|
||||
/**
|
||||
* The selected decryption algorithm
|
||||
*
|
||||
* @var string
|
||||
* @access private
|
||||
*/
|
||||
private $decrypt_algorithm = '';
|
||||
|
||||
/**
|
||||
* Should we try to re-connect to re-establish keys?
|
||||
*
|
||||
* @var bool
|
||||
* @access private
|
||||
*/
|
||||
private $retry_connect = false;
|
||||
|
||||
/**
|
||||
* Binary Packet Buffer
|
||||
*
|
||||
* @var string|false
|
||||
* @access private
|
||||
*/
|
||||
private $binary_packet_buffer = false;
|
||||
|
||||
/**
|
||||
* Default Constructor.
|
||||
*
|
||||
@ -1027,7 +1059,7 @@ class SSH2
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function sendIdentificationStringFirst()
|
||||
public function sendIdentificationStringFirst()
|
||||
{
|
||||
$this->send_id_string_first = true;
|
||||
}
|
||||
@ -1041,7 +1073,7 @@ class SSH2
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function sendIdentificationStringLast()
|
||||
public function sendIdentificationStringLast()
|
||||
{
|
||||
$this->send_id_string_first = false;
|
||||
}
|
||||
@ -1055,7 +1087,7 @@ class SSH2
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function sendKEXINITFirst()
|
||||
public function sendKEXINITFirst()
|
||||
{
|
||||
$this->send_kex_first = true;
|
||||
}
|
||||
@ -1069,7 +1101,7 @@ class SSH2
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function sendKEXINITLast()
|
||||
public function sendKEXINITLast()
|
||||
{
|
||||
$this->send_kex_first = false;
|
||||
}
|
||||
@ -1190,8 +1222,8 @@ class SSH2
|
||||
$this->errors[] = utf8_decode($data);
|
||||
}
|
||||
|
||||
if ($matches[3] != '1.99' && $matches[3] != '2.0') {
|
||||
throw new \RuntimeException("Cannot connect to SSH $matches[1] servers");
|
||||
if (version_compare($matches[3], '1.99', '<')) {
|
||||
throw new \RuntimeException("Cannot connect to SSH $matches[3] servers");
|
||||
}
|
||||
|
||||
if (!$this->send_id_string_first) {
|
||||
@ -1789,6 +1821,8 @@ class SSH2
|
||||
throw new \UnexpectedValueException('Expected SSH_MSG_NEWKEYS');
|
||||
}
|
||||
|
||||
$this->decrypt_algorithm = $decrypt;
|
||||
|
||||
$keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
|
||||
|
||||
$this->encrypt = $this->encryption_algorithm_to_crypt_instance($encrypt);
|
||||
@ -1959,6 +1993,10 @@ class SSH2
|
||||
*/
|
||||
private function encryption_algorithm_to_key_size($algorithm)
|
||||
{
|
||||
if ($this->bad_key_size_fix && $this->bad_algorithm_candidate($algorithm)) {
|
||||
return 16;
|
||||
}
|
||||
|
||||
switch ($algorithm) {
|
||||
case 'none':
|
||||
return 0;
|
||||
@ -2033,6 +2071,27 @@ class SSH2
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests whether or not proposed algorithm has a potential for issues
|
||||
*
|
||||
* @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html
|
||||
* @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291
|
||||
* @param string $algorithm Name of the encryption algorithm
|
||||
* @return bool
|
||||
* @access private
|
||||
*/
|
||||
private function bad_algorithm_candidate($algorithm)
|
||||
{
|
||||
switch ($algorithm) {
|
||||
case 'arcfour256':
|
||||
case 'aes192-ctr':
|
||||
case 'aes256-ctr':
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login
|
||||
*
|
||||
@ -2114,6 +2173,13 @@ class SSH2
|
||||
|
||||
$response = $this->get_binary_packet();
|
||||
if ($response === false) {
|
||||
if ($this->retry_connect) {
|
||||
$this->retry_connect = false;
|
||||
if (!$this->connect()) {
|
||||
return false;
|
||||
}
|
||||
return $this->login_helper($username, $password);
|
||||
}
|
||||
throw new \RuntimeException('Connection closed by server');
|
||||
}
|
||||
|
||||
@ -2676,7 +2742,7 @@ class SSH2
|
||||
return false;
|
||||
}
|
||||
|
||||
$response = $this->get_binary_packet();
|
||||
$response = $this->get_binary_packet(true);
|
||||
if ($response === false) {
|
||||
throw new \RuntimeException('Connection closed by server');
|
||||
}
|
||||
@ -3130,6 +3196,24 @@ class SSH2
|
||||
return (bool) ($this->bitmap & self::MASK_LOGIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets a connection for re-use
|
||||
*
|
||||
* @param int $reason
|
||||
* @access private
|
||||
*/
|
||||
private function reset_connection($reason)
|
||||
{
|
||||
$this->disconnect_helper($reason);
|
||||
$this->decrypt = $this->encrypt = false;
|
||||
$this->decrypt_block_size = $this->encrypt_block_size = 8;
|
||||
$this->hmac_check = $this->hmac_create = false;
|
||||
$this->hmac_size = false;
|
||||
$this->session_id = false;
|
||||
$this->retry_connect = true;
|
||||
$this->get_seq_no = $this->send_seq_no = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Binary Packets
|
||||
*
|
||||
@ -3140,7 +3224,7 @@ class SSH2
|
||||
* @throws \RuntimeException on connection errors
|
||||
* @access private
|
||||
*/
|
||||
private function get_binary_packet()
|
||||
private function get_binary_packet($filter_channel_packets = false)
|
||||
{
|
||||
if (!is_resource($this->fsock) || feof($this->fsock)) {
|
||||
$this->bitmap = 0;
|
||||
@ -3169,6 +3253,11 @@ class SSH2
|
||||
// "implementations SHOULD check that the packet length is reasonable"
|
||||
// PuTTY uses 0x9000 as the actual max packet size and so to shall we
|
||||
if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
|
||||
if (!$this->bad_key_size_fix && $this->bad_algorithm_candidate($this->decrypt_algorithm) && !($this->bitmap & SSH2::MASK_LOGIN)) {
|
||||
$this->bad_key_size_fix = true;
|
||||
$this->reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
|
||||
return false;
|
||||
}
|
||||
throw new \RuntimeException('Invalid size');
|
||||
}
|
||||
|
||||
@ -3182,6 +3271,7 @@ class SSH2
|
||||
$buffer.= $temp;
|
||||
$remaining_length-= strlen($temp);
|
||||
}
|
||||
|
||||
$stop = microtime(true);
|
||||
if (strlen($buffer)) {
|
||||
$raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
|
||||
@ -3215,7 +3305,7 @@ class SSH2
|
||||
$this->last_packet = $current;
|
||||
}
|
||||
|
||||
return $this->filter($payload);
|
||||
return $this->filter($payload, $filter_channel_packets);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3227,7 +3317,7 @@ class SSH2
|
||||
* @return string
|
||||
* @access private
|
||||
*/
|
||||
private function filter($payload)
|
||||
private function filter($payload, $filter_channel_packets)
|
||||
{
|
||||
switch (ord($payload[0])) {
|
||||
case NET_SSH2_MSG_DISCONNECT:
|
||||
@ -3277,6 +3367,17 @@ class SSH2
|
||||
// only called when we've already logged in
|
||||
if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) {
|
||||
switch (ord($payload[0])) {
|
||||
case NET_SSH2_MSG_CHANNEL_DATA:
|
||||
case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
|
||||
case NET_SSH2_MSG_CHANNEL_REQUEST:
|
||||
case NET_SSH2_MSG_CHANNEL_CLOSE:
|
||||
case NET_SSH2_MSG_CHANNEL_EOF:
|
||||
if ($filter_channel_packets) {
|
||||
$this->binary_packet_buffer = $payload;
|
||||
$this->get_channel_packet(true);
|
||||
$payload = $this->get_binary_packet(true);
|
||||
}
|
||||
break;
|
||||
case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
|
||||
if (strlen($payload) < 4) {
|
||||
return false;
|
||||
@ -3461,31 +3562,37 @@ class SSH2
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if ($this->curTimeout) {
|
||||
if ($this->curTimeout < 0) {
|
||||
$this->is_timeout = true;
|
||||
return true;
|
||||
if ($this->binary_packet_buffer !== false) {
|
||||
$response = $this->binary_packet_buffer;
|
||||
$this->binary_packet_buffer = false;
|
||||
} else {
|
||||
if ($this->curTimeout) {
|
||||
if ($this->curTimeout < 0) {
|
||||
$this->is_timeout = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
$read = [$this->fsock];
|
||||
$write = $except = null;
|
||||
|
||||
$start = microtime(true);
|
||||
$sec = floor($this->curTimeout);
|
||||
$usec = 1000000 * ($this->curTimeout - $sec);
|
||||
// on windows this returns a "Warning: Invalid CRT parameters detected" error
|
||||
if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
|
||||
$this->is_timeout = true;
|
||||
return true;
|
||||
}
|
||||
$elapsed = microtime(true) - $start;
|
||||
$this->curTimeout-= $elapsed;
|
||||
}
|
||||
|
||||
$read = [$this->fsock];
|
||||
$write = $except = null;
|
||||
|
||||
$start = microtime(true);
|
||||
$sec = floor($this->curTimeout);
|
||||
$usec = 1000000 * ($this->curTimeout - $sec);
|
||||
// on windows this returns a "Warning: Invalid CRT parameters detected" error
|
||||
if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
|
||||
$this->is_timeout = true;
|
||||
return true;
|
||||
$response = $this->get_binary_packet();
|
||||
if ($response === false) {
|
||||
throw new \RuntimeException('Connection closed by server');
|
||||
}
|
||||
$elapsed = microtime(true) - $start;
|
||||
$this->curTimeout-= $elapsed;
|
||||
}
|
||||
|
||||
$response = $this->get_binary_packet();
|
||||
if ($response === false) {
|
||||
throw new \RuntimeException('Connection closed by server');
|
||||
}
|
||||
if ($client_channel == -1 && $response === true) {
|
||||
return true;
|
||||
}
|
||||
@ -3805,7 +3912,7 @@ class SSH2
|
||||
@flush();
|
||||
@ob_flush();
|
||||
break;
|
||||
// basically the same thing as self::LOG_REALTIME with the caveat that NET_SFTP_LOG_REALTIME_FILENAME
|
||||
// basically the same thing as self::LOG_REALTIME with the caveat that NET_SSH2_LOG_REALTIME_FILENAME
|
||||
// needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE.
|
||||
// the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
|
||||
// at the beginning of the file
|
||||
|
@ -148,5 +148,25 @@ class Functional_Net_SSH2Test extends PhpseclibFunctionalTestCase
|
||||
$ssh->exec('ls -latr');
|
||||
$ssh->disablePTY();
|
||||
$ssh->exec('pwd');
|
||||
|
||||
return $ssh;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testDisablePTY
|
||||
* @group github1167
|
||||
*/
|
||||
public function testChannelDataAfterOpen($ssh)
|
||||
{
|
||||
$ssh->write("ping 127.0.0.1\n");
|
||||
|
||||
$ssh->enablePTY();
|
||||
$ssh->exec('bash');
|
||||
|
||||
$ssh->write("ls -latr\n");
|
||||
|
||||
$ssh->setTimeout(1);
|
||||
|
||||
$ssh->read();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user