Commit f1317625 authored by Daniel Sommermann's avatar Daniel Sommermann Committed by Facebook Github Bot

Split out SSL test server for reuse

Summary:
The TestSSLServer is useful to have on its own for reuse. This
diff separates it out to make reuse possible.

Reviewed By: yfeldblum

Differential Revision: D4457976

fbshipit-source-id: 1fe6b71e930859ef04487e29047dcf603a79834b
parent 904ebc63
......@@ -250,6 +250,7 @@ nobase_follyinclude_HEADERS = \
io/async/test/MockTimeoutManager.h \
io/async/test/ScopedBoundPort.h \
io/async/test/SocketPair.h \
io/async/test/TestSSLServer.h \
io/async/test/TimeUtil.h \
io/async/test/UndelayedDestruction.h \
io/async/test/Util.h \
......
......@@ -15,7 +15,6 @@
*/
#include <folly/io/async/test/AsyncSSLSocketTest.h>
#include <pthread.h>
#include <signal.h>
#include <folly/SocketAddress.h>
......@@ -53,45 +52,9 @@ uint32_t TestSSLAsyncCacheServer::asyncCallbacks_ = 0;
uint32_t TestSSLAsyncCacheServer::asyncLookups_ = 0;
uint32_t TestSSLAsyncCacheServer::lookupDelay_ = 0;
const char* testCert = "folly/io/async/test/certs/tests-cert.pem";
const char* testKey = "folly/io/async/test/certs/tests-key.pem";
const char* testCA = "folly/io/async/test/certs/ca-cert.pem";
constexpr size_t SSLClient::kMaxReadBufferSz;
constexpr size_t SSLClient::kMaxReadsPerEvent;
TestSSLServer::TestSSLServer(SSLServerAcceptCallbackBase* acb, bool enableTFO)
: ctx_(new folly::SSLContext),
acb_(acb),
socket_(folly::AsyncServerSocket::newSocket(&evb_)) {
// Set up the SSL context
ctx_->loadCertificate(testCert);
ctx_->loadPrivateKey(testKey);
ctx_->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
acb_->ctx_ = ctx_;
acb_->base_ = &evb_;
// Enable TFO
if (enableTFO) {
LOG(INFO) << "server TFO enabled";
socket_->setTFOEnabled(true, 1000);
}
// set up the listening socket
socket_->bind(0);
socket_->getAddress(&address_);
socket_->listen(100);
socket_->addAcceptCallback(acb_, &evb_);
socket_->startAccepting();
int ret = pthread_create(&thread_, nullptr, Main, this);
assert(ret == 0);
(void)ret;
std::cerr << "Accepting connections on " << address_ << std::endl;
}
void getfds(int fds[2]) {
if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) != 0) {
FAIL() << "failed to create socketpair: " << strerror(errno);
......@@ -115,10 +78,8 @@ void getctx(
clientCtx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
serverCtx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
serverCtx->loadCertificate(
testCert);
serverCtx->loadPrivateKey(
testKey);
serverCtx->loadCertificate(kTestCert);
serverCtx->loadPrivateKey(kTestKey);
}
void sslsocketpair(
......@@ -1023,16 +984,16 @@ TEST(AsyncSSLSocketTest, SSLParseClientHelloSuccess) {
auto serverCtx = std::make_shared<SSLContext>();
serverCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);
serverCtx->ciphers("ECDHE-RSA-AES128-SHA:AES128-SHA:AES256-SHA");
serverCtx->loadPrivateKey(testKey);
serverCtx->loadCertificate(testCert);
serverCtx->loadTrustedCertificates(testCA);
serverCtx->loadClientCAList(testCA);
serverCtx->loadPrivateKey(kTestKey);
serverCtx->loadCertificate(kTestCert);
serverCtx->loadTrustedCertificates(kTestCA);
serverCtx->loadClientCAList(kTestCA);
clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);
clientCtx->ciphers("AES256-SHA:AES128-SHA");
clientCtx->loadPrivateKey(testKey);
clientCtx->loadCertificate(testCert);
clientCtx->loadTrustedCertificates(testCA);
clientCtx->loadPrivateKey(kTestKey);
clientCtx->loadCertificate(kTestCert);
clientCtx->loadTrustedCertificates(kTestCA);
int fds[2];
getfds(fds);
......@@ -1214,7 +1175,7 @@ TEST(AsyncSSLSocketTest, SSLHandshakeValidationSuccess) {
new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));
SSLHandshakeClient client(std::move(clientSock), true, true);
clientCtx->loadTrustedCertificates(testCA);
clientCtx->loadTrustedCertificates(kTestCA);
SSLHandshakeServer server(std::move(serverSock), true, true);
......@@ -1252,7 +1213,7 @@ TEST(AsyncSSLSocketTest, SSLHandshakeValidationFailure) {
new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));
SSLHandshakeClient client(std::move(clientSock), true, false);
clientCtx->loadTrustedCertificates(testCA);
clientCtx->loadTrustedCertificates(kTestCA);
SSLHandshakeServer server(std::move(serverSock), true, true);
......@@ -1292,7 +1253,7 @@ TEST(AsyncSSLSocketTest, OverrideSSLCtxDisableVerify) {
new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));
SSLHandshakeClientNoVerify client(std::move(clientSock), false, false);
clientCtx->loadTrustedCertificates(testCA);
clientCtx->loadTrustedCertificates(kTestCA);
SSLHandshakeServerNoVerify server(std::move(serverSock), false, false);
......@@ -1319,16 +1280,16 @@ TEST(AsyncSSLSocketTest, OverrideSSLCtxEnableVerify) {
auto serverCtx = std::make_shared<SSLContext>();
serverCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::NO_VERIFY);
serverCtx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
serverCtx->loadPrivateKey(testKey);
serverCtx->loadCertificate(testCert);
serverCtx->loadTrustedCertificates(testCA);
serverCtx->loadClientCAList(testCA);
serverCtx->loadPrivateKey(kTestKey);
serverCtx->loadCertificate(kTestCert);
serverCtx->loadTrustedCertificates(kTestCA);
serverCtx->loadClientCAList(kTestCA);
clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::NO_VERIFY);
clientCtx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
clientCtx->loadPrivateKey(testKey);
clientCtx->loadCertificate(testCert);
clientCtx->loadTrustedCertificates(testCA);
clientCtx->loadPrivateKey(kTestKey);
clientCtx->loadCertificate(kTestCert);
clientCtx->loadTrustedCertificates(kTestCA);
int fds[2];
getfds(fds);
......@@ -1436,16 +1397,16 @@ TEST(AsyncSSLSocketTest, ClientCertHandshakeSuccess) {
serverCtx->setVerificationOption(
SSLContext::SSLVerifyPeerEnum::VERIFY_REQ_CLIENT_CERT);
serverCtx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
serverCtx->loadPrivateKey(testKey);
serverCtx->loadCertificate(testCert);
serverCtx->loadTrustedCertificates(testCA);
serverCtx->loadClientCAList(testCA);
serverCtx->loadPrivateKey(kTestKey);
serverCtx->loadCertificate(kTestCert);
serverCtx->loadTrustedCertificates(kTestCA);
serverCtx->loadClientCAList(kTestCA);
clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);
clientCtx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
clientCtx->loadPrivateKey(testKey);
clientCtx->loadCertificate(testCert);
clientCtx->loadTrustedCertificates(testCA);
clientCtx->loadPrivateKey(kTestKey);
clientCtx->loadCertificate(kTestCert);
clientCtx->loadTrustedCertificates(kTestCA);
int fds[2];
getfds(fds);
......@@ -1481,10 +1442,10 @@ TEST(AsyncSSLSocketTest, NoClientCertHandshakeError) {
serverCtx->setVerificationOption(
SSLContext::SSLVerifyPeerEnum::VERIFY_REQ_CLIENT_CERT);
serverCtx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
serverCtx->loadPrivateKey(testKey);
serverCtx->loadCertificate(testCert);
serverCtx->loadTrustedCertificates(testCA);
serverCtx->loadClientCAList(testCA);
serverCtx->loadPrivateKey(kTestKey);
serverCtx->loadCertificate(kTestCert);
serverCtx->loadTrustedCertificates(kTestCA);
serverCtx->loadClientCAList(kTestCA);
clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::NO_VERIFY);
clientCtx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
......@@ -1509,8 +1470,8 @@ TEST(AsyncSSLSocketTest, NoClientCertHandshakeError) {
}
TEST(AsyncSSLSocketTest, LoadCertFromMemory) {
auto cert = getFileAsBuf(testCert);
auto key = getFileAsBuf(testKey);
auto cert = getFileAsBuf(kTestCert);
auto key = getFileAsBuf(kTestKey);
ssl::BioUniquePtr certBio(BIO_new(BIO_s_mem()));
BIO_write(certBio.get(), cert.data(), cert.size());
......@@ -1533,7 +1494,7 @@ TEST(AsyncSSLSocketTest, LoadCertFromMemory) {
auto ctx = std::make_shared<SSLContext>();
ctx->loadPrivateKeyFromBufferPEM(key);
ctx->loadCertificateFromBufferPEM(cert);
ctx->loadTrustedCertificates(testCA);
ctx->loadTrustedCertificates(kTestCA);
ssl::SSLUniquePtr ssl(ctx->createSSL());
......
......@@ -28,6 +28,7 @@
#include <folly/io/async/AsyncTransport.h>
#include <folly/io/async/EventBase.h>
#include <folly/io/async/ssl/SSLErrors.h>
#include <folly/io/async/test/TestSSLServer.h>
#include <folly/portability/GTest.h>
#include <folly/portability/Sockets.h>
#include <folly/portability/Unistd.h>
......@@ -40,12 +41,6 @@
namespace folly {
enum StateEnum {
STATE_WAITING,
STATE_SUCCEEDED,
STATE_FAILED
};
// The destructors of all callback classes assert that the state is
// STATE_SUCCEEDED, for both possitive and negative tests. The tests
// are responsible for setting the succeeded state properly before the
......@@ -375,57 +370,6 @@ public:
std::string errorString_;
};
class SSLServerAcceptCallbackBase:
public folly::AsyncServerSocket::AcceptCallback {
public:
explicit SSLServerAcceptCallbackBase(HandshakeCallback *hcb):
state(STATE_WAITING), hcb_(hcb) {}
~SSLServerAcceptCallbackBase() {
EXPECT_EQ(STATE_SUCCEEDED, state);
}
void acceptError(const std::exception& ex) noexcept override {
std::cerr << "SSLServerAcceptCallbackBase::acceptError "
<< ex.what() << std::endl;
state = STATE_FAILED;
}
void connectionAccepted(
int fd, const folly::SocketAddress& /* clientAddr */) noexcept override {
if (socket_) {
socket_->detachEventBase();
}
printf("Connection accepted\n");
try {
// Create a AsyncSSLSocket object with the fd. The socket should be
// added to the event base and in the state of accepting SSL connection.
socket_ = AsyncSSLSocket::newSocket(ctx_, base_, fd);
} catch (const std::exception &e) {
LOG(ERROR) << "Exception %s caught while creating a AsyncSSLSocket "
"object with socket " << e.what() << fd;
::close(fd);
acceptError(e);
return;
}
connAccepted(socket_);
}
virtual void connAccepted(
const std::shared_ptr<folly::AsyncSSLSocket> &s) = 0;
void detach() {
socket_->detachEventBase();
}
StateEnum state;
HandshakeCallback *hcb_;
std::shared_ptr<folly::SSLContext> ctx_;
std::shared_ptr<AsyncSSLSocket> socket_;
folly::EventBase* base_;
};
class SSLServerAcceptCallback: public SSLServerAcceptCallbackBase {
public:
uint32_t timeout_;
......@@ -614,46 +558,6 @@ class ConnectTimeoutCallback : public SSLServerAcceptCallbackBase {
}
};
class TestSSLServer {
protected:
EventBase evb_;
std::shared_ptr<folly::SSLContext> ctx_;
SSLServerAcceptCallbackBase *acb_;
std::shared_ptr<folly::AsyncServerSocket> socket_;
folly::SocketAddress address_;
pthread_t thread_;
static void *Main(void *ctx) {
TestSSLServer *self = static_cast<TestSSLServer*>(ctx);
self->evb_.loop();
self->acb_->detach();
std::cerr << "Server thread exited event loop" << std::endl;
return nullptr;
}
public:
// Create a TestSSLServer.
// This immediately starts listening on the given port.
explicit TestSSLServer(
SSLServerAcceptCallbackBase* acb,
bool enableTFO = false);
// Kill the thread.
~TestSSLServer() {
evb_.runInEventBaseThread([&](){
socket_->stopAccepting();
});
std::cerr << "Waiting for server thread to exit" << std::endl;
pthread_join(thread_, nullptr);
}
EventBase &getEventBase() { return evb_; }
const folly::SocketAddress& getAddress() const {
return address_;
}
};
class TestSSLAsyncCacheServer : public TestSSLServer {
public:
explicit TestSSLAsyncCacheServer(SSLServerAcceptCallbackBase *acb,
......
......@@ -25,10 +25,6 @@ using folly::ssl::SSLSession;
namespace folly {
const char* testCert = "folly/io/async/test/certs/tests-cert.pem";
const char* testKey = "folly/io/async/test/certs/tests-key.pem";
const char* testCA = "folly/io/async/test/certs/ca-cert.pem";
void getfds(int fds[2]) {
if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) != 0) {
LOG(ERROR) << "failed to create socketpair: " << strerror(errno);
......@@ -52,8 +48,8 @@ void getctx(
clientCtx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
serverCtx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
serverCtx->loadCertificate(testCert);
serverCtx->loadPrivateKey(testKey);
serverCtx->loadCertificate(kTestCert);
serverCtx->loadPrivateKey(kTestKey);
}
class SSLSessionTest : public testing::Test {
......
/*
* Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/io/async/test/TestSSLServer.h>
namespace folly {
const char* kTestCert = "folly/io/async/test/certs/tests-cert.pem";
const char* kTestKey = "folly/io/async/test/certs/tests-key.pem";
const char* kTestCA = "folly/io/async/test/certs/ca-cert.pem";
TestSSLServer::TestSSLServer(SSLServerAcceptCallbackBase* acb, bool enableTFO)
: ctx_(new SSLContext),
acb_(acb),
socket_(AsyncServerSocket::newSocket(&evb_)) {
// Set up the SSL context
ctx_->loadCertificate(kTestCert);
ctx_->loadPrivateKey(kTestKey);
ctx_->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
acb_->ctx_ = ctx_;
acb_->base_ = &evb_;
// Enable TFO
if (enableTFO) {
LOG(INFO) << "server TFO enabled";
socket_->setTFOEnabled(true, 1000);
}
// set up the listening socket
socket_->bind(0);
socket_->getAddress(&address_);
socket_->listen(100);
socket_->addAcceptCallback(acb_, &evb_);
socket_->startAccepting();
thread_ = std::thread([&] { Main(); });
LOG(INFO) << "Accepting connections on " << address_;
}
TestSSLServer::~TestSSLServer() {
if (thread_.joinable()) {
evb_.runInEventBaseThread([&]() { socket_->stopAccepting(); });
LOG(INFO) << "Waiting for server thread to exit";
thread_.join();
}
}
}
/*
* Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <folly/SocketAddress.h>
#include <folly/experimental/TestUtil.h>
#include <folly/io/async/AsyncSSLSocket.h>
#include <folly/io/async/AsyncServerSocket.h>
#include <folly/io/async/AsyncSocket.h>
#include <folly/io/async/AsyncTimeout.h>
#include <folly/io/async/AsyncTransport.h>
#include <folly/io/async/EventBase.h>
#include <folly/io/async/ssl/SSLErrors.h>
#include <folly/portability/GTest.h>
#include <folly/portability/Sockets.h>
#include <folly/portability/Unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <list>
namespace folly {
extern const char* kTestCert;
extern const char* kTestKey;
extern const char* kTestCA;
enum StateEnum { STATE_WAITING, STATE_SUCCEEDED, STATE_FAILED };
class HandshakeCallback;
class SSLServerAcceptCallbackBase : public AsyncServerSocket::AcceptCallback {
public:
explicit SSLServerAcceptCallbackBase(HandshakeCallback* hcb)
: state(STATE_WAITING), hcb_(hcb) {}
~SSLServerAcceptCallbackBase() {
EXPECT_EQ(STATE_SUCCEEDED, state);
}
void acceptError(const std::exception& ex) noexcept override {
LOG(WARNING) << "SSLServerAcceptCallbackBase::acceptError " << ex.what();
state = STATE_FAILED;
}
void connectionAccepted(
int fd,
const SocketAddress& /* clientAddr */) noexcept override {
if (socket_) {
socket_->detachEventBase();
}
LOG(INFO) << "Connection accepted";
try {
// Create a AsyncSSLSocket object with the fd. The socket should be
// added to the event base and in the state of accepting SSL connection.
socket_ = AsyncSSLSocket::newSocket(ctx_, base_, fd);
} catch (const std::exception& e) {
LOG(ERROR) << "Exception %s caught while creating a AsyncSSLSocket "
"object with socket "
<< e.what() << fd;
::close(fd);
acceptError(e);
return;
}
connAccepted(socket_);
}
virtual void connAccepted(const std::shared_ptr<AsyncSSLSocket>& s) = 0;
void detach() {
socket_->detachEventBase();
}
StateEnum state;
HandshakeCallback* hcb_;
std::shared_ptr<SSLContext> ctx_;
std::shared_ptr<AsyncSSLSocket> socket_;
EventBase* base_;
};
class TestSSLServer {
public:
// Create a TestSSLServer.
// This immediately starts listening on the given port.
explicit TestSSLServer(
SSLServerAcceptCallbackBase* acb,
bool enableTFO = false);
// Kills the thread.
virtual ~TestSSLServer();
EventBase& getEventBase() {
return evb_;
}
const SocketAddress& getAddress() const {
return address_;
}
protected:
void Main() {
evb_.loop();
acb_->detach();
LOG(INFO) << "Server thread exited event loop";
}
EventBase evb_;
std::shared_ptr<SSLContext> ctx_;
SSLServerAcceptCallbackBase* acb_;
std::shared_ptr<AsyncServerSocket> socket_;
SocketAddress address_;
std::thread thread_;
};
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment