Skip to content

Commit

Permalink
Add a test for fragmented SSL packets
Browse files Browse the repository at this point in the history
  • Loading branch information
valga authored and bukka committed Jan 25, 2019
1 parent 08c5679 commit 0c84c2e
Show file tree
Hide file tree
Showing 2 changed files with 216 additions and 0 deletions.
117 changes: 117 additions & 0 deletions ext/openssl/tests/ServerClientProxyTestCase.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

const WORKER_ARGV_VALUE = 'RUN_WORKER';

function phpt_notify($worker = null)
{
ServerClientProxyTestCase::getInstance()->notify($worker);
}

function phpt_wait($worker = null)
{
ServerClientProxyTestCase::getInstance()->wait($worker);
}

/**
* This is a singleton to let the wait/notify functions work
* I know it's horrible, but it's a means to an end
*/
class ServerClientProxyTestCase
{
private $isWorker = false;

private $workerHandles = [];

private $workerStdIn = [];

private $workerStdOut = [];

private static $instance;

public static function getInstance($isWorker = false)
{
if (!isset(self::$instance)) {
self::$instance = new self($isWorker);
}

return self::$instance;
}

public function __construct($isWorker = false)
{
if (!isset(self::$instance)) {
self::$instance = $this;
}

$this->isWorker = $isWorker;
}

private function spawnWorkerProcess($worker, $code)
{
if (defined("PHP_WINDOWS_VERSION_MAJOR")) {
$ini = php_ini_loaded_file();
$cmd = sprintf('%s %s "%s" %s', PHP_BINARY, $ini ? "-n -c $ini" : "", __FILE__, WORKER_ARGV_VALUE);
} else {
$cmd = sprintf('%s "%s" %s %s', PHP_BINARY, __FILE__, WORKER_ARGV_VALUE, $worker);
}
$this->workerHandle[$worker] = proc_open($cmd, [['pipe', 'r'], ['pipe', 'w'], STDERR], $pipes);
$this->workerStdIn[$worker] = $pipes[0];
$this->workerStdOut[$worker] = $pipes[1];

fwrite($this->workerStdIn[$worker], $code . "\n---\n");
}

private function cleanupWorkerProcess($worker)
{
fclose($this->workerStdIn[$worker]);
fclose($this->workerStdOut[$worker]);
proc_close($this->workerHandle[$worker]);
}

private function stripPhpTagsFromCode($code)
{
return preg_replace('/^\s*<\?(?:php)?|\?>\s*$/i', '', $code);
}

public function runWorker()
{
$code = '';

while (1) {
$line = fgets(STDIN);

if (trim($line) === "---") {
break;
}

$code .= $line;
}

eval($code);
}

public function run($testCode, array $workerCodes)
{
foreach ($workerCodes as $worker => $code) {
$this->spawnWorkerProcess($worker, $this->stripPhpTagsFromCode($code));
}
eval($this->stripPhpTagsFromCode($testCode));
foreach ($workerCodes as $worker => $code) {
$this->cleanupWorkerProcess($worker);
}
}

public function wait($worker)
{
fgets($this->isWorker ? STDIN : $this->workerStdOut[$worker]);
}

public function notify($worker)
{
fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "\n");
}
}

if (isset($argv[1]) && $argv[1] === WORKER_ARGV_VALUE) {
ServerClientProxyTestCase::getInstance(true)->runWorker();
}
99 changes: 99 additions & 0 deletions ext/openssl/tests/non_blocking_eof.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
--TEST--
php_stream_eof() should not block on SSL non-blocking streams when packets are fragmented
--SKIPIF--
<?php
if (!extension_loaded("openssl")) die("skip openssl not loaded");
if (!function_exists("proc_open")) die("skip no proc_open");
?>
--FILE--
<?php

$clientCode = <<<'CODE'
$context = stream_context_create(['ssl' => ['verify_peer' => false, 'peer_name' => 'bug54992.local']]);
phpt_wait('server');
phpt_notify('proxy');
phpt_wait('proxy');
$fp = stream_socket_client("ssl://127.0.0.1:10012", $errornum, $errorstr, 3000, STREAM_CLIENT_CONNECT, $context);
stream_set_blocking($fp, false);
$read = [$fp];
$buf = '';
while (stream_select($read, $write, $except, 1000)) {
$chunk = stream_get_contents($fp, 4096);
var_dump($chunk);
$buf .= $chunk;
if ($buf === 'hello, world') {
break;
}
}
phpt_notify('server');
phpt_notify('proxy');
CODE;

$serverCode = <<<'CODE'
$context = stream_context_create(['ssl' => ['local_cert' => __DIR__ . '/bug54992.pem']]);
$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
$fp = stream_socket_server("ssl://127.0.0.1:10011", $errornum, $errorstr, $flags, $context);
phpt_notify();
$conn = stream_socket_accept($fp);
fwrite($conn, 'hello, world');
phpt_wait();
fclose($conn);
CODE;

$proxyCode = <<<'CODE'
phpt_wait();
$upstream = stream_socket_client("tcp://127.0.0.1:10011", $errornum, $errorstr, 3000, STREAM_CLIENT_CONNECT);
stream_set_blocking($upstream, false);
$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
$server = stream_socket_server("tcp://127.0.0.1:10012", $errornum, $errorstr, $flags);
phpt_notify();
$conn = stream_socket_accept($server);
stream_set_blocking($conn, false);
$read = [$upstream, $conn];
while (stream_select($read, $write, $except, 1)) {
foreach ($read as $fp) {
$data = stream_get_contents($fp);
if ($fp === $conn) {
fwrite($upstream, $data);
} else {
if ($data !== '' && $data[0] === chr(23)) {
$parts = str_split($data, (int) ceil(strlen($data) / 3));
foreach ($parts as $part) {
fwrite($conn, $part);
usleep(1000);
}
} else {
fwrite($conn, $data);
}
}
}
if (feof($upstream)) {
break;
}
$read = [$upstream, $conn];
}
phpt_wait();
CODE;

include 'ServerClientProxyTestCase.inc';
ServerClientProxyTestCase::getInstance()->run($clientCode, [
'server' => $serverCode,
'proxy' => $proxyCode,
]);
?>
--EXPECT--
string(0) ""
string(0) ""
string(12) "hello, world"

0 comments on commit 0c84c2e

Please sign in to comment.