blob: 31b147012668606a1e41ee1d5a102fa107533d72 [file] [log] [blame]
/*
* Copyright (C) 2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "TCPServer.h"
#include <netinet/in.h>
#include <thread>
#include <unistd.h>
#include <wtf/Optional.h>
extern "C" {
struct BIO;
struct X509;
struct SSL_CTX;
struct EVP_PKEY;
struct SSL_METHOD;
struct pem_password_cb;
int BIO_free(BIO*);
int SSL_free(SSL*);
int X509_free(X509*);
int SSL_CTX_free(SSL_CTX*);
int EVP_PKEY_free(EVP_PKEY*);
int SSL_library_init();
const SSL_METHOD* SSLv23_server_method();
BIO* BIO_new_mem_buf(const void*, int);
X509* PEM_read_bio_X509(BIO*, X509**, pem_password_cb*, void*);
EVP_PKEY* PEM_read_bio_PrivateKey(BIO*, EVP_PKEY**, pem_password_cb*, void*);
SSL_CTX* SSL_CTX_new(const SSL_METHOD*);
const SSL_METHOD* SSLv23_server_method();
int SSL_CTX_use_certificate(SSL_CTX*, X509*);
int SSL_CTX_use_PrivateKey(SSL_CTX*, EVP_PKEY*);
SSL* SSL_new(SSL_CTX*);
int SSL_accept(SSL*);
int SSL_set_fd(SSL*, int);
} // extern "C"
namespace TestWebKitAPI {
template<typename> struct deleter;
template<> struct deleter<BIO> {
void operator()(BIO* bio)
{
BIO_free(bio);
}
};
template<> struct deleter<SSL> {
void operator()(SSL* ssl)
{
SSL_free(ssl);
}
};
template<> struct deleter<X509> {
void operator()(X509* x509)
{
X509_free(x509);
}
};
template<> struct deleter<SSL_CTX> {
void operator()(SSL_CTX* ctx)
{
SSL_CTX_free(ctx);
}
};
template<> struct deleter<EVP_PKEY> {
void operator()(EVP_PKEY* key)
{
EVP_PKEY_free(key);
}
};
TCPServer::TCPServer(Function<void(Socket)>&& connectionHandler, size_t connections)
: m_connectionHandler(WTFMove(connectionHandler))
{
listenForConnections(connections);
}
TCPServer::TCPServer(Protocol protocol, Function<void(SSL*)>&& secureConnectionHandler)
{
auto startSecureConnection = [secureConnectionHandler = WTFMove(secureConnectionHandler)] (Socket socket) {
SSL_library_init();
std::unique_ptr<SSL_CTX, deleter<SSL_CTX>> ctx(SSL_CTX_new(SSLv23_server_method()));
// This is a test certificate from BoringSSL.
char kCertPEM[] =
"-----BEGIN CERTIFICATE-----\n"
"MIICWDCCAcGgAwIBAgIJAPuwTC6rEJsMMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV\n"
"BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\n"
"aWRnaXRzIFB0eSBMdGQwHhcNMTQwNDIzMjA1MDQwWhcNMTcwNDIyMjA1MDQwWjBF\n"
"MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\n"
"ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB\n"
"gQDYK8imMuRi/03z0K1Zi0WnvfFHvwlYeyK9Na6XJYaUoIDAtB92kWdGMdAQhLci\n"
"HnAjkXLI6W15OoV3gA/ElRZ1xUpxTMhjP6PyY5wqT5r6y8FxbiiFKKAnHmUcrgfV\n"
"W28tQ+0rkLGMryRtrukXOgXBv7gcrmU7G1jC2a7WqmeI8QIDAQABo1AwTjAdBgNV\n"
"HQ4EFgQUi3XVrMsIvg4fZbf6Vr5sp3Xaha8wHwYDVR0jBBgwFoAUi3XVrMsIvg4f\n"
"Zbf6Vr5sp3Xaha8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQA76Hht\n"
"ldY9avcTGSwbwoiuIqv0jTL1fHFnzy3RHMLDh+Lpvolc5DSrSJHCP5WuK0eeJXhr\n"
"T5oQpHL9z/cCDLAKCKRa4uV0fhEdOWBqyR9p8y5jJtye72t6CuFUV5iqcpF4BH4f\n"
"j2VNHwsSrJwkD4QUGlUtH7vwnQmyCFxZMmWAJg==\n"
"-----END CERTIFICATE-----\n";
std::unique_ptr<BIO, deleter<BIO>> certBIO(BIO_new_mem_buf(kCertPEM, strlen(kCertPEM)));
std::unique_ptr<X509, deleter<X509>> certX509(PEM_read_bio_X509(certBIO.get(), nullptr, nullptr, nullptr));
ASSERT(certX509);
SSL_CTX_use_certificate(ctx.get(), certX509.get());
// This is a test key from BoringSSL.
char kKeyPEM[] =
"-----BEGIN RSA PRIVATE KEY-----\n"
"MIICXgIBAAKBgQDYK8imMuRi/03z0K1Zi0WnvfFHvwlYeyK9Na6XJYaUoIDAtB92\n"
"kWdGMdAQhLciHnAjkXLI6W15OoV3gA/ElRZ1xUpxTMhjP6PyY5wqT5r6y8FxbiiF\n"
"KKAnHmUcrgfVW28tQ+0rkLGMryRtrukXOgXBv7gcrmU7G1jC2a7WqmeI8QIDAQAB\n"
"AoGBAIBy09Fd4DOq/Ijp8HeKuCMKTHqTW1xGHshLQ6jwVV2vWZIn9aIgmDsvkjCe\n"
"i6ssZvnbjVcwzSoByhjN8ZCf/i15HECWDFFh6gt0P5z0MnChwzZmvatV/FXCT0j+\n"
"WmGNB/gkehKjGXLLcjTb6dRYVJSCZhVuOLLcbWIV10gggJQBAkEA8S8sGe4ezyyZ\n"
"m4e9r95g6s43kPqtj5rewTsUxt+2n4eVodD+ZUlCULWVNAFLkYRTBCASlSrm9Xhj\n"
"QpmWAHJUkQJBAOVzQdFUaewLtdOJoPCtpYoY1zd22eae8TQEmpGOR11L6kbxLQsk\n"
"aMly/DOnOaa82tqAGTdqDEZgSNmCeKKknmECQAvpnY8GUOVAubGR6c+W90iBuQLj\n"
"LtFp/9ihd2w/PoDwrHZaoUYVcT4VSfJQog/k7kjE4MYXYWL8eEKg3WTWQNECQQDk\n"
"104Wi91Umd1PzF0ijd2jXOERJU1wEKe6XLkYYNHWQAe5l4J4MWj9OdxFXAxIuuR/\n"
"tfDwbqkta4xcux67//khAkEAvvRXLHTaa6VFzTaiiO8SaFsHV3lQyXOtMrBpB5jd\n"
"moZWgjHvB2W9Ckn7sDqsPB+U2tyX0joDdQEyuiMECDY8oQ==\n"
"-----END RSA PRIVATE KEY-----\n";
std::unique_ptr<BIO, deleter<BIO>> privateKeyBIO(BIO_new_mem_buf(kKeyPEM, strlen(kKeyPEM)));
std::unique_ptr<EVP_PKEY, deleter<EVP_PKEY>> privateKey(PEM_read_bio_PrivateKey(privateKeyBIO.get(), nullptr, nullptr, nullptr));
ASSERT(privateKey);
SSL_CTX_use_PrivateKey(ctx.get(), privateKey.get());
std::unique_ptr<SSL, deleter<SSL>> ssl(SSL_new(ctx.get()));
ASSERT(ssl);
SSL_set_fd(ssl.get(), socket);
auto acceptResult = SSL_accept(ssl.get());
ASSERT_UNUSED(acceptResult, acceptResult > 0);
secureConnectionHandler(ssl.get());
};
switch (protocol) {
case Protocol::HTTPS:
m_connectionHandler = WTFMove(startSecureConnection);
break;
case Protocol::HTTPSProxy:
m_connectionHandler = [startSecureConnection = WTFMove(startSecureConnection)] (Socket socket) {
char readBuffer[1000];
auto bytesRead = ::read(socket, readBuffer, sizeof(readBuffer));
EXPECT_GT(bytesRead, 0);
EXPECT_TRUE(static_cast<size_t>(bytesRead) < sizeof(readBuffer));
const char* responseHeader = ""
"HTTP/1.1 200 Connection Established\r\n"
"Connection: close\r\n\r\n";
auto bytesWritten = ::write(socket, responseHeader, strlen(responseHeader));
EXPECT_EQ(static_cast<size_t>(bytesWritten), strlen(responseHeader));
startSecureConnection(socket);
};
break;
}
listenForConnections(1);
}
void TCPServer::listenForConnections(size_t connections)
{
auto listeningSocket = socketBindListen(connections);
ASSERT(listeningSocket);
m_listeningThread = std::thread([this, listeningSocket = *listeningSocket, connections] {
for (size_t i = 0; i < connections; ++i) {
Socket connectionSocket = accept(listeningSocket, nullptr, nullptr);
m_connectionThreads.append(std::thread([this, connectionSocket] {
m_connectionHandler(connectionSocket);
shutdown(connectionSocket, SHUT_RDWR);
close(connectionSocket);
}));
}
});
}
TCPServer::~TCPServer()
{
m_listeningThread.join();
for (auto& connectionThreads : m_connectionThreads)
connectionThreads.join();
}
auto TCPServer::socketBindListen(size_t connections) -> Optional<Socket>
{
Socket listeningSocket = socket(PF_INET, SOCK_STREAM, 0);
if (listeningSocket == -1)
return WTF::nullopt;
// Ports 49152-65535 are unallocated ports. Try until we find one that's free.
for (Port port = 49152; port; port++) {
struct sockaddr_in name;
memset(&name, 0, sizeof(name));
name.sin_family = AF_INET;
name.sin_port = htons(port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(listeningSocket, reinterpret_cast<sockaddr*>(&name), sizeof(name)) < 0) {
// This port is busy. Try the next port.
continue;
}
if (listen(listeningSocket, connections) == -1) {
// Listening failed.
close(listeningSocket);
return WTF::nullopt;
}
m_port = port;
return listeningSocket; // Successfully set up listening port.
}
// Couldn't find an available port.
close(listeningSocket);
return WTF::nullopt;
}
} // namespace TestWebKitAPI