Sync master:src/lib/valuepair.c and v3.0.x:src/lib/valuepair.c
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 4 Apr 2014 15:47:36 +0000 (16:47 +0100)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 4 Apr 2014 15:52:07 +0000 (16:52 +0100)
src/include/libradius.h
src/lib/valuepair.c
src/main/radsniff.c
src/modules/rlm_rest/rest.c

index 334c391..22d67f2 100644 (file)
@@ -414,6 +414,23 @@ typedef struct radius_packet {
 #endif
 } RADIUS_PACKET;
 
+typedef enum {
+       DECODE_FAIL_NONE = 0,
+       DECODE_FAIL_MIN_LENGTH_PACKET,
+       DECODE_FAIL_MIN_LENGTH_FIELD,
+       DECODE_FAIL_MIN_LENGTH_MISMATCH,
+       DECODE_FAIL_HEADER_OVERFLOW,
+       DECODE_FAIL_UNKNOWN_PACKET_CODE,
+       DECODE_FAIL_INVALID_ATTRIBUTE,
+       DECODE_FAIL_ATTRIBUTE_TOO_SHORT,
+       DECODE_FAIL_ATTRIBUTE_OVERFLOW,
+       DECODE_FAIL_MA_INVALID_LENGTH,
+       DECODE_FAIL_ATTRIBUTE_UNDERFLOW,
+       DECODE_FAIL_TOO_MANY_ATTRIBUTES,
+       DECODE_FAIL_MA_MISSING,
+       DECODE_FAIL_MAX
+} decode_fail_t;
+
 /*
  *     Version check.
  */
@@ -461,7 +478,7 @@ DICT_ATTR const     *dict_attrbyname(char const *attr);
 DICT_ATTR const        *dict_attrbytype(unsigned int attr, unsigned int vendor,
                                 PW_TYPE type);
 DICT_ATTR const        *dict_attrbyparent(DICT_ATTR const *parent, unsigned int attr,
-                                  unsigned int vendor);
+                                          unsigned int vendor);
 int            dict_attr_child(DICT_ATTR const *parent,
                                unsigned int *pattr, unsigned int *pvendor);
 DICT_VALUE     *dict_valbyattr(unsigned int attr, unsigned int vendor, int val);
@@ -522,8 +539,7 @@ int         rad_chap_encode(RADIUS_PACKET *packet, uint8_t *output,
                                int id, VALUE_PAIR *password);
 
 int            rad_attr_ok(RADIUS_PACKET const *packet, RADIUS_PACKET const *original,
-                           DICT_ATTR *da,
-                           uint8_t const *data, size_t length);
+                           DICT_ATTR *da, uint8_t const *data, size_t length);
 int            rad_tlv_ok(uint8_t const *data, size_t length,
                           size_t dv_type, size_t dv_length);
 
@@ -542,7 +558,6 @@ int         rad_vp2extended(RADIUS_PACKET const *packet,
                                RADIUS_PACKET const *original,
                                char const *secret, VALUE_PAIR const **pvp,
                                uint8_t *ptr, size_t room);
-
 int            rad_vp2wimax(RADIUS_PACKET const *packet,
                             RADIUS_PACKET const *original,
                             char const *secret, VALUE_PAIR const **pvp,
@@ -551,6 +566,7 @@ int         rad_vp2wimax(RADIUS_PACKET const *packet,
 int            rad_vp2vsa(RADIUS_PACKET const *packet, RADIUS_PACKET const *original,
                           char const *secret, VALUE_PAIR const **pvp, uint8_t *start,
                           size_t room);
+
 int            rad_vp2rfc(RADIUS_PACKET const *packet,
                           RADIUS_PACKET const *original,
                           char const *secret, VALUE_PAIR const **pvp,
@@ -582,10 +598,17 @@ VALUE_PAIR        *fr_cursor_replace(vp_cursor_t *cursor, VALUE_PAIR *new);
 void           pairdelete(VALUE_PAIR **, unsigned int attr, unsigned int vendor, int8_t tag);
 void           pairadd(VALUE_PAIR **, VALUE_PAIR *);
 void           pairreplace(VALUE_PAIR **first, VALUE_PAIR *add);
-int            paircmp(VALUE_PAIR *check, VALUE_PAIR *data);
-int            paircmp_op(VALUE_PAIR const *one, FR_TOKEN op, VALUE_PAIR const *two);
-void           pairsort(VALUE_PAIR **vps, bool with_tag);
+int8_t         paircmp_value(VALUE_PAIR const *a, VALUE_PAIR const *b);
+int8_t         paircmp_op(VALUE_PAIR const *a, FR_TOKEN op, VALUE_PAIR const *b);
+int8_t         paircmp(VALUE_PAIR *a, VALUE_PAIR *b);
+int8_t         pairlistcmp(VALUE_PAIR *a, VALUE_PAIR *b);
+
+typedef int8_t (*fr_pair_cmp_t)(VALUE_PAIR const *a, VALUE_PAIR const *b);
+int8_t         attrcmp(VALUE_PAIR const *a, VALUE_PAIR const *b);
+int8_t         attrtagcmp(VALUE_PAIR const *a, VALUE_PAIR const *b);
+void           pairsort(VALUE_PAIR **vps, fr_pair_cmp_t cmp);
 bool           pairvalidate(VALUE_PAIR *filter, VALUE_PAIR *list);
+bool           pairvalidate_relaxed(VALUE_PAIR *filter, VALUE_PAIR *list);
 VALUE_PAIR     *paircopyvp(TALLOC_CTX *ctx, VALUE_PAIR const *vp);
 VALUE_PAIR     *paircopyvpdata(TALLOC_CTX *ctx, DICT_ATTR const *da, VALUE_PAIR const *vp);
 VALUE_PAIR     *paircopy(TALLOC_CTX *ctx, VALUE_PAIR *from);
@@ -604,9 +627,9 @@ void                pairsprintf(VALUE_PAIR *vp, char const * fmt, ...)
 void           pairmove(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from);
 void           pairfilter(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from,
                           unsigned int attr, unsigned int vendor, int8_t tag);
+VALUE_PAIR     *pairmake_ip(TALLOC_CTX *ctx, char const *value,
+                            DICT_ATTR *ipv4, DICT_ATTR *ipv6, DICT_ATTR *ipv4_prefix, DICT_ATTR *ipv6_prefix);
 bool           pairparsevalue(VALUE_PAIR *vp, char const *value);
-VALUE_PAIR     *pairmake_ip(TALLOC_CTX *ctx, char const *value, DICT_ATTR *ipv4, DICT_ATTR *ipv6,
-                            DICT_ATTR *ipv4_prefix, DICT_ATTR *ipv6_prefix);
 VALUE_PAIR     *pairmake(TALLOC_CTX *ctx, VALUE_PAIR **vps, char const *attribute, char const *value, FR_TOKEN op);
 int            pairmark_xlat(VALUE_PAIR *vp, char const *value);
 FR_TOKEN       pairread(char const **ptr, VALUE_PAIR_RAW *raw);
@@ -626,6 +649,7 @@ void                fr_perror(char const *, ...)
                __attribute__ ((format (printf, 1, 2)))
 #endif
 ;
+
 extern bool fr_assert_cond(char const *file, int line, char const *expr, bool cond);
 #define fr_assert(_x) fr_assert_cond(__FILE__,  __LINE__, #_x, (_x))
 
index bb36e5a..e55f519 100644 (file)
@@ -196,24 +196,23 @@ int pair2unknown(VALUE_PAIR *vp)
 
        return 0;
 }
-
-/** Find the pair with the matching attribute
+/** Find the pair with the matching DAs
  *
- * @todo should take DAs and do a pointer comparison.
  */
-VALUE_PAIR *pairfind(VALUE_PAIR *vp, unsigned int attr, unsigned int vendor,
-                    int8_t tag)
+VALUE_PAIR *pairfind_da(VALUE_PAIR *vp, DICT_ATTR const *da, int8_t tag)
 {
        vp_cursor_t     cursor;
        VALUE_PAIR      *i;
 
+       if(!fr_assert(da)) {
+                return NULL;
+       }
+
        for (i = fr_cursor_init(&cursor, &vp);
             i;
             i = fr_cursor_next(&cursor)) {
                VERIFY_VP(i);
-               if ((i->da->attr == attr) && (i->da->vendor == vendor)
-                   && ((tag == TAG_ANY) || (i->da->flags.has_tag &&
-                       (i->tag == tag)))) {
+               if ((i->da == da) && (!i->da->flags.has_tag || (tag == TAG_ANY) || (i->tag == tag))) {
                        return i;
                }
        }
@@ -221,23 +220,22 @@ VALUE_PAIR *pairfind(VALUE_PAIR *vp, unsigned int attr, unsigned int vendor,
        return NULL;
 }
 
-/** Find the pair with the matching DAs
+
+/** Find the pair with the matching attribute
  *
+ * @todo should take DAs and do a pointer comparison.
  */
-VALUE_PAIR *pairfind_da(VALUE_PAIR *vp, DICT_ATTR const *da, int8_t tag)
+VALUE_PAIR *pairfind(VALUE_PAIR *vp, unsigned int attr, unsigned int vendor, int8_t tag)
 {
        vp_cursor_t     cursor;
        VALUE_PAIR      *i;
 
-       if(!fr_assert(da)) {
-                return NULL;
-       }
-
        for (i = fr_cursor_init(&cursor, &vp);
             i;
             i = fr_cursor_next(&cursor)) {
                VERIFY_VP(i);
-               if ((i->da == da) && (!i->da->flags.has_tag || (tag == TAG_ANY) || (i->tag == tag))) {
+               if ((i->da->attr == attr) && (i->da->vendor == vendor) && \
+                   (!i->da->flags.has_tag || (tag == TAG_ANY) || (i->tag == tag))) {
                        return i;
                }
        }
@@ -361,6 +359,44 @@ void pairreplace(VALUE_PAIR **first, VALUE_PAIR *replace)
        *prev = replace;
 }
 
+int8_t attrcmp(VALUE_PAIR const *a, VALUE_PAIR const *b)
+{
+       VERIFY_VP(a);
+       VERIFY_VP(b);
+
+       if (a->da < b->da) {
+               return -1;
+       }
+
+       if (a->da == b->da) {
+               return 0;
+       }
+
+       return 1;
+}
+
+int8_t attrtagcmp(VALUE_PAIR const *a, VALUE_PAIR const *b)
+{
+       VERIFY_VP(a);
+       VERIFY_VP(b);
+
+       uint8_t cmp;
+
+       cmp = attrcmp(a, b);
+
+       if (cmp != 0) return cmp;
+
+       if (a->tag < b->tag) {
+               return -1;
+       }
+
+       if (a->tag > b->tag) {
+               return 1;
+       }
+
+       return 0;
+}
+
 static void pairsort_split(VALUE_PAIR *source, VALUE_PAIR **front, VALUE_PAIR **back)
 {
        VALUE_PAIR *fast;
@@ -396,7 +432,7 @@ static void pairsort_split(VALUE_PAIR *source, VALUE_PAIR **front, VALUE_PAIR **
        slow->next = NULL;
 }
 
-static VALUE_PAIR *pairsort_merge(VALUE_PAIR *a, VALUE_PAIR *b, bool with_tag)
+static VALUE_PAIR *pairsort_merge(VALUE_PAIR *a, VALUE_PAIR *b, fr_pair_cmp_t cmp)
 {
        VALUE_PAIR *result = NULL;
 
@@ -406,12 +442,12 @@ static VALUE_PAIR *pairsort_merge(VALUE_PAIR *a, VALUE_PAIR *b, bool with_tag)
        /*
         *      Compare the DICT_ATTRs and tags
         */
-       if ((with_tag && (a->tag < b->tag)) || (a->da <= b->da)) {
+       if (cmp(a, b) <= 0) {
                result = a;
-               result->next = pairsort_merge(a->next, b, with_tag);
+               result->next = pairsort_merge(a->next, b, cmp);
        } else {
                result = b;
-               result->next = pairsort_merge(a, b->next, with_tag);
+               result->next = pairsort_merge(a, b->next, cmp);
        }
 
        return result;
@@ -420,9 +456,9 @@ static VALUE_PAIR *pairsort_merge(VALUE_PAIR *a, VALUE_PAIR *b, bool with_tag)
 /** Sort a linked list of VALUE_PAIRs using merge sort
  *
  * @param[in,out] vps List of VALUE_PAIRs to sort.
- * @param[in] with_tag sort by tag then by DICT_ATTR
+ * @param[in] cmp to sort with
  */
-void pairsort(VALUE_PAIR **vps, bool with_tag)
+void pairsort(VALUE_PAIR **vps, fr_pair_cmp_t cmp)
 {
        VALUE_PAIR *head = *vps;
        VALUE_PAIR *a;
@@ -436,13 +472,13 @@ void pairsort(VALUE_PAIR **vps, bool with_tag)
        }
 
        pairsort_split(head, &a, &b);   /* Split into sublists */
-       pairsort(&a, with_tag);         /* Traverse left */
-       pairsort(&b, with_tag);         /* Traverse right */
+       pairsort(&a, cmp);              /* Traverse left */
+       pairsort(&b, cmp);              /* Traverse right */
 
        /*
         *      merge the two sorted lists together
         */
-       *vps = pairsort_merge(a, b, with_tag);
+       *vps = pairsort_merge(a, b, cmp);
 }
 
 /** Uses paircmp to verify all VALUE_PAIRs in list match the filter defined by check
@@ -460,9 +496,6 @@ bool pairvalidate(VALUE_PAIR *filter, VALUE_PAIR *list)
        if (!filter && !list) {
                return true;
        }
-       if (!filter || !list) {
-               return false;
-       }
 
        /*
         *      This allows us to verify the sets of validate and reply are equal
@@ -470,8 +503,8 @@ bool pairvalidate(VALUE_PAIR *filter, VALUE_PAIR *list)
         *
         *      @todo this should be removed one we have sets and lists
         */
-       pairsort(&filter, true);
-       pairsort(&list, true);
+       pairsort(&filter, attrtagcmp);
+       pairsort(&list, attrtagcmp);
 
        match = fr_cursor_init(&list_cursor, &list);
        check = fr_cursor_init(&filter_cursor, &filter);
@@ -514,6 +547,73 @@ bool pairvalidate(VALUE_PAIR *filter, VALUE_PAIR *list)
        return true;
 }
 
+/** Uses paircmp to verify all VALUE_PAIRs in list match the filter defined by check
+ *
+ * @param filter attributes to check list against.
+ * @param list attributes, probably a request or reply
+ */
+bool pairvalidate_relaxed(VALUE_PAIR *filter, VALUE_PAIR *list)
+{
+       vp_cursor_t filter_cursor;
+       vp_cursor_t list_cursor;
+
+       VALUE_PAIR *check, *match, *last_check = NULL, *last_match;
+
+       if (!filter && !list) {
+               return true;
+       }
+
+       /*
+        *      This allows us to verify the sets of validate and reply are equal
+        *      i.e. we have a validate rule which matches every reply attribute.
+        *
+        *      @todo this should be removed one we have sets and lists
+        */
+       pairsort(&filter, attrtagcmp);
+       pairsort(&list, attrtagcmp);
+
+       fr_cursor_init(&list_cursor, &list);
+       for (check = fr_cursor_init(&filter_cursor, &filter);
+            check;
+            check = fr_cursor_next(&filter_cursor)) {
+               /*
+                *      Were processing check attributes of a new type.
+                */
+               if (!attribute_eq(last_check, check)) {
+                       /*
+                        *      Record the start of the matching attributes in the pair list
+                        *      For every other operator we require the match to be present
+                        */
+                       last_match = fr_cursor_next_by_da(&list_cursor, check->da, check->tag);
+                       if (!last_match) {
+                               if (check->op == T_OP_CMP_FALSE) {
+                                       continue;
+                               }
+                               return false;
+                       }
+
+                       fr_cursor_init(&list_cursor, &last_match);
+                       last_check = check;
+               }
+
+               /*
+                *      Now iterate over all attributes of the same type.
+                */
+               for (match = fr_cursor_first(&list_cursor);
+                    attribute_eq(match, check);
+                    match = fr_cursor_next(&list_cursor)) {
+                       /*
+                        *      This attribute passed the filter
+                        */
+                       if (!paircmp(check, match)) {
+                               return false;
+                       }
+               }
+       }
+
+       return true;
+}
+
 /** Copy a single valuepair
  *
  * Allocate a new valuepair and copy the da from the old vp.
@@ -681,6 +781,36 @@ VALUE_PAIR *paircopyvpdata(TALLOC_CTX *ctx, DICT_ATTR const *da, VALUE_PAIR cons
 }
 
 
+/** Copy a pairlist.
+ *
+ * Copy all pairs from 'from' regardless of tag, attribute or vendor.
+ *
+ * @param[in] ctx for new VALUE_PAIRs to be allocated in.
+ * @param[in] from whence to copy VALUE_PAIRs.
+ * @return the head of the new VALUE_PAIR list or NULL on error.
+ */
+VALUE_PAIR *paircopy(TALLOC_CTX *ctx, VALUE_PAIR *from)
+{
+       vp_cursor_t src, dst;
+
+       VALUE_PAIR *out = NULL, *vp;
+
+       fr_cursor_init(&dst, &out);
+       for (vp = fr_cursor_init(&src, &from);
+            vp;
+            vp = fr_cursor_next(&src)) {
+               VERIFY_VP(vp);
+               vp = paircopyvp(ctx, vp);
+               if (!vp) {
+                       pairfree(&out);
+                       return NULL;
+               }
+               fr_cursor_insert(&dst, vp); /* paircopy sets next pointer to NULL */
+       }
+
+       return out;
+}
+
 /** Copy matching pairs
  *
  * Copy pairs of a matching attribute number, vendor number and tag from the
@@ -725,35 +855,23 @@ VALUE_PAIR *paircopy2(TALLOC_CTX *ctx, VALUE_PAIR *from,
        return out;
 }
 
-
-/** Copy a pairlist.
- *
- * Copy all pairs from 'from' regardless of tag, attribute or vendor.
+/** Steal all members of a VALUE_PAIR list
  *
- * @param[in] ctx for new VALUE_PAIRs to be allocated in.
- * @param[in] from whence to copy VALUE_PAIRs.
- * @return the head of the new VALUE_PAIR list or NULL on error.
+ * @param[in] ctx to move VALUE_PAIRs into
+ * @param[in] from VALUE_PAIRs to move into the new context.
  */
-VALUE_PAIR *paircopy(TALLOC_CTX *ctx, VALUE_PAIR *from)
+VALUE_PAIR *pairsteal(TALLOC_CTX *ctx, VALUE_PAIR *from)
 {
-       vp_cursor_t src, dst;
-
-       VALUE_PAIR *out = NULL, *vp;
+       vp_cursor_t cursor;
+       VALUE_PAIR *vp;
 
-       fr_cursor_init(&dst, &out);
-       for (vp = fr_cursor_init(&src, &from);
+       for (vp = fr_cursor_init(&cursor, &from);
             vp;
-            vp = fr_cursor_next(&src)) {
-               VERIFY_VP(vp);
-               vp = paircopyvp(ctx, vp);
-               if (!vp) {
-                       pairfree(&out);
-                       return NULL;
-               }
-               fr_cursor_insert(&dst, vp); /* paircopy sets next pointer to NULL */
+            vp = fr_cursor_next(&cursor)) {
+               (void) talloc_steal(ctx, vp);
        }
 
-       return out;
+       return from;
 }
 
 /** Move pairs from source list to destination list respecting operator
@@ -1622,6 +1740,7 @@ finish:
        return vp;
 }
 
+
 /** Create a valuepair from an ASCII attribute and value
  *
  * Where the attribute name is in the form:
@@ -2205,13 +2324,144 @@ VALUE_PAIR *readvp2(TALLOC_CTX *ctx, FILE *fp, bool *pfiledone, char const *errp
        return list;
 }
 
+/** Compare two attribute values
+ *
+ * @param[in] one the first attribute.
+ * @param[in] two the second attribute.
+ * @return -1 if one is less than two, 0 if both are equal, 1 if one is more than two, < -1 on error.
+ */
+int8_t paircmp_value(VALUE_PAIR const *one, VALUE_PAIR const *two)
+{
+       int64_t compare = 0;
+
+       VERIFY_VP(one);
+       VERIFY_VP(two);
+
+       if (one->da->type != two->da->type) {
+               fr_strerror_printf("Can't compare attribute values of different types");
+               return -2;
+       }
+
+       /*
+        *      After doing the previous check for special comparisons,
+        *      do the per-type comparison here.
+        */
+       switch (one->da->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(one->vp_octets, two->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 = one->length - two->length;
+       }
+               break;
+
+       case PW_TYPE_STRING:
+               fr_assert(one->vp_strvalue);
+               fr_assert(two->vp_strvalue);
+               compare = strcmp(one->vp_strvalue, two->vp_strvalue);
+               break;
+
+       case PW_TYPE_BYTE:
+       case PW_TYPE_SHORT:
+       case PW_TYPE_INTEGER:
+       case PW_TYPE_DATE:
+               compare = (int64_t) one->vp_integer - (int64_t) two->vp_integer;
+               break;
+
+       case PW_TYPE_SIGNED:
+               compare = one->vp_signed - two->vp_signed;
+               break;
+
+       case PW_TYPE_INTEGER64:
+               /*
+                *      Don't want integer overflow!
+                */
+               if (one->vp_integer64 < two->vp_integer64) {
+                       compare = -1;
+               } else if (one->vp_integer64 > two->vp_integer64) {
+                       compare = 1;
+               }
+               break;
+
+       case PW_TYPE_ETHERNET:
+               compare = memcmp(&one->vp_ether, &two->vp_ether, sizeof(one->vp_ether));
+               break;
+
+       case PW_TYPE_IPADDR:
+               compare = (int64_t) ntohl(one->vp_ipaddr) - (int64_t) ntohl(two->vp_ipaddr);
+               break;
+
+       case PW_TYPE_IPV6ADDR:
+               compare = memcmp(&one->vp_ipv6addr, &two->vp_ipv6addr, sizeof(one->vp_ipv6addr));
+               break;
+
+       case PW_TYPE_IPV6PREFIX:
+               compare = memcmp(&one->vp_ipv6prefix, &two->vp_ipv6prefix, sizeof(one->vp_ipv6prefix));
+               break;
+
+       case PW_TYPE_IPV4PREFIX:
+               compare = memcmp(&one->vp_ipv4prefix, &two->vp_ipv4prefix, sizeof(one->vp_ipv4prefix));
+               break;
+
+       case PW_TYPE_IFID:
+               compare = memcmp(&one->vp_ifid, &two->vp_ifid, sizeof(one->vp_ifid));
+               break;
+
+       /*
+        *      None of the types below should be in the REQUEST
+        */
+       case PW_TYPE_COMBO_IP:          /* This should of been converted into IPADDR/IPV6ADDR */
+       case PW_TYPE_TLV:
+       case PW_TYPE_EXTENDED:
+       case PW_TYPE_LONG_EXTENDED:
+       case PW_TYPE_EVS:
+       case PW_TYPE_VSA:
+       case PW_TYPE_INVALID:           /* We should never see these */
+       case PW_TYPE_MAX:
+               fr_assert(0);   /* unknown type */
+               return -2;
+
+       /*
+        *      Do NOT add a default here, as new types are added
+        *      static analysis will warn us they're not handled
+        */
+       }
+
+       if (compare > 0) {
+               return 1;
+       } else if (compare < 0) {
+               return -1;
+       }
+       return 0;
+}
+
 /*
  *     We leverage the fact that IPv4 and IPv6 prefixes both
  *     have the same format:
  *
  *     reserved, prefix-len, data...
  */
-static int paircmp_cidr(FR_TOKEN op, int bytes, uint8_t const *one, uint8_t const *two)
+static int paircmp_op_cidr(FR_TOKEN op, int bytes,
+                          uint8_t one_net, uint8_t const *one,
+                          uint8_t two_net, uint8_t const *two)
 {
        int i, common;
        uint32_t mask;
@@ -2219,10 +2469,10 @@ static int paircmp_cidr(FR_TOKEN op, int bytes, uint8_t const *one, uint8_t cons
        /*
         *      Handle the case of netmasks being identical.
         */
-       if (one[1] == two[1]) {
+       if (one_net == two_net) {
                int compare;
 
-               compare = memcmp(one + 2, two + 2, bytes);
+               compare = memcmp(one, two, bytes);
 
                /*
                 *      If they're identical return true for
@@ -2258,14 +2508,14 @@ static int paircmp_cidr(FR_TOKEN op, int bytes, uint8_t const *one, uint8_t cons
 
        case T_OP_LE:
        case T_OP_LT:   /* 192/8 < 192.168/16 --> false */
-               if (one[1] < two[1]) {
+               if (one_net < two_net) {
                        return false;
                }
                break;
 
        case T_OP_GE:
        case T_OP_GT:   /* 192/16 > 192.168/8 --> false */
-               if (one[1] > two[1]) {
+               if (one_net > two_net) {
                        return false;
                }
                break;
@@ -2274,10 +2524,10 @@ static int paircmp_cidr(FR_TOKEN op, int bytes, uint8_t const *one, uint8_t cons
                return false;
        }
 
-       if (one[1] < two[1]) {
-               common = one[1];
+       if (one_net < two_net) {
+               common = one_net;
        } else {
-               common = two[1];
+               common = two_net;
        }
 
        /*
@@ -2285,8 +2535,8 @@ static int paircmp_cidr(FR_TOKEN op, int bytes, uint8_t const *one, uint8_t cons
         *      identical, it MAY be a match.  If they're different,
         *      it is NOT a match.
         */
-       i = 2;
-       while (i < (2 + bytes)) {
+       i = 0;
+       while (i < bytes) {
                /*
                 *      All leading bytes are identical.
                 */
@@ -2316,33 +2566,148 @@ static int paircmp_cidr(FR_TOKEN op, int bytes, uint8_t const *one, uint8_t cons
        return false;
 }
 
-/*
- *     Compare two pairs, using the operator from "one".
+/** Compare two attributes using an operator
+ *
+ * @param[in] a the first attribute
+ * @param[in] op the operator for comparison.
+ * @param[in] b the second attribute
+ * @return 1 if true, 0 if false, -1 on error.
+ */
+int8_t paircmp_op(VALUE_PAIR const *a, FR_TOKEN op, VALUE_PAIR const *b)
+{
+       int compare;
+
+       switch (a->da->type) {
+       case PW_TYPE_IPADDR:
+               switch (b->da->type) {
+               case PW_TYPE_IPADDR:            /* IPv4 and IPv4 */
+                       goto cmp;
+
+               case PW_TYPE_IPV4PREFIX:        /* IPv4 and IPv4 Prefix */
+                       return paircmp_op_cidr(op, 4, 32, (uint8_t const *) &a->vp_ipaddr,
+                                              b->vp_ipv4prefix[1], (uint8_t const *) &b->vp_ipv4prefix + 2);
+
+               default:
+                       fr_strerror_printf("Cannot compare IPv4 with IPv6 address");
+                       return -1;
+               }
+               break;
+
+       case PW_TYPE_IPV4PREFIX:                /* IPv4 and IPv4 Prefix */
+               switch (b->da->type) {
+               case PW_TYPE_IPADDR:
+                       return paircmp_op_cidr(op, 4, a->vp_ipv4prefix[1],
+                                              (uint8_t const *) &a->vp_ipv4prefix + 2,
+                                              32, (uint8_t const *) &b->vp_ipaddr);
+
+               case PW_TYPE_IPV4PREFIX:        /* IPv4 Prefix and IPv4 Prefix */
+                       return paircmp_op_cidr(op, 4, a->vp_ipv4prefix[1],
+                                              (uint8_t const *) &a->vp_ipv4prefix + 2,
+                                              b->vp_ipv4prefix[1], (uint8_t const *) &b->vp_ipv4prefix + 2);
+
+               default:
+                       fr_strerror_printf("Cannot compare IPv4 with IPv6 address");
+                       return -1;
+               }
+               break;
+
+       case PW_TYPE_IPV6ADDR:
+               switch (b->da->type) {
+               case PW_TYPE_IPV6ADDR:          /* IPv6 and IPv6 */
+                       goto cmp;
+
+               case PW_TYPE_IPV6PREFIX:        /* IPv6 and IPv6 Preifx */
+                       return paircmp_op_cidr(op, 16, 128, (uint8_t const *) &a->vp_ipv6addr,
+                                              b->vp_ipv6prefix[1], (uint8_t const *) &b->vp_ipv6prefix + 2);
+                       break;
+
+               default:
+                       fr_strerror_printf("Cannot compare IPv6 with IPv4 address");
+                       return -1;
+               }
+               break;
+
+       case PW_TYPE_IPV6PREFIX:
+               switch (b->da->type) {
+               case PW_TYPE_IPV6ADDR:          /* IPv6 Prefix and IPv6 */
+                       return paircmp_op_cidr(op, 16, a->vp_ipv6prefix[1],
+                                              (uint8_t const *) &a->vp_ipv6prefix + 2,
+                                              128, (uint8_t const *) &b->vp_ipv6addr);
+
+               case PW_TYPE_IPV6PREFIX:        /* IPv6 Prefix and IPv6 */
+                       return paircmp_op_cidr(op, 16, a->vp_ipv6prefix[1],
+                                              (uint8_t const *) &a->vp_ipv6prefix + 2,
+                                              b->vp_ipv6prefix[1], (uint8_t const *) &b->vp_ipv6prefix + 2);
+
+               default:
+                       fr_strerror_printf("Cannot compare IPv6 with IPv4 address");
+                       return -1;
+               }
+               break;
+
+       default:
+       cmp:
+               compare = paircmp_value(a, b);
+               if (compare < -1) {     /* comparison error */
+                       return -1;
+               }
+       }
+
+       /*
+        *      Now do the operator comparison.
+        */
+       switch (op) {
+       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;
+       }
+}
+
+/** Compare two pairs, using the operator from "a"
  *
  *     i.e. given two attributes, it does:
  *
- *     (two->data) (one->operator) (one->data)
+ *     (b->data) (a->operator) (a->data)
  *
  *     e.g. "foo" != "bar"
  *
- *     Returns true (comparison is true), or false (comparison is not true);
+ * @param[in] a the first attribute
+ * @param[in] b the second attribute
+ * @return 1 if true, 0 if false, -1 on error.
  */
-int paircmp(VALUE_PAIR *one, VALUE_PAIR *two)
+int8_t paircmp(VALUE_PAIR *a, VALUE_PAIR *b)
 {
        int compare;
 
-       VERIFY_VP(one);
-       VERIFY_VP(two);
+       VERIFY_VP(a);
+       VERIFY_VP(b);
 
-       switch (one->op) {
+       switch (a->op) {
        case T_OP_CMP_TRUE:
-               return (two != NULL);
+               return (b != NULL);
 
        case T_OP_CMP_FALSE:
-               return (two == NULL);
+               return (b == NULL);
 
                /*
-                *      One is a regex, compile it, print two to a string,
+                *      a is a regex, compile it, print b to a string,
                 *      and then do string comparisons.
                 */
        case T_OP_REG_EQ:
@@ -2354,15 +2719,15 @@ int paircmp(VALUE_PAIR *one, VALUE_PAIR *two)
                        regex_t reg;
                        char buffer[MAX_STRING_LEN * 4 + 1];
 
-                       compare = regcomp(&reg, one->vp_strvalue, REG_EXTENDED);
+                       compare = regcomp(&reg, a->vp_strvalue, REG_EXTENDED);
                        if (compare != 0) {
                                regerror(compare, &reg, buffer, sizeof(buffer));
                                fr_strerror_printf("Illegal regular expression in attribute: %s: %s",
-                                          one->da->name, buffer);
+                                                  a->da->name, buffer);
                                return -1;
                        }
 
-                       vp_prints_value(buffer, sizeof(buffer), two, 0);
+                       vp_prints_value(buffer, sizeof(buffer), b, 0);
 
                        /*
                         *      Don't care about substring matches,
@@ -2371,7 +2736,10 @@ int paircmp(VALUE_PAIR *one, VALUE_PAIR *two)
                        compare = regexec(&reg, buffer, 0, NULL, 0);
 
                        regfree(&reg);
-                       if (one->op == T_OP_REG_EQ) return (compare == 0);
+                       if (a->op == T_OP_REG_EQ) {
+                               return (compare == 0);
+                       }
+
                        return (compare != 0);
                }
 #endif
@@ -2380,183 +2748,62 @@ int paircmp(VALUE_PAIR *one, VALUE_PAIR *two)
                break;
        }
 
-       return paircmp_op(two, one->op, one);
+       return paircmp_op(b, a->op, a);
 }
 
-/* Compare two attributes
+/** Determine equality of two lists
  *
- * @param[in] one the first attribute
- * @param[in] op the operator for comparison
- * @param[in] two the second attribute
- * @return true (comparison is true), or false (comparison is not true);
+ * This is useful for comparing lists of attributes inserted into a binary tree.
+ *
+ * @param a first list of VALUE_PAIRs.
+ * @param b second list of VALUE_PAIRs.
+ * @return -1 if a < b, 0 if the two lists are equal, 1 if a > b, -2 on error.
  */
-int paircmp_op(VALUE_PAIR const *one, FR_TOKEN op, VALUE_PAIR const *two)
+int8_t pairlistcmp(VALUE_PAIR *a, VALUE_PAIR *b)
 {
-       int compare = 0;
-
-       VERIFY_VP(one);
-       VERIFY_VP(two);
-
-       /*
-        *      Can't compare two attributes of differing types
-        *
-        *      FIXME: maybe do checks for IP OP IP/mask ??
-        */
-       if (one->da->type != two->da->type) {
-               return one->da->type - two->da->type;
-       }
-
-       /*
-        *      After doing the previous check for special comparisons,
-        *      do the per-type comparison here.
-        */
-       switch (one->da->type) {
-       case PW_TYPE_ABINARY:
-       case PW_TYPE_OCTETS:
-       {
-               size_t length;
-
-               if (one->length > two->length) {
-                       length = one->length;
-               } else {
-                       length = two->length;
-               }
+       vp_cursor_t a_cursor, b_cursor;
+       VALUE_PAIR *a_p, *b_p;
+       int ret;
+
+       for (a_p = fr_cursor_init(&a_cursor, &a), b_p = fr_cursor_init(&b_cursor, &b);
+            a_p && b_p;
+            a_p = fr_cursor_next(&a_cursor), b_p = fr_cursor_next(&b_cursor)) {
+               /* Same VP, no point doing expensive checks */
+               if (a_p == b_p) {
+                       continue;
+               }
 
-               if (length) {
-                       compare = memcmp(one->vp_octets, two->vp_octets,
-                                        length);
-                       if (compare != 0) break;
+               if (a_p->da < b_p->da) {
+                       return -1;
                }
-
-               /*
-                *      Contents are the same.  The return code
-                *      is therefore the difference in lengths.
-                *
-                *      i.e. "0x00" is smaller than "0x0000"
-                */
-               compare = one->length - two->length;
-       }
-               break;
-
-       case PW_TYPE_STRING:
-               compare = strcmp(one->vp_strvalue, two->vp_strvalue);
-               break;
-
-       case PW_TYPE_BYTE:
-       case PW_TYPE_SHORT:
-       case PW_TYPE_INTEGER:
-       case PW_TYPE_DATE:
-               if (one->vp_integer < two->vp_integer) {
-                       compare = -1;
-               } else if (one->vp_integer == two->vp_integer) {
-                       compare = 0;
-               } else {
-                       compare = +1;
+               if (a_p->da > b_p->da) {
+                       return 1;
                }
-               break;
 
-       case PW_TYPE_SIGNED:
-               if (one->vp_signed < two->vp_signed) {
-                       compare = -1;
-               } else if (one->vp_signed == two->vp_signed) {
-                       compare = 0;
-               } else {
-                       compare = +1;
+               if (a_p->tag < b_p->tag) {
+                       return -1;
                }
-               break;
-
-       case PW_TYPE_INTEGER64:
-               /*
-                *      Don't want integer overflow!
-                */
-               if (one->vp_integer64 < two->vp_integer64) {
-                       compare = -1;
-               } else if (one->vp_integer64 > two->vp_integer64) {
-                       compare = +1;
-               } else {
-                       compare = 0;
+               if (a_p->tag > b_p->tag) {
+                       return 1;
                }
-               break;
 
-       case PW_TYPE_ETHERNET:
-               compare = memcmp(&one->vp_ether, &two->vp_ether,
-                                sizeof(one->vp_ether));
-               break;
-
-       case PW_TYPE_IPADDR:
-               if (ntohl(one->vp_ipaddr) < ntohl(two->vp_ipaddr)) {
-                       compare = -1;
-               } else if (one->vp_ipaddr  == two->vp_ipaddr) {
-                       compare = 0;
-               } else {
-                       compare = +1;
+               ret = paircmp_value(a_p, b_p);
+               if (ret != 0) {
+                       fr_assert(ret >= -1);   /* Comparison error */
+                       return ret;
                }
-               break;
-
-       case PW_TYPE_IPV6ADDR:
-               compare = memcmp(&one->vp_ipv6addr, &two->vp_ipv6addr,
-                                sizeof(one->vp_ipv6addr));
-               break;
-
-       case PW_TYPE_IPV6PREFIX:
-               return paircmp_cidr(op, 16,
-                                   (uint8_t const *) &one->vp_ipv6prefix,
-                                   (uint8_t const *) &two->vp_ipv6prefix);
-
-       case PW_TYPE_IPV4PREFIX:
-               return paircmp_cidr(op, 4,
-                                   (uint8_t const *) &one->vp_ipv4prefix,
-                                   (uint8_t const *) &two->vp_ipv4prefix);
-
-       case PW_TYPE_IFID:
-               compare = memcmp(&one->vp_ifid, &two->vp_ifid, sizeof(one->vp_ifid));
-               break;
+       }
 
-       /*
-        *      None of the types below should be in the REQUEST
-        */
-       case PW_TYPE_COMBO_IP:          /* This should of been converted into IPADDR/IPV6ADDR */
-       case PW_TYPE_TLV:
-       case PW_TYPE_EXTENDED:
-       case PW_TYPE_LONG_EXTENDED:
-       case PW_TYPE_EVS:
-       case PW_TYPE_VSA:
-       case PW_TYPE_INVALID:           /* We should never see these */
-       case PW_TYPE_MAX:
-               fr_assert(0);   /* unknown type */
+       if (!a_p && !b_p) {
                return 0;
-
-       /*
-        *      Do NOT add a default here, as new types are added
-        *      static analysis will warn us they're not handled
-        */
        }
 
-       /*
-        *      Now do the operator comparison.
-        */
-       switch (op) {
-       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;
+       if (!a_p) {
+               return -1;
        }
+
+       /* if(!b_p) */
+       return 1;
 }
 
 /** Set the type of the VALUE_PAIR value buffer to match it's DICT_ATTR
index c209f04..737c333 100644 (file)
@@ -303,7 +303,7 @@ static void got_packet(UNUSED uint8_t *args, struct pcap_pkthdr const*header, ui
                DEBUG(log_dst, "\n");
                if (packet->vps) {
                        if (do_sort) {
-                               pairsort(&packet->vps, true);
+                               pairsort(&packet->vps, attrtagcmp);
                        }
                        vp_printlist(log_dst, packet->vps);
                        pairfree(&packet->vps);
index 611ed6d..b1a92c6 100644 (file)
@@ -878,7 +878,7 @@ static void rest_request_init(REQUEST *request, rlm_rest_request_t *ctx, bool so
         *      Sorts pairs in place, oh well...
         */
        if (sort) {
-               pairsort(&request->packet->vps, true);
+               pairsort(&request->packet->vps, attrtagcmp);
        }
        fr_cursor_init(&ctx->cursor, &request->packet->vps);
 }