From 9397c7c793f9261c6456213e304793d03be6b3ef Mon Sep 17 00:00:00 2001 From: Ezra Chung Date: Thu, 25 May 2023 15:10:33 -0500 Subject: [PATCH 01/14] Update decl/prelude checks to ignore mcd-*.h headers --- .evergreen/scripts/check-preludes.py | 8 ++++---- .evergreen/scripts/check-public-decls.sh | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.evergreen/scripts/check-preludes.py b/.evergreen/scripts/check-preludes.py index d6a3d028eee..6f4469b5c13 100644 --- a/.evergreen/scripts/check-preludes.py +++ b/.evergreen/scripts/check-preludes.py @@ -30,10 +30,10 @@ checks = [ { "name": "libmongoc", - "headers": list(MONGOC_PREFIX.glob("*.h")), + "headers": list(MONGOC_PREFIX.glob("mongoc-*.h")), "exclusions": [ MONGOC_PREFIX / "mongoc-prelude.h", - MONGOC_PREFIX / "mongoc.h" + MONGOC_PREFIX / "mongoc.h", ], "include": "#include \"mongoc-prelude.h\"" }, @@ -41,9 +41,9 @@ "name": "libbson", "headers": list(BSON_PREFIX.glob("*.h")), "exclusions": [ - BSON_PREFIX / "bson-prelude.h", BSON_PREFIX / "bson-dsl.h", - BSON_PREFIX / "bson.h" + BSON_PREFIX / "bson-prelude.h", + BSON_PREFIX / "bson.h", ], "include": "#include \"bson-prelude.h\"" }, diff --git a/.evergreen/scripts/check-public-decls.sh b/.evergreen/scripts/check-public-decls.sh index 61c6e601948..f9a55d1ee14 100755 --- a/.evergreen/scripts/check-public-decls.sh +++ b/.evergreen/scripts/check-public-decls.sh @@ -12,7 +12,7 @@ exclude="\.\/src\/libmongoc\/src\/mongoc\/mongoc-macros.h|.\/src\/libmongoc\/src find ./src/libmongoc/src/mongoc -regex $pattern -regextype posix-extended -not -regex $exclude | sort >/tmp/public_headers.txt # get all public headers with BSON_BEGIN_DECLS. -find ./src/libmongoc/src/mongoc -regextype posix-extended -regex $pattern -not -regex $exclude | xargs grep -l "BSON_BEGIN_DECLS" | sort >/tmp/public_headers_with_extern_c.txt +find ./src/libmongoc/src/mongoc -regex $pattern -regextype posix-extended -not -regex $exclude | xargs grep -l "BSON_BEGIN_DECLS" | sort >/tmp/public_headers_with_extern_c.txt echo "checking if any public headers are missing 'extern C' declaration" From eb10b31ce20599e3046cdd2dfdcc7fab417d338d Mon Sep 17 00:00:00 2001 From: Ezra Chung Date: Thu, 4 May 2023 15:55:02 -0500 Subject: [PATCH 02/14] CDRIVER-4625 Add mcd-rpc internal static library --- src/libmongoc/CMakeLists.txt | 22 + src/libmongoc/src/mongoc/CMakeLists.txt | 2 + src/libmongoc/src/mongoc/mcd-rpc.c | 2709 ++++++++++++++++++ src/libmongoc/src/mongoc/mcd-rpc.h | 776 +++++ src/libmongoc/tests/test-libmongoc-main.c | 1 + src/libmongoc/tests/test-mcd-rpc.c | 3165 +++++++++++++++++++++ 6 files changed, 6675 insertions(+) create mode 100644 src/libmongoc/src/mongoc/mcd-rpc.c create mode 100644 src/libmongoc/src/mongoc/mcd-rpc.h create mode 100644 src/libmongoc/tests/test-mcd-rpc.c diff --git a/src/libmongoc/CMakeLists.txt b/src/libmongoc/CMakeLists.txt index 6bbbfeed8cf..082c396982d 100644 --- a/src/libmongoc/CMakeLists.txt +++ b/src/libmongoc/CMakeLists.txt @@ -495,6 +495,7 @@ endif () set (SOURCES ${SOURCES} ${PROJECT_SOURCE_DIR}/src/mongoc/mcd-azure.c + ${PROJECT_SOURCE_DIR}/src/mongoc/mcd-rpc.c ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-aggregate.c ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-apm.c ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-array.c @@ -758,6 +759,17 @@ if (MONGOC_ENABLE_MONGODB_AWS_AUTH) endif() endif () +if (MONGOC_ENABLE_STATIC_BUILD) + add_library (mcd_rpc STATIC EXCLUDE_FROM_ALL ${PROJECT_SOURCE_DIR}/src/mongoc/mcd-rpc.c) + target_include_directories (mcd_rpc PUBLIC ${PROJECT_SOURCE_DIR}/src) + target_link_libraries (mcd_rpc PUBLIC ${BSON_STATIC_LIBRARIES}) + if (NOT WIN32 AND ENABLE_PIC) + target_compile_options (mcd_rpc PUBLIC -fPIC) + endif () + target_compile_definitions (mcd_rpc PUBLIC ${BSON_STATIC_PUBLIC_DEFINITIONS}) + set_target_properties (mcd_rpc PROPERTIES OUTPUT_NAME "mcd-rpc") +endif () + add_library (mongoc_shared SHARED ${SOURCES} ${HEADERS} ${HEADERS_FORWARDING}) set_target_properties (mongoc_shared PROPERTIES CMAKE_CXX_VISIBILITY_PRESET hidden) target_link_libraries (mongoc_shared PRIVATE ${LIBRARIES} PUBLIC ${BSON_LIBRARIES}) @@ -982,6 +994,7 @@ set (test-libmongoc-sources ${PROJECT_SOURCE_DIR}/tests/test-mcd-azure-imds.c ${PROJECT_SOURCE_DIR}/tests/test-service-gcp.c ${PROJECT_SOURCE_DIR}/tests/test-mcd-integer.c + ${PROJECT_SOURCE_DIR}/tests/test-mcd-rpc.c ${PROJECT_SOURCE_DIR}/tests/TestSuite.c ${PROJECT_SOURCE_DIR}/tests/unified/operation.c ${PROJECT_SOURCE_DIR}/tests/unified/entity-map.c @@ -1260,6 +1273,15 @@ export (EXPORT mongoc-targets FILE "${CMAKE_CURRENT_BINARY_DIR}/mongoc/mongoc-targets.cmake" ) +if (TARGET mcd_rpc) + # Note: the (mongo::)bson_static target must be included separately via find_package or include. + export ( + TARGETS mcd_rpc + NAMESPACE mongo:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/mongoc/mcd-rpc-targets.cmake" + ) +endif () + configure_file (src/mongoc-config.cmake "${CMAKE_CURRENT_BINARY_DIR}/mongoc/mongoc-${MONGOC_API_VERSION}-config.cmake" @ONLY diff --git a/src/libmongoc/src/mongoc/CMakeLists.txt b/src/libmongoc/src/mongoc/CMakeLists.txt index ed03c3f716d..3efb2d302c0 100644 --- a/src/libmongoc/src/mongoc/CMakeLists.txt +++ b/src/libmongoc/src/mongoc/CMakeLists.txt @@ -76,6 +76,7 @@ extra_dist_generated ( set (src_libmongoc_src_mongoc_DIST_noinst_hs mcd-azure.h mcd-integer.h + mcd-rpc.h mcd-time.h mongoc-aggregate-private.h mongoc-apm-private.h @@ -182,6 +183,7 @@ set (src_libmongoc_src_mongoc_DIST_noinst_hs set (src_libmongoc_src_mongoc_DIST_cs mcd-azure.c + mcd-rpc.c mongoc-aggregate.c mongoc-apm.c mongoc-array.c diff --git a/src/libmongoc/src/mongoc/mcd-rpc.c b/src/libmongoc/src/mongoc/mcd-rpc.c new file mode 100644 index 00000000000..17a3c3936da --- /dev/null +++ b/src/libmongoc/src/mongoc/mcd-rpc.c @@ -0,0 +1,2709 @@ +#include "mcd-rpc.h" + +// Header-only dependency. Does NOT require linking with libmongoc. +#define MONGOC_INSIDE +#include "mongoc-iovec.h" +#undef MONGOC_INSIDE + +#include + + +typedef struct _mcd_rpc_message_header mcd_rpc_message_header; + +typedef struct _mcd_rpc_op_compressed mcd_rpc_op_compressed; + +typedef struct _mcd_rpc_op_msg_section mcd_rpc_op_msg_section; +typedef struct _mcd_rpc_op_msg mcd_rpc_op_msg; + +typedef struct _mcd_rpc_op_reply mcd_rpc_op_reply; +typedef struct _mcd_rpc_op_update mcd_rpc_op_update; +typedef struct _mcd_rpc_op_insert mcd_rpc_op_insert; +typedef struct _mcd_rpc_op_query mcd_rpc_op_query; +typedef struct _mcd_rpc_op_get_more mcd_rpc_op_get_more; +typedef struct _mcd_rpc_op_delete mcd_rpc_op_delete; +typedef struct _mcd_rpc_op_kill_cursors mcd_rpc_op_kill_cursors; + + +// See: https://www.mongodb.com/docs/manual/reference/mongodb-wire-protocol +struct _mcd_rpc_message_header { + int32_t message_length; + int32_t request_id; + int32_t response_to; + int32_t op_code; +}; + +struct _mcd_rpc_op_compressed { + mcd_rpc_message_header header; + int32_t original_opcode; + int32_t uncompressed_size; + uint8_t compressor_id; + const void *compressed_message; // Non-owning. + size_t compressed_message_len; // Not part of actual message. +}; + +struct _mcd_rpc_op_msg_section { + uint8_t kind; + + union payload { + // Kind 0. + struct body { + int32_t section_len; // Not part of actual message. + const void *bson; // bson_t data, non-owning. + } body; + + // Kind 1. + struct document_sequence { + int32_t section_len; + const char *identifier; // Non-owning. + size_t identifier_len; // Not part of actual message. + const void *bson_objects; // Array of bson_t data, non-owning. + size_t bson_objects_len; // Not part of actual message. + } document_sequence; + } payload; +}; + +struct _mcd_rpc_op_msg { + mcd_rpc_message_header header; + uint32_t flag_bits; + mcd_rpc_op_msg_section *sections; // Owning. + size_t sections_count; // Not part of actual message. + uint32_t checksum; // Optional, ignored by Drivers. + bool checksum_set; // Not part of actual message. +}; + +struct _mcd_rpc_op_reply { + mcd_rpc_message_header header; + int32_t response_flags; + int64_t cursor_id; + int32_t starting_from; + int32_t number_returned; + const void *documents; // Array of bson_t data, non-owning. + size_t documents_len; // Not part of actual message. +}; + +struct _mcd_rpc_op_update { + mcd_rpc_message_header header; + const char *full_collection_name; // Non-owning. + size_t full_collection_name_len; // Not part of actual message. + int32_t flags; + const void *selector; // bson_t data, non-owning. + const void *update; // bson_t data, non-owning. +}; + +struct _mcd_rpc_op_insert { + mcd_rpc_message_header header; + int32_t flags; + const char *full_collection_name; // Non-owning. + size_t full_collection_name_len; // Not part of actual message. + const void *documents; // Array of bson_t data, non-owning. + size_t documents_len; // Not part of actual message. +}; + +struct _mcd_rpc_op_query { + mcd_rpc_message_header header; + int32_t flags; + const char *full_collection_name; // Non-owning. + size_t full_collection_name_len; // Not part of actual message. + int32_t number_to_skip; + int32_t number_to_return; + const void *query; // bson_t, non-owning. + const void *return_fields_selector; // Optional bson_t, non-owning. +}; + +struct _mcd_rpc_op_get_more { + mcd_rpc_message_header header; + const char *full_collection_name; // Non-owning. + size_t full_collection_name_len; // Not part of actual message. + int32_t number_to_return; + int64_t cursor_id; +}; + +struct _mcd_rpc_op_delete { + mcd_rpc_message_header header; + const char *full_collection_name; + size_t full_collection_name_len; // Not part of actual message. + int32_t flags; + const void *selector; // bson_t data, non-owning. +}; + +struct _mcd_rpc_op_kill_cursors { + mcd_rpc_message_header header; + int32_t zero; // Reserved. + int32_t number_of_cursor_ids; + int64_t *cursor_ids; // Array of int64_t, owning. +}; + +union _mcd_rpc_message { + mcd_rpc_message_header msg_header; // Common initial sequence. + + mcd_rpc_op_compressed op_compressed; + mcd_rpc_op_msg op_msg; + + mcd_rpc_op_reply op_reply; + mcd_rpc_op_update op_update; + mcd_rpc_op_insert op_insert; + mcd_rpc_op_query op_query; + mcd_rpc_op_get_more op_get_more; + mcd_rpc_op_delete op_delete; + mcd_rpc_op_kill_cursors op_kill_cursors; +}; + + +// The minimum byte length of a valid RPC message is 16 bytes (messageHeader is +// the common initial sequence for all opcodes). RPC message lengths less than +// 16 may be encountered parsing due to invalid or malformed input. +#define MONGOC_RPC_MINIMUM_MESSAGE_LENGTH INT32_C (16) + +// The minimum byte length of a valid BSON document is 5 bytes (empty document): +// - length (int32): total document length (including itself). +// - "\x00": document terminator. +// BSON document lengths less than 5 may be encountered during parsing due to +// invalid or malformed input. +#define MONGOC_RPC_MINIMUM_BSON_LENGTH INT32_C (5) + + +static int32_t +_int32_from_le (const void *data) +{ + BSON_ASSERT_PARAM (data); + return bson_iter_int32_unsafe (&(bson_iter_t){.raw = data}); +} + + +// In addition to validating expected size against remaining bytes, ensure +// proper conversion from little endian format. +#define MONGOC_RPC_CONSUME(type, raw_type, from_le) \ + static bool _consume_##type ( \ + type *target, const uint8_t **ptr, size_t *remaining_bytes) \ + { \ + BSON_ASSERT_PARAM (target); \ + BSON_ASSERT_PARAM (ptr); \ + BSON_ASSERT_PARAM (remaining_bytes); \ + \ + if (*remaining_bytes < sizeof (type)) { \ + return false; \ + } \ + \ + raw_type raw; \ + memcpy (&raw, *ptr, sizeof (type)); \ + \ + const raw_type native = from_le (raw); \ + memcpy (target, &native, sizeof (type)); \ + \ + *ptr += sizeof (type); \ + *remaining_bytes -= sizeof (type); \ + \ + return true; \ + } + +MONGOC_RPC_CONSUME (uint8_t, uint8_t, (uint8_t)) +MONGOC_RPC_CONSUME (int32_t, uint32_t, BSON_UINT32_FROM_LE) +MONGOC_RPC_CONSUME (uint32_t, uint32_t, BSON_UINT32_FROM_LE) +MONGOC_RPC_CONSUME (int64_t, uint64_t, BSON_UINT64_FROM_LE) + +static bool +_consume_utf8 (const char **target, + size_t *length, + const uint8_t **ptr, + size_t *remaining_bytes) +{ + BSON_ASSERT_PARAM (target); + BSON_ASSERT_PARAM (length); + BSON_ASSERT_PARAM (ptr); + BSON_ASSERT_PARAM (remaining_bytes); + + *target = (const char *) *ptr; + + const uint8_t *iter = *ptr; + size_t rem = *remaining_bytes; + + while (rem > 0u && *iter != '\0') { + iter += 1u; + rem -= 1u; + } + + if (rem == 0u) { + return false; + } + + // Consume string including the null terminator. + iter += 1u; + rem -= 1u; + *length = *remaining_bytes - rem; + *ptr = iter; + *remaining_bytes = rem; + + return true; +} + + +static bool +_consume_reserved_zero (const uint8_t **ptr, size_t *remaining_bytes) +{ + int32_t zero; + + if (!_consume_int32_t (&zero, ptr, remaining_bytes)) { + return false; + } + + if (zero != 0) { + *ptr -= sizeof (int32_t); // Revert so *data_end points to start of + // ZERO as invalid input. + return false; + } + + return true; +} + + +static bool +_consume_bson_objects (const uint8_t **ptr, + size_t *remaining_bytes, + int32_t *num_parsed, + int32_t limit) +{ + BSON_ASSERT_PARAM (ptr); + BSON_ASSERT_PARAM (remaining_bytes); + BSON_ASSERT (num_parsed || true); + + int32_t count = 0; + + // Validate document count and lengths. + while ((count < limit) && (*remaining_bytes > 0u)) { + int32_t doc_len; + if (!_consume_int32_t (&doc_len, ptr, remaining_bytes)) { + return false; + } + + if (doc_len < MONGOC_RPC_MINIMUM_BSON_LENGTH || + bson_cmp_greater_su (doc_len, *remaining_bytes + sizeof (int32_t))) { + *ptr -= sizeof (int32_t); // Revert so *data_end points to start of + // document as invalid input. + return false; + } + + // Consume rest of document without validation. + *ptr += (size_t) doc_len - sizeof (int32_t); + *remaining_bytes -= (size_t) doc_len - sizeof (int32_t); + + count += 1; + } + + if (num_parsed) { + *num_parsed = count; + } + + return true; +} + + +static bool +_consume_op_compressed (mcd_rpc_message *rpc, + const uint8_t **ptr, + size_t *remaining_bytes) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT_PARAM (ptr); + BSON_ASSERT_PARAM (remaining_bytes); + + mcd_rpc_op_compressed *const op_compressed = &rpc->op_compressed; + + if (!_consume_int32_t ( + &op_compressed->original_opcode, ptr, remaining_bytes)) { + return false; + } + + if (!_consume_int32_t ( + &op_compressed->uncompressed_size, ptr, remaining_bytes)) { + return false; + } + + if (!_consume_uint8_t ( + &op_compressed->compressor_id, ptr, remaining_bytes)) { + return false; + } + + // Consume compressedMessage without validation. + op_compressed->compressed_message = *ptr; + op_compressed->compressed_message_len = *remaining_bytes; + *ptr += *remaining_bytes; + *remaining_bytes = 0u; + + return true; +} + + +static bool +_consume_op_msg_section (mcd_rpc_op_msg *op_msg, + const uint8_t **ptr, + size_t *remaining_bytes, + size_t *capacity, + bool *found_kind_0) +{ + BSON_ASSERT_PARAM (op_msg); + BSON_ASSERT_PARAM (ptr); + BSON_ASSERT_PARAM (remaining_bytes); + BSON_ASSERT_PARAM (capacity); + BSON_ASSERT_PARAM (found_kind_0); + + mcd_rpc_op_msg_section section; + + if (!_consume_uint8_t (§ion.kind, ptr, remaining_bytes)) { + return false; + } + + // There is no ordering implied by payload types. A section with payload type + // 1 can be serialized before payload type 0. + if (section.kind == 0) { + if (*found_kind_0) { + *ptr -= sizeof (uint8_t); // Revert so *data_end points to start of + // section as invalid input. + return false; + } + + *found_kind_0 = true; + } + + switch (section.kind) { + case 0: { // Body + section.payload.body.section_len = _int32_from_le (*ptr); + section.payload.body.bson = *ptr; + + int32_t num_parsed = 0; + if (!_consume_bson_objects (ptr, remaining_bytes, &num_parsed, 1) || + num_parsed != 1) { + return false; + } + + break; + } + + case 1: { // Document Sequence + if (!_consume_int32_t (§ion.payload.document_sequence.section_len, + ptr, + remaining_bytes)) { + return false; + } + + // Minimum byte length of a valid document sequence section is 4 bytes + // (section length). Actual minimum length would also account for the + // identifier field, but 4 bytes is sufficient to avoid unsigned integer + // overflow when computing `remaining_section_bytes` and to encourage as + // much progress is made parsing input data as able. + if (bson_cmp_less_su (section.payload.document_sequence.section_len, + sizeof (int32_t))) { + *ptr -= sizeof (int32_t); // Revert so *data_end points to start of + // document sequence as invalid input. + return false; + } + + size_t remaining_section_bytes = + (size_t) section.payload.document_sequence.section_len - + sizeof (int32_t); + + // Section length exceeds remaining data. + if (remaining_section_bytes > *remaining_bytes) { + *ptr -= sizeof (int32_t); // Revert so *data_end points to start of + // document sequence as invalid input. + return false; + } + + // Consume identifier without validating uniqueness. + { + const uint8_t *const identifier_begin = *ptr; + if (!_consume_utf8 (§ion.payload.document_sequence.identifier, + §ion.payload.document_sequence.identifier_len, + ptr, + &remaining_section_bytes)) { + return false; + } + *remaining_bytes -= (size_t) (*ptr - identifier_begin); + } + + section.payload.document_sequence.bson_objects = *ptr; + section.payload.document_sequence.bson_objects_len = + remaining_section_bytes; + + _consume_bson_objects (ptr, &remaining_section_bytes, NULL, INT32_MAX); + + // Should have exhausted all bytes in the section. + if (remaining_section_bytes != 0u) { + return false; + } + + *remaining_bytes -= + (size_t) (*ptr - (const uint8_t *) + section.payload.document_sequence.bson_objects); + + break; + } + + default: + *ptr -= sizeof (uint8_t); // Revert so *data_end points to start of kind + // as invalid input. + return false; + } + + // Expand sections capacity if required. + if (op_msg->sections_count >= *capacity) { + *capacity *= 2u; + op_msg->sections = bson_realloc ( + op_msg->sections, *capacity * sizeof (mcd_rpc_op_msg_section)); + } + + // Append the valid section. + op_msg->sections[op_msg->sections_count++] = section; + + return true; +} + +static bool +_consume_op_msg (mcd_rpc_message *rpc, + const uint8_t **ptr, + size_t *remaining_bytes) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT_PARAM (ptr); + BSON_ASSERT_PARAM (remaining_bytes); + + mcd_rpc_op_msg *const op_msg = &rpc->op_msg; + + if (!_consume_uint32_t (&op_msg->flag_bits, ptr, remaining_bytes)) { + return false; + } + + { + const uint32_t defined_bits = MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT | + MONGOC_OP_MSG_FLAG_MORE_TO_COME | + MONGOC_OP_MSG_FLAG_EXHAUST_ALLOWED; + + // Clients MUST error if any unsupported or undefined required bits are + // set to 1 and MUST ignore all undefined optional bits. + if (((op_msg->flag_bits & UINT32_C (0x0000FFFF)) & ~defined_bits) != 0u) { + *ptr -= sizeof (int32_t); // Revert so *data_end points to start of + // flagBits as invalid input. + return false; + } + } + + // Each message contains one or more sections. Preallocate space for two + // sections, which should cover the most frequent cases (including APM and + // CSE). + size_t capacity = 2u; + op_msg->sections = bson_malloc (capacity * sizeof (mcd_rpc_op_msg_section)); + op_msg->sections_count = 0u; + + // A fully constructed OP_MSG MUST contain exactly one Payload Type 0, and + // optionally any number of Payload Type 1 where each identifier MUST be + // unique per message. + { + bool found_kind_0 = false; + + // A section requires at least 5 bytes for kind (1) + length (4). + while (*remaining_bytes > 4u) { + if (!_consume_op_msg_section ( + op_msg, ptr, remaining_bytes, &capacity, &found_kind_0)) { + return false; + } + } + + if (!found_kind_0) { + return false; + } + } + + if ((op_msg->flag_bits & MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT) != 0u) { + if (!_consume_uint32_t (&op_msg->checksum, ptr, remaining_bytes)) { + return false; + } + + op_msg->checksum_set = true; + } + + return true; +} + + +static bool +_consume_op_reply (mcd_rpc_message *rpc, + const uint8_t **ptr, + size_t *remaining_bytes) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT_PARAM (ptr); + BSON_ASSERT_PARAM (remaining_bytes); + + mcd_rpc_op_reply *const op_reply = &rpc->op_reply; + + if (!_consume_int32_t (&op_reply->response_flags, ptr, remaining_bytes)) { + return false; + } + + if (!_consume_int64_t (&op_reply->cursor_id, ptr, remaining_bytes)) { + return false; + } + + if (!_consume_int32_t (&op_reply->starting_from, ptr, remaining_bytes)) { + return false; + } + + if (!_consume_int32_t (&op_reply->number_returned, ptr, remaining_bytes)) { + return false; + } + + if (op_reply->number_returned < 0) { + *ptr -= sizeof (int32_t); // Revert so *data_end points to start of + // numberReturned as invalid input. + return false; + } + + if (op_reply->number_returned > 0) { + op_reply->documents = *ptr; + op_reply->documents_len = *remaining_bytes; + } else { + op_reply->documents = NULL; + op_reply->documents_len = 0u; + } + + int32_t num_parsed = 0; + if (!_consume_bson_objects ( + ptr, remaining_bytes, &num_parsed, op_reply->number_returned) || + num_parsed != op_reply->number_returned) { + return false; + } + + return true; +} + + +static bool +_consume_op_update (mcd_rpc_message *rpc, + const uint8_t **ptr, + size_t *remaining_bytes) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT_PARAM (ptr); + BSON_ASSERT_PARAM (remaining_bytes); + + mcd_rpc_op_update *const op_update = &rpc->op_update; + + if (!_consume_reserved_zero (ptr, remaining_bytes)) { + return false; + } + + if (!_consume_utf8 (&op_update->full_collection_name, + &op_update->full_collection_name_len, + ptr, + remaining_bytes)) { + return false; + } + + if (!_consume_int32_t (&op_update->flags, ptr, remaining_bytes)) { + return false; + } + + // Bits 2-31 are reserved. Must be set to 0. + if ((op_update->flags & ~(0x00000003)) != 0) { + *ptr -= sizeof (int32_t); // Revert so *data_end points to start of + // flags as invalid input. + return false; + } + + int32_t num_parsed = 0; + + op_update->selector = *ptr; + if (!_consume_bson_objects (ptr, remaining_bytes, &num_parsed, 1) || + num_parsed != 1) { + return false; + } + + op_update->update = *ptr; + if (!_consume_bson_objects (ptr, remaining_bytes, &num_parsed, 1) || + num_parsed != 1) { + return false; + } + + return true; +} + + +static bool +_consume_op_insert (mcd_rpc_message *rpc, + const uint8_t **ptr, + size_t *remaining_bytes) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT_PARAM (ptr); + BSON_ASSERT_PARAM (remaining_bytes); + + mcd_rpc_op_insert *const op_insert = &rpc->op_insert; + + if (!_consume_int32_t (&op_insert->flags, ptr, remaining_bytes)) { + return false; + } + + // Bits 1-31 are reserved. Must be set to 0. + if ((op_insert->flags & ~(0x00000001)) != 0) { + *ptr -= sizeof (int32_t); // Revert so *data_end points to start of + // flags as invalid input. + return false; + } + + if (!_consume_utf8 (&op_insert->full_collection_name, + &op_insert->full_collection_name_len, + ptr, + remaining_bytes)) { + return false; + } + + op_insert->documents = *ptr; + op_insert->documents_len = *remaining_bytes; + + int32_t num_parsed = 0; + if (!_consume_bson_objects (ptr, remaining_bytes, &num_parsed, INT32_MAX) || + num_parsed == 0) { + return false; + } + + return true; +} + + +static bool +_consume_op_query (mcd_rpc_message *rpc, + const uint8_t **ptr, + size_t *remaining_bytes) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT_PARAM (ptr); + BSON_ASSERT_PARAM (remaining_bytes); + + mcd_rpc_op_query *const op_query = &rpc->op_query; + + if (!_consume_int32_t (&op_query->flags, ptr, remaining_bytes)) { + return false; + } + + // Bit 0 is reserved. Must be set to 0. + if ((op_query->flags & 0x01) != 0) { + *ptr -= sizeof (int32_t); // Revert so *data_end points to start of + // flags as invalid input. + return false; + } + + // Bits 8-31 are reserved. Must be set to 0. + if ((op_query->flags & ~(0x0000007F)) != 0) { + *ptr -= sizeof (int32_t); // Revert so *data_end points to start of + // flags as invalid input. + return false; + } + + if (!_consume_utf8 (&op_query->full_collection_name, + &op_query->full_collection_name_len, + ptr, + remaining_bytes)) { + return false; + } + + if (!_consume_int32_t (&op_query->number_to_skip, ptr, remaining_bytes)) { + return false; + } + + if (!_consume_int32_t (&op_query->number_to_return, ptr, remaining_bytes)) { + return false; + } + + int32_t num_parsed = 0; + + op_query->query = *ptr; + if (!_consume_bson_objects (ptr, remaining_bytes, &num_parsed, 1) || + num_parsed != 1) { + return false; + } + + op_query->return_fields_selector = *ptr; + if (!_consume_bson_objects (ptr, remaining_bytes, &num_parsed, 1)) { + return false; + } + + // returnFieldsSelector is optional. + if (num_parsed == 0) { + op_query->return_fields_selector = NULL; + } + + return true; +} + + +static bool +_consume_op_get_more (mcd_rpc_message *rpc, + const uint8_t **ptr, + size_t *remaining_bytes) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT_PARAM (ptr); + BSON_ASSERT_PARAM (remaining_bytes); + + mcd_rpc_op_get_more *const op_get_more = &rpc->op_get_more; + + if (!_consume_reserved_zero (ptr, remaining_bytes)) { + return false; + } + + if (!_consume_utf8 (&op_get_more->full_collection_name, + &op_get_more->full_collection_name_len, + ptr, + remaining_bytes)) { + return false; + } + + if (!_consume_int32_t ( + &op_get_more->number_to_return, ptr, remaining_bytes)) { + return false; + } + + if (!_consume_int64_t (&op_get_more->cursor_id, ptr, remaining_bytes)) { + return false; + } + + return true; +} + + +static bool +_consume_op_delete (mcd_rpc_message *rpc, + const uint8_t **ptr, + size_t *remaining_bytes) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT_PARAM (ptr); + BSON_ASSERT_PARAM (remaining_bytes); + + mcd_rpc_op_delete *const op_delete = &rpc->op_delete; + + if (!_consume_reserved_zero (ptr, remaining_bytes)) { + return false; + } + + if (!_consume_utf8 (&op_delete->full_collection_name, + &op_delete->full_collection_name_len, + ptr, + remaining_bytes)) { + return false; + } + + if (!_consume_int32_t (&op_delete->flags, ptr, remaining_bytes)) { + return false; + } + + // Bits 1-31 are reserved. Must be set to 0. + if ((op_delete->flags & ~(0x00000001)) != 0) { + *ptr -= sizeof (int32_t); // Revert so *data_end points to start of + // flags as invalid input. + return false; + } + + op_delete->selector = *ptr; + + int32_t num_parsed = 0; + if (!_consume_bson_objects (ptr, remaining_bytes, &num_parsed, 1) || + num_parsed != 1) { + return false; + } + + return true; +} + + +static bool +_consume_op_kill_cursors (mcd_rpc_message *rpc, + const uint8_t **ptr, + size_t *remaining_bytes) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT_PARAM (ptr); + BSON_ASSERT_PARAM (remaining_bytes); + + mcd_rpc_op_kill_cursors *op_kill_cursors = &rpc->op_kill_cursors; + + if (!_consume_reserved_zero (ptr, remaining_bytes)) { + return false; + } + + if (!_consume_int32_t ( + &op_kill_cursors->number_of_cursor_ids, ptr, remaining_bytes)) { + return false; + } + + if (op_kill_cursors->number_of_cursor_ids < 0 || + // Truncation may (deliberately) leave unparsed bytes that will later + // trigger validation failure due to unexpected remaining bytes. + bson_cmp_greater_su (op_kill_cursors->number_of_cursor_ids, + *remaining_bytes / sizeof (int64_t))) { + *ptr -= sizeof (int32_t); // Revert so *data_len points to start of + // numberOfCursorIds as invalid input. + return false; + } + + const size_t cursor_ids_length = + (size_t) op_kill_cursors->number_of_cursor_ids * sizeof (int64_t); + + bson_free (op_kill_cursors->cursor_ids); + + if (op_kill_cursors->number_of_cursor_ids > 0) { + op_kill_cursors->cursor_ids = bson_malloc (cursor_ids_length); + for (int32_t i = 0; i < op_kill_cursors->number_of_cursor_ids; ++i) { + if (!_consume_int64_t ( + op_kill_cursors->cursor_ids + i, ptr, remaining_bytes)) { + return false; + } + } + } else { + op_kill_cursors->cursor_ids = NULL; + } + + return true; +} + + +mcd_rpc_message * +mcd_rpc_message_from_data (const void *data, + size_t length, + const void **data_end) +{ + BSON_ASSERT_PARAM (data); + BSON_ASSERT (data_end || true); + + mcd_rpc_message *rpc = bson_malloc (sizeof (mcd_rpc_message)); + mcd_rpc_message *ret = NULL; + + *rpc = (mcd_rpc_message){.msg_header = {0}}; + + if (!mcd_rpc_message_from_data_in_place (rpc, data, length, data_end)) { + goto fail; + } + + ret = rpc; + rpc = NULL; + +fail: + mcd_rpc_message_destroy (rpc); + return ret; +} + +bool +mcd_rpc_message_from_data_in_place (mcd_rpc_message *rpc, + const void *data, + size_t length, + const void **data_end) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT_PARAM (data); + BSON_ASSERT (data_end || true); + + bool ret = false; + + size_t remaining_bytes = length; + + const uint8_t *ptr = data; + + if (!_consume_int32_t ( + &rpc->msg_header.message_length, &ptr, &remaining_bytes)) { + goto fail; + } + + if (rpc->msg_header.message_length < MONGOC_RPC_MINIMUM_MESSAGE_LENGTH || + bson_cmp_greater_su (rpc->msg_header.message_length, + remaining_bytes + sizeof (int32_t))) { + ptr -= sizeof (int32_t); // Revert so *data_end points to start of + // messageLength as invalid input. + goto fail; + } + + // Use reported message length as upper bound. + remaining_bytes = (size_t) rpc->msg_header.message_length - sizeof (int32_t); + + if (!_consume_int32_t ( + &rpc->msg_header.request_id, &ptr, &remaining_bytes)) { + goto fail; + } + + if (!_consume_int32_t ( + &rpc->msg_header.response_to, &ptr, &remaining_bytes)) { + goto fail; + } + + if (!_consume_int32_t (&rpc->msg_header.op_code, &ptr, &remaining_bytes)) { + goto fail; + } + + switch (rpc->msg_header.op_code) { + case MONGOC_OP_CODE_COMPRESSED: + if (!_consume_op_compressed (rpc, &ptr, &remaining_bytes)) { + goto fail; + } + break; + + case MONGOC_OP_CODE_MSG: + if (!_consume_op_msg (rpc, &ptr, &remaining_bytes)) { + goto fail; + } + break; + + case MONGOC_OP_CODE_REPLY: + if (!_consume_op_reply (rpc, &ptr, &remaining_bytes)) { + goto fail; + } + break; + + case MONGOC_OP_CODE_UPDATE: + if (!_consume_op_update (rpc, &ptr, &remaining_bytes)) { + goto fail; + } + break; + + case MONGOC_OP_CODE_INSERT: + if (!_consume_op_insert (rpc, &ptr, &remaining_bytes)) { + goto fail; + } + break; + + case MONGOC_OP_CODE_QUERY: + if (!_consume_op_query (rpc, &ptr, &remaining_bytes)) { + goto fail; + } + break; + + case MONGOC_OP_CODE_GET_MORE: + if (!_consume_op_get_more (rpc, &ptr, &remaining_bytes)) { + goto fail; + } + break; + + case MONGOC_OP_CODE_DELETE: + if (!_consume_op_delete (rpc, &ptr, &remaining_bytes)) { + goto fail; + } + break; + + case MONGOC_OP_CODE_KILL_CURSORS: + if (!_consume_op_kill_cursors (rpc, &ptr, &remaining_bytes)) { + goto fail; + } + break; + + default: + ptr -= sizeof (int32_t); // Revert so *data_end points to start of opCode + // as invalid input. + goto fail; + } + + // Number of bytes parsed do not match the reported message length. + if (remaining_bytes > 0) { + goto fail; + } + + ret = true; + +fail: + if (data_end) { + *data_end = ptr; + } + + return ret; +} + +static void +_append_iovec_reserve_space_for (mongoc_iovec_t **iovecs, + size_t *capacity, + const mongoc_iovec_t *header_iovecs, + size_t additional_capacity) +{ + BSON_ASSERT_PARAM (iovecs); + BSON_ASSERT_PARAM (capacity); + BSON_ASSERT_PARAM (header_iovecs); + + // Expect this function to be invoked only once after initializing the + // `header_iovecs` array. + BSON_ASSERT (*capacity == 4u); + + *capacity += additional_capacity; + *iovecs = bson_malloc (*capacity * sizeof (mongoc_iovec_t)); + memcpy (*iovecs, header_iovecs, 4u * sizeof (mongoc_iovec_t)); +} + +static bool +_append_iovec (mongoc_iovec_t *iovecs, + size_t *capacity, + size_t *count, + mongoc_iovec_t iovec) +{ + BSON_ASSERT_PARAM (iovecs); + BSON_ASSERT_PARAM (capacity); + BSON_ASSERT_PARAM (count); + + if (!iovec.iov_base || iovec.iov_len == 0u) { + return false; + } + + // Expect iovecs array capacity to have been reserved upfront according to + // the upper bound of potential iovec objects required for the opcode being + // converted. This is to minimize (re)allocations. + BSON_ASSERT (*count < *capacity); + + iovecs[*count] = iovec; + *count += 1u; + + return true; +} + +#define MONGOC_RPC_APPEND_IOVEC(type, raw_type, to_le) \ + static bool _append_iovec_##type ( \ + mongoc_iovec_t *iovecs, size_t *capacity, size_t *count, type *value) \ + { \ + raw_type storage; \ + memcpy (&storage, value, sizeof (raw_type)); \ + storage = to_le (storage); \ + memcpy (value, &storage, sizeof (raw_type)); \ + return _append_iovec (iovecs, \ + capacity, \ + count, \ + (mongoc_iovec_t){ \ + .iov_base = (void *) value, \ + .iov_len = sizeof (type), \ + }); \ + } + +MONGOC_RPC_APPEND_IOVEC (uint8_t, uint8_t, (uint8_t)) +MONGOC_RPC_APPEND_IOVEC (int32_t, uint32_t, BSON_UINT32_TO_LE) +MONGOC_RPC_APPEND_IOVEC (uint32_t, uint32_t, BSON_UINT32_TO_LE) +MONGOC_RPC_APPEND_IOVEC (int64_t, uint64_t, BSON_UINT64_TO_LE) + +static bool +_append_iovec_data (mongoc_iovec_t *iovecs, + size_t *capacity, + size_t *count, + const void *data, + size_t length) +{ + return _append_iovec (iovecs, + capacity, + count, + (mongoc_iovec_t){ + .iov_base = (void *) data, + .iov_len = length, + }); +} + +static bool +_append_iovec_reserved_zero (mongoc_iovec_t *iovecs, + size_t *capacity, + size_t *count) +{ + static int32_t zero = 0u; + + return _append_iovec (iovecs, + capacity, + count, + (mongoc_iovec_t){ + .iov_base = (void *) &zero, + .iov_len = sizeof (zero), + }); +} + +static bool +_append_iovec_op_compressed (mongoc_iovec_t **iovecs, + size_t *capacity, + size_t *count, + mcd_rpc_op_compressed *op_compressed, + const mongoc_iovec_t *header_iovecs) +{ + BSON_ASSERT_PARAM (iovecs); + BSON_ASSERT_PARAM (capacity); + BSON_ASSERT_PARAM (count); + BSON_ASSERT_PARAM (op_compressed); + BSON_ASSERT_PARAM (header_iovecs); + + _append_iovec_reserve_space_for (iovecs, capacity, header_iovecs, 4u); + + if (!_append_iovec_int32_t ( + *iovecs, capacity, count, &op_compressed->original_opcode)) { + return false; + } + + if (!_append_iovec_int32_t ( + *iovecs, capacity, count, &op_compressed->uncompressed_size)) { + return false; + } + + if (!_append_iovec_uint8_t ( + *iovecs, capacity, count, &op_compressed->compressor_id)) { + return false; + } + + if (!_append_iovec_data (*iovecs, + capacity, + count, + op_compressed->compressed_message, + op_compressed->compressed_message_len)) { + return false; + } + + return true; +} + +static bool +_count_section_iovecs (const mcd_rpc_op_msg *op_msg, size_t *section_iovecs) +{ + BSON_ASSERT_PARAM (op_msg); + BSON_ASSERT_PARAM (section_iovecs); + + for (size_t i = 0u; i < op_msg->sections_count; ++i) { + *section_iovecs += 1u; + + switch (op_msg->sections[i].kind) { + case 0: // Body. + *section_iovecs += 1u; + break; + + case 1: // Document Sequence. + *section_iovecs += 3u; + break; + + default: + return false; + } + } + + return true; +} + +static bool +_append_iovec_op_msg (mongoc_iovec_t **iovecs, + size_t *capacity, + size_t *count, + mcd_rpc_op_msg *op_msg, + const mongoc_iovec_t *header_iovecs) +{ + BSON_ASSERT_PARAM (iovecs); + BSON_ASSERT_PARAM (capacity); + BSON_ASSERT_PARAM (count); + BSON_ASSERT_PARAM (op_msg); + + size_t section_iovecs = 0u; + if (!_count_section_iovecs (op_msg, §ion_iovecs)) { + return false; + } + + _append_iovec_reserve_space_for ( + iovecs, capacity, header_iovecs, 2u + section_iovecs); + + if (!_append_iovec_uint32_t (*iovecs, capacity, count, &op_msg->flag_bits)) { + return false; + } + + for (size_t i = 0u; i < op_msg->sections_count; ++i) { + mcd_rpc_op_msg_section *section = &op_msg->sections[i]; + + if (!section) { + return false; + } + + if (!_append_iovec_uint8_t (*iovecs, capacity, count, §ion->kind)) { + return false; + } + + switch (section->kind) { + case 0: // Body + if (!_append_iovec_data (*iovecs, + capacity, + count, + section->payload.body.bson, + (size_t) section->payload.body.section_len)) { + return false; + } + break; + + case 1: // Document Sequence + if (!_append_iovec_int32_t ( + *iovecs, + capacity, + count, + §ion->payload.document_sequence.section_len)) { + return false; + } + + if (!_append_iovec_data ( + *iovecs, + capacity, + count, + section->payload.document_sequence.identifier, + section->payload.document_sequence.identifier_len)) { + return false; + } + + if (!_append_iovec_data ( + *iovecs, + capacity, + count, + section->payload.document_sequence.bson_objects, + section->payload.document_sequence.bson_objects_len)) { + return false; + } + + break; + + default: + return false; + } + } + + if (op_msg->checksum_set) { + if (!_append_iovec_uint32_t ( + *iovecs, capacity, count, &op_msg->checksum)) { + return false; + } + } + + return true; +} + +static bool +_append_iovec_op_reply (mongoc_iovec_t **iovecs, + size_t *capacity, + size_t *count, + mcd_rpc_op_reply *op_reply, + const mongoc_iovec_t *header_iovecs) +{ + BSON_ASSERT_PARAM (iovecs); + BSON_ASSERT_PARAM (capacity); + BSON_ASSERT_PARAM (count); + BSON_ASSERT_PARAM (op_reply); + + _append_iovec_reserve_space_for (iovecs, capacity, header_iovecs, 5u); + + if (!_append_iovec_int32_t ( + *iovecs, capacity, count, &op_reply->response_flags)) { + return false; + } + + if (!_append_iovec_int64_t ( + *iovecs, capacity, count, &op_reply->cursor_id)) { + return false; + } + + if (!_append_iovec_int32_t ( + *iovecs, capacity, count, &op_reply->starting_from)) { + return false; + } + + if (!_append_iovec_int32_t ( + *iovecs, capacity, count, &op_reply->number_returned)) { + return false; + } + + if (op_reply->number_returned > 0 && + !_append_iovec_data (*iovecs, + capacity, + count, + op_reply->documents, + op_reply->documents_len)) { + return false; + } + + return true; +} + +static bool +_append_iovec_op_update (mongoc_iovec_t **iovecs, + size_t *capacity, + size_t *count, + mcd_rpc_op_update *op_update, + const mongoc_iovec_t *header_iovecs) +{ + BSON_ASSERT_PARAM (iovecs); + BSON_ASSERT_PARAM (capacity); + BSON_ASSERT_PARAM (count); + BSON_ASSERT_PARAM (op_update); + + _append_iovec_reserve_space_for (iovecs, capacity, header_iovecs, 5u); + + if (!_append_iovec_reserved_zero (*iovecs, capacity, count)) { + return false; + } + + if (!_append_iovec_data (*iovecs, + capacity, + count, + op_update->full_collection_name, + op_update->full_collection_name_len)) { + return false; + } + + if (!_append_iovec_int32_t (*iovecs, capacity, count, &op_update->flags)) { + return false; + } + + if (!_append_iovec_data (*iovecs, + capacity, + count, + op_update->selector, + (size_t) _int32_from_le (op_update->selector))) { + return false; + } + + if (!_append_iovec_data (*iovecs, + capacity, + count, + op_update->update, + (size_t) _int32_from_le (op_update->update))) { + return false; + } + + return true; +} + +static bool +_append_iovec_op_insert (mongoc_iovec_t **iovecs, + size_t *capacity, + size_t *count, + mcd_rpc_op_insert *op_insert, + const mongoc_iovec_t *header_iovecs) +{ + BSON_ASSERT_PARAM (iovecs); + BSON_ASSERT_PARAM (capacity); + BSON_ASSERT_PARAM (count); + BSON_ASSERT_PARAM (op_insert); + + _append_iovec_reserve_space_for (iovecs, capacity, header_iovecs, 3u); + + if (!_append_iovec_int32_t (*iovecs, capacity, count, &op_insert->flags)) { + return false; + } + + if (!_append_iovec_data (*iovecs, + capacity, + count, + op_insert->full_collection_name, + op_insert->full_collection_name_len)) { + return false; + } + + if (!_append_iovec_data (*iovecs, + capacity, + count, + op_insert->documents, + op_insert->documents_len)) { + return false; + } + + + return true; +} + +static bool +_append_iovec_op_query (mongoc_iovec_t **iovecs, + size_t *capacity, + size_t *count, + mcd_rpc_op_query *op_query, + const mongoc_iovec_t *header_iovecs) +{ + BSON_ASSERT_PARAM (iovecs); + BSON_ASSERT_PARAM (capacity); + BSON_ASSERT_PARAM (count); + BSON_ASSERT_PARAM (op_query); + + _append_iovec_reserve_space_for ( + iovecs, + capacity, + header_iovecs, + 6u + (size_t) (!!op_query->return_fields_selector)); + + if (!_append_iovec_int32_t (*iovecs, capacity, count, &op_query->flags)) { + return false; + } + + if (!_append_iovec_data (*iovecs, + capacity, + count, + op_query->full_collection_name, + op_query->full_collection_name_len)) { + return false; + } + + if (!_append_iovec_int32_t ( + *iovecs, capacity, count, &op_query->number_to_skip)) { + return false; + } + + if (!_append_iovec_int32_t ( + *iovecs, capacity, count, &op_query->number_to_return)) { + return false; + } + + if (!_append_iovec_data (*iovecs, + capacity, + count, + op_query->query, + (size_t) _int32_from_le (op_query->query))) { + return false; + } + + if (op_query->return_fields_selector) { + if (!_append_iovec_data ( + *iovecs, + capacity, + count, + op_query->return_fields_selector, + (size_t) _int32_from_le (op_query->return_fields_selector))) { + return false; + } + } + + return true; +} + +static bool +_append_iovec_op_get_more (mongoc_iovec_t **iovecs, + size_t *capacity, + size_t *count, + mcd_rpc_op_get_more *op_get_more, + const mongoc_iovec_t *header_iovecs) +{ + BSON_ASSERT_PARAM (iovecs); + BSON_ASSERT_PARAM (count); + BSON_ASSERT_PARAM (capacity); + BSON_ASSERT_PARAM (op_get_more); + + _append_iovec_reserve_space_for (iovecs, capacity, header_iovecs, 4u); + + if (!_append_iovec_reserved_zero (*iovecs, capacity, count)) { + return false; + } + + if (!_append_iovec_data (*iovecs, + capacity, + count, + op_get_more->full_collection_name, + op_get_more->full_collection_name_len)) { + return false; + } + + if (!_append_iovec_int32_t ( + *iovecs, capacity, count, &op_get_more->number_to_return)) { + return false; + } + + if (!_append_iovec_int64_t ( + *iovecs, capacity, count, &op_get_more->cursor_id)) { + return false; + } + + return true; +} + +static bool +_append_iovec_op_delete (mongoc_iovec_t **iovecs, + size_t *capacity, + size_t *count, + mcd_rpc_op_delete *op_delete, + const mongoc_iovec_t *header_iovecs) +{ + BSON_ASSERT_PARAM (iovecs); + BSON_ASSERT_PARAM (capacity); + BSON_ASSERT_PARAM (count); + BSON_ASSERT_PARAM (op_delete); + + _append_iovec_reserve_space_for (iovecs, capacity, header_iovecs, 4u); + + if (!_append_iovec_reserved_zero (*iovecs, capacity, count)) { + return false; + } + + if (!_append_iovec_data (*iovecs, + capacity, + count, + op_delete->full_collection_name, + op_delete->full_collection_name_len)) { + return false; + } + + if (!_append_iovec_int32_t (*iovecs, capacity, count, &op_delete->flags)) { + return false; + } + + if (!_append_iovec_data (*iovecs, + capacity, + count, + op_delete->selector, + (size_t) _int32_from_le (op_delete->selector))) { + return false; + } + + return true; +} + +static bool +_append_iovec_op_kill_cursors (mongoc_iovec_t **iovecs, + size_t *capacity, + size_t *count, + mcd_rpc_op_kill_cursors *op_kill_cursors, + const mongoc_iovec_t *header_iovecs) +{ + BSON_ASSERT_PARAM (iovecs); + BSON_ASSERT_PARAM (capacity); + BSON_ASSERT_PARAM (count); + BSON_ASSERT_PARAM (op_kill_cursors); + + // Store value before conversion to little endian. + const int32_t number_of_cursor_ids = op_kill_cursors->number_of_cursor_ids; + + _append_iovec_reserve_space_for (iovecs, capacity, header_iovecs, 3u); + + if (!_append_iovec_reserved_zero (*iovecs, capacity, count)) { + return false; + } + + if (!_append_iovec_int32_t ( + *iovecs, capacity, count, &op_kill_cursors->number_of_cursor_ids)) { + return false; + } + + // Each cursorID must be converted to little endian. + for (int32_t i = 0; i < number_of_cursor_ids; ++i) { + int64_t *const cursor_id = op_kill_cursors->cursor_ids + i; + uint64_t storage; + memcpy (&storage, cursor_id, sizeof (int64_t)); + storage = BSON_UINT64_TO_LE (storage); + memcpy (cursor_id, &storage, sizeof (int64_t)); + } + + if (number_of_cursor_ids > 0 && + !_append_iovec_data (*iovecs, + capacity, + count, + op_kill_cursors->cursor_ids, + (size_t) number_of_cursor_ids * sizeof (int64_t))) { + return false; + } + + return true; +} + + +void * +mcd_rpc_message_to_iovecs (mcd_rpc_message *rpc, size_t *count) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT_PARAM (count); + + const int32_t op_code = rpc->msg_header.op_code; + + // Preallocated space for msgHeader fields. + mongoc_iovec_t header_iovecs[4]; + size_t capacity = 4u; + *count = 0u; + + (void) _append_iovec_int32_t ( + header_iovecs, &capacity, count, &rpc->msg_header.message_length); + (void) _append_iovec_int32_t ( + header_iovecs, &capacity, count, &rpc->msg_header.request_id); + (void) _append_iovec_int32_t ( + header_iovecs, &capacity, count, &rpc->msg_header.response_to); + (void) _append_iovec_int32_t ( + header_iovecs, &capacity, count, &rpc->msg_header.op_code); + + mongoc_iovec_t *iovecs = NULL; + mongoc_iovec_t *ret = NULL; + + switch (op_code) { + case MONGOC_OP_CODE_COMPRESSED: + if (!_append_iovec_op_compressed ( + &iovecs, &capacity, count, &rpc->op_compressed, header_iovecs)) { + goto fail; + } + break; + + case MONGOC_OP_CODE_MSG: { + if (!_append_iovec_op_msg ( + &iovecs, &capacity, count, &rpc->op_msg, header_iovecs)) { + goto fail; + } + break; + } + + case MONGOC_OP_CODE_REPLY: + if (!_append_iovec_op_reply ( + &iovecs, &capacity, count, &rpc->op_reply, header_iovecs)) { + goto fail; + } + break; + + case MONGOC_OP_CODE_UPDATE: + if (!_append_iovec_op_update ( + &iovecs, &capacity, count, &rpc->op_update, header_iovecs)) { + goto fail; + } + break; + + case MONGOC_OP_CODE_INSERT: + if (!_append_iovec_op_insert ( + &iovecs, &capacity, count, &rpc->op_insert, header_iovecs)) { + goto fail; + } + break; + + case MONGOC_OP_CODE_QUERY: + if (!_append_iovec_op_query ( + &iovecs, &capacity, count, &rpc->op_query, header_iovecs)) { + goto fail; + } + break; + + case MONGOC_OP_CODE_GET_MORE: + if (!_append_iovec_op_get_more ( + &iovecs, &capacity, count, &rpc->op_get_more, header_iovecs)) { + goto fail; + } + break; + + case MONGOC_OP_CODE_DELETE: + if (!_append_iovec_op_delete ( + &iovecs, &capacity, count, &rpc->op_delete, header_iovecs)) { + goto fail; + } + break; + + case MONGOC_OP_CODE_KILL_CURSORS: + if (!_append_iovec_op_kill_cursors ( + &iovecs, &capacity, count, &rpc->op_kill_cursors, header_iovecs)) { + goto fail; + } + break; + + default: + goto fail; + } + + ret = iovecs; + iovecs = NULL; + +fail: + bson_free (iovecs); + + return ret; +} + +mcd_rpc_message * +mcd_rpc_message_new (void) +{ + mcd_rpc_message *const rpc = bson_malloc (sizeof (mcd_rpc_message)); + *rpc = (mcd_rpc_message){.msg_header = {0}}; + return rpc; +} + +static int32_t +_mcd_rpc_header_get_op_code_maybe_le (const mcd_rpc_message *rpc) +{ + int32_t op_code = rpc->msg_header.op_code; + + // May already be in native endian. + switch (op_code) { + case MONGOC_OP_CODE_COMPRESSED: + case MONGOC_OP_CODE_MSG: + case MONGOC_OP_CODE_REPLY: + case MONGOC_OP_CODE_UPDATE: + case MONGOC_OP_CODE_INSERT: + case MONGOC_OP_CODE_QUERY: + case MONGOC_OP_CODE_GET_MORE: + case MONGOC_OP_CODE_DELETE: + case MONGOC_OP_CODE_KILL_CURSORS: + return op_code; + + default: + // May be in little endian. + op_code = _int32_from_le (&op_code); + + switch (op_code) { + case MONGOC_OP_CODE_COMPRESSED: + case MONGOC_OP_CODE_MSG: + case MONGOC_OP_CODE_REPLY: + case MONGOC_OP_CODE_UPDATE: + case MONGOC_OP_CODE_INSERT: + case MONGOC_OP_CODE_QUERY: + case MONGOC_OP_CODE_GET_MORE: + case MONGOC_OP_CODE_DELETE: + case MONGOC_OP_CODE_KILL_CURSORS: + return op_code; + + default: + // Doesn't seem to have been a valid opCode. + return rpc->msg_header.op_code; + } + } +} + +static void +_mcd_rpc_message_free_owners (mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + + switch (_mcd_rpc_header_get_op_code_maybe_le (rpc)) { + case MONGOC_OP_CODE_MSG: + bson_free (rpc->op_msg.sections); + rpc->op_msg.sections = NULL; + return; + + case MONGOC_OP_CODE_KILL_CURSORS: + bson_free (rpc->op_kill_cursors.cursor_ids); + rpc->op_kill_cursors.cursor_ids = NULL; + return; + + case MONGOC_OP_CODE_COMPRESSED: + case MONGOC_OP_CODE_REPLY: + case MONGOC_OP_CODE_UPDATE: + case MONGOC_OP_CODE_INSERT: + case MONGOC_OP_CODE_QUERY: + case MONGOC_OP_CODE_GET_MORE: + case MONGOC_OP_CODE_DELETE: + return; + + default: + return; + } +} + +void +mcd_rpc_message_destroy (mcd_rpc_message *rpc) +{ + if (!rpc) { + return; + } + + _mcd_rpc_message_free_owners (rpc); + + bson_free ((void *) rpc); +} + +void +mcd_rpc_message_reset (mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + + _mcd_rpc_message_free_owners (rpc); + + *rpc = (mcd_rpc_message){.msg_header = {0}}; +} + +void +mcd_rpc_message_set_length (mcd_rpc_message *rpc, int32_t value) +{ + BSON_ASSERT_PARAM (rpc); + rpc->msg_header.message_length += value; +} + +int32_t +mcd_rpc_header_get_message_length (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + return rpc->msg_header.message_length; +} + +int32_t +mcd_rpc_header_get_request_id (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + return rpc->msg_header.request_id; +} + +int32_t +mcd_rpc_header_get_response_to (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + return rpc->msg_header.response_to; +} + +int32_t +mcd_rpc_header_get_op_code (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + return rpc->msg_header.op_code; +} + +int32_t +mcd_rpc_header_set_message_length (mcd_rpc_message *rpc, int32_t message_length) +{ + BSON_ASSERT_PARAM (rpc); + rpc->msg_header.message_length = message_length; + return sizeof (message_length); +} + +int32_t +mcd_rpc_header_set_request_id (mcd_rpc_message *rpc, int32_t request_id) +{ + BSON_ASSERT_PARAM (rpc); + rpc->msg_header.request_id = request_id; + return sizeof (request_id); +} + +int32_t +mcd_rpc_header_set_response_to (mcd_rpc_message *rpc, int32_t response_to) +{ + BSON_ASSERT_PARAM (rpc); + rpc->msg_header.response_to = response_to; + return sizeof (response_to); +} + +int32_t +mcd_rpc_header_set_op_code (mcd_rpc_message *rpc, int32_t op_code) +{ + BSON_ASSERT_PARAM (rpc); + + _mcd_rpc_message_free_owners (rpc); + + rpc->msg_header.op_code = op_code; + return sizeof (op_code); +} + + +int32_t +mcd_rpc_op_compressed_get_original_opcode (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); + return rpc->op_compressed.original_opcode; +} + +int32_t +mcd_rpc_op_compressed_get_uncompressed_size (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); + return rpc->op_compressed.uncompressed_size; +} + +uint8_t +mcd_rpc_op_compressed_get_compressor_id (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); + return rpc->op_compressed.compressor_id; +} + +const void * +mcd_rpc_op_compressed_get_compressed_message (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); + return rpc->op_compressed.compressed_message; +} + +size_t +mcd_rpc_op_compressed_get_compressed_message_length (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); + return rpc->op_compressed.compressed_message_len; +} + +int32_t +mcd_rpc_op_compressed_set_original_opcode (mcd_rpc_message *rpc, + int32_t original_opcode) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); + rpc->op_compressed.original_opcode = original_opcode; + return sizeof (original_opcode); +} + +int32_t +mcd_rpc_op_compressed_set_uncompressed_size (mcd_rpc_message *rpc, + int32_t uncompressed_size) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); + rpc->op_compressed.uncompressed_size = uncompressed_size; + return sizeof (uncompressed_size); +} + +int32_t +mcd_rpc_op_compressed_set_compressor_id (mcd_rpc_message *rpc, + uint8_t compressor_id) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); + rpc->op_compressed.compressor_id = compressor_id; + return sizeof (compressor_id); +} + +int32_t +mcd_rpc_op_compressed_set_compressed_message (mcd_rpc_message *rpc, + const void *compressed_message, + size_t compressed_message_length) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); + BSON_ASSERT (bson_in_range_unsigned (int32_t, compressed_message_length)); + rpc->op_compressed.compressed_message = compressed_message; + rpc->op_compressed.compressed_message_len = compressed_message_length; + return (int32_t) compressed_message_length; +} + + +uint8_t +mcd_rpc_op_msg_section_get_kind (const mcd_rpc_message *rpc, size_t index) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + BSON_ASSERT (index < rpc->op_msg.sections_count); + return rpc->op_msg.sections[index].kind; +} + +int32_t +mcd_rpc_op_msg_section_get_length (const mcd_rpc_message *rpc, size_t index) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + BSON_ASSERT (index < rpc->op_msg.sections_count); + + const mcd_rpc_op_msg_section *const section = &rpc->op_msg.sections[index]; + + switch (section->kind) { + case 0: { // Body + return _int32_from_le (section->payload.body.bson); + } + + case 1: { // Document Sequence + return section->payload.document_sequence.section_len; + } + + default: + BSON_UNREACHABLE ("invalid section kind"); + } +} + +const char * +mcd_rpc_op_msg_section_get_identifier (const mcd_rpc_message *rpc, size_t index) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + BSON_ASSERT (index < rpc->op_msg.sections_count); + + const mcd_rpc_op_msg_section *const section = &rpc->op_msg.sections[index]; + BSON_ASSERT (section->kind == 1); + return section->payload.document_sequence.identifier; +} + +const void * +mcd_rpc_op_msg_section_get_body (const mcd_rpc_message *rpc, size_t index) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + BSON_ASSERT (index < rpc->op_msg.sections_count); + + const mcd_rpc_op_msg_section *const section = &rpc->op_msg.sections[index]; + BSON_ASSERT (section->kind == 0); + return section->payload.body.bson; +} + +const void * +mcd_rpc_op_msg_section_get_document_sequence (const mcd_rpc_message *rpc, + size_t index) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + BSON_ASSERT (index < rpc->op_msg.sections_count); + + const mcd_rpc_op_msg_section *const section = &rpc->op_msg.sections[index]; + BSON_ASSERT (section->kind == 1); + return section->payload.document_sequence.bson_objects; +} + +size_t +mcd_rpc_op_msg_section_get_document_sequence_length (const mcd_rpc_message *rpc, + size_t index) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + BSON_ASSERT (index < rpc->op_msg.sections_count); + + const mcd_rpc_op_msg_section *const section = &rpc->op_msg.sections[index]; + BSON_ASSERT (section->kind == 1); + return section->payload.document_sequence.bson_objects_len; +} + +int32_t +mcd_rpc_op_msg_section_set_kind (mcd_rpc_message *rpc, + size_t index, + uint8_t kind) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + BSON_ASSERT (index < rpc->op_msg.sections_count); + rpc->op_msg.sections[index].kind = kind; + return sizeof (kind); +} + +int32_t +mcd_rpc_op_msg_section_set_length (mcd_rpc_message *rpc, + size_t index, + int32_t length) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + BSON_ASSERT (index < rpc->op_msg.sections_count); + BSON_ASSERT (rpc->op_msg.sections[index].kind == 1); + rpc->op_msg.sections[index].payload.document_sequence.section_len = length; + return sizeof (length); +} + +int32_t +mcd_rpc_op_msg_section_set_identifier (mcd_rpc_message *rpc, + size_t index, + const char *identifier) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + BSON_ASSERT (index < rpc->op_msg.sections_count); + BSON_ASSERT (rpc->op_msg.sections[index].kind == 1); + + const size_t identifier_len = identifier ? strlen (identifier) + 1u : 0u; + + rpc->op_msg.sections[index].payload.document_sequence.identifier = + identifier; + rpc->op_msg.sections[index].payload.document_sequence.identifier_len = + identifier_len; + + BSON_ASSERT (bson_in_range_unsigned (int32_t, identifier_len)); + return (int32_t) identifier_len; +} + +int32_t +mcd_rpc_op_msg_section_set_body (mcd_rpc_message *rpc, + size_t index, + const void *body) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + BSON_ASSERT (index < rpc->op_msg.sections_count); + BSON_ASSERT (rpc->op_msg.sections[index].kind == 0); + + const int32_t section_len = body ? _int32_from_le (body) : 0; + + rpc->op_msg.sections[index].payload.body.bson = body; + rpc->op_msg.sections[index].payload.body.section_len = section_len; + + return section_len; +} + +int32_t +mcd_rpc_op_msg_section_set_document_sequence (mcd_rpc_message *rpc, + size_t index, + const void *document_sequence, + size_t document_sequence_length) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + BSON_ASSERT (index < rpc->op_msg.sections_count); + BSON_ASSERT (rpc->op_msg.sections[index].kind == 1); + + const size_t bson_objects_len = + document_sequence ? document_sequence_length : 0u; + + rpc->op_msg.sections[index].payload.document_sequence.bson_objects = + document_sequence; + rpc->op_msg.sections[index].payload.document_sequence.bson_objects_len = + bson_objects_len; + + BSON_ASSERT (bson_in_range_unsigned (int32_t, document_sequence_length)); + return (int32_t) bson_objects_len; +} + + +uint32_t +mcd_rpc_op_msg_get_flag_bits (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + return rpc->op_msg.flag_bits; +} + +size_t +mcd_rpc_op_msg_get_sections_count (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + return rpc->op_msg.sections_count; +} + +const uint32_t * +mcd_rpc_op_msg_get_checksum (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + return rpc->op_msg.checksum_set ? &rpc->op_msg.checksum : NULL; +} + +int32_t +mcd_rpc_op_msg_set_flag_bits (mcd_rpc_message *rpc, uint32_t flag_bits) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + rpc->op_msg.flag_bits = flag_bits; + return sizeof (flag_bits); +} + +void +mcd_rpc_op_msg_set_sections_count (mcd_rpc_message *rpc, size_t section_count) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + + rpc->op_msg.sections = bson_realloc ( + rpc->op_msg.sections, section_count * sizeof (mcd_rpc_op_msg_section)); + rpc->op_msg.sections_count = section_count; +} + +int32_t +mcd_rpc_op_msg_set_checksum (mcd_rpc_message *rpc, uint32_t checksum) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + rpc->op_msg.checksum = checksum; + rpc->op_msg.checksum_set = true; + return sizeof (checksum); +} + +void +mcd_rpc_op_msg_unset_checksum (mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); + rpc->op_msg.checksum_set = false; +} + + +int32_t +mcd_rpc_op_reply_get_response_flags (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY); + return rpc->op_reply.response_flags; +} + +int64_t +mcd_rpc_op_reply_get_cursor_id (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY); + return rpc->op_reply.cursor_id; +} + +int32_t +mcd_rpc_op_reply_get_starting_from (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY); + return rpc->op_reply.starting_from; +} + +int32_t +mcd_rpc_op_reply_get_number_returned (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY); + return rpc->op_reply.number_returned; +} + +const void * +mcd_rpc_op_reply_get_documents (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY); + return rpc->op_reply.documents_len > 0 ? rpc->op_reply.documents : NULL; +} + +size_t +mcd_rpc_op_reply_get_documents_len (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY); + return rpc->op_reply.documents_len; +} + +int32_t +mcd_rpc_op_reply_set_response_flags (mcd_rpc_message *rpc, + int32_t response_flags) +{ + BSON_ASSERT_PARAM (rpc); + rpc->op_reply.response_flags = response_flags; + return sizeof (response_flags); +} + +int32_t +mcd_rpc_op_reply_set_cursor_id (mcd_rpc_message *rpc, int64_t cursor_id) +{ + BSON_ASSERT_PARAM (rpc); + rpc->op_reply.cursor_id = cursor_id; + return sizeof (cursor_id); +} + +int32_t +mcd_rpc_op_reply_set_starting_from (mcd_rpc_message *rpc, int32_t starting_from) +{ + BSON_ASSERT_PARAM (rpc); + rpc->op_reply.starting_from = starting_from; + return sizeof (starting_from); +} + +int32_t +mcd_rpc_op_reply_set_number_returned (mcd_rpc_message *rpc, + int32_t number_returned) +{ + BSON_ASSERT_PARAM (rpc); + rpc->op_reply.number_returned = number_returned; + return sizeof (number_returned); +} + +int32_t +mcd_rpc_op_reply_set_documents (mcd_rpc_message *rpc, + const void *documents, + size_t documents_len) +{ + BSON_ASSERT_PARAM (rpc); + + rpc->op_reply.documents = documents; + rpc->op_reply.documents_len = documents_len; + + BSON_ASSERT (bson_in_range_unsigned (int32_t, documents_len)); + return (int32_t) documents_len; +} + + +const char * +mcd_rpc_op_update_get_full_collection_name (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_UPDATE); + return rpc->op_update.full_collection_name; +} + +int32_t +mcd_rpc_op_update_get_flags (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_UPDATE); + return rpc->op_update.flags; +} + +const void * +mcd_rpc_op_update_get_selector (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_UPDATE); + return rpc->op_update.selector; +} + +const void * +mcd_rpc_op_update_get_update (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_UPDATE); + return rpc->op_update.update; +} + +int32_t +mcd_rpc_op_update_set_full_collection_name (mcd_rpc_message *rpc, + const char *full_collection_name) +{ + BSON_ASSERT_PARAM (rpc); + + const size_t length = + full_collection_name ? strlen (full_collection_name) + 1u : 0u; + + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_UPDATE); + rpc->op_update.full_collection_name = full_collection_name; + rpc->op_update.full_collection_name_len = length; + + BSON_ASSERT (bson_in_range_unsigned (int32_t, length)); + return (int32_t) length; +} + +int32_t +mcd_rpc_op_update_set_flags (mcd_rpc_message *rpc, int32_t flags) +{ + BSON_ASSERT_PARAM (rpc); + rpc->op_update.flags = flags; + return sizeof (flags); +} + +int32_t +mcd_rpc_op_update_set_selector (mcd_rpc_message *rpc, const void *selector) +{ + BSON_ASSERT_PARAM (rpc); + rpc->op_update.selector = selector; + return selector ? _int32_from_le (selector) : 0; +} + +int32_t +mcd_rpc_op_update_set_update (mcd_rpc_message *rpc, const void *update) +{ + BSON_ASSERT_PARAM (rpc); + rpc->op_update.update = update; + return update ? _int32_from_le (update) : 0; +} + + +int32_t +mcd_rpc_op_insert_get_flags (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT); + return rpc->op_insert.flags; +} + +const char * +mcd_rpc_op_insert_get_full_collection_name (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT); + return rpc->op_insert.full_collection_name; +} + +const void * +mcd_rpc_op_insert_get_documents (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT); + return rpc->op_insert.documents; +} + +size_t +mcd_rpc_op_insert_get_documents_len (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT); + return rpc->op_insert.documents_len; +} + +int32_t +mcd_rpc_op_insert_set_flags (mcd_rpc_message *rpc, int32_t flags) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT); + rpc->op_insert.flags = flags; + return sizeof (flags); +} + +int32_t +mcd_rpc_op_insert_set_full_collection_name (mcd_rpc_message *rpc, + const char *full_collection_name) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT); + + const size_t length = + full_collection_name ? strlen (full_collection_name) + 1u : 0u; + + rpc->op_insert.full_collection_name = full_collection_name; + rpc->op_insert.full_collection_name_len = length; + + BSON_ASSERT (bson_in_range_unsigned (int32_t, length)); + return (int32_t) length; +} + +int32_t +mcd_rpc_op_insert_set_documents (mcd_rpc_message *rpc, + const void *documents, + size_t documents_len) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT); + + rpc->op_insert.documents = documents; + rpc->op_insert.documents_len = documents_len; + + BSON_ASSERT (bson_in_range_unsigned (int32_t, documents_len)); + return (int32_t) documents_len; +} + + +int32_t +mcd_rpc_op_query_get_flags (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); + return rpc->op_query.flags; +} + +const char * +mcd_rpc_op_query_get_full_collection_name (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); + return rpc->op_query.full_collection_name; +} + +int32_t +mcd_rpc_op_query_get_number_to_skip (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); + return rpc->op_query.number_to_skip; +} + +int32_t +mcd_rpc_op_query_get_number_to_return (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); + return rpc->op_query.number_to_return; +} + +const void * +mcd_rpc_op_query_get_query (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); + return rpc->op_query.query; +} + +const void * +mcd_rpc_op_query_get_return_fields_selector (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); + return rpc->op_query.return_fields_selector; +} + +int32_t +mcd_rpc_op_query_set_flags (mcd_rpc_message *rpc, int32_t flags) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); + rpc->op_query.flags = flags; + return sizeof (flags); +} + +int32_t +mcd_rpc_op_query_set_full_collection_name (mcd_rpc_message *rpc, + const char *full_collection_name) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); + + const size_t length = + full_collection_name ? strlen (full_collection_name) + 1u : 0u; + + rpc->op_query.full_collection_name = full_collection_name; + rpc->op_query.full_collection_name_len = length; + + BSON_ASSERT (bson_in_range_unsigned (int32_t, length)); + return (int32_t) length; +} + +int32_t +mcd_rpc_op_query_set_number_to_skip (mcd_rpc_message *rpc, + int32_t number_to_skip) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); + rpc->op_query.number_to_skip = number_to_skip; + return sizeof (number_to_skip); +} + +int32_t +mcd_rpc_op_query_set_number_to_return (mcd_rpc_message *rpc, + int32_t number_to_return) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); + rpc->op_query.number_to_return = number_to_return; + return sizeof (number_to_return); +} + +int32_t +mcd_rpc_op_query_set_query (mcd_rpc_message *rpc, const void *query) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); + rpc->op_query.query = query; + return _int32_from_le (query); +} + +int32_t +mcd_rpc_op_query_set_return_fields_selector (mcd_rpc_message *rpc, + const void *return_fields_selector) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); + rpc->op_query.return_fields_selector = return_fields_selector; + return return_fields_selector ? _int32_from_le (return_fields_selector) : 0; +} + + +const char * +mcd_rpc_op_get_more_get_full_collection_name (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE); + return rpc->op_get_more.full_collection_name; +} + +int32_t +mcd_rpc_op_get_more_get_number_to_return (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE); + return rpc->op_get_more.number_to_return; +} + +int64_t +mcd_rpc_op_get_more_get_cursor_id (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE); + return rpc->op_get_more.cursor_id; +} + +int32_t +mcd_rpc_op_get_more_set_full_collection_name (mcd_rpc_message *rpc, + const char *full_collection_name) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE); + + const size_t length = + full_collection_name ? strlen (full_collection_name) + 1u : 0u; + + rpc->op_get_more.full_collection_name = full_collection_name; + rpc->op_get_more.full_collection_name_len = length; + + BSON_ASSERT (bson_in_range_unsigned (int32_t, length)); + return (int32_t) length; +} + +int32_t +mcd_rpc_op_get_more_set_number_to_return (mcd_rpc_message *rpc, + int32_t number_to_return) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE); + rpc->op_get_more.number_to_return = number_to_return; + return sizeof (number_to_return); +} + +int32_t +mcd_rpc_op_get_more_set_cursor_id (mcd_rpc_message *rpc, int64_t cursor_id) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE); + rpc->op_get_more.cursor_id = cursor_id; + return sizeof (cursor_id); +} + + +const char * +mcd_rpc_op_delete_get_full_collection_name (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE); + return rpc->op_delete.full_collection_name; +} + +int32_t +mcd_rpc_op_delete_get_flags (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + return rpc->op_delete.flags; + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE); +} + +const void * +mcd_rpc_op_delete_get_selector (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE); + return rpc->op_delete.selector; +} + +int32_t +mcd_rpc_op_delete_set_full_collection_name (mcd_rpc_message *rpc, + const char *full_collection_name) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE); + + const size_t length = + full_collection_name ? strlen (full_collection_name) + 1u : 0u; + + rpc->op_delete.full_collection_name = full_collection_name; + rpc->op_delete.full_collection_name_len = length; + + BSON_ASSERT (bson_in_range_unsigned (int32_t, length)); + return (int32_t) length; +} + +int32_t +mcd_rpc_op_delete_set_flags (mcd_rpc_message *rpc, int32_t flags) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE); + rpc->op_delete.flags = flags; + return sizeof (flags); +} + +int32_t +mcd_rpc_op_delete_set_selector (mcd_rpc_message *rpc, const void *selector) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE); + rpc->op_delete.selector = selector; + return selector ? _int32_from_le (selector) : 0; +} + + +int32_t +mcd_rpc_op_kill_cursors_get_number_of_cursor_ids (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_KILL_CURSORS); + return rpc->op_kill_cursors.number_of_cursor_ids; +} + +const int64_t * +mcd_rpc_op_kill_cursors_get_cursor_ids (const mcd_rpc_message *rpc) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_KILL_CURSORS); + return rpc->op_kill_cursors.number_of_cursor_ids > 0 + ? rpc->op_kill_cursors.cursor_ids + : NULL; +} + +int32_t +mcd_rpc_op_kill_cursors_set_cursor_ids (mcd_rpc_message *rpc, + const int64_t *cursor_ids, + int32_t number_of_cursor_ids) +{ + BSON_ASSERT_PARAM (rpc); + BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_KILL_CURSORS); + BSON_ASSERT (bson_cmp_less_su (number_of_cursor_ids, + (size_t) INT32_MAX / sizeof (int64_t))); + + const size_t cursor_ids_length = + (size_t) number_of_cursor_ids * sizeof (int64_t); + + rpc->op_kill_cursors.number_of_cursor_ids = number_of_cursor_ids; + + bson_free (rpc->op_kill_cursors.cursor_ids); + + if (number_of_cursor_ids > 0) { + rpc->op_kill_cursors.cursor_ids = bson_malloc (cursor_ids_length); + memcpy (rpc->op_kill_cursors.cursor_ids, cursor_ids, cursor_ids_length); + } else { + rpc->op_kill_cursors.cursor_ids = NULL; + } + + return (int32_t) sizeof (int32_t) + (int32_t) cursor_ids_length; +} diff --git a/src/libmongoc/src/mongoc/mcd-rpc.h b/src/libmongoc/src/mongoc/mcd-rpc.h new file mode 100644 index 00000000000..fdb7bad8bcb --- /dev/null +++ b/src/libmongoc/src/mongoc/mcd-rpc.h @@ -0,0 +1,776 @@ +/* + * Copyright 2023 MongoDB, 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. + */ + + +#if !defined(MCD_RPC_H_INCLUDED) +#define MCD_RPC_H_INCLUDED + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef union _mcd_rpc_message mcd_rpc_message; + + +// See: https://www.mongodb.com/docs/manual/reference/mongodb-wire-protocol +#define MONGOC_OP_CODE_NONE INT32_C (0) +#define MONGOC_OP_CODE_COMPRESSED INT32_C (2012) +#define MONGOC_OP_CODE_MSG INT32_C (2013) + +#define MONGOC_OP_COMPRESSED_COMPRESSOR_ID_NOOP UINT8_C (0) +#define MONGOC_OP_COMPRESSED_COMPRESSOR_ID_SNAPPY UINT8_C (1) +#define MONGOC_OP_COMPRESSED_COMPRESSOR_ID_ZLIB UINT8_C (2) +#define MONGOC_OP_COMPRESSED_COMPRESSOR_ID_ZSTD UINT8_C (3) + +#define MONGOC_OP_MSG_FLAG_NONE UINT32_C (0) +#define MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT (UINT32_C (0x01) << 0) +#define MONGOC_OP_MSG_FLAG_MORE_TO_COME (UINT32_C (0x01) << 1) +#define MONGOC_OP_MSG_FLAG_EXHAUST_ALLOWED (UINT32_C (0x01) << 16) + + +// See: https://www.mongodb.com/docs/manual/legacy-opcodes/ +#define MONGOC_OP_CODE_REPLY INT32_C (1) +#define MONGOC_OP_CODE_UPDATE INT32_C (2001) +#define MONGOC_OP_CODE_INSERT INT32_C (2002) +#define MONGOC_OP_CODE_QUERY INT32_C (2004) +#define MONGOC_OP_CODE_GET_MORE INT32_C (2005) +#define MONGOC_OP_CODE_DELETE INT32_C (2006) +#define MONGOC_OP_CODE_KILL_CURSORS INT32_C (2007) + +#define MONGOC_OP_REPLY_RESPONSE_FLAG_NONE INT32_C (0) +#define MONGOC_OP_REPLY_RESPONSE_FLAG_CURSOR_NOT_FOUND (INT32_C (0x01) << 0) +#define MONGOC_OP_REPLY_RESPONSE_FLAG_QUERY_FAILURE (INT32_C (0x01) << 1) +#define MONGOC_OP_REPLY_RESPONSE_FLAG_SHARD_CONFIG_STALE (INT32_C (0x01) << 2) +#define MONGOC_OP_REPLY_RESPONSE_FLAG_AWAIT_CAPABLE (INT32_C (0x01) << 3) + +#define MONGOC_OP_UPDATE_FLAG_NONE INT32_C (0) +#define MONGOC_OP_UPDATE_FLAG_UPSERT (INT32_C (0x01) << 0) +#define MONGOC_OP_UPDATE_FLAG_MULTI_UPDATE (INT32_C (0x01) << 1) + +#define MONGOC_OP_INSERT_FLAG_NONE INT32_C (0) +#define MONGOC_OP_INSERT_FLAG_CONTINUE_ON_ERROR (INT32_C (0x01) << 0) + +#define MONGOC_OP_QUERY_FLAG_NONE INT32_C (0) +#define MONGOC_OP_QUERY_FLAG_TAILABLE_CURSOR (INT32_C (0x01) << 1) +#define MONGOC_OP_QUERY_FLAG_SECONDARY_OK (INT32_C (0x01) << 2) +#define MONGOC_OP_QUERY_FLAG_OPLOG_REPLAY (INT32_C (0x01) << 3) +#define MONGOC_OP_QUERY_FLAG_NO_CURSOR_TIMEOUT (INT32_C (0x01) << 4) +#define MONGOC_OP_QUERY_FLAG_AWAIT_DATA (INT32_C (0x01) << 5) +#define MONGOC_OP_QUERY_FLAG_EXHAUST (INT32_C (0x01) << 6) +#define MONGOC_OP_QUERY_FLAG_PARTIAL (INT32_C (0x01) << 7) + +#define MONGOC_OP_DELETE_FLAG_NONE INT32_C (0) +#define MONGOC_OP_DELETE_FLAG_SINGLE_REMOVE (INT32_C (0x01) << 0) + + +// Convert the given array of bytes into an RPC message object. The RPC message +// object must be freed by `mcd_rpc_message_destroy`. +// +// data: an array of `length` bytes. +// data_end: if not `NULL`, `*data_end` is set to one past the last byte of +// valid input data. Useful for diagnosing failures. +// +// Note: the fields of the RPC message object are automatically converted from +// little endian to native endian. +// +// Returns the new RPC message object on success. Returns `NULL` on failure. +mcd_rpc_message * +mcd_rpc_message_from_data (const void *data, + size_t length, + const void **data_end); + +// The in-place version of `mcd_rpc_message_from_data`. +// +// rpc: an RPC message object in an initialized state. +// +// Returns `true` on success. Returns `false` on failure. +bool +mcd_rpc_message_from_data_in_place (mcd_rpc_message *rpc, + const void *data, + size_t length, + const void **data_end); + +// Convert the given RPC message object into an array of iovec structures. The +// return value must be freed by `bson_free`. +// +// rpc: a valid RPC message object whose fields are in native endian. +// length: if not `NULL`, `*length` is set to the number of iovec structures in +// the array. +// +// Note: this function converts the fields of the RPC message object to little +// endian. +// +// Returns the array of iovec structures on success. Returns `NULL` on failure. +void * +mcd_rpc_message_to_iovecs (mcd_rpc_message *rpc, size_t *count); + +// Return an RPC message object in an initialized state whose fields will be set +// manually. The return value must be freed by `mcd_rpc_message_destroy`. +mcd_rpc_message * +mcd_rpc_message_new (void); + +// Destroy the given RPC message object. +void +mcd_rpc_message_destroy (mcd_rpc_message *rpc); + +// Restore the given RPC message object to an initialized state. +void +mcd_rpc_message_reset (mcd_rpc_message *rpc); + +// Set the message length for the given RPC message object. Expected to be used +// in conjunction with the return values of setters. +void +mcd_rpc_message_set_length (mcd_rpc_message *rpc, int32_t value); + +// Get the msgHeader.messageLength field. +int32_t +mcd_rpc_header_get_message_length (const mcd_rpc_message *rpc); + +// Get the msgHeader.requestId field. +int32_t +mcd_rpc_header_get_request_id (const mcd_rpc_message *rpc); + +// Get the msgHeader.responseTo field. +int32_t +mcd_rpc_header_get_response_to (const mcd_rpc_message *rpc); + +// Get the msgHeader.opCode field. +int32_t +mcd_rpc_header_get_op_code (const mcd_rpc_message *rpc); + +// Set the msgHeader.messageLength field. +// +// Returns the length of the field as part of msgHeader.messageLength. +int32_t +mcd_rpc_header_set_message_length (mcd_rpc_message *rpc, + int32_t message_length); + +// Set the msgHeader.requestId field. +// +// Returns the length of the field as part of msgHeader.messageLength. +int32_t +mcd_rpc_header_set_request_id (mcd_rpc_message *rpc, int32_t request_id); + +// Set the msgHeader.responseTo field. +// +// Returns the length of the field as part of msgHeader.messageLength. +int32_t +mcd_rpc_header_set_response_to (mcd_rpc_message *rpc, int32_t response_to); + +// Set the msgHeader.opCode field. +// +// Note: the msgHeader.opCode field may be set more than once. +// +// Returns the length of the field as part of msgHeader.messageLength. +int32_t +mcd_rpc_header_set_op_code (mcd_rpc_message *rpc, int32_t op_code); + + +// Get the OP_COMPRESSED originalOpcode field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_COMPRESSED. +int32_t +mcd_rpc_op_compressed_get_original_opcode (const mcd_rpc_message *rpc); + +// Get the OP_COMPRESSED uncompressedSize field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_COMPRESSED. +int32_t +mcd_rpc_op_compressed_get_uncompressed_size (const mcd_rpc_message *rpc); + +// Get the OP_COMPRESSED compressorId field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_COMPRESSED. +uint8_t +mcd_rpc_op_compressed_get_compressor_id (const mcd_rpc_message *rpc); + +// Get the OP_COMPRESSED compressedMessage field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_COMPRESSED. +const void * +mcd_rpc_op_compressed_get_compressed_message (const mcd_rpc_message *rpc); + +// Get the length of the OP_COMPRESSED compressedMessage field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_COMPRESSED. +size_t +mcd_rpc_op_compressed_get_compressed_message_length ( + const mcd_rpc_message *rpc); + +// Set the OP_COMPRESSED originalOpcode field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_COMPRESSED. +int32_t +mcd_rpc_op_compressed_set_original_opcode (mcd_rpc_message *rpc, + int32_t original_opcode); + +// Set the OP_COMPRESSED uncompressedSize field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_COMPRESSED. +int32_t +mcd_rpc_op_compressed_set_uncompressed_size (mcd_rpc_message *rpc, + int32_t uncompressed_size); + +// Set the OP_COMPRESSED compressorId field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_COMPRESSED. +int32_t +mcd_rpc_op_compressed_set_compressor_id (mcd_rpc_message *rpc, + uint8_t compressor_id); + +// Set the OP_COMPRESSED compressedMessage field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_COMPRESSED. +int32_t +mcd_rpc_op_compressed_set_compressed_message (mcd_rpc_message *rpc, + const void *compressed_message, + size_t compressed_message_length); + + +// Get the kind byte for the OP_MSG section at the given index. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +// The given index MUST be a valid index into the OP_MSG sections array. +uint8_t +mcd_rpc_op_msg_section_get_kind (const mcd_rpc_message *rpc, size_t index); + +// Get the length of the OP_MSG section at the given index. +// +// If the section kind is 0, returns the length of the single BSON object. +// If the section kind is 1, returns the total length of the section. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +// The given index MUST be a valid index into the OP_MSG sections array. +int32_t +mcd_rpc_op_msg_section_get_length (const mcd_rpc_message *rpc, size_t index); + +// Get the document sequence identifier of the OP_MSG document sequence section +// at the given index. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +// The given index MUST be a valid index into the OP_MSG sections array. +// The section kind at the given index MUST equal 1. +const char * +mcd_rpc_op_msg_section_get_identifier (const mcd_rpc_message *rpc, + size_t index); + +// Get a pointer to the beginning of the single BSON object of the OP_MSG body +// section at the given index. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +// The given index MUST be a valid index into the OP_MSG sections array. +// The section kind at the given index MUST equal 0. +const void * +mcd_rpc_op_msg_section_get_body (const mcd_rpc_message *rpc, size_t index); + +// Get a pointer to the beginning of the document sequence of the OP_MSG +// document sequence section at the given index. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +// The given index MUST be a valid index into the OP_MSG sections array. +// The section kind at the given index MUST equal 1. +const void * +mcd_rpc_op_msg_section_get_document_sequence (const mcd_rpc_message *rpc, + size_t index); + +// Get the length of the document sequence of the OP_MSG document sequence +// section at the given index. +// +// Note: the length is the number of bytes, NOT the number of documents. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +// The given index MUST be a valid index into the OP_MSG sections array. +// The section kind at the given index MUST equal 1. +size_t +mcd_rpc_op_msg_section_get_document_sequence_length (const mcd_rpc_message *rpc, + size_t index); + +// Set the kind byte for the OP_MSG section at the given index. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +// The given index MUST be a valid index into the OP_MSG sections array. +int32_t +mcd_rpc_op_msg_section_set_kind (mcd_rpc_message *rpc, + size_t index, + uint8_t kind); + +// Set the length of the OP_MSG document sequence section at the given index. +// +// Note: the section length of an OP_MSG body section is equal to the length +// of the single BSON object, thus does not require a seperate setter. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +// The given index MUST be a valid index into the OP_MSG sections array. +// The section kind at the given index MUST equal 1. +int32_t +mcd_rpc_op_msg_section_set_length (mcd_rpc_message *rpc, + size_t index, + int32_t length); + +// Set the document sequence identifier of the OP_MSG document sequence section +// at the given index. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +// The given index MUST be a valid index into the OP_MSG sections array. +// The section kind at the given index MUST equal 1. +int32_t +mcd_rpc_op_msg_section_set_identifier (mcd_rpc_message *rpc, + size_t index, + const char *identifier); + +// Set the BSON object for the OP_MSG body section at the given index. +// +// Note: the section length of an OP_MSG body section is equal to the length +// of the single BSON object, thus does not require a seperate setter. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +// The given index MUST be a valid index into the OP_MSG sections array. +// The section kind at the given index MUST equal 0. +int32_t +mcd_rpc_op_msg_section_set_body (mcd_rpc_message *rpc, + size_t index, + const void *body); + +// Set the document sequence for the OP_MSG document sequence section at the +// given index. +// +// `document_sequence_length` MUST equal the length in bytes of the document +// sequence. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +// The given index MUST be a valid index into the OP_MSG sections array. +// The section kind at the given index MUST equal 1. +int32_t +mcd_rpc_op_msg_section_set_document_sequence (mcd_rpc_message *rpc, + size_t index, + const void *document_sequence, + size_t document_sequence_length); + + +// Get the OP_MSG flagBits field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +uint32_t +mcd_rpc_op_msg_get_flag_bits (const mcd_rpc_message *rpc); + +// Get the number of sections in the OP_MSG sections array. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +size_t +mcd_rpc_op_msg_get_sections_count (const mcd_rpc_message *rpc); + +// Get the OP_MSG checksum field. +// +// Returns `NULL` if the field is not set. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +const uint32_t * +mcd_rpc_op_msg_get_checksum (const mcd_rpc_message *rpc); + +// Set the OP_MSG flagBits field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +int32_t +mcd_rpc_op_msg_set_flag_bits (mcd_rpc_message *rpc, uint32_t flag_bits); + +// Set the number of sections in the OP_MSG section array. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +void +mcd_rpc_op_msg_set_sections_count (mcd_rpc_message *rpc, size_t section_count); + +// Set the OP_MSG checksum field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +int32_t +mcd_rpc_op_msg_set_checksum (mcd_rpc_message *rpc, uint32_t checksum); + +// Unset the OP_MSG checksum field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. +void +mcd_rpc_op_msg_unset_checksum (mcd_rpc_message *rpc); + + +// Get the OP_REPLY responseFlags field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_REPLY. +int32_t +mcd_rpc_op_reply_get_response_flags (const mcd_rpc_message *rpc); + +// Get the OP_REPLY cursorID field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_REPLY. +int64_t +mcd_rpc_op_reply_get_cursor_id (const mcd_rpc_message *rpc); + +// Get the OP_REPLY startingFrom field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_REPLY. +int32_t +mcd_rpc_op_reply_get_starting_from (const mcd_rpc_message *rpc); + +// Get the OP_REPLY numberReturned field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_REPLY. +int32_t +mcd_rpc_op_reply_get_number_returned (const mcd_rpc_message *rpc); + +// Get a pointer to the beginning of the OP_REPLY documents array. +// +// Returns `NULL` if the OP_REPLY numberReturned field equals 0. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_REPLY. +const void * +mcd_rpc_op_reply_get_documents (const mcd_rpc_message *rpc); + +// Get the length of the OP_REPLY documents array. +// +// Note: the length is the number of bytes, NOT the number of documents. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_REPLY. +size_t +mcd_rpc_op_reply_get_documents_len (const mcd_rpc_message *rpc); + +// Set the OP_REPLY responseFlags field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_REPLY. +int32_t +mcd_rpc_op_reply_set_response_flags (mcd_rpc_message *rpc, + int32_t response_flags); + +// Set the OP_REPLY cursorID field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_REPLY. +int32_t +mcd_rpc_op_reply_set_cursor_id (mcd_rpc_message *rpc, int64_t cursor_id); + +// Set the OP_REPLY startingFrom field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_REPLY. +int32_t +mcd_rpc_op_reply_set_starting_from (mcd_rpc_message *rpc, + int32_t starting_from); + +// Set the OP_REPLY numberReturned field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_REPLY. +int32_t +mcd_rpc_op_reply_set_number_returned (mcd_rpc_message *rpc, + int32_t number_returned); + +// Set the OP_REPLY documents field. +// +// `documents_len` MUST equal the length in bytes of the documents array. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_REPLY. +int32_t +mcd_rpc_op_reply_set_documents (mcd_rpc_message *rpc, + const void *documents, + size_t documents_len); + + +// Get the OP_UPDATE fullCollectionName field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_UPDATE. +const char * +mcd_rpc_op_update_get_full_collection_name (const mcd_rpc_message *rpc); + +// Get the OP_UPDATE flags field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_UPDATE. +int32_t +mcd_rpc_op_update_get_flags (const mcd_rpc_message *rpc); + +// Get the OP_UPDATE selector field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_UPDATE. +const void * +mcd_rpc_op_update_get_selector (const mcd_rpc_message *rpc); + +// Get the OP_UPDATE update field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_UPDATE. +const void * +mcd_rpc_op_update_get_update (const mcd_rpc_message *rpc); + +// Set the OP_UPDATE fullCollectionName field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_UPDATE. +int32_t +mcd_rpc_op_update_set_full_collection_name (mcd_rpc_message *rpc, + const char *full_collection_name); + +// Set the OP_UPDATE flags field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_UPDATE. +int32_t +mcd_rpc_op_update_set_flags (mcd_rpc_message *rpc, int32_t flags); + +// Set the OP_UPDATE selector field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_UPDATE. +int32_t +mcd_rpc_op_update_set_selector (mcd_rpc_message *rpc, const void *selector); + +// Set the OP_UPDATE update field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_UPDATE. +int32_t +mcd_rpc_op_update_set_update (mcd_rpc_message *rpc, const void *update); + + +// Get the OP_INSERT flags field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_INSERT. +int32_t +mcd_rpc_op_insert_get_flags (const mcd_rpc_message *rpc); + +// Get the OP_INSERT fullCollectionName field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_INSERT. +const char * +mcd_rpc_op_insert_get_full_collection_name (const mcd_rpc_message *rpc); + +// Get a pointer to the beginning of the OP_INSERT documents array. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_INSERT. +const void * +mcd_rpc_op_insert_get_documents (const mcd_rpc_message *rpc); + +// Get the length of the OP_INSERT documents array. +// +// Note: the length is the number of bytes, NOT the number of documents. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_INSERT. +size_t +mcd_rpc_op_insert_get_documents_len (const mcd_rpc_message *rpc); + +// Set the OP_INSERT flags field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_INSERT. +int32_t +mcd_rpc_op_insert_set_flags (mcd_rpc_message *rpc, int32_t flags); + +// Set the OP_INSERT fullCollectionName field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_INSERT. +int32_t +mcd_rpc_op_insert_set_full_collection_name (mcd_rpc_message *rpc, + const char *full_collection_name); + +// Set the OP_INSERT documents array. +// +// `documents_len` MUST equal the length in bytes of the documents array. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_INSERT. +int32_t +mcd_rpc_op_insert_set_documents (mcd_rpc_message *rpc, + const void *documents, + size_t documents_len); + + +// Get the OP_QUERY flags field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_QUERY. +int32_t +mcd_rpc_op_query_get_flags (const mcd_rpc_message *rpc); + +// Get the OP_QUERY fullCollectionName field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_QUERY. +const char * +mcd_rpc_op_query_get_full_collection_name (const mcd_rpc_message *rpc); + +// Get the OP_QUERY numberToSkip field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_QUERY. +int32_t +mcd_rpc_op_query_get_number_to_skip (const mcd_rpc_message *rpc); + +// Get the OP_QUERY numberToReturn field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_QUERY. +int32_t +mcd_rpc_op_query_get_number_to_return (const mcd_rpc_message *rpc); + +// Get the OP_QUERY query field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_QUERY. +const void * +mcd_rpc_op_query_get_query (const mcd_rpc_message *rpc); + +// Get the OP_QUERY returnFieldsSelector field. +// +// Returns `NULL` if the field is not set. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_QUERY. +const void * +mcd_rpc_op_query_get_return_fields_selector (const mcd_rpc_message *rpc); + +// Set the OP_QUERY flags field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_QUERY. +int32_t +mcd_rpc_op_query_set_flags (mcd_rpc_message *rpc, int32_t flags); + +// Set the OP_QUERY fullCollectionName field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_QUERY. +int32_t +mcd_rpc_op_query_set_full_collection_name (mcd_rpc_message *rpc, + const char *full_collection_name); + +// Set the OP_QUERY numberToSkip field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_QUERY. +int32_t +mcd_rpc_op_query_set_number_to_skip (mcd_rpc_message *rpc, + int32_t number_to_skip); + +// Set the OP_QUERY numberToReturn field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_QUERY. +int32_t +mcd_rpc_op_query_set_number_to_return (mcd_rpc_message *rpc, + int32_t number_to_return); + +// Set the OP_QUERY query field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_QUERY. +int32_t +mcd_rpc_op_query_set_query (mcd_rpc_message *rpc, const void *query); + +// Set the OP_QUERY returnFieldsSelector field. +// +// Note: `return_fields_selector` may be `NULL` to unset the field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_QUERY. +int32_t +mcd_rpc_op_query_set_return_fields_selector ( + mcd_rpc_message *rpc, const void *return_fields_selector); + + +// Get the OP_GET_MORE fullCollectionName field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_GET_MORE. +const char * +mcd_rpc_op_get_more_get_full_collection_name (const mcd_rpc_message *rpc); + +// Get the OP_GET_MORE numberToReturn field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_GET_MORE. +int32_t +mcd_rpc_op_get_more_get_number_to_return (const mcd_rpc_message *rpc); + +// Get the OP_GET_MORE cursorID field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_GET_MORE. +int64_t +mcd_rpc_op_get_more_get_cursor_id (const mcd_rpc_message *rpc); + +// Set the OP_GET_MORE fullCollectionName field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_GET_MORE. +int32_t +mcd_rpc_op_get_more_set_full_collection_name (mcd_rpc_message *rpc, + const char *full_collection_name); + +// Set the OP_GET_MORE numberToReturn field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_GET_MORE. +int32_t +mcd_rpc_op_get_more_set_number_to_return (mcd_rpc_message *rpc, + int32_t number_to_return); + +// Set the OP_GET_MORE cursorID field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_GET_MORE. +int32_t +mcd_rpc_op_get_more_set_cursor_id (mcd_rpc_message *rpc, int64_t cursor_id); + + +// Get the OP_DELETE fullCollectionName field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_DELETE. +const char * +mcd_rpc_op_delete_get_full_collection_name (const mcd_rpc_message *rpc); + +// Get the OP_DELETE flags field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_DELETE. +int32_t +mcd_rpc_op_delete_get_flags (const mcd_rpc_message *rpc); + +// Get the OP_DELETE selector field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_DELETE. +const void * +mcd_rpc_op_delete_get_selector (const mcd_rpc_message *rpc); + +// Set the OP_DELETE fullCollectionName field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_DELETE. +int32_t +mcd_rpc_op_delete_set_full_collection_name (mcd_rpc_message *rpc, + const char *full_collection_name); + +// Set the OP_DELETE flags field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_DELETE. +int32_t +mcd_rpc_op_delete_set_flags (mcd_rpc_message *rpc, int32_t flags); + +// Set the OP_DELETE selector field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_DELETE. +int32_t +mcd_rpc_op_delete_set_selector (mcd_rpc_message *rpc, const void *selector); + + +// Get the OP_KILL_CURSORS numberOfCursorIDs field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_KILL_CURSORS. +int32_t +mcd_rpc_op_kill_cursors_get_number_of_cursor_ids (const mcd_rpc_message *rpc); + +// Get the OP_KILL_CURSORS cursorIDs field. +// +// Returns `NULL` if the OP_KILL_CURSORS numberOfCursorIDs field equals 0. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_KILL_CURSORS. +const int64_t * +mcd_rpc_op_kill_cursors_get_cursor_ids (const mcd_rpc_message *rpc); + +// Set the OP_KILL_CURSORS cursorIDs field. +// +// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_KILL_CURSORS. +int32_t +mcd_rpc_op_kill_cursors_set_cursor_ids (mcd_rpc_message *rpc, + const int64_t *cursor_ids, + int32_t number_of_cursor_ids); + + +#ifdef __cplusplus +} +#endif + +#endif // !defined(MCD_RPC_H_INCLUDED) diff --git a/src/libmongoc/tests/test-libmongoc-main.c b/src/libmongoc/tests/test-libmongoc-main.c index b743e141971..2963b826696 100644 --- a/src/libmongoc/tests/test-libmongoc-main.c +++ b/src/libmongoc/tests/test-libmongoc-main.c @@ -152,6 +152,7 @@ main (int argc, char *argv[]) TEST_INSTALL (test_mcd_azure_imds_install); TEST_INSTALL (test_mcd_integer_install); + TEST_INSTALL (test_mcd_rpc_install); TEST_INSTALL (test_service_gcp_install); ret = TestSuite_Run (&suite); diff --git a/src/libmongoc/tests/test-mcd-rpc.c b/src/libmongoc/tests/test-mcd-rpc.c new file mode 100644 index 00000000000..8024412066c --- /dev/null +++ b/src/libmongoc/tests/test-mcd-rpc.c @@ -0,0 +1,3165 @@ +#include + +#include + +#include + +#include "test-conveniences.h" +#include "TestSuite.h" + +#include + + +// clang-format off +#define TEST_DATA_OP_COMPRESSED \ + /* */ /* header (16 bytes) */ \ + /* 0 */ 0x2b, 0x00, 0x00, 0x00, /* messageLength (43) */ \ + /* 4 */ 0x04, 0x03, 0x02, 0x01, /* requestID (16909060) */ \ + /* 8 */ 0x08, 0x07, 0x06, 0x05, /* responseTo (84281096) */ \ + /* 12 */ 0xdc, 0x07, 0x00, 0x00, /* opCode (2012: OP_COMPRESSED) */ \ + /* */ \ + /* */ /* OP_COMPRESSED fields (9 bytes) */ \ + /* 16 */ 0xdd, 0x07, 0x00, 0x00, /* originalOpcode (2013: OP_MSG) */ \ + /* 20 */ 0x12, 0x00, 0x00, 0x00, /* uncompressedSize (18) */ \ + /* 24 */ 0x00, /* compressorId (0: noop) */ \ + /* */ \ + /* */ /* compressedMessage (18 bytes) */ \ + /* 25 */ 0x00, 0x00, 0x00, 0x00, /* flagBits (MONGOC_OP_MSG_FLAG_NONE) */ \ + /* 29 */ 0x00, /* Kind 0: Body */ \ + /* 30 */ 0x0d, 0x00, 0x00, 0x00, /* (13 bytes) { */ \ + /* 34 */ 0x02, /* (string) */ \ + /* 35 */ 0x6f, 0x70, 0x5f, 0x6d, 0x73, 0x67, 0x00, /* 'op_msg' */ \ + /* 42 */ 0x00 /* } */ \ + /* 43 */ +// clang-format on + +// clang-format off +#define TEST_DATA_OP_MSG_KIND_0 \ + /* */ /* header (16 bytes) */ \ + /* 0 */ 0x28, 0x00, 0x00, 0x00, /* messageLength (40) */ \ + /* 4 */ 0x04, 0x03, 0x02, 0x01, /* requestID (16909060) */ \ + /* 8 */ 0x08, 0x07, 0x06, 0x05, /* responseTo (84281096) */ \ + /* 12 */ 0xdd, 0x07, 0x00, 0x00, /* opCode (2013: OP_MSG) */ \ + /* */ \ + /* */ /* flagBits (4 bytes) */ \ + /* 16 */ 0x01, 0x00, 0x00, 0x00, /* MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT */ \ + /* */ \ + /* */ /* Section 0 (16 bytes) */ \ + /* 20 */ 0x00, /* Kind 0: Body */ \ + /* 21 */ 0x0f, 0x00, 0x00, 0x00, /* (15 bytes) { */ \ + /* 25 */ 0x10, /* (int32) */ \ + /* 26 */ 0x6b, 0x69, 0x6e, 0x64, 0x00, /* 'kind': */ \ + /* 31 */ 0x00, 0x00, 0x00, 0x00, /* 0 */ \ + /* 25 */ 0x00, /* } */ \ + /* */ \ + /* */ /* Optional checksum (4 bytes) */ \ + /* 36 */ 0x44, 0x33, 0x22, 0x11 /* checksum (287454020) */ \ + /* 40 */ +// clang-format on + +// clang-format off +#define TEST_DATA_OP_MSG_KIND_1_SINGLE \ + /* */ /* header (16 bytes) */ \ + /* 0 */ 0x43, 0x00, 0x00, 0x00, /* messageLength (67) */ \ + /* 4 */ 0x04, 0x03, 0x02, 0x01, /* requestID (16909060) */ \ + /* 8 */ 0x08, 0x07, 0x06, 0x05, /* responseTo (84281096) */ \ + /* 12 */ 0xdd, 0x07, 0x00, 0x00, /* opCode (2013: OP_MSG) */ \ + /* */ \ + /* */ /* flagBits (4 bytes) */ \ + /* 16 */ 0x01, 0x00, 0x00, 0x00, /* MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT */ \ + /* */ \ + /* */ /* Section 0 (16 bytes) */ \ + /* 20 */ 0x00, /* Kind 0: Body */ \ + /* 21 */ 0x0f, 0x00, 0x00, 0x00, /* (15 bytes) { */ \ + /* 25 */ 0x10, /* (int32) */ \ + /* 26 */ 0x6b, 0x69, 0x6e, 0x64, 0x00, /* 'kind': */ \ + /* 31 */ 0x00, 0x00, 0x00, 0x00, /* 0 */ \ + /* 35 */ 0x00, /* } */ \ + /* */ \ + /* */ /* Section 1 (27 bytes) */ \ + /* 36 */ 0x01, /* Kind 1: Document Sequence */ \ + /* 37 */ 0x1a, 0x00, 0x00, 0x00, /* size (26 bytes) */ \ + /* 41 */ 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x00, /* identifier ("single") */ \ + /* 48 */ 0x0f, 0x00, 0x00, 0x00, /* (15 bytes) { */ \ + /* 52 */ 0x10, /* (int32) */ \ + /* 53 */ 0x6b, 0x69, 0x6e, 0x64, 0x00, /* 'kind': */ \ + /* 58 */ 0x01, 0x00, 0x00, 0x00, /* 1 */ \ + /* 62 */ 0x00, /* } */ \ + /* */ \ + /* */ /* Optional checksum (4 bytes) */ \ + /* 63 */ 0x44, 0x33, 0x22, 0x11 /* checksum (287454020) */ \ + /* 67 */ +// clang-format on + +// clang-format off +#define TEST_DATA_OP_MSG_KIND_1_MULTIPLE \ + /* */ /* header (16 bytes) */ \ + /* 0 */ 0x6e, 0x00, 0x00, 0x00, /* messageLength (110) */ \ + /* 4 */ 0x04, 0x03, 0x02, 0x01, /* requestID (16909060) */ \ + /* 8 */ 0x08, 0x07, 0x06, 0x05, /* responseTo (84281096) */ \ + /* 12 */ 0xdd, 0x07, 0x00, 0x00, /* opCode (2013: OP_MSG) */ \ + /* */ \ + /* */ /* flagBits (4 bytes) */ \ + /* 16 */ 0x01, 0x00, 0x00, 0x00, /* MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT */ \ + /* */ \ + /* */ /* Section 0 (16 bytes) */ \ + /* 20 */ 0x00, /* Kind 0: Body. */ \ + /* 21 */ 0x0f, 0x00, 0x00, 0x00, /* (15 bytes) { */ \ + /* 25 */ 0x10, /* (int32) */ \ + /* 26 */ 0x6b, 0x69, 0x6e, 0x64, 0x00, /* 'kind': */ \ + /* 31 */ 0x00, 0x00, 0x00, 0x00, /* 0 */ \ + /* 35 */ 0x00, /* } */ \ + /* */ \ + /* */ /* Section 1 (26 bytes) */ \ + /* 36 */ 0x01, /* Kind 1: Document Sequence */ \ + /* 37 */ 0x19, 0x00, 0x00, 0x00, /* size (25 bytes) */ \ + /* 41 */ 0x66, 0x69, 0x72, 0x73, 0x74, 0x00, /* identifier ("first") */ \ + /* 47 */ 0x0f, 0x00, 0x00, 0x00, /* (15 bytes) { */ \ + /* 51 */ 0x10, /* (int32) */ \ + /* 52 */ 0x6b, 0x69, 0x6e, 0x64, 0x00, /* 'kind': */ \ + /* 57 */ 0x01, 0x00, 0x00, 0x00, /* 1 */ \ + /* 61 */ 0x00, /* } */ \ + /* */ \ + /* */ /* Section 2 (44 bytes) */ \ + /* 62 */ 0x01, /* Kind 1: Document Sequence */ \ + /* 63 */ 0x2b, 0x00, 0x00, 0x00, /* size (43 bytes) */ \ + /* 67 */ 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x00, /* identifier ("second") */ \ + /* 74 */ 0x10, 0x00, 0x00, 0x00, /* (16 bytes) { */ \ + /* 78 */ 0x10, /* (int32) */ \ + /* 79 */ 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, /* 'index': */ \ + /* 85 */ 0x00, 0x00, 0x00, 0x00, /* 0 */ \ + /* 89 */ 0x00, /* } */ \ + /* 90 */ 0x10, 0x00, 0x00, 0x00, /* (16 bytes) { */ \ + /* 94 */ 0x10, /* (int32) */ \ + /* 95 */ 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, /* 'index': */ \ + /* 101 */ 0x01, 0x00, 0x00, 0x00, /* 1 */ \ + /* 105 */ 0x00, /* } */ \ + /* */ \ + /* */ /* Optional checksum (4 bytes) */ \ + /* 106 */ 0x44, 0x33, 0x22, 0x11 /* checksum (287454020) */ \ + /* 110 */ +// clang-format on + +// clang-format off +#define TEST_DATA_OP_REPLY \ + /* */ /* header (16 bytes) */ \ + /* 0 */ 0x44, 0x00, 0x00, 0x00, /* messageLength (68) */ \ + /* 4 */ 0x04, 0x03, 0x02, 0x01, /* requestID (16909060) */ \ + /* 8 */ 0x08, 0x07, 0x06, 0x05, /* responseTo (84281096) */ \ + /* 12 */ 0x01, 0x00, 0x00, 0x00, /* opCode (1: OP_REPLY) */ \ + /* */ \ + /* */ /* OP_REPLY fields (52 bytes) */ \ + /* 16 */ 0x00, 0x00, 0x00, 0x00, /* responseFlags (0: none) */ \ + /* 20 */ 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, /* cursorID (1234605616436508552) */ \ + /* 28 */ 0x00, 0x00, 0x00, 0x00, /* startingFrom (0) */ \ + /* 32 */ 0x02, 0x00, 0x00, 0x00, /* numberReturned (2) */ \ + /* */ \ + /* */ /* documents (32 bytes) */ \ + /* 36 */ 0x10, 0x00, 0x00, 0x00, /* (16 bytes) { */ \ + /* 40 */ 0x10, /* (int32) */ \ + /* 41 */ 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, /* 'index': */ \ + /* 47 */ 0x00, 0x00, 0x00, 0x00, /* 0 */ \ + /* 51 */ 0x00, /* } */ \ + /* 52 */ 0x10, 0x00, 0x00, 0x00, /* (16 bytes) { */ \ + /* 56 */ 0x10, /* (int32) */ \ + /* 57 */ 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, /* 'index': */ \ + /* 63 */ 0x01, 0x00, 0x00, 0x00, /* 1 */ \ + /* 67 */ 0x00 /* } */ \ + /* 68 */ +// clang-format on + +// clang-format off +#define TEST_DATA_OP_UPDATE \ + /* */ /* header (16 bytes) */ \ + /* 0 */ 0x3e, 0x00, 0x00, 0x00, /* messageLength (62) */ \ + /* 4 */ 0x04, 0x03, 0x02, 0x01, /* requestID (16909060) */ \ + /* 8 */ 0x08, 0x07, 0x06, 0x05, /* responseTo (84281096) */ \ + /* 12 */ 0xd1, 0x07, 0x00, 0x00, /* opCode (2001: OP_UPDATE) */ \ + /* */ \ + /* */ /* OP_UPDATE fields (46 bytes) */ \ + /* 16 */ 0x00, 0x00, 0x00, 0x00, /* ZERO */ \ + /* 20 */ 0x64, 0x62, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x00, /* fullCollectionName ("db.coll") */ \ + /* 28 */ 0x00, 0x00, 0x00, 0x00, /* flags (0: none) */ \ + /* */ \ + /* */ /* selector (16 bytes) */ \ + /* 32 */ 0x10, 0x00, 0x00, 0x00, /* (16 bytes) { */ \ + /* 36 */ 0x08, /* (boolean) */ \ + /* 37 */ 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x00, /* 'selector': */ \ + /* 46 */ 0x00, /* false */ \ + /* 47 */ 0x00, /* } */ \ + /* */ \ + /* */ /* update (14 bytes) */ \ + /* 48 */ 0x0e, 0x00, 0x00, 0x00, /* (14 bytes) */ \ + /* 52 */ 0x08, /* (boolean) */ \ + /* 53 */ 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x00, /* 'update': */ \ + /* 54 */ 0x01, /* true */ \ + /* 61 */ 0x00 /* } */ \ + /* 62 */ +// clang-format on + +// clang-format off +#define TEST_DATA_OP_INSERT \ + /* */ /* header (16 bytes) */ \ + /* 0 */ 0x3c, 0x00, 0x00, 0x00, /* messageLength (60) */ \ + /* 4 */ 0x04, 0x03, 0x02, 0x01, /* requestID (16909060) */ \ + /* 8 */ 0x08, 0x07, 0x06, 0x05, /* responseTo (84281096) */ \ + /* 12 */ 0xd2, 0x07, 0x00, 0x00, /* opCode (2002: OP_INSERT) */ \ + /* */ \ + /* */ /* OP_INSERT fields (48 bytes ) */ \ + /* 16 */ 0x00, 0x00, 0x00, 0x00, /* flags (0: none) */ \ + /* 20 */ 0x64, 0x62, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x00, /* fullCollectionName ("db.coll") */ \ + /* */ \ + /* */ /* documents (32 bytes) */ \ + /* 28 */ 0x10, 0x00, 0x00, 0x00, /* (16 bytes) { */ \ + /* 32 */ 0x10, /* (int32) */ \ + /* 33 */ 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, /* 'index': */ \ + /* 39 */ 0x00, 0x00, 0x00, 0x00, /* 0 */ \ + /* 43 */ 0x00, /* } */ \ + /* 44 */ 0x10, 0x00, 0x00, 0x00, /* (16 bytes) { */ \ + /* 48 */ 0x10, /* (int32) */ \ + /* 49 */ 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, /* 'index': */ \ + /* 55 */ 0x01, 0x00, 0x00, 0x00, /* 1 */ \ + /* 59 */ 0x00 /* } */ \ + /* 60 */ +// clang-format on + +// clang-format off +#define TEST_DATA_OP_QUERY \ + /* */ /* header (16 bytes) */ \ + /* 0 */ 0x41, 0x00, 0x00, 0x00, /* messageLength (65) */ \ + /* 4 */ 0x04, 0x03, 0x02, 0x01, /* requestID (16909060) */ \ + /* 8 */ 0x08, 0x07, 0x06, 0x05, /* responseTo (84281096) */ \ + /* 12 */ 0xd4, 0x07, 0x00, 0x00, /* opCode (2004: OP_QUERY) */ \ + /* */ \ + /* */ /* OP_QUERY fields (49 bytes) */ \ + /* 16 */ 0x00, 0x00, 0x00, 0x00, /* flags (0: none) */ \ + /* 20 */ 0x64, 0x62, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x00, /* fullCollectionName ("db.coll") */ \ + /* 28 */ 0x00, 0x00, 0x00, 0x00, /* numberToSkip (0) */ \ + /* 32 */ 0x00, 0x00, 0x00, 0x00, /* numberToReturn (0) */ \ + /* */ \ + /* */ /* query (13 bytes) */ \ + /* 36 */ 0x0d, 0x00, 0x00, 0x00, /* (13 bytes) { */ \ + /* 40 */ 0x08, /* (boolean) */ \ + /* 41 */ 0x71, 0x75, 0x65, 0x72, 0x79, 0x00, /* 'query': */ \ + /* 47 */ 0x00, /* false */ \ + /* 48 */ 0x00, /* } */ \ + /* */ \ + /* */ /* Optional returnFieldsSelector (16 bytes) */ \ + /* 49 */ 0x10, 0x00, 0x00, 0x00, /* (16 bytes) { */ \ + /* 53 */ 0x08, /* (boolean) */ \ + /* 54 */ 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x00, /* 'selector': */ \ + /* 63 */ 0x01, /* true */ \ + /* 64 */ 0x00 /* } */ \ + /* 65 */ +// clang-format on + +// clang-format off +#define TEST_DATA_OP_GET_MORE \ + /* */ /* header (16 bytes) */ \ + /* 0 */ 0x28, 0x00, 0x00, 0x00, /* messageLength (40) */ \ + /* 4 */ 0x04, 0x03, 0x02, 0x01, /* requestID (16909060) */ \ + /* 8 */ 0x08, 0x07, 0x06, 0x05, /* responseTo (84281096) */ \ + /* 12 */ 0xd5, 0x07, 0x00, 0x00, /* opCode (2005: OP_GET_MORE) */ \ + /* */ \ + /* */ /* OP_GET_MORE fields (24 bytes) */ \ + /* 16 */ 0x00, 0x00, 0x00, 0x00, /* ZERO */ \ + /* 20 */ 0x64, 0x62, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x00, /* fullCollectionName ("db.coll") */ \ + /* 28 */ 0x00, 0x00, 0x00, 0x00, /* numberToReturn (0) */ \ + /* 32 */ 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 /* cursorID (1234605616436508552) */ \ + /* 40 */ +// clang-format on + +// clang-format off +#define TEST_DATA_OP_DELETE \ + /* */ /* header (16 bytes) */ \ + /* 0 */ 0x30, 0x00, 0x00, 0x00, /* messageLength (48) */ \ + /* 4 */ 0x04, 0x03, 0x02, 0x01, /* requestID (16909060) */ \ + /* 8 */ 0x08, 0x07, 0x06, 0x05, /* responseTo (84281096) */ \ + /* 12 */ 0xd6, 0x07, 0x00, 0x00, /* opCode (2006: OP_DELETE) */ \ + /* */ \ + /* */ /* OP_DELETE fields (16 bytes) */ \ + /* 16 */ 0x00, 0x00, 0x00, 0x00, /* ZERO */ \ + /* 20 */ 0x64, 0x62, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x00, /* fullCollectionName ("db.coll") */ \ + /* 28 */ 0x00, 0x00, 0x00, 0x00, /* flags (0: none) */ \ + /* */ \ + /* */ /* selector (16 bytes) */ \ + /* 32 */ 0x10, 0x00, 0x00, 0x00, /* (16 bytes) { */ \ + /* 36 */ 0x08, /* (boolean) */ \ + /* 37 */ 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x00, /* 'selector': */ \ + /* 46 */ 0x00, /* false */ \ + /* 47 */ 0x00 /* } */ \ + /* 48 */ +// clang-format on + +// clang-format off +#define TEST_DATA_OP_KILL_CURSORS \ + /* */ /* header (16 bytes) */ \ + /* 0 */ 0x28, 0x00, 0x00, 0x00, /* messageLength (40) */ \ + /* 4 */ 0x04, 0x03, 0x02, 0x01, /* requestID (16909060) */ \ + /* 8 */ 0x08, 0x07, 0x06, 0x05, /* responseTo (84281096) */ \ + /* 12 */ 0xd7, 0x07, 0x00, 0x00, /* opCode (2007: OP_KILL_CURSORS) */ \ + /* */ \ + /* */ /* OP_KILL_CURSORS fields (8 bytes) */ \ + /* 16 */ 0x00, 0x00, 0x00, 0x00, /* ZERO */ \ + /* 20 */ 0x02, 0x00, 0x00, 0x00, /* numberOfCursorIds (2)*/ \ + /* */ \ + /* */ /* cursorIDs (16 bytes) */ \ + /* 24 */ 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, /* cursorID (1230066625199609624) */ \ + /* 32 */ 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21 /* cursorID (2387509390608836392) */ \ + /* 40 */ +// clang-format on + + +#define ASSERT_RPC_MESSAGE_RESULT(rpc, data_begin, data_end, data_len) \ + if (1) { \ + const size_t parsed_len = \ + (size_t) ((const uint8_t *) data_end - data_begin); \ + if (rpc) { \ + ASSERT_WITH_MSG ( \ + parsed_len == data_len, \ + "converted only %zu bytes despite %zu bytes of valid input data", \ + parsed_len, \ + data_len); \ + } else { \ + ASSERT_WITH_MSG (rpc, \ + "failed to convert valid input data into an RPC " \ + "message due to byte %zu of %zu", \ + parsed_len, \ + data_len); \ + } \ + } else \ + (void) 0 + + +static int32_t +_int32_from_le (const void *data) +{ + BSON_ASSERT_PARAM (data); + return bson_iter_int32_unsafe (&(bson_iter_t){.raw = data}); +} + +static int64_t +_int64_from_le (const void *data) +{ + BSON_ASSERT_PARAM (data); + return bson_iter_int64_unsafe (&(bson_iter_t){.raw = data}); +} + + +static void +test_rpc_message_from_data_op_compressed_valid (void) +{ + uint8_t data[] = {TEST_DATA_OP_COMPRESSED}; + const size_t data_len = sizeof (data); + + // Valid test input data. + { + const void *data_end = NULL; + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, &data_end); + + ASSERT_RPC_MESSAGE_RESULT (rpc, data, data_end, data_len); + ASSERT (bson_in_range_unsigned (int32_t, data_len)); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_message_length (rpc), ==, (int32_t) data_len); + ASSERT_CMPINT32 (mcd_rpc_header_get_request_id (rpc), ==, 16909060); + ASSERT_CMPINT32 (mcd_rpc_header_get_response_to (rpc), ==, 84281096); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_op_code (rpc), ==, MONGOC_OP_CODE_COMPRESSED); + + ASSERT_CMPINT32 (mcd_rpc_op_compressed_get_original_opcode (rpc), + ==, + MONGOC_OP_CODE_MSG); + + ASSERT_CMPINT32 ( + mcd_rpc_op_compressed_get_uncompressed_size (rpc), ==, 18); + + ASSERT_CMPUINT (mcd_rpc_op_compressed_get_compressor_id (rpc), + ==, + MONGOC_OP_COMPRESSED_COMPRESSOR_ID_NOOP); + + const uint8_t *const compressed_message = + mcd_rpc_op_compressed_get_compressed_message (rpc); + ASSERT_CMPSIZE_T ((size_t) (compressed_message - data), ==, 25u); + + ASSERT_CMPSIZE_T ( + mcd_rpc_op_compressed_get_compressed_message_length (rpc), ==, 18u); + + mcd_rpc_message_destroy (rpc); + } + + // Test that compressorId is being parsed correctly. + { + data[24] = MONGOC_OP_COMPRESSED_COMPRESSOR_ID_SNAPPY; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, NULL); + ASSERT (rpc); + ASSERT_CMPUINT (mcd_rpc_op_compressed_get_compressor_id (rpc), + ==, + MONGOC_OP_COMPRESSED_COMPRESSOR_ID_SNAPPY); + mcd_rpc_message_destroy (rpc); + + data[24] = MONGOC_OP_COMPRESSED_COMPRESSOR_ID_NOOP; + } +} + + +static void +_test_rpc_message_from_data_op_msg_valid (uint8_t *data, + size_t data_len, + void (*test) (const uint8_t *data, + size_t data_len, + bool with_checksum)) +{ + ASSERT_WITH_MSG ( + (data[16] & MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT) != 0, + "test input data did not set MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT"); + + // Test with and without the optional checksum by temporarily modifying the + // data and data length accordingly. + { + data[0] = (uint8_t) (data[0] - 4u); // Reduce messageLength. + data[16] = (uint8_t) (data[16] & ~MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT); + + test (data, data_len - 4u, false); + + data[0] = (uint8_t) (data[0] + 4u); // Revert messageLength. + data[16] = (uint8_t) (data[16] | MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT); + } + + test (data, data_len, true); +} + +static void +_test_rpc_message_from_data_op_msg_valid_kind_0 (const uint8_t *data, + size_t data_len, + bool with_checksum) +{ + const void *data_end = NULL; + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, &data_end); + + ASSERT_RPC_MESSAGE_RESULT (rpc, data, data_end, data_len); + ASSERT (bson_in_range_unsigned (int32_t, data_len)); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_message_length (rpc), ==, (int32_t) data_len); + ASSERT_CMPINT32 (mcd_rpc_header_get_request_id (rpc), ==, 16909060); + ASSERT_CMPINT32 (mcd_rpc_header_get_response_to (rpc), ==, 84281096); + ASSERT_CMPINT32 (mcd_rpc_header_get_op_code (rpc), ==, MONGOC_OP_CODE_MSG); + + if (with_checksum) { + ASSERT_CMPUINT32 (mcd_rpc_op_msg_get_flag_bits (rpc), + ==, + MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT); + } else { + ASSERT_CMPUINT32 ( + mcd_rpc_op_msg_get_flag_bits (rpc), ==, MONGOC_OP_MSG_FLAG_NONE); + } + + ASSERT_CMPSIZE_T (mcd_rpc_op_msg_get_sections_count (rpc), ==, 1u); + + ASSERT_CMPUINT (mcd_rpc_op_msg_section_get_kind (rpc, 0u), ==, 0u); + const int32_t section_len = mcd_rpc_op_msg_section_get_length (rpc, 0u); + ASSERT_CMPINT32 (section_len, ==, 15); + const void *const body = mcd_rpc_op_msg_section_get_body (rpc, 0u); + ASSERT (body); + + bson_t bson; + ASSERT (bson_init_static (&bson, body, (size_t) section_len)); + ASSERT_MATCH (&bson, "{'kind': 0}"); + + if (with_checksum) { + const uint32_t *checksum = mcd_rpc_op_msg_get_checksum (rpc); + ASSERT (checksum); + ASSERT_CMPUINT32 (*checksum, ==, 287454020u); + } else { + ASSERT (!mcd_rpc_op_msg_get_checksum (rpc)); + } + + mcd_rpc_message_destroy (rpc); +} + +static void +test_rpc_message_from_data_op_msg_valid_kind_0 (void) +{ + uint8_t data[] = {TEST_DATA_OP_MSG_KIND_0}; + + _test_rpc_message_from_data_op_msg_valid ( + data, sizeof (data), _test_rpc_message_from_data_op_msg_valid_kind_0); +} + +static void +_test_rpc_message_from_data_op_msg_valid_kind_1_single (const uint8_t *data, + size_t data_len, + bool with_checksum) +{ + const void *data_end = NULL; + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, &data_end); + + ASSERT_RPC_MESSAGE_RESULT (rpc, data, data_end, data_len); + ASSERT (bson_in_range_unsigned (int32_t, data_len)); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_message_length (rpc), ==, (int32_t) data_len); + ASSERT_CMPINT32 (mcd_rpc_header_get_request_id (rpc), ==, 16909060); + ASSERT_CMPINT32 (mcd_rpc_header_get_response_to (rpc), ==, 84281096); + ASSERT_CMPINT32 (mcd_rpc_header_get_op_code (rpc), ==, MONGOC_OP_CODE_MSG); + + if (with_checksum) { + ASSERT_CMPUINT32 (mcd_rpc_op_msg_get_flag_bits (rpc), + ==, + MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT); + } else { + ASSERT_CMPUINT32 ( + mcd_rpc_op_msg_get_flag_bits (rpc), ==, MONGOC_OP_MSG_FLAG_NONE); + } + + ASSERT_CMPSIZE_T (mcd_rpc_op_msg_get_sections_count (rpc), ==, 2u); + + // Section 0. + { + ASSERT_CMPUINT (mcd_rpc_op_msg_section_get_kind (rpc, 0u), ==, 0u); + const int32_t section_len = mcd_rpc_op_msg_section_get_length (rpc, 0u); + ASSERT_CMPINT32 (section_len, ==, 15); + const void *const body = mcd_rpc_op_msg_section_get_body (rpc, 0u); + ASSERT (body); + + bson_t bson; + ASSERT (bson_init_static (&bson, body, (size_t) section_len)); + ASSERT_MATCH (&bson, "{'kind': 0}"); + } + + // Section 1. + { + ASSERT_CMPUINT (mcd_rpc_op_msg_section_get_kind (rpc, 1u), ==, 1u); + const int32_t section_len = mcd_rpc_op_msg_section_get_length (rpc, 1u); + ASSERT_CMPINT32 (section_len, ==, 26); + ASSERT_CMPSTR (mcd_rpc_op_msg_section_get_identifier (rpc, 1u), "single"); + const void *const sequence = + mcd_rpc_op_msg_section_get_document_sequence (rpc, 1u); + ASSERT (sequence); + ASSERT_CMPSIZE_T ( + mcd_rpc_op_msg_section_get_document_sequence_length (rpc, 1u), + ==, + 15u); + + const int32_t bson_len = _int32_from_le (sequence); + ASSERT_CMPINT32 (bson_len, ==, 15); + + bson_t bson; + ASSERT (bson_init_static (&bson, sequence, (size_t) bson_len)); + ASSERT_MATCH (&bson, "{'kind': 1}"); + } + + if (with_checksum) { + const uint32_t *checksum = mcd_rpc_op_msg_get_checksum (rpc); + ASSERT (checksum); + ASSERT_CMPUINT32 (*checksum, ==, 287454020u); + } else { + ASSERT (!mcd_rpc_op_msg_get_checksum (rpc)); + } + + mcd_rpc_message_destroy (rpc); +} + +static void +test_rpc_message_from_data_op_msg_valid_kind_1_single (void) +{ + uint8_t data[] = {TEST_DATA_OP_MSG_KIND_1_SINGLE}; + + _test_rpc_message_from_data_op_msg_valid ( + data, + sizeof (data), + _test_rpc_message_from_data_op_msg_valid_kind_1_single); +} + +static void +_test_rpc_message_from_data_op_msg_valid_kind_1_multiple (const uint8_t *data, + size_t data_len, + bool with_checksum) +{ + const void *data_end = NULL; + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, &data_end); + + ASSERT_RPC_MESSAGE_RESULT (rpc, data, data_end, data_len); + ASSERT (bson_in_range_unsigned (int32_t, data_len)); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_message_length (rpc), ==, (int32_t) data_len); + ASSERT_CMPINT32 (mcd_rpc_header_get_request_id (rpc), ==, 16909060); + ASSERT_CMPINT32 (mcd_rpc_header_get_response_to (rpc), ==, 84281096); + ASSERT_CMPINT32 (mcd_rpc_header_get_op_code (rpc), ==, MONGOC_OP_CODE_MSG); + + if (with_checksum) { + ASSERT_CMPUINT32 (mcd_rpc_op_msg_get_flag_bits (rpc), + ==, + MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT); + } else { + ASSERT_CMPUINT32 ( + mcd_rpc_op_msg_get_flag_bits (rpc), ==, MONGOC_OP_MSG_FLAG_NONE); + } + + ASSERT_CMPSIZE_T (mcd_rpc_op_msg_get_sections_count (rpc), ==, 3u); + + // Section 0. + { + ASSERT_CMPUINT (mcd_rpc_op_msg_section_get_kind (rpc, 0u), ==, 0u); + const int32_t section_len = mcd_rpc_op_msg_section_get_length (rpc, 0u); + ASSERT_CMPINT32 (section_len, ==, 15); + const void *const body = mcd_rpc_op_msg_section_get_body (rpc, 0u); + ASSERT (body); + + bson_t bson; + ASSERT (bson_init_static (&bson, body, (size_t) section_len)); + ASSERT_MATCH (&bson, "{'kind': 0}"); + } + + // Section 1. + { + ASSERT_CMPUINT (mcd_rpc_op_msg_section_get_kind (rpc, 1u), ==, 1u); + const int32_t section_len = mcd_rpc_op_msg_section_get_length (rpc, 1u); + ASSERT_CMPINT32 (section_len, ==, 25); + ASSERT_CMPSTR (mcd_rpc_op_msg_section_get_identifier (rpc, 1u), "first"); + const void *const sequence = + mcd_rpc_op_msg_section_get_document_sequence (rpc, 1u); + ASSERT (sequence); + + const int32_t bson_len = _int32_from_le (sequence); + ASSERT_CMPINT32 (bson_len, ==, 15); + + bson_t bson; + ASSERT (bson_init_static (&bson, sequence, (size_t) bson_len)); + ASSERT_MATCH (&bson, "{'kind': 1}"); + } + + // Section 2. + { + ASSERT_CMPUINT (mcd_rpc_op_msg_section_get_kind (rpc, 2u), ==, 1u); + const int32_t section_len = mcd_rpc_op_msg_section_get_length (rpc, 2u); + ASSERT_CMPINT32 (section_len, ==, 43); + ASSERT_CMPSTR (mcd_rpc_op_msg_section_get_identifier (rpc, 2u), "second"); + const uint8_t *const sequence = + mcd_rpc_op_msg_section_get_document_sequence (rpc, 2u); + ASSERT (sequence); + + // BSON objects, index 0. + { + const uint8_t *const doc_0 = sequence; + const int32_t bson_len = _int32_from_le (doc_0); + ASSERT_CMPINT32 (bson_len, ==, 16); + + bson_t bson; + ASSERT (bson_init_static (&bson, doc_0, (size_t) bson_len)); + ASSERT_MATCH (&bson, "{'index': 0}"); + bson_destroy (&bson); + } + + // BSON objects, index 1. + { + const uint8_t *const doc_1 = sequence + 16; + const int32_t bson_len = _int32_from_le (doc_1); + ASSERT_CMPINT32 (bson_len, ==, 16); + + bson_t bson; + ASSERT (bson_init_static (&bson, doc_1, (size_t) bson_len)); + ASSERT_MATCH (&bson, "{'index': 1}"); + bson_destroy (&bson); + } + } + + if (with_checksum) { + const uint32_t *checksum = mcd_rpc_op_msg_get_checksum (rpc); + ASSERT (checksum); + ASSERT_CMPUINT32 (*checksum, ==, 287454020u); + } else { + ASSERT (!mcd_rpc_op_msg_get_checksum (rpc)); + } + + mcd_rpc_message_destroy (rpc); +} + +static void +test_rpc_message_from_data_op_msg_valid_kind_1_multiple (void) +{ + uint8_t data[] = {TEST_DATA_OP_MSG_KIND_1_MULTIPLE}; + + _test_rpc_message_from_data_op_msg_valid ( + data, + sizeof (data), + _test_rpc_message_from_data_op_msg_valid_kind_1_multiple); +} + +static void +test_rpc_message_from_data_op_msg_valid (void) +{ + test_rpc_message_from_data_op_msg_valid_kind_0 (); + test_rpc_message_from_data_op_msg_valid_kind_1_single (); + test_rpc_message_from_data_op_msg_valid_kind_1_multiple (); +} + +static void +test_rpc_message_from_data_op_reply_valid (void) +{ + uint8_t data[] = {TEST_DATA_OP_REPLY}; + const size_t data_len = sizeof (data); + + { + const void *data_end = NULL; + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, &data_end); + + ASSERT_RPC_MESSAGE_RESULT (rpc, data, data_end, data_len); + ASSERT (bson_in_range_unsigned (int32_t, data_len)); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_message_length (rpc), ==, (int32_t) data_len); + ASSERT_CMPINT32 (mcd_rpc_header_get_request_id (rpc), ==, 16909060); + ASSERT_CMPINT32 (mcd_rpc_header_get_response_to (rpc), ==, 84281096); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_op_code (rpc), ==, MONGOC_OP_CODE_REPLY); + + ASSERT_CMPINT32 (mcd_rpc_op_reply_get_response_flags (rpc), + ==, + MONGOC_OP_REPLY_RESPONSE_FLAG_NONE); + + ASSERT_CMPINT64 ( + mcd_rpc_op_reply_get_cursor_id (rpc), ==, 1234605616436508552); + + ASSERT_CMPINT32 (mcd_rpc_op_reply_get_starting_from (rpc), ==, 0); + + ASSERT_CMPINT32 (mcd_rpc_op_reply_get_number_returned (rpc), ==, 2); + + const uint8_t *const documents = mcd_rpc_op_reply_get_documents (rpc); + ASSERT_CMPSIZE_T ((size_t) (documents - data), ==, 36u); + + ASSERT_CMPSIZE_T (mcd_rpc_op_reply_get_documents_len (rpc), ==, 32u); + + // Documents, index 0. + { + const uint8_t *const doc_0 = documents; + const int32_t bson_len = _int32_from_le (doc_0); + ASSERT_CMPINT32 (bson_len, ==, 16); + + bson_t bson; + ASSERT (bson_init_static (&bson, doc_0, (size_t) bson_len)); + ASSERT_MATCH (&bson, "{'index': 0}"); + bson_destroy (&bson); + } + + // Documents, index 1. + { + const uint8_t *const doc_1 = documents + 16; + const int32_t bson_len = _int32_from_le (doc_1); + ASSERT_CMPINT32 (bson_len, ==, 16); + + bson_t bson; + ASSERT (bson_init_static (&bson, doc_1, (size_t) bson_len)); + ASSERT_MATCH (&bson, "{'index': 1}"); + bson_destroy (&bson); + } + + mcd_rpc_message_destroy (rpc); + } + + // Test that responseFlags is being parsed correctly. + { + data[16] = MONGOC_OP_REPLY_RESPONSE_FLAG_CURSOR_NOT_FOUND; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, NULL); + ASSERT (rpc); + ASSERT_CMPINT32 (mcd_rpc_op_reply_get_response_flags (rpc), + ==, + MONGOC_OP_REPLY_RESPONSE_FLAG_CURSOR_NOT_FOUND); + mcd_rpc_message_destroy (rpc); + + data[16] = MONGOC_OP_REPLY_RESPONSE_FLAG_NONE; + } + + // Test that startingFrom is being parsed correctly. + { + data[28] = 1u; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, NULL); + ASSERT (rpc); + ASSERT_CMPINT32 (mcd_rpc_op_reply_get_starting_from (rpc), ==, 1); + mcd_rpc_message_destroy (rpc); + + data[28] = 0u; + } + + // Test that documents are being parsed correctly. + { + data[0] = (uint8_t) (data[0] - 32u); // Exclude documents. + data[32] = 0x00u; // Set numberReturned to 0. + + { + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, NULL); + ASSERT (rpc); + ASSERT_CMPINT32 (mcd_rpc_op_reply_get_number_returned (rpc), ==, 0); + ASSERT_CMPSIZE_T (mcd_rpc_op_reply_get_documents_len (rpc), ==, 0u); + ASSERT (mcd_rpc_op_reply_get_documents (rpc) == NULL); + mcd_rpc_message_destroy (rpc); + } + + data[32] = 0x02u; // Revert numberReturned to 2. + data[0] = (uint8_t) (data[0] + 32u); // Restore documents. + } +} + +static void +test_rpc_message_from_data_op_update_valid (void) +{ + uint8_t data[] = {TEST_DATA_OP_UPDATE}; + const size_t data_len = sizeof (data); + + { + const void *data_end = NULL; + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, &data_end); + + ASSERT_RPC_MESSAGE_RESULT (rpc, data, data_end, data_len); + ASSERT (bson_in_range_unsigned (int32_t, data_len)); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_message_length (rpc), ==, (int32_t) data_len); + ASSERT_CMPINT32 (mcd_rpc_header_get_request_id (rpc), ==, 16909060); + ASSERT_CMPINT32 (mcd_rpc_header_get_response_to (rpc), ==, 84281096); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_op_code (rpc), ==, MONGOC_OP_CODE_UPDATE); + + ASSERT_CMPSTR (mcd_rpc_op_update_get_full_collection_name (rpc), + "db.coll"); + + ASSERT_CMPINT32 ( + mcd_rpc_op_update_get_flags (rpc), ==, MONGOC_OP_UPDATE_FLAG_NONE); + + { + const uint8_t *const selector = mcd_rpc_op_update_get_selector (rpc); + ASSERT_CMPSIZE_T ((size_t) (selector - data), ==, 32u); + + const int32_t selector_len = _int32_from_le (selector); + ASSERT_CMPINT32 (selector_len, ==, 16); + + bson_t bson; + ASSERT (bson_init_static (&bson, selector, (size_t) selector_len)); + ASSERT_MATCH (&bson, "{'selector': false}"); + bson_destroy (&bson); + } + + { + const uint8_t *const update = mcd_rpc_op_update_get_update (rpc); + ASSERT_CMPSIZE_T ((size_t) (update - data), ==, 48u); + + const int32_t update_len = _int32_from_le (update); + ASSERT_CMPINT32 (update_len, ==, 14); + + bson_t bson; + ASSERT (bson_init_static (&bson, update, (size_t) update_len)); + ASSERT_MATCH (&bson, "{'update': true}"); + bson_destroy (&bson); + } + + mcd_rpc_message_destroy (rpc); + } + + // Test that flags is being parsed correctly. + { + data[28] = MONGOC_OP_UPDATE_FLAG_UPSERT; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, NULL); + ASSERT (rpc); + ASSERT_CMPINT32 ( + mcd_rpc_op_update_get_flags (rpc), ==, MONGOC_OP_UPDATE_FLAG_UPSERT); + mcd_rpc_message_destroy (rpc); + + data[28] = MONGOC_OP_UPDATE_FLAG_NONE; + } +} + +static void +test_rpc_message_from_data_op_insert_valid (void) +{ + uint8_t data[] = {TEST_DATA_OP_INSERT}; + const size_t data_len = sizeof (data); + + { + const void *data_end = NULL; + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, &data_end); + + ASSERT_RPC_MESSAGE_RESULT (rpc, data, data_end, data_len); + ASSERT (bson_in_range_unsigned (int32_t, data_len)); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_message_length (rpc), ==, (int32_t) data_len); + ASSERT_CMPINT32 (mcd_rpc_header_get_request_id (rpc), ==, 16909060); + ASSERT_CMPINT32 (mcd_rpc_header_get_response_to (rpc), ==, 84281096); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_op_code (rpc), ==, MONGOC_OP_CODE_INSERT); + + ASSERT_CMPINT32 ( + mcd_rpc_op_insert_get_flags (rpc), ==, MONGOC_OP_INSERT_FLAG_NONE); + + ASSERT_CMPSTR (mcd_rpc_op_insert_get_full_collection_name (rpc), + "db.coll"); + + const uint8_t *const documents = mcd_rpc_op_insert_get_documents (rpc); + ASSERT (documents); + ASSERT_CMPSIZE_T (mcd_rpc_op_insert_get_documents_len (rpc), ==, 32u); + + // Documents, index 0. + { + const uint8_t *const doc_0 = documents; + const int32_t bson_len = _int32_from_le (doc_0); + ASSERT_CMPINT32 (bson_len, ==, 16); + + bson_t bson; + ASSERT (bson_init_static (&bson, doc_0, (size_t) bson_len)); + ASSERT_MATCH (&bson, "{'index': 0}"); + bson_destroy (&bson); + } + + // Documents, index 1. + { + const uint8_t *const doc_1 = documents + 16; + const int32_t bson_len = _int32_from_le (doc_1); + ASSERT_CMPINT32 (bson_len, ==, 16); + + bson_t bson; + ASSERT (bson_init_static (&bson, doc_1, (size_t) bson_len)); + ASSERT_MATCH (&bson, "{'index': 1}"); + bson_destroy (&bson); + } + + mcd_rpc_message_destroy (rpc); + } + + // Test that flags is being parsed correctly. + { + data[16] = MONGOC_OP_INSERT_FLAG_CONTINUE_ON_ERROR; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, NULL); + ASSERT (rpc); + ASSERT_CMPINT32 (mcd_rpc_op_insert_get_flags (rpc), + ==, + MONGOC_OP_INSERT_FLAG_CONTINUE_ON_ERROR); + mcd_rpc_message_destroy (rpc); + + data[16] = MONGOC_OP_INSERT_FLAG_NONE; + } + + // Test that documents are being parsed correctly. + { + data[0] = (uint8_t) (data[0] - 16u); // Exclude document 1. + + { + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, NULL); + ASSERT (rpc); + ASSERT_CMPSIZE_T (mcd_rpc_op_insert_get_documents_len (rpc), ==, 16u); + mcd_rpc_message_destroy (rpc); + } + + data[0] = (uint8_t) (data[0] + 16u); // Restore document 1. + } +} + +static void +test_rpc_message_from_data_op_query_valid (void) +{ + uint8_t data[] = {TEST_DATA_OP_QUERY}; + const size_t data_len = sizeof (data); + + { + const void *data_end = NULL; + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, &data_end); + + ASSERT_RPC_MESSAGE_RESULT (rpc, data, data_end, data_len); + ASSERT (bson_in_range_unsigned (int32_t, data_len)); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_message_length (rpc), ==, (int32_t) data_len); + ASSERT_CMPINT32 (mcd_rpc_header_get_request_id (rpc), ==, 16909060); + ASSERT_CMPINT32 (mcd_rpc_header_get_response_to (rpc), ==, 84281096); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_op_code (rpc), ==, MONGOC_OP_CODE_QUERY); + + ASSERT_CMPINT32 ( + mcd_rpc_op_query_get_flags (rpc), ==, MONGOC_OP_QUERY_FLAG_NONE); + + ASSERT_CMPSTR (mcd_rpc_op_query_get_full_collection_name (rpc), + "db.coll"); + + ASSERT_CMPINT32 (mcd_rpc_op_query_get_number_to_skip (rpc), ==, 0); + + ASSERT_CMPINT32 (mcd_rpc_op_query_get_number_to_return (rpc), ==, 0); + + { + const uint8_t *const query = mcd_rpc_op_query_get_query (rpc); + ASSERT_CMPSIZE_T ((size_t) (query - data), ==, 36u); + + const int32_t query_len = _int32_from_le (query); + ASSERT_CMPINT32 (query_len, ==, 13); + + bson_t bson; + ASSERT (bson_init_static (&bson, query, (size_t) query_len)); + ASSERT_MATCH (&bson, "{'query': false}"); + bson_destroy (&bson); + } + + { + const uint8_t *const selector = + mcd_rpc_op_query_get_return_fields_selector (rpc); + ASSERT_CMPSIZE_T ((size_t) (selector - data), ==, 49u); + + const int32_t selector_len = _int32_from_le (selector); + ASSERT_CMPINT32 (selector_len, ==, 16); + + bson_t bson; + ASSERT (bson_init_static (&bson, selector, (size_t) selector_len)); + ASSERT_MATCH (&bson, "{'selector': true}"); + bson_destroy (&bson); + } + + mcd_rpc_message_destroy (rpc); + } + + // Test that flags is being parsed correctly. + { + data[16] = MONGOC_OP_QUERY_FLAG_TAILABLE_CURSOR; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, NULL); + ASSERT (rpc); + ASSERT_CMPINT32 (mcd_rpc_op_query_get_flags (rpc), + ==, + MONGOC_OP_QUERY_FLAG_TAILABLE_CURSOR); + mcd_rpc_message_destroy (rpc); + + data[16] = MONGOC_OP_QUERY_FLAG_NONE; + } + + // Test that numberToSkip is being parsed correctly. + { + data[28] = 1; // Set numberToSkip to 1. + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, NULL); + ASSERT (rpc); + ASSERT_CMPINT32 (mcd_rpc_op_query_get_number_to_skip (rpc), ==, 1); + mcd_rpc_message_destroy (rpc); + + data[28] = 0; // Restore numberToSkip. + } + + // Test that numberToReturn is being parsed correctly. + { + data[32] = 1; // Set numberToReturn to 1. + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, NULL); + ASSERT (rpc); + ASSERT_CMPINT32 (mcd_rpc_op_query_get_number_to_return (rpc), ==, 1); + mcd_rpc_message_destroy (rpc); + + data[32] = 0; // Restore numberToReturn. + } + + // Test that returnFieldSelector is optional. + { + data[0] = (uint8_t) (data[0] - 16u); // Omit returnFieldSelector. + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, NULL); + ASSERT (rpc); + ASSERT (!mcd_rpc_op_query_get_return_fields_selector (rpc)); + mcd_rpc_message_destroy (rpc); + + data[0] = (uint8_t) (data[0] + 16u); // Restore returnFieldSelector. + } +} + +static void +test_rpc_message_from_data_op_get_more_valid (void) +{ + uint8_t data[] = {TEST_DATA_OP_GET_MORE}; + const size_t data_len = sizeof (data); + + // Valid test input data. + { + const void *data_end = NULL; + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, &data_end); + + ASSERT_RPC_MESSAGE_RESULT (rpc, data, data_end, data_len); + ASSERT (bson_in_range_unsigned (int32_t, data_len)); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_message_length (rpc), ==, (int32_t) data_len); + ASSERT_CMPINT32 (mcd_rpc_header_get_request_id (rpc), ==, 16909060); + ASSERT_CMPINT32 (mcd_rpc_header_get_response_to (rpc), ==, 84281096); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_op_code (rpc), ==, MONGOC_OP_CODE_GET_MORE); + + ASSERT_CMPSTR (mcd_rpc_op_get_more_get_full_collection_name (rpc), + "db.coll"); + + ASSERT_CMPINT32 (mcd_rpc_op_get_more_get_number_to_return (rpc), ==, 0); + + ASSERT_CMPINT64 ( + mcd_rpc_op_get_more_get_cursor_id (rpc), ==, 1234605616436508552); + + mcd_rpc_message_destroy (rpc); + } +} + +static void +test_rpc_message_from_data_op_delete_valid (void) +{ + uint8_t data[] = {TEST_DATA_OP_DELETE}; + const size_t data_len = sizeof (data); + + // Valid test input data. + { + const void *data_end = NULL; + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, &data_end); + + ASSERT_RPC_MESSAGE_RESULT (rpc, data, data_end, data_len); + ASSERT (bson_in_range_unsigned (int32_t, data_len)); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_message_length (rpc), ==, (int32_t) data_len); + ASSERT_CMPINT32 (mcd_rpc_header_get_request_id (rpc), ==, 16909060); + ASSERT_CMPINT32 (mcd_rpc_header_get_response_to (rpc), ==, 84281096); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_op_code (rpc), ==, MONGOC_OP_CODE_DELETE); + + ASSERT_CMPSTR (mcd_rpc_op_delete_get_full_collection_name (rpc), + "db.coll"); + + ASSERT_CMPINT32 ( + mcd_rpc_op_delete_get_flags (rpc), ==, MONGOC_OP_DELETE_FLAG_NONE); + + { + const uint8_t *const selector = mcd_rpc_op_delete_get_selector (rpc); + ASSERT_CMPSIZE_T ((size_t) (selector - data), ==, 32u); + + const int32_t selector_len = _int32_from_le (selector); + ASSERT_CMPINT32 (selector_len, ==, 16); + + bson_t bson; + ASSERT (bson_init_static (&bson, selector, (size_t) selector_len)); + ASSERT_MATCH (&bson, "{'selector': false}"); + bson_destroy (&bson); + } + + mcd_rpc_message_destroy (rpc); + } +} + +static void +test_rpc_message_from_data_op_kill_cursors_valid (void) +{ + uint8_t data[] = {TEST_DATA_OP_KILL_CURSORS}; + const size_t data_len = sizeof (data); + + // Valid test input data. + { + const void *data_end = NULL; + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, &data_end); + + ASSERT_RPC_MESSAGE_RESULT (rpc, data, data_end, data_len); + ASSERT (bson_in_range_unsigned (int32_t, data_len)); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_message_length (rpc), ==, (int32_t) data_len); + ASSERT_CMPINT32 (mcd_rpc_header_get_request_id (rpc), ==, 16909060); + ASSERT_CMPINT32 (mcd_rpc_header_get_response_to (rpc), ==, 84281096); + ASSERT_CMPINT32 ( + mcd_rpc_header_get_op_code (rpc), ==, MONGOC_OP_CODE_KILL_CURSORS); + + ASSERT_CMPINT32 ( + mcd_rpc_op_kill_cursors_get_number_of_cursor_ids (rpc), ==, 2); + + const int64_t *const cursor_ids = + mcd_rpc_op_kill_cursors_get_cursor_ids (rpc); + + ASSERT_CMPINT64 (cursor_ids[0], ==, 1230066625199609624); + ASSERT_CMPINT64 (cursor_ids[1], ==, 2387509390608836392); + + mcd_rpc_message_destroy (rpc); + } + + // Test that cursorIDs is being parsed correctly. + { + data[0] = (uint8_t) (data[0] - 8u); // Truncate cursorID 1. + data[20] = (uint8_t) (data[20] - 1u); // Set numberOfCursorIds to 1. + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, NULL); + ASSERT (rpc); + ASSERT_CMPINT32 ( + mcd_rpc_op_kill_cursors_get_number_of_cursor_ids (rpc), ==, 1); + ASSERT_CMPINT64 (mcd_rpc_op_kill_cursors_get_cursor_ids (rpc)[0], + ==, + 1230066625199609624); + mcd_rpc_message_destroy (rpc); + + data[20] = (uint8_t) (data[20] + 1u); // Restore numberOfCursorIds. + data[0] = (uint8_t) (data[0] + 8u); // Restore cursorID 1. + } + + // Test that cursorIDs is being parsed correctly. + { + data[0] = (uint8_t) (data[0] - 16u); // Exclude cursorIDs. + data[20] = (uint8_t) (data[20] - 2u); // Set numberOfCursorIds to 0. + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, NULL); + ASSERT (rpc); + ASSERT_CMPINT32 ( + mcd_rpc_op_kill_cursors_get_number_of_cursor_ids (rpc), ==, 0); + ASSERT (mcd_rpc_op_kill_cursors_get_cursor_ids (rpc) == NULL); + mcd_rpc_message_destroy (rpc); + + data[20] = (uint8_t) (data[20] + 2u); // Restore numberOfCursorIds. + data[0] = (uint8_t) (data[0] + 16u); // Restore cursorID 1. + } +} + + +static void +_test_from_data_invalid_decr (const char *file, + int line, + uint8_t *data, + size_t data_len, + bool expect_success, + size_t min, + size_t max, + size_t bytes_parsed_expected) +{ + ASSERT_WITH_MSG ( + min <= max, + "%s:%d: min (%zu) should be less than or equal to max (%zu)", + file, + line, + min, + max); + ASSERT_WITH_MSG (max < data_len, + "%s:%d: max byte %zu exceeds input data length %zu", + file, + line, + max, + data_len); + + for (size_t i = min; i <= max; ++i) { + data[i] = (uint8_t) (data[i] - 1u); // Set to original value - 1. + + { + const void *data_end = NULL; + mcd_rpc_message *rpc = + mcd_rpc_message_from_data (data, data_len, &data_end); + + if (expect_success) { + ASSERT_WITH_MSG ( + rpc, + "%s:%d: byte %zu: expected decrement to still succeed", + file, + line, + i); + } else { + ASSERT_WITH_MSG ( + !rpc, + "%s:%d: byte %zu: expected decrement to trigger failure", + file, + line, + i); + } + + const size_t bytes_parsed_actual = + (size_t) ((const uint8_t *) data_end - data); + + ASSERT_WITH_MSG (bytes_parsed_expected == bytes_parsed_actual, + "%s:%d: byte %zu: expected decrement to cause " + "%zu bytes to be parsed, but parsed %zu bytes", + file, + line, + i, + bytes_parsed_expected, + bytes_parsed_actual); + + mcd_rpc_message_destroy (rpc); + } + + data[i] = (uint8_t) (data[i] + 1u); // Revert to original value. + } +} + +static void +_test_from_data_invalid_incr (const char *file, + int line, + uint8_t *data, + size_t data_len, + bool expect_success, + size_t min, + size_t max, + size_t bytes_parsed_expected) +{ + ASSERT_WITH_MSG (max < data_len, + "%s:%d: max byte %zu exceeds input data length %zu", + file, + line, + max, + data_len); + + for (size_t i = min; i <= max; ++i) { + data[i] = (uint8_t) (data[i] + 1u); // Set to original value + 1. + + { + const void *data_end = NULL; + mcd_rpc_message *rpc = + mcd_rpc_message_from_data (data, data_len, &data_end); + + if (expect_success) { + ASSERT_WITH_MSG ( + rpc, + "%s:%d: byte %zu: expected increment to still succeed", + file, + line, + i); + } else { + ASSERT_WITH_MSG ( + !rpc, + "%s:%d: byte %zu: expected increment to trigger failure", + file, + line, + i); + } + + const size_t bytes_parsed_actual = + (size_t) ((const uint8_t *) data_end - data); + + ASSERT_WITH_MSG (bytes_parsed_expected == bytes_parsed_actual, + "%s:%d: byte %zu: expected increment to cause " + "%zu bytes to be parsed, but parsed %zu bytes", + file, + line, + i, + bytes_parsed_expected, + bytes_parsed_actual); + + mcd_rpc_message_destroy (rpc); + } + + data[i] = (uint8_t) (data[i] - 1u); // Revert to original value. + } +} + +static void +_test_from_data_input_bounds (const uint8_t *data, size_t data_len) +{ + ASSERT_WITH_MSG (data[data_len] == 0xFF && data[data_len + 1u] == 0x00, + "expected input data to have extra bytes available for " + "boundary testing"); + + // Reducing data length below messageLength should always trigger failure due + // to insufficient bytes. + for (size_t i = 0u; i < data_len; ++i) { + const void *data_end = NULL; + ASSERT_WITH_MSG (!mcd_rpc_message_from_data (data, i, &data_end), + "expected reduced data length of %zu to trigger " + "failure, but successfully parsed %zu bytes", + i, + (size_t) ((const uint8_t *) data_end - data)); + } + + // It is NOT an error for data length to be greater than messageLength. + { + const void *data_end = NULL; + mcd_rpc_message *rpc = + mcd_rpc_message_from_data (data, data_len + 1u, &data_end); + ASSERT_CMPSIZE_T ( + (size_t) ((const uint8_t *) data_end - data), ==, data_len); + ASSERT_WITH_MSG (rpc, "expected extra bytes to remain unparsed"); + ASSERT_CMPUINT (*(const uint8_t *) data_end, ==, 0xFF); + mcd_rpc_message_destroy (rpc); + } +} + + +#define EXPECT_DECR_FAILURE(min, max, end) \ + _test_from_data_invalid_decr ( \ + __FILE__, __LINE__, data, data_len, false, min, max, end) +#define EXPECT_DECR_SUCCESS(min, max, end) \ + _test_from_data_invalid_decr ( \ + __FILE__, __LINE__, data, data_len, true, min, max, end) +#define EXPECT_INCR_FAILURE(min, max, end) \ + _test_from_data_invalid_incr ( \ + __FILE__, __LINE__, data, data_len, false, min, max, end) +#define EXPECT_INCR_SUCCESS(min, max, end) \ + _test_from_data_invalid_incr ( \ + __FILE__, __LINE__, data, data_len, true, min, max, end) +#define EXPECT_DECR_IGNORED(min, max, end) +#define EXPECT_INCR_IGNORED(min, max, end) + + +static void +test_rpc_message_from_data_op_compressed_invalid (void) +{ + uint8_t data[] = {TEST_DATA_OP_COMPRESSED, 0xFF, 0x00}; + const size_t data_len = sizeof (data) - 2u; // Exclude the extra bytes. + + // clang-format off + EXPECT_DECR_SUCCESS ( 0u, 0u, 42u); // messageLength (byte 0). + EXPECT_DECR_FAILURE ( 1u, 3u, 0u); // messageLength (bytes 1-3): too large. + EXPECT_DECR_SUCCESS ( 4u, 11u, 43u); // requestID, responseTo. + EXPECT_DECR_FAILURE (12u, 15u, 12u); // opCode: invalid. + EXPECT_DECR_SUCCESS (16u, 42u, 43u); // originalOpcode, uncompressedSize, compressorId, compressedMessage. + + EXPECT_INCR_FAILURE ( 0u, 3u, 0u); // messageLength: too large. + EXPECT_INCR_SUCCESS ( 4u, 11u, 43u); // requestId, responseTo. + EXPECT_INCR_IGNORED (12u, 12u, 12u); // opCode (byte 0): parse as OP_MSG. + EXPECT_INCR_FAILURE (13u, 15u, 12u); // opCode (byte 1-3): invalid. + EXPECT_INCR_SUCCESS (16u, 42u, 43u); // originalOpcode, uncompressedSize, compressorId, compressedMessage. + // clang-format on + + _test_from_data_input_bounds (data, data_len); +} + + +static void +test_rpc_message_from_data_op_msg_invalid_kind_0 (void) +{ + uint8_t data[] = {TEST_DATA_OP_MSG_KIND_0, 0xFF, 0x00}; + const size_t data_len = sizeof (data) - 2u; // Exclude the extra bytes. + + // clang-format off + EXPECT_DECR_FAILURE ( 0u, 0u, 36u); // messageLength (byte 0): insufficient bytes to parse checksum. + EXPECT_DECR_FAILURE ( 1u, 3u, 0u); // messageLength (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS ( 4u, 11u, 40u); // requestID, responseTo. + EXPECT_DECR_IGNORED (12u, 12u, 12u); // opCode (byte 0): parse as OP_COMPRESSED. + EXPECT_DECR_FAILURE (13u, 15u, 12u); // opCode (bytes 1-3): invalid. + EXPECT_DECR_FAILURE (16u, 16u, 36u); // flagBits (byte 0): set to MONGOC_OP_MSG_FLAG_NONE -> unexpected bytes remaining. + EXPECT_DECR_FAILURE (17u, 17u, 16u); // flagBits (byte 1): invalid. + EXPECT_DECR_SUCCESS (18u, 19u, 40u); // flagBits (bytes 2-3). + EXPECT_DECR_FAILURE (20u, 20u, 20u); // Section 0 kind: invalid. + EXPECT_DECR_FAILURE (21u, 21u, 35u); // Section 0 body length (byte 0): truncated body content -> invalid section 1 with duplicate kind 0. + EXPECT_DECR_FAILURE (22u, 24u, 21u); // Section 0 body length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS (25u, 35u, 40u); // Section 0 body content. + EXPECT_DECR_SUCCESS (36u, 39u, 40u); // Checksum. + // clang-format on + + // clang-format off + EXPECT_INCR_FAILURE ( 0u, 3u, 0u); // messageLength: invalid. + EXPECT_INCR_SUCCESS ( 4u, 11u, 40u); // requestID, responseTo. + EXPECT_INCR_FAILURE (16u, 16u, 36u); // flagBits (byte 0): set to MONGOC_OP_MSG_FLAG_MORE_TO_COME -> unexpected bytes remaining. + EXPECT_INCR_FAILURE (17u, 17u, 16u); // flagBits (byte 1): invalid. + EXPECT_INCR_FAILURE (12u, 15u, 12u); // opCode: invalid opCode. + EXPECT_INCR_SUCCESS (18u, 19u, 40u); // flagBits (bytes 2-3). + EXPECT_INCR_FAILURE (20u, 20u, 31u); // Section 0 kind: parse as document sequence -> invalid document 0 length (0x00000000). + EXPECT_INCR_FAILURE (21u, 21u, 37u); // Section 0 body length (byte 0): extended body content -> insufficient bytes to parse checksum. + EXPECT_INCR_FAILURE (22u, 24u, 21u); // Section 0 body length (bytes 1-3): invalid. + EXPECT_INCR_SUCCESS (25u, 35u, 40u); // Section 0 body content. + EXPECT_INCR_SUCCESS (36u, 39u, 40u); // Checksum. + // clang-format on + + _test_from_data_input_bounds (data, data_len); +} + +static void +test_rpc_message_from_data_op_msg_invalid_kind_1_single (void) +{ + uint8_t data[] = {TEST_DATA_OP_MSG_KIND_1_SINGLE, 0xFF, 0x00}; + const size_t data_len = sizeof (data) - 2u; // Exclude the extra bytes. + + // clang-format off + EXPECT_DECR_FAILURE ( 0u, 0u, 63u); // messageLength (byte 0): insufficient bytes to parse checksum. + EXPECT_DECR_FAILURE ( 1u, 3u, 0u); // messageLength (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS ( 4u, 11u, 67u); // requestID, responseTo. + EXPECT_DECR_IGNORED (12u, 12u, 12u); // opCode (byte 0): parse as OP_COMPRESSED. + EXPECT_DECR_FAILURE (13u, 15u, 12u); // opCode (bytes 1-3): invalid opCode. + EXPECT_DECR_FAILURE (16u, 16u, 63u); // flagBits (byte 0): set to MONGOC_OP_MSG_FLAG_NONE -> unexpected bytes remaining. + EXPECT_DECR_FAILURE (17u, 17u, 16u); // flagBits (byte 1): invalid. + EXPECT_DECR_SUCCESS (18u, 19u, 67u); // flagBits (bytes 2-3). + EXPECT_DECR_FAILURE (20u, 20u, 20u); // Section 0 kind: invalid. + EXPECT_DECR_FAILURE (21u, 21u, 35u); // Section 0 length (byte 0): truncated body content -> invalid section 1 with duplicate kind 0. + EXPECT_DECR_FAILURE (22u, 24u, 21u); // Section 0 length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS (25u, 35u, 67u); // Section 0 body. + EXPECT_DECR_FAILURE (36u, 36u, 36u); // Section 1 kind: parse as body -> invalid section 1 with duplicate kind 0. + EXPECT_DECR_FAILURE (37u, 37u, 48u); // Section 1 length (byte 0): truncated document sequence content -> invalid document 0 length (15 bytes, 14 remaining). + EXPECT_DECR_FAILURE (38u, 40u, 37u); // Section 1 length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS (41u, 46u, 67u); // Section 1 identifier (content). + EXPECT_DECR_FAILURE (47u, 47u, 50u); // Section 1 identifier (terminator): extended identifier -> invalid document 0 length (0x6b100000). + EXPECT_DECR_FAILURE (48u, 48u, 62u); // Section 1 document 0 length (byte 0): truncated document 0 content -> unexpected section bytes remaining. + EXPECT_DECR_FAILURE (49u, 51u, 48u); // Section 1 document 0 length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS (52u, 62u, 67u); // Section 1 document 0 content. + EXPECT_DECR_SUCCESS (63u, 66u, 67u); // Checksum. + // clang-format on + + // clang-format off + EXPECT_INCR_FAILURE ( 0u, 3u, 0u); // messageLength: invalid. + EXPECT_INCR_SUCCESS ( 4u, 11u, 67u); // requestID, responseTo. + EXPECT_INCR_FAILURE (12u, 15u, 12u); // opCode: invalid. + EXPECT_INCR_FAILURE (16u, 16u, 63u); // flagBits (byte 0): set to MONGOC_OP_MSG_FLAG_MORE_TO_COME -> unexpected bytes remaining. + EXPECT_INCR_FAILURE (17u, 17u, 16u); // flagBits (byte 1): invalid. + EXPECT_INCR_SUCCESS (18u, 19u, 67u); // flagBits (bytes 2-3). + EXPECT_INCR_FAILURE (20u, 20u, 31u); // Section 0 kind: parse as document sequence -> invalid document 0 length (0x6b100000). + EXPECT_INCR_FAILURE (21u, 21u, 37u); // Section 0 length (byte 0): extended body content -> invalid section 1 kind (0x1a). + EXPECT_INCR_FAILURE (22u, 24u, 21u); // Section 0 length (bytes 1-3): invalid. + EXPECT_INCR_SUCCESS (25u, 35u, 67u); // Section 0 body. + EXPECT_INCR_FAILURE (36u, 36u, 36u); // Section 1 kind: invalid. + EXPECT_INCR_FAILURE (37u, 37u, 63u); // Section 1 length (byte 0): extended document sequence content -> insufficient bytes to parse checksum. + EXPECT_INCR_FAILURE (38u, 40u, 37u); // Section 1 length (bytes 1-3): invalid. + EXPECT_INCR_SUCCESS (41u, 46u, 67u); // Section 1 identifier (content). + EXPECT_INCR_FAILURE (47u, 47u, 50u); // Section 1 identifier (terminator): invalid document 0 length (0x6b100000). + EXPECT_INCR_FAILURE (48u, 51u, 48u); // Section 1 document 0 length: invalid. + EXPECT_DECR_SUCCESS (52u, 62u, 67u); // Section 1 document 0 content. + EXPECT_INCR_SUCCESS (63u, 66u, 67u); // Checksum. + // clang-format on + + _test_from_data_input_bounds (data, data_len); +} + +static void +test_rpc_message_from_data_op_msg_invalid_kind_1_multiple (void) +{ + uint8_t data[] = {TEST_DATA_OP_MSG_KIND_1_MULTIPLE, 0xFF, 0x00}; + const size_t data_len = sizeof (data) - 2u; // Exclude the extra bytes. + + // clang-format off + EXPECT_DECR_FAILURE ( 0u, 0u, 106u); // messageLength (byte 0): insufficient bytes to parse checksum. + EXPECT_DECR_FAILURE ( 1u, 3u, 0u); // messageLength (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS ( 4u, 11u, 110u); // requestID, responseTo. + EXPECT_DECR_IGNORED ( 12u, 12u, 12u); // opCode (byte 0): parse as OP_COMPRESSED. + EXPECT_DECR_FAILURE ( 13u, 15u, 12u); // opCode (bytes 1-3): invalid. + EXPECT_DECR_FAILURE ( 16u, 16u, 106u); // flagBits (byte 0): set to MONGOC_OP_MSG_FLAG_NONE -> unexpected bytes remaining. + EXPECT_DECR_FAILURE ( 17u, 17u, 16u); // flagBits (byte 1): invalid. + EXPECT_DECR_SUCCESS ( 18u, 19u, 110u); // flagBits (bytes 2-3). + EXPECT_DECR_FAILURE ( 20u, 20u, 20u); // Section 0 kind: invalid. + EXPECT_DECR_FAILURE ( 21u, 21u, 35u); // Section 0 length (byte 0): truncated body content -> invalid section 1 with duplicate kind 0. + EXPECT_DECR_FAILURE ( 22u, 24u, 21u); // Section 0 length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS ( 25u, 35u, 110u); // Section 0 body. + EXPECT_DECR_FAILURE ( 36u, 36u, 36u); // Section 1 kind: invalid section 1 with duplicate kind 0. + EXPECT_DECR_FAILURE ( 37u, 37u, 47u); // Section 1 length (byte 0): truncated document sequence content -> invalid document 2 length (15 bytes, 14 remaining). + EXPECT_DECR_FAILURE ( 38u, 40u, 37u); // Section 1 length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS ( 41u, 45u, 110u); // Section 1 identifier (content). + EXPECT_DECR_FAILURE ( 46u, 46u, 49u); // Section 1 identifier (terminator): extended identifier -> invalid document 0 length (0x6b100000). + EXPECT_DECR_FAILURE ( 47u, 47u, 61u); // Section 1 document 0 length (byte 0): truncated document 0 content -> invalid section 2 with duplicate kind 0. + EXPECT_DECR_FAILURE ( 48u, 50u, 47u); // Section 1 document 0 length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS ( 51u, 61u, 110u); // Section 1 document 0 content. + EXPECT_DECR_FAILURE ( 62u, 62u, 62u); // Section 2 kind: invalid section 2 with duplicate kind 0. + EXPECT_DECR_FAILURE ( 63u, 63u, 90u); // Section 2 length (byte 0): truncated document sequence -> invalid document 1 length (16 bytes, 15 remaining). + EXPECT_DECR_FAILURE ( 64u, 66u, 63u); // Section 2 length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS ( 67u, 72u, 110u); // Section 2 identifier (content). + EXPECT_DECR_FAILURE ( 73u, 73u, 76u); // Section 2 identifier (terminator): extended identifier -> invalid document 0 length (0x69100000). + EXPECT_DECR_FAILURE ( 74u, 74u, 89u); // Section 2 document 0 length (byte 0): truncated document 0 content -> invalid document 1 length (0x00001000). + EXPECT_DECR_FAILURE ( 75u, 77u, 74u); // Section 2 document 0 length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS ( 78u, 89u, 110u); // Section 2 document 0 content. + EXPECT_DECR_FAILURE ( 90u, 90u, 105u); // Section 2 document 1 length (byte 0): truncated document 1 content -> unexpected section bytes remaining. + EXPECT_DECR_FAILURE ( 91u, 93u, 90u); // Section 2 document 1 length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS ( 94u, 105u, 110u); // Section 2 document 1 content. + EXPECT_DECR_SUCCESS (106u, 109u, 110u); // Checksum. + // clang-format on + + // clang-format off + EXPECT_INCR_FAILURE ( 0u, 3u, 0u); // messageLength: invalid. + EXPECT_INCR_SUCCESS ( 4u, 11u, 110u); // requestID, responseTo. + EXPECT_INCR_FAILURE ( 12u, 15u, 12u); // opCode: invalid. + EXPECT_INCR_FAILURE ( 16u, 16u, 106u); // flagBits (byte 0): set to MONGOC_OP_MSG_FLAG_MORE_TO_COME -> unexpected bytes remaining. + EXPECT_INCR_FAILURE ( 17u, 17u, 16u); // flagBits (byte 1): invalid. + EXPECT_INCR_SUCCESS ( 18u, 19u, 110u); // flagBits (bytes 2-3). + EXPECT_INCR_FAILURE ( 20u, 20u, 31u); // Section 0 kind: parse as document sequence -> invalid document 0 length (0x00000000). + EXPECT_INCR_FAILURE ( 21u, 21u, 37u); // Section 0 length (byte 0): extended body content -> invalid section 1 kind (0x19). + EXPECT_INCR_FAILURE ( 22u, 24u, 21u); // Section 0 length (bytes 1-3): invalid. + EXPECT_INCR_SUCCESS ( 25u, 35u, 110u); // Section 0 body. + EXPECT_INCR_FAILURE ( 36u, 36u, 36u); // Section 1 kind: invalid. + EXPECT_INCR_FAILURE ( 37u, 37u, 62u); // Section 1 length (byte 0): extended document sequence content -> unexpected section bytes remaining. + EXPECT_INCR_FAILURE ( 38u, 40u, 37u); // Section 1 length (bytes 1-3): invalid. + EXPECT_INCR_SUCCESS ( 41u, 45u, 110u); // Section 1 identifier (content). + EXPECT_INCR_FAILURE ( 46u, 46u, 49u); // Section 1 identifier (terminator): extended identifier -> invalid document 0 length (0x6b000000). + EXPECT_INCR_FAILURE ( 47u, 50u, 47u); // Section 1 document 0 length: invalid. + EXPECT_INCR_SUCCESS ( 51u, 61u, 110u); // Section 1 document 0 content. + EXPECT_INCR_FAILURE ( 62u, 62u, 62u); // Section 2 kind: invalid. + EXPECT_INCR_FAILURE ( 63u, 63u, 106u); // Section 2 length (byte 0): extended document sequence content -> unexpected section bytes remaining. + EXPECT_INCR_FAILURE ( 64u, 66u, 63u); // Section 2 length (bytes 1-3): invalid. + EXPECT_INCR_SUCCESS ( 67u, 72u, 110u); // Section 2 identifier (content). + EXPECT_INCR_FAILURE ( 73u, 73u, 76u); // Section 2 identifier (terminator): extended identifier -> invalid document 0 length (0x69100000). + EXPECT_INCR_FAILURE ( 74u, 74u, 91u); // Section 2 document 0 length (byte 0): extended document 0 content -> invalid document 1 length (0x10000000). + EXPECT_INCR_FAILURE ( 75u, 77u, 74u); // Section 2 document 0 length (bytes 1-3): invalid. + EXPECT_INCR_SUCCESS ( 78u, 89u, 110u); // Section 2 document 0 content. + EXPECT_INCR_FAILURE ( 90u, 93u, 90u); // Section 2 document 1 length: invalid. + EXPECT_INCR_SUCCESS ( 94u, 105u, 110u); // Section 2 document 1 content. + EXPECT_INCR_SUCCESS (106u, 109u, 110u); // Checksum. + // clang-format on + + _test_from_data_input_bounds (data, data_len); +} + +static void +test_rpc_message_from_data_op_msg_invalid (void) +{ + test_rpc_message_from_data_op_msg_invalid_kind_0 (); + test_rpc_message_from_data_op_msg_invalid_kind_1_single (); + test_rpc_message_from_data_op_msg_invalid_kind_1_multiple (); +} + +static void +test_rpc_message_from_data_op_reply_invalid (void) +{ + uint8_t data[] = {TEST_DATA_OP_REPLY, 0xFF, 0x00}; + const size_t data_len = sizeof (data) - 2u; // Exclude the extra bytes. + + // clang-format off + EXPECT_DECR_FAILURE ( 0u, 0u, 52u); // messageLength (byte 0): truncated document 1 content -> invalid document 1 length (16 bytes, 15 remaining). + EXPECT_DECR_FAILURE ( 1u, 3u, 0u); // messageLength (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS ( 4u, 11u, 68u); // requestID, responseTo. + EXPECT_DECR_FAILURE (12u, 15u, 12u); // opCode: invalid. + EXPECT_DECR_SUCCESS (16u, 31u, 68u); // responseFlags, cursorID, startingFrom. + EXPECT_DECR_FAILURE (32u, 32u, 52u); // numberReturned (byte 0): set to 1 -> unexpected bytes remaining. + EXPECT_DECR_FAILURE (33u, 34u, 68u); // numberReturned (bytes 1-2): insufficient bytes to parse document 2. + EXPECT_DECR_FAILURE (35u, 35u, 32u); // numberReturned (bytes 3): invalid. + EXPECT_DECR_FAILURE (36u, 36u, 51u); // Document 0 length (byte 0): truncated document 0 content -> invalid document 1 length (0x00001000). + EXPECT_DECR_FAILURE (37u, 39u, 36u); // Document 0 length (bytes 1-3): invalid length. + EXPECT_DECR_SUCCESS (40u, 51u, 68u); // Document 0 content. + EXPECT_DECR_FAILURE (52u, 52u, 67u); // Document 1 length (byte 0): truncated document 1 content -> unexpected bytes remaining. + EXPECT_DECR_FAILURE (53u, 55u, 52u); // Document 1 length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS (56u, 67u, 68u); // Document 1 content. + // clang-format on + + // clang-format off + EXPECT_INCR_FAILURE ( 0u, 3u, 0u); // messageLength: invalid. + EXPECT_INCR_SUCCESS ( 4u, 11u, 68u); // requestID, responseTo. + EXPECT_INCR_FAILURE (12u, 15u, 12u); // opCode: invalid. + EXPECT_INCR_SUCCESS (16u, 31u, 68u); // responseFlags, cursorID, startingFrom. + EXPECT_INCR_FAILURE (32u, 35u, 68u); // numberReturned: insufficient bytes to parse document 2. + EXPECT_INCR_FAILURE (36u, 36u, 53u); // Document 0 length (byte 0): extended document 0 content -> invalid document 1 length (0x10000000). + EXPECT_INCR_FAILURE (37u, 39u, 36u); // Document 0 length (bytes 1-3): invalid. + EXPECT_INCR_SUCCESS (40u, 51u, 68u); // Document 0 content. + EXPECT_INCR_FAILURE (52u, 55u, 52u); // Document 1 length: invalid. + EXPECT_INCR_SUCCESS (56u, 67u, 68u); // Document 1 content. + // clang-format on + + _test_from_data_input_bounds (data, data_len); +} + +static void +test_rpc_message_from_data_op_update_invalid (void) +{ + uint8_t data[] = {TEST_DATA_OP_UPDATE, 0xFF, 0x00}; + const size_t data_len = sizeof (data) - 2u; // Exclude the extra bytes. + + // clang-format off + EXPECT_DECR_FAILURE ( 0u, 0u, 48u); // messageLength (byte 0): truncated update document content -> invalid update document length (14 bytes, 13 remaining). + EXPECT_DECR_FAILURE ( 1u, 3u, 0u); // messageLength (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS ( 4u, 11u, 62u); // requestID, responseTo. + EXPECT_DECR_FAILURE (12u, 15u, 12u); // opCode: invalid. + EXPECT_DECR_FAILURE (16u, 19u, 16u); // ZERO: invalid. + EXPECT_DECR_SUCCESS (20u, 26u, 62u); // fullCollectionName (content). + EXPECT_DECR_FAILURE (27u, 27u, 29u); // fullCollectionName (terminator): extended fullCollectionName -> invalid flags (0x10000000). + EXPECT_DECR_FAILURE (28u, 31u, 28u); // flags: invalid. + EXPECT_DECR_FAILURE (32u, 32u, 47u); // selector document length (byte 0): truncated selector document content -> invalid update document length (0x00000e00). + EXPECT_DECR_FAILURE (33u, 35u, 32u); // selector document length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS (36u, 47u, 62u); // selector document content. + EXPECT_DECR_FAILURE (48u, 48u, 61u); // update document length (byte 0): truncated update document content -> unexpected bytes remaining. + EXPECT_DECR_FAILURE (49u, 51u, 48u); // update document length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS (52u, 61u, 62u); // update document content. + // clang-format on + + // clang-format off + EXPECT_INCR_FAILURE ( 0u, 3u, 0u); // messageLength: invalid. + EXPECT_INCR_SUCCESS ( 4u, 11u, 62u); // requestID, responseTo. + EXPECT_INCR_IGNORED (12u, 12u, 12u); // opCode: parse as OP_INSERT. + EXPECT_INCR_FAILURE (13u, 15u, 12u); // opCode: invalid. + EXPECT_INCR_FAILURE (16u, 19u, 16u); // ZERO: invalid. + EXPECT_INCR_SUCCESS (20u, 26u, 62u); // fullCollectionName (content). + EXPECT_INCR_FAILURE (27u, 27u, 29u); // fullCollectionName (terminator): extended fullCollectionName -> invalid flags (0x10000000). + EXPECT_INCR_SUCCESS (28u, 28u, 62u); // flags (byte 0): set to MONGOC_OP_UPDATE_FLAG_UPSERT. + EXPECT_INCR_FAILURE (29u, 31u, 28u); // flags (bytes 1-3): invalid. + EXPECT_INCR_FAILURE (32u, 32u, 49u); // selector document length (byte 0): extended selector document -> invalid update document length (0x08000000). + EXPECT_INCR_FAILURE (33u, 35u, 32u); // selector document length (bytes 1-3): invalid. + EXPECT_INCR_SUCCESS (36u, 47u, 62u); // selector document content. + EXPECT_INCR_FAILURE (48u, 51u, 48u); // update document length: invalid length. + EXPECT_INCR_SUCCESS (52u, 61u, 62u); // update document content. + // clang-format on + + _test_from_data_input_bounds (data, data_len); +} + +static void +test_rpc_message_from_data_op_insert_invalid (void) +{ + uint8_t data[] = {TEST_DATA_OP_INSERT, 0xFF, 0x00}; + const size_t data_len = sizeof (data) - 2u; // Exclude the extra bytes. + + // clang-format off + EXPECT_DECR_FAILURE ( 0u, 0u, 44u); // messageLength (byte 0): truncated document 1 content -> invalid document 1 length (16 bytes, 15 remaining). + EXPECT_DECR_FAILURE ( 1u, 3u, 0u); // messageLength (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS ( 4u, 11u, 60u); // requestID, responseTo. + EXPECT_DECR_IGNORED (12u, 12u, 12u); // opCode: parse as OP_UPDATE. + EXPECT_DECR_FAILURE (13u, 15u, 12u); // opCode: invalid. + EXPECT_DECR_FAILURE (16u, 19u, 16u); // flags: invalid. + EXPECT_DECR_SUCCESS (20u, 26u, 60u); // fullCollectionName (content). + EXPECT_DECR_FAILURE (27u, 27u, 30u); // fullCollectionName (terminator): extended fullCollectionName -> invalid document 0 length (0x69100000). + EXPECT_DECR_FAILURE (28u, 28u, 43u); // Document 0 length (byte 0): truncated document 0 content -> invalid document 1 length (0x00001000). + EXPECT_DECR_FAILURE (29u, 31u, 28u); // Document 0 length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS (32u, 43u, 60u); // Document 0 content. + EXPECT_DECR_FAILURE (44u, 44u, 59u); // Document 1 length (byte 0): truncated document 1 content -> unexpected bytes remaining. + EXPECT_DECR_FAILURE (45u, 47u, 44u); // Document 1 length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS (48u, 59u, 60u); // Document 1 content. + // clang-format on + + // clang-format off + EXPECT_INCR_FAILURE ( 0u, 3u, 0u); // messageLength: invalid. + EXPECT_INCR_SUCCESS ( 4u, 11u, 60u); // requestID, responseTo. + EXPECT_INCR_FAILURE (12u, 15u, 12u); // opCode: invalid opCode. + EXPECT_INCR_SUCCESS (16u, 16u, 60u); // flags (byte 0): set to MONGOC_OP_INSERT_FLAG_CONTINUE_ON_ERROR. + EXPECT_INCR_FAILURE (17u, 19u, 16u); // flags (bytes 1-3): invalid. + EXPECT_INCR_SUCCESS (20u, 26u, 60u); // fullCollectionName (content). + EXPECT_INCR_FAILURE (27u, 27u, 30u); // fullCollectionName (terminator): extended fullCollectionName -> invalid document 0 length (0x69100000). + EXPECT_INCR_FAILURE (28u, 28u, 45u); // Document 0 length (byte 0): extended document 0 content -> invalid document 1 length (0x10000000). + EXPECT_INCR_FAILURE (29u, 31u, 28u); // Document 0 length (bytes 1-3): invalid. + EXPECT_INCR_SUCCESS (32u, 43u, 60u); // Document 0 content. + EXPECT_INCR_FAILURE (44u, 47u, 44u); // Document 1 length: invalid. + EXPECT_INCR_SUCCESS (48u, 59u, 60u); // Document 1 content. + // clang-format on + + _test_from_data_input_bounds (data, data_len); + + // Test that documents are parsed correctly. + { + data[0] = (uint8_t) (data[0] - 32u); // Exclude both documents. + + const void *data_end = NULL; + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, &data_end); + const size_t parsed_len = (size_t) ((const uint8_t *) data_end - data); + const size_t expected_len = 28u; + + ASSERT_WITH_MSG (!rpc, "OP_INSERT requires at least one document"); + ASSERT_WITH_MSG ( + parsed_len == expected_len, + "expected %zu bytes to be parsed before error, but parsed %zu bytes", + expected_len, + parsed_len); + + mcd_rpc_message_destroy (rpc); + + data[0] = (uint8_t) (data[0] + 32u); // Restore documents. + } +} + +static void +test_rpc_message_from_data_op_query_invalid (void) +{ + uint8_t data[] = {TEST_DATA_OP_QUERY, 0xFF, 0x00}; + const size_t data_len = sizeof (data) - 2u; // Exclude the extra bytes. + + // clang-format off + EXPECT_DECR_FAILURE ( 0u, 0u, 49u); // messageLength (byte 0): truncated returnFieldsSelector document content -> invalid returnFieldsSelector document length (16 bytes, 15 remaining). + EXPECT_DECR_FAILURE ( 1u, 3u, 0u); // messageLength (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS ( 4u, 11u, 65u); // requestID, responseTo. + EXPECT_DECR_FAILURE (12u, 15u, 12u); // opCode: invalid. + EXPECT_DECR_FAILURE (16u, 19u, 16u); // flags: invalid. + EXPECT_DECR_SUCCESS (20u, 26u, 65u); // fullCollectionName (content). + EXPECT_DECR_FAILURE (27u, 27u, 37u); // fullCollectionName (terminator): extended fullCollectionName -> invalid query document length (0x08000000). + EXPECT_DECR_SUCCESS (28u, 35u, 65u); // numberToSkip, numberToReturn. + EXPECT_DECR_FAILURE (36u, 36u, 48u); // query document length (byte 0): truncated query document content -> invalid returnFieldsSelector document length (0x00001000). + EXPECT_DECR_FAILURE (37u, 39u, 36u); // query document length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS (40u, 48u, 65u); // query document content. + EXPECT_DECR_FAILURE (49u, 49u, 64u); // returnFieldsSelector document length (byte 0): truncated returnFieldsSelector document content -> unexpected bytes remaining. + EXPECT_DECR_FAILURE (50u, 52u, 49u); // returnFieldsSelector document length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS (53u, 64u, 65u); // returnFieldsSelector document content. + // clang-format on + + // clang-format off + EXPECT_INCR_FAILURE ( 0u, 3u, 0u); // messageLength: invalid. + EXPECT_INCR_SUCCESS ( 4u, 11u, 65u); // requestID, responseTo. + EXPECT_INCR_IGNORED (12u, 12u, 12u); // opCode (byte 0): parse as OP_GET_MORE. + EXPECT_INCR_FAILURE (13u, 15u, 12u); // opCode (bytes 1-3): invalid. + EXPECT_INCR_FAILURE (16u, 19u, 16u); // flags (bytes 1-3): invalid. + EXPECT_INCR_SUCCESS (20u, 26u, 65u); // fullCollectionName (content). + EXPECT_INCR_FAILURE (27u, 27u, 37u); // fullCollectionName (terminator): extended fullCollectionName -> invalid query document length (0x08000000). + EXPECT_INCR_SUCCESS (28u, 35u, 65u); // numberToSkip, numberToReturn. + EXPECT_INCR_FAILURE (36u, 36u, 50u); // query document length (byte 0): extended query document content -> invalid returnFieldsSelector document length (0x08000000). + EXPECT_INCR_FAILURE (37u, 39u, 36u); // query document length (bytes 1-3): invalid. + EXPECT_INCR_SUCCESS (40u, 48u, 65u); // query document content. + EXPECT_INCR_FAILURE (49u, 52u, 49u); // returnFieldsSelector document length: invalid. + EXPECT_INCR_SUCCESS (53u, 64u, 65u); // returnFieldsSelector document content. + // clang-format on + + _test_from_data_input_bounds (data, data_len); + + // Test that the query document is parsed correctly. + { + data[0] = (uint8_t) (data[0] - 29u); // Exclude both query and + // returnFieldsSelector documents. + + const void *data_end = NULL; + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, data_len, &data_end); + const size_t parsed_len = (size_t) ((const uint8_t *) data_end - data); + const size_t expected_len = 36u; + + ASSERT_WITH_MSG (!rpc, "OP_QUERY requires a query document"); + ASSERT_WITH_MSG ( + parsed_len == expected_len, + "expected %zu bytes to be parsed before error, but parsed %zu bytes", + expected_len, + parsed_len); + + mcd_rpc_message_destroy (rpc); + + data[0] = (uint8_t) (data[0] + 29u); // Restore both query and + // returnFieldsSelector documents. + } +} + +static void +test_rpc_message_from_data_op_get_more_invalid (void) +{ + uint8_t data[] = {TEST_DATA_OP_GET_MORE, 0xFF, 0x00}; + const size_t data_len = sizeof (data) - 2u; // Exclude the extra bytes. + + // clang-format off + EXPECT_DECR_FAILURE ( 0u, 0u, 32u); // messageLength (byte 0): insufficient bytes to parse cursorID. + EXPECT_DECR_FAILURE ( 1u, 3u, 0u); // messageLength (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS ( 4u, 11u, 40u); // requestID, responseTo. + EXPECT_DECR_IGNORED (12u, 12u, 12u); // opCode (byte 0): parse as OP_QUERY. + EXPECT_DECR_FAILURE (13u, 15u, 12u); // opCode (bytes 1-3): invalid. + EXPECT_DECR_FAILURE (16u, 19u, 16u); // ZERO: invalid. + EXPECT_DECR_SUCCESS (20u, 26u, 40u); // fullCollectionName (content). + EXPECT_DECR_FAILURE (27u, 27u, 33u); // fullCollectionName (terminator): extended fullCollectionName -> insufficient bytes to parse cursorID. + EXPECT_DECR_SUCCESS (28u, 39u, 40u); // fullCollectionName, numberToReturn, cursorID. + // clang-format on + + // clang-format off + EXPECT_INCR_FAILURE ( 0u, 3u, 0u); // messageLength: invalid. + EXPECT_INCR_SUCCESS ( 4u, 11u, 40u); // requestID, responseTo. + EXPECT_INCR_IGNORED (12u, 12u, 12u); // opCode (byte 0): parse as OP_DELETE. + EXPECT_INCR_FAILURE (13u, 15u, 12u); // opCode (bytes 1-3): invalid. + EXPECT_INCR_FAILURE (16u, 19u, 16u); // ZERO: invalid. + EXPECT_INCR_SUCCESS (20u, 26u, 40u); // fullCollectionName (content). + EXPECT_INCR_FAILURE (27u, 27u, 33u); // fullCollectionName (terminator): extended fullCollectionName -> insufficient bytes to parse cursorID. + EXPECT_INCR_SUCCESS (28u, 39u, 40u); // fullCollectionName, numberToReturn, cursorID. + // clang-format on + + _test_from_data_input_bounds (data, data_len); +} + +static void +test_rpc_message_from_data_op_delete_invalid (void) +{ + uint8_t data[] = {TEST_DATA_OP_DELETE, 0xFF, 0x00}; + const size_t data_len = sizeof (data) - 2u; // Exclude the extra bytes. + + // clang-format off + EXPECT_DECR_FAILURE ( 0u, 0u, 32u); // messageLength (byte 0): insufficient bytes to parse cursorID. + EXPECT_DECR_FAILURE ( 1u, 3u, 0u); // messageLength (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS ( 4u, 11u, 48u); // requestID, responseTo. + EXPECT_DECR_IGNORED (12u, 12u, 12u); // opCode (byte 0): parse as OP_GET_MORE. + EXPECT_DECR_FAILURE (13u, 15u, 12u); // opCode (bytes 1-3): invalid. + EXPECT_DECR_FAILURE (16u, 19u, 16u); // ZERO: invalid. + EXPECT_DECR_SUCCESS (20u, 26u, 48u); // fullCollectionName (content). + EXPECT_DECR_FAILURE (27u, 27u, 29u); // fullCollectionName (terminator): extended fullCollectionName -> invalid flags (0x10000000). + EXPECT_DECR_FAILURE (28u, 31u, 28u); // flags: invalid. + EXPECT_DECR_FAILURE (32u, 32u, 47u); // selector document length (byte 0): truncated selector document content -> unexpected bytes remaining. + EXPECT_DECR_FAILURE (33u, 35u, 32u); // selector document length (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS (36u, 47u, 48u); // selector document content. + // clang-format on + + // clang-format off + EXPECT_INCR_FAILURE ( 0u, 3u, 0u); // messageLength: invalid. + EXPECT_INCR_SUCCESS ( 4u, 11u, 48u); // requestID, responseTo. + EXPECT_INCR_IGNORED (12u, 12u, 12u); // opCode (byte 0): parse as OP_KILL_CURSORS. + EXPECT_INCR_FAILURE (13u, 15u, 12u); // opCode (bytes 1-3): invalid. + EXPECT_INCR_FAILURE (16u, 19u, 16u); // ZERO: invalid. + EXPECT_INCR_SUCCESS (20u, 26u, 48u); // fullCollectionName (content). + EXPECT_INCR_FAILURE (27u, 27u, 29u); // fullCollectionName (terminator): extended fullCollectionName -> invalid flags (0x10000000). + EXPECT_INCR_SUCCESS (28u, 28u, 48u); // flags (byte 0): set to MONGOC_OP_DELETE_FLAG_SINGLE_REMOVE. + EXPECT_INCR_FAILURE (29u, 31u, 28u); // flags (bytes 1-3): invalid. + EXPECT_INCR_FAILURE (32u, 35u, 32u); // selector document length: invalid. + EXPECT_INCR_SUCCESS (36u, 47u, 48u); // selector document content. + // clang-format on + + _test_from_data_input_bounds (data, data_len); +} + +static void +test_rpc_message_from_data_op_kill_cursors_invalid (void) +{ + uint8_t data[] = {TEST_DATA_OP_KILL_CURSORS, 0xFF, 0x00}; + const size_t data_len = sizeof (data) - 2u; // Exclude the extra bytes. + + // clang-format off + EXPECT_DECR_FAILURE ( 0u, 0u, 20u); // messageLength (byte 0): invalid numberOfCursorIds. + EXPECT_DECR_FAILURE ( 1u, 3u, 0u); // messageLength (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS ( 4u, 11u, 40u); // requestID, responseTo. + EXPECT_DECR_IGNORED (12u, 12u, 12u); // opCode (byte 0): parse as OP_DELETE. + EXPECT_DECR_FAILURE (13u, 15u, 12u); // opCode (bytes 1-3): invalid. + EXPECT_DECR_FAILURE (16u, 19u, 16u); // ZERO: invalid. + EXPECT_DECR_FAILURE (20u, 20u, 32u); // numberOfCursorIds (byte 0): truncated cursorIDs -> unexpected bytes remaining. + EXPECT_DECR_FAILURE (21u, 23u, 20u); // numberOfCursorIds (bytes 1-3): invalid. + EXPECT_DECR_SUCCESS (24u, 39u, 40u); // cursorIDs. + // clang-format on + + // clang-format off + EXPECT_INCR_FAILURE ( 0u, 3u, 0u); // messageLength: invalid. + EXPECT_INCR_SUCCESS ( 4u, 11u, 40u); // requestID, responseTo. + EXPECT_INCR_FAILURE (12u, 15u, 12u); // opCode: invalid. + EXPECT_INCR_FAILURE (16u, 19u, 16u); // ZERO: invalid. + EXPECT_INCR_FAILURE (20u, 23u, 20u); // numberOfCursorIds: invalid. + EXPECT_INCR_SUCCESS (24u, 39u, 40u); // cursorIDs. + // clang-format on + + _test_from_data_input_bounds (data, data_len); +} + + +#define ASSERT_IOVEC_VALUE(index, expected, type, raw_type, from_le, spec) \ + if (1) { \ + const mongoc_iovec_t iovec = iovecs[index]; \ + const size_t len = sizeof (type); \ + ASSERT_WITH_MSG (iovec.iov_len == sizeof (type), \ + "expected iov_len to be %zu, but got %zu", \ + len, \ + iovec.iov_len); \ + raw_type storage; \ + memcpy (&storage, iovec.iov_base, sizeof (type)); \ + storage = from_le (storage); \ + type value; \ + memcpy (&value, &storage, sizeof (type)); \ + ASSERT_WITH_MSG (value == expected, \ + "expected iov_base to point to %s with value %" spec \ + ", but got %" spec, \ + #type, \ + expected, \ + value); \ + } else \ + (void) 0 + +#define ASSERT_IOVEC_UINT8(index, expected) \ + ASSERT_IOVEC_VALUE (index, expected, uint8_t, uint8_t, (uint8_t), PRIu8) +#define ASSERT_IOVEC_INT32(index, expected) \ + ASSERT_IOVEC_VALUE ( \ + index, expected, int32_t, uint32_t, BSON_UINT32_FROM_LE, PRId32) +#define ASSERT_IOVEC_UINT32(index, expected) \ + ASSERT_IOVEC_VALUE ( \ + index, expected, uint32_t, uint32_t, BSON_UINT32_FROM_LE, PRIu32) +#define ASSERT_IOVEC_INT64(index, expected) \ + ASSERT_IOVEC_VALUE ( \ + index, expected, int64_t, uint64_t, BSON_UINT64_FROM_LE, PRId64) + +#define ASSERT_IOVEC_BYTES(index, expected_base_index, expected_len) \ + if (1) { \ + const mongoc_iovec_t iovec = iovecs[index]; \ + ASSERT_WITH_MSG (iovec.iov_len == expected_len, \ + "expected iov_len to be %zu, but got %zu", \ + (size_t) expected_len, \ + iovec.iov_len); \ + ASSERT_WITH_MSG ( \ + iovec.iov_base == (data + expected_base_index), \ + "expected iov_base to point to byte %zu (%p), but got byte %zu (%p)", \ + (size_t) (expected_base_index), \ + (void *) (data + expected_base_index), \ + (size_t) ((const uint8_t *) iovec.iov_base - data), \ + iovec.iov_base); \ + } else \ + (void) 0 + + +static void +test_rpc_message_to_iovecs_op_compressed (void) +{ + const uint8_t data[] = {TEST_DATA_OP_COMPRESSED}; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, 8u); + ASSERT_IOVEC_INT32 (0, 43); // messageLength + ASSERT_IOVEC_INT32 (1, 16909060); // requestID + ASSERT_IOVEC_INT32 (2, 84281096); // responseTo + ASSERT_IOVEC_INT32 (3, 2012); // opCode + ASSERT_IOVEC_INT32 (4, 2013); // originalOpcode + ASSERT_IOVEC_INT32 (5, 18); // uncompressedSize + ASSERT_IOVEC_UINT8 (6, 0u); // compressorId + ASSERT_IOVEC_BYTES (7, 25u, 18u); // compressedMessage + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); +} + +static void +test_rpc_message_to_iovecs_op_msg_kind_0 (void) +{ + const uint8_t data[] = {TEST_DATA_OP_MSG_KIND_0}; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, 8u); + ASSERT_IOVEC_INT32 (0, 40); // messageLength + ASSERT_IOVEC_INT32 (1, 16909060); // requestID + ASSERT_IOVEC_INT32 (2, 84281096); // responseTo + ASSERT_IOVEC_INT32 (3, 2013); // opCode + ASSERT_IOVEC_INT32 (4, 1); // flagBits + ASSERT_IOVEC_UINT8 (5, 0); // Section 0 Kind + ASSERT_IOVEC_BYTES (6, 21u, 15u); // Section 0 Body + ASSERT_IOVEC_UINT32 (7, 287454020); // Checksum + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); +} + +static void +test_rpc_message_to_iovecs_op_msg_kind_1_single (void) +{ + const uint8_t data[] = {TEST_DATA_OP_MSG_KIND_1_SINGLE}; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, 12u); + ASSERT_IOVEC_INT32 (0, 67); // messageLength + ASSERT_IOVEC_INT32 (1, 16909060); // requestID + ASSERT_IOVEC_INT32 (2, 84281096); // responseTo + ASSERT_IOVEC_INT32 (3, 2013); // opCode + ASSERT_IOVEC_INT32 (4, 1); // flagBits + ASSERT_IOVEC_UINT8 (5, 0); // Section 0 Kind + ASSERT_IOVEC_BYTES (6, 21u, 15u); // Section 0 Body + ASSERT_IOVEC_UINT8 (7, 1); // Section 1 Kind + ASSERT_IOVEC_INT32 (8, 26); // Section 1 Length + ASSERT_IOVEC_BYTES (9, 41u, 7u); // Section 1 Identifier + ASSERT_IOVEC_BYTES (10, 48u, 15u); // Section 1 Documents + ASSERT_IOVEC_UINT32 (11, 287454020); // Checksum + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); +} + +static void +test_rpc_message_to_iovecs_op_msg_kind_1_multiple (void) +{ + const uint8_t data[] = {TEST_DATA_OP_MSG_KIND_1_MULTIPLE}; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, 16u); + ASSERT_IOVEC_INT32 (0, 110); // messageLength + ASSERT_IOVEC_INT32 (1, 16909060); // requestID + ASSERT_IOVEC_INT32 (2, 84281096); // responseTo + ASSERT_IOVEC_INT32 (3, 2013); // opCode + ASSERT_IOVEC_INT32 (4, 1); // flagBits + ASSERT_IOVEC_UINT8 (5, 0); // Section 0 Kind + ASSERT_IOVEC_BYTES (6, 21u, 15u); // Section 0 Body + ASSERT_IOVEC_UINT8 (7, 1); // Section 1 Kind + ASSERT_IOVEC_INT32 (8, 25); // Section 1 Length + ASSERT_IOVEC_BYTES (9, 41u, 6u); // Section 1 Identifier + ASSERT_IOVEC_BYTES (10, 47u, 15u); // Section 1 Documents + ASSERT_IOVEC_UINT8 (11, 1); // Section 2 Kind + ASSERT_IOVEC_INT32 (12, 43); // Section 2 Length + ASSERT_IOVEC_BYTES (13, 67u, 7u); // Section 2 Identifier + ASSERT_IOVEC_BYTES (14, 74u, 32u); // Section 2 Documents + ASSERT_IOVEC_UINT32 (15, 287454020); // Checksum + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); +} + +static void +test_rpc_message_to_iovecs_op_msg (void) +{ + test_rpc_message_to_iovecs_op_msg_kind_0 (); + test_rpc_message_to_iovecs_op_msg_kind_1_single (); + test_rpc_message_to_iovecs_op_msg_kind_1_multiple (); +} + +static void +test_rpc_message_to_iovecs_op_reply (void) +{ + const uint8_t data[] = {TEST_DATA_OP_REPLY}; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, 9u); + ASSERT_IOVEC_INT32 (0, 68); // messageLength + ASSERT_IOVEC_INT32 (1, 16909060); // requestID + ASSERT_IOVEC_INT32 (2, 84281096); // responseTo + ASSERT_IOVEC_INT32 (3, 1); // opCode + ASSERT_IOVEC_INT32 (4, 0); // responseFlags + ASSERT_IOVEC_INT64 (5, 1234605616436508552); // cursorID + ASSERT_IOVEC_INT32 (6, 0); // startingFrom + ASSERT_IOVEC_INT32 (7, 2); // numberReturned + ASSERT_IOVEC_BYTES (8, 36u, 32u); // documents + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); +} + +static void +test_rpc_message_to_iovecs_op_update (void) +{ + const uint8_t data[] = {TEST_DATA_OP_UPDATE}; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, 9u); + ASSERT_IOVEC_INT32 (0, 62); // messageLength + ASSERT_IOVEC_INT32 (1, 16909060); // requestID + ASSERT_IOVEC_INT32 (2, 84281096); // responseTo + ASSERT_IOVEC_INT32 (3, 2001); // opCode + ASSERT_IOVEC_INT32 (4, 0); // ZERO + ASSERT_IOVEC_BYTES (5, 20u, 8u); // fullCollectionName + ASSERT_IOVEC_INT32 (6, 0); // flags + ASSERT_IOVEC_BYTES (7, 32u, 16u); // selector + ASSERT_IOVEC_BYTES (8, 48u, 14u); // update + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); +} + +static void +test_rpc_message_to_iovecs_op_insert (void) +{ + const uint8_t data[] = {TEST_DATA_OP_INSERT}; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, 7u); + ASSERT_IOVEC_INT32 (0, 60); // messageLength + ASSERT_IOVEC_INT32 (1, 16909060); // requestID + ASSERT_IOVEC_INT32 (2, 84281096); // responseTo + ASSERT_IOVEC_INT32 (3, 2002); // opCode + ASSERT_IOVEC_INT32 (4, 0); // flags + ASSERT_IOVEC_BYTES (5, 20u, 8u); // fullCollectionName + ASSERT_IOVEC_BYTES (6, 28u, 32u); // documents + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); +} + +static void +test_rpc_message_to_iovecs_op_query (void) +{ + const uint8_t data[] = {TEST_DATA_OP_QUERY}; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, 10u); + ASSERT_IOVEC_INT32 (0, 65); // messageLength + ASSERT_IOVEC_INT32 (1, 16909060); // requestID + ASSERT_IOVEC_INT32 (2, 84281096); // responseTo + ASSERT_IOVEC_INT32 (3, 2004); // opCode + ASSERT_IOVEC_INT32 (4, 0); // flags + ASSERT_IOVEC_BYTES (5, 20u, 8u); // fullCollectionName + ASSERT_IOVEC_INT32 (6, 0); // numberToSkip + ASSERT_IOVEC_INT32 (7, 0); // numberToReturn + ASSERT_IOVEC_BYTES (8, 36u, 13u); // query + ASSERT_IOVEC_BYTES (9, 49u, 16u); // returnFieldsSelector + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); +} + +static void +test_rpc_message_to_iovecs_op_get_more (void) +{ + const uint8_t data[] = {TEST_DATA_OP_GET_MORE}; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, 8u); + ASSERT_IOVEC_INT32 (0, 40); // messageLength + ASSERT_IOVEC_INT32 (1, 16909060); // requestID + ASSERT_IOVEC_INT32 (2, 84281096); // responseTo + ASSERT_IOVEC_INT32 (3, 2005); // opCode + ASSERT_IOVEC_INT32 (4, 0); // ZERO + ASSERT_IOVEC_BYTES (5, 20u, 8u); // fullCollectionName + ASSERT_IOVEC_INT32 (6, 0); // numberToReturn + ASSERT_IOVEC_INT64 (7, 1234605616436508552); // cursorID + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); +} + +static void +test_rpc_message_to_iovecs_op_delete (void) +{ + const uint8_t data[] = {TEST_DATA_OP_DELETE}; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, 8u); + ASSERT_IOVEC_INT32 (0, 48); // messageLength + ASSERT_IOVEC_INT32 (1, 16909060); // requestID + ASSERT_IOVEC_INT32 (2, 84281096); // responseTo + ASSERT_IOVEC_INT32 (3, 2006); // opCode + ASSERT_IOVEC_INT32 (4, 0); // ZERO + ASSERT_IOVEC_BYTES (5, 20u, 8u); // fullCollectionName + ASSERT_IOVEC_INT32 (6, 0); // flags + ASSERT_IOVEC_BYTES (7, 32u, 16u); // selector + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); +} + +static void +test_rpc_message_to_iovecs_op_kill_cursors (void) +{ + const uint8_t data[] = {TEST_DATA_OP_KILL_CURSORS}; + + mcd_rpc_message *const rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, 7u); + ASSERT_IOVEC_INT32 (0, 40); // messageLength + ASSERT_IOVEC_INT32 (1, 16909060); // requestID + ASSERT_IOVEC_INT32 (2, 84281096); // responseTo + ASSERT_IOVEC_INT32 (3, 2007); // opCode + ASSERT_IOVEC_INT32 (4, 0); // ZERO + ASSERT_IOVEC_INT32 (5, 2); // numberOfCursorIds + + // The cursorIDs iovec does not point to original data due to endian + // conversion requirements. Assert values individually. + { + const mongoc_iovec_t *const iovec = iovecs + 6; + + ASSERT_CMPSIZE_T (iovec->iov_len, ==, 16u); + + const int64_t *const cursor_ids = iovec->iov_base; + + const int64_t cursor_id_0 = _int64_from_le (cursor_ids + 0); + const int64_t cursor_id_1 = _int64_from_le (cursor_ids + 1); + + ASSERT_CMPINT64 (cursor_id_0, ==, 1230066625199609624); + ASSERT_CMPINT64 (cursor_id_1, ==, 2387509390608836392); + } + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); +} + + +#define ASSERT_CMPIOVEC_VALUE(index, type, raw_type, from_le, spec) \ + if (1) { \ + const mongoc_iovec_t _actual = iovecs[index]; \ + const mongoc_iovec_t _expected = expected_iovecs[index]; \ + ASSERT_WITH_MSG ( \ + _expected.iov_len == sizeof (type), \ + "expected iov_len does not match expected type length"); \ + ASSERT_WITH_MSG (_actual.iov_len == _expected.iov_len, \ + "expected iov_len to be %zu, but got %zu", \ + _expected.iov_len, \ + _actual.iov_len); \ + raw_type storage; \ + type actual_value; \ + type expected_value; \ + memcpy (&storage, _actual.iov_base, sizeof (type)); \ + storage = from_le (storage); \ + memcpy (&actual_value, &storage, sizeof (type)); \ + memcpy (&storage, _expected.iov_base, sizeof (type)); \ + storage = from_le (storage); \ + memcpy (&expected_value, &storage, sizeof (type)); \ + ASSERT_WITH_MSG (actual_value == expected_value, \ + "expected iov_base to point to %s with value %" spec \ + ", but got %" spec, \ + #type, \ + expected_value, \ + actual_value); \ + } else \ + (void) 0 + +#define ASSERT_CMPIOVEC_UINT8(index) \ + ASSERT_CMPIOVEC_VALUE (index, uint8_t, uint8_t, (uint8_t), PRIu8) +#define ASSERT_CMPIOVEC_INT32(index) \ + ASSERT_CMPIOVEC_VALUE (index, int32_t, uint32_t, BSON_UINT32_FROM_LE, PRId32) +#define ASSERT_CMPIOVEC_UINT32(index) \ + ASSERT_CMPIOVEC_VALUE ( \ + index, uint32_t, uint32_t, BSON_UINT32_FROM_LE, PRIu32) +#define ASSERT_CMPIOVEC_INT64(index) \ + ASSERT_CMPIOVEC_VALUE (index, int64_t, uint64_t, BSON_UINT64_FROM_LE, PRId64) + +#define ASSERT_CMPIOVEC_BYTES(index) \ + if (1) { \ + const mongoc_iovec_t _actual = iovecs[index]; \ + const mongoc_iovec_t _expected = expected_iovecs[index]; \ + ASSERT_WITH_MSG (_actual.iov_len == _expected.iov_len, \ + "expected iov_len to be %zu, but got %zu", \ + _expected.iov_len, \ + _actual.iov_len); \ + ASSERT_WITH_MSG ( \ + _actual.iov_base == _expected.iov_base, \ + "expected iov_base to point to byte %zu (%p), but got byte %zu (%p)", \ + (size_t) ((const uint8_t *) _expected.iov_base - data), \ + _expected.iov_base, \ + (size_t) ((const uint8_t *) _actual.iov_base - data), \ + _actual.iov_base); \ + } else \ + (void) 0 + + +static void +test_rpc_message_setters_op_compressed (void) +{ + const uint8_t data[] = {TEST_DATA_OP_COMPRESSED}; + + mcd_rpc_message *const expected_rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + ASSERT (expected_rpc); + + size_t expected_num_iovecs; + mongoc_iovec_t *const expected_iovecs = + mcd_rpc_message_to_iovecs (expected_rpc, &expected_num_iovecs); + ASSERT (expected_iovecs); + + mcd_rpc_message *const rpc = mcd_rpc_message_new (); + + // clang-format off + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_message_length (rpc, 43)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_request_id (rpc, 16909060)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_response_to (rpc, 84281096)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_op_code (rpc, MONGOC_OP_CODE_COMPRESSED)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_compressed_set_original_opcode (rpc, MONGOC_OP_CODE_MSG)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_compressed_set_uncompressed_size (rpc, 18)); + ASSERT_CMPINT32 ( 1, ==, mcd_rpc_op_compressed_set_compressor_id (rpc, 0)); + ASSERT_CMPINT32 (18, ==, mcd_rpc_op_compressed_set_compressed_message (rpc, data + 25, 18u)); + // clang-format on + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, expected_num_iovecs); + ASSERT_CMPSIZE_T (num_iovecs, ==, 8u); + ASSERT_CMPIOVEC_INT32 (0); // messageLength + ASSERT_CMPIOVEC_INT32 (1); // requestID + ASSERT_CMPIOVEC_INT32 (2); // responseTo + ASSERT_CMPIOVEC_INT32 (3); // opCode + ASSERT_CMPIOVEC_INT32 (4); // originalOpcode + ASSERT_CMPIOVEC_INT32 (5); // uncompressedSize + ASSERT_CMPIOVEC_UINT8 (6); // compressorId + ASSERT_CMPIOVEC_BYTES (7); // compressedMessage + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); + + bson_free (expected_iovecs); + mcd_rpc_message_destroy (expected_rpc); +} + +static void +test_rpc_message_setters_op_msg_kind_0 (void) +{ + const uint8_t data[] = {TEST_DATA_OP_MSG_KIND_0}; + + mcd_rpc_message *const expected_rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + ASSERT (expected_rpc); + + size_t expected_num_iovecs; + mongoc_iovec_t *const expected_iovecs = + mcd_rpc_message_to_iovecs (expected_rpc, &expected_num_iovecs); + ASSERT (expected_iovecs); + + mcd_rpc_message *const rpc = mcd_rpc_message_new (); + + // clang-format off + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_message_length (rpc, 40)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_request_id (rpc, 16909060)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_response_to (rpc, 84281096)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_op_code (rpc, MONGOC_OP_CODE_MSG)); + mcd_rpc_op_msg_set_sections_count (rpc, 1u); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_msg_set_flag_bits (rpc, MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT)); + ASSERT_CMPINT32 ( 1, ==, mcd_rpc_op_msg_section_set_kind (rpc, 0u, 0)); + ASSERT_CMPINT32 (15, ==, mcd_rpc_op_msg_section_set_body (rpc, 0u, data + 21)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_msg_set_checksum (rpc, 287454020)); + // clang-format on + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, expected_num_iovecs); + ASSERT_CMPSIZE_T (num_iovecs, ==, 8u); + ASSERT_CMPIOVEC_INT32 (0); // messageLength + ASSERT_CMPIOVEC_INT32 (1); // requestID + ASSERT_CMPIOVEC_INT32 (2); // responseTo + ASSERT_CMPIOVEC_INT32 (3); // opCode + ASSERT_CMPIOVEC_INT32 (4); // flagBits + ASSERT_CMPIOVEC_UINT8 (5); // Section 0 Kind + ASSERT_CMPIOVEC_BYTES (6); // Section 0 Body + ASSERT_CMPIOVEC_UINT32 (7); // Checksum + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); + + bson_free (expected_iovecs); + mcd_rpc_message_destroy (expected_rpc); +} + +static void +test_rpc_message_setters_op_msg_kind_1_single (void) +{ + const uint8_t data[] = {TEST_DATA_OP_MSG_KIND_1_SINGLE}; + + mcd_rpc_message *const expected_rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + ASSERT (expected_rpc); + + size_t expected_num_iovecs; + mongoc_iovec_t *const expected_iovecs = + mcd_rpc_message_to_iovecs (expected_rpc, &expected_num_iovecs); + ASSERT (expected_iovecs); + + mcd_rpc_message *const rpc = mcd_rpc_message_new (); + + // clang-format off + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_message_length (rpc, 67)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_request_id (rpc, 16909060)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_response_to (rpc, 84281096)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_op_code (rpc, MONGOC_OP_CODE_MSG)); + mcd_rpc_op_msg_set_sections_count (rpc, 2u); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_msg_set_flag_bits (rpc, MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT)); + ASSERT_CMPINT32 ( 1, ==, mcd_rpc_op_msg_section_set_kind (rpc, 0u, 0)); + ASSERT_CMPINT32 (15, ==, mcd_rpc_op_msg_section_set_body (rpc, 0u, data + 21)); + ASSERT_CMPINT32 ( 1, ==, mcd_rpc_op_msg_section_set_kind (rpc, 1u, 1)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_msg_section_set_length (rpc, 1u, 26u)); + ASSERT_CMPINT32 ( 7, ==, mcd_rpc_op_msg_section_set_identifier (rpc, 1u, (const char *) (data + 41))); + ASSERT_CMPINT32 (15, ==, mcd_rpc_op_msg_section_set_document_sequence (rpc, 1u, data + 48, 15u)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_msg_set_checksum (rpc, 287454020)); + // clang-format on + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, expected_num_iovecs); + ASSERT_CMPSIZE_T (num_iovecs, ==, 12u); + ASSERT_CMPIOVEC_INT32 (0); // messageLength + ASSERT_CMPIOVEC_INT32 (1); // requestID + ASSERT_CMPIOVEC_INT32 (2); // responseTo + ASSERT_CMPIOVEC_INT32 (3); // opCode + ASSERT_CMPIOVEC_INT32 (4); // flagBits + ASSERT_CMPIOVEC_UINT8 (5); // Section 0 Kind + ASSERT_CMPIOVEC_BYTES (6); // Section 0 Body + ASSERT_CMPIOVEC_UINT8 (7); // Section 1 Kind + ASSERT_CMPIOVEC_INT32 (8); // Section 1 Length + ASSERT_CMPIOVEC_BYTES (9); // Section 1 Identifier + ASSERT_CMPIOVEC_BYTES (10); // Section 1 Documents + ASSERT_CMPIOVEC_UINT32 (11); // Checksum + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); + + bson_free (expected_iovecs); + mcd_rpc_message_destroy (expected_rpc); +} + +static void +test_rpc_message_setters_op_msg_kind_1_multiple (void) +{ + const uint8_t data[] = {TEST_DATA_OP_MSG_KIND_1_MULTIPLE}; + + mcd_rpc_message *const expected_rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + ASSERT (expected_rpc); + + size_t expected_num_iovecs; + mongoc_iovec_t *const expected_iovecs = + mcd_rpc_message_to_iovecs (expected_rpc, &expected_num_iovecs); + ASSERT (expected_iovecs); + + mcd_rpc_message *const rpc = mcd_rpc_message_new (); + + // clang-format off + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_message_length (rpc, 110)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_request_id (rpc, 16909060)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_response_to (rpc, 84281096)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_op_code (rpc, MONGOC_OP_CODE_MSG)); + mcd_rpc_op_msg_set_sections_count (rpc, 3u); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_msg_set_flag_bits (rpc, MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT)); + ASSERT_CMPINT32 ( 1, ==, mcd_rpc_op_msg_section_set_kind (rpc, 0u, 0)); + ASSERT_CMPINT32 (15, ==, mcd_rpc_op_msg_section_set_body (rpc, 0u, data + 21)); + ASSERT_CMPINT32 ( 1, ==, mcd_rpc_op_msg_section_set_kind (rpc, 1u, 1)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_msg_section_set_length (rpc, 1u, 25u)); + ASSERT_CMPINT32 ( 6, ==, mcd_rpc_op_msg_section_set_identifier (rpc, 1u, (const char *) (data + 41))); + ASSERT_CMPINT32 (15, ==, mcd_rpc_op_msg_section_set_document_sequence (rpc, 1u, data + 47, 15u)); + ASSERT_CMPINT32 ( 1, ==, mcd_rpc_op_msg_section_set_kind (rpc, 2u, 1)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_msg_section_set_length (rpc, 2u, 43u)); + ASSERT_CMPINT32 ( 7, ==, mcd_rpc_op_msg_section_set_identifier (rpc, 2u, (const char *) (data + 67))); + ASSERT_CMPINT32 (32, ==, mcd_rpc_op_msg_section_set_document_sequence (rpc, 2u, data + 74, 32u)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_msg_set_checksum (rpc, 287454020)); + // clang-format on + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, expected_num_iovecs); + ASSERT_CMPSIZE_T (num_iovecs, ==, 16u); + ASSERT_CMPIOVEC_INT32 (0); // messageLength + ASSERT_CMPIOVEC_INT32 (1); // requestID + ASSERT_CMPIOVEC_INT32 (2); // responseTo + ASSERT_CMPIOVEC_INT32 (3); // opCode + ASSERT_CMPIOVEC_INT32 (4); // flagBits + ASSERT_CMPIOVEC_UINT8 (5); // Section 0 Kind + ASSERT_CMPIOVEC_BYTES (6); // Section 0 Body + ASSERT_CMPIOVEC_UINT8 (7); // Section 1 Kind + ASSERT_CMPIOVEC_INT32 (8); // Section 1 Length + ASSERT_CMPIOVEC_BYTES (9); // Section 1 Identifier + ASSERT_CMPIOVEC_BYTES (10); // Section 1 Documents + ASSERT_CMPIOVEC_UINT8 (11); // Section 2 Kind + ASSERT_CMPIOVEC_INT32 (12); // Section 2 Length + ASSERT_CMPIOVEC_BYTES (13); // Section 2 Identifier + ASSERT_CMPIOVEC_BYTES (14); // Section 2 Documents + ASSERT_CMPIOVEC_UINT32 (15); // Checksum + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); + + bson_free (expected_iovecs); + mcd_rpc_message_destroy (expected_rpc); +} + +static void +test_rpc_message_setters_op_msg (void) +{ + test_rpc_message_setters_op_msg_kind_0 (); + test_rpc_message_setters_op_msg_kind_1_single (); + test_rpc_message_setters_op_msg_kind_1_multiple (); +} + +static void +test_rpc_message_setters_op_reply (void) +{ + const uint8_t data[] = {TEST_DATA_OP_REPLY}; + + mcd_rpc_message *const expected_rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + ASSERT (expected_rpc); + + size_t expected_num_iovecs; + mongoc_iovec_t *const expected_iovecs = + mcd_rpc_message_to_iovecs (expected_rpc, &expected_num_iovecs); + ASSERT (expected_iovecs); + + mcd_rpc_message *const rpc = mcd_rpc_message_new (); + + // clang-format off + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_message_length (rpc, 68)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_request_id (rpc, 16909060)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_response_to (rpc, 84281096)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_op_code (rpc, MONGOC_OP_CODE_REPLY)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_reply_set_response_flags (rpc, MONGOC_OP_REPLY_RESPONSE_FLAG_NONE)); + ASSERT_CMPINT32 ( 8, ==, mcd_rpc_op_reply_set_cursor_id (rpc, 1234605616436508552)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_reply_set_starting_from (rpc, 0)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_reply_set_number_returned (rpc, 2)); + ASSERT_CMPINT32 (32, ==, mcd_rpc_op_reply_set_documents (rpc, data + 36, 32u)); + // clang-format on + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, expected_num_iovecs); + ASSERT_CMPSIZE_T (num_iovecs, ==, 9u); + ASSERT_CMPIOVEC_INT32 (0); // messageLength + ASSERT_CMPIOVEC_INT32 (1); // requestID + ASSERT_CMPIOVEC_INT32 (2); // responseTo + ASSERT_CMPIOVEC_INT32 (3); // opCode + ASSERT_CMPIOVEC_INT32 (4); // responseFlags + ASSERT_CMPIOVEC_INT64 (5); // cursorID + ASSERT_CMPIOVEC_INT32 (6); // startingFrom + ASSERT_CMPIOVEC_INT32 (7); // numberReturned + ASSERT_CMPIOVEC_BYTES (8); // documents + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); + + bson_free (expected_iovecs); + mcd_rpc_message_destroy (expected_rpc); +} + +static void +test_rpc_message_setters_op_update (void) +{ + const uint8_t data[] = {TEST_DATA_OP_UPDATE}; + + mcd_rpc_message *const expected_rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + ASSERT (expected_rpc); + + size_t expected_num_iovecs; + mongoc_iovec_t *const expected_iovecs = + mcd_rpc_message_to_iovecs (expected_rpc, &expected_num_iovecs); + ASSERT (expected_iovecs); + + mcd_rpc_message *const rpc = mcd_rpc_message_new (); + + // clang-format off + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_message_length (rpc, 62)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_request_id (rpc, 16909060)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_response_to (rpc, 84281096)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_op_code (rpc, MONGOC_OP_CODE_UPDATE)); + ASSERT_CMPINT32 ( 8, ==, mcd_rpc_op_update_set_full_collection_name (rpc, (const char *) (data + 20))); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_update_set_flags (rpc, MONGOC_OP_UPDATE_FLAG_NONE)); + ASSERT_CMPINT32 (16, ==, mcd_rpc_op_update_set_selector (rpc, data + 32)); + ASSERT_CMPINT32 (14, ==, mcd_rpc_op_update_set_update (rpc, data + 48)); + // clang-format on + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, expected_num_iovecs); + ASSERT_CMPSIZE_T (num_iovecs, ==, 9u); + ASSERT_CMPIOVEC_INT32 (0); // messageLength + ASSERT_CMPIOVEC_INT32 (1); // requestID + ASSERT_CMPIOVEC_INT32 (2); // responseTo + ASSERT_CMPIOVEC_INT32 (3); // opCode + ASSERT_CMPIOVEC_INT32 (4); // ZERO + ASSERT_CMPIOVEC_BYTES (5); // fullCollectionName + ASSERT_CMPIOVEC_INT32 (6); // flags + ASSERT_CMPIOVEC_BYTES (7); // selector + ASSERT_CMPIOVEC_BYTES (8); // update + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); + + bson_free (expected_iovecs); + mcd_rpc_message_destroy (expected_rpc); +} + +static void +test_rpc_message_setters_op_insert (void) +{ + const uint8_t data[] = {TEST_DATA_OP_INSERT}; + + mcd_rpc_message *const expected_rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + ASSERT (expected_rpc); + + size_t expected_num_iovecs; + mongoc_iovec_t *const expected_iovecs = + mcd_rpc_message_to_iovecs (expected_rpc, &expected_num_iovecs); + ASSERT (expected_iovecs); + + mcd_rpc_message *const rpc = mcd_rpc_message_new (); + + // clang-format off + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_message_length (rpc, 60)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_request_id (rpc, 16909060)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_response_to (rpc, 84281096)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_op_code (rpc, MONGOC_OP_CODE_INSERT)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_insert_set_flags (rpc, MONGOC_OP_INSERT_FLAG_NONE)); + ASSERT_CMPINT32 ( 8, ==, mcd_rpc_op_insert_set_full_collection_name (rpc, (const char *) (data + 20))); + ASSERT_CMPINT32 (32, ==, mcd_rpc_op_insert_set_documents (rpc, data + 28, 32u)); + // clang-format on + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, expected_num_iovecs); + ASSERT_CMPSIZE_T (num_iovecs, ==, 7u); + ASSERT_CMPIOVEC_INT32 (0); // messageLength + ASSERT_CMPIOVEC_INT32 (1); // requestID + ASSERT_CMPIOVEC_INT32 (2); // responseTo + ASSERT_CMPIOVEC_INT32 (3); // opCode + ASSERT_CMPIOVEC_INT32 (4); // flags + ASSERT_CMPIOVEC_BYTES (5); // fullCollectionName + ASSERT_CMPIOVEC_BYTES (6); // documents + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); + + bson_free (expected_iovecs); + mcd_rpc_message_destroy (expected_rpc); +} + +static void +test_rpc_message_setters_op_query (void) +{ + const uint8_t data[] = {TEST_DATA_OP_QUERY}; + + mcd_rpc_message *const expected_rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + ASSERT (expected_rpc); + + size_t expected_num_iovecs; + mongoc_iovec_t *const expected_iovecs = + mcd_rpc_message_to_iovecs (expected_rpc, &expected_num_iovecs); + ASSERT (expected_iovecs); + + mcd_rpc_message *const rpc = mcd_rpc_message_new (); + + // clang-format off + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_message_length (rpc, 65)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_request_id (rpc, 16909060)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_response_to (rpc, 84281096)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_op_code (rpc, MONGOC_OP_CODE_QUERY)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_query_set_flags (rpc, MONGOC_OP_QUERY_FLAG_NONE)); + ASSERT_CMPINT32 ( 8, ==, mcd_rpc_op_query_set_full_collection_name (rpc, (const char *) (data + 20))); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_query_set_number_to_skip (rpc, 0)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_query_set_number_to_return (rpc, 0)); + ASSERT_CMPINT32 (13, ==, mcd_rpc_op_query_set_query (rpc, data + 36)); + ASSERT_CMPINT32 (16, ==, mcd_rpc_op_query_set_return_fields_selector (rpc, data + 49)); + // clang-format on + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, expected_num_iovecs); + ASSERT_CMPSIZE_T (num_iovecs, ==, 10u); + ASSERT_CMPIOVEC_INT32 (0); // messageLength + ASSERT_CMPIOVEC_INT32 (1); // requestID + ASSERT_CMPIOVEC_INT32 (2); // responseTo + ASSERT_CMPIOVEC_INT32 (3); // opCode + ASSERT_CMPIOVEC_INT32 (4); // flags + ASSERT_CMPIOVEC_BYTES (5); // fullCollectionName + ASSERT_CMPIOVEC_INT32 (6); // numberToSkip + ASSERT_CMPIOVEC_INT32 (7); // numberToReturn + ASSERT_CMPIOVEC_BYTES (8); // query + ASSERT_CMPIOVEC_BYTES (9); // returnFieldsSelector + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); + + bson_free (expected_iovecs); + mcd_rpc_message_destroy (expected_rpc); +} + +static void +test_rpc_message_setters_op_get_more (void) +{ + const uint8_t data[] = {TEST_DATA_OP_GET_MORE}; + + mcd_rpc_message *const expected_rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + ASSERT (expected_rpc); + + size_t expected_num_iovecs; + mongoc_iovec_t *const expected_iovecs = + mcd_rpc_message_to_iovecs (expected_rpc, &expected_num_iovecs); + ASSERT (expected_iovecs); + + mcd_rpc_message *const rpc = mcd_rpc_message_new (); + + // clang-format off + ASSERT_CMPINT32 (4, ==, mcd_rpc_header_set_message_length (rpc, 40)); + ASSERT_CMPINT32 (4, ==, mcd_rpc_header_set_request_id (rpc, 16909060)); + ASSERT_CMPINT32 (4, ==, mcd_rpc_header_set_response_to (rpc, 84281096)); + ASSERT_CMPINT32 (4, ==, mcd_rpc_header_set_op_code (rpc, MONGOC_OP_CODE_GET_MORE)); + ASSERT_CMPINT32 (8, ==, mcd_rpc_op_get_more_set_full_collection_name (rpc, (const char *) (data + 20))); + ASSERT_CMPINT32 (4, ==, mcd_rpc_op_get_more_set_number_to_return (rpc, 0)); + ASSERT_CMPINT32 (8, ==, mcd_rpc_op_get_more_set_cursor_id (rpc, 1234605616436508552)); + // clang-format on + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, expected_num_iovecs); + ASSERT_CMPSIZE_T (num_iovecs, ==, 8u); + ASSERT_CMPIOVEC_INT32 (0); // messageLength + ASSERT_CMPIOVEC_INT32 (1); // requestID + ASSERT_CMPIOVEC_INT32 (2); // responseTo + ASSERT_CMPIOVEC_INT32 (3); // opCode + ASSERT_CMPIOVEC_INT32 (4); // ZERO + ASSERT_CMPIOVEC_BYTES (5); // fullCollectionName + ASSERT_CMPIOVEC_INT32 (6); // numberToReturn + ASSERT_CMPIOVEC_INT64 (7); // cursorID + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); + + bson_free (expected_iovecs); + mcd_rpc_message_destroy (expected_rpc); +} + +static void +test_rpc_message_setters_op_delete (void) +{ + const uint8_t data[] = {TEST_DATA_OP_DELETE}; + + mcd_rpc_message *const expected_rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + ASSERT (expected_rpc); + + size_t expected_num_iovecs; + mongoc_iovec_t *const expected_iovecs = + mcd_rpc_message_to_iovecs (expected_rpc, &expected_num_iovecs); + ASSERT (expected_iovecs); + + mcd_rpc_message *const rpc = mcd_rpc_message_new (); + + // clang-format off + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_message_length (rpc, 48)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_request_id (rpc, 16909060)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_response_to (rpc, 84281096)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_op_code (rpc, MONGOC_OP_CODE_DELETE)); + ASSERT_CMPINT32 ( 8, ==, mcd_rpc_op_delete_set_full_collection_name (rpc, (const char *) (data + 20))); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_delete_set_flags (rpc, MONGOC_OP_DELETE_FLAG_NONE)); + ASSERT_CMPINT32 (16, ==, mcd_rpc_op_delete_set_selector (rpc, data + 32)); + // clang-format on + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, expected_num_iovecs); + ASSERT_CMPSIZE_T (num_iovecs, ==, 8u); + ASSERT_CMPIOVEC_INT32 (0); // messageLength + ASSERT_CMPIOVEC_INT32 (1); // requestID + ASSERT_CMPIOVEC_INT32 (2); // responseTo + ASSERT_CMPIOVEC_INT32 (3); // opCode + ASSERT_CMPIOVEC_INT32 (4); // ZERO + ASSERT_CMPIOVEC_BYTES (5); // fullCollectionName + ASSERT_CMPIOVEC_INT32 (6); // flags + ASSERT_CMPIOVEC_BYTES (7); // selector + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); + + bson_free (expected_iovecs); + mcd_rpc_message_destroy (expected_rpc); +} + +static void +test_rpc_message_setters_op_kill_cursors (void) +{ + const uint8_t data[] = {TEST_DATA_OP_KILL_CURSORS}; + + mcd_rpc_message *const expected_rpc = + mcd_rpc_message_from_data (data, sizeof (data), NULL); + ASSERT (expected_rpc); + + size_t expected_num_iovecs; + mongoc_iovec_t *const expected_iovecs = + mcd_rpc_message_to_iovecs (expected_rpc, &expected_num_iovecs); + ASSERT (expected_iovecs); + + const int64_t cursor_ids[] = {1230066625199609624, 2387509390608836392}; + + mcd_rpc_message *const rpc = mcd_rpc_message_new (); + + // clang-format off + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_message_length (rpc, 40)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_request_id (rpc, 16909060)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_response_to (rpc, 84281096)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_op_code (rpc, MONGOC_OP_CODE_KILL_CURSORS)); + ASSERT_CMPINT32 (20, ==, mcd_rpc_op_kill_cursors_set_cursor_ids (rpc, cursor_ids, 2)); + // clang-format on + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs); + + ASSERT_CMPSIZE_T (num_iovecs, ==, expected_num_iovecs); + ASSERT_CMPSIZE_T (num_iovecs, ==, 7u); + ASSERT_CMPIOVEC_INT32 (0); // messageLength + ASSERT_CMPIOVEC_INT32 (1); // requestID + ASSERT_CMPIOVEC_INT32 (2); // responseTo + ASSERT_CMPIOVEC_INT32 (3); // opCode + ASSERT_CMPIOVEC_INT32 (4); // ZERO + ASSERT_CMPIOVEC_INT32 (5); // numberOfCursorIds + + // The cursorIDs iovec does not point to original data due to endian + // conversion requirements. Assert values individually. + { + const mongoc_iovec_t *const iovec = iovecs + 6; + const mongoc_iovec_t *const expected_iovec = expected_iovecs + 6; + + ASSERT_CMPSIZE_T (iovec->iov_len, ==, expected_iovec->iov_len); + ASSERT (memcmp (iovec->iov_base, + expected_iovec->iov_base, + expected_iovec->iov_len) == 0); + } + + bson_free (iovecs); + mcd_rpc_message_destroy (rpc); + + bson_free (expected_iovecs); + mcd_rpc_message_destroy (expected_rpc); +} + + +static void +test_rpc_message_from_data_in_place (void) +{ + const uint8_t data_op_compressed[] = {TEST_DATA_OP_COMPRESSED}; + const uint8_t data_op_msg_kind_0[] = {TEST_DATA_OP_MSG_KIND_0}; + const uint8_t data_op_msg_kind_1_single[] = {TEST_DATA_OP_MSG_KIND_1_SINGLE}; + const uint8_t data_op_msg_kind_1_multiple[] = { + TEST_DATA_OP_MSG_KIND_1_MULTIPLE}; + const uint8_t data_op_reply[] = {TEST_DATA_OP_REPLY}; + const uint8_t data_op_update[] = {TEST_DATA_OP_UPDATE}; + const uint8_t data_op_insert[] = {TEST_DATA_OP_INSERT}; + const uint8_t data_op_query[] = {TEST_DATA_OP_QUERY}; + const uint8_t data_op_get_more[] = {TEST_DATA_OP_GET_MORE}; + const uint8_t data_op_delete[] = {TEST_DATA_OP_DELETE}; + const uint8_t data_op_kill_cursors[] = {TEST_DATA_OP_KILL_CURSORS}; + + typedef struct test_data_type { + const uint8_t *data; + size_t data_len; + } test_data_type; + + test_data_type test_data[] = { + {data_op_compressed, sizeof (data_op_compressed)}, + {data_op_msg_kind_0, sizeof (data_op_msg_kind_0)}, + {data_op_msg_kind_1_single, sizeof (data_op_msg_kind_1_single)}, + {data_op_msg_kind_1_multiple, sizeof (data_op_msg_kind_1_multiple)}, + {data_op_reply, sizeof (data_op_reply)}, + {data_op_update, sizeof (data_op_update)}, + {data_op_insert, sizeof (data_op_insert)}, + {data_op_query, sizeof (data_op_query)}, + {data_op_get_more, sizeof (data_op_get_more)}, + {data_op_delete, sizeof (data_op_delete)}, + {data_op_kill_cursors, sizeof (data_op_kill_cursors)}, + }; + const size_t num_test_data = sizeof (test_data) / sizeof (*test_data); + + mcd_rpc_message *const rpc = mcd_rpc_message_new (); + + ASSERT_CMPINT32 (mcd_rpc_header_get_message_length (rpc), ==, 0); + ASSERT_CMPINT32 (mcd_rpc_header_get_request_id (rpc), ==, 0); + ASSERT_CMPINT32 (mcd_rpc_header_get_response_to (rpc), ==, 0); + ASSERT_CMPINT32 (mcd_rpc_header_get_op_code (rpc), ==, MONGOC_OP_CODE_NONE); + + const void *data_end = NULL; + bool res = false; + + // Test reuse of RPC message object. + for (size_t i = 0u; i < num_test_data; ++i) { + const uint8_t *const data = test_data[i].data; + const size_t data_len = test_data[i].data_len; + + res = mcd_rpc_message_from_data_in_place (rpc, data, data_len, &data_end); + ASSERT_RPC_MESSAGE_RESULT (res, data, data_end, data_len); + mcd_rpc_message_reset (rpc); + } + + // Again but in reverse order. + for (size_t i = 0u; i < num_test_data; ++i) { + const size_t idx = num_test_data - 1u - i; + const uint8_t *const data = test_data[idx].data; + const size_t data_len = test_data[idx].data_len; + + res = mcd_rpc_message_from_data_in_place (rpc, data, data_len, &data_end); + ASSERT_RPC_MESSAGE_RESULT (res, data, data_end, data_len); + mcd_rpc_message_reset (rpc); + } + + // Also test post-conversion to iovecs and little endian. + for (size_t i = 0u; i < num_test_data; ++i) { + const uint8_t *const data = test_data[i].data; + const size_t data_len = test_data[i].data_len; + + res = mcd_rpc_message_from_data_in_place (rpc, data, data_len, &data_end); + ASSERT_RPC_MESSAGE_RESULT (res, data, data_end, data_len); + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = + mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs && num_iovecs > 0u); + bson_free (iovecs); + + mcd_rpc_message_reset (rpc); + } + + // Again but in reverse order. + for (size_t i = 0u; i < num_test_data; ++i) { + const size_t idx = num_test_data - 1u - i; + const uint8_t *const data = test_data[idx].data; + const size_t data_len = test_data[idx].data_len; + + res = mcd_rpc_message_from_data_in_place (rpc, data, data_len, &data_end); + ASSERT_RPC_MESSAGE_RESULT (res, data, data_end, data_len); + + size_t num_iovecs; + mongoc_iovec_t *const iovecs = + mcd_rpc_message_to_iovecs (rpc, &num_iovecs); + ASSERT (iovecs && num_iovecs > 0u); + bson_free (iovecs); + + mcd_rpc_message_reset (rpc); + } + + mcd_rpc_message_destroy (rpc); +} + + +void +test_mcd_rpc_install (TestSuite *suite) +{ + TestSuite_Add (suite, + "/rpc_message/from_data/op_compressed/valid", + test_rpc_message_from_data_op_compressed_valid); + TestSuite_Add (suite, + "/rpc_message/from_data/op_msg/valid", + test_rpc_message_from_data_op_msg_valid); + TestSuite_Add (suite, + "/rpc_message/from_data/op_reply/valid", + test_rpc_message_from_data_op_reply_valid); + TestSuite_Add (suite, + "/rpc_message/from_data/op_update/valid", + test_rpc_message_from_data_op_update_valid); + TestSuite_Add (suite, + "/rpc_message/from_data/op_insert/valid", + test_rpc_message_from_data_op_insert_valid); + TestSuite_Add (suite, + "/rpc_message/from_data/op_query/valid", + test_rpc_message_from_data_op_query_valid); + TestSuite_Add (suite, + "/rpc_message/from_data/op_get_more/valid", + test_rpc_message_from_data_op_get_more_valid); + TestSuite_Add (suite, + "/rpc_message/from_data/op_delete/valid", + test_rpc_message_from_data_op_delete_valid); + TestSuite_Add (suite, + "/rpc_message/from_data/op_kill_cursors/valid", + test_rpc_message_from_data_op_kill_cursors_valid); + + TestSuite_Add (suite, + "/rpc_message/from_data/op_compressed/invalid", + test_rpc_message_from_data_op_compressed_invalid); + TestSuite_Add (suite, + "/rpc_message/from_data/op_msg/invalid", + test_rpc_message_from_data_op_msg_invalid); + TestSuite_Add (suite, + "/rpc_message/from_data/op_reply/invalid", + test_rpc_message_from_data_op_reply_invalid); + TestSuite_Add (suite, + "/rpc_message/from_data/op_update/invalid", + test_rpc_message_from_data_op_update_invalid); + TestSuite_Add (suite, + "/rpc_message/from_data/op_insert/invalid", + test_rpc_message_from_data_op_insert_invalid); + TestSuite_Add (suite, + "/rpc_message/from_data/op_query/invalid", + test_rpc_message_from_data_op_query_invalid); + TestSuite_Add (suite, + "/rpc_message/from_data/op_get_more/invalid", + test_rpc_message_from_data_op_get_more_invalid); + TestSuite_Add (suite, + "/rpc_message/from_data/op_delete/invalid", + test_rpc_message_from_data_op_delete_invalid); + TestSuite_Add (suite, + "/rpc_message/from_data/op_kill_cursors/invalid", + test_rpc_message_from_data_op_kill_cursors_invalid); + + TestSuite_Add (suite, + "/rpc_message/to_iovecs/op_compressed", + test_rpc_message_to_iovecs_op_compressed); + TestSuite_Add (suite, + "/rpc_message/to_iovecs/op_msg", + test_rpc_message_to_iovecs_op_msg); + TestSuite_Add (suite, + "/rpc_message/to_iovecs/op_reply", + test_rpc_message_to_iovecs_op_reply); + TestSuite_Add (suite, + "/rpc_message/to_iovecs/op_update", + test_rpc_message_to_iovecs_op_update); + TestSuite_Add (suite, + "/rpc_message/to_iovecs/op_insert", + test_rpc_message_to_iovecs_op_insert); + TestSuite_Add (suite, + "/rpc_message/to_iovecs/op_query", + test_rpc_message_to_iovecs_op_query); + TestSuite_Add (suite, + "/rpc_message/to_iovecs/op_get_more", + test_rpc_message_to_iovecs_op_get_more); + TestSuite_Add (suite, + "/rpc_message/to_iovecs/op_delete", + test_rpc_message_to_iovecs_op_delete); + TestSuite_Add (suite, + "/rpc_message/to_iovecs/op_kill_cursors", + test_rpc_message_to_iovecs_op_kill_cursors); + + TestSuite_Add (suite, + "/rpc_message/setters/op_compressed", + test_rpc_message_setters_op_compressed); + TestSuite_Add ( + suite, "/rpc_message/setters/op_msg", test_rpc_message_setters_op_msg); + TestSuite_Add (suite, + "/rpc_message/setters/op_reply", + test_rpc_message_setters_op_reply); + TestSuite_Add (suite, + "/rpc_message/setters/op_update", + test_rpc_message_setters_op_update); + TestSuite_Add (suite, + "/rpc_message/setters/op_insert", + test_rpc_message_setters_op_insert); + TestSuite_Add (suite, + "/rpc_message/setters/op_query", + test_rpc_message_setters_op_query); + TestSuite_Add (suite, + "/rpc_message/setters/op_get_more", + test_rpc_message_setters_op_get_more); + TestSuite_Add (suite, + "/rpc_message/setters/op_delete", + test_rpc_message_setters_op_delete); + TestSuite_Add (suite, + "/rpc_message/setters/op_kill_cursors", + test_rpc_message_setters_op_kill_cursors); + + + TestSuite_Add (suite, + "/rpc_message/from_data/in_place", + test_rpc_message_from_data_in_place); +} From 86e5baee21816887c00c55e2f7777616f61c565d Mon Sep 17 00:00:00 2001 From: Ezra Chung Date: Tue, 30 May 2023 00:17:55 -0500 Subject: [PATCH 03/14] Add mongoc-opcode.c and mongoc-flags.c with equivalence assertions --- src/libmongoc/CMakeLists.txt | 2 + src/libmongoc/src/mongoc/CMakeLists.txt | 2 + src/libmongoc/src/mongoc/mongoc-flags.c | 79 ++++++++++++++++++++++++ src/libmongoc/src/mongoc/mongoc-opcode.c | 35 +++++++++++ 4 files changed, 118 insertions(+) create mode 100644 src/libmongoc/src/mongoc/mongoc-flags.c create mode 100644 src/libmongoc/src/mongoc/mongoc-opcode.c diff --git a/src/libmongoc/CMakeLists.txt b/src/libmongoc/CMakeLists.txt index 082c396982d..c344cbeddbd 100644 --- a/src/libmongoc/CMakeLists.txt +++ b/src/libmongoc/CMakeLists.txt @@ -526,6 +526,7 @@ set (SOURCES ${SOURCES} ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-cursor-array.c ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-database.c ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-error.c + ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-flags.c ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-find-and-modify.c ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-generation-map.c ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-init.c @@ -549,6 +550,7 @@ set (SOURCES ${SOURCES} ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-matcher-op.c ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-memcmp.c ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-cmd.c + ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-opcode.c ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-optional.c ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-opts-helpers.c ${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-opts.c diff --git a/src/libmongoc/src/mongoc/CMakeLists.txt b/src/libmongoc/src/mongoc/CMakeLists.txt index 3efb2d302c0..3115072ed89 100644 --- a/src/libmongoc/src/mongoc/CMakeLists.txt +++ b/src/libmongoc/src/mongoc/CMakeLists.txt @@ -213,6 +213,7 @@ set (src_libmongoc_src_mongoc_DIST_cs mongoc-cursor-cmd-deprecated.c mongoc-database.c mongoc-error.c + mongoc-flags.c mongoc-find-and-modify.c mongoc-generation-map.c mongoc-host-list.c @@ -235,6 +236,7 @@ set (src_libmongoc_src_mongoc_DIST_cs mongoc-memcmp.c mongoc-cmd.c mongoc-ocsp-cache.c + mongoc-opcode.c mongoc-optional.c mongoc-opts.c mongoc-opts-helpers.c diff --git a/src/libmongoc/src/mongoc/mongoc-flags.c b/src/libmongoc/src/mongoc/mongoc-flags.c new file mode 100644 index 00000000000..1393e49999a --- /dev/null +++ b/src/libmongoc/src/mongoc/mongoc-flags.c @@ -0,0 +1,79 @@ +/* + * Copyright 2023-present MongoDB, 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 "mongoc-flags.h" + +#include "mongoc-compression-private.h" +#include "mongoc-flags-private.h" + +#include "mcd-rpc.h" + +#include + +// Document and ensure consistency between equivalent macros in mcd-rpc and +// libmongoc. + +BSON_STATIC_ASSERT (MONGOC_OP_COMPRESSED_COMPRESSOR_ID_NOOP == + MONGOC_COMPRESSOR_NOOP_ID); +BSON_STATIC_ASSERT (MONGOC_OP_COMPRESSED_COMPRESSOR_ID_SNAPPY == + MONGOC_COMPRESSOR_SNAPPY_ID); +BSON_STATIC_ASSERT (MONGOC_OP_COMPRESSED_COMPRESSOR_ID_ZLIB == + MONGOC_COMPRESSOR_ZLIB_ID); +BSON_STATIC_ASSERT (MONGOC_OP_COMPRESSED_COMPRESSOR_ID_ZSTD == + MONGOC_COMPRESSOR_ZSTD_ID); + +BSON_STATIC_ASSERT (MONGOC_OP_MSG_FLAG_NONE == MONGOC_MSG_NONE); +BSON_STATIC_ASSERT (MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT == + MONGOC_MSG_CHECKSUM_PRESENT); +BSON_STATIC_ASSERT (MONGOC_OP_MSG_FLAG_MORE_TO_COME == MONGOC_MSG_MORE_TO_COME); +BSON_STATIC_ASSERT (MONGOC_OP_MSG_FLAG_EXHAUST_ALLOWED == + MONGOC_MSG_EXHAUST_ALLOWED); + +BSON_STATIC_ASSERT (MONGOC_OP_REPLY_RESPONSE_FLAG_NONE == MONGOC_REPLY_NONE); +BSON_STATIC_ASSERT (MONGOC_OP_REPLY_RESPONSE_FLAG_CURSOR_NOT_FOUND == + MONGOC_REPLY_CURSOR_NOT_FOUND); +BSON_STATIC_ASSERT (MONGOC_OP_REPLY_RESPONSE_FLAG_QUERY_FAILURE == + MONGOC_REPLY_QUERY_FAILURE); +BSON_STATIC_ASSERT (MONGOC_OP_REPLY_RESPONSE_FLAG_SHARD_CONFIG_STALE == + MONGOC_REPLY_SHARD_CONFIG_STALE); +BSON_STATIC_ASSERT (MONGOC_OP_REPLY_RESPONSE_FLAG_AWAIT_CAPABLE == + MONGOC_REPLY_AWAIT_CAPABLE); + +BSON_STATIC_ASSERT (MONGOC_OP_UPDATE_FLAG_NONE == MONGOC_UPDATE_NONE); +BSON_STATIC_ASSERT (MONGOC_OP_UPDATE_FLAG_UPSERT == MONGOC_UPDATE_UPSERT); +BSON_STATIC_ASSERT (MONGOC_OP_UPDATE_FLAG_MULTI_UPDATE == + MONGOC_UPDATE_MULTI_UPDATE); + +BSON_STATIC_ASSERT (MONGOC_OP_INSERT_FLAG_NONE == MONGOC_INSERT_NONE); +BSON_STATIC_ASSERT (MONGOC_OP_INSERT_FLAG_CONTINUE_ON_ERROR == + MONGOC_INSERT_CONTINUE_ON_ERROR); + +BSON_STATIC_ASSERT (MONGOC_OP_QUERY_FLAG_NONE == MONGOC_QUERY_NONE); +BSON_STATIC_ASSERT (MONGOC_OP_QUERY_FLAG_TAILABLE_CURSOR == + MONGOC_QUERY_TAILABLE_CURSOR); +BSON_STATIC_ASSERT (MONGOC_OP_QUERY_FLAG_SECONDARY_OK == + MONGOC_QUERY_SECONDARY_OK); +BSON_STATIC_ASSERT (MONGOC_OP_QUERY_FLAG_OPLOG_REPLAY == + MONGOC_QUERY_OPLOG_REPLAY); +BSON_STATIC_ASSERT (MONGOC_OP_QUERY_FLAG_NO_CURSOR_TIMEOUT == + MONGOC_QUERY_NO_CURSOR_TIMEOUT); +BSON_STATIC_ASSERT (MONGOC_OP_QUERY_FLAG_AWAIT_DATA == MONGOC_QUERY_AWAIT_DATA); +BSON_STATIC_ASSERT (MONGOC_OP_QUERY_FLAG_EXHAUST == MONGOC_QUERY_EXHAUST); +BSON_STATIC_ASSERT (MONGOC_OP_QUERY_FLAG_PARTIAL == MONGOC_QUERY_PARTIAL); + +BSON_STATIC_ASSERT (MONGOC_OP_DELETE_FLAG_NONE == MONGOC_DELETE_NONE); +BSON_STATIC_ASSERT (MONGOC_OP_DELETE_FLAG_SINGLE_REMOVE == + MONGOC_DELETE_SINGLE_REMOVE); diff --git a/src/libmongoc/src/mongoc/mongoc-opcode.c b/src/libmongoc/src/mongoc/mongoc-opcode.c new file mode 100644 index 00000000000..0bdcf7acc7e --- /dev/null +++ b/src/libmongoc/src/mongoc/mongoc-opcode.c @@ -0,0 +1,35 @@ +/* + * Copyright 2023-present MongoDB, 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 "mongoc-opcode.h" + +#include "mcd-rpc.h" + +#include + +// Document and ensure consistency between equivalent macros in mcd-rpc and +// libmongoc. + +BSON_STATIC_ASSERT (MONGOC_OP_CODE_COMPRESSED == MONGOC_OPCODE_COMPRESSED); +BSON_STATIC_ASSERT (MONGOC_OP_CODE_MSG == MONGOC_OPCODE_MSG); + +BSON_STATIC_ASSERT (MONGOC_OP_CODE_REPLY == MONGOC_OPCODE_REPLY); +BSON_STATIC_ASSERT (MONGOC_OP_CODE_UPDATE == MONGOC_OPCODE_UPDATE); +BSON_STATIC_ASSERT (MONGOC_OP_CODE_INSERT == MONGOC_OPCODE_INSERT); +BSON_STATIC_ASSERT (MONGOC_OP_CODE_QUERY == MONGOC_OPCODE_QUERY); +BSON_STATIC_ASSERT (MONGOC_OP_CODE_GET_MORE == MONGOC_OPCODE_GET_MORE); +BSON_STATIC_ASSERT (MONGOC_OP_CODE_DELETE == MONGOC_OPCODE_DELETE); +BSON_STATIC_ASSERT (MONGOC_OP_CODE_KILL_CURSORS == MONGOC_OPCODE_KILL_CURSORS); From d93a6a4523f4c0f34686ae2f2f4ae5d5d838d3b6 Mon Sep 17 00:00:00 2001 From: Ezra Chung Date: Mon, 12 Jun 2023 13:00:19 -0500 Subject: [PATCH 04/14] Remove mention of APM and CSE in _consume_op_msg --- src/libmongoc/src/mongoc/mcd-rpc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libmongoc/src/mongoc/mcd-rpc.c b/src/libmongoc/src/mongoc/mcd-rpc.c index 17a3c3936da..e363fc2db34 100644 --- a/src/libmongoc/src/mongoc/mcd-rpc.c +++ b/src/libmongoc/src/mongoc/mcd-rpc.c @@ -487,8 +487,7 @@ _consume_op_msg (mcd_rpc_message *rpc, } // Each message contains one or more sections. Preallocate space for two - // sections, which should cover the most frequent cases (including APM and - // CSE). + // sections, which should cover the most frequent cases. size_t capacity = 2u; op_msg->sections = bson_malloc (capacity * sizeof (mcd_rpc_op_msg_section)); op_msg->sections_count = 0u; From ee3e7daadbc3f99b285115590585ef41d124ca76 Mon Sep 17 00:00:00 2001 From: Ezra Chung Date: Mon, 12 Jun 2023 13:01:00 -0500 Subject: [PATCH 05/14] Removed unused include of BSON DSL utilities --- src/libmongoc/tests/test-mcd-rpc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libmongoc/tests/test-mcd-rpc.c b/src/libmongoc/tests/test-mcd-rpc.c index 8024412066c..6638e776cff 100644 --- a/src/libmongoc/tests/test-mcd-rpc.c +++ b/src/libmongoc/tests/test-mcd-rpc.c @@ -2,8 +2,6 @@ #include -#include - #include "test-conveniences.h" #include "TestSuite.h" From c36c70a34d218071ee3dd6b89b706bc682624e77 Mon Sep 17 00:00:00 2001 From: Ezra Chung Date: Mon, 12 Jun 2023 13:08:25 -0500 Subject: [PATCH 06/14] Ensure expected cursorID literals are representable --- src/libmongoc/tests/test-mcd-rpc.c | 34 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/libmongoc/tests/test-mcd-rpc.c b/src/libmongoc/tests/test-mcd-rpc.c index 6638e776cff..63ba344ab43 100644 --- a/src/libmongoc/tests/test-mcd-rpc.c +++ b/src/libmongoc/tests/test-mcd-rpc.c @@ -2104,15 +2104,15 @@ test_rpc_message_to_iovecs_op_reply (void) ASSERT (iovecs); ASSERT_CMPSIZE_T (num_iovecs, ==, 9u); - ASSERT_IOVEC_INT32 (0, 68); // messageLength - ASSERT_IOVEC_INT32 (1, 16909060); // requestID - ASSERT_IOVEC_INT32 (2, 84281096); // responseTo - ASSERT_IOVEC_INT32 (3, 1); // opCode - ASSERT_IOVEC_INT32 (4, 0); // responseFlags - ASSERT_IOVEC_INT64 (5, 1234605616436508552); // cursorID - ASSERT_IOVEC_INT32 (6, 0); // startingFrom - ASSERT_IOVEC_INT32 (7, 2); // numberReturned - ASSERT_IOVEC_BYTES (8, 36u, 32u); // documents + ASSERT_IOVEC_INT32 (0, 68); // messageLength + ASSERT_IOVEC_INT32 (1, 16909060); // requestID + ASSERT_IOVEC_INT32 (2, 84281096); // responseTo + ASSERT_IOVEC_INT32 (3, 1); // opCode + ASSERT_IOVEC_INT32 (4, 0); // responseFlags + ASSERT_IOVEC_INT64 (5, INT64_C (1234605616436508552)); // cursorID + ASSERT_IOVEC_INT32 (6, 0); // startingFrom + ASSERT_IOVEC_INT32 (7, 2); // numberReturned + ASSERT_IOVEC_BYTES (8, 36u, 32u); // documents bson_free (iovecs); mcd_rpc_message_destroy (rpc); @@ -2211,14 +2211,14 @@ test_rpc_message_to_iovecs_op_get_more (void) ASSERT (iovecs); ASSERT_CMPSIZE_T (num_iovecs, ==, 8u); - ASSERT_IOVEC_INT32 (0, 40); // messageLength - ASSERT_IOVEC_INT32 (1, 16909060); // requestID - ASSERT_IOVEC_INT32 (2, 84281096); // responseTo - ASSERT_IOVEC_INT32 (3, 2005); // opCode - ASSERT_IOVEC_INT32 (4, 0); // ZERO - ASSERT_IOVEC_BYTES (5, 20u, 8u); // fullCollectionName - ASSERT_IOVEC_INT32 (6, 0); // numberToReturn - ASSERT_IOVEC_INT64 (7, 1234605616436508552); // cursorID + ASSERT_IOVEC_INT32 (0, 40); // messageLength + ASSERT_IOVEC_INT32 (1, 16909060); // requestID + ASSERT_IOVEC_INT32 (2, 84281096); // responseTo + ASSERT_IOVEC_INT32 (3, 2005); // opCode + ASSERT_IOVEC_INT32 (4, 0); // ZERO + ASSERT_IOVEC_BYTES (5, 20u, 8u); // fullCollectionName + ASSERT_IOVEC_INT32 (6, 0); // numberToReturn + ASSERT_IOVEC_INT64 (7, INT64_C (1234605616436508552)); // cursorID bson_free (iovecs); mcd_rpc_message_destroy (rpc); From 5595eb434a166e1023cbcd981f1f60bf96844ce3 Mon Sep 17 00:00:00 2001 From: Ezra Chung Date: Mon, 12 Jun 2023 13:15:33 -0500 Subject: [PATCH 07/14] Use valid BSON document in OP_COMPRESSED test data --- src/libmongoc/tests/test-mcd-rpc.c | 45 +++++++++++++++--------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/libmongoc/tests/test-mcd-rpc.c b/src/libmongoc/tests/test-mcd-rpc.c index 63ba344ab43..354825b363c 100644 --- a/src/libmongoc/tests/test-mcd-rpc.c +++ b/src/libmongoc/tests/test-mcd-rpc.c @@ -11,24 +11,25 @@ // clang-format off #define TEST_DATA_OP_COMPRESSED \ /* */ /* header (16 bytes) */ \ - /* 0 */ 0x2b, 0x00, 0x00, 0x00, /* messageLength (43) */ \ + /* 0 */ 0x2d, 0x00, 0x00, 0x00, /* messageLength (45) */ \ /* 4 */ 0x04, 0x03, 0x02, 0x01, /* requestID (16909060) */ \ /* 8 */ 0x08, 0x07, 0x06, 0x05, /* responseTo (84281096) */ \ /* 12 */ 0xdc, 0x07, 0x00, 0x00, /* opCode (2012: OP_COMPRESSED) */ \ /* */ \ /* */ /* OP_COMPRESSED fields (9 bytes) */ \ /* 16 */ 0xdd, 0x07, 0x00, 0x00, /* originalOpcode (2013: OP_MSG) */ \ - /* 20 */ 0x12, 0x00, 0x00, 0x00, /* uncompressedSize (18) */ \ + /* 20 */ 0x14, 0x00, 0x00, 0x00, /* uncompressedSize (20) */ \ /* 24 */ 0x00, /* compressorId (0: noop) */ \ /* */ \ - /* */ /* compressedMessage (18 bytes) */ \ + /* */ /* compressedMessage (20 bytes) */ \ /* 25 */ 0x00, 0x00, 0x00, 0x00, /* flagBits (MONGOC_OP_MSG_FLAG_NONE) */ \ - /* 29 */ 0x00, /* Kind 0: Body */ \ - /* 30 */ 0x0d, 0x00, 0x00, 0x00, /* (13 bytes) { */ \ - /* 34 */ 0x02, /* (string) */ \ - /* 35 */ 0x6f, 0x70, 0x5f, 0x6d, 0x73, 0x67, 0x00, /* 'op_msg' */ \ - /* 42 */ 0x00 /* } */ \ - /* 43 */ + /* 29 */ 0x00, /* Kind 0: Body */ \ + /* 30 */ 0x0f, 0x00, 0x00, 0x00, /* (15 bytes) { */ \ + /* 34 */ 0x10, /* (int32) */ \ + /* 35 */ 0x6b, 0x69, 0x6e, 0x64, 0x00, /* 'kind': */ \ + /* 40 */ 0x00, 0x00, 0x00, 0x00, /* 0 */ \ + /* 44 */ 0x00 /* } */ \ + /* 45 */ // clang-format on // clang-format off @@ -370,7 +371,7 @@ test_rpc_message_from_data_op_compressed_valid (void) MONGOC_OP_CODE_MSG); ASSERT_CMPINT32 ( - mcd_rpc_op_compressed_get_uncompressed_size (rpc), ==, 18); + mcd_rpc_op_compressed_get_uncompressed_size (rpc), ==, 20); ASSERT_CMPUINT (mcd_rpc_op_compressed_get_compressor_id (rpc), ==, @@ -381,7 +382,7 @@ test_rpc_message_from_data_op_compressed_valid (void) ASSERT_CMPSIZE_T ((size_t) (compressed_message - data), ==, 25u); ASSERT_CMPSIZE_T ( - mcd_rpc_op_compressed_get_compressed_message_length (rpc), ==, 18u); + mcd_rpc_op_compressed_get_compressed_message_length (rpc), ==, 20u); mcd_rpc_message_destroy (rpc); } @@ -1410,17 +1411,17 @@ test_rpc_message_from_data_op_compressed_invalid (void) const size_t data_len = sizeof (data) - 2u; // Exclude the extra bytes. // clang-format off - EXPECT_DECR_SUCCESS ( 0u, 0u, 42u); // messageLength (byte 0). + EXPECT_DECR_SUCCESS ( 0u, 0u, 44u); // messageLength (byte 0). EXPECT_DECR_FAILURE ( 1u, 3u, 0u); // messageLength (bytes 1-3): too large. - EXPECT_DECR_SUCCESS ( 4u, 11u, 43u); // requestID, responseTo. + EXPECT_DECR_SUCCESS ( 4u, 11u, 45u); // requestID, responseTo. EXPECT_DECR_FAILURE (12u, 15u, 12u); // opCode: invalid. - EXPECT_DECR_SUCCESS (16u, 42u, 43u); // originalOpcode, uncompressedSize, compressorId, compressedMessage. + EXPECT_DECR_SUCCESS (16u, 42u, 45u); // originalOpcode, uncompressedSize, compressorId, compressedMessage. EXPECT_INCR_FAILURE ( 0u, 3u, 0u); // messageLength: too large. - EXPECT_INCR_SUCCESS ( 4u, 11u, 43u); // requestId, responseTo. + EXPECT_INCR_SUCCESS ( 4u, 11u, 45u); // requestId, responseTo. EXPECT_INCR_IGNORED (12u, 12u, 12u); // opCode (byte 0): parse as OP_MSG. EXPECT_INCR_FAILURE (13u, 15u, 12u); // opCode (byte 1-3): invalid. - EXPECT_INCR_SUCCESS (16u, 42u, 43u); // originalOpcode, uncompressedSize, compressorId, compressedMessage. + EXPECT_INCR_SUCCESS (16u, 42u, 45u); // originalOpcode, uncompressedSize, compressorId, compressedMessage. // clang-format on _test_from_data_input_bounds (data, data_len); @@ -1980,14 +1981,14 @@ test_rpc_message_to_iovecs_op_compressed (void) ASSERT (iovecs); ASSERT_CMPSIZE_T (num_iovecs, ==, 8u); - ASSERT_IOVEC_INT32 (0, 43); // messageLength + ASSERT_IOVEC_INT32 (0, 45); // messageLength ASSERT_IOVEC_INT32 (1, 16909060); // requestID ASSERT_IOVEC_INT32 (2, 84281096); // responseTo ASSERT_IOVEC_INT32 (3, 2012); // opCode ASSERT_IOVEC_INT32 (4, 2013); // originalOpcode - ASSERT_IOVEC_INT32 (5, 18); // uncompressedSize + ASSERT_IOVEC_INT32 (5, 20); // uncompressedSize ASSERT_IOVEC_UINT8 (6, 0u); // compressorId - ASSERT_IOVEC_BYTES (7, 25u, 18u); // compressedMessage + ASSERT_IOVEC_BYTES (7, 25u, 20u); // compressedMessage bson_free (iovecs); mcd_rpc_message_destroy (rpc); @@ -2366,14 +2367,14 @@ test_rpc_message_setters_op_compressed (void) mcd_rpc_message *const rpc = mcd_rpc_message_new (); // clang-format off - ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_message_length (rpc, 43)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_message_length (rpc, 45)); ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_request_id (rpc, 16909060)); ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_response_to (rpc, 84281096)); ASSERT_CMPINT32 ( 4, ==, mcd_rpc_header_set_op_code (rpc, MONGOC_OP_CODE_COMPRESSED)); ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_compressed_set_original_opcode (rpc, MONGOC_OP_CODE_MSG)); - ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_compressed_set_uncompressed_size (rpc, 18)); + ASSERT_CMPINT32 ( 4, ==, mcd_rpc_op_compressed_set_uncompressed_size (rpc, 20)); ASSERT_CMPINT32 ( 1, ==, mcd_rpc_op_compressed_set_compressor_id (rpc, 0)); - ASSERT_CMPINT32 (18, ==, mcd_rpc_op_compressed_set_compressed_message (rpc, data + 25, 18u)); + ASSERT_CMPINT32 (20, ==, mcd_rpc_op_compressed_set_compressed_message (rpc, data + 25, 20u)); // clang-format on size_t num_iovecs; From 4f94ea88303bac01f607ee729a396d9ef4f2f532 Mon Sep 17 00:00:00 2001 From: Ezra Chung Date: Mon, 12 Jun 2023 13:17:42 -0500 Subject: [PATCH 08/14] Fix index typo in OP_MSG Kind 0 test data --- src/libmongoc/tests/test-mcd-rpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/tests/test-mcd-rpc.c b/src/libmongoc/tests/test-mcd-rpc.c index 354825b363c..468bb13ebfd 100644 --- a/src/libmongoc/tests/test-mcd-rpc.c +++ b/src/libmongoc/tests/test-mcd-rpc.c @@ -49,7 +49,7 @@ /* 25 */ 0x10, /* (int32) */ \ /* 26 */ 0x6b, 0x69, 0x6e, 0x64, 0x00, /* 'kind': */ \ /* 31 */ 0x00, 0x00, 0x00, 0x00, /* 0 */ \ - /* 25 */ 0x00, /* } */ \ + /* 35 */ 0x00, /* } */ \ /* */ \ /* */ /* Optional checksum (4 bytes) */ \ /* 36 */ 0x44, 0x33, 0x22, 0x11 /* checksum (287454020) */ \ From f6bdedf3544fabf69f93a707efcd2e3cab348d6f Mon Sep 17 00:00:00 2001 From: Ezra Chung Date: Mon, 12 Jun 2023 13:25:37 -0500 Subject: [PATCH 09/14] Remove mcd_rpc_op_msg_unset_checksum --- src/libmongoc/src/mongoc/mcd-rpc.c | 8 -------- src/libmongoc/src/mongoc/mcd-rpc.h | 6 ------ 2 files changed, 14 deletions(-) diff --git a/src/libmongoc/src/mongoc/mcd-rpc.c b/src/libmongoc/src/mongoc/mcd-rpc.c index e363fc2db34..fdb92f98258 100644 --- a/src/libmongoc/src/mongoc/mcd-rpc.c +++ b/src/libmongoc/src/mongoc/mcd-rpc.c @@ -2173,14 +2173,6 @@ mcd_rpc_op_msg_set_checksum (mcd_rpc_message *rpc, uint32_t checksum) return sizeof (checksum); } -void -mcd_rpc_op_msg_unset_checksum (mcd_rpc_message *rpc) -{ - BSON_ASSERT_PARAM (rpc); - BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); - rpc->op_msg.checksum_set = false; -} - int32_t mcd_rpc_op_reply_get_response_flags (const mcd_rpc_message *rpc) diff --git a/src/libmongoc/src/mongoc/mcd-rpc.h b/src/libmongoc/src/mongoc/mcd-rpc.h index fdb7bad8bcb..9585fbe29dd 100644 --- a/src/libmongoc/src/mongoc/mcd-rpc.h +++ b/src/libmongoc/src/mongoc/mcd-rpc.h @@ -403,12 +403,6 @@ mcd_rpc_op_msg_set_sections_count (mcd_rpc_message *rpc, size_t section_count); int32_t mcd_rpc_op_msg_set_checksum (mcd_rpc_message *rpc, uint32_t checksum); -// Unset the OP_MSG checksum field. -// -// The msgHeader.opCode field MUST equal MONGOC_OP_CODE_MSG. -void -mcd_rpc_op_msg_unset_checksum (mcd_rpc_message *rpc); - // Get the OP_REPLY responseFlags field. // From fd471d6ce61a97e4324b6e8a037da59e0b82abbb Mon Sep 17 00:00:00 2001 From: Ezra Chung Date: Mon, 12 Jun 2023 13:26:54 -0500 Subject: [PATCH 10/14] Add missing not-null assertions for header_iovecs --- src/libmongoc/src/mongoc/mcd-rpc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libmongoc/src/mongoc/mcd-rpc.c b/src/libmongoc/src/mongoc/mcd-rpc.c index fdb92f98258..4563f61a77e 100644 --- a/src/libmongoc/src/mongoc/mcd-rpc.c +++ b/src/libmongoc/src/mongoc/mcd-rpc.c @@ -1188,6 +1188,7 @@ _append_iovec_op_msg (mongoc_iovec_t **iovecs, BSON_ASSERT_PARAM (capacity); BSON_ASSERT_PARAM (count); BSON_ASSERT_PARAM (op_msg); + BSON_ASSERT_PARAM (header_iovecs); size_t section_iovecs = 0u; if (!_count_section_iovecs (op_msg, §ion_iovecs)) { @@ -1278,6 +1279,7 @@ _append_iovec_op_reply (mongoc_iovec_t **iovecs, BSON_ASSERT_PARAM (capacity); BSON_ASSERT_PARAM (count); BSON_ASSERT_PARAM (op_reply); + BSON_ASSERT_PARAM (header_iovecs); _append_iovec_reserve_space_for (iovecs, capacity, header_iovecs, 5u); @@ -1324,6 +1326,7 @@ _append_iovec_op_update (mongoc_iovec_t **iovecs, BSON_ASSERT_PARAM (capacity); BSON_ASSERT_PARAM (count); BSON_ASSERT_PARAM (op_update); + BSON_ASSERT_PARAM (header_iovecs); _append_iovec_reserve_space_for (iovecs, capacity, header_iovecs, 5u); @@ -1373,6 +1376,7 @@ _append_iovec_op_insert (mongoc_iovec_t **iovecs, BSON_ASSERT_PARAM (capacity); BSON_ASSERT_PARAM (count); BSON_ASSERT_PARAM (op_insert); + BSON_ASSERT_PARAM (header_iovecs); _append_iovec_reserve_space_for (iovecs, capacity, header_iovecs, 3u); @@ -1411,6 +1415,7 @@ _append_iovec_op_query (mongoc_iovec_t **iovecs, BSON_ASSERT_PARAM (capacity); BSON_ASSERT_PARAM (count); BSON_ASSERT_PARAM (op_query); + BSON_ASSERT_PARAM (header_iovecs); _append_iovec_reserve_space_for ( iovecs, @@ -1473,6 +1478,7 @@ _append_iovec_op_get_more (mongoc_iovec_t **iovecs, BSON_ASSERT_PARAM (count); BSON_ASSERT_PARAM (capacity); BSON_ASSERT_PARAM (op_get_more); + BSON_ASSERT_PARAM (header_iovecs); _append_iovec_reserve_space_for (iovecs, capacity, header_iovecs, 4u); @@ -1512,6 +1518,7 @@ _append_iovec_op_delete (mongoc_iovec_t **iovecs, BSON_ASSERT_PARAM (capacity); BSON_ASSERT_PARAM (count); BSON_ASSERT_PARAM (op_delete); + BSON_ASSERT_PARAM (header_iovecs); _append_iovec_reserve_space_for (iovecs, capacity, header_iovecs, 4u); @@ -1553,6 +1560,7 @@ _append_iovec_op_kill_cursors (mongoc_iovec_t **iovecs, BSON_ASSERT_PARAM (capacity); BSON_ASSERT_PARAM (count); BSON_ASSERT_PARAM (op_kill_cursors); + BSON_ASSERT_PARAM (header_iovecs); // Store value before conversion to little endian. const int32_t number_of_cursor_ids = op_kill_cursors->number_of_cursor_ids; From 4e416c1ab979f20c8186b8a01b49b202c2d598c7 Mon Sep 17 00:00:00 2001 From: Ezra Chung Date: Mon, 12 Jun 2023 13:27:51 -0500 Subject: [PATCH 11/14] Fix miscount of required OP_QUERY fields in _append_iovec_op_query --- src/libmongoc/src/mongoc/mcd-rpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mcd-rpc.c b/src/libmongoc/src/mongoc/mcd-rpc.c index 4563f61a77e..88d28b6d8d8 100644 --- a/src/libmongoc/src/mongoc/mcd-rpc.c +++ b/src/libmongoc/src/mongoc/mcd-rpc.c @@ -1421,7 +1421,7 @@ _append_iovec_op_query (mongoc_iovec_t **iovecs, iovecs, capacity, header_iovecs, - 6u + (size_t) (!!op_query->return_fields_selector)); + 5u + (size_t) (!!op_query->return_fields_selector)); if (!_append_iovec_int32_t (*iovecs, capacity, count, &op_query->flags)) { return false; From c045147caa434e3ca4b2c2b5fac4c762004a430e Mon Sep 17 00:00:00 2001 From: Ezra Chung Date: Mon, 12 Jun 2023 13:42:52 -0500 Subject: [PATCH 12/14] Set to given value in mcd_rpc_message_set_length, not add --- src/libmongoc/src/mongoc/mcd-rpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mcd-rpc.c b/src/libmongoc/src/mongoc/mcd-rpc.c index 88d28b6d8d8..16465201e47 100644 --- a/src/libmongoc/src/mongoc/mcd-rpc.c +++ b/src/libmongoc/src/mongoc/mcd-rpc.c @@ -1806,7 +1806,7 @@ void mcd_rpc_message_set_length (mcd_rpc_message *rpc, int32_t value) { BSON_ASSERT_PARAM (rpc); - rpc->msg_header.message_length += value; + rpc->msg_header.message_length = value; } int32_t From be2ad125192fbd499f39a75feb5e4e0ad59accff Mon Sep 17 00:00:00 2001 From: Ezra Chung Date: Tue, 13 Jun 2023 12:59:58 -0500 Subject: [PATCH 13/14] Add missing note guaranteeing iovec data layout --- src/libmongoc/src/mongoc/mcd-rpc.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libmongoc/src/mongoc/mcd-rpc.h b/src/libmongoc/src/mongoc/mcd-rpc.h index 9585fbe29dd..3b9815adf15 100644 --- a/src/libmongoc/src/mongoc/mcd-rpc.h +++ b/src/libmongoc/src/mongoc/mcd-rpc.h @@ -111,6 +111,9 @@ mcd_rpc_message_from_data_in_place (mcd_rpc_message *rpc, // Convert the given RPC message object into an array of iovec structures. The // return value must be freed by `bson_free`. // +// The data layout of the iovec structures is consistent with the definition of +// `mongoc_iovec_t` as defined in ``. +// // rpc: a valid RPC message object whose fields are in native endian. // length: if not `NULL`, `*length` is set to the number of iovec structures in // the array. From afb3790b69916efa73f866902ef7b4e4768ac36b Mon Sep 17 00:00:00 2001 From: Ezra Chung Date: Tue, 13 Jun 2023 13:00:13 -0500 Subject: [PATCH 14/14] Document and assert RPC message iovecs state --- src/libmongoc/src/mongoc/mcd-rpc.c | 202 ++++++++++++++++------------- src/libmongoc/src/mongoc/mcd-rpc.h | 17 ++- 2 files changed, 124 insertions(+), 95 deletions(-) diff --git a/src/libmongoc/src/mongoc/mcd-rpc.c b/src/libmongoc/src/mongoc/mcd-rpc.c index 16465201e47..446e4ef8129 100644 --- a/src/libmongoc/src/mongoc/mcd-rpc.c +++ b/src/libmongoc/src/mongoc/mcd-rpc.c @@ -30,6 +30,7 @@ struct _mcd_rpc_message_header { int32_t request_id; int32_t response_to; int32_t op_code; + bool is_in_iovecs_state; // Not part of actual message. }; struct _mcd_rpc_op_compressed { @@ -161,6 +162,18 @@ union _mcd_rpc_message { // invalid or malformed input. #define MONGOC_RPC_MINIMUM_BSON_LENGTH INT32_C (5) +// To avoid unexpected behavior on big endian targets after +// `mcd_rpc_message_to_iovecs` due to fields being converted to little endian, +// forbid use of accessors unless the RPC message has been reset to an +// initialized state by asserting `!is_in_iovecs_state` even on little endian +// targets. +#define ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS \ + if (1) { \ + BSON_ASSERT_PARAM (rpc); \ + BSON_ASSERT (!rpc->msg_header.is_in_iovecs_state); \ + } else \ + (void) 0 + static int32_t _int32_from_le (const void *data) @@ -896,7 +909,7 @@ mcd_rpc_message_from_data_in_place (mcd_rpc_message *rpc, size_t length, const void **data_end) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT_PARAM (data); BSON_ASSERT (data_end || true); @@ -1601,7 +1614,7 @@ _append_iovec_op_kill_cursors (mongoc_iovec_t **iovecs, void * mcd_rpc_message_to_iovecs (mcd_rpc_message *rpc, size_t *count) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT_PARAM (count); const int32_t op_code = rpc->msg_header.op_code; @@ -1623,6 +1636,11 @@ mcd_rpc_message_to_iovecs (mcd_rpc_message *rpc, size_t *count) mongoc_iovec_t *iovecs = NULL; mongoc_iovec_t *ret = NULL; + // Fields may be converted to little endian even on failure, so consider the + // RPC object to be in an iovecs state from this point forward regardless of + // success or failure. + rpc->msg_header.is_in_iovecs_state = true; + switch (op_code) { case MONGOC_OP_CODE_COMPRESSED: if (!_append_iovec_op_compressed ( @@ -1712,6 +1730,8 @@ mcd_rpc_message_new (void) static int32_t _mcd_rpc_header_get_op_code_maybe_le (const mcd_rpc_message *rpc) { + BSON_ASSERT_PARAM (rpc); + int32_t op_code = rpc->msg_header.op_code; // May already be in native endian. @@ -1805,42 +1825,43 @@ mcd_rpc_message_reset (mcd_rpc_message *rpc) void mcd_rpc_message_set_length (mcd_rpc_message *rpc, int32_t value) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; rpc->msg_header.message_length = value; } int32_t mcd_rpc_header_get_message_length (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; return rpc->msg_header.message_length; } int32_t mcd_rpc_header_get_request_id (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; return rpc->msg_header.request_id; } int32_t mcd_rpc_header_get_response_to (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; return rpc->msg_header.response_to; } int32_t mcd_rpc_header_get_op_code (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + BSON_ASSERT_PARAM (rpc); // Permit read access even if the RPC message object + // is in an iovecs state. return rpc->msg_header.op_code; } int32_t mcd_rpc_header_set_message_length (mcd_rpc_message *rpc, int32_t message_length) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; rpc->msg_header.message_length = message_length; return sizeof (message_length); } @@ -1848,7 +1869,7 @@ mcd_rpc_header_set_message_length (mcd_rpc_message *rpc, int32_t message_length) int32_t mcd_rpc_header_set_request_id (mcd_rpc_message *rpc, int32_t request_id) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; rpc->msg_header.request_id = request_id; return sizeof (request_id); } @@ -1856,7 +1877,7 @@ mcd_rpc_header_set_request_id (mcd_rpc_message *rpc, int32_t request_id) int32_t mcd_rpc_header_set_response_to (mcd_rpc_message *rpc, int32_t response_to) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; rpc->msg_header.response_to = response_to; return sizeof (response_to); } @@ -1864,7 +1885,7 @@ mcd_rpc_header_set_response_to (mcd_rpc_message *rpc, int32_t response_to) int32_t mcd_rpc_header_set_op_code (mcd_rpc_message *rpc, int32_t op_code) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; _mcd_rpc_message_free_owners (rpc); @@ -1876,7 +1897,8 @@ mcd_rpc_header_set_op_code (mcd_rpc_message *rpc, int32_t op_code) int32_t mcd_rpc_op_compressed_get_original_opcode (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + BSON_ASSERT_PARAM (rpc); // Permit read access even if the RPC message object + // is in an iovecs state. BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); return rpc->op_compressed.original_opcode; } @@ -1884,7 +1906,7 @@ mcd_rpc_op_compressed_get_original_opcode (const mcd_rpc_message *rpc) int32_t mcd_rpc_op_compressed_get_uncompressed_size (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); return rpc->op_compressed.uncompressed_size; } @@ -1892,7 +1914,7 @@ mcd_rpc_op_compressed_get_uncompressed_size (const mcd_rpc_message *rpc) uint8_t mcd_rpc_op_compressed_get_compressor_id (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); return rpc->op_compressed.compressor_id; } @@ -1900,7 +1922,7 @@ mcd_rpc_op_compressed_get_compressor_id (const mcd_rpc_message *rpc) const void * mcd_rpc_op_compressed_get_compressed_message (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); return rpc->op_compressed.compressed_message; } @@ -1908,7 +1930,7 @@ mcd_rpc_op_compressed_get_compressed_message (const mcd_rpc_message *rpc) size_t mcd_rpc_op_compressed_get_compressed_message_length (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); return rpc->op_compressed.compressed_message_len; } @@ -1917,7 +1939,7 @@ int32_t mcd_rpc_op_compressed_set_original_opcode (mcd_rpc_message *rpc, int32_t original_opcode) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); rpc->op_compressed.original_opcode = original_opcode; return sizeof (original_opcode); @@ -1927,7 +1949,7 @@ int32_t mcd_rpc_op_compressed_set_uncompressed_size (mcd_rpc_message *rpc, int32_t uncompressed_size) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); rpc->op_compressed.uncompressed_size = uncompressed_size; return sizeof (uncompressed_size); @@ -1937,7 +1959,7 @@ int32_t mcd_rpc_op_compressed_set_compressor_id (mcd_rpc_message *rpc, uint8_t compressor_id) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); rpc->op_compressed.compressor_id = compressor_id; return sizeof (compressor_id); @@ -1948,7 +1970,7 @@ mcd_rpc_op_compressed_set_compressed_message (mcd_rpc_message *rpc, const void *compressed_message, size_t compressed_message_length) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED); BSON_ASSERT (bson_in_range_unsigned (int32_t, compressed_message_length)); rpc->op_compressed.compressed_message = compressed_message; @@ -1960,7 +1982,7 @@ mcd_rpc_op_compressed_set_compressed_message (mcd_rpc_message *rpc, uint8_t mcd_rpc_op_msg_section_get_kind (const mcd_rpc_message *rpc, size_t index) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); BSON_ASSERT (index < rpc->op_msg.sections_count); return rpc->op_msg.sections[index].kind; @@ -1969,7 +1991,7 @@ mcd_rpc_op_msg_section_get_kind (const mcd_rpc_message *rpc, size_t index) int32_t mcd_rpc_op_msg_section_get_length (const mcd_rpc_message *rpc, size_t index) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); BSON_ASSERT (index < rpc->op_msg.sections_count); @@ -1992,7 +2014,7 @@ mcd_rpc_op_msg_section_get_length (const mcd_rpc_message *rpc, size_t index) const char * mcd_rpc_op_msg_section_get_identifier (const mcd_rpc_message *rpc, size_t index) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); BSON_ASSERT (index < rpc->op_msg.sections_count); @@ -2004,7 +2026,7 @@ mcd_rpc_op_msg_section_get_identifier (const mcd_rpc_message *rpc, size_t index) const void * mcd_rpc_op_msg_section_get_body (const mcd_rpc_message *rpc, size_t index) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); BSON_ASSERT (index < rpc->op_msg.sections_count); @@ -2017,7 +2039,7 @@ const void * mcd_rpc_op_msg_section_get_document_sequence (const mcd_rpc_message *rpc, size_t index) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); BSON_ASSERT (index < rpc->op_msg.sections_count); @@ -2030,7 +2052,7 @@ size_t mcd_rpc_op_msg_section_get_document_sequence_length (const mcd_rpc_message *rpc, size_t index) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); BSON_ASSERT (index < rpc->op_msg.sections_count); @@ -2044,7 +2066,7 @@ mcd_rpc_op_msg_section_set_kind (mcd_rpc_message *rpc, size_t index, uint8_t kind) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); BSON_ASSERT (index < rpc->op_msg.sections_count); rpc->op_msg.sections[index].kind = kind; @@ -2056,7 +2078,7 @@ mcd_rpc_op_msg_section_set_length (mcd_rpc_message *rpc, size_t index, int32_t length) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); BSON_ASSERT (index < rpc->op_msg.sections_count); BSON_ASSERT (rpc->op_msg.sections[index].kind == 1); @@ -2069,7 +2091,7 @@ mcd_rpc_op_msg_section_set_identifier (mcd_rpc_message *rpc, size_t index, const char *identifier) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); BSON_ASSERT (index < rpc->op_msg.sections_count); BSON_ASSERT (rpc->op_msg.sections[index].kind == 1); @@ -2090,7 +2112,7 @@ mcd_rpc_op_msg_section_set_body (mcd_rpc_message *rpc, size_t index, const void *body) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); BSON_ASSERT (index < rpc->op_msg.sections_count); BSON_ASSERT (rpc->op_msg.sections[index].kind == 0); @@ -2109,7 +2131,7 @@ mcd_rpc_op_msg_section_set_document_sequence (mcd_rpc_message *rpc, const void *document_sequence, size_t document_sequence_length) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); BSON_ASSERT (index < rpc->op_msg.sections_count); BSON_ASSERT (rpc->op_msg.sections[index].kind == 1); @@ -2130,7 +2152,7 @@ mcd_rpc_op_msg_section_set_document_sequence (mcd_rpc_message *rpc, uint32_t mcd_rpc_op_msg_get_flag_bits (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); return rpc->op_msg.flag_bits; } @@ -2138,7 +2160,7 @@ mcd_rpc_op_msg_get_flag_bits (const mcd_rpc_message *rpc) size_t mcd_rpc_op_msg_get_sections_count (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); return rpc->op_msg.sections_count; } @@ -2146,7 +2168,7 @@ mcd_rpc_op_msg_get_sections_count (const mcd_rpc_message *rpc) const uint32_t * mcd_rpc_op_msg_get_checksum (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); return rpc->op_msg.checksum_set ? &rpc->op_msg.checksum : NULL; } @@ -2154,7 +2176,7 @@ mcd_rpc_op_msg_get_checksum (const mcd_rpc_message *rpc) int32_t mcd_rpc_op_msg_set_flag_bits (mcd_rpc_message *rpc, uint32_t flag_bits) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); rpc->op_msg.flag_bits = flag_bits; return sizeof (flag_bits); @@ -2163,7 +2185,7 @@ mcd_rpc_op_msg_set_flag_bits (mcd_rpc_message *rpc, uint32_t flag_bits) void mcd_rpc_op_msg_set_sections_count (mcd_rpc_message *rpc, size_t section_count) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); rpc->op_msg.sections = bson_realloc ( @@ -2174,7 +2196,7 @@ mcd_rpc_op_msg_set_sections_count (mcd_rpc_message *rpc, size_t section_count) int32_t mcd_rpc_op_msg_set_checksum (mcd_rpc_message *rpc, uint32_t checksum) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_MSG); rpc->op_msg.checksum = checksum; rpc->op_msg.checksum_set = true; @@ -2185,7 +2207,7 @@ mcd_rpc_op_msg_set_checksum (mcd_rpc_message *rpc, uint32_t checksum) int32_t mcd_rpc_op_reply_get_response_flags (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY); return rpc->op_reply.response_flags; } @@ -2193,7 +2215,7 @@ mcd_rpc_op_reply_get_response_flags (const mcd_rpc_message *rpc) int64_t mcd_rpc_op_reply_get_cursor_id (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY); return rpc->op_reply.cursor_id; } @@ -2201,7 +2223,7 @@ mcd_rpc_op_reply_get_cursor_id (const mcd_rpc_message *rpc) int32_t mcd_rpc_op_reply_get_starting_from (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY); return rpc->op_reply.starting_from; } @@ -2209,7 +2231,7 @@ mcd_rpc_op_reply_get_starting_from (const mcd_rpc_message *rpc) int32_t mcd_rpc_op_reply_get_number_returned (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY); return rpc->op_reply.number_returned; } @@ -2217,7 +2239,7 @@ mcd_rpc_op_reply_get_number_returned (const mcd_rpc_message *rpc) const void * mcd_rpc_op_reply_get_documents (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY); return rpc->op_reply.documents_len > 0 ? rpc->op_reply.documents : NULL; } @@ -2225,7 +2247,7 @@ mcd_rpc_op_reply_get_documents (const mcd_rpc_message *rpc) size_t mcd_rpc_op_reply_get_documents_len (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY); return rpc->op_reply.documents_len; } @@ -2234,7 +2256,7 @@ int32_t mcd_rpc_op_reply_set_response_flags (mcd_rpc_message *rpc, int32_t response_flags) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; rpc->op_reply.response_flags = response_flags; return sizeof (response_flags); } @@ -2242,7 +2264,7 @@ mcd_rpc_op_reply_set_response_flags (mcd_rpc_message *rpc, int32_t mcd_rpc_op_reply_set_cursor_id (mcd_rpc_message *rpc, int64_t cursor_id) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; rpc->op_reply.cursor_id = cursor_id; return sizeof (cursor_id); } @@ -2250,7 +2272,7 @@ mcd_rpc_op_reply_set_cursor_id (mcd_rpc_message *rpc, int64_t cursor_id) int32_t mcd_rpc_op_reply_set_starting_from (mcd_rpc_message *rpc, int32_t starting_from) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; rpc->op_reply.starting_from = starting_from; return sizeof (starting_from); } @@ -2259,7 +2281,7 @@ int32_t mcd_rpc_op_reply_set_number_returned (mcd_rpc_message *rpc, int32_t number_returned) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; rpc->op_reply.number_returned = number_returned; return sizeof (number_returned); } @@ -2269,7 +2291,7 @@ mcd_rpc_op_reply_set_documents (mcd_rpc_message *rpc, const void *documents, size_t documents_len) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; rpc->op_reply.documents = documents; rpc->op_reply.documents_len = documents_len; @@ -2282,7 +2304,7 @@ mcd_rpc_op_reply_set_documents (mcd_rpc_message *rpc, const char * mcd_rpc_op_update_get_full_collection_name (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_UPDATE); return rpc->op_update.full_collection_name; } @@ -2290,7 +2312,7 @@ mcd_rpc_op_update_get_full_collection_name (const mcd_rpc_message *rpc) int32_t mcd_rpc_op_update_get_flags (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_UPDATE); return rpc->op_update.flags; } @@ -2298,7 +2320,7 @@ mcd_rpc_op_update_get_flags (const mcd_rpc_message *rpc) const void * mcd_rpc_op_update_get_selector (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_UPDATE); return rpc->op_update.selector; } @@ -2306,7 +2328,7 @@ mcd_rpc_op_update_get_selector (const mcd_rpc_message *rpc) const void * mcd_rpc_op_update_get_update (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_UPDATE); return rpc->op_update.update; } @@ -2315,7 +2337,7 @@ int32_t mcd_rpc_op_update_set_full_collection_name (mcd_rpc_message *rpc, const char *full_collection_name) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; const size_t length = full_collection_name ? strlen (full_collection_name) + 1u : 0u; @@ -2331,7 +2353,7 @@ mcd_rpc_op_update_set_full_collection_name (mcd_rpc_message *rpc, int32_t mcd_rpc_op_update_set_flags (mcd_rpc_message *rpc, int32_t flags) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; rpc->op_update.flags = flags; return sizeof (flags); } @@ -2339,7 +2361,7 @@ mcd_rpc_op_update_set_flags (mcd_rpc_message *rpc, int32_t flags) int32_t mcd_rpc_op_update_set_selector (mcd_rpc_message *rpc, const void *selector) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; rpc->op_update.selector = selector; return selector ? _int32_from_le (selector) : 0; } @@ -2347,7 +2369,7 @@ mcd_rpc_op_update_set_selector (mcd_rpc_message *rpc, const void *selector) int32_t mcd_rpc_op_update_set_update (mcd_rpc_message *rpc, const void *update) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; rpc->op_update.update = update; return update ? _int32_from_le (update) : 0; } @@ -2356,7 +2378,7 @@ mcd_rpc_op_update_set_update (mcd_rpc_message *rpc, const void *update) int32_t mcd_rpc_op_insert_get_flags (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT); return rpc->op_insert.flags; } @@ -2364,7 +2386,7 @@ mcd_rpc_op_insert_get_flags (const mcd_rpc_message *rpc) const char * mcd_rpc_op_insert_get_full_collection_name (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT); return rpc->op_insert.full_collection_name; } @@ -2372,7 +2394,7 @@ mcd_rpc_op_insert_get_full_collection_name (const mcd_rpc_message *rpc) const void * mcd_rpc_op_insert_get_documents (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT); return rpc->op_insert.documents; } @@ -2380,7 +2402,7 @@ mcd_rpc_op_insert_get_documents (const mcd_rpc_message *rpc) size_t mcd_rpc_op_insert_get_documents_len (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT); return rpc->op_insert.documents_len; } @@ -2388,7 +2410,7 @@ mcd_rpc_op_insert_get_documents_len (const mcd_rpc_message *rpc) int32_t mcd_rpc_op_insert_set_flags (mcd_rpc_message *rpc, int32_t flags) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT); rpc->op_insert.flags = flags; return sizeof (flags); @@ -2398,7 +2420,7 @@ int32_t mcd_rpc_op_insert_set_full_collection_name (mcd_rpc_message *rpc, const char *full_collection_name) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT); const size_t length = @@ -2416,7 +2438,7 @@ mcd_rpc_op_insert_set_documents (mcd_rpc_message *rpc, const void *documents, size_t documents_len) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT); rpc->op_insert.documents = documents; @@ -2430,7 +2452,7 @@ mcd_rpc_op_insert_set_documents (mcd_rpc_message *rpc, int32_t mcd_rpc_op_query_get_flags (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); return rpc->op_query.flags; } @@ -2438,7 +2460,7 @@ mcd_rpc_op_query_get_flags (const mcd_rpc_message *rpc) const char * mcd_rpc_op_query_get_full_collection_name (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); return rpc->op_query.full_collection_name; } @@ -2446,7 +2468,7 @@ mcd_rpc_op_query_get_full_collection_name (const mcd_rpc_message *rpc) int32_t mcd_rpc_op_query_get_number_to_skip (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); return rpc->op_query.number_to_skip; } @@ -2454,7 +2476,7 @@ mcd_rpc_op_query_get_number_to_skip (const mcd_rpc_message *rpc) int32_t mcd_rpc_op_query_get_number_to_return (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); return rpc->op_query.number_to_return; } @@ -2462,7 +2484,7 @@ mcd_rpc_op_query_get_number_to_return (const mcd_rpc_message *rpc) const void * mcd_rpc_op_query_get_query (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); return rpc->op_query.query; } @@ -2470,7 +2492,7 @@ mcd_rpc_op_query_get_query (const mcd_rpc_message *rpc) const void * mcd_rpc_op_query_get_return_fields_selector (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); return rpc->op_query.return_fields_selector; } @@ -2478,7 +2500,7 @@ mcd_rpc_op_query_get_return_fields_selector (const mcd_rpc_message *rpc) int32_t mcd_rpc_op_query_set_flags (mcd_rpc_message *rpc, int32_t flags) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); rpc->op_query.flags = flags; return sizeof (flags); @@ -2488,7 +2510,7 @@ int32_t mcd_rpc_op_query_set_full_collection_name (mcd_rpc_message *rpc, const char *full_collection_name) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); const size_t length = @@ -2505,7 +2527,7 @@ int32_t mcd_rpc_op_query_set_number_to_skip (mcd_rpc_message *rpc, int32_t number_to_skip) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); rpc->op_query.number_to_skip = number_to_skip; return sizeof (number_to_skip); @@ -2515,7 +2537,7 @@ int32_t mcd_rpc_op_query_set_number_to_return (mcd_rpc_message *rpc, int32_t number_to_return) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); rpc->op_query.number_to_return = number_to_return; return sizeof (number_to_return); @@ -2524,7 +2546,7 @@ mcd_rpc_op_query_set_number_to_return (mcd_rpc_message *rpc, int32_t mcd_rpc_op_query_set_query (mcd_rpc_message *rpc, const void *query) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); rpc->op_query.query = query; return _int32_from_le (query); @@ -2534,7 +2556,7 @@ int32_t mcd_rpc_op_query_set_return_fields_selector (mcd_rpc_message *rpc, const void *return_fields_selector) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY); rpc->op_query.return_fields_selector = return_fields_selector; return return_fields_selector ? _int32_from_le (return_fields_selector) : 0; @@ -2544,7 +2566,7 @@ mcd_rpc_op_query_set_return_fields_selector (mcd_rpc_message *rpc, const char * mcd_rpc_op_get_more_get_full_collection_name (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE); return rpc->op_get_more.full_collection_name; } @@ -2552,7 +2574,7 @@ mcd_rpc_op_get_more_get_full_collection_name (const mcd_rpc_message *rpc) int32_t mcd_rpc_op_get_more_get_number_to_return (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE); return rpc->op_get_more.number_to_return; } @@ -2560,7 +2582,7 @@ mcd_rpc_op_get_more_get_number_to_return (const mcd_rpc_message *rpc) int64_t mcd_rpc_op_get_more_get_cursor_id (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE); return rpc->op_get_more.cursor_id; } @@ -2569,7 +2591,7 @@ int32_t mcd_rpc_op_get_more_set_full_collection_name (mcd_rpc_message *rpc, const char *full_collection_name) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE); const size_t length = @@ -2586,7 +2608,7 @@ int32_t mcd_rpc_op_get_more_set_number_to_return (mcd_rpc_message *rpc, int32_t number_to_return) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE); rpc->op_get_more.number_to_return = number_to_return; return sizeof (number_to_return); @@ -2595,7 +2617,7 @@ mcd_rpc_op_get_more_set_number_to_return (mcd_rpc_message *rpc, int32_t mcd_rpc_op_get_more_set_cursor_id (mcd_rpc_message *rpc, int64_t cursor_id) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE); rpc->op_get_more.cursor_id = cursor_id; return sizeof (cursor_id); @@ -2605,7 +2627,7 @@ mcd_rpc_op_get_more_set_cursor_id (mcd_rpc_message *rpc, int64_t cursor_id) const char * mcd_rpc_op_delete_get_full_collection_name (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE); return rpc->op_delete.full_collection_name; } @@ -2613,7 +2635,7 @@ mcd_rpc_op_delete_get_full_collection_name (const mcd_rpc_message *rpc) int32_t mcd_rpc_op_delete_get_flags (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; return rpc->op_delete.flags; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE); } @@ -2621,7 +2643,7 @@ mcd_rpc_op_delete_get_flags (const mcd_rpc_message *rpc) const void * mcd_rpc_op_delete_get_selector (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE); return rpc->op_delete.selector; } @@ -2630,7 +2652,7 @@ int32_t mcd_rpc_op_delete_set_full_collection_name (mcd_rpc_message *rpc, const char *full_collection_name) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE); const size_t length = @@ -2646,7 +2668,7 @@ mcd_rpc_op_delete_set_full_collection_name (mcd_rpc_message *rpc, int32_t mcd_rpc_op_delete_set_flags (mcd_rpc_message *rpc, int32_t flags) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE); rpc->op_delete.flags = flags; return sizeof (flags); @@ -2655,7 +2677,7 @@ mcd_rpc_op_delete_set_flags (mcd_rpc_message *rpc, int32_t flags) int32_t mcd_rpc_op_delete_set_selector (mcd_rpc_message *rpc, const void *selector) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE); rpc->op_delete.selector = selector; return selector ? _int32_from_le (selector) : 0; @@ -2665,7 +2687,7 @@ mcd_rpc_op_delete_set_selector (mcd_rpc_message *rpc, const void *selector) int32_t mcd_rpc_op_kill_cursors_get_number_of_cursor_ids (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_KILL_CURSORS); return rpc->op_kill_cursors.number_of_cursor_ids; } @@ -2673,7 +2695,7 @@ mcd_rpc_op_kill_cursors_get_number_of_cursor_ids (const mcd_rpc_message *rpc) const int64_t * mcd_rpc_op_kill_cursors_get_cursor_ids (const mcd_rpc_message *rpc) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_KILL_CURSORS); return rpc->op_kill_cursors.number_of_cursor_ids > 0 ? rpc->op_kill_cursors.cursor_ids @@ -2685,7 +2707,7 @@ mcd_rpc_op_kill_cursors_set_cursor_ids (mcd_rpc_message *rpc, const int64_t *cursor_ids, int32_t number_of_cursor_ids) { - BSON_ASSERT_PARAM (rpc); + ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS; BSON_ASSERT (rpc->msg_header.op_code == MONGOC_OP_CODE_KILL_CURSORS); BSON_ASSERT (bson_cmp_less_su (number_of_cursor_ids, (size_t) INT32_MAX / sizeof (int64_t))); diff --git a/src/libmongoc/src/mongoc/mcd-rpc.h b/src/libmongoc/src/mongoc/mcd-rpc.h index 3b9815adf15..ba66ea7a45d 100644 --- a/src/libmongoc/src/mongoc/mcd-rpc.h +++ b/src/libmongoc/src/mongoc/mcd-rpc.h @@ -108,8 +108,14 @@ mcd_rpc_message_from_data_in_place (mcd_rpc_message *rpc, size_t length, const void **data_end); -// Convert the given RPC message object into an array of iovec structures. The -// return value must be freed by `bson_free`. +// Convert the given RPC message object into an array of iovec structures, +// putting the RPC message object in an iovecs state. The return value must be +// freed by `bson_free`. +// +// Unless otherwise specified, it is undefined behavior to access any RPC +// message field when the object is in an iovecs state. Use +// `mcd_rpc_message_reset` to return the object to an initialized state before +// further reuse. // // The data layout of the iovec structures is consistent with the definition of // `mongoc_iovec_t` as defined in ``. @@ -118,9 +124,6 @@ mcd_rpc_message_from_data_in_place (mcd_rpc_message *rpc, // length: if not `NULL`, `*length` is set to the number of iovec structures in // the array. // -// Note: this function converts the fields of the RPC message object to little -// endian. -// // Returns the array of iovec structures on success. Returns `NULL` on failure. void * mcd_rpc_message_to_iovecs (mcd_rpc_message *rpc, size_t *count); @@ -156,6 +159,8 @@ int32_t mcd_rpc_header_get_response_to (const mcd_rpc_message *rpc); // Get the msgHeader.opCode field. +// +// This function may be called even if the RPC message is in an iovecs state. int32_t mcd_rpc_header_get_op_code (const mcd_rpc_message *rpc); @@ -190,6 +195,8 @@ mcd_rpc_header_set_op_code (mcd_rpc_message *rpc, int32_t op_code); // Get the OP_COMPRESSED originalOpcode field. // // The msgHeader.opCode field MUST equal MONGOC_OP_CODE_COMPRESSED. +// +// This function may be called even if the RPC message is in an iovecs state. int32_t mcd_rpc_op_compressed_get_original_opcode (const mcd_rpc_message *rpc);