Skip to content

Commit 72d70dc

Browse files
authored
Merge pull request clue#2 from clue-labs/fds
Do not inherit open FDs to SSH child process
2 parents dddf768 + eed436b commit 72d70dc

File tree

2 files changed

+49
-4
lines changed

2 files changed

+49
-4
lines changed

src/SshProcessConnector.php

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,27 @@ public function connect($uri)
6666
return \React\Promise\reject(new \InvalidArgumentException('Invalid target URI'));
6767
}
6868

69-
$process = new Process($this->cmd . ' -W ' . \escapeshellarg($parts['host'] . ':' . $parts['port']));
69+
$command = $this->cmd . ' -W ' . \escapeshellarg($parts['host'] . ':' . $parts['port']);
70+
71+
// try to get list of all open FDs (Linux only) or simply assume range 3-1024 (FD_SETSIZE)
72+
$fds = @scandir('/proc/self/fd');
73+
if ($fds === false) {
74+
$fds = range(3, 1024); // @codeCoverageIgnore
75+
}
76+
77+
// do not inherit open FDs by explicitly closing all of them
78+
foreach ($fds as $fd) {
79+
if ($fd > 2) {
80+
$command .= ' ' . $fd . '>&-';
81+
}
82+
}
83+
84+
// default `sh` only accepts single-digit FDs, so run in bash if needed
85+
if ($fds && max($fds) > 9) {
86+
$command = 'exec bash -c ' . escapeshellarg($command);
87+
}
88+
89+
$process = new Process($command);
7090
$process->start($this->loop);
7191

7292
$deferred = new Deferred(function () use ($process, $uri) {
@@ -110,9 +130,6 @@ public function connect($uri)
110130
}
111131

112132
$connection = new CompositeConnection($process->stdout, $process->stdin);
113-
$connection->on('close', function() use ($process) {
114-
//$process->terminate();
115-
});
116133
$deferred->resolve($connection);
117134
});
118135

tests/FunctionalSshProcessConnectorTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,32 @@ public function testConnectValidTargetWillReturnPromiseWhichResolvesToConnection
6868
$this->assertTrue($connection->isWritable());
6969
$connection->close();
7070
}
71+
72+
public function testConnectValidTargetWillNotInheritActiveFileDescriptors()
73+
{
74+
$server = stream_socket_server('tcp://127.0.0.1:0');
75+
$address = stream_socket_get_name($server, false);
76+
77+
// ensure that we can not listen on the same address twice
78+
$copy = @stream_socket_server('tcp://' . $address);
79+
if ($copy !== false) {
80+
fclose($server);
81+
fclose($copy);
82+
83+
$this->markTestSkipped('Platform does not prevent binding to same address (Windows?)');
84+
}
85+
86+
// wait for successful connection
87+
$promise = $this->connector->connect('example.com:80');
88+
$connection = \Clue\React\Block\await($promise, $this->loop, self::TIMEOUT);
89+
90+
// close server and ensure we can start a new server on the previous address
91+
// the open SSH connection process should not inherit the existing server socket
92+
fclose($server);
93+
$server = stream_socket_server('tcp://' . $address);
94+
$this->assertTrue(is_resource($server));
95+
96+
fclose($server);
97+
$connection->close();
98+
}
7199
}

0 commit comments

Comments
 (0)