/*
- * valuepair.c Functions to handle value_data_t
+ * value.c Functions to handle value_data_t
*
* Version: $Id$
*
* @param[in] b Value to compare.
* @return -1 if a is less than b, 0 if both are equal, 1 if a is more than b, < -1 on error.
*/
-int value_data_cmp(PW_TYPE a_type, size_t a_len, value_data_t const *a,
- PW_TYPE b_type, size_t b_len, value_data_t const *b)
+int value_data_cmp(PW_TYPE a_type, value_data_t const *a, size_t a_len,
+ PW_TYPE b_type, value_data_t const *b, size_t b_len)
{
int compare = 0;
{
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_INVALID: /* We should never see these */
case PW_TYPE_COMBO_IP_ADDR: /* This should have been converted into IPADDR/IPV6ADDR */
- case PW_TYPE_IP_PREFIX: /* This should have been converted into IPADDR/IPV6ADDR */
+ case PW_TYPE_COMBO_IP_PREFIX: /* This should have been converted into IPADDR/IPV6ADDR */
case PW_TYPE_TLV:
case PW_TYPE_EXTENDED:
case PW_TYPE_LONG_EXTENDED:
* @return 1 if true, 0 if false, -1 on error.
*/
int value_data_cmp_op(FR_TOKEN op,
- PW_TYPE a_type, size_t a_len, value_data_t const *a,
- PW_TYPE b_type, size_t b_len, value_data_t const *b)
+ PW_TYPE a_type, value_data_t const *a, size_t a_len,
+ PW_TYPE b_type, value_data_t const *b, size_t b_len)
{
int compare = 0;
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:
- compare = value_data_cmp(a_type, a_len, a,
- b_type, b_len, b);
+ compare = value_data_cmp(a_type, a, a_len,
+ b_type, b, b_len);
if (compare < -1) { /* comparison error */
return -1;
}
/** Convert string value to a value_data_t type
*
* @param[in] ctx to alloc strings in.
- * @param[out] out where to write parsed value.
- * @param[in,out] type of value data to create/type of value created.
- * @param[in] enumv DICT_ATTR with string aliases for integer values.
- * @param[in] value String to convert. Binary safe for variable length values if len is provided.
- * @param[in] inlen may be < 0 in which case strlen(len) is used to determine length, else inlen
+ * @param[out] dst where to write parsed value.
+ * @param[in,out] src_type of value data to create/type of value created.
+ * @param[in] src_enumv DICT_ATTR with string aliases for integer values.
+ * @param[in] src String to convert. Binary safe for variable length values if len is provided.
+ * @param[in] src_len may be < 0 in which case strlen(len) is used to determine length, else src_len
* should be the length of the string or sub string to parse.
+ * @param[in] quote quotation character used to drive de-escaping
* @return length of data written to out or -1 on parse error.
*/
-ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *out,
- PW_TYPE *type, DICT_ATTR const *enumv,
- char const *value, ssize_t inlen)
+ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
+ PW_TYPE *src_type, DICT_ATTR const *src_enumv,
+ char const *src, ssize_t src_len, char quote)
{
DICT_VALUE *dval;
size_t len;
ssize_t ret;
char buffer[256];
- if (!value) return -1;
+ if (!src) return -1;
- len = (inlen < 0) ? strlen(value) : (size_t)inlen;
+ len = (src_len < 0) ? strlen(src) : (size_t)src_len;
/*
* Set size for all fixed length attributes.
*/
- ret = dict_attr_sizes[*type][1]; /* Max length */
+ ret = dict_attr_sizes[*src_type][1]; /* Max length */
/*
- * It's a variable ret type so we just alloc a new buffer
+ * It's a variable ret src_type so we just alloc a new buffer
* of size len and copy.
*/
- switch (*type) {
+ switch (*src_type) {
case PW_TYPE_STRING:
{
- size_t p_len;
- char const *cp;
- char *p;
+ char *p, *buff;
+ char const *q;
int x;
+ buff = p = talloc_bstrndup(ctx, src, len);
+
/*
- * Do escaping here
+ * No de-quoting. Just copy the string.
*/
- out->strvalue = p = talloc_memdup(ctx, value, len + 1);
- p[len] = '\0';
- talloc_set_type(p, char);
+ if (!quote) {
+ ret = len;
+ dst->strvalue = buff;
+ goto finish;
+ }
- cp = value;
- p_len = 0;
- while (*cp) {
- char c = *cp++;
+ /*
+ * Do escaping for single quoted strings. Only
+ * single quotes get escaped. Everything else is
+ * left as-is.
+ */
+ if (quote == '\'') {
+ q = p;
+
+ 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 += 2;
+ continue;
+ }
+
+ /*
+ * Not escaped, just copy it over.
+ */
+ *(p++) = *(q++);
+ }
- if (c == '\\') switch (*cp) {
- case 'r':
- c = '\r';
- cp++;
- break;
- case 'n':
- c = '\n';
- cp++;
- break;
- case 't':
- c = '\t';
- cp++;
- break;
- case '"':
- c = '"';
- cp++;
- break;
- case '\'':
- c = '\'';
- cp++;
- break;
- case '\\':
- c = '\\';
- cp++;
- break;
- case '`':
- c = '`';
- cp++;
- break;
- case '\0':
- c = '\\'; /* no cp++ */
- break;
- default:
- if ((cp[0] >= '0') &&
- (cp[0] <= '9') &&
- (cp[1] >= '0') &&
- (cp[1] <= '9') &&
- (cp[2] >= '0') &&
- (cp[2] <= '9') &&
- (sscanf(cp, "%3o", &x) == 1)) {
- c = x;
- cp += 3;
-
- } else if (cp[0]) {
+ *p = '\0';
+ ret = p - buff;
+
+ /* Shrink the buffer to the correct size */
+ dst->strvalue = talloc_realloc(ctx, buff, char, ret + 1);
+ goto finish;
+ }
+
+ /*
+ * It's "string" or `string`, do all standard
+ * escaping.
+ */
+ q = p;
+ while (q < (buff + len)) {
+ char c = *q++;
+
+ if ((c == '\\') && (q >= (buff + len))) {
+ fr_strerror_printf("Invalid escape at end of string");
+ talloc_free(buff);
+ return -1;
+ }
+
+ /*
+ * Fix up \X -> ... the binary form of it.
+ */
+ if (c == '\\') {
+ switch (*q) {
+ case 'r':
+ c = '\r';
+ q++;
+ break;
+
+ case 'n':
+ c = '\n';
+ q++;
+ break;
+
+ case 't':
+ c = '\t';
+ q++;
+ break;
+
+ case '\\':
+ c = '\\';
+ q++;
+ break;
+
+ default:
/*
- * \p --> p
+ * \" --> ", but only inside of double quoted strings, etc.
*/
- c = *cp++;
- } /* else at EOL \ --> \ */
+ if (*q == quote) {
+ c = quote;
+ q++;
+ break;
+ }
+
+ /*
+ * \000 --> binary zero character
+ */
+ if ((q[0] >= '0') &&
+ (q[0] <= '9') &&
+ (q[1] >= '0') &&
+ (q[1] <= '9') &&
+ (q[2] >= '0') &&
+ (q[2] <= '9') &&
+ (sscanf(q, "%3o", &x) == 1)) {
+ c = x;
+ q += 3;
+ }
+
+ /*
+ * Else It's not a recognised escape sequence DON'T
+ * consume the backslash. This is identical
+ * behaviour to bash and most other things that
+ * use backslash escaping.
+ */
+ }
}
+
*p++ = c;
- p_len++;
}
+
*p = '\0';
- ret = p_len;
+ ret = p - buff;
+ dst->strvalue = talloc_realloc(ctx, buff, char, ret + 1);
}
goto finish;
- /* raw octets: 0x01020304... */
case PW_TYPE_VSA:
- if (strcmp(value, "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;
/*
* No 0x prefix, just copy verbatim.
*/
- if ((len < 2) || (strncasecmp(value, "0x", 2) != 0)) {
- out->octets = talloc_memdup(ctx, (uint8_t const *)value, len);
- talloc_set_type(out->octets, uint8_t);
+ if ((len < 2) || (strncasecmp(src, "0x", 2) != 0)) {
+ dst->octets = talloc_memdup(ctx, (uint8_t const *)src, len);
+ talloc_set_type(dst->octets, uint8_t);
ret = len;
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;
}
ret = len >> 1;
p = talloc_array(ctx, uint8_t, ret);
- if (fr_hex2bin(p, ret, value + 2, len) != (size_t)ret) {
+ if (fr_hex2bin(p, ret, src + 2, len) != (size_t)ret) {
talloc_free(p);
fr_strerror_printf("Invalid hex data");
return -1;
}
- out->octets = p;
+ dst->octets = p;
}
goto finish;
case PW_TYPE_ABINARY:
#ifdef WITH_ASCEND_BINARY
- if ((len > 1) && (strncasecmp(value, "0x", 2) == 0)) goto do_octets;
+ if ((len > 1) && (strncasecmp(src, "0x", 2) == 0)) {
+ ssize_t bin;
- if (ascend_parse_filter(out, value, 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(out->filter);
+
+ ret = sizeof(dst->filter);
goto finish;
#else
/*
/* don't use this! */
case PW_TYPE_TLV:
- {
- uint8_t *p;
-
- if ((len < 2) || (len & 0x01) || (strncasecmp(value, "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, value + 2, len) != (size_t)ret) {
- fr_strerror_printf("Invalid hex data in TLV");
- return -1;
- }
-
- out->tlv = p;
- }
- goto finish;
+ fr_strerror_printf("Cannot parse TLV");
+ return -1;
case PW_TYPE_IPV4_ADDR:
{
fr_ipaddr_t addr;
- if (fr_pton4(&addr, value, inlen, fr_hostname_lookups, false) < 0) return -1;
+ if (fr_pton4(&addr, src, src_len, fr_hostname_lookups, false) < 0) return -1;
/*
* We allow v4 addresses to have a /32 suffix as some databases (PostgreSQL)
return -1;
}
- out->ipaddr.s_addr = addr.ipaddr.ip4addr.s_addr;
+ dst->ipaddr.s_addr = addr.ipaddr.ip4addr.s_addr;
}
goto finish;
{
fr_ipaddr_t addr;
- if (fr_pton4(&addr, value, inlen, fr_hostname_lookups, false) < 0) return -1;
+ if (fr_pton4(&addr, src, src_len, fr_hostname_lookups, false) < 0) return -1;
- out->ipv4prefix[1] = addr.prefix;
- memcpy(out->ipv4prefix + 2, &addr.ipaddr.ip4addr.s_addr, sizeof(out->ipv4prefix) - 2);
+ dst->ipv4prefix[1] = addr.prefix;
+ memcpy(&dst->ipv4prefix[2], &addr.ipaddr.ip4addr.s_addr, sizeof(dst->ipv4prefix) - 2);
}
goto finish;
{
fr_ipaddr_t addr;
- if (fr_pton6(&addr, value, inlen, fr_hostname_lookups, false) < 0) return -1;
+ if (fr_pton6(&addr, src, src_len, fr_hostname_lookups, false) < 0) return -1;
/*
* We allow v6 addresses to have a /128 suffix as some databases (PostgreSQL)
return -1;
}
- memcpy(&out->ipv6addr, &addr.ipaddr.ip6addr.s6_addr, sizeof(out->ipv6addr));
+ memcpy(&dst->ipv6addr, addr.ipaddr.ip6addr.s6_addr, sizeof(dst->ipv6addr));
}
goto finish;
{
fr_ipaddr_t addr;
- if (fr_pton6(&addr, value, inlen, fr_hostname_lookups, false) < 0) return -1;
+ if (fr_pton6(&addr, src, src_len, fr_hostname_lookups, false) < 0) return -1;
- out->ipv6prefix[1] = addr.prefix;
- memcpy(out->ipv6prefix + 2, &addr.ipaddr.ip6addr.s6_addr, sizeof(out->ipv6prefix) - 2);
+ dst->ipv6prefix[1] = addr.prefix;
+ memcpy(&dst->ipv6prefix[2], addr.ipaddr.ip6addr.s6_addr, sizeof(dst->ipv6prefix) - 2);
}
goto finish;
}
/*
- * It's a fixed size type, copy to a temporary buffer and
+ * It's a fixed size src_type, copy to a temporary buffer and
* \0 terminate if insize >= 0.
*/
- if (inlen > 0) {
+ if (src_len > 0) {
if (len >= sizeof(buffer)) {
fr_strerror_printf("Temporary buffer too small");
return -1;
}
- memcpy(buffer, value, inlen);
- buffer[inlen] = '\0';
- value = buffer;
+ memcpy(buffer, src, src_len);
+ buffer[src_len] = '\0';
+ src = buffer;
}
- switch(*type) {
+ switch (*src_type) {
case PW_TYPE_BYTE:
{
char *p;
/*
* Note that ALL integers are unsigned!
*/
- i = fr_strtoul(value, &p);
+ i = fr_strtoul(src, &p);
/*
- * Look for the named value for the given
+ * Look for the named src for the given
* attribute.
*/
- if (enumv && *p && !is_whitespace(p)) {
- if ((dval = dict_valbyname(enumv->attr, enumv->vendor, value)) == NULL) {
- fr_strerror_printf("Unknown value '%s' for attribute '%s'", value, enumv->name);
+ 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",
+ src, src_enumv->name);
return -1;
}
- out->byte = dval->value;
+ dst->byte = dval->value;
} else {
if (i > 255) {
- fr_strerror_printf("Byte value \"%s\" is larger than 255", value);
+ fr_strerror_printf("Byte value \"%s\" is larger than 255", src);
return -1;
}
- out->byte = i;
+ dst->byte = i;
}
break;
}
/*
* Note that ALL integers are unsigned!
*/
- i = fr_strtoul(value, &p);
+ i = fr_strtoul(src, &p);
/*
- * Look for the named value for the given
+ * Look for the named src for the given
* attribute.
*/
- if (enumv && *p && !is_whitespace(p)) {
- if ((dval = dict_valbyname(enumv->attr, enumv->vendor, value)) == NULL) {
- fr_strerror_printf("Unknown value '%s' for attribute '%s'", value, enumv->name);
+ 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",
+ src, src_enumv->name);
return -1;
}
- out->ushort = dval->value;
+ dst->ushort = dval->value;
} else {
if (i > 65535) {
- fr_strerror_printf("Short value \"%s\" is larger than 65535", value);
+ fr_strerror_printf("Short value \"%s\" is larger than 65535", src);
return -1;
}
- out->ushort = i;
+ dst->ushort = i;
}
break;
}
/*
* Note that ALL integers are unsigned!
*/
- i = fr_strtoul(value, &p);
+ i = fr_strtoul(src, &p);
/*
- * Look for the named value for the given
+ * Look for the named src for the given
* attribute.
*/
- if (enumv && *p && !is_whitespace(p)) {
- if ((dval = dict_valbyname(enumv->attr, enumv->vendor, value)) == NULL) {
- fr_strerror_printf("Unknown value '%s' for attribute '%s'", value, enumv->name);
+ 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",
+ src, src_enumv->name);
return -1;
}
- out->integer = dval->value;
+ dst->integer = dval->value;
} else {
/*
* Value is always within the limits
*/
- out->integer = i;
+ dst->integer = i;
}
}
break;
/*
* Note that ALL integers are unsigned!
*/
- if (sscanf(value, "%" PRIu64, &i) != 1) {
- fr_strerror_printf("Invalid value '%s' for attribute '%s'",
- value, enumv->name);
+ if (sscanf(src, "%" PRIu64, &i) != 1) {
+ fr_strerror_printf("Failed parsing \"%s\" as unsigned 64bit integer", src);
return -1;
}
- out->integer64 = i;
+ dst->integer64 = i;
}
break;
*/
time_t date;
- if (fr_get_time(value, &date) < 0) {
- fr_strerror_printf("failed to parse time string \"%s\"", value);
+ if (fr_get_time(src, &date) < 0) {
+ fr_strerror_printf("failed to parse time string \"%s\"", src);
return -1;
}
- out->date = date;
+ dst->date = date;
}
break;
case PW_TYPE_IFID:
- if (ifid_aton(value, (void *) &out->ifid) == NULL) {
- fr_strerror_printf("Failed to parse interface-id string \"%s\"", value);
+ if (ifid_aton(src, (void *) dst->ifid) == NULL) {
+ fr_strerror_printf("Failed to parse interface-id string \"%s\"", src);
return -1;
}
break;
* We assume the number is the bigendian representation of the
* ethernet address.
*/
- if (is_integer(value)) {
- uint64_t integer = htonll(atoll(value));
+ if (is_integer(src)) {
+ uint64_t integer = htonll(atoll(src));
- memcpy(&out->ether, &integer, sizeof(out->ether));
+ memcpy(dst->ether, &integer, sizeof(dst->ether));
break;
}
- cp = value;
+ cp = src;
while (*cp) {
if (cp[1] == ':') {
c1 = hextab;
} else {
c1 = c2 = NULL;
}
- if (!c1 || !c2 || (p_len >= sizeof(out->ether))) {
- fr_strerror_printf("failed to parse Ethernet address \"%s\"", value);
+ if (!c1 || !c2 || (p_len >= sizeof(dst->ether))) {
+ fr_strerror_printf("failed to parse Ethernet address \"%s\"", src);
return -1;
}
- out->ether[p_len] = ((c1-hextab)<<4) + (c2-hextab);
+ dst->ether[p_len] = ((c1-hextab)<<4) + (c2-hextab);
p_len++;
}
}
break;
/*
- * Crazy polymorphic (IPv4/IPv6) attribute type for WiMAX.
+ * Crazy polymorphic (IPv4/IPv6) attribute src_type for WiMAX.
*
* We try and make is saner by replacing the original
- * da, with either an IPv4 or IPv6 da type.
+ * da, with either an IPv4 or IPv6 da src_type.
*
* These are not dynamic da, and will have the same vendor
* and attribute as the original.
*/
case PW_TYPE_COMBO_IP_ADDR:
{
- if (inet_pton(AF_INET6, value, &out->ipv6addr) > 0) {
- *type = PW_TYPE_IPV6_ADDR;
+ if (inet_pton(AF_INET6, src, &dst->ipv6addr) > 0) {
+ *src_type = PW_TYPE_IPV6_ADDR;
ret = dict_attr_sizes[PW_TYPE_COMBO_IP_ADDR][1]; /* size of IPv6 address */
} else {
fr_ipaddr_t ipaddr;
- if (ip_hton(&ipaddr, AF_INET, value, false) < 0) {
- fr_strerror_printf("Failed to find IPv4 address for %s", value);
+ if (ip_hton(&ipaddr, AF_INET, src, false) < 0) {
+ fr_strerror_printf("Failed to find IPv4 address for %s", src);
return -1;
}
- *type = PW_TYPE_IPV4_ADDR;
- out->ipaddr.s_addr = ipaddr.ipaddr.ip4addr.s_addr;
+ *src_type = PW_TYPE_IPV4_ADDR;
+ dst->ipaddr.s_addr = ipaddr.ipaddr.ip4addr.s_addr;
ret = dict_attr_sizes[PW_TYPE_COMBO_IP_ADDR][0]; /* size of IPv4 address */
}
}
case PW_TYPE_SIGNED:
/* Damned code for 1 WiMAX attribute */
- out->sinteger = (int32_t)strtol(value, NULL, 10);
+ dst->sinteger = (int32_t)strtol(src, NULL, 10);
break;
- /*
- * Anything else.
- */
+ /*
+ * Anything else.
+ */
default:
- fr_strerror_printf("Unknown attribute type %d", *type);
+ fr_strerror_printf("Unknown attribute type %d", *src_type);
return -1;
}
return ret;
}
+/** Performs byte order reversal for types that need it
+ *
+ */
+static void value_data_hton(value_data_t *dst, PW_TYPE type, void const *src, size_t src_len)
+{
+ /* 8 byte integers */
+ switch (type) {
+ case PW_TYPE_INTEGER64:
+ dst->integer64 = htonll(*(uint64_t const *)src);
+ break;
+
+ /* 4 byte integers */
+ case PW_TYPE_INTEGER:
+ case PW_TYPE_DATE:
+ case PW_TYPE_SIGNED:
+ dst->integer = htonl(*(uint32_t const *)src);
+ break;
+
+ /* 2 byte integers */
+ case PW_TYPE_SHORT:
+ dst->ushort = htons(*(uint16_t const *)src);
+ break;
+
+ case PW_TYPE_OCTETS:
+ case PW_TYPE_STRING:
+ fr_assert(0);
+ return; /* shouldn't happen */
+
+ default:
+ memcpy(dst, src, src_len);
+ }
+}
+
+/** Convert one type of value_data_t to another
+ *
+ * @note This should be the canonical function used to convert between data types.
+ *
+ * @param ctx to allocate buffers in (usually the same as dst)
+ * @param dst Where to write result of casting.
+ * @param dst_type to cast to.
+ * @param dst_enumv Enumerated values used to converts strings to integers.
+ * @param src_type to cast from.
+ * @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 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,
+ PW_TYPE src_type, DICT_ATTR const *src_enumv,
+ value_data_t const *src, size_t src_len)
+{
+ if (!fr_assert(dst_type != src_type)) return -1;
+
+ /*
+ * Deserialise a value_data_t
+ */
+ if (src_type == PW_TYPE_STRING) {
+ return value_data_from_str(ctx, dst, &dst_type, dst_enumv, src->strvalue, src_len, '\0');
+ }
+
+ /*
+ * Converts the src data to octets with no processing.
+ */
+ if (dst_type == PW_TYPE_OCTETS) {
+ 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 = 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));
+ dst->integer64 = htonll(dst->integer64);
+ fixed_length:
+ return dict_attr_sizes[dst_type][0];
+ }
+
+ if ((src_type == PW_TYPE_INTEGER64) &&
+ (dst_type == PW_TYPE_ETHERNET)) {
+ uint8_t array[8];
+ uint64_t i;
+
+ i = htonll(src->integer64);
+ memcpy(array, &i, 8);
+
+ /*
+ * For OUIs in the DB.
+ */
+ if ((array[0] != 0) || (array[1] != 0)) return -1;
+
+ memcpy(dst->ether, &array[2], 6);
+ goto fixed_length;
+ }
+
+ /*
+ * For integers, we allow the casting of a SMALL type to
+ * a larger type, but not vice-versa.
+ */
+ if (dst_type == PW_TYPE_INTEGER64) {
+ switch (src_type) {
+ case PW_TYPE_BYTE:
+ dst->integer64 = src->byte;
+ break;
+
+ case PW_TYPE_SHORT:
+ dst->integer64 = src->ushort;
+ break;
+
+ case PW_TYPE_INTEGER:
+ dst->integer64 = src->integer;
+ break;
+
+ case PW_TYPE_DATE:
+ dst->integer64 = src->date;
+ break;
+
+ case PW_TYPE_OCTETS:
+ goto do_octets;
+
+ default:
+ invalid_cast:
+ fr_strerror_printf("Invalid cast from %s to %s",
+ fr_int2str(dict_attr_types, src_type, "<INVALID>"),
+ fr_int2str(dict_attr_types, dst_type, "<INVALID>"));
+ return -1;
+
+ }
+ goto fixed_length;
+ }
+
+ /*
+ * We can cast LONG integers to SHORTER ones, so long
+ * as the long one is on the LHS.
+ */
+ if (dst_type == PW_TYPE_INTEGER) {
+ switch (src_type) {
+ case PW_TYPE_BYTE:
+ dst->integer = src->byte;
+ break;
+
+ case PW_TYPE_SHORT:
+ dst->integer = src->ushort;
+ break;
+
+ case PW_TYPE_OCTETS:
+ goto do_octets;
+
+ default:
+ goto invalid_cast;
+ }
+ goto fixed_length;
+ }
+
+ if (dst_type == PW_TYPE_SHORT) {
+ switch (src_type) {
+ case PW_TYPE_BYTE:
+ dst->ushort = src->byte;
+ break;
+
+ case PW_TYPE_OCTETS:
+ goto do_octets;
+
+ default:
+ goto invalid_cast;
+ }
+ goto fixed_length;
+ }
+
+ /*
+ * 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.
+ */
+ if ((src_len < dict_attr_sizes[dst_type][0]) ||
+ (src_len > dict_attr_sizes[dst_type][1])) {
+ char const *src_type_name;
+
+ src_type_name = fr_int2str(dict_attr_types, src_type, "<INVALID>");
+ fr_strerror_printf("Invalid cast from %s to %s. Length should be between %zu and %zu but is %zu",
+ src_type_name,
+ fr_int2str(dict_attr_types, dst_type, "<INVALID>"),
+ dict_attr_sizes[dst_type][0], dict_attr_sizes[dst_type][1],
+ src_len);
+ return -1;
+ }
+
+ if (src_type == PW_TYPE_OCTETS) {
+ do_octets:
+ value_data_hton(dst, dst_type, src->octets, src_len);
+ return src_len;
+ }
+
+ /*
+ * Convert host order to network byte order.
+ */
+ if ((dst_type == PW_TYPE_IPV4_ADDR) &&
+ ((src_type == PW_TYPE_INTEGER) ||
+ (src_type == PW_TYPE_DATE) ||
+ (src_type == PW_TYPE_SIGNED))) {
+ dst->ipaddr.s_addr = htonl(src->integer);
+
+ } else if ((src_type == PW_TYPE_IPV4_ADDR) &&
+ ((dst_type == PW_TYPE_INTEGER) ||
+ (dst_type == PW_TYPE_DATE) ||
+ (dst_type == PW_TYPE_SIGNED))) {
+ dst->integer = htonl(src->ipaddr.s_addr);
+
+ } else { /* they're of the same byte order */
+ memcpy(&dst, &src, src_len);
+ }
+
+ return src_len;
+}
+
+/** 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 (src_type) {
+ default:
+ memcpy(dst, src, sizeof(*src));
+ break;
+
+ case PW_TYPE_STRING:
+ dst->strvalue = talloc_bstrndup(ctx, src->strvalue, src_len);
+ if (!dst->strvalue) return -1;
+ break;
+
+ case PW_TYPE_OCTETS:
+ dst->octets = talloc_memdup(ctx, src->octets, src_len);
+ talloc_set_type(dst->strvalue, uint8_t);
+ if (!dst->octets) return -1;
+ break;
+ }
+
+ 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) */
+}
+