/** * Copyright (C) 2018-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Server Side Public License for more details. * * You should have received a copy of the Server Side Public License * along with this program. If not, see * . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the Server Side Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #pragma once #ifndef _WIN32 #include #endif #include #include "mongo/base/status.h" #include "mongo/base/string_data.h" #include "mongo/base/system_error.h" #include "mongo/config.h" #include "mongo/logv2/log_severity.h" #include "mongo/stdx/type_traits.h" #include "mongo/util/errno_util.h" #include "mongo/util/future.h" #include "mongo/util/hex.h" #include "mongo/util/net/hostandport.h" #include "mongo/util/net/sockaddr.h" #include "mongo/util/net/ssl_manager.h" #include "mongo/util/net/ssl_options.h" namespace mongo::transport { inline SockAddr endpointToSockAddr(const asio::generic::stream_protocol::endpoint& endPoint) { SockAddr wrappedAddr(endPoint.data(), endPoint.size()); return wrappedAddr; } // Utility function to turn an ASIO endpoint into a mongo HostAndPort inline HostAndPort endpointToHostAndPort(const asio::generic::stream_protocol::endpoint& endPoint) { return HostAndPort(endpointToSockAddr(endPoint).toString(true)); } Status errorCodeToStatus(const std::error_code& ec); /** * The ASIO implementation of poll (i.e. socket.wait()) cannot poll for a mask of events, and * doesn't support timeouts. * * This wraps up ::select/::poll for Windows/POSIX for a single socket and handles EINTR on POSIX * * - On timeout: it returns Status(ErrorCodes::NetworkTimeout) * - On poll returning with an event: it returns the EventsMask for the socket, the caller must * check whether it matches the expected events mask. * - On error: it returns a Status(ErrorCodes::InternalError) */ StatusWith pollASIOSocket(asio::generic::stream_protocol::socket& socket, unsigned mask, Milliseconds timeout); /** * Attempts to fill up the passed in buffer sequence with bytes from the underlying stream * without blocking. Returns the number of bytes we were actually able to fill in. Throws * on failure to read socket for reasons other than blocking. */ template size_t peekASIOStream(Stream& stream, const MutableBufferSequence& buffers) { std::error_code ec; size_t bytesRead; do { bytesRead = stream.receive(buffers, stream.message_peek, ec); } while (ec == asio::error::interrupted); // On a completely empty socket, receive returns 0 bytes read and sets // the error code to either would_block or try_again. Since this isn't // actually an error condition for our purposes, we ignore these two // errors. if (ec != asio::error::would_block && ec != asio::error::try_again) { uassertStatusOK(errorCodeToStatus(ec)); } return bytesRead; } #ifdef MONGO_CONFIG_SSL /** * Peeks at a fragment of a client issued TLS handshake packet. Returns a TLS alert * packet if the client has selected a protocol which has been disabled by the server. */ boost::optional> checkTLSRequest(const asio::const_buffer& buffer); #endif /** * setSocketOption failed. Log the error. * This is in the .cpp file just to keep LOGV2 out of this header. */ void failedSetSocketOption(const std::system_error& ex, StringData note, BSONObj optionDescription, logv2::LogSeverity errorLogSeverity); /** * Calls Asio `socket.set_option(opt)` with better failure diagnostics. * To be used instead of Asio `socket.set_option``, because errors are hard to diagnose. * Emits a log message about what option was attempted and what went wrong with * it. The `note` string should uniquely identify the source of the call. * * Two overloads are provided matching the Asio `socket.set_option` overloads, with an additional * parameter to indicate the level at which the failure diagnostics should logged. * * setSocketOption(socket, opt, note, errorLogSeverity) * setSocketOption(socket, opt, note, errorLogSeverity, ec) * * If an `ec` is provided, errors are reported by mutating it. * Otherwise, the Asio `std::system_error` exception is rethrown. */ template void setSocketOption(Socket& socket, const Option& opt, StringData note, logv2::LogSeverity errorLogSeverity) { try { socket.set_option(opt); } catch (const std::system_error& ex) { BSONObj optionDescription = [&opt, p = socket.local_endpoint().protocol()] { return BSONObjBuilder{} .append("level", opt.level(p)) .append("name", opt.name(p)) .append("data", hexdump(opt.data(p), opt.size(p))) .obj(); }(); auto&& p = socket.local_endpoint().protocol(); failedSetSocketOption(ex, note, BSONObjBuilder{} .append("level", opt.level(p)) .append("name", opt.name(p)) .append("data", hexdump(opt.data(p), opt.size(p))) .obj(), errorLogSeverity); throw; } } template void setSocketOption(Socket& socket, const Option& opt, StringData note, logv2::LogSeverity errorLogSeverity, std::error_code& ec) { try { setSocketOption(socket, opt, note, errorLogSeverity); } catch (const std::system_error& ex) { ec = ex.code(); } } /** * Pass this to asio functions in place of a callback to have them return a Future. This behaves * similarly to asio::use_future_t, however it returns a mongo::Future rather than a * std::future. * * The type of the Future will be determined by the arguments that the callback would have if one * was used. If the arguments start with std::error_code, it will be used to set the Status of the * Future and will not affect the Future's type. For the remaining arguments: * - if none: Future * - if one: Future * - more than one: Future> * * Example: * Future future = my_socket.async_read_some(my_buffer, UseFuture{}); */ struct UseFuture { template class Adapter; }; template class UseFuture::Adapter { private: template struct ArgPack : stdx::type_identity> {}; template struct ArgPack : stdx::type_identity {}; template struct ArgPack : stdx::type_identity {}; /** * If an Asio callback takes a leading error_code, it's stripped from * the Future's value_type. Any errors reported by Asio will instead * be delivered by setting the Future's error Status. */ template struct StripError : ArgPack {}; template struct StripError : ArgPack {}; using Result = typename StripError::type; struct Handler { private: template void _onSuccess(As&&... args) { promise.emplaceValue(std::forward(args)...); } template void _onInvoke(As&&... args) { _onSuccess(std::forward(args)...); } template void _onInvoke(std::error_code ec, As&&... args) { if (ec) { promise.setError(errorCodeToStatus(ec)); return; } _onSuccess(std::forward(args)...); } public: explicit Handler(const UseFuture&) {} template void operator()(As&&... args) { static_assert((std::is_same_v, std::decay_t> && ...), "Unexpected argument list from Asio async result callback."); _onInvoke(std::forward(args)...); } Promise promise; }; public: using return_type = Future; using completion_handler_type = Handler; explicit Adapter(Handler& handler) { auto&& [p, f] = makePromiseFuture(); _fut = std::move(f); handler.promise = std::move(p); } return_type get() { return std::move(_fut); } private: Future _fut; }; } // namespace mongo::transport namespace asio { template class async_result : public mongo::transport::UseFuture::Adapter { using mongo::transport::UseFuture::Adapter::Adapter; }; } // namespace asio