From bbe197a30a51739c9c1a4e51af041fbdcfb9f75f Mon Sep 17 00:00:00 2001 From: seladb Date: Sun, 2 Jun 2024 23:50:41 -0700 Subject: [PATCH 1/6] Add LDAP search request and result entry layers (#1424) * Add LDAP layer * Add doxygen * Parse multiple LDAP messages in one packet * Add tryGet * Fix pre-commit * Fix pre-commit * Temporarily replace Npcap with WinPcap * Fix WinPcap path * Modify `EnumClassHash` * Modify Int -> Uint * Revert CI changes * Add field index constexpr * Add LDAP search request layer * Doxygen doc * Address PR comment: use `std::mem_fn` * Address PR comments * Add documentation * Add `LdapSearchResultEntryLayer` * Few fixes * Ignore cppcheck * Address PR comments * Small indentation fix --- Packet++/header/LdapLayer.h | 309 +++++++++++++++++- Packet++/src/LdapLayer.cpp | 208 +++++++++++- README.md | 17 +- .../PacketExamples/ldap_search_request2.dat | 1 + .../ldap_search_result_entry.dat | 1 + Tests/Packet++Test/Tests/LdapTests.cpp | 116 +++++++ 6 files changed, 638 insertions(+), 14 deletions(-) create mode 100644 Tests/Packet++Test/PacketExamples/ldap_search_request2.dat create mode 100644 Tests/Packet++Test/PacketExamples/ldap_search_result_entry.dat diff --git a/Packet++/header/LdapLayer.h b/Packet++/header/LdapLayer.h index 188403fb7..5a8c730b5 100644 --- a/Packet++/header/LdapLayer.h +++ b/Packet++/header/LdapLayer.h @@ -126,6 +126,28 @@ namespace pcpp } }; + /** + * @struct LdapAttribute + * A struct that represents an LDAP attribute + */ + struct LdapAttribute + { + /// Attribute description + std::string type; + /// A list of attribute values (zero or more) + std::vector values; + + /** + * Equality operator overload for this struct + * @param[in] other The value to compare with + * @return True if both values are equal, false otherwise + */ + bool operator==(const LdapAttribute& other) const + { + return type == other.type && values == other.values; + } + }; + /** * @class LdapLayer * Represents an LDAP message @@ -239,7 +261,7 @@ namespace pcpp protected: std::unique_ptr m_Asn1Record; - LdapLayer(std::unique_ptr& asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + LdapLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); LdapLayer() = default; void init(uint16_t messageId, LdapOperationType operationType, const std::vector& messageRecords, const std::vector& controls); virtual std::string getExtendedStringInfo() const { return ""; } @@ -266,6 +288,277 @@ namespace pcpp } }; + /** + * @class LdapSearchRequestLayer + * Represents LDAP search request operation + */ + class LdapSearchRequestLayer : public LdapLayer + { + public: + /** + * @class SearchRequestScope + * An enum wrapper class for LDAP search request scope + */ + class SearchRequestScope + { + public: + /** + * Define enum types and the corresponding int values + */ + enum Value : uint8_t + { + /** + * The search operation should only be performed against the entry specified as the search base DN + */ + BaseObject = 0, + /** + * The search operation should only be performed against entries that are immediate subordinates + * of the entry specified as the search base DN + */ + SingleLevel = 1, + /** + * The search operation should be performed against the entry specified as the search base + * and all of its subordinates to any depth + */ + WholeSubtree = 2, + /** + * The search operation should be performed against any subordinate entries (to any depth) below the + * entry specified by the base DN should be considered, but the base entry itself + * should not be considered + */ + subordinateSubtree = 3, + /** + * Unknown or unsupported scope + */ + Unknown = 255 + }; + + SearchRequestScope() = default; + + // cppcheck-suppress noExplicitConstructor + /** + * Construct SearchRequestScope from Value enum + * @param[in] value the scope enum value + */ + constexpr SearchRequestScope(Value value) : m_Value(value) {} + + /** + * @return A string representation of the scope value + */ + std::string toString() const; + + /** + * A static method that creates SearchRequestScope from an integer value + * @param[in] value The scope integer value + * @return The scope that corresponds to the integer value. If the integer value + * doesn't corresponds to any enum value, SearchRequestScope::Unknown is returned + */ + static SearchRequestScope fromUintValue(uint8_t value); + + // Allow switch and comparisons. + constexpr operator Value() const { return m_Value; } + + // Prevent usage: if(LdapOperationType) + explicit operator bool() const = delete; + private: + Value m_Value = SearchRequestScope::Unknown; + }; + + /** + * @class DerefAliases + * An enum wrapper class for LDAP search request dereferencing aliases + */ + class DerefAliases + { + public: + /** + * Define enum types and the corresponding int values + */ + enum Value : uint8_t + { + /// Never dereferences aliases + NeverDerefAliases = 0, + /// Dereferences aliases only after name resolution + DerefInSearching = 1, + /// Dereferences aliases only during name resolution + DerefFindingBaseObj = 2, + /// Always dereference aliases + DerefAlways = 3, + /// Unknown value + Unknown = 255 + }; + + DerefAliases() = default; + + // cppcheck-suppress noExplicitConstructor + /** + * Construct DerefAliases from Value enum + * @param[in] value the dereference alias enum value + */ + constexpr DerefAliases(Value value) : m_Value(value) {} + + /** + * @return A string representation of the dereference alias value + */ + std::string toString() const; + + /** + * A static method that creates DerefAliases from an integer value + * @param[in] value The dereference alias integer value + * @return The dereference alias that corresponds to the integer value. If the integer value + * doesn't corresponds to any enum value, DerefAliases::Unknown is returned + */ + static DerefAliases fromUintValue(uint8_t value); + + // Allow switch and comparisons. + constexpr operator Value() const { return m_Value; } + + // Prevent usage: if(LdapOperationType) + explicit operator bool() const = delete; + private: + Value m_Value = DerefAliases::Unknown; + }; + + /** + * A constructor to create a new LDAP search request message + * @param[in] messageId The LDAP message ID + * @param[in] baseObject The base object for the LDAP search request entry + * @param[in] scope The portion of the target subtree that should be considered + * @param[in] derefAliases The alias dereferencing behavior, which indicates how the server should treat + * any aliases encountered while processing the search + * @param[in] sizeLimit The maximum number of entries that should be returned from the search + * @param[in] timeLimit The time limit for the search in seconds + * @param[in] typesOnly If this is given a value of true, then it indicates that entries that match the + * search criteria should be returned containing only the attribute descriptions for the attributes + * contained in that entry but should not include the values for those attributes. + * If this is given a value of false, then it indicates that the attribute values should be included + * in the entries that are returned + * @param[in] filterRecord The filter for the search. Please note that parsing for the search filter + * doesn't exist yet. Therefore, the expected input value should be a plain ASN.1 record + * @param[in] attributes A set of attributes to request for inclusion in entries that match the search + * criteria and are returned to the client + * @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message + * will be created without LDAP controls + */ + LdapSearchRequestLayer( + uint16_t messageId, const std::string& baseObject, SearchRequestScope scope, DerefAliases derefAliases, + uint8_t sizeLimit, uint8_t timeLimit, bool typesOnly, Asn1Record* filterRecord, + const std::vector& attributes, const std::vector& controls = std::vector()); + + /** + * @return The base object for the LDAP search request entry + */ + std::string getBaseObject() const; + + /** + * @return The portion of the target subtree that should be considered + */ + SearchRequestScope getScope() const; + + /** + * @return The alias dereferencing behavior + */ + DerefAliases getDerefAlias() const; + + /** + * @return The maximum number of entries that should be returned from the search + */ + uint8_t getSizeLimit() const; + + /** + * @return The time limit for the search in seconds + */ + uint8_t getTimeLimit() const; + + /** + * @return If this flag is true, then it indicates that entries that match the search criteria should be + * returned containing only the attribute descriptions for the attributes contained in that entry but + * should not include the values for those attributes. If this flag is false, then it indicates that the + * attribute values should be included in the entries that are returned + */ + bool getTypesOnly() const; + + /** + * @return The filter for the search. Please note that parsing for the search filter doesn't exist yet. + * Therefore, the return value is a plain ASN.1 record + */ + Asn1Record* getFilter() const; + + /** + * @return A list of search request attributes + */ + std::vector getAttributes() const; + + template + bool tryGet(Method method, ResultType& result) + { + return internalTryGet(this, method, result); + } + + protected: + friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + static constexpr int baseObjectIndex = 0; + static constexpr int scopeIndex = 1; + static constexpr int derefAliasIndex = 2; + static constexpr int sizeLimitIndex = 3; + static constexpr int timeLimitIndex = 4; + static constexpr int typesOnlyIndex = 5; + static constexpr int filterIndex = 6; + static constexpr int attributesIndex = 7; + + LdapSearchRequestLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : LdapLayer(std::move(asn1Record), data, dataLen, prevLayer, packet) {} + + + std::string getExtendedStringInfo() const override; + }; + + /** + * @class LdapSearchResultEntryLayer + * Represents LDAP search result entry message + */ + class LdapSearchResultEntryLayer : public LdapLayer + { + public: + /** + * A constructor to create a new LDAP search result entry message + * @param[in] messageId The LDAP message ID + * @param[in] objectName The entry's DN + * @param[in] attributes The entry's attributes + * @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message + * will be created without LDAP controls + */ + LdapSearchResultEntryLayer(uint16_t messageId, const std::string& objectName, + const std::vector& attributes, + const std::vector& controls = std::vector()); + + /** + * @return The entry's DN + */ + std::string getObjectName() const; + + /** + * @return The entry's attributes + */ + std::vector getAttributes() const; + + template + bool tryGet(Method method, ResultType& result) + { + return internalTryGet(this, method, result); + } + protected: + friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + static constexpr int objectNameIndex = 0; + static constexpr int attributesIndex = 1; + static constexpr int attributeTypeIndex = 0; + static constexpr int attributeValueIndex = 1; + + LdapSearchResultEntryLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : LdapLayer(std::move(asn1Record), data, dataLen, prevLayer, packet) {} + }; } // namespace pcpp inline std::ostream& operator<<(std::ostream& os, const pcpp::LdapControl& control) @@ -273,3 +566,17 @@ inline std::ostream& operator<<(std::ostream& os, const pcpp::LdapControl& contr os << "{" << control.controlType << ", " << control.controlValue << "}"; return os; } + +inline std::ostream& operator<<(std::ostream& os, const pcpp::LdapAttribute& attr) +{ + std::string valuesStream; + bool first = true; + for (const auto& value : attr.values) + { + if (!first) valuesStream += ", "; + valuesStream += value; + first = false; + } + os << "{" << attr.type << ", {" << valuesStream << "}}"; + return os; +} diff --git a/Packet++/src/LdapLayer.cpp b/Packet++/src/LdapLayer.cpp index 1a4cb06e8..1b0eea236 100644 --- a/Packet++/src/LdapLayer.cpp +++ b/Packet++/src/LdapLayer.cpp @@ -80,7 +80,7 @@ namespace pcpp { init(messageId, operationType, messageRecords, controls); } - LdapLayer::LdapLayer(std::unique_ptr& asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) + LdapLayer::LdapLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = LDAP; m_Asn1Record = std::move(asn1Record); @@ -139,12 +139,17 @@ namespace pcpp { { auto asn1Record = Asn1Record::decode(data, dataLen, true); auto operationType = LdapOperationType::fromUintValue(asn1Record->castAs()->getSubRecords().at(operationTypeIndex)->getTagType()); - if (operationType != LdapOperationType::Unknown) + switch (operationType) { - return new LdapLayer(asn1Record, data, dataLen, prevLayer, packet); + case LdapOperationType::SearchRequest: + return new LdapSearchRequestLayer(std::move(asn1Record), data, dataLen, prevLayer, packet); + case LdapOperationType::SearchResultEntry: + return new LdapSearchResultEntryLayer(std::move(asn1Record), data, dataLen, prevLayer, packet); + case LdapOperationType::Unknown: + return nullptr; + default: + return new LdapLayer(std::move(asn1Record), data, dataLen, prevLayer, packet); } - - return nullptr; } catch (...) { @@ -208,4 +213,197 @@ namespace pcpp { m_NextLayer = LdapLayer::parseLdapMessage(payload, payloadLen, this, m_Packet); } // endregion + + // region LdapSearchRequestLayer + + const std::unordered_map> SearchRequestScopeToString { + {LdapSearchRequestLayer::SearchRequestScope::BaseObject, "BaseObject"}, + {LdapSearchRequestLayer::SearchRequestScope::SingleLevel, "SingleLevel"}, + {LdapSearchRequestLayer::SearchRequestScope::WholeSubtree, "WholeSubtree"}, + {LdapSearchRequestLayer::SearchRequestScope::Unknown, "Unknown"} + }; + + const std::unordered_map> DerefAliasesToString { + {LdapSearchRequestLayer::DerefAliases::NeverDerefAliases, "NeverDerefAliases"}, + {LdapSearchRequestLayer::DerefAliases::DerefInSearching, "DerefInSearching"}, + {LdapSearchRequestLayer::DerefAliases::DerefFindingBaseObj, "DerefFindingBaseObj"}, + {LdapSearchRequestLayer::DerefAliases::DerefAlways, "DerefAlways"}, + {LdapSearchRequestLayer::DerefAliases::Unknown, "Unknown"} + }; + + std::string LdapSearchRequestLayer::SearchRequestScope::toString() const + { + return SearchRequestScopeToString.at(m_Value); + } + + LdapSearchRequestLayer::SearchRequestScope LdapSearchRequestLayer::SearchRequestScope::fromUintValue(uint8_t value) + { + if (value >= 0 && value <= 2) + { + return static_cast(value); + } + + return LdapSearchRequestLayer::SearchRequestScope::Unknown; + } + + std::string LdapSearchRequestLayer::DerefAliases::toString() const + { + return DerefAliasesToString.at(m_Value); + } + + LdapSearchRequestLayer::DerefAliases LdapSearchRequestLayer::DerefAliases::fromUintValue(uint8_t value) + { + if (value >= 0 && value <= 3) + { + return static_cast(value); + } + + return LdapSearchRequestLayer::DerefAliases::Unknown; + } + + LdapSearchRequestLayer::LdapSearchRequestLayer( + uint16_t messageId, const std::string& baseObject, SearchRequestScope scope, DerefAliases derefAliases, + uint8_t sizeLimit, uint8_t timeLimit, bool typesOnly, Asn1Record* filterRecord, + const std::vector& attributes, const std::vector& controls) + { + Asn1OctetStringRecord baseObjectRecord(baseObject); + Asn1EnumeratedRecord scopeRecord(scope); + Asn1EnumeratedRecord derefAliasesRecord(derefAliases); + Asn1IntegerRecord sizeLimitRecord(sizeLimit); + Asn1IntegerRecord timeLimitRecord(timeLimit); + Asn1BooleanRecord typeOnlyRecord(typesOnly); + + PointerVector attributeSubRecords; + for (const auto& attribute : attributes) + { + attributeSubRecords.pushBack(new Asn1OctetStringRecord(attribute)); + } + Asn1SequenceRecord attributesRecord(attributeSubRecords); + + LdapLayer::init(messageId, LdapOperationType::SearchRequest, {&baseObjectRecord, &scopeRecord, &derefAliasesRecord, &sizeLimitRecord, &timeLimitRecord, &typeOnlyRecord, filterRecord, &attributesRecord}, controls); + } + + std::string LdapSearchRequestLayer::getBaseObject() const + { + return getLdapOperationAsn1Record()->getSubRecords().at(baseObjectIndex)->castAs()->getValue(); + } + + LdapSearchRequestLayer::SearchRequestScope LdapSearchRequestLayer::getScope() const + { + return LdapSearchRequestLayer::SearchRequestScope::fromUintValue(getLdapOperationAsn1Record()->getSubRecords().at(scopeIndex)->castAs()->getValue()); + } + + LdapSearchRequestLayer::DerefAliases LdapSearchRequestLayer::getDerefAlias() const + { + return LdapSearchRequestLayer::DerefAliases::fromUintValue(getLdapOperationAsn1Record()->getSubRecords().at(derefAliasIndex)->castAs()->getValue()); + } + + uint8_t LdapSearchRequestLayer::getSizeLimit() const + { + return static_cast(getLdapOperationAsn1Record()->getSubRecords().at(sizeLimitIndex)->castAs()->getValue()); + } + + uint8_t LdapSearchRequestLayer::getTimeLimit() const + { + return static_cast(getLdapOperationAsn1Record()->getSubRecords().at(timeLimitIndex)->castAs()->getValue()); + } + + bool LdapSearchRequestLayer::getTypesOnly() const + { + return getLdapOperationAsn1Record()->getSubRecords().at(typesOnlyIndex)->castAs()->getValue(); + } + + Asn1Record* LdapSearchRequestLayer::getFilter() const + { + return getLdapOperationAsn1Record()->getSubRecords().at(filterIndex); + } + + std::vector LdapSearchRequestLayer::getAttributes() const + { + std::vector result; + if (getLdapOperationAsn1Record()->getSubRecords().size() <= attributesIndex) + { + return result; + } + + auto attributesRecord = getLdapOperationAsn1Record()->getSubRecords().at(attributesIndex)->castAs(); + for (auto attribute : attributesRecord->getSubRecords()) + { + result.push_back(attribute->castAs()->getValue()); + } + + return result; + } + + std::string LdapSearchRequestLayer::getExtendedStringInfo() const + { + auto baseObject = getBaseObject(); + if (baseObject.empty()) + { + baseObject = "ROOT"; + } + + return "\"" + baseObject + "\", " + getScope().toString(); + } + + // endregion + + // region LdapSearchResultEntryLayer + + LdapSearchResultEntryLayer::LdapSearchResultEntryLayer(uint16_t messageId, const std::string& objectName, + const std::vector& attributes, const std::vector& controls) + { + PointerVector attributesSubRecords; + for (const auto& attribute : attributes) + { + PointerVector valuesSubRecords; + for (const auto& value : attribute.values) + { + valuesSubRecords.pushBack(new Asn1OctetStringRecord(value)); + } + + Asn1OctetStringRecord typeRecord(attribute.type); + Asn1SetRecord valuesRecord(valuesSubRecords); + + attributesSubRecords.pushBack(new Asn1SequenceRecord({&typeRecord, &valuesRecord})); + } + + Asn1OctetStringRecord objectNameRecord(objectName); + Asn1SequenceRecord attributesRecord(attributesSubRecords); + + LdapLayer::init(messageId, LdapOperationType::SearchResultEntry, {&objectNameRecord, &attributesRecord}, controls); + } + + std::string LdapSearchResultEntryLayer::getObjectName() const + { + return getLdapOperationAsn1Record()->getSubRecords().at(objectNameIndex)->castAs()->getValue(); + } + + std::vector LdapSearchResultEntryLayer::getAttributes() const + { + std::vector result; + + auto attributes = getLdapOperationAsn1Record()->getSubRecords().at(attributesIndex)->castAs(); + for (auto attributeRecord : attributes->getSubRecords()) + { + auto attrAsSequence = attributeRecord->castAs(); + + auto type = attrAsSequence->getSubRecords().at(attributeTypeIndex)->castAs()->getValue(); + + std::vector values; + auto valuesRecord = attrAsSequence->getSubRecords().at(attributeValueIndex)->castAs(); + + for (auto valueRecord : valuesRecord->getSubRecords()) + { + values.push_back(valueRecord->castAs()->getValue()); + } + + LdapAttribute ldapAttribute = {type, values}; + result.push_back(ldapAttribute); + } + + return result; + } + + // endregion } diff --git a/README.md b/README.md index 5961ca32a..d08afc8f6 100644 --- a/README.md +++ b/README.md @@ -256,14 +256,15 @@ PcapPlusPlus currently supports parsing, editing and creation of packets of the 38. DNS 39. FTP 40. HTTP headers (request & response) -41. NTP (v3, v4) -42. Radius -43. S7 Communication (S7comm) -44. SMTP -45. SOME/IP -46. SSH - parsing only (no editing capabilities) -47. Telnet - parsing only (no editing capabilities) -48. Generic payload +41. LDAP +42. NTP (v3, v4) +43. Radius +44. S7 Communication (S7comm) +45. SMTP +46. SOME/IP +47. SSH - parsing only (no editing capabilities) +48. Telnet - parsing only (no editing capabilities) +49. Generic payload ## DPDK And PF_RING Support diff --git a/Tests/Packet++Test/PacketExamples/ldap_search_request2.dat b/Tests/Packet++Test/PacketExamples/ldap_search_request2.dat new file mode 100644 index 000000000..a5da80676 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_search_request2.dat @@ -0,0 +1 @@ +08b4b11a46ad0800272cf6a20800450001181ac6400040063178c0a856683439a25889f101857a93bd5e47364582801801f5eeac00000101080aec65d6aff706caa03081e10201096381db0409636e3d736368656d610a01000a0103020100020100010100a318040b6f626a656374436c6173730409737562736368656d613081a4040d6f626a656374436c6173736573040e6174747269627574655479706573040c6c64617053796e7461786573040d6d61746368696e6752756c6573040f6d61746368696e6752756c65557365040f644954436f6e74656e7452756c6573041164495453747275637475726552756c657304096e616d65466f726d73040f63726561746554696d657374616d70040f6d6f6469667954696d657374616d7004012a04012b \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/ldap_search_result_entry.dat b/Tests/Packet++Test/PacketExamples/ldap_search_result_entry.dat new file mode 100644 index 000000000..bf5b8f6e4 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_search_result_entry.dat @@ -0,0 +1 @@ +0800272cf6a208b4b11a46ad0800450000f30e60400023065b033439a258c0a85668018589f1473d08af7a93c15b801801e5bd7f00000101080af70a67c3ec6973263081bc0201106481b60437636e3d622e736d6974682c6f753d6c646170332d7475746f7269616c2c64633d64656d6f312c64633d667265656970612c64633d6f7267307b3041040b6f626a656374636c6173733132040d696e65744f7267506572736f6e04146f7267616e697a6174696f6e616c506572736f6e0406706572736f6e0403746f70300d0402736e31070405596f756e67300f0402636e31090407622e736d69746830160409676976656e6e616d653109040742656174726978 \ No newline at end of file diff --git a/Tests/Packet++Test/Tests/LdapTests.cpp b/Tests/Packet++Test/Tests/LdapTests.cpp index 3d2e84d50..b146c2f30 100644 --- a/Tests/Packet++Test/Tests/LdapTests.cpp +++ b/Tests/Packet++Test/Tests/LdapTests.cpp @@ -104,6 +104,65 @@ PTF_TEST_CASE(LdapParsingTest) PTF_ASSERT_EQUAL(ldapLayer->getLdapOperationType(), pcpp::LdapOperationType::SearchResultDone, enum); PTF_ASSERT_NULL(ldapLayer->getNextLayer()); } + + // SearchRequest + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_search_request2.dat"); + pcpp::Packet searchRequestPacket(&rawPacket1); + + auto searchRequestLayer = searchRequestPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(searchRequestLayer); + PTF_ASSERT_EQUAL(searchRequestLayer->getMessageID(), 9); + PTF_ASSERT_EQUAL(searchRequestLayer->getLdapOperationType(), pcpp::LdapOperationType::SearchRequest, enum); + PTF_ASSERT_EQUAL(searchRequestLayer->getBaseObject(), "cn=schema"); + PTF_ASSERT_EQUAL(searchRequestLayer->getScope(), pcpp::LdapSearchRequestLayer::SearchRequestScope::BaseObject, enum); + PTF_ASSERT_EQUAL(searchRequestLayer->getDerefAlias(), pcpp::LdapSearchRequestLayer::DerefAliases::DerefAlways, enum); + PTF_ASSERT_EQUAL(searchRequestLayer->getSizeLimit(), 0); + PTF_ASSERT_EQUAL(searchRequestLayer->getTimeLimit(), 0); + PTF_ASSERT_FALSE(searchRequestLayer->getTypesOnly()); + std::ostringstream expectedFilter; + expectedFilter + << "ContextSpecific (3) (constructed), Length: 2+24" << std::endl + << " OctetString, Length: 2+11, Value: objectClass" << std::endl + << " OctetString, Length: 2+9, Value: subschema"; + PTF_ASSERT_EQUAL(searchRequestLayer->getFilter()->toString(), expectedFilter.str()); + PTF_ASSERT_EQUAL(searchRequestLayer->toString(), "LDAP Layer, SearchRequest, \"cn=schema\", BaseObject"); + auto attributes = searchRequestLayer->getAttributes(); + std::vector expectedAttributes = { + "objectClasses", + "attributeTypes", + "ldapSyntaxes", + "matchingRules", + "matchingRuleUse", + "dITContentRules", + "dITStructureRules", + "nameForms", + "createTimestamp", + "modifyTimestamp", + "*", + "+" + }; + PTF_ASSERT_VECTORS_EQUAL(attributes, expectedAttributes); + } + + // SearchResultEntry + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_search_result_entry.dat"); + pcpp::Packet searchResEntryPacket(&rawPacket1); + + auto searchResultEntryLayer = searchResEntryPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(searchResultEntryLayer); + PTF_ASSERT_EQUAL(searchResultEntryLayer->getMessageID(), 16); + PTF_ASSERT_EQUAL(searchResultEntryLayer->getLdapOperationType(), pcpp::LdapOperationType::SearchResultEntry, enum); + PTF_ASSERT_EQUAL(searchResultEntryLayer->getObjectName(), "cn=b.smith,ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org"); + std::vector expectedAttributes = { + {"objectclass", {"inetOrgPerson", "organizationalPerson", "person", "top"}}, + {"sn", {"Young"}}, + {"cn", {"b.smith"}}, + {"givenname", {"Beatrix"}} + }; + PTF_ASSERT_VECTORS_EQUAL(searchResultEntryLayer->getAttributes(), expectedAttributes); + } } // LdapParsingTest @@ -166,4 +225,61 @@ PTF_TEST_CASE(LdapCreationTest) PTF_ASSERT_BUF_COMPARE(ldapLayer.getData(), expectedLdapLayer->getData(), expectedLdapLayer->getDataLen()); } + + // SearchRequest + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_search_request2.dat"); + pcpp::Packet searchRequestPacket(&rawPacket1); + + pcpp::Asn1OctetStringRecord filterSubRecord1("objectClass"); + pcpp::Asn1OctetStringRecord filterSubRecord2("subschema"); + pcpp::Asn1ConstructedRecord filterRecord(pcpp::Asn1TagClass::ContextSpecific, 3, {&filterSubRecord1, &filterSubRecord2}); + + std::vector attributes = { + "objectClasses", + "attributeTypes", + "ldapSyntaxes", + "matchingRules", + "matchingRuleUse", + "dITContentRules", + "dITStructureRules", + "nameForms", + "createTimestamp", + "modifyTimestamp", + "*", + "+" + }; + + pcpp::LdapSearchRequestLayer searchRequestLayer( + 9, "cn=schema", pcpp::LdapSearchRequestLayer::SearchRequestScope::BaseObject, + pcpp::LdapSearchRequestLayer::DerefAliases::DerefAlways, + 0, 0, false, &filterRecord, attributes); + + auto expectedSearchRequestLayer = searchRequestPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedSearchRequestLayer); + + PTF_ASSERT_BUF_COMPARE(searchRequestLayer.getData(), expectedSearchRequestLayer->getData(), + expectedSearchRequestLayer->getDataLen()); + } + + // SearchResultEntry + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_search_result_entry.dat"); + pcpp::Packet searchResultEntryPacket(&rawPacket1); + + std::vector attributes = { + {"objectclass", {"inetOrgPerson", "organizationalPerson", "person", "top"}}, + {"sn", {"Young"}}, + {"cn", {"b.smith"}}, + {"givenname", {"Beatrix"}} + }; + + pcpp::LdapSearchResultEntryLayer searchResultEntryLayer(16, "cn=b.smith,ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org", attributes); + + auto expectedSearchResultEntryLayer = searchResultEntryPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedSearchResultEntryLayer); + + PTF_ASSERT_BUF_COMPARE(searchResultEntryLayer.getData(), expectedSearchResultEntryLayer->getData(), + expectedSearchResultEntryLayer->getDataLen()); + } } // LdapCreationTest From f9c29f75bd83a0aebbcc4aac3cb65fb358944fb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20P=C3=A9ron?= Date: Mon, 3 Jun 2024 09:06:24 +0200 Subject: [PATCH 2/6] github: ci: fix pipeline due to bump of GCC in mingw (#1425) --- 3rdParty/LightPcapNg/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/3rdParty/LightPcapNg/CMakeLists.txt b/3rdParty/LightPcapNg/CMakeLists.txt index 2f10732f0..cc79a42b3 100644 --- a/3rdParty/LightPcapNg/CMakeLists.txt +++ b/3rdParty/LightPcapNg/CMakeLists.txt @@ -24,6 +24,13 @@ add_library( target_compile_definitions(light_pcapng PUBLIC -DUNIVERSAL) +# FIXME: https://github.com/seladb/PcapPlusPlus/issues/1347 +include(CheckCCompilerFlag) +check_c_compiler_flag(-Wincompatible-pointer-types HAVE_W_INCOMPATIBLE_POINTER_TYPES) +if(HAVE_W_INCOMPATIBLE_POINTER_TYPES) + target_compile_options(light_pcapng PRIVATE -Wno-incompatible-pointer-types) +endif() + if(BUILD_SHARED_LIBS) set_property(TARGET light_pcapng PROPERTY POSITION_INDEPENDENT_CODE ON) endif() From b20c6f8406965865185e78622e99a4409ceb2496 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 00:37:58 -0700 Subject: [PATCH 3/6] Bump actions/upload-artifact from 4.3.1 to 4.3.3 (#1418) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.1 to 4.3.3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.3.1...65462800fd760344b1a7b4382951275a0abb4808) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/cifuzz.yml | 2 +- .github/workflows/package.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index 3931f62dd..84805e64a 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -31,7 +31,7 @@ jobs: dry-run: false sanitizer: ${{ matrix.sanitizer }} - name: Upload Crash - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 if: failure() && steps.build.outcome == 'success' with: name: artifacts diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 5ac3e1362..cb9710864 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -290,7 +290,7 @@ jobs: mkdir -p "android-package" mv "${COMBINED_PACKAGE_DIR}" "android-package" - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: path: android-package name: android-package-${{ matrix.target }}-${{ matrix.api-version }} From 817b0f0a07aa1d2cf523368b985b96d9026ff264 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 01:43:58 -0700 Subject: [PATCH 4/6] Bump msys2/setup-msys2 from 2.22.0 to 2.23.0 (#1422) Bumps [msys2/setup-msys2](https://github.com/msys2/setup-msys2) from 2.22.0 to 2.23.0. - [Release notes](https://github.com/msys2/setup-msys2/releases) - [Changelog](https://github.com/msys2/setup-msys2/blob/main/CHANGELOG.md) - [Commits](https://github.com/msys2/setup-msys2/compare/cc11e9188b693c2b100158c3322424c4cc1dadea...d0e80f58dffbc64f6a3a1f43527d469b4fc7b6c8) --- updated-dependencies: - dependency-name: msys2/setup-msys2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build_and_test.yml | 2 +- .github/workflows/package.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index c7bfb4beb..ccfd2ce65 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -404,7 +404,7 @@ jobs: uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Setup MSYS2 - uses: msys2/setup-msys2@cc11e9188b693c2b100158c3322424c4cc1dadea # v2.22.0 + uses: msys2/setup-msys2@d0e80f58dffbc64f6a3a1f43527d469b4fc7b6c8 # v2.23.0 with: msystem: ${{matrix.sys}} install: >- diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index cb9710864..b3fa3ae82 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -154,7 +154,7 @@ jobs: uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Setup MSYS2 - uses: msys2/setup-msys2@cc11e9188b693c2b100158c3322424c4cc1dadea # v2.22.0 + uses: msys2/setup-msys2@d0e80f58dffbc64f6a3a1f43527d469b4fc7b6c8 # v2.23.0 with: msystem: ${{matrix.sys}} update: true From 001b2bc56e204b1f7242d6b950e202d8561d1571 Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Tue, 4 Jun 2024 10:37:40 +0300 Subject: [PATCH 5/6] Cpp casts and const changes. (#1428) * Fixed uninitialized variable. * Marked function as const. * Fixed potential overflow warning. * Replaced C-style casts -> Cpp casts. * C cast -> Cpp cast * fixed cppcheck suggestion * Simplified nullptr check. --- Packet++/header/IPv4Layer.h | 32 ++++++++++++++++---------------- Packet++/header/IPv6Layer.h | 6 +++--- Packet++/src/IPv6Layer.cpp | 3 +-- Pcap++/header/PcapLiveDevice.h | 2 +- Pcap++/src/PcapLiveDevice.cpp | 4 ++-- Pcap++/src/PcapRemoteDevice.cpp | 2 +- 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/Packet++/header/IPv4Layer.h b/Packet++/header/IPv4Layer.h index b98709022..7e1e82633 100644 --- a/Packet++/header/IPv4Layer.h +++ b/Packet++/header/IPv4Layer.h @@ -242,9 +242,9 @@ namespace pcpp if (dataSize < 2) return res; - uint8_t valueOffset = (uint8_t)(1); + uint8_t valueOffset = static_cast(1); - while ((size_t)valueOffset < dataSize) + while (static_cast(valueOffset) < dataSize) { uint32_t curValue; memcpy(&curValue, m_Data->recordValue + valueOffset, sizeof(uint32_t)); @@ -253,7 +253,7 @@ namespace pcpp res.push_back(IPv4Address(curValue)); - valueOffset += (uint8_t)(4); + valueOffset += static_cast(4); } return res; @@ -281,12 +281,12 @@ namespace pcpp if (dataSize < 2) return res; - res.type = (IPv4TimestampOptionValue::TimestampType)m_Data->recordValue[1]; + res.type = static_cast(m_Data->recordValue[1]); - uint8_t valueOffset = (uint8_t)(2); + uint8_t valueOffset = static_cast(2); bool readIPAddr = (res.type == IPv4TimestampOptionValue::TimestampAndIP); - while ((size_t)valueOffset < dataSize) + while (static_cast(valueOffset) < dataSize) { uint32_t curValue; memcpy(&curValue, m_Data->recordValue + valueOffset, sizeof(uint32_t)); @@ -301,7 +301,7 @@ namespace pcpp if (res.type == IPv4TimestampOptionValue::TimestampAndIP) readIPAddr = !readIPAddr; - valueOffset += (uint8_t)(4); + valueOffset += static_cast(4); } return res; @@ -323,14 +323,14 @@ namespace pcpp */ static bool canAssign(const uint8_t* recordRawData, size_t tlvDataLen) { - auto data = (TLVRawData*)recordRawData; + auto data = reinterpret_cast(recordRawData); if (data == nullptr) return false; if (tlvDataLen < sizeof(TLVRawData::recordType)) return false; - if (getIPv4OptionType(data) == (uint8_t)IPV4OPT_EndOfOptionsList || data->recordType == (uint8_t)IPV4OPT_NOP) + if (getIPv4OptionType(data) == static_cast(IPV4OPT_EndOfOptionsList) || data->recordType == static_cast(IPV4OPT_NOP)) return true; return TLVRecord::canAssign(recordRawData, tlvDataLen); @@ -343,10 +343,10 @@ namespace pcpp if (m_Data == nullptr) return 0; - if (getIPv4OptionType() == (uint8_t)IPV4OPT_EndOfOptionsList || m_Data->recordType == (uint8_t)IPV4OPT_NOP) + if (getIPv4OptionType() == static_cast(IPV4OPT_EndOfOptionsList) || m_Data->recordType == static_cast(IPV4OPT_NOP)) return sizeof(uint8_t); - return (size_t)m_Data->recordLen; + return static_cast(m_Data->recordLen); } size_t getDataSize() const @@ -354,10 +354,10 @@ namespace pcpp if (m_Data == nullptr) return 0; - if (getIPv4OptionType() == (uint8_t)IPV4OPT_EndOfOptionsList || m_Data->recordType == (uint8_t)IPV4OPT_NOP) - return (size_t)0; + if (getIPv4OptionType() == static_cast(IPV4OPT_EndOfOptionsList) || m_Data->recordType == static_cast(IPV4OPT_NOP)) + return 0; - return (size_t)m_Data->recordLen - (2*sizeof(uint8_t)); + return static_cast(m_Data->recordLen) - (2*sizeof(uint8_t)); } private: @@ -369,7 +369,7 @@ namespace pcpp if (data == nullptr) return IPV4OPT_Unknown; - return (IPv4OptionTypes)data->recordType; + return static_cast(data->recordType); } }; @@ -637,7 +637,7 @@ namespace pcpp /** * @return Size of IPv4 header (including IPv4 options if exist) */ - size_t getHeaderLen() const { return (size_t)((uint16_t)(getIPv4Header()->internetHeaderLength) * 4) + m_TempHeaderExtension; } + size_t getHeaderLen() const { return static_cast(static_cast(getIPv4Header()->internetHeaderLength) * 4) + m_TempHeaderExtension; } /** * Calculate the following fields: diff --git a/Packet++/header/IPv6Layer.h b/Packet++/header/IPv6Layer.h index 584dbcae7..f603083d5 100644 --- a/Packet++/header/IPv6Layer.h +++ b/Packet++/header/IPv6Layer.h @@ -231,19 +231,19 @@ namespace pcpp while (curExt != NULL && dynamic_cast(curExt) == NULL) curExt = curExt->getNextHeader(); - return (TIPv6Extension*)curExt; + return static_cast(curExt); } template TIPv6Extension* IPv6Layer::addExtension(const TIPv6Extension& extensionHeader) { - int offsetToAddHeader = (int)getHeaderLen(); + int offsetToAddHeader = static_cast(getHeaderLen()); if (!extendLayer(offsetToAddHeader, extensionHeader.getExtensionLen())) { return NULL; } - TIPv6Extension* newHeader = new TIPv6Extension(this, (size_t)offsetToAddHeader); + TIPv6Extension* newHeader = new TIPv6Extension(this, static_cast(offsetToAddHeader)); (*newHeader) = extensionHeader; if (m_FirstExtension != NULL) diff --git a/Packet++/src/IPv6Layer.cpp b/Packet++/src/IPv6Layer.cpp index 72d97657a..f6b5d7854 100644 --- a/Packet++/src/IPv6Layer.cpp +++ b/Packet++/src/IPv6Layer.cpp @@ -187,8 +187,7 @@ void IPv6Layer::removeAllExtensions() bool IPv6Layer::isFragment() const { - IPv6FragmentationHeader* fragHdr = getExtensionOfType(); - return (fragHdr != nullptr); + return getExtensionOfType() != nullptr; } void IPv6Layer::parseNextLayer() diff --git a/Pcap++/header/PcapLiveDevice.h b/Pcap++/header/PcapLiveDevice.h index d6dd5756d..e4b88ba2f 100644 --- a/Pcap++/header/PcapLiveDevice.h +++ b/Pcap++/header/PcapLiveDevice.h @@ -438,7 +438,7 @@ namespace pcpp * @param[in] packetPayloadLength The length of the IP layer of the packet * @return True if the packetPayloadLength is less than or equal to the device MTU */ - bool doMtuCheck(int packetPayloadLength); + bool doMtuCheck(int packetPayloadLength) const; /** * Send a RawPacket to the network diff --git a/Pcap++/src/PcapLiveDevice.cpp b/Pcap++/src/PcapLiveDevice.cpp index e368d71b5..7a55a0a2b 100644 --- a/Pcap++/src/PcapLiveDevice.cpp +++ b/Pcap++/src/PcapLiveDevice.cpp @@ -75,7 +75,7 @@ static pcap_direction_t directionTypeMap(PcapLiveDevice::PcapDirection direction PcapLiveDevice::PcapLiveDevice(pcap_if_t* pInterface, bool calculateMTU, bool calculateMacAddress, bool calculateDefaultGateway) : - IPcapDevice(), m_PcapSelectableFd(-1), m_DefaultGateway(IPv4Address::Zero), m_UsePoll(false) + IPcapDevice(), m_PcapSendDescriptor(nullptr), m_PcapSelectableFd(-1), m_DefaultGateway(IPv4Address::Zero), m_UsePoll(false) { m_DeviceMtu = 0; m_LinkType = LINKTYPE_ETHERNET; @@ -677,7 +677,7 @@ void PcapLiveDevice::getStatistics(PcapStats& stats) const stats.packetsDropByInterface = pcapStats.ps_ifdrop; } -bool PcapLiveDevice::doMtuCheck(int packetPayloadLength) +bool PcapLiveDevice::doMtuCheck(int packetPayloadLength) const { if (packetPayloadLength > static_cast(m_DeviceMtu)) { diff --git a/Pcap++/src/PcapRemoteDevice.cpp b/Pcap++/src/PcapRemoteDevice.cpp index b08733f10..ac1ae9c46 100644 --- a/Pcap++/src/PcapRemoteDevice.cpp +++ b/Pcap++/src/PcapRemoteDevice.cpp @@ -119,7 +119,7 @@ void PcapRemoteDevice::getStatistics(PcapStats& stats) const return; } stats.packetsRecv = tempStats->ps_capt; - stats.packetsDrop = tempStats->ps_drop + tempStats->ps_netdrop; + stats.packetsDrop = static_cast(tempStats->ps_drop) + tempStats->ps_netdrop; stats.packetsDropByInterface = tempStats->ps_ifdrop; } From df105a79e4549a5bfa7a8244c31d9b3336cc7c5e Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Tue, 4 Jun 2024 13:26:04 +0300 Subject: [PATCH 6/6] Refactor of IP utils to use exceptions on invalid input. (#1426) * Changed IpUtils to raise exceptions on invalid input instead of returning nullptr. * Added 'try' methods for extraction of IP address from socket address. * Updated calls to sockaddr2* to try_sockaddr2* to keep current functionality. * Lint * Added overload of sockaddr2string that returns std::string. * Added debug logs about failed extractions. * Lint * Removed 'struct' keywords from function parameters. * Changed C-style cast to Cpp cast * Changed reference to pointer as most usages use pointer. * C cast -> Cpp cast * Added length parameter to sockaddr2string to protect against buffer overflow. * Lint * Removed struct keyword from parameter. * Changed inet_ntop to be passed the actual length of the buffer. Added runtime exception if inet_ntop fails for some reason. * Removed unused function * Changed switch to if statement. * Fixed accidentally removing wrong function. * Update Common++/header/IpUtils.h Removed unused include. --- Common++/header/IpUtils.h | 26 +++++++-- Common++/src/IpUtils.cpp | 86 +++++++++++++++++++++++------ Pcap++/src/PcapLiveDevice.cpp | 23 ++++---- Pcap++/src/PcapLiveDeviceList.cpp | 18 +++--- Pcap++/src/PcapRemoteDeviceList.cpp | 17 +++--- 5 files changed, 121 insertions(+), 49 deletions(-) diff --git a/Common++/header/IpUtils.h b/Common++/header/IpUtils.h index cd35ed58c..6ad4d8314 100644 --- a/Common++/header/IpUtils.h +++ b/Common++/header/IpUtils.h @@ -62,22 +62,40 @@ namespace pcpp * Extract IPv4 address from sockaddr * @param[in] sa - input sockaddr * @return Address in in_addr format + * @throws std::invalid_argument Sockaddr family is not AF_INET or sockaddr is nullptr. */ - in_addr* sockaddr2in_addr(struct sockaddr *sa); + in_addr* sockaddr2in_addr(sockaddr* sa); + + /** + * Attempt to extract IPv4 address from sockaddr + * @param[in] sa - input sockaddr + * @return Pointer to address in in_addr format or nullptr if extraction fails. + */ + in_addr* try_sockaddr2in_addr(sockaddr* sa); /** * Extract IPv6 address from sockaddr * @param[in] sa - input sockaddr * @return Address in in6_addr format + * @throws std::invalid_argument Sockaddr family is not AF_INET6 or sockaddr is nullptr. + */ + in6_addr* sockaddr2in6_addr(sockaddr* sa); + + /** + * Attempt to extract IPv6 address from sockaddr + * @param[in] sa - input sockaddr + * @return Pointer to address in in6_addr format or nullptr if extraction fails. */ - in6_addr* sockaddr2in6_addr(struct sockaddr *sa); + in6_addr* try_sockaddr2in6_addr(sockaddr* sa); /** * Converts a sockaddr format address to its string representation * @param[in] sa Address in sockaddr format - * @param[out] resultString String representation of the address + * @param[out] resultString String representation of the address + * @param[in] resultBufLen Length of the result buffer. + * @throws std::invalid_argument Sockaddr family is not AF_INET or AF_INET6, sockaddr is nullptr or the result str buffer is insufficient. */ - void sockaddr2string(struct sockaddr *sa, char* resultString); + void sockaddr2string(sockaddr const* sa, char* resultString, size_t resultBufLen); /** * Convert a in_addr format address to 32bit representation diff --git a/Common++/src/IpUtils.cpp b/Common++/src/IpUtils.cpp index c78a1807f..04337eae3 100644 --- a/Common++/src/IpUtils.cpp +++ b/Common++/src/IpUtils.cpp @@ -4,6 +4,7 @@ #include "Logger.h" #include #include +#include #ifndef NS_INADDRSZ #define NS_INADDRSZ 4 #endif @@ -18,36 +19,87 @@ namespace pcpp { namespace internal { - in_addr* sockaddr2in_addr(struct sockaddr* sa) + in_addr* sockaddr2in_addr(sockaddr* sa) { if (sa == nullptr) + throw std::invalid_argument("sockaddr is nullptr"); + + if (sa->sa_family != AF_INET) + throw std::invalid_argument("sockaddr family is not AF_INET."); + + return &(reinterpret_cast(sa)->sin_addr); + } + + in_addr* try_sockaddr2in_addr(sockaddr* sa) + { + try + { + return sockaddr2in_addr(sa); + } + catch (const std::invalid_argument& e) + { + PCPP_LOG_DEBUG("Extraction failed: " << e.what() << " Returning nullptr."); return nullptr; - if (sa->sa_family == AF_INET) - return &(((struct sockaddr_in*)sa)->sin_addr); - PCPP_LOG_DEBUG("sockaddr family is not AF_INET. Returning NULL"); - return nullptr; + } } - in6_addr* sockaddr2in6_addr(struct sockaddr* sa) + in6_addr* sockaddr2in6_addr(sockaddr* sa) { - if (sa->sa_family == AF_INET6) - return &(((struct sockaddr_in6*)sa)->sin6_addr); - PCPP_LOG_DEBUG("sockaddr family is not AF_INET6. Returning NULL"); - return nullptr; + if (sa == nullptr) + throw std::invalid_argument("sockaddr is nullptr"); + + if (sa->sa_family != AF_INET6) + throw std::invalid_argument("sockaddr family is not AF_INET6."); + + return &(reinterpret_cast(sa)->sin6_addr); } - void sockaddr2string(struct sockaddr* sa, char* resultString) + in6_addr* try_sockaddr2in6_addr(sockaddr* sa) { - in_addr* ipv4Addr = sockaddr2in_addr(sa); - if (ipv4Addr != nullptr) + try + { + return sockaddr2in6_addr(sa); + } + catch (const std::invalid_argument& e) + { + PCPP_LOG_DEBUG("Extraction failed: " << e.what() << " Returning nullptr."); + return nullptr; + } + } + + void sockaddr2string(sockaddr const* sa, char* resultString, size_t resultBufLen) + { + if (sa == nullptr) + throw std::invalid_argument("sockaddr is nullptr"); + + switch (sa->sa_family) + { + case AF_INET: { PCPP_LOG_DEBUG("IPv4 packet address"); - inet_ntop(AF_INET, &(((sockaddr_in*)sa)->sin_addr), resultString, INET_ADDRSTRLEN); + if (resultBufLen < INET_ADDRSTRLEN) + throw std::invalid_argument("Insufficient buffer"); + + if (inet_ntop(AF_INET, &(reinterpret_cast(sa)->sin_addr), resultString, resultBufLen) == nullptr) + { + throw std::runtime_error("Unknown error during conversion"); + } + break; } - else + case AF_INET6: { - PCPP_LOG_DEBUG("Not IPv4 packet address. Assuming IPv6 packet"); - inet_ntop(AF_INET6, &(((sockaddr_in6*)sa)->sin6_addr), resultString, INET6_ADDRSTRLEN); + PCPP_LOG_DEBUG("IPv6 packet address"); + if (resultBufLen < INET6_ADDRSTRLEN) + throw std::invalid_argument("Insufficient buffer"); + + if(inet_ntop(AF_INET6, &(reinterpret_cast(sa)->sin6_addr), resultString, resultBufLen) == nullptr) + { + throw std::runtime_error("Unknown error during conversion"); + } + break; + } + default: + throw std::invalid_argument("Unsupported sockaddr family. Family is not AF_INET or AF_INET6."); } } diff --git a/Pcap++/src/PcapLiveDevice.cpp b/Pcap++/src/PcapLiveDevice.cpp index 7a55a0a2b..dcc32f0b7 100644 --- a/Pcap++/src/PcapLiveDevice.cpp +++ b/Pcap++/src/PcapLiveDevice.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #if defined(_WIN32) // The definition of BPF_MAJOR_VERSION is required to support Npcap. In Npcap there are // compilation errors due to struct redefinition when including both Packet32.h and pcap.h @@ -93,9 +94,9 @@ PcapLiveDevice::PcapLiveDevice(pcap_if_t* pInterface, bool calculateMTU, bool ca pInterface->addresses = pInterface->addresses->next; if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && pInterface->addresses != nullptr && pInterface->addresses->addr != nullptr) { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(pInterface->addresses->addr, addrAsString); - PCPP_LOG_DEBUG(" " << addrAsString); + std::array addrAsString; + internal::sockaddr2string(pInterface->addresses->addr, addrAsString.data(), addrAsString.size()); + PCPP_LOG_DEBUG(" " << addrAsString.data()); } } @@ -1094,12 +1095,12 @@ IPv4Address PcapLiveDevice::getIPv4Address() const { if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter.addr != nullptr) { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter.addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); + std::array addrAsString; + internal::sockaddr2string(addrIter.addr, addrAsString.data(), addrAsString.size()); + PCPP_LOG_DEBUG("Searching address " << addrAsString.data()); } - in_addr* currAddr = internal::sockaddr2in_addr(addrIter.addr); + in_addr* currAddr = internal::try_sockaddr2in_addr(addrIter.addr); if (currAddr == nullptr) { PCPP_LOG_DEBUG("Address is NULL"); @@ -1125,11 +1126,11 @@ IPv6Address PcapLiveDevice::getIPv6Address() const { if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter.addr != nullptr) { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter.addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); + std::array addrAsString; + internal::sockaddr2string(addrIter.addr, addrAsString.data(), addrAsString.size()); + PCPP_LOG_DEBUG("Searching address " << addrAsString.data()); } - in6_addr *currAddr = internal::sockaddr2in6_addr(addrIter.addr); + in6_addr *currAddr = internal::try_sockaddr2in6_addr(addrIter.addr); if (currAddr == nullptr) { PCPP_LOG_DEBUG("Address is NULL"); diff --git a/Pcap++/src/PcapLiveDeviceList.cpp b/Pcap++/src/PcapLiveDeviceList.cpp index b1d238a3c..47f179f47 100644 --- a/Pcap++/src/PcapLiveDeviceList.cpp +++ b/Pcap++/src/PcapLiveDeviceList.cpp @@ -236,7 +236,7 @@ void PcapLiveDeviceList::setDnsServers() sockaddr* saddr = (sockaddr*)&_res.nsaddr_list[i]; if (saddr == nullptr) continue; - in_addr* inaddr = internal::sockaddr2in_addr(saddr); + in_addr* inaddr = internal::try_sockaddr2in_addr(saddr); if (inaddr == nullptr) continue; @@ -275,12 +275,12 @@ PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPv4Address& ipA { if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter.addr != nullptr) { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter.addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); + std::array addrAsString; + internal::sockaddr2string(addrIter.addr, addrAsString.data(), addrAsString.size()); + PCPP_LOG_DEBUG("Searching address " << addrAsString.data()); } - in_addr* currAddr = internal::sockaddr2in_addr(addrIter.addr); + in_addr* currAddr = internal::try_sockaddr2in_addr(addrIter.addr); if (currAddr == nullptr) { PCPP_LOG_DEBUG("Address is nullptr"); @@ -308,12 +308,12 @@ PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPv6Address& ip6 { if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter.addr != nullptr) { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter.addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); + std::array addrAsString; + internal::sockaddr2string(addrIter.addr, addrAsString.data(), addrAsString.size()); + PCPP_LOG_DEBUG("Searching address " << addrAsString.data()); } - in6_addr* currAddr = internal::sockaddr2in6_addr(addrIter.addr); + in6_addr* currAddr = internal::try_sockaddr2in6_addr(addrIter.addr); if (currAddr == nullptr) { PCPP_LOG_DEBUG("Address is nullptr"); diff --git a/Pcap++/src/PcapRemoteDeviceList.cpp b/Pcap++/src/PcapRemoteDeviceList.cpp index b4797e97c..af107fee9 100644 --- a/Pcap++/src/PcapRemoteDeviceList.cpp +++ b/Pcap++/src/PcapRemoteDeviceList.cpp @@ -7,6 +7,7 @@ #include "IpUtils.h" #include "IpAddressUtils.h" #include "pcap.h" +#include #include namespace pcpp @@ -108,12 +109,12 @@ PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP(const IPv4Address& i { if (Logger::getInstance().isDebugEnabled(PcapLogModuleRemoteDevice) && addrIter.addr != NULL) { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter.addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); + std::array addrAsString; + internal::sockaddr2string(addrIter.addr, addrAsString.data(), addrAsString.size()); + PCPP_LOG_DEBUG("Searching address " << addrAsString.data()); } - in_addr* currAddr = internal::sockaddr2in_addr(addrIter.addr); + in_addr* currAddr = internal::try_sockaddr2in_addr(addrIter.addr); if (currAddr == NULL) { PCPP_LOG_DEBUG("Address is NULL"); @@ -142,12 +143,12 @@ PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP(const IPv6Address& i { if (Logger::getInstance().isDebugEnabled(PcapLogModuleRemoteDevice) && addrIter.addr != NULL) { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter.addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); + std::array addrAsString; + internal::sockaddr2string(addrIter.addr, addrAsString.data(), addrAsString.size()); + PCPP_LOG_DEBUG("Searching address " << addrAsString.data()); } - in6_addr* currAddr = internal::sockaddr2in6_addr(addrIter.addr); + in6_addr* currAddr = internal::try_sockaddr2in6_addr(addrIter.addr); if (currAddr == NULL) { PCPP_LOG_DEBUG("Address is NULL");