Skip to content

Server with Transport Layer Security. #35

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

Closed
wants to merge 2 commits into from
Closed
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
57 changes: 57 additions & 0 deletions src/SecureConnection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace React\Socket;

/**
* A connection supporting transport layer security.
*/
class SecureConnection extends Connection
{
protected $isSecure = false;
protected $protocolNumber;

public function handleData($stream)
{
if (! $this->isSecure) {
$enabled = stream_socket_enable_crypto($stream, true, $this->protocolNumber);
if ($enabled === false) {
$this
->err('Failed to complete a secure handshake with the client.')
->end()
;
return;
} elseif ($enabled === 0) {
return;
}
$this->isSecure = true;
$this->emit('connection', array($this));
}

$data = fread($stream, $this->bufferSize);

if ('' !== $data && false !== $data) {
$this->emit('data', array($data, $this));
}

if (false === $data || !is_resource($stream) || feof($stream)) {
$this->end();
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the first read after enabling crypto seems always to be empty: so ending on an empty read will prevent a subsequent non-empty read.

}

/**
* Set the STREAM_CRYPTO_METHOD_*_SERVER flags suitable for enabling TLS on
* the socket stream handled by this connection.
*
* @param int $protocolNumber
*/
public function setProtocol($protocolNumber = null)
{
$this->protocolNumber = $protocolNumber;
}

private function err($message)
{
$this->emit('error', array(new \RuntimeException($message)));
return $this;
}
}
69 changes: 66 additions & 3 deletions src/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,26 @@ public function __construct(LoopInterface $loop)
$this->loop = $loop;
}

public function listen($port, $host = '127.0.0.1')
public function listen($port, $host = '127.0.0.1', $streamContext = array())
{
if (isset($streamContext['ssl']) && PHP_VERSION_ID < 50600) {
throw new \RuntimeException(
'Secure connections are not available before PHP 5.6.0'
Copy link
Author

@boite boite May 12, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not strictly true, but I haven't been able to get it working with PHP 5.5.x using a simple HTTP server (with a select loop) and web browser. The problem seems to be that select() will permit a read of the first character (The 'G' in 'GET / HTTP/1.1') and then no more; so the http server doesn't send a response. Works fine with wget though. More work needed to work out what's going wrong.

);
}

if (strpos($host, ':') !== false) {
// enclose IPv6 addresses in square brackets before appending port
$host = '[' . $host . ']';
}

$this->master = @stream_socket_server("tcp://$host:$port", $errno, $errstr);
$this->master = @stream_socket_server(
"tcp://$host:$port",
$errno,
$errstr,
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,
stream_context_create($streamContext)
);
if (false === $this->master) {
$message = "Could not bind to tcp://$host:$port: $errstr";
throw new ConnectionException($message, $errno);
Expand Down Expand Up @@ -68,6 +80,57 @@ public function shutdown()

public function createConnection($socket)
{
return new Connection($socket, $this->loop);
$connection = null;
$context = stream_context_get_options($socket);

if (! isset($context['ssl'])) {
$connection = new Connection($socket, $this->loop);
} else {
$connection = new SecureConnection($socket, $this->loop);
$connection->setProtocol($this->getSecureProtocolNumber($context));
$scope = $this;
$connection->on('connection', function ($dataConn) use ($scope) {
$scope->emit('connection', array($dataConn));
});
}

return $connection;
}

/**
* Get the STREAM_CRYPTO_METHOD_*_SERVER flags suitable for enabling TLS
* on a server socket.
*
* Used the supplied $streamContext['ssl']['crypto_method'] or a default set
* which will support as many SSL/TLS protocols as possible.
*
* @param array $streamContext
*
* @return int
*/
public function getSecureProtocolNumber($streamContext)
{
if (isset($streamContext['ssl']['crypto_method'])) {
return $streamContext['ssl']['crypto_method'];
} elseif (defined('STREAM_CRYPTO_METHOD_ANY_SERVER')) {
return constant('STREAM_CRYPTO_METHOD_ANY_SERVER');
}
$protoNum = 0;
if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_SERVER')) {
$protoNum |= constant('STREAM_CRYPTO_METHOD_TLSv1_2_SERVER');
}
if (defined('STREAM_CRYPTO_METHOD_TLSv1_1_SERVER')) {
$protoNum |= constant('STREAM_CRYPTO_METHOD_TLSv1_1_SERVER');
}
if (defined('STREAM_CRYPTO_METHOD_TLSv1_0_SERVER')) {
$protoNum |= constant('STREAM_CRYPTO_METHOD_TLSv1_0_SERVER');
}
if (defined('STREAM_CRYPTO_METHOD_SSLv3_SERVER')) {
$protoNum |= constant('STREAM_CRYPTO_METHOD_SSLv3_SERVER');
}
if (defined('STREAM_CRYPTO_METHOD_SSLv2_SERVER')) {
$protoNum |= constant('STREAM_CRYPTO_METHOD_SSLv2_SERVER');
}
return $protoNum;
}
}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy