diff options
author | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2016-08-19 11:26:05 -0400 |
---|---|---|
committer | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2016-08-19 11:43:31 -0400 |
commit | 78c06c7e342bb159bcf22c85502cb7db3a695ffd (patch) | |
tree | 2ac566e4a34dd2945db79e949cc5c94d8463e034 | |
parent | 1a3d60af4d27d72e15637bb43510fe1b592a6c43 (diff) | |
download | mongo-78c06c7e342bb159bcf22c85502cb7db3a695ffd.tar.gz |
SERVER-25393 Disallow mongos making connections to older versions
-rw-r--r-- | jstests/libs/override_methods/multiversion_override_balancer_control.js | 21 | ||||
-rw-r--r-- | jstests/multiVersion/2_test_launching_cluster.js | 6 | ||||
-rw-r--r-- | src/mongo/client/dbclient.cpp | 16 | ||||
-rw-r--r-- | src/mongo/db/db.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/dbdirectclient.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/repl/replication_info.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/s/set_shard_version_command.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/wire_version.h | 38 | ||||
-rw-r--r-- | src/mongo/dbtests/dbtests.cpp | 8 | ||||
-rw-r--r-- | src/mongo/executor/network_interface_asio_auth.cpp | 11 | ||||
-rw-r--r-- | src/mongo/executor/network_interface_asio_connect.cpp | 3 | ||||
-rw-r--r-- | src/mongo/executor/network_interface_asio_test.cpp | 16 | ||||
-rw-r--r-- | src/mongo/rpc/protocol.cpp | 73 | ||||
-rw-r--r-- | src/mongo/rpc/protocol.h | 22 | ||||
-rw-r--r-- | src/mongo/rpc/protocol_test.cpp | 74 | ||||
-rw-r--r-- | src/mongo/s/client/sharding_network_connection_hook.cpp | 15 | ||||
-rw-r--r-- | src/mongo/s/commands/cluster_is_master_cmd.cpp | 4 | ||||
-rw-r--r-- | src/mongo/s/server.cpp | 8 |
18 files changed, 252 insertions, 83 deletions
diff --git a/jstests/libs/override_methods/multiversion_override_balancer_control.js b/jstests/libs/override_methods/multiversion_override_balancer_control.js index aa4c27a9fde..b24d209a7d6 100644 --- a/jstests/libs/override_methods/multiversion_override_balancer_control.js +++ b/jstests/libs/override_methods/multiversion_override_balancer_control.js @@ -19,6 +19,27 @@ arguments[0].other = {}; } + // Do not start the mongos server for controlling the balancer if the config server is 3.2. + var skipControlMongoS = false; + + var configOptions = arguments[0].other.configOptions; + if (configOptions && configOptions.binVersion == "last-stable") { + skipControlMongoS = true; + } + + // Check if config server options were specified as an array instead. + if (arguments[0].config && Array.isArray(arguments[0].config) && + arguments[0].config[0].binVersion == "last-stable") { + skipControlMongoS = true; + } + + if (skipControlMongoS) { + // Construct the original object and skip creating the separate mongos for controlling + // the balancer. + originalShardingTest.apply(this, arguments); + return; + } + var originalEnableBalancer = arguments[0].other.enableBalancer; if (!originalEnableBalancer) { arguments[0].other.enableBalancer = true; diff --git a/jstests/multiVersion/2_test_launching_cluster.js b/jstests/multiVersion/2_test_launching_cluster.js index eb0ca105947..1d126e79c85 100644 --- a/jstests/multiVersion/2_test_launching_cluster.js +++ b/jstests/multiVersion/2_test_launching_cluster.js @@ -20,8 +20,9 @@ load('./jstests/multiVersion/libs/verify_versions.js'); mongosOptions: {binVersion: versionsToCheckMongos}, configOptions: {binVersion: versionsToCheck}, shardOptions: {binVersion: versionsToCheck}, + enableBalancer: true, // TODO: SERVER-24163 remove after v3.4 - waitForCSRSSecondaries: false + waitForCSRSSecondaries: false, } }); @@ -63,8 +64,9 @@ load('./jstests/multiVersion/libs/verify_versions.js'); mongosOptions: {binVersion: versionsToCheckMongos}, configOptions: {binVersion: versionsToCheck}, rsOptions: {binVersion: versionsToCheck, protocolVersion: 0}, + enableBalancer: true, // TODO: SERVER-24163 remove after v3.4 - waitForCSRSSecondaries: false + waitForCSRSSecondaries: false, } }); diff --git a/src/mongo/client/dbclient.cpp b/src/mongo/client/dbclient.cpp index 51f5fe04c94..87a8f39fd3b 100644 --- a/src/mongo/client/dbclient.cpp +++ b/src/mongo/client/dbclient.cpp @@ -802,12 +802,18 @@ Status DBClientConnection::connect(const HostAndPort& serverAddress, StringData return swProtocolSet.getStatus(); } - _setServerRPCProtocols(swProtocolSet.getValue()); + auto validateStatus = + rpc::validateWireVersion(WireSpec::instance().outgoing, swProtocolSet.getValue().version); + if (!validateStatus.isOK()) { + warning() << "remote host has incompatible wire version: " << validateStatus; - auto negotiatedProtocol = - rpc::negotiate(getServerRPCProtocols(), - rpc::computeProtocolSet(WireSpec::instance().minWireVersionOutgoing, - WireSpec::instance().maxWireVersionOutgoing)); + return validateStatus; + } + + _setServerRPCProtocols(swProtocolSet.getValue().protocolSet); + + auto negotiatedProtocol = rpc::negotiate( + getServerRPCProtocols(), rpc::computeProtocolSet(WireSpec::instance().outgoing)); if (!negotiatedProtocol.isOK()) { return negotiatedProtocol.getStatus(); diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index c10f85b94c4..57d0c52a840 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -468,11 +468,11 @@ static void repairDatabasesAndCheckVersion(OperationContext* txn) { static void _initWireSpec() { WireSpec& spec = WireSpec::instance(); // accept from any version - spec.minWireVersionIncoming = RELEASE_2_4_AND_BEFORE; - spec.maxWireVersionIncoming = COMMANDS_ACCEPT_WRITE_CONCERN; + spec.incoming.minWireVersion = RELEASE_2_4_AND_BEFORE; + spec.incoming.maxWireVersion = COMMANDS_ACCEPT_WRITE_CONCERN; // connect to any version - spec.minWireVersionOutgoing = RELEASE_2_4_AND_BEFORE; - spec.maxWireVersionOutgoing = COMMANDS_ACCEPT_WRITE_CONCERN; + spec.outgoing.minWireVersion = RELEASE_2_4_AND_BEFORE; + spec.outgoing.maxWireVersion = COMMANDS_ACCEPT_WRITE_CONCERN; } diff --git a/src/mongo/db/dbdirectclient.cpp b/src/mongo/db/dbdirectclient.cpp index 06c06991722..f4bf62ed644 100644 --- a/src/mongo/db/dbdirectclient.cpp +++ b/src/mongo/db/dbdirectclient.cpp @@ -89,12 +89,12 @@ std::string DBDirectClient::getServerAddress() const { // Returned version should match the incoming connections restrictions. int DBDirectClient::getMinWireVersion() { - return WireSpec::instance().minWireVersionIncoming; + return WireSpec::instance().incoming.minWireVersion; } // Returned version should match the incoming connections restrictions. int DBDirectClient::getMaxWireVersion() { - return WireSpec::instance().maxWireVersionIncoming; + return WireSpec::instance().incoming.maxWireVersion; } ConnectionString::ConnectionType DBDirectClient::type() const { diff --git a/src/mongo/db/repl/replication_info.cpp b/src/mongo/db/repl/replication_info.cpp index 90dd3fb451a..d27257a8eb0 100644 --- a/src/mongo/db/repl/replication_info.cpp +++ b/src/mongo/db/repl/replication_info.cpp @@ -286,8 +286,8 @@ public: result.appendNumber("maxMessageSizeBytes", MaxMessageSizeBytes); result.appendNumber("maxWriteBatchSize", BatchedCommandRequest::kMaxWriteBatchSize); result.appendDate("localTime", jsTime()); - result.append("maxWireVersion", WireSpec::instance().maxWireVersionIncoming); - result.append("minWireVersion", WireSpec::instance().minWireVersionIncoming); + result.append("maxWireVersion", WireSpec::instance().incoming.maxWireVersion); + result.append("minWireVersion", WireSpec::instance().incoming.minWireVersion); result.append("readOnly", storageGlobalParams.readOnly); const auto parameter = mapFindWithDefault(ServerParameterSet::getGlobal()->getMap(), diff --git a/src/mongo/db/s/set_shard_version_command.cpp b/src/mongo/db/s/set_shard_version_command.cpp index b19e72c9489..de56695a4cb 100644 --- a/src/mongo/db/s/set_shard_version_command.cpp +++ b/src/mongo/db/s/set_shard_version_command.cpp @@ -148,8 +148,8 @@ public: // TODO: SERVER-21397 remove post v3.3. // Send back wire version to let mongos know what protocol we can speak - result.append("minWireVersion", WireSpec::instance().minWireVersionIncoming); - result.append("maxWireVersion", WireSpec::instance().maxWireVersionIncoming); + result.append("minWireVersion", WireSpec::instance().incoming.minWireVersion); + result.append("maxWireVersion", WireSpec::instance().incoming.maxWireVersion); return true; } diff --git a/src/mongo/db/wire_version.h b/src/mongo/db/wire_version.h index 86f629e0ca7..de816c0ae1b 100644 --- a/src/mongo/db/wire_version.h +++ b/src/mongo/db/wire_version.h @@ -26,6 +26,10 @@ * it in the license file. */ +#pragma once + +#include "mongo/base/disallow_copying.h" + namespace mongo { /** @@ -63,6 +67,14 @@ enum WireVersion { COMMANDS_ACCEPT_WRITE_CONCERN = 5, }; +/** + * Struct to pass around information about wire version. + */ +struct WireVersionInfo { + int minWireVersion; + int maxWireVersion; +}; + struct WireSpec { MONGO_DISALLOW_COPYING(WireSpec); @@ -71,18 +83,20 @@ struct WireSpec { return instance; } - // Minimum version that the server accepts on incoming requests. We should bump this whenever - // we don't want to allow incoming connections from clients that are too old. - int minWireVersionIncoming; - // Latest version that the server accepts on incoming requests. This should always be at the - // latest entry in WireVersion. - int maxWireVersionIncoming; - - // Minimum version allowed on remote nodes when the server sends requests. We should bump this - // whenever we don't want to connect to clients that are too old. - int minWireVersionOutgoing; - // Latest version allowed on remote nodes when the server sends requests. - int maxWireVersionOutgoing; + // incoming.minWireVersion - Minimum version that the server accepts on incoming requests. We + // should bump this whenever we don't want to allow incoming connections from clients that are + // too old. + + // incoming.maxWireVersion - Latest version that the server accepts on incoming requests. This + // should always be at the latest entry in WireVersion. + WireVersionInfo incoming; + + // outgoing.minWireVersion - Minimum version allowed on remote nodes when the server sends + // requests. We should bump this whenever we don't want to connect to clients that are too old. + + // outgoing.maxWireVersion - Latest version allowed on remote nodes when the server sends + // requests. + WireVersionInfo outgoing; private: WireSpec() = default; diff --git a/src/mongo/dbtests/dbtests.cpp b/src/mongo/dbtests/dbtests.cpp index d9221e0944e..96ad9253f08 100644 --- a/src/mongo/dbtests/dbtests.cpp +++ b/src/mongo/dbtests/dbtests.cpp @@ -58,11 +58,11 @@ namespace dbtests { void initWireSpec() { WireSpec& spec = WireSpec::instance(); // accept from any version - spec.minWireVersionIncoming = RELEASE_2_4_AND_BEFORE; - spec.maxWireVersionIncoming = COMMANDS_ACCEPT_WRITE_CONCERN; + spec.incoming.minWireVersion = RELEASE_2_4_AND_BEFORE; + spec.incoming.maxWireVersion = COMMANDS_ACCEPT_WRITE_CONCERN; // connect to any version - spec.minWireVersionOutgoing = RELEASE_2_4_AND_BEFORE; - spec.maxWireVersionOutgoing = COMMANDS_ACCEPT_WRITE_CONCERN; + spec.outgoing.minWireVersion = RELEASE_2_4_AND_BEFORE; + spec.outgoing.maxWireVersion = COMMANDS_ACCEPT_WRITE_CONCERN; } Status createIndex(OperationContext* txn, StringData ns, const BSONObj& keys, bool unique) { diff --git a/src/mongo/executor/network_interface_asio_auth.cpp b/src/mongo/executor/network_interface_asio_auth.cpp index d2e341007e6..78c66318805 100644 --- a/src/mongo/executor/network_interface_asio_auth.cpp +++ b/src/mongo/executor/network_interface_asio_auth.cpp @@ -39,6 +39,7 @@ #include "mongo/db/auth/internal_user_auth.h" #include "mongo/db/commands.h" #include "mongo/db/server_options.h" +#include "mongo/db/wire_version.h" #include "mongo/rpc/factory.h" #include "mongo/rpc/get_status_from_command_result.h" #include "mongo/rpc/legacy_request_builder.h" @@ -109,7 +110,15 @@ void NetworkInterfaceASIO::_runIsMaster(AsyncOp* op) { if (!protocolSet.isOK()) return _completeOperation(op, protocolSet.getStatus()); - op->connection().setServerProtocols(protocolSet.getValue()); + auto validateStatus = + rpc::validateWireVersion(WireSpec::instance().outgoing, protocolSet.getValue().version); + if (!validateStatus.isOK()) { + warning() << "remote host has incompatible wire version: " << validateStatus; + + return _completeOperation(op, validateStatus); + } + + op->connection().setServerProtocols(protocolSet.getValue().protocolSet); invariant(op->connection().clientProtocols() != rpc::supports::kNone); // Set the operation protocol diff --git a/src/mongo/executor/network_interface_asio_connect.cpp b/src/mongo/executor/network_interface_asio_connect.cpp index ffd392e15c7..57a9f302b58 100644 --- a/src/mongo/executor/network_interface_asio_connect.cpp +++ b/src/mongo/executor/network_interface_asio_connect.cpp @@ -52,8 +52,7 @@ NetworkInterfaceASIO::AsyncConnection::AsyncConnection(std::unique_ptr<AsyncStre rpc::ProtocolSet protocols) : _stream(std::move(stream)), _serverProtocols(protocols), - _clientProtocols(rpc::computeProtocolSet(WireSpec::instance().minWireVersionOutgoing, - WireSpec::instance().maxWireVersionOutgoing)) {} + _clientProtocols(rpc::computeProtocolSet(WireSpec::instance().outgoing)) {} AsyncStreamInterface& NetworkInterfaceASIO::AsyncConnection::stream() { return *_stream; diff --git a/src/mongo/executor/network_interface_asio_test.cpp b/src/mongo/executor/network_interface_asio_test.cpp index 0c1117a1693..a7d894a7deb 100644 --- a/src/mongo/executor/network_interface_asio_test.cpp +++ b/src/mongo/executor/network_interface_asio_test.cpp @@ -59,11 +59,11 @@ HostAndPort testHost{"localhost", 20000}; void initWireSpecMongoD() { WireSpec& spec = WireSpec::instance(); // accept from any version - spec.minWireVersionIncoming = RELEASE_2_4_AND_BEFORE; - spec.maxWireVersionIncoming = COMMANDS_ACCEPT_WRITE_CONCERN; + spec.incoming.minWireVersion = RELEASE_2_4_AND_BEFORE; + spec.incoming.maxWireVersion = COMMANDS_ACCEPT_WRITE_CONCERN; // connect to any version - spec.minWireVersionOutgoing = RELEASE_2_4_AND_BEFORE; - spec.maxWireVersionOutgoing = COMMANDS_ACCEPT_WRITE_CONCERN; + spec.outgoing.minWireVersion = RELEASE_2_4_AND_BEFORE; + spec.outgoing.maxWireVersion = COMMANDS_ACCEPT_WRITE_CONCERN; } // Utility function to use with mock streams @@ -72,9 +72,9 @@ RemoteCommandResponse simulateIsMaster(RemoteCommandRequest request) { ASSERT_EQ(request.dbname, "admin"); RemoteCommandResponse response; - response.data = BSON("minWireVersion" << mongo::WireSpec::instance().minWireVersionIncoming + response.data = BSON("minWireVersion" << mongo::WireSpec::instance().incoming.minWireVersion << "maxWireVersion" - << mongo::WireSpec::instance().maxWireVersionIncoming); + << mongo::WireSpec::instance().incoming.maxWireVersion); return response; } @@ -671,9 +671,9 @@ TEST_F(NetworkInterfaceASIOConnectionHookTest, ValidateHostInvalid) { rpc::Protocol::kOpQuery, [](RemoteCommandRequest request) -> RemoteCommandResponse { RemoteCommandResponse response; response.data = - BSON("minWireVersion" << mongo::WireSpec::instance().minWireVersionIncoming + BSON("minWireVersion" << mongo::WireSpec::instance().incoming.minWireVersion << "maxWireVersion" - << mongo::WireSpec::instance().maxWireVersionIncoming + << mongo::WireSpec::instance().incoming.maxWireVersion << "TESTKEY" << "TESTVALUE"); return response; diff --git a/src/mongo/rpc/protocol.cpp b/src/mongo/rpc/protocol.cpp index 32b9f5b4442..90e7ad58389 100644 --- a/src/mongo/rpc/protocol.cpp +++ b/src/mongo/rpc/protocol.cpp @@ -108,7 +108,8 @@ StatusWith<ProtocolSet> parseProtocolSet(StringData repr) { << "and 'all' (0x3) are supported."); } -StatusWith<ProtocolSet> parseProtocolSetFromIsMasterReply(const BSONObj& isMasterReply) { +StatusWith<ProtocolSetAndWireVersionInfo> parseProtocolSetFromIsMasterReply( + const BSONObj& isMasterReply) { long long maxWireVersion; auto maxWireExtractStatus = bsonExtractIntegerField(isMasterReply, "maxWireVersion", &maxWireVersion); @@ -120,7 +121,7 @@ StatusWith<ProtocolSet> parseProtocolSetFromIsMasterReply(const BSONObj& isMaste // MongoDB 2.4 and earlier do not have maxWireVersion/minWireVersion in their 'isMaster' replies if ((maxWireExtractStatus == minWireExtractStatus) && (maxWireExtractStatus == ErrorCodes::NoSuchKey)) { - return supports::kOpQueryOnly; + return {{supports::kOpQueryOnly, {0, 0}}}; } else if (!maxWireExtractStatus.isOK()) { return maxWireExtractStatus; } else if (!minWireExtractStatus.isOK()) { @@ -140,29 +141,77 @@ StatusWith<ProtocolSet> parseProtocolSetFromIsMasterReply(const BSONObj& isMaste isMongos = (msgField == "isdbgrid"); } - return (!isMongos && supportsWireVersionForOpCommandInMongod(minWireVersion, maxWireVersion)) - ? supports::kAll - : supports::kOpQueryOnly; + if (minWireVersion < 0 || maxWireVersion < 0 || + minWireVersion >= std::numeric_limits<int>::max() || + maxWireVersion >= std::numeric_limits<int>::max()) { + return Status(ErrorCodes::BadValue, + str::stream() << "Server min and max wire version have invalid values (" + << minWireVersion + << "," + << maxWireVersion + << ")"); + } + + WireVersionInfo version{static_cast<int>(minWireVersion), static_cast<int>(maxWireVersion)}; + + return {{(!isMongos && supportsWireVersionForOpCommandInMongod(version)) + ? supports::kAll + : supports::kOpQueryOnly, + version}}; } -bool supportsWireVersionForOpCommandInMongod(int minWireVersion, int maxWireVersion) { +bool supportsWireVersionForOpCommandInMongod(const WireVersionInfo version) { // FIND_COMMAND versions support OP_COMMAND (in mongod but not mongos). - return (minWireVersion <= WireVersion::FIND_COMMAND) && - (maxWireVersion >= WireVersion::FIND_COMMAND); + return (version.minWireVersion <= WireVersion::FIND_COMMAND) && + (version.maxWireVersion >= WireVersion::FIND_COMMAND); } -ProtocolSet computeProtocolSet(int minWireVersion, int maxWireVersion) { +ProtocolSet computeProtocolSet(const WireVersionInfo version) { ProtocolSet result = supports::kNone; - if (minWireVersion <= maxWireVersion) { - if (maxWireVersion >= WireVersion::FIND_COMMAND) { + if (version.minWireVersion <= version.maxWireVersion) { + if (version.maxWireVersion >= WireVersion::FIND_COMMAND) { result |= supports::kOpCommandOnly; } - if (minWireVersion <= WireVersion::RELEASE_2_4_AND_BEFORE) { + if (version.minWireVersion <= WireVersion::RELEASE_2_4_AND_BEFORE) { result |= supports::kOpQueryOnly; } } return result; } +Status validateWireVersion(const WireVersionInfo client, const WireVersionInfo server) { + // Since this is defined in the code, it should always hold true since this is the versions that + // mongos/d wants to connect to. + invariant(client.minWireVersion <= client.maxWireVersion); + + // Server may return bad data. + if (server.minWireVersion > server.maxWireVersion) { + return Status(ErrorCodes::BadValue, + str::stream() << "Server min and max wire version are incorrect (" + << server.minWireVersion + << "," + << server.maxWireVersion + << ")"); + } + + // Determine if the [min, max] tuples overlap. + // We assert the invariant that min < max above. + if (!(client.minWireVersion <= server.maxWireVersion && + client.maxWireVersion >= server.minWireVersion)) { + return Status(ErrorCodes::BadValue, + str::stream() << "Server min and max wire version are incompatible (" + << server.minWireVersion + << "," + << server.maxWireVersion + << ") with client min wire version (" + << client.minWireVersion + << "," + << client.maxWireVersion + << ")"); + } + + return Status::OK(); +} + } // namespace rpc } // namespace mongo diff --git a/src/mongo/rpc/protocol.h b/src/mongo/rpc/protocol.h index 45280849c29..6ac205ccc2f 100644 --- a/src/mongo/rpc/protocol.h +++ b/src/mongo/rpc/protocol.h @@ -33,6 +33,7 @@ #include <type_traits> #include "mongo/base/status_with.h" +#include "mongo/db/wire_version.h" namespace mongo { class BSONObj; @@ -108,19 +109,34 @@ StatusWith<StringData> toString(ProtocolSet protocols); StatusWith<ProtocolSet> parseProtocolSet(StringData repr); /** + * Validates client and server wire version. The server is returned from isMaster, and the client is + * from WireSpec.instance(). + */ +Status validateWireVersion(const WireVersionInfo client, const WireVersionInfo server); + +/** + * Struct to pass around information about protocol set and wire version. + */ +struct ProtocolSetAndWireVersionInfo { + ProtocolSet protocolSet; + WireVersionInfo version; +}; + +/** * Determines the ProtocolSet of a remote server from an isMaster reply. */ -StatusWith<ProtocolSet> parseProtocolSetFromIsMasterReply(const BSONObj& isMasterReply); +StatusWith<ProtocolSetAndWireVersionInfo> parseProtocolSetFromIsMasterReply( + const BSONObj& isMasterReply); /** * Returns true if wire version supports OP_COMMAND in mongod (not mongos). */ -bool supportsWireVersionForOpCommandInMongod(int minWireVersion, int maxWireVersion); +bool supportsWireVersionForOpCommandInMongod(const WireVersionInfo version); /** * Computes supported protocols from wire versions. */ -ProtocolSet computeProtocolSet(int minWireVersion, int maxWireVersion); +ProtocolSet computeProtocolSet(const WireVersionInfo version); } // namespace rpc } // namespace mongo diff --git a/src/mongo/rpc/protocol_test.cpp b/src/mongo/rpc/protocol_test.cpp index 7ee5abd09ac..a02d8de5159 100644 --- a/src/mongo/rpc/protocol_test.cpp +++ b/src/mongo/rpc/protocol_test.cpp @@ -76,7 +76,8 @@ TEST(Protocol, parseProtocolSetFromIsMasterReply) { << "minWireVersion" << static_cast<int>(WireVersion::RELEASE_2_4_AND_BEFORE)); - ASSERT_EQ(assertGet(parseProtocolSetFromIsMasterReply(mongod32)), supports::kAll); + ASSERT_EQ(assertGet(parseProtocolSetFromIsMasterReply(mongod32)).protocolSet, + supports::kAll); } { // MongoDB 3.2 (mongos) @@ -87,19 +88,84 @@ TEST(Protocol, parseProtocolSetFromIsMasterReply) { << "msg" << "isdbgrid"); - ASSERT_EQ(assertGet(parseProtocolSetFromIsMasterReply(mongos32)), supports::kOpQueryOnly); + ASSERT_EQ(assertGet(parseProtocolSetFromIsMasterReply(mongos32)).protocolSet, + supports::kOpQueryOnly); } { // MongoDB 3.0 (mongod) auto mongod30 = BSON( "maxWireVersion" << static_cast<int>(WireVersion::RELEASE_2_7_7) << "minWireVersion" << static_cast<int>(WireVersion::RELEASE_2_4_AND_BEFORE)); - ASSERT_EQ(assertGet(parseProtocolSetFromIsMasterReply(mongod30)), supports::kOpQueryOnly); + ASSERT_EQ(assertGet(parseProtocolSetFromIsMasterReply(mongod30)).protocolSet, + supports::kOpQueryOnly); } { auto mongod24 = BSONObj(); - ASSERT_EQ(assertGet(parseProtocolSetFromIsMasterReply(mongod24)), supports::kOpQueryOnly); + ASSERT_EQ(assertGet(parseProtocolSetFromIsMasterReply(mongod24)).protocolSet, + supports::kOpQueryOnly); } } +#define VALIDATE_WIRE_VERSION(macro, clientMin, clientMax, serverMin, serverMax) \ + do { \ + auto msg = BSON("minWireVersion" << static_cast<int>(serverMin) << "maxWireVersion" \ + << static_cast<int>(serverMax)); \ + auto swReply = parseProtocolSetFromIsMasterReply(msg); \ + ASSERT_OK(swReply.getStatus()); \ + macro(validateWireVersion({clientMin, clientMax}, swReply.getValue().version)); \ + } while (0); + +TEST(Protocol, validateWireVersion) { + // Base Test + VALIDATE_WIRE_VERSION(ASSERT_OK, + WireVersion::RELEASE_2_4_AND_BEFORE, + WireVersion::COMMANDS_ACCEPT_WRITE_CONCERN, + WireVersion::RELEASE_2_4_AND_BEFORE, + WireVersion::COMMANDS_ACCEPT_WRITE_CONCERN); + + // Allowed during upgrade + // MongoD 3.4 client -> MongoD 3.4 server + VALIDATE_WIRE_VERSION(ASSERT_OK, + WireVersion::RELEASE_2_4_AND_BEFORE, + WireVersion::COMMANDS_ACCEPT_WRITE_CONCERN, + WireVersion::RELEASE_2_4_AND_BEFORE, + WireVersion::COMMANDS_ACCEPT_WRITE_CONCERN); + + // MongoD 3.4 client -> MongoD 3.2 server + VALIDATE_WIRE_VERSION(ASSERT_OK, + WireVersion::RELEASE_2_4_AND_BEFORE, + WireVersion::COMMANDS_ACCEPT_WRITE_CONCERN, + WireVersion::RELEASE_2_4_AND_BEFORE, + WireVersion::FIND_COMMAND); + + // MongoD 3.2 client -> MongoD 3.4 server + VALIDATE_WIRE_VERSION(ASSERT_OK, + WireVersion::RELEASE_2_4_AND_BEFORE, + WireVersion::COMMANDS_ACCEPT_WRITE_CONCERN, + WireVersion::RELEASE_2_4_AND_BEFORE, + WireVersion::COMMANDS_ACCEPT_WRITE_CONCERN); + + // MongoS 3.4 client -> MongoD 3.4 server + VALIDATE_WIRE_VERSION(ASSERT_OK, + WireVersion::COMMANDS_ACCEPT_WRITE_CONCERN, + WireVersion::COMMANDS_ACCEPT_WRITE_CONCERN, + WireVersion::RELEASE_2_4_AND_BEFORE, + WireVersion::COMMANDS_ACCEPT_WRITE_CONCERN); + + // MongoS 3.2 client -> MongoD 3.4 server + VALIDATE_WIRE_VERSION(ASSERT_OK, + WireVersion::RELEASE_2_4_AND_BEFORE, + WireVersion::FIND_COMMAND, + WireVersion::RELEASE_2_4_AND_BEFORE, + WireVersion::COMMANDS_ACCEPT_WRITE_CONCERN); + + // Disallowed + // MongoS 3.4 -> MongoDB 3.2 server + VALIDATE_WIRE_VERSION(ASSERT_NOT_OK, + WireVersion::COMMANDS_ACCEPT_WRITE_CONCERN, + WireVersion::COMMANDS_ACCEPT_WRITE_CONCERN, + WireVersion::RELEASE_2_4_AND_BEFORE, + WireVersion::FIND_COMMAND); +} + } // namespace diff --git a/src/mongo/s/client/sharding_network_connection_hook.cpp b/src/mongo/s/client/sharding_network_connection_hook.cpp index a6b7a071250..e3544a4617b 100644 --- a/src/mongo/s/client/sharding_network_connection_hook.cpp +++ b/src/mongo/s/client/sharding_network_connection_hook.cpp @@ -77,20 +77,7 @@ Status ShardingNetworkConnectionHook::validateHostImpl( if (!shard->isConfig()) { return Status::OK(); } - long long remoteMaxWireVersion; - status = bsonExtractIntegerFieldWithDefault(isMasterReply.data, - "maxWireVersion", - RELEASE_2_4_AND_BEFORE, - &remoteMaxWireVersion); - if (!status.isOK()) { - return status; - } - if (remoteMaxWireVersion < FIND_COMMAND) { - // Prior to the introduction of the find command and the 3.1 release series, it was - // not possible to distinguish a config server from a shard server from its ismaster - // response. As such, we must assume that the system is properly configured. - return Status::OK(); - } + return {ErrorCodes::InvalidOptions, str::stream() << "Surprised to discover that " << remoteHost.toString() << " does not believe it is a config server"}; diff --git a/src/mongo/s/commands/cluster_is_master_cmd.cpp b/src/mongo/s/commands/cluster_is_master_cmd.cpp index 399cf8fd4b3..fade2fdb73f 100644 --- a/src/mongo/s/commands/cluster_is_master_cmd.cpp +++ b/src/mongo/s/commands/cluster_is_master_cmd.cpp @@ -112,8 +112,8 @@ public: // Mongos tries to keep exactly the same version range of the server for which // it is compiled. - result.append("maxWireVersion", WireSpec::instance().maxWireVersionIncoming); - result.append("minWireVersion", WireSpec::instance().minWireVersionIncoming); + result.append("maxWireVersion", WireSpec::instance().incoming.maxWireVersion); + result.append("minWireVersion", WireSpec::instance().incoming.minWireVersion); const auto parameter = mapFindWithDefault(ServerParameterSet::getGlobal()->getMap(), "automationServiceDescriptor", diff --git a/src/mongo/s/server.cpp b/src/mongo/s/server.cpp index 809164b6e04..613800a8547 100644 --- a/src/mongo/s/server.cpp +++ b/src/mongo/s/server.cpp @@ -213,11 +213,11 @@ static Status initializeSharding(OperationContext* txn) { static void _initWireSpec() { WireSpec& spec = WireSpec::instance(); // accept from any version - spec.minWireVersionIncoming = RELEASE_2_4_AND_BEFORE; - spec.maxWireVersionIncoming = COMMANDS_ACCEPT_WRITE_CONCERN; + spec.incoming.minWireVersion = RELEASE_2_4_AND_BEFORE; + spec.incoming.maxWireVersion = COMMANDS_ACCEPT_WRITE_CONCERN; // connect to version supporting Write Concern only - spec.minWireVersionOutgoing = COMMANDS_ACCEPT_WRITE_CONCERN; - spec.maxWireVersionOutgoing = COMMANDS_ACCEPT_WRITE_CONCERN; + spec.outgoing.minWireVersion = COMMANDS_ACCEPT_WRITE_CONCERN; + spec.outgoing.maxWireVersion = COMMANDS_ACCEPT_WRITE_CONCERN; } static ExitCode runMongosServer() { |