/*
- * valuepair.c Functions to handle value_data_t
+ * value.c Functions to handle value_data_t
*
* Version: $Id$
*
{
size_t length;
- if (a_len > b_len) {
+ if (a_len < b_len) {
length = a_len;
} else {
length = b_len;
break;
case PW_TYPE_ETHERNET:
- compare = memcmp(&a->ether, &b->ether, sizeof(a->ether));
+ compare = memcmp(a->ether, b->ether, sizeof(a->ether));
break;
case PW_TYPE_IPV4_ADDR: {
break;
case PW_TYPE_IPV6_PREFIX:
- compare = memcmp(&a->ipv6prefix, &b->ipv6prefix, sizeof(a->ipv6prefix));
+ compare = memcmp(a->ipv6prefix, b->ipv6prefix, sizeof(a->ipv6prefix));
break;
case PW_TYPE_IPV4_PREFIX:
- compare = memcmp(&a->ipv4prefix, &b->ipv4prefix, sizeof(a->ipv4prefix));
+ compare = memcmp(a->ipv4prefix, b->ipv4prefix, sizeof(a->ipv4prefix));
break;
case PW_TYPE_IFID:
- compare = memcmp(&a->ifid, &b->ifid, sizeof(a->ifid));
+ compare = memcmp(a->ifid, b->ifid, sizeof(a->ifid));
break;
/*
case PW_TYPE_IPV4_PREFIX: /* IPv4 and IPv4 Prefix */
return value_data_cidr_cmp_op(op, 4, 32, (uint8_t const *) &a->ipaddr,
- b->ipv4prefix[1], (uint8_t const *) &b->ipv4prefix + 2);
+ b->ipv4prefix[1], (uint8_t const *) &b->ipv4prefix[2]);
default:
fr_strerror_printf("Cannot compare IPv4 with IPv6 address");
return -1;
}
- break;
case PW_TYPE_IPV4_PREFIX: /* IPv4 and IPv4 Prefix */
switch (b_type) {
case PW_TYPE_IPV4_ADDR:
return value_data_cidr_cmp_op(op, 4, a->ipv4prefix[1],
- (uint8_t const *) &a->ipv4prefix + 2,
+ (uint8_t const *) &a->ipv4prefix[2],
32, (uint8_t const *) &b->ipaddr);
case PW_TYPE_IPV4_PREFIX: /* IPv4 Prefix and IPv4 Prefix */
return value_data_cidr_cmp_op(op, 4, a->ipv4prefix[1],
- (uint8_t const *) &a->ipv4prefix + 2,
- b->ipv4prefix[1], (uint8_t const *) &b->ipv4prefix + 2);
+ (uint8_t const *) &a->ipv4prefix[2],
+ b->ipv4prefix[1], (uint8_t const *) &b->ipv4prefix[2]);
default:
fr_strerror_printf("Cannot compare IPv4 with IPv6 address");
return -1;
}
- break;
case PW_TYPE_IPV6_ADDR:
switch (b_type) {
case PW_TYPE_IPV6_PREFIX: /* IPv6 and IPv6 Preifx */
return value_data_cidr_cmp_op(op, 16, 128, (uint8_t const *) &a->ipv6addr,
- b->ipv6prefix[1], (uint8_t const *) &b->ipv6prefix + 2);
- break;
+ b->ipv6prefix[1], (uint8_t const *) &b->ipv6prefix[2]);
default:
fr_strerror_printf("Cannot compare IPv6 with IPv4 address");
return -1;
}
- break;
case PW_TYPE_IPV6_PREFIX:
switch (b_type) {
case PW_TYPE_IPV6_ADDR: /* IPv6 Prefix and IPv6 */
return value_data_cidr_cmp_op(op, 16, a->ipv6prefix[1],
- (uint8_t const *) &a->ipv6prefix + 2,
- 128, (uint8_t const *) &b->ipv6addr);
+ (uint8_t const *) &a->ipv6prefix[2],
+ 128, (uint8_t const *) &b->ipv6addr);
case PW_TYPE_IPV6_PREFIX: /* IPv6 Prefix and IPv6 */
return value_data_cidr_cmp_op(op, 16, a->ipv6prefix[1],
- (uint8_t const *) &a->ipv6prefix + 2,
- b->ipv6prefix[1], (uint8_t const *) &b->ipv6prefix + 2);
+ (uint8_t const *) &a->ipv6prefix[2],
+ b->ipv6prefix[1], (uint8_t const *) &b->ipv6prefix[2]);
default:
fr_strerror_printf("Cannot compare IPv6 with IPv4 address");
return -1;
}
- break;
default:
cmp:
switch (*src_type) {
case PW_TYPE_STRING:
{
- char *p;
+ char *p, *buff;
char const *q;
int x;
- dst->strvalue = p = talloc_memdup(ctx, src, len + 1);
- p[len] = '\0';
- talloc_set_type(p, char);
+ buff = p = talloc_bstrndup(ctx, src, len);
/*
* No de-quoting. Just copy the string.
*/
if (!quote) {
ret = len;
+ dst->strvalue = buff;
goto finish;
}
if (quote == '\'') {
q = p;
- /*
- * Escape ONLY the quotation character.
- * Everything else is left as-is.
- */
- while (q < (dst->strvalue + len)) {
+ while (q < (buff + len)) {
+ /*
+ * The quotation character is escaped.
+ */
+ if ((q[0] == '\\') &&
+ (q[1] == quote)) {
+ *(p++) = quote;
+ q += 2;
+ continue;
+ }
+
+ /*
+ * Two backslashes get mangled to one.
+ */
if ((q[0] == '\\') &&
- (q[1] == '\'')) {
- *(p++) = '\'';
+ (q[1] == '\\')) {
+ *(p++) = '\\';
q += 2;
continue;
}
}
*p = '\0';
- ret = p - dst->strvalue;
- dst->ptr = talloc_realloc(ctx, dst->ptr, char, ret + 1);
+ ret = p - buff;
+
+ /* Shrink the buffer to the correct size */
+ dst->strvalue = talloc_realloc(ctx, buff, char, ret + 1);
goto finish;
}
* escaping.
*/
q = p;
- while (q < (dst->strvalue + len)) {
+ while (q < (buff + len)) {
char c = *q++;
- if ((c == '\\') && (q >= (dst->strvalue + len))) {
+ if ((c == '\\') && (q >= (buff + len))) {
fr_strerror_printf("Invalid escape at end of string");
+ talloc_free(buff);
return -1;
}
}
*p = '\0';
- ret = p - dst->strvalue;
- dst->ptr = talloc_realloc(ctx, dst->ptr, char, ret + 1);
+ ret = p - buff;
+ dst->strvalue = talloc_realloc(ctx, buff, char, ret + 1);
}
goto finish;
- /* raw octets: 0x01020304... */
case PW_TYPE_VSA:
- if (strcmp(src, "ANY") == 0) {
- ret = 0;
- goto finish;
- } /* else it's hex */
+ fr_strerror_printf("Must use 'Attr-26 = ...' instead of 'Vendor-Specific = ...'");
+ return -1;
+ /* raw octets: 0x01020304... */
+#ifndef WITH_ASCEND_BINARY
+ do_octets:
+#endif
case PW_TYPE_OCTETS:
{
uint8_t *p;
goto finish;
}
- do_octets:
len -= 2;
/*
* Invalid.
*/
if ((len & 0x01) != 0) {
- fr_strerror_printf("Length of Hex String is not even, got %zu bytes", ret);
+ fr_strerror_printf("Length of Hex String is not even, got %zu bytes", len);
return -1;
}
case PW_TYPE_ABINARY:
#ifdef WITH_ASCEND_BINARY
- if ((len > 1) && (strncasecmp(src, "0x", 2) == 0)) goto do_octets;
+ if ((len > 1) && (strncasecmp(src, "0x", 2) == 0)) {
+ ssize_t bin;
- if (ascend_parse_filter(dst, src, len) < 0 ) {
- /* Allow ascend_parse_filter's strerror to bubble up */
- return -1;
+ if (len > ((sizeof(dst->filter) + 1) * 2)) {
+ fr_strerror_printf("Hex data is too large for ascend filter");
+ return -1;
+ }
+
+ bin = fr_hex2bin((uint8_t *) &dst->filter, ret, src + 2, len - 2);
+ if (bin < ret) {
+ memset(((uint8_t *) &dst->filter) + bin, 0, ret - bin);
+ }
+ } else {
+ if (ascend_parse_filter(dst, src, len) < 0 ) {
+ /* Allow ascend_parse_filter's strerror to bubble up */
+ return -1;
+ }
}
+
ret = sizeof(dst->filter);
goto finish;
#else
/* don't use this! */
case PW_TYPE_TLV:
- {
- uint8_t *p;
-
- if ((len < 2) || (len & 0x01) || (strncasecmp(src, "0x", 2) != 0)) {
- fr_strerror_printf("Invalid TLV specification");
- return -1;
- }
- len -= 2;
-
- ret = len >> 1;
- p = talloc_array(ctx, uint8_t, ret);
- if (!p) {
- fr_strerror_printf("No memory");
- return -1;
- }
- if (fr_hex2bin(p, ret, src + 2, len) != (size_t)ret) {
- fr_strerror_printf("Invalid hex data in TLV");
- return -1;
- }
-
- dst->tlv = p;
- }
- goto finish;
+ fr_strerror_printf("Cannot parse TLV");
+ return -1;
case PW_TYPE_IPV4_ADDR:
{
if (fr_pton4(&addr, src, src_len, fr_hostname_lookups, false) < 0) return -1;
dst->ipv4prefix[1] = addr.prefix;
- memcpy(dst->ipv4prefix + 2, &addr.ipaddr.ip4addr.s_addr, sizeof(dst->ipv4prefix) - 2);
+ memcpy(&dst->ipv4prefix[2], &addr.ipaddr.ip4addr.s_addr, sizeof(dst->ipv4prefix) - 2);
}
goto finish;
return -1;
}
- memcpy(&dst->ipv6addr, &addr.ipaddr.ip6addr.s6_addr, sizeof(dst->ipv6addr));
+ memcpy(&dst->ipv6addr, addr.ipaddr.ip6addr.s6_addr, sizeof(dst->ipv6addr));
}
goto finish;
if (fr_pton6(&addr, src, src_len, fr_hostname_lookups, false) < 0) return -1;
dst->ipv6prefix[1] = addr.prefix;
- memcpy(dst->ipv6prefix + 2, &addr.ipaddr.ip6addr.s6_addr, sizeof(dst->ipv6prefix) - 2);
+ memcpy(&dst->ipv6prefix[2], addr.ipaddr.ip6addr.s6_addr, sizeof(dst->ipv6prefix) - 2);
}
goto finish;
*/
if (src_enumv && *p && !is_whitespace(p)) {
if ((dval = dict_valbyname(src_enumv->attr, src_enumv->vendor, src)) == NULL) {
- fr_strerror_printf("Unknown or invalid value \"%s\" for attribute '%s'",
+ fr_strerror_printf("Unknown or invalid value \"%s\" for attribute %s",
src, src_enumv->name);
return -1;
}
*/
if (src_enumv && *p && !is_whitespace(p)) {
if ((dval = dict_valbyname(src_enumv->attr, src_enumv->vendor, src)) == NULL) {
- fr_strerror_printf("Unknown or invalid value \"%s\" for attribute '%s'",
+ fr_strerror_printf("Unknown or invalid value \"%s\" for attribute %s",
src, src_enumv->name);
return -1;
}
*/
if (src_enumv && *p && !is_whitespace(p)) {
if ((dval = dict_valbyname(src_enumv->attr, src_enumv->vendor, src)) == NULL) {
- fr_strerror_printf("Unknown or invalid value \"%s\" for attribute \"%s\"",
+ fr_strerror_printf("Unknown or invalid value \"%s\" for attribute %s",
src, src_enumv->name);
return -1;
}
break;
case PW_TYPE_IFID:
- if (ifid_aton(src, (void *) &dst->ifid) == NULL) {
+ if (ifid_aton(src, (void *) dst->ifid) == NULL) {
fr_strerror_printf("Failed to parse interface-id string \"%s\"", src);
return -1;
}
if (is_integer(src)) {
uint64_t integer = htonll(atoll(src));
- memcpy(&dst->ether, &integer, sizeof(dst->ether));
+ memcpy(dst->ether, &integer, sizeof(dst->ether));
break;
}
dst->sinteger = (int32_t)strtol(src, NULL, 10);
break;
- /*
- * Anything else.
- */
+ /*
+ * Anything else.
+ */
default:
fr_strerror_printf("Unknown attribute type %d", *src_type);
return -1;
case PW_TYPE_OCTETS:
case PW_TYPE_STRING:
fr_assert(0);
+ return; /* shouldn't happen */
default:
memcpy(dst, src, src_len);
* @param src_enumv Enumerated values used to convert integers to strings.
* @param src Input data.
* @param src_len Input data len.
- * @return the length of data in the dst.
+ * @return the length of data in the dst or -1 on error.
*/
ssize_t value_data_cast(TALLOC_CTX *ctx, value_data_t *dst,
PW_TYPE dst_type, DICT_ATTR const *dst_enumv,
* Converts the src data to octets with no processing.
*/
if (dst_type == PW_TYPE_OCTETS) {
- if (src_type == PW_TYPE_STRING) {
- dst->octets = talloc_memdup(ctx, src->strvalue, src_len);
- } else {
- value_data_hton(dst, src_type, src, src_len);
- dst->octets = talloc_memdup(ctx, dst, src_len);
- }
+ value_data_hton(dst, src_type, src, src_len);
+ dst->octets = talloc_memdup(ctx, dst, src_len);
talloc_set_type(dst->octets, uint8_t);
return talloc_array_length(dst->strvalue);
}
* Serialise a value_data_t
*/
if (dst_type == PW_TYPE_STRING) {
- dst->strvalue = vp_data_aprints_value(ctx, src_type, src_enumv, src, src_len, '\0');
+ dst->strvalue = value_data_aprints(ctx, src_type, src_enumv, src, src_len, '\0');
return talloc_array_length(dst->strvalue) - 1;
}
if ((src_type == PW_TYPE_IFID) &&
(dst_type == PW_TYPE_INTEGER64)) {
- memcpy(&dst->integer64, &src->ifid, sizeof(src->ifid));
+ memcpy(&dst->integer64, src->ifid, sizeof(src->ifid));
dst->integer64 = htonll(dst->integer64);
fixed_length:
return dict_attr_sizes[dst_type][0];
*/
if ((array[0] != 0) || (array[1] != 0)) return -1;
- memcpy(&dst->ether, &array[2], 6);
+ memcpy(dst->ether, &array[2], 6);
goto fixed_length;
}
dst->integer64 = src->integer;
break;
+ case PW_TYPE_DATE:
+ dst->integer64 = src->date;
+ break;
+
case PW_TYPE_OCTETS:
goto do_octets;
}
/*
+ * We can cast integers less that < INT_MAX to signed
+ */
+ if (dst_type == PW_TYPE_SIGNED) {
+ switch (src_type) {
+ case PW_TYPE_BYTE:
+ dst->sinteger = src->byte;
+ break;
+
+ case PW_TYPE_SHORT:
+ dst->sinteger = src->ushort;
+ break;
+
+ case PW_TYPE_INTEGER:
+ if (src->integer > INT_MAX) {
+ fr_strerror_printf("Invalid cast: From integer to signed. integer value %u is larger "
+ "than max signed int and would overflow", src->integer);
+ return -1;
+ }
+ dst->sinteger = (int)src->integer;
+ break;
+
+ case PW_TYPE_INTEGER64:
+ if (src->integer > INT_MAX) {
+ fr_strerror_printf("Invalid cast: From integer64 to signed. integer64 value %" PRIu64
+ " is larger than max signed int and would overflow", src->integer64);
+ return -1;
+ }
+ dst->sinteger = (int)src->integer64;
+ break;
+
+ case PW_TYPE_OCTETS:
+ goto do_octets;
+
+ default:
+ goto invalid_cast;
+ }
+ goto fixed_length;
+ }
+ /*
+ * Conversions between IPv4 addresses, IPv6 addresses, IPv4 prefixes and IPv6 prefixes
+ *
+ * For prefix to ipaddress conversions, we assume that the host portion has already
+ * been zeroed out.
+ *
+ * We allow casts from v6 to v4 if the v6 address has the correct mapping prefix.
+ *
+ * We only allow casts from prefixes to addresses if the prefix is the the length of
+ * the address, e.g. 32 for ipv4 128 for ipv6.
+ */
+ {
+ /*
+ * 10 bytes of 0x00 2 bytes of 0xff
+ */
+ static uint8_t const v4_v6_map[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff };
+
+ switch (dst_type) {
+ case PW_TYPE_IPV4_ADDR:
+ switch (src_type) {
+ case PW_TYPE_IPV6_ADDR:
+ if (memcmp(src->ipv6addr.s6_addr, v4_v6_map, sizeof(v4_v6_map)) != 0) {
+ bad_v6_prefix_map:
+ fr_strerror_printf("Invalid cast from %s to %s. No IPv4-IPv6 mapping prefix",
+ fr_int2str(dict_attr_types, src_type, "<INVALID>"),
+ fr_int2str(dict_attr_types, dst_type, "<INVALID>"));
+ return -1;
+ }
+
+ memcpy(&dst->ipaddr, &src->ipv6addr.s6_addr[sizeof(v4_v6_map)],
+ sizeof(dst->ipaddr));
+ goto fixed_length;
+
+ case PW_TYPE_IPV4_PREFIX:
+ if (src->ipv4prefix[1] != 32) {
+ bad_v4_prefix_len:
+ fr_strerror_printf("Invalid cast from %s to %s. Only /32 prefixes may be "
+ "cast to IP address types",
+ fr_int2str(dict_attr_types, src_type, "<INVALID>"),
+ fr_int2str(dict_attr_types, dst_type, "<INVALID>"));
+ return -1;
+ }
+
+ memcpy(&dst->ipaddr, &src->ipv4prefix[2], sizeof(dst->ipaddr));
+ goto fixed_length;
+
+ case PW_TYPE_IPV6_PREFIX:
+ if (src->ipv6prefix[1] != 128) {
+ bad_v6_prefix_len:
+ fr_strerror_printf("Invalid cast from %s to %s. Only /128 prefixes may be "
+ "cast to IP address types",
+ fr_int2str(dict_attr_types, src_type, "<INVALID>"),
+ fr_int2str(dict_attr_types, dst_type, "<INVALID>"));
+ return -1;
+ }
+ if (memcmp(&src->ipv6prefix[2], v4_v6_map, sizeof(v4_v6_map)) != 0) {
+ goto bad_v6_prefix_map;
+ }
+ memcpy(&dst->ipaddr, &src->ipv6prefix[2 + sizeof(v4_v6_map)],
+ sizeof(dst->ipaddr));
+ goto fixed_length;
+
+ default:
+ break;
+ }
+ break;
+
+ case PW_TYPE_IPV6_ADDR:
+ switch (src_type) {
+ case PW_TYPE_IPV4_ADDR:
+ /* Add the v4/v6 mapping prefix */
+ memcpy(dst->ipv6addr.s6_addr, v4_v6_map, sizeof(v4_v6_map));
+ memcpy(&dst->ipv6addr.s6_addr[sizeof(v4_v6_map)], &src->ipaddr,
+ sizeof(dst->ipv6addr.s6_addr) - sizeof(v4_v6_map));
+
+ goto fixed_length;
+
+ case PW_TYPE_IPV4_PREFIX:
+ if (src->ipv4prefix[1] != 32) goto bad_v4_prefix_len;
+
+ /* Add the v4/v6 mapping prefix */
+ memcpy(dst->ipv6addr.s6_addr, v4_v6_map, sizeof(v4_v6_map));
+ memcpy(&dst->ipv6addr.s6_addr[sizeof(v4_v6_map)], &src->ipv4prefix[2],
+ sizeof(dst->ipv6addr.s6_addr) - sizeof(v4_v6_map));
+ goto fixed_length;
+
+ case PW_TYPE_IPV6_PREFIX:
+ if (src->ipv4prefix[1] != 128) goto bad_v6_prefix_len;
+
+ memcpy(dst->ipv6addr.s6_addr, &src->ipv6prefix[2], sizeof(dst->ipv6addr.s6_addr));
+ goto fixed_length;
+
+ default:
+ break;
+ }
+ break;
+
+ case PW_TYPE_IPV4_PREFIX:
+ switch (src_type) {
+ case PW_TYPE_IPV4_ADDR:
+ memcpy(&dst->ipv4prefix[2], &src->ipaddr, sizeof(dst->ipv4prefix) - 2);
+ dst->ipv4prefix[0] = 0;
+ dst->ipv4prefix[1] = 32;
+ goto fixed_length;
+
+ case PW_TYPE_IPV6_ADDR:
+ if (memcmp(src->ipv6addr.s6_addr, v4_v6_map, sizeof(v4_v6_map)) != 0) {
+ goto bad_v6_prefix_map;
+ }
+ memcpy(&dst->ipv4prefix[2], &src->ipv6addr.s6_addr[sizeof(v4_v6_map)],
+ sizeof(dst->ipv4prefix) - 2);
+ dst->ipv4prefix[0] = 0;
+ dst->ipv4prefix[1] = 32;
+ goto fixed_length;
+
+ case PW_TYPE_IPV6_PREFIX:
+ if (memcmp(&src->ipv6prefix[2], v4_v6_map, sizeof(v4_v6_map)) != 0) {
+ goto bad_v6_prefix_map;
+ }
+
+ /*
+ * Prefix must be >= 96 bits. If it's < 96 bytes and the
+ * above check passed, the v6 address wasn't masked
+ * correctly when it was packet into a value_data_t.
+ */
+ if (!fr_assert(src->ipv6prefix[1] >= (sizeof(v4_v6_map) * 8))) return -1;
+
+ memcpy(&dst->ipv4prefix[2], &src->ipv6prefix[2 + sizeof(v4_v6_map)],
+ sizeof(dst->ipv4prefix) - 2);
+ dst->ipv4prefix[0] = 0;
+ dst->ipv4prefix[1] = src->ipv6prefix[1] - (sizeof(v4_v6_map) * 8);
+ goto fixed_length;
+
+ default:
+ break;
+ }
+ break;
+
+ case PW_TYPE_IPV6_PREFIX:
+ switch (src_type) {
+ case PW_TYPE_IPV4_ADDR:
+ /* Add the v4/v6 mapping prefix */
+ memcpy(&dst->ipv6prefix[2], v4_v6_map, sizeof(v4_v6_map));
+ memcpy(&dst->ipv6prefix[2 + sizeof(v4_v6_map)], &src->ipaddr,
+ (sizeof(dst->ipv6prefix) - 2) - sizeof(v4_v6_map));
+ dst->ipv6prefix[0] = 0;
+ dst->ipv6prefix[1] = 128;
+ goto fixed_length;
+
+ case PW_TYPE_IPV4_PREFIX:
+ /* Add the v4/v6 mapping prefix */
+ memcpy(&dst->ipv6prefix[2], v4_v6_map, sizeof(v4_v6_map));
+ memcpy(&dst->ipv6prefix[2 + sizeof(v4_v6_map)], &src->ipv4prefix[2],
+ (sizeof(dst->ipv6prefix) - 2) - sizeof(v4_v6_map));
+ dst->ipv6prefix[0] = 0;
+ dst->ipv6prefix[1] = (sizeof(v4_v6_map) * 8) + src->ipv4prefix[1];
+ goto fixed_length;
+
+ case PW_TYPE_IPV6_ADDR:
+ memcpy(&dst->ipv6prefix[2], &src->ipv6addr, sizeof(dst->ipv6prefix) - 2);
+ dst->ipv6prefix[0] = 0;
+ dst->ipv6prefix[1] = 128;
+ goto fixed_length;
+
+ default:
+ break;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /*
* The attribute we've found has to have a size which is
* compatible with the type of the destination cast.
*/
return src_len;
}
-ssize_t value_data_copy(TALLOC_CTX *ctx, value_data_t *dst, PW_TYPE type,
+/** Copy value data verbatim duplicating any buffers
+ *
+ * @param ctx To allocate buffers in.
+ * @param dst Where to copy value_data to.
+ * @param src_type Type of src.
+ * @param src Where to copy value_data from.
+ * @param src_len Where
+ */
+ssize_t value_data_copy(TALLOC_CTX *ctx, value_data_t *dst, PW_TYPE src_type,
const value_data_t *src, size_t src_len)
{
- switch (type) {
+ switch (src_type) {
default:
memcpy(dst, src, sizeof(*src));
break;
case PW_TYPE_STRING:
- dst->strvalue = talloc_memdup(ctx, src->strvalue, src_len + 1);
+ dst->strvalue = talloc_bstrndup(ctx, src->strvalue, src_len);
if (!dst->strvalue) return -1;
- talloc_set_type(dst->strvalue, char);
break;
case PW_TYPE_OCTETS:
return src_len;
}
+
+
+
+/** Print one attribute value to a string
+ *
+ */
+char *value_data_aprints(TALLOC_CTX *ctx,
+ PW_TYPE type, DICT_ATTR const *enumv, value_data_t const *data,
+ size_t inlen, char quote)
+{
+ char *p = NULL;
+ unsigned int i;
+
+ switch (type) {
+ case PW_TYPE_STRING:
+ {
+ size_t len, ret;
+
+ if (!quote) {
+ p = talloc_bstrndup(ctx, data->strvalue, inlen);
+ if (!p) return NULL;
+ talloc_set_type(p, char);
+ return p;
+ }
+
+ /* Gets us the size of the buffer we need to alloc */
+ len = fr_prints_len(data->strvalue, inlen, quote);
+ p = talloc_array(ctx, char, len);
+ if (!p) return NULL;
+
+ ret = fr_prints(p, len, data->strvalue, inlen, quote);
+ if (!fr_assert(ret == (len - 1))) {
+ talloc_free(p);
+ return NULL;
+ }
+ break;
+ }
+
+ case PW_TYPE_INTEGER:
+ i = data->integer;
+ goto print_int;
+
+ case PW_TYPE_SHORT:
+ i = data->ushort;
+ goto print_int;
+
+ case PW_TYPE_BYTE:
+ i = data->byte;
+
+ print_int:
+ {
+ DICT_VALUE const *dv;
+
+ if (enumv && (dv = dict_valbyattr(enumv->attr, enumv->vendor, i))) {
+ p = talloc_typed_strdup(ctx, dv->name);
+ } else {
+ p = talloc_typed_asprintf(ctx, "%u", i);
+ }
+ }
+ break;
+
+ case PW_TYPE_SIGNED:
+ p = talloc_typed_asprintf(ctx, "%d", data->sinteger);
+ break;
+
+ case PW_TYPE_INTEGER64:
+ p = talloc_typed_asprintf(ctx, "%" PRIu64 , data->integer64);
+ break;
+
+ case PW_TYPE_ETHERNET:
+ p = talloc_typed_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x",
+ data->ether[0], data->ether[1],
+ data->ether[2], data->ether[3],
+ data->ether[4], data->ether[5]);
+ break;
+
+ case PW_TYPE_ABINARY:
+#ifdef WITH_ASCEND_BINARY
+ p = talloc_array(ctx, char, 128);
+ if (!p) return NULL;
+ print_abinary(p, 128, (uint8_t const *) &data->filter, inlen, 0);
+ break;
+#else
+ /* FALL THROUGH */
+#endif
+
+ case PW_TYPE_OCTETS:
+ p = talloc_array(ctx, char, 2 + 1 + inlen * 2);
+ if (!p) return NULL;
+ p[0] = '0';
+ p[1] = 'x';
+
+ fr_bin2hex(p + 2, data->octets, inlen);
+ p[2 + (inlen * 2)] = '\0';
+ break;
+
+ case PW_TYPE_DATE:
+ {
+ time_t t;
+ struct tm s_tm;
+
+ t = data->date;
+
+ p = talloc_array(ctx, char, 64);
+ strftime(p, 64, "%b %e %Y %H:%M:%S %Z",
+ localtime_r(&t, &s_tm));
+ break;
+ }
+
+ /*
+ * We need to use the proper inet_ntop functions for IP
+ * addresses, else the output might not match output of
+ * other functions, which makes testing difficult.
+ *
+ * An example is tunnelled ipv4 in ipv6 addresses.
+ */
+ case PW_TYPE_IPV4_ADDR:
+ case PW_TYPE_IPV4_PREFIX:
+ {
+ char buff[INET_ADDRSTRLEN + 4]; // + /prefix
+
+ buff[0] = '\0';
+ value_data_prints(buff, sizeof(buff), type, enumv, data, inlen, '\0');
+
+ p = talloc_typed_strdup(ctx, buff);
+ }
+ break;
+
+ case PW_TYPE_IPV6_ADDR:
+ case PW_TYPE_IPV6_PREFIX:
+ {
+ char buff[INET6_ADDRSTRLEN + 4]; // + /prefix
+
+ buff[0] = '\0';
+ value_data_prints(buff, sizeof(buff), type, enumv, data, inlen, '\0');
+
+ p = talloc_typed_strdup(ctx, buff);
+ }
+ break;
+
+ case PW_TYPE_IFID:
+ p = talloc_typed_asprintf(ctx, "%x:%x:%x:%x",
+ (data->ifid[0] << 8) | data->ifid[1],
+ (data->ifid[2] << 8) | data->ifid[3],
+ (data->ifid[4] << 8) | data->ifid[5],
+ (data->ifid[6] << 8) | data->ifid[7]);
+ break;
+
+ case PW_TYPE_BOOLEAN:
+ p = talloc_typed_strdup(ctx, data->byte ? "yes" : "no");
+ break;
+
+ /*
+ * Don't add default here
+ */
+ case PW_TYPE_INVALID:
+ case PW_TYPE_COMBO_IP_ADDR:
+ case PW_TYPE_COMBO_IP_PREFIX:
+ case PW_TYPE_TLV:
+ case PW_TYPE_EXTENDED:
+ case PW_TYPE_LONG_EXTENDED:
+ case PW_TYPE_EVS:
+ case PW_TYPE_VSA:
+ case PW_TYPE_TIMEVAL:
+ case PW_TYPE_MAX:
+ fr_assert(0);
+ return NULL;
+ }
+
+ return p;
+}
+
+
+/** Print the value of an attribute to a string
+ *
+ * @note return value should be checked with is_truncated.
+ * @note Will always \0 terminate unless outlen == 0.
+ *
+ * @param out Where to write the printed version of the attribute value.
+ * @param outlen Length of the output buffer.
+ * @param type of data being printed.
+ * @param enumv Enumerated string values for integer types.
+ * @param data to print.
+ * @param inlen Length of data.
+ * @param quote char to escape in string output.
+ * @return the number of bytes written to the out buffer, or a number >= outlen if truncation has occurred.
+ */
+size_t value_data_prints(char *out, size_t outlen,
+ PW_TYPE type, DICT_ATTR const *enumv, value_data_t const *data,
+ ssize_t inlen, char quote)
+{
+ DICT_VALUE *v;
+ char buf[1024]; /* Interim buffer to use with poorly behaved printing functions */
+ char const *a = NULL;
+ char *p = out;
+ time_t t;
+ struct tm s_tm;
+ unsigned int i;
+
+ size_t len = 0, freespace = outlen;
+
+ if (!data) return 0;
+ if (outlen == 0) return inlen;
+
+ *out = '\0';
+
+ p = out;
+
+ switch (type) {
+ case PW_TYPE_STRING:
+
+ /*
+ * Ensure that WE add the quotation marks around the string.
+ */
+ if (quote) {
+ if (freespace < 3) return inlen + 2;
+
+ *p++ = quote;
+ freespace--;
+
+ len = fr_prints(p, freespace, data->strvalue, inlen, quote);
+ /* always terminate the quoted string with another quote */
+ if (len >= (freespace - 1)) {
+ /* Use out not p as we're operating on the entire buffer */
+ out[outlen - 2] = (char) quote;
+ out[outlen - 1] = '\0';
+ return len + 2;
+ }
+ p += len;
+ freespace -= len;
+
+ *p++ = (char) quote;
+ freespace--;
+ *p = '\0';
+
+ return len + 2;
+ }
+
+ return fr_prints(out, outlen, data->strvalue, inlen, quote);
+
+ case PW_TYPE_INTEGER:
+ i = data->integer;
+ goto print_int;
+
+ case PW_TYPE_SHORT:
+ i = data->ushort;
+ goto print_int;
+
+ case PW_TYPE_BYTE:
+ i = data->byte;
+
+print_int:
+ /* Normal, non-tagged attribute */
+ if (enumv && (v = dict_valbyattr(enumv->attr, enumv->vendor, i)) != NULL) {
+ a = v->name;
+ len = strlen(a);
+ } else {
+ /* should never be truncated */
+ len = snprintf(buf, sizeof(buf), "%u", i);
+ a = buf;
+ }
+ break;
+
+ case PW_TYPE_INTEGER64:
+ return snprintf(out, outlen, "%" PRIu64, data->integer64);
+
+ case PW_TYPE_DATE:
+ t = data->date;
+ if (quote > 0) {
+ len = strftime(buf, sizeof(buf) - 1, "%%%b %e %Y %H:%M:%S %Z%%", localtime_r(&t, &s_tm));
+ buf[0] = (char) quote;
+ buf[len - 1] = (char) quote;
+ buf[len] = '\0';
+ } else {
+ len = strftime(buf, sizeof(buf), "%b %e %Y %H:%M:%S %Z", localtime_r(&t, &s_tm));
+ }
+ a = buf;
+ break;
+
+ case PW_TYPE_SIGNED: /* Damned code for 1 WiMAX attribute */
+ len = snprintf(buf, sizeof(buf), "%d", data->sinteger);
+ a = buf;
+ break;
+
+ case PW_TYPE_IPV4_ADDR:
+ a = inet_ntop(AF_INET, &(data->ipaddr), buf, sizeof(buf));
+ len = strlen(buf);
+ break;
+
+ case PW_TYPE_ABINARY:
+#ifdef WITH_ASCEND_BINARY
+ print_abinary(buf, sizeof(buf), (uint8_t const *) data->filter, inlen, quote);
+ a = buf;
+ len = strlen(buf);
+ break;
+#else
+ /* FALL THROUGH */
+#endif
+ case PW_TYPE_OCTETS:
+ case PW_TYPE_TLV:
+ {
+ size_t binlen;
+ size_t hexlen;
+
+ binlen = inlen;
+ hexlen = (binlen * 2) + 2; /* NOT accounting for trailing NUL */
+
+ /*
+ * If the buffer is too small, put something into
+ * it, and return how much we should have written
+ *
+ * 0 + x + H + H + NUL = 5
+ */
+ if (freespace < 5) {
+ switch (freespace) {
+ case '4':
+ case '3':
+ out[0] = '0';
+ out[1] = 'x';
+ out[2] = '\0';
+ return hexlen;
+
+ case 2:
+ *out = '0';
+ out++;
+ /* FALL-THROUGH */
+
+ case 1:
+ *out = '\0';
+ break;
+
+ case 0:
+ break;
+ }
+
+ return hexlen;
+ }
+
+ /*
+ * The output buffer is at least 5 bytes, we haev
+ * room for '0xHH' plus a trailing NUL byte.
+ */
+ out[0] = '0';
+ out[1] = 'x';
+
+ /*
+ * Get maximum number of bytes we can encode
+ * given freespace, ensuring we account for '0',
+ * 'x', and the trailing NUL in the buffer.
+ *
+ * Note that we can't have "freespace = 0" after
+ * this, as 'freespace' has to be at least 5.
+ */
+ freespace -= 3;
+ freespace /= 2;
+ if (binlen > freespace) {
+ binlen = freespace;
+ }
+
+ fr_bin2hex(out + 2, data->octets, binlen);
+ return hexlen;
+ }
+
+ case PW_TYPE_IFID:
+ a = ifid_ntoa(buf, sizeof(buf), data->ifid);
+ len = strlen(buf);
+ break;
+
+ case PW_TYPE_IPV6_ADDR:
+ a = inet_ntop(AF_INET6, &data->ipv6addr, buf, sizeof(buf));
+ len = strlen(buf);
+ break;
+
+ case PW_TYPE_IPV6_PREFIX:
+ {
+ struct in6_addr addr;
+
+ /*
+ * Alignment issues.
+ */
+ memcpy(&addr, &(data->ipv6prefix[2]), sizeof(addr));
+
+ a = inet_ntop(AF_INET6, &addr, buf, sizeof(buf));
+ if (a) {
+ p = buf;
+
+ len = strlen(buf);
+ p += len;
+ len += snprintf(p, sizeof(buf) - len, "/%u", (unsigned int) data->ipv6prefix[1]);
+ }
+ }
+ break;
+
+ case PW_TYPE_IPV4_PREFIX:
+ {
+ struct in_addr addr;
+
+ /*
+ * Alignment issues.
+ */
+ memcpy(&addr, &(data->ipv4prefix[2]), sizeof(addr));
+
+ a = inet_ntop(AF_INET, &addr, buf, sizeof(buf));
+ if (a) {
+ p = buf;
+
+ len = strlen(buf);
+ p += len;
+ len += snprintf(p, sizeof(buf) - len, "/%u", (unsigned int) (data->ipv4prefix[1] & 0x3f));
+ }
+ }
+ break;
+
+ case PW_TYPE_ETHERNET:
+ return snprintf(out, outlen, "%02x:%02x:%02x:%02x:%02x:%02x",
+ data->ether[0], data->ether[1],
+ data->ether[2], data->ether[3],
+ data->ether[4], data->ether[5]);
+
+ /*
+ * Don't add default here
+ */
+ case PW_TYPE_INVALID:
+ case PW_TYPE_COMBO_IP_ADDR:
+ case PW_TYPE_COMBO_IP_PREFIX:
+ case PW_TYPE_EXTENDED:
+ case PW_TYPE_LONG_EXTENDED:
+ case PW_TYPE_EVS:
+ case PW_TYPE_VSA:
+ case PW_TYPE_TIMEVAL:
+ case PW_TYPE_BOOLEAN:
+ case PW_TYPE_MAX:
+ fr_assert(0);
+ *out = '\0';
+ return 0;
+ }
+
+ if (a) strlcpy(out, a, outlen);
+
+ return len; /* Return the number of bytes we would of written (for truncation detection) */
+}
+