Signed / unsigned fixes and function prototypes
[freeradius.git] / src / lib / valuepair.c
index 9f8661b..57fe9a2 100644 (file)
  *   License along with this library; if not, write to the Free Software
  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  *
- * Copyright 2000  The FreeRADIUS server project
+ * Copyright 2000,2006  The FreeRADIUS server project
  */
 
-static const char rcsid[] = "$Id$";
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
 
-#include       "autoconf.h"
+#include       <freeradius-devel/libradius.h>
 
-#include       <sys/types.h>
-
-#include       <stdio.h>
-#include       <stdlib.h>
-#include       <string.h>
 #include       <ctype.h>
 
-#include       "libradius.h"
-
 #ifdef HAVE_MALLOC_H
 #  include     <malloc.h>
 #endif
@@ -41,57 +35,97 @@ static const char rcsid[] = "$Id$";
 #  include     <regex.h>
 #endif
 
-#include       "missing.h"
-
 static const char *months[] = {
         "jan", "feb", "mar", "apr", "may", "jun",
         "jul", "aug", "sep", "oct", "nov", "dec" };
 
-
 /*
- *     Create a new valuepair.
+ *     This padding is necessary only for attributes that are NOT
+ *     in the dictionary, and then only because the rest of the
+ *     code accesses vp->name directly, rather than through an
+ *     accessor function.
+ *
+ *     The name padding only has to large enough for:
+ *
+ *             Vendor-65535-Attr-65535
+ *
+ *     i.e. 23 characters, plus a zero.  We add another 8 bytes for
+ *     padding, because the VALUE_PAIR structure may be un-aligned.
+ *
+ *     The result is that for the normal case, the server uses a less
+ *     memory (36 bytes * number of VALUE_PAIRs).
  */
-VALUE_PAIR *paircreate(int attr, int type)
-{
-       VALUE_PAIR      *vp;
-       DICT_ATTR       *da;
+#define FR_VP_NAME_PAD (32)
+#define FR_VP_NAME_LEN (24)
 
-       if ((vp = malloc(sizeof(VALUE_PAIR))) == NULL) {
-               librad_log("out of memory");
-               return NULL;
-       }
-       memset(vp, 0, sizeof(VALUE_PAIR));
-       vp->attribute = attr;
-       vp->operator = T_OP_EQ;
-       vp->type = type;
+VALUE_PAIR *pairalloc(DICT_ATTR *da)
+{
+       size_t name_len = 0;
+       VALUE_PAIR *vp;
 
        /*
-        *      Dictionary type over-rides what the caller says.
+        *      Not in the dictionary: the name is allocated AFTER
+        *      the VALUE_PAIR struct.
         */
-       if ((da = dict_attrbyvalue(attr)) != NULL) {
-               strcpy(vp->name, da->name);
+       if (!da) name_len = FR_VP_NAME_PAD;
+
+       vp = malloc(sizeof(*vp) + name_len);
+       if (!vp) return NULL;
+       memset(vp, 0, sizeof(*vp));
+
+       if (da) {
+               vp->attribute = da->attr;
+               vp->vendor = da->vendor;
                vp->type = da->type;
+               vp->name = da->name;
                vp->flags = da->flags;
-       } else if (VENDOR(attr) == 0) {
-               sprintf(vp->name, "Attr-%u", attr);
        } else {
-               DICT_VENDOR *v;
-
-               v = dict_vendorbyvalue(VENDOR(attr));
-               if (v) {
-                       sprintf(vp->name, "%s-Attr-%u",
-                               v->name, attr & 0xffff);
-               } else {
-                       sprintf(vp->name, "Vendor-%u-Attr-%u",
-                               VENDOR(attr), attr & 0xffff);
-               }
+               vp->attribute = 0;
+               vp->vendor = 0;
+               vp->type = PW_TYPE_OCTETS;
+               vp->name = NULL;
+               memset(&vp->flags, 0, sizeof(vp->flags));
+               vp->flags.unknown_attr = 1;
        }
+
        switch (vp->type) {
+               case PW_TYPE_BYTE:
+                       vp->length = 1;
+                       break;
+
+               case PW_TYPE_SHORT:
+                       vp->length = 2;
+                       break;
+
                case PW_TYPE_INTEGER:
                case PW_TYPE_IPADDR:
                case PW_TYPE_DATE:
+               case PW_TYPE_SIGNED:
                        vp->length = 4;
                        break;
+
+               case PW_TYPE_IFID:
+                       vp->length = sizeof(vp->vp_ifid);
+                       break;
+
+               case PW_TYPE_IPV6ADDR:
+                       vp->length = sizeof(vp->vp_ipv6addr);
+                       break;
+
+               case PW_TYPE_IPV6PREFIX:
+                       vp->length = sizeof(vp->vp_ipv6prefix);
+                       break;
+
+               case PW_TYPE_ETHERNET:
+                       vp->length = sizeof(vp->vp_ether);
+                       break;
+
+               case PW_TYPE_TLV:
+                       vp->vp_tlv = NULL;
+                       vp->length = 0;
+                       break;
+
+               case PW_TYPE_COMBO_IP:
                default:
                        vp->length = 0;
                        break;
@@ -101,11 +135,64 @@ VALUE_PAIR *paircreate(int attr, int type)
 }
 
 /*
+ *     Create a new valuepair.
+ */
+VALUE_PAIR *paircreate_raw(int attr, int vendor, int type, VALUE_PAIR *vp)
+{
+       char *p = (char *) (vp + 1);
+
+       if (!vp->flags.unknown_attr) {
+               pairfree(&vp);
+               return NULL;
+       }
+
+       vp->vendor = vendor;
+       vp->attribute = attr;
+       vp->operator = T_OP_EQ;
+       vp->name = p;
+       vp->type = type;
+       vp->length = 0;
+       memset(&vp->flags, 0, sizeof(vp->flags));
+       vp->flags.unknown_attr = 1;
+       
+       if (!vp_print_name(p, FR_VP_NAME_LEN, vp->attribute, vp->vendor)) {
+               free(vp);
+               return NULL;
+       }
+
+       return vp;
+}
+
+/*
+ *     Create a new valuepair.
+ */
+VALUE_PAIR *paircreate(int attr, int vendor, int type)
+{
+       VALUE_PAIR      *vp;
+       DICT_ATTR       *da;
+
+       da = dict_attrbyvalue(attr, vendor);
+       if ((vp = pairalloc(da)) == NULL) {
+               fr_strerror_printf("out of memory");
+               return NULL;
+       }
+       vp->operator = T_OP_EQ;
+
+       /*
+        *      It isn't in the dictionary: update the name.
+        */
+       if (!da) return paircreate_raw(attr, vendor, type, vp);
+
+       return vp;
+}
+
+/*
  *      release the memory used by a single attribute-value pair
  *      just a wrapper around free() for now.
  */
 void pairbasicfree(VALUE_PAIR *pair)
 {
+       if (pair->type == PW_TYPE_TLV) free(pair->vp_tlv);
        /* clear the memory here */
        memset(pair, 0, sizeof(*pair));
        free(pair);
@@ -135,25 +222,30 @@ void pairfree(VALUE_PAIR **pair_ptr)
 /*
  *     Find the pair with the matching attribute
  */
-VALUE_PAIR * pairfind(VALUE_PAIR *first, int attr)
+VALUE_PAIR * pairfind(VALUE_PAIR *first, unsigned int attr, unsigned int vendor)
 {
-       while(first && first->attribute != attr)
+       while (first) {
+               if ((first->attribute == attr) && (first->vendor == vendor)) {
+                       return first;
+               }
                first = first->next;
-       return first;
+       }
+
+       return NULL;
 }
 
 
 /*
  *     Delete the pair(s) with the matching attribute
  */
-void pairdelete(VALUE_PAIR **first, int attr)
+void pairdelete(VALUE_PAIR **first, unsigned int attr, unsigned int vendor)
 {
        VALUE_PAIR *i, *next;
        VALUE_PAIR **last = first;
 
        for(i = *first; i; i = next) {
                next = i->next;
-               if (i->attribute == attr) {
+               if ((i->attribute == attr) && (i->vendor == vendor)) {
                        *last = next;
                        pairbasicfree(i);
                } else {
@@ -169,6 +261,8 @@ void pairadd(VALUE_PAIR **first, VALUE_PAIR *add)
 {
        VALUE_PAIR *i;
 
+       if (!add) return;
+
        if (*first == NULL) {
                *first = add;
                return;
@@ -203,7 +297,8 @@ void pairreplace(VALUE_PAIR **first, VALUE_PAIR *replace)
                 *      Found the first attribute, replace it,
                 *      and return.
                 */
-               if (i->attribute == replace->attribute) {
+               if ((i->attribute == replace->attribute) &&
+                   (i->vendor == replace->vendor)) {
                        *prev = replace;
 
                        /*
@@ -227,10 +322,49 @@ void pairreplace(VALUE_PAIR **first, VALUE_PAIR *replace)
        *prev = replace;
 }
 
+
+/*
+ *     Copy just one VP.
+ */
+VALUE_PAIR *paircopyvp(const VALUE_PAIR *vp)
+{
+       size_t name_len;
+       VALUE_PAIR *n;
+       
+       if (!vp->flags.unknown_attr) {
+               name_len = 0;
+       } else {
+               name_len = FR_VP_NAME_PAD;
+       }
+       
+       if ((n = malloc(sizeof(*n) + name_len)) == NULL) {
+               fr_strerror_printf("out of memory");
+               return NULL;
+       }
+       memcpy(n, vp, sizeof(*n) + name_len);
+
+       /*
+        *      Reset the name field to point to the NEW attribute,
+        *      rather than to the OLD one.
+        */
+       if (vp->flags.unknown_attr) n->name = (char *) (n + 1);
+
+       n->next = NULL;
+
+       if ((n->type == PW_TYPE_TLV) &&
+           (n->vp_tlv != NULL)) {
+               n->vp_tlv = malloc(n->length);
+               memcpy(n->vp_tlv, vp->vp_tlv, n->length);
+       }
+
+       return n;
+}
+
+
 /*
  *     Copy just a certain type of pairs.
  */
-VALUE_PAIR *paircopy2(VALUE_PAIR *vp, int attr)
+VALUE_PAIR *paircopy2(VALUE_PAIR *vp, unsigned int attr, unsigned int vendor)
 {
        VALUE_PAIR      *first, *n, **last;
 
@@ -238,16 +372,14 @@ VALUE_PAIR *paircopy2(VALUE_PAIR *vp, int attr)
        last = &first;
 
        while (vp) {
-               if (attr >= 0 && vp->attribute != attr) {
+               if ((attr > 0) &&
+                   !((vp->attribute == attr) && (vp->vendor == vendor))) {
                        vp = vp->next;
                        continue;
                }
-               if ((n = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
-                       librad_log("out of memory");
-                       return first;
-               }
-               memcpy(n, vp, sizeof(VALUE_PAIR));
-               n->next = NULL;
+
+               n = paircopyvp(vp);
+               if (!n) return first;
                *last = n;
                last = &n->next;
                vp = vp->next;
@@ -261,7 +393,7 @@ VALUE_PAIR *paircopy2(VALUE_PAIR *vp, int attr)
  */
 VALUE_PAIR *paircopy(VALUE_PAIR *vp)
 {
-       return paircopy2(vp, -1);
+       return paircopy2(vp, 0, 0);
 }
 
 
@@ -276,19 +408,13 @@ void pairmove(VALUE_PAIR **to, VALUE_PAIR **from)
        VALUE_PAIR *found;
        int has_password = 0;
 
-       if (*to == NULL) {
-               *to = *from;
-               *from = NULL;
-               return;
-       }
-
        /*
         *      First, see if there are any passwords here, and
         *      point "tailto" to the end of the "to" list.
         */
        tailto = to;
        for(i = *to; i; i = i->next) {
-               if (i->attribute == PW_PASSWORD ||
+               if (i->attribute == PW_USER_PASSWORD ||
                    i->attribute == PW_CRYPT_PASSWORD)
                        has_password = 1;
                tailto = &i->next;
@@ -299,17 +425,40 @@ void pairmove(VALUE_PAIR **to, VALUE_PAIR **from)
         */
        for(i = *from; i; i = next) {
                next = i->next;
+
                /*
                 *      If there was a password in the "to" list,
                 *      do not move any other password from the
                 *      "from" to the "to" list.
                 */
                if (has_password &&
-                   (i->attribute == PW_PASSWORD ||
+                   (i->attribute == PW_USER_PASSWORD ||
                     i->attribute == PW_CRYPT_PASSWORD)) {
                        tailfrom = i;
                        continue;
                }
+
+               switch (i->operator) {
+                       /*
+                        *      These are COMPARISON attributes
+                        *      from a check list, and are not
+                        *      supposed to be copied!
+                        */
+                       case T_OP_NE:
+                       case T_OP_GE:
+                       case T_OP_GT:
+                       case T_OP_LE:
+                       case T_OP_LT:
+                       case T_OP_CMP_TRUE:
+                       case T_OP_CMP_FALSE:
+                       case T_OP_CMP_EQ:
+                               tailfrom = i;
+                               continue;
+
+                       default:
+                               break;
+               }
+
                /*
                 *      If the attribute is already present in "to",
                 *      do not move it from "from" to "to". We make
@@ -319,19 +468,19 @@ void pairmove(VALUE_PAIR **to, VALUE_PAIR **from)
                if (i->attribute == PW_FALL_THROUGH ||
                    (i->attribute != PW_HINT && i->attribute != PW_FRAMED_ROUTE)) {
 
-                       found = pairfind(*to, i->attribute);
+                       found = pairfind(*to, i->attribute, i->vendor);
                        switch (i->operator) {
 
                          /*
-                          *  If a similar attribute is found,
-                          *  delete it.
+                          *    If matching attributes are found,
+                          *    delete them.
                           */
                        case T_OP_SUB:          /* -= */
                                if (found) {
-                                       if (!i->strvalue[0] ||
-                                           (strcmp((char *)found->strvalue,
-                                                   (char *)i->strvalue) == 0)){
-                                               pairdelete(to, found->attribute);
+                                       if (!i->vp_strvalue[0] ||
+                                           (strcmp((char *)found->vp_strvalue,
+                                                   (char *)i->vp_strvalue) == 0)){
+                                               pairdelete(to, found->attribute, found->vendor);
 
                                                /*
                                                 *      'tailto' may have been
@@ -358,29 +507,29 @@ void pairmove(VALUE_PAIR **to, VALUE_PAIR **from)
 
                        case T_OP_REG_EQ:
                          if (found &&
-                             (i->strvalue[0] == 's')) {
+                             (i->vp_strvalue[0] == 's')) {
                            regex_t             reg;
                            regmatch_t          match[1];
 
                            char *str;
                            char *p, *q;
 
-                           p = i->strvalue + 1;
+                           p = i->vp_strvalue + 1;
                            q = strchr(p + 1, *p);
                            if (!q || (q[strlen(q) - 1] != *p)) {
                              tailfrom = i;
                              continue;
                            }
-                           str = strdup(i->strvalue + 2);
+                           str = strdup(i->vp_strvalue + 2);
                            q = strchr(str, *p);
                            *(q++) = '\0';
                            q[strlen(q) - 1] = '\0';
 
                            regcomp(&reg, str, 0);
-                           if (regexec(&reg, found->strvalue,
+                           if (regexec(&reg, found->vp_strvalue,
                                        1, match, 0) == 0) {
                              fprintf(stderr, "\"%s\" will have %d to %d replaced with %s\n",
-                                     found->strvalue, match[0].rm_so,
+                                     found->vp_strvalue, match[0].rm_so,
                                      match[0].rm_eo, q);
 
                            }
@@ -410,15 +559,32 @@ void pairmove(VALUE_PAIR **to, VALUE_PAIR **from)
                           */
                        case T_OP_SET:          /* := */
                                if (found) {
-                                       pairdelete(to, found->attribute);
+                                       VALUE_PAIR *mynext = found->next;
+
+                                       /*
+                                        *      Do NOT call pairdelete()
+                                        *      here, due to issues with
+                                        *      re-writing "request->username".
+                                        *
+                                        *      Everybody calls pairmove,
+                                        *      and expects it to work.
+                                        *      We can't update request->username
+                                        *      here, so instead we over-write
+                                        *      the vp that it's pointing to.
+                                        */
+                                       memcpy(found, i, sizeof(*found));
+                                       found->next = mynext;
+
+                                       pairdelete(&found->next, found->attribute, found->vendor);
+
                                        /*
                                         *      'tailto' may have been
                                         *      deleted...
                                         */
-                                       tailto = to;
-                                       for(j = *to; j; j = j->next) {
+                                       for(j = found; j; j = j->next) {
                                                tailto = &j->next;
                                        }
+                                       continue;
                                }
                                break;
 
@@ -455,7 +621,7 @@ void pairmove(VALUE_PAIR **to, VALUE_PAIR **from)
 /*
  *     Move one kind of attributes from one list to the other
  */
-void pairmove2(VALUE_PAIR **to, VALUE_PAIR **from, int attr)
+void pairmove2(VALUE_PAIR **to, VALUE_PAIR **from, unsigned int attr, unsigned int vendor)
 {
        VALUE_PAIR *to_tail, *i, *next;
        VALUE_PAIR *iprev = NULL;
@@ -473,27 +639,37 @@ void pairmove2(VALUE_PAIR **to, VALUE_PAIR **from, int attr)
        for(i = *from; i; i = next) {
                next = i->next;
 
-
                /*
-                *      If the attribute to move is NOT a VSA, then it
-                *      ignores any attributes which do not match exactly.
+                *      vendor=0, attr = PW_VENDOR_SPECIFIC means
+                *      "match any vendor attribute".
                 */
-               if ((attr != PW_VENDOR_SPECIFIC) &&
-                   (i->attribute != attr)) {
+               if ((vendor == 0) && (attr == PW_VENDOR_SPECIFIC)) {
+                       /*
+                        *      It's a VSA: move it over.
+                        */
+                       if (i->vendor != 0) goto move;
+
+                       /*
+                        *      It's Vendor-Specific: move it over.
+                        */
+                       if (i->attribute == attr) goto move;
+
+                       /*
+                        *      It's not a VSA: ignore it.
+                        */
                        iprev = i;
                        continue;
                }
 
                /*
-                *      If the attribute to move IS a VSA, then it ignores
-                *      any non-VSA attribute.
+                *      If it isn't an exact match, ignore it.
                 */
-               if ((attr == PW_VENDOR_SPECIFIC) &&
-                   (VENDOR(i->attribute) == 0)) {
+               if (!((i->vendor == vendor) && (i->attribute == attr))) {
                        iprev = i;
                        continue;
                }
 
+       move:
                /*
                 *      Remove the attribute from the "from" list.
                 */
@@ -540,7 +716,7 @@ static char *mystrtok(char **ptr, const char *sep)
  *     Turn printable string into time_t
  *     Returns -1 on error, 0 on OK.
  */
-static int gettime(const char *valstr, time_t *lvalue)
+static int gettime(const char *valstr, time_t *date)
 {
        int             i;
        time_t          t;
@@ -553,7 +729,7 @@ static int gettime(const char *valstr, time_t *lvalue)
        /*
         * Test for unix timestamp date
         */
-       *lvalue = strtoul(valstr, &tail, 10);
+       *date = strtoul(valstr, &tail, 10);
        if (*tail == '\0') {
                return 0;
        }
@@ -562,7 +738,7 @@ static int gettime(const char *valstr, time_t *lvalue)
        memset(tm, 0, sizeof(*tm));
        tm->tm_isdst = -1;      /* don't know, and don't care about DST */
 
-       strNcpy(buf, valstr, sizeof(buf));
+       strlcpy(buf, valstr, sizeof(buf));
 
        p = buf;
        f[0] = mystrtok(&p, " \t");
@@ -572,6 +748,21 @@ static int gettime(const char *valstr, time_t *lvalue)
        if (!f[0] || !f[1] || !f[2]) return -1;
 
        /*
+        *      The time has a colon, where nothing else does.
+        *      So if we find it, bubble it to the back of the list.
+        */
+       if (f[3]) {
+               for (i = 0; i < 3; i++) {
+                       if (strchr(f[i], ':')) {
+                               p = f[3];
+                               f[3] = f[i];
+                               f[i] = p;
+                               break;
+                       }
+               }
+       }
+
+       /*
         *  The month is text, which allows us to find it easily.
         */
        tm->tm_mon = 12;
@@ -638,12 +829,13 @@ static int gettime(const char *valstr, time_t *lvalue)
                *(f[1]++) = '\0'; /* nuke it, and point to MM:SS */
 
                f[2] = strchr(f[1], ':'); /* find : separator */
-               if (!f[2]) return -1;
-               *(f[2]++) = '\0';       /* nuke it, and point to SS */
+               if (f[2]) {
+                 *(f[2]++) = '\0';     /* nuke it, and point to SS */
+                 tm->tm_sec = atoi(f[2]);
+               }                       /* else leave it as zero */
 
                tm->tm_hour = atoi(f[0]);
                tm->tm_min = atoi(f[1]);
-               tm->tm_sec = atoi(f[2]);
        }
 
        /*
@@ -652,41 +844,143 @@ static int gettime(const char *valstr, time_t *lvalue)
        t = mktime(tm);
        if (t == (time_t) -1) return -1;
 
-       *lvalue = t;
+       *date = t;
 
        return 0;
 }
 
+static const char *hextab = "0123456789abcdef";
+
 /*
  *  Parse a string value into a given VALUE_PAIR
+ *
+ *  FIXME: we probably want to fix this function to accept
+ *  octets as values for any type of attribute.  We should then
+ *  double-check the parsed value, to be sure it's legal for that
+ *  type (length, etc.)
  */
+static uint32_t getint(const char *value, char **end)
+{
+       if ((value[0] == '0') && (value[1] == 'x')) {
+               return strtoul(value, end, 16);
+       }
+
+       return strtoul(value, end, 10);
+}
+
+static int check_for_whitespace(const char *value)
+{
+       while (*value) {
+               if (!isspace((int) *value)) return 0;
+
+               value++;
+       }
+
+       return 1;
+}
+
+
 VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value)
 {
        char            *p, *s=0;
        const char      *cp, *cs;
+       int             x;
+       size_t          length;
        DICT_VALUE      *dval;
 
+       if (!value) return NULL;
+
        /*
         *      Even for integers, dates and ip addresses we
-        *      keep the original string in vp->strvalue.
+        *      keep the original string in vp->vp_strvalue.
         */
-       strNcpy((char *)vp->strvalue, value, sizeof(vp->strvalue));
-       vp->length = strlen(vp->strvalue);
+       if (vp->type != PW_TYPE_TLV) {
+               strlcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue));
+               vp->length = strlen(vp->vp_strvalue);
+       }
 
        switch(vp->type) {
                case PW_TYPE_STRING:
                        /*
-                        *      Already handled above.
+                        *      Do escaping here
                         */
+                       p = vp->vp_strvalue;
+                       cp = value;
+                       length = 0;
+
+                       while (*cp && (length < (sizeof(vp->vp_strvalue) - 1))) {
+                               char c = *cp++;
+
+                               if (c == '\\') {
+                                       switch (*cp) {
+                                       case 'r':
+                                               c = '\r';
+                                               cp++;
+                                               break;
+                                       case 'n':
+                                               c = '\n';
+                                               cp++;
+                                               break;
+                                       case 't':
+                                               c = '\t';
+                                               cp++;
+                                               break;
+                                       case '"':
+                                               c = '"';
+                                               cp++;
+                                               break;
+                                       case '\'':
+                                               c = '\'';
+                                               cp++;
+                                               break;
+                                       case '\\':
+                                               c = '\\';
+                                               cp++;
+                                               break;
+                                       case '`':
+                                               c = '`';
+                                               cp++;
+                                               break;
+                                       case '\0':
+                                               c = '\\'; /* no cp++ */
+                                               break;
+                                       default:
+                                               if ((cp[0] >= '0') &&
+                                                   (cp[0] <= '9') &&
+                                                   (cp[1] >= '0') &&
+                                                   (cp[1] <= '9') &&
+                                                   (cp[2] >= '0') &&
+                                                   (cp[2] <= '9') &&
+                                                   (sscanf(cp, "%3o", &x) == 1)) {
+                                                       c = x;
+                                                       cp += 3;
+                                               } /* else just do '\\' */
+                                       }
+                               }
+                               *p++ = c;
+                               length++;
+                       }
+                       vp->vp_strvalue[length] = '\0';
+                       vp->length = length;
                        break;
 
                case PW_TYPE_IPADDR:
                        /*
+                        *      It's a comparison, not a real IP.
+                        */
+                       if ((vp->operator == T_OP_REG_EQ) ||
+                           (vp->operator == T_OP_REG_NE)) {
+                               break;
+                       }
+
+                       /*
                         *      FIXME: complain if hostname
                         *      cannot be resolved, or resolve later!
                         */
+                       s = NULL;
                        if ((p = strrchr(value, '+')) != NULL && !p[1]) {
                                cs = s = strdup(value);
+                               if (!s) return NULL;
                                p = strrchr(s, '+');
                                *p = 0;
                                vp->flags.addport = 1;
@@ -694,68 +988,125 @@ VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value)
                                p = NULL;
                                cs = value;
                        }
-                       vp->lvalue = librad_dodns ? ip_getaddr(cs) :
-                                                   ip_addr(cs);
-                       if (s) free(s);
+
+                       {
+                               fr_ipaddr_t ipaddr;
+
+                               if (ip_hton(cs, AF_INET, &ipaddr) < 0) {
+                                       free(s);
+                                       fr_strerror_printf("Failed to find IP address for %s", cs);
+                                       return NULL;
+                               }
+
+                               vp->vp_ipaddr = ipaddr.ipaddr.ip4addr.s_addr;
+                       }
+                       free(s);
                        vp->length = 4;
                        break;
-               case PW_TYPE_INTEGER:
+
+               case PW_TYPE_BYTE:
+                       vp->length = 1;
+
                        /*
-                        *      If it starts with a digit, it must
-                        *      be a number (or a range).
-                        *
                         *      Note that ALL integers are unsigned!
                         */
-                       if (isdigit((int) *value)) {
-                               vp->lvalue = (uint32_t) strtoul(value, NULL, 10);
-                               vp->length = 4;
+                       vp->vp_integer = getint(value, &p);
+                       if (!*p) {
+                               if (vp->vp_integer > 255) {
+                                       fr_strerror_printf("Byte value \"%s\" is larger than 255", value);
+                                       return NULL;
+                               }
+                               break;
                        }
+                       if (check_for_whitespace(p)) break;
+                       goto check_for_value;
+
+               case PW_TYPE_SHORT:
+                       /*
+                        *      Note that ALL integers are unsigned!
+                        */
+                       vp->vp_integer = getint(value, &p);
+                       vp->length = 2;
+                       if (!*p) {
+                               if (vp->vp_integer > 65535) {
+                                       fr_strerror_printf("Byte value \"%s\" is larger than 65535", value);
+                                       return NULL;
+                               }
+                               break;
+                       }
+                       if (check_for_whitespace(p)) break;
+                       goto check_for_value;
+
+               case PW_TYPE_INTEGER:
+                       /*
+                        *      Note that ALL integers are unsigned!
+                        */
+                       vp->vp_integer = getint(value, &p);
+                       vp->length = 4;
+                       if (!*p) break;
+                       if (check_for_whitespace(p)) break;
+
+       check_for_value:
                        /*
                         *      Look for the named value for the given
                         *      attribute.
                         */
-                       else if ((dval = dict_valbyname(vp->attribute, value)) == NULL) {
-                               librad_log("Unknown value %s for attribute %s",
+                       if ((dval = dict_valbyname(vp->attribute, vp->vendor, value)) == NULL) {
+                               fr_strerror_printf("Unknown value %s for attribute %s",
                                           value, vp->name);
                                return NULL;
-                       } else {
-                               vp->lvalue = dval->value;
-                               vp->length = 4;
                        }
+                       vp->vp_integer = dval->value;
                        break;
 
                case PW_TYPE_DATE:
-                       if (gettime(value, (time_t *)&vp->lvalue) < 0) {
-                               librad_log("failed to parse time string "
-                                          "\"%s\"", value);
-                               return NULL;
+                       {
+                               /*
+                                *      time_t may be 64 bits, whule vp_date
+                                *      MUST be 32-bits.  We need an
+                                *      intermediary variable to handle
+                                *      the conversions.
+                                */
+                               time_t date;
+
+                               if (gettime(value, &date) < 0) {
+                                       fr_strerror_printf("failed to parse time string "
+                                                  "\"%s\"", value);
+                                       return NULL;
+                               }
+
+                               vp->vp_date = date;
                        }
                        vp->length = 4;
                        break;
+
                case PW_TYPE_ABINARY:
 #ifdef ASCEND_BINARY
-                       /*
-                        *      Special case to convert filter to binary
-                        */
-                       strNcpy(vp->strvalue, value, sizeof(vp->strvalue));
+                       if (strncasecmp(value, "0x", 2) == 0) {
+                               vp->type = PW_TYPE_OCTETS;
+                               goto do_octets;
+                       }
+
                        if (ascend_parse_filter(vp) < 0 ) {
-                               librad_log("failed to parse Ascend binary attribute: %s",
-                                          librad_errstr);
+                               fr_strerror_printf("failed to parse Ascend binary attribute: %s",
+                                          fr_strerror());
                                return NULL;
                        }
                        break;
+
                        /*
                         *      If Ascend binary is NOT defined,
                         *      then fall through to raw octets, so that
                         *      the user can at least make them by hand...
                         */
+       do_octets:
 #endif
                        /* raw octets: 0x01020304... */
                case PW_TYPE_OCTETS:
                        if (strncasecmp(value, "0x", 2) == 0) {
-                               u_char *us;
+                               uint8_t *us;
                                cp = value + 2;
-                               us = vp->strvalue;
+                               us = vp->vp_octets;
                                vp->length = 0;
 
 
@@ -764,16 +1115,17 @@ VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value)
                                 *      die.
                                 */
                                if ((strlen(cp) & 0x01) != 0) {
-                                       librad_log("Hex string is not an even length string.");
+                                       fr_strerror_printf("Hex string is not an even length string.");
                                        return NULL;
                                }
-                               
-                               
-                               while (*cp && vp->length < MAX_STRING_LEN) {
+
+
+                               while (*cp &&
+                                      (vp->length < MAX_STRING_LEN)) {
                                        unsigned int tmp;
 
                                        if (sscanf(cp, "%02x", &tmp) != 1) {
-                                               librad_log("Non-hex characters at %c%c", cp[0], cp[1]);
+                                               fr_strerror_printf("Non-hex characters at %c%c", cp[0], cp[1]);
                                                return NULL;
                                        }
 
@@ -781,34 +1133,152 @@ VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value)
                                        *(us++) = tmp;
                                        vp->length++;
                                }
-                               *us = '\0';
                        }
                        break;
 
                case PW_TYPE_IFID:
-                       if (ifid_aton(value, vp->strvalue) == NULL) {
-                               librad_log("failed to parse interface-id "
+                       if (ifid_aton(value, (void *) &vp->vp_ifid) == NULL) {
+                               fr_strerror_printf("failed to parse interface-id "
                                           "string \"%s\"", value);
                                return NULL;
                        }
                        vp->length = 8;
-                       vp->strvalue[vp->length] = '\0';
                        break;
 
                case PW_TYPE_IPV6ADDR:
-                       if (ipv6_addr(value, vp->strvalue) < 0) {
-                               librad_log("failed to parse IPv6 address "
+                       {
+                               fr_ipaddr_t ipaddr;
+
+                               if (ip_hton(value, AF_INET6, &ipaddr) < 0) {
+                                       char buffer[1024];
+
+                                       strlcpy(buffer, fr_strerror(), sizeof(buffer));
+
+                                       fr_strerror_printf("failed to parse IPv6 address "
+                                                           "string \"%s\": %s", value, buffer);
+                                       return NULL;
+                               }
+                               vp->vp_ipv6addr = ipaddr.ipaddr.ip6addr;
+                               vp->length = 16; /* length of IPv6 address */
+                       }
+                       break;
+
+               case PW_TYPE_IPV6PREFIX:
+                       p = strchr(value, '/');
+                       if (!p || ((p - value) >= 256)) {
+                               fr_strerror_printf("invalid IPv6 prefix "
                                           "string \"%s\"", value);
                                return NULL;
+                       } else {
+                               unsigned int prefix;
+                               char buffer[256], *eptr;
+
+                               memcpy(buffer, value, p - value);
+                               buffer[p - value] = '\0';
+
+                               if (inet_pton(AF_INET6, buffer, vp->vp_octets + 2) <= 0) {
+                                       fr_strerror_printf("failed to parse IPv6 address "
+                                                  "string \"%s\"", value);
+                                       return NULL;
+                               }
+
+                               prefix = strtoul(p + 1, &eptr, 10);
+                               if ((prefix > 128) || *eptr) {
+                                       fr_strerror_printf("failed to parse IPv6 address "
+                                                  "string \"%s\"", value);
+                                       return NULL;
+                               }
+                               vp->vp_octets[1] = prefix;
+                       }
+                       vp->vp_octets[0] = '\0';
+                       vp->length = 16 + 2;
+                       break;
+
+               case PW_TYPE_ETHERNET:
+                       {
+                               const char *c1, *c2;
+
+                               length = 0;
+                               cp = value;
+                               while (*cp) {
+                                       if (cp[1] == ':') {
+                                               c1 = hextab;
+                                               c2 = memchr(hextab, tolower((int) cp[0]), 16);
+                                               cp += 2;
+                                       } else if ((cp[1] != '\0') &&
+                                                  ((cp[2] == ':') ||
+                                                   (cp[2] == '\0'))) {
+                                                  c1 = memchr(hextab, tolower((int) cp[0]), 16);
+                                                  c2 = memchr(hextab, tolower((int) cp[1]), 16);
+                                                  cp += 2;
+                                                  if (*cp == ':') cp++;
+                                       } else {
+                                               c1 = c2 = NULL;
+                                       }
+                                       if (!c1 || !c2 || (length >= sizeof(vp->vp_ether))) {
+                                               fr_strerror_printf("failed to parse Ethernet address \"%s\"", value);
+                                               return NULL;
+                                       }
+                                       vp->vp_ether[length] = ((c1-hextab)<<4) + (c2-hextab);
+                                       length++;
+                               }
+                       }
+                       vp->length = 6;
+                       break;
+
+               case PW_TYPE_COMBO_IP:
+                       if (inet_pton(AF_INET6, value, vp->vp_strvalue) > 0) {
+                               vp->type = PW_TYPE_IPV6ADDR;
+                               vp->length = 16; /* length of IPv6 address */
+                               vp->vp_strvalue[vp->length] = '\0';
+
+                       } else {
+                               fr_ipaddr_t ipaddr;
+
+                               if (ip_hton(value, AF_INET, &ipaddr) < 0) {
+                                       fr_strerror_printf("Failed to find IPv4 address for %s", value);
+                                       return NULL;
+                               }
+
+                               vp->type = PW_TYPE_IPADDR;
+                               vp->vp_ipaddr = ipaddr.ipaddr.ip4addr.s_addr;
+                               vp->length = 4;
+                       }
+                       break;
+
+               case PW_TYPE_SIGNED: /* Damned code for 1 WiMAX attribute */
+                       vp->vp_signed = (int32_t) strtol(value, &p, 10);
+                       vp->length = 4;
+                       break;
+
+               case PW_TYPE_TLV: /* don't use this! */
+                       if (strncasecmp(value, "0x", 2) != 0) {
+                               fr_strerror_printf("Invalid TLV specification");
+                               return NULL;
+                       }
+                       length = strlen(value + 2) / 2;
+                       if (vp->length < length) {
+                               free(vp->vp_tlv);
+                               vp->vp_tlv = NULL;
+                       }
+                       vp->vp_tlv = malloc(length);
+                       if (!vp->vp_tlv) {
+                               fr_strerror_printf("No memory");
+                               return NULL;
+                       }
+                       if (fr_hex2bin(value + 2, vp->vp_tlv,
+                                      length) != length) {
+                               fr_strerror_printf("Invalid hex data in TLV");
+                               return NULL;
                        }
-                       vp->length = 16; /* length of IPv6 address */
-                       vp->strvalue[vp->length] = '\0';
+                       vp->length = length;
                        break;
+
                        /*
                         *  Anything else.
                         */
                default:
-                       librad_log("unknown attribute type %d", vp->type);
+                       fr_strerror_printf("unknown attribute type %d", vp->type);
                        return NULL;
        }
 
@@ -821,170 +1291,178 @@ VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value)
  *
  *     Attr-%d
  *     Vendor-%d-Attr-%d
+ *     VendorName-Attr-%d
  */
 static VALUE_PAIR *pairmake_any(const char *attribute, const char *value,
                                int operator)
 {
-       int             attr;
-       const char      *p;
+       int             attr, vendor;
+       size_t          size;
+       const char      *p = attribute;
+       char            *q;
        VALUE_PAIR      *vp;
-       DICT_ATTR       *da;
 
        /*
         *      Unknown attributes MUST be of type 'octets'
         */
        if (value && (strncasecmp(value, "0x", 2) != 0)) {
-               goto error;
+               fr_strerror_printf("Invalid octet string \"%s\" for attribute name \"%s\"", value, attribute);
+               return NULL;
        }
 
+       vendor = 0;
+
        /*
-        *      Attr-%d
+        *      Pull off vendor prefix first.
         */
-       if (strncasecmp(attribute, "Attr-", 5) == 0) {
-               attr = atoi(attribute + 5);
-               p = attribute + 5;
-               p += strspn(p, "0123456789");
-               if (*p != 0) goto error;
-
-
-               /*
-                *      Vendor-%d-Attr-%d
-                */
-       } else if (strncasecmp(attribute, "Vendor-", 7) == 0) {
-               int vendor;
-
-               vendor = atoi(attribute + 7);
-               if ((vendor == 0) || (vendor > 65535)) goto error;
-
-               p = attribute + 7;
-               p += strspn(p, "0123456789");
-
-               /*
-                *      Not Vendor-%d-Attr-%d
-                */
-               if (strncasecmp(p, "-Attr-", 6) != 0) goto error;
-
-               p += 6;
-               attr = atoi(p);
-
-               p += strspn(p, "0123456789");
-               if (*p != 0) goto error;
-
-               if ((attr == 0) || (attr > 65535)) goto error;
-
-               attr |= (vendor << 16);
+       if (strncasecmp(p, "Attr-", 5) != 0) {
+               if (strncasecmp(p, "Vendor-", 7) == 0) {
+                       vendor = (int) strtol(p + 7, &q, 10);
+                       if ((vendor == 0) || (vendor > FR_MAX_VENDOR)) {
+                               fr_strerror_printf("Invalid vendor value in attribute name \"%s\"", attribute);
+                               return NULL;
+                       }
 
-               /*
-                *      VendorName-Attr-%d
-                */
-       } else if (((p = strchr(attribute, '-')) != NULL) &&
-                  (strncasecmp(p, "-Attr-", 6) == 0)) {
-               int vendor;
-               char buffer[256];
+                       p = q;
 
-               if ((p - attribute) >= sizeof(buffer)) goto error;
+               } else {        /* must be vendor name */
+                       char buffer[256];
 
-               memcpy(buffer, attribute, p - attribute);
-               buffer[p - attribute] = '\0';
+                       q = strchr(p, '-');
 
-               vendor = dict_vendorbyname(buffer);
-               if (vendor == 0) goto error;
+                       if (!q) {
+                               fr_strerror_printf("Invalid vendor name in attribute name \"%s\"", attribute);
+                               return NULL;
+                       }
 
-               p += 6;
-               attr = atoi(p);
+                       if ((size_t) (q - p) >= sizeof(buffer)) {
+                               fr_strerror_printf("Vendor name too long in attribute name \"%s\"", attribute);
+                               return NULL;
+                       }
 
-               p += strspn(p, "0123456789");
-               if (*p != 0) goto error;
+                       memcpy(buffer, p, (q - p));
+                       buffer[q - p] = '\0';
 
-               if ((attr == 0) || (attr > 65535)) goto error;
+                       vendor = dict_vendorbyname(buffer);
+                       if (!vendor) {
+                               fr_strerror_printf("Unknown vendor name in attribute name \"%s\"", attribute);
+                               return NULL;
+                       }
 
-               attr |= (vendor << 16);
+                       p = q;
+               }
 
-       } else {                /* very much unknown: die */
-       error:
-               librad_log("Unknown attribute \"%s\"", attribute);
-               return NULL;
+               if (*p != '-') {
+                       fr_strerror_printf("Invalid text following vendor definition in attribute name \"%s\"", attribute);
+                       return NULL;
+               }
+               p++;
        }
 
        /*
-        *      We've now parsed the attribute properly, and verified
-        *      it to have value 'octets'.  Let's create it.
+        *      Attr-%d
         */
-       if ((vp = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
-               librad_log("out of memory");
+       if (strncasecmp(p, "Attr-", 5) != 0) {
+               fr_strerror_printf("Invalid format in attribute name \"%s\"", attribute);
                return NULL;
        }
-       memset(vp, 0, sizeof(VALUE_PAIR));
-       vp->type = PW_TYPE_OCTETS;
+
+       attr = strtol(p + 5, &q, 10);
 
        /*
-        *      It may not be valid hex characters.  If not, die.
+        *      Invalid, or trailing text after number.
         */
-       if (pairparsevalue(vp, value) == NULL) {
-               pairfree(&vp);
+       if ((attr == 0) || *q) {
+               fr_strerror_printf("Invalid value in attribute name \"%s\"", attribute);
                return NULL;
        }
 
        /*
-        *      Dictionary type over-rides what the caller says.
-        *      This "converts" the parsed value into the appropriate
-        *      type.
-        *
-        *      Also, normalize the name of the attribute...
-        *
-        *      Much of this code is copied from paircreate()
+        *      Double-check the size of attr.
         */
-       if ((da = dict_attrbyvalue(attr)) != NULL) {
-               strcpy(vp->name, da->name);
-               vp->type = da->type;
-               vp->flags = da->flags;
+       if (vendor) {
+               DICT_VENDOR *dv = dict_vendorbyvalue(vendor);
 
-               /*
-                *      Sanity check the type for length.  We don't
-                *      want to look at attributes which are of the
-                *      wrong length.
-                */
-               switch (vp->type) {
-               case PW_TYPE_DATE:
-               case PW_TYPE_INTEGER:
-               case PW_TYPE_IPADDR: /* always kept in network byte order */
-                       if (vp->length != 4) {
-                       length_error:
-                               pairfree(&vp);
-                               librad_log("Attribute has invalid length");
+               if (!dv) {
+                       if (attr > 255) {
+                       attr_error:
+                               fr_strerror_printf("Invalid attribute number in attribute name \"%s\"", attribute);
                                return NULL;
                        }
-                       memcpy(&vp->lvalue, vp->strvalue, sizeof(vp->lvalue));
-                       break;
 
-               case PW_TYPE_IFID:
-                       if (vp->length != 8) goto length_error;
-                       break;
+               } else switch (dv->type) {
+                       case 1:
+                               if (attr > 255) goto attr_error;
+                               break;
 
-               case PW_TYPE_IPV6ADDR:
-                       if (vp->length != 16) goto length_error;
-                       break;
+                       case 2:
+                               if (attr > 65535) goto attr_error;
+                               break;
 
-#ifdef ASCEND_BINARY
-               case PW_TYPE_ABINARY:
-                       if (vp->length != 32) goto length_error;
-                       break;
-#endif
-               default:        /* string, octets, etc. */
-                       break;
+                       case 4:
+                               break;
+
+                       default:
+                               fr_strerror_printf("Internal sanity check failed");
+                               return NULL;
                }
+       }
 
-       } else if (VENDOR(attr) == 0) {
-               sprintf(vp->name, "Attr-%u", attr);
-       } else {
-               sprintf(vp->name, "Vendor-%u-Attr-%u",
-                       VENDOR(attr), attr & 0xffff);
+       /*
+        *      We've now parsed the attribute properly, Let's create
+        *      it.  This next stop also looks the attribute up in the
+        *      dictionary, and creates the appropriate type for it.
+        */
+       if ((vp = paircreate(attr, vendor, PW_TYPE_OCTETS)) == NULL) {
+               fr_strerror_printf("out of memory");
+               return NULL;
        }
 
-       vp->attribute = attr;
        vp->operator = (operator == 0) ? T_OP_EQ : operator;
-       vp->next = NULL;
+       if (!value) return vp;
+
+       size = strlen(value + 2);
 
+       /*
+        *      We may be reading something like Attr-5.  i.e.
+        *      who-ever wrote the text didn't understand it, but we
+        *      do.
+        */
+       switch (vp->type) {
+       default:
+               if (size == (vp->length * 2)) break;
+               vp->type = PW_TYPE_OCTETS;
+               /* FALL-THROUGH */
+               
+       case PW_TYPE_STRING:
+       case PW_TYPE_OCTETS:
+       case PW_TYPE_ABINARY:
+               vp->length = size >> 1;
+               break;
+       }
+
+       if (fr_hex2bin(value + 2, vp->vp_octets, size) != vp->length) {
+               fr_strerror_printf("Invalid hex string");
+               free(vp);
+               return NULL;
+       }
+
+       /*
+        *      Move contents around based on type.  This is
+        *      to work around the historical use of "lvalue".
+        */
+       switch (vp->type) {
+       case PW_TYPE_DATE:
+       case PW_TYPE_IPADDR:
+       case PW_TYPE_INTEGER:
+               memcpy(&vp->lvalue, vp->vp_octets, sizeof(vp->lvalue));
+               vp->vp_strvalue[0] = '\0';
+               break;
+               
+       default:
+               break;
+       }
+       
        return vp;
 }
 
@@ -999,10 +1477,8 @@ VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator)
        char            *tc, *ts;
        signed char     tag;
        int             found_tag;
-#ifdef HAVE_REGEX_H
-       int             res;
-       regex_t         cre;
-#endif
+       char            buffer[64];
+       const char      *attrname = attribute;
 
        /*
         *    Check for tags in 'Attribute:Tag' format.
@@ -1010,8 +1486,17 @@ VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator)
        found_tag = 0;
        tag = 0;
 
-       ts = strrchr( attribute, ':' );
+       ts = strrchr(attribute, ':');
+       if (ts && !ts[1]) {
+               fr_strerror_printf("Invalid tag for attribute %s", attribute);
+               return NULL;
+       }
+
        if (ts && ts[1]) {
+               strlcpy(buffer, attribute, sizeof(buffer));
+               attrname = buffer;
+               ts = strrchr(attrname, ':');
+
                 /* Colon found with something behind it */
                 if (ts[1] == '*' && ts[2] == 0) {
                         /* Wildcard tag for check items */
@@ -1024,7 +1509,7 @@ VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator)
                                 *ts = 0;
                         else tag = 0;
                 } else {
-                        librad_log("Invalid tag for attribute %s", attribute);
+                        fr_strerror_printf("Invalid tag for attribute %s", attribute);
                         return NULL;
                 }
                 found_tag = 1;
@@ -1034,22 +1519,15 @@ VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator)
         *      It's not found in the dictionary, so we use
         *      another method to create the attribute.
         */
-       if ((da = dict_attrbyname(attribute)) == NULL) {
-               return pairmake_any(attribute, value, operator);
+       if ((da = dict_attrbyname(attrname)) == NULL) {
+               return pairmake_any(attrname, value, operator);
        }
 
-       if ((vp = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
-               librad_log("out of memory");
+       if ((vp = pairalloc(da)) == NULL) {
+               fr_strerror_printf("out of memory");
                return NULL;
        }
-
-       memset(vp, 0, sizeof(VALUE_PAIR));
-       vp->attribute = da->attr;
-       vp->type = da->type;
        vp->operator = (operator == 0) ? T_OP_EQ : operator;
-       strcpy(vp->name, da->name);
-       vp->flags = da->flags;
-       vp->next = NULL;
 
        /*      Check for a tag in the 'Merit' format of:
         *      :Tag:Value.  Print an error if we already found
@@ -1059,13 +1537,12 @@ VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator)
        if (value && (*value == ':' && da->flags.has_tag)) {
                /* If we already found a tag, this is invalid */
                if(found_tag) {
-                       pairbasicfree(vp);
-                       librad_log("Duplicate tag %s for attribute %s",
+                       fr_strerror_printf("Duplicate tag %s for attribute %s",
                                   value, vp->name);
                        DEBUG("Duplicate tag %s for attribute %s\n",
                                   value, vp->name);
+                       pairbasicfree(vp);
                        return NULL;
-
                }
                /* Colon found and attribute allows a tag */
                if (value[1] == '*' && value[2] == ':') {
@@ -1096,7 +1573,7 @@ VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator)
                 */
        case T_OP_CMP_TRUE:
        case T_OP_CMP_FALSE:
-               vp->strvalue[0] = '\0';
+               vp->vp_strvalue[0] = '\0';
                vp->length = 0;
                return vp;
                break;
@@ -1108,38 +1585,31 @@ VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator)
                 */
        case T_OP_REG_EQ:       /* =~ */
        case T_OP_REG_NE:       /* !~ */
-               if (vp->type == PW_TYPE_INTEGER) {
-                       return vp;
-               }
-#ifdef HAVE_REGEX_H
-               /*
-                *      Regular expression match with no regular
-                *      expression is wrong.
-                */
                if (!value) {
-                       pairfree(&vp);
+                       fr_strerror_printf("No regular expression found in %s",
+                                          vp->name);
+                       pairbasicfree(vp);
                        return NULL;
                }
+         
+               strlcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue));
+               vp->length = strlen(vp->vp_strvalue);
+               /*
+                *      If anything goes wrong, this is a run-time error,
+                *      not a compile-time error.
+                */
+               return vp;
 
-               res = regcomp(&cre, value, REG_EXTENDED|REG_NOSUB);
-               if (res != 0) {
-                       char    msg[128];
-
-                       regerror(res, &cre, msg, sizeof(msg));
-                       librad_log("Illegal regular expression in attribute: %s: %s",
-                               vp->name, msg);
-                       pairbasicfree(vp);
-                       return NULL;
-               }
-               regfree(&cre);
-#else
-               librad_log("Regelar expressions not enabled in this build, error in attribute %s",
-                               vp->name);
-               pairbasicfree(vp);
-               return NULL;
-#endif
        }
 
+       /*
+        *      FIXME: if (strcasecmp(attribute, vp->name) != 0)
+        *      then the user MAY have typed in the attribute name
+        *      as Vendor-%d-Attr-%d, and the value MAY be octets.
+        *
+        *      We probably want to fix pairparsevalue to accept
+        *      octets as values for any attribute.
+        */
        if (value && (pairparsevalue(vp, value) == NULL)) {
                pairbasicfree(vp);
                return NULL;
@@ -1148,49 +1618,97 @@ VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator)
        return vp;
 }
 
+
+/*
+ *     [a-zA-Z0-9_-:]+
+ */
+static const int valid_attr_name[256] = {
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+       0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
+       0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
 /*
  *     Read a valuepair from a buffer, and advance pointer.
  *     Sets *eol to T_EOL if end of line was encountered.
  */
-VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol)
+VALUE_PAIR *pairread(const char **ptr, FR_TOKEN *eol)
 {
        char            buf[64];
        char            attr[64];
-       char            value[256];
-       char            *p;
-       LRAD_TOKEN      token, t, xlat;
+       char            value[1024], *q;
+       const char      *p;
+       FR_TOKEN        token, t, xlat;
        VALUE_PAIR      *vp;
+       size_t          len;
 
-       *eol = T_INVALID;
+       *eol = T_OP_INVALID;
+
+       p = *ptr;
+       while ((*p == ' ') || (*p == '\t')) p++;
 
-       /* Get attribute. */
-       token = gettoken(ptr, attr, sizeof(attr));
+       if (!*p) {
+               *eol = T_OP_INVALID;
+               fr_strerror_printf("No token read where we expected an attribute name");
+               return NULL;
+       }
 
-       /*  If it's a comment, then exit, as we haven't read a pair */
-       if (token == T_HASH) {
-               *eol = token;
-               librad_log("Read a comment instead of a token");
+       if (*p == '#') {
+               *eol = T_HASH;
+               fr_strerror_printf("Read a comment instead of a token");
                return NULL;
        }
 
-       /*  It's not a comment, so it MUST be an attribute */
-       if ((token == T_EOL) ||
-           (attr[0] == 0)) {
-               librad_log("No token read where we expected an attribute name");
+       q = attr;
+       for (len = 0; len < sizeof(attr); len++) {
+               if (valid_attr_name[(int)*p]) {
+                       *q++ = *p++;
+                       continue;
+               }
+               break;
+       }
+
+       if (len == sizeof(attr)) {
+               *eol = T_OP_INVALID;
+               fr_strerror_printf("Attribute name is too long");
                return NULL;
        }
 
-       /* Now we should have an '=' here. */
+       /*
+        *      We may have Foo-Bar:= stuff, so back up.
+        */
+       if ((len > 0) && (attr[len - 1] == ':')) {
+               p--;
+               len--;
+       }
+
+       attr[len] = '\0';
+       *ptr = p;
+
+       /* Now we should have an operator here. */
        token = gettoken(ptr, buf, sizeof(buf));
        if (token < T_EQSTART || token > T_EQEND) {
-               librad_log("expecting '='");
+               fr_strerror_printf("expecting operator");
                return NULL;
        }
 
        /* Read value.  Note that empty string values are allowed */
        xlat = gettoken(ptr, value, sizeof(value));
        if (xlat == T_EOL) {
-               librad_log("failed to get value");
+               fr_strerror_printf("failed to get value");
                return NULL;
        }
 
@@ -1200,7 +1718,7 @@ VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol)
        p = *ptr;
        t = gettoken(&p, buf, sizeof(buf));
        if (t != T_EOL && t != T_COMMA && t != T_HASH) {
-               librad_log("Expected end of line or comma");
+               fr_strerror_printf("Expected end of line or comma");
                return NULL;
        }
 
@@ -1209,6 +1727,7 @@ VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol)
                *ptr = p;
        }
 
+       vp = NULL;
        switch (xlat) {
                /*
                 *      Make the full pair now.
@@ -1223,37 +1742,88 @@ VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol)
        case T_DOUBLE_QUOTED_STRING:
                p = strchr(value, '%');
                if (p && (p[1] == '{')) {
+                       if (strlen(value) >= sizeof(vp->vp_strvalue)) {
+                               fr_strerror_printf("Value too long");
+                               return NULL;
+                       }
                        vp = pairmake(attr, NULL, token);
                        if (!vp) {
-                               *eol = T_INVALID;
+                               *eol = T_OP_INVALID;
                                return NULL;
                        }
 
-                       strNcpy(vp->strvalue, value, sizeof(vp->strvalue));
+                       strlcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue));
                        vp->flags.do_xlat = 1;
                        vp->length = 0;
                } else {
+                       /*
+                        *      Parse && escape it, as defined by the
+                        *      data type.
+                        */
                        vp = pairmake(attr, value, token);
+                       if (!vp) {
+                               *eol = T_OP_INVALID;
+                               return NULL;
+                       }
                }
                break;
 
+       case T_SINGLE_QUOTED_STRING:
+               vp = pairmake(attr, NULL, token);
+               if (!vp) {
+                       *eol = T_OP_INVALID;
+                       return NULL;
+               }
+
+               /*
+                *      String and octet types get copied verbatim.
+                */
+               if ((vp->type == PW_TYPE_STRING) ||
+                   (vp->type == PW_TYPE_OCTETS)) {
+                       strlcpy(vp->vp_strvalue, value,
+                               sizeof(vp->vp_strvalue));
+                       vp->length = strlen(vp->vp_strvalue);
+
+                       /*
+                        *      Everything else gets parsed: it's
+                        *      DATA, not a string!
+                        */
+               } else if (!pairparsevalue(vp, value)) {
+                       pairfree(&vp);
+                       *eol = T_OP_INVALID;
+                       return NULL;
+               }
+               break;
 
                /*
                 *      Mark the pair to be allocated later.
                 */
        case T_BACK_QUOTED_STRING:
+               if (strlen(value) >= sizeof(vp->vp_strvalue)) {
+                       fr_strerror_printf("Value too long");
+                       return NULL;
+               }
+
                vp = pairmake(attr, NULL, token);
                if (!vp) {
-                       *eol = T_INVALID;
+                       *eol = T_OP_INVALID;
                        return NULL;
                }
 
                vp->flags.do_xlat = 1;
-               strNcpy(vp->strvalue, value, sizeof(vp->strvalue));
+               strlcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue));
                vp->length = 0;
                break;
        }
 
+       /*
+        *      If we didn't make a pair, return an error.
+        */
+       if (!vp) {
+               *eol = T_OP_INVALID;
+               return NULL;
+       }
+
        return vp;
 }
 
@@ -1261,12 +1831,12 @@ VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol)
  *     Read one line of attribute/value pairs. This might contain
  *     multiple pairs seperated by comma's.
  */
-LRAD_TOKEN userparse(char *buffer, VALUE_PAIR **first_pair)
+FR_TOKEN userparse(const char *buffer, VALUE_PAIR **first_pair)
 {
        VALUE_PAIR      *vp;
-       char            *p;
-       LRAD_TOKEN      last_token = T_INVALID;
-       LRAD_TOKEN      previous_token;
+       const char      *p;
+       FR_TOKEN        last_token = T_OP_INVALID;
+       FR_TOKEN        previous_token;
 
        /*
         *      We allow an empty line.
@@ -1304,7 +1874,7 @@ LRAD_TOKEN userparse(char *buffer, VALUE_PAIR **first_pair)
 VALUE_PAIR *readvp2(FILE *fp, int *pfiledone, const char *errprefix)
 {
        char buf[8192];
-       LRAD_TOKEN last_token = T_EOL;
+       FR_TOKEN last_token = T_EOL;
        VALUE_PAIR *vp;
        VALUE_PAIR *list;
        int error = 0;
@@ -1335,7 +1905,7 @@ VALUE_PAIR *readvp2(FILE *fp, int *pfiledone, const char *errprefix)
                last_token = userparse(buf, &vp);
                if (!vp) {
                        if (last_token != T_EOL) {
-                               librad_perror(errprefix);
+                               fr_perror("%s", errprefix);
                                error = 1;
                                break;
                        }
@@ -1353,3 +1923,163 @@ VALUE_PAIR *readvp2(FILE *fp, int *pfiledone, const char *errprefix)
        return error ? NULL: list;
 }
 
+
+
+/*
+ *     Compare two pairs, using the operator from "one".
+ *
+ *     i.e. given two attributes, it does:
+ *
+ *     (two->data) (one->operator) (one->data)
+ *
+ *     e.g. "foo" != "bar"
+ *
+ *     Returns true (comparison is true), or false (comparison is not true);
+ *
+ *     FIXME: Ignores tags!
+ */
+int paircmp(VALUE_PAIR *one, VALUE_PAIR *two)
+{
+       int compare;
+
+       switch (one->operator) {
+       case T_OP_CMP_TRUE:
+               return (two != NULL);
+
+       case T_OP_CMP_FALSE:
+               return (two == NULL);
+
+               /*
+                *      One is a regex, compile it, print two to a string,
+                *      and then do string comparisons.
+                */
+       case T_OP_REG_EQ:
+       case T_OP_REG_NE:
+#ifndef HAVE_REGEX_H
+               return -1;
+#else
+               {
+                       regex_t reg;
+                       char buffer[MAX_STRING_LEN * 4 + 1];
+
+                       compare = regcomp(&reg, one->vp_strvalue,
+                                         REG_EXTENDED);
+                       if (compare != 0) {
+                               regerror(compare, &reg, buffer, sizeof(buffer));
+                               fr_strerror_printf("Illegal regular expression in attribute: %s: %s",
+                                          one->name, buffer);
+                               return -1;
+                       }
+
+                       vp_prints_value(buffer, sizeof(buffer), two, 0);
+
+                       /*
+                        *      Don't care about substring matches,
+                        *      oh well...
+                        */
+                       compare = regexec(&reg, buffer, 0, NULL, 0);
+
+                       regfree(&reg);
+                       if (one->operator == T_OP_REG_EQ) return (compare == 0);
+                       return (compare != 0);
+               }
+#endif
+
+       default:                /* we're OK */
+               break;
+       }
+
+       /*
+        *      After doing the previous check for special comparisons,
+        *      do the per-type comparison here.
+        */
+       switch (one->type) {
+       case PW_TYPE_ABINARY:
+       case PW_TYPE_OCTETS:
+       {
+               size_t length;
+
+               if (one->length < two->length) {
+                       length = one->length;
+               } else {
+                       length = two->length;
+               }
+
+               if (length) {
+                       compare = memcmp(two->vp_octets, one->vp_octets,
+                                        length);
+                       if (compare != 0) break;
+               }
+
+               /*
+                *      Contents are the same.  The return code
+                *      is therefore the difference in lengths.
+                *
+                *      i.e. "0x00" is smaller than "0x0000"
+                */
+               compare = two->length - one->length;
+       }
+               break;
+
+       case PW_TYPE_STRING:
+               compare = strcmp(two->vp_strvalue, one->vp_strvalue);
+               break;
+
+       case PW_TYPE_BYTE:
+       case PW_TYPE_SHORT:
+       case PW_TYPE_INTEGER:
+       case PW_TYPE_DATE:
+               compare = two->vp_integer - one->vp_integer;
+               break;
+
+       case PW_TYPE_IPADDR:
+               compare = ntohl(two->vp_ipaddr) - ntohl(one->vp_ipaddr);
+               break;
+
+       case PW_TYPE_IPV6ADDR:
+               compare = memcmp(&two->vp_ipv6addr, &one->vp_ipv6addr,
+                                sizeof(two->vp_ipv6addr));
+               break;
+
+       case PW_TYPE_IPV6PREFIX:
+               compare = memcmp(&two->vp_ipv6prefix, &one->vp_ipv6prefix,
+                                sizeof(two->vp_ipv6prefix));
+               break;
+
+       case PW_TYPE_IFID:
+               compare = memcmp(&two->vp_ifid, &one->vp_ifid,
+                                sizeof(two->vp_ifid));
+               break;
+
+       default:
+               return 0;       /* unknown type */
+       }
+
+       /*
+        *      Now do the operator comparison.
+        */
+       switch (one->operator) {
+       case T_OP_CMP_EQ:
+               return (compare == 0);
+
+       case T_OP_NE:
+               return (compare != 0);
+
+       case T_OP_LT:
+               return (compare < 0);
+
+       case T_OP_GT:
+               return (compare > 0);
+
+       case T_OP_LE:
+               return (compare <= 0);
+
+       case T_OP_GE:
+               return (compare >= 0);
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}