From 8339373aa13c33ff41e2634202d70721ea697aad Mon Sep 17 00:00:00 2001 From: Attila Feher Date: Fri, 17 May 2024 15:49:49 -0400 Subject: [PATCH] Limit maximum size of accepted ISO 8601 data. (#4738) --- groups/bal/balber/balber_berdecoder.t.cpp | 448 +++++++++++++++++- groups/bal/balber/balber_berencoder.t.cpp | 269 +++++------ groups/bal/balber/balber_berutil.h | 8 + groups/bal/balber/balber_berutil.t.cpp | 546 +++++++++++++++++++++- 4 files changed, 1131 insertions(+), 140 deletions(-) diff --git a/groups/bal/balber/balber_berdecoder.t.cpp b/groups/bal/balber/balber_berdecoder.t.cpp index 587b19467e..b664a15ff4 100644 --- a/groups/bal/balber/balber_berdecoder.t.cpp +++ b/groups/bal/balber/balber_berdecoder.t.cpp @@ -111,7 +111,9 @@ namespace test = BloombergLP::s_baltst; // [19] DECODE COMPLEX TYPES WITH DEFAULT ELEMENTS // [20] DECODE SEQUENCES OF MAXIMUM SIZE // [21] DECODE INTS AS ENUMS AND VICE VERSA -// [22] USAGE EXAMPLE +// [22] DECODE DATE/TIME WITH LENGTH ANOMALIES +// [23] USAGE EXAMPLE +// // [-1] PERFORMANCE TEST // ============================================================================ @@ -356,6 +358,201 @@ void printDiagnostic(balber::BerDecoder & decoder) } } +template +void verifyDateTypesLengthAnomalies(bsl::size_t MAX_LEN, + bsl::size_t ISOSTR_LEN, + int LINE, + const DATE_TYPE& EXPECTED, + const bsl::string_view& encoded) +{ + const bsl::size_t LEN_POS = encoded.length() - ISOSTR_LEN - 1; + + balber::BerDecoder decoder; + int rc; + + // Verify that it decodes as is + { + bdlsb::FixedMemInStreamBuf isb(encoded.data(), encoded.length()); + DATE_TYPE value; + rc = decoder.decode(&isb, &value); + ASSERTV(LINE, rc, 0 == rc); + + // Verify that we get the expected value + ASSERTV(LINE, EXPECTED, value, EXPECTED == value); + } + + // Modifiable input to change encoded length + bsl::string encAsStr(encoded.data(), encoded.length()); + + // Verify that we are writing the length octet to the proper position + { + // Write the same length + encAsStr[LEN_POS] = static_cast(ISOSTR_LEN); + + // Verify that decoding still succeeds + bdlsb::FixedMemInStreamBuf isb(encAsStr.data(), encAsStr.length()); + DATE_TYPE value; + rc = decoder.decode(&isb, &value); + ASSERTV(LINE, rc, 0 == rc); + + // Verify that we get the expected value + ASSERTV(LINE, EXPECTED, value, EXPECTED == value); + } + + // Verify that length is obeyed + { + // Write reduced length + encAsStr[LEN_POS] = static_cast(ISOSTR_LEN - 1); + + // Verify that decoding now fails + bdlsb::FixedMemInStreamBuf isb(encAsStr.data(), encAsStr.length()); + DATE_TYPE value; + rc = decoder.decode(&isb, &value); + ASSERTV(LINE, rc, 0 != rc); + } + + // Verify that length longer than data leads to an error + { + // Write increased length + encAsStr[LEN_POS] = static_cast(ISOSTR_LEN + 1); + + // Verify that decoding now fails + bdlsb::FixedMemInStreamBuf isb(encAsStr.data(), encAsStr.length()); + DATE_TYPE value; + rc = decoder.decode(&isb, &value); + ASSERTV(LINE, rc, 0 != rc); + } + + // Verify that length longer than maximum won't read data + { + // Write absurd length + encAsStr[LEN_POS] = static_cast(MAX_LEN + 1); + + bdlsb::FixedMemInStreamBuf isb(encAsStr.data(), encAsStr.length()); + + // Double-check that decoding fails + DATE_TYPE value; + rc = decoder.decode(&isb, &value); + ASSERTV(LINE, rc, 0 != rc); + + // Verify that decoding did not read the data part at all + ASSERTV(LINE, ISOSTR_LEN, isb.length(), ISOSTR_LEN == isb.length()); + } + + // Verify that with length exactly at maximum we read the data + { + encAsStr[LEN_POS] = static_cast(MAX_LEN); + + bdlsb::FixedMemInStreamBuf isb(encAsStr.data(), encAsStr.length()); + + // Decoding fails because we don't have enough data + DATE_TYPE value; + rc = decoder.decode(&isb, &value); + ASSERTV(LINE, rc, 0 != rc); + + // Verify that decoding read all the available data + ASSERTV(LINE, isb.length(), 0 == isb.length()); + } +} + +template +void verifyTimeTypesLengthAnomalies(bsl::size_t MAX_LEN, + bsl::size_t ISOSTR_LEN, + bsl::size_t INVALIDATE_LEN, + int LINE, + const TIME_TYPE& EXPECTED, + const bsl::string_view& encoded) +{ + const bsl::size_t LEN_POS = encoded.length() - ISOSTR_LEN - 1; + + balber::BerDecoder decoder; + int rc; + + // Verify that it decodes as is + { + bdlsb::FixedMemInStreamBuf isb(encoded.data(), encoded.length()); + TIME_TYPE value; + rc = decoder.decode(&isb, &value); + ASSERTV(LINE, rc, 0 == rc); + + // Verify that we get the expected value + ASSERTV(LINE, EXPECTED, value, EXPECTED == value); + } + + // Modifiable input to change encoded length + bsl::string encAsStr(encoded.data(), encoded.length()); + + // Verify that we are writing the length octet to the proper position + { + // Write the same length + encAsStr[LEN_POS] = static_cast(ISOSTR_LEN); + + // Verify that decoding still succeeds + bdlsb::FixedMemInStreamBuf isb(encAsStr.data(), encAsStr.length()); + TIME_TYPE value; + rc = decoder.decode(&isb, &value); + ASSERTV(LINE, rc, 0 == rc); + + // Verify that we get the expected value + ASSERTV(LINE, EXPECTED, value, EXPECTED == value); + } + + // Verify that length is obeyed + { + // Write reduced length + encAsStr[LEN_POS] = static_cast(ISOSTR_LEN - INVALIDATE_LEN); + + // Verify that decoding now fails + bdlsb::FixedMemInStreamBuf isb(encAsStr.data(), encAsStr.length()); + TIME_TYPE value; + rc = decoder.decode(&isb, &value); + ASSERTV(LINE, rc, 0 != rc); + } + + // Verify that length longer than data leads to an error + { + // Write increased length + encAsStr[LEN_POS] = static_cast(ISOSTR_LEN + 1); + + // Verify that decoding now fails + bdlsb::FixedMemInStreamBuf isb(encAsStr.data(), encAsStr.length()); + TIME_TYPE value; + rc = decoder.decode(&isb, &value); + ASSERTV(LINE, rc, 0 != rc); + } + + // Verify that length longer than maximum won't read data + { + // Write absurd length + encAsStr[LEN_POS] = static_cast(MAX_LEN + 1); + + bdlsb::FixedMemInStreamBuf isb(encAsStr.data(), encAsStr.length()); + + // Double-check that decoding fails + TIME_TYPE value; + rc = decoder.decode(&isb, &value); + ASSERTV(LINE, rc, 0 != rc); + + // Verify that decoding did not read the data part at all + ASSERTV(LINE, ISOSTR_LEN, isb.length(), ISOSTR_LEN == isb.length()); + } + + // Verify that with length exactly at maximum we read the data + { + encAsStr[LEN_POS] = static_cast(MAX_LEN); + + bdlsb::FixedMemInStreamBuf isb(encAsStr.data(), encAsStr.length()); + + // Decoding fails because we don't have enough data + TIME_TYPE value; + rc = decoder.decode(&isb, &value); + ASSERTV(LINE, rc, 0 != rc); + + // Verify that decoding read all the available data + ASSERTV(LINE, isb.length(), 0 == isb.length()); + } +} + // ============================================================================ // GLOBAL HELPER CLASSES FOR TESTING // ---------------------------------------------------------------------------- @@ -2068,7 +2265,7 @@ int main(int argc, char *argv[]) bsl::cout << "TEST " << __FILE__ << " CASE " << test << bsl::endl;; switch (test) { case 0: // Zero is always the leading case. - case 22: { + case 23: { // -------------------------------------------------------------------- // USAGE EXAMPLE // Extracted from component header file. @@ -2088,11 +2285,256 @@ int main(int argc, char *argv[]) if (verbose) cout << "\nUSAGE EXAMPLE" "\n=============\n"; - usageExample(); if (verbose) cout << "\nEnd of test.\n"; } break; + case 22: { + // -------------------------------------------------------------------- + // DECODE DATE/TIME WITH LENGTH ANOMALIES + // + // Concerns: + //: 1 Length above 127 characters results in an error regardless of the + //: content. + //: + //: 2 Length precisely at 127 characters is decoded as usual. + //: + //: 3 Length beyond available data results in an error regardless of + //: the available content. + // + // Plan: + //: 1 Table based test for each sub-type. + //: + //: 2 For each valid type for a test data ISO 8601 string: + //: 1 Encoded input is generated from the string + //: + //: 2 Encoded input is then manipulated to change the length for + //: concerns that require a length different from the size of + //: available data + //: + //: 3 Decoding return value is verified using assertions + // + // Testing: + // DECODE DATE/TIME WITH LENGTH ANOMALIES + // -------------------------------------------------------------------- + + if (verbose) cout << "\nDECODE DATE/TIME WITH LENGTH ANOMALIES" + "\n======================================\n"; + + // Kept at 126 for "+1" to fit in one byte, BER-encoded + const int MAX_LEN = 126; + + if (verbose) cout << "\t'bdlt::Date'/'DateTz'\n"; + { + // Create the encoder that encodes date as ISO 8601 strings + balber::BerEncoderOptions mOptions; + mOptions.setEncodeDateAndTimeTypesAsBinary(false); + const balber::BerEncoderOptions OPTS = mOptions; + balber::BerEncoder encoder(&OPTS); + + using bdlt::Date; + using bdlt::DateTz; + + static struct { + int d_line; + Date d_date; + int d_offset; + } TEST_DATA[] = { + { L_, Date(2024,5,15), 120 }, + { L_, Date(2042,2,28), 0 }, + }; + + const bsl::size_t TEST_SIZE = sizeof TEST_DATA / sizeof *TEST_DATA; + + for (bsl::size_t ti = 0; ti < TEST_SIZE; ++ti) { + const int LINE = TEST_DATA[ti].d_line; + const Date& DATE = TEST_DATA[ti].d_date; + const int OFFSET = TEST_DATA[ti].d_offset; + + if (veryVeryVerbose) { P_(LINE) P_(DATE) P(OFFSET); } + + { + // Create a complete & valid encoded 'Date' octets + bdlsb::MemOutStreamBuf buff; + int rc = encoder.encode(&buff, DATE); + ASSERTV(rc, 0 == rc); + + const bsl::string_view encoded(buff.data(), buff.length()); + + const bsl::size_t DATE_LEN = 10; // YYYY-MM-DD + verifyDateTypesLengthAnomalies(MAX_LEN, + DATE_LEN, + LINE, + DATE, + encoded); + } + + { + // Create a complete & valid encoded 'DateTz' octets + const DateTz DATETZ(DATE, OFFSET); + + bdlsb::MemOutStreamBuf buff; + int rc = encoder.encode(&buff, DATETZ); + ASSERTV(rc, 0 == rc); + + const bsl::string_view encoded(buff.data(), buff.length()); + + const bsl::size_t DATETZ_LEN = 16; // YYYY-MM-DD+HH:MM + verifyDateTypesLengthAnomalies(MAX_LEN, + DATETZ_LEN, + LINE, + DATETZ, + encoded); + } + } + } + + if (verbose) cout << "\t'bdlt::Time'/'TimeTz'\n"; + { + // Create the encoder that encodes times as ISO 8601 strings + balber::BerEncoderOptions mOptions; + mOptions.setEncodeDateAndTimeTypesAsBinary(false); + const bsl::size_t SEC_FRAC_PREC = 6; + mOptions.setDatetimeFractionalSecondPrecision(SEC_FRAC_PREC); + const balber::BerEncoderOptions OPTS = mOptions; + balber::BerEncoder encoder(&OPTS); + + using bdlt::Time; + using bdlt::TimeTz; + + static struct { + int d_line; + Time d_time; + int d_offset; + } TEST_DATA[] = { + { L_, Time(12,21,42,999,999), 120 }, + { L_, Time(12,21,42,999,999), 0 }, + }; + + const bsl::size_t TEST_SIZE = sizeof TEST_DATA / sizeof *TEST_DATA; + + for (bsl::size_t ti = 0; ti < TEST_SIZE; ++ti) { + const int LINE = TEST_DATA[ti].d_line; + const Time& TIME = TEST_DATA[ti].d_time; + const int OFFSET = TEST_DATA[ti].d_offset; + + if (veryVeryVerbose) { P_(LINE) P_(TIME) P(OFFSET); } + + { + // Create a complete & valid encoded 'Date' octets + bdlsb::MemOutStreamBuf buff; + int rc = encoder.encode(&buff, TIME); + ASSERTV(rc, 0 == rc); + + const bsl::string_view encoded(buff.data(), buff.length()); + + const bsl::size_t BASE_LEN = 8; // hh:mm:ss [.ssssss] + const bsl::size_t TIME_LEN = BASE_LEN + SEC_FRAC_PREC + 1; + verifyTimeTypesLengthAnomalies(MAX_LEN, + TIME_LEN, + SEC_FRAC_PREC, + LINE, + TIME, + encoded); + } + + { + // Create a complete & valid encoded 'TimeTz' octets + const TimeTz TIMETZ(TIME, OFFSET); + + bdlsb::MemOutStreamBuf buff; + int rc = encoder.encode(&buff, TIMETZ); + ASSERTV(rc, 0 == rc); + + const bsl::string_view encoded(buff.data(), buff.length()); + + const bsl::size_t BASE_LEN = 8; // hh:mm:ss [.ssssss] + const bsl::size_t TIME_LEN = BASE_LEN + SEC_FRAC_PREC + 1; + const bsl::size_t TZ_LEN = 6; // +HH:MM + verifyTimeTypesLengthAnomalies(MAX_LEN, + TIME_LEN + TZ_LEN, + 1, + LINE, + TIMETZ, + encoded); + } + } + } + + if (verbose) cout << "\t'bdlt::Datetime'/'DatetimeTz'\n"; + { + // Create the encoder that encodes datetimes as ISO 8601 strings + balber::BerEncoderOptions mOptions; + mOptions.setEncodeDateAndTimeTypesAsBinary(false); + const bsl::size_t SEC_FRAC_PREC = 6; + mOptions.setDatetimeFractionalSecondPrecision(SEC_FRAC_PREC); + const balber::BerEncoderOptions OPTS = mOptions; + balber::BerEncoder encoder(&OPTS); + + using bdlt::Datetime; + using bdlt::DatetimeTz; + + static struct { + int d_line; + Datetime d_datetime; + int d_offset; + } TEST_DATA[] = { + { L_, Datetime(2024,5,15,12,21,42,999,999), 120 }, + { L_, Datetime(2024,5,15,12,21,42,999,999), 0 }, + }; + + const bsl::size_t TEST_SIZE = sizeof TEST_DATA / sizeof *TEST_DATA; + + for (bsl::size_t ti = 0; ti < TEST_SIZE; ++ti) { + const int LINE = TEST_DATA[ti].d_line; + const Datetime& DATETIME = TEST_DATA[ti].d_datetime; + const int OFFSET = TEST_DATA[ti].d_offset; + + if (veryVeryVerbose) { P_(LINE) P_(DATETIME) P(OFFSET); } + + { + // Create a complete & valid encoded 'Date' octets + bdlsb::MemOutStreamBuf buff; + int rc = encoder.encode(&buff, DATETIME); + ASSERTV(rc, 0 == rc); + + const bsl::string_view encoded(buff.data(), buff.length()); + + const bsl::size_t BASE_LEN = 19; // YYYY-MM-DD+hh:mm:ss + const bsl::size_t DT_LEN = BASE_LEN + SEC_FRAC_PREC + 1; + // .ssssss + verifyTimeTypesLengthAnomalies(MAX_LEN, + DT_LEN, + SEC_FRAC_PREC, + LINE, + DATETIME, + encoded); + } + + { + // Create a complete & valid encoded 'TimeTz' octets + const DatetimeTz DATETIMETZ(DATETIME, OFFSET); + + bdlsb::MemOutStreamBuf buff; + int rc = encoder.encode(&buff, DATETIMETZ); + ASSERTV(rc, 0 == rc); + + const bsl::string_view encoded(buff.data(), buff.length()); + + const bsl::size_t BASE_LEN = 19; // YYYY-MM-DD+hh:mm:ss + const bsl::size_t DT_LEN = BASE_LEN + SEC_FRAC_PREC + 1; + // .ssssss + const bsl::size_t TZ_LEN = 6; // +HH:MM + verifyTimeTypesLengthAnomalies(MAX_LEN, + DT_LEN + TZ_LEN, + 1, + LINE, + DATETIMETZ, + encoded); + } + } + } + } break; case 21: { // -------------------------------------------------------------------- // DECODE INTS AS ENUMS AND VICE VERSA diff --git a/groups/bal/balber/balber_berencoder.t.cpp b/groups/bal/balber/balber_berencoder.t.cpp index ae77d645ac..b5086ebeaf 100644 --- a/groups/bal/balber/balber_berencoder.t.cpp +++ b/groups/bal/balber/balber_berencoder.t.cpp @@ -79,8 +79,28 @@ using bsl::flush; // ============================================================================ // TEST PLAN // ---------------------------------------------------------------------------- - +// Overview +// -------- +// TBD... +// ---------------------------------------------------------------------------- +// [ ] TBD // ---------------------------------------------------------------------------- +// [ 1] FUNDAMENTALS TEST +// [ 2] REAL/FLOATING-POINT +// [ 3] VOCABULARY TYPES +// [ 4] CUSTOMIZED TYPES +// [ 5] ENUMERATIONS +// [ 6] SEQUENCES +// [ 7] CHOICES +// [ 8] NULLABLE VALUES +// [ 9] ARRAYS +// [10] ANONYMOUS CHOICES +// [11] NILLABLE VALUES +// [12] ARRAYS WITH 'encodeEmptyArrays' OPTION {DRQS 29114951 } +// [13] DATE/TIME COMPONENTS +// [14] USAGE EXAMPLE +// +// [-1] PERFORMANCE TEST // ============================================================================ // STANDARD BDE ASSERT TEST MACRO @@ -199,8 +219,8 @@ void printBuffer(const char *buffer, std::size_t length) bsl::cout << bsl::dec << bsl::endl; } -#define DOUBLE_MANTISSA_MASK 0xfffffffffffffLL -#define DOUBLE_SIGN_MASK ((long long) ((long long) 1 \ +#define DOUBLE_MANTISSA_MASK 0xfffffffffffffLL +#define DOUBLE_SIGN_MASK ((long long) ((long long) 1 \ << (sizeof(long long) * 8 - 1))) void assembleDouble(double *value, int sign, int exponent, long long mantissa) @@ -404,7 +424,7 @@ struct Messages { attributeName, attributeNameLength)) { - return bdlat_sequenceManipulateAttribute( + return bdlat_sequenceManipulateAttribute( // RETURN object, manipulator, EmployeeRecord::AGE_ATTRIBUTE_ID); @@ -414,7 +434,7 @@ struct Messages { attributeName, attributeNameLength)) { - return bdlat_sequenceManipulateAttribute( + return bdlat_sequenceManipulateAttribute( // RETURN object, manipulator, EmployeeRecord::SALARY_ATTRIBUTE_ID); @@ -440,7 +460,7 @@ struct Messages { info.name() = "name"; info.nameLength() = 4; - return manipulator(&object->d_name, info); + return manipulator(&object->d_name, info); // RETURN } case EmployeeRecord::AGE_ATTRIBUTE_ID: { bdlat_AttributeInfo info; @@ -451,7 +471,7 @@ struct Messages { info.name() = "age"; info.nameLength() = 3; - return manipulator(&object->d_age, info); + return manipulator(&object->d_age, info); // RETURN } case EmployeeRecord::SALARY_ATTRIBUTE_ID: { bdlat_AttributeInfo info; @@ -462,10 +482,10 @@ struct Messages { info.name() = "salary"; info.nameLength() = 6; - return manipulator(&object->d_salary, info); + return manipulator(&object->d_salary, info); // RETURN } default: { - return k_NOT_FOUND; + return k_NOT_FOUND; // RETURN } } } @@ -483,7 +503,7 @@ struct Messages { EmployeeRecord::NAME_ATTRIBUTE_ID); if (0 != retVal) { - return retVal; + return retVal; // RETURN } retVal = bdlat_sequenceManipulateAttribute( @@ -492,7 +512,7 @@ struct Messages { EmployeeRecord::AGE_ATTRIBUTE_ID); if (0 != retVal) { - return retVal; + return retVal; // RETURN } retVal = bdlat_sequenceManipulateAttribute( @@ -500,7 +520,7 @@ struct Messages { manipulator, EmployeeRecord::SALARY_ATTRIBUTE_ID); - return retVal; + return retVal; // RETURN } // ACCESSORS @@ -516,7 +536,7 @@ struct Messages { if (bdlb::String::areEqualCaseless("name", attributeName, attributeNameLength)) { - return bdlat_sequenceAccessAttribute( + return bdlat_sequenceAccessAttribute( // RETURN object, accessor, EmployeeRecord::NAME_ATTRIBUTE_ID); @@ -525,7 +545,7 @@ struct Messages { if (bdlb::String::areEqualCaseless("age", attributeName, attributeNameLength)) { - return bdlat_sequenceAccessAttribute( + return bdlat_sequenceAccessAttribute( // RETURN object, accessor, EmployeeRecord::AGE_ATTRIBUTE_ID); @@ -535,7 +555,7 @@ struct Messages { attributeName, attributeNameLength)) { - return bdlat_sequenceAccessAttribute( + return bdlat_sequenceAccessAttribute( // RETURN object, accessor, EmployeeRecord::SALARY_ATTRIBUTE_ID); @@ -561,7 +581,7 @@ struct Messages { info.name() = "name"; info.nameLength() = 4; - return accessor(object.d_name, info); + return accessor(object.d_name, info); // RETURN } case EmployeeRecord::AGE_ATTRIBUTE_ID: { bdlat_AttributeInfo info; @@ -572,7 +592,7 @@ struct Messages { info.name() = "age"; info.nameLength() = 3; - return accessor(object.d_age, info); + return accessor(object.d_age, info); // RETURN } case EmployeeRecord::SALARY_ATTRIBUTE_ID: { bdlat_AttributeInfo info; @@ -583,10 +603,10 @@ struct Messages { info.name() = "salary"; info.nameLength() = 6; - return accessor(object.d_salary, info); + return accessor(object.d_salary, info); // RETURN } default: { - return k_NOT_FOUND; + return k_NOT_FOUND; // RETURN } } } @@ -603,7 +623,7 @@ struct Messages { EmployeeRecord::NAME_ATTRIBUTE_ID); if (0 != retVal) { - return retVal; + return retVal; // RETURN } retVal = bdlat_sequenceAccessAttribute( @@ -612,7 +632,7 @@ struct Messages { EmployeeRecord::AGE_ATTRIBUTE_ID); if (0 != retVal) { - return retVal; + return retVal; // RETURN } retVal = bdlat_sequenceAccessAttribute( @@ -620,7 +640,7 @@ struct Messages { accessor, EmployeeRecord::SALARY_ATTRIBUTE_ID); - return retVal; + return retVal; // RETURN } bool usage::bdlat_sequenceHasAttribute( @@ -737,7 +757,7 @@ static void usageExample() // 'CONTEXT_SPECIFIC' (i.e., member of a larger construct) and the 'tagType' is // 'PRIMITIVE' ('bsl::string', 'int', and 'float' each correspond to a // primitive BER type. The 'tagNumber' for each field was defined (in the -// elided definiton) to correspond the position of the field in the +// elided definition) to correspond the position of the field in the // 'usage::EmployeeRecord' class. //.. rc = balber::BerUtil::getIdentifierOctets(&isb, @@ -829,15 +849,13 @@ int main(int argc, char *argv[]) // USAGE EXAMPLE // -------------------------------------------------------------------- - if (verbose) cout << endl - << "USAGE EXAMPLE" << endl - << "=============" << endl; + if (verbose) cout << "\nUSAGE EXAMPLE" + "\n=============\n"; usageExample(); - } break; case 13: { // -------------------------------------------------------------------- - // TESTING 'encode' for date/time components + // DATE/TIME COMPONENTS // // Concerns: // @@ -846,11 +864,10 @@ int main(int argc, char *argv[]) // Testing: // -------------------------------------------------------------------- - if (verbose) bsl::cout << "\nTESTING 'encode' for date/time" - << "\n==============================" - << bsl::endl; + if (verbose) cout << "\nDATE/TIME COMPONENTS" + "\n====================\n"; - if (verbose) bsl::cout << "\nTesting Date." << bsl::endl; + if (verbose) cout << "\t'bdlt::Date'\n"; { static const struct { int d_lineNum; // source line number @@ -1235,7 +1252,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nTesting Time." << bsl::endl; + if (verbose) cout << "\t'bdlt::Time'\n"; { static const struct { int d_lineNum; // source line number @@ -1283,7 +1300,7 @@ int main(int argc, char *argv[]) { L_, 23, 59, 59, 999, 0, "1A 0C 32333A35 393A3539 2E393939" }, { L_, 24, 0, 0, 0, 1, "04 01 00" }, -// TBD: Current doesnt work +// TBD: Doesn't work currently // { L_, 24, 0, 0, 0, 0, "1A 0C 30303A30 303A3030 2E303030" }, //------------v }; @@ -1322,7 +1339,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nTesting TimeTz." << bsl::endl; + if (verbose) cout << "\t'bdlt::TimeTz'\n"; { static const struct { int d_lineNum; // source line number @@ -1446,8 +1463,8 @@ int main(int argc, char *argv[]) "1A 12 32333A35 393A3539 2E393939 2D32333A 3539" }, { L_, 24, 0, 0, 0, 0, 1, "04 01 00" }, -// TBD: Current doesnt work -// { L_, 24, 0, 0, 0, 0, "04 0C 30303A30 303A3030 2E303030" }, + // TBD: Doesn't work currently + // { L_, 24, 0, 0, 0, 0, "04 0C 30303A30 303A3030 2E303030" }, //------------v }; const int NUM_DATA = sizeof DATA / sizeof *DATA; @@ -1470,7 +1487,7 @@ int main(int argc, char *argv[]) options.setEncodeDateAndTimeTypesAsBinary(BIN); const bdlt::TimeTz VALUE(bdlt::Time(HOUR, MIN, SECS, MSEC), - OFF); + OFF); bdlsb::MemOutStreamBuf osb; balber::BerEncoder encoder(&options); @@ -1487,7 +1504,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nTesting Datetime." << bsl::endl; + if (verbose) cout << "\t'bdlt::Datetime'\n"; { static const struct { int d_lineNum; // source line number @@ -1870,8 +1887,8 @@ int main(int argc, char *argv[]) DAY)); if (veryVerbose) { P_(YEAR) P_(MONTH) P_(DAY) - P_(HOUR) P_(MIN) P_(SECS) - P(MSEC) P(USEC) P(EXP) } + P_(HOUR) P_(MIN) P_(SECS) + P(MSEC) P(USEC) P(EXP) } balber::BerEncoderOptions options; options.setEncodeDateAndTimeTypesAsBinary(BIN); @@ -1894,7 +1911,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nTesting DatetimeTz." << bsl::endl; + if (verbose) cout << "\t'bdlt::DatetimeTz'\n"; { static const struct { int d_lineNum; // source line number @@ -2405,9 +2422,9 @@ int main(int argc, char *argv[]) MONTH, DAY)); - if (veryVerbose) { P_(YEAR) P_(MONTH) P_(DAY) P_(OFF) P(BIN) - P_(HOUR) P_(MIN) P_(SECS) P(MSEC) - P(USEC) P(EXP) } + if (veryVerbose) { P_(YEAR) P_(MONTH) P_(DAY) P_(OFF) P(BIN) + P_(HOUR) P_(MIN) P_(SECS) P_(MSEC) + P_(USEC) P(EXP) } balber::BerEncoderOptions options; options.setEncodeDateAndTimeTypesAsBinary(BIN); @@ -2434,7 +2451,7 @@ int main(int argc, char *argv[]) } break; case 12: { // -------------------------------------------------------------------- - // TESTING ARRAYS WITH the 'encodeEmptyArrays' option (DRQS 29114951) + // ARRAYS WITH 'encodeEmptyArrays' OPTION {DRQS 29114951 } // // Concerns: //: 1 If 'balber::BerEncoderOptions' is not specified then empty arrays @@ -2483,11 +2500,10 @@ int main(int argc, char *argv[]) // Encoding of vectors // -------------------------------------------------------------------- - if (verbose) bsl::cout << "\nTesting Arrays with 'encodeEmptyArrays'" - << "\n=======================================" - << bsl::endl; + if (verbose) cout << "\nARRAYS WITH 'encodeEmptyArrays' OPTION" + "\n======================================\n"; - if (verbose) bsl::cout << "\nTesting with empty array." << bsl::endl; + if (verbose) cout << "\tEmpty array\n"; { balber::BerEncoderOptions options1, options2, options3; options1.setEncodeEmptyArrays(true); @@ -2532,8 +2548,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nTesting with a non-empty array." - << bsl::endl; + if (verbose) cout << "\tNon-empty array\n"; { balber::BerEncoderOptions options1, options2, options3; options1.setEncodeEmptyArrays(true); @@ -2581,11 +2596,11 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nEnd of test." << bsl::endl; + if (verbose) cout << "End of test.\n"; } break; case 11: { // -------------------------------------------------------------------- - // TESTING NILLABLE VALUES + // NILLABLE VALUES // // Concerns: // @@ -2595,10 +2610,10 @@ int main(int argc, char *argv[]) // // -------------------------------------------------------------------- - if (verbose) bsl::cout << "\nTesting Nillable Values" - << "\n=======================" << bsl::endl; + if (verbose) cout << "\nNILLABLE VALUES" + "\n===============\n"; - if (verbose) bsl::cout << "\nTesting with null value." << bsl::endl; + if (verbose) cout << "\tNull value\n"; { bdlsb::MemOutStreamBuf osb; @@ -2615,8 +2630,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nTesting with non-null value." - << bsl::endl; + if (verbose) cout << "\tNon-null value\n"; { bdlsb::MemOutStreamBuf osb; @@ -2634,11 +2648,11 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nEnd of test." << bsl::endl; + if (verbose) cout << "End of test.\n"; } break; case 10: { // -------------------------------------------------------------------- - // TESTING ANONYMOUS CHOICES + // ANONYMOUS CHOICES // // Concerns: //: 1 If 'balber::BerEncoderOptions' is not specified then the encoder @@ -2682,10 +2696,10 @@ int main(int argc, char *argv[]) // Encoding of anonymous choices // -------------------------------------------------------------------- - if (verbose) bsl::cout << "\nTesting Anonymous Choice" - << "\n========================" << bsl::endl; + if (verbose) cout << "\nANONYMOUS CHOICES" + << "\n=================\n"; - if (verbose) bsl::cout << "\nTesting with no selection." << bsl::endl; + if (verbose) cout << "\tChoice with no selection\n"; { bdlsb::MemOutStreamBuf osb; @@ -2741,7 +2755,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nTesting with selection." << bsl::endl; + if (verbose) cout << "\tChoice with selection\n"; { bdlsb::MemOutStreamBuf osb; @@ -2853,11 +2867,11 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nEnd of test." << bsl::endl; + if (verbose) cout << "End of test.\n"; } break; case 9: { // -------------------------------------------------------------------- - // TESTING ARRAYS + // ARRAYS // // Concerns: // @@ -2867,10 +2881,10 @@ int main(int argc, char *argv[]) // // -------------------------------------------------------------------- - if (verbose) bsl::cout << "\nTesting Arrays" - << "\n==============" << bsl::endl; + if (verbose) cout << "\nARRAYS" + "\n======\n"; - if (verbose) bsl::cout << "\nTesting with empty array." << bsl::endl; + if (verbose) cout << "\tEmpty array\n"; { bdlsb::MemOutStreamBuf osb; @@ -2886,8 +2900,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nTesting with non-empty array." - << bsl::endl; + if (verbose) cout << "\tNon-empty array\n"; { bdlsb::MemOutStreamBuf osb; @@ -2905,11 +2918,11 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nEnd of test." << bsl::endl; + if (verbose) cout << "End of test.\n"; } break; case 8: { // -------------------------------------------------------------------- - // TESTING NULLABLE VALUES + // NULLABLE VALUES // // Concerns: // @@ -2919,10 +2932,10 @@ int main(int argc, char *argv[]) // // -------------------------------------------------------------------- - if (verbose) bsl::cout << "\nTesting Nullable Values" - << "\n=======================" << bsl::endl; + if (verbose) cout << "\nNULLABLE VALUES" + "\n===============\n"; - if (verbose) bsl::cout << "\nTesting with null value." << bsl::endl; + if (verbose) cout << "\tNull value\n"; { bdlsb::MemOutStreamBuf osb; @@ -2938,8 +2951,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nTesting with non-null value." - << bsl::endl; + if (verbose) cout << "\tNon-null value\n"; { bdlsb::MemOutStreamBuf osb; @@ -2956,11 +2968,11 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nEnd of test." << bsl::endl; + if (verbose) cout << "End of test.\n"; } break; case 7: { // -------------------------------------------------------------------- - // TESTING CHOICES + // CHOICES // // Concerns: //: 1 If 'balber::BerEncoderOptions' is not specified then the encoder @@ -3004,10 +3016,10 @@ int main(int argc, char *argv[]) // Encoding of choices // -------------------------------------------------------------------- - if (verbose) bsl::cout << "\nTesting Choices" - << "\n===============" << bsl::endl; + if (verbose) cout << "\nCHOICES" + << "\n=======\n"; - if (verbose) bsl::cout << "\nTesting with no selection." << bsl::endl; + if (verbose) cout << "\tChoice with no selection\n"; { bdlsb::MemOutStreamBuf osb; @@ -3057,7 +3069,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nTesting with selection." << bsl::endl; + if (verbose) cout << "\tChoice with selection\n"; { bdlsb::MemOutStreamBuf osb; @@ -3113,11 +3125,11 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nEnd of test." << bsl::endl; + if (verbose) cout << "End of test.\n"; } break; case 6: { // -------------------------------------------------------------------- - // TESTING SEQUENCES + // SEQUENCES // // Concerns: // @@ -3127,8 +3139,8 @@ int main(int argc, char *argv[]) // // -------------------------------------------------------------------- - if (verbose) bsl::cout << "\nTesting Sequences" - << "\n=================" << bsl::endl; + if (verbose) cout << "\nSEQUENCES" + "\n=========\n"; bdlsb::MemOutStreamBuf osb; @@ -3144,11 +3156,11 @@ int main(int argc, char *argv[]) printBuffer(osb.data(), osb.length()); } - if (verbose) bsl::cout << "\nEnd of test." << bsl::endl; + if (verbose) cout << "End of test.\n"; } break; case 5: { // -------------------------------------------------------------------- - // TESTING ENUMERATIONS + // ENUMERATIONS // // Concerns: // @@ -3158,8 +3170,8 @@ int main(int argc, char *argv[]) // // -------------------------------------------------------------------- - if (verbose) bsl::cout << "\nTesting Enumerations" - << "\n====================" << bsl::endl; + if (verbose) cout << "\nENUMERATIONS" + "\n============\n"; bdlsb::MemOutStreamBuf osb; @@ -3173,11 +3185,11 @@ int main(int argc, char *argv[]) printBuffer(osb.data(), osb.length()); } - if (verbose) bsl::cout << "\nEnd of test." << bsl::endl; + if (verbose) cout << "End of test.\n"; } break; case 4: { // -------------------------------------------------------------------- - // TESTING CUSTOMIZED TYPES + // CUSTOMIZED TYPES // // Concerns: // @@ -3187,14 +3199,14 @@ int main(int argc, char *argv[]) // // -------------------------------------------------------------------- - if (verbose) bsl::cout << "\nTesting Customized Types" - << "\n========================" << bsl::endl; + if (verbose) cout << "\nCUSTOMIZED TYPES" + "\n================\n"; bdlsb::MemOutStreamBuf osb1, osb2, osb3, osb4; const bsl::string VALUE = "Hello"; - if (verbose) bsl::cout << "\nEncoding customized string." << bsl::endl; + if (verbose) cout << "\tCustomized string\n"; { test::CustomizedString value; value.fromString(VALUE); @@ -3208,8 +3220,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nEncoding bsl::string (control)." - << bsl::endl; + if (verbose) cout << "\t'bsl::string' (control)\n"; { bsl::string value = VALUE; @@ -3222,8 +3233,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nEncoding bsl::string_view (control)." - << bsl::endl; + if (verbose) cout << "\t'bsl::string_view' (control)\n"; { bsl::string_view value = VALUE; @@ -3236,8 +3246,8 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nEncoding bslstl::StringRef (control)." - << bsl::endl; + if (verbose) cout << "\t'bslstl::StringRef' (control)\n"; + { bslstl::StringRef value = VALUE; @@ -3260,11 +3270,11 @@ int main(int argc, char *argv[]) ASSERT(0 == bsl::memcmp(osb1.data(), osb3.data(), osb1.length())); ASSERT(0 == bsl::memcmp(osb1.data(), osb4.data(), osb1.length())); - if (verbose) bsl::cout << "\nEnd of test." << bsl::endl; + if (verbose) cout << "End of test.\n"; } break; case 3: { // -------------------------------------------------------------------- - // VOCABULARY TYPES TEST + // VOCABULARY TYPES // // Concerns: // @@ -3274,12 +3284,10 @@ int main(int argc, char *argv[]) // // -------------------------------------------------------------------- - if (verbose) bsl::cout << "\nVOCABULARY TYPES TEST" - << "\n=====================" << bsl::endl; - - if (verbose) bsl::cout << "\nTesting bdlt::Date" - << "\n=================" << bsl::endl; + if (verbose) cout << "\nVOCABULARY TYPES" + "\n================\n"; + if (verbose) cout << "\t'bdlt::Date'\n"; { const int YEAR = 2005, MONTH = 12, DAY = 15; @@ -3295,9 +3303,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nTesting bdlt::DateTz" - << "\n===================" << bsl::endl; - + if (verbose) cout << "\t'bdlt::DateTz'\n"; { const int YEAR = 2005, MONTH = 12, DAY = 15, OFFSET = 45; @@ -3313,9 +3319,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nTesting bdlt::Time" - << "\n=================" << bsl::endl; - + if (verbose) cout << "\t'bdlt::Time'\n"; { const int HOUR = 12, MIN = 56, SECS = 9, MILLISECS = 134; @@ -3331,9 +3335,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nTesting bdlt::TimeTz" - << "\n===================" << bsl::endl; - + if (verbose) cout << "\t'bdlt::TimeTz'\n"; { const int HOUR = 12, MIN = 56, SECS = 9, MILLISECS = 134, OFFSET = 45; @@ -3350,9 +3352,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nTesting bdlt::Datetime" - << "\n=====================" << bsl::endl; - + if (verbose) cout << "\t'bdlt::Datetime'\n"; { const int YEAR = 2005, MONTH = 12, DAY = 15; const int HOUR = 12, MIN = 56, SECS = 9, MILLISECS = 134; @@ -3369,9 +3369,7 @@ int main(int argc, char *argv[]) } } - if (verbose) bsl::cout << "\nTesting bdlt::DatetimeTz" - << "\n=======================" << bsl::endl; - + if (verbose) cout << "\t'bdlt::DatetimeTz'\n"; { const int YEAR = 2005, MONTH = 12, DAY = 15; const int HOUR = 12, MIN = 56, SECS = 9, MILLISECS = 134; @@ -3390,10 +3388,12 @@ int main(int argc, char *argv[]) printBuffer(osb.data(), osb.length()); } } + + if (verbose) cout << "End of test.\n"; } break; case 2: { // -------------------------------------------------------------------- - // REAL TEST + // REAL/FLOATING-POINT // // Concerns: // @@ -3402,8 +3402,8 @@ int main(int argc, char *argv[]) // Testing: // -------------------------------------------------------------------- - if (verbose) bsl::cout << "\nREAL TEST" - << "\n=========" << bsl::endl; + if (verbose) cout << "\nREAL/FLOATING-POINT" + "\n===================\n"; { static const struct { @@ -3442,12 +3442,11 @@ int main(int argc, char *argv[]) "09 0A 81 FF 4E 10 00 00 00 0A 63 9B" }, { L_, 3.402823466E+38, "09 09 80 4C 0F FF FF EF F8 38 1B" }, - }; const int NUM_DATA = sizeof REAL / sizeof *REAL; - if (verbose) { cout << "\nTesting normal real values" << endl; } + if (verbose) cout << "\tNormal values\n"; for (int di = 0; di < NUM_DATA; ++di) { const int LINE = REAL[di].d_lineNum; @@ -3468,7 +3467,7 @@ int main(int argc, char *argv[]) } } - if (verbose) { cout << "\nTesting for special values" << endl; } + if (verbose) cout << "\tSpecial values\n"; { static const struct { int d_lineNum; // source line number @@ -3524,6 +3523,8 @@ int main(int argc, char *argv[]) } } } + + if (verbose) cout << "End of test.\n"; } break; case 1: { // -------------------------------------------------------------------- @@ -3537,7 +3538,7 @@ int main(int argc, char *argv[]) // -------------------------------------------------------------------- if (verbose) bsl::cout << "\nFUNDAMENTALS TEST" - << "\n=================" << bsl::endl; + << "\n=================\n"; { const unsigned char XA1 = UCHAR_MAX; @@ -3803,6 +3804,8 @@ int main(int argc, char *argv[]) } } } + + if (verbose) cout << "End of test.\n"; } break; case -1: { // -------------------------------------------------------------------- diff --git a/groups/bal/balber/balber_berutil.h b/groups/bal/balber/balber_berutil.h index 4c5f27d61a..6f45f1db07 100644 --- a/groups/bal/balber/balber_berutil.h +++ b/groups/bal/balber/balber_berutil.h @@ -4463,6 +4463,14 @@ int BerUtil_Iso8601ImpUtil::getValue(TYPE *value, return -1; // RETURN } + // DoS attack mitigation: Any reasonable ISO8601 string will be smaller + // than this. Even an ISO 8601 duration (not currently supported) with all + // its elements (years, months, days, hours, minutes, and seconds) present + // with 17 significant digits and negative signs would be 117 characters. + if (length > 126) { + return -1; // RETURN + } + char localBuf[32]; // for common case where length < 32 bsl::vector vecBuf; // for length >= 32 char *buf; diff --git a/groups/bal/balber/balber_berutil.t.cpp b/groups/bal/balber/balber_berutil.t.cpp index 77fc576e14..a0824a4ec9 100644 --- a/groups/bal/balber/balber_berutil.t.cpp +++ b/groups/bal/balber/balber_berutil.t.cpp @@ -149,8 +149,9 @@ using namespace bsl; // [27] CONCERN: 'getValue' reports all failures to read from stream buffer // [28] CONCERN: 'put'- & 'getValue' for date/time types in extended binary fmt // [29] CONCERN: 'putValue' encoding formation selection -// [30] CONCERN: TESTING +/- ZERO FLOATING-POINT\n" -// [31] USAGE EXAMPLE +// [30] CONCERN: ISO 8601 TEXT LENGTH ANOMALIES +// [31] CONCERN: TESTING +/- ZERO FLOATING-POINT\n" +// [32] USAGE EXAMPLE // ============================================================================ // STANDARD BDE ASSERT TEST MACRO // ---------------------------------------------------------------------------- @@ -5424,7 +5425,7 @@ int main(int argc, char *argv[]) bsls::ReviewFailureHandlerGuard reviewGuard(&bsls::Review::failByAbort); switch (test) { case 0: // Zero is always the leading case. - case 31: { + case 32: { // -------------------------------------------------------------------- // USAGE EXAMPLE // Extracted from component header file. @@ -5506,7 +5507,7 @@ int main(int argc, char *argv[]) if (verbose) bsl::cout << "\nEnd of test." << bsl::endl; } break; - case 30: { + case 31: { // -------------------------------------------------------------------- // CONCERN: TESTING +/- ZERO FLOATING-POINT // @@ -5725,6 +5726,543 @@ int main(int argc, char *argv[]) ASSERT(!bsl::memcmp(&xx, &yy, sizeof(xx))); } } break; + case 30: { + // -------------------------------------------------------------------- + // CONCERN: ISO 8601 TEXT LENGTH ANOMALIES + // + // + // Concerns: + //: 1 ISO 8601-encoded strings longer than the maximum length result in + //: a decoding error. + //: + //: 2 Valid ISO 8601-encoded strings of exactly the maximum length are + //: decoded properly. + //: + //: 3 If no data is available for the specified length decoding results + //: in an error even if the available ISO 8601-encoded strings forms + //: a valid value. + //: + //: 4 Decoding obeys the length parameter even if more data is + //: available from the buffer. + // + // Plan: + //: 1 Specify valid ISO 8601 strings for each individual date, time, + //: and the duration type. + //: + //: 2 Create variations of the above valid strings that are exactly the + //: maximum length, and are still valid ISO 8601. + //: + //: 3 Create variations of the above valid strings that are exactly one + //: character longer than the maximum length, and are still valid ISO + //: 8601. + //: + //: 4 Execute 'getValue' on each value and verify that failure occurs + //: only on the too long value. + //: + //: 5 Execute 'getValue' on each value while specifying a length longer + //: than the available data in the buffer and verify that a failure + //: occurs for all three values. + //: + //: 6 Execute 'getValue' on the valid values while specifying a length + //: short-enough to truncate the data in the buffer to an invalid + //: string and verify that a failure occurs for all valid values. + //: + //: 7 Execute 'getValue' on the invalid values while specifying a + //: length short-enough to truncate invalid data to a valid ISO 8601 + //: string and verify that no failure occurs. + // + // Testing: + // int BerUtil_Iso8601ImpUtil::getValue(buf, TYPE, length); + // -------------------------------------------------------------------- + + if (verbose) cout << "CONCERN: ISO 8601 TEXT LENGTH ANOMALIES\n" + "=======================================\n"; + + const int MAX_LEN = 126; + + typedef balber::BerUtil_Iso8601ImpUtil IsoUtil; + + if (veryVerbose) cout << "\tbdlt::Date\n"; + { + + const char *STR = "2024-05-15"; + const int LEN = static_cast(bsl::strlen(STR)); + bdlt::Date value; + + { + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getDateValue(&value, &streamBuf, LEN); + ASSERTV(rc, 0 == rc); + } + { // LENGTH MORE THAN DATA + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getDateValue(&value, &streamBuf, LEN + 1); + ASSERTV(rc, 0 != rc); + } + { // TRUNCATED to invalid value + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getDateValue(&value, &streamBuf, LEN - 1); + ASSERTV(rc, 0 != rc); + } + + using bsl::string; + const string STR_MAX = string(STR) + string(MAX_LEN - LEN, '1'); + ASSERTV(STR_MAX.size(), STR_MAX.size() == MAX_LEN); + { + bdlsb::FixedMemInStreamBuf streamBuf(STR_MAX.data(), MAX_LEN); + int rc = IsoUtil::getDateValue(&value, &streamBuf, MAX_LEN); + ASSERTV(rc, -1 == rc); // Invalid date + } + { // TRUNCATED to valid value + bdlsb::FixedMemInStreamBuf streamBuf(STR_MAX.data(), MAX_LEN); + + int rc = IsoUtil::getDateValue(&value, &streamBuf, LEN); + ASSERTV(rc, 0 == rc); // Only sees the valid part + } + + const string STR_OVER = STR_MAX + "1"; + { + bdlsb::FixedMemInStreamBuf streamBuf(STR_OVER.data(), + MAX_LEN + 1); + int rc = IsoUtil::getDateValue(&value, + &streamBuf, + MAX_LEN + 1); + ASSERTV(rc, 0 != rc); // Too long input + + // Verify that nothing was read from the buffer + ASSERTV(MAX_LEN + 1, streamBuf.length(), + MAX_LEN + 1 == streamBuf.length()); + } + { // TRUNCATED to valid + bdlsb::FixedMemInStreamBuf streamBuf(STR_OVER.data(), + MAX_LEN + 1); + + int rc = IsoUtil::getDateValue(&value, &streamBuf, LEN); + ASSERTV(rc, 0 == rc); // Only sees the valid part + } + } + + if (veryVerbose) cout << "\tbdlt::DateTz\n"; + { + + const char *STR = "2024-05-15+05:00"; + const int LEN = static_cast(bsl::strlen(STR)); + bdlt::DateTz value; + + { + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getDateTzValue(&value, &streamBuf, LEN); + ASSERTV(rc, 0 == rc); + } + { // LENGTH MORE THAN DATA + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getDateTzValue(&value, &streamBuf, LEN + 1); + ASSERTV(rc, 0 != rc); + } + { // TRUNCATED to invalid value + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getDateTzValue(&value, &streamBuf, LEN - 1); + ASSERTV(rc, 0 != rc); + } + + using bsl::string; + const string STR_MAX = string(STR) + string(MAX_LEN - LEN, '1'); + ASSERTV(STR_MAX.size(), STR_MAX.size() == MAX_LEN); + { + bdlsb::FixedMemInStreamBuf streamBuf(STR_MAX.data(), MAX_LEN); + + int rc = IsoUtil::getDateTzValue(&value, &streamBuf, MAX_LEN); + ASSERTV(rc, -1 == rc); // Invalid date + } + { // TRUNCATED to valid value + bdlsb::FixedMemInStreamBuf streamBuf(STR_MAX.data(), MAX_LEN); + + int rc = IsoUtil::getDateTzValue(&value, &streamBuf, LEN); + ASSERTV(rc, 0 == rc); // Only sees the valid part + } + + const string STR_OVER = STR_MAX + "1"; + { + bdlsb::FixedMemInStreamBuf streamBuf(STR_OVER.data(), + MAX_LEN + 1); + + int rc = IsoUtil::getDateTzValue(&value, + &streamBuf, + MAX_LEN + 1); + ASSERTV(rc, 0 != rc); // Too long input + + // Verify that nothing was read from the buffer + ASSERTV(MAX_LEN + 1, streamBuf.length(), + MAX_LEN + 1 == streamBuf.length()); + } + { // TRUNCATED to valid value + bdlsb::FixedMemInStreamBuf streamBuf(STR_OVER.data(), + MAX_LEN + 1); + + int rc = IsoUtil::getDateTzValue(&value, &streamBuf, LEN); + ASSERTV(rc, 0 == rc); // Only sees the valid part + } + } + + if (veryVerbose) cout << "\tbdlt::Time\n"; + { + + const char *STR = "12:01:01.001"; + const int LEN = static_cast(bsl::strlen(STR)); + bdlt::Time value; + + { + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getTimeValue(&value, &streamBuf, LEN); + ASSERTV(rc, 0 == rc); + } + { // LENGTH MORE THAN DATA + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getTimeValue(&value, &streamBuf, LEN + 1); + ASSERTV(rc, 0 != rc); + } + { // TRUNCATED to invalid value + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getTimeValue(&value, &streamBuf, LEN - 3); + ASSERTV(rc, 0 != rc); + } + + using bsl::string; + const string STR_MAX = string(STR) + string(MAX_LEN - LEN, '1'); + ASSERTV(STR_MAX.size(), STR_MAX.size() == MAX_LEN); + { + bdlsb::FixedMemInStreamBuf streamBuf(STR_MAX.data(), MAX_LEN); + + int rc = IsoUtil::getTimeValue(&value, &streamBuf, MAX_LEN); + ASSERTV(rc, 0 == rc); + } + { // LENGTH more than data + bdlsb::FixedMemInStreamBuf streamBuf(STR_MAX.data(), + MAX_LEN - 1); + + int rc = IsoUtil::getTimeValue(&value, &streamBuf, MAX_LEN); + ASSERTV(rc, 0 != rc); + } + { // TRUNCATED to invalid value + bdlsb::FixedMemInStreamBuf streamBuf(STR_MAX.data(), MAX_LEN); + + int rc = IsoUtil::getTimeValue(&value, &streamBuf, LEN - 3); + ASSERTV(rc, 0 != rc); + } + + const string STR_OVER = STR_MAX + "1"; + { + bdlsb::FixedMemInStreamBuf streamBuf(STR_OVER.data(), + MAX_LEN + 1); + int rc = IsoUtil::getTimeValue(&value, + &streamBuf, + MAX_LEN + 1); + ASSERTV(rc, 0 != rc); // Too long input + + // Verify that nothing was read from the buffer + ASSERTV(MAX_LEN + 1, streamBuf.length(), + MAX_LEN + 1 == streamBuf.length()); + } + { // TRUNCATED to valid length + bdlsb::FixedMemInStreamBuf streamBuf(STR_OVER.data(), + MAX_LEN + 1); + int rc = IsoUtil::getTimeValue(&value, + &streamBuf, + MAX_LEN); + ASSERTV(rc, 0 == rc); // Length is fine, extra data ignored + } + { // TRUNCATED to invalid value + bdlsb::FixedMemInStreamBuf streamBuf(STR_OVER.data(), + MAX_LEN + 1); + int rc = IsoUtil::getTimeValue(&value, + &streamBuf, + LEN - 3); + ASSERTV(rc, 0 != rc); + } + } + + if (veryVerbose) cout << "\tbdlt::TimeTz\n"; + { + + const char *STR = "12:01:01.001-05:00"; + const int LEN = static_cast(bsl::strlen(STR)); + bdlt::TimeTz value; + + { + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getTimeTzValue(&value, &streamBuf, LEN); + ASSERTV(rc, 0 == rc); + } + { // LENGTH MORE THAN DATA + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getTimeTzValue(&value, &streamBuf, LEN + 1); + ASSERTV(rc, 0 != rc); + } + { // TRUNCATED to invalid value + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getTimeTzValue(&value, &streamBuf, LEN - 1); + ASSERTV(rc, 0 != rc); + } + + using bsl::string; + const string STR_MAX = "12:01:01.001" + string(MAX_LEN - LEN, '1') + + "-05:00"; + ASSERTV(STR_MAX.size(), STR_MAX.size() == MAX_LEN); + { + bdlsb::FixedMemInStreamBuf streamBuf(STR_MAX.data(), MAX_LEN); + + int rc = IsoUtil::getTimeTzValue(&value, &streamBuf, MAX_LEN); + ASSERTV(rc, 0 == rc); + } + { // LENGTH MORE THAN DATA + bdlsb::FixedMemInStreamBuf streamBuf(STR_MAX.data(), + MAX_LEN - 1); + + int rc = IsoUtil::getTimeTzValue(&value, &streamBuf, MAX_LEN); + ASSERTV(rc, 0 != rc); + } + { // TRUNCATED to invalid value + bdlsb::FixedMemInStreamBuf streamBuf(STR_MAX.data(), MAX_LEN); + + int rc = IsoUtil::getTimeTzValue(&value, + &streamBuf, + MAX_LEN - 1); + ASSERTV(rc, 0 != rc); + } + + const string STR_OVER = STR_MAX + "1"; + { + bdlsb::FixedMemInStreamBuf streamBuf(STR_OVER.data(), + MAX_LEN + 1); + + int rc = IsoUtil::getTimeTzValue(&value, + &streamBuf, + MAX_LEN + 1); + ASSERTV(rc, 0 != rc); // Too long input + + // Verify that nothing was read from the buffer + ASSERTV(MAX_LEN + 1, streamBuf.length(), + MAX_LEN + 1 == streamBuf.length()); + } + { // LENGTH MORE THAN DATA + bdlsb::FixedMemInStreamBuf streamBuf(STR_OVER.data(), + MAX_LEN - 1); + + int rc = IsoUtil::getTimeTzValue(&value, + &streamBuf, + MAX_LEN); + ASSERTV(rc, 0 != rc); + } + { // TRUNCATED to valid length + bdlsb::FixedMemInStreamBuf streamBuf(STR_OVER.data(), + MAX_LEN + 1); + + int rc = IsoUtil::getTimeTzValue(&value, &streamBuf, MAX_LEN); + ASSERTV(rc, 0 == rc); + } + } + + if (veryVerbose) cout << "\tbdlt::Datetime\n"; + { + + const char *STR = "2024-05-15T12:01:01.001"; + const int LEN = static_cast(bsl::strlen(STR)); + bdlt::Datetime value; + + { + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getDatetimeValue(&value, &streamBuf, LEN); + ASSERTV(rc, 0 == rc); + } + { // LENGTH MORE THAN DATA + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getDatetimeValue(&value, + &streamBuf, + LEN + 1); + ASSERTV(rc, 0 != rc); + } + { // TRUNCATED to invalid value + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getDatetimeValue(&value, + &streamBuf, + LEN - 3); + ASSERTV(rc, 0 != rc); + } + + using bsl::string; + const string STR_MAX = string(STR) + string(MAX_LEN - LEN, '1'); + ASSERTV(STR_MAX.size(), STR_MAX.size() == MAX_LEN); + { + bdlsb::FixedMemInStreamBuf streamBuf(STR_MAX.data(), MAX_LEN); + + int rc = IsoUtil::getDatetimeValue(&value, + &streamBuf, + MAX_LEN); + ASSERTV(rc, 0 == rc); + } + { // LENGTH MORE THAN DATA + bdlsb::FixedMemInStreamBuf streamBuf(STR_MAX.data(), + MAX_LEN - 1); + + int rc = IsoUtil::getDatetimeValue(&value, + &streamBuf, + MAX_LEN); + ASSERTV(rc, 0 != rc); + } + { // TRUNCATED to invalid value + bdlsb::FixedMemInStreamBuf streamBuf(STR_MAX.data(), + MAX_LEN); + + int rc = IsoUtil::getDatetimeValue(&value, + &streamBuf, + LEN - 3); + ASSERTV(rc, 0 != rc); + } + + const string STR_OVER = STR_MAX + "1"; + { + bdlsb::FixedMemInStreamBuf streamBuf(STR_OVER.data(), + MAX_LEN + 1); + + int rc = IsoUtil::getDatetimeValue(&value, + &streamBuf, + MAX_LEN + 1); + ASSERTV(rc, 0 != rc); // Too long input + + // Verify that nothing was read from the buffer + ASSERTV(MAX_LEN + 1, streamBuf.length(), + MAX_LEN + 1 == streamBuf.length()); + } + { // LENGTH MORE THAN DATA + bdlsb::FixedMemInStreamBuf streamBuf(STR_OVER.data(), + MAX_LEN - 1); + + int rc = IsoUtil::getDatetimeValue(&value, + &streamBuf, + MAX_LEN); + ASSERTV(rc, 0 != rc); + } + { // TRUNCATE to valid length + bdlsb::FixedMemInStreamBuf streamBuf(STR_OVER.data(), + MAX_LEN + 1); + + int rc = IsoUtil::getDatetimeValue(&value, + &streamBuf, + MAX_LEN); + ASSERTV(rc, 0 == rc); + } + } + + if (veryVerbose) cout << "\tbdlt::DatetimeTz\n"; + { + + const char *STR = "2024-05-15T12:01:01.001-05:00"; + const int LEN = static_cast(bsl::strlen(STR)); + bdlt::DatetimeTz value; + + { + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getDatetimeTzValue(&value, &streamBuf, LEN); + ASSERTV(rc, 0 == rc); + } + { // LENGTH MORE THAN DATA + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getDatetimeTzValue(&value, + &streamBuf, + LEN + 1); + ASSERTV(rc, 0 != rc); + } + { // TRUNCATED to invalid value + bdlsb::FixedMemInStreamBuf streamBuf(STR, LEN); + + int rc = IsoUtil::getDatetimeTzValue(&value, + &streamBuf, + LEN - 1); + ASSERTV(rc, 0 != rc); + } + + using bsl::string; + const string STR_MAX = "2024-05-15T12:01:01.001" + + string(MAX_LEN - LEN, '1') + + "-05:00"; + ASSERTV(STR_MAX.size(), STR_MAX.size() == MAX_LEN); + { + bdlsb::FixedMemInStreamBuf streamBuf(STR_MAX.data(), MAX_LEN); + + int rc = IsoUtil::getDatetimeTzValue(&value, + &streamBuf, + MAX_LEN); + ASSERTV(rc, 0 == rc); + } + { // LENGTH MORE THAN DATA + bdlsb::FixedMemInStreamBuf streamBuf(STR_MAX.data(), + MAX_LEN - 1); + + int rc = IsoUtil::getDatetimeTzValue(&value, + &streamBuf, + MAX_LEN); + ASSERTV(rc, 0 != rc); + } + { // TRUNCATE to invalid value + bdlsb::FixedMemInStreamBuf streamBuf(STR_MAX.data(), MAX_LEN); + + int rc = IsoUtil::getDatetimeTzValue(&value, + &streamBuf, + MAX_LEN - 1); + ASSERTV(rc, 0 != rc); + } + + const string STR_OVER = STR_MAX + "1"; + { + bdlsb::FixedMemInStreamBuf streamBuf(STR_OVER.data(), + MAX_LEN + 1); + + int rc = IsoUtil::getDatetimeTzValue(&value, + &streamBuf, + MAX_LEN + 1); + ASSERTV(rc, 0 != rc); // Too long input + + // Verify that nothing was read from the buffer + ASSERTV(MAX_LEN + 1, streamBuf.length(), + MAX_LEN + 1 == streamBuf.length()); + } + { // TRUNCATE to valid length + bdlsb::FixedMemInStreamBuf streamBuf(STR_OVER.data(), + MAX_LEN + 1); + + int rc = IsoUtil::getDatetimeTzValue(&value, + &streamBuf, + MAX_LEN); + ASSERTV(rc, 0 == rc); + } + { // TRUNCATE to invalid value + bdlsb::FixedMemInStreamBuf streamBuf(STR_OVER.data(), + MAX_LEN + 1); + + int rc = IsoUtil::getDatetimeTzValue(&value, + &streamBuf, + MAX_LEN - 1); + ASSERTV(rc, 0 != rc); + } + } + } break; case 29: { // -------------------------------------------------------------------- // TESTING DATE/TIME FORMAT SELECTION