Beispiel #1
0
/**
 * Append the result of a template engine into an autobuffer.
 * Each usage of a key will be replaced with the corresponding
 * value.
 * @param autobuf pointer to autobuf object
 * @param file code file where this function was called (supplied by macro)
 * @param line line number in file where this function was called (supplied by macro)
 * @param format format string (as supplied to abuf_template_init()
 * @param values array of values (same number as keys)
 * @param table pointer to index table initialized by abuf_template_init()
 * @param indexCount length of index table as returned by abuf_template_init()
 * @return -1 if an out-of-memory error happened, 0 otherwise
 */
int
abuf_templatef (struct autobuf *autobuf,
    const char *format, char **values, size_t *table, size_t indexCount) {
  size_t i, last = 0;

  if (autobuf == NULL) return 0;

  for (i=0; i<indexCount; i+=3) {
    /* copy prefix text */
    if (last < table[i+1]) {
      if (abuf_memcpy(autobuf, &format[last], table[i+1] - last) < 0) {
        return -1;
      }
    }
    if (abuf_puts(autobuf, values[table[i]]) < 0) {
      return -1;
    }
    last = table[i+2];
  }

  if (last < strlen(format)) {
    if (abuf_puts(autobuf, &format[last]) < 0) {
      return -1;
    }
  }
  return 0;
}
Beispiel #2
0
/**
 * Print start of message
 * @param context rfc5444 tlvblock reader context
 * @return see rfc5444_result enum
 */
enum rfc5444_result
_cb_print_msg_start(struct rfc5444_reader_tlvblock_context *context)
{
  struct rfc5444_print_session *session;
  struct netaddr_str buf;

  assert(context->type == RFC5444_CONTEXT_MESSAGE);

  session = container_of(context->consumer, struct rfc5444_print_session, _msg);

  abuf_puts(session->output, "\t|    ,-------------------\n");
  abuf_puts(session->output, "\t|    |  MESSAGE\n");
  abuf_puts(session->output, "\t|    |-------------------\n");
  abuf_appendf(session->output, "\t|    | * Message type:       %u\n", context->msg_type);
  abuf_appendf(session->output, "\t|    | * Message flags:      0x%02x\n", context->msg_flags);
  abuf_appendf(session->output, "\t|    | * Address length:     %u\n", context->addr_len);

  if (context->has_origaddr) {
    abuf_appendf(session->output, "\t|    | * Originator address: %s\n", netaddr_to_string(&buf, &context->orig_addr));
  }
  if (context->has_hoplimit) {
    abuf_appendf(session->output, "\t|    | * Hop limit:          %u\n", context->hoplimit);
  }
  if (context->has_hopcount) {
    abuf_appendf(session->output, "\t|    | * Hop count:          %u\n", context->hopcount);
  }
  if (context->has_seqno) {
    abuf_appendf(session->output, "\t|    | * Message seq number: %u\n", context->seqno);
  }

  return RFC5444_OKAY;
}
Beispiel #3
0
/**
 * Append the result of a template engine into an autobuffer.
 * Each usage of a key will be replaced with the corresponding
 * value.
 * @param out pointer to autobuf object
 * @param format format string (as supplied to abuf_template_init()
 * @param storage pointer to template storage object, which will be filled by
 *   this function
 * @return -1 if an out-of-memory error happened, 0 otherwise
 */
int
abuf_add_template(struct autobuf *out, const char *format,
    struct abuf_template_storage *storage) {
  struct abuf_template_storage_entry *entry;
  size_t i, last = 0;

  if (out == NULL) return 0;

  for (i=0; i<storage->count; i++) {
    entry = &storage->indices[i];

    /* copy prefix text */
    if (last < entry->start) {
      if (abuf_memcpy(out, &format[last], entry->start - last) < 0) {
        return -1;
      }
    }

    if (entry->data->value) {
      if (abuf_puts(out, entry->data->value) < 0) {
        return -1;
      }
    }
    last = entry->end;
  }

  if (last < strlen(format)) {
    if (abuf_puts(out, &format[last]) < 0) {
      return -1;
    }
  }
  return 0;
}
Beispiel #4
0
/**
 * Prints a string to an autobuffer, using JSON escape rules
 * @param out pointer to output buffer
 * @param txt string to print
 * @param delimiter true if string must be enclosed in quotation marks
 * @return -1 if an error happened, 0 otherwise
 */
static int
_json_printvalue(struct autobuf *out, const char *txt, bool delimiter) {
  const char *ptr;
  bool unprintable;

  if (delimiter) {
    if (abuf_puts(out, "\"") < 0) {
      return -1;
    }
  }
  else if (*txt == 0) {
    abuf_puts(out, "0");
  }

  ptr = txt;
  while (*ptr) {
    unprintable = !str_char_is_printable(*ptr);
    if (unprintable || *ptr == '\\' || *ptr == '\"') {
      if (ptr != txt) {
        if (abuf_memcpy(out, txt, ptr - txt) < 0) {
          return -1;
        }
      }

      if (unprintable) {
        if (abuf_appendf(out, "\\u00%02x", (unsigned char)(*ptr++)) < 0) {
          return -1;
        }
      }
      else {
        if (abuf_appendf(out, "\\%c", *ptr++) < 0) {
          return -1;
        }
      }
      txt = ptr;
    }
    else {
      ptr++;
    }
  }

  if (abuf_puts(out, txt) < 0) {
    return -1;
  }
  if (delimiter) {
    if (abuf_puts(out, "\"") < 0) {
      return -1;
    }
  }

  return 0;
}
Beispiel #5
0
void
cfg_help_choice(struct autobuf *out, bool preamble,
    const char **choices, size_t choice_count) {
  size_t i;

  if (preamble) {
    cfg_append_printable_line(out, _PREFIX "Parameter must be on of the following list:");
  }

  abuf_puts(out, "    ");
  for (i=0; i < choice_count; i++) {
    abuf_appendf(out, "%s'%s'",
        i==0 ? "" : ", ", choices[i]);
  }
  abuf_puts(out, "\n");
}
Beispiel #6
0
void
cfg_help_netaddr(struct autobuf *out, bool preamble,
    bool prefix, const int8_t *af_types, size_t af_types_count) {
  int8_t type;
  bool first;
  size_t i;

  if (preamble) {
    abuf_puts(out, _PREFIX "Parameter must be an address of the following type: ");
  }

  first = true;
  for (i=0; i<af_types_count; i++) {
    type = af_types[i];

    if (type == -1) {
      continue;
    }

    if (first) {
      first = false;
    }
    else {
      abuf_puts(out, ", ");
    }

    switch (type) {
      case AF_INET:
        abuf_puts(out, "IPv4");
        break;
      case AF_INET6:
        abuf_puts(out, "IPv6");
        break;
      case AF_MAC48:
        abuf_puts(out, "MAC48");
        break;
      case AF_EUI64:
        abuf_puts(out, "EUI64");
        break;
      default:
        abuf_puts(out, "Unspec (-)");
        break;
    }
  }

  if (prefix) {
    abuf_puts(out, "\n" _PREFIX "    (the address can have an optional prefix)");
  }
  abuf_puts(out, "\n");
}
Beispiel #7
0
/**
 * Converts a key/value list for the template engine into
 * JSON compatible output.
 * @param out output buffer
 * @param prefix string prefix for all lines
 * @param data array of template data
 * @param data_count number of template data entries
 * @return -1 if an error happened, 0 otherwise
 */
int
abuf_add_json(struct autobuf *out, const char *prefix,
    struct abuf_template_data *data, size_t data_count) {
  bool first;
  size_t i;

  if (abuf_appendf(out, "%s{\n", prefix) < 0) {
    return -1;
  }

  first = true;
  for (i=0; i<data_count; i++) {
    if (data[i].value == NULL) {
      continue;
    }

    if (!first) {
      if (abuf_puts(out, ",\n") < 0) {
        return -1;
      }
    }
    else {
      first = false;
    }

    if (abuf_appendf(out, "%s    \"%s\" : ",
        prefix, data[i].key) < 0) {
      return -1;
    }
    if (_json_printvalue(out, data[i].value, data[i].string)) {
      return -1;
    }
  }

  if (!first) {
    if (abuf_puts(out, "\n") < 0) {
      return -1;
    }
  }

  if (abuf_appendf(out, "%s}\n", prefix) < 0) {
    return -1;
  }
  return 0;
}
Beispiel #8
0
/**
 * Clear output buffer and print start of packet
 * @param context rfc5444 tlvblock reader context
 * @return see rfc5444_result enum
 */
static enum rfc5444_result
_cb_print_pkt_start(struct rfc5444_reader_tlvblock_context *context) {
  struct rfc5444_print_session *session;

  assert(context->type == RFC5444_CONTEXT_PACKET);

  session = container_of(context->consumer, struct rfc5444_print_session, _pkt);

  abuf_puts(session->output, "\t,------------------\n");
  abuf_puts(session->output, "\t|  PACKET\n");
  abuf_puts(session->output, "\t|------------------\n");
  abuf_appendf(session->output, "\t| * Packet version:    %u\n", context->pkt_version);
  abuf_appendf(session->output, "\t| * Packet flags:      0x%x\n", context->pkt_flags);
  if (context->has_pktseqno) {
    abuf_appendf(session->output, "\t| * Packet seq number: %u\n", context->pkt_seqno);
  }

  return RFC5444_OKAY;
}
Beispiel #9
0
void
cfg_help_acl(struct autobuf *out, bool preamble,
    bool prefix, const int8_t *af_types, size_t af_types_count) {
  if (preamble) {
    abuf_puts(out, _PREFIX "Parameter is an apache2 style access control list made from a list of network addresses of the following types:\n");
  }

  cfg_help_netaddr(out, false, prefix, af_types, af_types_count);

  abuf_puts(out,
      _PREFIX "    Each of the addresses/prefixes can start with a"
              " '+' to add them to the whitelist and '-' to add it to the blacklist"
              " (default is the whitelist).\n"
      _PREFIX "    In addition to this there are four keywords to configure the ACL:\n"
      _PREFIX "    - '" ACL_FIRST_ACCEPT "' to parse the whitelist first\n"
      _PREFIX "    - '" ACL_FIRST_REJECT "' to parse the blacklist first\n"
      _PREFIX "    - '" ACL_DEFAULT_ACCEPT "' to accept input if it doesn't match either list\n"
      _PREFIX "    - '" ACL_DEFAULT_REJECT "' to not accept it if it doesn't match either list\n"
      _PREFIX "    (default mode is '" ACL_FIRST_ACCEPT "' and '" ACL_DEFAULT_REJECT "')\n");
}
Beispiel #10
0
/**
 * Print end of address
 * @param context rfc5444 tlvblock reader context
 * @param dropped unused
 * @return see rfc5444_result enum
 */
enum rfc5444_result
_cb_print_addr_end(struct rfc5444_reader_tlvblock_context *context, bool dropped __attribute__((unused)))
{
  struct rfc5444_print_session *session;

  assert(context->type == RFC5444_CONTEXT_ADDRESS);

  session = container_of(context->consumer, struct rfc5444_print_session, _addr);

  abuf_puts(session->output, "\t|    |    `-------------------\n");
  return RFC5444_OKAY;
}
Beispiel #11
0
/**
 * Print address tlv
 * @param tlv tlvblock entry
 * @param context rfc5444 tlvblock reader context
 * @return see rfc5444_result enum
 */
enum rfc5444_result
_cb_print_addr_tlv(struct rfc5444_reader_tlvblock_entry *tlv, struct rfc5444_reader_tlvblock_context *context)
{
  struct rfc5444_print_session *session;

  assert(context->type == RFC5444_CONTEXT_ADDRESS);

  session = container_of(context->consumer, struct rfc5444_print_session, _addr);

  abuf_puts(session->output, "\t|    |    |    | - TLV\n");
  abuf_appendf(session->output, "\t|    |    |    |     Flags = 0x%02x\n", tlv->flags);
  abuf_appendf(session->output, "\t|    |    |    |     Type = %u", tlv->type);
  if (tlv->type_ext != 0) {
    abuf_appendf(session->output, "; Type ext. = %u", tlv->type_ext);
  }
  abuf_puts(session->output, "\n");
  if (tlv->length > 0) {
    abuf_appendf(session->output, "\t|    |    |    |     Value length: %u\n", tlv->length);
    abuf_hexdump(session->output, "\t|    |    |    |       ", tlv->single_value, tlv->length);
  }
  return RFC5444_OKAY;
}
Beispiel #12
0
/**
 * Print end of packet and call print callback if necessary
 * @param context rfc5444 tlvblock reader context
 * @param dropped unused
 * @return see rfc5444_result enum
 */
enum rfc5444_result
_cb_print_pkt_end(struct rfc5444_reader_tlvblock_context *context, bool dropped __attribute__((unused)))
{
  struct rfc5444_print_session *session;
  session = container_of(context->consumer, struct rfc5444_print_session, _pkt);

  abuf_puts(session->output, "\t`------------------\n");

  if (session->print_packet) {
    session->print_packet(session);
  }
  return RFC5444_OKAY;
}
Beispiel #13
0
/**
 * Print start of address
 * @param context rfc5444 tlvblock reader context
 * @return see rfc5444_result enum
 */
enum rfc5444_result
_cb_print_addr_start(struct rfc5444_reader_tlvblock_context *context)
{
  struct rfc5444_print_session *session;
  struct netaddr_str buf;

  assert(context->type == RFC5444_CONTEXT_ADDRESS);

  session = container_of(context->consumer, struct rfc5444_print_session, _addr);

  abuf_puts(session->output, "\t|    |    ,-------------------\n");
  abuf_appendf(session->output, "\t|    |    |  Address: %s\n", netaddr_to_string(&buf, &context->addr));
  return RFC5444_OKAY;
}
Beispiel #14
0
/**
 * Print a hexdump of a buffer to an autobuf and prepends a prefix string
 * to each line.
 * @param out output buffer
 * @param prefix string to prepend to each line
 * @param buffer buffer to be hexdumped
 * @param length length of buffer in bytes
 */
void
abuf_hexdump(struct autobuf *out, const char *prefix, void *buffer, size_t length) {
  uint8_t *buf;
  size_t j, l;

  buf = buffer;

  for (j = 0; j < length; j += 32) {
    abuf_appendf(out, "%s%04zx:", prefix, j);

    l = length - j;
    if (l > 32) {
      l = 32;
    }
    _print_hexline(out, &buf[j], l);
    abuf_puts(out, "\n");
  }
}
Beispiel #15
0
int
rfc5444_print_raw(struct autobuf *out, void *buffer, size_t length) {
  static uint8_t ZEROTAIL[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  uint8_t *ptr, *end;
  size_t idx, idx2, prefix_idx, i;
  uint8_t flags, head_len, tail_len, *head, *tail, num_addr;
  uint16_t msg_size, addr_length, mid_len;

  ptr = buffer;
  idx = 0;
  end = &ptr[length];
  
  if (idx + 1 > length) {
    return -1;
  }

  abuf_puts(out, "\t,------------------\n");
  abuf_puts(out, "\t|  PACKET\n");
  abuf_puts(out, "\t|------------------\n");

  flags = ptr[0];
  abuf_appendf(out, "\t| Packet version:    %u\n", flags >> 4);
  abuf_appendf(out, "\t| Packet flags:      0x%02x\n", flags & 0x0f);
  idx++;

  if (flags & RFC5444_PKT_FLAG_SEQNO) {
    if (idx + 2 > length) {
      return -1;
    }

    abuf_appendf(out, "\t| Packet seq number: %u\n", (ptr[0] << 8) | ptr[1]);
    idx += 2;
  }

  if (flags & RFC5444_PKT_FLAG_TLV) {
    if (idx + 2 > length) {
      return -1;
    }
    if (_print_raw_tlvblock(out, "\t|    | ", ptr, &idx, length)) {
      return -1;
    }
  }

  while (idx < length) {
    idx2 = idx;

    /* print messages */
    if (idx2 + 4 > length) {
      return -1;
    }

    abuf_puts(out, "\t|    ,-------------------\n");
    abuf_puts(out, "\t|    |  MESSAGE\n");
    abuf_puts(out, "\t|    |-------------------\n");
    abuf_appendf(out, "\t|    | Message type:       %u\n", ptr[idx]);

    flags = ptr[idx2 + 1];
    abuf_appendf(out, "\t|    | Message flags:      0x%02x\n", flags >> 4);

    addr_length = (flags & 15) + 1;
    abuf_appendf(out, "\t|    | Address length:     %u\n", addr_length);

    msg_size = (ptr[idx2 + 2] << 8) + ptr[idx + 3];
    abuf_appendf(out, "\t|    | Size:             %u\n", msg_size);
    idx2 += 4;

    if (flags & RFC5444_MSG_FLAG_ORIGINATOR) {
      if (idx2 + addr_length > idx + msg_size) {
        return -1;
      }
      abuf_appendf(out, "\t|    | Originator address: ");
      _print_hex(out, &ptr[idx2], addr_length, end);
      idx2 += addr_length;
    }
    if (flags & RFC5444_MSG_FLAG_HOPLIMIT) {
      if (idx2 + 1 > idx + msg_size) {
        return -1;
      }
      abuf_appendf(out, "\t|    | Hop limit:          %u\n", ptr[idx2]);
      idx2++;
    }
    if (flags & RFC5444_MSG_FLAG_HOPCOUNT) {
      if (idx2 + 1 > idx + msg_size) {
        return -1;
      }
      abuf_appendf(out, "\t|    | Hop count:          %u\n", ptr[idx2]);
      idx2++;
    }
    if (flags & RFC5444_MSG_FLAG_SEQNO) {
      if (idx2 + 2 > idx + msg_size) {
        return -1;
      }
      abuf_appendf(out, "\t|    | Sequence Number:    %u\n", (ptr[idx2] << 8) | ptr[idx2 + 1]);
      idx2 += 2;
    }

    if (_print_raw_tlvblock(out, "\t|    |    ", ptr, &idx2, msg_size)) {
      return -1;
    }

    while (idx2 < idx + msg_size) {
      /* print address blocks */
      if (idx2 + 2 > idx + msg_size) {
        return -1;
      }

      abuf_puts(out, "\t|    |    ,-------------------\n");
      abuf_puts(out, "\t|    |    |  ADDRESS-BLOCK\n");
      abuf_puts(out, "\t|    |    |-------------------\n");

      num_addr = ptr[idx2];
      abuf_appendf(out, "\t|    |    | Num-Addr: %u\n", num_addr);

      flags = ptr[idx2 + 1];
      abuf_appendf(out, "\t|    |    | Flags:    0x%02x\n", flags);

      idx2 += 2;

      head_len = tail_len = 0;
      head = NULL;
      tail = NULL;
      if (flags & RFC5444_ADDR_FLAG_HEAD) {
        if (idx2 + 1 > idx + msg_size) {
          return -1;
        }

        head_len = ptr[idx2];
        idx2++;
        if (idx2 + head_len > idx + msg_size) {
          return -1;
        }

        head = &ptr[idx2];

        abuf_appendf(out, "\t|    |    | Head:     ");
        _print_hex(out, head, head_len, end);
        abuf_puts(out, "\n");

        idx2 += head_len;
      }
      if (flags & RFC5444_ADDR_FLAG_FULLTAIL) {
        if (idx2 + 1 > idx + msg_size) {
          return -1;
        }

        tail_len = ptr[idx2];
        idx2++;
        if (idx2 + tail_len > idx + msg_size) {
          return -1;
        }

        tail = &ptr[idx2];
        abuf_appendf(out, "\t|    |    | Tail:     ");
        _print_hex(out, tail, tail_len, end);
        abuf_puts(out, "\n");
        idx2 += tail_len;
      }
      if (flags & RFC5444_ADDR_FLAG_ZEROTAIL) {
        if (idx2 + 1 > idx + msg_size) {
          return -1;
        }

        tail_len = ptr[idx2];

        tail = ZEROTAIL;
        abuf_appendf(out, "\t|    |    | ZeroTail: ");
        _print_hex(out, tail, tail_len, end);
        abuf_puts(out, "\n");

        idx2++;
      }

      if (head_len + tail_len >= addr_length) {
        return -1;
      }

      mid_len = (addr_length - head_len - tail_len) * num_addr;
      if (idx2 + mid_len > idx + msg_size) {
        return -1;
      }

      prefix_idx = idx + mid_len;
      if (flags & RFC5444_ADDR_FLAG_SINGLEPLEN) {
        if (prefix_idx + 1 > idx + msg_size) {
          return -1;
        }
      }
      else if (flags & RFC5444_ADDR_FLAG_MULTIPLEN) {
        if (prefix_idx + num_addr > idx + msg_size) {
          return -1;
        }
      }
      else {
        prefix_idx = 0;
      }

      for (i = 0; i < num_addr; i++) {
        abuf_puts(out, "\t|    |    |    ,-------------------\n");
        abuf_puts(out, "\t|    |    |    |  Address\n");
        abuf_puts(out, "\t|    |    |    |-------------------\n");

        abuf_appendf(out, "\t|    |    |    | Address: ");

        if (head_len) {
          _print_hex(out, head, head_len, end);

          abuf_puts(out, " | ");
        }
        _print_hex(out, &ptr[idx2], addr_length - head_len - tail_len, end);
        idx2 += addr_length - head_len - tail_len;

        if (tail_len) {
          abuf_puts(out, " | ");

          _print_hex(out, tail, tail_len, end);
        }

        if (prefix_idx) {
          abuf_appendf(out, " / %u", ptr[prefix_idx]);
        }
        if (flags & RFC5444_ADDR_FLAG_MULTIPLEN) {
          prefix_idx++;
        }

        abuf_puts(out, "\n");
      }

      if (_print_raw_tlvblock(out, "\t|    |    |    ", ptr, &idx2, msg_size)) {
        return -1;
      }
    }

    if (idx + msg_size != idx2) {
      return -1;
    }

    idx = idx2;
  }

  return 0;
}
Beispiel #16
0
static int
_print_raw_tlvblock(struct autobuf *out, const char *prefix, uint8_t *blockptr, size_t *idx, size_t length) {
  char valueprefix[128];
  uint16_t blocklength, tlv_len, tlv_singlelength;
  uint8_t *tlvptr;
  size_t idx2;
  uint8_t tlv_flags, startidx, endidx;

  if (2 > length) {
    return -1;
  }

  abuf_appendf(out, "%s,-------------------\n", prefix);
  abuf_appendf(out, "%s|  TLV BLOCK\n", prefix);
  abuf_appendf(out, "%s|-------------------\n", prefix);

  blocklength = (blockptr[*idx] << 8) | blockptr[*idx + 1];
  abuf_appendf(out, "%s| * TLV Block Size: %u\n", prefix, blocklength);

  if (blocklength + 2u > length) {
    return -1;
  }

  *idx += 2;
  tlvptr = &blockptr[*idx];
  for (idx2 = 0; idx2 < blocklength;) {
    if (idx2 + 2 > blocklength) {
      return -1;
    }

    abuf_appendf(out, "%s|    ,-------------------\n", prefix);
    abuf_appendf(out, "%s|    |  TLV\n", prefix);
    abuf_appendf(out, "%s|    |-------------------\n", prefix);

    abuf_appendf(out, "%s|    | type:        %u\n", prefix, tlvptr[idx2]);
    idx2++;

    tlv_flags = tlvptr[idx2];
    abuf_appendf(out, "%s|    | flags:       0x%02x\n", prefix, tlv_flags);
    idx2++;

    if (tlv_flags & RFC5444_TLV_FLAG_TYPEEXT) {
      if (idx2 + 1 > blocklength) {
        return -1;
      }
      abuf_appendf(out, "%s|    | ext-type:    %u\n", prefix, tlvptr[idx2]);
      idx2++;
    }

    startidx = 0;
    endidx = 0;
    if (tlv_flags & (RFC5444_TLV_FLAG_SINGLE_IDX | RFC5444_TLV_FLAG_MULTI_IDX)) {
      if (idx2 + 1 > blocklength) {
        return -1;
      }
      startidx = tlvptr[idx2];
      endidx = startidx;
      abuf_appendf(out, "%s|    | index-start: %u\n", prefix, startidx);
      idx2++;
    }
    if (tlv_flags & (RFC5444_TLV_FLAG_MULTI_IDX)) {
      if (idx2 + 1 > blocklength) {
        return -1;
      }
      endidx = tlvptr[idx2];
      if (endidx < startidx) {
        return -1;
      }
      abuf_appendf(out, "%s|    | index-end:   %u\n", prefix, endidx);
      idx2++;
    }
    tlv_len = 0;
    if (tlv_flags & (RFC5444_TLV_FLAG_EXTVALUE)) {
      if (idx2 + 1 > blocklength) {
        return -1;
      }
      tlv_len = tlvptr[idx2] << 8;
      idx2++;
    }
    if (tlv_flags & (RFC5444_TLV_FLAG_VALUE)) {
      if (idx2 + 1 > blocklength) {
        return -1;
      }
      tlv_len |= tlvptr[idx2];
      idx2++;
    }
    if (tlv_flags & (RFC5444_TLV_FLAG_EXTVALUE | RFC5444_TLV_FLAG_VALUE)) {
      abuf_appendf(out, "%s|    | length:      %u\n", prefix, tlv_len);
    }

    if (idx2 + tlv_len > blocklength) {
      return -1;
    }
    if (tlv_flags & RFC5444_TLV_FLAG_MULTIVALUE) {
      if (tlv_len % (endidx - startidx + 1)) {
        return -1;
      }
      tlv_singlelength = tlv_len / (endidx - startidx + 1);
    }
    else {
      tlv_singlelength = tlv_len;
      endidx = startidx;
    }

    snprintf(valueprefix, sizeof(valueprefix), "%s|    |   ", prefix);
    for (; startidx <= endidx; startidx++) {
      if (idx2 + tlv_singlelength > blocklength) {
        return -1;
      }
      abuf_hexdump(out, valueprefix, &tlvptr[idx2], tlv_singlelength);
      idx2 += tlv_singlelength;
      abuf_puts(out, "\n");
    }
  }

  if (blocklength != idx2) {
    return -1;
  }
  *idx += blocklength;
  return 0;
}