in src/message.c [653:818]
static qd_section_status_t message_section_check_LH(qd_message_content_t *content,
qd_buffer_t **buffer,
unsigned char **cursor,
const unsigned char *pattern,
int pattern_length,
const unsigned char *expected_tags,
qd_field_location_t *location,
bool dup_ok,
bool protect_buffer)
{
if (!*cursor || !can_advance(cursor, buffer))
return QD_SECTION_NEED_MORE;
qd_buffer_t *test_buffer = *buffer;
unsigned char *test_cursor = *cursor;
unsigned char *end_of_buffer = qd_buffer_cursor(test_buffer);
int idx = 0;
while (idx < pattern_length && *test_cursor == pattern[idx]) {
idx++;
test_cursor++;
if (test_cursor == end_of_buffer) {
test_buffer = test_buffer->next;
if (test_buffer == 0)
return QD_SECTION_NEED_MORE;
test_cursor = qd_buffer_base(test_buffer);
end_of_buffer = test_cursor + qd_buffer_size(test_buffer);
}
}
if (idx < pattern_length)
return QD_SECTION_NO_MATCH;
//
// Pattern matched, check the tag
//
while (*expected_tags && *test_cursor != *expected_tags)
expected_tags++;
if (*expected_tags == 0)
return QD_SECTION_INVALID; // Error: Unexpected tag
if (location->parsed && !dup_ok)
return QD_SECTION_INVALID; // Error: Duplicate section
//
// Pattern matched and tag is expected. Mark the beginning of the section.
//
location->buffer = *buffer;
location->offset = *cursor - qd_buffer_base(*buffer);
location->length = 0;
location->hdr_length = pattern_length;
//
// Check that the full section is present, if so advance the pointers to
// consume the whole section.
//
int pre_consume = 1; // Count the already extracted tag
uint32_t consume = 0;
unsigned char tag;
unsigned char octet;
if (!next_octet(&test_cursor, &test_buffer, &tag))
return QD_SECTION_NEED_MORE;
unsigned char tag_subcat = tag & 0xF0;
// if there is no more data the only valid data type is a null type (0x40),
// size is implied as 0
if (!can_advance(&test_cursor, &test_buffer) && tag_subcat != 0x40)
return QD_SECTION_NEED_MORE;
switch (tag_subcat) {
// fixed sizes:
case 0x40: /* null */ break;
case 0x50: consume = 1; break;
case 0x60: consume = 2; break;
case 0x70: consume = 4; break;
case 0x80: consume = 8; break;
case 0x90: consume = 16; break;
case 0xB0:
case 0xD0:
case 0xF0:
// uint32_t size field:
pre_consume += 3;
if (!next_octet(&test_cursor, &test_buffer, &octet))
return QD_SECTION_NEED_MORE;
consume |= ((uint32_t) octet) << 24;
if (!next_octet(&test_cursor, &test_buffer, &octet))
return QD_SECTION_NEED_MORE;
consume |= ((uint32_t) octet) << 16;
if (!next_octet(&test_cursor, &test_buffer, &octet))
return QD_SECTION_NEED_MORE;
consume |= ((uint32_t) octet) << 8;
// Fall through to the next case...
case 0xA0:
case 0xC0:
case 0xE0:
// uint8_t size field
pre_consume += 1;
if (!next_octet(&test_cursor, &test_buffer, &octet))
return QD_SECTION_NEED_MORE;
consume |= (uint32_t) octet;
break;
}
location->length = pre_consume + consume;
if (consume) {
if (!advance(&test_cursor, &test_buffer, consume)) {
return QD_SECTION_NEED_MORE; // whole section not fully received
}
}
if (protect_buffer) {
//
// increment the reference count of the parsed section as location now
// references it. Note that the cursor may have advanced to the octet after
// the parsed section, so be careful not to include an extra buffer past
// the end. And cursor + buffer will be null if the parsed section ends at
// the end of the buffer chain, so be careful of that, too!
//
bool buffers_protected = false;
qd_buffer_t *start = *buffer;
qd_buffer_t *last = test_buffer;
if (last && last != start) {
if (test_cursor == qd_buffer_base(last)) {
// last does not include octets for the current section
last = DEQ_PREV(last);
}
}
while (start) {
qd_buffer_inc_fanout(start);
buffers_protected = true;
if (start == last)
break;
start = DEQ_NEXT(start);
}
// DISPATCH-2191: protected buffers are never released - even after
// being sent - because they are referenced by the content->section_xxx
// location fields and remain valid for the life of the content
// instance. Since these buffers are never freed they must not be
// included in the Q2 threshold check!
if (buffers_protected) {
content->protected_buffers = 0;
start = DEQ_HEAD(content->buffers);
while (start) {
++content->protected_buffers;
if (start == last)
break;
start = DEQ_NEXT(start);
}
}
}
location->parsed = 1;
*cursor = test_cursor;
*buffer = test_buffer;
return QD_SECTION_MATCH;
}