#include "bej_dictionary.h" #include #include #include #include /** * @brief Get the index for a property offset. First property will be at index * 0. * * @param[in] propertyOffset - a valid property offset. * @return index of the property. */ static uint16_t bejGetPropertyEntryIndex(uint16_t propertyOffset) { return (propertyOffset - bejDictGetPropertyHeadOffset()) / sizeof(struct BejDictionaryProperty); } /** * @brief Validate a property offset. * * @param[in] dictionary - pointer to the dictionary. * @param[in] propertyOffset - offset needed to be validated. * @return true if propertyOffset is a valid offset. */ static bool bejValidatePropertyOffset(const uint8_t* dictionary, uint16_t propertyOffset) { // propertyOffset should be greater than or equal to first property offset. if (propertyOffset < bejDictGetPropertyHeadOffset()) { fprintf( stderr, "Invalid property offset. Pointing to Dictionary header data\n"); return false; } // propertyOffset should be a multiple of sizeof(BejDictionaryProperty) // starting from first property within the dictionary. if ((propertyOffset - bejDictGetPropertyHeadOffset()) % sizeof(struct BejDictionaryProperty)) { fprintf(stderr, "Invalid property offset. Does not point to beginning " "of property\n"); return false; } const struct BejDictionaryHeader* header = (const struct BejDictionaryHeader*)dictionary; uint16_t propertyIndex = bejGetPropertyEntryIndex(propertyOffset); if (propertyIndex >= header->entryCount) { fprintf(stderr, "Invalid property offset %u. It falls outside of dictionary " "properties\n", propertyOffset); return false; } return true; } static bool bejValidatePropertyNameLength( const uint8_t* dictionary, uint32_t dictionarySize, uint16_t nameOffset, uint8_t nameLength) { if (nameLength == 0) { return true; } // After the property names, the dictionary has at least the // uint8 CopyrightLength field. So we compare with dictionarySize - 1. // nameOffset is uint16_t and nameLength is uint8_t so it cannot overflow // uint32_t dictionarySize here. if (((uint32_t)nameOffset) + (uint32_t)(nameLength) > (dictionarySize - 1)) { fprintf(stderr, "Property name length goes beyond dictionary size\n"); return false; } // nameLength includes the NULL terminating. So the actual string size // should be nameLength - 1. const char* name = (const char*)dictionary + nameOffset; const void* nullTerminator = memchr(name, '\0', nameLength); if (nullTerminator == NULL || ((const char*)nullTerminator - name) != (ptrdiff_t)(nameLength - 1)) { fprintf(stderr, "Property name length doesn't match actual length\n"); return false; } return true; } uint16_t bejDictGetPropertyHeadOffset(void) { // First property is present soon after the dictionary header. return sizeof(struct BejDictionaryHeader); } uint16_t bejDictGetFirstAnnotatedPropertyOffset(void) { // The first property available is the "Annotations" set which is the parent // for all properties. Next immediate property is the first property we // need. return sizeof(struct BejDictionaryHeader) + sizeof(struct BejDictionaryProperty); } int bejDictGetProperty(const uint8_t* dictionary, uint16_t startingPropertyOffset, uint16_t sequenceNumber, const struct BejDictionaryProperty** property) { uint16_t propertyOffset = startingPropertyOffset; const struct BejDictionaryHeader* header = (const struct BejDictionaryHeader*)dictionary; if (!bejValidatePropertyOffset(dictionary, propertyOffset)) { return bejErrorInvalidPropertyOffset; } uint16_t propertyIndex = bejGetPropertyEntryIndex(propertyOffset); for (uint16_t index = propertyIndex; index < header->entryCount; ++index) { const struct BejDictionaryProperty* p = (const struct BejDictionaryProperty*)(dictionary + propertyOffset); if (p->sequenceNumber == sequenceNumber) { if (!bejValidatePropertyNameLength(dictionary, header->dictionarySize, p->nameOffset, p->nameLength)) { return bejErrorInvalidSize; } *property = p; return 0; } propertyOffset += sizeof(struct BejDictionaryProperty); } return bejErrorUnknownProperty; } const char* bejDictGetPropertyName(const uint8_t* dictionary, uint16_t nameOffset, uint8_t nameLength) { if (nameLength == 0) { return ""; } return (const char*)(dictionary + nameOffset); } int bejDictGetPropertyByName( const uint8_t* dictionary, uint16_t startingPropertyOffset, const char* propertyName, const struct BejDictionaryProperty** property, uint16_t* propertyOffset) { NULL_CHECK(property, "property in bejDictGetPropertyByName"); uint16_t curPropertyOffset = startingPropertyOffset; const struct BejDictionaryHeader* header = (const struct BejDictionaryHeader*)dictionary; if (!bejValidatePropertyOffset(dictionary, curPropertyOffset)) { return bejErrorInvalidPropertyOffset; } uint16_t propertyIndex = bejGetPropertyEntryIndex(curPropertyOffset); for (uint16_t index = propertyIndex; index < header->entryCount; ++index) { const struct BejDictionaryProperty* p = (const struct BejDictionaryProperty*)(dictionary + curPropertyOffset); if (strcmp(propertyName, bejDictGetPropertyName(dictionary, p->nameOffset, p->nameLength)) == 0) { *property = p; // propertyOffset is an optional output. if (propertyOffset != NULL) { *propertyOffset = curPropertyOffset; } return 0; } curPropertyOffset += sizeof(struct BejDictionaryProperty); } return bejErrorUnknownProperty; }