]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add functions for doing buffer assignment in value.c
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 2 May 2017 02:24:23 +0000 (22:24 -0400)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 2 May 2017 02:25:07 +0000 (22:25 -0400)
src/include/value.h
src/lib/util/value.c

index 6bf809ba6fd6b0c701e457a8a3ab315ec5546b85..f8fa6f33378198c52d117e9ab5a46470d605c53f 100644 (file)
  */
 #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 */
index 5832bf1d2507e60ffc8790b09d5c5b4d489f3f1b..d200f93deb84d81126c46b88f6b116951146e4da 100644 (file)
@@ -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, "<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;
 }