Fix logic for using udpfromto
[freeradius.git] / src / lib / radius.c
index 774dcd1..37757a8 100644 (file)
 #include       <freeradius-devel/ident.h>
 RCSID("$Id$")
 
-#include       <freeradius-devel/autoconf.h>
+#include       <freeradius-devel/libradius.h>
 #include       <freeradius-devel/md5.h>
 
-#include       <stdlib.h>
-
-#ifdef HAVE_UNISTD_H
-#include       <unistd.h>
-#endif
-
 #include       <fcntl.h>
 #include       <ctype.h>
 
@@ -39,18 +33,10 @@ RCSID("$Id$")
 #include       <freeradius-devel/udpfromto.h>
 #endif
 
-
 #ifdef HAVE_MALLOC_H
 #include       <malloc.h>
 #endif
 
-#ifdef WIN32
-#include       <process.h>
-#endif
-
-#include       <freeradius-devel/missing.h>
-#include       <freeradius-devel/libradius.h>
-
 /*
  *  The RFC says 4096 octets max, and most packets are less than 256.
  */
@@ -67,7 +53,8 @@ RCSID("$Id$")
  *     is unsigned, and the attacker can use resources on the server,
  *     even if the end request is rejected.
  */
-int librad_max_attributes = 0;
+int fr_max_attributes = 0;
+FILE *fr_log_fp = NULL;
 
 typedef struct radius_packet_t {
   uint8_t      code;
@@ -77,13 +64,11 @@ typedef struct radius_packet_t {
   uint8_t      data[1];
 } radius_packet_t;
 
-static lrad_randctx lrad_rand_pool;    /* across multiple calls */
-static int lrad_rand_initialized = 0;
+static fr_randctx fr_rand_pool;        /* across multiple calls */
+static int fr_rand_initialized = 0;
 static unsigned int salt_offset = 0;
 
-
-#define MAX_PACKET_CODE (52)
-static const char *packet_codes[] = {
+const char *fr_packet_codes[FR_MAX_PACKET_CODE] = {
   "",
   "Access-Request",
   "Access-Accept",
@@ -139,72 +124,107 @@ static const char *packet_codes[] = {
 };
 
 
+void fr_printf_log(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       if ((fr_debug_flag == 0) || !fr_log_fp) {
+               va_end(ap);
+               return;
+       }
+
+       vfprintf(fr_log_fp, fmt, ap);
+       va_end(ap);
+
+       return;
+}
+
+static void print_hex(RADIUS_PACKET *packet)
+{
+       int i;
+
+       if (!packet->data) 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) |
+                                  (packet->data[3])));
+       printf("  Vector:\t");
+       for (i = 4; i < 20; i++) {
+               printf("%02x", packet->data[i]);
+       }
+       printf("\n");
+
+       if (packet->data_len > 20) {
+               int total;
+               const uint8_t *ptr;
+               printf("  Data:");
+
+               total = packet->data_len - 20;
+               ptr = packet->data + 20;
+
+               while (total > 0) {
+                       int attrlen;
+
+                       printf("\t\t");
+                       if (total < 2) { /* too short */
+                               printf("%02x\n", *ptr);
+                               break;
+                       }
+
+                       if (ptr[1] > total) { /* too long */
+                               for (i = 0; i < total; i++) {
+                                       printf("%02x ", ptr[i]);
+                               }
+                               break;
+                       }
+
+                       printf("%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 ((attrlen & 0x0f) != 0x00) printf("\n");
+
+                       ptr += attrlen;
+                       total -= attrlen;
+               }
+       }
+       fflush(stdout);
+}
+
+
 /*
  *     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,
-                     lrad_ipaddr_t *src_ipaddr, lrad_ipaddr_t *dst_ipaddr,
-                     int dst_port)
+                     fr_ipaddr_t *src_ipaddr, int src_port,
+                     fr_ipaddr_t *dst_ipaddr, int dst_port)
 {
        struct sockaddr_storage dst;
-       socklen_t               sizeof_dst = sizeof(dst);
+       socklen_t               sizeof_dst;
 
 #ifdef WITH_UDPFROMTO
        struct sockaddr_storage src;
-       socklen_t               sizeof_src = sizeof(src);
-
-       memset(&src, 0, sizeof(src));
-#endif
-       memset(&dst, 0, sizeof(dst));
-
-       /*
-        *      IPv4 is supported.
-        */
-       if (dst_ipaddr->af == AF_INET) {
-               struct sockaddr_in      *s4;
-
-               s4 = (struct sockaddr_in *)&dst;
-               sizeof_dst = sizeof(struct sockaddr_in);
+       socklen_t               sizeof_src;
 
-               s4->sin_family = AF_INET;
-               s4->sin_addr = dst_ipaddr->ipaddr.ip4addr;
-               s4->sin_port = htons(dst_port);
-
-#ifdef WITH_UDPFROMTO
-               s4 = (struct sockaddr_in *)&src;
-               sizeof_src = sizeof(struct sockaddr_in);
-
-               s4->sin_family = AF_INET;
-               s4->sin_addr = src_ipaddr->ipaddr.ip4addr;
+       fr_ipaddr2sockaddr(src_ipaddr, src_port, &src, &sizeof_src);
+#else
+       src_port = src_port;    /* -Wunused */
 #endif
 
-       /*
-        *      IPv6 MAY be supported.
-        */
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
-       } else if (dst_ipaddr->af == AF_INET6) {
-               struct sockaddr_in6     *s6;
-
-               s6 = (struct sockaddr_in6 *)&dst;
-               sizeof_dst = sizeof(struct sockaddr_in6);
-               
-               s6->sin6_family = AF_INET6;
-               s6->sin6_addr = dst_ipaddr->ipaddr.ip6addr;
-               s6->sin6_port = htons(dst_port);
-
-#ifdef WITH_UDPFROMTO
-               return -1;      /* UDPFROMTO && IPv6 are not supported */
-#if 0
-               s6 = (struct sockaddr_in6 *)&src;
-               sizeof_src = sizeof(struct sockaddr_in6);
-
-               s6->sin6_family = AF_INET6;
-               s6->sin6_addr = src_ipaddr->ipaddr.ip6addr;
-#endif /* #if 0 */
-#endif /* WITH_UDPFROMTO */
-#endif /* HAVE_STRUCT_SOCKADDR_IN6 */
-       } else return -1;   /* Unknown address family, Die Die Die! */
+       if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &dst, &sizeof_dst)) {
+               return -1;
+       }
 
 #ifdef WITH_UDPFROMTO
        /*
@@ -213,10 +233,10 @@ static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
         *      And if they don't specify a source IP address, don't
         *      use udpfromto.
         */
-       if ((dst_ipaddr->af == AF_INET) ||
+       if ((dst_ipaddr->af == AF_INET) &&
            (src_ipaddr->af != AF_UNSPEC)) {
                return sendfromto(sockfd, data, data_len, flags,
-                                 (struct sockaddr *)&src, sizeof_src, 
+                                 (struct sockaddr *)&src, sizeof_src,
                                  (struct sockaddr *)&dst, sizeof_dst);
        }
 #else
@@ -226,18 +246,97 @@ static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
        /*
         *      No udpfromto, OR an IPv6 socket, fail gracefully.
         */
-       return sendto(sockfd, data, data_len, flags, 
-                     (struct sockaddr *)&dst, sizeof_dst);
+       return sendto(sockfd, data, data_len, flags,
+                     (struct sockaddr *) &dst, sizeof_dst);
+}
+
+
+void rad_recv_discard(int sockfd)
+{
+       uint8_t                 header[4];
+       struct sockaddr_storage src;
+       socklen_t               sizeof_src = sizeof(src);
+
+       recvfrom(sockfd, header, sizeof(header), 0,
+                (struct sockaddr *)&src, &sizeof_src);
+}
+
+
+ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port,
+                       int *code)
+{
+       ssize_t                 data_len, packet_len;
+       uint8_t                 header[4];
+       struct sockaddr_storage src;
+       socklen_t               sizeof_src = sizeof(src);
+
+       data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK,
+                           (struct sockaddr *)&src, &sizeof_src);
+       if (data_len < 0) {
+               if ((errno == EAGAIN) || (errno == EINTR)) return 0;
+               return -1;
+       }
+
+       /*
+        *      Too little data is available, discard the packet.
+        */
+       if (data_len < 4) {
+               recvfrom(sockfd, header, sizeof(header), 0,
+                        (struct sockaddr *)&src, &sizeof_src);
+               return 1;
+
+       } else {                /* we got 4 bytes of data. */
+               /*
+                *      See how long the packet says it is.
+                */
+               packet_len = (header[2] * 256) + header[3];
+
+               /*
+                *      The length in the packet says it's less than
+                *      a RADIUS header length: discard it.
+                */
+               if (packet_len < AUTH_HDR_LEN) {
+                       recvfrom(sockfd, header, sizeof(header), 0,
+                                (struct sockaddr *)&src, &sizeof_src);
+                       return 1;
+
+                       /*
+                        *      Enforce RFC requirements, for sanity.
+                        *      Anything after 4k will be discarded.
+                        */
+               } else if (packet_len > MAX_PACKET_LEN) {
+                       recvfrom(sockfd, header, sizeof(header), 0,
+                                (struct sockaddr *)&src, &sizeof_src);
+                       return 1;
+               }
+       }
+
+       /*
+        *      Convert AF.  If unknown, discard packet.
+        */
+       if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, src_port)) {
+               recvfrom(sockfd, header, sizeof(header), 0,
+                        (struct sockaddr *)&src, &sizeof_src);
+               return 1;
+       }
+
+       *code = header[0];
+
+       /*
+        *      The packet says it's this long, but the actual UDP
+        *      size could still be smaller.
+        */
+       return packet_len;
 }
 
 
 /*
- *     Wrapper for recvfrom, which handles recvfromto, IPv6, and all
+ *     wrapper for recvfrom, which handles recvfromto, IPv6, and all
  *     possible combinations.
  */
 static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
-                           lrad_ipaddr_t *src_ipaddr, uint16_t *src_port,
-                           lrad_ipaddr_t *dst_ipaddr, uint16_t *dst_port)
+                           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;
@@ -247,6 +346,7 @@ static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
        uint8_t                 header[4];
        void                    *buf;
        size_t                  len;
+       int                     port;
 
        memset(&src, 0, sizeof_src);
        memset(&dst, 0, sizeof_dst);
@@ -268,13 +368,19 @@ static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
         */
        data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK,
                            (struct sockaddr *)&src, &sizeof_src);
-       if (data_len < 0) return -1;
+       if (data_len < 0) {
+               if ((errno == EAGAIN) || (errno == EINTR)) return 0;
+               return -1;
+       }
 
        /*
-        *      Too little data is available, round it up to 4 bytes.
+        *      Too little data is available, discard the packet.
         */
        if (data_len < 4) {
-               len = 4;
+               recvfrom(sockfd, header, sizeof(header), flags,
+                        (struct sockaddr *)&src, &sizeof_src);
+               return 0;
+
        } else {                /* we got 4 bytes of data. */
                /*
                 *      See how long the packet says it is.
@@ -282,17 +388,22 @@ static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
                len = (header[2] * 256) + header[3];
 
                /*
-                *      Too short: read 4 bytes, and discard the rest.
+                *      The length in the packet says it's less than
+                *      a RADIUS header length: discard it.
                 */
-               if (len < 4) {
-                       len = 4;
+               if (len < AUTH_HDR_LEN) {
+                       recvfrom(sockfd, header, sizeof(header), flags,
+                                (struct sockaddr *)&src, &sizeof_src);
+                       return 0;
 
                        /*
                         *      Enforce RFC requirements, for sanity.
                         *      Anything after 4k will be discarded.
                         */
                } else if (len > MAX_PACKET_LEN) {
-                       len = MAX_PACKET_LEN;
+                       recvfrom(sockfd, header, sizeof(header), flags,
+                                (struct sockaddr *)&src, &sizeof_src);
+                       return len;
                }
        }
 
@@ -306,55 +417,29 @@ static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
 #ifdef WITH_UDPFROMTO
        if (dst.ss_family == AF_INET) {
                data_len = recvfromto(sockfd, buf, len, flags,
-                                     (struct sockaddr *)&src, &sizeof_src, 
+                                     (struct sockaddr *)&src, &sizeof_src,
                                      (struct sockaddr *)&dst, &sizeof_dst);
        } else
 #endif
                /*
                 *      No udpfromto, OR an IPv6 socket.  Fail gracefully.
                 */
-               data_len = recvfrom(sockfd, buf, len, flags, 
+               data_len = recvfrom(sockfd, buf, len, flags,
                                    (struct sockaddr *)&src, &sizeof_src);
        if (data_len < 0) {
                free(buf);
                return data_len;
        }
 
-       /*
-        *      Check address families, and update src/dst ports, etc.
-        */
-       if (src.ss_family == AF_INET) {
-               struct sockaddr_in      *s4;
-
-               s4 = (struct sockaddr_in *)&src;
-               src_ipaddr->af = AF_INET;
-               src_ipaddr->ipaddr.ip4addr = s4->sin_addr;
-               *src_port = ntohs(s4->sin_port);
-
-               s4 = (struct sockaddr_in *)&dst;
-               dst_ipaddr->af = AF_INET;
-               dst_ipaddr->ipaddr.ip4addr = s4->sin_addr;
-               *dst_port = ntohs(s4->sin_port);
-
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
-       } else if (src.ss_family == AF_INET6) {
-               struct sockaddr_in6     *s6;
-
-               s6 = (struct sockaddr_in6 *)&src;
-               src_ipaddr->af = AF_INET6;
-               src_ipaddr->ipaddr.ip6addr = s6->sin6_addr;
-               *src_port = ntohs(s6->sin6_port);
-
-               s6 = (struct sockaddr_in6 *)&dst;
-               dst_ipaddr->af = AF_INET6;
-               dst_ipaddr->ipaddr.ip6addr = s6->sin6_addr;
-               *dst_port = ntohs(s6->sin6_port);
-#endif
-       } else {
+       if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, &port)) {
                free(buf);
                return -1;      /* Unknown address family, Die Die Die! */
        }
-       
+       *src_port = port;
+
+       fr_sockaddr2ipaddr(&dst, sizeof_dst, dst_ipaddr, &port);
+       *dst_port = port;
+
        /*
         *      Different address families should never happen.
         */
@@ -387,13 +472,13 @@ static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
 static void make_secret(uint8_t *digest, const uint8_t *vector,
                        const char *secret, const uint8_t *value)
 {
-       lrad_MD5_CTX context;
+       FR_MD5_CTX context;
         int             i;
 
-       lrad_MD5Init(&context);
-       lrad_MD5Update(&context, vector, AUTH_VECTOR_LEN);
-       lrad_MD5Update(&context, secret, strlen(secret));
-       lrad_MD5Final(digest, &context);
+       fr_MD5Init(&context);
+       fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
+       fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
+       fr_MD5Final(digest, &context);
 
         for ( i = 0; i < AUTH_VECTOR_LEN; i++ ) {
                digest[i] ^= value[i];
@@ -401,11 +486,11 @@ static void make_secret(uint8_t *digest, const uint8_t *vector,
 }
 
 #define MAX_PASS_LEN (128)
-static void make_passwd(uint8_t *output, int *outlen,
-                       const uint8_t *input, int inlen,
+static void make_passwd(uint8_t *output, size_t *outlen,
+                       const uint8_t *input, size_t inlen,
                        const char *secret, const uint8_t *vector)
 {
-       lrad_MD5_CTX context, old;
+       FR_MD5_CTX context, old;
        uint8_t digest[AUTH_VECTOR_LEN];
        uint8_t passwd[MAX_PASS_LEN];
        int     i, n;
@@ -415,10 +500,15 @@ static void make_passwd(uint8_t *output, int *outlen,
         *      If the length is zero, round it up.
         */
        len = inlen;
+
+       if (len > MAX_PASS_LEN) len = MAX_PASS_LEN;
+
+       memcpy(passwd, input, len);
+       memset(passwd + len, 0, sizeof(passwd) - len);
+
        if (len == 0) {
                len = AUTH_PASS_LEN;
        }
-       else if (len > MAX_PASS_LEN) len = MAX_PASS_LEN;
 
        else if ((len & 0x0f) != 0) {
                len += 0x0f;
@@ -426,27 +516,24 @@ static void make_passwd(uint8_t *output, int *outlen,
        }
        *outlen = len;
 
-       memcpy(passwd, input, len);
-       memset(passwd + len, 0, sizeof(passwd) - len);
-
-       lrad_MD5Init(&context);
-       lrad_MD5Update(&context, secret, strlen(secret));
+       fr_MD5Init(&context);
+       fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
        old = context;
 
        /*
         *      Do first pass.
         */
-       lrad_MD5Update(&context, vector, AUTH_PASS_LEN);
+       fr_MD5Update(&context, vector, AUTH_PASS_LEN);
 
        for (n = 0; n < len; n += AUTH_PASS_LEN) {
                if (n > 0) {
                        context = old;
-                       lrad_MD5Update(&context,
+                       fr_MD5Update(&context,
                                       passwd + n - AUTH_PASS_LEN,
                                       AUTH_PASS_LEN);
                }
 
-               lrad_MD5Final(digest, &context);
+               fr_MD5Final(digest, &context);
                for (i = 0; i < AUTH_PASS_LEN; i++) {
                        passwd[i + n] ^= digest[i];
                }
@@ -455,11 +542,11 @@ static void make_passwd(uint8_t *output, int *outlen,
        memcpy(output, passwd, len);
 }
 
-static void make_tunnel_passwd(uint8_t *output, int *outlen,
-                              const uint8_t *input, int inlen, int room,
+static void make_tunnel_passwd(uint8_t *output, size_t *outlen,
+                              const uint8_t *input, size_t inlen, size_t room,
                               const char *secret, const uint8_t *vector)
 {
-       lrad_MD5_CTX context, old;
+       FR_MD5_CTX context, old;
        uint8_t digest[AUTH_VECTOR_LEN];
        uint8_t passwd[MAX_STRING_LEN + AUTH_VECTOR_LEN];
        int     i, n;
@@ -519,26 +606,27 @@ static void make_tunnel_passwd(uint8_t *output, int *outlen,
         *      add in some CSPRNG data.  should be OK..
         */
        passwd[0] = (0x80 | ( ((salt_offset++) & 0x0f) << 3) |
-                    (lrad_rand() & 0x07));
-       passwd[1] = lrad_rand();
+                    (fr_rand() & 0x07));
+       passwd[1] = fr_rand();
        passwd[2] = inlen;      /* length of the password string */
 
-       lrad_MD5Init(&context);
-       lrad_MD5Update(&context, secret, strlen(secret));
+       fr_MD5Init(&context);
+       fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
        old = context;
 
-       lrad_MD5Update(&context, vector, AUTH_VECTOR_LEN);
-       lrad_MD5Update(&context, &passwd[0], 2);
+       fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
+       fr_MD5Update(&context, &passwd[0], 2);
 
        for (n = 0; n < len; n += AUTH_PASS_LEN) {
                if (n > 0) {
                        context = old;
-                       lrad_MD5Update(&context,
+                       fr_MD5Update(&context,
                                       passwd + 2 + n - AUTH_PASS_LEN,
                                       AUTH_PASS_LEN);
                }
 
-               lrad_MD5Final(digest, &context);
+               fr_MD5Final(digest, &context);
+
                for (i = 0; i < AUTH_PASS_LEN; i++) {
                        passwd[i + 2 + n] ^= digest[i];
                }
@@ -547,253 +635,18 @@ static void make_tunnel_passwd(uint8_t *output, int *outlen,
 }
 
 /*
- *     Hack for IETF RADEXT.  Don't use in a production environment!
- *
- *     FIXME: Pack multiple diameter attributes into one
- *     Extended-Attribute!
- */
-static int vp2diameter(const RADIUS_PACKET *packet,
-                      const RADIUS_PACKET *original, const char *secret,
-                      const VALUE_PAIR *vp, uint8_t *ptr)
-{
-       uint32_t attr;
-       uint32_t length = vp->length;
-       uint32_t vendor;
-       int total, align;
-       uint8_t *length_ptr;
-
-       *(ptr++) = PW_EXTENDED_ATTRIBUTE;
-       length_ptr = ptr++;     /* fill this in later. */
-       total = 2;
-
-       attr = vp->attribute;
-       attr &= ~(1 << 31);     /* implemented limitations, see dict_addattr */
-       vendor = VENDOR(attr);
-       attr = attr & 0xffff;
-       attr = ntohl(attr);
-       
-       memcpy(ptr, &attr, sizeof(attr));
-       ptr += 4;
-       total += 4;
-       length += 8;           /* includes 8 bytes of attr & length */
-       align = 0;              /* no padding */
-
-       if (vendor != 0) {
-               length += 4; /* include 4 bytes of vendor */
-               
-               length |= (1 << 31);
-               length = ntohl(length);
-               memcpy(ptr, &length, sizeof(length));
-               ptr += 4;
-               total += 4;
-               
-               vendor = ntohl(vendor);
-               memcpy(ptr, &vendor, sizeof(vendor));
-               ptr += 4;
-               total += 4;
-       } else {
-               length = ntohl(length);
-               memcpy(ptr, &length, sizeof(length));
-               ptr += 4;
-               total += 4;
-       }
-       *length_ptr = total;
-
-       switch (vp->type) {
-       case PW_TYPE_INTEGER:
-       case PW_TYPE_DATE:
-               attr = ntohl(vp->lvalue); /* stored in host order */
-               memcpy(ptr, &attr, sizeof(attr));
-               length = 4;
-               break;
-               
-       case PW_TYPE_IPADDR:
-               attr = vp->lvalue; /* stored in network order */
-               memcpy(ptr, &attr, sizeof(attr));
-               length = 4;
-               break;
-               
-       case PW_TYPE_IFID:
-       case PW_TYPE_IPV6ADDR:
-       case PW_TYPE_IPV6PREFIX:
-       case PW_TYPE_ABINARY:
-               memcpy(ptr, vp->vp_strvalue, vp->length);
-               length = vp->length;
-               if ((length & 0x03) != 0) align = 4 - (length & 0x03);
-               break;
-
-               /*
-                *      Length MAY be larger than can fit into the
-                *      encapsulating attribute!
-                */
-       case PW_TYPE_STRING:
-       case PW_TYPE_OCTETS:
-       default:
-               {
-                       uint32_t offset = 0;
-                       
-                       length = vp->length;
-                       if ((length & 0x03) != 0) align = 4 - (length & 0x03);
-                       
-                       if (total + length > 255) {
-                               *length_ptr = 255;
-                               offset = 255 - total;
-
-                               memcpy(ptr, vp->vp_octets, offset);
-                               ptr += offset;
-                               *(ptr++) = PW_EXTENDED_ATTRIBUTE;
-                               length_ptr = ptr++;
-                               *length_ptr = 2;
-                               length -= offset;
-
-                               total += offset + 2;
-                       }
-                       
-                       memcpy(ptr, vp->vp_octets + offset, length);
-               }
-               break;
-       }
-       
-       /*
-        *      Skip to the end of the data.
-        */
-       ptr += length;
-       total += length;
-       *length_ptr += length;
-
-       /*
-        *      Align the data to a multiple of 4 bytes.
-        */
-       if (align != 0) {
-               unsigned int i;
-               
-               *length_ptr += align;
-               for (i = 0; i < align; i++) {
-                       *(ptr++) = '\0';
-                       total++;
-               }
-       }
-
-       return total;
-}
-
-
-/*
- *     Parse a data structure into a RADIUS attribute.
+ *     Returns the end of the data.
  */
-int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
-               const char *secret, const VALUE_PAIR *vp, uint8_t *ptr)
+static int vp2data(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
+                  const char *secret, const VALUE_PAIR *vp, uint8_t *start,
+                  size_t room)
 {
-       int             vendorcode;
-       int             offset, len, total_length;
-       uint32_t        lvalue;
-       uint8_t         *length_ptr, *vsa_length_ptr;
-       const uint8_t   *data = NULL;
-       uint8_t         array[4];
-
-       vendorcode = total_length = 0;
-       length_ptr = vsa_length_ptr = NULL;
-
-       /*
-        *      For interoperability, always put vendor attributes
-        *      into their own VSA.
-        */
-       if ((vendorcode = VENDOR(vp->attribute)) == 0) {
-               *(ptr++) = vp->attribute & 0xFF;
-               length_ptr = ptr;
-               *(ptr++) = 2;
-               total_length += 2;
+       uint32_t lvalue;
+       size_t len;
+       const uint8_t *data;
+       uint8_t *ptr = start;
+       uint8_t array[4];
 
-       } else {
-               int vsa_tlen = 1;
-               int vsa_llen = 1;
-               DICT_VENDOR *dv = dict_vendorbyvalue(vendorcode);
-
-               /*
-                *      This must be an RFC-format attribute.  If it
-                *      wasn't, then the "decode" function would have
-                *      made a Vendor-Specific attribute (i.e. type
-                *      26), and we would have "vendorcode == 0" here.
-                */
-               if (dv) {
-                       vsa_tlen = dv->type;
-                       vsa_llen = dv->length;
-               }
-
-               /*
-                *      Build a VSA header.
-                */
-               *ptr++ = PW_VENDOR_SPECIFIC;
-               vsa_length_ptr = ptr;
-               *ptr++ = 6;
-               lvalue = htonl(vendorcode);
-               memcpy(ptr, &lvalue, 4);
-               ptr += 4;
-               total_length += 6;
-
-               switch (vsa_tlen) {
-               case 1:
-                       ptr[0] = (vp->attribute & 0xFF);
-                       break;
-
-               case 2:
-                       ptr[0] = ((vp->attribute >> 8) & 0xFF);
-                       ptr[1] = (vp->attribute & 0xFF);
-                       break;
-
-               case 4:
-                       ptr[0] = 0;
-                       ptr[1] = 0;
-                       ptr[2] = ((vp->attribute >> 8) & 0xFF);
-                       ptr[3] = (vp->attribute & 0xFF);
-                       break;
-
-               default:
-                       return 0; /* silently discard it */
-               }
-               ptr += vsa_tlen;
-
-               switch (vsa_llen) {
-               case 0:
-                       length_ptr = vsa_length_ptr;
-                       vsa_length_ptr = NULL;
-                       break;
-               case 1:
-                       ptr[0] = 0;
-                       length_ptr = ptr;
-                       break;
-               case 2:
-                       ptr[0] = 0;
-                       ptr[1] = 0;
-                       length_ptr = ptr + 1;
-                       break;
-
-               default:
-                       return 0; /* silently discard it */
-               }
-               ptr += vsa_llen;
-
-               total_length += vsa_tlen + vsa_llen;
-               if (vsa_length_ptr) *vsa_length_ptr += vsa_tlen + vsa_llen;
-               *length_ptr += vsa_tlen + vsa_llen;
-       }
-
-       offset = 0;
-       if (vp->flags.has_tag) {
-               if (TAG_VALID(vp->flags.tag)) {
-                       ptr[0] = vp->flags.tag & 0xff;
-                       offset = 1;
-           
-               } else if (vp->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD) {
-                       /*
-                        *      Tunnel passwords REQUIRE a tag, even
-                        *      if don't have a valid tag.
-                        */
-                       ptr[0] = 0;
-                       offset = 1;
-               } /* else don't write a tag */
-       } /* else the attribute doesn't have a tag */
-       
        /*
         *      Set up the default sources for the data.
         */
@@ -809,37 +662,29 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
        case PW_TYPE_ABINARY:
                /* nothing more to do */
                break;
-                       
+
        case PW_TYPE_BYTE:
                len = 1;        /* just in case */
-               array[0] = vp->lvalue & 0xff;
+               array[0] = vp->vp_integer & 0xff;
                data = array;
-               offset = 0;
                break;
-                       
 
        case PW_TYPE_SHORT:
                len = 2;        /* just in case */
-               array[0] = (vp->lvalue >> 8) & 0xff;
-               array[1] = vp->lvalue & 0xff;
+               array[0] = (vp->vp_integer >> 8) & 0xff;
+               array[1] = vp->vp_integer & 0xff;
                data = array;
-               offset = 0;
                break;
 
        case PW_TYPE_INTEGER:
                len = 4;        /* just in case */
-               lvalue = htonl(vp->lvalue);
+               lvalue = htonl(vp->vp_integer);
                memcpy(array, &lvalue, sizeof(lvalue));
-
-               /*
-                *      Perhaps discard the first octet.
-                */
-               data = &array[offset];
-               len -= offset;
+               data = array;
                break;
-                       
+
        case PW_TYPE_IPADDR:
-               data = (const uint8_t *) &vp->lvalue;
+               data = (const uint8_t *) &vp->vp_ipaddr;
                len = 4;        /* just in case */
                break;
 
@@ -847,22 +692,39 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                 *  There are no tagged date attributes.
                 */
        case PW_TYPE_DATE:
-               lvalue = htonl(vp->lvalue);
+               lvalue = htonl(vp->vp_date);
                data = (const uint8_t *) &lvalue;
                len = 4;        /* just in case */
                break;
 
-       default:                /* unknown type: ignore it */
-               librad_log("ERROR: Unknown attribute type %d", vp->type);
-               return -1;
-       }
+       case PW_TYPE_SIGNED:
+       {
+               int32_t slvalue;
 
-       /*
-        *      Bound the data to 255 bytes.
+               len = 4;        /* just in case */
+               slvalue = htonl(vp->vp_signed);
+               memcpy(array, &slvalue, sizeof(slvalue));
+               break;
+       }
+
+       case PW_TYPE_TLV:
+               data = vp->vp_tlv;
+               if (!data) {
+                       fr_strerror_printf("ERROR: Cannot encode NULL TLV");
+                       return -1;
+               }
+               if (vp->length > room) return 0; /* can't chop TLVs to fit */
+               break;
+
+       default:                /* unknown type: ignore it */
+               fr_strerror_printf("ERROR: Unknown attribute type %d", vp->type);
+               return -1;
+       }
+
+       /*
+        *      Bound the data to the calling size
         */
-       if (len + offset + total_length > 255) {
-               len = 255 - offset - total_length;
-       }       
+       if (len > room) len = room;
 
        /*
         *      Encrypt the various password styles
@@ -872,31 +734,46 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
         */
        switch (vp->flags.encrypt) {
        case FLAG_ENCRYPT_USER_PASSWORD:
-               make_passwd(ptr + offset, &len,
-                           data, len,
+               make_passwd(ptr, &len, data, len,
                            secret, packet->vector);
                break;
-               
+
        case FLAG_ENCRYPT_TUNNEL_PASSWORD:
-               if (!original) {
-                       librad_log("ERROR: No request packet, cannot encrypt %s attribute in the vp.", vp->name);
-                       return -1;
-               }
+               lvalue = 0;
+               if (vp->flags.has_tag) lvalue = 1;
 
                /*
-                *      Check if 255 - offset - total_length is less
-                *      than 18.  If so, we can't fit the data into
-                *      the available space, and we discard the
-                *      attribute.
+                *      Check if there's enough room.  If there isn't,
+                *      we discard the attribute.
                 *
                 *      This is ONLY a problem if we have multiple VSA's
                 *      in one Vendor-Specific, though.
                 */
-               if ((255 - offset - total_length) < 18) return 0;
+               if (room < (18 + lvalue)) return 0;
+
+               switch (packet->code) {
+               case PW_AUTHENTICATION_ACK:
+               case PW_AUTHENTICATION_REJECT:
+               case PW_ACCESS_CHALLENGE:
+               default:
+                       if (!original) {
+                               fr_strerror_printf("ERROR: No request packet, cannot encrypt %s attribute in the vp.", vp->name);
+                               return -1;
+                       }
 
-               make_tunnel_passwd(ptr + offset, &len,
-                                  data, len, 255 - offset - total_length,
-                                  secret, original->vector);
+                       if (lvalue) ptr[0] = vp->flags.tag;
+                       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->flags.tag;
+                       make_tunnel_passwd(ptr + 1, &len, data, len - 1, room,
+                                          secret, packet->vector);
+                       break;
+               }
                break;
 
                /*
@@ -904,40 +781,398 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                 *      always fits.
                 */
        case FLAG_ENCRYPT_ASCEND_SECRET:
-               make_secret(ptr + offset, packet->vector,
-                           secret, data);
+               make_secret(ptr, packet->vector, secret, data);
                len = AUTH_VECTOR_LEN;
                break;
 
-               
+
        default:
-               /*
-                *      Just copy the data over
-                */
-               memcpy(ptr + offset, data, len);
+               if (vp->flags.has_tag && TAG_VALID(vp->flags.tag)) {
+                       if (vp->type == PW_TYPE_STRING) {
+                               if (len > (room - 1)) len = room - 1;
+                               ptr[0] = vp->flags.tag;
+                               ptr++;
+                       } else if (vp->type == PW_TYPE_INTEGER) {
+                               array[0] = vp->flags.tag;
+                       } /* else it can't be any other type */
+               }
+               memcpy(ptr, data, len);
                break;
        } /* switch over encryption flags */
 
+       return len + (ptr - start);;
+}
+
+
+static int rad_vp2rfc(const RADIUS_PACKET *packet,
+                     const RADIUS_PACKET *original,
+                     const char *secret, const VALUE_PAIR *vp,
+                     unsigned int attribute, uint8_t *ptr, size_t room)
+{
+       int len;
+
+       if (room < 2) return 0;
+
+       ptr[0] = attribute & 0xff; /* NOT vp->attribute */
+       ptr[1] = 2;
+
+       if (room > (255 - ptr[1])) room = 255 - ptr[1];
+       len = vp2data(packet, original, secret, vp, ptr + 2, room);
+       if (len < 0) return len;
+
+       ptr[1] += len;
+
+       return ptr[1];
+}
+
+extern int fr_wimax_max_tlv;
+extern int fr_wimax_shift[];
+extern int fr_wimax_mask[];
+
+static int tlv2data(const RADIUS_PACKET *packet,
+                   const RADIUS_PACKET *original,
+                   const char *secret, const VALUE_PAIR *vp,
+                   uint8_t *ptr, size_t room, int nest)
+{
+       int len;
+
+       if (nest > fr_wimax_max_tlv) return -1;
+
+       if (room < 2) return 0;
+       room -= 2;
+
+       ptr[0] = (vp->attribute >> fr_wimax_shift[nest]) & fr_wimax_mask[nest];
+       ptr[1] = 2;
+
+       /*
+        *      No more nested TLVs: pack the data.
+        */
+       if ((nest == fr_wimax_max_tlv) ||
+           ((vp->attribute >> fr_wimax_shift[nest + 1]) == 0)) {
+               len = vp2data(packet, original, secret, vp, ptr + 2, room);
+       } else {
+               len = tlv2data(packet, original, secret, vp, ptr + 2, room,
+                              nest + 1);
+       }
+       if (len <= 0) return len;
+
+       ptr[1] += len;
+
+       return ptr[1];
+}
+
+static int wimax2data(const RADIUS_PACKET *packet,
+                     const RADIUS_PACKET *original,
+                     const char *secret, const VALUE_PAIR *vp,
+                     uint8_t *start, size_t room, uint8_t *ptr)
+{
+       int len;
+
+       /*
+        *      Offsets to Vendor-Specific length, and to length of
+        *      WiMAX attribute.
+        */
+#define VS_OFF (1)
+#define WM_OFF (7)
+
+       if (room < 1) return 0;
+       room--;
+
+       /*
+        *      Account for continuation bytes.  The caller has
+        *      already accounting for the continuation byte in the
+        *      Vendor-Specific "length" field.
+        */
+       start[WM_OFF]++;
+       *(ptr++) = 0;
+
+       /*
+        *      Chop everything to fit in one attribute.
+        */
+       if (room > (255 - 9)) room = (255 - 9);
+
+       /*
+        *      The attribute contains TLVs that we have NOT decoded
+        *      properly, OR it contains TLV that the user has encoded
+        *      manually.  If it has no data, OR it's too long,
+        *      discard it.  We're not going to walk through its
+        *      contents trying to figure out how to chop it across
+        *      multiple continuations.
+        */
+       if (vp->flags.has_tlv && (!vp->vp_tlv || (vp->length > room))) {
+               return 0;
+       }
+
+       /*
+        *      The attribute is a top-level integer, ipaddr, etc.
+        *      Encode it.
+        */
+       if (!vp->flags.is_tlv) {
+               len = vp2data(packet, original, secret, vp, ptr, room);
+       } else {
+               len = tlv2data(packet, original, secret, vp, ptr, room, 1);
+       }
+
+       if (len <= 0) return len;
+
+       start[VS_OFF] += len;
+       start[WM_OFF] += len;
+
+       return start[VS_OFF];
+}
+
+
+static int rad_vp2extended(const RADIUS_PACKET *packet,
+                     const RADIUS_PACKET *original,
+                     const char *secret, const VALUE_PAIR *vp,
+                     unsigned int attribute, uint8_t *ptr, size_t room)
+{
+       int len = 2;
+
+       if (room < 3) return 0;
+
+       ptr[0] = attribute & 0xff; /* NOT vp->attribute */
+       ptr[1] = 3;
+
+       if (vp->flags.extended) {
+               ptr[2] = (attribute & 0xff00) >> 8;
+               len++;
+
+       } else if (vp->flags.extended_flags) {
+               if (room < 4) return 0;
+
+               ptr[1] = 4;
+               ptr[2] = (attribute & 0xff00) >> 8;
+               ptr[3] = 0;
+
+               len += 2;
+       }
+
+       /*
+        *      For now, no extended attribute can be longer than the
+        *      encapsulating attribute.  Once we add support for the
+        *      "M" bit, this restriction will be relaxed.
+        */
+       if (room > (255 - ptr[1])) room = 255 - ptr[1];
+
+       if (!vp->flags.is_tlv) {
+               len = vp2data(packet, original, secret, vp, ptr + ptr[1], room);
+       } else {
+               len = tlv2data(packet, original, secret, vp, ptr + ptr[1], room, 2);
+       }
+
+       if (len < 0) return len;
+
+       ptr[1] += len;
+
+       return ptr[1];
+}
+
+/*
+ *     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 *vp, uint8_t *start,
+               size_t room)
+{
+       int len;
+       uint32_t lvalue;
+       uint8_t *ptr;
+       DICT_VENDOR *dv;
+
+       /*
+        *      RFC format attributes take the fast path.
+        */
+       if (vp->vendor == 0) {
+               return rad_vp2rfc(packet, original, secret, vp,
+                                 vp->attribute, start, room);
+       }
+
+       if (vp->vendor == VENDORPEC_EXTENDED) {
+               return rad_vp2extended(packet, original, secret, vp,
+                                      vp->attribute, start, room);
+       }
+
+       /*
+        *      Not enough room for:
+        *              attr, len, vendor-id, vsa, vsalen
+        */
+       if (room < 8) return 0;
+
+       /*
+        *      Build the Vendor-Specific header
+        */
+       ptr = start;
+       *ptr++ = PW_VENDOR_SPECIFIC;
+       *ptr++ = 6;
+       room -= 6;
+       lvalue = htonl(vp->vendor);
+       memcpy(ptr, &lvalue, 4);
+       ptr += 4;
+
+       /*
+        *      Unknown vendors, and type=1,length=1,no-continuation
+        *      are RFC format attributes.
+        */
+       dv = dict_vendorbyvalue(vp->vendor);
+       if (!dv ||
+           ((dv->type == 1) && (dv->length = 1) && !dv->flags)) {
+               len = rad_vp2rfc(packet, original, secret, vp,
+                                vp->attribute, ptr, room);
+               if (len <= 0) return len;
+
+               start[1] += len;
+               return start[1];
+       }
+
+       if (room < (dv->type + dv->length + dv->flags)) return 0;
+       room -= (dv->type + dv->length + dv->flags);
+       start[1] += (dv->type + dv->length + dv->flags);
+
+       switch (dv->type) {
+               case 1:
+                       ptr[0] = (vp->attribute & 0xFF);
+                       break;
+
+               case 2:
+                       ptr[0] = ((vp->attribute >> 8) & 0xFF);
+                       ptr[1] = (vp->attribute & 0xFF);
+                       break;
+
+               case 4:
+                       ptr[0] = 0;
+                       ptr[1] = ((vp->attribute >> 16) & 0xFF);
+                       ptr[2] = ((vp->attribute >> 8) & 0xFF);
+                       ptr[3] = (vp->attribute & 0xFF);
+                       break;
+
+               default:
+                       return 0; /* silently discard it */
+       }
+       ptr += dv->type;
+
+       switch (dv->length) {
+       case 0:
+               break;
+       case 1:
+               ptr[0] = dv->type + 1;
+               break;
+       case 2:
+               ptr[0] = 0;
+               ptr[1] = dv->type + 2;
+               break;
+
+       default:
+               return 0; /* silently discard it */
+       }
+       ptr += dv->length;
+
+       /*
+        *      WiMAX attributes take their own path through the
+        *      system.
+        */
+       if (dv->flags) return wimax2data(packet, original, secret, vp,
+                                        start, room, ptr);
+
+       len = vp2data(packet, original, secret, vp, ptr, room);
+       if (len <= 0) return len;
+
+       if (dv->length != 0) ptr[-1] += len;
+
+       start[1] += len;
+
+       return start[1];
+}
+
+/*
+ *  Swap 123a -> 0321
+ */
+#define REORDER(x) ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000 >> 24))
+
+
+/*
+ *     Encode a WiMAX sub-TLV.  It must NOT be called for WiMAX
+ *     attributes that are of type integer, string, etc.
+ */
+static int rad_encode_wimax(const RADIUS_PACKET *packet,
+                           const RADIUS_PACKET *original,
+                           const char *secret, VALUE_PAIR *reply,
+                           uint8_t *start, size_t room)
+{
+       int len, redo;
+       uint32_t lvalue;
+       uint8_t *ptr = start, *vsa = start;
+       uint32_t maxattr;
+       VALUE_PAIR *vp = reply;
+
        /*
-        *      Account for the tag (if any).
+        *      Swap the order of the WiMAX hacks, to make later
+        *      comparisons easier.
         */
-       len += offset;
+       maxattr = REORDER(vp->attribute);
 
        /*
-        *      RFC 2865 section 5 says that zero-length attributes
-        *      MUST NOT be sent.
+        *      Build the Vendor-Specific header
         */
-       if (len == 0) return 0;
+       ptr = start;
+       redo = 0;
+
+redo_vsa:
+       vsa = ptr;
+
+       if (room < 9) return 0;
+       *ptr++ = PW_VENDOR_SPECIFIC;
+       *ptr++ = 9;
+       room -= 9;
+       lvalue = htonl(vp->vendor);
+       memcpy(ptr, &lvalue, 4);
+       ptr += 4;
+       *(ptr++) = vp->attribute & 0xff;
+       *(ptr++) = 3;
+       *(ptr++) = 0;           /* continuation */
+       room -= 9;
+
+redo_tlv:
+       len = tlv2data(packet, original, secret, vp, ptr, room, 1);
+       if (len < 0) return len;
 
        /*
-        *      Update the various lengths.
+        *      Not enough room.  Do a continuation.
         */
-       *length_ptr += len;
-       if (vsa_length_ptr) *vsa_length_ptr += len;
+       if ((len == 0) || ((vsa[VS_OFF] + len) > 255)) {
+               if (redo) return (start - vsa);
+
+               vsa[8] = 0x80;
+               redo = 1;
+               goto redo_vsa;
+       }
+       redo = 0;
+
        ptr += len;
-       total_length += len;
+       vsa[VS_OFF] += len;
+       vsa[WM_OFF] += len;
+
+       vp->flags.encoded = 1;
+       vp = vp->next;
 
-       return total_length;    /* of attribute */
+       /*
+        *      Look at the NEXT tlv.  Ensure that we encode
+        *      attributes into a common VSA *only* if they are for
+        *      the same WiMAX VSA, AND if the TLVs are in numerically
+        *      increasing order.
+        */
+       if (vp && vp->flags.is_tlv && (reply->vendor == vp->vendor) &&
+           ((reply->attribute & 0xff) == (vp->attribute & 0xff))) {
+               uint32_t attr;
+
+               attr = REORDER(vp->attribute);
+               if (attr >= maxattr) {
+                       maxattr = attr;
+                       goto redo_tlv;
+               }
+       }
+
+       return ptr - start;
 }
 
 
@@ -954,18 +1189,14 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
        VALUE_PAIR      *reply;
        const char      *what;
        char            ip_buffer[128];
-       
+
        /*
-        *      For simplicity in the following logic, we allow
-        *      the attributes to "overflow" the 4k maximum
-        *      RADIUS packet size, by one attribute.
-        *
-        *      It's uint32_t, for alignment purposes.
+        *      A 4K packet, aligned on 64-bits.
         */
-       uint32_t        data[(MAX_PACKET_LEN + 256) / 4];
+       uint64_t        data[MAX_PACKET_LEN / sizeof(uint64_t)];
 
-       if ((packet->code > 0) && (packet->code < MAX_PACKET_CODE)) {
-               what = packet_codes[packet->code];
+       if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+               what = fr_packet_codes[packet->code];
        } else {
                what = "Reply";
        }
@@ -985,11 +1216,11 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
        case PW_AUTHENTICATION_REJECT:
        case PW_ACCESS_CHALLENGE:
                if (!original) {
-                       librad_log("ERROR: Cannot sign response packet without a request packet.");
+                       fr_strerror_printf("ERROR: Cannot sign response packet without a request packet.");
                        return -1;
                }
                break;
-               
+
                /*
                 *      These packet vectors start off as all zero.
                 */
@@ -998,32 +1229,32 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
        case PW_COA_REQUEST:
                memset(packet->vector, 0, sizeof(packet->vector));
                break;
-               
+
        default:
                break;
        }
-               
+
        /*
         *      Use memory on the stack, until we know how
         *      large the packet will be.
         */
        hdr = (radius_packet_t *) data;
-       
+
        /*
         *      Build standard header
         */
        hdr->code = packet->code;
        hdr->id = packet->id;
-       
+
        memcpy(hdr->vector, packet->vector, sizeof(hdr->vector));
 
        total_length = AUTH_HDR_LEN;
-       packet->verified = 0;
-       
+
        /*
         *      Load up the configuration values for the user
         */
        ptr = hdr->data;
+       packet->offset = 0;
 
        /*
         *      FIXME: Loop twice over the reply list.  The first time,
@@ -1033,32 +1264,32 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
         *      Hmm... this may be slower than just doing a small
         *      memcpy.
         */
-       
+
        /*
         *      Loop over the reply attributes for the packet.
         */
        for (reply = packet->vps; reply; reply = reply->next) {
                /*
-                *      Ignore non-wire attributes
+                *      Ignore non-wire attributes, but allow extended
+                *      attributes.
                 */
-               if ((VENDOR(reply->attribute) == 0) &&
-                   ((reply->attribute & 0xFFFF) > 0xff) &&
-                   !reply->flags.diameter) {
+               if ((reply->vendor == 0) &&
+                   ((reply->attribute & 0xFFFF) >= 256) &&
+                   !reply->flags.extended && !reply->flags.extended_flags) {
+#ifndef NDEBUG
+                       /*
+                        *      Permit the admin to send BADLY formatted
+                        *      attributes with a debug build.
+                        */
+                       if (reply->attribute == PW_RAW_ATTRIBUTE) {
+                               memcpy(ptr, reply->vp_octets, reply->length);
+                               len = reply->length;
+                               goto next;
+                       }
+#endif
                        continue;
                }
-               
-               /*
-                *      Check that the packet is no more than 4k in
-                *      size, AFTER over-flowing the 4k boundary.
-                *      Note that the 'data' buffer, above, is one
-                *      attribute longer than necessary, in order to
-                *      permit this overflow.
-                */
-               if (total_length > MAX_PACKET_LEN) {
-                       librad_log("ERROR: Too many attributes for packet, result is larger than RFC maximum of 4k");
-                       return -1;
-               }
-               
+
                /*
                 *      Set the Message-Authenticator to the correct
                 *      length and initial value.
@@ -1066,50 +1297,53 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                if (reply->attribute == PW_MESSAGE_AUTHENTICATOR) {
                        reply->length = AUTH_VECTOR_LEN;
                        memset(reply->vp_strvalue, 0, AUTH_VECTOR_LEN);
-                       packet->verified = total_length; /* HACK! */
+
+                       /*
+                        *      Cache the offset to the
+                        *      Message-Authenticator
+                        */
+                       packet->offset = total_length;
                }
 
                /*
-                *      Check for overflow.
+                *      Print out ONLY the attributes which
+                *      we're sending over the wire, and print
+                *      them out BEFORE they're encrypted.
                 */
-               if (reply->flags.diameter) {
-                       len = reply->length;
-                       
-                       if (len >= 4096) {
-                               librad_log("ERROR: Too many attributes for packet, result is larger than RFC maximum of 4k");
-                               return -1;
-                       }
+               debug_pair(reply);
 
-                       len += 8; /* Code, length */
-                       if (VENDOR(reply->attribute) != 0) len += 4;
-                       if ((len & 0x03) != 0) len += 4 - (len & 0x03);
+               /*
+                *      Skip attributes that are encoded.
+                */
+               if (reply->flags.encoded) continue;
 
-                       if (len > 253) len += (len / 253) * 2;
+               if ((reply->vendor == VENDORPEC_WIMAX) && reply->flags.is_tlv) {
+                       len = rad_encode_wimax(packet, original, secret,
+                                              reply, ptr,
+                                              ((uint8_t *) data) + sizeof(data) - ptr);
+               } else {
 
-                       if ((total_length + len) > MAX_PACKET_LEN) {
-                               librad_log("ERROR: Too many attributes for packet, result is larger than RFC maximum of 4k");
-                               return -1;
-                       }
+                       len = rad_vp2attr(packet, original, secret, reply, ptr,
+                                         ((uint8_t *) data) + sizeof(data) - ptr);
+               }
 
-                       debug_pair(reply);
+               if (len < 0) return -1;
 
-                       len = vp2diameter(packet, original, secret, reply, ptr);
-               } else {
-                       /*
-                        *      Print out ONLY the attributes which
-                        *      we're sending over the wire, and print
-                        *      them out BEFORE they're encrypted.
-                        */
-                       debug_pair(reply);
-                       
-                       len = rad_vp2attr(packet, original, secret, reply, ptr);
+               /*
+                *      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) return -1;
+       next:
                ptr += len;
                total_length += len;
        } /* done looping over all attributes */
-       
+
        /*
         *      Fill in the rest of the fields, and copy the data over
         *      from the local stack to the newly allocated memory.
@@ -1121,13 +1355,13 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
        packet->data_len = total_length;
        packet->data = (uint8_t *) malloc(packet->data_len);
        if (!packet->data) {
-               librad_log("Out of memory");
+               fr_strerror_printf("Out of memory");
                return -1;
        }
 
-       memcpy(packet->data, data, packet->data_len);
+       memcpy(packet->data, hdr, packet->data_len);
        hdr = (radius_packet_t *) packet->data;
-       
+
        total_length = htons(total_length);
        memcpy(hdr->length, &total_length, sizeof(total_length));
 
@@ -1147,25 +1381,23 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
         *      It wasn't assigned an Id, this is bad!
         */
        if (packet->id < 0) {
-               librad_log("ERROR: RADIUS packets must be assigned an Id.");
+               fr_strerror_printf("ERROR: RADIUS packets must be assigned an Id.");
                return -1;
        }
 
        if (!packet->data || (packet->data_len < AUTH_HDR_LEN) ||
-           (packet->verified < 0)) {
-               librad_log("ERROR: You must call rad_encode() before rad_sign()");
+           (packet->offset < 0)) {
+               fr_strerror_printf("ERROR: You must call rad_encode() before rad_sign()");
                return -1;
        }
 
        /*
         *      If there's a Message-Authenticator, update it
         *      now, BEFORE updating the authentication vector.
-        *
-        *      This is a hack...
         */
-       if (packet->verified > 0) {
+       if (packet->offset > 0) {
                uint8_t calc_auth_vector[AUTH_VECTOR_LEN];
-               
+
                switch (packet->code) {
                case PW_ACCOUNTING_REQUEST:
                case PW_ACCOUNTING_RESPONSE:
@@ -1182,7 +1414,7 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                case PW_AUTHENTICATION_REJECT:
                case PW_ACCESS_CHALLENGE:
                        if (!original) {
-                               librad_log("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,
@@ -1191,28 +1423,28 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
 
                default:        /* others have vector already set to zero */
                        break;
-                       
+
                }
-               
+
                /*
                 *      Set the authentication vector to zero,
                 *      calculate the signature, and put it
                 *      into the Message-Authenticator
                 *      attribute.
                 */
-               lrad_hmac_md5(packet->data, packet->data_len,
-                             secret, strlen(secret),
-                             calc_auth_vector);
-               memcpy(packet->data + packet->verified + 2,
+               fr_hmac_md5(packet->data, packet->data_len,
+                           (const uint8_t *) secret, strlen(secret),
+                           calc_auth_vector);
+               memcpy(packet->data + packet->offset + 2,
                       calc_auth_vector, AUTH_VECTOR_LEN);
-               
+
                /*
                 *      Copy the original request vector back
                 *      to the raw packet.
                 */
                memcpy(hdr->vector, packet->vector, AUTH_VECTOR_LEN);
        }
-       
+
        /*
         *      Switch over the packet code, deciding how to
         *      sign the packet.
@@ -1225,7 +1457,7 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
        case PW_AUTHENTICATION_REQUEST:
        case PW_STATUS_SERVER:
                break;
-               
+
                /*
                 *      Reply packets are signed with the
                 *      authentication vector of the request.
@@ -1233,13 +1465,14 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
        default:
                {
                        uint8_t digest[16];
-                       
-                       MD5_CTX context;
-                       MD5Init(&context);
-                       MD5Update(&context, packet->data, packet->data_len);
-                       MD5Update(&context, secret, strlen(secret));
-                       MD5Final(digest, &context);
-                       
+
+                       FR_MD5_CTX      context;
+                       fr_MD5Init(&context);
+                       fr_MD5Update(&context, packet->data, packet->data_len);
+                       fr_MD5Update(&context, (const uint8_t *) secret,
+                                    strlen(secret));
+                       fr_MD5Final(digest, &context);
+
                        memcpy(hdr->vector, digest, AUTH_VECTOR_LEN);
                        memcpy(packet->vector, digest, AUTH_VECTOR_LEN);
                        break;
@@ -1267,8 +1500,8 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                return 0;
        }
 
-       if ((packet->code > 0) && (packet->code < MAX_PACKET_CODE)) {
-               what = packet_codes[packet->code];
+       if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+               what = fr_packet_codes[packet->code];
        } else {
                what = "Reply";
        }
@@ -1283,7 +1516,7 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                if (rad_encode(packet, original, secret) < 0) {
                        return -1;
                }
-               
+
                /*
                 *      Re-sign it, including updating the
                 *      Message-Authenticator.
@@ -1296,15 +1529,16 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                 *      If packet->data points to data, then we print out
                 *      the VP list again only for debugging.
                 */
-       } else if (librad_debug) {
-               DEBUG("Re-sending %s of id %d to %s port %d\n", what, packet->id,
+       } else if (fr_debug_flag) {
+               DEBUG("Sending %s of id %d to %s port %d\n", what, packet->id,
                      inet_ntop(packet->dst_ipaddr.af,
                                &packet->dst_ipaddr.ipaddr,
                                ip_buffer, sizeof(ip_buffer)),
                      packet->dst_port);
 
                for (reply = packet->vps; reply; reply = reply->next) {
-                       /* FIXME: ignore attributes > 0xff */
+                       if ((reply->vendor == 0) &&
+                           ((reply->attribute & 0xFFFF) > 0xff)) continue;
                        debug_pair(reply);
                }
        }
@@ -1313,30 +1547,40 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
         *      And send it on it's way.
         */
        return rad_sendto(packet->sockfd, packet->data, packet->data_len, 0,
-                         &packet->src_ipaddr, &packet->dst_ipaddr,
-                         packet->dst_port);
+                         &packet->src_ipaddr, packet->src_port,
+                         &packet->dst_ipaddr, packet->dst_port);
 }
 
-
 /*
- *     Validates the requesting client NAS.  Calculates the
- *     signature based on the clients private key.
+ *     Do a comparison of two authentication digests by comparing
+ *     the FULL digest.  Otehrwise, the server can be subject to
+ *     timing attacks that allow attackers find a valid message
+ *     authenticator.
+ *
+ *     http://www.cs.rice.edu/~dwallach/pub/crosby-timing2009.pdf
  */
-static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret)
+static int digest_cmp(const uint8_t *a, const uint8_t *b, size_t length)
 {
-       uint8_t         digest[AUTH_VECTOR_LEN];
-       MD5_CTX         context;
+       int result = 0;
+       size_t i;
 
-       /*
-        *      Older clients have the authentication vector set to
-        *      all zeros. Return `1' in that case.
-        */
-       memset(digest, 0, sizeof(digest));
-       if (memcmp(packet->vector, digest, AUTH_VECTOR_LEN) == 0) {
-               packet->verified = 1;
-               return 1;
+       for (i = 0; i < length; i++) {
+               result |= a[i] ^ b[i];
        }
 
+       return result;          /* 0 is OK, !0 is !OK, just like memcmp */
+}
+
+
+/*
+ *     Validates the requesting client NAS.  Calculates the
+ *     signature based on the clients private key.
+ */
+static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret)
+{
+       uint8_t         digest[AUTH_VECTOR_LEN];
+       FR_MD5_CTX              context;
+
        /*
         *      Zero out the auth_vector in the received packet.
         *      Then append the shared secret to the received packet,
@@ -1348,20 +1592,19 @@ static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret)
        /*
         *  MD5(packet + secret);
         */
-       MD5Init(&context);
-       MD5Update(&context, packet->data, packet->data_len);
-       MD5Update(&context, secret, strlen(secret));
-       MD5Final(digest, &context);
+       fr_MD5Init(&context);
+       fr_MD5Update(&context, packet->data, packet->data_len);
+       fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
+       fr_MD5Final(digest, &context);
 
        /*
         *      Return 0 if OK, 2 if not OK.
         */
-       packet->verified =
-       memcmp(digest, packet->vector, AUTH_VECTOR_LEN) ? 2 : 0;
-
-       return packet->verified;
+       if (digest_cmp(digest, packet->vector, AUTH_VECTOR_LEN) != 0) return 2;
+       return 0;
 }
 
+
 /*
  *     Validates the requesting client NAS.  Calculates the
  *     signature based on the clients private key.
@@ -1370,7 +1613,7 @@ static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                            const char *secret)
 {
        uint8_t         calc_digest[AUTH_VECTOR_LEN];
-       MD5_CTX         context;
+       FR_MD5_CTX              context;
 
        /*
         *      Very bad!
@@ -1387,10 +1630,10 @@ static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original,
        /*
         *  MD5(packet + secret);
         */
-       MD5Init(&context);
-       MD5Update(&context, packet->data, packet->data_len);
-       MD5Update(&context, secret, strlen(secret));
-       MD5Final(calc_digest, &context);
+       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);
 
        /*
         *  Copy the packet's vector back to the packet.
@@ -1400,9 +1643,8 @@ static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original,
        /*
         *      Return 0 if OK, 2 if not OK.
         */
-       packet->verified =
-               memcmp(packet->vector, calc_digest, AUTH_VECTOR_LEN) ? 2 : 0;
-       return packet->verified;
+       if (digest_cmp(packet->vector, calc_digest, AUTH_VECTOR_LEN) != 0) return 2;
+       return 0;
 }
 
 
@@ -1412,14 +1654,15 @@ static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original,
  *     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.
  */
-int rad_packet_ok(RADIUS_PACKET *packet)
+int rad_packet_ok(RADIUS_PACKET *packet, int flags)
 {
        uint8_t                 *attr;
        int                     totallen;
        int                     count;
        radius_packet_t         *hdr;
        char                    host_ipaddr[128];
-       int                     seen_eap;
+       int                     require_ma = 0;
+       int                     seen_ma = 0;
        int                     num_attributes;
 
        /*
@@ -1430,11 +1673,11 @@ int rad_packet_ok(RADIUS_PACKET *packet)
         *      "The minimum length is 20 ..."
         */
        if (packet->data_len < AUTH_HDR_LEN) {
-               librad_log("WARNING: Malformed RADIUS packet from host %s: too short (received %d < minimum %d)",
+               fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: too short (received %d < minimum %d)",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
-                          packet->data_len, AUTH_HDR_LEN);
+                                  (int) packet->data_len, AUTH_HDR_LEN);
                return 0;
        }
 
@@ -1444,11 +1687,11 @@ int rad_packet_ok(RADIUS_PACKET *packet)
         *      " ... and maximum length is 4096."
         */
        if (packet->data_len > MAX_PACKET_LEN) {
-               librad_log("WARNING: Malformed RADIUS packet from host %s: too long (received %d > maximum %d)",
+               fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: too long (received %d > maximum %d)",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
-                          packet->data_len, MAX_PACKET_LEN);
+                                  (int) packet->data_len, MAX_PACKET_LEN);
                return 0;
        }
 
@@ -1465,8 +1708,8 @@ int rad_packet_ok(RADIUS_PACKET *packet)
         *      Code of 16 or greate is not understood.
         */
        if ((hdr->code == 0) ||
-           (hdr->code >= MAX_PACKET_CODE)) {
-               librad_log("WARNING: Bad RADIUS packet from host %s: unknown packet code %d",
+           (hdr->code >= FR_MAX_PACKET_CODE)) {
+               fr_strerror_printf("WARNING: Bad RADIUS packet from host %s: unknown packet code%d ",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
@@ -1475,6 +1718,17 @@ int rad_packet_ok(RADIUS_PACKET *packet)
        }
 
        /*
+        *      Message-Authenticator is required in Status-Server
+        *      packets, otherwise they can be trivially forged.
+        */
+       if (hdr->code == PW_STATUS_SERVER) require_ma = 1;
+
+       /*
+        *      It's also required if the caller asks for it.
+        */
+       if (flags) require_ma = 1;
+
+       /*
         *      Repeat the length checks.  This time, instead of
         *      looking at the data we received, look at the value
         *      of the 'length' field inside of the packet.
@@ -1486,7 +1740,7 @@ int rad_packet_ok(RADIUS_PACKET *packet)
         *      "The minimum length is 20 ..."
         */
        if (totallen < AUTH_HDR_LEN) {
-               librad_log("WARNING: Malformed RADIUS packet from host %s: too short (length %d < minimum %d)",
+               fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: too short (length %d < minimum %d)",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
@@ -1502,7 +1756,7 @@ int rad_packet_ok(RADIUS_PACKET *packet)
         *      " ... and maximum length is 4096."
         */
        if (totallen > MAX_PACKET_LEN) {
-               librad_log("WARNING: Malformed RADIUS packet from host %s: too long (length %d > maximum %d)",
+               fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: too long (length %d > maximum %d)",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
@@ -1519,11 +1773,11 @@ int rad_packet_ok(RADIUS_PACKET *packet)
         *      i.e. No response to the NAS.
         */
        if (packet->data_len < totallen) {
-               librad_log("WARNING: Malformed RADIUS packet from host %s: received %d octets, packet length says %d",
+               fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: received %d octets, packet length says %d",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
-                          packet->data_len, totallen);
+                                  (int) packet->data_len, totallen);
                return 0;
        }
 
@@ -1556,15 +1810,26 @@ int rad_packet_ok(RADIUS_PACKET *packet)
         */
        attr = hdr->data;
        count = totallen - AUTH_HDR_LEN;
-       seen_eap = 0;
        num_attributes = 0;
 
        while (count > 0) {
                /*
+                *      We need at least 2 bytes to check the
+                *      attribute header.
+                */
+               if (count < 2) {
+                       fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute header overflows the packet",
+                                  inet_ntop(packet->src_ipaddr.af,
+                                            &packet->src_ipaddr.ipaddr,
+                                            host_ipaddr, sizeof(host_ipaddr)));
+                       return 0;
+               }
+
+               /*
                 *      Attribute number zero is NOT defined.
                 */
                if (attr[0] == 0) {
-                       librad_log("WARNING: Malformed RADIUS packet from host %s: Invalid attribute 0",
+                       fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: Invalid attribute 0",
                                   inet_ntop(packet->src_ipaddr.af,
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)));
@@ -1576,7 +1841,7 @@ int rad_packet_ok(RADIUS_PACKET *packet)
                 *      fields.  Anything shorter is an invalid attribute.
                 */
                        if (attr[1] < 2) {
-                       librad_log("WARNING: Malformed RADIUS packet from host %s: attribute %d too short",
+                       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)),
@@ -1585,26 +1850,43 @@ int rad_packet_ok(RADIUS_PACKET *packet)
                }
 
                /*
+                *      If there are fewer bytes in the packet than in the
+                *      attribute, it's a bad packet.
+                */
+               if (count < attr[1]) {
+                       fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute %u data overflows the packet",
+                                  inet_ntop(packet->src_ipaddr.af,
+                                            &packet->src_ipaddr.ipaddr,
+                                            host_ipaddr, sizeof(host_ipaddr)),
+                                          attr[0]);
+                       return 0;
+               }
+
+               /*
                 *      Sanity check the attributes for length.
                 */
                switch (attr[0]) {
                default:        /* don't do anything by default */
                        break;
 
+                       /*
+                        *      If there's an EAP-Message, we require
+                        *      a Message-Authenticator.
+                        */
                case PW_EAP_MESSAGE:
-                       seen_eap |= PW_EAP_MESSAGE;
+                       require_ma = 1;
                        break;
 
                case PW_MESSAGE_AUTHENTICATOR:
                        if (attr[1] != 2 + AUTH_VECTOR_LEN) {
-                               librad_log("WARNING: Malformed RADIUS packet from host %s: Message-Authenticator has invalid length %d",
+                               fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: Message-Authenticator has invalid length %d",
                                           inet_ntop(packet->src_ipaddr.af,
                                                     &packet->src_ipaddr.ipaddr,
                                                     host_ipaddr, sizeof(host_ipaddr)),
                                           attr[1] - 2);
                                return 0;
                        }
-                       seen_eap |= PW_MESSAGE_AUTHENTICATOR;
+                       seen_ma = 1;
                        break;
                }
 
@@ -1625,7 +1907,7 @@ int rad_packet_ok(RADIUS_PACKET *packet)
         *      If not, we complain, and throw the packet away.
         */
        if (count != 0) {
-               librad_log("WARNING: Malformed RADIUS packet from host %s: packet attributes do NOT exactly fill the packet",
+               fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: packet attributes do NOT exactly fill the packet",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)));
@@ -1637,13 +1919,13 @@ int rad_packet_ok(RADIUS_PACKET *packet)
         *      attributes, and we've seen more than that maximum,
         *      then throw the packet away, as a possible DoS.
         */
-       if ((librad_max_attributes > 0) &&
-           (num_attributes > librad_max_attributes)) {
-               librad_log("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).",
+       if ((fr_max_attributes > 0) &&
+           (num_attributes > fr_max_attributes)) {
+               fr_strerror_printf("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
-                          num_attributes, librad_max_attributes);
+                          num_attributes, fr_max_attributes);
                return 0;
        }
 
@@ -1654,11 +1936,12 @@ int rad_packet_ok(RADIUS_PACKET *packet)
         *      a Message-Authenticator attribute.
         *
         *      A Message-Authenticator all by itself is OK, though.
+        *
+        *      Similarly, Status-Server packets MUST contain
+        *      Message-Authenticator attributes.
         */
-       if (seen_eap &&
-           (seen_eap != PW_MESSAGE_AUTHENTICATOR) &&
-           (seen_eap != (PW_EAP_MESSAGE | PW_MESSAGE_AUTHENTICATOR))) {
-               librad_log("WARNING: Insecure packet from host %s:  Received EAP-Message with no Message-Authenticator.",
+       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)));
@@ -1680,20 +1963,26 @@ int rad_packet_ok(RADIUS_PACKET *packet)
  *     Receive UDP client requests, and fill in
  *     the basics of a RADIUS_PACKET structure.
  */
-RADIUS_PACKET *rad_recv(int fd)
+RADIUS_PACKET *rad_recv(int fd, int flags)
 {
+       int sock_flags = 0;
        RADIUS_PACKET           *packet;
 
        /*
         *      Allocate the new request data structure
         */
        if ((packet = malloc(sizeof(*packet))) == NULL) {
-               librad_log("out of memory");
+               fr_strerror_printf("out of memory");
                return NULL;
        }
        memset(packet, 0, sizeof(*packet));
 
-       packet->data_len = rad_recvfrom(fd, &packet->data, 0,
+       if (flags & 0x02) {
+               sock_flags = MSG_PEEK;
+               flags &= ~0x02;
+       }
+
+       packet->data_len = rad_recvfrom(fd, &packet->data, sock_flags,
                                        &packet->src_ipaddr, &packet->src_port,
                                        &packet->dst_ipaddr, &packet->dst_port);
 
@@ -1701,16 +1990,40 @@ RADIUS_PACKET *rad_recv(int fd)
         *      Check for socket errors.
         */
        if (packet->data_len < 0) {
-               librad_log("Error receiving packet: %s", strerror(errno));
+               fr_strerror_printf("Error receiving packet: %s", strerror(errno));
+               /* packet->data is NULL */
+               free(packet);
+               return NULL;
+       }
+
+       /*
+        *      If the packet is too big, then rad_recvfrom did NOT
+        *      allocate memory.  Instead, it just discarded the
+        *      packet.
+        */
+       if (packet->data_len > MAX_PACKET_LEN) {
+               fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes.");
                /* packet->data is NULL */
                free(packet);
                return NULL;
        }
 
        /*
+        *      Read no data.  Continue.
+        *      This check is AFTER the MAX_PACKET_LEN check above, because
+        *      if the packet is larger than MAX_PACKET_LEN, we also have
+        *      packet->data == NULL
+        */
+       if ((packet->data_len == 0) || !packet->data) {
+               fr_strerror_printf("Empty packet: Socket is not ready.");
+               free(packet);
+               return NULL;
+       }
+
+       /*
         *      See if it's a well-formed RADIUS packet.
         */
-       if (!rad_packet_ok(packet)) {
+       if (!rad_packet_ok(packet, flags)) {
                rad_free(&packet);
                return NULL;
        }
@@ -1731,25 +2044,26 @@ RADIUS_PACKET *rad_recv(int fd)
         */
        packet->vps = NULL;
 
-       if (librad_debug) {
+       if (fr_debug_flag) {
                char host_ipaddr[128];
 
-               if ((packet->code > 0) && (packet->code < MAX_PACKET_CODE)) {
-                       printf("rad_recv: %s packet from host %s port %d",
-                              packet_codes[packet->code],
-                              inet_ntop(packet->src_ipaddr.af,
-                                        &packet->src_ipaddr.ipaddr,
-                                        host_ipaddr, sizeof(host_ipaddr)),
-                              packet->src_port);
+               if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+                       DEBUG("rad_recv: %s packet from host %s port %d",
+                             fr_packet_codes[packet->code],
+                             inet_ntop(packet->src_ipaddr.af,
+                                       &packet->src_ipaddr.ipaddr,
+                                       host_ipaddr, sizeof(host_ipaddr)),
+                             packet->src_port);
                } else {
-                       printf("rad_recv: Packet from host %s port %d code=%d",
-                              inet_ntop(packet->src_ipaddr.af,
-                                        &packet->src_ipaddr.ipaddr,
-                                        host_ipaddr, sizeof(host_ipaddr)),
-                              packet->src_port,
-                              packet->code);
+                       DEBUG("rad_recv: Packet from host %s port %d code=%d",
+                             inet_ntop(packet->src_ipaddr.af,
+                                       &packet->src_ipaddr.ipaddr,
+                                       host_ipaddr, sizeof(host_ipaddr)),
+                             packet->src_port,
+                             packet->code);
                }
-               printf(", id=%d, length=%d\n", packet->id, packet->data_len);
+               DEBUG(", id=%d, length=%d\n",
+                     packet->id, (int) packet->data_len);
        }
 
        return packet;
@@ -1811,19 +2125,20 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                        case PW_AUTHENTICATION_REJECT:
                        case PW_ACCESS_CHALLENGE:
                                if (!original) {
-                                       librad_log("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;
                        }
 
-                       lrad_hmac_md5(packet->data, packet->data_len,
-                                     secret, strlen(secret), calc_auth_vector);
-                       if (memcmp(calc_auth_vector, msg_auth_vector,
+                       fr_hmac_md5(packet->data, packet->data_len,
+                                   (const uint8_t *) secret, strlen(secret),
+                                   calc_auth_vector);
+                       if (digest_cmp(calc_auth_vector, msg_auth_vector,
                                   sizeof(calc_auth_vector)) != 0) {
                                char buffer[32];
-                               librad_log("Received packet from %s with invalid Message-Authenticator!  (Shared secret is incorrect.)",
+                               fr_strerror_printf("Received packet from %s with invalid Message-Authenticator!  (Shared secret is incorrect.)",
                                           inet_ntop(packet->src_ipaddr.af,
                                                     &packet->src_ipaddr.ipaddr,
                                                     buffer, sizeof(buffer)));
@@ -1847,10 +2162,10 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
         *      It looks like a RADIUS packet, but we can't validate
         *      the signature.
         */
-       if ((packet->code == 0) || packet->code >= MAX_PACKET_CODE) {
+       if ((packet->code == 0) || (packet->code >= FR_MAX_PACKET_CODE)) {
                char buffer[32];
-               librad_log("Received Unknown packet code %d"
-                          "from client %s port %d: Cannot validate signature",
+               fr_strerror_printf("Received Unknown packet code %d "
+                          "from client %s port %d: Cannot validate signature.",
                           packet->code,
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
@@ -1868,17 +2183,19 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
 
                case PW_AUTHENTICATION_REQUEST:
                case PW_STATUS_SERVER:
-               case PW_DISCONNECT_REQUEST:
                        /*
                         *      The authentication vector is random
                         *      nonsense, invented by the client.
                         */
                        break;
 
+               case PW_COA_REQUEST:
+               case PW_DISCONNECT_REQUEST:
                case PW_ACCOUNTING_REQUEST:
                        if (calc_acctdigest(packet, secret) > 1) {
-                               librad_log("Received Accounting-Request packet "
-                                          "from %s with invalid signature!  (Shared secret is incorrect.)",
+                               fr_strerror_printf("Received %s packet "
+                                          "from client %s with invalid signature!  (Shared secret is incorrect.)",
+                                          fr_packet_codes[packet->code],
                                           inet_ntop(packet->src_ipaddr.af,
                                                     &packet->src_ipaddr.ipaddr,
                                                     buffer, sizeof(buffer)));
@@ -1897,20 +2214,19 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                case PW_COA_NAK:
                        rcode = calc_replydigest(packet, original, secret);
                        if (rcode > 1) {
-                               librad_log("Received %s packet "
-                                          "from client %s port %d with invalid signature (err=%d)!  (Shared secret is incorrect.)",
-                                          packet_codes[packet->code],
+                               fr_strerror_printf("Received %s packet "
+                                          "from home server %s port %d with invalid signature!  (Shared secret is incorrect.)",
+                                          fr_packet_codes[packet->code],
                                           inet_ntop(packet->src_ipaddr.af,
                                                     &packet->src_ipaddr.ipaddr,
                                                     buffer, sizeof(buffer)),
-                                          packet->src_port,
-                                          rcode);
+                                          packet->src_port);
                                return -1;
                        }
                        break;
 
                default:
-                       librad_log("Received Unknown packet code %d"
+                       fr_strerror_printf("Received Unknown packet code %d "
                                   "from client %s port %d: Cannot validate signature",
                                   packet->code,
                                   inet_ntop(packet->src_ipaddr.af,
@@ -1924,213 +2240,154 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
 }
 
 
-/*
- *     Hack for IETF RADEXT.  Don't use in a production environment.
- *
- *     Note that due to architecture limitations, we can't handle
- *     AVP's with more than 253 bytes of data, but that should be
- *     enough for initial inter-operability.
- */
-static int diameter2vp(const RADIUS_PACKET *packet,
-                      const RADIUS_PACKET *original, const char *secret,
-                      const uint8_t *data, int total_length,
-                      VALUE_PAIR **pvp)
+static VALUE_PAIR *data2vp(const RADIUS_PACKET *packet,
+                          const RADIUS_PACKET *original,
+                          const char *secret, size_t length,
+                          const uint8_t *data, VALUE_PAIR *vp)
 {
-       int lookup, offset, radius_length, attr_length, diameter_length, raw;
-       uint32_t attr, length, vendor;
-       DICT_ATTR *da;
-       VALUE_PAIR *head, **tail, *vp;
-       uint8_t *header;        /* diameter header */
-       uint8_t diameter[4096];
-
-       diameter_length = radius_length = 0;
-       header = diameter;
-
-       *pvp = NULL;
+       int offset = 0;
 
        /*
-        *      Unpack all contiguous Extended-Attributes into a local
-        *      buffer.  It's slow, but it's safe.
+        *      If length is greater than 253, something is SERIOUSLY
+        *      wrong.
         */
-       while (total_length > 0) {
-               if (data[0] != PW_EXTENDED_ATTRIBUTE) break;
-
-               attr_length = data[1];
-               radius_length += attr_length;
-               total_length -= attr_length;
-               attr_length -= 2;
-
-               memcpy(header, data + 2, attr_length);
-               header += attr_length;
-               diameter_length += attr_length;
-
-               data += attr_length + 2;
-       }
-
-       head = NULL;
-       tail = NULL;
-       header = diameter;
-
-next_diameter:
-       raw = 0;
+       if (length > 253) length = 253; /* paranoia (pair-anoia?) */
 
-       if ((vp = paircreate(PW_EXTENDED_ATTRIBUTE, PW_TYPE_OCTETS)) == NULL) {
-               pairfree(&head);
-               return radius_length;
-       }
+       vp->length = length;
+       vp->operator = T_OP_EQ;
+       vp->next = NULL;
 
        /*
-        *      Don't add it to the tail until we know it's OK.
+        *      It's supposed to be a fixed length, but we found
+        *      a different length instead.  Make it type "octets",
+        *      and do no more processing on it.
         */
+       if ((vp->flags.length > 0) && (vp->flags.length != length)) {
+               goto raw;
+       }
 
        /*
-        *      Too little data to contain a diameter header.
+        *      Handle tags.
         */
-       if (diameter_length < 12) {
-       done:
-               vp->type = PW_TYPE_OCTETS;
-               memcpy(vp->vp_octets, header, diameter_length);
-               vp->length = diameter_length;
-               
-               /*
-                *      Ensure there's no encryption or tag stuff,
-                *      we just pass the attribute as-is.
-                */
-               memset(&vp->flags, 0, sizeof(vp->flags));
-
+       if (vp->flags.has_tag) {
+               if (TAG_VALID(data[0]) ||
+                   (vp->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD)) {
+                       /*
+                        *      Tunnel passwords REQUIRE a tag, even
+                        *      if don't have a valid tag.
+                        */
+                       vp->flags.tag = data[0];
 
-               if (!head) {    /* add vp to the list */
-                       *pvp = vp;
-               } else {
-                       *pvp = head;
-                       *tail = vp;
+                       if ((vp->type == PW_TYPE_STRING) ||
+                           (vp->type == PW_TYPE_OCTETS)) {
+                               if (length == 0) goto raw;
+                               offset = 1;
+                       }
                }
-
-               return radius_length;
        }
 
        /*
-        *      Sanity check the lengths, next.  If the length is too
-        *      short, then the rest of the diameter buffer can't be
-        *      parsed.
+        *      Copy the data to be decrypted
         */
-       length = (header[5] << 16) | (header[6] << 8) | header[7];
+       memcpy(&vp->vp_octets[0], data + offset, length - offset);
+       vp->length -= offset;
 
        /*
-        *      lies about the length (too short)
-        *      or lies about the length (too long)
-        *      or VSA
-        *              with too little diameter data
-        *              or with too little content
+        *      Decrypt the attribute.
         */
-       if ((length < 9) ||
-           (length > diameter_length) ||
-           (((header[4] & 0x80) != 0) &&
-            ((diameter_length < 16) || (length < 13)))) {
+       switch (vp->flags.encrypt) {
                /*
-                *      The rest of the data is small enough to fit
-                *      into one VP.  Copy it over, and return it.
+                *  User-Password
                 */
-               if (diameter_length <= 253) goto done;
+       case FLAG_ENCRYPT_USER_PASSWORD:
+               if (original) {
+                       rad_pwdecode((char *)vp->vp_strvalue,
+                                    vp->length, secret,
+                                    original->vector);
+               } else {
+                       rad_pwdecode((char *)vp->vp_strvalue,
+                                    vp->length, secret,
+                                    packet->vector);
+               }
+               if (vp->attribute == PW_USER_PASSWORD) {
+                       vp->length = strlen(vp->vp_strvalue);
+               }
+               break;
 
                /*
-                *      FIXME: make multiple VP's of the rest of the
-                *      data, so that we don't lose anything!
+                *      Tunnel-Password's may go ONLY
+                *      in response packets.
                 */
-               pairfree(&vp);
-
-               *pvp = head;
-               return radius_length;
-       }
+       case FLAG_ENCRYPT_TUNNEL_PASSWORD:
+               if (!original) goto raw;
 
-       memcpy(&attr, header, 4);
-       attr = ntohl(attr);
-       if (attr > 65535) raw = 1; /* implementation limitations */
+               if (rad_tunnel_pwdecode(vp->vp_octets, &vp->length,
+                                       secret, original->vector) < 0) {
+                       goto raw;
+               }
+               break;
 
-       /*
-        *      1..255 are RADIUS, and shouldn't be encapsulated this way.
-        *      256.. are Diameter.
-        *
-        *      We've arbitrarily assigned 32768..65535 from the
-        *      Diameter space to "extended RADIUS" attributes.
-        */
-       if (attr < 32768) raw = 1;
+               /*
+                *  Ascend-Send-Secret
+                *  Ascend-Receive-Secret
+                */
+       case FLAG_ENCRYPT_ASCEND_SECRET:
+               if (!original) {
+                       goto raw;
+               } else {
+                       uint8_t my_digest[AUTH_VECTOR_LEN];
+                       make_secret(my_digest,
+                                   original->vector,
+                                   secret, data);
+                       memcpy(vp->vp_strvalue, my_digest,
+                              AUTH_VECTOR_LEN );
+                       vp->vp_strvalue[AUTH_VECTOR_LEN] = '\0';
+                       vp->length = strlen(vp->vp_strvalue);
+               }
+               break;
 
-       /*
-        *      We don't like any non-vendor flag bits being set.
-        */
-       if ((header[4] & 0x7f) != 0) raw = 1;
+       default:
+               break;
+       } /* switch over encryption flags */
 
-       vendor = 0;
-       if ((header[4] & 0x80) != 0) {
-               memcpy(&vendor, header + 8 , 4);
-               vendor = ntohl(vendor);
-               if (vendor > 32767) raw = 1; /* implementation limitations */
 
-               offset = 12;
-       } else {
-               offset = 8;
-       }
-       length -= offset;
-       header += offset;
-       diameter_length -= offset;
+       switch (vp->type) {
+       case PW_TYPE_STRING:
+       case PW_TYPE_OCTETS:
+       case PW_TYPE_ABINARY:
+               /* nothing more to do */
+               break;
 
-       /*
-        *      FIXME: This is an implementation limitation.  We
-        *      should really allow strings longer than 253 bytes...
-        *
-        *      And bailing out completely (i.e. throwing away the rest
-        *      of the data) isn't an intelligent thing to do, either.
-        */
-       if (length > 253) {
-               *pvp = head;
-               pairfree(&vp);
-               return radius_length;
-       }
+       case PW_TYPE_BYTE:
+               if (vp->length != 1) goto raw;
 
-       lookup = attr;
-       lookup |= (vendor << 16);
-       lookup |= (1 << 31);    /* see dict_addattr */
+               vp->vp_integer = vp->vp_octets[0];
+               break;
 
-       da = dict_attrbyvalue(lookup);
-       if (!da) raw = 1;
 
-       if (!raw) {
-               /*
-                *      Copied from paircreate.
-                */
-               strcpy(vp->name, da->name);
-               vp->type = da->type;
-               vp->flags = da->flags;
-               vp->attribute = da->attr;
-       } else {
-               vp->type = PW_TYPE_OCTETS;
-       }
+       case PW_TYPE_SHORT:
+               if (vp->length != 2) goto raw;
 
-       switch (vp->type) {
-       case PW_TYPE_STRING:
-       case PW_TYPE_OCTETS:
-       case PW_TYPE_ABINARY:
-               memcpy(vp->vp_octets, header, length);
-               vp->length = length;
+               vp->vp_integer = (vp->vp_octets[0] << 8) | vp->vp_octets[1];
                break;
 
        case PW_TYPE_INTEGER:
-               if (length != 4) goto force_octets;
+               if (vp->length != 4) goto raw;
+
+               memcpy(&vp->vp_integer, vp->vp_octets, 4);
+               vp->vp_integer = ntohl(vp->vp_integer);
 
-               memcpy(&vp->lvalue, header, 4);
-               vp->lvalue = ntohl(vp->lvalue);
-               vp->length = 4;
+               if (vp->flags.has_tag) vp->vp_integer &= 0x00ffffff;
 
                /*
                 *      Try to get named VALUEs
                 */
                {
                        DICT_VALUE *dval;
-                       dval = dict_valbyattr(vp->attribute,
-                                             vp->lvalue);
+                       dval = dict_valbyattr(vp->attribute, vp->vendor,
+                                             vp->vp_integer);
                        if (dval) {
-                               strNcpy(vp->vp_strvalue,
+                               strlcpy(vp->vp_strvalue,
                                        dval->name,
                                        sizeof(vp->vp_strvalue));
                        }
@@ -2138,39 +2395,35 @@ next_diameter:
                break;
 
        case PW_TYPE_DATE:
-               if (length != 4) goto force_octets;
+               if (vp->length != 4) goto raw;
 
-               memcpy(&vp->lvalue, header, 4);
-               vp->lvalue = ntohl(vp->lvalue);
-               vp->length = 4;
+               memcpy(&vp->vp_date, vp->vp_octets, 4);
+               vp->vp_date = ntohl(vp->vp_date);
                break;
 
 
        case PW_TYPE_IPADDR:
-               if (length != 4) goto force_octets;
+               if (vp->length != 4) goto raw;
 
-               memcpy(&vp->lvalue, header, 4);
-               vp->length = 4;
+               memcpy(&vp->vp_ipaddr, vp->vp_octets, 4);
                break;
 
                /*
                 *      IPv6 interface ID is 8 octets long.
                 */
        case PW_TYPE_IFID:
-               if (length != 8) goto force_octets;
-               memcpy(vp->vp_ifid, header, 8);
-               vp->length = 8;
+               if (vp->length != 8) goto raw;
+               /* vp->vp_ifid == vp->vp_octets */
                break;
-               
+
                /*
                 *      IPv6 addresses are 16 octets long
                 */
        case PW_TYPE_IPV6ADDR:
-               if (length != 16) goto force_octets;
-               memcpy(&vp->vp_ipv6addr, header, 16);
-               vp->length = 16;
+               if (vp->length != 16) goto raw;
+               /* vp->vp_ipv6addr == vp->vp_octets */
                break;
-               
+
                /*
                 *      IPv6 prefixes are 2 to 18 octets long.
                 *
@@ -2181,12 +2434,8 @@ next_diameter:
                 *      The prefix length can have value 0 to 128.
                 */
        case PW_TYPE_IPV6PREFIX:
-               if (length < 2 || length > 18) goto force_octets;
-               if (header[1] > 128) goto force_octets;
-
-               memcpy(vp->vp_ipv6prefix, header, length);
-               vp->length = length;
-               
+               if (vp->length < 2 || vp->length > 18) goto raw;
+               if (vp->vp_octets[1] > 128) goto raw;
 
                /*
                 *      FIXME: double-check that
@@ -2198,264 +2447,481 @@ next_diameter:
                }
                break;
 
-       default:
-       force_octets:
-               vp->type = PW_TYPE_OCTETS;
-               
+       case PW_TYPE_SIGNED:
+               if (vp->length != 4) goto raw;
+
                /*
-                *      Ensure there's no encryption or tag stuff,
-                *      we just pass the attribute as-is.
+                *      Overload vp_integer for ntohl, which takes
+                *      uint32_t, not int32_t
                 */
-               memset(&vp->flags, 0, sizeof(vp->flags));
+               memcpy(&vp->vp_integer, vp->vp_octets, 4);
+               vp->vp_integer = ntohl(vp->vp_integer);
+               memcpy(&vp->vp_signed, &vp->vp_integer, 4);
                break;
-       }
 
-       if (!head) {
-               head = vp;
-       } else {
-               *tail = vp;
-       }
-       tail = &vp->next;
+       case PW_TYPE_TLV:
+               vp->length = length;
+               vp->vp_tlv = malloc(length);
+               if (!vp->vp_tlv) {
+                       pairfree(&vp);
+                       fr_strerror_printf("No memory");
+                       return NULL;
+               }
+               memcpy(vp->vp_tlv, data, length);
+               break;
 
-       header += length;
-       diameter_length -= length;
+       case PW_TYPE_COMBO_IP:
+               if (vp->length == 4) {
+                       vp->type = PW_TYPE_IPADDR;
+                       memcpy(&vp->vp_ipaddr, vp->vp_octets, 4);
+                       break;
 
-       if ((length & 0x03) != 0) {
-               attr_length = 4 - (length & 0x03); /* padding */
+               } else if (vp->length == 16) {
+                       vp->type = PW_TYPE_IPV6ADDR;
+                       /* vp->vp_ipv6addr == vp->vp_octets */
+                       break;
 
-               if (diameter_length < attr_length) {
-                       *pvp = head;
-                       return radius_length;
                }
+               /* FALL-THROUGH */
+
+       default:
+       raw:
+               /*
+                *      Change the name to show the user that the
+                *      attribute is not of the correct format.
+                */
+               {
+                       int attr = vp->attribute;
+                       int vendor = vp->vendor;
+                       VALUE_PAIR *vp2;
+
+                       vp2 = pairalloc(NULL);
+                       if (!vp2) {
+                               pairfree(&vp);
+                               return NULL;
+                       }
+                       pairfree(&vp);
+                       vp = vp2;
 
-               header += attr_length;
-               diameter_length -= attr_length;
+                       /*
+                        *      This sets "vp->flags" appropriately,
+                        *      and vp->type.
+                        */
+                       if (!paircreate_raw(attr, vendor, PW_TYPE_OCTETS, vp)) {
+                               return NULL;
+                       }
+
+                       vp->length = length;
+                       memcpy(vp->vp_octets, data, length);
+               }
+               break;
        }
-       if (diameter_length > 0) goto next_diameter;
 
-       *pvp = head;
-       return radius_length;
+       return vp;
 }
 
+static void rad_sortvp(VALUE_PAIR **head)
+{
+       int swapped;
+       VALUE_PAIR *vp, **tail;
+
+       /*
+        *      Walk over the VP's, sorting them in order.  Did I
+        *      mention that I hate WiMAX continuations?
+        *
+        *      And bubble sort!  WTF is up with that?
+        */
+       do {
+               swapped = 0;
+               tail = head;
+               while (*tail) {
+                       vp = *tail;
+                       if (!vp->next) break;
+
+                       if (vp->attribute > vp->next->attribute) {
+                               *tail = vp->next;
+                               vp->next = (*tail)->next;
+                               (*tail)->next = vp;
+                               swapped = 1;
+                       }
+                       tail = &(vp->next);
+               }
+       } while (swapped);
+}
 
 
 /*
- *     Parse a RADIUS attribute into a data structure.
+ *     Walk the packet, looking for continuations of this attribute.
+ *
+ *     This is (worst-case) O(N^2) in the number of RADIUS
+ *     attributes.  That happens only when perverse clients create
+ *     continued attributes, AND separate the fragmented portions
+ *     with a lot of other attributes.
+ *
+ *     Sane clients should put the fragments next to each other, in
+ *     which case this is O(N), in the number of fragments.
  */
-VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
-                       const char *secret, int attribute, int length,
-                       const uint8_t *data)
+static uint8_t *rad_coalesce(unsigned int attribute, int vendor,
+                            size_t length, uint8_t *data,
+                            size_t packet_length, size_t *ptlv_length)
+                            
 {
-       int offset = 0;
-       VALUE_PAIR *vp;
+       uint32_t lvalue;
+       size_t tlv_length = length;
+       uint8_t *ptr, *tlv, *tlv_data;
+
+       for (ptr = data + length;
+            ptr != (data + packet_length);
+            ptr += ptr[1]) {
+               /* FIXME: Check that there are 6 bytes of data here... */
+               if ((ptr[0] != PW_VENDOR_SPECIFIC) ||
+                   (ptr[1] < (2 + 4 + 3)) || /* WiMAX VSA with continuation */
+                   (ptr[2] != 0) || (ptr[3] != 0) ||  /* our requirement */
+                   (ptr[4] != ((vendor >> 8) & 0xff)) ||
+                   (ptr[5] != (vendor & 0xff))) {
+                       continue;
+               }
 
-       if ((vp = paircreate(attribute, PW_TYPE_OCTETS)) == NULL) {
-               return NULL;
+               memcpy(&lvalue, ptr + 2, 4); /* Vendor Id */
+               lvalue = ntohl(lvalue);
+               lvalue <<= 16;
+               lvalue |= ptr[2 + 4]; /* add in VSA number */
+               if (lvalue != attribute) continue;
+
+               /*
+                *      If the vendor-length is too small, it's badly
+                *      formed, so we stop.
+                */
+               if ((ptr[2 + 4 + 1]) < 3) break;
+
+               tlv_length += ptr[2 + 4 + 1] - 3;
+               if ((ptr[2 + 4 + 1 + 1] & 0x80) == 0) break;
        }
-       
-       /*
-        *      If length is greater than 253, something is SERIOUSLY
-        *      wrong.
-        */
-       if (length > 253) length = 253; /* paranoia (pair-anoia?) */
 
-       vp->length = length;
-       vp->operator = T_OP_EQ;
-       vp->next = NULL;
+       tlv = tlv_data = malloc(tlv_length);
+       if (!tlv_data) return NULL;
+
+       memcpy(tlv, data, length);
+       tlv += length;
 
        /*
-        *      Handle tags.
+        *      Now we walk the list again, copying the data over to
+        *      our newly created memory.
         */
-       if (vp->flags.has_tag) {
-               if (TAG_VALID(data[0]) ||
-                   (vp->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD)) {
-                       /*
-                        *      Tunnel passwords REQUIRE a tag, even
-                        *      if don't have a valid tag.
-                        */
-                       vp->flags.tag = data[0];
+       for (ptr = data + length;
+            ptr != (data + packet_length);
+            ptr += ptr[1]) {
+               int this_length;
 
-                       if ((vp->type == PW_TYPE_STRING) ||
-                           (vp->type == PW_TYPE_OCTETS)) offset = 1;
+               if ((ptr[0] != PW_VENDOR_SPECIFIC) ||
+                   (ptr[1] < (2 + 4 + 3)) || /* WiMAX VSA with continuation */
+                   (ptr[2] != 0) || (ptr[3] != 0)) { /* our requirement */
+                       continue;
                }
+
+               memcpy(&lvalue, ptr + 2, 4);
+               lvalue = ntohl(lvalue);
+               lvalue <<= 16;
+               lvalue |= ptr[2 + 4];
+               if (lvalue != attribute) continue;
+
+               /*
+                *      If the vendor-length is too small, it's badly
+                *      formed, so we stop.
+                */
+               if ((ptr[2 + 4 + 1]) < 3) break;
+
+               this_length = ptr[2 + 4 + 1] - 3;
+               memcpy(tlv, ptr + 2 + 4 + 3, this_length);
+               tlv += this_length;
+
+               ptr[2 + 4] = 0; /* What a hack! */
+               if ((ptr[2 + 4 + 1 + 1] & 0x80) == 0) break;
        }
 
-       /*
-        *      Copy the data to be decrypted
-        */
-       memcpy(&vp->vp_octets[0], data + offset, length - offset);
-       vp->length -= offset;
+       *ptlv_length = tlv_length;
+       return tlv_data;
+}
+
+
+/*
+ *     Walk over Evil WIMAX TLVs, creating attributes.
+ */
+static VALUE_PAIR *tlv2wimax(const RADIUS_PACKET *packet,
+                            const RADIUS_PACKET *original,
+                            const char *secret,
+                            int attribute, int vendor,
+                            uint8_t *ptr, size_t len, int nest)
+{
+       VALUE_PAIR *head = NULL;
+       VALUE_PAIR **tail = &head;
+       VALUE_PAIR *vp;
+       uint8_t *y;             /* why do I need to do this? */
+
+       if (nest > fr_wimax_max_tlv) return NULL;
 
        /*
-        *      Decrypt the attribute.
+        *      Sanity check the attribute.
         */
-       switch (vp->flags.encrypt) {
-               /*
-                *  User-Password
-                */
-       case FLAG_ENCRYPT_USER_PASSWORD:
-               if (original) {
-                       rad_pwdecode((char *)vp->vp_strvalue,
-                                    vp->length, secret,
-                                    original->vector);
-               } else {
-                       rad_pwdecode((char *)vp->vp_strvalue,
-                                    vp->length, secret,
-                                    packet->vector);
-               }
-               if (vp->attribute == PW_USER_PASSWORD) {
-                       vp->length = strlen(vp->vp_strvalue);
-               }
-               break;
-               
-               /*
-                *      Tunnel-Password's may go ONLY
-                *      in response packets.
-                */
-       case FLAG_ENCRYPT_TUNNEL_PASSWORD:
-               if (!original) goto raw;
-               
-               if (rad_tunnel_pwdecode(vp->vp_octets, &vp->length,
-                                       secret, original->vector) < 0) {
-                       goto raw;
-               }
-               break;
-               
+       for (y = ptr; y < (ptr + len); y += y[1]) {
+               if ((y[0] == 0) || ((y + 2) > (ptr + len)) ||
+                   (y[1] < 2) || ((y + y[1]) > (ptr + len))) {
+                       return NULL;
+               }
+
                /*
-                *  Ascend-Send-Secret
-                *  Ascend-Receive-Secret
+                *      Attribute number is too large for us to
+                *      represent it in our horrible internal
+                *      representation.
                 */
-       case FLAG_ENCRYPT_ASCEND_SECRET:
-               if (!original) {
-                       goto raw;
-               } else {
-                       uint8_t my_digest[AUTH_VECTOR_LEN];
-                       make_secret(my_digest,
-                                   original->vector,
-                                   secret, data);
-                       memcpy(vp->vp_strvalue, my_digest,
-                              AUTH_VECTOR_LEN );
-                       vp->vp_strvalue[AUTH_VECTOR_LEN] = '\0';
-                       vp->length = strlen(vp->vp_strvalue);
+               if ((ptr[0] & ~fr_wimax_mask[nest]) != 0) {
+                       return NULL;
                }
-               break;
+       }
 
-       default:
-               break;
-       } /* switch over encryption flags */
+       for (y = ptr; y < (ptr + len); y += y[1]) {
+               DICT_ATTR *da;
 
+               da = dict_attrbyvalue(attribute | (ptr[0] << fr_wimax_shift[nest]), vendor);
+               if (da && (da->type == PW_TYPE_TLV)) {
+                       vp = tlv2wimax(packet, original, secret,
+                                      attribute | (ptr[0] << fr_wimax_shift[nest]),
+                                      vendor, ptr + 2, ptr[1] - 2,
+                                      nest + 1);
+                       if (!vp) goto error;
+               } else {
+                       vp = paircreate(attribute | (ptr[0] << fr_wimax_shift[nest]), vendor,
+                                       PW_TYPE_OCTETS);
+                       if (!vp) {
+                       error:
+                               pairfree(&head);
+                               return NULL;
+                       }
 
-       switch (vp->type) {
-       case PW_TYPE_STRING:
-       case PW_TYPE_OCTETS:
-       case PW_TYPE_ABINARY:
-               /* nothing more to do */
-               break;
+                       if (!data2vp(packet, original, secret,
+                                    y[1] - 2, y + 2, vp)) {
+                               goto error;
+                       }
+               }
 
-       case PW_TYPE_BYTE:
-               if (vp->length != 1) goto raw;
+               *tail = vp;
+               while (*tail) tail = &((*tail)->next);
+       }
 
-               vp->lvalue = vp->vp_octets[0];
-               break;
+       return head;
+}
 
+/*
+ *     Start at the *data* portion of a continued attribute.  search
+ *     through the rest of the attributes to find a matching one, and
+ *     add it's contents to our contents.
+ */
+static VALUE_PAIR *rad_continuation2vp(const RADIUS_PACKET *packet,
+                                      const RADIUS_PACKET *original,
+                                      const char *secret, int attribute,
+                                      int vendor,
+                                      int length, /* CANNOT be zero */
+                                      uint8_t *data, size_t packet_length,
+                                      int flag, DICT_ATTR *da)
+{
+       size_t tlv_length, left;
+       uint8_t *ptr;
+       uint8_t *tlv_data;
+       VALUE_PAIR *vp, *head, **tail;
+       DICT_ATTR *tlv_da;
 
-       case PW_TYPE_SHORT:
-               if (vp->length != 2) goto raw;
+       /*
+        *      Ensure we have data that hasn't been split across
+        *      multiple attributes.
+        */
+       if (flag) {
+               tlv_data = rad_coalesce(attribute, vendor, length,
+                                       data, packet_length, &tlv_length);
+               if (!tlv_data) return NULL;
+       } else {
+               tlv_data = data;
+               tlv_length = length;
+       }
 
-               vp->lvalue = (vp->vp_octets[0] << 8) | vp->vp_octets[1];
-               break;
+       /*
+        *      Non-TLV types cannot be continued across multiple
+        *      attributes.  This is true even of keys that are
+        *      encrypted with the tunnel-password method.  The spec
+        *      says that they can be continued... but also that the
+        *      keys are 160 bits, which means that they CANNOT be
+        *      continued.  <sigh>
+        *
+        *      Note that we don't check "flag" here.  The calling
+        *      code ensures that 
+        */
+       if (!da || (da->type != PW_TYPE_TLV)) {
+       not_well_formed:
+               if (tlv_data == data) { /* true if we had 'goto' */
+                       tlv_data = malloc(tlv_length);
+                       if (!tlv_data) return NULL;
+                       memcpy(tlv_data, data, tlv_length);
+               }
+               
+               vp = paircreate(attribute, vendor, PW_TYPE_OCTETS);
+               if (!vp) return NULL;
+                       
+               vp->type = PW_TYPE_TLV;
+               vp->flags.encrypt = FLAG_ENCRYPT_NONE;
+               vp->flags.has_tag = 0;
+               vp->flags.is_tlv = 0;
+               vp->vp_tlv = tlv_data;
+               vp->length = tlv_length;
+               return vp;
+       } /* else it WAS a TLV, go decode the sub-tlv's */
+
+       /*
+        *      Now (sigh) we walk over the TLV, seeing if it is
+        *      well-formed.
+        */
+       left = tlv_length;
+       for (ptr = tlv_data;
+            ptr != (tlv_data + tlv_length);
+            ptr += ptr[1]) {
+               if ((left < 2) ||
+                   (ptr[1] < 2) ||
+                   (ptr[1] > left)) {
+                       goto not_well_formed;
+               }
 
-       case PW_TYPE_INTEGER:
-               if (vp->length != 4) goto raw;
+               left -= ptr[1];
+       }
 
-               memcpy(&vp->lvalue, vp->vp_octets, 4);
-               vp->lvalue = ntohl(vp->lvalue);
+       /*
+        *      Now we walk over the TLV *again*, creating sub-tlv's.
+        */
+       head = NULL;
+       tail = &head;
 
-               if (vp->flags.has_tag) vp->lvalue &= 0x00ffffff;
+       for (ptr = tlv_data;
+            ptr != (tlv_data + tlv_length);
+            ptr += ptr[1]) {
 
-               /*
-                *      Try to get named VALUEs
-                */
-               {
-                       DICT_VALUE *dval;
-                       dval = dict_valbyattr(vp->attribute,
-                                             vp->lvalue);
-                       if (dval) {
-                               strNcpy(vp->vp_strvalue,
-                                       dval->name,
-                                       sizeof(vp->vp_strvalue));
+               tlv_da = dict_attrbyvalue(attribute | (ptr[0] << fr_wimax_shift[1]), vendor);
+               if (tlv_da && (tlv_da->type == PW_TYPE_TLV)) {
+                       vp = tlv2wimax(packet, original, secret,
+                                      attribute | (ptr[0] << 8),
+                                      vendor, ptr + 2, ptr[1] - 2, 2);
+
+                       if (!vp) goto error;
+               } else {
+                       vp = paircreate(attribute | (ptr[0] << fr_wimax_shift[1]), vendor,
+                                       PW_TYPE_OCTETS);
+                       if (!vp) {
+                       error:
+                               pairfree(&head);
+                               goto not_well_formed;
+                       }
+
+                       if (!data2vp(packet, original, secret,
+                                    ptr[1] - 2, ptr + 2, vp)) {
+                               goto error;
                        }
                }
-               break;
 
-       case PW_TYPE_DATE:
-               if (vp->length != 4) goto raw;
+               *tail = vp;
 
-               memcpy(&vp->lvalue, vp->vp_octets, 4);
-               vp->lvalue = ntohl(vp->lvalue);
-               break;
+               while (*tail) tail = &((*tail)->next);
+       }
 
+       /*
+        *      TLV's MAY be continued, but sometimes they're not.
+        */
+       if (tlv_data != data) free(tlv_data);
 
-       case PW_TYPE_IPADDR:
-               if (vp->length != 4) goto raw;
+       if (head->next) rad_sortvp(&head);
 
-               memcpy(&vp->lvalue, vp->vp_octets, 4);
-               break;
+       return head;
+}
 
-               /*
-                *      IPv6 interface ID is 8 octets long.
-                */
-       case PW_TYPE_IFID:
-               if (vp->length != 8) goto raw;
-               /* vp->vp_ifid == vp->vp_octets */
-               break;
-               
-               /*
-                *      IPv6 addresses are 16 octets long
-                */
-       case PW_TYPE_IPV6ADDR:
-               if (vp->length != 16) goto raw;
-               /* vp->vp_ipv6addr == vp->vp_octets */
-               break;
-               
-               /*
-                *      IPv6 prefixes are 2 to 18 octets long.
-                *
-                *      RFC 3162: The first octet is unused.
-                *      The second is the length of the prefix
-                *      the rest are the prefix data.
-                *
-                *      The prefix length can have value 0 to 128.
-                */
-       case PW_TYPE_IPV6PREFIX:
-               if (vp->length < 2 || vp->length > 18) goto raw;
-               if (vp->vp_octets[1] > 128) goto raw;
 
-               /*
-                *      FIXME: double-check that
-                *      (vp->vp_octets[1] >> 3) matches vp->length + 2
-                */
-               if (vp->length < 18) {
-                       memset(vp->vp_octets + vp->length, 0,
-                              18 - vp->length);
-               }
-               break;
+/*
+ *     Extended attribute TLV to VP.
+ */
+static VALUE_PAIR *tlv2vp(const RADIUS_PACKET *packet,
+                           const RADIUS_PACKET *original,
+                           const char *secret, int attribute,
+                           int length, const uint8_t *data)
+{
+       VALUE_PAIR *vp;
 
-       default:
-       raw:
-               vp->type = PW_TYPE_OCTETS;
-               vp->length = length;
-               memcpy(vp->vp_octets, data, length);
-               
+       if ((length < 2) || (data[1] < 2)) return NULL;
 
-               /*
-                *      Ensure there's no encryption or tag stuff,
-                *      we just pass the attribute as-is.
-                */
-               memset(&vp->flags, 0, sizeof(vp->flags));
+       /*
+        *      For now, only one TLV is allowed.
+        */
+       if (data[1] != length) return NULL;
+
+       attribute |= (data[0] << fr_wimax_shift[2]);
+
+       vp = paircreate(attribute, VENDORPEC_EXTENDED, PW_TYPE_OCTETS);
+       if (!vp) return NULL;
+
+       return data2vp(packet, original, secret, length - 2, data + 2, vp);
+}
+
+/*
+ *     Parse a RADIUS attribute into a data structure.
+ */
+VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet,
+                       const RADIUS_PACKET *original,
+                       const char *secret, int attribute, int vendor,
+                       int length, const uint8_t *data)
+{
+       VALUE_PAIR *vp;
+
+       /*
+        *      Hard-coded values are bad...
+        */
+       if ((vendor == 0) && (attribute >= 241) && (attribute <= 246)) {
+               DICT_ATTR *da;
+
+               da = dict_attrbyvalue(attribute, VENDORPEC_EXTENDED);
+               if (da && (da->flags.extended || da->flags.extended_flags)) {
+
+                       if (length == 0) return NULL;
+
+                       attribute |= (data[0] << fr_wimax_shift[1]);
+                       vendor = VENDORPEC_EXTENDED;
+
+                       data++;
+                       length--;
+
+                       /*
+                        *      There may be a flag octet.
+                        */
+                       if (da->flags.extended_flags) {
+                               if (length == 0) return NULL;
+
+                               /*
+                                *      If there's a flag, we can't
+                                *      handle it.
+                                */
+                               if (data[0] != 0) return NULL;
+                               data++;
+                               length--;
+                       }
+
+                       /*
+                        *      Now look up the extended attribute, to
+                        *      see if it's a TLV carrying more data.
+                        *      
+                        */
+                       da = dict_attrbyvalue(attribute, VENDORPEC_EXTENDED);
+                       if (da && da->flags.has_tlv) {
+                               return tlv2vp(packet, original, secret,
+                                             attribute, length, data);
+                       }
+               }
        }
+       vp = paircreate(attribute, vendor, PW_TYPE_OCTETS);
+       if (!vp) return NULL;
 
-       return vp;
+       return data2vp(packet, original, secret, length, data, vp);
 }
 
 
@@ -2472,14 +2938,15 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
        uint32_t                vendorcode;
        VALUE_PAIR              **tail;
        VALUE_PAIR              *pair;
-       uint8_t                 *ptr;
+       uint8_t                 *ptr, *vsa_ptr;
        int                     packet_length;
        int                     attribute;
        int                     attrlen;
        int                     vendorlen;
        radius_packet_t         *hdr;
-       int                     vsa_tlen, vsa_llen;
+       int                     vsa_tlen, vsa_llen, vsa_offset;
        DICT_VENDOR             *dv = NULL;
+       int                     num_attributes = 0;
 
        /*
         *      Extract attribute-value pairs
@@ -2499,6 +2966,7 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
        vendorcode = 0;
        vendorlen  = 0;
        vsa_tlen = vsa_llen = 1;
+       vsa_offset = 0;
 
        /*
         *      We have to read at least two bytes.
@@ -2523,19 +2991,6 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                            (ptr[0] == 0) ||  (ptr[1] < 2) ||
                            (ptr[1] > packet_length)) break;
 
-                       /*
-                        *      192 is "integer" for Ascend.  So if we
-                        *      get 12 or more bytes, it must be the
-                        *      new extended format.
-                        */
-                       if ((ptr[0] == PW_EXTENDED_ATTRIBUTE) &&
-                           (ptr[1] >= 2 + 12)) {
-                               pair = NULL;
-                               attrlen = diameter2vp(packet, original, secret,
-                                                     ptr, packet_length, &pair);
-                               goto check_pair;
-                       }
-
                        attribute = *ptr++;
                        attrlen   = *ptr++;
 
@@ -2543,7 +2998,7 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                        packet_length  -= 2;
 
                        if (attribute != PW_VENDOR_SPECIFIC) goto create_pair;
-                       
+
                        /*
                         *      No vendor code, or ONLY vendor code.
                         */
@@ -2551,7 +3006,7 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
 
                        vendorlen = 0;
                }
-               
+
                /*
                 *      Handle Vendor-Specific
                 */
@@ -2559,7 +3014,7 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                        uint8_t *subptr;
                        int sublen;
                        int myvendor;
-                       
+
                        /*
                         *      attrlen was checked above.
                         */
@@ -2570,23 +3025,22 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                         *      Zero isn't allowed.
                         */
                        if (myvendor == 0) goto create_pair;
-                       
+
                        /*
-                        *      This is an implementation issue.
-                        *      We currently pack vendor into the upper
-                        *      16 bits of a 32-bit attribute number,
-                        *      so we can't handle vendor numbers larger
-                        *      than 16 bits.
+                        *      Allow vendors up to 2^24.  Past that,
+                        *      get confused.
                         */
-                       if (myvendor > 65535) goto create_pair;
-                       
+                       if (myvendor > FR_MAX_VENDOR) goto create_pair;
+
                        vsa_tlen = vsa_llen = 1;
+                       vsa_offset = 0;
                        dv = dict_vendorbyvalue(myvendor);
                        if (dv) {
                                vsa_tlen = dv->type;
                                vsa_llen = dv->length;
+                               if (dv->flags) vsa_offset = 1;
                        }
-                       
+
                        /*
                         *      Sweep through the list of VSA's,
                         *      seeing if they exactly fill the
@@ -2604,10 +3058,11 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                                int myattr = 0;
 
                                /*
-                                *      Don't have a type, it's bad.
+                                *      Not enough room for one more
+                                *      attribute.  Die!
                                 */
-                               if (sublen < vsa_tlen) goto create_pair;
-                               
+                               if (sublen < (vsa_tlen + vsa_llen + vsa_offset)) goto create_pair;
+
                                /*
                                 *      Ensure that the attribute number
                                 *      is OK.
@@ -2616,44 +3071,49 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                                case 1:
                                        myattr = subptr[0];
                                        break;
-                                       
+
                                case 2:
                                        myattr = (subptr[0] << 8) | subptr[1];
                                        break;
-                                       
+
                                case 4:
                                        if ((subptr[0] != 0) ||
                                            (subptr[1] != 0)) goto create_pair;
-                                       
+
                                        myattr = (subptr[2] << 8) | subptr[3];
                                        break;
-                                       
+
                                        /*
                                         *      Our dictionary is broken.
                                         */
                                default:
                                        goto create_pair;
                                }
-                               
-                               /*
-                                *      Not enough room for one more
-                                *      attribute.  Die!
-                                */
-                               if (sublen < vsa_tlen + vsa_llen) goto create_pair;
+
                                switch (vsa_llen) {
                                case 0:
-                                       attribute = (myvendor << 16) | myattr;
+                                       attribute = myattr;
                                        ptr += 4 + vsa_tlen;
                                        attrlen -= (4 + vsa_tlen);
                                        packet_length -= 4 + vsa_tlen;
                                        goto create_pair;
 
                                case 1:
-                                       if (subptr[vsa_tlen] < (vsa_tlen + vsa_llen))
+                                       if (subptr[vsa_tlen] < (vsa_tlen + vsa_llen + vsa_offset))
                                                goto create_pair;
 
                                        if (subptr[vsa_tlen] > sublen)
                                                goto create_pair;
+
+                                       /*
+                                        *      WiMAX: 0bCrrrrrrr
+                                        *      Reserved bits MUST be
+                                        *      zero.
+                                        */
+                                       if (vsa_offset &&
+                                           ((subptr[vsa_tlen + vsa_llen] & 0x7f) != 0))
+                                               goto create_pair;
+
                                        sublen -= subptr[vsa_tlen];
                                        subptr += subptr[vsa_tlen];
                                        break;
@@ -2693,7 +3153,7 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                case 1:
                        attribute = ptr[0];
                        break;
-                       
+
                case 2:
                        attribute = (ptr[0] << 8) | ptr[1];
                        break;
@@ -2701,14 +3161,14 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                default:        /* can't hit this. */
                        return -1;
                }
-               attribute |= (vendorcode << 16);
+               vsa_ptr = ptr;
                ptr += vsa_tlen;
 
                switch (vsa_llen) {
                case 1:
-                       attrlen = ptr[0] - (vsa_tlen + vsa_llen);
+                       attrlen = ptr[0] - (vsa_tlen + vsa_llen + vsa_offset);
                        break;
-                       
+
                case 2:
                        attrlen = ptr[1] - (vsa_tlen + vsa_llen);
                        break;
@@ -2716,34 +3176,103 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                default:        /* can't hit this. */
                        return -1;
                }
-               ptr += vsa_llen;
-               vendorlen -= vsa_tlen + vsa_llen + attrlen;
-               if (vendorlen == 0) vendorcode = 0;
-               packet_length -= (vsa_tlen + vsa_llen);
+
+               ptr += vsa_llen + vsa_offset;
+               vendorlen -= vsa_tlen + vsa_llen + vsa_offset + attrlen;
+               packet_length -= (vsa_tlen + vsa_llen + vsa_offset);
+
+               /*
+                *      Ignore VSAs that have no data.
+                */
+               if (attrlen == 0) goto next;
+
+               /*
+                *      WiMAX attributes of type 0 are ignored.  They
+                *      are a secret flag to us that the attribute has
+                *      already been dealt with.
+                */
+               if ((vendorcode == VENDORPEC_WIMAX) && (attribute == 0)) {
+                       goto next;
+               }
+
+               if (vsa_offset) {
+                       DICT_ATTR *da;
+
+                       da = dict_attrbyvalue(attribute, vendorcode);
+
+                       /*
+                        *      If it's NOT continued, AND we know
+                        *      about it, AND it's not a TLV, we can
+                        *      create a normal pair.
+                        */
+                       if (((vsa_ptr[2] & 0x80) == 0) &&
+                           da && (da->type != PW_TYPE_TLV)) goto create_pair;
+
+                       /*
+                        *      Else it IS continued, or it's a TLV.
+                        *      Go do a lot of work to find the stuff.
+                        */
+                       pair = rad_continuation2vp(packet, original, secret,
+                                                  attribute, vendorcode,
+                                                  attrlen, ptr,
+                                                  packet_length,
+                                                  ((vsa_ptr[2] & 0x80) != 0),
+                                                  da);
+                       goto created_pair;
+               }
 
                /*
                 *      Create the attribute, setting the default type
                 *      to 'octets'.  If the type in the dictionary
                 *      is different, then the dictionary type will
                 *      over-ride this one.
+                *
+                *      If the attribute has no data, then discard it.
+                *
+                *      Unless it's CUI.  Damn you, CUI!
                 */
        create_pair:
+               if (!attrlen &&
+                   (attribute != PW_CHARGEABLE_USER_IDENTITY)) goto next;
+
                pair = rad_attr2vp(packet, original, secret,
-                                  attribute, attrlen, ptr);
-       check_pair:
+                                  attribute, vendorcode, attrlen, ptr);
                if (!pair) {
                        pairfree(&packet->vps);
-                       librad_log("out of memory");
+                       fr_strerror_printf("out of memory");
                        return -1;
                }
 
+       created_pair:
                *tail = pair;
                while (pair) {
+                       num_attributes++;
                        debug_pair(pair);
                        tail = &pair->next;
                        pair = pair->next;
                }
 
+               /*
+                *      VSA's may not have been counted properly in
+                *      rad_packet_ok() above, as it is hard to count
+                *      then without using the dictionary.  We
+                *      therefore enforce the limits here, too.
+                */
+               if ((fr_max_attributes > 0) &&
+                   (num_attributes > fr_max_attributes)) {
+                       char host_ipaddr[128];
+
+                       pairfree(&packet->vps);
+                       fr_strerror_printf("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).",
+                                  inet_ntop(packet->src_ipaddr.af,
+                                            &packet->src_ipaddr.ipaddr,
+                                            host_ipaddr, sizeof(host_ipaddr)),
+                                  num_attributes, fr_max_attributes);
+                       return -1;
+               }
+
+       next:
+               if (vendorlen == 0) vendorcode = 0;
                ptr += attrlen;
                packet_length -= attrlen;
        }
@@ -2752,8 +3281,8 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
         *      Merge information from the outside world into our
         *      random pool.
         */
-       lrad_rand_seed(packet->data, AUTH_HDR_LEN);
-         
+       fr_rand_seed(packet->data, AUTH_HDR_LEN);
+
        return 0;
 }
 
@@ -2769,10 +3298,10 @@ 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, int *pwlen, const char *secret,
+int rad_pwencode(char *passwd, size_t *pwlen, const char *secret,
                 const uint8_t *vector)
 {
-       lrad_MD5_CTX context, old;
+       FR_MD5_CTX context, old;
        uint8_t digest[AUTH_VECTOR_LEN];
        int     i, n, secretlen;
        int     len;
@@ -2802,9 +3331,9 @@ int rad_pwencode(char *passwd, int *pwlen, const char *secret,
         *      Use the secret to setup the decryption digest
         */
        secretlen = strlen(secret);
-       
-       lrad_MD5Init(&context);
-       lrad_MD5Update(&context, secret, secretlen);
+
+       fr_MD5Init(&context);
+       fr_MD5Update(&context, (const uint8_t *) secret, secretlen);
        old = context;          /* save intermediate work */
 
        /*
@@ -2813,16 +3342,16 @@ int rad_pwencode(char *passwd, int *pwlen, const char *secret,
         */
        for (n = 0; n < len; n += AUTH_PASS_LEN) {
                if (n == 0) {
-                       lrad_MD5Update(&context, vector, AUTH_PASS_LEN);
-                       lrad_MD5Final(digest, &context);
+                       fr_MD5Update(&context, vector, AUTH_PASS_LEN);
+                       fr_MD5Final(digest, &context);
                } else {
                        context = old;
-                       lrad_MD5Update(&context,
-                                        passwd + n - AUTH_PASS_LEN,
-                                        AUTH_PASS_LEN);
-                       lrad_MD5Final(digest, &context);
+                       fr_MD5Update(&context,
+                                    (uint8_t *) passwd + n - AUTH_PASS_LEN,
+                                    AUTH_PASS_LEN);
+                       fr_MD5Final(digest, &context);
                }
-               
+
                for (i = 0; i < AUTH_PASS_LEN; i++) {
                        passwd[i + n] ^= digest[i];
                }
@@ -2834,12 +3363,13 @@ int rad_pwencode(char *passwd, int *pwlen, const char *secret,
 /*
  *     Decode password.
  */
-int rad_pwdecode(char *passwd, int pwlen, const char *secret,
+int rad_pwdecode(char *passwd, size_t pwlen, const char *secret,
                 const uint8_t *vector)
 {
-       lrad_MD5_CTX context, old;
+       FR_MD5_CTX context, old;
        uint8_t digest[AUTH_VECTOR_LEN];
-       int     i, n, secretlen;
+       int     i;
+       size_t  n, secretlen;
 
        /*
         *      The RFC's say that the maximum is 128.
@@ -2857,9 +3387,9 @@ int rad_pwdecode(char *passwd, int pwlen, const char *secret,
         *      Use the secret to setup the decryption digest
         */
        secretlen = strlen(secret);
-       
-       lrad_MD5Init(&context);
-       lrad_MD5Update(&context, secret, secretlen);
+
+       fr_MD5Init(&context);
+       fr_MD5Update(&context, (const uint8_t *) secret, secretlen);
        old = context;          /* save intermediate work */
 
        /*
@@ -2867,18 +3397,24 @@ int rad_pwdecode(char *passwd, int pwlen, const char *secret,
         */
        for (n = 0; n < pwlen; n += AUTH_PASS_LEN) {
                if (n == 0) {
-                       lrad_MD5Update(&context, vector, AUTH_VECTOR_LEN);
-                       lrad_MD5Final(digest, &context);
+                       fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
+                       fr_MD5Final(digest, &context);
 
                        context = old;
-                       lrad_MD5Update(&context, passwd, AUTH_PASS_LEN);
+                       if (pwlen > AUTH_PASS_LEN) {
+                               fr_MD5Update(&context, (uint8_t *) passwd,
+                                            AUTH_PASS_LEN);
+                       }
                } else {
-                       lrad_MD5Final(digest, &context);
+                       fr_MD5Final(digest, &context);
 
                        context = old;
-                       lrad_MD5Update(&context, passwd + n, AUTH_PASS_LEN);
+                       if (pwlen > (n + AUTH_PASS_LEN)) {
+                               fr_MD5Update(&context, (uint8_t *) passwd + n,
+                                            AUTH_PASS_LEN);
+                       }
                }
-               
+
                for (i = 0; i < AUTH_PASS_LEN; i++) {
                        passwd[i + n] ^= digest[i];
                }
@@ -2899,7 +3435,7 @@ int rad_pwdecode(char *passwd, int 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, int *pwlen, const char *secret,
+int rad_tunnel_pwencode(char *passwd, size_t *pwlen, const char *secret,
                        const uint8_t *vector)
 {
        uint8_t buffer[AUTH_VECTOR_LEN + MAX_STRING_LEN + 3];
@@ -2936,8 +3472,8 @@ int rad_tunnel_pwencode(char *passwd, int *pwlen, const char *secret,
         *      add in some CSPRNG data.  should be OK..
         */
        salt[0] = (0x80 | ( ((salt_offset++) & 0x0f) << 3) |
-                  (lrad_rand() & 0x07));
-       salt[1] = lrad_rand();
+                  (fr_rand() & 0x07));
+       salt[1] = fr_rand();
 
        /*
         *      Padd password to multiple of AUTH_PASS_LEN bytes.
@@ -2961,10 +3497,10 @@ int rad_tunnel_pwencode(char *passwd, int *pwlen, const char *secret,
                if (!n2) {
                        memcpy(buffer + secretlen, vector, AUTH_VECTOR_LEN);
                        memcpy(buffer + secretlen + AUTH_VECTOR_LEN, salt, 2);
-                       librad_md5_calc(digest, buffer, secretlen + AUTH_VECTOR_LEN + 2);
+                       fr_md5_calc(digest, buffer, secretlen + AUTH_VECTOR_LEN + 2);
                } else {
                        memcpy(buffer + secretlen, passwd + n2 - AUTH_PASS_LEN, AUTH_PASS_LEN);
-                       librad_md5_calc(digest, buffer, secretlen + AUTH_PASS_LEN);
+                       fr_md5_calc(digest, buffer, secretlen + AUTH_PASS_LEN);
                }
 
                for (i = 0; i < AUTH_PASS_LEN; i++) {
@@ -2982,10 +3518,10 @@ int rad_tunnel_pwencode(char *passwd, int *pwlen, const char *secret,
  *      initial intermediate value, to differentiate it from the
  *      above.
  */
-int rad_tunnel_pwdecode(uint8_t *passwd, int *pwlen, const char *secret,
+int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, const char *secret,
                        const uint8_t *vector)
 {
-       lrad_MD5_CTX  context, old;
+       FR_MD5_CTX  context, old;
        uint8_t         digest[AUTH_VECTOR_LEN];
        int             secretlen;
        unsigned        i, n, len, reallen;
@@ -2996,7 +3532,7 @@ int rad_tunnel_pwdecode(uint8_t *passwd, int *pwlen, const char *secret,
         *      We need at least a salt.
         */
        if (len < 2) {
-               librad_log("tunnel password is too short");
+               fr_strerror_printf("tunnel password is too short");
                return -1;
        }
 
@@ -3023,8 +3559,8 @@ int rad_tunnel_pwdecode(uint8_t *passwd, int *pwlen, const char *secret,
         */
        secretlen = strlen(secret);
 
-       lrad_MD5Init(&context);
-       lrad_MD5Update(&context, secret, secretlen);
+       fr_MD5Init(&context);
+       fr_MD5Update(&context, (const uint8_t *) secret, secretlen);
        old = context;          /* save intermediate work */
 
        /*
@@ -3032,15 +3568,15 @@ int rad_tunnel_pwdecode(uint8_t *passwd, int *pwlen, const char *secret,
         *
         *       b(1) = MD5(secret + vector + salt)
         */
-       lrad_MD5Update(&context, vector, AUTH_VECTOR_LEN);
-       lrad_MD5Update(&context, passwd, 2);
+       fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
+       fr_MD5Update(&context, passwd, 2);
 
        reallen = 0;
        for (n = 0; n < len; n += AUTH_PASS_LEN) {
                int base = 0;
 
                if (n == 0) {
-                       lrad_MD5Final(digest, &context);
+                       fr_MD5Final(digest, &context);
 
                        context = old;
 
@@ -3051,18 +3587,18 @@ int rad_tunnel_pwdecode(uint8_t *passwd, int *pwlen, const char *secret,
                         */
                        reallen = passwd[2] ^ digest[0];
                        if (reallen >= len) {
-                               librad_log("tunnel password is too long for the attribute");
+                               fr_strerror_printf("tunnel password is too long for the attribute");
                                return -1;
                        }
 
-                       lrad_MD5Update(&context, passwd + 2, AUTH_PASS_LEN);
+                       fr_MD5Update(&context, passwd + 2, AUTH_PASS_LEN);
 
                        base = 1;
                } else {
-                       lrad_MD5Final(digest, &context);
+                       fr_MD5Final(digest, &context);
 
                        context = old;
-                       lrad_MD5Update(&context, passwd + n + 2, AUTH_PASS_LEN);
+                       fr_MD5Update(&context, passwd + n + 2, AUTH_PASS_LEN);
                }
 
                for (i = base; i < AUTH_PASS_LEN; i++) {
@@ -3092,7 +3628,7 @@ int rad_chap_encode(RADIUS_PACKET *packet, uint8_t *output, int id,
                    VALUE_PAIR *password)
 {
        int             i;
-       char            *ptr;
+       uint8_t         *ptr;
        uint8_t         string[MAX_STRING_LEN * 2 + 1];
        VALUE_PAIR      *challenge;
 
@@ -3123,7 +3659,7 @@ int rad_chap_encode(RADIUS_PACKET *packet, uint8_t *output, int id,
         *      Use Chap-Challenge pair if present,
         *      Request-Authenticator otherwise.
         */
-       challenge = pairfind(packet->vps, PW_CHAP_CHALLENGE);
+       challenge = pairfind(packet->vps, PW_CHAP_CHALLENGE, 0);
        if (challenge) {
                memcpy(ptr, challenge->vp_strvalue, challenge->length);
                i += challenge->length;
@@ -3133,7 +3669,7 @@ int rad_chap_encode(RADIUS_PACKET *packet, uint8_t *output, int id,
        }
 
        *output = id;
-       librad_md5_calc((uint8_t *)output + 1, (uint8_t *)string, i);
+       fr_md5_calc((uint8_t *)output + 1, (uint8_t *)string, i);
 
        return 0;
 }
@@ -3144,17 +3680,17 @@ int rad_chap_encode(RADIUS_PACKET *packet, uint8_t *output, int id,
  *
  *     May be called any number of times.
  */
-void lrad_rand_seed(const void *data, size_t size)
+void fr_rand_seed(const void *data, size_t size)
 {
        uint32_t hash;
 
        /*
         *      Ensure that the pool is initialized.
         */
-       if (!lrad_rand_initialized) {
+       if (!fr_rand_initialized) {
                int fd;
-               
-               memset(&lrad_rand_pool, 0, sizeof(lrad_rand_pool));
+
+               memset(&fr_rand_pool, 0, sizeof(fr_rand_pool));
 
                fd = open("/dev/urandom", O_RDONLY);
                if (fd >= 0) {
@@ -3162,22 +3698,22 @@ void lrad_rand_seed(const void *data, size_t size)
                        ssize_t this;
 
                        total = this = 0;
-                       while (total < sizeof(lrad_rand_pool.randrsl)) {
-                               this = read(fd, lrad_rand_pool.randrsl,
-                                           sizeof(lrad_rand_pool.randrsl) - total);
+                       while (total < sizeof(fr_rand_pool.randrsl)) {
+                               this = read(fd, fr_rand_pool.randrsl,
+                                           sizeof(fr_rand_pool.randrsl) - total);
                                if ((this < 0) && (errno != EINTR)) break;
                                if (this > 0) total += this;
                        }
                        close(fd);
                } else {
-                       lrad_rand_pool.randrsl[0] = fd;
-                       lrad_rand_pool.randrsl[1] = time(NULL);
-                       lrad_rand_pool.randrsl[2] = errno;
+                       fr_rand_pool.randrsl[0] = fd;
+                       fr_rand_pool.randrsl[1] = time(NULL);
+                       fr_rand_pool.randrsl[2] = errno;
                }
 
-               lrad_randinit(&lrad_rand_pool, 1);
-               lrad_rand_pool.randcnt = 0;
-               lrad_rand_initialized = 1;
+               fr_randinit(&fr_rand_pool, 1);
+               fr_rand_pool.randcnt = 0;
+               fr_rand_initialized = 1;
        }
 
        if (!data) return;
@@ -3185,32 +3721,32 @@ void lrad_rand_seed(const void *data, size_t size)
        /*
         *      Hash the user data
         */
-       hash = lrad_rand();
-       if (!hash) hash = lrad_rand();
-       hash = lrad_hash_update(data, size, hash);
-       
-       lrad_rand_pool.randmem[lrad_rand_pool.randcnt] ^= hash;
+       hash = fr_rand();
+       if (!hash) hash = fr_rand();
+       hash = fr_hash_update(data, size, hash);
+
+       fr_rand_pool.randmem[fr_rand_pool.randcnt] ^= hash;
 }
 
 
 /*
  *     Return a 32-bit random number.
  */
-uint32_t lrad_rand(void)
+uint32_t fr_rand(void)
 {
        uint32_t num;
 
        /*
         *      Ensure that the pool is initialized.
         */
-       if (!lrad_rand_initialized) {
-               lrad_rand_seed(NULL, 0);
+       if (!fr_rand_initialized) {
+               fr_rand_seed(NULL, 0);
        }
 
-       num = lrad_rand_pool.randrsl[lrad_rand_pool.randcnt++];
-       if (lrad_rand_pool.randcnt == 256) {
-               lrad_isaac(&lrad_rand_pool);
-               lrad_rand_pool.randcnt = 0;
+       num = fr_rand_pool.randrsl[fr_rand_pool.randcnt++];
+       if (fr_rand_pool.randcnt >= 256) {
+               fr_rand_pool.randcnt = 0;
+               fr_isaac(&fr_rand_pool);
        }
 
        return num;
@@ -3225,12 +3761,12 @@ RADIUS_PACKET *rad_alloc(int newvector)
        RADIUS_PACKET   *rp;
 
        if ((rp = malloc(sizeof(RADIUS_PACKET))) == NULL) {
-               librad_log("out of memory");
+               fr_strerror_printf("out of memory");
                return NULL;
        }
        memset(rp, 0, sizeof(*rp));
        rp->id = -1;
-       rp->verified = -1;
+       rp->offset = -1;
 
        if (newvector) {
                int i;
@@ -3240,17 +3776,46 @@ RADIUS_PACKET *rad_alloc(int newvector)
                 *      Don't expose the actual contents of the random
                 *      pool.
                 */
-               base = lrad_rand();
+               base = fr_rand();
                for (i = 0; i < AUTH_VECTOR_LEN; i += sizeof(uint32_t)) {
-                       hash = lrad_rand() ^ base;
+                       hash = fr_rand() ^ base;
                        memcpy(rp->vector + i, &hash, sizeof(hash));
                }
        }
-       lrad_rand();            /* stir the pool again */
+       fr_rand();              /* stir the pool again */
 
        return rp;
 }
 
+RADIUS_PACKET *rad_alloc_reply(RADIUS_PACKET *packet)
+{
+       RADIUS_PACKET *reply;
+
+       if (!packet) return NULL;
+
+       reply = rad_alloc(0);
+       if (!reply) return NULL;
+
+       /*
+        *      Initialize the fields from the request.
+        */
+       reply->sockfd = packet->sockfd;
+       reply->dst_ipaddr = packet->src_ipaddr;
+       reply->src_ipaddr = packet->dst_ipaddr;
+       reply->dst_port = packet->src_port;
+       reply->src_port = packet->dst_port;
+       reply->id = packet->id;
+       reply->code = 0; /* UNKNOWN code */
+       memcpy(reply->vector, packet->vector,
+              sizeof(reply->vector));
+       reply->vps = NULL;
+       reply->data = NULL;
+       reply->data_len = 0;
+
+       return reply;
+}
+
+
 /*
  *     Free a RADIUS_PACKET
  */
@@ -3258,10 +3823,11 @@ void rad_free(RADIUS_PACKET **radius_packet_ptr)
 {
        RADIUS_PACKET *radius_packet;
 
-       if (!radius_packet_ptr) return;
+       if (!radius_packet_ptr || !*radius_packet_ptr) return;
        radius_packet = *radius_packet_ptr;
 
        free(radius_packet->data);
+
        pairfree(&radius_packet->vps);
 
        free(radius_packet);