Add DISCONNECT bitmap mask and use to prevent looping potential while disconnecting. Do not attempt disconnect packet send on closed socket.

This commit is contained in:
Robert 2024-08-14 10:32:36 -04:00
parent 621c73f7dc
commit c82594e222
2 changed files with 50 additions and 1 deletions

View File

@ -104,6 +104,7 @@ class SSH2
const MASK_LOGIN_REQ = 0x00000004;
const MASK_LOGIN = 0x00000008;
const MASK_SHELL = 0x00000010;
const MASK_DISCONNECT = 0x00000020;
/*
* Channel constants
@ -4593,7 +4594,12 @@ class SSH2
*/
protected function disconnect_helper($reason)
{
if ($this->bitmap & self::MASK_CONNECTED) {
if ($this->bitmap & self::MASK_DISCONNECT) {
// Disregard subsequent disconnect requests
return false;
}
$this->bitmap |= self::MASK_DISCONNECT;
if ($this->isConnected()) {
$data = Strings::packSSH2('CNss', NET_SSH2_MSG_DISCONNECT, $reason, '', '');
try {
$this->send_binary_packet($data);

View File

@ -33,6 +33,24 @@ class SSH2UnitTest extends PhpseclibTestCase
];
}
/**
* @requires PHPUnit < 10
* Verify that MASK_* constants remain distinct
*/
public function testBitmapMasks()
{
$reflection = new \ReflectionClass(SSH2::class);
$masks = array_filter($reflection->getConstants(), function ($k) {
return strpos($k, 'MASK_') === 0;
}, ARRAY_FILTER_USE_KEY);
$bitmap = 0;
foreach ($masks as $mask => $bit) {
$this->assertEquals(0, $bitmap & $bit, "Got unexpected mask {$mask}");
$bitmap |= $bit;
$this->assertEquals($bit, $bitmap & $bit, "Absent expected mask {$mask}");
}
}
/**
* @dataProvider formatLogDataProvider
* @requires PHPUnit < 10
@ -384,6 +402,31 @@ class SSH2UnitTest extends PhpseclibTestCase
self::callFunc($ssh, 'send_channel_packet', [1, 'hello world']);
}
/**
* @requires PHPUnit < 10
*/
public function testDisconnectHelper()
{
$ssh = $this->getMockBuilder('phpseclib3\Net\SSH2')
->disableOriginalConstructor()
->setMethods(['__destruct', 'isConnected', 'send_binary_packet'])
->getMock();
$ssh->expects($this->once())
->method('isConnected')
->willReturn(true);
$ssh->expects($this->once())
->method('send_binary_packet')
->with($this->isType('string'))
->willReturnCallback(function () use ($ssh) {
self::callFunc($ssh, 'disconnect_helper', [1]);
throw new \Exception('catch me');
});
$this->assertEquals(0, self::getVar($ssh, 'bitmap'));
self::callFunc($ssh, 'disconnect_helper', [1]);
$this->assertEquals(0, self::getVar($ssh, 'bitmap'));
}
/**
* @return SSH2
*/