Merge branch 'sam'
[freeradius.git] / src / lib / radius.c
index 27441d8..8bcca4f 100644 (file)
@@ -1,5 +1,6 @@
-/*
- * radius.c    Functions to send/receive radius packets.
+/**
+ * @file radius.c
+ * @brief Functions to send/receive radius packets.
  *
  * Version:    $Id$
  *
@@ -37,6 +38,13 @@ RCSID("$Id$")
 #include       <malloc.h>
 #endif
 
+#if 0
+#define VP_TRACE if (fr_debug_flag) printf
+#else
+#define VP_TRACE(_x, ...)
+#endif
+
+
 /*
  *  The RFC says 4096 octets max, and most packets are less than 256.
  */
@@ -140,60 +148,81 @@ void fr_printf_log(const char *fmt, ...)
        return;
 }
 
-#if 0
-static void print_hex(RADIUS_PACKET *packet)
+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 void print_hex_data(const uint8_t *ptr, int attrlen, int depth)
 {
        int i;
 
-       if (!packet->data) return;
+       for (i = 0; i < attrlen; i++) {
+               if ((i > 0) && ((i & 0x0f) == 0x00))
+                       fprintf(fr_log_fp, "%.*s", depth, tabs);
+               fprintf(fr_log_fp, "%02x ", ptr[i]);
+               if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n");
+       }
+       if ((i & 0x0f) != 0) fprintf(fr_log_fp, "\n");
+}
+
+
+void rad_print_hex(RADIUS_PACKET *packet)
+{
+       int i;
+
+       if (!packet->data || !fr_log_fp) return;
 
-       printf("  Code:\t\t%u\n", packet->data[0]);
-       printf("  Id:\t\t%u\n", packet->data[1]);
-       printf("  Length:\t%u\n", ((packet->data[2] << 8) |
+       fprintf(fr_log_fp, "  Code:\t\t%u\n", packet->data[0]);
+       fprintf(fr_log_fp, "  Id:\t\t%u\n", packet->data[1]);
+       fprintf(fr_log_fp, "  Length:\t%u\n", ((packet->data[2] << 8) |
                                   (packet->data[3])));
-       printf("  Vector:\t");
+       fprintf(fr_log_fp, "  Vector:\t");
        for (i = 4; i < 20; i++) {
-               printf("%02x", packet->data[i]);
+               fprintf(fr_log_fp, "%02x", packet->data[i]);
        }
-       printf("\n");
+       fprintf(fr_log_fp, "\n");
 
        if (packet->data_len > 20) {
                int total;
                const uint8_t *ptr;
-               printf("  Data:");
+               fprintf(fr_log_fp, "  Data:");
 
                total = packet->data_len - 20;
                ptr = packet->data + 20;
 
                while (total > 0) {
                        int attrlen;
+                       unsigned int vendor = 0;
 
-                       printf("\t\t");
+                       fprintf(fr_log_fp, "\t\t");
                        if (total < 2) { /* too short */
-                               printf("%02x\n", *ptr);
+                               fprintf(fr_log_fp, "%02x\n", *ptr);
                                break;
                        }
 
                        if (ptr[1] > total) { /* too long */
                                for (i = 0; i < total; i++) {
-                                       printf("%02x ", ptr[i]);
+                                       fprintf(fr_log_fp, "%02x ", ptr[i]);
                                }
                                break;
                        }
 
-                       printf("%02x  %02x  ", ptr[0], ptr[1]);
+                       fprintf(fr_log_fp, "%02x  %02x  ", ptr[0], ptr[1]);
                        attrlen = ptr[1] - 2;
-                       ptr += 2;
-                       total -= 2;
-
-                       for (i = 0; i < attrlen; i++) {
-                               if ((i > 0) && ((i & 0x0f) == 0x00))
-                                       printf("\t\t\t");
-                               printf("%02x ", ptr[i]);
-                               if ((i & 0x0f) == 0x0f) printf("\n");
+
+                       if ((ptr[0] == PW_VENDOR_SPECIFIC) &&
+                           (attrlen > 4)) {
+                               vendor = (ptr[3] << 16) | (ptr[4] << 8) | ptr[5];
+                               fprintf(fr_log_fp, "%02x%02x%02x%02x (%u)  ",
+                                      ptr[2], ptr[3], ptr[4], ptr[5], vendor);
+                               attrlen -= 4;
+                               ptr += 6;
+                               total -= 6;
+
+                       } else {
+                               ptr += 2;
+                               total -= 2;
                        }
 
-                       if ((attrlen & 0x0f) != 0x00) printf("\n");
+                       print_hex_data(ptr, attrlen, 3);
 
                        ptr += attrlen;
                        total -= attrlen;
@@ -201,10 +230,9 @@ static void print_hex(RADIUS_PACKET *packet)
        }
        fflush(stdout);
 }
-#endif
 
-/*
- *     Wrapper for sendto which handles sendfromto, IPv6, and all
+/**
+ * @brief Wrapper for sendto which handles sendfromto, IPv6, and all
  *     possible combinations.
  */
 static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
@@ -234,7 +262,8 @@ static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
         *      use udpfromto.
         */
        if (((dst_ipaddr->af == AF_INET) || (dst_ipaddr->af == AF_INET6)) &&
-           (src_ipaddr->af != AF_UNSPEC)) {
+           (src_ipaddr->af != AF_UNSPEC) &&
+           !fr_inaddr_any(src_ipaddr)) {
                rcode = sendfromto(sockfd, data, data_len, flags,
                                   (struct sockaddr *)&src, sizeof_src,
                                   (struct sockaddr *)&dst, sizeof_dst);
@@ -249,7 +278,9 @@ static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
         */
        rcode = sendto(sockfd, data, data_len, flags,
                       (struct sockaddr *) &dst, sizeof_dst);
+#ifdef WITH_UDPFROMTO
 done:
+#endif
        if (rcode < 0) {
                DEBUG("rad_send() failed: %s\n", strerror(errno));
        }
@@ -337,8 +368,8 @@ ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port,
 }
 
 
-/*
- *     wrapper for recvfrom, which handles recvfromto, IPv6, and all
+/**
+ * @brief wrapper for recvfrom, which handles recvfromto, IPv6, and all
  *     possible combinations.
  */
 static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
@@ -465,17 +496,15 @@ static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
 
 
 #define AUTH_PASS_LEN (AUTH_VECTOR_LEN)
-/*************************************************************************
- *
- *      Function: make_secret
- *
- *      Purpose: Build an encrypted secret value to return in a reply
- *               packet.  The secret is hidden by xoring with a MD5 digest
+/**
+ * @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.
  *
- *************************************************************************/
+ */
 static void make_secret(uint8_t *digest, const uint8_t *vector,
                        const char *secret, const uint8_t *value)
 {
@@ -645,12 +674,14 @@ 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, int nest)
+static int do_next_tlv(const VALUE_PAIR *vp, const VALUE_PAIR *next, int nest)
 {
        unsigned int tlv1, tlv2;
 
        if (nest > fr_attr_max_tlv) return 0;
 
+       if (!vp) return 0;
+
        /*
         *      Keep encoding TLVs which have the same scope.
         *      e.g. two attributes of:
@@ -662,24 +693,18 @@ static int do_next_tlv(const VALUE_PAIR *vp, int nest)
        /*
         *      Nothing to follow, we're done.
         */
-       if (!vp->next) return 0;
+       if (!next) return 0;
 
        /*
         *      Not from the same vendor, skip it.
         */
-       if (vp->vendor != vp->next->vendor) return 0;
-
-       /*
-        *      The next one has already been done.  Maybe by
-        *      another level of recursion.  Skip it.
-        */
-       if (vp->next->flags.encoded) return 0;
+       if (vp->vendor != next->vendor) return 0;
 
        /*
         *      In a different TLV space, skip it.
         */
        tlv1 = vp->attribute;
-       tlv2 = vp->next->attribute;
+       tlv2 = next->attribute;
        
        tlv1 &= ((1 << fr_attr_shift[nest]) - 1);
        tlv2 &= ((1 << fr_attr_shift[nest]) - 1);
@@ -692,18 +717,33 @@ static int do_next_tlv(const VALUE_PAIR *vp, int nest)
 
 static ssize_t vp2data_any(const RADIUS_PACKET *packet,
                           const RADIUS_PACKET *original,
-                          const char *secret, int nest, VALUE_PAIR *vps,
+                          const char *secret, int nest,
+                          const VALUE_PAIR **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,
+                          unsigned int attribute, uint8_t *ptr, size_t room);
+
+/**
+ * @brief This is really a sub-function of vp2data_any().  It encodes
+ *     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, VALUE_PAIR *vps,
+                           const char *secret, int nest,
+                           const VALUE_PAIR **pvp,
                            uint8_t *start, size_t room)
 {
        ssize_t len;
+       size_t my_room;
        uint8_t *ptr = start;
-       uint8_t *end = start + room;
-       VALUE_PAIR *vp = vps;
+       const VALUE_PAIR *vp = *pvp;
+       const VALUE_PAIR *svp = vp;
+
+       if (!svp) return 0;
 
 #ifndef NDEBUG
        if (nest > fr_attr_max_tlv) {
@@ -712,35 +752,58 @@ static ssize_t vp2data_tlvs(const RADIUS_PACKET *packet,
        }
 #endif
 
-       while (1) {
+       while (vp) {
+               if (room < 2) return ptr - start;
+               
                ptr[0] = (vp->attribute >> fr_attr_shift[nest]) & fr_attr_mask[nest];
                ptr[1] = 2;
                
-               len = vp2data_any(packet, original, secret, nest + 1, vp,
-                                 ptr + ptr[1], end - ptr);
-               if (len < 0) {
-                       if (vp != vps) break;
-                       return len;
-               }
-               
+               my_room = room;
+               if (room > 255) my_room = 255;
+
+               len = vp2data_any(packet, original, secret, nest,
+                                 &vp, ptr + 2, my_room - 2);
+               if (len < 0) return len;
+               if (len == 0) return ptr - start;
+               /* len can NEVER be more than 253 */
+
                ptr[1] += len;
+
+#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
+
+               room -= ptr[1];
                ptr += ptr[1];
-               vp->flags.encoded = 1;
+               *pvp = vp;
                
-               if (!do_next_tlv(vp, nest)) break;
-               vp = vp->next;
+               if (!do_next_tlv(svp, vp, nest)) break;
        }
-       
+
+#ifndef NDEBUG
+       if ((fr_debug_flag > 3) && fr_log_fp) {
+               DICT_ATTR *da;
+               
+               da = dict_attrbyvalue(svp->attribute & ((1 << fr_attr_shift[nest ]) - 1), svp->vendor);
+               if (da) fprintf(fr_log_fp, "\t%s = ...\n", da->name);
+       }
+#endif
+
        return ptr - start;
 }
 
-/*
- *     Encodes the data portion of an attribute.
- *     Returns -1 on error, or the length of the data portion.
+
+/**
+ * @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, VALUE_PAIR *vp,
+                          const char *secret, int nest,
+                          const VALUE_PAIR **pvp,
                           uint8_t *start, size_t room)
 {
        uint32_t lvalue;
@@ -748,6 +811,8 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
        const uint8_t *data;
        uint8_t *ptr = start;
        uint8_t array[4];
+       uint64_t lvalue64;
+       const VALUE_PAIR *vp = *pvp;
 
        /*
         *      See if we need to encode a TLV.  The low portion of
@@ -757,18 +822,26 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
         *
         *      If we cared about the stack, we could unroll the loop.
         */
-       if ((nest > 0) && (nest <= fr_attr_max_tlv) &&
-           ((vp->attribute >> fr_attr_shift[nest]) != 0)) {
-               return vp2data_tlvs(packet, original, secret, nestvp,
+       if (vp->flags.is_tlv && (nest < fr_attr_max_tlv) &&
+           ((vp->attribute >> fr_attr_shift[nest + 1]) != 0)) {
+               return vp2data_tlvs(packet, original, secret, nest + 1, pvp,
                                    start, room);
        }
 
+       debug_pair(vp);
+
        /*
         *      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->type & PW_FLAG_LONG) != 0) goto do_tlv;
+
        switch(vp->type) {
        case PW_TYPE_STRING:
        case PW_TYPE_OCTETS:
@@ -799,6 +872,12 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
                data = array;
                break;
 
+       case PW_TYPE_INTEGER64:
+               len = 8;        /* just in case */
+               lvalue64 = htonll(vp->vp_integer64);
+               data = (uint8_t *) &lvalue64;
+               break;
+
        case PW_TYPE_IPADDR:
                data = (const uint8_t *) &vp->vp_ipaddr;
                len = 4;        /* just in case */
@@ -824,12 +903,12 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
        }
 
        case PW_TYPE_TLV:
+       do_tlv:
                data = vp->vp_tlv;
                if (!data) {
                        fr_strerror_printf("ERROR: Cannot encode NULL TLV");
                        return -1;
                }
-               len = vp->length;
                break;
 
        default:                /* unknown type: ignore it */
@@ -838,6 +917,14 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
        }
 
        /*
+        *      No data: skip it.
+        */
+       if (len == 0) {
+               *pvp = vp->next;
+               return 0;
+       }
+
+       /*
         *      Bound the data to the calling size
         */
        if (len > (ssize_t) room) len = room;
@@ -916,8 +1003,8 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
                break;
        } /* switch over encryption flags */
 
-       vp->flags.encoded = 1;
-       return len + (ptr - start);;
+       *pvp = vp->next;
+       return len + (ptr - start);
 }
 
 static ssize_t attr_shift(const uint8_t *start, const uint8_t *end,
@@ -980,18 +1067,19 @@ static ssize_t attr_shift(const uint8_t *start, const uint8_t *end,
 }
 
 
-/*
- *     Encode an "extended" attribute.
+/**
+ * @brief Encode an "extended" attribute.
  */
 int rad_vp2extended(const RADIUS_PACKET *packet,
                    const RADIUS_PACKET *original,
-                   const char *secret, VALUE_PAIR *vp,
+                   const char *secret, const VALUE_PAIR **pvp,
                    uint8_t *ptr, size_t room)
 {
        int len;
        int hdr_len;
-       int nest = 2;
+       int nest = 1;
        uint8_t *start = ptr;
+       const VALUE_PAIR *vp = *pvp;
 
        if (vp->vendor < VENDORPEC_EXTENDED) {
                fr_strerror_printf("rad_vp2extended called for non-extended attribute");
@@ -1046,13 +1134,13 @@ int rad_vp2extended(const RADIUS_PACKET *packet,
                evs[4] = vp->attribute & 0xff;          
 
                ptr[1] += 5;
-               nest = 1;
+               nest = 0;
        }
        hdr_len = ptr[1];
 
        len = vp2data_any(packet, original, secret, nest,
-                         vp, ptr + ptr[1], room - hdr_len);
-       if (len < 0) return len;
+                         pvp, ptr + ptr[1], room - hdr_len);
+       if (len <= 0) return len;
 
        /*
         *      There may be more than 252 octets of data encoded in
@@ -1065,28 +1153,57 @@ int rad_vp2extended(const RADIUS_PACKET *packet,
        }
 
        ptr[1] += len;
+       
+#ifndef NDEBUG
+       if ((fr_debug_flag > 3) && fr_log_fp) {
+               int jump = 3;
+
+               fprintf(fr_log_fp, "\t\t%02x %02x  ", ptr[0], ptr[1]);
+               if (!vp->flags.extended_flags) {
+                       fprintf(fr_log_fp, "%02x  ", ptr[2]);
+                       
+               } else {
+                       fprintf(fr_log_fp, "%02x %02x  ", ptr[2], ptr[3]);
+                       jump = 4;
+               }
+
+               if (vp->flags.evs) {
+                       fprintf(fr_log_fp, "%02x%02x%02x%02x (%u)  %02x  ",
+                               ptr[jump], ptr[jump + 1],
+                               ptr[jump + 2], ptr[jump + 3],
+                               ((ptr[jump + 1] << 16) |
+                                (ptr[jump + 2] << 8) |
+                                ptr[jump + 3]),
+                               ptr[jump + 4]);
+                       jump += 5;
+               }
+
+               print_hex_data(ptr + jump, len, 3);
+       }
+#endif
 
        return (ptr + ptr[1]) - start;
 }
 
 
-/*
- *     Encode a WiMAX attribute.
+/**
+ * @brief Encode a WiMAX attribute.
  */
 int rad_vp2wimax(const RADIUS_PACKET *packet,
                 const RADIUS_PACKET *original,
-                const char *secret, VALUE_PAIR *vp,
+                const char *secret, const VALUE_PAIR **pvp,
                 uint8_t *ptr, size_t room)
 {
        int len;
        uint32_t lvalue;
        int hdr_len;
        uint8_t *start = ptr;
+       const VALUE_PAIR *vp = *pvp;
 
        /*
-        *      Double-check for WiMAX
+        *      Double-check for WiMAX format.
         */
-       if (vp->vendor != VENDORPEC_WIMAX) {
+       if (!vp->flags.wimax) {
                fr_strerror_printf("rad_vp2wimax called for non-WIMAX VSA");
                return -1;
        }
@@ -1111,7 +1228,7 @@ int rad_vp2wimax(const RADIUS_PACKET *packet,
 
        hdr_len = 9;
 
-       len = vp2data_any(packet, original, secret, 1, vp, ptr + ptr[1],
+       len = vp2data_any(packet, original, secret, 0, pvp, ptr + ptr[1],
                          room - hdr_len);
        if (len <= 0) return len;
 
@@ -1128,18 +1245,31 @@ int rad_vp2wimax(const RADIUS_PACKET *packet,
        ptr[1] += len;
        ptr[7] += len;
 
+#ifndef NDEBUG
+       if ((fr_debug_flag > 3) && fr_log_fp) {
+               fprintf(fr_log_fp, "\t\t%02x %02x  %02x%02x%02x%02x (%u)  %02x %02x %02x   ",
+                      ptr[0], ptr[1],
+                      ptr[2], ptr[3], ptr[4], ptr[5],
+                      (ptr[3] << 16) | (ptr[4] << 8) | ptr[5],
+                      ptr[6], ptr[7], ptr[8]);
+               print_hex_data(ptr + 9, len, 3);
+       }
+#endif
+
        return (ptr + ptr[1]) - start;
 }
 
-/*
- *     Encode an RFC format TLV.  This could be a standard attribute,
+/**
+ * @brief Encode an RFC format TLV.
+ *
+ *     This could be a standard attribute,
  *     or a TLV data type.  If it's a standard attribute, then
  *     vp->attribute == attribute.  Otherwise, attribute may be
  *     something else.
  */
 static ssize_t vp2attr_rfc(const RADIUS_PACKET *packet,
                           const RADIUS_PACKET *original,
-                          const char *secret, VALUE_PAIR *vp,
+                          const char *secret, const VALUE_PAIR **pvp,
                           unsigned int attribute, uint8_t *ptr, size_t room)
 {
        ssize_t len;
@@ -1151,35 +1281,44 @@ static ssize_t vp2attr_rfc(const RADIUS_PACKET *packet,
 
        if (room > ((unsigned) 255 - ptr[1])) room = 255 - ptr[1];
 
-       len = vp2data_any(packet, original, secret, 0, vp, ptr + ptr[1], room);
-       if (len < 0) return len;
+       len = vp2data_any(packet, original, secret, 0, pvp, ptr + ptr[1], room);
+       if (len <= 0) return len;
 
        ptr[1] += len;
 
+#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
+
        return ptr[1];
 }
 
 
-/*
- *     Encode a VSA which is a TLV.  If it's in the RFC format, call
+/**
+ * @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, VALUE_PAIR *vp,
+                          const char *secret, const VALUE_PAIR **pvp,
                           unsigned int attribute, unsigned int vendor,
                           uint8_t *ptr, size_t room)
 {
        ssize_t len;
        DICT_VENDOR *dv;
+       const VALUE_PAIR *vp = *pvp;
 
        /*
         *      Unknown vendor: RFC format.
         *      Known vendor and RFC format: go do that.
         */
        dv = dict_vendorbyvalue(vendor);
-       if (!dv || ((dv->type == 1) && (dv->length == 1))) {
-               return vp2attr_rfc(packet, original, secret, vp,
+       if (!dv ||
+           (!vp->flags.is_tlv && (dv->type == 1) && (dv->length == 1))) {
+               return vp2attr_rfc(packet, original, secret, pvp,
                                   attribute, ptr, room);
        }
 
@@ -1217,10 +1356,11 @@ static ssize_t vp2attr_vsa(const RADIUS_PACKET *packet,
 
        case 2:
                ptr[dv->type] = 0;
-               /* FALL-THROUGH */
+               ptr[dv->type + 1] = dv->type + 2;
+               break;
 
        case 1:
-               ptr[dv->type + dv->length - 1] = dv->type + dv->length;
+               ptr[dv->type] = dv->type + 1;
                break;
 
        }
@@ -1229,31 +1369,79 @@ static ssize_t vp2attr_vsa(const RADIUS_PACKET *packet,
                room = 255 - (dv->type + dv->length);
        }
 
-       len = vp2data_any(packet, original, secret, 0, vp,
+       len = vp2data_any(packet, original, secret, 0, pvp,
                          ptr + dv->type + dv->length, room);
-       if (len < 0) return len;
+       if (len <= 0) return len;
 
        if (dv->length) ptr[dv->type + dv->length - 1] += len;
 
+#ifndef NDEBUG
+       if ((fr_debug_flag > 3) && fr_log_fp) {
+               switch (dv->type) {
+               default:
+                       break;
+
+               case 4:
+                       if ((fr_debug_flag > 3) && fr_log_fp)
+                               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;
+
+               case 0:
+                       fprintf(fr_log_fp, "  ");
+                       break;
+
+               case 1:
+                       fprintf(fr_log_fp, "%02x  ",
+                               ptr[dv->type]);
+                       break;
+
+               case 2:
+                       fprintf(fr_log_fp, "%02x%02x  ",
+                               ptr[dv->type], ptr[dv->type] + 1);
+                       break;
+               }
+
+               print_hex_data(ptr + dv->type + dv->length, len, 3);
+       }
+#endif
+
        return dv->type + dv->length + len;
 }
 
 
-/*
- *     Encode a Vendor-Specific attribute.
+/**
+ * @brief Encode a Vendor-Specific attribute.
  */
 int rad_vp2vsa(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
-               const char *secret, VALUE_PAIR *vp, uint8_t *ptr,
+               const char *secret, const VALUE_PAIR **pvp, uint8_t *ptr,
                size_t room)
 {
        ssize_t len;
        uint32_t lvalue;
+       const VALUE_PAIR *vp = *pvp;
 
        /*
-        *      Double-check for WiMAX
+        *      Double-check for WiMAX format.
         */
-       if (vp->vendor == VENDORPEC_WIMAX) {
-               return rad_vp2wimax(packet, original, secret, vp,
+       if (vp->flags.wimax) {
+               return rad_vp2wimax(packet, original, secret, pvp,
                                    ptr, room);
        }
 
@@ -1278,25 +1466,37 @@ int rad_vp2vsa(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
 
        if (room > ((unsigned) 255 - ptr[1])) room = 255 - ptr[1];
 
-       len = vp2attr_vsa(packet, original, secret, vp,
+       len = vp2attr_vsa(packet, original, secret, pvp,
                          vp->attribute, vp->vendor,
                          ptr + ptr[1], room);
        if (len < 0) return len;
 
+#ifndef NDEBUG
+       if ((fr_debug_flag > 3) && fr_log_fp) {
+               fprintf(fr_log_fp, "\t\t%02x %02x  %02x%02x%02x%02x (%u)  ",
+                      ptr[0], ptr[1],
+                      ptr[2], ptr[3], ptr[4], ptr[5],
+                      (ptr[3] << 16) | (ptr[4] << 8) | ptr[5]);
+               print_hex_data(ptr + 6, len, 3);
+       }
+#endif
+
        ptr[1] += len;
 
        return ptr[1];
 }
 
 
-/*
- *     Encode an RFC standard attribute 1..255
+/**
+ * @brief Encode an RFC standard attribute 1..255
  */
 int rad_vp2rfc(const RADIUS_PACKET *packet,
               const RADIUS_PACKET *original,
-              const char *secret, VALUE_PAIR *vp,
+              const char *secret, const VALUE_PAIR **pvp,
               uint8_t *ptr, size_t room)
 {
+       const VALUE_PAIR *vp = *pvp;
+
        if (vp->vendor != 0) {
                fr_strerror_printf("rad_vp2rfc called with VSA");
                return -1;
@@ -1307,50 +1507,84 @@ int rad_vp2rfc(const RADIUS_PACKET *packet,
                return -1;
        }
 
+       /*
+        *      Only CUI is allowed to have zero length.
+        *      Thank you, WiMAX!
+        */
        if ((vp->length == 0) &&
-           (vp->attribute != PW_CHARGEABLE_USER_IDENTITY)) {
-               return 0;
+           (vp->attribute == PW_CHARGEABLE_USER_IDENTITY)) {
+               ptr[0] = PW_CHARGEABLE_USER_IDENTITY;
+               ptr[1] = 2;
+
+               *pvp = vp->next;
+               return 2;
        }
 
-       return vp2attr_rfc(packet, original, secret, vp, vp->attribute,
+       return vp2attr_rfc(packet, original, secret, pvp, vp->attribute,
                           ptr, room);
 }
 
 
-/*
- *     Parse a data structure into a RADIUS attribute.
+/**
+ * @brief Parse a data structure into a RADIUS attribute.
  */
 int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
-               const char *secret, VALUE_PAIR *vp, uint8_t *start,
+               const char *secret, const VALUE_PAIR **pvp, uint8_t *start,
                size_t room)
 {
+       const VALUE_PAIR *vp;
+
+       if (!pvp || !*pvp || !start || (room <= 2)) return -1;
+
+       vp = *pvp;
+
        /*
         *      RFC format attributes take the fast path.
         */
        if (vp->vendor == 0) {
                if (vp->attribute > 255) return 0;
 
-               return rad_vp2rfc(packet, original, secret, vp,
+               /*
+                *      Message-Authenticator is hard-coded.
+                */
+               if (vp->attribute == PW_MESSAGE_AUTHENTICATOR) {
+                       if (room < 18) return -1;
+                       
+                       debug_pair(vp);
+                       start[0] = PW_MESSAGE_AUTHENTICATOR;
+                       start[1] = 18;
+                       memset(start + 2, 0, 16);
+#ifndef NDEBUG
+                       if ((fr_debug_flag > 3) && fr_log_fp) {
+                               fprintf(fr_log_fp, "\t\t50 12 ...\n");
+                       }
+#endif
+
+                       *pvp = (*pvp)->next;
+                       return 18;
+               }
+
+               return rad_vp2rfc(packet, original, secret, pvp,
                                  start, room);
        }
 
        if (vp->vendor > FR_MAX_VENDOR) {
-               return rad_vp2extended(packet, original, secret, vp,
+               return rad_vp2extended(packet, original, secret, pvp,
                                       start, room);
        }
 
-       if (vp->vendor == VENDORPEC_WIMAX) {
-               return rad_vp2wimax(packet, original, secret, vp,
+       if (vp->flags.wimax) {
+               return rad_vp2wimax(packet, original, secret, pvp,
                                    start, room);
        }
 
-       return rad_vp2vsa(packet, original, secret, vp,
+       return rad_vp2vsa(packet, original, secret, pvp,
                          start, room);
 }
 
 
-/*
- *     Encode a packet.
+/**
+ * @brief Encode a packet.
  */
 int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
               const char *secret)
@@ -1359,7 +1593,7 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
        uint8_t         *ptr;
        uint16_t        total_length;
        int             len;
-       VALUE_PAIR      *reply;
+       const VALUE_PAIR        *reply;
        const char      *what;
        char            ip_buffer[128];
 
@@ -1441,7 +1675,11 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
        /*
         *      Loop over the reply attributes for the packet.
         */
-       for (reply = packet->vps; reply; reply = reply->next) {
+       reply = packet->vps;
+       while (reply) {
+               size_t last_len;
+               const char *last_name = NULL;
+
                /*
                 *      Ignore non-wire attributes, but allow extended
                 *      attributes.
@@ -1457,9 +1695,11 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                        if (reply->attribute == PW_RAW_ATTRIBUTE) {
                                memcpy(ptr, reply->vp_octets, reply->length);
                                len = reply->length;
+                               reply = reply->next;
                                goto next;
                        }
 #endif
+                       reply = reply->next;
                        continue;
                }
 
@@ -1468,31 +1708,18 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                 *      length and initial value.
                 */
                if (reply->attribute == PW_MESSAGE_AUTHENTICATOR) {
-                       reply->length = AUTH_VECTOR_LEN;
-                       memset(reply->vp_strvalue, 0, AUTH_VECTOR_LEN);
-
                        /*
                         *      Cache the offset to the
                         *      Message-Authenticator
                         */
                        packet->offset = total_length;
+                       last_len = 16;
+               } else {
+                       last_len = reply->length;
                }
+               last_name = reply->name;
 
-               /*
-                *      Print out ONLY the attributes which
-                *      we're sending over the wire, and print
-                *      them out BEFORE they're encrypted.
-                */
-               debug_pair(reply);
-
-               /*
-                *      Skip attributes that have already been
-                *      encoded.  This can be done when the "vp2attr"
-                *      function sees multiple contiguous TLVs.
-                */
-               if (reply->flags.encoded) continue;
-
-               len = rad_vp2attr(packet, original, secret, reply, ptr,
+               len = rad_vp2attr(packet, original, secret, &reply, ptr,
                                  ((uint8_t *) data) + sizeof(data) - ptr);
                if (len < 0) return -1;
 
@@ -1500,13 +1727,17 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                 *      Failed to encode the attribute, likely because
                 *      the packet is full.
                 */
-               if ((len == 0) &&
-                   (total_length > (sizeof(data) - 2 - reply->length))) {
-                       DEBUG("WARNING: Attributes are too long for packet.  Discarding data past %d bytes", total_length);
-                       break;
+               if (len == 0) {
+                       if (last_len != 0) {
+                               DEBUG("WARNING: Failed encoding attribute %s\n", last_name);
+                       } else {
+                               DEBUG("WARNING: Skipping zero-length attribute %s\n", last_name);
+                       }
                }
 
-       next:
+#ifndef NDEBUG
+       next:                   /* Used only for Raw-Attribute */
+#endif
                ptr += len;
                total_length += len;
        } /* done looping over all attributes */
@@ -1536,8 +1767,8 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
 }
 
 
-/*
- *     Sign a previously encoded packet.
+/**
+ * @brief Sign a previously encoded packet.
  */
 int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
             const char *secret)
@@ -1566,8 +1797,12 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                uint8_t calc_auth_vector[AUTH_VECTOR_LEN];
 
                switch (packet->code) {
-               case PW_ACCOUNTING_REQUEST:
                case PW_ACCOUNTING_RESPONSE:
+                       if (original && original->code == PW_STATUS_SERVER) {
+                               goto do_ack;
+                       }
+
+               case PW_ACCOUNTING_REQUEST:
                case PW_DISCONNECT_REQUEST:
                case PW_DISCONNECT_ACK:
                case PW_DISCONNECT_NAK:
@@ -1577,6 +1812,7 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                        memset(hdr->vector, 0, AUTH_VECTOR_LEN);
                        break;
 
+               do_ack:
                case PW_AUTHENTICATION_ACK:
                case PW_AUTHENTICATION_REJECT:
                case PW_ACCESS_CHALLENGE:
@@ -1649,8 +1885,8 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
        return 0;
 }
 
-/*
- *     Reply to the request.  Also attach
+/**
+ * @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,
@@ -1710,6 +1946,10 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                }
        }
 
+#ifndef NDEBUG
+       if ((fr_debug_flag > 3) && fr_log_fp) rad_print_hex(packet);
+#endif
+
        /*
         *      And send it on it's way.
         */
@@ -1718,9 +1958,11 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                          &packet->dst_ipaddr, packet->dst_port);
 }
 
-/*
- *     Do a comparison of two authentication digests by comparing
- *     the FULL digest.  Otehrwise, the server can be subject to
+/**
+ * @brief Do a comparison of two authentication digests by comparing
+ *     the FULL digest.
+ *
+ *     Otherwise, the server can be subject to
  *     timing attacks that allow attackers find a valid message
  *     authenticator.
  *
@@ -1739,8 +1981,8 @@ int rad_digest_cmp(const uint8_t *a, const uint8_t *b, size_t length)
 }
 
 
-/*
- *     Validates the requesting client NAS.  Calculates the
+/**
+ * @brief Validates the requesting client NAS.  Calculates the
  *     signature based on the clients private key.
  */
 static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret)
@@ -1772,8 +2014,8 @@ static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret)
 }
 
 
-/*
- *     Validates the requesting client NAS.  Calculates the
+/**
+ * @brief Validates the requesting client NAS.  Calculates the
  *     signature based on the clients private key.
  */
 static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original,
@@ -1815,8 +2057,8 @@ static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original,
 }
 
 
-/*
- *     Check if a set of RADIUS formatted TLVs are OK.
+/**
+ * @brief Check if a set of RADIUS formatted TLVs are OK.
  */
 int rad_tlv_ok(const uint8_t *data, size_t length,
               size_t dv_type, size_t dv_length)
@@ -1902,8 +2144,8 @@ int rad_tlv_ok(const uint8_t *data, size_t length,
 }
 
 
-/*
- *     See if the data pointed to by PTR is a valid RADIUS packet.
+/**
+ * @brief 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.
@@ -2213,8 +2455,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
 }
 
 
-/*
- *     Receive UDP client requests, and fill in
+/**
+ * @brief Receive UDP client requests, and fill in
  *     the basics of a RADIUS_PACKET structure.
  */
 RADIUS_PACKET *rad_recv(int fd, int flags)
@@ -2320,12 +2562,16 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
                      packet->id, (int) packet->data_len);
        }
 
+#ifndef NDEBUG
+       if ((fr_debug_flag > 3) && fr_log_fp) rad_print_hex(packet);
+#endif
+
        return packet;
 }
 
 
-/*
- *     Verify the signature of a packet.
+/**
+ * @brief Verify the signature of a packet.
  */
 int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
               const char *secret)
@@ -2364,8 +2610,13 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                        default:
                                break;
 
-                       case PW_ACCOUNTING_REQUEST:
                        case PW_ACCOUNTING_RESPONSE:
+                               if (original &&
+                                   (original->code == PW_STATUS_SERVER)) {
+                                       goto do_ack;
+                               }
+
+                       case PW_ACCOUNTING_REQUEST:
                        case PW_DISCONNECT_REQUEST:
                        case PW_DISCONNECT_ACK:
                        case PW_DISCONNECT_NAK:
@@ -2375,6 +2626,7 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                                memset(packet->data + 4, 0, AUTH_VECTOR_LEN);
                                break;
 
+                       do_ack:
                        case PW_AUTHENTICATION_ACK:
                        case PW_AUTHENTICATION_REJECT:
                        case PW_ACCESS_CHALLENGE:
@@ -2494,8 +2746,8 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
 }
 
 
-/*
- *     Create a "raw" attribute from the attribute contents.
+/**
+ * @brief Create a "raw" attribute from the attribute contents.
  */
 static ssize_t data2vp_raw(UNUSED const RADIUS_PACKET *packet,
                           UNUSED const RADIUS_PACKET *original,
@@ -2519,12 +2771,13 @@ static ssize_t data2vp_raw(UNUSED const RADIUS_PACKET *packet,
        vp->length = length;
 
        /*
-        *      If the data is too large, mark it as a "TLV".
+        *      If it's short, put it into the array.  If it's too
+        *      long, flag it as such, and put it somewhere else;
         */
        if (length <= sizeof(vp->vp_octets)) {
                memcpy(vp->vp_octets, data, length);
        } else {
-               vp->type = PW_TYPE_TLV;
+               vp->type |= PW_FLAG_LONG;
                vp->vp_tlv = malloc(length);
                if (!vp->vp_tlv) {
                        pairfree(&vp);
@@ -2547,10 +2800,9 @@ static ssize_t data2vp_tlvs(const RADIUS_PACKET *packet,
                            const uint8_t *start, size_t length,
                            VALUE_PAIR **pvp);
 
-/*
- *     Create any kind of VP from the attribute contents.
- *
- *     Will return -1 on error, or "length".
+/**
+ * @brief Create any kind of VP from the attribute contents.
+ * @return -1 on error, or "length".
  */
 static ssize_t data2vp_any(const RADIUS_PACKET *packet,
                           const RADIUS_PACKET *original,
@@ -2562,6 +2814,7 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
        int data_offset = 0;
        DICT_ATTR *da;
        VALUE_PAIR *vp = NULL;
+       uint8_t buffer[256];
 
        if (length == 0) {
                /*
@@ -2586,6 +2839,7 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
         *      Unknown attribute.  Create it as a "raw" attribute.
         */
        if (!da) {
+               VP_TRACE("Not found %u.%u\n", vendor, attribute);
        raw:
                if (vp) pairfree(&vp);
                return data2vp_raw(packet, original, secret,
@@ -2597,12 +2851,38 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
         *      they can't be encrypted.
         */
        if (da->type == PW_TYPE_TLV) {
+               VP_TRACE("Found TLV %u.%u\n", vendor, attribute);
                return data2vp_tlvs(packet, original, secret,
                                    attribute, vendor, nest,
                                    data, length, pvp);
        }
 
        /*
+        *      The data is very long.
+        */
+       if (length > sizeof(vp->vp_octets)) {
+               /*
+                *      Long encrypted attributes are forbidden.
+                */
+               if (da->flags.encrypt != FLAG_ENCRYPT_NONE) goto raw;
+
+#ifndef NDEBUG
+               /*
+                *      Catch programming errors.
+                */
+               if ((da->type != PW_TYPE_STRING) &&
+                   (da->type != PW_TYPE_OCTETS)) goto raw;
+
+#endif
+
+               /*
+                *      FIXME: Figure out how to deal with long
+                *      strings and binary data!
+                */
+               goto raw;
+       }
+
+       /*
         *      The attribute is known, and well formed.  We can now
         *      create it.  The main failure from here on in is being
         *      out of memory.
@@ -2633,28 +2913,29 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
        /*
         *      Copy the data to be decrypted
         */
-       vp->length = length - data_offset;
-       memcpy(&vp->vp_octets[0], data + data_offset, vp->length);
+       vp->length = length - data_offset;      
+       memcpy(buffer, data + data_offset, vp->length);
 
        /*
         *      Decrypt the attribute.
         */
-       switch (vp->flags.encrypt) {
+       if (secret && packet) switch (vp->flags.encrypt) {
                /*
                 *  User-Password
                 */
        case FLAG_ENCRYPT_USER_PASSWORD:
                if (original) {
-                       rad_pwdecode(vp->vp_strvalue,
+                       rad_pwdecode((char *) buffer,
                                     vp->length, secret,
                                     original->vector);
                } else {
-                       rad_pwdecode(vp->vp_strvalue,
+                       rad_pwdecode((char *) buffer,
                                     vp->length, secret,
                                     packet->vector);
                }
+               buffer[253] = '\0';
                if (vp->attribute == PW_USER_PASSWORD) {
-                       vp->length = strlen(vp->vp_strvalue);
+                       vp->length = strlen((char *) buffer);
                }
                break;
 
@@ -2665,7 +2946,7 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
        case FLAG_ENCRYPT_TUNNEL_PASSWORD:
                if (!original) goto raw;
 
-               if (rad_tunnel_pwdecode(vp->vp_octets, &vp->length,
+               if (rad_tunnel_pwdecode(buffer, &vp->length,
                                        secret, original->vector) < 0) {
                        goto raw;
                }
@@ -2683,10 +2964,10 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
                        make_secret(my_digest,
                                    original->vector,
                                    secret, data);
-                       memcpy(vp->vp_strvalue, my_digest,
+                       memcpy(buffer, my_digest,
                               AUTH_VECTOR_LEN );
-                       vp->vp_strvalue[AUTH_VECTOR_LEN] = '\0';
-                       vp->length = strlen(vp->vp_strvalue);
+                       buffer[AUTH_VECTOR_LEN] = '\0';
+                       vp->length = strlen((char *) buffer);
                }
                break;
 
@@ -2697,51 +2978,49 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
 
        switch (vp->type) {
        case PW_TYPE_STRING:
+               memcpy(vp->vp_strvalue, buffer, vp->length);
+               vp->vp_strvalue[vp->length] = '\0';
+               break;
+
        case PW_TYPE_OCTETS:
        case PW_TYPE_ABINARY:
-               /* nothing more to do */
+               memcpy(vp->vp_octets, buffer, vp->length);
                break;
 
        case PW_TYPE_BYTE:
                if (vp->length != 1) goto raw;
 
-               vp->vp_integer = vp->vp_octets[0];
+               vp->vp_integer = buffer[0];
                break;
 
 
        case PW_TYPE_SHORT:
                if (vp->length != 2) goto raw;
 
-               vp->vp_integer = (vp->vp_octets[0] << 8) | vp->vp_octets[1];
+               vp->vp_integer = (buffer[0] << 8) | buffer[1];
                break;
 
        case PW_TYPE_INTEGER:
                if (vp->length != 4) goto raw;
 
-               memcpy(&vp->vp_integer, vp->vp_octets, 4);
+               memcpy(&vp->vp_integer, buffer, 4);
                vp->vp_integer = ntohl(vp->vp_integer);
 
                if (vp->flags.has_tag) vp->vp_integer &= 0x00ffffff;
+               break;
 
-               /*
-                *      Try to get named VALUEs
-                */
-               {
-                       DICT_VALUE *dval;
-                       dval = dict_valbyattr(vp->attribute, vp->vendor,
-                                             vp->vp_integer);
-                       if (dval) {
-                               strlcpy(vp->vp_strvalue,
-                                       dval->name,
-                                       sizeof(vp->vp_strvalue));
-                       }
-               }
+       case PW_TYPE_INTEGER64:
+               if (vp->length != 8) goto raw;
+
+               /* vp_integer64 is a union with vp_octets */
+               memcpy(&vp->vp_integer64, buffer, 8);
+               vp->vp_integer64 = ntohll(vp->vp_integer64);
                break;
 
        case PW_TYPE_DATE:
                if (vp->length != 4) goto raw;
 
-               memcpy(&vp->vp_date, vp->vp_octets, 4);
+               memcpy(&vp->vp_date, buffer, 4);
                vp->vp_date = ntohl(vp->vp_date);
                break;
 
@@ -2749,7 +3028,7 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
        case PW_TYPE_IPADDR:
                if (vp->length != 4) goto raw;
 
-               memcpy(&vp->vp_ipaddr, vp->vp_octets, 4);
+               memcpy(&vp->vp_ipaddr, buffer, 4);
                break;
 
                /*
@@ -2757,7 +3036,7 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
                 */
        case PW_TYPE_IFID:
                if (vp->length != 8) goto raw;
-               /* vp->vp_ifid == vp->vp_octets */
+               memcpy(&vp->vp_ifid, buffer, 8);
                break;
 
                /*
@@ -2765,7 +3044,7 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
                 */
        case PW_TYPE_IPV6ADDR:
                if (vp->length != 16) goto raw;
-               /* vp->vp_ipv6addr == vp->vp_octets */
+               memcpy(&vp->vp_ipv6addr, buffer, 16);
                break;
 
                /*
@@ -2779,14 +3058,15 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
                 */
        case PW_TYPE_IPV6PREFIX:
                if (vp->length < 2 || vp->length > 18) goto raw;
-               if (vp->vp_octets[1] > 128) goto raw;
+               if (buffer[1] > 128) goto raw;
 
                /*
                 *      FIXME: double-check that
                 *      (vp->vp_octets[1] >> 3) matches vp->length + 2
                 */
+               memcpy(&vp->vp_ipv6prefix, buffer, vp->length);
                if (vp->length < 18) {
-                       memset(vp->vp_octets + vp->length, 0,
+                       memset(((uint8_t *)vp->vp_ipv6prefix) + vp->length, 0,
                               18 - vp->length);
                }
                break;
@@ -2798,9 +3078,8 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
                 *      Overload vp_integer for ntohl, which takes
                 *      uint32_t, not int32_t
                 */
-               memcpy(&vp->vp_integer, vp->vp_octets, 4);
+               memcpy(&vp->vp_integer, buffer, 4);
                vp->vp_integer = ntohl(vp->vp_integer);
-               memcpy(&vp->vp_signed, &vp->vp_integer, 4);
                break;
 
        case PW_TYPE_TLV:
@@ -2811,12 +3090,12 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
        case PW_TYPE_COMBO_IP:
                if (vp->length == 4) {
                        vp->type = PW_TYPE_IPADDR;
-                       memcpy(&vp->vp_ipaddr, vp->vp_octets, 4);
+                       memcpy(&vp->vp_ipaddr, buffer, 4);
                        break;
 
                } else if (vp->length == 16) {
                        vp->type = PW_TYPE_IPV6ADDR;
-                       /* vp->vp_ipv6addr == vp->vp_octets */
+                       memcpy(&vp->vp_ipv6addr, buffer, 16);
                        break;
 
                }
@@ -2832,8 +3111,8 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Convert a top-level VSA to a VP.
+/**
+ * @brief Convert a top-level VSA to a VP.
  */
 static ssize_t attr2vp_vsa(const RADIUS_PACKET *packet,
                           const RADIUS_PACKET *original,
@@ -2919,8 +3198,8 @@ static ssize_t attr2vp_vsa(const RADIUS_PACKET *packet,
        return dv_type + dv_length + attrlen;
 }
 
-/*
- *     Convert one or more TLVs to VALUE_PAIRs.  This function can
+/**
+ * @brief Convert one or more TLVs to VALUE_PAIRs.  This function can
  *     be called recursively...
  */
 static ssize_t data2vp_tlvs(const RADIUS_PACKET *packet,
@@ -2968,6 +3247,7 @@ static ssize_t data2vp_tlvs(const RADIUS_PACKET *packet,
         *      The *entire* TLV is malformed.
         */
        if (rad_tlv_ok(data, length, dv_type, dv_length) < 0) {
+               VP_TRACE("TLV malformed %u.%u\n", vendor, attribute);
                return data2vp_raw(packet, original, secret,
                                   attribute, vendor, data, length, pvp);
        }
@@ -3062,8 +3342,9 @@ static ssize_t data2vp_tlvs(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Group "continued" attributes together, and create VPs from them.
+/**
+ * @brief Group "continued" attributes together, and create VPs from them.
+ *
  *     The caller ensures that the RADIUS packet is OK, and that the
  *     continuations have all been checked.
  */
@@ -3101,6 +3382,7 @@ static ssize_t data2vp_continued(const RADIUS_PACKET *packet,
        while (left > 0) {
 #ifndef NDEBUG
                if (data >= (start + length)) {
+                       free(attr);
                        fr_strerror_printf("data2vp_continued: Internal sanity check failed");
                        return -1;
                }
@@ -3121,8 +3403,8 @@ static ssize_t data2vp_continued(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Create a "raw" VALUE_PAIR from a RADIUS attribute.
+/**
+ * @brief Create a "raw" VALUE_PAIR from a RADIUS attribute.
  */
 ssize_t rad_attr2vp_raw(const RADIUS_PACKET *packet,
                        const RADIUS_PACKET *original,
@@ -3145,16 +3427,16 @@ ssize_t rad_attr2vp_raw(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Get the length of the data portion of all of the contiguous
+/**
+ * @brief Get the length of the data portion of all of the contiguous
  *     continued attributes.
- *
+ * @return
  *     0 for "no continuation"
  *     -1 on malformed packets (continuation followed by non-wimax, etc.)
  */
-static ssize_t wimax_attrlen(const uint8_t *start, const uint8_t *end)
+static ssize_t wimax_attrlen(uint32_t vendor,
+                            const uint8_t *start, const uint8_t *end)
 {
-       uint32_t lvalue = htonl(VENDORPEC_WIMAX);
        ssize_t total;
        const uint8_t *data = start;
 
@@ -3168,7 +3450,7 @@ static ssize_t wimax_attrlen(const uint8_t *start, const uint8_t *end)
 
                if ((data[0] != PW_VENDOR_SPECIFIC) ||
                    (data[1] < 9) ||
-                   (memcmp(data + 2, &lvalue, 4) != 0) ||
+                   (memcmp(data + 2, &vendor, 4) != 0) ||
                    (data[6] != start[6]) ||
                    ((data[7] + 6) != data[1])) return -1;
 
@@ -3181,11 +3463,12 @@ static ssize_t wimax_attrlen(const uint8_t *start, const uint8_t *end)
 }
 
 
-/*
- *     Get the length of the data portion of all of the contiguous
+/**
+ * @brief Get the length of the data portion of all of the contiguous
  *     continued attributes.
  *
- *     0 for "no continuation"
+ * @return
+ *     0 for "no continuation"
  *     -1 on malformed packets (continuation followed by non-wimax, etc.)
  */
 static ssize_t extended_attrlen(const uint8_t *start, const uint8_t *end)
@@ -3213,8 +3496,8 @@ static ssize_t extended_attrlen(const uint8_t *start, const uint8_t *end)
 }
 
 
-/*
- *     Create WiMAX VALUE_PAIRs from a RADIUS attribute.
+/**
+ * @brief Create WiMAX VALUE_PAIRs from a RADIUS attribute.
  */
 ssize_t rad_attr2vp_wimax(const RADIUS_PACKET *packet,
                          const RADIUS_PACKET *original,
@@ -3248,11 +3531,16 @@ ssize_t rad_attr2vp_wimax(const RADIUS_PACKET *packet,
        lvalue = ntohl(lvalue);
 
        /*
-        *      Not WiMAX
+        *      Not WiMAX format.
         */
        if (lvalue != VENDORPEC_WIMAX) {
-               fr_strerror_printf("rad_attr2vp_wimax: Not a WiMAX attribute");
-               return -1;
+               DICT_VENDOR *dv;
+
+               dv = dict_vendorbyvalue(lvalue);
+               if (!dv || !dv->flags) {
+                       fr_strerror_printf("rad_attr2vp_wimax: Not a WiMAX attribute");
+                       return -1;
+               }
        }
 
        /*
@@ -3270,7 +3558,7 @@ ssize_t rad_attr2vp_wimax(const RADIUS_PACKET *packet,
         *      Attribute is continued.  Do some more work.
         */
        if (data[8] != 0) {
-               my_len = wimax_attrlen(data, data + length);
+               my_len = wimax_attrlen(htonl(lvalue), data, data + length);
                if (my_len < 0) {
                        return rad_attr2vp_raw(packet, original, secret,
                                               data, length, pvp);
@@ -3278,7 +3566,7 @@ ssize_t rad_attr2vp_wimax(const RADIUS_PACKET *packet,
 
                return data2vp_continued(packet, original, secret,
                                         data, length, pvp, 0,
-                                        data[6], VENDORPEC_WIMAX,
+                                        data[6], lvalue,
                                         9, 9, my_len);
        }
 
@@ -3289,8 +3577,8 @@ ssize_t rad_attr2vp_wimax(const RADIUS_PACKET *packet,
        return data[1];
 }
 
-/*
- *     Create Vendor-Specifc VALUE_PAIRs from a RADIUS attribute.
+/**
+ * @brief Create Vendor-Specifc VALUE_PAIRs from a RADIUS attribute.
  */
 ssize_t rad_attr2vp_vsa(const RADIUS_PACKET *packet,
                        const RADIUS_PACKET *original,
@@ -3329,6 +3617,7 @@ ssize_t rad_attr2vp_vsa(const RADIUS_PACKET *packet,
         *      WiMAX gets its own set of magic.
         */
        if (lvalue == VENDORPEC_WIMAX) {
+       wimax:
                return rad_attr2vp_wimax(packet, original, secret,
                                         data, length, pvp);
        }
@@ -3338,6 +3627,8 @@ ssize_t rad_attr2vp_vsa(const RADIUS_PACKET *packet,
        if (dv) {
                dv_type = dv->type;
                dv_length = dv->length;
+
+               if (dv->flags) goto wimax;
        }
 
        /*
@@ -3353,19 +3644,21 @@ ssize_t rad_attr2vp_vsa(const RADIUS_PACKET *packet,
                             data + 6, data[1] - 6, pvp);
        if (my_len < 0) return my_len;
 
-#ifndef NDEBUG
+       /*
+        *      Incomplete decode means that something is wrong
+        *      with the attribute.  Back up, and make it "raw".
+        */
        if (my_len != (data[1] - 6)) {
                pairfree(pvp);
-               fr_strerror_printf("rad_attr2vp_vsa: Incomplete decode");
-               return -1;
+               return rad_attr2vp_raw(packet, original, secret,
+                                      data, length, pvp);
        }
-#endif
 
        return data[1];
 }
 
-/*
- *     Create an "extended" VALUE_PAIR from a RADIUS attribute.
+/**
+ * @brief Create an "extended" VALUE_PAIR from a RADIUS attribute.
  */
 ssize_t rad_attr2vp_extended(const RADIUS_PACKET *packet,
                             const RADIUS_PACKET *original,
@@ -3389,7 +3682,8 @@ ssize_t rad_attr2vp_extended(const RADIUS_PACKET *packet,
        }
 
        da = dict_attrbyvalue(data[0], vendor);
-       if (!da) {
+       if (!da ||
+           (!da->flags.extended && !da->flags.extended_flags)) {
                fr_strerror_printf("rad_attr2vp_extended: Attribute is not extended format");
                return -1;
        }
@@ -3457,7 +3751,7 @@ ssize_t rad_attr2vp_extended(const RADIUS_PACKET *packet,
                
                /*
                 *      Pack the *encapsulating* attribute number into
-                *      the vendor id.
+                *      the vendor id.  This number should be >= 241.
                 */
                vendor |= start[0] * FR_MAX_VENDOR;
                shift = 0;
@@ -3497,8 +3791,8 @@ ssize_t rad_attr2vp_extended(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Create a "standard" RFC VALUE_PAIR from the given data.
+/**
+ * @brief Create a "standard" RFC VALUE_PAIR from the given data.
  */
 ssize_t rad_attr2vp_rfc(const RADIUS_PACKET *packet,
                        const RADIUS_PACKET *original,
@@ -3519,8 +3813,8 @@ ssize_t rad_attr2vp_rfc(const RADIUS_PACKET *packet,
        return data[1];
 }      
 
-/*
- *     Create a "normal" VALUE_PAIR from the given data.
+/**
+ * @brief Create a "normal" VALUE_PAIR from the given data.
  */
 ssize_t rad_attr2vp(const RADIUS_PACKET *packet,
                    const RADIUS_PACKET *original,
@@ -3553,11 +3847,9 @@ ssize_t rad_attr2vp(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Calculate/check digest, and decode radius attributes.
- *     Returns:
- *     -1 on decoding error
- *     0 on success
+/**
+ * @brief Calculate/check digest, and decode radius attributes.
+ * @return -1 on decoding error, 0 on success
  */
 int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
               const char *secret)
@@ -3646,8 +3938,8 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
 }
 
 
-/*
- *     Encode password.
+/**
+ * @brief Encode password.
  *
  *     We assume that the passwd buffer passed is big enough.
  *     RFC2138 says the password is max 128 chars, so the size
@@ -3719,8 +4011,8 @@ int rad_pwencode(char *passwd, size_t *pwlen, const char *secret,
        return 0;
 }
 
-/*
- *     Decode password.
+/**
+ * @brief Decode password.
  */
 int rad_pwdecode(char *passwd, size_t pwlen, const char *secret,
                 const uint8_t *vector)
@@ -3785,8 +4077,8 @@ int rad_pwdecode(char *passwd, size_t pwlen, const char *secret,
 }
 
 
-/*
- *     Encode Tunnel-Password attributes when sending them out on the wire.
+/**
+ * @brief Encode Tunnel-Password attributes when sending them out on the wire.
  *
  *     int *pwlen is updated to the new length of the encrypted
  *     password - a multiple of 16 bytes.
@@ -3870,8 +4162,8 @@ int rad_tunnel_pwencode(char *passwd, size_t *pwlen, const char *secret,
        return 0;
 }
 
-/*
- *     Decode Tunnel-Password encrypted attributes.
+/**
+ * @brief Decode Tunnel-Password encrypted attributes.
  *
  *      Defined in RFC-2868, this uses a two char SALT along with the
  *      initial intermediate value, to differentiate it from the
@@ -3976,10 +4268,10 @@ int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, const char *secret,
        return reallen;
 }
 
-/*
- *     Encode a CHAP password
+/**
+ * @brief Encode a CHAP password
  *
- *     FIXME: might not work with Ascend because
+ *     @bug FIXME: might not work with Ascend because
  *     we use vp->length, and Ascend gear likes
  *     to send an extra '\0' in the string!
  */
@@ -4034,8 +4326,8 @@ int rad_chap_encode(RADIUS_PACKET *packet, uint8_t *output, int id,
 }
 
 
-/*
- *     Seed the random number generator.
+/**
+ * @brief Seed the random number generator.
  *
  *     May be called any number of times.
  */
@@ -4088,8 +4380,8 @@ void fr_rand_seed(const void *data, size_t size)
 }
 
 
-/*
- *     Return a 32-bit random number.
+/**
+ * @brief Return a 32-bit random number.
  */
 uint32_t fr_rand(void)
 {
@@ -4112,8 +4404,8 @@ uint32_t fr_rand(void)
 }
 
 
-/*
- *     Allocate a new RADIUS_PACKET
+/**
+ * @brief Allocate a new RADIUS_PACKET
  */
 RADIUS_PACKET *rad_alloc(int newvector)
 {
@@ -4175,8 +4467,8 @@ RADIUS_PACKET *rad_alloc_reply(RADIUS_PACKET *packet)
 }
 
 
-/*
- *     Free a RADIUS_PACKET
+/**
+ * @brief Free a RADIUS_PACKET
  */
 void rad_free(RADIUS_PACKET **radius_packet_ptr)
 {