Fix logic for using udpfromto
[freeradius.git] / src / lib / radius.c
index d85d404..37757a8 100644 (file)
@@ -53,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;
@@ -67,9 +68,7 @@ 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",
@@ -125,6 +124,84 @@ 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.
@@ -134,66 +211,20 @@ static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
                      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);
-
-               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);
+       socklen_t               sizeof_src;
 
-               s4->sin_family = AF_INET;
-               s4->sin_addr = src_ipaddr->ipaddr.ip4addr;
-               s4->sin_port = htons(src_port);
+       fr_ipaddr2sockaddr(src_ipaddr, src_port, &src, &sizeof_src);
 #else
-               src_port = src_port; /* -Wunused */
+       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
        /*
@@ -202,7 +233,7 @@ 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,
@@ -216,7 +247,7 @@ 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);
+                     (struct sockaddr *) &dst, sizeof_dst);
 }
 
 
@@ -280,25 +311,10 @@ ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port,
                }
        }
 
-       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);
-
-#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);
-
-#endif
-       } else {
+       /*
+        *      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;
@@ -330,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);
@@ -414,40 +431,14 @@ static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
                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.
@@ -495,8 +486,8 @@ 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)
 {
        FR_MD5_CTX context, old;
@@ -509,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;
@@ -520,9 +516,6 @@ static void make_passwd(uint8_t *output, int *outlen,
        }
        *outlen = len;
 
-       memcpy(passwd, input, len);
-       memset(passwd + len, 0, sizeof(passwd) - len);
-
        fr_MD5Init(&context);
        fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret));
        old = context;
@@ -549,8 +542,8 @@ 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)
 {
        FR_MD5_CTX context, old;
@@ -633,6 +626,7 @@ static void make_tunnel_passwd(uint8_t *output, int *outlen,
                }
 
                fr_MD5Final(digest, &context);
+
                for (i = 0; i < AUTH_PASS_LEN; i++) {
                        passwd[i + 2 + n] ^= digest[i];
                }
@@ -640,122 +634,18 @@ static void make_tunnel_passwd(uint8_t *output, int *outlen,
        memcpy(output, passwd, len + 2);
 }
 
-
 /*
- *     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;
-
-       } 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 */
+       uint32_t lvalue;
+       size_t len;
+       const uint8_t *data;
+       uint8_t *ptr = start;
+       uint8_t array[4];
 
        /*
         *      Set up the default sources for the data.
@@ -777,28 +667,20 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                len = 1;        /* just in case */
                array[0] = vp->vp_integer & 0xff;
                data = array;
-               offset = 0;
                break;
 
-
        case PW_TYPE_SHORT:
                len = 2;        /* just in case */
                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->vp_integer);
                memcpy(array, &lvalue, sizeof(lvalue));
-
-               /*
-                *      Perhaps discard the first octet.
-                */
-               data = &array[offset];
-               len -= offset;
+               data = array;
                break;
 
        case PW_TYPE_IPADDR:
@@ -815,17 +697,34 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                len = 4;        /* just in case */
                break;
 
+       case PW_TYPE_SIGNED:
+       {
+               int32_t slvalue;
+
+               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 */
-               librad_log("ERROR: Unknown attribute type %d", vp->type);
+               fr_strerror_printf("ERROR: Unknown attribute type %d", vp->type);
                return -1;
        }
 
        /*
-        *      Bound the data to 255 bytes.
+        *      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
@@ -835,22 +734,22 @@ 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:
+               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:
@@ -858,18 +757,20 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                case PW_ACCESS_CHALLENGE:
                default:
                        if (!original) {
-                               librad_log("ERROR: No request packet, cannot encrypt %s attribute in the vp.", vp->name);
+                               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,
+
+                       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:
-                       make_tunnel_passwd(ptr + offset, &len,
-                                          data, len, 255 - offset - total_length,
+                       ptr[0] = vp->flags.tag;
+                       make_tunnel_passwd(ptr + 1, &len, data, len - 1, room,
                                           secret, packet->vector);
                        break;
                }
@@ -880,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 the tag (if any).
+        *      Account for continuation bytes.  The caller has
+        *      already accounting for the continuation byte in the
+        *      Vendor-Specific "length" field.
         */
-       len += offset;
+       start[WM_OFF]++;
+       *(ptr++) = 0;
 
        /*
-        *      RFC 2865 section 5 says that zero-length attributes
-        *      MUST NOT be sent.
+        *      Chop everything to fit in one attribute.
         */
-       if (len == 0) return 0;
+       if (room > (255 - 9)) room = (255 - 9);
 
        /*
-        *      Update the various lengths.
+        *      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.
         */
-       *length_ptr += len;
-       if (vsa_length_ptr) *vsa_length_ptr += len;
+       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;
+
+       /*
+        *      Swap the order of the WiMAX hacks, to make later
+        *      comparisons easier.
+        */
+       maxattr = REORDER(vp->attribute);
+
+       /*
+        *      Build the Vendor-Specific header
+        */
+       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;
+
+       /*
+        *      Not enough room.  Do a continuation.
+        */
+       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;
+
+       /*
+        *      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;
 
-       return total_length;    /* of attribute */
+               attr = REORDER(vp->attribute);
+               if (attr >= maxattr) {
+                       maxattr = attr;
+                       goto redo_tlv;
+               }
+       }
+
+       return ptr - start;
 }
 
 
@@ -932,16 +1191,12 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
        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";
        }
@@ -961,7 +1216,7 @@ 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;
@@ -1015,10 +1270,12 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
         */
        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)) {
+               if ((reply->vendor == 0) &&
+                   ((reply->attribute & 0xFFFF) >= 256) &&
+                   !reply->flags.extended && !reply->flags.extended_flags) {
 #ifndef NDEBUG
                        /*
                         *      Permit the admin to send BADLY formatted
@@ -1055,19 +1312,30 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                 */
                debug_pair(reply);
 
-               len = rad_vp2attr(packet, original, secret, reply, ptr);
+               /*
+                *      Skip attributes that are encoded.
+                */
+               if (reply->flags.encoded) continue;
+
+               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 {
+
+                       len = rad_vp2attr(packet, original, secret, reply, ptr,
+                                         ((uint8_t *) data) + sizeof(data) - ptr);
+               }
 
                if (len < 0) return -1;
 
                /*
-                *      Check that the packet is no more than 4k in
-                *      size, AFTER writing the attribute past the 4k
-                *      boundary, but BEFORE deciding to increase the
-                *      size of the packet. Note that the 'data'
-                *      buffer, above, is one attribute longer than
-                *      necessary, in order to permit this overflow.
+                *      Failed to encode the attribute, likely because
+                *      the packet is full.
                 */
-               if ((total_length + len) > MAX_PACKET_LEN) {
+               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;
                }
 
@@ -1087,11 +1355,11 @@ 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);
@@ -1113,13 +1381,13 @@ 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->offset < 0)) {
-               librad_log("ERROR: You must call rad_encode() before rad_sign()");
+               fr_strerror_printf("ERROR: You must call rad_encode() before rad_sign()");
                return -1;
        }
 
@@ -1146,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,
@@ -1232,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";
        }
@@ -1261,7 +1529,7 @@ 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) {
+       } 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,
@@ -1269,17 +1537,38 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                      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);
                }
        }
 
-       /*
-        *      And send it on it's way.
-        */
-       return rad_sendto(packet->sockfd, packet->data, packet->data_len, 0,
-                         &packet->src_ipaddr, packet->src_port,
-                         &packet->dst_ipaddr, packet->dst_port);
+       /*
+        *      And send it on it's way.
+        */
+       return rad_sendto(packet->sockfd, packet->data, packet->data_len, 0,
+                         &packet->src_ipaddr, packet->src_port,
+                         &packet->dst_ipaddr, packet->dst_port);
+}
+
+/*
+ *     Do a comparison of two authentication digests by comparing
+ *     the FULL digest.  Otehrwise, the server can be subject to
+ *     timing attacks that allow attackers find a valid message
+ *     authenticator.
+ *
+ *     http://www.cs.rice.edu/~dwallach/pub/crosby-timing2009.pdf
+ */
+static int digest_cmp(const uint8_t *a, const uint8_t *b, size_t length)
+{
+       int result = 0;
+       size_t i;
+
+       for (i = 0; i < length; i++) {
+               result |= a[i] ^ b[i];
+       }
+
+       return result;          /* 0 is OK, !0 is !OK, just like memcmp */
 }
 
 
@@ -1311,7 +1600,7 @@ static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret)
        /*
         *      Return 0 if OK, 2 if not OK.
         */
-       if (memcmp(digest, packet->vector, AUTH_VECTOR_LEN) != 0) return 2;
+       if (digest_cmp(digest, packet->vector, AUTH_VECTOR_LEN) != 0) return 2;
        return 0;
 }
 
@@ -1354,7 +1643,7 @@ static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original,
        /*
         *      Return 0 if OK, 2 if not OK.
         */
-       if (memcmp(packet->vector, calc_digest, AUTH_VECTOR_LEN) != 0) return 2;
+       if (digest_cmp(packet->vector, calc_digest, AUTH_VECTOR_LEN) != 0) return 2;
        return 0;
 }
 
@@ -1384,11 +1673,11 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
         *      "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;
        }
 
@@ -1398,11 +1687,11 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
         *      " ... 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;
        }
 
@@ -1419,8 +1708,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
         *      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)),
@@ -1451,7 +1740,7 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
         *      "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)),
@@ -1467,7 +1756,7 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
         *      " ... 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)),
@@ -1484,11 +1773,11 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
         *      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;
        }
 
@@ -1525,10 +1814,22 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
 
        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)));
@@ -1540,7 +1841,7 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                 *      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)),
@@ -1549,6 +1850,19 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                }
 
                /*
+                *      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]) {
@@ -1565,7 +1879,7 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
 
                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)),
@@ -1593,7 +1907,7 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
         *      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)));
@@ -1605,13 +1919,13 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
         *      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;
        }
 
@@ -1627,7 +1941,7 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
         *      Message-Authenticator attributes.
         */
        if (require_ma && ! seen_ma) {
-               librad_log("WARNING: Insecure packet from host %s:  Packet does not contain required Message-Authenticator attribute",
+               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)));
@@ -1651,18 +1965,24 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
  */
 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);
 
@@ -1670,7 +1990,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
         *      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;
@@ -1682,7 +2002,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
         *      packet.
         */
        if (packet->data_len > MAX_PACKET_LEN) {
-               librad_log("Discarding packet: Larger than RFC limitation of 4096 bytes.");
+               fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes.");
                /* packet->data is NULL */
                free(packet);
                return NULL;
@@ -1695,7 +2015,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
         *      packet->data == NULL
         */
        if ((packet->data_len == 0) || !packet->data) {
-               librad_log("Empty packet: Socket is not ready.");
+               fr_strerror_printf("Empty packet: Socket is not ready.");
                free(packet);
                return NULL;
        }
@@ -1724,25 +2044,26 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
         */
        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;
@@ -1804,7 +2125,7 @@ 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);
@@ -1814,10 +2135,10 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                        fr_hmac_md5(packet->data, packet->data_len,
                                    (const uint8_t *) secret, strlen(secret),
                                    calc_auth_vector);
-                       if (memcmp(calc_auth_vector, msg_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)));
@@ -1841,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,
@@ -1862,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)));
@@ -1891,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,
@@ -1918,19 +2240,12 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
 }
 
 
-/*
- *     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 length,
-                       const uint8_t *data)
+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 offset = 0;
-       VALUE_PAIR *vp;
-
-       if ((vp = paircreate(attribute, PW_TYPE_OCTETS)) == NULL) {
-               return NULL;
-       }
 
        /*
         *      If length is greater than 253, something is SERIOUSLY
@@ -1943,6 +2258,15 @@ VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet, const RADIUS_PACKET *origin
        vp->next = NULL;
 
        /*
+        *      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;
+       }
+
+       /*
         *      Handle tags.
         */
        if (vp->flags.has_tag) {
@@ -1955,7 +2279,10 @@ VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet, const RADIUS_PACKET *origin
                        vp->flags.tag = data[0];
 
                        if ((vp->type == PW_TYPE_STRING) ||
-                           (vp->type == PW_TYPE_OCTETS)) offset = 1;
+                           (vp->type == PW_TYPE_OCTETS)) {
+                               if (length == 0) goto raw;
+                               offset = 1;
+                       }
                }
        }
 
@@ -2057,7 +2384,7 @@ VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet, const RADIUS_PACKET *origin
                 */
                {
                        DICT_VALUE *dval;
-                       dval = dict_valbyattr(vp->attribute,
+                       dval = dict_valbyattr(vp->attribute, vp->vendor,
                                              vp->vp_integer);
                        if (dval) {
                                strlcpy(vp->vp_strvalue,
@@ -2120,23 +2447,483 @@ VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet, const RADIUS_PACKET *origin
                }
                break;
 
-       default:
-       raw:
-               vp->type = PW_TYPE_OCTETS;
+       case PW_TYPE_SIGNED:
+               if (vp->length != 4) goto raw;
+
+               /*
+                *      Overload vp_integer for ntohl, which takes
+                *      uint32_t, not int32_t
+                */
+               memcpy(&vp->vp_integer, vp->vp_octets, 4);
+               vp->vp_integer = ntohl(vp->vp_integer);
+               memcpy(&vp->vp_signed, &vp->vp_integer, 4);
+               break;
+
+       case PW_TYPE_TLV:
                vp->length = length;
-               memcpy(vp->vp_octets, data, 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;
+
+       case PW_TYPE_COMBO_IP:
+               if (vp->length == 4) {
+                       vp->type = PW_TYPE_IPADDR;
+                       memcpy(&vp->vp_ipaddr, vp->vp_octets, 4);
+                       break;
+
+               } else if (vp->length == 16) {
+                       vp->type = PW_TYPE_IPV6ADDR;
+                       /* vp->vp_ipv6addr == vp->vp_octets */
+                       break;
 
+               }
+               /* FALL-THROUGH */
 
+       default:
+       raw:
                /*
-                *      Ensure there's no encryption or tag stuff,
-                *      we just pass the attribute as-is.
+                *      Change the name to show the user that the
+                *      attribute is not of the correct format.
                 */
-               memset(&vp->flags, 0, sizeof(vp->flags));
+               {
+                       int attr = vp->attribute;
+                       int vendor = vp->vendor;
+                       VALUE_PAIR *vp2;
+
+                       vp2 = pairalloc(NULL);
+                       if (!vp2) {
+                               pairfree(&vp);
+                               return NULL;
+                       }
+                       pairfree(&vp);
+                       vp = vp2;
+
+                       /*
+                        *      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;
        }
 
        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);
+}
+
+
+/*
+ *     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.
+ */
+static uint8_t *rad_coalesce(unsigned int attribute, int vendor,
+                            size_t length, uint8_t *data,
+                            size_t packet_length, size_t *ptlv_length)
+                            
+{
+       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;
+               }
+
+               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;
+       }
+
+       tlv = tlv_data = malloc(tlv_length);
+       if (!tlv_data) return NULL;
+
+       memcpy(tlv, data, length);
+       tlv += length;
+
+       /*
+        *      Now we walk the list again, copying the data over to
+        *      our newly created memory.
+        */
+       for (ptr = data + length;
+            ptr != (data + packet_length);
+            ptr += ptr[1]) {
+               int this_length;
+
+               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;
+       }
+
+       *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;
+
+       /*
+        *      Sanity check the attribute.
+        */
+       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;
+               }
+
+               /*
+                *      Attribute number is too large for us to
+                *      represent it in our horrible internal
+                *      representation.
+                */
+               if ((ptr[0] & ~fr_wimax_mask[nest]) != 0) {
+                       return NULL;
+               }
+       }
+
+       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;
+                       }
+
+                       if (!data2vp(packet, original, secret,
+                                    y[1] - 2, y + 2, vp)) {
+                               goto error;
+                       }
+               }
+
+               *tail = vp;
+               while (*tail) tail = &((*tail)->next);
+       }
+
+       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;
+
+       /*
+        *      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;
+       }
+
+       /*
+        *      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;
+               }
+
+               left -= ptr[1];
+       }
+
+       /*
+        *      Now we walk over the TLV *again*, creating sub-tlv's.
+        */
+       head = NULL;
+       tail = &head;
+
+       for (ptr = tlv_data;
+            ptr != (tlv_data + tlv_length);
+            ptr += ptr[1]) {
+
+               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;
+                       }
+               }
+
+               *tail = vp;
+
+               while (*tail) tail = &((*tail)->next);
+       }
+
+       /*
+        *      TLV's MAY be continued, but sometimes they're not.
+        */
+       if (tlv_data != data) free(tlv_data);
+
+       if (head->next) rad_sortvp(&head);
+
+       return head;
+}
+
+
+/*
+ *     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;
+
+       if ((length < 2) || (data[1] < 2)) return NULL;
+
+       /*
+        *      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 data2vp(packet, original, secret, length, data, vp);
+}
+
 
 /*
  *     Calculate/check digest, and decode radius attributes.
@@ -2151,13 +2938,13 @@ 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;
 
@@ -2179,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.
@@ -2239,19 +3027,18 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                        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;
                        }
 
                        /*
@@ -2271,9 +3058,10 @@ 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
@@ -2302,25 +3090,30 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                                        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;
@@ -2368,12 +3161,12 @@ 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:
@@ -2383,10 +3176,50 @@ 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
@@ -2395,18 +3228,22 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                 *      over-ride this one.
                 *
                 *      If the attribute has no data, then discard it.
+                *
+                *      Unless it's CUI.  Damn you, CUI!
                 */
        create_pair:
-               if (!attrlen) goto next;
+               if (!attrlen &&
+                   (attribute != PW_CHARGEABLE_USER_IDENTITY)) goto next;
 
                pair = rad_attr2vp(packet, original, secret,
-                                  attribute, attrlen, ptr);
+                                  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++;
@@ -2421,20 +3258,21 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                 *      then without using the dictionary.  We
                 *      therefore enforce the limits here, too.
                 */
-               if ((librad_max_attributes > 0) &&
-                   (num_attributes > librad_max_attributes)) {
+               if ((fr_max_attributes > 0) &&
+                   (num_attributes > fr_max_attributes)) {
                        char host_ipaddr[128];
 
                        pairfree(&packet->vps);
-                       librad_log("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).",
+                       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 -1;
                }
 
-                   next:
+       next:
+               if (vendorlen == 0) vendorcode = 0;
                ptr += attrlen;
                packet_length -= attrlen;
        }
@@ -2694,7 +3532,7 @@ int rad_tunnel_pwdecode(uint8_t *passwd, size_t *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;
        }
 
@@ -2749,7 +3587,7 @@ int rad_tunnel_pwdecode(uint8_t *passwd, size_t *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;
                        }
 
@@ -2821,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;
@@ -2906,7 +3744,7 @@ uint32_t fr_rand(void)
        }
 
        num = fr_rand_pool.randrsl[fr_rand_pool.randcnt++];
-       if (fr_rand_pool.randcnt == 256) {
+       if (fr_rand_pool.randcnt >= 256) {
                fr_rand_pool.randcnt = 0;
                fr_isaac(&fr_rand_pool);
        }
@@ -2923,7 +3761,7 @@ 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));
@@ -2949,6 +3787,35 @@ RADIUS_PACKET *rad_alloc(int newvector)
        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
  */