From: Arran Cudbard-Bell Date: Tue, 2 May 2017 02:24:23 +0000 (-0400) Subject: Add functions for doing buffer assignment in value.c X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3711d44bdafb141695474d48ea8fdb7873007da1;p=thirdparty%2Ffreeradius-server.git Add functions for doing buffer assignment in value.c --- diff --git a/src/include/value.h b/src/include/value.h index 6bf809ba6fd..f8fa6f33378 100644 --- a/src/include/value.h +++ b/src/include/value.h @@ -15,48 +15,66 @@ */ #ifndef _FR_VALUE_H #define _FR_VALUE_H - - extern size_t const value_box_field_sizes[]; extern size_t const value_box_offsets[]; - #define value_box_foreach(_v, _iv) for (value_box_t *_iv = v; _iv; _iv = _iv->next) +/* + * Allocation + */ value_box_t *value_box_alloc(TALLOC_CTX *ctx, PW_TYPE type); -void value_box_list_free(value_box_t **head); +void value_box_clear(value_box_t *data); +/* + * Comparison + */ int value_box_cmp(value_box_t const *a, value_box_t const *b); int value_box_cmp_op(FR_TOKEN op, value_box_t const *a, value_box_t const *b); +/* + * Conversion + */ size_t value_str_unescape(uint8_t *out, char const *in, size_t inlen, char quote); -void value_box_clear(value_box_t *data); - -int value_box_from_str(TALLOC_CTX *ctx, value_box_t *dst, - PW_TYPE *src_type, fr_dict_attr_t const *src_enumv, - char const *src, ssize_t src_len, char quote); - int value_box_hton(value_box_t *dst, value_box_t const *src); 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); -value_box_t value_box_dup(TALLOC_CTX *ctx, const value_box_t *src); - -void value_box_copy_shallow(value_box_t *dst, const value_box_t *src); - +/* + * Assignment + */ int value_box_copy(TALLOC_CTX *ctx, value_box_t *dst, const value_box_t *src); +void value_box_copy_shallow(TALLOC_CTX *ctx, value_box_t *dst, const value_box_t *src); +int value_box_steal(TALLOC_CTX *ctx, value_box_t *dst, value_box_t const *src); -int value_box_talloc_strcpy(VALUE_PAIR *vp, void const *src); +int value_box_strdup(TALLOC_CTX *ctx, value_box_t *dst, char const *src); +int value_box_strdup_buffer(TALLOC_CTX *ctx, value_box_t *dst, char const *src); +int value_box_strsteal(TALLOC_CTX *ctx, value_box_t *dst, char *src); +int value_box_strdup_shallow(value_box_t *dst, char const *src); +int value_box_strdup_buffer_shallow(TALLOC_CTX *ctx, value_box_t *dst, char const *src); -size_t value_box_snprint(char *out, size_t outlen, value_box_t const *data, char quote); +int value_box_memdup(TALLOC_CTX *ctx, value_box_t *dst, uint8_t const *src, size_t len); +int value_box_memdup_buffer(TALLOC_CTX *ctx, value_box_t *dst, uint8_t *src); +int value_box_memsteal(TALLOC_CTX *ctx, value_box_t *dst, uint8_t const *src); +int value_box_memdup_shallow(value_box_t *dst, uint8_t *src, size_t len); +int value_box_memdup_buffer_shallow(TALLOC_CTX *ctx, value_box_t *dst, uint8_t *src); -int value_box_steal(TALLOC_CTX *ctx, value_box_t *dst, value_box_t const *src); +/* + * Parsing + */ +int value_box_from_str(TALLOC_CTX *ctx, value_box_t *dst, + PW_TYPE *src_type, fr_dict_attr_t const *src_enumv, + char const *src, ssize_t src_len, char quote); +/* + * Printing + */ char *value_box_asprint(TALLOC_CTX *ctx, value_box_t const *data, char quote); +size_t value_box_snprint(char *out, size_t outlen, value_box_t const *data, char quote); #endif /* _FR_VALUE_H */ diff --git a/src/lib/util/value.c b/src/lib/util/value.c index 5832bf1d250..d200f93deb8 100644 --- a/src/lib/util/value.c +++ b/src/lib/util/value.c @@ -92,19 +92,6 @@ size_t const value_box_offsets[] = { [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 @@ -137,6 +124,49 @@ value_box_t *value_box_alloc(TALLOC_CTX *ctx, PW_TYPE type) 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. @@ -735,1198 +765,1668 @@ size_t value_str_unescape(uint8_t *out, char const *in, size_t inlen, char quote 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, ""), + fr_int2str(dict_attr_types, dst_type, "")); + 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, ""), + fr_int2str(dict_attr_types, dst_type, "")); 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, ""), + fr_int2str(dict_attr_types, dst_type, "")); + 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, ""), + fr_int2str(dict_attr_types, dst_type, "")); + 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, ""), + fr_int2str(dict_attr_types, dst_type, "")); + 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, ""); + 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, ""), + 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, ""), + fr_int2str(dict_attr_types, dst_type, ""), + 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, "")); + 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, "")); + 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, "")); + 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, "")); + 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, "")); + 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, ""), - fr_int2str(dict_attr_types, dst_type, "")); - 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, "")); + 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, "")); + 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, "")); + 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, "")); + 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, ""), - fr_int2str(dict_attr_types, dst_type, "")); + 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, ""), - fr_int2str(dict_attr_types, dst_type, "")); - 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, ""), - fr_int2str(dict_attr_types, dst_type, "")); - 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, ""), - fr_int2str(dict_attr_types, dst_type, "")); - 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, ""); - 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, ""), - 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, ""), - fr_int2str(dict_attr_types, dst_type, ""), - 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; }