static void advance(unsigned char **cursor, qd_buffer_t **buffer, int consume, buffer_process_t handler, void *context) { unsigned char *local_cursor = *cursor; qd_buffer_t *local_buffer = *buffer; int remaining = qd_buffer_size(local_buffer) - (local_cursor - qd_buffer_base(local_buffer)); while (consume > 0) { if (consume < remaining) { if (handler) handler(context, local_cursor, consume); local_cursor += consume; consume = 0; } else { if (handler) handler(context, local_cursor, remaining); consume -= remaining; local_buffer = local_buffer->next; if (local_buffer == 0){ local_cursor = 0; break; } local_cursor = qd_buffer_base(local_buffer); remaining = qd_buffer_size(local_buffer) - (local_cursor - qd_buffer_base(local_buffer)); } } *cursor = local_cursor; *buffer = local_buffer; }
ssize_t qd_message_field_copy(qd_message_t *msg, qd_message_field_t field, void *buffer, size_t *hdr_length) { qd_field_location_t *loc = qd_message_field_location(msg, field); if (!loc) return -1; qd_buffer_t *buf = loc->buffer; size_t bufsize = qd_buffer_size(buf) - loc->offset; void *base = qd_buffer_base(buf) + loc->offset; size_t remaining = loc->length + loc->hdr_length; *hdr_length = loc->hdr_length; while (remaining > 0) { if (bufsize > remaining) bufsize = remaining; memcpy(buffer, base, bufsize); buffer += bufsize; remaining -= bufsize; if (remaining > 0) { buf = buf->next; base = qd_buffer_base(buf); bufsize = qd_buffer_size(buf); } } return loc->length + loc->hdr_length; }
qd_iovec_t *qd_iterator_iovec(const qd_iterator_t *iter) { if (!iter) return 0; // // Count the number of buffers this field straddles // pointer_t pointer = iter->view_start_pointer; int bufcnt = 1; qd_buffer_t *buf = pointer.buffer; size_t bufsize = qd_buffer_size(buf) - (pointer.cursor - qd_buffer_base(pointer.buffer)); ssize_t remaining = pointer.remaining - bufsize; while (remaining > 0) { bufcnt++; buf = buf->next; if (!buf) return 0; remaining -= qd_buffer_size(buf); } // // Allocate an iovec object big enough to hold the number of buffers // qd_iovec_t *iov = qd_iovec(bufcnt); if (!iov) return 0; // // Build out the io vectors with pointers to the segments of the field in buffers // bufcnt = 0; buf = pointer.buffer; bufsize = qd_buffer_size(buf) - (pointer.cursor - qd_buffer_base(pointer.buffer)); void *base = pointer.cursor; remaining = pointer.remaining; while (remaining > 0) { if (bufsize > remaining) bufsize = remaining; qd_iovec_array(iov)[bufcnt].iov_base = base; qd_iovec_array(iov)[bufcnt].iov_len = bufsize; bufcnt++; remaining -= bufsize; if (remaining > 0) { buf = buf->next; base = qd_buffer_base(buf); bufsize = qd_buffer_size(buf); } } return iov; }
qd_field_iterator_t *qd_message_field_iterator(qd_message_t *msg, qd_message_field_t field) { qd_field_location_t *loc = qd_message_field_location(msg, field); if (!loc) return 0; if (loc->tag == QD_AMQP_NULL) return 0; qd_buffer_t *buffer = loc->buffer; unsigned char *cursor = qd_buffer_base(loc->buffer) + loc->offset; advance(&cursor, &buffer, loc->hdr_length, 0, 0); return qd_field_iterator_buffer(buffer, cursor - qd_buffer_base(buffer), loc->length); }
unsigned int qd_buffer_list_clone(qd_buffer_list_t *dst, const qd_buffer_list_t *src) { uint32_t len = 0; DEQ_INIT(*dst); qd_buffer_t *buf = DEQ_HEAD(*src); while (buf) { size_t to_copy = qd_buffer_size(buf); unsigned char *src = qd_buffer_base(buf); len += to_copy; while (to_copy) { qd_buffer_t *newbuf = qd_buffer(); size_t count = qd_buffer_capacity(newbuf); // default buffer capacity may have changed, // so don't assume it will fit: if (count > to_copy) count = to_copy; memcpy(qd_buffer_cursor(newbuf), src, count); qd_buffer_insert(newbuf, count); DEQ_INSERT_TAIL(*dst, newbuf); src += count; to_copy -= count; } buf = DEQ_NEXT(buf); } return len; }
static inline void field_iterator_move_cursor(qd_iterator_t *iter, uint32_t length) { // Only safe to call this help method if the cursor is parsing the data, // i.e. if iter is an address iterator, the cursor must be 'past' the // prefix assert(iter->state == STATE_IN_BODY); uint32_t count = (length > iter->view_pointer.remaining) ? iter->view_pointer.remaining : length; if (iter->view_pointer.buffer) { while (count) { uint32_t remaining = qd_buffer_cursor(iter->view_pointer.buffer) - iter->view_pointer.cursor; remaining = (remaining > count) ? count : remaining; iter->view_pointer.cursor += remaining; iter->view_pointer.remaining -= remaining; count -= remaining; if (iter->view_pointer.cursor == qd_buffer_cursor(iter->view_pointer.buffer)) { iter->view_pointer.buffer = iter->view_pointer.buffer->next; if (iter->view_pointer.buffer == 0) { iter->view_pointer.remaining = 0; iter->view_pointer.cursor = 0; break; } else { iter->view_pointer.cursor = qd_buffer_base(iter->view_pointer.buffer); } } } } else { // string/binary data iter->view_pointer.cursor += count; iter->view_pointer.remaining -= count; } }
static int traverse_field(unsigned char **cursor, qd_buffer_t **buffer, qd_field_location_t *field) { qd_buffer_t *start_buffer = *buffer; unsigned char *start_cursor = *cursor; unsigned char tag = next_octet(cursor, buffer); if (!(*cursor)) return 0; int consume = 0; size_t hdr_length = 1; switch (tag & 0xF0) { case 0x40 : consume = 0; 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 : hdr_length += 3; consume |= ((int) next_octet(cursor, buffer)) << 24; if (!(*cursor)) return 0; consume |= ((int) next_octet(cursor, buffer)) << 16; if (!(*cursor)) return 0; consume |= ((int) next_octet(cursor, buffer)) << 8; if (!(*cursor)) return 0; // Fall through to the next case... case 0xA0 : case 0xC0 : case 0xE0 : hdr_length++; consume |= (int) next_octet(cursor, buffer); if (!(*cursor)) return 0; break; } if (field && !field->parsed) { field->buffer = start_buffer; field->offset = start_cursor - qd_buffer_base(start_buffer); field->length = consume; field->hdr_length = hdr_length; field->parsed = true; field->tag = tag; } advance(cursor, buffer, consume, 0, 0); return 1; }
// get the field location of a field in the message properties (if it exists, // else 0). static qd_field_location_t *qd_message_properties_field(qd_message_t *msg, qd_message_field_t field) { static const intptr_t offsets[] = { // position of the field's qd_field_location_t in the message content // object (intptr_t) &((qd_message_content_t *)0)->field_message_id, (intptr_t) &((qd_message_content_t *)0)->field_user_id, (intptr_t) &((qd_message_content_t *)0)->field_to, (intptr_t) &((qd_message_content_t *)0)->field_subject, (intptr_t) &((qd_message_content_t *)0)->field_reply_to, (intptr_t) &((qd_message_content_t *)0)->field_correlation_id }; // update table above if new fields need to be accessed: assert(QD_FIELD_MESSAGE_ID <= field && field <= QD_FIELD_CORRELATION_ID); qd_message_content_t *content = MSG_CONTENT(msg); if (!content->section_message_properties.parsed) { if (!qd_message_check(msg, QD_DEPTH_PROPERTIES) || !content->section_message_properties.parsed) return 0; } if (field == QD_FIELD_PROPERTIES) return &content->section_message_properties; const int index = field - QD_FIELD_MESSAGE_ID; qd_field_location_t *const location = (qd_field_location_t *)((char *)content + offsets[index]); if (location->parsed) return location; // requested field not parsed out. Need to parse out up to the requested field: qd_buffer_t *buffer = content->section_message_properties.buffer; unsigned char *cursor = qd_buffer_base(buffer) + content->section_message_properties.offset; advance(&cursor, &buffer, content->section_message_properties.hdr_length, 0, 0); if (index >= start_list(&cursor, &buffer)) return 0; // properties list too short int position = 0; while (position < index) { qd_field_location_t *f = (qd_field_location_t *)((char *)content + offsets[position]); if (f->parsed) advance(&cursor, &buffer, f->hdr_length + f->length, 0, 0); else // parse it out if (!traverse_field(&cursor, &buffer, f)) return 0; position++; } // all fields previous to the target have now been parsed and cursor/buffer // are in the correct position, parse out the field: if (traverse_field(&cursor, &buffer, location)) return location; return 0; }
qd_iterator_t *qd_iterator_buffer(qd_buffer_t *buffer, int offset, int length, qd_iterator_view_t view) { qd_iterator_t *iter = new_qd_iterator_t(); if (!iter) return 0; ZERO(iter); iter->start_pointer.buffer = buffer; iter->start_pointer.cursor = qd_buffer_base(buffer) + offset; iter->start_pointer.remaining = length; iter->phase = '0'; qd_iterator_reset_view(iter, view); return iter; }
qd_field_iterator_t *qd_address_iterator_buffer(qd_buffer_t *buffer, int offset, int length, qd_iterator_view_t view) { qd_field_iterator_t *iter = new_qd_field_iterator_t(); if (!iter) return 0; iter->start_pointer.buffer = buffer; iter->start_pointer.cursor = qd_buffer_base(buffer) + offset; iter->start_pointer.length = length; iter->phase = '0'; iter->prefix_override = '\0'; DEQ_INIT(iter->hash_segments); qd_address_iterator_reset_view(iter, view); return iter; }
const char *qd_parse_annotations_v1( bool strip_anno_in, qd_iterator_t *ma_iter_in, qd_parsed_field_t **ma_ingress, qd_parsed_field_t **ma_phase, qd_parsed_field_t **ma_to_override, qd_parsed_field_t **ma_trace, qd_iterator_pointer_t *blob_pointer, uint32_t *blob_item_count) { // Do full parse qd_iterator_reset(ma_iter_in); qd_parsed_turbo_list_t annos; uint32_t user_entries; uint32_t user_bytes; const char * parse_error = qd_parse_turbo(ma_iter_in, &annos, &user_entries, &user_bytes); if (parse_error) { return parse_error; } qd_parsed_turbo_t *anno; if (!strip_anno_in) { anno = DEQ_HEAD(annos); while (anno) { qd_iterator_t *key_iter = qd_iterator_buffer(anno->bufptr.buffer, anno->bufptr.cursor - qd_buffer_base(anno->bufptr.buffer), anno->size, ITER_VIEW_ALL); assert(key_iter); qd_parsed_field_t *key_field = qd_parse(key_iter); assert(key_field); qd_iterator_t *iter = qd_parse_raw(key_field); assert(iter); qd_parsed_turbo_t *anno_val = DEQ_NEXT(anno); assert(anno_val); qd_iterator_t *val_iter = qd_iterator_buffer(anno_val->bufptr.buffer, anno_val->bufptr.cursor - qd_buffer_base(anno_val->bufptr.buffer), anno_val->size, ITER_VIEW_ALL); assert(val_iter); qd_parsed_field_t *val_field = qd_parse(val_iter); assert(val_field); // Hoist the key name out of the buffers into a normal char array char key_name[QD_MA_MAX_KEY_LEN + 1]; (void)qd_iterator_strncpy(iter, key_name, QD_MA_MAX_KEY_LEN + 1); // transfer ownership of the extracted value to the message if (!strcmp(key_name, QD_MA_TRACE)) { *ma_trace = val_field; } else if (!strcmp(key_name, QD_MA_INGRESS)) { *ma_ingress = val_field; } else if (!strcmp(key_name, QD_MA_TO)) { *ma_to_override = val_field; } else if (!strcmp(key_name, QD_MA_PHASE)) { *ma_phase = val_field; } else { // TODO: this key had the QD_MA_PREFIX but it does not match // one of the actual fields. qd_parse_free(val_field); } qd_iterator_free(key_iter); qd_parse_free(key_field); qd_iterator_free(val_iter); // val_field is usually handed over to message_private and is freed anno = DEQ_NEXT(anno_val); } } anno = DEQ_HEAD(annos); while (anno) { DEQ_REMOVE_HEAD(annos); free_qd_parsed_turbo_t(anno); anno = DEQ_HEAD(annos); } // Adjust size of user annotation blob by the size of the router // annotations blob_pointer->remaining = user_bytes; assert(blob_pointer->remaining >= 0); *blob_item_count = user_entries; assert(*blob_item_count >= 0); return 0; }
static bool qd_message_check_LH(qd_message_content_t *content, qd_message_depth_t depth) { qd_error_clear(); qd_buffer_t *buffer = DEQ_HEAD(content->buffers); if (!buffer) { qd_error(QD_ERROR_MESSAGE, "No data"); return false; } if (depth <= content->parse_depth) return true; // We've already parsed at least this deep if (content->parse_buffer == 0) { content->parse_buffer = buffer; content->parse_cursor = qd_buffer_base(content->parse_buffer); } if (depth == QD_DEPTH_NONE) return true; // // MESSAGE HEADER // if (0 == qd_check_field_LH(content, QD_DEPTH_HEADER, MSG_HDR_LONG, MSG_HDR_SHORT, TAGS_LIST, &content->section_message_header, 0)) { qd_error(QD_ERROR_MESSAGE, "Invalid header"); return false; } if (depth == QD_DEPTH_HEADER) return true; // // DELIVERY ANNOTATION // if (0 == qd_check_field_LH(content, QD_DEPTH_DELIVERY_ANNOTATIONS, DELIVERY_ANNOTATION_LONG, DELIVERY_ANNOTATION_SHORT, TAGS_MAP, &content->section_delivery_annotation, 0)) { qd_error(QD_ERROR_MESSAGE, "Invalid delivery-annotations"); return false; } if (depth == QD_DEPTH_DELIVERY_ANNOTATIONS) return true; // // MESSAGE ANNOTATION // if (0 == qd_check_field_LH(content, QD_DEPTH_MESSAGE_ANNOTATIONS, MESSAGE_ANNOTATION_LONG, MESSAGE_ANNOTATION_SHORT, TAGS_MAP, &content->section_message_annotation, 0)) { qd_error(QD_ERROR_MESSAGE, "Invalid annotations"); return false; } if (depth == QD_DEPTH_MESSAGE_ANNOTATIONS) return true; // // PROPERTIES // if (0 == qd_check_field_LH(content, QD_DEPTH_PROPERTIES, PROPERTIES_LONG, PROPERTIES_SHORT, TAGS_LIST, &content->section_message_properties, 0)) { qd_error(QD_ERROR_MESSAGE, "Invalid message properties"); return false; } if (depth == QD_DEPTH_PROPERTIES) return true; // // APPLICATION PROPERTIES // if (0 == qd_check_field_LH(content, QD_DEPTH_APPLICATION_PROPERTIES, APPLICATION_PROPERTIES_LONG, APPLICATION_PROPERTIES_SHORT, TAGS_MAP, &content->section_application_properties, 0)) { qd_error(QD_ERROR_MESSAGE, "Invalid application-properties"); return false; } if (depth == QD_DEPTH_APPLICATION_PROPERTIES) return true; // // BODY // Note that this function expects a limited set of types in a VALUE section. This is // not a problem for messages passing through Dispatch because through-only messages won't // be parsed to BODY-depth. // if (0 == qd_check_field_LH(content, QD_DEPTH_BODY, BODY_DATA_LONG, BODY_DATA_SHORT, TAGS_BINARY, &content->section_body, 1)) { qd_error(QD_ERROR_MESSAGE, "Invalid body data"); return false; } if (0 == qd_check_field_LH(content, QD_DEPTH_BODY, BODY_SEQUENCE_LONG, BODY_SEQUENCE_SHORT, TAGS_LIST, &content->section_body, 1)) { qd_error(QD_ERROR_MESSAGE, "Invalid body sequence"); return false; } if (0 == qd_check_field_LH(content, QD_DEPTH_BODY, BODY_VALUE_LONG, BODY_VALUE_SHORT, TAGS_ANY, &content->section_body, 0)) { qd_error(QD_ERROR_MESSAGE, "Invalid body value"); return false; } if (depth == QD_DEPTH_BODY) return true; // // FOOTER // if (0 == qd_check_field_LH(content, QD_DEPTH_ALL, FOOTER_LONG, FOOTER_SHORT, TAGS_MAP, &content->section_footer, 0)) { qd_error(QD_ERROR_MESSAGE, "Invalid footer"); return false; } return true; }
void qd_message_send(qd_message_t *in_msg, qd_link_t *link, bool strip_annotations) { qd_message_pvt_t *msg = (qd_message_pvt_t*) in_msg; qd_message_content_t *content = msg->content; qd_buffer_t *buf = DEQ_HEAD(content->buffers); unsigned char *cursor; pn_link_t *pnl = qd_link_pn(link); char repr[qd_message_repr_len()]; qd_log(log_source, QD_LOG_TRACE, "Sending %s on link %s", qd_message_repr(in_msg, repr, sizeof(repr)), pn_link_name(pnl)); qd_buffer_list_t new_ma; DEQ_INIT(new_ma); if (strip_annotations || compose_message_annotations(msg, &new_ma)) { // // This is the case where the message annotations have been modified. // The message send must be divided into sections: The existing header; // the new message annotations; the rest of the existing message. // Note that the original message annotations that are still in the // buffer chain must not be sent. // // Start by making sure that we've parsed the message sections through // the message annotations // // ??? NO LONGER NECESSARY??? if (!qd_message_check(in_msg, QD_DEPTH_MESSAGE_ANNOTATIONS)) { qd_log(log_source, QD_LOG_ERROR, "Cannot send: %s", qd_error_message); return; } // // Send header if present // cursor = qd_buffer_base(buf); if (content->section_message_header.length > 0) { buf = content->section_message_header.buffer; cursor = content->section_message_header.offset + qd_buffer_base(buf); advance(&cursor, &buf, content->section_message_header.length + content->section_message_header.hdr_length, send_handler, (void*) pnl); } // // Send new message annotations // qd_buffer_t *da_buf = DEQ_HEAD(new_ma); while (da_buf) { pn_link_send(pnl, (char*) qd_buffer_base(da_buf), qd_buffer_size(da_buf)); da_buf = DEQ_NEXT(da_buf); } qd_buffer_list_free_buffers(&new_ma); // // Skip over replaced message annotations // if (content->section_message_annotation.length > 0) advance(&cursor, &buf, content->section_message_annotation.hdr_length + content->section_message_annotation.length, 0, 0); // // Send remaining partial buffer // if (buf) { size_t len = qd_buffer_size(buf) - (cursor - qd_buffer_base(buf)); advance(&cursor, &buf, len, send_handler, (void*) pnl); } // Fall through to process the remaining buffers normally // Note that 'advance' will have moved us to the next buffer in the chain. } while (buf) { pn_link_send(pnl, (char*) qd_buffer_base(buf), qd_buffer_size(buf)); buf = DEQ_NEXT(buf); } }
// // Check the buffer chain, starting at cursor to see if it matches the pattern. // If the pattern matches, check the next tag to see if it's in the set of expected // tags. If not, return zero. If so, set the location descriptor to the good // tag and advance the cursor (and buffer, if needed) to the end of the matched section. // // If there is no match, don't advance the cursor. // // Return 0 if the pattern matches but the following tag is unexpected // Return 0 if the pattern matches and the location already has a pointer (duplicate section) // Return 1 if the pattern matches and we've advanced the cursor/buffer // Return 1 if the pattern does not match // static int qd_check_and_advance(qd_buffer_t **buffer, unsigned char **cursor, const unsigned char *pattern, int pattern_length, const unsigned char *expected_tags, qd_field_location_t *location) { qd_buffer_t *test_buffer = *buffer; unsigned char *test_cursor = *cursor; if (!test_cursor) return 1; // no match unsigned char *end_of_buffer = qd_buffer_base(test_buffer) + qd_buffer_size(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 1; // Pattern didn't match test_cursor = qd_buffer_base(test_buffer); end_of_buffer = test_cursor + qd_buffer_size(test_buffer); } } if (idx < pattern_length) return 1; // Pattern didn't match // // Pattern matched, check the tag // while (*expected_tags && *test_cursor != *expected_tags) expected_tags++; if (*expected_tags == 0) return 0; // Unexpected tag if (location->parsed) return 0; // Duplicate section // // Pattern matched and tag is expected. Mark the beginning of the section. // location->parsed = 1; location->buffer = *buffer; location->offset = *cursor - qd_buffer_base(*buffer); location->length = 0; location->hdr_length = pattern_length; // // Advance the pointers to consume the whole section. // int pre_consume = 1; // Count the already extracted tag int consume = 0; unsigned char tag = next_octet(&test_cursor, &test_buffer); if (!test_cursor) return 0; switch (tag) { case 0x45 : // list0 break; case 0xd0 : // list32 case 0xd1 : // map32 case 0xb0 : // vbin32 pre_consume += 3; consume |= ((int) next_octet(&test_cursor, &test_buffer)) << 24; if (!test_cursor) return 0; consume |= ((int) next_octet(&test_cursor, &test_buffer)) << 16; if (!test_cursor) return 0; consume |= ((int) next_octet(&test_cursor, &test_buffer)) << 8; if (!test_cursor) return 0; // Fall through to the next case... case 0xc0 : // list8 case 0xc1 : // map8 case 0xa0 : // vbin8 pre_consume += 1; consume |= (int) next_octet(&test_cursor, &test_buffer); if (!test_cursor) return 0; break; } location->length = pre_consume + consume; if (consume) advance(&test_cursor, &test_buffer, consume, 0, 0); *cursor = test_cursor; *buffer = test_buffer; return 1; }