* @param[in] data_len of data to parse.
* @param[in] decode_ctx passed to decode_tlv
* @param[in] decode_tlv function to decode one attribute / option / tlv
+ * @param[in] verify_tlvs simple function to see if the TLVs are even vaguely well-formed
* @param[in] nested whether or not we create nested VPs.
* <0 on error - decode error, or OOM
* data_len on success
ssize_t fr_pair_tlvs_from_network(TALLOC_CTX *ctx, fr_pair_list_t *out,
fr_dict_attr_t const *parent,
uint8_t const *data, size_t const data_len,
- void *decode_ctx, fr_pair_decode_value_t decode_tlv, bool nested)
+ void *decode_ctx, fr_pair_decode_value_t decode_tlv,
+ fr_pair_tlvs_verify_t verify_tlvs,
+ bool nested)
{
uint8_t const *p, *end;
fr_pair_list_t tlvs, *list;
if (!fr_cond_assert_msg((parent->type == FR_TYPE_TLV || (parent->type == FR_TYPE_VENDOR)),
"%s: Internal sanity check failed, attribute \"%s\" is not of type 'tlv'",
__FUNCTION__, parent->name)) return PAIR_DECODE_FATAL_ERROR;
+
+ /*
+ * Do a quick sanity check to see if the TLVs are at all OK.
+ */
+ if (verify_tlvs && !verify_tlvs(data, data_len)) return fr_pair_raw_from_network(ctx, out, parent, data, data_len);
+
p = data;
end = data + data_len;
ssize_t slen;
slen = decode_tlv(child_ctx, list, parent, p, (end - p), decode_ctx);
- if (slen <= 0) {
+ if (slen < 0) {
+ FR_PROTO_TRACE(" tlv decode failed at offset %zu - converting to raw", (size_t) (p - data));
fr_pair_list_free(list);
talloc_free(vp);
return fr_pair_raw_from_network(ctx, out, parent, data, data_len);
fr_dict_attr_t const *parent,
uint8_t const *data, size_t const data_len, void *decode_ctx);
+typedef bool (*fr_pair_tlvs_verify_t)(uint8_t const *data, size_t const data_len);
+
#define PROTO_DECODE_FUNC(_name) static ssize_t decode_ ## _name(TALLOC_CTX *ctx, fr_pair_list_t *out, \
fr_dict_attr_t const *parent, \
uint8_t const *data, size_t const data_len, void *decode_ctx)
ssize_t fr_pair_tlvs_from_network(TALLOC_CTX *ctx, fr_pair_list_t *out,
fr_dict_attr_t const *parent,
uint8_t const *data, size_t const data_len,
- void *decode_ctx, fr_pair_decode_value_t decode_tlv, bool nested) CC_HINT(nonnull(1,2,3,4,7));
+ void *decode_ctx, fr_pair_decode_value_t decode_tlv, fr_pair_tlvs_verify_t verify_tlvs,
+ bool nested) CC_HINT(nonnull(1,2,3,4,7));
ssize_t fr_pair_dns_labels_from_network(TALLOC_CTX *ctx, fr_pair_list_t *out,
fr_dict_attr_t const *parent, uint8_t const *start,
fr_dict_attr_t const *parent,
uint8_t const *data, size_t const data_len, void *decode_ctx);
+static bool verify_tlvs(uint8_t const *data, size_t data_len)
+{
+ uint8_t const *p = data;
+ uint8_t const *end = data + data_len;
+
+ while (p < end) {
+ if ((end - p) < 2) return false;
+
+ if ((p + p[1]) > end) return false;
+
+ p += 2 + p[1];
+ }
+
+ return true;
+}
+
static ssize_t decode_tlv_trampoline(TALLOC_CTX *ctx, fr_pair_list_t *out,
fr_dict_attr_t const *parent,
uint8_t const *data, size_t const data_len, void *decode_ctx)
{
- return fr_pair_tlvs_from_network(ctx, out, parent, data, data_len, decode_ctx, decode_option, false);
+ return fr_pair_tlvs_from_network(ctx, out, parent, data, data_len, decode_ctx, decode_option, verify_tlvs, false);
}
static ssize_t decode_value(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent,
}
p++;
- len = fr_pair_tlvs_from_network(ctx, out, vendor, p, option_len, decode_ctx, decode_option, false);
+ len = fr_pair_tlvs_from_network(ctx, out, vendor, p, option_len, decode_ctx, decode_option, verify_tlvs, false);
if (len <= 0) return len;
p += len;
if (!da) {
da = fr_dict_unknown_attr_afrom_num(packet_ctx->tmp_ctx, parent, option);
if (!da) return PAIR_DECODE_OOM;
- }
- FR_PROTO_TRACE("decode context changed %s -> %s",da->parent->name, da->name);
- if ((da->type == FR_TYPE_STRING) && da_is_dns_label(da)) {
+ slen = fr_pair_raw_from_network(ctx, out, da, data + 2, len);
+
+ } else if ((da->type == FR_TYPE_STRING) && da_is_dns_label(da)) {
slen = fr_pair_dns_labels_from_network(ctx, out, da, data + 2, data + 2, len, NULL, true);
} else if (da->flags.array) {
slen = decode_vsa(ctx, out, da, data + 2, len, decode_ctx);
} else if (da->type == FR_TYPE_TLV) {
- slen = fr_pair_tlvs_from_network(ctx, out, da, data + 2, len, decode_ctx, decode_option, false);
+ slen = fr_pair_tlvs_from_network(ctx, out, da, data + 2, len, decode_ctx, decode_option, verify_tlvs, false);
} else {
slen = decode_value(ctx, out, da, data + 2, len, decode_ctx);
slen = decode_vsa(ctx, out, da, packet_ctx->buffer, q - packet_ctx->buffer, packet_ctx);
} else if (da->type == FR_TYPE_TLV) {
- slen = fr_pair_tlvs_from_network(ctx, out, da, packet_ctx->buffer, q - packet_ctx->buffer, packet_ctx, decode_option, false);
+ slen = fr_pair_tlvs_from_network(ctx, out, da, packet_ctx->buffer, q - packet_ctx->buffer,
+ packet_ctx, decode_option, verify_tlvs, false);
} else if (da->flags.array) {
slen = fr_pair_array_from_network(ctx, out, da, packet_ctx->buffer, q - packet_ctx->buffer, packet_ctx, decode_value);
fr_dict_attr_t const *parent,
uint8_t const *data, size_t const data_len, void *decode_ctx)
{
- return fr_pair_tlvs_from_network(ctx, out, parent, data, data_len, decode_ctx, decode_option, true);
+ return fr_pair_tlvs_from_network(ctx, out, parent, data, data_len, decode_ctx, decode_option, NULL, true);
}
static ssize_t decode_value(TALLOC_CTX *ctx, fr_pair_list_t *out,
* header, as we're just decoding the values
* here.
*/
- slen = fr_pair_tlvs_from_network(vp, &vp->vp_group, fr_dict_root(dict_dhcpv6), data, data_len, decode_ctx, decode_option, false);
+ slen = fr_pair_tlvs_from_network(vp, &vp->vp_group, fr_dict_root(dict_dhcpv6), data, data_len, decode_ctx, decode_option, NULL, false);
if (slen < 0) {
talloc_free(vp);
return slen;
FR_PROTO_TRACE("decode context %s -> %s", parent->name, da->name);
- return fr_pair_tlvs_from_network(ctx, out, da, data + 4, data_len - 4, decode_ctx, decode_option, false);
+ return fr_pair_tlvs_from_network(ctx, out, da, data + 4, data_len - 4, decode_ctx, decode_option, NULL, false);
}
static ssize_t decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out,
slen = decode_vsa(ctx, out, da, data + 4, len, decode_ctx);
} else if (da->type == FR_TYPE_TLV) {
- slen = fr_pair_tlvs_from_network(ctx, out, da, data + 4, len, decode_ctx, decode_option, false);
+ slen = fr_pair_tlvs_from_network(ctx, out, da, data + 4, len, decode_ctx, decode_option, NULL, false);
} else {
slen = decode_value(ctx, out, da, data + 4, len, decode_ctx);
fr_dict_attr_t const *parent,
uint8_t const *data, size_t const data_len, void *decode_ctx)
{
- return fr_pair_tlvs_from_network(ctx, out, parent, data, data_len, decode_ctx, decode_option, true);
+ return fr_pair_tlvs_from_network(ctx, out, parent, data, data_len, decode_ctx, decode_option, NULL, true);
}
slen = fr_pair_array_from_network(ctx, out, da, data + 2, len - 2, decode_ctx, decode_value);
} else if (da->type == FR_TYPE_TLV) {
- slen = fr_pair_tlvs_from_network(ctx, out, da, data + 2, len - 2, decode_ctx, decode_rfc, false);
+ slen = fr_pair_tlvs_from_network(ctx, out, da, data + 2, len - 2, decode_ctx, decode_rfc, NULL, false);
} else {
slen = decode_value(ctx, out, da, data + 2, len - 2, decode_ctx);
if (!vp) return PAIR_DECODE_OOM;
redo:
- slen = fr_pair_tlvs_from_network(vp, &vp->vp_group, parent, p + 2, p[1] - 2, packet_ctx, decode_rfc, false);
+ slen = fr_pair_tlvs_from_network(vp, &vp->vp_group, parent, p + 2, p[1] - 2, packet_ctx, decode_rfc, NULL, false);
if (slen <= 0) {
talloc_free(vp);
return slen;