[PW_TYPE_MAX] = 0 /* Force compiler to allocate memory for all types */
};
-/** Copy flags and type data from one value box to another
- *
- * @param[in] dst to copy flags to
- * @param[in] src of data.
- */
-static inline void value_box_copy_attrs(value_box_t *dst, value_box_t const *src)
-{
- dst->type = src->type;
- dst->length = src->length;
- dst->tainted = src->tainted;
- if (fr_dict_enum_types[dst->type]) dst->datum.enumv = src->datum.enumv;
-}
-
/** Allocate a value box of a specific type
*
* Allocates memory for the box, and sets the length of the value
return value;
}
+/** Clear/free any existing value
+ *
+ * @note Do not use on uninitialised memory.
+ *
+ * @param[in] data to clear.
+ */
+void inline value_box_clear(value_box_t *data)
+{
+ switch (data->type) {
+ case PW_TYPE_OCTETS:
+ case PW_TYPE_STRING:
+ TALLOC_FREE(data->datum.ptr);
+ break;
+
+ case PW_TYPE_STRUCTURAL:
+ if (!fr_cond_assert(0)) return;
+
+ case PW_TYPE_INVALID:
+ return;
+
+ default:
+ memset(&data->datum, 0, dict_attr_sizes[data->type][1]);
+ break;
+ }
+
+ data->tainted = false;
+ data->type = PW_TYPE_INVALID;
+ data->length = 0;
+}
+
+/** Copy flags and type data from one value box to another
+ *
+ * @param[in] dst to copy flags to
+ * @param[in] src of data.
+ */
+static inline void value_box_copy_meta(value_box_t *dst, value_box_t const *src)
+{
+ dst->type = src->type;
+ dst->length = src->length;
+ dst->tainted = src->tainted;
+ if (fr_dict_enum_types[dst->type]) dst->datum.enumv = src->datum.enumv;
+}
+
/** Compare two values
*
* @param[in] a Value to compare.
return out_p - out;
}
-/** Clear/free any existing value
- *
- * @note Do not use on uninitialised memory.
+/** Performs byte order reversal for types that need it
*
- * @param[in] data to clear.
+ * @param[in] dst Where to write the result. May be the same as src.
+ * @param[in] src #value_box_t containing an integer value.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
*/
-void value_box_clear(value_box_t *data)
+int value_box_hton(value_box_t *dst, value_box_t const *src)
{
- switch (data->type) {
- case PW_TYPE_OCTETS:
- case PW_TYPE_STRING:
- TALLOC_FREE(data->datum.ptr);
+ if (!fr_cond_assert(src->type != PW_TYPE_INVALID)) return -1;
+
+ /* 8 byte integers */
+ switch (src->type) {
+ case PW_TYPE_INTEGER64:
+ dst->datum.integer64 = htonll(src->datum.integer64);
break;
- case PW_TYPE_STRUCTURAL:
- if (!fr_cond_assert(0)) return;
+ /* 4 byte integers */
+ case PW_TYPE_INTEGER:
+ case PW_TYPE_DATE:
+ case PW_TYPE_SIGNED:
+ dst->datum.integer = htonl(src->datum.integer);
+ break;
- case PW_TYPE_INVALID:
- return;
+ /* 2 byte integers */
+ case PW_TYPE_SHORT:
+ dst->datum.ushort = htons(src->datum.ushort);
+ break;
+
+ case PW_TYPE_OCTETS:
+ case PW_TYPE_STRING:
+ if (!fr_cond_assert(0)) return -1; /* shouldn't happen */
default:
- memset(&data->datum, 0, dict_attr_sizes[data->type][1]);
+ value_box_copy(NULL, dst, src);
break;
}
- data->tainted = false;
- data->type = PW_TYPE_INVALID;
- data->length = 0;
+ value_box_copy_meta(dst, src);
+
+ return 0;
}
-/** Convert string value to a value_box_t type
+/** Convert one type of value_box_t to another
*
- * @todo Should take taint param.
+ * @note This should be the canonical function used to convert between data types.
*
- * @param[in] ctx to alloc strings in.
- * @param[out] dst where to write parsed value.
- * @param[in,out] dst_type of value data to create/dst_type of value created.
- * @param[in] dst_enumv fr_dict_attr_t with string aliases for integer values.
- * @param[in] in 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 should be the length of the string or
- * sub string to parse.
- * @param[in] quote character used set unescape mode. @see value_str_unescape.
+ * @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 Input data.
* @return
* - 0 on success.
- * - -1 on parse error.
+ * - -1 on failure.
*/
-int value_box_from_str(TALLOC_CTX *ctx, value_box_t *dst,
- PW_TYPE *dst_type, fr_dict_attr_t const *dst_enumv,
- char const *in, ssize_t inlen, char quote)
+int value_box_cast(TALLOC_CTX *ctx, value_box_t *dst,
+ PW_TYPE dst_type, fr_dict_attr_t const *dst_enumv,
+ value_box_t const *src)
{
- fr_dict_enum_t *dval;
- size_t len;
- ssize_t ret;
- char buffer[256];
-
- if (!fr_cond_assert(*dst_type != PW_TYPE_INVALID)) return -1;
+ if (!fr_cond_assert(dst_type != PW_TYPE_INVALID)) return -1;
+ if (!fr_cond_assert(src->type != PW_TYPE_INVALID)) return -1;
- if (!in) return -1;
+ if (fr_dict_non_data_types[dst_type]) {
+ fr_strerror_printf("Invalid cast from %s to %s. Can only cast simple data types.",
+ fr_int2str(dict_attr_types, src->type, "<INVALID>"),
+ fr_int2str(dict_attr_types, dst_type, "<INVALID>"));
+ return -1;
+ }
- len = (inlen < 0) ? strlen(in) : (size_t)inlen;
+ /*
+ * If it's the same type, copy.
+ */
+ if (dst_type == src->type) return value_box_copy(ctx, dst, src);
/*
- * Set size for all fixed length attributes.
+ * Deserialise a value_box_t
*/
- ret = dict_attr_sizes[*dst_type][1]; /* Max length */
+ if (src->type == PW_TYPE_STRING) {
+ return value_box_from_str(ctx, dst, &dst_type, dst_enumv, src->datum.strvalue, src->length, '\0');
+ }
/*
- * It's a variable ret src->dst_type so we just alloc a new buffer
- * of size len and copy.
+ * Converts the src data to octets with no processing.
*/
- switch (*dst_type) {
- case PW_TYPE_STRING:
- {
- char *buff, *p;
+ if (dst_type == PW_TYPE_OCTETS) {
+ value_box_hton(dst, src);
+ dst->datum.octets = talloc_memdup(ctx, &dst->datum, src->length);
+ dst->length = src->length;
+ dst->type = dst_type;
+ talloc_set_type(dst->datum.octets, uint8_t);
+ return 0;
+ }
- buff = talloc_bstrndup(ctx, in, len);
+ /*
+ * Serialise a value_box_t
+ */
+ if (dst_type == PW_TYPE_STRING) {
+ dst->datum.strvalue = value_box_asprint(ctx, src, '\0');
+ dst->length = talloc_array_length(dst->datum.strvalue) - 1;
+ dst->type = dst_type;
+ return 0;
+ }
- /*
- * No de-quoting. Just copy the string.
- */
- if (!quote) {
- ret = len;
- dst->datum.strvalue = buff;
- goto finish;
- }
+ if ((src->type == PW_TYPE_IFID) &&
+ (dst_type == PW_TYPE_INTEGER64)) {
+ memcpy(&dst->datum.integer64, src->datum.ifid, sizeof(src->datum.ifid));
+ dst->datum.integer64 = htonll(dst->datum.integer64);
- len = value_str_unescape((uint8_t *)buff, in, len, quote);
+ fixed_length:
+ dst->length = dict_attr_sizes[dst_type][0];
+ dst->type = dst_type;
+ if (fr_dict_enum_types[dst_type]) dst->datum.enumv = dst_enumv;
- /*
- * Shrink the buffer to the correct size
- * and \0 terminate it. There is a significant
- * amount of legacy code that assumes the string
- * buffer in value pairs is a C string.
- *
- * It's better for the server to print partial
- * strings, instead of SEGV.
- */
- dst->datum.strvalue = p = talloc_realloc(ctx, buff, char, len + 1);
- p[len] = '\0';
- ret = len;
+ return 0;
}
- goto finish;
- case PW_TYPE_VSA:
- fr_strerror_printf("Must use 'Attr-26 = ...' instead of 'Vendor-Specific = ...'");
- return -1;
+ if ((src->type == PW_TYPE_INTEGER64) &&
+ (dst_type == PW_TYPE_ETHERNET)) {
+ uint8_t array[8];
+ uint64_t i;
- /* raw octets: 0x01020304... */
- case PW_TYPE_OCTETS:
- {
- uint8_t *p;
+ i = htonll(src->datum.integer64);
+ memcpy(array, &i, 8);
/*
- * No 0x prefix, just copy verbatim.
+ * For OUIs in the DB.
*/
- if ((len < 2) || (strncasecmp(in, "0x", 2) != 0)) {
- dst->datum.octets = talloc_memdup(ctx, (uint8_t const *)in, len);
- talloc_set_type(dst->datum.octets, uint8_t);
- ret = len;
- goto finish;
- }
+ if ((array[0] != 0) || (array[1] != 0)) return -1;
- len -= 2;
+ memcpy(dst->datum.ether, &array[2], 6);
+ goto fixed_length;
+ }
- /*
- * Invalid.
- */
- if ((len & 0x01) != 0) {
- fr_strerror_printf("Length of Hex String is not even, got %zu bytes", len);
- return -1;
- }
+ if (dst_type == PW_TYPE_SHORT) {
+ switch (src->type) {
+ case PW_TYPE_BYTE:
+ dst->datum.ushort = src->datum.byte;
+ break;
- ret = len >> 1;
- p = talloc_array(ctx, uint8_t, ret);
- if (fr_hex2bin(p, ret, in + 2, len) != (size_t)ret) {
- talloc_free(p);
- fr_strerror_printf("Invalid hex data");
- return -1;
- }
+ case PW_TYPE_OCTETS:
+ goto do_octets;
- dst->datum.octets = p;
+ default:
+ goto invalid_cast;
+ }
+ goto fixed_length;
}
- goto finish;
- case PW_TYPE_ABINARY:
-#ifdef WITH_ASCEND_BINARY
- if ((len > 1) && (strncasecmp(in, "0x", 2) == 0)) {
- ssize_t bin;
+ /*
+ * 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->datum.integer = src->datum.byte;
+ break;
- if (len > ((sizeof(dst->datum.filter) + 1) * 2)) {
- fr_strerror_printf("Hex data is too large for ascend filter");
- return -1;
- }
+ case PW_TYPE_SHORT:
+ dst->datum.integer = src->datum.ushort;
+ break;
- bin = fr_hex2bin((uint8_t *) &dst->datum.filter, ret, in + 2, len - 2);
- if (bin < ret) {
- memset(((uint8_t *) &dst->datum.filter) + bin, 0, ret - bin);
- }
- } else {
- if (ascend_parse_filter(dst, in, len) < 0 ) {
- /* Allow ascend_parse_filter's strerror to bubble up */
+ case PW_TYPE_SIGNED:
+ if (src->datum.sinteger < 0 ) {
+ fr_strerror_printf("Invalid cast: From signed to integer. signed value %d is negative ",
+ src->datum.sinteger);
return -1;
}
- }
-
- ret = sizeof(dst->datum.filter);
- goto finish;
-#else
- /*
- * If Ascend binary is NOT defined,
- * then fall through to raw octets, so that
- * the user can at least make them by hand...
- */
- goto do_octets;
-#endif
-
- case PW_TYPE_IPV4_ADDR:
- {
- fr_ipaddr_t addr;
+ dst->datum.integer = (uint32_t)src->datum.sinteger;
+ break;
- if (fr_inet_pton4(&addr, in, inlen, fr_hostname_lookups, false, true) < 0) return -1;
+ case PW_TYPE_OCTETS:
+ goto do_octets;
- /*
- * We allow v4 addresses to have a /32 suffix as some databases (PostgreSQL)
- * print them this way.
- */
- if (addr.prefix != 32) {
- fr_strerror_printf("Invalid IPv4 mask length \"/%i\". Only \"/32\" permitted "
- "for non-prefix types", addr.prefix);
- return -1;
+ default:
+ goto invalid_cast;
}
-
- dst->datum.ipaddr.s_addr = addr.ipaddr.ip4addr.s_addr;
+ goto fixed_length;
}
- goto finish;
- case PW_TYPE_IPV4_PREFIX:
- {
- fr_ipaddr_t addr;
+ /*
+ * 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->datum.integer64 = src->datum.byte;
+ break;
- if (fr_inet_pton4(&addr, in, inlen, fr_hostname_lookups, false, true) < 0) return -1;
+ case PW_TYPE_SHORT:
+ dst->datum.integer64 = src->datum.ushort;
+ break;
- dst->datum.ipv4prefix[1] = addr.prefix;
- memcpy(&dst->datum.ipv4prefix[2], &addr.ipaddr.ip4addr.s_addr, sizeof(dst->datum.ipv4prefix) - 2);
- }
- goto finish;
+ case PW_TYPE_INTEGER:
+ dst->datum.integer64 = src->datum.integer;
+ break;
- case PW_TYPE_IPV6_ADDR:
- {
- fr_ipaddr_t addr;
+ case PW_TYPE_DATE:
+ dst->datum.integer64 = src->datum.date;
+ break;
- if (fr_inet_pton6(&addr, in, inlen, fr_hostname_lookups, false, true) < 0) return -1;
+ case PW_TYPE_OCTETS:
+ goto do_octets;
- /*
- * We allow v6 addresses to have a /128 suffix as some databases (PostgreSQL)
- * print them this way.
- */
- if (addr.prefix != 128) {
- fr_strerror_printf("Invalid IPv6 mask length \"/%i\". Only \"/128\" permitted "
- "for non-prefix types", addr.prefix);
+ 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;
- }
- memcpy(&dst->datum.ipv6addr, addr.ipaddr.ip6addr.s6_addr, sizeof(dst->datum.ipv6addr));
+ }
+ goto fixed_length;
}
- goto finish;
- case PW_TYPE_IPV6_PREFIX:
- {
- fr_ipaddr_t addr;
+ /*
+ * We can cast integers less that < INT_MAX to signed
+ */
+ if (dst_type == PW_TYPE_SIGNED) {
+ switch (src->type) {
+ case PW_TYPE_BYTE:
+ dst->datum.sinteger = src->datum.byte;
+ break;
- if (fr_inet_pton6(&addr, in, inlen, fr_hostname_lookups, false, true) < 0) return -1;
+ case PW_TYPE_SHORT:
+ dst->datum.sinteger = src->datum.ushort;
+ break;
- dst->datum.ipv6prefix[1] = addr.prefix;
- memcpy(&dst->datum.ipv6prefix[2], addr.ipaddr.ip6addr.s6_addr, sizeof(dst->datum.ipv6prefix) - 2);
- }
- goto finish;
+ case PW_TYPE_INTEGER:
+ if (src->datum.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->datum.integer);
+ return -1;
+ }
+ dst->datum.sinteger = (int)src->datum.integer;
+ break;
- /*
- * Dealt with below
- */
- case PW_TYPE_BOUNDED:
- break;
+ case PW_TYPE_INTEGER64:
+ if (src->datum.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->datum.integer64);
+ return -1;
+ }
+ dst->datum.sinteger = (int)src->datum.integer64;
+ break;
- case PW_TYPE_STRUCTURAL_EXCEPT_VSA:
- case PW_TYPE_VENDOR:
- case PW_TYPE_BAD:
- fr_strerror_printf("Invalid dst_type %d", *dst_type);
- return -1;
- }
+ case PW_TYPE_OCTETS:
+ goto do_octets;
- /*
- * It's a fixed size src->dst_type, copy to a temporary buffer and
- * \0 terminate if insize >= 0.
- */
- if (inlen > 0) {
- if (len >= sizeof(buffer)) {
- fr_strerror_printf("Temporary buffer too small");
- return -1;
+ default:
+ goto invalid_cast;
}
-
- memcpy(buffer, in, inlen);
- buffer[inlen] = '\0';
- in = buffer;
+ goto fixed_length;
}
- switch (*dst_type) {
- case PW_TYPE_BYTE:
- {
- char *p;
- unsigned int i;
+ if (dst_type == PW_TYPE_TIMEVAL) {
+ switch (src->type) {
+ case PW_TYPE_BYTE:
+ dst->datum.timeval.tv_sec = src->datum.byte;
+ dst->datum.timeval.tv_usec = 0;
+ break;
- /*
- * Note that ALL integers are unsigned!
- */
- i = fr_strtoul(in, &p);
+ case PW_TYPE_SHORT:
+ dst->datum.timeval.tv_sec = src->datum.ushort;
+ dst->datum.timeval.tv_usec = 0;
+ break;
- /*
- * Look for the named in for the given
- * attribute.
- */
- if (dst_enumv && *p && !is_whitespace(p)) {
- if ((dval = fr_dict_enum_by_name(NULL, dst_enumv, in)) == NULL) {
- fr_strerror_printf("Unknown or invalid value \"%s\" for attribute %s",
- in, dst_enumv->name);
- return -1;
- }
+ case PW_TYPE_INTEGER:
+ dst->datum.timeval.tv_sec = src->datum.integer;
+ dst->datum.timeval.tv_usec = 0;
+ break;
- dst->datum.byte = dval->value;
- } else {
- if (i > 255) {
- fr_strerror_printf("Byte value \"%s\" is larger than 255", in);
- return -1;
- }
+ case PW_TYPE_INTEGER64:
+ /*
+ * tv_sec is a time_t, which is variable in size
+ * depending on the system.
+ *
+ * It should be >= 64bits on modern systems,
+ * but you never know...
+ */
+ if (sizeof(uint64_t) > SIZEOF_MEMBER(struct timeval, tv_sec)) goto invalid_cast;
+ dst->datum.timeval.tv_sec = src->datum.integer64;
+ dst->datum.timeval.tv_usec = 0;
+ break;
- dst->datum.byte = i;
+ default:
+ goto invalid_cast;
}
- break;
}
- case PW_TYPE_SHORT:
+ /*
+ * 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.
+ */
{
- char *p;
- unsigned int i;
-
/*
- * Note that ALL integers are unsigned!
+ * 10 bytes of 0x00 2 bytes of 0xff
*/
- i = fr_strtoul(in, &p);
+ static uint8_t const v4_v6_map[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff };
- /*
- * Look for the named in for the given
- * attribute.
- */
- if (dst_enumv && *p && !is_whitespace(p)) {
- if ((dval = fr_dict_enum_by_name(NULL, dst_enumv, in)) == NULL) {
- fr_strerror_printf("Unknown or invalid value \"%s\" for attribute %s",
- in, dst_enumv->name);
- return -1;
- }
+ switch (dst_type) {
+ case PW_TYPE_IPV4_ADDR:
+ switch (src->type) {
+ case PW_TYPE_IPV6_ADDR:
+ if (memcmp(src->datum.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;
+ }
- dst->datum.ushort = dval->value;
- } else {
- if (i > 65535) {
- fr_strerror_printf("Short value \"%s\" is larger than 65535", in);
- return -1;
+ memcpy(&dst->datum.ipaddr, &src->datum.ipv6addr.s6_addr[sizeof(v4_v6_map)],
+ sizeof(dst->datum.ipaddr));
+ goto fixed_length;
+
+ case PW_TYPE_IPV4_PREFIX:
+ if (src->datum.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->datum.ipaddr, &src->datum.ipv4prefix[2], sizeof(dst->datum.ipaddr));
+ goto fixed_length;
+
+ case PW_TYPE_IPV6_PREFIX:
+ if (src->datum.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->datum.ipv6prefix[2], v4_v6_map, sizeof(v4_v6_map)) != 0) {
+ goto bad_v6_prefix_map;
+ }
+ memcpy(&dst->datum.ipaddr, &src->datum.ipv6prefix[2 + sizeof(v4_v6_map)],
+ sizeof(dst->datum.ipaddr));
+ goto fixed_length;
+
+ default:
+ break;
}
+ break;
- dst->datum.ushort = i;
- }
- break;
- }
+ case PW_TYPE_IPV6_ADDR:
+ switch (src->type) {
+ case PW_TYPE_IPV4_ADDR:
+ /* Add the v4/v6 mapping prefix */
+ memcpy(dst->datum.ipv6addr.s6_addr, v4_v6_map, sizeof(v4_v6_map));
+ memcpy(&dst->datum.ipv6addr.s6_addr[sizeof(v4_v6_map)], &src->datum.ipaddr,
+ sizeof(dst->datum.ipv6addr.s6_addr) - sizeof(v4_v6_map));
- case PW_TYPE_INTEGER:
- {
- char *p;
+ goto fixed_length;
- /*
- * If we have an enum, and the value isn't an
- * integer or hex string, try to parse it as a
- * named value. Some VALUE names begin with
- * numbers, so we have to be a bit flexible
- * here.
- */
- if (dst_enumv &&
- (!is_integer(in) || (!(in[0] == '0') && (in[1] == 'x')))) {
- if ((dval = fr_dict_enum_by_name(NULL, dst_enumv, in)) == NULL) {
- fr_strerror_printf("Unknown or invalid value \"%s\" for attribute %s",
- in, dst_enumv->name);
- return -1;
+ case PW_TYPE_IPV4_PREFIX:
+ if (src->datum.ipv4prefix[1] != 32) goto bad_v4_prefix_len;
+
+ /* Add the v4/v6 mapping prefix */
+ memcpy(dst->datum.ipv6addr.s6_addr, v4_v6_map, sizeof(v4_v6_map));
+ memcpy(&dst->datum.ipv6addr.s6_addr[sizeof(v4_v6_map)], &src->datum.ipv4prefix[2],
+ sizeof(dst->datum.ipv6addr.s6_addr) - sizeof(v4_v6_map));
+ goto fixed_length;
+
+ case PW_TYPE_IPV6_PREFIX:
+ if (src->datum.ipv4prefix[1] != 128) goto bad_v6_prefix_len;
+
+ memcpy(dst->datum.ipv6addr.s6_addr, &src->datum.ipv6prefix[2], sizeof(dst->datum.ipv6addr.s6_addr));
+ goto fixed_length;
+
+ default:
+ break;
}
+ break;
- dst->datum.integer = dval->value;
+ case PW_TYPE_IPV4_PREFIX:
+ switch (src->type) {
+ case PW_TYPE_IPV4_ADDR:
+ memcpy(&dst->datum.ipv4prefix[2], &src->datum.ipaddr, sizeof(dst->datum.ipv4prefix) - 2);
+ dst->datum.ipv4prefix[0] = 0;
+ dst->datum.ipv4prefix[1] = 32;
+ goto fixed_length;
- } else {
- unsigned long i;
- int base = 10;
+ case PW_TYPE_IPV6_ADDR:
+ if (memcmp(src->datum.ipv6addr.s6_addr, v4_v6_map, sizeof(v4_v6_map)) != 0) {
+ goto bad_v6_prefix_map;
+ }
+ memcpy(&dst->datum.ipv4prefix[2], &src->datum.ipv6addr.s6_addr[sizeof(v4_v6_map)],
+ sizeof(dst->datum.ipv4prefix) - 2);
+ dst->datum.ipv4prefix[0] = 0;
+ dst->datum.ipv4prefix[1] = 32;
+ goto fixed_length;
- /*
- * Empty strings or invalid strings get
- * parsed as zero for backwards
- * compatability.
- */
- if (!*in || !isdigit((int) *in)) {
- dst->datum.integer = 0;
+ case PW_TYPE_IPV6_PREFIX:
+ if (memcmp(&src->datum.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_box_t.
+ */
+ if (!fr_cond_assert(src->datum.ipv6prefix[1] >= (sizeof(v4_v6_map) * 8))) return -1;
+
+ memcpy(&dst->datum.ipv4prefix[2], &src->datum.ipv6prefix[2 + sizeof(v4_v6_map)],
+ sizeof(dst->datum.ipv4prefix) - 2);
+ dst->datum.ipv4prefix[0] = 0;
+ dst->datum.ipv4prefix[1] = src->datum.ipv6prefix[1] - (sizeof(v4_v6_map) * 8);
+ goto fixed_length;
+
+ default:
break;
}
+ break;
- /*
- * Hex strings are base 16.
- */
- if ((in[0] == '0') && in[1] == 'x') base = 16;
+ case PW_TYPE_IPV6_PREFIX:
+ switch (src->type) {
+ case PW_TYPE_IPV4_ADDR:
+ /* Add the v4/v6 mapping prefix */
+ memcpy(&dst->datum.ipv6prefix[2], v4_v6_map, sizeof(v4_v6_map));
+ memcpy(&dst->datum.ipv6prefix[2 + sizeof(v4_v6_map)], &src->datum.ipaddr,
+ (sizeof(dst->datum.ipv6prefix) - 2) - sizeof(v4_v6_map));
+ dst->datum.ipv6prefix[0] = 0;
+ dst->datum.ipv6prefix[1] = 128;
+ goto fixed_length;
- i = strtoul(in, &p, base);
+ case PW_TYPE_IPV4_PREFIX:
+ /* Add the v4/v6 mapping prefix */
+ memcpy(&dst->datum.ipv6prefix[2], v4_v6_map, sizeof(v4_v6_map));
+ memcpy(&dst->datum.ipv6prefix[2 + sizeof(v4_v6_map)], &src->datum.ipv4prefix[2],
+ (sizeof(dst->datum.ipv6prefix) - 2) - sizeof(v4_v6_map));
+ dst->datum.ipv6prefix[0] = 0;
+ dst->datum.ipv6prefix[1] = (sizeof(v4_v6_map) * 8) + src->datum.ipv4prefix[1];
+ goto fixed_length;
- /*
- * Catch and complain on overflows.
- */
- if ((i == ULONG_MAX) || (i >= ((unsigned long) 1) << 32)) {
- fr_strerror_printf("Integer Value \"%s\" is larger than 1<<32", in);
- return -1;
+ case PW_TYPE_IPV6_ADDR:
+ memcpy(&dst->datum.ipv6prefix[2], &src->datum.ipv6addr, sizeof(dst->datum.ipv6prefix) - 2);
+ dst->datum.ipv6prefix[0] = 0;
+ dst->datum.ipv6prefix[1] = 128;
+ goto fixed_length;
+
+ default:
+ break;
}
- /*
- * Value is always within the limits
- */
- dst->datum.integer = (uint32_t) i;
+ break;
+
+ default:
+ break;
}
}
- break;
- case PW_TYPE_INTEGER64:
- {
- uint64_t i;
+ /*
+ * The attribute we've found has to have a size which is
+ * compatible with the type of the destination cast.
+ */
+ if ((src->length < dict_attr_sizes[dst_type][0]) ||
+ (src->length > dict_attr_sizes[dst_type][1])) {
+ char const *type_name;
- /*
- * Note that ALL integers are unsigned!
- */
- if (sscanf(in, "%" PRIu64, &i) != 1) {
- fr_strerror_printf("Failed parsing \"%s\" as unsigned 64bit integer", in);
+ 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",
+ type_name,
+ fr_int2str(dict_attr_types, dst_type, "<INVALID>"),
+ dict_attr_sizes[dst_type][0], dict_attr_sizes[dst_type][1],
+ src->length);
+ return -1;
+ }
+
+ if (src->type == PW_TYPE_OCTETS) {
+ value_box_t tmp;
+
+ do_octets:
+ if (src->length < value_box_field_sizes[dst_type]) {
+ fr_strerror_printf("Invalid cast from %s to %s. Source is length %zd is smaller than "
+ "destination type size %zd",
+ fr_int2str(dict_attr_types, src->type, "<INVALID>"),
+ fr_int2str(dict_attr_types, dst_type, "<INVALID>"),
+ src->length,
+ value_box_field_sizes[dst_type]);
return -1;
}
- dst->datum.integer64 = i;
+
+ /*
+ * Copy the raw octets into the datum of a value_box
+ * inverting bytesex for integers (if LE).
+ */
+ memcpy(&tmp.datum, src->datum.octets, value_box_field_sizes[dst_type]);
+ tmp.type = dst_type;
+ tmp.length = value_box_field_sizes[dst_type];
+ if (fr_dict_enum_types[dst_type]) dst->datum.enumv = dst_enumv;
+
+ value_box_hton(dst, &tmp);
+
+ return 0;
}
+
+ /*
+ * 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->datum.ipaddr.s_addr = htonl(src->datum.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->datum.integer = htonl(src->datum.ipaddr.s_addr);
+
+ } else { /* they're of the same byte order */
+ memcpy(&dst->datum, &src->datum, src->length);
+ }
+
+ dst->length = src->length;
+ dst->type = dst_type;
+ if (fr_dict_enum_types[dst_type]) dst->datum.enumv = dst_enumv;
+
+ return 0;
+}
+
+/** Copy value data verbatim duplicating any buffers
+ *
+ * @note Will free any exiting buffers associated with the dst #value_box_t.
+ *
+ * @param ctx To allocate buffers in.
+ * @param dst Where to copy value_box to.
+ * @param src Where to copy value_box from.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int value_box_copy(TALLOC_CTX *ctx, value_box_t *dst, const value_box_t *src)
+{
+ if (!fr_cond_assert(src->type != PW_TYPE_INVALID)) return -1;
+
+ switch (src->type) {
+ default:
+ memcpy(((uint8_t *)dst) + value_box_offsets[src->type],
+ ((uint8_t const *)src) + value_box_offsets[src->type],
+ value_box_field_sizes[src->type]);
break;
- case PW_TYPE_SIZE:
+ case PW_TYPE_STRING:
{
- size_t i;
+ char *str;
- if (sscanf(in, "%zu", &i) != 1) {
- fr_strerror_printf("Failed parsing \"%s\" as a file or memory size", in);
+ str = talloc_bstrndup(ctx, src->datum.strvalue, src->length);
+ if (!str) {
+ fr_strerror_printf("Failed allocating string buffer");
return -1;
}
- dst->datum.size = i;
+ value_box_clear(dst);
+ dst->datum.strvalue = str;
}
break;
- case PW_TYPE_TIMEVAL:
- if (fr_timeval_from_str(&dst->datum.timeval, in) < 0) return -1;
+ case PW_TYPE_OCTETS:
+ {
+ uint8_t *bin;
+
+ bin = talloc_memdup(ctx, src->datum.octets, src->length);
+ talloc_set_type(bin, uint8_t);
+ if (!bin) return -1;
+ value_box_clear(dst);
+ dst->datum.octets = bin;
+ }
break;
+ }
- case PW_TYPE_DECIMAL:
+ value_box_copy_meta(dst, src);
+
+ return 0;
+}
+
+/** Perform a shallow copy of a value_box
+ *
+ * Like #value_box_copy, but does not duplicate the buffers of the src value_box.
+ *
+ * For #PW_TYPE_STRING and #PW_TYPE_OCTETS adds a reference from ctx so that the
+ * buffer cannot be freed until the ctx is freed.
+ *
+ * @note Will free any exiting buffers associated with the dst #value_box_t.
+ *
+ * @param[in] ctx to add reference from. If NULL no reference will be added.
+ * @param[in] dst to copy value to.
+ * @param[in] src to copy value from.
+ */
+void value_box_copy_shallow(TALLOC_CTX *ctx, value_box_t *dst, const value_box_t *src)
+{
+ value_box_clear(dst);
+
+ switch (src->type) {
+ case PW_TYPE_STRING:
+ case PW_TYPE_OCTETS:
+ dst->datum.ptr = ctx ? talloc_reference(ctx, src->datum.ptr) : src->datum.ptr;
+ break;
+
+ default:
+ memcpy(((uint8_t *)dst) + value_box_offsets[src->type],
+ ((uint8_t const *)src) + value_box_offsets[src->type],
+ value_box_field_sizes[src->type]);
+ break;
+ }
+
+ value_box_copy_meta(dst, src);
+}
+
+/** Copy value data verbatim moving any buffers to the specified context
+ *
+ * @note Will free any exiting buffers associated with the dst #value_box_t.
+ *
+ * @param[in] ctx to allocate any new buffers in.
+ * @param[in] dst to copy value to.
+ * @param[in] src to copy value from.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int value_box_steal(TALLOC_CTX *ctx, value_box_t *dst, value_box_t const *src)
+{
+ if (!fr_cond_assert(src->type != PW_TYPE_INVALID)) return -1;
+
+ switch (src->type) {
+ default:
+ value_box_clear(dst);
+ memcpy(((uint8_t *)dst) + value_box_offsets[src->type],
+ ((uint8_t const *)src) + value_box_offsets[src->type],
+ value_box_field_sizes[src->type]);
+ break;
+
+ case PW_TYPE_STRING:
{
- double d;
+ char const *str;
- if (sscanf(in, "%lf", &d) != 1) {
- fr_strerror_printf("Failed parsing \"%s\" as a decimal", in);
+ str = talloc_steal(ctx, src->datum.strvalue);
+ if (!str) {
+ fr_strerror_printf("Failed stealing string buffer");
return -1;
}
- dst->datum.decimal = d;
+ value_box_clear(dst);
+ dst->datum.strvalue = str;
}
break;
- case PW_TYPE_DATE:
+ case PW_TYPE_OCTETS:
{
- /*
- * time_t may be 64 bits, whule vp_date MUST be 32-bits. We need an
- * intermediary variable to handle the conversions.
- */
- time_t date;
+ uint8_t const *bin;
- if (fr_time_from_str(&date, in) < 0) {
- fr_strerror_printf("failed to parse time string \"%s\"", in);
+ bin = talloc_steal(ctx, src->datum.octets);
+ if (!bin) {
+ fr_strerror_printf("Failed stealing octets buffer");
return -1;
}
+ value_box_clear(dst);
+ dst->datum.octets = bin;
+ }
+ break;
+ }
+ value_box_copy_meta(dst, src);
- dst->datum.date = date;
+ return 0;
+}
+
+/** Copy a nul terminated string to a #value_box_t
+ *
+ * @note Will free any exiting buffers associated with the dst #value_box_t.
+ *
+ * @param[in] ctx to allocate any new buffers in.
+ * @param[in] dst to assign new buffer to.
+ * @param[in] src a nul terminated buffer.
+ */
+int value_box_strdup(TALLOC_CTX *ctx, value_box_t *dst, char const *src)
+{
+ PW_TYPE type = dst->type;
+ char const *str;
+
+ switch (type) {
+ case PW_TYPE_INVALID:
+ type = PW_TYPE_STRING;
+ break;
+
+ case PW_TYPE_STRING:
+ break;
+
+ default:
+ fr_strerror_printf("nul terminated buffers can only be assigned to 'string' boxes, not '%s' boxes",
+ fr_int2str(dict_attr_types, type, "<INVALID>"));
+ return -1;
+ }
+
+ str = talloc_typed_strdup(ctx, src);
+ if (!str) {
+ fr_strerror_printf("Failed allocating string buffer");
+ return -1;
+ }
+
+ if (dst->type != PW_TYPE_INVALID) value_box_clear(dst); /* only clear initialised boxes */
+ dst->type = type;
+ dst->datum.strvalue = str;
+ dst->length = talloc_array_length(str) - 1;
+
+ return 0;
+}
+
+/** Copy a nul terminated talloced buffer to a #value_box_t
+ *
+ * Copy a talloced nul terminated buffer, setting fields in the dst value box appropriately.
+ *
+ * The buffer must be \0 terminated, or an error will be returned.
+ *
+ * Where dst->type == PW_TYPE_OCTETS the \0 byte will be trimmed in the copied buffer.
+ *
+ * Caller should set dst->taint = true, where the value was acquired from an untrusted source.
+ *
+ * @param[in] ctx to allocate any new buffers in.
+ * @param[in] dst to assign new buffer to.
+ * @param[in] src a talloced C string buffer.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int value_box_strdup_buffer(TALLOC_CTX *ctx, value_box_t *dst, char const *src)
+{
+ PW_TYPE type = dst->type;
+ size_t len;
+
+ len = talloc_array_length(src);
+ if ((len == 1) || (src[len - 1] != '\0')) {
+ fr_strerror_printf("Input buffer not \\0 terminated");
+ return -1;
}
+ switch (type) {
+ case PW_TYPE_INVALID:
+ type = PW_TYPE_STRING;
break;
- case PW_TYPE_IFID:
- if (fr_inet_ifid_pton((void *) dst->datum.ifid, in) == NULL) {
- fr_strerror_printf("Failed to parse interface-id string \"%s\"", in);
+ case PW_TYPE_STRING:
+ {
+ char *str;
+
+ str = talloc_memdup(ctx, src, len);
+ if (!str) {
+ fr_strerror_printf("Failed allocating string buffer");
return -1;
}
+ talloc_set_type(str, char);
+
+ value_box_clear(dst);
+ dst->datum.strvalue = str;
+
+ len--;
+ }
break;
- case PW_TYPE_ETHERNET:
+ case PW_TYPE_OCTETS:
{
- char const *c1, *c2, *cp;
- size_t p_len = 0;
+ uint8_t *bin;
- /*
- * Convert things which are obviously integers to Ethernet addresses
- *
- * We assume the number is the bigendian representation of the
- * ethernet address.
- */
- if (is_integer(in)) {
- uint64_t integer = htonll(atoll(in));
+ len--;
- memcpy(dst->datum.ether, &integer, sizeof(dst->datum.ether));
- break;
+ bin = talloc_memdup(ctx, src, len); /* Don't copy \0 byte */
+ if (!bin) {
+ fr_strerror_printf("Failed allocating octets buffer");
+ return -1;
}
+ talloc_set_type(bin, uint8_t);
- cp = in;
- while (*cp) {
- if (cp[1] == ':') {
- c1 = hextab;
- c2 = memchr(hextab, tolower((int) cp[0]), 16);
- cp += 2;
- } else if ((cp[1] != '\0') && ((cp[2] == ':') || (cp[2] == '\0'))) {
- c1 = memchr(hextab, tolower((int) cp[0]), 16);
- c2 = memchr(hextab, tolower((int) cp[1]), 16);
- cp += 2;
- if (*cp == ':') cp++;
- } else {
- c1 = c2 = NULL;
- }
- if (!c1 || !c2 || (p_len >= sizeof(dst->datum.ether))) {
- fr_strerror_printf("failed to parse Ethernet address \"%s\"", in);
- return -1;
- }
- dst->datum.ether[p_len] = ((c1-hextab)<<4) + (c2-hextab);
- p_len++;
- }
+ value_box_clear(dst);
+ dst->datum.octets = bin;
}
break;
- /*
- * Crazy polymorphic (IPv4/IPv6) attribute src->dst_type for WiMAX.
- *
- * We try and make is saner by replacing the original
- * da, with either an IPv4 or IPv6 da src->dst_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, in, &dst->datum.ipv6addr) > 0) {
- *dst_type = PW_TYPE_IPV6_ADDR;
- ret = dict_attr_sizes[PW_TYPE_COMBO_IP_ADDR][1]; /* size of IPv6 address */
- } else {
- fr_ipaddr_t ipaddr;
+ default:
+ fr_strerror_printf("Buffers can only be assigned to 'string' or 'octets' boxes, not '%s' boxes",
+ fr_int2str(dict_attr_types, dst->type, "<INVALID>"));
+ return -1;
+ }
- if (fr_inet_hton(&ipaddr, AF_INET, in, false) < 0) {
- fr_strerror_printf("Failed to find IPv4 address for %s", in);
- return -1;
- }
+ dst->type = type;
+ dst->length = len;
+
+ return 0;
+}
+
+/** Steal a nul terminated talloced buffer into a specified ctx, and assign to a #value_box_t
+ *
+ * Steal a talloced nul terminated buffer, setting fields in the dst value box appropriately.
+ *
+ * The buffer must be \0 terminated, or an error will be returned.
+ *
+ * Caller should set dst->taint = true, where the value was acquired from an untrusted source.
+ *
+ * @param[in] ctx to allocate any new buffers in.
+ * @param[in] dst to assign new buffer to.
+ * @param[in] src a talloced nul terminated buffer.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int value_box_strsteal(TALLOC_CTX *ctx, value_box_t *dst, char *src)
+{
+ PW_TYPE type = dst->type;
+ size_t len;
+ char *str;
- *dst_type = PW_TYPE_IPV4_ADDR;
- dst->datum.ipaddr.s_addr = ipaddr.ipaddr.ip4addr.s_addr;
- ret = dict_attr_sizes[PW_TYPE_COMBO_IP_ADDR][0]; /* size of IPv4 address */
- }
+ len = talloc_array_length(src);
+ if ((len == 1) || (src[len - 1] != '\0')) {
+ fr_strerror_printf("Input buffer not \\0 terminated");
+ return -1;
}
- break;
- case PW_TYPE_SIGNED:
- /* Damned code for 1 WiMAX attribute */
- dst->datum.sinteger = (int32_t)strtol(in, NULL, 10);
+ switch (type) {
+ case PW_TYPE_INVALID:
+ type = PW_TYPE_STRING;
break;
- case PW_TYPE_BOOLEAN:
- case PW_TYPE_COMBO_IP_PREFIX:
+ case PW_TYPE_STRING:
break;
- case PW_TYPE_UNBOUNDED: /* Should have been dealt with above */
- case PW_TYPE_STRUCTURAL: /* Listed again to suppress compiler warnings */
- case PW_TYPE_BAD:
- fr_strerror_printf("Unknown attribute dst_type %d", *dst_type);
- return -1;
+ default:
+ fr_strerror_printf("nul terminated buffers can only be assigned to 'string' boxes, not '%s' boxes",
+ fr_int2str(dict_attr_types, type, "<INVALID>"));
+ return -1;
}
-finish:
- dst->length = ret;
- dst->type = *dst_type;
+ str = talloc_steal(ctx, src);
+ if (!str) {
+ fr_strerror_printf("Failed stealing string buffer");
+ return -1;
+ }
- /*
- * Fixup enumv
- */
- if (fr_dict_enum_types[dst->type]) dst->datum.enumv = dst_enumv;
+ if (dst->type != PW_TYPE_INVALID) value_box_clear(dst); /* only clear initialised boxes */
+ dst->type = type;
+ dst->length = len - 1;
return 0;
}
-/** Performs byte order reversal for types that need it
+/** Assign a buffer containing a nul terminated string to a box, but don't copy it
*
- * @param[in] dst Where to write the result. May be the same as src.
- * @param[in] src #value_box_t containing an integer value.
+ * @note Will free any exiting buffers associated with the dst #value_box_t.
+ *
+ * @param[in] dst to assign string to.
+ * @param[in] src to copy string to
* @return
* - 0 on success.
* - -1 on failure.
*/
-int value_box_hton(value_box_t *dst, value_box_t const *src)
+int value_box_strdup_shallow(value_box_t *dst, char const *src)
{
- if (!fr_cond_assert(src->type != PW_TYPE_INVALID)) return -1;
+ size_t len;
+ PW_TYPE type = dst->type;
- /* 8 byte integers */
- switch (src->type) {
- case PW_TYPE_INTEGER64:
- dst->datum.integer64 = htonll(src->datum.integer64);
+ len = talloc_array_length(src);
+ if ((len == 1) || (src[len - 1] != '\0')) {
+ fr_strerror_printf("Input buffer not \\0 terminated");
+ return -1;
+ }
+
+ switch (type) {
+ case PW_TYPE_INVALID:
+ type = PW_TYPE_STRING;
break;
- /* 4 byte integers */
- case PW_TYPE_INTEGER:
- case PW_TYPE_DATE:
- case PW_TYPE_SIGNED:
- dst->datum.integer = htonl(src->datum.integer);
+ case PW_TYPE_STRING:
break;
- /* 2 byte integers */
- case PW_TYPE_SHORT:
- dst->datum.ushort = htons(src->datum.ushort);
+ default:
+ fr_strerror_printf("nul terminated buffers can only be assigned to 'string' boxes, not '%s' boxes",
+ fr_int2str(dict_attr_types, type, "<INVALID>"));
+ return -1;
+ }
+ if (dst->datum.ptr) value_box_clear(dst);
+
+ dst->type = type;
+ dst->datum.strvalue = src;
+ dst->length = strlen(src);
+
+ return 0;
+}
+
+/** Assign a talloced buffer containing a nul terminated string to a box, but don't copy it
+ *
+ * Adds a reference to the src buffer so that it cannot be freed until the ctx is freed.
+ *
+ * @note Will free any exiting buffers associated with the dst #value_box_t.
+ *
+ * @param[in] ctx to add reference from. If NULL no reference will be added.
+ * @param[in] dst to assign string to.
+ * @param[in] src to copy string to.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int value_box_strdup_buffer_shallow(TALLOC_CTX *ctx, value_box_t *dst, char const *src)
+{
+ PW_TYPE type = dst->type;
+
+ switch (type) {
+ case PW_TYPE_INVALID:
+ type = PW_TYPE_STRING;
break;
- case PW_TYPE_OCTETS:
case PW_TYPE_STRING:
- if (!fr_cond_assert(0)) return -1; /* shouldn't happen */
+ break;
default:
- value_box_copy(NULL, dst, src);
- break;
+ fr_strerror_printf("nul terminated buffers can only be assigned to 'string' boxes, not '%s' boxes",
+ fr_int2str(dict_attr_types, type, "<INVALID>"));
+ return -1;
}
+ if (dst->datum.ptr) value_box_clear(dst);
- value_box_copy_attrs(dst, src);
+ dst->type = type;
+ dst->datum.strvalue = ctx ? talloc_reference(ctx, src) : src;
+ dst->length = talloc_array_length(src) - 1;
return 0;
}
-/** Convert one type of value_box_t to another
+/** Copy a buffer to a value_box_t
*
- * @note This should be the canonical function used to convert between data types.
+ * Copy a buffer containing binary data, setting fields in the dst value box appropriately.
*
- * @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 Input data.
+ * Caller should set dst->taint = true, where the value was acquired from an untrusted source.
+ *
+ * @note Will free any exiting buffers associated with the value box.
+ *
+ * @param[in] ctx to allocate any new buffers in.
+ * @param[in] dst to assign new buffer to.
+ * @param[in] src a buffer.
+ * @param[in] len of data in the buffer.
* @return
* - 0 on success.
* - -1 on failure.
*/
-int value_box_cast(TALLOC_CTX *ctx, value_box_t *dst,
- PW_TYPE dst_type, fr_dict_attr_t const *dst_enumv,
- value_box_t const *src)
+int value_box_memdup(TALLOC_CTX *ctx, value_box_t *dst, uint8_t const *src, size_t len)
{
- if (!fr_cond_assert(dst_type != PW_TYPE_INVALID)) return -1;
- if (!fr_cond_assert(src->type != PW_TYPE_INVALID)) return -1;
+ PW_TYPE type = dst->type;
- if (fr_dict_non_data_types[dst_type]) {
- fr_strerror_printf("Invalid cast from %s to %s. Can only cast simple data types.",
- fr_int2str(dict_attr_types, src->type, "<INVALID>"),
- fr_int2str(dict_attr_types, dst_type, "<INVALID>"));
- return -1;
+ switch (type) {
+ case PW_TYPE_INVALID:
+ type = PW_TYPE_OCTETS;
+ /* FALL-THROUGH */
+
+ case PW_TYPE_OCTETS:
+ {
+ uint8_t *bin;
+
+ bin = talloc_memdup(ctx, src, len);
+ if (!bin) {
+ fr_strerror_printf("Failed allocating octets buffer");
+ return -1;
+ }
+ if (dst->type != PW_TYPE_INVALID) value_box_clear(dst); /* only clear initialised boxes */
+ dst->datum.octets = bin;
}
+ break;
- /*
- * If it's the same type, copy.
- */
- if (dst_type == src->type) return value_box_copy(ctx, dst, src);
+ case PW_TYPE_STRING:
+ {
+ char *str;
- /*
- * Deserialise a value_box_t
- */
- if (src->type == PW_TYPE_STRING) {
- return value_box_from_str(ctx, dst, &dst_type, dst_enumv, src->datum.strvalue, src->length, '\0');
+ str = talloc_array(ctx, char, len + 1);
+ if (!str) {
+ fr_strerror_printf("Failed allocating string buffer");
+ return -1;
+ }
+ str[len] = '\0';
+ value_box_clear(dst);
+ dst->datum.strvalue = str;
}
+ break;
- /*
- * Converts the src data to octets with no processing.
- */
- if (dst_type == PW_TYPE_OCTETS) {
- value_box_hton(dst, src);
- dst->datum.octets = talloc_memdup(ctx, &dst->datum, src->length);
- dst->length = src->length;
- dst->type = dst_type;
- talloc_set_type(dst->datum.octets, uint8_t);
- return 0;
+ default:
+ fr_strerror_printf("Buffers can only be assigned to 'string' or 'octets' boxes, not '%s' boxes",
+ fr_int2str(dict_attr_types, dst->type, "<INVALID>"));
+ return -1;
}
- /*
- * Serialise a value_box_t
- */
- if (dst_type == PW_TYPE_STRING) {
- dst->datum.strvalue = value_box_asprint(ctx, src, '\0');
- dst->length = talloc_array_length(dst->datum.strvalue) - 1;
- dst->type = dst_type;
- return 0;
+ dst->type = type;
+ dst->length = len;
+
+ return 0;
+}
+
+/** Copy a talloced buffer to a value_box_t
+ *
+ * Copy a buffer containing binary data, setting fields in the dst value box appropriately.
+ *
+ * Caller should set dst->taint = true, where the value was acquired from an untrusted source.
+ *
+ * @note Will free any exiting buffers associated with the value box.
+ *
+ * @param[in] ctx to allocate any new buffers in.
+ * @param[in] dst to assign new buffer to.
+ * @param[in] src a buffer.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int value_box_memdup_buffer(TALLOC_CTX *ctx, value_box_t *dst, uint8_t *src)
+{
+ return value_box_memdup(ctx, dst, src, talloc_array_length(src));
+}
+
+/** Steal a talloced buffer into a specified ctx, and assign to a #value_box_t
+ *
+ * Steal a talloced buffer, setting fields in the dst value box appropriately.
+ *
+ * Caller should set dst->taint = true, where the value was acquired from an untrusted source.
+ *
+ * @note Will free any exiting buffers associated with the value box.
+ *
+ * @param[in] ctx to allocate any new buffers in.
+ * @param[in] dst to assign new buffer to.
+ * @param[in] src a talloced nul terminated buffer.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int value_box_memsteal(TALLOC_CTX *ctx, value_box_t *dst, uint8_t const *src)
+{
+ PW_TYPE type = dst->type;
+ uint8_t const *bin;
+
+ switch (type) {
+ case PW_TYPE_INVALID:
+ type = PW_TYPE_OCTETS;
+ break;
+
+ case PW_TYPE_OCTETS:
+ break;
+
+ default:
+ fr_strerror_printf("Buffers can only be assigned to 'octets' boxes, not '%s' boxes",
+ fr_int2str(dict_attr_types, type, "<INVALID>"));
+ return -1;
}
- if ((src->type == PW_TYPE_IFID) &&
- (dst_type == PW_TYPE_INTEGER64)) {
- memcpy(&dst->datum.integer64, src->datum.ifid, sizeof(src->datum.ifid));
- dst->datum.integer64 = htonll(dst->datum.integer64);
+ bin = talloc_steal(ctx, src);
+ if (!bin) {
+ fr_strerror_printf("Failed stealing buffer");
+ return -1;
+ }
- fixed_length:
- dst->length = dict_attr_sizes[dst_type][0];
- dst->type = dst_type;
- if (fr_dict_enum_types[dst_type]) dst->datum.enumv = dst_enumv;
+ if (dst->type != PW_TYPE_INVALID) value_box_clear(dst); /* only clear initialised boxes */
+ dst->type = type;
+ dst->length = talloc_array_length(src);
- return 0;
+ return 0;
+}
+
+/** Assign a buffer to a box, but don't copy it
+ *
+ * Adds a reference to the src buffer so that it cannot be freed until the ctx is freed.
+ *
+ * Caller should set dst->taint = true, where the value was acquired from an untrusted source.
+ *
+ * @note Will free any exiting buffers associated with the value box.
+ *
+ * @param[in] dst to assign buffer to.
+ * @param[in] src a talloced buffer.
+ * @param[in] len of buffer.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int value_box_memdup_shallow(value_box_t *dst, uint8_t *src, size_t len)
+{
+ if (dst->datum.ptr) value_box_clear(dst);
+
+ if (dst->type != PW_TYPE_OCTETS) {
+ fr_strerror_printf("Buffers can only be assigned to 'octets' boxes, not '%s' boxes",
+ fr_int2str(dict_attr_types, dst->type, "<INVALID>"));
+ return -1;
}
- if ((src->type == PW_TYPE_INTEGER64) &&
- (dst_type == PW_TYPE_ETHERNET)) {
- uint8_t array[8];
- uint64_t i;
+ dst->datum.octets = src;
+ dst->length = len;
- i = htonll(src->datum.integer64);
- memcpy(array, &i, 8);
+ return 0;
+}
- /*
- * For OUIs in the DB.
- */
- if ((array[0] != 0) || (array[1] != 0)) return -1;
+/** Assign a talloced buffer to a box, but don't copy it
+ *
+ * Adds a reference to the src buffer so that it cannot be freed until the ctx is freed.
+ *
+ * Caller should set dst->taint = true, where the value was acquired from an untrusted source.
+ *
+ * @note Will free any exiting buffers associated with the value box.
+ *
+ * @param[in] ctx to allocate any new buffers in.
+ * @param[in] dst to assign buffer to.
+ * @param[in] src a talloced buffer.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int value_box_memdup_buffer_shallow(TALLOC_CTX *ctx, value_box_t *dst, uint8_t *src)
+{
+ if (dst->datum.ptr) value_box_clear(dst);
- memcpy(dst->datum.ether, &array[2], 6);
- goto fixed_length;
+ if (dst->type != PW_TYPE_OCTETS) {
+ fr_strerror_printf("Buffers can only be assigned to 'octets' boxes, not '%s' boxes",
+ fr_int2str(dict_attr_types, dst->type, "<INVALID>"));
+ return -1;
}
- if (dst_type == PW_TYPE_SHORT) {
- switch (src->type) {
- case PW_TYPE_BYTE:
- dst->datum.ushort = src->datum.byte;
- break;
+ dst->datum.octets = ctx ? talloc_reference(ctx, src) : src;
+ dst->length = talloc_array_length(src);
+
+ return 0;
+}
+
+/** Convert string value to a value_box_t type
+ *
+ * @todo Should take taint param.
+ *
+ * @param[in] ctx to alloc strings in.
+ * @param[out] dst where to write parsed value.
+ * @param[in,out] dst_type of value data to create/dst_type of value created.
+ * @param[in] dst_enumv fr_dict_attr_t with string aliases for integer values.
+ * @param[in] in 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 should be the length of the string or
+ * sub string to parse.
+ * @param[in] quote character used set unescape mode. @see value_str_unescape.
+ * @return
+ * - 0 on success.
+ * - -1 on parse error.
+ */
+int value_box_from_str(TALLOC_CTX *ctx, value_box_t *dst,
+ PW_TYPE *dst_type, fr_dict_attr_t const *dst_enumv,
+ char const *in, ssize_t inlen, char quote)
+{
+ fr_dict_enum_t *dval;
+ size_t len;
+ ssize_t ret;
+ char buffer[256];
- case PW_TYPE_OCTETS:
- goto do_octets;
+ if (!fr_cond_assert(*dst_type != PW_TYPE_INVALID)) return -1;
- default:
- goto invalid_cast;
- }
- goto fixed_length;
- }
+ if (!in) return -1;
+
+ len = (inlen < 0) ? strlen(in) : (size_t)inlen;
/*
- * We can cast LONG integers to SHORTER ones, so long
- * as the long one is on the LHS.
+ * Set size for all fixed length attributes.
*/
- if (dst_type == PW_TYPE_INTEGER) {
- switch (src->type) {
- case PW_TYPE_BYTE:
- dst->datum.integer = src->datum.byte;
- break;
-
- case PW_TYPE_SHORT:
- dst->datum.integer = src->datum.ushort;
- break;
+ ret = dict_attr_sizes[*dst_type][1]; /* Max length */
- case PW_TYPE_SIGNED:
- if (src->datum.sinteger < 0 ) {
- fr_strerror_printf("Invalid cast: From signed to integer. signed value %d is negative ",
- src->datum.sinteger);
- return -1;
- }
- dst->datum.integer = (uint32_t)src->datum.sinteger;
- break;
+ /*
+ * It's a variable ret src->dst_type so we just alloc a new buffer
+ * of size len and copy.
+ */
+ switch (*dst_type) {
+ case PW_TYPE_STRING:
+ {
+ char *buff, *p;
- case PW_TYPE_OCTETS:
- goto do_octets;
+ buff = talloc_bstrndup(ctx, in, len);
- default:
- goto invalid_cast;
+ /*
+ * No de-quoting. Just copy the string.
+ */
+ if (!quote) {
+ ret = len;
+ dst->datum.strvalue = buff;
+ goto finish;
}
- 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->datum.integer64 = src->datum.byte;
- break;
+ len = value_str_unescape((uint8_t *)buff, in, len, quote);
- case PW_TYPE_SHORT:
- dst->datum.integer64 = src->datum.ushort;
- break;
+ /*
+ * Shrink the buffer to the correct size
+ * and \0 terminate it. There is a significant
+ * amount of legacy code that assumes the string
+ * buffer in value pairs is a C string.
+ *
+ * It's better for the server to print partial
+ * strings, instead of SEGV.
+ */
+ dst->datum.strvalue = p = talloc_realloc(ctx, buff, char, len + 1);
+ p[len] = '\0';
+ ret = len;
+ }
+ goto finish;
- case PW_TYPE_INTEGER:
- dst->datum.integer64 = src->datum.integer;
- break;
+ case PW_TYPE_VSA:
+ fr_strerror_printf("Must use 'Attr-26 = ...' instead of 'Vendor-Specific = ...'");
+ return -1;
- case PW_TYPE_DATE:
- dst->datum.integer64 = src->datum.date;
- break;
+ /* raw octets: 0x01020304... */
+ case PW_TYPE_OCTETS:
+ {
+ uint8_t *p;
- case PW_TYPE_OCTETS:
- goto do_octets;
+ /*
+ * No 0x prefix, just copy verbatim.
+ */
+ if ((len < 2) || (strncasecmp(in, "0x", 2) != 0)) {
+ dst->datum.octets = talloc_memdup(ctx, (uint8_t const *)in, len);
+ talloc_set_type(dst->datum.octets, uint8_t);
+ ret = len;
+ goto finish;
+ }
- 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>"));
+ len -= 2;
+
+ /*
+ * Invalid.
+ */
+ if ((len & 0x01) != 0) {
+ 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, in + 2, len) != (size_t)ret) {
+ talloc_free(p);
+ fr_strerror_printf("Invalid hex data");
+ return -1;
}
- 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->datum.sinteger = src->datum.byte;
- break;
+ dst->datum.octets = p;
+ }
+ goto finish;
- case PW_TYPE_SHORT:
- dst->datum.sinteger = src->datum.ushort;
- break;
+ case PW_TYPE_ABINARY:
+#ifdef WITH_ASCEND_BINARY
+ if ((len > 1) && (strncasecmp(in, "0x", 2) == 0)) {
+ ssize_t bin;
- case PW_TYPE_INTEGER:
- if (src->datum.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->datum.integer);
+ if (len > ((sizeof(dst->datum.filter) + 1) * 2)) {
+ fr_strerror_printf("Hex data is too large for ascend filter");
return -1;
}
- dst->datum.sinteger = (int)src->datum.integer;
- break;
- case PW_TYPE_INTEGER64:
- if (src->datum.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->datum.integer64);
+ bin = fr_hex2bin((uint8_t *) &dst->datum.filter, ret, in + 2, len - 2);
+ if (bin < ret) {
+ memset(((uint8_t *) &dst->datum.filter) + bin, 0, ret - bin);
+ }
+ } else {
+ if (ascend_parse_filter(dst, in, len) < 0 ) {
+ /* Allow ascend_parse_filter's strerror to bubble up */
return -1;
}
- dst->datum.sinteger = (int)src->datum.integer64;
- break;
+ }
- case PW_TYPE_OCTETS:
- goto do_octets;
+ ret = sizeof(dst->datum.filter);
+ goto finish;
+#else
+ /*
+ * If Ascend binary is NOT defined,
+ * then fall through to raw octets, so that
+ * the user can at least make them by hand...
+ */
+ goto do_octets;
+#endif
- default:
- goto invalid_cast;
+ case PW_TYPE_IPV4_ADDR:
+ {
+ fr_ipaddr_t addr;
+
+ if (fr_inet_pton4(&addr, in, inlen, fr_hostname_lookups, false, true) < 0) return -1;
+
+ /*
+ * We allow v4 addresses to have a /32 suffix as some databases (PostgreSQL)
+ * print them this way.
+ */
+ if (addr.prefix != 32) {
+ fr_strerror_printf("Invalid IPv4 mask length \"/%i\". Only \"/32\" permitted "
+ "for non-prefix types", addr.prefix);
+ return -1;
}
- goto fixed_length;
+
+ dst->datum.ipaddr.s_addr = addr.ipaddr.ip4addr.s_addr;
}
+ goto finish;
- if (dst_type == PW_TYPE_TIMEVAL) {
- switch (src->type) {
- case PW_TYPE_BYTE:
- dst->datum.timeval.tv_sec = src->datum.byte;
- dst->datum.timeval.tv_usec = 0;
- break;
+ case PW_TYPE_IPV4_PREFIX:
+ {
+ fr_ipaddr_t addr;
- case PW_TYPE_SHORT:
- dst->datum.timeval.tv_sec = src->datum.ushort;
- dst->datum.timeval.tv_usec = 0;
- break;
+ if (fr_inet_pton4(&addr, in, inlen, fr_hostname_lookups, false, true) < 0) return -1;
- case PW_TYPE_INTEGER:
- dst->datum.timeval.tv_sec = src->datum.integer;
- dst->datum.timeval.tv_usec = 0;
- break;
+ dst->datum.ipv4prefix[1] = addr.prefix;
+ memcpy(&dst->datum.ipv4prefix[2], &addr.ipaddr.ip4addr.s_addr, sizeof(dst->datum.ipv4prefix) - 2);
+ }
+ goto finish;
- case PW_TYPE_INTEGER64:
- /*
- * tv_sec is a time_t, which is variable in size
- * depending on the system.
- *
- * It should be >= 64bits on modern systems,
- * but you never know...
- */
- if (sizeof(uint64_t) > SIZEOF_MEMBER(struct timeval, tv_sec)) goto invalid_cast;
- dst->datum.timeval.tv_sec = src->datum.integer64;
- dst->datum.timeval.tv_usec = 0;
- break;
+ case PW_TYPE_IPV6_ADDR:
+ {
+ fr_ipaddr_t addr;
- default:
- goto invalid_cast;
+ if (fr_inet_pton6(&addr, in, inlen, fr_hostname_lookups, false, true) < 0) return -1;
+
+ /*
+ * We allow v6 addresses to have a /128 suffix as some databases (PostgreSQL)
+ * print them this way.
+ */
+ if (addr.prefix != 128) {
+ fr_strerror_printf("Invalid IPv6 mask length \"/%i\". Only \"/128\" permitted "
+ "for non-prefix types", addr.prefix);
+ return -1;
}
+
+ memcpy(&dst->datum.ipv6addr, addr.ipaddr.ip6addr.s6_addr, sizeof(dst->datum.ipv6addr));
+ }
+ goto finish;
+
+ case PW_TYPE_IPV6_PREFIX:
+ {
+ fr_ipaddr_t addr;
+
+ if (fr_inet_pton6(&addr, in, inlen, fr_hostname_lookups, false, true) < 0) return -1;
+
+ dst->datum.ipv6prefix[1] = addr.prefix;
+ memcpy(&dst->datum.ipv6prefix[2], addr.ipaddr.ip6addr.s6_addr, sizeof(dst->datum.ipv6prefix) - 2);
}
+ goto finish;
/*
- * 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.
+ * Dealt with below
*/
- {
- /*
- * 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 };
+ case PW_TYPE_BOUNDED:
+ break;
- switch (dst_type) {
- case PW_TYPE_IPV4_ADDR:
- switch (src->type) {
- case PW_TYPE_IPV6_ADDR:
- if (memcmp(src->datum.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;
- }
+ case PW_TYPE_STRUCTURAL_EXCEPT_VSA:
+ case PW_TYPE_VENDOR:
+ case PW_TYPE_BAD:
+ fr_strerror_printf("Invalid dst_type %d", *dst_type);
+ return -1;
+ }
- memcpy(&dst->datum.ipaddr, &src->datum.ipv6addr.s6_addr[sizeof(v4_v6_map)],
- sizeof(dst->datum.ipaddr));
- goto fixed_length;
+ /*
+ * It's a fixed size src->dst_type, copy to a temporary buffer and
+ * \0 terminate if insize >= 0.
+ */
+ if (inlen > 0) {
+ if (len >= sizeof(buffer)) {
+ fr_strerror_printf("Temporary buffer too small");
+ return -1;
+ }
- case PW_TYPE_IPV4_PREFIX:
- if (src->datum.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(buffer, in, inlen);
+ buffer[inlen] = '\0';
+ in = buffer;
+ }
- memcpy(&dst->datum.ipaddr, &src->datum.ipv4prefix[2], sizeof(dst->datum.ipaddr));
- goto fixed_length;
+ switch (*dst_type) {
+ case PW_TYPE_BYTE:
+ {
+ char *p;
+ unsigned int i;
- case PW_TYPE_IPV6_PREFIX:
- if (src->datum.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->datum.ipv6prefix[2], v4_v6_map, sizeof(v4_v6_map)) != 0) {
- goto bad_v6_prefix_map;
- }
- memcpy(&dst->datum.ipaddr, &src->datum.ipv6prefix[2 + sizeof(v4_v6_map)],
- sizeof(dst->datum.ipaddr));
- goto fixed_length;
+ /*
+ * Note that ALL integers are unsigned!
+ */
+ i = fr_strtoul(in, &p);
- default:
- break;
+ /*
+ * Look for the named in for the given
+ * attribute.
+ */
+ if (dst_enumv && *p && !is_whitespace(p)) {
+ if ((dval = fr_dict_enum_by_name(NULL, dst_enumv, in)) == NULL) {
+ fr_strerror_printf("Unknown or invalid value \"%s\" for attribute %s",
+ in, dst_enumv->name);
+ return -1;
}
- break;
-
- case PW_TYPE_IPV6_ADDR:
- switch (src->type) {
- case PW_TYPE_IPV4_ADDR:
- /* Add the v4/v6 mapping prefix */
- memcpy(dst->datum.ipv6addr.s6_addr, v4_v6_map, sizeof(v4_v6_map));
- memcpy(&dst->datum.ipv6addr.s6_addr[sizeof(v4_v6_map)], &src->datum.ipaddr,
- sizeof(dst->datum.ipv6addr.s6_addr) - sizeof(v4_v6_map));
- goto fixed_length;
+ dst->datum.byte = dval->value;
+ } else {
+ if (i > 255) {
+ fr_strerror_printf("Byte value \"%s\" is larger than 255", in);
+ return -1;
+ }
- case PW_TYPE_IPV4_PREFIX:
- if (src->datum.ipv4prefix[1] != 32) goto bad_v4_prefix_len;
+ dst->datum.byte = i;
+ }
+ break;
+ }
- /* Add the v4/v6 mapping prefix */
- memcpy(dst->datum.ipv6addr.s6_addr, v4_v6_map, sizeof(v4_v6_map));
- memcpy(&dst->datum.ipv6addr.s6_addr[sizeof(v4_v6_map)], &src->datum.ipv4prefix[2],
- sizeof(dst->datum.ipv6addr.s6_addr) - sizeof(v4_v6_map));
- goto fixed_length;
+ case PW_TYPE_SHORT:
+ {
+ char *p;
+ unsigned int i;
- case PW_TYPE_IPV6_PREFIX:
- if (src->datum.ipv4prefix[1] != 128) goto bad_v6_prefix_len;
+ /*
+ * Note that ALL integers are unsigned!
+ */
+ i = fr_strtoul(in, &p);
- memcpy(dst->datum.ipv6addr.s6_addr, &src->datum.ipv6prefix[2], sizeof(dst->datum.ipv6addr.s6_addr));
- goto fixed_length;
+ /*
+ * Look for the named in for the given
+ * attribute.
+ */
+ if (dst_enumv && *p && !is_whitespace(p)) {
+ if ((dval = fr_dict_enum_by_name(NULL, dst_enumv, in)) == NULL) {
+ fr_strerror_printf("Unknown or invalid value \"%s\" for attribute %s",
+ in, dst_enumv->name);
+ return -1;
+ }
- default:
- break;
+ dst->datum.ushort = dval->value;
+ } else {
+ if (i > 65535) {
+ fr_strerror_printf("Short value \"%s\" is larger than 65535", in);
+ return -1;
}
- break;
- case PW_TYPE_IPV4_PREFIX:
- switch (src->type) {
- case PW_TYPE_IPV4_ADDR:
- memcpy(&dst->datum.ipv4prefix[2], &src->datum.ipaddr, sizeof(dst->datum.ipv4prefix) - 2);
- dst->datum.ipv4prefix[0] = 0;
- dst->datum.ipv4prefix[1] = 32;
- goto fixed_length;
+ dst->datum.ushort = i;
+ }
+ break;
+ }
- case PW_TYPE_IPV6_ADDR:
- if (memcmp(src->datum.ipv6addr.s6_addr, v4_v6_map, sizeof(v4_v6_map)) != 0) {
- goto bad_v6_prefix_map;
- }
- memcpy(&dst->datum.ipv4prefix[2], &src->datum.ipv6addr.s6_addr[sizeof(v4_v6_map)],
- sizeof(dst->datum.ipv4prefix) - 2);
- dst->datum.ipv4prefix[0] = 0;
- dst->datum.ipv4prefix[1] = 32;
- goto fixed_length;
+ case PW_TYPE_INTEGER:
+ {
+ char *p;
- case PW_TYPE_IPV6_PREFIX:
- if (memcmp(&src->datum.ipv6prefix[2], v4_v6_map, sizeof(v4_v6_map)) != 0) {
- goto bad_v6_prefix_map;
- }
+ /*
+ * If we have an enum, and the value isn't an
+ * integer or hex string, try to parse it as a
+ * named value. Some VALUE names begin with
+ * numbers, so we have to be a bit flexible
+ * here.
+ */
+ if (dst_enumv &&
+ (!is_integer(in) || (!(in[0] == '0') && (in[1] == 'x')))) {
+ if ((dval = fr_dict_enum_by_name(NULL, dst_enumv, in)) == NULL) {
+ fr_strerror_printf("Unknown or invalid value \"%s\" for attribute %s",
+ in, dst_enumv->name);
+ return -1;
+ }
- /*
- * 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_box_t.
- */
- if (!fr_cond_assert(src->datum.ipv6prefix[1] >= (sizeof(v4_v6_map) * 8))) return -1;
+ dst->datum.integer = dval->value;
- memcpy(&dst->datum.ipv4prefix[2], &src->datum.ipv6prefix[2 + sizeof(v4_v6_map)],
- sizeof(dst->datum.ipv4prefix) - 2);
- dst->datum.ipv4prefix[0] = 0;
- dst->datum.ipv4prefix[1] = src->datum.ipv6prefix[1] - (sizeof(v4_v6_map) * 8);
- goto fixed_length;
+ } else {
+ unsigned long i;
+ int base = 10;
- default:
+ /*
+ * Empty strings or invalid strings get
+ * parsed as zero for backwards
+ * compatability.
+ */
+ if (!*in || !isdigit((int) *in)) {
+ dst->datum.integer = 0;
break;
}
- break;
- case PW_TYPE_IPV6_PREFIX:
- switch (src->type) {
- case PW_TYPE_IPV4_ADDR:
- /* Add the v4/v6 mapping prefix */
- memcpy(&dst->datum.ipv6prefix[2], v4_v6_map, sizeof(v4_v6_map));
- memcpy(&dst->datum.ipv6prefix[2 + sizeof(v4_v6_map)], &src->datum.ipaddr,
- (sizeof(dst->datum.ipv6prefix) - 2) - sizeof(v4_v6_map));
- dst->datum.ipv6prefix[0] = 0;
- dst->datum.ipv6prefix[1] = 128;
- goto fixed_length;
-
- case PW_TYPE_IPV4_PREFIX:
- /* Add the v4/v6 mapping prefix */
- memcpy(&dst->datum.ipv6prefix[2], v4_v6_map, sizeof(v4_v6_map));
- memcpy(&dst->datum.ipv6prefix[2 + sizeof(v4_v6_map)], &src->datum.ipv4prefix[2],
- (sizeof(dst->datum.ipv6prefix) - 2) - sizeof(v4_v6_map));
- dst->datum.ipv6prefix[0] = 0;
- dst->datum.ipv6prefix[1] = (sizeof(v4_v6_map) * 8) + src->datum.ipv4prefix[1];
- goto fixed_length;
+ /*
+ * Hex strings are base 16.
+ */
+ if ((in[0] == '0') && in[1] == 'x') base = 16;
- case PW_TYPE_IPV6_ADDR:
- memcpy(&dst->datum.ipv6prefix[2], &src->datum.ipv6addr, sizeof(dst->datum.ipv6prefix) - 2);
- dst->datum.ipv6prefix[0] = 0;
- dst->datum.ipv6prefix[1] = 128;
- goto fixed_length;
+ i = strtoul(in, &p, base);
- default:
- break;
+ /*
+ * Catch and complain on overflows.
+ */
+ if ((i == ULONG_MAX) || (i >= ((unsigned long) 1) << 32)) {
+ fr_strerror_printf("Integer Value \"%s\" is larger than 1<<32", in);
+ return -1;
}
- break;
-
- default:
- break;
+ /*
+ * Value is always within the limits
+ */
+ dst->datum.integer = (uint32_t) i;
}
}
+ break;
- /*
- * The attribute we've found has to have a size which is
- * compatible with the type of the destination cast.
- */
- if ((src->length < dict_attr_sizes[dst_type][0]) ||
- (src->length > dict_attr_sizes[dst_type][1])) {
- char const *type_name;
+ case PW_TYPE_INTEGER64:
+ {
+ uint64_t i;
- 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",
- type_name,
- fr_int2str(dict_attr_types, dst_type, "<INVALID>"),
- dict_attr_sizes[dst_type][0], dict_attr_sizes[dst_type][1],
- src->length);
- return -1;
+ /*
+ * Note that ALL integers are unsigned!
+ */
+ if (sscanf(in, "%" PRIu64, &i) != 1) {
+ fr_strerror_printf("Failed parsing \"%s\" as unsigned 64bit integer", in);
+ return -1;
+ }
+ dst->datum.integer64 = i;
}
+ break;
- if (src->type == PW_TYPE_OCTETS) {
- value_box_t tmp;
+ case PW_TYPE_SIZE:
+ {
+ size_t i;
- do_octets:
- if (src->length < value_box_field_sizes[dst_type]) {
- fr_strerror_printf("Invalid cast from %s to %s. Source is length %zd is smaller than destination type size %zd",
- fr_int2str(dict_attr_types, src->type, "<INVALID>"),
- fr_int2str(dict_attr_types, dst_type, "<INVALID>"),
- src->length,
- value_box_field_sizes[dst_type]);
+ if (sscanf(in, "%zu", &i) != 1) {
+ fr_strerror_printf("Failed parsing \"%s\" as a file or memory size", in);
return -1;
}
+ dst->datum.size = i;
+ }
+ break;
- /*
- * Copy the raw octets into the datum of a value_box
- * inverting bytesex for integers (if LE).
- */
- memcpy(&tmp.datum, src->datum.octets, value_box_field_sizes[dst_type]);
- tmp.type = dst_type;
- tmp.length = value_box_field_sizes[dst_type];
- if (fr_dict_enum_types[dst_type]) dst->datum.enumv = dst_enumv;
+ case PW_TYPE_TIMEVAL:
+ if (fr_timeval_from_str(&dst->datum.timeval, in) < 0) return -1;
+ break;
- value_box_hton(dst, &tmp);
+ case PW_TYPE_DECIMAL:
+ {
+ double d;
- return 0;
+ if (sscanf(in, "%lf", &d) != 1) {
+ fr_strerror_printf("Failed parsing \"%s\" as a decimal", in);
+ return -1;
+ }
+ dst->datum.decimal = d;
}
+ break;
- /*
- * 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->datum.ipaddr.s_addr = htonl(src->datum.integer);
+ case PW_TYPE_DATE:
+ {
+ /*
+ * time_t may be 64 bits, whule vp_date MUST be 32-bits. We need an
+ * intermediary variable to handle the conversions.
+ */
+ time_t date;
- } else if ((src->type == PW_TYPE_IPV4_ADDR) &&
- ((dst_type == PW_TYPE_INTEGER) ||
- (dst_type == PW_TYPE_DATE) ||
- (dst_type == PW_TYPE_SIGNED))) {
- dst->datum.integer = htonl(src->datum.ipaddr.s_addr);
+ if (fr_time_from_str(&date, in) < 0) {
+ fr_strerror_printf("failed to parse time string \"%s\"", in);
+ return -1;
+ }
- } else { /* they're of the same byte order */
- memcpy(&dst->datum, &src->datum, src->length);
+ dst->datum.date = date;
}
- dst->length = src->length;
- dst->type = dst_type;
- if (fr_dict_enum_types[dst_type]) dst->datum.enumv = dst_enumv;
-
- return 0;
-}
-
-/** Perform a shallow copy of a value_box
- *
- * Like #value_box_copy, but does not duplicate the buffers of the src value_box.
- *
- * @param[out] dst Where to copy value_box to.
- * @param[in] src Where to copy value_box from.
- */
-void value_box_copy_shallow(value_box_t *dst, const value_box_t *src)
-{
- memcpy(((uint8_t *)dst) + value_box_offsets[src->type],
- ((uint8_t const *)src) + value_box_offsets[src->type],
- value_box_field_sizes[src->type]);
+ break;
- dst->type = src->type;
- dst->length = src->length;
- dst->tainted = src->tainted;
-}
+ case PW_TYPE_IFID:
+ if (fr_inet_ifid_pton((void *) dst->datum.ifid, in) == NULL) {
+ fr_strerror_printf("Failed to parse interface-id string \"%s\"", in);
+ return -1;
+ }
+ break;
-/** Copy value data verbatim duplicating any buffers
- *
- * @param ctx To allocate buffers in.
- * @param dst Where to copy value_box to.
- * @param src Where to copy value_box from.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int value_box_copy(TALLOC_CTX *ctx, value_box_t *dst, const value_box_t *src)
-{
- if (!fr_cond_assert(src->type != PW_TYPE_INVALID)) return -1;
+ case PW_TYPE_ETHERNET:
+ {
+ char const *c1, *c2, *cp;
+ size_t p_len = 0;
- switch (src->type) {
- default:
- memcpy(((uint8_t *)dst) + value_box_offsets[src->type],
- ((uint8_t const *)src) + value_box_offsets[src->type],
- value_box_field_sizes[src->type]);
- break;
+ /*
+ * Convert things which are obviously integers to Ethernet addresses
+ *
+ * We assume the number is the bigendian representation of the
+ * ethernet address.
+ */
+ if (is_integer(in)) {
+ uint64_t integer = htonll(atoll(in));
- case PW_TYPE_STRING:
- dst->datum.strvalue = talloc_bstrndup(ctx, src->datum.strvalue, src->length);
- if (!dst->datum.strvalue) return -1;
- break;
+ memcpy(dst->datum.ether, &integer, sizeof(dst->datum.ether));
+ break;
+ }
- case PW_TYPE_OCTETS:
- dst->datum.octets = talloc_memdup(ctx, src->datum.octets, src->length);
- talloc_set_type(dst->datum.strvalue, uint8_t);
- if (!dst->datum.octets) return -1;
- break;
+ cp = in;
+ while (*cp) {
+ if (cp[1] == ':') {
+ c1 = hextab;
+ c2 = memchr(hextab, tolower((int) cp[0]), 16);
+ cp += 2;
+ } else if ((cp[1] != '\0') && ((cp[2] == ':') || (cp[2] == '\0'))) {
+ c1 = memchr(hextab, tolower((int) cp[0]), 16);
+ c2 = memchr(hextab, tolower((int) cp[1]), 16);
+ cp += 2;
+ if (*cp == ':') cp++;
+ } else {
+ c1 = c2 = NULL;
+ }
+ if (!c1 || !c2 || (p_len >= sizeof(dst->datum.ether))) {
+ fr_strerror_printf("failed to parse Ethernet address \"%s\"", in);
+ return -1;
+ }
+ dst->datum.ether[p_len] = ((c1-hextab)<<4) + (c2-hextab);
+ p_len++;
+ }
}
+ break;
- value_box_copy_attrs(dst, src);
-
- return 0;
-}
+ /*
+ * Crazy polymorphic (IPv4/IPv6) attribute src->dst_type for WiMAX.
+ *
+ * We try and make is saner by replacing the original
+ * da, with either an IPv4 or IPv6 da src->dst_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, in, &dst->datum.ipv6addr) > 0) {
+ *dst_type = PW_TYPE_IPV6_ADDR;
+ ret = dict_attr_sizes[PW_TYPE_COMBO_IP_ADDR][1]; /* size of IPv6 address */
+ } else {
+ fr_ipaddr_t ipaddr;
-/** Copy value data verbatim moving any buffers to the specified context
- *
- * @param ctx To allocate buffers in.
- * @param dst Where to copy value_box to.
- * @param src Where to copy value_box from.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int value_box_steal(TALLOC_CTX *ctx, value_box_t *dst, const value_box_t *src)
-{
- if (!fr_cond_assert(src->type != PW_TYPE_INVALID)) return -1;
+ if (fr_inet_hton(&ipaddr, AF_INET, in, false) < 0) {
+ fr_strerror_printf("Failed to find IPv4 address for %s", in);
+ return -1;
+ }
- switch (src->type) {
- default:
- memcpy(dst, src, sizeof(*src));
+ *dst_type = PW_TYPE_IPV4_ADDR;
+ dst->datum.ipaddr.s_addr = ipaddr.ipaddr.ip4addr.s_addr;
+ ret = dict_attr_sizes[PW_TYPE_COMBO_IP_ADDR][0]; /* size of IPv4 address */
+ }
+ }
break;
- case PW_TYPE_STRING:
- dst->datum.strvalue = talloc_steal(ctx, src->datum.strvalue);
- dst->tainted = src->tainted;
- if (!dst->datum.strvalue) {
- fr_strerror_printf("Failed stealing string buffer");
- return -1;
- }
+ case PW_TYPE_SIGNED:
+ /* Damned code for 1 WiMAX attribute */
+ dst->datum.sinteger = (int32_t)strtol(in, NULL, 10);
break;
- case PW_TYPE_OCTETS:
- dst->datum.octets = talloc_steal(ctx, src->datum.octets);
- dst->tainted = src->tainted;
- if (!dst->datum.octets) {
- fr_strerror_printf("Failed stealing octets buffer");
- return -1;
- }
+ case PW_TYPE_BOOLEAN:
+ case PW_TYPE_COMBO_IP_PREFIX:
break;
+
+ case PW_TYPE_UNBOUNDED: /* Should have been dealt with above */
+ case PW_TYPE_STRUCTURAL: /* Listed again to suppress compiler warnings */
+ case PW_TYPE_BAD:
+ fr_strerror_printf("Unknown attribute dst_type %d", *dst_type);
+ return -1;
}
- value_box_copy_attrs(dst, src);
+finish:
+ dst->length = ret;
+ dst->type = *dst_type;
+
+ /*
+ * Fixup enumv
+ */
+ if (fr_dict_enum_types[dst->type]) dst->datum.enumv = dst_enumv;
return 0;
}