# endif
#endif
-#define attribute_eq(_x, _y) ((_x && _y) && (_x->da == _y->da) && (_x->tag == _y->tag))
+
/** Free a VALUE_PAIR
*
}
#ifndef NDEBUG
- vp->vp_integer = FREE_MAGIC;
+ vp->vp_integer = 0xf4eef4ee;
#endif
#ifdef TALLOC_DEBUG
vp->da = da;
vp->op = T_OP_EQ;
+ vp->tag = TAG_ANY;
vp->type = VT_NONE;
vp->length = da->flags.length;
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_EQ(tag, i->tag))) {
return i;
}
}
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;
- }
+ /* List head may be NULL if it contains no VPs */
+ if (!vp) return NULL;
+
+ VERIFY_LIST(vp);
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_EQ(tag, i->tag))) {
return i;
}
}
* @param[in,out] first VP in list.
* @param[in] attr to match.
* @param[in] vendor to match.
- * @param[in] tag to match. TAG_ANY matches any tag, TAG_UNUSED matches tagless VPs.
+ * @param[in] tag to match. TAG_ANY matches any tag, TAG_NONE matches tagless VPs.
*
* @todo should take DAs and do a point comparison.
*/
VERIFY_VP(i);
next = i->next;
if ((i->da->attr == attr) && (i->da->vendor == vendor) &&
- ((tag == TAG_ANY) ||
- (i->da->flags.has_tag && (i->tag == tag)))) {
+ (!i->da->flags.has_tag || TAG_EQ(tag, i->tag))) {
*last = next;
talloc_free(i);
} else {
* Found the first attribute, replace it,
* and return.
*/
- if ((i->da == replace->da) &&
- (!i->da->flags.has_tag || (i->tag == replace->tag))
- ) {
+ if ((i->da == replace->da) && (!i->da->flags.has_tag || TAG_EQ(replace->tag, i->tag))) {
*prev = replace;
/*
*prev = replace;
}
+int8_t attrtagcmp(void const *a, void const *b)
+{
+ VALUE_PAIR const *my_a = a;
+ VALUE_PAIR const *my_b = b;
+
+ VERIFY_VP(my_a);
+ VERIFY_VP(my_b);
+
+ uint8_t cmp;
+
+ cmp = fr_pointer_cmp(my_a->da, my_b->da);
+
+ if (cmp != 0) return cmp;
+
+ if (my_a->tag < my_b->tag) {
+ return -1;
+ }
+
+ if (my_a->tag > my_b->tag) {
+ return 1;
+ }
+
+ return 0;
+}
+
static void pairsort_split(VALUE_PAIR *source, VALUE_PAIR **front, VALUE_PAIR **back)
{
VALUE_PAIR *fast;
* Stopping condition - no more elements left to split
*/
if (!source || !source->next) {
- *front = source;
- *back = NULL;
+ *front = source;
+ *back = NULL;
- return;
- }
+ return;
+ }
/*
* Fast advances twice as fast as slow, so when it gets to the end,
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_cmp_t cmp)
{
VALUE_PAIR *result = NULL;
if (!a) return b;
if (!b) return a;
- /*
- * Compare the DICT_ATTRs and tags
- */
- if ((with_tag && (a->tag < b->tag)) || (a->da <= b->da)) {
+ /*
+ * Compare the DICT_ATTRs and tags
+ */
+ if (cmp(a, b) <= 0) {
result = a;
- result->next = pairsort_merge(a->next, b, with_tag);
- } else {
+ 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;
/** 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_cmp_t cmp)
{
VALUE_PAIR *head = *vps;
VALUE_PAIR *a;
}
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, cmp);
+}
+
+/** Write an error to the library errorbuff detailing the mismatch
+ *
+ * Retrieve output with fr_strerror();
+ *
+ * @todo add thread specific talloc contexts.
+ *
+ * @param ctx a hack until we have thread specific talloc contexts.
+ * @param failed pair of attributes which didn't match.
+ */
+void pairvalidate_debug(TALLOC_CTX *ctx, VALUE_PAIR const *failed[2])
+{
+ VALUE_PAIR const *filter = failed[0];
+ VALUE_PAIR const *list = failed[1];
+
+ char *value, *pair;
+
+ (void) fr_strerror(); /* Clear any existing messages */
+
+ if (!fr_assert(!(!filter && !list))) return;
+
+ if (!list) {
+ if (!filter) return;
+ fr_strerror_printf("Attribute \"%s\" not found in list", filter->da->name);
+ return;
+ }
+
+ if (!filter || (filter->da != list->da)) {
+ fr_strerror_printf("Attribute \"%s\" not found in filter", list->da->name);
+ return;
+ }
+
+ if (!TAG_EQ(filter->tag, list->tag)) {
+ fr_strerror_printf("Attribute \"%s\" tag \"%i\" didn't match filter tag \"%i\"",
+ list->da->name, list->tag, filter->tag);
+ return;
+ }
+
+ pair = vp_aprint(ctx, filter);
+ value = vp_aprint_value(ctx, list);
+
+ fr_strerror_printf("Attribute value \"%s\" didn't match filter \"%s\"", value, pair);
+
+ talloc_free(pair);
+ talloc_free(value);
- /*
- * merge the two sorted lists together
- */
- *vps = pairsort_merge(a, b, with_tag);
+ return;
}
/** Uses paircmp to verify all VALUE_PAIRs in list match the filter defined by check
*
+ * @note will sort both filter and list in place.
+ *
+ * @param failed pointer to an array to write the pointers of the filter/list attributes that didn't match.
+ * May be NULL.
* @param filter attributes to check list against.
* @param list attributes, probably a request or reply
*/
-bool pairvalidate(VALUE_PAIR *filter, VALUE_PAIR *list)
+bool pairvalidate(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list)
{
vp_cursor_t filter_cursor;
vp_cursor_t list_cursor;
if (!filter && !list) {
return true;
}
- if (!filter || !list) {
- return false;
- }
/*
* This allows us to verify the sets of validate and reply are equal
*
* @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);
+ match = fr_cursor_init(&list_cursor, &list);
+ while (match || check) {
+ /*
+ * Lists are of different lengths
+ */
+ if ((!match && check) || (check && !match)) goto mismatch;
- while (true) {
/*
* The lists are sorted, so if the first
* attributes aren't of the same type, then we're
* done.
*/
- if (!attribute_eq(check, match)) {
- return false;
- }
+ if (!ATTRIBUTE_EQ(check, match)) goto mismatch;
/*
* They're of the same type, but don't have the
* Note that the RFCs say that for attributes of
* the same type, order is important.
*/
- if (!paircmp(check, match)) {
- return false;
- }
+ if (paircmp(check, match) != 1) goto mismatch;
- match = fr_cursor_next(&list_cursor);
check = fr_cursor_next(&filter_cursor);
+ match = fr_cursor_next(&list_cursor);
+ }
+
+ return true;
+
+mismatch:
+ if (failed) {
+ failed[0] = check;
+ failed[1] = match;
+ }
+ return false;
+}
+
+/** Uses paircmp to verify all VALUE_PAIRs in list match the filter defined by check
+ *
+ * @note will sort both filter and list in place.
+ *
+ * @param failed pointer to an array to write the pointers of the filter/list attributes that didn't match.
+ * May be NULL.
+ * @param filter attributes to check list against.
+ * @param list attributes, probably a request or reply
+ */
+bool pairvalidate_relaxed(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list)
+{
+ vp_cursor_t filter_cursor;
+ vp_cursor_t list_cursor;
+
+ VALUE_PAIR *check, *last_check = NULL, *match = NULL;
+
+ if (!filter && !list) {
+ return true;
+ }
- if (!match && !check) break;
+ /*
+ * 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)) {
/*
- * One list ended earlier than the others, they
- * didn't match.
+ * Were processing check attributes of a new type.
*/
- if (!match || !check) {
- return false;
+ 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
+ */
+ match = fr_cursor_next_by_da(&list_cursor, check->da, check->tag);
+ if (!match) {
+ if (check->op == T_OP_CMP_FALSE) continue;
+ goto mismatch;
+ }
+
+ fr_cursor_init(&list_cursor, &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)) goto mismatch;
}
}
return true;
+
+mismatch:
+ if (failed) {
+ failed[0] = check;
+ failed[1] = match;
+ }
+ return false;
}
/** Copy a single valuepair
* Now copy the value
*/
if (vp->type == VT_XLAT) {
- n->value.xlat = talloc_strdup(n, n->value.xlat);
+ n->value.xlat = talloc_typed_strdup(n, n->value.xlat);
}
n->da = dict_attr_copy(vp->da, true);
n->next = NULL;
- if ((n->da->type == PW_TYPE_TLV) ||
- (n->da->type == PW_TYPE_OCTETS)) {
- if (n->vp_octets != NULL) {
- n->vp_octets = talloc_memdup(n, vp->vp_octets, n->length);
- }
+ switch (vp->da->type) {
+ case PW_TYPE_TLV:
+ case PW_TYPE_OCTETS:
+ n->vp_octets = NULL; /* else pairmemcpy will free vp's value */
+ pairmemcpy(n, vp->vp_octets, n->length);
+ break;
- } else if (n->da->type == PW_TYPE_STRING) {
- if (n->vp_strvalue != NULL) {
- /*
- * Equivalent to, and faster than strdup.
- */
- n->vp_strvalue = talloc_memdup(n, vp->vp_strvalue, n->length + 1);
- }
+ case PW_TYPE_STRING:
+ n->vp_strvalue = NULL; /* else pairstrnpy will free vp's value */
+ pairstrncpy(n, vp->vp_strvalue, n->length);
+ break;
+
+ default:
+ break;
}
return n;
}
-/** Copy data from one VP to another
- *
- * Allocate a new pair using da, and copy over the value from the specified
- * vp.
+/** Copy a pairlist.
*
- * @todo Should be able to do type conversions.
+ * Copy all pairs from 'from' regardless of tag, attribute or vendor.
*
- * @param[in] ctx for talloc
- * @param[in] da of new attribute to alloc.
- * @param[in] vp to copy data from.
- * @return the new valuepair.
+ * @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 *paircopyvpdata(TALLOC_CTX *ctx, DICT_ATTR const *da, VALUE_PAIR const *vp)
+VALUE_PAIR *paircopy(TALLOC_CTX *ctx, VALUE_PAIR *from)
{
- VALUE_PAIR *n;
-
- if (!vp) return NULL;
-
- VERIFY_VP(vp);
-
- /*
- * The types have to be identical, OR the "from" VP has
- * to be octets.
- */
- if (da->type != vp->da->type) {
- int length;
- uint8_t *p;
- VALUE_PAIR const **pvp;
-
- if (vp->da->type == PW_TYPE_OCTETS) {
- /*
- * Decode the data. It may be wrong!
- */
- if (rad_data2vp(da->attr, da->vendor, vp->vp_octets, vp->length, &n) < 0) {
- return NULL;
- }
-
- n->type = VT_DATA;
- return n;
- }
-
- /*
- * Else the destination type is octets
- */
- switch (vp->da->type) {
- default:
- return NULL; /* can't do it */
-
- case PW_TYPE_INTEGER:
- case PW_TYPE_IPADDR:
- case PW_TYPE_DATE:
- case PW_TYPE_IFID:
- case PW_TYPE_IPV6ADDR:
- case PW_TYPE_IPV6PREFIX:
- case PW_TYPE_BYTE:
- case PW_TYPE_SHORT:
- case PW_TYPE_ETHERNET:
- case PW_TYPE_SIGNED:
- case PW_TYPE_INTEGER64:
- case PW_TYPE_IPV4PREFIX:
- break;
- }
-
- n = pairalloc(ctx, da);
- if (!n) return NULL;
+ vp_cursor_t src, dst;
- p = talloc_array(n, uint8_t, dict_attr_sizes[vp->da->type][1] + 2);
+ VALUE_PAIR *out = NULL, *vp;
- pvp = &vp;
- length = rad_vp2attr(NULL, NULL, NULL, pvp, p, dict_attr_sizes[vp->da->type][1]);
- if (length < 0) {
- pairfree(&n);
+ 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;
}
-
- pairmemcpy(n, p + 2, length - 2);
- talloc_free(p);
- return n;
- }
-
- n = pairalloc(ctx, da);
- if (!n) return NULL;
-
- memcpy(n, vp, sizeof(*n));
- n->da = da;
-
- if (n->type == VT_XLAT) {
- n->value.xlat = talloc_strdup(n, n->value.xlat);
- }
-
- switch (n->da->type) {
- case PW_TYPE_TLV:
- case PW_TYPE_OCTETS:
- if (n->vp_octets != NULL) {
- n->vp_octets = talloc_memdup(n, vp->vp_octets, n->length);
- }
- break;
-
- case PW_TYPE_STRING:
- if (n->vp_strvalue != NULL) {
- n->vp_strvalue = talloc_memdup(n, vp->vp_strvalue, n->length + 1); /* NULL byte */
- }
- break;
- default:
- return NULL;
+ fr_cursor_insert(&dst, vp); /* paircopy sets next pointer to NULL */
}
- n->next = NULL;
-
- return n;
+ return out;
}
-
/** Copy matching pairs
*
* Copy pairs of a matching attribute number, vendor number and tag from the
* @param[in] from whence to copy VALUE_PAIRs.
* @param[in] attr to match, if 0 input list will not be filtered by attr.
* @param[in] vendor to match.
- * @param[in] tag to match, TAG_ANY matches any tag, TAG_UNUSED matches tagless VPs.
+ * @param[in] tag to match, TAG_ANY matches any tag, TAG_NONE matches tagless VPs.
* @return the head of the new VALUE_PAIR list or NULL on error.
*/
VALUE_PAIR *paircopy2(TALLOC_CTX *ctx, VALUE_PAIR *from,
for (vp = fr_cursor_init(&src, &from);
vp;
vp = fr_cursor_next(&src)) {
- VERIFY_VP(vp);
+ VERIFY_VP(vp);
if ((vp->da->attr != attr) || (vp->da->vendor != vendor)) {
continue;
}
- if ((tag != TAG_ANY) && vp->da->flags.has_tag && (vp->tag != tag)) {
+ if (vp->da->flags.has_tag && TAG_EQ(tag, vp->tag)) {
continue;
}
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
* it doesn't already exist.
*/
case T_OP_EQ:
- found = pairfind(*to, i->da->attr, i->da->vendor,
- TAG_ANY);
+ found = pairfind(*to, i->da->attr, i->da->vendor, TAG_ANY);
if (!found) goto do_add;
tail_from = &(i->next);
* of the same vendor/attr which already exists.
*/
case T_OP_SET:
- found = pairfind(*to, i->da->attr, i->da->vendor,
- TAG_ANY);
+ found = pairfind(*to, i->da->attr, i->da->vendor, TAG_ANY);
if (!found) goto do_add;
/*
* Move pairs of a matching attribute number, vendor number and tag from the
* the input list to the output list.
*
+ * @note pairs which are moved have their parent changed to ctx.
+ *
* @note pairfree should be called on the head of the old list to free unmoved
- attributes (if they're no longer needed).
+ attributes (if they're no longer needed).
*
* @param[in] ctx for talloc
* @param[in,out] to destination list.
* @param[in,out] from source list.
- * @param[in] attr to match, if PW_VENDOR_SPECIFIC and vendor 0, only VSAs will
- * be copied. If 0 and 0, all attributes will match
+ * @param[in] attr to match. If attribute PW_VENDOR_SPECIFIC and vendor 0,
+ * will match (and therefore copy) only VSAs.
+ * If attribute 0 and vendor 0 will match (and therefore copy) all
+ * attributes.
* @param[in] vendor to match.
- * @param[in] tag to match, TAG_ANY matches any tag, TAG_UNUSED matches tagless VPs.
+ * @param[in] tag to match, TAG_ANY matches any tag, TAG_NONE matches tagless VPs.
*/
void pairfilter(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from, unsigned int attr, unsigned int vendor, int8_t tag)
{
VERIFY_VP(i);
next = i->next;
- if ((tag != TAG_ANY) && i->da->flags.has_tag &&
- (i->tag != tag)) {
+ if (i->da->flags.has_tag && TAG_EQ(tag, i->tag)) {
continue;
}
}
}
-static char const *hextab = "0123456789abcdef";
+static char const hextab[] = "0123456789abcdef";
-bool pairparsevalue(VALUE_PAIR *vp, char const *value)
+/** Convert string value to native attribute value
+ *
+ * @param vp to assign value to.
+ * @param value string to convert. Binary safe for variable length values if len is provided.
+ * @param inlen may be 0 in which case strlen(len) is used to determine length, else inline
+ * should be the length of the string or sub string to parse.
+ * @return true on success, else false.
+ */
+int pairparsevalue(VALUE_PAIR *vp, char const *value, size_t inlen)
{
- char *p;
- char const *cp, *cs;
- int x;
- uint64_t y;
- size_t length;
DICT_VALUE *dval;
+ size_t len;
+ char buffer[256];
- if (!value) return false;
+ if (!value) return -1;
VERIFY_VP(vp);
/*
goto finish;
}
+ len = (inlen == 0) ? strlen(value) : inlen;
+
+ /*
+ * It's a variable length type so we just alloc a new buffer
+ * of size len and copy.
+ */
switch(vp->da->type) {
case PW_TYPE_STRING:
+ {
+ size_t vp_len;
+ char const *cp;
+ char *p;
+ int x;
+
/*
* Do escaping here
*/
- p = talloc_strdup(vp, value);
- vp->vp_strvalue = p;
- cp = value;
- length = 0;
+ vp->vp_strvalue = p = talloc_memdup(vp, value, len + 1);
+ p[len] = '\0';
+ talloc_set_type(p, char);
+ cp = value;
+ vp_len = 0;
while (*cp) {
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 if (cp[0]) {
- /*
- * \p --> p
- */
- c = *cp++;
- } /* else at EOL \ --> \ */
- }
+ 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 if (cp[0]) {
+ /*
+ * \p --> p
+ */
+ c = *cp++;
+ } /* else at EOL \ --> \ */
}
*p++ = c;
- length++;
+ vp_len++;
}
*p = '\0';
- vp->length = length;
- break;
+ vp->length = vp_len;
+ }
+ goto finish;
+
+ /* raw octets: 0x01020304... */
+ case PW_TYPE_VSA:
+ if (strcmp(value, "ANY") == 0) {
+ vp->length = 0;
+ goto finish;
+ } /* else it's hex */
+
+ case PW_TYPE_OCTETS:
+ {
+ uint8_t *p;
- case PW_TYPE_IPADDR:
/*
- * FIXME: complain if hostname
- * cannot be resolved, or resolve later!
+ * No 0x prefix, just copy verbatim.
*/
- p = NULL;
- {
- fr_ipaddr_t ipaddr;
- char ipv4[16];
+ if ((len < 2) || (strncasecmp(value, "0x", 2) != 0)) {
+ pairmemcpy(vp, (uint8_t const *) value, len);
+ goto finish;
+ }
- /*
- * Convert things which are obviously integers to IP addresses
- *
- * We assume the number is the bigendian representation of the
- * IP address.
- */
- if (fr_integer_check(value)) {
- vp->vp_ipaddr = htonl(atol(value));
- break;
- }
- /*
- * Certain applications/databases print IPv4 addresses with a
- * /32 suffix. Strip it off if the mask is 32, else error out.
- */
- p = strchr(value, '/');
- if (p) {
- if ((p[1] != '3') || (p[2] != '2') || (p[3] != '\0')) {
- fr_strerror_printf("Invalid IP address suffix \"%s\". Only '/32' permitted "
- "for non-prefix types", p);
- return false;
- }
+#ifdef WITH_ASCEND_BINARY
+ do_octets:
+#endif
+ len -= 2;
- strlcpy(ipv4, value, sizeof(ipv4));
- ipv4[p - value] = '\0';
- cs = ipv4;
- } else {
- cs = value;
- }
+ /*
+ * Invalid.
+ */
+ if ((len & 0x01) != 0) {
+ fr_strerror_printf("Length of Hex String is not even, got %zu bytes", vp->length);
+ return -1;
+ }
- if (ip_hton(cs, AF_INET, &ipaddr) < 0) {
- fr_strerror_printf("Failed to find IP address for %s", cs);
- return false;
- }
+ vp->length = len >> 1;
+ p = talloc_array(vp, uint8_t, vp->length);
+ if (fr_hex2bin(p, vp->length, value + 2, len) != vp->length) {
+ talloc_free(p);
+ fr_strerror_printf("Invalid hex data");
+ return -1;
+ }
- vp->vp_ipaddr = ipaddr.ipaddr.ip4addr.s_addr;
+ vp->vp_octets = p;
+ }
+ goto finish;
+
+ case PW_TYPE_ABINARY:
+#ifdef WITH_ASCEND_BINARY
+ if ((len > 1) && (strncasecmp(value, "0x", 2) == 0)) goto do_octets;
+
+ if (ascend_parse_filter(vp, value, len) < 0 ) {
+ /* Allow ascend_parse_filter's strerror to bubble up */
+ return -1;
}
- vp->length = 4;
+ goto finish;
+#else
+ /*
+ * If Ascend binary is NOT defined,
+ * then fall through to raw octets, so that
+ * the user can at least make them by hand...
+ */
+ goto do_octets;
+#endif
+
+ /* don't use this! */
+ case PW_TYPE_TLV:
+ {
+ uint8_t *p;
+
+ if ((len < 2) || (len & 0x01) || (strncasecmp(value, "0x", 2) != 0)) {
+ fr_strerror_printf("Invalid TLV specification");
+ return -1;
+ }
+ len -= 2;
+
+ vp->length = len >> 1;
+ p = talloc_array(vp, uint8_t, vp->length);
+ if (!p) {
+ fr_strerror_printf("No memory");
+ return -1;
+ }
+ if (fr_hex2bin(p, vp->length, value + 2, len) != vp->length) {
+ fr_strerror_printf("Invalid hex data in TLV");
+ return -1;
+ }
+
+ vp->vp_tlv = p;
+ }
+ goto finish;
+
+ case PW_TYPE_IPV4_ADDR:
+ {
+ fr_ipaddr_t addr;
+
+ if (fr_pton4(&addr, value, inlen, fr_hostname_lookups, false) < 0) return -1;
+
+ /*
+ * We allow v4 addresses to have a /32 suffix as some databases (PostgreSQL)
+ * print them this way.
+ */
+ if (addr.prefix != 32) {
+ fr_strerror_printf("Invalid IPv4 mask length \"/%i\". Only \"/32\" permitted "
+ "for non-prefix types", addr.prefix);
+ return -1;
+ }
+
+ vp->vp_ipaddr = addr.ipaddr.ip4addr.s_addr;
+ vp->length = sizeof(vp->vp_ipaddr);
+ }
+ goto finish;
+
+ case PW_TYPE_IPV4_PREFIX:
+ {
+ fr_ipaddr_t addr;
+
+ if (fr_pton4(&addr, value, inlen, fr_hostname_lookups, false) < 0) return -1;
+
+ vp->vp_ipv4prefix[1] = addr.prefix;
+ memcpy(vp->vp_ipv4prefix + 2, &addr.ipaddr.ip4addr.s_addr, sizeof(vp->vp_ipv4prefix) - 2);
+ vp->length = sizeof(vp->vp_ipv4prefix);
+ }
+ goto finish;
+
+ case PW_TYPE_IPV6_ADDR:
+ {
+ fr_ipaddr_t addr;
+
+ if (fr_pton6(&addr, value, inlen, fr_hostname_lookups, false) < 0) return -1;
+
+ /*
+ * We allow v6 addresses to have a /128 suffix as some databases (PostgreSQL)
+ * print them this way.
+ */
+ if (addr.prefix != 128) {
+ fr_strerror_printf("Invalid IPv6 mask length \"/%i\". Only \"/128\" permitted "
+ "for non-prefix types", addr.prefix);
+ return -1;
+ }
+
+ memcpy(&vp->vp_ipv6addr, &addr.ipaddr.ip6addr.s6_addr, sizeof(vp->vp_ipv6addr));
+ vp->length = sizeof(vp->vp_ipv6addr);
+ }
+ goto finish;
+
+ case PW_TYPE_IPV6_PREFIX:
+ {
+ fr_ipaddr_t addr;
+
+ if (fr_pton6(&addr, value, inlen, fr_hostname_lookups, false) < 0) return -1;
+
+ vp->vp_ipv6prefix[1] = addr.prefix;
+ memcpy(vp->vp_ipv6prefix + 2, &addr.ipaddr.ip6addr.s6_addr, sizeof(vp->vp_ipv6prefix) - 2);
+ vp->length = sizeof(vp->vp_ipv6prefix);
+ }
+ goto finish;
+
+ default:
break;
+ }
+
+ /*
+ * It's a fixed size type, copy to a temporary buffer and
+ * \0 terminate if insize >= 0.
+ */
+ if (inlen > 0) {
+ if (len >= sizeof(buffer)) {
+ fr_strerror_printf("Temporary buffer too small");
+ return -1;
+ }
+
+ memcpy(buffer, value, inlen);
+ buffer[inlen] = '\0';
+ value = buffer;
+ }
+ switch(vp->da->type) {
case PW_TYPE_BYTE:
+ {
+ char *p;
vp->length = 1;
/*
if (!*p) {
if (vp->vp_integer > 255) {
fr_strerror_printf("Byte value \"%s\" is larger than 255", value);
- return false;
+ return -1;
}
break;
}
- if (fr_whitespace_check(p)) break;
+ if (is_whitespace(p)) break;
+ }
goto check_for_value;
case PW_TYPE_SHORT:
+ {
+ char *p;
+
/*
* Note that ALL integers are unsigned!
*/
if (!*p) {
if (vp->vp_integer > 65535) {
fr_strerror_printf("Byte value \"%s\" is larger than 65535", value);
- return false;
+ return -1;
}
break;
}
- if (fr_whitespace_check(p)) break;
+ if (is_whitespace(p)) break;
+ }
goto check_for_value;
case PW_TYPE_INTEGER:
+ {
+ char *p;
+
/*
* Note that ALL integers are unsigned!
*/
vp->vp_integer = fr_strtoul(value, &p);
vp->length = 4;
if (!*p) break;
- if (fr_whitespace_check(p)) break;
+ if (is_whitespace(p)) break;
check_for_value:
/*
*/
if ((dval = dict_valbyname(vp->da->attr, vp->da->vendor, value)) == NULL) {
fr_strerror_printf("Unknown value '%s' for attribute '%s'", value, vp->da->name);
- return false;
+ return -1;
}
vp->vp_integer = dval->value;
+ }
break;
case PW_TYPE_INTEGER64:
+ {
+ uint64_t y;
+
/*
* Note that ALL integers are unsigned!
*/
if (sscanf(value, "%" PRIu64, &y) != 1) {
fr_strerror_printf("Invalid value '%s' for attribute '%s'",
value, vp->da->name);
- return false;
+ return -1;
}
vp->vp_integer64 = y;
vp->length = 8;
- length = strspn(value, "0123456789");
- if (fr_whitespace_check(value + length)) break;
+ }
break;
case PW_TYPE_DATE:
- {
- /*
- * 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 (fr_get_time(value, &date) < 0) {
- fr_strerror_printf("failed to parse time string "
- "\"%s\"", value);
- return false;
- }
-
- vp->vp_date = date;
- }
- vp->length = 4;
- break;
-
- case PW_TYPE_ABINARY:
-#ifdef WITH_ASCEND_BINARY
- if (strncasecmp(value, "0x", 2) == 0) {
- goto do_octets;
- }
-
- if (ascend_parse_filter(vp, value) < 0 ) {
- /* Allow ascend_parse_filter's strerror to bubble up */
- return false;
- }
- break;
-
+ {
/*
- * If Ascend binary is NOT defined,
- * then fall through to raw octets, so that
- * the user can at least make them by hand...
+ * time_t may be 64 bits, whule vp_date
+ * MUST be 32-bits. We need an
+ * intermediary variable to handle
+ * the conversions.
*/
-#endif
- /* raw octets: 0x01020304... */
- case PW_TYPE_VSA:
- if (strcmp(value, "ANY") == 0) {
- vp->length = 0;
- break;
- } /* else it's hex */
-
- case PW_TYPE_OCTETS:
- if (strncasecmp(value, "0x", 2) == 0) {
- size_t size;
- uint8_t *us;
+ time_t date;
-#ifdef WITH_ASCEND_BINARY
- do_octets:
-#endif
- cp = value + 2;
- size = strlen(cp);
- vp->length = size >> 1;
- us = talloc_array(vp, uint8_t, vp->length);
+ if (fr_get_time(value, &date) < 0) {
+ fr_strerror_printf("failed to parse time string "
+ "\"%s\"", value);
+ return -1;
+ }
- /*
- * Invalid.
- */
- if ((size & 0x01) != 0) {
- fr_strerror_printf("Hex string is not an even length string");
- return false;
- }
+ vp->vp_date = date;
+ vp->length = 4;
+ }
- if (fr_hex2bin(us, cp, vp->length) != vp->length) {
- fr_strerror_printf("Invalid hex data");
- return false;
- }
- vp->vp_octets = us;
- } else {
- pairstrcpy(vp, value);
- }
break;
case PW_TYPE_IFID:
if (ifid_aton(value, (void *) &vp->vp_ifid) == NULL) {
fr_strerror_printf("Failed to parse interface-id string \"%s\"", value);
- return false;
+ return -1;
}
vp->length = 8;
break;
- case PW_TYPE_IPV6ADDR:
- {
- 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 false;
- }
- 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 false;
- } 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_ipv6prefix + 2) <= 0) {
- fr_strerror_printf("failed to parse IPv6 address string \"%s\"", value);
- return false;
- }
-
- prefix = strtoul(p + 1, &eptr, 10);
- if ((prefix > 128) || *eptr) {
- fr_strerror_printf("failed to parse IPv6 address string \"%s\"", value);
- return false;
- }
- vp->vp_ipv6prefix[1] = prefix;
-
- if (prefix < 128) {
- struct in6_addr addr;
-
- addr = fr_ipaddr_mask6((struct in6_addr *)(&vp->vp_ipv6prefix[2]), prefix);
- memcpy(vp->vp_ipv6prefix + 2, &addr, sizeof(addr));
- }
- }
- vp->length = 16 + 2;
- break;
-
- case PW_TYPE_IPV4PREFIX:
- p = strchr(value, '/');
+ case PW_TYPE_ETHERNET:
+ {
+ char const *c1, *c2, *cp;
+ size_t vp_len = 0;
/*
- * 192.0.2.2 is parsed as if it was /32
+ * Convert things which are obviously integers to Ethernet addresses
+ *
+ * We assume the number is the bigendian representation of the
+ * ethernet address.
*/
- if (!p) {
- vp->vp_ipv4prefix[1] = 32;
+ if (is_integer(value)) {
+ uint64_t integer = htonll(atoll(value));
- if (inet_pton(AF_INET, value, vp->vp_ipv4prefix + 2) <= 0) {
- fr_strerror_printf("failed to parse IPv4 address string \"%s\"", value);
- return false;
- }
- vp->length = sizeof(vp->vp_ipv4prefix);
+ memcpy(&vp->vp_ether, &integer, sizeof(vp->vp_ether));
break;
}
- /*
- * Otherwise parse the prefix
- */
- if ((p - value) >= 256) {
- fr_strerror_printf("invalid IPv4 prefix string \"%s\"", value);
- return false;
- } else {
- unsigned int prefix;
- char buffer[256], *eptr;
-
- memcpy(buffer, value, p - value);
- buffer[p - value] = '\0';
-
- if (inet_pton(AF_INET, buffer, vp->vp_ipv4prefix + 2) <= 0) {
- fr_strerror_printf("failed to parse IPv4 address string \"%s\"", value);
- return false;
- }
-
- prefix = strtoul(p + 1, &eptr, 10);
- if ((prefix > 32) || *eptr) {
- fr_strerror_printf("failed to parse IPv4 address string \"%s\"", value);
- return false;
+ 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;
}
- vp->vp_ipv4prefix[1] = prefix;
-
- if (prefix < 32) {
- struct in_addr addr;
-
- addr = fr_ipaddr_mask((struct in_addr *)(&vp->vp_ipv4prefix[2]), prefix);
- memcpy(vp->vp_ipv4prefix + 2, &addr, sizeof(addr));
+ if (!c1 || !c2 || (vp_len >= sizeof(vp->vp_ether))) {
+ fr_strerror_printf("failed to parse Ethernet address \"%s\"", value);
+ return -1;
}
+ vp->vp_ether[vp_len] = ((c1-hextab)<<4) + (c2-hextab);
+ vp_len++;
}
- vp->length = sizeof(vp->vp_ipv4prefix);
- break;
-
- case PW_TYPE_ETHERNET:
- {
- char const *c1, *c2;
-
- /*
- * Convert things which are obviously integers to Ethernet addresses
- *
- * We assume the number is the bigendian representation of the
- * ethernet address.
- */
- if (fr_integer_check(value)) {
- uint64_t integer = htonll(atoll(value));
-
- memcpy(&vp->vp_ether, &integer, sizeof(vp->vp_ether));
- break;
- }
- 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 false;
- }
- vp->vp_ether[length] = ((c1-hextab)<<4) + (c2-hextab);
- length++;
- }
- }
vp->length = 6;
+ }
break;
/*
* These are not dynamic da, and will have the same vendor
* and attribute as the original.
*/
- case PW_TYPE_COMBO_IP:
- {
- DICT_ATTR const *da;
-
- if (inet_pton(AF_INET6, value, &vp->vp_ipv6addr) > 0) {
- da = dict_attrbytype(vp->da->attr, vp->da->vendor,
- PW_TYPE_IPV6ADDR);
- if (!da) {
- fr_strerror_printf("Cannot find ipv6addr for %s", vp->da->name);
- return false;
- }
+ case PW_TYPE_IP_ADDR:
+ {
+ DICT_ATTR const *da;
- vp->length = 16; /* length of IPv6 address */
- } else {
- fr_ipaddr_t ipaddr;
+ if (inet_pton(AF_INET6, value, &vp->vp_ipv6addr) > 0) {
+ da = dict_attrbytype(vp->da->attr, vp->da->vendor, PW_TYPE_IPV6_ADDR);
+ if (!da) {
+ fr_strerror_printf("Cannot find ipv6addr for %s", vp->da->name);
+ return -1;
+ }
- da = dict_attrbytype(vp->da->attr, vp->da->vendor,
- PW_TYPE_IPADDR);
- if (!da) {
- fr_strerror_printf("Cannot find ipaddr for %s", vp->da->name);
- return false;
- }
+ vp->length = 16; /* length of IPv6 address */
+ } 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 false;
- }
+ da = dict_attrbytype(vp->da->attr, vp->da->vendor,
+ PW_TYPE_IPV4_ADDR);
+ if (!da) {
+ fr_strerror_printf("Cannot find ipaddr for %s", vp->da->name);
+ return -1;
+ }
- vp->vp_ipaddr = ipaddr.ipaddr.ip4addr.s_addr;
- vp->length = 4;
+ if (ip_hton(&ipaddr, AF_INET, value, false) < 0) {
+ fr_strerror_printf("Failed to find IPv4 address for %s", value);
+ return -1;
}
- vp->da = da;
+ 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;
+ vp->da = da;
+ }
break;
- case PW_TYPE_TLV: /* don't use this! */
- if (strncasecmp(value, "0x", 2) != 0) {
- fr_strerror_printf("Invalid TLV specification");
- return false;
- }
- length = strlen(value + 2) / 2;
- if (vp->length < length) {
- TALLOC_FREE(vp->vp_tlv);
- }
- vp->vp_tlv = talloc_array(vp, uint8_t, length);
- if (!vp->vp_tlv) {
- fr_strerror_printf("No memory");
- return false;
- }
- if (fr_hex2bin(vp->vp_tlv, value + 2, length) != length) {
- fr_strerror_printf("Invalid hex data in TLV");
- return false;
- }
- vp->length = length;
+ case PW_TYPE_SIGNED:
+ /* Damned code for 1 WiMAX attribute */
+ vp->vp_signed = (int32_t) strtol(value, NULL, 10);
+ vp->length = 4;
break;
/*
*/
default:
fr_strerror_printf("unknown attribute type %d", vp->da->type);
- return false;
+ return -1;
}
- finish:
+finish:
vp->type = VT_DATA;
- return true;
+ return 0;
}
/** Use simple heuristics to create an VALUE_PAIR from an unknown address string
finish:
vp = pairalloc(ctx, da);
if (!vp) return NULL;
- if (!pairparsevalue(vp, value)) {
+ if (pairparsevalue(vp, value, 0) < 0) {
talloc_free(vp);
return NULL;
}
return vp;
}
+
/** Create a valuepair from an ASCII attribute and value
*
* Where the attribute name is in the form:
vp->length = size >> 1;
data = talloc_array(vp, uint8_t, vp->length);
- if (fr_hex2bin(data, value + 2, size) != vp->length) {
+ if (fr_hex2bin(data, vp->length, value + 2, size) != vp->length) {
fr_strerror_printf("Invalid hex string");
talloc_free(vp);
return NULL;
VALUE_PAIR *vp;
char *tc, *ts;
int8_t tag;
- int found_tag;
+ bool found_tag;
char buffer[256];
char const *attrname = attribute;
/*
* Check for tags in 'Attribute:Tag' format.
*/
- found_tag = 0;
- tag = 0;
+ found_tag = false;
+ tag = TAG_ANY;
ts = strrchr(attribute, ':');
if (ts && !ts[1]) {
if (ts[1] == '*' && ts[2] == 0) {
/* Wildcard tag for check items */
tag = TAG_ANY;
- *ts = 0;
+ *ts = '\0';
} else if ((ts[1] >= '0') && (ts[1] <= '9')) {
/* It's not a wild card tag */
tag = strtol(ts + 1, &tc, 0);
if (tc && !*tc && TAG_VALID_ZERO(tag))
- *ts = 0;
- else tag = 0;
+ *ts = '\0';
+ else tag = TAG_ANY;
} else {
fr_strerror_printf("Invalid tag for attribute %s", attribute);
return NULL;
}
- found_tag = 1;
+ found_tag = true;
}
/*
vp->tag = tag;
switch (vp->op) {
- default:
- break;
-
case T_OP_CMP_TRUE:
case T_OP_CMP_FALSE:
vp->vp_strvalue = NULL;
*/
case T_OP_REG_EQ: /* =~ */
case T_OP_REG_NE: /* !~ */
+ {
+
+ int compare;
+ regex_t reg;
#ifndef WITH_REGEX
fr_strerror_printf("Regular expressions are not supported");
return NULL;
talloc_free(vp);
- if (1) {
- int compare;
- regex_t reg;
-
- compare = regcomp(®, value, REG_EXTENDED);
- if (compare != 0) {
- regerror(compare, ®, buffer, sizeof(buffer));
- fr_strerror_printf("Illegal regular expression in attribute: %s: %s",
+ compare = regcomp(®, value, REG_EXTENDED);
+ if (compare != 0) {
+ regerror(compare, ®, buffer, sizeof(buffer));
+ fr_strerror_printf("Illegal regular expression in attribute: %s: %s",
attribute, buffer);
- return NULL;
- }
+ return NULL;
}
+ regfree(®);
vp = pairmake(ctx, NULL, attribute, NULL, op);
if (!vp) return NULL;
break;
#endif
}
-
+ default:
+ break;
+ }
+
/*
* FIXME: if (strcasecmp(attribute, vp->da->name) != 0)
* then the user MAY have typed in the attribute name
* We probably want to fix pairparsevalue to accept
* octets as values for any attribute.
*/
- if (value && !pairparsevalue(vp, value)) {
+ if (value && (pairparsevalue(vp, value, 0) < 0)) {
talloc_free(vp);
return NULL;
}
return -1;
}
- raw = talloc_strdup(vp, value);
+ raw = talloc_typed_strdup(vp, value);
if (!raw) {
return -1;
}
*ptr = p;
/* Now we should have an operator here. */
- raw->op = gettoken(ptr, buf, sizeof(buf));
+ raw->op = gettoken(ptr, buf, sizeof(buf), false);
if (raw->op < T_EQSTART || raw->op > T_EQEND) {
fr_strerror_printf("Expecting operator");
/*
* Read value. Note that empty string values are allowed
*/
- quote = gettoken(ptr, raw->r_opand, sizeof(raw->r_opand));
+ quote = gettoken(ptr, raw->r_opand, sizeof(raw->r_opand), false);
if (quote == T_EOL) {
fr_strerror_printf("Failed to get value");
*/
p = *ptr;
- next = gettoken(&p, buf, sizeof(buf));
+ next = gettoken(&p, buf, sizeof(buf), false);
switch (next) {
case T_EOL:
case T_HASH:
/*
* Read valuepairs from the fp up to End-Of-File.
- *
- * Hmm... this function is only used by radclient..
*/
-VALUE_PAIR *readvp2(TALLOC_CTX *ctx, FILE *fp, bool *pfiledone, char const *errprefix)
+int readvp2(VALUE_PAIR **out, TALLOC_CTX *ctx, FILE *fp, bool *pfiledone)
{
char buf[8192];
FR_TOKEN last_token = T_EOL;
- VALUE_PAIR *vp;
- VALUE_PAIR *list;
- bool error = false;
- list = NULL;
+ vp_cursor_t cursor;
- while (!error && fgets(buf, sizeof(buf), fp) != NULL) {
+ VALUE_PAIR *vp = NULL;
+
+ fr_cursor_init(&cursor, out);
+
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
/*
* If we get a '\n' by itself, we assume that's
* the end of that VP
*/
- if ((buf[0] == '\n') && (list)) {
- return list;
- }
- if ((buf[0] == '\n') && (!list)) {
+ if (buf[0] == '\n') {
+ if (vp) return 0;
continue;
}
vp = NULL;
last_token = userparse(ctx, buf, &vp);
if (!vp) {
- if (last_token != T_EOL) {
- fr_perror("%s", errprefix);
- error = true;
- break;
- }
+ if (last_token != T_EOL) goto error;
break;
}
- pairadd(&list, vp);
+ fr_cursor_insert(&cursor, vp);
buf[0] = '\0';
}
- if (error) pairfree(&list);
-
*pfiledone = true;
- return list;
+ return 0;
+
+error:
+ vp = fr_cursor_first(&cursor);
+ if (vp) pairfree(&vp);
+
+ return -1;
+}
+
+/** 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_BOOLEAN:
+ 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_IPV4_ADDR:
+ compare = (int64_t) ntohl(one->vp_ipaddr) - (int64_t) ntohl(two->vp_ipaddr);
+ break;
+
+ case PW_TYPE_IPV6_ADDR:
+ compare = memcmp(&one->vp_ipv6addr, &two->vp_ipv6addr, sizeof(one->vp_ipv6addr));
+ break;
+
+ case PW_TYPE_IPV6_PREFIX:
+ compare = memcmp(&one->vp_ipv6prefix, &two->vp_ipv6prefix, sizeof(one->vp_ipv6prefix));
+ break;
+
+ case PW_TYPE_IPV4_PREFIX:
+ 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_INVALID: /* We should never see these */
+ case PW_TYPE_IP_ADDR: /* This should of been converted into IPADDR/IPV6ADDR */
+ case PW_TYPE_IP_PREFIX: /* 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_TIMEVAL:
+ 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;
}
/*
*
* 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;
/*
* 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
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;
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;
}
/*
* 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.
*/
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;
+
+ if (!a || !b) return -1;
+
+ switch (a->da->type) {
+ case PW_TYPE_IPV4_ADDR:
+ switch (b->da->type) {
+ case PW_TYPE_IPV4_ADDR: /* IPv4 and IPv4 */
+ goto cmp;
+
+ case PW_TYPE_IPV4_PREFIX: /* 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_IPV4_PREFIX: /* IPv4 and IPv4 Prefix */
+ switch (b->da->type) {
+ case PW_TYPE_IPV4_ADDR:
+ 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_IPV4_PREFIX: /* 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_IPV6_ADDR:
+ switch (b->da->type) {
+ case PW_TYPE_IPV6_ADDR: /* IPv6 and IPv6 */
+ goto cmp;
+
+ case PW_TYPE_IPV6_PREFIX: /* 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_IPV6_PREFIX:
+ switch (b->da->type) {
+ case PW_TYPE_IPV6_ADDR: /* 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_IPV6_PREFIX: /* 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;
+ if (!a) return -1;
- VERIFY_VP(one);
- VERIFY_VP(two);
+ VERIFY_VP(a);
+ if (b) 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:
return -1;
#else
{
+ int compare;
regex_t reg;
char buffer[MAX_STRING_LEN * 4 + 1];
- compare = regcomp(®, one->vp_strvalue, REG_EXTENDED);
+ compare = regcomp(®, a->vp_strvalue, REG_EXTENDED);
if (compare != 0) {
regerror(compare, ®, buffer, sizeof(buffer));
fr_strerror_printf("Illegal regular expression in attribute: %s: %s",
- one->da->name, buffer);
+ a->da->name, buffer);
+ return -1;
+ }
+
+ if (!b) {
+ regfree(®);
return -1;
}
- vp_prints_value(buffer, sizeof(buffer), two, 0);
+ vp_prints_value(buffer, sizeof(buffer), b, 0);
/*
* Don't care about substring matches,
compare = regexec(®, buffer, 0, NULL, 0);
regfree(®);
- if (one->op == T_OP_REG_EQ) return (compare == 0);
+ if (a->op == T_OP_REG_EQ) {
+ return (compare == 0);
+ }
+
return (compare != 0);
}
#endif
break;
}
- return paircmp_op(two, one->op, one);
+ return paircmp_op(b, a->op, a);
}
-/* Compare two attributes
+/** Determine equality of two lists
+ *
+ * This is useful for comparing lists of attributes inserted into a binary tree.
*
- * @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);
+ * @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);
+ if (!a_p) {
+ return -1;
+ }
- case T_OP_LT:
- return (compare < 0);
+ /* if(!b_p) */
+ return 1;
+}
- case T_OP_GT:
- return (compare > 0);
+/** Set the type of the VALUE_PAIR value buffer to match it's DICT_ATTR
+ *
+ * @param vp to fixup.
+ */
+static void pairtypeset(VALUE_PAIR *vp)
+{
+ if (!vp->data.ptr) return;
- case T_OP_LE:
- return (compare <= 0);
+ switch(vp->da->type) {
+ case PW_TYPE_OCTETS:
+ case PW_TYPE_TLV:
+ talloc_set_type(vp->data.ptr, uint8_t);
+ return;
- case T_OP_GE:
- return (compare >= 0);
+ case PW_TYPE_STRING:
+ talloc_set_type(vp->data.ptr, char);
+ return;
default:
- return 0;
+ return;
}
}
*
* @param[in,out] vp to update
* @param[in] src data to copy
- * @param[in] size of the data
+ * @param[in] size of the data, may be 0 in which case previous value will be freed.
*/
void pairmemcpy(VALUE_PAIR *vp, uint8_t const *src, size_t size)
{
- uint8_t *p, *q;
+ uint8_t *p = NULL, *q;
VERIFY_VP(vp);
- p = talloc_memdup(vp, src, size);
- if (!p) return;
+ if (size > 0) {
+ p = talloc_memdup(vp, src, size);
+ if (!p) return;
+ talloc_set_type(p, uint8_t);
+ }
memcpy(&q, &vp->vp_octets, sizeof(q));
- talloc_free(q);
+ TALLOC_FREE(q);
vp->vp_octets = p;
vp->length = size;
+
+ if (size > 0) pairtypeset(vp);
}
/** Reparent an allocated octet buffer to a VALUE_PAIR
vp->vp_octets = talloc_steal(vp, src);
vp->type = VT_DATA;
- vp->length = talloc_array_length(vp->vp_octets);
+ vp->length = talloc_array_length(vp->vp_strvalue);
+ pairtypeset(vp);
}
/** Reparent an allocated char buffer to a VALUE_PAIR
vp->vp_strvalue = talloc_steal(vp, src);
vp->type = VT_DATA;
vp->length = talloc_array_length(vp->vp_strvalue) - 1;
+ pairtypeset(vp);
}
/** Copy data into an "string" data type.
VERIFY_VP(vp);
p = talloc_strdup(vp, src);
+
if (!p) return;
memcpy(&q, &vp->vp_strvalue, sizeof(q));
vp->vp_strvalue = p;
vp->type = VT_DATA;
vp->length = talloc_array_length(vp->vp_strvalue) - 1;
+ pairtypeset(vp);
}
+/** Copy data into an "string" data type.
+ *
+ * @param[in,out] vp to update.
+ * @param[in] src data to copy.
+ * @param[in] len of data to copy.
+ */
+void pairstrncpy(VALUE_PAIR *vp, char const *src, size_t len)
+{
+ char *p, *q;
+
+ VERIFY_VP(vp);
+
+ p = talloc_array(vp, char, len + 1);
+ if (!p) return;
+
+ memcpy(p, src, len); /* embdedded \0 safe */
+ p[len] = '\0';
+
+ memcpy(&q, &vp->vp_strvalue, sizeof(q));
+ talloc_free(q);
+
+ vp->vp_strvalue = p;
+ vp->type = VT_DATA;
+ vp->length = len;
+ pairtypeset(vp);
+}
+
+/** Copy data from one VP to another
+ *
+ * Allocate a new pair using da, and copy over the value from the specified vp.
+ *
+ * @todo Should be able to do type conversions.
+ *
+ * @param[in,out] vp to update.
+ * @param[in] da Type of data represented by data.
+ * @param[in] data to copy.
+ * @param[in] len of data to copy.
+ */
+int pairdatacpy(VALUE_PAIR *vp, DICT_ATTR const *da, value_data_t const *data, size_t len)
+{
+ void *old;
+ VERIFY_VP(vp);
+
+ /*
+ * The da->types have to be identical, OR the "from" da->type has
+ * to be octets.
+ */
+ if (vp->da->type != da->type) {
+ /*
+ * Decode the octets buffer using the RADIUS decoder.
+ */
+ if (da->type == PW_TYPE_OCTETS) {
+ if (data2vp(vp, NULL, NULL, NULL, vp->da, data->octets, len, len, &vp) < 0) return -1;
+ vp->type = VT_DATA;
+ return 0;
+ }
+
+ /*
+ * Else if the destination da->type is octets
+ */
+ if (vp->da->type == PW_TYPE_OCTETS) {
+ int ret;
+ uint8_t *buff;
+ VALUE_PAIR const *pvp = vp;
+
+ buff = talloc_array(vp, uint8_t, dict_attr_sizes[da->type][1] + 2);
+
+ ret = rad_vp2rfc(NULL, NULL, NULL, &pvp, buff, dict_attr_sizes[da->type][1]);
+ if (ret < 0) return -1;
+
+ pairmemcpy(vp, buff + 2, ret - 2);
+ talloc_free(buff);
+
+ return 0;
+ }
+
+ /*
+ * Fixme...
+ */
+ fr_strerror_printf("Data conversion not supported");
+ return -1;
+ }
+
+ /*
+ * Clear existing value if there is one
+ */
+ memcpy(&old, &vp->data.ptr, sizeof(old));
+ talloc_free(old);
+
+ switch (vp->da->type) {
+ case PW_TYPE_TLV:
+ case PW_TYPE_OCTETS:
+ pairmemcpy(vp, data->octets, len);
+ break;
+
+ case PW_TYPE_STRING:
+ pairstrncpy(vp, data->strvalue, len);
+ break;
+
+ default:
+ memcpy(&vp->data, data, sizeof(vp->data));
+ break;
+ }
+ vp->length = len;
+
+ return 0;
+}
/** Print data into an "string" data type.
*
vp->type = VT_DATA;
vp->length = talloc_array_length(vp->vp_strvalue) - 1;
+ pairtypeset(vp);
}