summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAdam Midvidy <amidvidy@gmail.com>2015-07-22 17:16:00 -0400
committerAdam Midvidy <amidvidy@gmail.com>2015-07-22 17:16:00 -0400
commitc99d98a9a82d86f65c55ff7bf7df04c44d48d5d5 (patch)
tree266d3331da3fa44d88f3ccb82ea2033e843df863 /src
parent917290ba9644cbf6d981bc730444a107248d1aa3 (diff)
downloadmongo-c99d98a9a82d86f65c55ff7bf7df04c44d48d5d5.tar.gz
Revert "SERVER-19221 implement async SSL in NetworkInterfaceASIO"
This reverts commit 917290ba9644cbf6d981bc730444a107248d1aa3.
Diffstat (limited to 'src')
-rw-r--r--src/mongo/base/error_codes.err4
-rw-r--r--src/mongo/db/db.cpp7
-rw-r--r--src/mongo/executor/network_interface_asio.cpp15
-rw-r--r--src/mongo/executor/network_interface_asio.h76
-rw-r--r--src/mongo/executor/network_interface_asio_auth.cpp2
-rw-r--r--src/mongo/executor/network_interface_asio_command.cpp49
-rw-r--r--src/mongo/executor/network_interface_asio_connect.cpp109
-rw-r--r--src/mongo/executor/network_interface_asio_operation.cpp65
-rw-r--r--src/mongo/executor/network_interface_asio_ssl.cpp81
-rw-r--r--src/mongo/util/net/sock.cpp4
-rw-r--r--src/mongo/util/net/ssl_manager.cpp151
-rw-r--r--src/mongo/util/net/ssl_manager.h31
-rw-r--r--src/third_party/asio-asio-1-11-0/SConscript12
13 files changed, 198 insertions, 408 deletions
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err
index 83a0c6bd81f..951dd09e81e 100644
--- a/src/mongo/base/error_codes.err
+++ b/src/mongo/base/error_codes.err
@@ -139,8 +139,6 @@ error_code("CappedPositionLost", 136)
error_code("IncompatibleShardingConfigVersion", 137)
error_code("RemoteOplogStale", 138)
error_code("JSInterpreterFailure", 139)
-error_code("InvalidSSLConfiguration", 140)
-error_code("SSLHandshakeFailed", 141)
# Non-sequential error codes (for compatibility only)
error_code("NotMaster", 10107) #this comes from assert_util.h
@@ -160,5 +158,5 @@ error_code("PrepareConfigsFailedCode", 13104);
error_class("NetworkError", ["HostUnreachable", "HostNotFound", "NetworkTimeout"])
error_class("Interruption", ["Interrupted", "InterruptedAtShutdown", "ExceededTimeLimit"])
-error_class("IndexCreationError", ["CannotCreateIndex", "IndexOptionsConflict",
+error_class("IndexCreationError", ["CannotCreateIndex", "IndexOptionsConflict",
"IndexKeySpecsConflict", "IndexAlreadyExists"])
diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp
index 2b4ab0e3f26..f0d95373b04 100644
--- a/src/mongo/db/db.cpp
+++ b/src/mongo/db/db.cpp
@@ -731,7 +731,12 @@ static void startupConfigActions(const std::vector<std::string>& args) {
#endif
}
-MONGO_INITIALIZER_WITH_PREREQUISITES(CreateReplicationManager, ("SetGlobalEnvironment"))
+MONGO_INITIALIZER_WITH_PREREQUISITES(CreateReplicationManager,
+#if MONGO_CONFIG_SSL
+ ("SetGlobalEnvironment", "SSLManager"))
+#else
+ ("SetGlobalEnvironment"))
+#endif
(InitializerContext* context) {
auto replCoord = stdx::make_unique<repl::ReplicationCoordinatorImpl>(
getGlobalReplSettings(),
diff --git a/src/mongo/executor/network_interface_asio.cpp b/src/mongo/executor/network_interface_asio.cpp
index c190c3aa662..3b6acdd6083 100644
--- a/src/mongo/executor/network_interface_asio.cpp
+++ b/src/mongo/executor/network_interface_asio.cpp
@@ -34,32 +34,17 @@
#include <utility>
-#include "mongo/config.h"
#include "mongo/stdx/chrono.h"
#include "mongo/stdx/memory.h"
#include "mongo/util/log.h"
#include "mongo/util/net/sock.h"
-#include "mongo/util/net/ssl_manager.h"
-
namespace mongo {
namespace executor {
NetworkInterfaceASIO::NetworkInterfaceASIO()
: _io_service(), _resolver(_io_service), _state(State::kReady), _isExecutorRunnable(false) {
_connPool = stdx::make_unique<ConnectionPool>(kMessagingPortKeepOpen);
-
-#if MONGO_CONFIG_SSL
- if (getSSLManager()) {
- // We use sslv23, which corresponds to OpenSSLs SSLv23_method, for compatibility with older
- // versions of OpenSSL. This mirrors the call to SSL_CTX_new in ssl_manager.cpp. In
- // initAsyncSSLContext we explicitly disable all protocols other than TLSv1, TLSv1.1,
- // and TLSv1.2.
- _sslContext.emplace(asio::ssl::context::sslv23);
- uassertStatusOK(
- getSSLManager()->initSSLContext(_sslContext->native_handle(), getSSLGlobalParams()));
- }
-#endif
}
std::string NetworkInterfaceASIO::getDiagnosticString() {
diff --git a/src/mongo/executor/network_interface_asio.h b/src/mongo/executor/network_interface_asio.h
index bc1a4a81e5a..0f82c2c618d 100644
--- a/src/mongo/executor/network_interface_asio.h
+++ b/src/mongo/executor/network_interface_asio.h
@@ -28,20 +28,13 @@
#pragma once
-#include "mongo/config.h"
-
#include <asio.hpp>
-
#include <boost/optional.hpp>
#include <memory>
#include <string>
#include <system_error>
#include <unordered_map>
-#ifdef MONGO_CONFIG_SSL
-#include <asio/ssl.hpp>
-#endif
-
#include "mongo/base/status.h"
#include "mongo/client/connection_pool.h"
#include "mongo/executor/network_interface.h"
@@ -50,7 +43,6 @@
#include "mongo/platform/atomic_word.h"
#include "mongo/rpc/protocol.h"
#include "mongo/stdx/condition_variable.h"
-#include "mongo/stdx/functional.h"
#include "mongo/stdx/mutex.h"
#include "mongo/stdx/thread.h"
#include "mongo/util/net/message.h"
@@ -59,30 +51,6 @@ namespace mongo {
namespace executor {
/**
- * A two-way stream supporting asynchronous reads and writes.
- */
-class AsyncStreamInterface {
- MONGO_DISALLOW_COPYING(AsyncStreamInterface);
-
-public:
- virtual ~AsyncStreamInterface() = default;
-
- using ConnectHandler = stdx::function<void(std::error_code)>;
-
- using StreamHandler = stdx::function<void(std::error_code, std::size_t)>;
-
- virtual void connect(const asio::ip::tcp::resolver::iterator endpoints,
- ConnectHandler&& connectHandler) = 0;
-
- virtual void write(asio::const_buffer buf, StreamHandler&& writeHandler) = 0;
-
- virtual void read(asio::mutable_buffer buf, StreamHandler&& readHandler) = 0;
-
-protected:
- AsyncStreamInterface() = default;
-};
-
-/**
* Implementation of the replication system's network interface using Christopher
* Kohlhoff's ASIO library instead of existing MongoDB networking primitives.
*/
@@ -112,21 +80,18 @@ private:
enum class State { kReady, kRunning, kShutdown };
- class AsyncStream;
- class AsyncSecureStream;
-
/**
* AsyncConnection encapsulates the per-connection state we maintain.
*/
class AsyncConnection {
public:
- AsyncConnection(std::unique_ptr<AsyncStreamInterface>, rpc::ProtocolSet serverProtocols);
+ AsyncConnection(asio::ip::tcp::socket&& sock, rpc::ProtocolSet serverProtocols);
- AsyncConnection(std::unique_ptr<AsyncStreamInterface>,
+ AsyncConnection(asio::ip::tcp::socket&& sock,
rpc::ProtocolSet serverProtocols,
boost::optional<ConnectionPool::ConnectionPtr>&& bootstrapConn);
- AsyncStreamInterface& stream();
+ asio::ip::tcp::socket& sock();
rpc::ProtocolSet serverProtocols() const;
rpc::ProtocolSet clientProtocols() const;
@@ -142,7 +107,7 @@ private:
#endif
private:
- std::unique_ptr<AsyncStreamInterface> _stream;
+ asio::ip::tcp::socket _sock;
rpc::ProtocolSet _serverProtocols;
rpc::ProtocolSet _clientProtocols{rpc::supports::kAll};
@@ -194,14 +159,18 @@ private:
const RemoteCommandCompletionFn& onFinish,
Date_t now);
+ std::string toString() const;
+
void cancel();
bool canceled() const;
const TaskExecutor::CallbackHandle& cbHandle() const;
- AsyncConnection& connection();
+ AsyncConnection* connection();
+ void connect(ConnectionPool* const pool, asio::io_service* service, Date_t now);
void setConnection(AsyncConnection&& conn);
+ bool connected() const;
// AsyncOp may run multiple commands over its lifetime (for example, an ismaster
// command, the command provided to the NetworkInterface via startCommand(), etc.)
@@ -220,6 +189,14 @@ private:
void setOperationProtocol(rpc::Protocol proto);
private:
+ enum class OpState {
+ kReady,
+ kConnectionAcquired,
+ kConnectionVerified,
+ kConnected,
+ kCompleted
+ };
+
// Information describing a task enqueued on the NetworkInterface
// via a call to startCommand().
TaskExecutor::CallbackHandle _cbHandle;
@@ -240,6 +217,7 @@ private:
const Date_t _start;
+ OpState _state;
AtomicUInt64 _canceled;
/**
@@ -277,17 +255,10 @@ private:
// Connection
void _connectASIO(AsyncOp* op);
void _connectWithDBClientConnection(AsyncOp* op);
-
- // setup plaintext TCP socket
- void _setupSocket(AsyncOp* op, const asio::ip::tcp::resolver::iterator endpoints);
-
-#ifdef MONGO_CONFIG_SSL
- // setup SSL socket
- void _setupSecureSocket(AsyncOp* op, asio::ip::tcp::resolver::iterator endpoints);
-#endif
-
+ void _setupSocket(AsyncOp* op, const asio::ip::tcp::resolver::iterator& endpoints);
void _runIsMaster(AsyncOp* op);
void _authenticate(AsyncOp* op);
+ void _sslHandshake(AsyncOp* op);
// Communication state machine
void _beginCommunication(AsyncOp* op);
@@ -314,13 +285,6 @@ private:
stdx::condition_variable _isExecutorRunnableCondition;
std::unique_ptr<ConnectionPool> _connPool;
-
-#ifdef MONGO_CONFIG_SSL
- // The SSL context. This declaration is wrapped in an ifdef because the asio::ssl::context
- // type does not exist unless SSL support is compiled in. We also use a boost::optional as
- // even if SSL support is compiled in, it can be disabled at runtime.
- boost::optional<asio::ssl::context> _sslContext;
-#endif
};
} // namespace executor
diff --git a/src/mongo/executor/network_interface_asio_auth.cpp b/src/mongo/executor/network_interface_asio_auth.cpp
index d3e72bf956a..176d435fa6b 100644
--- a/src/mongo/executor/network_interface_asio_auth.cpp
+++ b/src/mongo/executor/network_interface_asio_auth.cpp
@@ -61,7 +61,7 @@ void NetworkInterfaceASIO::_runIsMaster(AsyncOp* op) {
if (!protocolSet.isOK())
return _completeOperation(op, protocolSet.getStatus());
- op->connection().setServerProtocols(protocolSet.getValue());
+ op->connection()->setServerProtocols(protocolSet.getValue());
// Advance the state machine
_authenticate(op);
diff --git a/src/mongo/executor/network_interface_asio_command.cpp b/src/mongo/executor/network_interface_asio_command.cpp
index 28560bd4f5f..e19b8e6224c 100644
--- a/src/mongo/executor/network_interface_asio_command.cpp
+++ b/src/mongo/executor/network_interface_asio_command.cpp
@@ -32,7 +32,6 @@
#include "mongo/executor/network_interface_asio.h"
-#include <type_traits>
#include <utility>
#include "mongo/db/dbmessage.h"
@@ -54,43 +53,25 @@ namespace executor {
namespace {
using asio::ip::tcp;
+using NetworkOpHandler = stdx::function<void(std::error_code, size_t)>;
-// A type conforms to the NetworkHandler concept if it is a callable type that takes a
-// std::error_code and std::size_t and returns void. The std::error_code parameter is used
-// to inform the handler if the asynchronous operation it was waiting on succeeded, and the size_t
-// parameter conveys how many bytes were read or written.
-template <typename FunctionLike>
-using IsNetworkHandler =
- std::is_convertible<FunctionLike, stdx::function<void(std::error_code, std::size_t)>>;
-
-template <typename Handler>
-void asyncSendMessage(AsyncStreamInterface& stream, Message* m, Handler&& handler) {
- static_assert(IsNetworkHandler<Handler>::value,
- "Handler passed to asyncSendMessage does not conform to NetworkHandler concept");
+// TODO: Consider templatizing on handler here to avoid using stdx::functions.
+void asyncSendMessage(tcp::socket& sock, Message* m, NetworkOpHandler handler) {
// TODO: Some day we may need to support vector messages.
fassert(28708, m->buf() != 0);
- stream.write(asio::buffer(m->buf(), m->size()), std::forward<Handler>(handler));
+ asio::const_buffer buf(m->buf(), m->size());
+ asio::async_write(sock, asio::buffer(buf), handler);
}
-template <typename Handler>
-void asyncRecvMessageHeader(AsyncStreamInterface& stream,
- MSGHEADER::Value* header,
- Handler&& handler) {
- static_assert(
- IsNetworkHandler<Handler>::value,
- "Handler passed to asyncRecvMessageHeader does not conform to NetworkHandler concept");
- stream.read(asio::buffer(header->view().view2ptr(), sizeof(decltype(*header))),
- std::forward<Handler>(handler));
+void asyncRecvMessageHeader(tcp::socket& sock, MSGHEADER::Value* header, NetworkOpHandler handler) {
+ asio::async_read(
+ sock, asio::buffer(header->view().view2ptr(), sizeof(MSGHEADER::Value)), handler);
}
-template <typename Handler>
-void asyncRecvMessageBody(AsyncStreamInterface& stream,
+void asyncRecvMessageBody(tcp::socket& sock,
MSGHEADER::Value* header,
Message* m,
- Handler&& handler) {
- static_assert(
- IsNetworkHandler<Handler>::value,
- "Handler passed to asyncRecvMessageBody does not conform to NetworkHandler concept");
+ NetworkOpHandler handler) {
// TODO: This error code should be more meaningful.
std::error_code ec;
@@ -118,7 +99,7 @@ void asyncRecvMessageBody(AsyncStreamInterface& stream,
invariant(bodyLength >= 0);
// receive remaining data into md->data
- stream.read(asio::buffer(mdView.data(), bodyLength), std::forward<Handler>(handler));
+ asio::async_read(sock, asio::buffer(mdView.data(), bodyLength), handler);
}
} // namespace
@@ -183,7 +164,7 @@ std::unique_ptr<Message> NetworkInterfaceASIO::_messageFromRequest(
void NetworkInterfaceASIO::_beginCommunication(AsyncOp* op) {
auto negotiatedProtocol =
- rpc::negotiate(op->connection().serverProtocols(), op->connection().clientProtocols());
+ rpc::negotiate(op->connection()->serverProtocols(), op->connection()->clientProtocols());
if (!negotiatedProtocol.isOK()) {
return _completeOperation(op, negotiatedProtocol.getStatus());
@@ -283,7 +264,7 @@ void NetworkInterfaceASIO::_asyncRunCommand(AsyncCommand* cmd, NetworkOpHandler
}
asyncRecvMessageBody(
- cmd->conn().stream(), &cmd->header(), &cmd->toRecv(), std::move(recvMessageCallback));
+ cmd->conn().sock(), &cmd->header(), &cmd->toRecv(), std::move(recvMessageCallback));
};
// Step 2
@@ -292,11 +273,11 @@ void NetworkInterfaceASIO::_asyncRunCommand(AsyncCommand* cmd, NetworkOpHandler
if (ec)
return handler(ec, bytes);
- asyncRecvMessageHeader(cmd->conn().stream(), &cmd->header(), std::move(recvHeaderCallback));
+ asyncRecvMessageHeader(cmd->conn().sock(), &cmd->header(), std::move(recvHeaderCallback));
};
// Step 1
- asyncSendMessage(cmd->conn().stream(), &cmd->toSend(), std::move(sendMessageCallback));
+ asyncSendMessage(cmd->conn().sock(), &cmd->toSend(), std::move(sendMessageCallback));
}
} // namespace executor
diff --git a/src/mongo/executor/network_interface_asio_connect.cpp b/src/mongo/executor/network_interface_asio_connect.cpp
index 2d01a97e773..2ac64ddcf43 100644
--- a/src/mongo/executor/network_interface_asio_connect.cpp
+++ b/src/mongo/executor/network_interface_asio_connect.cpp
@@ -34,59 +34,23 @@
#include <utility>
-#include "mongo/config.h"
-#include "mongo/stdx/memory.h"
#include "mongo/util/log.h"
#include "mongo/util/net/sock.h"
-#ifdef MONGO_CONFIG_SSL
-#include "mongo/util/net/ssl_manager.h"
-#include "mongo/util/net/ssl_options.h"
-#endif
-
namespace mongo {
namespace executor {
using asio::ip::tcp;
-class NetworkInterfaceASIO::AsyncStream final : public AsyncStreamInterface {
-public:
- // TODO: after we get rid of the bootstrap connection path, change this constructor
- // to take the io_service instead to more closely match AsyncSecureStream
- AsyncStream(tcp::socket&& stream) : _stream(std::move(stream)) {}
-
- void connect(const tcp::resolver::iterator iter, ConnectHandler&& connectHandler) override {
- asio::async_connect(
- _stream,
- std::move(iter),
- // We need to wrap this with a lambda of the right signature so it compiles, even
- // if we don't actually use the resolver iterator.
- [this, connectHandler](std::error_code ec, tcp::resolver::iterator) {
- return connectHandler(ec);
- });
- }
-
- void write(asio::const_buffer buffer, StreamHandler&& streamHandler) override {
- asio::async_write(_stream, asio::buffer(buffer), std::move(streamHandler));
- }
-
- void read(asio::mutable_buffer buffer, StreamHandler&& streamHandler) override {
- asio::async_read(_stream, asio::buffer(buffer), std::move(streamHandler));
- }
-
-private:
- tcp::socket _stream;
-};
-
-NetworkInterfaceASIO::AsyncConnection::AsyncConnection(std::unique_ptr<AsyncStreamInterface> stream,
+NetworkInterfaceASIO::AsyncConnection::AsyncConnection(asio::ip::tcp::socket&& sock,
rpc::ProtocolSet protocols)
- : AsyncConnection(std::move(stream), protocols, boost::none) {}
+ : AsyncConnection(std::move(sock), protocols, boost::none) {}
NetworkInterfaceASIO::AsyncConnection::AsyncConnection(
- std::unique_ptr<AsyncStreamInterface> stream,
+ asio::ip::tcp::socket&& sock,
rpc::ProtocolSet protocols,
boost::optional<ConnectionPool::ConnectionPtr>&& bootstrapConn)
- : _stream(std::move(stream)),
+ : _sock(std::move(sock)),
_serverProtocols(protocols),
_bootstrapConn(std::move(bootstrapConn)) {}
@@ -105,8 +69,8 @@ NetworkInterfaceASIO::AsyncConnection& NetworkInterfaceASIO::AsyncConnection::op
}
#endif
-AsyncStreamInterface& NetworkInterfaceASIO::AsyncConnection::stream() {
- return *_stream;
+asio::ip::tcp::socket& NetworkInterfaceASIO::AsyncConnection::sock() {
+ return _sock;
}
rpc::ProtocolSet NetworkInterfaceASIO::AsyncConnection::serverProtocols() const {
@@ -125,24 +89,11 @@ void NetworkInterfaceASIO::_connectASIO(AsyncOp* op) {
tcp::resolver::query query(op->request().target.host(),
std::to_string(op->request().target.port()));
// TODO: Investigate how we might hint or use shortcuts to resolve when possible.
- const auto thenConnect = [this, op](std::error_code ec, tcp::resolver::iterator endpoints) {
- _validateAndRun(op,
- ec,
- [this, op, endpoints]() {
-
-#ifdef MONGO_CONFIG_SSL
- int sslModeVal = getSSLGlobalParams().sslMode.load();
- if (sslModeVal == SSLParams::SSLMode_preferSSL ||
- sslModeVal == SSLParams::SSLMode_requireSSL) {
- invariant(_sslContext.is_initialized());
- return _setupSecureSocket(op, std::move(endpoints));
- }
-#endif
- _setupSocket(op, std::move(endpoints));
-
- });
- };
- _resolver.async_resolve(query, std::move(thenConnect));
+ _resolver.async_resolve(
+ query,
+ [this, op](std::error_code ec, asio::ip::basic_resolver_iterator<tcp> endpoints) {
+ _validateAndRun(op, ec, [this, op, endpoints]() { _setupSocket(op, endpoints); });
+ });
}
void NetworkInterfaceASIO::_connectWithDBClientConnection(AsyncOp* op) {
@@ -153,24 +104,7 @@ void NetworkInterfaceASIO::_connectWithDBClientConnection(AsyncOp* op) {
// - we cannot get a new connection from the pool
// - we get a connection from the pool, but cannot use it
// - we fail to transfer the connection's socket to an ASIO wrapper
- // TODO(amidvidy): why is this hardcoded to 1 second? That seems too low.
- ConnectionPool::ConnectionPtr conn(
- _connPool.get(), op->request().target, now(), Milliseconds(1000));
-
- // TODO: Add a case here for unix domain sockets.
- int protocol = conn.get()->port().localAddr().getType();
- if (protocol != AF_INET && protocol != AF_INET6) {
- throw SocketException(SocketException::CONNECT_ERROR, "Unsupported family");
- }
-
- tcp::socket sock{_io_service,
- protocol == AF_INET ? tcp::v4() : tcp::v6(),
- conn.get()->port().psock->rawFD()};
-
- op->setConnection(AsyncConnection(stdx::make_unique<AsyncStream>(std::move(sock)),
- conn.get()->getServerRPCProtocols(),
- std::move(conn)));
-
+ op->connect(_connPool.get(), &_io_service, now());
} catch (...) {
LOG(3) << "failed to connect, posting mock completion";
@@ -192,17 +126,18 @@ void NetworkInterfaceASIO::_connectWithDBClientConnection(AsyncOp* op) {
t.detach();
}
-void NetworkInterfaceASIO::_setupSocket(AsyncOp* op, const tcp::resolver::iterator endpoints) {
- // TODO: Consider moving this call to post-auth so we only assign completed connections.
- op->setConnection(AsyncConnection(stdx::make_unique<AsyncStream>(tcp::socket{_io_service}),
- rpc::supports::kOpQueryOnly));
+void NetworkInterfaceASIO::_setupSocket(AsyncOp* op, const tcp::resolver::iterator& endpoints) {
+ tcp::socket sock(_io_service);
+ AsyncConnection conn(std::move(sock), rpc::supports::kOpQueryOnly);
- auto& stream = op->connection().stream();
+ // TODO: Consider moving this call to post-auth so we only assign completed connections.
+ op->setConnection(std::move(conn));
- stream.connect(std::move(endpoints),
- [this, op](std::error_code ec) {
- _validateAndRun(op, ec, [this, op]() { _authenticate(op); });
- });
+ asio::async_connect(op->connection()->sock(),
+ std::move(endpoints),
+ [this, op](std::error_code ec, tcp::resolver::iterator iter) {
+ _validateAndRun(op, ec, [this, op]() { _sslHandshake(op); });
+ });
}
} // namespace executor
diff --git a/src/mongo/executor/network_interface_asio_operation.cpp b/src/mongo/executor/network_interface_asio_operation.cpp
index 2e7df88a172..82038fc56f3 100644
--- a/src/mongo/executor/network_interface_asio_operation.cpp
+++ b/src/mongo/executor/network_interface_asio_operation.cpp
@@ -41,7 +41,33 @@ NetworkInterfaceASIO::AsyncOp::AsyncOp(const TaskExecutor::CallbackHandle& cbHan
const RemoteCommandRequest& request,
const RemoteCommandCompletionFn& onFinish,
Date_t now)
- : _cbHandle(cbHandle), _request(request), _onFinish(onFinish), _start(now), _canceled(0) {}
+ : _cbHandle(cbHandle),
+ _request(request),
+ _onFinish(onFinish),
+ _start(now),
+ _state(OpState::kReady),
+ _canceled(0) {}
+
+std::string NetworkInterfaceASIO::AsyncOp::toString() const {
+ str::stream output;
+ output << "Operation state: ";
+ if (_state == OpState::kReady) {
+ output << "kReady";
+ } else if (_state == OpState::kConnectionAcquired) {
+ output << "kConnectionAcquired";
+ } else if (_state == OpState::kConnectionVerified) {
+ output << "kConnectionVerified";
+ } else if (_state == OpState::kConnected) {
+ output << "kConnected";
+ } else if (_state == OpState::kCompleted) {
+ output << "kCompleted";
+ } else {
+ MONGO_UNREACHABLE;
+ }
+
+ output << "\n";
+ return output;
+}
void NetworkInterfaceASIO::AsyncOp::cancel() {
// An operation may be in mid-flight when it is canceled, so we
@@ -57,14 +83,46 @@ const TaskExecutor::CallbackHandle& NetworkInterfaceASIO::AsyncOp::cbHandle() co
return _cbHandle;
}
-NetworkInterfaceASIO::AsyncConnection& NetworkInterfaceASIO::AsyncOp::connection() {
+void NetworkInterfaceASIO::AsyncOp::connect(ConnectionPool* const pool,
+ asio::io_service* service,
+ Date_t now) {
+ // TODO(amidvidy): why is this hardcoded to 1 second? That seems too low.
+ ConnectionPool::ConnectionPtr conn(pool, _request.target, now, Milliseconds(1000));
+
+ _state = OpState::kConnectionAcquired;
+
+ // TODO: Add a case here for unix domain sockets.
+ int protocol = conn.get()->port().localAddr().getType();
+ if (protocol != AF_INET && protocol != AF_INET6) {
+ throw SocketException(SocketException::CONNECT_ERROR, "Unsupported family");
+ }
+
+ _state = OpState::kConnectionVerified;
+
+ tcp::socket sock{
+ *service, protocol == AF_INET ? tcp::v4() : tcp::v6(), conn.get()->port().psock->rawFD()};
+
+ _connection.emplace(std::move(sock), conn.get()->getServerRPCProtocols(), std::move(conn));
+
+ _state = OpState::kConnected;
+}
+
+NetworkInterfaceASIO::AsyncConnection* NetworkInterfaceASIO::AsyncOp::connection() {
invariant(_connection.is_initialized());
- return *_connection;
+ return _connection.get_ptr();
}
void NetworkInterfaceASIO::AsyncOp::setConnection(AsyncConnection&& conn) {
invariant(!_connection.is_initialized());
_connection = std::move(conn);
+ _state = OpState::kConnected;
+}
+
+bool NetworkInterfaceASIO::AsyncOp::connected() const {
+ return (_state == OpState::kConnected ||
+ // NOTE: if we fail at kConnectionVerified,
+ // ASIO will have closed the socket, don't disconnect
+ _state == OpState::kConnectionAcquired);
}
NetworkInterfaceASIO::AsyncCommand& NetworkInterfaceASIO::AsyncOp::beginCommand(
@@ -89,6 +147,7 @@ NetworkInterfaceASIO::AsyncCommand& NetworkInterfaceASIO::AsyncOp::command() {
void NetworkInterfaceASIO::AsyncOp::finish(const ResponseStatus& status) {
_onFinish(status);
+ _state = OpState::kCompleted;
}
const RemoteCommandRequest& NetworkInterfaceASIO::AsyncOp::request() const {
diff --git a/src/mongo/executor/network_interface_asio_ssl.cpp b/src/mongo/executor/network_interface_asio_ssl.cpp
index 220885f0089..d61cb6ce156 100644
--- a/src/mongo/executor/network_interface_asio_ssl.cpp
+++ b/src/mongo/executor/network_interface_asio_ssl.cpp
@@ -28,92 +28,19 @@
#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kExecutor
-#include "mongo/config.h"
-
-#ifdef MONGO_CONFIG_SSL
-
#include "mongo/platform/basic.h"
#include "mongo/executor/network_interface_asio.h"
-#include "mongo/stdx/functional.h"
-#include "mongo/stdx/memory.h"
-#include "mongo/util/log.h"
-#include "mongo/util/net/ssl_manager.h"
-
namespace mongo {
namespace executor {
-class NetworkInterfaceASIO::AsyncSecureStream final : public AsyncStreamInterface {
-public:
- AsyncSecureStream(asio::io_service* io_service, asio::ssl::context* sslContext)
- : _stream(*io_service, *sslContext) {}
-
- void connect(const asio::ip::tcp::resolver::iterator endpoints,
- ConnectHandler&& connectHandler) override {
- // Stash the connectHandler as we won't be able to call it until we re-enter the state
- // machine.
- _userHandler = std::move(connectHandler);
- asio::async_connect(_stream.lowest_layer(),
- std::move(endpoints),
- [this](std::error_code ec, asio::ip::tcp::resolver::iterator iter) {
- if (ec) {
- return _userHandler(ec);
- }
- return _handleConnect(ec, std::move(iter));
- });
- }
-
- void write(asio::const_buffer buffer, StreamHandler&& streamHandler) override {
- asio::async_write(_stream, asio::buffer(buffer), std::move(streamHandler));
- }
-
- void read(asio::mutable_buffer buffer, StreamHandler&& streamHandler) override {
- asio::async_read(_stream, asio::buffer(buffer), std::move(streamHandler));
- }
+void NetworkInterfaceASIO::_sslHandshake(AsyncOp* op) {
+ // TODO: Implement asynchronous SSL, SERVER-19221
-private:
- void _handleConnect(std::error_code ec, asio::ip::tcp::resolver::iterator iter) {
- _stream.async_handshake(decltype(_stream)::client,
- [this, iter](std::error_code ec) {
- if (ec) {
- return _userHandler(ec);
- }
- return _handleHandshake(ec, iter->host_name());
- });
- }
-
- void _handleHandshake(std::error_code ec, const std::string& hostName) {
- auto certStatus =
- getSSLManager()->parseAndValidatePeerCertificate(_stream.native_handle(), hostName);
- if (!certStatus.isOK()) {
- warning() << certStatus.getStatus();
- return _userHandler(
- // TODO: fix handling of std::error_code w.r.t. codes used by Status
- std::error_code(certStatus.getStatus().code(), std::generic_category()));
- }
- _userHandler(std::error_code());
- }
-
- asio::ssl::stream<asio::ip::tcp::socket> _stream;
- ConnectHandler _userHandler;
-};
-
-void NetworkInterfaceASIO::_setupSecureSocket(AsyncOp* op,
- asio::ip::tcp::resolver::iterator endpoints) {
- auto secureStream = stdx::make_unique<AsyncSecureStream>(&_io_service, _sslContext.get_ptr());
-
- // See TODO in _setupSocket for how this call may change.
- op->setConnection(AsyncConnection(std::move(secureStream), rpc::supports::kOpQueryOnly));
-
- auto& stream = op->connection().stream();
- stream.connect(std::move(endpoints),
- [this, op](std::error_code ec) {
- _validateAndRun(op, ec, [this, op] { _authenticate(op); });
- });
+ // Advance the state machine
+ _runIsMaster(op);
}
} // namespace executor
} // namespace mongo
-
-#endif
diff --git a/src/mongo/util/net/sock.cpp b/src/mongo/util/net/sock.cpp
index 4c34ced371a..d9deed4036e 100644
--- a/src/mongo/util/net/sock.cpp
+++ b/src/mongo/util/net/sock.cpp
@@ -516,7 +516,7 @@ bool Socket::secure(SSLManagerInterface* mgr, const std::string& remoteHost) {
}
_sslManager = mgr;
_sslConnection.reset(_sslManager->connect(this));
- mgr->parseAndValidatePeerCertificateDeprecated(_sslConnection.get(), remoteHost);
+ mgr->parseAndValidatePeerCertificate(_sslConnection.get(), remoteHost);
return true;
}
@@ -534,7 +534,7 @@ std::string Socket::doSSLHandshake(const char* firstBytes, int len) {
remoteString());
}
_sslConnection.reset(_sslManager->accept(this, firstBytes, len));
- return _sslManager->parseAndValidatePeerCertificateDeprecated(_sslConnection.get(), "");
+ return _sslManager->parseAndValidatePeerCertificate(_sslConnection.get(), "");
}
#endif
diff --git a/src/mongo/util/net/ssl_manager.cpp b/src/mongo/util/net/ssl_manager.cpp
index 3f6a42745fb..92a67e13b65 100644
--- a/src/mongo/util/net/ssl_manager.cpp
+++ b/src/mongo/util/net/ssl_manager.cpp
@@ -65,10 +65,6 @@ namespace mongo {
SSLParams sslGlobalParams;
-const SSLParams& getSSLGlobalParams() {
- return sslGlobalParams;
-}
-
#ifdef MONGO_CONFIG_SSL
// Old copies of OpenSSL will not have constants to disable protocols they don't support.
// Define them to values we can OR together safely to generically disable these protocols across
@@ -174,21 +170,12 @@ class SSLManager : public SSLManagerInterface {
public:
explicit SSLManager(const SSLParams& params, bool isServer);
- /**
- * Initializes an OpenSSL context according to the provided settings. Only settings which are
- * acceptable on non-blocking connections are set.
- */
- Status initSSLContext(SSL_CTX* context, const SSLParams& params) final;
-
virtual SSLConnection* connect(Socket* socket);
virtual SSLConnection* accept(Socket* socket, const char* initialBytes, int len);
- virtual std::string parseAndValidatePeerCertificateDeprecated(const SSLConnection* conn,
- const std::string& remoteHost);
-
- StatusWith<boost::optional<std::string>> parseAndValidatePeerCertificate(
- SSL* conn, const std::string& remoteHost) final;
+ virtual std::string parseAndValidatePeerCertificate(const SSLConnection* conn,
+ const std::string& remoteHost);
virtual void cleanupThreadLocals();
@@ -232,10 +219,9 @@ private:
MONGO_COMPILER_NORETURN void _handleSSLError(int code, int ret);
/*
- * Init the SSL context using parameters provided in params. This SSL context will
- * be configured for blocking send/receive.
+ * Init the SSL context using parameters provided in params.
*/
- bool _initSynchronousSSLContext(UniqueSSLContext* context, const SSLParams& params);
+ bool _initSSLContext(UniqueSSLContext* context, const SSLParams& params);
/*
* Converts time from OpenSSL return value to unsigned long long
@@ -337,7 +323,7 @@ MONGO_INITIALIZER(SetupOpenSSL)(InitializerContext*) {
return Status::OK();
}
-MONGO_INITIALIZER_GENERAL(SSLManager, ("SetupOpenSSL"), ("CreateReplicationManager"))
+MONGO_INITIALIZER_WITH_PREREQUISITES(SSLManager, ("SetupOpenSSL"))
(InitializerContext*) {
stdx::lock_guard<SimpleMutex> lck(sslManagerMtx);
if (sslGlobalParams.sslMode.load() != SSLParams::SSLMode_disabled) {
@@ -426,7 +412,7 @@ SSLManager::SSLManager(const SSLParams& params, bool isServer)
_weakValidation(params.sslWeakCertificateValidation),
_allowInvalidCertificates(params.sslAllowInvalidCertificates),
_allowInvalidHostnames(params.sslAllowInvalidHostnames) {
- if (!_initSynchronousSSLContext(&_clientContext, params)) {
+ if (!_initSSLContext(&_clientContext, params)) {
uasserted(16768, "ssl initialization problem");
}
@@ -448,7 +434,7 @@ SSLManager::SSLManager(const SSLParams& params, bool isServer)
}
// SSL server specific initialization
if (isServer) {
- if (!_initSynchronousSSLContext(&_serverContext, params)) {
+ if (!_initSSLContext(&_serverContext, params)) {
uasserted(16562, "ssl initialization problem");
}
@@ -525,7 +511,13 @@ void SSLManager::SSL_free(SSLConnection* conn) {
return ::SSL_free(conn->ssl);
}
-Status SSLManager::initSSLContext(SSL_CTX* context, const SSLParams& params) {
+bool SSLManager::_initSSLContext(UniqueSSLContext* contextPtr, const SSLParams& params) {
+ UniqueSSLContext context(SSL_CTX_new(SSLv23_method()), _free_ssl_context);
+ massert(15864,
+ mongoutils::str::stream()
+ << "can't create SSL Context: " << getSSLErrorMessage(ERR_get_error()),
+ context);
+
// SSL_OP_ALL - Activate all bug workaround options, to support buggy client SSL's.
// SSL_OP_NO_SSLv2 - Disable SSL v2 support
// SSL_OP_NO_SSLv3 - Disable SSL v3 support
@@ -543,7 +535,7 @@ Status SSLManager::initSSLContext(SSL_CTX* context, const SSLParams& params) {
}
}
}
- ::SSL_CTX_set_options(context, supportedProtocols);
+ SSL_CTX_set_options(context.get(), supportedProtocols);
// HIGH - Enable strong ciphers
// !EXPORT - Disable export ciphers (40/56 bit)
@@ -556,58 +548,51 @@ Status SSLManager::initSSLContext(SSL_CTX* context, const SSLParams& params) {
cipherConfig = params.sslCipherConfig;
}
- if (0 == ::SSL_CTX_set_cipher_list(context, cipherConfig.c_str())) {
- return Status(ErrorCodes::InvalidSSLConfiguration,
- str::stream() << "Can not set supported cipher suites: "
- << getSSLErrorMessage(ERR_get_error()));
- }
+ massert(28615,
+ mongoutils::str::stream()
+ << "can't set supported cipher suites: " << getSSLErrorMessage(ERR_get_error()),
+ SSL_CTX_set_cipher_list(context.get(), cipherConfig.c_str()));
- // We use the address of the context as the session id context.
- if (0 == ::SSL_CTX_set_session_id_context(
- context, reinterpret_cast<unsigned char*>(&context), sizeof(context))) {
- return Status(ErrorCodes::InvalidSSLConfiguration,
- str::stream() << "Can not store ssl session id context: "
- << getSSLErrorMessage(ERR_get_error()));
- }
+ // If renegotiation is needed, don't return from recv() or send() until it's successful.
+ // Note: this is for blocking sockets only.
+ SSL_CTX_set_mode(context.get(), SSL_MODE_AUTO_RETRY);
- if (!params.sslClusterFile.empty()) {
- ::EVP_set_pw_prompt("Enter cluster certificate passphrase");
- if (!_setupPEM(context, params.sslClusterFile, params.sslClusterPassword)) {
- return Status(ErrorCodes::InvalidSSLConfiguration, "Can not set up ssl clusterFile.");
+ massert(28607,
+ mongoutils::str::stream()
+ << "can't store ssl session id context: " << getSSLErrorMessage(ERR_get_error()),
+ SSL_CTX_set_session_id_context(
+ context.get(),
+ static_cast<unsigned char*>(static_cast<void*>(contextPtr)),
+ sizeof(*contextPtr)));
+
+ // Use the clusterfile for internal outgoing SSL connections if specified
+ if (contextPtr == &_clientContext && !params.sslClusterFile.empty()) {
+ EVP_set_pw_prompt("Enter cluster certificate passphrase");
+ if (!_setupPEM(context.get(), params.sslClusterFile, params.sslClusterPassword)) {
+ return false;
}
- } else if (!params.sslPEMKeyFile.empty()) {
- // Use the pemfile for everything else
- ::EVP_set_pw_prompt("Enter PEM passphrase");
- if (!_setupPEM(context, params.sslPEMKeyFile, params.sslPEMKeyPassword)) {
- return Status(ErrorCodes::InvalidSSLConfiguration, "Can not set up PEM key file.");
+ }
+ // Use the pemfile for everything else
+ else if (!params.sslPEMKeyFile.empty()) {
+ EVP_set_pw_prompt("Enter PEM passphrase");
+ if (!_setupPEM(context.get(), params.sslPEMKeyFile, params.sslPEMKeyPassword)) {
+ return false;
}
}
if (!params.sslCAFile.empty()) {
// Set up certificate validation with a certificate authority
- if (!_setupCA(context, params.sslCAFile)) {
- return Status(ErrorCodes::InvalidSSLConfiguration, "Can not set up CA file.");
+ if (!_setupCA(context.get(), params.sslCAFile)) {
+ return false;
}
}
if (!params.sslCRLFile.empty()) {
- if (!_setupCRL(context, params.sslCRLFile)) {
- return Status(ErrorCodes::InvalidSSLConfiguration, "Can not set up CRL file.");
+ if (!_setupCRL(context.get(), params.sslCRLFile)) {
+ return false;
}
}
- return Status::OK();
-}
-
-bool SSLManager::_initSynchronousSSLContext(UniqueSSLContext* contextPtr, const SSLParams& params) {
- UniqueSSLContext context(SSL_CTX_new(SSLv23_method()), _free_ssl_context);
-
- uassertStatusOK(initSSLContext(context.get(), params));
-
- // If renegotiation is needed, don't return from recv() or send() until it's successful.
- // Note: this is for blocking sockets only.
- SSL_CTX_set_mode(context.get(), SSL_MODE_AUTO_RETRY);
-
*contextPtr = std::move(context);
return true;
}
@@ -882,38 +867,35 @@ bool SSLManager::_hostNameMatch(const char* nameToMatch, const char* certHostNam
}
}
-StatusWith<boost::optional<std::string>> SSLManager::parseAndValidatePeerCertificate(
- SSL* conn, const std::string& remoteHost) {
+std::string SSLManager::parseAndValidatePeerCertificate(const SSLConnection* conn,
+ const std::string& remoteHost) {
// only set if a CA cert has been provided
if (!_sslConfiguration.hasCA)
- return {boost::none};
+ return "";
- X509* peerCert = SSL_get_peer_certificate(conn);
+ X509* peerCert = SSL_get_peer_certificate(conn->ssl);
if (NULL == peerCert) { // no certificate presented by peer
if (_weakValidation) {
warning() << "no SSL certificate provided by peer" << endl;
} else {
- auto msg = "no SSL certificate provided by peer; connection rejected";
- error() << msg;
- return Status(ErrorCodes::SSLHandshakeFailed, msg);
+ error() << "no SSL certificate provided by peer; connection rejected" << endl;
+ throw SocketException(SocketException::CONNECT_ERROR, "");
}
- return {boost::none};
+ return "";
}
ON_BLOCK_EXIT(X509_free, peerCert);
- long result = SSL_get_verify_result(conn);
+ long result = SSL_get_verify_result(conn->ssl);
if (result != X509_V_OK) {
if (_allowInvalidCertificates) {
warning() << "SSL peer certificate validation failed:"
<< X509_verify_cert_error_string(result);
} else {
- str::stream msg;
- msg << "SSL peer certificate validation failed:"
- << X509_verify_cert_error_string(result);
- error() << msg.ss.str();
- return Status(ErrorCodes::SSLHandshakeFailed, msg);
+ error() << "SSL peer certificate validation failed:"
+ << X509_verify_cert_error_string(result);
+ throw SocketException(SocketException::CONNECT_ERROR, "");
}
}
@@ -923,7 +905,7 @@ StatusWith<boost::optional<std::string>> SSLManager::parseAndValidatePeerCertifi
// If this is an SSL client context (on a MongoDB server or client)
// perform hostname validation of the remote server
if (remoteHost.empty()) {
- return boost::make_optional(peerSubjectName);
+ return peerSubjectName;
}
// Try to match using the Subject Alternate Name, if it exists.
@@ -965,25 +947,12 @@ StatusWith<boost::optional<std::string>> SSLManager::parseAndValidatePeerCertifi
if (_allowInvalidCertificates || _allowInvalidHostnames) {
warning() << "The server certificate does not match the host name " << remoteHost;
} else {
- str::stream msg;
- msg << "The server certificate does not match the host name " << remoteHost;
- error() << msg.ss.str();
- return Status(ErrorCodes::SSLHandshakeFailed, msg);
+ error() << "The server certificate does not match the host name " << remoteHost;
+ throw SocketException(SocketException::CONNECT_ERROR, "");
}
}
- return boost::make_optional(peerSubjectName);
-}
-
-std::string SSLManager::parseAndValidatePeerCertificateDeprecated(const SSLConnection* conn,
- const std::string& remoteHost) {
- auto swPeerSubjectName = parseAndValidatePeerCertificate(conn->ssl, remoteHost);
- // We can't use uassertStatusOK here because we need to throw a SocketException.
- if (!swPeerSubjectName.isOK()) {
- throw SocketException(SocketException::CONNECT_ERROR,
- swPeerSubjectName.getStatus().reason());
- }
- return swPeerSubjectName.getValue().get_value_or("");
+ return peerSubjectName;
}
void SSLManager::cleanupThreadLocals() {
diff --git a/src/mongo/util/net/ssl_manager.h b/src/mongo/util/net/ssl_manager.h
index f9bd9b51484..1c6295ed517 100644
--- a/src/mongo/util/net/ssl_manager.h
+++ b/src/mongo/util/net/ssl_manager.h
@@ -27,7 +27,6 @@
#pragma once
-#include <boost/optional.hpp>
#include <memory>
#include <string>
@@ -110,13 +109,9 @@ public:
* Fetches a peer certificate and validates it if it exists
* Throws SocketException on failure
* @return a std::string containing the certificate's subject name.
- *
- * This version of parseAndValidatePeerCertificate is deprecated because it throws a
- * SocketException upon failure. New code should prefer the version that returns
- * a StatusWith instead.
*/
- virtual std::string parseAndValidatePeerCertificateDeprecated(
- const SSLConnection* conn, const std::string& remoteHost) = 0;
+ virtual std::string parseAndValidatePeerCertificate(const SSLConnection* conn,
+ const std::string& remoteHost) = 0;
/**
* Cleans up SSL thread local memory; use at thread exit
@@ -151,33 +146,11 @@ public:
virtual int SSL_shutdown(SSLConnection* conn) = 0;
virtual void SSL_free(SSLConnection* conn) = 0;
-
- /**
- * Initializes an OpenSSL context according to the provided settings. Only settings which are
- * acceptable on non-blocking connections are set.
- */
- virtual Status initSSLContext(SSL_CTX* context, const SSLParams& params) = 0;
-
- /**
- * Fetches a peer certificate and validates it if it exists. If validation fails, but weak
- * validation is enabled, boost::none will be returned. If validation fails, and invalid
- * certificates are not allowed, a non-OK status will be returned. If validation is successful,
- * an engaged optional containing the certificate's subject name will be returned.
- */
- virtual StatusWith<boost::optional<std::string>> parseAndValidatePeerCertificate(
- SSL* ssl, const std::string& remoteHost) = 0;
};
// Access SSL functions through this instance.
SSLManagerInterface* getSSLManager();
extern bool isSSLServer;
-
-/**
- * The global SSL configuration. This should be accessed only after global initialization has
- * completed. If it must be accessed in an initializer, the initializer should have
- * "EndStartupOptionStorage" as a prerequisite.
- */
-const SSLParams& getSSLGlobalParams();
}
#endif // #ifdef MONGO_CONFIG_SSL
diff --git a/src/third_party/asio-asio-1-11-0/SConscript b/src/third_party/asio-asio-1-11-0/SConscript
index 242bb7db889..afa03658783 100644
--- a/src/third_party/asio-asio-1-11-0/SConscript
+++ b/src/third_party/asio-asio-1-11-0/SConscript
@@ -1,14 +1,8 @@
Import("env")
-Import("has_option")
-
-asio_src = [
- "asio/src/asio.cpp",
-]
-
-if has_option("ssl"):
- asio_src.append("asio/src/asio_ssl.cpp")
env.Library(
target="asio",
- source=asio_src
+ source=[
+ "asio/src/asio.cpp",
+ ]
)