From: Daniel Sommermann Date: Wed, 25 Jan 2017 17:32:24 +0000 (-0800) Subject: Split out SSL test server for reuse X-Git-Tag: v2017.03.06.00~81 X-Git-Url: http://plrg.eecs.uci.edu/git/?a=commitdiff_plain;h=f1317625208617cc2451734f5b63b2230b16bdc0;p=folly.git 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 --- diff --git a/folly/Makefile.am b/folly/Makefile.am index 2fc98c82..80dbbdf1 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -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 \ diff --git a/folly/io/async/test/AsyncSSLSocketTest.cpp b/folly/io/async/test/AsyncSSLSocketTest.cpp index 87c1453c..b5c6430a 100644 --- a/folly/io/async/test/AsyncSSLSocketTest.cpp +++ b/folly/io/async/test/AsyncSSLSocketTest.cpp @@ -15,7 +15,6 @@ */ #include -#include #include #include @@ -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(); 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(); 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(); ctx->loadPrivateKeyFromBufferPEM(key); ctx->loadCertificateFromBufferPEM(cert); - ctx->loadTrustedCertificates(testCA); + ctx->loadTrustedCertificates(kTestCA); ssl::SSLUniquePtr ssl(ctx->createSSL()); diff --git a/folly/io/async/test/AsyncSSLSocketTest.h b/folly/io/async/test/AsyncSSLSocketTest.h index 1aba12ed..7965f8e4 100644 --- a/folly/io/async/test/AsyncSSLSocketTest.h +++ b/folly/io/async/test/AsyncSSLSocketTest.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -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 &s) = 0; - - void detach() { - socket_->detachEventBase(); - } - - StateEnum state; - HandshakeCallback *hcb_; - std::shared_ptr ctx_; - std::shared_ptr 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 ctx_; - SSLServerAcceptCallbackBase *acb_; - std::shared_ptr socket_; - folly::SocketAddress address_; - pthread_t thread_; - - static void *Main(void *ctx) { - TestSSLServer *self = static_cast(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, diff --git a/folly/io/async/test/SSLSessionTest.cpp b/folly/io/async/test/SSLSessionTest.cpp index be0bfd64..77ab567f 100644 --- a/folly/io/async/test/SSLSessionTest.cpp +++ b/folly/io/async/test/SSLSessionTest.cpp @@ -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 { diff --git a/folly/io/async/test/TestSSLServer.cpp b/folly/io/async/test/TestSSLServer.cpp new file mode 100644 index 00000000..dabb1d53 --- /dev/null +++ b/folly/io/async/test/TestSSLServer.cpp @@ -0,0 +1,60 @@ +/* + * 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 + +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(); + } +} +} diff --git a/folly/io/async/test/TestSSLServer.h b/folly/io/async/test/TestSSLServer.h new file mode 100644 index 00000000..a710d37d --- /dev/null +++ b/folly/io/async/test/TestSSLServer.h @@ -0,0 +1,128 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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& s) = 0; + + void detach() { + socket_->detachEventBase(); + } + + StateEnum state; + HandshakeCallback* hcb_; + std::shared_ptr ctx_; + std::shared_ptr 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 ctx_; + SSLServerAcceptCallbackBase* acb_; + std::shared_ptr socket_; + SocketAddress address_; + std::thread thread_; +}; +}