/** * Copyright (C) 2015 MongoDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * 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 * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General 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 GNU Affero General 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. */ #include "mongo/platform/basic.h" #include "mongo/rpc/protocol.h" #include #include #include "mongo/base/string_data.h" #include "mongo/bson/util/bson_extract.h" #include "mongo/db/jsobj.h" #include "mongo/db/operation_context.h" #include "mongo/db/wire_version.h" #include "mongo/util/mongoutils/str.h" namespace mongo { namespace rpc { namespace { /** * Protocols supported by order of preference. */ const Protocol kPreferredProtos[] = {Protocol::kOpCommandV1, Protocol::kOpQuery}; const char kNone[] = "none"; const char kOpQueryOnly[] = "opQueryOnly"; const char kOpCommandOnly[] = "opCommandOnly"; const char kAll[] = "all"; const OperationContext::Decoration operationProtocolDecoration = OperationContext::declareDecoration(); } // namespace Protocol getOperationProtocol(OperationContext* txn) { return operationProtocolDecoration(txn); } void setOperationProtocol(OperationContext* txn, Protocol protocol) { operationProtocolDecoration(txn) = protocol; } StatusWith negotiate(ProtocolSet fst, ProtocolSet snd) { using std::begin; using std::end; ProtocolSet common = fst & snd; auto it = std::find_if(begin(kPreferredProtos), end(kPreferredProtos), [common](Protocol p) { return common & static_cast(p); }); if (it == end(kPreferredProtos)) { return Status(ErrorCodes::RPCProtocolNegotiationFailed, "No common protocol found."); } return *it; } StatusWith toString(ProtocolSet protocols) { switch (protocols) { case supports::kNone: return StringData(kNone); case supports::kOpQueryOnly: return StringData(kOpQueryOnly); case supports::kOpCommandOnly: return StringData(kOpCommandOnly); case supports::kAll: return StringData(kAll); default: return Status(ErrorCodes::BadValue, str::stream() << "Can not convert ProtocolSet " << protocols << " to a string, only the predefined ProtocolSet " << "constants 'none' (0x0), 'opQueryOnly' (0x1), " << "'opCommandOnly' (0x2), and 'all' (0x3) are supported."); } } StatusWith parseProtocolSet(StringData repr) { if (repr == kNone) { return supports::kNone; } else if (repr == kOpQueryOnly) { return supports::kOpQueryOnly; } else if (repr == kOpCommandOnly) { return supports::kOpCommandOnly; } else if (repr == kAll) { return supports::kAll; } return Status(ErrorCodes::BadValue, str::stream() << "Can not parse a ProtocolSet from " << repr << " only the predefined ProtocolSet constants " << "'none' (0x0), 'opQueryOnly' (0x1), 'opCommandOnly' (0x2), " << "and 'all' (0x3) are supported."); } StatusWith parseProtocolSetFromIsMasterReply(const BSONObj& isMasterReply) { long long maxWireVersion; auto maxWireExtractStatus = bsonExtractIntegerField(isMasterReply, "maxWireVersion", &maxWireVersion); long long minWireVersion; auto minWireExtractStatus = bsonExtractIntegerField(isMasterReply, "minWireVersion", &minWireVersion); // MongoDB 2.4 and earlier do not have maxWireVersion/minWireVersion in their 'isMaster' replies if ((maxWireExtractStatus == minWireExtractStatus) && (maxWireExtractStatus == ErrorCodes::NoSuchKey)) { return supports::kOpQueryOnly; } else if (!maxWireExtractStatus.isOK()) { return maxWireExtractStatus; } else if (!minWireExtractStatus.isOK()) { return minWireExtractStatus; } bool isMongos = false; std::string msgField; auto msgFieldExtractStatus = bsonExtractStringField(isMasterReply, "msg", &msgField); if (msgFieldExtractStatus == ErrorCodes::NoSuchKey) { isMongos = false; } else if (!msgFieldExtractStatus.isOK()) { return msgFieldExtractStatus; } else { isMongos = (msgField == "isdbgrid"); } return (!isMongos && supportsWireVersionForOpCommandInMongod(minWireVersion, maxWireVersion)) ? supports::kAll : supports::kOpQueryOnly; } bool supportsWireVersionForOpCommandInMongod(int minWireVersion, int maxWireVersion) { // FIND_COMMAND versions support OP_COMMAND (in mongod but not mongos). return (minWireVersion <= WireVersion::FIND_COMMAND) && (maxWireVersion >= WireVersion::FIND_COMMAND); } ProtocolSet computeProtocolSet(int minWireVersion, int maxWireVersion) { ProtocolSet result = supports::kNone; if (minWireVersion <= maxWireVersion) { if (maxWireVersion >= WireVersion::FIND_COMMAND) { result |= supports::kOpCommandOnly; } if (minWireVersion <= WireVersion::RELEASE_2_4_AND_BEFORE) { result |= supports::kOpQueryOnly; } } return result; } } // namespace rpc } // namespace mongo