newvector should be a bool
[freeradius.git] / src / lib / radius.c
index 025b802..0090f51 100644 (file)
  * @copyright 2000-2003,2006  The FreeRADIUS server project
  */
 
-#include       <freeradius-devel/ident.h>
 RCSID("$Id$")
 
 #include       <freeradius-devel/libradius.h>
+
 #include       <freeradius-devel/md5.h>
 
 #include       <fcntl.h>
@@ -36,14 +36,10 @@ RCSID("$Id$")
 #include       <freeradius-devel/udpfromto.h>
 #endif
 
-#ifdef HAVE_MALLOC_H
-#include       <malloc.h>
-#endif
-
 #if 0
 #define VP_TRACE if (fr_debug_flag) printf
 
-static void VP_HEXDUMP(const char *msg, const uint8_t *data, size_t len)
+static void VP_HEXDUMP(char const *msg, uint8_t const *data, size_t len)
 {
        size_t i;
 
@@ -78,7 +74,7 @@ static void VP_HEXDUMP(const char *msg, const uint8_t *data, size_t len)
  *     is unsigned, and the attacker can use resources on the server,
  *     even if the end request is rejected.
  */
-int fr_max_attributes = 0;
+uint32_t fr_max_attributes = 0;
 FILE *fr_log_fp = NULL;
 
 typedef struct radius_packet_t {
@@ -94,8 +90,8 @@ static int fr_rand_initialized = 0;
 static unsigned int salt_offset = 0;
 static uint8_t nullvector[AUTH_VECTOR_LEN] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* for CoA decode */
 
-const char *fr_packet_codes[FR_MAX_PACKET_CODE] = {
-  "",
+char const *fr_packet_codes[FR_MAX_PACKET_CODE] = {
+  "",                                  //!< 0
   "Access-Request",
   "Access-Accept",
   "Access-Reject",
@@ -105,7 +101,7 @@ const char *fr_packet_codes[FR_MAX_PACKET_CODE] = {
   "Password-Request",
   "Password-Accept",
   "Password-Reject",
-  "Accounting-Message",
+  "Accounting-Message",                        //!< 10
   "Access-Challenge",
   "Status-Server",
   "Status-Client",
@@ -115,7 +111,7 @@ const char *fr_packet_codes[FR_MAX_PACKET_CODE] = {
   "17",
   "18",
   "19",
-  "20",
+  "20",                                        //!< 20
   "Resource-Free-Request",
   "Resource-Free-Response",
   "Resource-Query-Request",
@@ -125,7 +121,7 @@ const char *fr_packet_codes[FR_MAX_PACKET_CODE] = {
   "NAS-Reboot-Response",
   "28",
   "Next-Passcode",
-  "New-Pin",
+  "New-Pin",                           //!< 30
   "Terminate-Session",
   "Password-Expired",
   "Event-Request",
@@ -135,7 +131,7 @@ const char *fr_packet_codes[FR_MAX_PACKET_CODE] = {
   "37",
   "38",
   "39",
-  "Disconnect-Request",
+  "Disconnect-Request",                        //!< 40
   "Disconnect-ACK",
   "Disconnect-NAK",
   "CoA-Request",
@@ -146,11 +142,11 @@ const char *fr_packet_codes[FR_MAX_PACKET_CODE] = {
   "48",
   "49",
   "IP-Address-Allocate",
-  "IP-Address-Release"
+  "IP-Address-Release",                        //!< 50
 };
 
 
-void fr_printf_log(const char *fmt, ...)
+void fr_printf_log(char const *fmt, ...)
 {
        va_list ap;
 
@@ -166,9 +162,9 @@ void fr_printf_log(const char *fmt, ...)
        return;
 }
 
-static const char *tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+static char const tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
 
-static void print_hex_data(const uint8_t *ptr, int attrlen, int depth)
+static void print_hex_data(uint8_t const *ptr, int attrlen, int depth)
 {
        int i;
 
@@ -200,7 +196,7 @@ void rad_print_hex(RADIUS_PACKET *packet)
 
        if (packet->data_len > 20) {
                int total;
-               const uint8_t *ptr;
+               uint8_t const *ptr;
                fprintf(fr_log_fp, "  Data:");
 
                total = packet->data_len - 20;
@@ -254,8 +250,12 @@ void rad_print_hex(RADIUS_PACKET *packet)
  *     possible combinations.
  */
 static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
-                     fr_ipaddr_t *src_ipaddr, int src_port,
-                     fr_ipaddr_t *dst_ipaddr, int dst_port)
+#ifdef WITH_UDPFROMTO
+                     fr_ipaddr_t *src_ipaddr, uint16_t src_port,
+#else
+                     UNUSED fr_ipaddr_t *src_ipaddr, UNUSED uint16_t src_port,
+#endif
+                     fr_ipaddr_t *dst_ipaddr, uint16_t dst_port)
 {
        int rcode;
        struct sockaddr_storage dst;
@@ -266,8 +266,6 @@ static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
        socklen_t               sizeof_src;
 
        fr_ipaddr2sockaddr(src_ipaddr, src_port, &src, &sizeof_src);
-#else
-       src_port = src_port;    /* -Wunused */
 #endif
 
        if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &dst, &sizeof_dst)) {
@@ -287,8 +285,6 @@ static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
                                   (struct sockaddr *)&dst, sizeof_dst);
                goto done;
        }
-#else
-       src_ipaddr = src_ipaddr; /* -Wunused */
 #endif
 
        /*
@@ -300,7 +296,7 @@ static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
 done:
 #endif
        if (rcode < 0) {
-               DEBUG("rad_send() failed: %s\n", strerror(errno));
+               fr_strerror_printf("sendto failed: %s", fr_syserror(errno));
        }
 
        return rcode;
@@ -314,12 +310,11 @@ void rad_recv_discard(int sockfd)
        socklen_t               sizeof_src = sizeof(src);
 
        (void) recvfrom(sockfd, header, sizeof(header), 0,
-                       (struct sockaddr *)&src, &sizeof_src);
+                       (struct sockaddr *)&src, &sizeof_src);
 }
 
 
-ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port,
-                       int *code)
+ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, uint16_t *src_port, int *code)
 {
        ssize_t                 data_len, packet_len;
        uint8_t                 header[4];
@@ -338,7 +333,7 @@ ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port,
         */
        if (data_len < 4) {
                rad_recv_discard(sockfd);
-               
+
                return 1;
 
        } else {                /* we got 4 bytes of data. */
@@ -353,7 +348,7 @@ ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port,
                 */
                if (packet_len < AUTH_HDR_LEN) {
                        rad_recv_discard(sockfd);
-                       
+
                        return 1;
 
                        /*
@@ -362,7 +357,7 @@ ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port,
                         */
                } else if (packet_len > MAX_PACKET_LEN) {
                        rad_recv_discard(sockfd);
-                       
+
                        return 1;
                }
        }
@@ -372,7 +367,7 @@ ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port,
         */
        if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, src_port)) {
                rad_recv_discard(sockfd);
-               
+
                return 1;
        }
 
@@ -390,34 +385,23 @@ ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port,
  * @brief wrapper for recvfrom, which handles recvfromto, IPv6, and all
  *     possible combinations.
  */
-static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
+static ssize_t rad_recvfrom(int sockfd, RADIUS_PACKET *packet, int flags,
                            fr_ipaddr_t *src_ipaddr, uint16_t *src_port,
                            fr_ipaddr_t *dst_ipaddr, uint16_t *dst_port)
 {
        struct sockaddr_storage src;
        struct sockaddr_storage dst;
        socklen_t               sizeof_src = sizeof(src);
-       socklen_t               sizeof_dst = sizeof(dst);
+       socklen_t               sizeof_dst = sizeof(dst);
        ssize_t                 data_len;
        uint8_t                 header[4];
-       void                    *buf;
        size_t                  len;
-       int                     port;
+       uint16_t                port;
 
        memset(&src, 0, sizeof_src);
        memset(&dst, 0, sizeof_dst);
 
        /*
-        *      Get address family, etc. first, so we know if we
-        *      need to do udpfromto.
-        *
-        *      FIXME: udpfromto also does this, but it's not
-        *      a critical problem.
-        */
-       if (getsockname(sockfd, (struct sockaddr *)&dst,
-                       &sizeof_dst) < 0) return -1;
-
-       /*
         *      Read the length of the packet, from the packet.
         *      This lets us allocate the buffer to use for
         *      reading the rest of the packet.
@@ -463,32 +447,32 @@ static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
                }
        }
 
-       buf = malloc(len);
-       if (!buf) return -1;
+       packet->data = talloc_array(packet, uint8_t, len);
+       if (!packet->data) return -1;
 
        /*
         *      Receive the packet.  The OS will discard any data in the
         *      packet after "len" bytes.
         */
 #ifdef WITH_UDPFROMTO
-       if ((dst.ss_family == AF_INET) || (dst.ss_family == AF_INET6)) {
-               data_len = recvfromto(sockfd, buf, len, flags,
-                                     (struct sockaddr *)&src, &sizeof_src,
-                                     (struct sockaddr *)&dst, &sizeof_dst);
-       } else
+       data_len = recvfromto(sockfd, packet->data, len, flags,
+                             (struct sockaddr *)&src, &sizeof_src,
+                             (struct sockaddr *)&dst, &sizeof_dst);
+#else
+       data_len = recvfrom(sockfd, packet->data, len, flags,
+                           (struct sockaddr *)&src, &sizeof_src);
+
+       /*
+        *      Get the destination address, too.
+        */
+       if (getsockname(sockfd, (struct sockaddr *)&dst,
+                       &sizeof_dst) < 0) return -1;
 #endif
-               /*
-                *      No udpfromto, fail gracefully.
-                */
-               data_len = recvfrom(sockfd, buf, len, flags,
-                                   (struct sockaddr *)&src, &sizeof_src);
        if (data_len < 0) {
-               free(buf);
                return data_len;
        }
 
        if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, &port)) {
-               free(buf);
                return -1;      /* Unknown address family, Die Die Die! */
        }
        *src_port = port;
@@ -500,15 +484,9 @@ static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
         *      Different address families should never happen.
         */
        if (src.ss_family != dst.ss_family) {
-               free(buf);
                return -1;
        }
 
-       /*
-        *      Tell the caller about the data
-        */
-       *pbuf = buf;
-
        return data_len;
 }
 
@@ -516,33 +494,33 @@ static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
 #define AUTH_PASS_LEN (AUTH_VECTOR_LEN)
 /**
  * @brief Build an encrypted secret value to return in a reply packet
- * 
- *               The secret is hidden by xoring with a MD5 digest
- *               created from the shared secret and the authentication
- *               vector.  We put them into MD5 in the reverse order from
- *               that used when encrypting passwords to RADIUS.
+ *
+ *            The secret is hidden by xoring with a MD5 digest
+ *            created from the shared secret and the authentication
+ *            vector.  We put them into MD5 in the reverse order from
+ *            that used when encrypting passwords to RADIUS.
  *
  */
-static void make_secret(uint8_t *digest, const uint8_t *vector,
-                       const char *secret, const uint8_t *value)
+static void make_secret(uint8_t *digest, uint8_t const *vector,
+                       char const *secret, uint8_t const *value)
 {
        FR_MD5_CTX context;
-        int             i;
+       int          i;
 
-       fr_MD5Init(&context);
-       fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
-       fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
-       fr_MD5Final(digest, &context);
+       fr_md5_init(&context);
+       fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
+       fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
+       fr_md5_final(digest, &context);
 
-        for ( i = 0; i < AUTH_VECTOR_LEN; i++ ) {
+       for ( i = 0; i < AUTH_VECTOR_LEN; i++ ) {
                digest[i] ^= value[i];
-        }
+       }
 }
 
 #define MAX_PASS_LEN (128)
 static void make_passwd(uint8_t *output, ssize_t *outlen,
-                       const uint8_t *input, size_t inlen,
-                       const char *secret, const uint8_t *vector)
+                       uint8_t const *input, size_t inlen,
+                       char const *secret, uint8_t const *vector)
 {
        FR_MD5_CTX context, old;
        uint8_t digest[AUTH_VECTOR_LEN];
@@ -570,24 +548,24 @@ static void make_passwd(uint8_t *output, ssize_t *outlen,
        }
        *outlen = len;
 
-       fr_MD5Init(&context);
-       fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
+       fr_md5_init(&context);
+       fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
        old = context;
 
        /*
         *      Do first pass.
         */
-       fr_MD5Update(&context, vector, AUTH_PASS_LEN);
+       fr_md5_update(&context, vector, AUTH_PASS_LEN);
 
        for (n = 0; n < len; n += AUTH_PASS_LEN) {
                if (n > 0) {
                        context = old;
-                       fr_MD5Update(&context,
+                       fr_md5_update(&context,
                                       passwd + n - AUTH_PASS_LEN,
                                       AUTH_PASS_LEN);
                }
 
-               fr_MD5Final(digest, &context);
+               fr_md5_final(digest, &context);
                for (i = 0; i < AUTH_PASS_LEN; i++) {
                        passwd[i + n] ^= digest[i];
                }
@@ -597,8 +575,8 @@ static void make_passwd(uint8_t *output, ssize_t *outlen,
 }
 
 static void make_tunnel_passwd(uint8_t *output, ssize_t *outlen,
-                              const uint8_t *input, size_t inlen, size_t room,
-                              const char *secret, const uint8_t *vector)
+                              uint8_t const *input, size_t inlen, size_t room,
+                              char const *secret, uint8_t const *vector)
 {
        FR_MD5_CTX context, old;
        uint8_t digest[AUTH_VECTOR_LEN];
@@ -664,22 +642,22 @@ static void make_tunnel_passwd(uint8_t *output, ssize_t *outlen,
        passwd[1] = fr_rand();
        passwd[2] = inlen;      /* length of the password string */
 
-       fr_MD5Init(&context);
-       fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
+       fr_md5_init(&context);
+       fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
        old = context;
 
-       fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
-       fr_MD5Update(&context, &passwd[0], 2);
+       fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
+       fr_md5_update(&context, &passwd[0], 2);
 
        for (n = 0; n < len; n += AUTH_PASS_LEN) {
                if (n > 0) {
                        context = old;
-                       fr_MD5Update(&context,
+                       fr_md5_update(&context,
                                       passwd + 2 + n - AUTH_PASS_LEN,
                                       AUTH_PASS_LEN);
                }
 
-               fr_MD5Final(digest, &context);
+               fr_md5_final(digest, &context);
 
                for (i = 0; i < AUTH_PASS_LEN; i++) {
                        passwd[i + 2 + n] ^= digest[i];
@@ -692,7 +670,7 @@ extern int fr_attr_max_tlv;
 extern int fr_attr_shift[];
 extern int fr_attr_mask[];
 
-static int do_next_tlv(const VALUE_PAIR *vp, const VALUE_PAIR *next, int nest)
+static int do_next_tlv(VALUE_PAIR const *vp, VALUE_PAIR const *next, int nest)
 {
        unsigned int tlv1, tlv2;
 
@@ -723,25 +701,25 @@ static int do_next_tlv(const VALUE_PAIR *vp, const VALUE_PAIR *next, int nest)
         */
        tlv1 = vp->da->attr;
        tlv2 = next->da->attr;
-       
+
        tlv1 &= ((1 << fr_attr_shift[nest]) - 1);
        tlv2 &= ((1 << fr_attr_shift[nest]) - 1);
-       
+
        if (tlv1 != tlv2) return 0;
 
        return 1;
 }
 
 
-static ssize_t vp2data_any(const RADIUS_PACKET *packet,
-                          const RADIUS_PACKET *original,
-                          const char *secret, int nest,
-                          const VALUE_PAIR **pvp,
+static ssize_t vp2data_any(RADIUS_PACKET const *packet,
+                          RADIUS_PACKET const *original,
+                          char const *secret, int nest,
+                          VALUE_PAIR const **pvp,
                           uint8_t *start, size_t room);
 
-static ssize_t vp2attr_rfc(const RADIUS_PACKET *packet,
-                          const RADIUS_PACKET *original,
-                          const char *secret, const VALUE_PAIR **pvp,
+static ssize_t vp2attr_rfc(RADIUS_PACKET const *packet,
+                          RADIUS_PACKET const *original,
+                          char const *secret, VALUE_PAIR const **pvp,
                           unsigned int attribute, uint8_t *ptr, size_t room);
 
 /**
@@ -749,17 +727,17 @@ static ssize_t vp2attr_rfc(const RADIUS_PACKET *packet,
  *     the *data* portion of the TLV, and assumes that the encapsulating
  *     attribute has already been encoded.
  */
-static ssize_t vp2data_tlvs(const RADIUS_PACKET *packet,
-                           const RADIUS_PACKET *original,
-                           const char *secret, int nest,
-                           const VALUE_PAIR **pvp,
+static ssize_t vp2data_tlvs(RADIUS_PACKET const *packet,
+                           RADIUS_PACKET const *original,
+                           char const *secret, int nest,
+                           VALUE_PAIR const **pvp,
                            uint8_t *start, size_t room)
 {
        ssize_t len;
        size_t my_room;
        uint8_t *ptr = start;
-       const VALUE_PAIR *vp = *pvp;
-       const VALUE_PAIR *svp = vp;
+       VALUE_PAIR const *vp = *pvp;
+       VALUE_PAIR const *svp = vp;
 
        if (!svp) return 0;
 
@@ -771,11 +749,13 @@ static ssize_t vp2data_tlvs(const RADIUS_PACKET *packet,
 #endif
 
        while (vp) {
-               if (room < 2) return ptr - start;
-               
+               VERIFY_VP(vp);
+
+               if (room <= 2) return ptr - start;
+
                ptr[0] = (vp->da->attr >> fr_attr_shift[nest]) & fr_attr_mask[nest];
                ptr[1] = 2;
-               
+
                my_room = room;
                if (room > 255) my_room = 255;
 
@@ -797,14 +777,14 @@ static ssize_t vp2data_tlvs(const RADIUS_PACKET *packet,
                room -= ptr[1];
                ptr += ptr[1];
                *pvp = vp;
-               
+
                if (!do_next_tlv(svp, vp, nest)) break;
        }
 
 #ifndef NDEBUG
        if ((fr_debug_flag > 3) && fr_log_fp) {
-               const DICT_ATTR *da;
-               
+               DICT_ATTR const *da;
+
                da = dict_attrbyvalue(svp->da->attr & ((1 << fr_attr_shift[nest ]) - 1), svp->da->vendor);
                if (da) fprintf(fr_log_fp, "\t%s = ...\n", da->name);
        }
@@ -817,19 +797,21 @@ static ssize_t vp2data_tlvs(const RADIUS_PACKET *packet,
  * @brief Encodes the data portion of an attribute.
  * @return -1 on error, or the length of the data portion.
  */
-static ssize_t vp2data_any(const RADIUS_PACKET *packet,
-                          const RADIUS_PACKET *original,
-                          const char *secret, int nest,
-                          const VALUE_PAIR **pvp,
+static ssize_t vp2data_any(RADIUS_PACKET const *packet,
+                          RADIUS_PACKET const *original,
+                          char const *secret, int nest,
+                          VALUE_PAIR const **pvp,
                           uint8_t *start, size_t room)
 {
        uint32_t lvalue;
        ssize_t len;
-       const uint8_t *data;
+       uint8_t const *data;
        uint8_t *ptr = start;
        uint8_t array[4];
        uint64_t lvalue64;
-       const VALUE_PAIR *vp = *pvp;
+       VALUE_PAIR const *vp = *pvp;
+
+       VERIFY_VP(vp);
 
        /*
         *      See if we need to encode a TLV.  The low portion of
@@ -850,36 +832,39 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
        /*
         *      Set up the default sources for the data.
         */
-       data = vp->vp_octets;
        len = vp->length;
 
-       /*
-        *      Short-circuit it for long attributes.  They can't be
-        *      encrypted, tagged, etc.
-        */
-       if ((vp->da->type & PW_FLAG_LONG) != 0) goto do_tlv;
-
        switch(vp->da->type) {
        case PW_TYPE_STRING:
        case PW_TYPE_OCTETS:
+       case PW_TYPE_TLV:
+               data = vp->data.ptr;
+               if (!data) {
+                       fr_strerror_printf("ERROR: Cannot encode NULL data");
+                       return -1;
+               }
+               break;
+
        case PW_TYPE_IFID:
-       case PW_TYPE_IPV6ADDR:
-       case PW_TYPE_IPV6PREFIX:
-       case PW_TYPE_IPV4PREFIX:
+       case PW_TYPE_IPV4_ADDR:
+       case PW_TYPE_IPV6_ADDR:
+       case PW_TYPE_IPV6_PREFIX:
+       case PW_TYPE_IPV4_PREFIX:
        case PW_TYPE_ABINARY:
-               /* nothing more to do */
+       case PW_TYPE_ETHERNET:  /* just in case */
+               data = (uint8_t const *) &vp->data;
                break;
 
        case PW_TYPE_BYTE:
                len = 1;        /* just in case */
-               array[0] = vp->vp_integer & 0xff;
+               array[0] = vp->vp_byte;
                data = array;
                break;
 
        case PW_TYPE_SHORT:
                len = 2;        /* just in case */
-               array[0] = (vp->vp_integer >> 8) & 0xff;
-               array[1] = vp->vp_integer & 0xff;
+               array[0] = (vp->vp_short >> 8) & 0xff;
+               array[1] = vp->vp_short & 0xff;
                data = array;
                break;
 
@@ -896,17 +881,12 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
                data = (uint8_t *) &lvalue64;
                break;
 
-       case PW_TYPE_IPADDR:
-               data = (const uint8_t *) &vp->vp_ipaddr;
-               len = 4;        /* just in case */
-               break;
-
                /*
                 *  There are no tagged date attributes.
                 */
        case PW_TYPE_DATE:
                lvalue = htonl(vp->vp_date);
-               data = (const uint8_t *) &lvalue;
+               data = (uint8_t const *) &lvalue;
                len = 4;        /* just in case */
                break;
 
@@ -917,21 +897,12 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
                len = 4;        /* just in case */
                slvalue = htonl(vp->vp_signed);
                memcpy(array, &slvalue, sizeof(slvalue));
+               data = array;
                break;
        }
 
-       case PW_TYPE_TLV:
-       do_tlv:
-               data = vp->vp_tlv;
-               if (!data) {
-                       fr_strerror_printf("ERROR: Cannot encode NULL TLV");
-                       return -1;
-               }
-               break;
-
        default:                /* unknown type: ignore it */
-               fr_strerror_printf("ERROR: Unknown attribute type %d",
-                                  vp->da->type);
+               fr_strerror_printf("ERROR: Unknown attribute type %d", vp->da->type);
                return -1;
        }
 
@@ -973,29 +944,29 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
                 */
                if (room < (18 + lvalue)) return 0;
 
-               switch (packet->code) {
-               case PW_AUTHENTICATION_ACK:
-               case PW_AUTHENTICATION_REJECT:
-               case PW_ACCESS_CHALLENGE:
-               default:
+               switch (packet->code) {
+               case PW_CODE_ACCESS_ACCEPT:
+               case PW_CODE_ACCESS_REJECT:
+               case PW_CODE_ACCESS_CHALLENGE:
+               default:
                        if (!original) {
                                fr_strerror_printf("ERROR: No request packet, cannot encrypt %s attribute in the vp.", vp->da->name);
                                return -1;
                        }
 
-                       if (lvalue) ptr[0] = vp->tag;
+                       if (lvalue) ptr[0] = TAG_VALID(vp->tag) ? vp->tag : TAG_NONE;
                        make_tunnel_passwd(ptr + lvalue, &len, data, len,
                                           room - lvalue,
                                           secret, original->vector);
-                       break;
-               case PW_ACCOUNTING_REQUEST:
-               case PW_DISCONNECT_REQUEST:
-               case PW_COA_REQUEST:
-                       ptr[0] = vp->tag;
+                       break;
+               case PW_CODE_ACCOUNTING_REQUEST:
+               case PW_CODE_DISCONNECT_REQUEST:
+               case PW_CODE_COA_REQUEST:
+                       ptr[0] = TAG_VALID(vp->tag) ? vp->tag : TAG_NONE;
                        make_tunnel_passwd(ptr + 1, &len, data, len - 1, room,
                                           secret, packet->vector);
-                       break;
-               }
+                       break;
+               }
                break;
 
                /*
@@ -1027,19 +998,19 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
        return len + (ptr - start);
 }
 
-static ssize_t attr_shift(const uint8_t *start, const uint8_t *end,
+static ssize_t attr_shift(uint8_t const *start, uint8_t const *end,
                          uint8_t *ptr, int hdr_len, ssize_t len,
                          int flag_offset, int vsa_offset)
 {
        int check_len = len - ptr[1];
        int total = len + hdr_len;
-       
+
        /*
         *      Pass 1: Check if the addition of the headers
         *      overflows the available room.  If so, return
         *      what we were capable of encoding.
         */
-       
+
        while (check_len > (255 - hdr_len)) {
                total += hdr_len;
                check_len -= (255 - hdr_len);
@@ -1055,7 +1026,7 @@ static ssize_t attr_shift(const uint8_t *start, const uint8_t *end,
        if ((ptr + ptr[1] + total) > end) {
                return (ptr + ptr[1]) - start;
        }
-       
+
        /*
         *      Pass 2: Now that we know there's enough room,
         *      re-arrange the data to form a set of valid
@@ -1063,18 +1034,18 @@ static ssize_t attr_shift(const uint8_t *start, const uint8_t *end,
         */
        while (1) {
                int sublen = 255 - ptr[1];
-               
+
                if (len <= sublen) {
                        break;
                }
-               
+
                len -= sublen;
                memmove(ptr + 255 + hdr_len, ptr + 255, sublen);
                memcpy(ptr + 255, ptr, hdr_len);
                ptr[1] += sublen;
                if (vsa_offset) ptr[vsa_offset] += sublen;
                ptr[flag_offset] |= 0x80;
-               
+
                ptr += 255;
                ptr[1] = hdr_len;
                if (vsa_offset) ptr[vsa_offset] = 3;
@@ -1090,15 +1061,17 @@ static ssize_t attr_shift(const uint8_t *start, const uint8_t *end,
 /**
  * @brief Encode an "extended" attribute.
  */
-int rad_vp2extended(const RADIUS_PACKET *packet,
-                   const RADIUS_PACKET *original,
-                   const char *secret, const VALUE_PAIR **pvp,
+int rad_vp2extended(RADIUS_PACKET const *packet,
+                   RADIUS_PACKET const *original,
+                   char const *secret, VALUE_PAIR const **pvp,
                    uint8_t *ptr, size_t room)
 {
        int len;
        int hdr_len;
        uint8_t *start = ptr;
-       const VALUE_PAIR *vp = *pvp;
+       VALUE_PAIR const *vp = *pvp;
+
+       VERIFY_VP(vp);
 
        if (!vp->da->flags.extended) {
                fr_strerror_printf("rad_vp2extended called for non-extended attribute");
@@ -1113,7 +1086,7 @@ int rad_vp2extended(const RADIUS_PACKET *packet,
 
        if (!vp->da->flags.long_extended) {
                if (room < 3) return 0;
-       
+
                ptr[1] = 3;
                ptr[2] = vp->da->attr & fr_attr_mask[0];
 
@@ -1147,7 +1120,7 @@ int rad_vp2extended(const RADIUS_PACKET *packet,
                evs[1] = (vp->da->vendor >> 16) & 0xff;
                evs[2] = (vp->da->vendor >> 8) & 0xff;
                evs[3] = vp->da->vendor & 0xff;
-               evs[4] = vp->da->attr & fr_attr_mask[0];                
+               evs[4] = vp->da->attr & fr_attr_mask[0];
 
                ptr[1] += 5;
        }
@@ -1168,7 +1141,7 @@ int rad_vp2extended(const RADIUS_PACKET *packet,
        }
 
        ptr[1] += len;
-       
+
 #ifndef NDEBUG
        if ((fr_debug_flag > 3) && fr_log_fp) {
                int jump = 3;
@@ -1176,7 +1149,7 @@ int rad_vp2extended(const RADIUS_PACKET *packet,
                fprintf(fr_log_fp, "\t\t%02x %02x  ", ptr[0], ptr[1]);
                if (!vp->da->flags.long_extended) {
                        fprintf(fr_log_fp, "%02x  ", ptr[2]);
-                       
+
                } else {
                        fprintf(fr_log_fp, "%02x %02x  ", ptr[2], ptr[3]);
                        jump = 4;
@@ -1204,16 +1177,18 @@ int rad_vp2extended(const RADIUS_PACKET *packet,
 /**
  * @brief Encode a WiMAX attribute.
  */
-int rad_vp2wimax(const RADIUS_PACKET *packet,
-                const RADIUS_PACKET *original,
-                const char *secret, const VALUE_PAIR **pvp,
+int rad_vp2wimax(RADIUS_PACKET const *packet,
+                RADIUS_PACKET const *original,
+                char const *secret, VALUE_PAIR const **pvp,
                 uint8_t *ptr, size_t room)
 {
        int len;
        uint32_t lvalue;
        int hdr_len;
        uint8_t *start = ptr;
-       const VALUE_PAIR *vp = *pvp;
+       VALUE_PAIR const *vp = *pvp;
+
+       VERIFY_VP(vp);
 
        /*
         *      Double-check for WiMAX format.
@@ -1275,6 +1250,62 @@ int rad_vp2wimax(const RADIUS_PACKET *packet,
 }
 
 /**
+ * @brief Encode an RFC format attribute, with the "concat" flag set.
+ *
+ *     If there isn't enough room in the packet, the data is
+ *     truncated to fit.
+ */
+static ssize_t vp2attr_concat(UNUSED RADIUS_PACKET const *packet,
+                             UNUSED RADIUS_PACKET const *original,
+                             UNUSED char const *secret, VALUE_PAIR const **pvp,
+                             unsigned int attribute, uint8_t *start, size_t room)
+{
+       uint8_t *ptr = start;
+       uint8_t const *p;
+       size_t len, left;
+       VALUE_PAIR const *vp = *pvp;
+
+       VERIFY_VP(vp);
+
+       debug_pair(vp);
+
+       p = vp->vp_octets;
+       len = vp->length;
+
+       while (len > 0) {
+               if (room <= 2) break;
+
+               ptr[0] = attribute;
+               ptr[1] = 2;
+
+               left = len;
+
+               /* no more than 253 octets */
+               if (left > 253) left = 253;
+
+               /* no more than "room" octets */
+               if (room < (left + 2)) left = room - 2;
+
+               memcpy(ptr + 2, p, left);
+
+#ifndef NDEBUG
+               if ((fr_debug_flag > 3) && fr_log_fp) {
+                       fprintf(fr_log_fp, "\t\t%02x %02x  ", ptr[0], ptr[1]);
+                       print_hex_data(ptr + 2, len, 3);
+               }
+#endif
+               ptr[1] += left;
+               ptr += ptr[1];
+               p += left;
+               room -= left;
+               len -= left;
+       }
+
+       *pvp = vp->next;
+       return ptr - start;
+}
+
+/**
  * @brief Encode an RFC format TLV.
  *
  *     This could be a standard attribute,
@@ -1282,14 +1313,14 @@ int rad_vp2wimax(const RADIUS_PACKET *packet,
  *     vp->da->attr == attribute.  Otherwise, attribute may be
  *     something else.
  */
-static ssize_t vp2attr_rfc(const RADIUS_PACKET *packet,
-                          const RADIUS_PACKET *original,
-                          const char *secret, const VALUE_PAIR **pvp,
+static ssize_t vp2attr_rfc(RADIUS_PACKET const *packet,
+                          RADIUS_PACKET const *original,
+                          char const *secret, VALUE_PAIR const **pvp,
                           unsigned int attribute, uint8_t *ptr, size_t room)
 {
        ssize_t len;
 
-       if (room < 2) return 0;
+       if (room <= 2) return 0;
 
        ptr[0] = attribute & 0xff;
        ptr[1] = 2;
@@ -1316,16 +1347,17 @@ static ssize_t vp2attr_rfc(const RADIUS_PACKET *packet,
  * @brief Encode a VSA which is a TLV.  If it's in the RFC format, call
  *     vp2attr_rfc.  Otherwise, encode it here.
  */
-static ssize_t vp2attr_vsa(const RADIUS_PACKET *packet,
-                          const RADIUS_PACKET *original,
-                          const char *secret, const VALUE_PAIR **pvp,
+static ssize_t vp2attr_vsa(RADIUS_PACKET const *packet,
+                          RADIUS_PACKET const *original,
+                          char const *secret, VALUE_PAIR const **pvp,
                           unsigned int attribute, unsigned int vendor,
                           uint8_t *ptr, size_t room)
 {
        ssize_t len;
        DICT_VENDOR *dv;
-       const VALUE_PAIR *vp = *pvp;
+       VALUE_PAIR const *vp = *pvp;
 
+       VERIFY_VP(vp);
        /*
         *      Unknown vendor: RFC format.
         *      Known vendor and RFC format: go do that.
@@ -1401,19 +1433,19 @@ static ssize_t vp2attr_vsa(const RADIUS_PACKET *packet,
                                fprintf(fr_log_fp, "\t\t%02x%02x%02x%02x ",
                                        ptr[0], ptr[1], ptr[2], ptr[3]);
                        break;
-                       
+
                case 2:
                        if ((fr_debug_flag > 3) && fr_log_fp)
                                fprintf(fr_log_fp, "\t\t%02x%02x ",
                                        ptr[0], ptr[1]);
                break;
-               
+
                case 1:
                        if ((fr_debug_flag > 3) && fr_log_fp)
                                fprintf(fr_log_fp, "\t\t%02x ", ptr[0]);
                        break;
                }
-               
+
                switch (dv->length) {
                default:
                        break;
@@ -1444,14 +1476,15 @@ static ssize_t vp2attr_vsa(const RADIUS_PACKET *packet,
 /**
  * @brief Encode a Vendor-Specific attribute.
  */
-int rad_vp2vsa(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
-               const char *secret, const VALUE_PAIR **pvp, uint8_t *ptr,
+int rad_vp2vsa(RADIUS_PACKET const *packet, RADIUS_PACKET const *original,
+               char const *secret, VALUE_PAIR const **pvp, uint8_t *ptr,
                size_t room)
 {
        ssize_t len;
        uint32_t lvalue;
-       const VALUE_PAIR *vp = *pvp;
+       VALUE_PAIR const *vp = *pvp;
 
+       VERIFY_VP(vp);
        /*
         *      Double-check for WiMAX format.
         */
@@ -1505,12 +1538,14 @@ int rad_vp2vsa(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
 /**
  * @brief Encode an RFC standard attribute 1..255
  */
-int rad_vp2rfc(const RADIUS_PACKET *packet,
-              const RADIUS_PACKET *original,
-              const char *secret, const VALUE_PAIR **pvp,
+int rad_vp2rfc(RADIUS_PACKET const *packet,
+              RADIUS_PACKET const *original,
+              char const *secret, VALUE_PAIR const **pvp,
               uint8_t *ptr, size_t room)
 {
-       const VALUE_PAIR *vp = *pvp;
+       VALUE_PAIR const *vp = *pvp;
+
+       VERIFY_VP(vp);
 
        if (vp->da->vendor != 0) {
                fr_strerror_printf("rad_vp2rfc called with VSA");
@@ -1540,7 +1575,7 @@ int rad_vp2rfc(const RADIUS_PACKET *packet,
         */
        if (vp->da->attr == PW_MESSAGE_AUTHENTICATOR) {
                if (room < 18) return -1;
-               
+
                debug_pair(vp);
                ptr[0] = PW_MESSAGE_AUTHENTICATOR;
                ptr[1] = 18;
@@ -1550,22 +1585,32 @@ int rad_vp2rfc(const RADIUS_PACKET *packet,
                        fprintf(fr_log_fp, "\t\t50 12 ...\n");
                }
 #endif
-               
+
                *pvp = (*pvp)->next;
                return 18;
        }
 
+       /*
+        *      EAP-Message is special.
+        */
+       if (vp->da->flags.concat && (vp->length > 253)) {
+               return vp2attr_concat(packet, original, secret, pvp, vp->da->attr,
+                                     ptr, room);
+       }
+
        return vp2attr_rfc(packet, original, secret, pvp, vp->da->attr,
                           ptr, room);
 }
 
-static ssize_t rad_vp2rfctlv(const RADIUS_PACKET *packet,
-                            const RADIUS_PACKET *original,
-                            const char *secret, const VALUE_PAIR **pvp,
+static ssize_t rad_vp2rfctlv(RADIUS_PACKET const *packet,
+                            RADIUS_PACKET const *original,
+                            char const *secret, VALUE_PAIR const **pvp,
                             uint8_t *start, size_t room)
 {
        ssize_t len;
-       const VALUE_PAIR *vp = *pvp;
+       VALUE_PAIR const *vp = *pvp;
+
+       VERIFY_VP(vp);
 
        if (!vp->da->flags.is_tlv) {
                fr_strerror_printf("rad_vp2rfctlv: attr is not a TLV");
@@ -1592,7 +1637,6 @@ static ssize_t rad_vp2rfctlv(const RADIUS_PACKET *packet,
        if (len <= 0) return len;
 
        if (len > 253) {
-               fprintf(stderr, "SHIT!\n");
                return -1;
        }
 
@@ -1605,16 +1649,18 @@ static ssize_t rad_vp2rfctlv(const RADIUS_PACKET *packet,
 /**
  * @brief Parse a data structure into a RADIUS attribute.
  */
-int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
-               const char *secret, const VALUE_PAIR **pvp, uint8_t *start,
+int rad_vp2attr(RADIUS_PACKET const *packet, RADIUS_PACKET const *original,
+               char const *secret, VALUE_PAIR const **pvp, uint8_t *start,
                size_t room)
 {
-       const VALUE_PAIR *vp;
+       VALUE_PAIR const *vp;
 
        if (!pvp || !*pvp || !start || (room <= 2)) return -1;
 
        vp = *pvp;
 
+       VERIFY_VP(vp);
+
        /*
         *      RFC format attributes take the fast path.
         */
@@ -1652,30 +1698,30 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
 /**
  * @brief Encode a packet.
  */
-int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
-              const char *secret)
+int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+              char const *secret)
 {
-       radius_packet_t *hdr;
-       uint8_t         *ptr;
-       uint16_t        total_length;
-       int             len;
-       const VALUE_PAIR        *reply;
-       const char      *what;
-       char            ip_src_buffer[128];
-       char            ip_dst_buffer[128];
+       radius_packet_t         *hdr;
+       uint8_t                 *ptr;
+       uint16_t                total_length;
+       int                     len;
+       VALUE_PAIR const        *reply;
+       char const              *what;
+       char                    ip_src_buffer[INET6_ADDRSTRLEN];
+       char                    ip_dst_buffer[INET6_ADDRSTRLEN];
 
        /*
         *      A 4K packet, aligned on 64-bits.
         */
        uint64_t        data[MAX_PACKET_LEN / sizeof(uint64_t)];
 
-       if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+       if (is_radius_code(packet->code)) {
                what = fr_packet_codes[packet->code];
        } else {
                what = "Reply";
        }
 
-       DEBUG("Sending %s of id %d from %s port %u to %s port %u\n",
+       DEBUG("Sending %s Id %d from %s:%u to %s:%u\n",
              what, packet->id,
              inet_ntop(packet->src_ipaddr.af,
                        &packet->src_ipaddr.ipaddr,
@@ -1690,11 +1736,11 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
         *      Double-check some things based on packet code.
         */
        switch (packet->code) {
-       case PW_AUTHENTICATION_ACK:
-       case PW_AUTHENTICATION_REJECT:
-       case PW_ACCESS_CHALLENGE:
+       case PW_CODE_ACCESS_ACCEPT:
+       case PW_CODE_ACCESS_REJECT:
+       case PW_CODE_ACCESS_CHALLENGE:
                if (!original) {
-                       fr_strerror_printf("ERROR: Cannot sign response packet without a request packet.");
+                       fr_strerror_printf("ERROR: Cannot sign response packet without a request packet");
                        return -1;
                }
                break;
@@ -1702,9 +1748,9 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                /*
                 *      These packet vectors start off as all zero.
                 */
-       case PW_ACCOUNTING_REQUEST:
-       case PW_DISCONNECT_REQUEST:
-       case PW_COA_REQUEST:
+       case PW_CODE_ACCOUNTING_REQUEST:
+       case PW_CODE_DISCONNECT_REQUEST:
+       case PW_CODE_COA_REQUEST:
                memset(packet->vector, 0, sizeof(packet->vector));
                break;
 
@@ -1749,7 +1795,9 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
        reply = packet->vps;
        while (reply) {
                size_t last_len;
-               const char *last_name = NULL;
+               char const *last_name = NULL;
+
+               VERIFY_VP(reply);
 
                /*
                 *      Ignore non-wire attributes, but allow extended
@@ -1800,9 +1848,10 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                 */
                if (len == 0) {
                        if (last_len != 0) {
-                               DEBUG("WARNING: Failed encoding attribute %s\n", last_name);
+                               fr_strerror_printf("WARNING: Failed encoding attribute %s\n", last_name);
+                               break;
                        } else {
-                               DEBUG("WARNING: Skipping zero-length attribute %s\n", last_name);
+                               fr_strerror_printf("WARNING: Skipping zero-length attribute %s\n", last_name);
                        }
                }
 
@@ -1822,7 +1871,7 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
         *      memory for a request.
         */
        packet->data_len = total_length;
-       packet->data = (uint8_t *) malloc(packet->data_len);
+       packet->data = talloc_array(packet, uint8_t, packet->data_len);
        if (!packet->data) {
                fr_strerror_printf("Out of memory");
                return -1;
@@ -1841,8 +1890,8 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
 /**
  * @brief Sign a previously encoded packet.
  */
-int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
-            const char *secret)
+int rad_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+            char const *secret)
 {
        radius_packet_t *hdr = (radius_packet_t *)packet->data;
 
@@ -1850,7 +1899,7 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
         *      It wasn't assigned an Id, this is bad!
         */
        if (packet->id < 0) {
-               fr_strerror_printf("ERROR: RADIUS packets must be assigned an Id.");
+               fr_strerror_printf("ERROR: RADIUS packets must be assigned an Id");
                return -1;
        }
 
@@ -1868,27 +1917,26 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                uint8_t calc_auth_vector[AUTH_VECTOR_LEN];
 
                switch (packet->code) {
-               case PW_ACCOUNTING_RESPONSE:
-                       if (original && original->code == PW_STATUS_SERVER) {
+               case PW_CODE_ACCOUNTING_RESPONSE:
+                       if (original && original->code == PW_CODE_STATUS_SERVER) {
                                goto do_ack;
                        }
 
-               case PW_ACCOUNTING_REQUEST:
-               case PW_DISCONNECT_REQUEST:
-               case PW_DISCONNECT_ACK:
-               case PW_DISCONNECT_NAK:
-               case PW_COA_REQUEST:
-               case PW_COA_ACK:
-               case PW_COA_NAK:
+               case PW_CODE_ACCOUNTING_REQUEST:
+               case PW_CODE_DISCONNECT_REQUEST:
+               case PW_CODE_DISCONNECT_ACK:
+               case PW_CODE_DISCONNECT_NAK:
+               case PW_CODE_COA_REQUEST:
+               case PW_CODE_COA_ACK:
                        memset(hdr->vector, 0, AUTH_VECTOR_LEN);
                        break;
 
                do_ack:
-               case PW_AUTHENTICATION_ACK:
-               case PW_AUTHENTICATION_REJECT:
-               case PW_ACCESS_CHALLENGE:
+               case PW_CODE_ACCESS_ACCEPT:
+               case PW_CODE_ACCESS_REJECT:
+               case PW_CODE_ACCESS_CHALLENGE:
                        if (!original) {
-                               fr_strerror_printf("ERROR: Cannot sign response packet without a request packet.");
+                               fr_strerror_printf("ERROR: Cannot sign response packet without a request packet");
                                return -1;
                        }
                        memcpy(hdr->vector, original->vector,
@@ -1906,9 +1954,8 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                 *      into the Message-Authenticator
                 *      attribute.
                 */
-               fr_hmac_md5(packet->data, packet->data_len,
-                           (const uint8_t *) secret, strlen(secret),
-                           calc_auth_vector);
+               fr_hmac_md5(calc_auth_vector, packet->data, packet->data_len,
+                           (uint8_t const *) secret, strlen(secret));
                memcpy(packet->data + packet->offset + 2,
                       calc_auth_vector, AUTH_VECTOR_LEN);
 
@@ -1928,8 +1975,8 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                 *      Request packets are not signed, bur
                 *      have a random authentication vector.
                 */
-       case PW_AUTHENTICATION_REQUEST:
-       case PW_STATUS_SERVER:
+       case PW_CODE_ACCESS_REQUEST:
+       case PW_CODE_STATUS_SERVER:
                break;
 
                /*
@@ -1941,11 +1988,11 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                        uint8_t digest[16];
 
                        FR_MD5_CTX      context;
-                       fr_MD5Init(&context);
-                       fr_MD5Update(&context, packet->data, packet->data_len);
-                       fr_MD5Update(&context, (const uint8_t *) secret,
+                       fr_md5_init(&context);
+                       fr_md5_update(&context, packet->data, packet->data_len);
+                       fr_md5_update(&context, (uint8_t const *) secret,
                                     strlen(secret));
-                       fr_MD5Final(digest, &context);
+                       fr_md5_final(digest, &context);
 
                        memcpy(hdr->vector, digest, AUTH_VECTOR_LEN);
                        memcpy(packet->vector, digest, AUTH_VECTOR_LEN);
@@ -1960,11 +2007,11 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
  * @brief Reply to the request.  Also attach
  *     reply attribute value pairs and any user message provided.
  */
-int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
-            const char *secret)
+int rad_send(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+            char const *secret)
 {
        VALUE_PAIR              *reply;
-       const char              *what;
+       char const              *what;
        char                    ip_src_buffer[128];
        char                    ip_dst_buffer[128];
 
@@ -1975,7 +2022,7 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                return 0;
        }
 
-       if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+       if (is_radius_code(packet->code)) {
                what = fr_packet_codes[packet->code];
        } else {
                what = "Reply";
@@ -2005,14 +2052,12 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                 *      the VP list again only for debugging.
                 */
        } else if (fr_debug_flag) {
-               DEBUG("Sending %s of id %d from %s port %u to %s port %u\n", what,
-                     packet->id,
-                     inet_ntop(packet->src_ipaddr.af,
-                               &packet->src_ipaddr.ipaddr,
+               DEBUG("Sending %s Id %d from %s:%u to %s:%u\n", what,
+                     packet->id,
+                     inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr,
                                ip_src_buffer, sizeof(ip_src_buffer)),
                      packet->src_port,
-                     inet_ntop(packet->dst_ipaddr.af,
-                               &packet->dst_ipaddr.ipaddr,
+                     inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.ipaddr,
                                ip_dst_buffer, sizeof(ip_dst_buffer)),
                      packet->dst_port);
 
@@ -2027,6 +2072,24 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
        if ((fr_debug_flag > 3) && fr_log_fp) rad_print_hex(packet);
 #endif
 
+#ifdef WITH_TCP
+       /*
+        *      If the socket is TCP, call write().  Calling sendto()
+        *      is allowed on some platforms, but it's not nice.  Even
+        *      worse, if UDPFROMTO is defined, we *can't* use it on
+        *      TCP sockets.  So... just call write().
+        */
+       if (packet->proto == IPPROTO_TCP) {
+               ssize_t rcode;
+
+               rcode = write(packet->sockfd, packet->data, packet->data_len);
+               if (rcode >= 0) return rcode;
+
+               fr_strerror_printf("sendto failed: %s", fr_syserror(errno));
+               return -1;
+       }
+#endif
+
        /*
         *      And send it on it's way.
         */
@@ -2045,7 +2108,7 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
  *
  *     http://www.cs.rice.edu/~dwallach/pub/crosby-timing2009.pdf
  */
-int rad_digest_cmp(const uint8_t *a, const uint8_t *b, size_t length)
+int rad_digest_cmp(uint8_t const *a, uint8_t const *b, size_t length)
 {
        int result = 0;
        size_t i;
@@ -2062,7 +2125,7 @@ int rad_digest_cmp(const uint8_t *a, const uint8_t *b, size_t length)
  * @brief Validates the requesting client NAS.  Calculates the
  *     Request Authenticator based on the clients private key.
  */
-static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret)
+static int calc_acctdigest(RADIUS_PACKET *packet, char const *secret)
 {
        uint8_t         digest[AUTH_VECTOR_LEN];
        FR_MD5_CTX              context;
@@ -2078,10 +2141,10 @@ static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret)
        /*
         *  MD5(packet + secret);
         */
-       fr_MD5Init(&context);
-       fr_MD5Update(&context, packet->data, packet->data_len);
-       fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
-       fr_MD5Final(digest, &context);
+       fr_md5_init(&context);
+       fr_md5_update(&context, packet->data, packet->data_len);
+       fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
+       fr_md5_final(digest, &context);
 
        /*
         *      Return 0 if OK, 2 if not OK.
@@ -2096,7 +2159,7 @@ static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret)
  *     Response Authenticator based on the clients private key.
  */
 static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original,
-                           const char *secret)
+                           char const *secret)
 {
        uint8_t         calc_digest[AUTH_VECTOR_LEN];
        FR_MD5_CTX              context;
@@ -2116,10 +2179,10 @@ static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original,
        /*
         *  MD5(packet + secret);
         */
-       fr_MD5Init(&context);
-       fr_MD5Update(&context, packet->data, packet->data_len);
-       fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
-       fr_MD5Final(calc_digest, &context);
+       fr_md5_init(&context);
+       fr_md5_update(&context, packet->data, packet->data_len);
+       fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
+       fr_md5_final(calc_digest, &context);
 
        /*
         *  Copy the packet's vector back to the packet.
@@ -2137,10 +2200,10 @@ static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original,
 /**
  * @brief Check if a set of RADIUS formatted TLVs are OK.
  */
-int rad_tlv_ok(const uint8_t *data, size_t length,
+int rad_tlv_ok(uint8_t const *data, size_t length,
               size_t dv_type, size_t dv_length)
 {
-       const uint8_t *end = data + length;
+       uint8_t const *end = data + length;
 
        if ((dv_length > 2) || (dv_type == 0) || (dv_type > 4)) {
                fr_strerror_printf("rad_tlv_ok: Invalid arguments");
@@ -2221,22 +2284,27 @@ int rad_tlv_ok(const uint8_t *data, size_t length,
 }
 
 
-/**
- * @brief See if the data pointed to by PTR is a valid RADIUS packet.
+/** See if the data pointed to by PTR is a valid RADIUS packet.
+ *
+ * Packet is not 'const * const' because we may update data_len, if there's more data
+ * in the UDP packet than in the RADIUS packet.
  *
- *     packet is not 'const * const' because we may update data_len,
- *     if there's more data in the UDP packet than in the RADIUS packet.
+ * @param packet to check
+ * @param flags to control decoding
+ * @param reason if not NULL, will have the failure reason written to where it points.
+ * @return bool, true on success, false on failure.
  */
-int rad_packet_ok(RADIUS_PACKET *packet, int flags)
+bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
 {
        uint8_t                 *attr;
        size_t                  totallen;
        int                     count;
        radius_packet_t         *hdr;
        char                    host_ipaddr[128];
-       int                     require_ma = 0;
-       int                     seen_ma = 0;
-       int                     num_attributes;
+       bool                    require_ma = false;
+       bool                    seen_ma = false;
+       uint32_t                num_attributes;
+       decode_fail_t           failure = DECODE_FAIL_NONE;
 
        /*
         *      Check for packets smaller than the packet header.
@@ -2251,7 +2319,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
                                     packet->data_len, AUTH_HDR_LEN);
-               return 0;
+               failure = DECODE_FAIL_MIN_LENGTH_PACKET;
+               goto finish;
        }
 
 
@@ -2274,19 +2343,20 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
                           hdr->code);
-               return 0;
+               failure = DECODE_FAIL_UNKNOWN_PACKET_CODE;
+               goto finish;
        }
 
        /*
         *      Message-Authenticator is required in Status-Server
         *      packets, otherwise they can be trivially forged.
         */
-       if (hdr->code == PW_STATUS_SERVER) require_ma = 1;
+       if (hdr->code == PW_CODE_STATUS_SERVER) require_ma = true;
 
        /*
         *      It's also required if the caller asks for it.
         */
-       if (flags) require_ma = 1;
+       if (flags) require_ma = true;
 
        /*
         *      Repeat the length checks.  This time, instead of
@@ -2305,7 +2375,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
                                     totallen, AUTH_HDR_LEN);
-               return 0;
+               failure = DECODE_FAIL_MIN_LENGTH_FIELD;
+               goto finish;
        }
 
        /*
@@ -2337,7 +2408,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
                                     packet->data_len, totallen);
-               return 0;
+               failure = DECODE_FAIL_MIN_LENGTH_MISMATCH;
+               goto finish;
        }
 
        /*
@@ -2381,7 +2453,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                   inet_ntop(packet->src_ipaddr.af,
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)));
-                       return 0;
+                       failure = DECODE_FAIL_HEADER_OVERFLOW;
+                       goto finish;
                }
 
                /*
@@ -2392,20 +2465,22 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                   inet_ntop(packet->src_ipaddr.af,
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)));
-                       return 0;
+                       failure = DECODE_FAIL_INVALID_ATTRIBUTE;
+                       goto finish;
                }
 
                /*
                 *      Attributes are at LEAST as long as the ID & length
                 *      fields.  Anything shorter is an invalid attribute.
                 */
-                       if (attr[1] < 2) {
+               if (attr[1] < 2) {
                        fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute %u too short",
                                   inet_ntop(packet->src_ipaddr.af,
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)),
                                   attr[0]);
-                       return 0;
+                       failure = DECODE_FAIL_ATTRIBUTE_TOO_SHORT;
+                       goto finish;
                }
 
                /*
@@ -2418,7 +2493,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)),
                                           attr[0]);
-                       return 0;
+                       failure = DECODE_FAIL_ATTRIBUTE_OVERFLOW;
+                       goto finish;
                }
 
                /*
@@ -2433,7 +2509,7 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                         *      a Message-Authenticator.
                         */
                case PW_EAP_MESSAGE:
-                       require_ma = 1;
+                       require_ma = true;
                        break;
 
                case PW_MESSAGE_AUTHENTICATOR:
@@ -2443,9 +2519,10 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                                     &packet->src_ipaddr.ipaddr,
                                                     host_ipaddr, sizeof(host_ipaddr)),
                                           attr[1] - 2);
-                               return 0;
+                               failure = DECODE_FAIL_MA_INVALID_LENGTH;
+                               goto finish;
                        }
-                       seen_ma = 1;
+                       seen_ma = true;
                        break;
                }
 
@@ -2470,7 +2547,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)));
-               return 0;
+               failure = DECODE_FAIL_ATTRIBUTE_UNDERFLOW;
+               goto finish;
        }
 
        /*
@@ -2485,7 +2563,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
                           num_attributes, fr_max_attributes);
-               return 0;
+               failure = DECODE_FAIL_TOO_MANY_ATTRIBUTES;
+               goto finish;
        }
 
        /*
@@ -2499,12 +2578,13 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
         *      Similarly, Status-Server packets MUST contain
         *      Message-Authenticator attributes.
         */
-       if (require_ma && ! seen_ma) {
+       if (require_ma && !seen_ma) {
                fr_strerror_printf("WARNING: Insecure packet from host %s:  Packet does not contain required Message-Authenticator attribute",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)));
-               return 0;
+               failure = DECODE_FAIL_MA_MISSING;
+               goto finish;
        }
 
        /*
@@ -2514,7 +2594,13 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
        packet->id = hdr->id;
        memcpy(packet->vector, hdr->vector, AUTH_VECTOR_LEN);
 
-       return 1;
+
+       finish:
+
+       if (reason) {
+               *reason = failure;
+       }
+       return (failure == DECODE_FAIL_NONE);
 }
 
 
@@ -2531,7 +2617,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
        /*
         *      Allocate the new request data structure
         */
-       packet = rad_alloc(NULL, 0);
+       packet = rad_alloc(NULL, false);
        if (!packet) {
                fr_strerror_printf("out of memory");
                return NULL;
@@ -2542,15 +2628,15 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
                flags &= ~0x02;
        }
 
-       data_len = rad_recvfrom(fd, &packet->data, sock_flags,
-                                       &packet->src_ipaddr, &packet->src_port,
-                                       &packet->dst_ipaddr, &packet->dst_port);
+       data_len = rad_recvfrom(fd, packet, sock_flags,
+                               &packet->src_ipaddr, &packet->src_port,
+                               &packet->dst_ipaddr, &packet->dst_port);
 
        /*
         *      Check for socket errors.
         */
        if (data_len < 0) {
-               fr_strerror_printf("Error receiving packet: %s", strerror(errno));
+               fr_strerror_printf("Error receiving packet: %s", fr_syserror(errno));
                /* packet->data is NULL */
                rad_free(&packet);
                return NULL;
@@ -2563,7 +2649,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
         *      packet.
         */
        if (packet->data_len > MAX_PACKET_LEN) {
-               fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes.");
+               fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes");
                /* packet->data is NULL */
                rad_free(&packet);
                return NULL;
@@ -2576,7 +2662,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
         *      packet->data == NULL
         */
        if ((packet->data_len == 0) || !packet->data) {
-               fr_strerror_printf("Empty packet: Socket is not ready.");
+               fr_strerror_printf("Empty packet: Socket is not ready");
                rad_free(&packet);
                return NULL;
        }
@@ -2584,7 +2670,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
        /*
         *      See if it's a well-formed RADIUS packet.
         */
-       if (!rad_packet_ok(packet, flags)) {
+       if (!rad_packet_ok(packet, flags, NULL)) {
                rad_free(&packet);
                return NULL;
        }
@@ -2606,25 +2692,36 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
        packet->vps = NULL;
 
        if (fr_debug_flag) {
-               char host_ipaddr[128];
+               char src_ipaddr[128];
+               char dst_ipaddr[128];
 
-               if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
-                       DEBUG("rad_recv: %s packet from host %s port %d",
+               if (is_radius_code(packet->code)) {
+                       DEBUG("Received %s Id %d from %s:%d to %s:%d length %d\n",
                              fr_packet_codes[packet->code],
+                             packet->id,
                              inet_ntop(packet->src_ipaddr.af,
                                        &packet->src_ipaddr.ipaddr,
-                                       host_ipaddr, sizeof(host_ipaddr)),
-                             packet->src_port);
+                                       src_ipaddr, sizeof(src_ipaddr)),
+                             packet->src_port,
+                             inet_ntop(packet->dst_ipaddr.af,
+                                       &packet->dst_ipaddr.ipaddr,
+                                       dst_ipaddr, sizeof(dst_ipaddr)),
+                             packet->dst_port,
+                             (int) packet->data_len);
                } else {
-                       DEBUG("rad_recv: Packet from host %s port %d code=%d",
+                       DEBUG("Received code %d Id %d from %s:%d to %s:%d length %d\n",
+                             packet->code,
+                             packet->id,
                              inet_ntop(packet->src_ipaddr.af,
                                        &packet->src_ipaddr.ipaddr,
-                                       host_ipaddr, sizeof(host_ipaddr)),
+                                       src_ipaddr, sizeof(src_ipaddr)),
                              packet->src_port,
-                             packet->code);
+                             inet_ntop(packet->dst_ipaddr.af,
+                                       &packet->dst_ipaddr.ipaddr,
+                                       dst_ipaddr, sizeof(dst_ipaddr)),
+                             packet->dst_port,
+                             (int) packet->data_len);
                }
-               DEBUG(", id=%d, length=%d\n",
-                     packet->id, (int) packet->data_len);
        }
 
 #ifndef NDEBUG
@@ -2640,7 +2737,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
  *     (and Message-Authenticator if present) of a packet.
  */
 int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
-              const char *secret)
+              char const *secret)
 {
        uint8_t                 *ptr;
        int                     length;
@@ -2676,37 +2773,36 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                        default:
                                break;
 
-                       case PW_ACCOUNTING_RESPONSE:
+                       case PW_CODE_ACCOUNTING_RESPONSE:
                                if (original &&
-                                   (original->code == PW_STATUS_SERVER)) {
+                                   (original->code == PW_CODE_STATUS_SERVER)) {
                                        goto do_ack;
                                }
 
-                       case PW_ACCOUNTING_REQUEST:
-                       case PW_DISCONNECT_REQUEST:
-                       case PW_DISCONNECT_ACK:
-                       case PW_DISCONNECT_NAK:
-                       case PW_COA_REQUEST:
-                       case PW_COA_ACK:
-                       case PW_COA_NAK:
-                               memset(packet->data + 4, 0, AUTH_VECTOR_LEN);
+                       case PW_CODE_ACCOUNTING_REQUEST:
+                       case PW_CODE_DISCONNECT_REQUEST:
+                       case PW_CODE_COA_REQUEST:
+                               memset(packet->data + 4, 0, AUTH_VECTOR_LEN);
                                break;
 
                        do_ack:
-                       case PW_AUTHENTICATION_ACK:
-                       case PW_AUTHENTICATION_REJECT:
-                       case PW_ACCESS_CHALLENGE:
+                       case PW_CODE_ACCESS_ACCEPT:
+                       case PW_CODE_ACCESS_REJECT:
+                       case PW_CODE_ACCESS_CHALLENGE:
+                       case PW_CODE_DISCONNECT_ACK:
+                       case PW_CODE_DISCONNECT_NAK:
+                       case PW_CODE_COA_ACK:
+                       case PW_CODE_COA_NAK:
                                if (!original) {
-                                       fr_strerror_printf("ERROR: Cannot validate Message-Authenticator in response packet without a request packet.");
+                                       fr_strerror_printf("ERROR: Cannot validate Message-Authenticator in response packet without a request packet");
                                        return -1;
                                }
                                memcpy(packet->data + 4, original->vector, AUTH_VECTOR_LEN);
                                break;
                        }
 
-                       fr_hmac_md5(packet->data, packet->data_len,
-                                   (const uint8_t *) secret, strlen(secret),
-                                   calc_auth_vector);
+                       fr_hmac_md5(calc_auth_vector, packet->data, packet->data_len,
+                                   (uint8_t const *) secret, strlen(secret));
                        if (rad_digest_cmp(calc_auth_vector, msg_auth_vector,
                                   sizeof(calc_auth_vector)) != 0) {
                                char buffer[32];
@@ -2749,21 +2845,21 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
        /*
         *      Calculate and/or verify Request or Response Authenticator.
         */
-       switch(packet->code) {
+       switch (packet->code) {
                int rcode;
                char buffer[32];
 
-               case PW_AUTHENTICATION_REQUEST:
-               case PW_STATUS_SERVER:
+               case PW_CODE_ACCESS_REQUEST:
+               case PW_CODE_STATUS_SERVER:
                        /*
                         *      The authentication vector is random
                         *      nonsense, invented by the client.
                         */
                        break;
 
-               case PW_COA_REQUEST:
-               case PW_DISCONNECT_REQUEST:
-               case PW_ACCOUNTING_REQUEST:
+               case PW_CODE_COA_REQUEST:
+               case PW_CODE_DISCONNECT_REQUEST:
+               case PW_CODE_ACCOUNTING_REQUEST:
                        if (calc_acctdigest(packet, secret) > 1) {
                                fr_strerror_printf("Received %s packet "
                                           "from client %s with invalid Request Authenticator!  (Shared secret is incorrect.)",
@@ -2776,14 +2872,14 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                        break;
 
                        /* Verify the reply digest */
-               case PW_AUTHENTICATION_ACK:
-               case PW_AUTHENTICATION_REJECT:
-               case PW_ACCESS_CHALLENGE:
-               case PW_ACCOUNTING_RESPONSE:
-               case PW_DISCONNECT_ACK:
-               case PW_DISCONNECT_NAK:
-               case PW_COA_ACK:
-               case PW_COA_NAK:
+               case PW_CODE_ACCESS_ACCEPT:
+               case PW_CODE_ACCESS_REJECT:
+               case PW_CODE_ACCESS_CHALLENGE:
+               case PW_CODE_ACCOUNTING_RESPONSE:
+               case PW_CODE_DISCONNECT_ACK:
+               case PW_CODE_DISCONNECT_NAK:
+               case PW_CODE_COA_ACK:
+               case PW_CODE_COA_NAK:
                        rcode = calc_replydigest(packet, original, secret);
                        if (rcode > 1) {
                                fr_strerror_printf("Received %s packet "
@@ -2812,24 +2908,73 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
 }
 
 
-static ssize_t data2vp(const RADIUS_PACKET *packet,
-                      const RADIUS_PACKET *original,
-                      const char *secret,
-                      const DICT_ATTR *da, const uint8_t *start,
-                      const size_t attrlen, const size_t packetlen,
-                      VALUE_PAIR **pvp);
+/**
+ * @brief convert a "concatenated" attribute to one long VP.
+ */
+static ssize_t data2vp_concat(TALLOC_CTX *ctx,
+                             DICT_ATTR const *da, uint8_t const *start,
+                             size_t const packetlen, VALUE_PAIR **pvp)
+{
+       size_t total;
+       uint8_t attr;
+       uint8_t const *ptr = start;
+       uint8_t const *end = start + packetlen;
+       uint8_t *p;
+       VALUE_PAIR *vp;
+
+       total = 0;
+       attr = ptr[0];
+
+       /*
+        *      The packet has already been sanity checked, so we
+        *      don't care about walking off of the end of it.
+        */
+       while (ptr < end) {
+               total += ptr[1] - 2;
+
+               ptr += ptr[1];
+
+               /*
+                *      Attributes MUST be consecutive.
+                */
+               if (ptr[0] != attr) break;
+       }
+
+       vp = pairalloc(ctx, da);
+       if (!vp) return -1;
+
+       vp->length = total;
+       vp->vp_octets = p = talloc_array(vp, uint8_t, vp->length);
+       if (!p) {
+               pairfree(&vp);
+               return -1;
+       }
+
+       total = 0;
+       ptr = start;
+       while (total < vp->length) {
+               memcpy(p, ptr + 2, ptr[1] - 2);
+               p += ptr[1] - 2;
+               total += ptr[1] - 2;
+               ptr += ptr[1];
+       }
+
+       *pvp = vp;
+       return ptr - start;
+}
+
 
 /**
  * @brief convert TLVs to one or more VPs
  */
-static ssize_t data2vp_tlvs(const RADIUS_PACKET *packet,
-                           const RADIUS_PACKET *original,
-                           const char *secret, const DICT_ATTR *da,
-                           const uint8_t *start, size_t length,
+static ssize_t data2vp_tlvs(TALLOC_CTX *ctx,
+                           RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+                           char const *secret, DICT_ATTR const *da,
+                           uint8_t const *start, size_t length,
                            VALUE_PAIR **pvp)
 {
-       const uint8_t *data = start;
-       const DICT_ATTR *child;
+       uint8_t const *data = start;
+       DICT_ATTR const *child;
        VALUE_PAIR *head, **tail;
 
        if (length < 3) return -1; /* type, length, value */
@@ -2863,14 +3008,14 @@ static ssize_t data2vp_tlvs(const RADIUS_PACKET *packet,
                                return -1;
                        }
 
-                       child = dict_attrunknown(my_attr, my_vendor, TRUE);
+                       child = dict_attrunknown(my_attr, my_vendor, true);
                        if (!child) {
                                pairfree(&head);
                                return -1;
                        }
                }
 
-               tlv_len = data2vp(packet, original, secret, child,
+               tlv_len = data2vp(ctx, packet, original, secret, child,
                                  data + 2, data[1] - 2, data[1] - 2, tail);
                if (tlv_len < 0) {
                        pairfree(&head);
@@ -2889,22 +3034,22 @@ static ssize_t data2vp_tlvs(const RADIUS_PACKET *packet,
  *
  *     "length" can be LONGER than just this sub-vsa
  */
-static ssize_t data2vp_vsa(const RADIUS_PACKET *packet,
-                          const RADIUS_PACKET *original,
-                          const char *secret, DICT_VENDOR *dv,
-                          const uint8_t *data, size_t length,
+static ssize_t data2vp_vsa(TALLOC_CTX *ctx, RADIUS_PACKET *packet,
+                          RADIUS_PACKET const *original,
+                          char const *secret, DICT_VENDOR *dv,
+                          uint8_t const *data, size_t length,
                           VALUE_PAIR **pvp)
 {
        unsigned int attribute;
        ssize_t attrlen, my_len;
-       const DICT_ATTR *da;
+       DICT_ATTR const *da;
 
 #ifndef NDEBUG
        if (length <= (dv->type + dv->length)) {
                fr_strerror_printf("data2vp_vsa: Failure to call rad_tlv_ok");
                return -1;
        }
-#endif 
+#endif
 
        switch (dv->type) {
        case 4:
@@ -2958,10 +3103,10 @@ static ssize_t data2vp_vsa(const RADIUS_PACKET *packet,
         *      See if the VSA is known.
         */
        da = dict_attrbyvalue(attribute, dv->vendorpec);
-       if (!da) da = dict_attrunknown(attribute, dv->vendorpec, TRUE);
+       if (!da) da = dict_attrunknown(attribute, dv->vendorpec, true);
        if (!da) return -1;
 
-       my_len = data2vp(packet, original, secret, da,
+       my_len = data2vp(ctx, packet, original, secret, da,
                         data + dv->type + dv->length,
                         attrlen - (dv->type + dv->length),
                         attrlen - (dv->type + dv->length),
@@ -2973,22 +3118,113 @@ static ssize_t data2vp_vsa(const RADIUS_PACKET *packet,
 
 
 /**
+ * @brief Convert a fragmented extended attr to a VP
+ *
+ *     Format is:
+ *
+ *     attr
+ *     length
+ *     extended-attr
+ *     flag
+ *     data...
+ *
+ *     But for the first fragment, we get passed a pointer to the
+ *     "extended-attr".
+ */
+static ssize_t data2vp_extended(TALLOC_CTX *ctx, RADIUS_PACKET *packet,
+                               RADIUS_PACKET const *original,
+                               char const *secret, DICT_ATTR const *da,
+                               uint8_t const *data,
+                               size_t attrlen, size_t packetlen,
+                               VALUE_PAIR **pvp)
+{
+       ssize_t rcode;
+       size_t fraglen;
+       uint8_t *head, *tail;
+       uint8_t const *frag, *end;
+       uint8_t const *attr;
+       int fragments;
+       bool last_frag;
+
+       if (attrlen < 3) return -1;
+
+       /*
+        *      Calculate the length of all of the fragments.  For
+        *      now, they MUST be contiguous in the packet, and they
+        *      MUST be all of the same TYPE and EXTENDED-TYPE
+        */
+       attr = data - 2;
+       fraglen = attrlen - 2;
+       frag = data + attrlen;
+       end = data + packetlen;
+       fragments = 1;
+       last_frag = false;
+
+       while (frag < end) {
+               if (last_frag ||
+                   (frag[0] != attr[0]) ||
+                   (frag[1] < 4) ||                   /* too short for long-extended */
+                   (frag[2] != attr[2]) ||
+                   ((frag + frag[1]) > end)) {         /* overflow */
+                       end = frag;
+                       break;
+               }
+
+               last_frag = ((frag[3] & 0x80) == 0);
+
+               fraglen += frag[1] - 4;
+               frag += frag[1];
+               fragments++;
+       }
+
+       head = tail = malloc(fraglen);
+       if (!head) return -1;
+
+       VP_TRACE("Fragments %d, total length %d\n", fragments, (int) fraglen);
+
+       /*
+        *      And again, but faster and looser.
+        *
+        *      We copy the first fragment, followed by the rest of
+        *      the fragments.
+        */
+       frag = attr;
+
+       while (fragments >  0) {
+               memcpy(tail, frag + 4, frag[1] - 4);
+               tail += frag[1] - 4;
+               frag += frag[1];
+               fragments--;
+       }
+
+       VP_HEXDUMP("long-extended fragments", head, fraglen);
+
+       rcode = data2vp(ctx, packet, original, secret, da,
+                       head, fraglen, fraglen, pvp);
+       free(head);
+       if (rcode < 0) return rcode;
+
+       return end - data;
+}
+
+/**
  * @brief Convert a Vendor-Specific WIMAX to vps
  *
  *     Called ONLY for Vendor-Specific
  */
-static ssize_t data2vp_wimax(const RADIUS_PACKET *packet,
-                            const RADIUS_PACKET *original,
-                            const char *secret, uint32_t vendor,
-                            const uint8_t *data,
+static ssize_t data2vp_wimax(TALLOC_CTX *ctx,
+                            RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+                            char const *secret, uint32_t vendor,
+                            uint8_t const *data,
                             size_t attrlen, size_t packetlen,
                             VALUE_PAIR **pvp)
 {
        ssize_t rcode;
        size_t fraglen;
+       bool last_frag;
        uint8_t *head, *tail;
-       const uint8_t *frag, *end;
-       const DICT_ATTR *child;
+       uint8_t const *frag, *end;
+       DICT_ATTR const *child;
 
        if (attrlen < 8) return -1;
 
@@ -2998,7 +3234,7 @@ static ssize_t data2vp_wimax(const RADIUS_PACKET *packet,
        if (!child) return -1;
 
        if ((data[6] & 0x80) == 0) {
-               rcode = data2vp(packet, original, secret, child,
+               rcode = data2vp(ctx, packet, original, secret, child,
                                data + 7, data[5] - 3, data[5] - 3,
                                pvp);
                if (rcode < 0) return -1;
@@ -3016,10 +3252,9 @@ static ssize_t data2vp_wimax(const RADIUS_PACKET *packet,
        fraglen = data[5] - 3;
        frag = data + attrlen;
        end = data + packetlen;
+       last_frag = false;
 
        while (frag < end) {
-               int last_frag = FALSE;
-
                if (last_frag ||
                    (frag[0] != PW_VENDOR_SPECIFIC) ||
                    (frag[1] < 9) ||                   /* too short for wimax */
@@ -3063,7 +3298,7 @@ static ssize_t data2vp_wimax(const RADIUS_PACKET *packet,
 
        VP_HEXDUMP("wimax fragments", head, fraglen);
 
-       rcode = data2vp(packet, original, secret, child,
+       rcode = data2vp(ctx, packet, original, secret, child,
                        head, fraglen, fraglen, pvp);
        free(head);
        if (rcode < 0) return rcode;
@@ -3075,9 +3310,9 @@ static ssize_t data2vp_wimax(const RADIUS_PACKET *packet,
 /**
  * @brief Convert a top-level VSA to one or more VPs
  */
-static ssize_t data2vp_vsas(const RADIUS_PACKET *packet,
-                           const RADIUS_PACKET *original,
-                           const char *secret, const uint8_t *data,
+static ssize_t data2vp_vsas(TALLOC_CTX *ctx, RADIUS_PACKET *packet,
+                           RADIUS_PACKET const *original,
+                           char const *secret, uint8_t const *data,
                            size_t attrlen, size_t packetlen,
                            VALUE_PAIR **pvp)
 {
@@ -3100,11 +3335,11 @@ static ssize_t data2vp_vsas(const RADIUS_PACKET *packet,
         *      WiMAX craziness
         */
        if ((vendor == VENDORPEC_WIMAX) && dv->flags) {
-               rcode = data2vp_wimax(packet, original, secret, vendor,
+               rcode = data2vp_wimax(ctx, packet, original, secret, vendor,
                                      data, attrlen, packetlen, pvp);
                return rcode;
        }
-       
+
        /*
         *      VSAs should normally be in TLV format.
         */
@@ -3124,8 +3359,8 @@ static ssize_t data2vp_vsas(const RADIUS_PACKET *packet,
 
        while (attrlen > 0) {
                ssize_t vsa_len;
-               
-               vsa_len = data2vp_vsa(packet, original, secret, dv,
+
+               vsa_len = data2vp_vsa(ctx, packet, original, secret, dv,
                                      data, attrlen, tail);
                if (vsa_len < 0) {
                        pairfree(&head);
@@ -3154,24 +3389,29 @@ static ssize_t data2vp_vsas(const RADIUS_PACKET *packet,
  *
  * @return -1 on error, or "length".
  */
-static ssize_t data2vp(const RADIUS_PACKET *packet,
-                      const RADIUS_PACKET *original,
-                      const char *secret,
-                      const DICT_ATTR *da, const uint8_t *start,
-                      const size_t attrlen, const size_t packetlen,
-                      VALUE_PAIR **pvp)
+ssize_t data2vp(TALLOC_CTX *ctx,
+               RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+               char const *secret,
+               DICT_ATTR const *da, uint8_t const *start,
+               size_t const attrlen, size_t const packetlen,
+               VALUE_PAIR **pvp)
 {
-       int tag = 0;
+       int8_t tag = TAG_NONE;
        size_t datalen;
        ssize_t rcode;
        uint32_t vendor;
-       const DICT_ATTR *child;
+       DICT_ATTR const *child;
        DICT_VENDOR *dv;
        VALUE_PAIR *vp;
-       const uint8_t *data = start;
+       uint8_t const *data = start;
+       char *p;
        uint8_t buffer[256];
 
-       if (!da || (attrlen > 253) || (attrlen > packetlen) ||
+       /*
+        *      FIXME: Attrlen can be larger than 253 for extended attrs!
+        */
+       if (!da || (attrlen > packetlen) ||
+           ((attrlen > 253) && (attrlen != packetlen)) ||
            (attrlen > 128*1024)) {
                fr_strerror_printf("data2vp: invalid arguments");
                return -1;
@@ -3195,29 +3435,46 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
                        return 0;
                }
 
-               data = (const uint8_t *) "";
-               datalen = 1;
+#ifndef NDEBUG
+               /*
+                *      Hacks for Coverity.  Editing the dictionary
+                *      will break assumptions about CUI.  We know
+                *      this, but Coverity doesn't.
+                */
+               if (da->type != PW_TYPE_OCTETS) return -1;
+#endif
+
+               data = NULL;
+               datalen = 0;
+               goto alloc_cui; /* skip everything */
        }
 
        /*
         *      Hacks for tags.  If the attribute is capable of
         *      encoding a tag, and there's room for the tag, and
-        *      there is a tag, or it's encryted with Tunnel-Password,
+        *      there is a tag, or it's encrypted with Tunnel-Password,
         *      then decode the tag.
         */
        if (da->flags.has_tag && (datalen > 1) &&
            ((data[0] < 0x20) ||
             (da->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD))) {
+               /*
+                *      Only "short" attributes can be encrypted.
+                */
+               if (datalen >= sizeof(buffer)) return -1;
 
-               if ((da->type == PW_TYPE_STRING) ||
-                   (da->type == PW_TYPE_OCTETS)) {
+               if (da->type == PW_TYPE_STRING) {
                        memcpy(buffer, data + 1, datalen - 1);
                        tag = data[0];
                        datalen -= 1;
-               } else {
+
+               } else if (da->type == PW_TYPE_INTEGER) {
                        memcpy(buffer, data, attrlen);
                        tag = buffer[0];
                        buffer[0] = 0;
+
+               } else {
+                       return -1; /* only string and integer can have tags */
                }
 
                data = buffer;
@@ -3227,7 +3484,18 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
         *      Decrypt the attribute.
         */
        if (secret && packet && (da->flags.encrypt != FLAG_ENCRYPT_NONE)) {
-               if (data == start) memcpy(buffer, data, attrlen);
+               /*
+                *      Encrypted attributes can only exist for the
+                *      old-style format.  Extended attributes CANNOT
+                *      be encrypted.
+                */
+               if (attrlen > 253) {
+                       return -1;
+               }
+
+               if (data == start) {
+                       memcpy(buffer, data, attrlen);
+               }
                data = buffer;
 
                switch (da->flags.encrypt) { /* can't be tagged */
@@ -3291,11 +3559,14 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
        switch (da->type) {
        case PW_TYPE_STRING:
        case PW_TYPE_OCTETS:
+               break;
+
        case PW_TYPE_ABINARY:
+               if (datalen > sizeof(vp->vp_filter)) goto raw;
                break;
 
        case PW_TYPE_INTEGER:
-       case PW_TYPE_IPADDR:
+       case PW_TYPE_IPV4_ADDR:
        case PW_TYPE_DATE:
        case PW_TYPE_SIGNED:
                if (datalen != 4) goto raw;
@@ -3306,11 +3577,11 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
                if (datalen != 8) goto raw;
                break;
 
-       case PW_TYPE_IPV6ADDR:
+       case PW_TYPE_IPV6_ADDR:
                if (datalen != 16) goto raw;
                break;
 
-       case PW_TYPE_IPV6PREFIX:
+       case PW_TYPE_IPV6_PREFIX:
                if ((datalen < 2) || (datalen > 18)) goto raw;
                if (data[1] > 128) goto raw;
                break;
@@ -3327,13 +3598,13 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
                if (datalen != 6) goto raw;
                break;
 
-       case PW_TYPE_COMBO_IP:
+       case PW_TYPE_IP_ADDR:
                if (datalen == 4) {
                        child = dict_attrbytype(da->attr, da->vendor,
-                                               PW_TYPE_IPADDR);
+                                               PW_TYPE_IPV4_ADDR);
                } else if (datalen == 16) {
                        child = dict_attrbytype(da->attr, da->vendor,
-                                            PW_TYPE_IPV6ADDR);
+                                            PW_TYPE_IPV6_ADDR);
                } else {
                        goto raw;
                }
@@ -3341,7 +3612,7 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
                da = child;     /* re-write it */
                break;
 
-       case PW_TYPE_IPV4PREFIX:
+       case PW_TYPE_IPV4_PREFIX:
                if (datalen != 6) goto raw;
                if ((data[1] & 0x3f) > 32) goto raw;
                break;
@@ -3363,7 +3634,7 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
                 *      the current attribute, and we ignore any extra
                 *      data after it.
                 */
-               rcode = data2vp(packet, original, secret, child,
+               rcode = data2vp(ctx, packet, original, secret, child,
                                data + 1, attrlen - 1, attrlen - 1, pvp);
                if (rcode < 0) goto raw;
                return 1 + rcode;
@@ -3372,7 +3643,28 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
                if (datalen < 3) goto raw; /* etype, flags, value */
 
                child = dict_attrbyparent(da, data[0], 0);
-               if (!child) goto raw;
+               if (!child) {
+                       if ((data[0] != PW_VENDOR_SPECIFIC) ||
+                           (datalen < (3 + 4 + 1))) {
+                               /* da->attr < 255, da->vendor == 0 */
+                               child = dict_attrunknown(data[0], da->attr * FR_MAX_VENDOR, true);
+                       } else {
+                               /*
+                                *      Try to find the VSA.
+                                */
+                               memcpy(&vendor, data + 3, 4);
+                               vendor = ntohl(vendor);
+
+                               if (vendor == 0) goto raw;
+
+                               child = dict_attrunknown(data[7], vendor | (da->attr * FR_MAX_VENDOR), true);
+                       }
+
+                       if (!child) {
+                               fr_strerror_printf("Internal sanity check %d", __LINE__);
+                               return -1;
+                       }
+               }
 
                /*
                 *      If there no more fragments, then the contents
@@ -3380,15 +3672,18 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
                 *
                 */
                if ((data[1] & 0x80) == 0) {
-                       rcode = data2vp(packet, original, secret, child,
+                       rcode = data2vp(ctx, packet, original, secret, child,
                                        data + 2, attrlen - 2, attrlen - 2,
                                        pvp);
                        if (rcode < 0) goto raw;
                        return 2 + rcode;
                }
 
-               fr_strerror_printf("Internal sanity check %d", __LINE__);
-               return -1;      /* TODO: fixme! */
+               /*
+                *      This requires a whole lot more work.
+                */
+               return data2vp_extended(ctx, packet, original, secret, child,
+                                       start, attrlen, packetlen, pvp);
 
        case PW_TYPE_EVS:
                if (datalen < 6) goto raw; /* vid, vtype, value */
@@ -3398,12 +3693,17 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
                memcpy(&vendor, data, 4);
                vendor = ntohl(vendor);
                dv = dict_vendorbyvalue(vendor);
-               if (!dv) goto raw;
-
-               child = dict_attrbyparent(da, data[5], vendor);
+               if (!dv) {
+                       child = dict_attrunknown(data[4], da->vendor | vendor, true);
+               } else {
+                       child = dict_attrbyparent(da, data[4], vendor);
+                       if (!child) {
+                               child = dict_attrunknown(data[4], da->vendor | vendor, true);
+                       }
+               }
                if (!child) goto raw;
 
-               rcode = data2vp(packet, original, secret, child,
+               rcode = data2vp(ctx, packet, original, secret, child,
                                data + 5, attrlen - 5, attrlen - 5, pvp);
                if (rcode < 0) goto raw;
                return 5 + rcode;
@@ -3414,7 +3714,7 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
                 *      attribute, OR they've already been grouped
                 *      into a contiguous memory buffer.
                 */
-               rcode = data2vp_tlvs(packet, original, secret, da,
+               rcode = data2vp_tlvs(ctx, packet, original, secret, da,
                                     data, attrlen, pvp);
                if (rcode < 0) goto raw;
                return rcode;
@@ -3424,7 +3724,7 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
                 *      VSAs can be WiMAX, in which case they don't
                 *      fit into one attribute.
                 */
-               rcode = data2vp_vsas(packet, original, secret,
+               rcode = data2vp_vsas(ctx, packet, original, secret,
                                     data, attrlen, packetlen, pvp);
                if (rcode < 0) goto raw;
                return rcode;
@@ -3436,12 +3736,21 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
                 *      therefore of type "octets", and will be
                 *      handled below.
                 */
-               da = dict_attrunknown(da->attr, da->vendor, TRUE);
+               da = dict_attrunknown(da->attr, da->vendor, true);
                if (!da) {
                        fr_strerror_printf("Internal sanity check %d", __LINE__);
                        return -1;
                }
-               tag = 0;
+               tag = TAG_NONE;
+#ifndef NDEBUG
+               /*
+                *      Fix for Coverity.
+                */
+               if (da->type != PW_TYPE_OCTETS) {
+                       dict_attr_free(&da);
+                       return -1;
+               }
+#endif
                break;
        }
 
@@ -3449,7 +3758,8 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
         *      And now that we've verified the basic type
         *      information, decode the actual data.
         */
-       vp = pairalloc(NULL, da);
+ alloc_cui:
+       vp = pairalloc(ctx, da);
        if (!vp) return -1;
 
        vp->length = datalen;
@@ -3457,22 +3767,29 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
 
        switch (da->type) {
        case PW_TYPE_STRING:
-               memcpy(vp->vp_strvalue, data, vp->length);
-               vp->vp_strvalue[vp->length] = '\0';
+               p = talloc_array(vp, char, vp->length + 1);
+               memcpy(p, data, vp->length);
+               p[vp->length] = '\0';
+               vp->vp_strvalue = p;
                break;
 
        case PW_TYPE_OCTETS:
+               pairmemcpy(vp, data, vp->length);
+               break;
+
        case PW_TYPE_ABINARY:
-               memcpy(vp->vp_octets, data, vp->length);
+               if (vp->length > sizeof(vp->vp_filter)) {
+                       vp->length = sizeof(vp->vp_filter);
+               }
+               memcpy(vp->vp_filter, data, vp->length);
                break;
 
        case PW_TYPE_BYTE:
-               vp->vp_integer = data[0];
+               vp->vp_byte = data[0];
                break;
 
-
        case PW_TYPE_SHORT:
-               vp->vp_integer = (data[0] << 8) | data[1];
+               vp->vp_short = (data[0] << 8) | data[1];
                break;
 
        case PW_TYPE_INTEGER:
@@ -3490,8 +3807,11 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
                vp->vp_date = ntohl(vp->vp_date);
                break;
 
+       case PW_TYPE_ETHERNET:
+               memcpy(&vp->vp_ether, data, 6);
+               break;
 
-       case PW_TYPE_IPADDR:
+       case PW_TYPE_IPV4_ADDR:
                memcpy(&vp->vp_ipaddr, data, 4);
                break;
 
@@ -3499,11 +3819,11 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
                memcpy(&vp->vp_ifid, data, 8);
                break;
 
-       case PW_TYPE_IPV6ADDR:
+       case PW_TYPE_IPV6_ADDR:
                memcpy(&vp->vp_ipv6addr, data, 16);
                break;
 
-       case PW_TYPE_IPV6PREFIX:
+       case PW_TYPE_IPV6_PREFIX:
                /*
                 *      FIXME: double-check that
                 *      (vp->vp_octets[1] >> 3) matches vp->length + 2
@@ -3515,9 +3835,9 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
                }
                break;
 
-       case PW_TYPE_IPV4PREFIX:
+       case PW_TYPE_IPV4_PREFIX:
                /* FIXME: do the same double-check as for IPv6Prefix */
-               memcpy(&vp->vp_ipv4prefix, buffer, sizeof(vp->vp_ipv4prefix));
+               memcpy(&vp->vp_ipv4prefix, data, vp->length);
 
                /*
                 *      /32 means "keep all bits".  Otherwise, mask
@@ -3528,12 +3848,12 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
 
                        memcpy(&addr, vp->vp_octets + 2, sizeof(addr));
                        mask = 1;
-                       mask <<= (32 - (buffer[1] & 0x3f));
+                       mask <<= (32 - (data[1] & 0x3f));
                        mask--;
                        mask = ~mask;
                        mask = htonl(mask);
                        addr &= mask;
-                       memcpy(vp->vp_octets + 2, &addr, sizeof(addr));
+                       memcpy(vp->vp_ipv4prefix + 2, &addr, sizeof(addr));
                }
                break;
 
@@ -3547,7 +3867,7 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
                fr_strerror_printf("Internal sanity check %d", __LINE__);
                return -1;
        }
-
+       vp->type = VT_DATA;
        *pvp = vp;
 
        return attrlen;
@@ -3557,15 +3877,15 @@ static ssize_t data2vp(const RADIUS_PACKET *packet,
 /**
  * @brief Create a "normal" VALUE_PAIR from the given data.
  */
-ssize_t rad_attr2vp(const RADIUS_PACKET *packet,
-                   const RADIUS_PACKET *original,
-                   const char *secret,
-                   const uint8_t *data, size_t length,
+ssize_t rad_attr2vp(TALLOC_CTX *ctx,
+                   RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+                   char const *secret,
+                   uint8_t const *data, size_t length,
                    VALUE_PAIR **pvp)
 {
        ssize_t rcode;
 
-       const DICT_ATTR *da;
+       DICT_ATTR const *da;
 
        if ((length < 2) || (data[1] < 2) || (data[1] > length)) {
                fr_strerror_printf("rad_attr2vp: Insufficient data");
@@ -3573,7 +3893,15 @@ ssize_t rad_attr2vp(const RADIUS_PACKET *packet,
        }
 
        da = dict_attrbyvalue(data[0], 0);
-       if (!da) da = dict_attrunknown(data[0], 0, TRUE);
+       if (!da) da = dict_attrunknown(data[0], 0, true);
+       if (!da) return -1;
+
+       /*
+        *      Pass the entire thing to the decoding function
+        */
+       if (da->flags.concat) {
+               return data2vp_concat(ctx, da, data, length, pvp);
+       }
 
        /*
         *      Note that we pass the entire length, not just the
@@ -3581,108 +3909,136 @@ ssize_t rad_attr2vp(const RADIUS_PACKET *packet,
         *      attributes may have the "continuation" bit set, and
         *      will thus be more than one attribute in length.
         */
-       rcode = data2vp(packet, original, secret, da,
+       rcode = data2vp(ctx, packet, original, secret, da,
                        data + 2, data[1] - 2, length - 2, pvp);
        if (rcode < 0) return rcode;
 
        return 2 + rcode;
 }
 
+fr_thread_local_setup(uint8_t *, rad_vp2data_buff);
 
-/**
- * @brief Converts data in network byte order to a VP
- * @return -1 on error, or the length of the data read
- */
-ssize_t  rad_data2vp(unsigned int attribute, unsigned int vendor,
-                    const uint8_t *data, size_t length,
-                    VALUE_PAIR **pvp)
-{
-       const DICT_ATTR *da;
-
-       if (!data || (length == 0) || !pvp) return -1;
-
-       da = dict_attrbyvalue(attribute, vendor);
-       if (!da) da = dict_attrunknown(attribute, vendor, TRUE);
-       if (!da) return -1;
-
-       return data2vp(NULL, NULL, NULL, da,
-                      data, length, length, pvp);
-}
-
-/**
- * @brief Converts vp_data to network byte order
+/** Converts vp_data to network byte order
+ *
+ * Provide a pointer to a buffer which contains the value of the VALUE_PAIR
+ * in an architecture independent format.
+ *
+ * The pointer is only guaranteed to be valid between calls to rad_vp2data, and so long
+ * as the source VALUE_PAIR is not freed.
+ *
+ * @param out where to write the pointer to the value.
+ * @param vp to get the value from.
  * @return -1 on error, or the length of the value
  */
-ssize_t rad_vp2data(const VALUE_PAIR *vp, uint8_t *out, size_t outlen)
+ssize_t rad_vp2data(uint8_t const **out, VALUE_PAIR const *vp)
 {
-       size_t          len = 0;
+       uint8_t         *buffer;
        uint32_t        lvalue;
        uint64_t        lvalue64;
 
-       len = vp->length;
-       if (outlen < len) {
-               fr_strerror_printf("ERROR: rad_vp2data buffer passed too small");
-               return -1;
+       *out = NULL;
+
+       buffer = fr_thread_local_init(rad_vp2data_buff, free);
+       if (!buffer) {
+               int ret;
+
+               buffer = malloc(sizeof(uint8_t) * sizeof(value_data_t));
+               if (!buffer) {
+                       fr_strerror_printf("Failed allocating memory for rad_vp2data buffer");
+                       return -1;
+               }
+
+               ret = fr_thread_local_set(rad_vp2data_buff, buffer);
+               if (ret != 0) {
+                       fr_strerror_printf("Failed setting up TLS for rad_vp2data buffer: %s", strerror(errno));
+                       free(buffer);
+                       return -1;
+               }
        }
-       
+
+       VERIFY_VP(vp);
+
+       switch(vp->da->type) {
+       case PW_TYPE_STRING:
+       case PW_TYPE_OCTETS:
+       case PW_TYPE_TLV:
+               memcpy(out, &vp->data.ptr, sizeof(*out));
+               break;
+
        /*
-        *      Short-circuit it for long attributes.
+        *      All of these values are at the same location.
         */
-       if ((vp->da->type & PW_FLAG_LONG) != 0) goto do_raw;
+       case PW_TYPE_IFID:
+       case PW_TYPE_IPV4_ADDR:
+       case PW_TYPE_IPV6_ADDR:
+       case PW_TYPE_IPV6_PREFIX:
+       case PW_TYPE_IPV4_PREFIX:
+       case PW_TYPE_ABINARY:
+       case PW_TYPE_ETHERNET:
+       case PW_TYPE_IP_ADDR:
+       case PW_TYPE_IP_PREFIX:
+       {
+               void const *p = &vp->data;
+               memcpy(out, &p, sizeof(*out));
+               break;
+       }
 
-       switch(vp->da->type) {
-               case PW_TYPE_STRING:
-               case PW_TYPE_OCTETS:
-               case PW_TYPE_IFID:
-               case PW_TYPE_IPADDR:
-               case PW_TYPE_IPV6ADDR:
-               case PW_TYPE_IPV6PREFIX:
-               case PW_TYPE_IPV4PREFIX:
-               case PW_TYPE_ABINARY:
-               case PW_TYPE_TLV:
-                       do_raw:
-                       memcpy(out, vp->vp_octets, len);
-                       break;
-               case PW_TYPE_BYTE:
-                       out[0] = vp->vp_integer & 0xff;
-                       break;
-       
-               case PW_TYPE_SHORT:
-                       out[0] = (vp->vp_integer >> 8) & 0xff;
-                       out[1] = vp->vp_integer & 0xff;
-                       break;
-       
-               case PW_TYPE_INTEGER:
-                       lvalue = htonl(vp->vp_integer);
-                       memcpy(out, &lvalue, sizeof(lvalue));
-                       break;
-       
-               case PW_TYPE_INTEGER64:
-                       lvalue64 = htonll(vp->vp_integer64);
-                       memcpy(out, &lvalue64, sizeof(lvalue64));
-                       break;
+       case PW_TYPE_BOOLEAN:
+               buffer[0] = vp->vp_integer & 0x01;
+               *out = buffer;
+               break;
 
-               case PW_TYPE_DATE:
-                       lvalue = htonl(vp->vp_date);
-                       memcpy(out, &lvalue, sizeof(lvalue));
-                       break;
-       
-               case PW_TYPE_SIGNED:
-               {
-                       int32_t slvalue;
-                       
-                       slvalue = htonl(vp->vp_signed);
-                       memcpy(out, &slvalue, sizeof(slvalue));
-                       break;
-               }
-               /* unknown type: ignore it */
-               default:                
-                       fr_strerror_printf("ERROR: Unknown attribute type %d",
-                                          vp->da->type);
-                       return -1;
+       case PW_TYPE_BYTE:
+               buffer[0] = vp->vp_integer & 0xff;
+               *out = buffer;
+               break;
+
+       case PW_TYPE_SHORT:
+               buffer[0] = (vp->vp_integer >> 8) & 0xff;
+               buffer[1] = vp->vp_integer & 0xff;
+               *out = buffer;
+               break;
+
+       case PW_TYPE_INTEGER:
+               lvalue = htonl(vp->vp_integer);
+               memcpy(buffer, &lvalue, sizeof(lvalue));
+               *out = buffer;
+               break;
+
+       case PW_TYPE_INTEGER64:
+               lvalue64 = htonll(vp->vp_integer64);
+               memcpy(buffer, &lvalue64, sizeof(lvalue64));
+               *out = buffer;
+               break;
+
+       case PW_TYPE_DATE:
+               lvalue = htonl(vp->vp_date);
+               memcpy(buffer, &lvalue, sizeof(lvalue));
+               *out = buffer;
+               break;
+
+       case PW_TYPE_SIGNED:
+       {
+               int32_t slvalue = htonl(vp->vp_signed);
+               memcpy(buffer, &slvalue, sizeof(slvalue));
+               *out = buffer;
+               break;
+       }
+
+       case PW_TYPE_INVALID:
+       case PW_TYPE_EXTENDED:
+       case PW_TYPE_LONG_EXTENDED:
+       case PW_TYPE_EVS:
+       case PW_TYPE_VSA:
+       case PW_TYPE_TIMEVAL:
+       case PW_TYPE_MAX:
+               fr_strerror_printf("Cannot get data for VALUE_PAIR type %i", vp->da->type);
+               return -1;
+
+       /* Don't add default */
        }
-       
-       return len;
+
+       return vp->length;
 }
 
 /**
@@ -3690,10 +4046,10 @@ ssize_t rad_vp2data(const VALUE_PAIR *vp, uint8_t *out, size_t outlen)
  * @return -1 on decoding error, 0 on success
  */
 int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
-              const char *secret)
+              char const *secret)
 {
        int                     packet_length;
-       int                     num_attributes;
+       uint32_t                num_attributes;
        uint8_t                 *ptr;
        radius_packet_t         *hdr;
        VALUE_PAIR *head, **tail, *vp;
@@ -3718,7 +4074,7 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                /*
                 *      This may return many VPs
                 */
-               my_len = rad_attr2vp(packet, original, secret,
+               my_len = rad_attr2vp(packet, packet, original, secret,
                                     ptr, packet_length, &vp);
                if (my_len < 0) {
                        pairfree(&head);
@@ -3761,7 +4117,7 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
         *      random pool.
         */
        fr_rand_seed(packet->data, AUTH_HDR_LEN);
-       
+
        /*
         *      There may be VP's already in the packet.  Don't
         *      destroy them.  Instead, add the decoded attributes to
@@ -3787,8 +4143,8 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
  *     int *pwlen is updated to the new length of the encrypted
  *     password - a multiple of 16 bytes.
  */
-int rad_pwencode(char *passwd, size_t *pwlen, const char *secret,
-                const uint8_t *vector)
+int rad_pwencode(char *passwd, size_t *pwlen, char const *secret,
+                uint8_t const *vector)
 {
        FR_MD5_CTX context, old;
        uint8_t digest[AUTH_VECTOR_LEN];
@@ -3821,8 +4177,8 @@ int rad_pwencode(char *passwd, size_t *pwlen, const char *secret,
         */
        secretlen = strlen(secret);
 
-       fr_MD5Init(&context);
-       fr_MD5Update(&context, (const uint8_t *) secret, secretlen);
+       fr_md5_init(&context);
+       fr_md5_update(&context, (uint8_t const *) secret, secretlen);
        old = context;          /* save intermediate work */
 
        /*
@@ -3831,14 +4187,14 @@ int rad_pwencode(char *passwd, size_t *pwlen, const char *secret,
         */
        for (n = 0; n < len; n += AUTH_PASS_LEN) {
                if (n == 0) {
-                       fr_MD5Update(&context, vector, AUTH_PASS_LEN);
-                       fr_MD5Final(digest, &context);
+                       fr_md5_update(&context, vector, AUTH_PASS_LEN);
+                       fr_md5_final(digest, &context);
                } else {
                        context = old;
-                       fr_MD5Update(&context,
+                       fr_md5_update(&context,
                                     (uint8_t *) passwd + n - AUTH_PASS_LEN,
                                     AUTH_PASS_LEN);
-                       fr_MD5Final(digest, &context);
+                       fr_md5_final(digest, &context);
                }
 
                for (i = 0; i < AUTH_PASS_LEN; i++) {
@@ -3852,8 +4208,8 @@ int rad_pwencode(char *passwd, size_t *pwlen, const char *secret,
 /**
  * @brief Decode password.
  */
-int rad_pwdecode(char *passwd, size_t pwlen, const char *secret,
-                const uint8_t *vector)
+int rad_pwdecode(char *passwd, size_t pwlen, char const *secret,
+                uint8_t const *vector)
 {
        FR_MD5_CTX context, old;
        uint8_t digest[AUTH_VECTOR_LEN];
@@ -3877,8 +4233,8 @@ int rad_pwdecode(char *passwd, size_t pwlen, const char *secret,
         */
        secretlen = strlen(secret);
 
-       fr_MD5Init(&context);
-       fr_MD5Update(&context, (const uint8_t *) secret, secretlen);
+       fr_md5_init(&context);
+       fr_md5_update(&context, (uint8_t const *) secret, secretlen);
        old = context;          /* save intermediate work */
 
        /*
@@ -3886,20 +4242,20 @@ int rad_pwdecode(char *passwd, size_t pwlen, const char *secret,
         */
        for (n = 0; n < pwlen; n += AUTH_PASS_LEN) {
                if (n == 0) {
-                       fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
-                       fr_MD5Final(digest, &context);
+                       fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
+                       fr_md5_final(digest, &context);
 
                        context = old;
                        if (pwlen > AUTH_PASS_LEN) {
-                               fr_MD5Update(&context, (uint8_t *) passwd,
+                               fr_md5_update(&context, (uint8_t *) passwd,
                                             AUTH_PASS_LEN);
                        }
                } else {
-                       fr_MD5Final(digest, &context);
+                       fr_md5_final(digest, &context);
 
                        context = old;
                        if (pwlen > (n + AUTH_PASS_LEN)) {
-                               fr_MD5Update(&context, (uint8_t *) passwd + n,
+                               fr_md5_update(&context, (uint8_t *) passwd + n,
                                             AUTH_PASS_LEN);
                        }
                }
@@ -3924,8 +4280,8 @@ int rad_pwdecode(char *passwd, size_t pwlen, const char *secret,
  *      This is per RFC-2868 which adds a two char SALT to the initial intermediate
  *      value MD5 hash.
  */
-int rad_tunnel_pwencode(char *passwd, size_t *pwlen, const char *secret,
-                       const uint8_t *vector)
+int rad_tunnel_pwencode(char *passwd, size_t *pwlen, char const *secret,
+                       uint8_t const *vector)
 {
        uint8_t buffer[AUTH_VECTOR_LEN + MAX_STRING_LEN + 3];
        unsigned char   digest[AUTH_VECTOR_LEN];
@@ -4007,8 +4363,8 @@ int rad_tunnel_pwencode(char *passwd, size_t *pwlen, const char *secret,
  *      initial intermediate value, to differentiate it from the
  *      above.
  */
-int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, const char *secret,
-                       const uint8_t *vector)
+int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, char const *secret,
+                       uint8_t const *vector)
 {
        FR_MD5_CTX  context, old;
        uint8_t         digest[AUTH_VECTOR_LEN];
@@ -4048,8 +4404,8 @@ int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, const char *secret,
         */
        secretlen = strlen(secret);
 
-       fr_MD5Init(&context);
-       fr_MD5Update(&context, (const uint8_t *) secret, secretlen);
+       fr_md5_init(&context);
+       fr_md5_update(&context, (uint8_t const *) secret, secretlen);
        old = context;          /* save intermediate work */
 
        /*
@@ -4057,15 +4413,15 @@ int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, const char *secret,
         *
         *       b(1) = MD5(secret + vector + salt)
         */
-       fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
-       fr_MD5Update(&context, passwd, 2);
+       fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
+       fr_md5_update(&context, passwd, 2);
 
        reallen = 0;
        for (n = 0; n < len; n += AUTH_PASS_LEN) {
                int base = 0;
 
                if (n == 0) {
-                       fr_MD5Final(digest, &context);
+                       fr_md5_final(digest, &context);
 
                        context = old;
 
@@ -4080,14 +4436,14 @@ int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, const char *secret,
                                return -1;
                        }
 
-                       fr_MD5Update(&context, passwd + 2, AUTH_PASS_LEN);
+                       fr_md5_update(&context, passwd + 2, AUTH_PASS_LEN);
 
                        base = 1;
                } else {
-                       fr_MD5Final(digest, &context);
+                       fr_md5_final(digest, &context);
 
                        context = old;
-                       fr_MD5Update(&context, passwd + n + 2, AUTH_PASS_LEN);
+                       fr_md5_update(&context, passwd + n + 2, AUTH_PASS_LEN);
                }
 
                for (i = base; i < AUTH_PASS_LEN; i++) {
@@ -4169,7 +4525,7 @@ int rad_chap_encode(RADIUS_PACKET *packet, uint8_t *output, int id,
  *
  *     May be called any number of times.
  */
-void fr_rand_seed(const void *data, size_t size)
+void fr_rand_seed(void const *data, size_t size)
 {
        uint32_t hash;
 
@@ -4192,7 +4548,7 @@ void fr_rand_seed(const void *data, size_t size)
                                            sizeof(fr_rand_pool.randrsl) - total);
                                if ((this < 0) && (errno != EINTR)) break;
                                if (this > 0) total += this;
-                       }
+                       }
                        close(fd);
                } else {
                        fr_rand_pool.randrsl[0] = fd;
@@ -4246,10 +4602,10 @@ uint32_t fr_rand(void)
  *
  * @param ctx the context in which the packet is allocated. May be NULL if
  *     the packet is not associated with a REQUEST.
- * @param newvector if TRUE a new request authenticator will be generated.
+ * @param new_vector if true a new request authenticator will be generated.
  * @return a new RADIUS_PACKET or NULL on error.
  */
-RADIUS_PACKET *rad_alloc(TALLOC_CTX *ctx, int newvector)
+RADIUS_PACKET *rad_alloc(TALLOC_CTX *ctx, bool new_vector)
 {
        RADIUS_PACKET   *rp;
 
@@ -4261,7 +4617,7 @@ RADIUS_PACKET *rad_alloc(TALLOC_CTX *ctx, int newvector)
        rp->id = -1;
        rp->offset = -1;
 
-       if (newvector) {
+       if (new_vector) {
                int i;
                uint32_t hash, base;
 
@@ -4293,7 +4649,7 @@ RADIUS_PACKET *rad_alloc_reply(TALLOC_CTX *ctx, RADIUS_PACKET *packet)
 
        if (!packet) return NULL;
 
-       reply = rad_alloc(ctx, 0);
+       reply = rad_alloc(ctx, false);
        if (!reply) return NULL;
 
        /*
@@ -4312,6 +4668,9 @@ RADIUS_PACKET *rad_alloc_reply(TALLOC_CTX *ctx, RADIUS_PACKET *packet)
        reply->data = NULL;
        reply->data_len = 0;
 
+#ifdef WITH_TCP
+       reply->proto = packet->proto;
+#endif
        return reply;
 }
 
@@ -4326,10 +4685,43 @@ void rad_free(RADIUS_PACKET **radius_packet_ptr)
        if (!radius_packet_ptr || !*radius_packet_ptr) return;
        radius_packet = *radius_packet_ptr;
 
-       free(radius_packet->data);
+       VERIFY_PACKET(radius_packet);
 
        pairfree(&radius_packet->vps);
 
        talloc_free(radius_packet);
        *radius_packet_ptr = NULL;
 }
+
+/** Duplicate a RADIUS_PACKET
+ *
+ * @param ctx the context in which the packet is allocated. May be NULL if
+ *     the packet is not associated with a REQUEST.
+ * @param in The packet to copy
+ * @return a new RADIUS_PACKET or NULL on error.
+ */
+RADIUS_PACKET *rad_copy_packet(TALLOC_CTX *ctx, RADIUS_PACKET const *in)
+{
+       RADIUS_PACKET *out;
+
+       out = rad_alloc(ctx, false);
+       if (!out) return NULL;
+
+       /*
+        *      Bootstrap by copying everything.
+        */
+       memcpy(out, in, sizeof(*out));
+
+       /*
+        *      Then reset necessary fields
+        */
+       out->sockfd = -1;
+
+       out->data = NULL;
+       out->data_len = 0;
+
+       out->vps = paircopy(out, in->vps);
+       out->offset = 0;
+
+       return out;
+}