Skip to content

PHP 5.6+ uses new SSL/TLS context options #61

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 8, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ $tcpConnector->create('127.0.0.1', 80)->then(function (React\Stream\Stream $stre
$loop->run();
```

You can optionally pass additional
[socket context options](http://php.net/manual/en/context.socket.php)
to the constructor like this:

```php
$tcpConnector = new React\SocketClient\TcpConnector($loop, array(
'bindto' => '192.168.0.1:0'
));
```

Note that this class only allows you to connect to IP/port combinations.
If you want to connect to hostname/port combinations, see also the following chapter.

Expand Down Expand Up @@ -102,6 +112,17 @@ $secureConnector->create('www.google.com', 443)->then(function (React\Stream\Str
$loop->run();
```

You can optionally pass additional
[SSL context options](http://php.net/manual/en/context.ssl.php)
to the constructor like this:

```php
$secureConnector = new React\SocketClient\SecureConnector($dnsConnector, $loop, array(
'verify_peer' => false,
'verify_peer_name' => false
));
```

### Unix domain sockets

Similarly, the `UnixConnector` class can be used to connect to Unix domain socket (UDS)
Expand Down
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,8 @@
"branch-alias": {
"dev-master": "0.4-dev"
}
},
"require-dev": {
"clue/block-react": "~1.0"
}
}
26 changes: 20 additions & 6 deletions src/SecureConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ class SecureConnector implements ConnectorInterface
{
private $connector;
private $streamEncryption;
private $context;

public function __construct(ConnectorInterface $connector, LoopInterface $loop)
public function __construct(ConnectorInterface $connector, LoopInterface $loop, array $context = array())
{
$this->connector = $connector;
$this->streamEncryption = new StreamEncryption($loop);
$this->context = $context;
}

public function create($host, $port)
Expand All @@ -23,14 +25,26 @@ public function create($host, $port)
return Promise\reject(new \BadMethodCallException('Encryption not supported on your platform (HHVM < 3.8?)'));
}

return $this->connector->create($host, $port)->then(function (Stream $stream) use ($host) {
$context = $this->context + array(
'SNI_enabled' => true,
'peer_name' => $host
);

// legacy PHP < 5.6 ignores peer_name and requires legacy context options instead
if (PHP_VERSION_ID < 50600) {
$context += array(
'SNI_server_name' => $host,
'CN_match' => $host
);
}

return $this->connector->create($host, $port)->then(function (Stream $stream) use ($context) {
// (unencrypted) TCP/IP connection succeeded

// set required SSL/TLS context options
$resource = $stream->stream;
stream_context_set_option($resource, 'ssl', 'SNI_enabled', true);
stream_context_set_option($resource, 'ssl', 'SNI_server_name', $host);
stream_context_set_option($resource, 'ssl', 'peer_name', $host);
foreach ($context as $name => $value) {
stream_context_set_option($stream->stream, 'ssl', $name, $value);
}

// try to enable encryption
return $this->streamEncryption->enable($stream)->then(null, function ($error) use ($stream) {
Expand Down
13 changes: 11 additions & 2 deletions src/TcpConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
class TcpConnector implements ConnectorInterface
{
private $loop;
private $context;

public function __construct(LoopInterface $loop)
public function __construct(LoopInterface $loop, array $context = array())
{
$this->loop = $loop;
$this->context = $context;
}

public function create($ip, $port)
Expand All @@ -25,7 +27,14 @@ public function create($ip, $port)

$url = $this->getSocketUrl($ip, $port);

$socket = @stream_socket_client($url, $errno, $errstr, 0, STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT);
$socket = @stream_socket_client(
$url,
$errno,
$errstr,
0,
STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT,
stream_context_create(array('socket' => $this->context))
);

if (false === $socket) {
return Promise\reject(new \RuntimeException(
Expand Down
50 changes: 50 additions & 0 deletions tests/IntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use React\SocketClient\Connector;
use React\SocketClient\SecureConnector;
use React\Stream\BufferedSink;
use Clue\React\Block;

class IntegrationTest extends TestCase
{
Expand Down Expand Up @@ -73,4 +74,53 @@ public function gettingEncryptedStuffFromGoogleShouldWork()
$this->assertTrue($connected);
$this->assertRegExp('#^HTTP/1\.0#', $response);
}

/** @test */
public function testSelfSignedRejectsIfVerificationIsEnabled()
{
if (defined('HHVM_VERSION')) {
$this->markTestSkipped('Not supported on HHVM');
}

$loop = new StreamSelectLoop();

$factory = new Factory();
$dns = $factory->create('8.8.8.8', $loop);


$secureConnector = new SecureConnector(
new Connector($loop, $dns),
$loop,
array(
'verify_peer' => true
)
);

$this->setExpectedException('RuntimeException');
Block\await($secureConnector->create('self-signed.badssl.com', 443), $loop);
}

/** @test */
public function testSelfSignedResolvesIfVerificationIsDisabled()
{
if (defined('HHVM_VERSION')) {
$this->markTestSkipped('Not supported on HHVM');
}

$loop = new StreamSelectLoop();

$factory = new Factory();
$dns = $factory->create('8.8.8.8', $loop);

$secureConnector = new SecureConnector(
new Connector($loop, $dns),
$loop,
array(
'verify_peer' => false
)
);

$conn = Block\await($secureConnector->create('self-signed.badssl.com', 443), $loop);
$conn->close();
}
}