+++ /dev/null
-/*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-/** AVP manipulation and search API
- *
- * @file src/lib/util/pair.c
- *
- * @copyright 2000,2006,2015 The FreeRADIUS server project
- */
-RCSID("$Id$")
-
-#include <freeradius-devel/util/debug.h>
-#include <freeradius-devel/util/misc.h>
-#include <freeradius-devel/util/pair.h>
-#include <freeradius-devel/util/pair_legacy.h>
-#include <freeradius-devel/util/print.h>
-#include <freeradius-devel/util/proto.h>
-#include <freeradius-devel/util/regex.h>
-#include <freeradius-devel/util/talloc.h>
-
-#include <ctype.h>
-
-/** Free a fr_pair_t
- *
- * @note Do not call directly, use talloc_free instead.
- *
- * @param vp to free.
- * @return 0
- */
-static int _fr_pair_free(fr_pair_t *vp)
-{
-#ifdef TALLOC_DEBUG
- talloc_report_depth_cb(NULL, 0, -1, fr_talloc_verify_cb, NULL);
-#endif
-
- /*
- * Pairs with children have the children
- * freed explicitly.
- */
- if (likely(vp->da != NULL)) switch (vp->da->type) {
- case FR_TYPE_STRUCTURAL:
- fr_pair_list_free(&vp->vp_group);
- break;
-
- default:
- break;
- }
-
-#ifndef NDEBUG
- memset(vp, 0, sizeof(*vp));
-#endif
-
- return 0;
-}
-
-/** Allocate a new pair list on the heap
- *
- * @param[in] ctx to allocate the pair list in.
- * @return
- * - A new #fr_pair_list_t.
- * - NULL if an error occurred.
- */
-fr_pair_list_t *fr_pair_list_alloc(TALLOC_CTX *ctx)
-{
- fr_pair_list_t *pl;
-
- pl = talloc(ctx, fr_pair_list_t);
- if (unlikely(!pl)) return NULL;
-
- fr_pair_list_init(pl);
-
- return pl;
-}
-
-/** Dynamically allocate a new attribute with no #fr_dict_attr_t assigned
- *
- * This is not the function you're looking for (unless you're binding
- * unknown attributes to pairs, and need to pre-allocate the memory).
- * You probably want #fr_pair_afrom_da instead.
- *
- * @note You must assign a #fr_dict_attr_t before freeing this #fr_pair_t.
- *
- * @param[in] ctx to allocate the pair list in.
- * @return
- * - A new #fr_pair_t.
- * - NULL if an error occurred.
- */
-fr_pair_t *fr_pair_alloc_null(TALLOC_CTX *ctx)
-{
- fr_pair_t *vp;
-
- vp = talloc_zero(ctx, fr_pair_t);
- if (unlikely(!vp)) {
- fr_strerror_const("Out of memory");
- return NULL;
- }
-
- vp->op = T_OP_EQ;
- vp->type = VT_NONE;
-
- talloc_set_destructor(vp, _fr_pair_free);
-
- return vp;
-}
-
-/** A special allocation function which disables child autofree
- *
- * This is intended to allocate root attributes for requests.
- * These roots are special in that they do not necessarily own
- * the child attributes and _MUST NOT_ free them when they
- * themselves are freed.
- *
- * @param[in] ctx to allocate the pair root in.
- * @param[in] da The root attribute.
- * @return
- * - A new root pair on success.
- * - NULL on failure.
- * @hidecallergraph
- */
-fr_pair_t *fr_pair_root_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
-{
- fr_pair_t *vp;
-
- vp = talloc_zero(ctx, fr_pair_t);
- if (unlikely(!vp)) {
- fr_strerror_const("Out of memory");
- return NULL;
- }
-
- vp->op = T_OP_EQ;
- vp->type = VT_NONE;
-
- if (unlikely(da->flags.is_unknown)) {
- fr_strerror_const("Root attribute cannot be unknown");
- return NULL;
- }
-
- vp->da = da;
-
-#ifndef NDEBUG
- switch (da->type) {
- case FR_TYPE_GROUP:
-#endif
- fr_pair_list_init(&vp->children);
-
-#ifndef NDEBUG
- break;
-
- default:
- fr_strerror_const("Root must be a group type");
- return NULL;
- }
-#endif
-
- return vp;
-}
-
-/** Dynamically allocate a new attribute and assign a #fr_dict_attr_t
- *
- * @note Will duplicate any unknown attributes passed as the da.
- *
- * @param[in] ctx for allocated memory, usually a pointer to a #fr_radius_packet_t
- * @param[in] da Specifies the dictionary attribute to build the #fr_pair_t from.
- * @return
- * - A new #fr_pair_t.
- * - NULL if an error occurred.
- * @hidecallergraph
- */
-fr_pair_t *fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
-{
- fr_pair_t *vp;
-
- vp = fr_pair_alloc_null(ctx);
- if (!vp) return NULL;
-
- /*
- * If we get passed an unknown da, we need to ensure that
- * it's parented by "vp".
- */
- if (da->flags.is_unknown) {
- fr_dict_attr_t const *unknown;
-
- unknown = fr_dict_unknown_afrom_da(vp, da);
- da = unknown;
- }
-
- /*
- * Use the 'da' to initialize more fields.
- */
- vp->da = da;
-
- switch (da->type) {
- case FR_TYPE_STRUCTURAL:
- fr_pair_list_init(&vp->children);
- break;
-
- default:
- fr_value_box_init(&vp->data, da->type, da, false);
- break;
- }
-
- return vp;
-}
-
-/** Create a new valuepair
- *
- * If attr and vendor match a dictionary entry then a VP with that #fr_dict_attr_t
- * will be returned.
- *
- * If attr or vendor are uknown will call dict_attruknown to create a dynamic
- * #fr_dict_attr_t of #FR_TYPE_OCTETS.
- *
- * Which type of #fr_dict_attr_t the #fr_pair_t was created with can be determined by
- * checking @verbatim vp->da->flags.is_unknown @endverbatim.
- *
- * @param[in] ctx for allocated memory, usually a pointer to a #fr_radius_packet_t.
- * @param[in] parent of the attribute being allocated (usually a dictionary or vendor).
- * @param[in] attr number.
- * @return
- * - A new #fr_pair_t.
- * - NULL on error.
- */
-fr_pair_t *fr_pair_afrom_child_num(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, unsigned int attr)
-{
- fr_dict_attr_t const *da;
- fr_pair_t *vp;
-
- vp = fr_pair_alloc_null(ctx);
- if (unlikely(!vp)) return NULL;
-
- da = fr_dict_attr_child_by_num(parent, attr);
- if (!da) {
- fr_dict_attr_t *unknown;
-
- unknown = fr_dict_unknown_attr_afrom_num(vp, parent, attr);
- if (!unknown) {
- talloc_free(vp);
- return NULL;
- }
- da = unknown;
- }
- vp->da = da;
- fr_value_box_init(&vp->data, da->type, da, false);
-
- return vp;
-}
-
-/** Copy a single valuepair
- *
- * Allocate a new valuepair and copy the da from the old vp.
- *
- * @param[in] ctx for talloc
- * @param[in] vp to copy.
- * @return
- * - A copy of the input VP.
- * - NULL on error.
- */
-fr_pair_t *fr_pair_copy(TALLOC_CTX *ctx, fr_pair_t const *vp)
-{
- fr_pair_t *n;
-
- if (!vp) return NULL;
-
- VP_VERIFY(vp);
-
- n = fr_pair_afrom_da(ctx, vp->da);
- if (!n) return NULL;
-
- n->op = vp->op;
- n->next = NULL;
- n->type = vp->type;
-
- /*
- * Copy the unknown attribute hierarchy
- */
- if (n->da->flags.is_unknown) {
- n->da = fr_dict_unknown_afrom_da(n, n->da);
- if (!n->da) {
- talloc_free(n);
- return NULL;
- }
- }
-
-
- /*
- * If it's an xlat, copy the raw string and return
- * early, so we don't pre-expand or otherwise mangle
- * the fr_pair_t.
- */
- if (vp->type == VT_XLAT) {
- n->xlat = talloc_typed_strdup(n, vp->xlat);
- return n;
- }
-
- /*
- * Groups are special.
- */
- switch (n->da->type) {
- case FR_TYPE_STRUCTURAL:
- if (fr_pair_list_copy(n, &n->vp_group, &vp->vp_group) < 0) {
- talloc_free(n);
- return NULL;
- }
- return n;
-
- default:
- break;
- }
-
- fr_value_box_copy(n, &n->data, &vp->data);
-
- return n;
-}
-
-/** Steal one VP
- *
- * @param[in] ctx to move fr_pair_t into
- * @param[in] vp fr_pair_t to move into the new context.
- */
-void fr_pair_steal(TALLOC_CTX *ctx, fr_pair_t *vp)
-{
- (void) talloc_steal(ctx, vp);
-
- /*
- * The DA may be unknown. If we're stealing the VPs to a
- * different context, copy the unknown DA. We use the VP
- * as a context here instead of "ctx", so that when the
- * VP is freed, so is the DA.
- *
- * Since we have no introspection into OTHER VPs using
- * the same DA, we can't have multiple VPs use the same
- * DA. So we might as well tie it to this VP.
- */
- if (vp->da->flags.is_unknown) {
- fr_dict_attr_t *da;
-
- da = fr_dict_unknown_afrom_da(vp, vp->da);
-
- fr_dict_unknown_free(&vp->da);
-
- vp->da = da;
- }
-}
-
-/** Free memory used by a valuepair list.
- *
- * @todo TLV: needs to free all dependents of each VP freed.
- *
- * @hidecallergraph
- */
-void fr_pair_list_free(fr_pair_list_t *vps)
-{
- fr_pair_t *vp, *next;
-
- if (!vps || !*vps) return;
-
- for (vp = *vps; vp != NULL; vp = next) {
- next = vp->next;
- VP_VERIFY(vp);
- talloc_free(vp);
- }
-
- *vps = NULL;
-}
-
-/** Mark malformed or unrecognised attributed as unknown
- *
- * @param vp to change fr_dict_attr_t of.
- * @return
- * - 0 on success (or if already unknown).
- * - -1 on failure.
- */
-int fr_pair_to_unknown(fr_pair_t *vp)
-{
- fr_dict_attr_t *unknown;
-
- VP_VERIFY(vp);
-
- if (vp->da->flags.is_unknown) return 0;
-
- if (!fr_cond_assert(vp->da->parent != NULL)) return -1;
-
- unknown = fr_dict_unknown_afrom_da(vp, vp->da);
- if (!unknown) return -1;
- unknown->flags.is_raw = 1;
-
- fr_dict_unknown_free(&vp->da); /* Only frees unknown attributes */
- vp->da = unknown;
-
- return 0;
-}
-
-/** Iterate over pairs with a specified da
- *
- * @param[in,out] prev The fr_pair_t before curr. Will be updated to point to the
- * pair before the one returned, or the last pair in the list
- * if no matching pairs found.
- * @param[in] to_eval The fr_pair_t after cursor->current. Will be checked to
- * see if it matches the specified fr_dict_attr_t.
- * @param[in] uctx The fr_dict_attr_t to search for.
- * @return
- * - Next matching fr_pair_t.
- * - NULL if not more matching fr_pair_ts could be found.
- */
-void *fr_pair_iter_next_by_da(void **prev, void *to_eval, void *uctx)
-{
- fr_pair_t *c, *p;
- fr_dict_attr_t *da = uctx;
-
- if (!to_eval) return NULL;
-
- for (p = *prev, c = to_eval; c; p = c, c = c->next) {
- VP_VERIFY(c);
- if (c->da == da) break;
- }
-
- *prev = p;
-
- return c;
-}
-
-/** Iterate over pairs which are decedents of the specified da
- *
- * @param[in,out] prev The fr_pair_t before curr. Will be updated to point to the
- * pair before the one returned, or the last pair in the list
- * if no matching pairs found.
- * @param[in] to_eval The fr_pair_t after cursor->current. Will be checked to
- * see if it matches the specified fr_dict_attr_t.
- * @param[in] uctx The fr_dict_attr_t to search for.
- * @return
- * - Next matching fr_pair_t.
- * - NULL if not more matching fr_pair_ts could be found.
- */
-void *fr_pair_iter_next_by_ancestor(void **prev, void *to_eval, void *uctx)
-{
- fr_pair_t *c, *p;
- fr_dict_attr_t *da = uctx;
-
- if (!to_eval) return NULL;
-
- for (p = *prev, c = to_eval; c; p = c, c = c->next) {
- VP_VERIFY(c);
- if (fr_dict_attr_common_parent(da, c->da, true)) break;
- }
-
- *prev = p;
-
- return c;
-}
-
-/** Find the pair with the matching DAs
- *
- * @hidecallergraph
- */
-fr_pair_t *fr_pair_find_by_da(fr_pair_list_t const *head, fr_dict_attr_t const *da)
-{
- fr_pair_t *vp;
-
- /* List head may be NULL if it contains no VPs */
- if (!*head) return NULL;
-
- LIST_VERIFY(head);
-
- if (!da) return NULL;
-
- for (vp = *head; vp != NULL; vp = vp->next) if (da == vp->da) return vp;
- return NULL;
-}
-
-
-/** Find the pair with the matching attribute
- *
- * @todo should take DAs and do a pointer comparison.
- */
-fr_pair_t *fr_pair_find_by_num(fr_pair_list_t *head, unsigned int vendor, unsigned int attr)
-{
- fr_pair_t *vp;
-
- /* List head may be NULL if it contains no VPs */
- if (!*head) return NULL;
-
- LIST_VERIFY(head);
-
- for (vp = *head; vp != NULL; vp = vp->next) {
- if (!fr_dict_attr_is_top_level(vp->da)) continue;
-
- if (vendor > 0) {
- fr_dict_vendor_t const *dv;
-
- dv = fr_dict_vendor_by_da(vp->da);
- if (!dv) continue;
-
- if (dv->pen != vendor) continue;
- }
-
- if (attr == vp->da->attr) return vp;
- }
-
- return NULL;
-}
-
-/** Find the pair with the matching attribute
- *
- */
-fr_pair_t *fr_pair_find_by_child_num(fr_pair_list_t *head, fr_dict_attr_t const *parent, unsigned int attr)
-{
- fr_dict_attr_t const *da;
- fr_pair_t *vp;
-
- /* List head may be NULL if it contains no VPs */
- if (!*head) return NULL;
-
- LIST_VERIFY(head);
-
- da = fr_dict_attr_child_by_num(parent, attr);
- if (!da) return NULL;
-
- for (vp = *head; vp != NULL; vp = vp->next) if (da == vp->da) return vp;
-
- return NULL;
-}
-
-static inline CC_HINT(always_inline) fr_pair_list_t *pair_children(fr_pair_t *vp)
-{
- if (!vp) return NULL;
-
- switch (vp->da->type) {
- case FR_TYPE_STRUCTURAL:
- return &vp->vp_group;
-
- default:
- return NULL;
- }
-}
-
-/** Get the child list of a group
- *
- * @param[in] vp which MUST be of a type
- * that can contain children.
- * @return
- * - NULL on error
- * - pointer to head of the child list.
- */
-fr_pair_list_t *fr_pair_children(fr_pair_t *vp)
-{
- return pair_children(vp);
-}
-
-
-/** Add a VP to the end of the list.
- *
- * Locates the end of 'head', and links an additional VP 'add' at the end.
- *
- * @param[in] head VP in linked list. Will add new VP to the end of this list.
- * @param[in] add VP to add to list.
- */
-void fr_pair_add(fr_pair_list_t *head, fr_pair_t *add)
-{
- fr_pair_t *i;
-
- if (!add) return;
-
- VP_VERIFY(add);
-
- if (*head == NULL) {
- *head = add;
- return;
- }
-
- for (i = *head; i->next; i = i->next) {
-#ifdef WITH_VERIFY_PTR
- VP_VERIFY(i);
- /*
- * The same VP should never by added multiple times
- * to the same list.
- */
- (void)fr_cond_assert(i != add);
-#endif
- }
-
- i->next = add;
-}
-
-/** Replace all matching VPs
- *
- * Walks over 'head', and replaces the head VP that matches 'replace'.
- *
- * @note Memory used by the VP being replaced will be freed.
- * @note Will not work with unknown attributes.
- *
- * @param[in,out] head VP in linked list. Will search and replace in this list.
- * @param[in] replace VP to replace.
- */
-void fr_pair_replace(fr_pair_list_t *head, fr_pair_t *replace)
-{
- fr_pair_t *i, *next;
- fr_pair_t **prev = head;
-
- VP_VERIFY(replace);
-
- if (*head == NULL) {
- *head = replace;
- return;
- }
-
- /*
- * Not an empty list, so find item if it is there, and
- * replace it. Note, we always replace the head one, and
- * we ignore any others that might exist.
- */
- for(i = *head; i; i = next) {
- VP_VERIFY(i);
- next = i->next;
-
- /*
- * Found the head attribute, replace it,
- * and return.
- */
- if (i->da == replace->da) {
- *prev = replace;
-
- /*
- * Should really assert that replace->next == NULL
- */
- replace->next = next;
- talloc_free(i);
- return;
- }
-
- /*
- * Point to where the attribute should go.
- */
- prev = &i->next;
- }
-
- /*
- * If we got here, we didn't find anything to replace, so
- * stopped at the last item, which we just append to.
- */
- *prev = replace;
-}
-
-/** Delete matching pairs
- *
- * Delete matching pairs from the attribute list.
- *
- * @param[in,out] head VP in list.
- * @param[in] attr to match.
- * @param[in] parent to match.
- */
-void fr_pair_delete_by_child_num(fr_pair_list_t *head, fr_dict_attr_t const *parent, unsigned int attr)
-{
- fr_pair_t *i, *next;
- fr_pair_t **last = head;
- fr_dict_attr_t const *da;
-
- da = fr_dict_attr_child_by_num(parent, attr);
- if (!da) return;
-
- for (i = *head; i; i = next) {
- VP_VERIFY(i);
- next = i->next;
- if (i->da == da) {
- *last = next;
- talloc_free(i);
- } else {
- last = &i->next;
- }
- }
-}
-
-/** Alloc a new fr_pair_t (and prepend)
- *
- * @param[in] ctx to allocate new #fr_pair_t in.
- * @param[out] out Pair we allocated. May be NULL if the caller doesn't
- * care about manipulating the fr_pair_t.
- * @param[in,out] list in search and insert into.
- * @param[in] da of attribute to update.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_add_by_da(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
-{
- fr_cursor_t cursor;
- fr_pair_t *vp;
-
- (void)fr_cursor_init(&cursor, list);
- vp = fr_pair_afrom_da(ctx, da);
- if (unlikely(!vp)) {
- if (out) *out = NULL;
- return -1;
- }
-
- fr_cursor_prepend(&cursor, vp);
- if (out) *out = vp;
-
- return 0;
-}
-
-/** Return the first fr_pair_t matching the #fr_dict_attr_t or alloc a new fr_pair_t (and prepend)
- *
- * @param[in] ctx to allocate any new #fr_pair_t in.
- * @param[out] out Pair we allocated or found. May be NULL if the caller doesn't
- * care about manipulating the fr_pair_t.
- * @param[in,out] list to search for attributes in or prepend attributes to.
- * @param[in] da of attribute to locate or alloc.
- * @return
- * - 1 if attribute already existed.
- * - 0 if we allocated a new attribute.
- * - -1 on failure.
- */
-int fr_pair_update_by_da(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
-{
- fr_cursor_t cursor;
- fr_pair_t *vp;
-
- vp = fr_cursor_iter_by_da_init(&cursor, list, da);
- if (vp) {
- VP_VERIFY(vp);
- if (out) *out = vp;
- return 1;
- }
-
- vp = fr_pair_afrom_da(ctx, da);
- if (unlikely(!vp)) {
- if (out) *out = NULL;
- return -1;
- }
-
- fr_cursor_prepend(&cursor, vp);
- if (out) *out = vp;
-
- return 0;
-}
-
-/** Delete matching pairs from the specified list
- *
- * @param[in,out] list to search for attributes in or prepend attributes to.
- * @param[in] da to match.
- * @return
- * - >0 the number of pairs deleted.
- * - 0 if no pairs were deleted.
- */
-int fr_pair_delete_by_da(fr_pair_list_t *list, fr_dict_attr_t const *da)
-{
- fr_cursor_t cursor;
- fr_pair_t *vp;
- int cnt;
-
- for (vp = fr_cursor_iter_by_da_init(&cursor, list, da), cnt = 0;
- vp;
- vp = fr_cursor_next(&cursor), cnt++) fr_cursor_free_item(&cursor);
-
- return cnt;
-}
-
-/** Remove fr_pair_t from a list
- *
- * @param[in] list of value pairs to remove VP from.
- * @param[in] vp to remove
- */
-void fr_pair_delete(fr_pair_list_t *list, fr_pair_t const *vp)
-{
- fr_cursor_t cursor;
- fr_pair_t *cvp;
-
- for (cvp = fr_cursor_init(&cursor, list);
- cvp;
- cvp = fr_cursor_next(&cursor)) {
- if (cvp == vp) {
- fr_cursor_free_item(&cursor);
- return;
- }
- }
-}
-
-/** Order attributes by their da, and tag
- *
- * Useful where attributes need to be aggregated, but not necessarily
- * ordered by attribute number.
- *
- * @param[in] a first dict_attr_t.
- * @param[in] b second dict_attr_t.
- * @return
- * - +1 if a > b
- * - 0 if a == b
- * - -1 if a < b
- */
-int8_t fr_pair_cmp_by_da(void const *a, void const *b)
-{
- fr_pair_t const *my_a = a;
- fr_pair_t const *my_b = b;
-
- VP_VERIFY(my_a);
- VP_VERIFY(my_b);
-
- return fr_pointer_cmp(my_a->da, my_b->da);
-}
-
-/** Order attributes by their attribute number, and tag
- *
- * @param[in] a first dict_attr_t.
- * @param[in] b second dict_attr_t.
- * @return
- * - +1 if a > b
- * - 0 if a == b
- * - -1 if a < b
- */
-static inline int8_t pair_cmp_by_num(void const *a, void const *b)
-{
- fr_pair_t const *my_a = a;
- fr_pair_t const *my_b = b;
-
- VP_VERIFY(my_a);
- VP_VERIFY(my_b);
-
- return (my_a->da->attr < my_b->da->attr) - (my_a->da->attr > my_b->da->attr);
-}
-
-/** Order attributes by their parent(s), attribute number, and tag
- *
- * Useful for some protocols where attributes of the same number should by aggregated
- * within a packet or container TLV.
- *
- * @param[in] a first dict_attr_t.
- * @param[in] b second dict_attr_t.
- * @return
- * - +1 if a > b
- * - 0 if a == b
- * - -1 if a < b
- */
-int8_t fr_pair_cmp_by_parent_num(void const *a, void const *b)
-{
- fr_pair_t const *vp_a = a;
- fr_pair_t const *vp_b = b;
- fr_dict_attr_t const *da_a = vp_a->da;
- fr_dict_attr_t const *da_b = vp_b->da;
- fr_da_stack_t da_stack_a;
- fr_da_stack_t da_stack_b;
- int8_t cmp;
- int i;
-
- /*
- * Fast path (assuming attributes
- * are in the same dictionary).
- */
- if ((da_a->parent->flags.is_root) && (da_b->parent->flags.is_root)) return pair_cmp_by_num(vp_a, vp_b);
-
- fr_proto_da_stack_build(&da_stack_a, da_a);
- fr_proto_da_stack_build(&da_stack_b, da_b);
-
- for (i = 0; (da_a = da_stack_a.da[i]) && (da_b = da_stack_b.da[i]); i++) {
- cmp = (da_a->attr > da_b->attr) - (da_a->attr < da_b->attr);
- if (cmp != 0) return cmp;
- }
-
- /*
- * If a has a shallower attribute
- * hierarchy than b, it should come
- * before b.
- */
- return (da_a && !da_b) - (!da_a && da_b);
-}
-
-/** Compare two pairs, using the operator from "a"
- *
- * i.e. given two attributes, it does:
- *
- * (b->data) (a->operator) (a->data)
- *
- * e.g. "foo" != "bar"
- *
- * @param[in] a the head attribute
- * @param[in] b the second attribute
- * @return
- * - 1 if true.
- * - 0 if false.
- * - -1 on failure.
- */
-int fr_pair_cmp(fr_pair_t *a, fr_pair_t *b)
-{
- if (!a) return -1;
-
- VP_VERIFY(a);
- if (b) VP_VERIFY(b);
-
- switch (a->op) {
- case T_OP_CMP_TRUE:
- return (b != NULL);
-
- case T_OP_CMP_FALSE:
- return (b == NULL);
-
- /*
- * a is a regex, compile it, print b to a string,
- * and then do string comparisons.
- */
- case T_OP_REG_EQ:
- case T_OP_REG_NE:
-#ifndef HAVE_REGEX
- return -1;
-#else
- if (!b) return false;
-
- {
- ssize_t slen;
- regex_t *preg;
- char *value;
-
- if (!fr_cond_assert(a->vp_type == FR_TYPE_STRING)) return -1;
-
- slen = regex_compile(NULL, &preg, a->xlat, talloc_array_length(a->xlat) - 1,
- NULL, false, true);
- if (slen <= 0) {
- fr_strerror_printf_push("Error at offset %zu compiling regex for %s", -slen,
- a->da->name);
- return -1;
- }
- fr_pair_aprint(NULL, &value, NULL, b);
- if (!value) {
- talloc_free(preg);
- return -1;
- }
-
- /*
- * Don't care about substring matches, oh well...
- */
- slen = regex_exec(preg, value, talloc_array_length(value) - 1, NULL);
- talloc_free(preg);
- talloc_free(value);
-
- if (slen < 0) return -1;
- if (a->op == T_OP_REG_EQ) return (int)slen;
- return !slen;
- }
-#endif
-
- default: /* we're OK */
- if (!b) return false;
- break;
- }
-
- return fr_pair_cmp_op(a->op, b, a);
-}
-
-/** Determine equality of two lists
- *
- * This is useful for comparing lists of attributes inserted into a binary tree.
- *
- * @param a head list of #fr_pair_t.
- * @param b second list of #fr_pair_t.
- * @return
- * - -1 if a < b.
- * - 0 if the two lists are equal.
- * - 1 if a > b.
- * - -2 on error.
- */
-int fr_pair_list_cmp(fr_pair_list_t const *a, fr_pair_list_t const *b)
-{
- fr_cursor_t a_cursor, b_cursor;
- fr_pair_t *a_p, *b_p;
-
- 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)) {
- int ret;
-
- /* Same VP, no point doing expensive checks */
- if (a_p == b_p) continue;
-
- ret = (a_p->da < b_p->da) - (a_p->da > b_p->da);
- if (ret != 0) return ret;
-
- switch (a_p->da->type) {
- case FR_TYPE_STRUCTURAL:
- ret = fr_pair_list_cmp(&a_p->vp_group, &b_p->vp_group);
- if (ret != 0) return ret;
- break;
-
- default:
- ret = fr_value_box_cmp(&a_p->data, &b_p->data);
- if (ret != 0) {
- (void)fr_cond_assert(ret >= -1); /* Comparison error */
- return ret;
- }
- }
-
- }
-
- if (!a_p && !b_p) return 0;
- if (!a_p) return -1;
-
- /* if(!b_p) */
- return 1;
-}
-
-static void _pair_list_sort_split(fr_pair_list_t *source, fr_pair_list_t *front, fr_pair_list_t *back)
-{
- fr_pair_t *fast;
- fr_pair_t *slow;
-
- /*
- * Stopping condition - no more elements left to split
- */
- if (!*source || !(*source)->next) {
- *front = *source;
- *back = NULL;
-
- return;
- }
-
- /*
- * Fast advances twice as fast as slow, so when it gets to the end,
- * slow will point to the middle of the linked list.
- */
- slow = *source;
- fast = (*source)->next;
-
- while (fast) {
- fast = fast->next;
- if (fast) {
- slow = slow->next;
- fast = fast->next;
- }
- }
-
- *front = *source;
- *back = slow->next;
- slow->next = NULL;
-}
-
-static fr_pair_t *_pair_list_sort_merge(fr_pair_list_t *a, fr_pair_list_t *b, fr_cmp_t cmp)
-{
- fr_pair_t *result = NULL;
-
- if (!*a) return *b;
- if (!*b) return *a;
-
- /*
- * Compare the fr_dict_attr_ts and tags
- */
- if (cmp(*a, *b) <= 0) {
- result = *a;
- result->next = _pair_list_sort_merge(&(*a)->next, b, cmp);
- } else {
- result = *b;
- result->next = _pair_list_sort_merge(a, &(*b)->next, cmp);
- }
-
- return result;
-}
-
-/** Sort a linked list of fr_pair_ts using merge sort
- *
- * @note We use a merge sort (which is a stable sort), making this
- * suitable for use on lists with things like EAP-Message
- * fragments where the order of EAP-Message attributes needs to
- * be maintained.
- *
- * @param[in,out] vps List of fr_pair_ts to sort.
- * @param[in] cmp to sort with
- */
-void fr_pair_list_sort(fr_pair_list_t *vps, fr_cmp_t cmp)
-{
- fr_pair_t *head = *vps;
- fr_pair_t *a;
- fr_pair_t *b;
-
- /*
- * If there's 0-1 elements it must already be sorted.
- */
- if (!head || !head->next) return;
-
- _pair_list_sort_split(&head, &a, &b); /* Split into sublists */
- fr_pair_list_sort(&a, cmp); /* Traverse left */
- fr_pair_list_sort(&b, cmp); /* Traverse right */
-
- /*
- * merge the two sorted lists together
- */
- *vps = _pair_list_sort_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 fr_pair_validate_debug(TALLOC_CTX *ctx, fr_pair_t const *failed[2])
-{
- fr_pair_t const *filter = failed[0];
- fr_pair_t const *list = failed[1];
-
- char *value, *str;
-
- (void) fr_strerror(); /* Clear any existing messages */
-
- if (!list) {
- if (!filter) {
- (void) fr_cond_assert(filter != NULL);
- 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;
- }
-
- fr_pair_aprint(ctx, &value, NULL, list);
- fr_pair_aprint(ctx, &str, NULL, filter);
-
- fr_strerror_printf("Attribute value \"%s\" didn't match filter: %s", value, str);
-
- talloc_free(str);
- talloc_free(value);
-
- return;
-}
-
-/** Uses fr_pair_cmp to verify all fr_pair_ts 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 fr_pair_validate(fr_pair_t const *failed[2], fr_pair_list_t *filter, fr_pair_list_t *list)
-{
- fr_cursor_t filter_cursor;
- fr_cursor_t list_cursor;
-
- fr_pair_t *check, *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
- */
- fr_pair_list_sort(filter, fr_pair_cmp_by_da);
- fr_pair_list_sort(list, fr_pair_cmp_by_da);
-
- 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) goto mismatch;
-
- /*
- * The lists are sorted, so if the head
- * attributes aren't of the same type, then we're
- * done.
- */
- if (!ATTRIBUTE_EQ(check, match)) goto mismatch;
-
- /*
- * They're of the same type, but don't have the
- * same values. This is a problem.
- *
- * Note that the RFCs say that for attributes of
- * the same type, order is important.
- */
- switch (check->da->type) {
- case FR_TYPE_STRUCTURAL:
- if (!fr_pair_validate(failed, &check->vp_group, &match->vp_group)) goto mismatch;
- break;
-
- default:
- /*
- * This attribute passed the filter
- */
- if (!fr_pair_cmp(check, match)) goto mismatch;
- break;
- }
-
- 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 fr_pair_cmp to verify all fr_pair_ts 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 fr_pair_validate_relaxed(fr_pair_t const *failed[2], fr_pair_list_t *filter, fr_pair_list_t *list)
-{
- fr_cursor_t filter_cursor;
- fr_cursor_t list_cursor;
-
- fr_pair_t *check, *last_check = NULL, *match = NULL;
-
- 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
- */
- fr_pair_list_sort(filter, fr_pair_cmp_by_da);
- fr_pair_list_sort(list, fr_pair_cmp_by_da);
-
- 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
- */
- match = fr_cursor_filter_next(&list_cursor, fr_pair_matches_da, check->da);
- 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_head(&list_cursor);
- ATTRIBUTE_EQ(match, check);
- match = fr_cursor_next(&list_cursor)) {
- switch (check->da->type) {
- case FR_TYPE_STRUCTURAL:
- if (!fr_pair_validate_relaxed(failed, &check->vp_group, &match->vp_group)) goto mismatch;
- break;
-
- default:
- /*
- * This attribute passed the filter
- */
- if (!fr_pair_cmp(check, match)) goto mismatch;
- break;
- }
- }
- }
-
- return true;
-
-mismatch:
- if (failed) {
- failed[0] = check;
- failed[1] = match;
- }
- return false;
-}
-
-/** Duplicate a list of pairs
- *
- * Copy all pairs from 'from' regardless of tag, attribute or vendor.
- *
- * @param[in] ctx for new #fr_pair_t (s) to be allocated in.
- * @param[in] to where to copy attributes to.
- * @param[in] from whence to copy #fr_pair_t (s).
- * @return
- * - >0 the number of attributes copied.
- * - 0 if no attributes copied.
- * - -1 on error.
- */
-int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from)
-{
- fr_cursor_t src, dst, tmp;
-
- fr_pair_t *head = NULL;
- fr_pair_t *vp;
- int cnt = 0;
-
- fr_cursor_talloc_init(&tmp, &head, fr_pair_t);
- for (vp = fr_cursor_talloc_init(&src, from, fr_pair_t);
- vp;
- vp = fr_cursor_next(&src), cnt++) {
- VP_VERIFY(vp);
- vp = fr_pair_copy(ctx, vp);
- if (!vp) {
- fr_pair_list_free(&head);
- return -1;
- }
- fr_cursor_append(&tmp, vp); /* fr_pair_list_copy sets next pointer to NULL */
- }
-
- if (!*to) { /* Fast Path */
- *to = head;
- } else {
- fr_cursor_talloc_init(&dst, to, fr_pair_t);
- fr_cursor_head(&tmp);
- fr_cursor_tail(&dst);
- fr_cursor_merge(&dst, &tmp);
- }
-
- return cnt;
-}
-
-/** Duplicate pairs in a list matching the specified da
- *
- * Copy all pairs from 'from' matching the specified da.
- *
- * @param[in] ctx for new #fr_pair_t (s) to be allocated in.
- * @param[in] to where to copy attributes to.
- * @param[in] from whence to copy #fr_pair_t (s).
- * @param[in] da to match.
- * @param[in] count How many instances to copy.
- * Use 0 for all attributes.
- * @return
- * - >0 the number of attributes copied.
- * - 0 if no attributes copied.
- * - -1 on error.
- */
-int fr_pair_list_copy_by_da(TALLOC_CTX *ctx, fr_pair_list_t *to,
- fr_pair_list_t *from, fr_dict_attr_t const *da, unsigned int count)
-{
- fr_cursor_t src, dst, tmp;
-
- fr_pair_t *head = NULL;
- fr_pair_t *vp;
- unsigned int cnt = 0;
-
- if (count == 0) count = UINT_MAX;
-
- if (unlikely(!da)) {
- fr_strerror_const("No search attribute provided");
- return -1;
- }
-
- fr_cursor_talloc_init(&tmp, &head, fr_pair_t);
- for (vp = fr_cursor_iter_by_da_init(&src, from, da);
- vp && (cnt < count);
- vp = fr_cursor_next(&src), cnt++) {
- VP_VERIFY(vp);
- vp = fr_pair_copy(ctx, vp);
- if (!vp) {
- fr_pair_list_free(&head);
- return -1;
- }
- fr_cursor_append(&tmp, vp); /* fr_pair_list_copy sets next pointer to NULL */
- }
-
- if (!*to) { /* Fast Path */
- *to = head;
- } else {
- fr_cursor_talloc_init(&dst, to, fr_pair_t);
- fr_cursor_head(&tmp);
- fr_cursor_merge(&dst, &tmp);
- }
-
- return cnt;
-}
-
-/** Duplicate pairs in a list where the da is a descendant of parent_da
- *
- * Copy all pairs from 'from' which are descendants of the specified 'parent_da'.
- * This is particularly useful for copying attributes of a particular vendor, where the vendor
- * da is passed as parent_da.
- *
- * @param[in] ctx for new #fr_pair_t (s) to be allocated in.
- * @param[in] to where to copy attributes to.
- * @param[in] from whence to copy #fr_pair_t (s).
- * @param[in] parent_da to match.
- * @param[in] count How many instances to copy.
- * Use 0 for all attributes.
- * @return
- * - >0 the number of attributes copied.
- * - 0 if no attributes copied.
- * - -1 on error.
- */
-int fr_pair_list_copy_by_ancestor(TALLOC_CTX *ctx, fr_pair_list_t *to,
- fr_pair_list_t *from, fr_dict_attr_t const *parent_da, unsigned int count)
-{
- fr_cursor_t src, dst, tmp;
-
- fr_pair_t *head = NULL;
- fr_pair_t *vp;
- unsigned int cnt = 0;
-
- if (count == 0) count = UINT_MAX;
-
- if (unlikely(!parent_da)) {
- fr_strerror_const("No search attribute provided");
- return -1;
- }
-
- fr_cursor_talloc_init(&tmp, &head, fr_pair_t);
- for (vp = fr_cursor_iter_by_ancestor_init(&src, from, parent_da);
- vp && (cnt < count);
- vp = fr_cursor_next(&src), cnt++) {
- VP_VERIFY(vp);
- vp = fr_pair_copy(ctx, vp);
- if (!vp) {
- fr_pair_list_free(&head);
- return -1;
- }
- fr_cursor_append(&tmp, vp); /* fr_pair_list_copy sets next pointer to NULL */
- }
-
- if (!*to) { /* Fast Path */
- *to = head;
- } else {
- fr_cursor_talloc_init(&dst, to, fr_pair_t);
- fr_cursor_head(&tmp);
- fr_cursor_merge(&dst, &tmp);
- }
-
- return cnt;
-}
-
-/** Free/zero out value (or children) of a given VP
- *
- * @param[in] vp to clear value from.
- */
-void fr_pair_value_clear(fr_pair_t *vp)
-{
- fr_pair_t *child;
- fr_cursor_t cursor;
-
- switch (vp->da->type) {
- default:
- fr_value_box_clear_value(&vp->data);
- break;
-
- case FR_TYPE_STRUCTURAL:
- if (!vp->vp_group) return;
-
- for (child = fr_cursor_init(&cursor, &vp->vp_group);
- child;
- child = fr_cursor_next(&cursor)) {
- fr_pair_value_clear(child);
- talloc_free(child);
- }
- break;
- }
-}
-
-/** Copy the value from one pair to another
- *
- * @param[out] dst where to copy the value to.
- * will clear assigned value.
- * @param[in] src where to copy the value from
- * Must have an assigned value.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_copy(fr_pair_t *dst, fr_pair_t *src)
-{
- if (!fr_cond_assert(src->data.type != FR_TYPE_INVALID)) return -1;
-
- if (dst->data.type != FR_TYPE_INVALID) fr_value_box_clear(&dst->data);
- fr_value_box_copy(dst, &dst->data, &src->data);
-
- return 0;
-}
-
-/** Convert string value to native attribute value
- *
- * @param[in] vp to assign value to.
- * @param[in] value string to convert. Binary safe for variable
- * length values if len is provided.
- * @param[in] inlen may be < 0 in which case strlen(len) is used
- * to determine length, else inlen should be the
- * length of the string or sub string to parse.
- * @param[in] quote character used set unescape mode. @see fr_value_str_unescape.
- * @param[in] tainted Whether the value came from a trusted source.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_from_str(fr_pair_t *vp, char const *value, ssize_t inlen, char quote, bool tainted)
-{
- fr_type_t type;
-
- if (!value) return -1;
-
- type = vp->da->type;
-
- /*
- * This is not yet supported because the rest of the APIs
- * to parse pair names, etc. don't yet enforce "inlen".
- * This is likely not a problem in practice, but we
- * haven't yet audited the uses of this function for that
- * behavior.
- */
- switch (type) {
- case FR_TYPE_STRUCTURAL:
- fr_strerror_printf("Attributes of type '%s' are not yet supported",
- fr_table_str_by_value(fr_value_box_type_table, type, "<INVALID>"));
- return -1;
-
- default:
- break;
- }
-
- /*
- * We presume that the input data is from a double quoted
- * string, and needs unescaping
- */
- if (fr_value_box_from_str(vp, &vp->data, &type, vp->da, value, inlen, quote, tainted) < 0) return -1;
-
- /*
- * If we parsed to a different type than the DA associated with
- * the fr_pair_t we now need to fixup the DA.
- *
- * This is for types COMBO_IP. fr_pair_ts have a fixed
- * data type, and not a polymorphic one. So instead of
- * hacking polymorphic crap through the entire server
- * code, we have this hack to make them static.
- */
- if (type != vp->da->type) {
- fr_dict_attr_t const *da;
-
- da = fr_dict_attr_by_type(vp->da, type);
- if (!da) {
- fr_strerror_printf("Cannot find %s variant of attribute \"%s\"",
- fr_table_str_by_value(fr_value_box_type_table, type, "<INVALID>"), vp->da->name);
- return -1;
- }
- vp->da = da;
- vp->data.enumv = da;
- }
- vp->type = VT_DATA;
-
- VP_VERIFY(vp);
-
- return 0;
-}
-
-/** Copy data into an "string" data type.
- *
- * @note vp->da must be of type FR_TYPE_STRING.
- *
- * @param[in,out] vp to update
- * @param[in] src data to copy
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_strdup(fr_pair_t *vp, char const *src)
-{
- int ret;
-
- if (!fr_cond_assert(vp->da->type == FR_TYPE_STRING)) return -1;
-
- fr_value_box_clear(&vp->data); /* Free any existing buffers */
- ret = fr_value_box_strdup(vp, &vp->data, vp->da, src, false);
- if (ret == 0) {
- vp->type = VT_DATA;
- VP_VERIFY(vp);
- }
-
- return ret;
-}
-
-/** Assign a buffer containing a nul terminated string to a vp, but don't copy it
- *
- * @param[in] vp to assign string to.
- * @param[in] src to copy string from.
- * @param[in] tainted Whether the value came from a trusted source.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_strdup_shallow(fr_pair_t *vp, char const *src, bool tainted)
-{
- if (!fr_cond_assert(vp->da->type == FR_TYPE_STRING)) return -1;
-
- fr_value_box_clear(&vp->data);
- fr_value_box_strdup_shallow(&vp->data, vp->da, src, tainted);
-
- vp->type = VT_DATA;
- VP_VERIFY(vp);
-
- return 0;
-}
-
-/** Trim the length of the string buffer to match the length of the C string
- *
- * @param[in,out] vp to trim.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_strtrim(fr_pair_t *vp)
-{
- int ret;
-
- if (!fr_cond_assert(vp->da->type == FR_TYPE_STRING)) return -1;
-
- ret = fr_value_box_strtrim(vp, &vp->data);
- if (ret == 0) {
- vp->type = VT_DATA;
- VP_VERIFY(vp);
- }
-
- return ret;
-}
-
-/** Print data into an "string" data type.
- *
- * @note vp->da must be of type FR_TYPE_STRING.
- *
- * @param[in,out] vp to update
- * @param[in] fmt the format string
- */
-int fr_pair_value_aprintf(fr_pair_t *vp, char const *fmt, ...)
-{
- int ret;
- va_list ap;
-
- if (!fr_cond_assert(vp->da->type == FR_TYPE_STRING)) return -1;
-
- fr_value_box_clear(&vp->data);
- va_start(ap, fmt);
- ret = fr_value_box_vasprintf(vp, &vp->data, vp->da, false, fmt, ap);
- va_end(ap);
-
- if (ret == 0) {
- vp->type = VT_DATA;
- VP_VERIFY(vp);
- return 0;
- }
-
- return -1;
-}
-
-/** Pre-allocate a memory buffer for a "string" type value pair
- *
- * @note Will clear existing values (including buffers).
- *
- * @param[in,out] vp to update
- * @param[out] out If non-null will be filled with a pointer to the
- * new buffer.
- * @param[in] size of the data.
- * @param[in] tainted Whether the value came from a trusted source.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_bstr_alloc(fr_pair_t *vp, char **out, size_t size, bool tainted)
-{
- int ret;
-
- if (!fr_cond_assert(vp->da->type == FR_TYPE_STRING)) return -1;
-
- fr_value_box_clear(&vp->data); /* Free any existing buffers */
- ret = fr_value_box_bstr_alloc(vp, out, &vp->data, vp->da, size, tainted);
- if (ret == 0) {
- vp->type = VT_DATA;
- VP_VERIFY(vp);
- }
-
- return ret;
-}
-
-/** Change the length of a buffer for a "string" type value pair
- *
- * @param[in,out] vp to update
- * @param[out] out If non-null will be filled with a pointer to the
- * new buffer.
- * @param[in] size of the data.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_bstr_realloc(fr_pair_t *vp, char **out, size_t size)
-{
- int ret;
-
- if (!fr_cond_assert(vp->da->type == FR_TYPE_STRING)) return -1;
-
- ret = fr_value_box_bstr_realloc(vp, out, &vp->data, size);
- if (ret == 0) {
- vp->type = VT_DATA;
- VP_VERIFY(vp);
- }
-
- return ret;
-}
-
-/** Copy data into a "string" type value pair
- *
- * @note unlike the original strncpy, this function does not stop
- * if it finds \0 bytes embedded in the string.
- *
- * @note vp->da must be of type FR_TYPE_STRING.
- *
- * @param[in,out] vp to update.
- * @param[in] src data to copy.
- * @param[in] len of data to copy.
- * @param[in] tainted Whether the value came from a trusted source.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_bstrndup(fr_pair_t *vp, char const *src, size_t len, bool tainted)
-{
- int ret;
-
- if (!fr_cond_assert(vp->da->type == FR_TYPE_STRING)) return -1;
-
- fr_value_box_clear(&vp->data);
- ret = fr_value_box_bstrndup(vp, &vp->data, vp->da, src, len, tainted);
- if (ret == 0) {
- vp->type = VT_DATA;
- VP_VERIFY(vp);
- }
-
- return ret;
-}
-
-/** Copy a nul terminated talloced buffer a "string" type value pair
- *
- * The buffer must be \0 terminated, or an error will be returned.
- *
- * @param[in,out] vp to update.
- * @param[in] src a talloced nul terminated buffer.
- * @param[in] tainted Whether the value came from a trusted source.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_bstrdup_buffer(fr_pair_t *vp, char const *src, bool tainted)
-{
- int ret;
-
- if (!fr_cond_assert(vp->da->type == FR_TYPE_STRING)) return -1;
-
- fr_value_box_clear(&vp->data);
- ret = fr_value_box_bstrdup_buffer(vp, &vp->data, vp->da, src, tainted);
- if (ret == 0) {
- vp->type = VT_DATA;
- VP_VERIFY(vp);
- }
-
- return ret;
-}
-
-/** Assign a string to a "string" type value pair
- *
- * @param[in] vp to assign new buffer to.
- * @param[in] src a string.
- * @param[in] len of src.
- * @param[in] tainted Whether the value came from a trusted source.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_bstrndup_shallow(fr_pair_t *vp, char const *src, size_t len, bool tainted)
-{
- if (!fr_cond_assert(vp->da->type == FR_TYPE_STRING)) return -1;
-
- fr_value_box_clear(&vp->data);
- fr_value_box_bstrndup_shallow(&vp->data, vp->da, src, len, tainted);
- vp->type = VT_DATA;
- VP_VERIFY(vp);
-
- return 0;
-}
-
-/** Assign a string to a "string" type value pair
- *
- * @param[in] vp to assign new buffer to.
- * @param[in] src a string.
- * @param[in] tainted Whether the value came from a trusted source.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_bstrdup_buffer_shallow(fr_pair_t *vp, char const *src, bool tainted)
-{
- int ret;
-
- if (!fr_cond_assert(vp->da->type == FR_TYPE_STRING)) return -1;
-
- fr_value_box_clear(&vp->data);
- ret = fr_value_box_bstrdup_buffer_shallow(NULL, &vp->data, vp->da, src, tainted);
- if (ret == 0) {
- vp->type = VT_DATA;
- VP_VERIFY(vp);
- }
-
- return ret;
-}
-
-/** Append bytes from a buffer to an existing "string" type value pair
- *
- * @param[in,out] vp to update.
- * @param[in] src data to copy.
- * @param[in] len of data to copy.
- * @param[in] tainted Whether the value came from a trusted source.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_bstrn_append(fr_pair_t *vp, char const *src, size_t len, bool tainted)
-{
- int ret;
-
- if (!fr_cond_assert(vp->da->type == FR_TYPE_STRING)) return -1;
-
- ret = fr_value_box_bstrn_append(vp, &vp->data, src, len, tainted);
- if (ret == 0) {
- vp->type = VT_DATA;
- VP_VERIFY(vp);
- }
-
- return ret;
-}
-
-/** Append a talloced buffer to an existing "string" type value pair
- *
- * @param[in,out] vp to update.
- * @param[in] src a talloced nul terminated buffer.
- * @param[in] tainted Whether the value came from a trusted source.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_bstr_append_buffer(fr_pair_t *vp, char const *src, bool tainted)
-{
- int ret;
-
- if (!fr_cond_assert(vp->da->type == FR_TYPE_STRING)) return -1;
-
- ret = fr_value_box_bstr_append_buffer(vp, &vp->data, src, tainted);
- if (ret == 0) {
- vp->type = VT_DATA;
- VP_VERIFY(vp);
- }
-
- return ret;
-}
-
-/** Pre-allocate a memory buffer for a "octets" type value pair
- *
- * @note Will clear existing values (including buffers).
- *
- * @param[in,out] vp to update
- * @param[out] out If non-null will be filled with a pointer to the
- * new buffer.
- * @param[in] size of the data.
- * @param[in] tainted Whether the value came from a trusted source.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_mem_alloc(fr_pair_t *vp, uint8_t **out, size_t size, bool tainted)
-{
- int ret;
-
- if (!fr_cond_assert(vp->da->type == FR_TYPE_OCTETS)) return -1;
-
- fr_value_box_clear(&vp->data); /* Free any existing buffers */
- ret = fr_value_box_mem_alloc(vp, out, &vp->data, vp->da, size, tainted);
- if (ret == 0) {
- vp->type = VT_DATA;
- VP_VERIFY(vp);
- }
-
- return ret;
-}
-
-/** Change the length of a buffer for a "octets" type value pair
- *
- * @param[in,out] vp to update
- * @param[out] out If non-null will be filled with a pointer to the
- * new buffer.
- * @param[in] size of the data.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_mem_realloc(fr_pair_t *vp, uint8_t **out, size_t size)
-{
- int ret;
-
- if (!fr_cond_assert(vp->da->type == FR_TYPE_OCTETS)) return -1;
-
- ret = fr_value_box_mem_realloc(vp, out, &vp->data, size);
- if (ret == 0) {
- vp->type = VT_DATA;
- VP_VERIFY(vp);
- }
-
- return ret;
-}
-
-/** Copy data into an "octets" data type.
- *
- * @note Will clear existing values (including buffers).
- *
- * @param[in,out] vp to update
- * @param[in] src data to copy
- * @param[in] size of the data.
- * @param[in] tainted Whether the value came from a trusted source.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_memdup(fr_pair_t *vp, uint8_t const *src, size_t size, bool tainted)
-{
- int ret;
-
- if (!fr_cond_assert(vp->da->type == FR_TYPE_OCTETS)) return -1;
-
- fr_value_box_clear(&vp->data); /* Free any existing buffers */
- ret = fr_value_box_memdup(vp, &vp->data, vp->da, src, size, tainted);
- if (ret == 0) {
- vp->type = VT_DATA;
- VP_VERIFY(vp);
- }
-
- return ret;
-}
-
-/** Copy data from a talloced buffer into an "octets" data type.
- *
- * @note Will clear existing values (including buffers).
- *
- * @param[in,out] vp to update
- * @param[in] src data to copy
- * @param[in] tainted Whether the value came from a trusted source.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_memdup_buffer(fr_pair_t *vp, uint8_t const *src, bool tainted)
-{
- int ret;
-
- if (!fr_cond_assert(vp->da->type == FR_TYPE_OCTETS)) return -1;
-
- fr_value_box_clear(&vp->data); /* Free any existing buffers */
- ret = fr_value_box_memdup_buffer(vp, &vp->data, vp->da, src, tainted);
- if (ret == 0) {
- vp->type = VT_DATA;
- VP_VERIFY(vp);
- }
-
- return ret;
-}
-
-/** Assign a buffer to a "octets" type value pair
- *
- * @param[in] vp to assign new buffer to.
- * @param[in] src data to copy.
- * @param[in] len of src.
- * @param[in] tainted Whether the value came from a trusted source.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_memdup_shallow(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
-{
- if (!fr_cond_assert(vp->da->type == FR_TYPE_OCTETS)) return -1;
-
- fr_value_box_clear(&vp->data);
- fr_value_box_memdup_shallow(&vp->data, vp->da, src, len, tainted);
- vp->type = VT_DATA;
- VP_VERIFY(vp);
-
- return 0;
-}
-
-/** Assign a talloced buffer to a "octets" type value pair
- *
- * @param[in] vp to assign new buffer to.
- * @param[in] src data to copy.
- * @param[in] tainted Whether the value came from a trusted source.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_memdup_buffer_shallow(fr_pair_t *vp, uint8_t const *src, bool tainted)
-{
- if (!fr_cond_assert(vp->da->type == FR_TYPE_OCTETS)) return -1;
-
- fr_value_box_clear(&vp->data);
- fr_value_box_memdup_buffer_shallow(NULL, &vp->data, vp->da, src, tainted);
- vp->type = VT_DATA;
- VP_VERIFY(vp);
-
- return 0;
-}
-
-
-/** Append bytes from a buffer to an existing "octets" type value pair
- *
- * @param[in,out] vp to update.
- * @param[in] src data to copy.
- * @param[in] len of data to copy.
- * @param[in] tainted Whether the value came from a trusted source.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_mem_append(fr_pair_t *vp, uint8_t *src, size_t len, bool tainted)
-{
- int ret;
-
- if (!fr_cond_assert(vp->da->type == FR_TYPE_OCTETS)) return -1;
-
- ret = fr_value_box_mem_append(vp, &vp->data, src, len, tainted);
- if (ret == 0) {
- vp->type = VT_DATA;
- VP_VERIFY(vp);
- }
-
- return ret;
-}
-
-/** Append a talloced buffer to an existing "octets" type value pair
- *
- * @param[in,out] vp to update.
- * @param[in] src data to copy.
- * @param[in] tainted Whether the value came from a trusted source.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_pair_value_mem_append_buffer(fr_pair_t *vp, uint8_t *src, bool tainted)
-{
- int ret;
-
- if (!fr_cond_assert(vp->da->type == FR_TYPE_OCTETS)) return -1;
-
- ret = fr_value_box_mem_append_buffer(vp, &vp->data, src, tainted);
- if (ret == 0) {
- vp->type = VT_DATA;
- VP_VERIFY(vp);
- }
-
- return ret;
-}
-
-/** Return a const buffer for an enum type attribute
- *
- * Where the vp type is numeric but does not have any enumv, or its value
- * does not map to an enumv, the integer value of the pair will be printed
- * to buff, and a pointer to buff will be returned.
- *
- * @param[in] vp to print.
- * @param[in] buff to print integer value to.
- * @return a talloced buffer.
- */
-char const *fr_pair_value_enum(fr_pair_t const *vp, char buff[20])
-{
- char const *str;
- fr_dict_enum_t const *enumv = NULL;
-
- switch (vp->vp_type) {
- case FR_TYPE_NUMERIC:
- break;
-
- default:
- fr_strerror_printf("Pair %s is not numeric", vp->da->name);
- return NULL;
- }
-
- if (vp->da->flags.has_value) switch (vp->vp_type) {
- case FR_TYPE_BOOL:
- return vp->vp_bool ? "yes" : "no";
-
- default:
- enumv = fr_dict_enum_by_value(vp->da, &vp->data);
- break;
- }
-
- if (!enumv) {
- fr_pair_print_value_quoted(&FR_SBUFF_OUT(buff, 20), vp, T_BARE_WORD);
- str = buff;
- } else {
- str = enumv->name;
- }
-
- return str;
-}
-
-/** Get value box of a VP, optionally prefer enum value.
- *
- * Get the data value box of the given VP. If 'e' is set to 1 and the VP has an
- * enum value, this will be returned instead. Otherwise it will be set to the
- * value box of the VP itself.
- *
- * @param[out] out pointer to a value box.
- * @param[in] vp to print.
- * @return 1 if the enum value has been used, 0 otherwise, -1 on error.
- */
-int fr_pair_value_enum_box(fr_value_box_t const **out, fr_pair_t *vp)
-{
- fr_dict_enum_t const *dv;
-
- if (!out || !vp ) return -1;
-
- if (vp->da && vp->da->flags.has_value &&
- (dv = fr_dict_enum_by_value(vp->da, &vp->data))) {
- *out = dv->value;
- return 1;
- }
-
- *out = &vp->data;
- return 0;
-}
-
-#ifdef WITH_VERIFY_PTR
-/*
- * Verify a fr_pair_t
- */
-void fr_pair_verify(char const *file, int line, fr_pair_t const *vp)
-{
- if (!vp) {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t pointer was NULL", file, line);
- }
-
- (void) talloc_get_type_abort_const(vp, fr_pair_t);
-
- if (!vp->da) {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t da pointer was NULL", file, line);
- }
-
- fr_dict_verify(file, line, vp->da);
- if (vp->data.enumv) fr_dict_verify(file, line, vp->data.enumv);
-
- if (vp->vp_ptr) switch (vp->vp_type) {
- case FR_TYPE_OCTETS:
- {
- size_t len;
- TALLOC_CTX *parent;
-
- if (!talloc_get_type(vp->vp_ptr, uint8_t)) {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t \"%s\" data buffer type should be "
- "uint8_t but is %s\n", file, line, vp->da->name, talloc_get_name(vp->vp_ptr));
- }
-
- len = talloc_array_length(vp->vp_octets);
- if (vp->vp_length > len) {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t \"%s\" length %zu is greater than "
- "uint8_t data buffer length %zu\n", file, line, vp->da->name, vp->vp_length, len);
- }
-
- parent = talloc_parent(vp->vp_ptr);
- if (parent != vp) {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t \"%s\" char buffer is not "
- "parented by fr_pair_t %p, instead parented by %p (%s)\n",
- file, line, vp->da->name,
- vp, parent, parent ? talloc_get_name(parent) : "NULL");
- }
- }
- break;
-
- case FR_TYPE_STRING:
- {
- size_t len;
- TALLOC_CTX *parent;
-
- if (!talloc_get_type(vp->vp_ptr, char)) {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t \"%s\" data buffer type should be "
- "char but is %s", file, line, vp->da->name, talloc_get_name(vp->vp_ptr));
- }
-
- len = (talloc_array_length(vp->vp_strvalue) - 1);
- if (vp->vp_length > len) {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t \"%s\" length %zu is greater than "
- "char buffer length %zu", file, line, vp->da->name, vp->vp_length, len);
- }
-
- if (vp->vp_strvalue[vp->vp_length] != '\0') {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t \"%s\" char buffer not \\0 "
- "terminated", file, line, vp->da->name);
- }
-
- parent = talloc_parent(vp->vp_ptr);
- if (parent != vp) {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t \"%s\" char buffer is not "
- "parented by fr_pair_t %p, instead parented by %p (%s)",
- file, line, vp->da->name,
- vp, parent, parent ? talloc_get_name(parent) : "NULL");
- fr_fatal_assert_fail("0");
- }
- }
- break;
-
- case FR_TYPE_IPV4_ADDR:
- if (vp->vp_ip.af != AF_INET) {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t \"%s\" address family is not "
- "set correctly for IPv4 address. Expected %i got %i",
- file, line, vp->da->name,
- AF_INET, vp->vp_ip.af);
- }
- if (vp->vp_ip.prefix != 32) {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t \"%s\" address prefix "
- "set correctly for IPv4 address. Expected %i got %i",
- file, line, vp->da->name,
- 32, vp->vp_ip.prefix);
- }
- break;
-
- case FR_TYPE_IPV6_ADDR:
- if (vp->vp_ip.af != AF_INET6) {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t \"%s\" address family is not "
- "set correctly for IPv6 address. Expected %i got %i",
- file, line, vp->da->name,
- AF_INET6, vp->vp_ip.af);
- }
- if (vp->vp_ip.prefix != 128) {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t \"%s\" address prefix "
- "set correctly for IPv6 address. Expected %i got %i",
- file, line, vp->da->name,
- 128, vp->vp_ip.prefix);
- }
- break;
-
- case FR_TYPE_STRUCTURAL:
- {
- fr_pair_t *child;
- fr_cursor_t cursor;
-
- for (child = fr_cursor_init(&cursor, &vp->vp_group);
- child;
- child = fr_cursor_next(&cursor)) {
- TALLOC_CTX *parent = talloc_parent(child);
-
- fr_fatal_assert_msg(parent == vp,
- "CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t \"%s\" should be parented "
- "by fr_pair_t \"%s\". Expected talloc parent %p (%s) got %p (%s)",
- file, line,
- child->da->name, vp->da->name,
- vp, talloc_get_name(vp),
- parent, talloc_get_name(parent));
-
- fr_pair_verify(file, line, child);
- }
- }
- break;
-
- default:
- break;
- }
-
- if (vp->da->flags.is_unknown || vp->da->flags.is_raw) {
- (void) talloc_get_type_abort_const(vp->da, fr_dict_attr_t);
- } else {
- fr_dict_attr_t const *da;
-
- da = vp->da;
-
- if (da->type == FR_TYPE_COMBO_IP_ADDR) {
- da = fr_dict_attr_by_type(vp->da, vp->da->type);
- if (!da) {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t attribute %p \"%s\" "
- "variant (%s) not found in global dictionary",
- file, line, vp->da, vp->da->name,
- fr_table_str_by_value(fr_value_box_type_table,
- vp->da->type, "<INVALID>"));
- }
- }
-
- if (da != vp->da) {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t "
- "dictionary pointer %p \"%s\" (%s) "
- "and global dictionary pointer %p \"%s\" (%s) differ",
- file, line, vp->da, vp->da->name,
- fr_table_str_by_value(fr_value_box_type_table, vp->da->type, "<INVALID>"),
- da, da->name,
- fr_table_str_by_value(fr_value_box_type_table, da->type, "<INVALID>"));
- }
- }
-
- if (vp->da->flags.is_raw || vp->da->flags.is_unknown) {
- if ((vp->da->parent->type != FR_TYPE_VSA) && (vp->data.type != FR_TYPE_VSA) && (vp->data.type != FR_TYPE_OCTETS)) {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t (raw/unknown) attribute %p \"%s\" "
- "data type incorrect. Expected %s, got %s",
- file, line, vp->da, vp->da->name,
- fr_table_str_by_value(fr_value_box_type_table, FR_TYPE_OCTETS, "<INVALID>"),
- fr_table_str_by_value(fr_value_box_type_table, vp->data.type, "<INVALID>"));
- }
- } else {
- switch (vp->da->type) {
- case FR_TYPE_VALUE:
- if (vp->da->type != vp->data.type) {
- char data_type_int[10], da_type_int[10];
-
- snprintf(data_type_int, sizeof(data_type_int), "%i", vp->data.type);
- snprintf(da_type_int, sizeof(da_type_int), "%i", vp->da->type);
-
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_pair_t attribute %p \"%s\" "
- "data type (%s) does not match da type (%s)",
- file, line, vp->da, vp->da->name,
- fr_table_str_by_value(fr_value_box_type_table, vp->data.type, data_type_int),
- fr_table_str_by_value(fr_value_box_type_table, vp->da->type, da_type_int));
- }
- break;
-
- default:
- break;
- }
- }
-}
-
-/*
- * Verify a pair list
- */
-void fr_pair_list_verify(char const *file, int line, TALLOC_CTX const *expected, fr_pair_list_t const *vps)
-{
- fr_cursor_t slow_cursor, fast_cursor;
- fr_pair_t *slow, *fast;
- TALLOC_CTX *parent;
-
- if (!*vps) return; /* Fast path */
-
- fr_cursor_init(&fast_cursor, vps);
-
- for (slow = fr_cursor_init(&slow_cursor, vps), fast = fr_cursor_init(&fast_cursor, vps);
- slow && fast;
- slow = fr_cursor_next(&fast_cursor), fast = fr_cursor_next(&fast_cursor)) {
- VP_VERIFY(slow);
-
- /*
- * Advances twice as fast as slow...
- */
- fast = fr_cursor_next(&fast_cursor);
- fr_fatal_assert_msg(fast != slow,
- "CONSISTENCY CHECK FAILED %s[%u]: Looping list found. Fast pointer hit "
- "slow pointer at \"%s\"",
- file, line, slow->da->name);
-
- parent = talloc_parent(slow);
- if (expected && (parent != expected)) {
- fr_log_talloc_report(expected);
- if (parent) fr_log_talloc_report(parent);
-
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: Expected fr_pair_t \"%s\" to be parented "
- "by %p (%s), instead parented by %p (%s)\n",
- file, line, slow->da->name,
- expected, talloc_get_name(expected),
- parent, parent ? talloc_get_name(parent) : "NULL");
- }
- }
-}
-#endif
-
-/** Mark up a list of VPs as tainted.
- *
- */
-void fr_pair_list_tainted(fr_pair_list_t *vps)
-{
- fr_pair_t *vp;
- fr_cursor_t cursor;
-
- if (!*vps) return;
-
- for (vp = fr_cursor_init(&cursor, vps);
- vp;
- vp = fr_cursor_next(&cursor)) {
- VP_VERIFY(vp);
-
- switch (vp->da->type) {
- case FR_TYPE_STRUCTURAL:
- fr_pair_list_tainted(&vp->vp_group);
- break;
-
- default:
- break;
- }
-
- vp->vp_tainted = true;
- }
-}
-
-
-/*
- * Parse a set of VPs from a value box.
- */
-fr_pair_t *fr_pair_list_afrom_box(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_value_box_t *box)
-{
- int comma = 0;
- char *p, *end, *last_comma = NULL;
- fr_pair_t *vps;
-
- fr_assert(box->type == FR_TYPE_STRING);
-
- /*
- * HACK: Replace '\n' with ',' so that
- * fr_pair_list_afrom_str() can parse the buffer in
- * one go (the proper way would be to
- * fix fr_pair_list_afrom_str(), but oh well).
- *
- * Note that we can mangle box->vb_strvalue, as it's
- * getting discarded immediately after this modification.
- */
- memcpy(&p, &box->vb_strvalue, sizeof(p)); /* const issues */
- end = p + talloc_array_length(box->vb_strvalue) - 1;
-
- while (p < end) {
- /*
- * Replace the first \n by a comma, and remaining
- * ones by a space.
- */
- if (*p == '\n') {
- if (comma) {
- *(p++) = ' ';
- } else {
- *p = ',';
- last_comma = p;
- p++;
- }
-
- comma = 0;
- continue;
- }
-
- if (*p == ',') {
- comma++;
- last_comma = p;
- p++;
- continue;
- }
-
- last_comma = NULL;
- p++;
- }
-
- /*
- * Don't end with a trailing comma
- */
- if (last_comma) *last_comma = '\0';
-
- vps = NULL;
- if (fr_pair_list_afrom_str(ctx, dict, box->vb_strvalue, &vps) == T_INVALID) {
- return NULL;
- }
-
- /*
- * Mark the attributes as tainted.
- */
- fr_pair_list_tainted(&vps);
- return vps;
-}
-
-/** Evaluation function for matching if vp matches a given da
- *
- * Can be used as a filter function for fr_cursor_filter_next()
- *
- * @param item pointer to a fr_pair_t
- * @param uctx da to match
- *
- * @return true if the pair matches the da
- */
-bool fr_pair_matches_da(void const *item, void const *uctx)
-{
- fr_pair_t const *vp = item;
- fr_dict_attr_t const *da = uctx;
- return da == vp->da;
-}
+++ /dev/null
-#pragma once
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-/** AVP manipulation and search API
- *
- * @file src/lib/util/pair.h
- *
- * @copyright 2015 The FreeRADIUS server project
- */
-#ifdef USE_DOUBLE_LIST
-#include <freeradius-devel/util/dpair.h>
-#else
-RCSIDH(pair_h, "$Id$")
-
-#include <freeradius-devel/build.h>
-#include <freeradius-devel/missing.h>
-#include <freeradius-devel/util/cursor.h>
-#include <freeradius-devel/util/value.h>
-#include <freeradius-devel/util/token.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef WITH_VERIFY_PTR
-# define VP_VERIFY(_x) fr_pair_verify(__FILE__, __LINE__, _x)
-# define LIST_VERIFY(_x) fr_pair_list_verify(__FILE__, __LINE__, NULL, _x)
-#else
-/*
- * Even if were building without WITH_VERIFY_PTR
- * the pointer must not be NULL when these various macros are used
- * so we can add some sneaky soft asserts.
- */
-# define VP_VERIFY(_x) fr_cond_assert(_x)
-/*
- * We don't assert the list head is non-NULL, as it's perfectly
- * valid to have an empty list.
- */
-# define LIST_VERIFY(_x)
-#endif
-
-/** The type of value a fr_pair_t contains
- *
- * This is used to add structure to nested fr_pair_ts and specifies what type of node it is (set, list, data).
- *
- * xlat is another type of data node which must first be expanded before use.
- */
-typedef enum value_type {
- VT_NONE = 0, //!< fr_pair_t has no value.
- VT_SET, //!< fr_pair_t has children.
- VT_LIST, //!< fr_pair_t has multiple values.
- VT_DATA, //!< fr_pair_t has a single value.
- VT_XLAT //!< valuepair value must be xlat expanded when it's
- //!< added to fr_pair_t tree.
-} value_type_t;
-
-typedef struct value_pair_s fr_pair_t;
-
-#ifdef USE_DOUBLE_LIST
-typedef struct {
- fr_dlist_head_t head;
-} fr_pair_list_t;
-#else
-typedef fr_pair_t* fr_pair_list_t;
-#endif
-
-/** Stores an attribute, a value and various bits of other data
- *
- * fr_pair_ts are the main data structure used in the server
- *
- * They also specify what behaviour should be used when the attribute is merged into a new list/tree.
- */
-struct value_pair_s {
- fr_dict_attr_t const *da; //!< Dictionary attribute defines the attribute
- //!< number, vendor and type of the attribute.
-
- fr_pair_t *next;
-
- /*
- * Legacy stuff that needs to die.
- */
- struct {
- fr_token_t op; //!< Operator to use when moving or inserting
- //!< valuepair into a list.
- char const *xlat; //!< Source string for xlat expansion.
- };
-
- value_type_t type; //!< Type of pointer in value union.
-
- /*
- * Pairs can have children or data but not both.
- */
- union {
- fr_value_box_t data; //!< The value of this pair.
- fr_pair_list_t children; //!< Nested attributes of this pair.
- };
-};
-
-/** A fr_pair_t in string format.
- *
- * Used to represent pairs in the legacy 'users' file format.
- */
-typedef struct {
- char l_opand[256]; //!< Left hand side of the pair.
- char r_opand[1024]; //!< Right hand side of the pair.
-
- fr_token_t quote; //!< Type of quoting around the r_opand.
-
- fr_token_t op; //!< Operator.
-} fr_pair_t_RAW;
-
-#define vp_strvalue data.vb_strvalue
-#define vp_octets data.vb_octets
-#define vp_ptr data.datum.ptr //!< Either octets or strvalue
-#define vp_length data.vb_length
-
-#define vp_ipv4addr data.vb_ip.addr.v4.s_addr
-#define vp_ipv6addr data.vb_ip.addr.v6.s6_addr
-#define vp_ip data.vb_ip
-#define vp_ifid data.vb_ifid
-#define vp_ether data.vb_ether
-
-#define vp_bool data.datum.boolean
-#define vp_uint8 data.vb_uint8
-#define vp_uint16 data.vb_uint16
-#define vp_uint32 data.vb_uint32
-#define vp_uint64 data.vb_uint64
-
-#define vp_int8 data.vb_int8
-#define vp_int16 data.vb_int16
-#define vp_int32 data.vb_int32
-#define vp_int64 data.vb_int64
-
-#define vp_float32 data.vb_float32
-#define vp_float64 data.vb_float64
-
-#define vp_date data.vb_date
-
-#define vp_group children
-
-#define vp_size data.datum.size
-#define vp_filter data.datum.filter
-
-#define vp_type data.type
-#define vp_tainted data.tainted
-
-#define ATTRIBUTE_EQ(_x, _y) ((_x && _y) && (_x->da == _y->da))
-
-# ifdef WITH_VERIFY_PTR
-void fr_pair_verify(char const *file, int line, fr_pair_t const *vp);
-void fr_pair_list_verify(char const *file, int line, TALLOC_CTX const *expected, fr_pair_list_t const *vps);
-# endif
-
-/*
- * Temporary hack to (a) get type-checking in macros, and (b) be fast
- */
-#define fr_pair_list_init(_list) (*(_list) = _Generic((_list), \
- fr_pair_list_t * : NULL, \
- default : (fr_pair_list_t *) NULL))
-
-/*
- * Temporary macro to point the head of a pair_list to a specific vp
- */
-#define fr_pair_list_set_head(_list, _vp) (_list = &_vp)
-
-/*
- * Temporary macro to point a pair_list to a single vp
- */
-#define fr_pair_list_single_value(_list, _vp) (_list = &_vp)
-
-/** Function to detect empty pair_list
- *
- * Returns true for empty lists.
- */
-static inline bool fr_pair_list_empty(fr_pair_list_t const *list)
-{
- return (!*list);
-}
-
-/* Allocation and management */
-fr_pair_list_t *fr_pair_list_alloc(TALLOC_CTX *ctx);
-
-fr_pair_t *fr_pair_alloc_null(TALLOC_CTX *ctx);
-
-fr_pair_t *fr_pair_root_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da) CC_HINT(nonnull(2));
-
-fr_pair_t *fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da) CC_HINT(nonnull(2));
-
-fr_pair_t *fr_pair_afrom_child_num(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, unsigned int attr);
-
-fr_pair_t *fr_pair_copy(TALLOC_CTX *ctx, fr_pair_t const *vp);
-
-void fr_pair_steal(TALLOC_CTX *ctx, fr_pair_t *vp);
-
-/** @hidecallergraph */
-void fr_pair_list_free(fr_pair_list_t *list);
-
-/* Searching and list modification */
-int fr_pair_to_unknown(fr_pair_t *vp);
-void *fr_pair_iter_next_by_da(void **prev, void *to_eval, void *uctx);
-
-void *fr_pair_iter_next_by_ancestor(void **prev, void *to_eval, void *uctx);
-bool fr_pair_matches_da(void const *item, void const *uctx);
-
-/** Initialise a cursor that will return only attributes matching the specified #fr_dict_attr_t
- *
- * @param[in] cursor to initialise.
- * @param[in] list to iterate over.
- * @param[in] da to search for.
- * @return
- * - The first matching pair.
- * - NULL if no pairs match.
- */
-static inline fr_pair_t *fr_cursor_iter_by_da_init(fr_cursor_t *cursor,
- fr_pair_list_t *list, fr_dict_attr_t const *da)
-{
- return fr_cursor_talloc_iter_init(cursor, list, fr_pair_iter_next_by_da, da, fr_pair_t);
-}
-
-/** Initialise a cursor that will return only attributes descended from the specified #fr_dict_attr_t
- *
- * @param[in] cursor to initialise.
- * @param[in] list to iterate over.
- * @param[in] da who's decentness to search for.
- * @return
- * - The first matching pair.
- * - NULL if no pairs match.
- */
-static inline fr_pair_t *fr_cursor_iter_by_ancestor_init(fr_cursor_t *cursor,
- fr_pair_list_t *list, fr_dict_attr_t const *da)
-{
- return fr_cursor_talloc_iter_init(cursor, list, fr_pair_iter_next_by_ancestor, da, fr_pair_t);
-}
-
-/**
- * @hidecallergraph
- */
-fr_pair_t *fr_pair_find_by_da(fr_pair_list_t const *head, fr_dict_attr_t const *da);
-
-fr_pair_t *fr_pair_find_by_num(fr_pair_list_t *head, unsigned int vendor, unsigned int attr);
-
-fr_pair_t *fr_pair_find_by_child_num(fr_pair_list_t *head, fr_dict_attr_t const *parent, unsigned int attr);
-
-void fr_pair_add(fr_pair_list_t *head, fr_pair_t *vp);
-
-void fr_pair_replace(fr_pair_list_t *head, fr_pair_t *add);
-
-void fr_pair_delete_by_child_num(fr_pair_list_t *head, fr_dict_attr_t const *parent, unsigned int attr);
-
-int fr_pair_add_by_da(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_pair_list_t *list, fr_dict_attr_t const *da);
-
-int fr_pair_update_by_da(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_pair_list_t *list, fr_dict_attr_t const *da);
-
-int fr_pair_delete_by_da(fr_pair_list_t *head, fr_dict_attr_t const *da);
-
-void fr_pair_delete(fr_pair_list_t *list, fr_pair_t const *vp);
-
-/* functions for FR_TYPE_STRUCTURAL */
-fr_pair_list_t *fr_pair_children(fr_pair_t *head);
-
-/* Sorting */
-typedef int8_t (*fr_cmp_t)(void const *a, void const *b);
-
-/** Compare two attributes using and operator.
- *
- * @return
- * - 1 if equal.
- * - 0 if not equal.
- * - -1 on failure.
- */
-#define fr_pair_cmp_op(_op, _a, _b) fr_value_box_cmp_op(_op, &_a->data, &_b->data)
-int8_t fr_pair_cmp_by_da(void const *a, void const *b);
-int8_t fr_pair_cmp_by_parent_num(void const *a, void const *b);
-int fr_pair_cmp(fr_pair_t *a, fr_pair_t *b);
-int fr_pair_list_cmp(fr_pair_list_t const *a, fr_pair_list_t const *b);
-void fr_pair_list_sort(fr_pair_list_t *vps, fr_cmp_t cmp) CC_HINT(nonnull);
-
-/* Filtering */
-void fr_pair_validate_debug(TALLOC_CTX *ctx, fr_pair_t const *failed[2]);
-bool fr_pair_validate(fr_pair_t const *failed[2], fr_pair_list_t *filter, fr_pair_list_t *list) CC_HINT(nonnull(2,3));
-bool fr_pair_validate_relaxed(fr_pair_t const *failed[2], fr_pair_list_t *filter, fr_pair_list_t *list) CC_HINT(nonnull(2,3));
-
-/* Lists */
-int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from);
-int fr_pair_list_copy_by_da(TALLOC_CTX *ctx, fr_pair_list_t *to,
- fr_pair_list_t *from, fr_dict_attr_t const *da, unsigned int count);
-int fr_pair_list_copy_by_ancestor(TALLOC_CTX *ctx, fr_pair_list_t *to,
- fr_pair_list_t *from, fr_dict_attr_t const *parent_da, unsigned int count);
-
-/** @name Pair to pair copying
- *
- * @{
- */
-void fr_pair_value_clear(fr_pair_t *vp);
-
-int fr_pair_value_copy(fr_pair_t *dst, fr_pair_t *src);
-/** @} */
-
-/** @name Assign and manipulate binary-unsafe C strings
- *
- * @{
- */
-int fr_pair_value_from_str(fr_pair_t *vp, char const *value, ssize_t len, char quote, bool tainted);
-
-int fr_pair_value_strdup(fr_pair_t *vp, char const *src);
-
-int fr_pair_value_strdup_shallow(fr_pair_t *vp, char const *src, bool tainted);
-
-int fr_pair_value_strtrim(fr_pair_t *vp);
-
-int fr_pair_value_aprintf(fr_pair_t *vp, char const *fmt, ...) CC_HINT(format (printf, 2, 3));
-/** @} */
-
-/** @name Assign and manipulate binary-safe strings
- *
- * @{
- */
-int fr_pair_value_bstr_alloc(fr_pair_t *vp, char **out, size_t size, bool tainted);
-
-int fr_pair_value_bstr_realloc(fr_pair_t *vp, char **out, size_t size);
-
-int fr_pair_value_bstrndup(fr_pair_t *vp, char const *src, size_t len, bool tainted);
-
-int fr_pair_value_bstrdup_buffer(fr_pair_t *vp, char const *src, bool tainted);
-
-int fr_pair_value_bstrndup_shallow(fr_pair_t *vp, char const *src, size_t len, bool tainted);
-
-int fr_pair_value_bstrdup_buffer_shallow(fr_pair_t *vp, char const *src, bool tainted);
-
-int fr_pair_value_bstrn_append(fr_pair_t *vp, char const *src, size_t len, bool tainted);
-
-int fr_pair_value_bstr_append_buffer(fr_pair_t *vp, char const *src, bool tainted);
- /** @} */
-
-/** @name Assign and manipulate octets strings
- *
- * @{
- */
-int fr_pair_value_mem_alloc(fr_pair_t *vp, uint8_t **out, size_t size, bool tainted);
-
-int fr_pair_value_mem_realloc(fr_pair_t *vp, uint8_t **out, size_t size);
-
-int fr_pair_value_memdup(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted);
-
-int fr_pair_value_memdup_buffer(fr_pair_t *vp, uint8_t const *src, bool tainted);
-
-int fr_pair_value_memdup_shallow(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted);
-
-int fr_pair_value_memdup_buffer_shallow(fr_pair_t *vp, uint8_t const *src, bool tainted);
-
-int fr_pair_value_mem_append(fr_pair_t *vp, uint8_t *src, size_t len, bool tainted);
-
-int fr_pair_value_mem_append_buffer(fr_pair_t *vp, uint8_t *src, bool tainted);
- /** @} */
-
-/** @name Enum functions
- *
- * @{
- */
-char const *fr_pair_value_enum(fr_pair_t const *vp, char buff[static 20]);
-
-int fr_pair_value_enum_box(fr_value_box_t const **out, fr_pair_t *vp);
-/** @} */
-
-/** @name Printing functions
- *
- * @{
- */
-ssize_t fr_pair_print_value_quoted(fr_sbuff_t *out,
- fr_pair_t const *vp, fr_token_t quote);
-
-static inline size_t fr_pair_aprint_value_quoted(TALLOC_CTX *ctx, char **out,
- fr_pair_t const *vp, fr_token_t quote)
-{
- SBUFF_OUT_TALLOC_FUNC_NO_LEN_DEF(fr_pair_print_value_quoted, vp, quote)
-}
-
-ssize_t fr_pair_print(fr_sbuff_t *out, fr_pair_t const *parent, fr_pair_t const *vp);
-
-static inline size_t fr_pair_aprint(TALLOC_CTX *ctx, char **out, fr_pair_t const *parent, fr_pair_t const *vp)
-{
- SBUFF_OUT_TALLOC_FUNC_NO_LEN_DEF(fr_pair_print, parent, vp)
-}
-
-void fr_pair_fprint(FILE *, fr_pair_t const *parent, fr_pair_t const *vp);
-
-#define fr_pair_list_log(_log, _list) _fr_pair_list_log(_log, 4, _list, __FILE__, __LINE__);
-void _fr_pair_list_log(fr_log_t const *log, int lvl, fr_pair_list_t const *list, char const *file, int line);
-
-void fr_pair_list_debug(fr_pair_list_t const *list);
-
-/** @} */
-
-void fr_pair_list_tainted(fr_pair_list_t *vps);
-fr_pair_t *fr_pair_list_afrom_box(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_value_box_t *box);
-
-/* Tokenization */
-typedef struct {
- TALLOC_CTX *ctx; //!< to allocate VPs in
- fr_dict_attr_t const *parent; //!< current attribute to allocate VPs in
- fr_cursor_t *cursor; //!< of VPs to add
-} fr_pair_ctx_t;
-
-ssize_t fr_pair_ctx_afrom_str(fr_pair_ctx_t *pair_ctx, char const *in, size_t inlen);
-void fr_pair_ctx_reset(fr_pair_ctx_t *pair_ctx, fr_dict_t const *dict);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
+++ /dev/null
-/*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-/** AVP manipulation and search API
- *
- * @file src/lib/util/pair.c
- *
- * @copyright 2000,2006,2015 The FreeRADIUS server project
- */
-RCSID("$Id$")
-
-#include <freeradius-devel/util/misc.h>
-#include <freeradius-devel/util/pair.h>
-#include <freeradius-devel/util/pair_legacy.h>
-#include <freeradius-devel/util/print.h>
-#include <freeradius-devel/util/proto.h>
-#include <freeradius-devel/util/regex.h>
-#include <freeradius-devel/util/talloc.h>
-
-#include <freeradius-devel/protocol/radius/rfc2865.h>
-#include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
-
-#include <ctype.h>
-
-static fr_sbuff_term_t const bareword_terminals =
- FR_SBUFF_TERMS(
- L("\t"),
- L("\n"),
- L(" "),
- L("!*"),
- L("!="),
- L("!~"),
- L("&&"), /* Logical operator */
- L(")"), /* Close condition/sub-condition */
- L("+="),
- L("-="),
- L(":="),
- L("<"),
- L("<="),
- L("=*"),
- L("=="),
- L("=~"),
- L(">"),
- L(">="),
- L("||"), /* Logical operator */
- );
-
-/** Mark a valuepair for xlat expansion
- *
- * Copies xlat source (unprocessed) string to valuepair value, and sets value type.
- *
- * @param vp to mark for expansion.
- * @param value to expand.
- * @return
- * - 0 if marking succeeded.
- * - -1 if #fr_pair_t already had a value, or OOM.
- */
-int fr_pair_mark_xlat(fr_pair_t *vp, char const *value)
-{
- char *raw;
-
- /*
- * valuepair should not already have a value.
- */
- if (vp->type != VT_NONE) {
- fr_strerror_const("Pair already has a value");
- return -1;
- }
-
- raw = talloc_typed_strdup(vp, value);
- if (!raw) {
- fr_strerror_const("Out of memory");
- return -1;
- }
-
- vp->type = VT_XLAT;
- vp->xlat = raw;
- vp->vp_length = 0;
-
- return 0;
-}
-
-/** Create a valuepair from an ASCII attribute and value
- *
- * Where the attribute name is in the form:
- * - Attr-%d
- * - Attr-%d.%d.%d...
- *
- * @param ctx for talloc
- * @param dict to user for partial resolution.
- * @param attribute name to parse.
- * @param value to parse (must be a hex string).
- * @param op to assign to new valuepair.
- * @return new #fr_pair_t or NULL on error.
- */
-static fr_pair_t *fr_pair_make_unknown(TALLOC_CTX *ctx, fr_dict_t const *dict,
- char const *attribute, char const *value,
- fr_token_t op)
-{
- fr_pair_t *vp;
- fr_dict_attr_t *n;
- fr_sbuff_t sbuff = FR_SBUFF_IN(attribute, strlen(attribute));
-
- vp = fr_pair_alloc_null(ctx);
- if (!vp) return NULL;
-
- if ((fr_dict_unknown_afrom_oid_substr(vp, NULL, &n, fr_dict_root(dict), &sbuff, NULL) <= 0) ||
- fr_sbuff_remaining(&sbuff)) {
- talloc_free(vp);
- return NULL;
- }
- vp->da = n;
-
- /*
- * No value, but ensure that we still set up vp->data properly.
- */
- if (!value) {
- value = "";
-
- } else if (strncasecmp(value, "0x", 2) != 0) {
- /*
- * Unknown attributes MUST be of type 'octets'
- */
- fr_strerror_printf("Unknown attribute \"%s\" requires a hex "
- "string, not \"%s\"", attribute, value);
- talloc_free(vp);
- return NULL;
- }
-
- if (fr_pair_value_from_str(vp, value, -1, '"', false) < 0) {
- talloc_free(vp);
- return NULL;
- }
-
- vp->op = (op == 0) ? T_OP_EQ : op;
- return vp;
-}
-
-/** Create a #fr_pair_t from ASCII strings
- *
- * Converts an attribute string identifier (with an optional tag qualifier)
- * and value string into a #fr_pair_t.
- *
- * The string value is parsed according to the type of #fr_pair_t being created.
- *
- * @param[in] ctx for talloc.
- * @param[in] dict to look attributes up in.
- * @param[in] vps list where the attribute will be added (optional)
- * @param[in] attribute name.
- * @param[in] value attribute value (may be NULL if value will be set later).
- * @param[in] op to assign to new #fr_pair_t.
- * @return a new #fr_pair_t.
- */
-fr_pair_t *fr_pair_make(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_pair_list_t *vps,
- char const *attribute, char const *value, fr_token_t op)
-{
- fr_dict_attr_t const *da;
- fr_pair_t *vp;
- char const *attrname = attribute;
-
- /*
- * It's not found in the dictionary, so we use
- * another method to create the attribute.
- */
- da = fr_dict_attr_search_by_qualified_oid(NULL, dict, attrname, true);
- if (!da) {
- vp = fr_pair_make_unknown(ctx, dict, attrname, value, op);
- if (!vp) return NULL;
-
- if (vps) fr_pair_add(vps, vp);
- return vp;
- }
-
- if (da->type == FR_TYPE_GROUP) {
- fr_strerror_const("Attributes of type 'group' are not supported");
- return NULL;
- }
-
- vp = fr_pair_afrom_da(ctx, da);
- if (!vp) return NULL;
- vp->op = (op == 0) ? T_OP_EQ : op;
-
- switch (vp->op) {
- case T_OP_CMP_TRUE:
- case T_OP_CMP_FALSE:
- fr_pair_value_clear(vp);
- value = NULL; /* ignore it! */
- break;
-
- /*
- * Regular expression comparison of integer attributes
- * does a STRING comparison of the names of their
- * integer attributes.
- */
- case T_OP_REG_EQ: /* =~ */
- case T_OP_REG_NE: /* !~ */
- {
-#ifndef HAVE_REGEX
- fr_strerror_const("Regular expressions are not supported");
- return NULL;
-#else
- ssize_t slen;
- regex_t *preg;
-
- /*
- * Someone else will fill in the value.
- */
- if (!value) break;
-
- talloc_free(vp);
-
- slen = regex_compile(ctx, &preg, value, strlen(value), NULL, false, true);
- if (slen <= 0) {
- fr_strerror_printf_push("Error at offset %zu compiling regex for %s", -slen, attribute);
- return NULL;
- }
- talloc_free(preg);
-
- vp = fr_pair_afrom_da(ctx, da);
- if (!vp) return NULL;
- vp->op = op;
-
- if (fr_pair_mark_xlat(vp, value) < 0) {
- talloc_free(vp);
- return NULL;
- }
-
- value = NULL; /* ignore it */
- break;
-#endif
- }
- default:
- break;
- }
-
- /*
- * We probably want to fix fr_pair_value_from_str to accept
- * octets as values for any attribute.
- */
- if (value && (fr_pair_value_from_str(vp, value, -1, '\"', true) < 0)) {
- talloc_free(vp);
- return NULL;
- }
-
- if (vps) fr_pair_add(vps, vp);
- return vp;
-}
-
-/** Read one line of attribute/value pairs into a list.
- *
- * The line may specify multiple attributes separated by commas.
- *
- * @note If the function returns #T_INVALID, an error has occurred and
- * @note the valuepair list should probably be freed.
- *
- * @param[in] ctx for talloc
- * @param[in] parent parent to start referencing from
- * @param[in] buffer to read valuepairs from.
- * @param[in] list where the parsed fr_pair_ts will be appended.
- * @param[in,out] token The last token we parsed
- * @param[in] depth the nesting depth for FR_TYPE_GROUP
- * @return
- * - <= 0 on failure.
- * - The number of bytes of name consumed on success.
- */
-static ssize_t fr_pair_list_afrom_substr(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, char const *buffer,
- fr_pair_list_t *list, fr_token_t *token, int depth)
-{
- fr_pair_t *vp, *head, **tail;
- char const *p, *next;
- fr_token_t last_token = T_INVALID;
- fr_pair_t_RAW raw;
- fr_dict_attr_t const *internal = fr_dict_root(fr_dict_internal());
-
- if (internal == parent) internal = NULL;
-
- /*
- * We allow an empty line.
- */
- if (buffer[0] == 0) {
- *token = T_EOL;
- return 0;
- }
-
- head = NULL;
- tail = &head;
-
- p = buffer;
- while (true) {
- ssize_t slen;
- fr_dict_attr_t const *da;
- fr_dict_attr_t *da_unknown = NULL;
- fr_skip_whitespace(p);
-
- /*
- * Stop at the end of the input, returning
- * whatever token was last read.
- */
- if (!*p) break;
-
- if (*p == '#') {
- last_token = T_EOL;
- break;
- }
-
- /*
- * Hacky hack...
- */
- if (strncmp(p, "raw.", 4) == 0) goto do_unknown;
-
- /*
- * Parse the name.
- */
- slen = fr_dict_attr_by_oid_substr(NULL, &da, parent,
- &FR_SBUFF_IN(p, strlen(p)), &bareword_terminals);
- if ((slen <= 0) && internal) {
- slen = fr_dict_attr_by_oid_substr(NULL, &da, internal,
- &FR_SBUFF_IN(p, strlen(p)), &bareword_terminals);
- }
- if (slen <= 0) {
- do_unknown:
- slen = fr_dict_unknown_afrom_oid_substr(ctx, NULL, &da_unknown, parent,
- &FR_SBUFF_IN(p, strlen(p)), &bareword_terminals);
- if (slen <= 0) {
- p += -slen;
-
- error:
- fr_pair_list_free(&head);
- *token = T_INVALID;
- return -(p - buffer);
- }
-
- da = da_unknown;
- }
-
- next = p + slen;
-
- if ((size_t) (next - p) >= sizeof(raw.l_opand)) {
- fr_dict_unknown_free(&da);
- fr_strerror_const("Attribute name too long");
- goto error;
- }
-
- memcpy(raw.l_opand, p, next - p);
- raw.l_opand[next - p] = '\0';
- raw.r_opand[0] = '\0';
-
- p = next;
- fr_skip_whitespace(p);
-
- /*
- * There must be an operator here.
- */
- raw.op = gettoken(&p, raw.r_opand, sizeof(raw.r_opand), false);
- if ((raw.op < T_EQSTART) || (raw.op > T_EQEND)) {
- fr_dict_unknown_free(&da);
- fr_strerror_printf("Expecting operator for %s", da->name);
- goto error;
- }
-
- fr_skip_whitespace(p);
-
- /*
- * Allow grouping attributes.
- */
- if ((da->type == FR_TYPE_GROUP) || (da->type == FR_TYPE_TLV) || (da->type == FR_TYPE_STRUCT)) {
- if (*p != '{') {
- fr_strerror_printf("Group list for %s MUST start with '{'", da->name);
- goto error;
- }
- p++;
-
- vp = fr_pair_afrom_da(ctx, da);
- if (!vp) goto error;
-
- /*
- * Find the new root attribute to start encoding from.
- */
- parent = fr_dict_attr_ref(da);
- if (!parent) parent = da;
-
- slen = fr_pair_list_afrom_substr(vp, parent, p, &vp->vp_group, &last_token, depth + 1);
- if (slen <= 0) {
- talloc_free(vp);
- goto error;
- }
-
- if (last_token != T_RCBRACE) {
- failed_group:
- fr_strerror_const("Failed to end group list with '}'");
- talloc_free(vp);
- goto error;
- }
-
- p += slen;
- fr_skip_whitespace(p);
- if (*p != '}') goto failed_group;
- p++;
-
- } else {
- fr_token_t quote;
- char const *q;
-
- /*
- * Get the RHS thing.
- */
- quote = gettoken(&p, raw.r_opand, sizeof(raw.r_opand), false);
- if (quote == T_EOL) {
- fr_strerror_printf("Failed to get value for %s", da->name);
- goto error;
- }
-
- switch (quote) {
- /*
- * Perhaps do xlat's
- */
- case T_DOUBLE_QUOTED_STRING:
- /*
- * Only report as double quoted if it contained valid
- * a valid xlat expansion.
- */
- q = strchr(raw.r_opand, '%');
- if (q && (q[1] == '{')) {
- raw.quote = quote;
- } else {
- raw.quote = T_SINGLE_QUOTED_STRING;
- }
- break;
-
- case T_SINGLE_QUOTED_STRING:
- case T_BACK_QUOTED_STRING:
- case T_BARE_WORD:
- raw.quote = quote;
- break;
-
- default:
- fr_strerror_printf("Failed to find expected value on right hand side in %s", da->name);
- goto error;
- }
-
- fr_skip_whitespace(p);
-
- /*
- * Regular expressions get sanity checked by pair_make().
- *
- * @todo - note that they will also be escaped,
- * so we may need to fix that later.
- */
- if ((raw.op == T_OP_REG_EQ) || (raw.op == T_OP_REG_NE)) {
- vp = fr_pair_afrom_da(ctx, da);
- if (!vp) goto error;
- vp->op = raw.op;
-
- fr_pair_value_bstrndup(vp, raw.r_opand, strlen(raw.r_opand), false);
- } else {
- /*
- * All other attributes get the name
- * parsed.
- */
- vp = fr_pair_afrom_da(ctx, da);
- if (!vp) goto error;
- vp->op = raw.op;
-
- /*
- * We don't care what the value is, so
- * ignore it.
- */
- if ((raw.op == T_OP_CMP_TRUE) || (raw.op == T_OP_CMP_FALSE)) goto next;
-
- /*
- * fr_pair_raw_from_str() only returns this when
- * the input looks like it needs to be xlat'd.
- */
- if (raw.quote == T_DOUBLE_QUOTED_STRING) {
- if (fr_pair_mark_xlat(vp, raw.r_opand) < 0) {
- talloc_free(vp);
- goto error;
- }
-
- /*
- * Parse it ourselves. The RHS
- * might NOT be tainted, but we
- * don't know. So just mark it
- * as such to be safe.
- */
- } else if (fr_pair_value_from_str(vp, raw.r_opand, -1, '"', true) < 0) {
- talloc_free(vp);
- goto error;
- }
- }
- }
-
- next:
- /*
- * Free the unknown attribute, we don't need it any more.
- */
- fr_dict_unknown_free(&da);
-
- *tail = vp;
- tail = &((*tail)->next);
-
- /*
- * Now look for EOL, hash, etc.
- */
- if (!*p || (*p == '#') || (*p == '\n')) {
- last_token = T_EOL;
- break;
- }
-
- /*
- * Check for nested groups.
- */
- if ((depth > 0) && (p[0] == ' ') && (p[1] == '}')) p++;
-
- /*
- * Stop at '}', too, if we're inside of a group.
- */
- if ((depth > 0) && (*p == '}')) {
- last_token = T_RCBRACE;
- break;
- }
-
- if (*p != ',') {
- fr_strerror_printf("Expected ',', got '%c' at offset %zu", *p, p - buffer);
- goto error;
- }
- p++;
- last_token = T_COMMA;
- }
-
- if (head) fr_pair_add(list, head);
-
- /*
- * And return the last token which we read.
- */
- *token = last_token;
- return p - buffer;
-}
-
-/** Read one line of attribute/value pairs into a list.
- *
- * The line may specify multiple attributes separated by commas.
- *
- * @note If the function returns #T_INVALID, an error has occurred and
- * @note the valuepair list should probably be freed.
- *
- * @param[in] ctx for talloc
- * @param[in] dict to resolve attributes in.
- * @param[in] buffer to read valuepairs from.
- * @param[in] list where the parsed fr_pair_ts will be appended.
- * @return the last token parsed, or #T_INVALID
- */
-fr_token_t fr_pair_list_afrom_str(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *buffer, fr_pair_list_t *list)
-{
- fr_token_t token;
-
- (void) fr_pair_list_afrom_substr(ctx, fr_dict_root(dict), buffer, list, &token, 0);
- return token;
-}
-
-/*
- * Read valuepairs from the fp up to End-Of-File.
- */
-int fr_pair_list_afrom_file(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_pair_list_t *out, FILE *fp, bool *pfiledone)
-{
- fr_token_t last_token = T_EOL;
- bool found = false;
- fr_cursor_t cursor;
- char buf[8192];
-
- fr_cursor_init(&cursor, out);
-
- while (fgets(buf, sizeof(buf), fp) != NULL) {
- fr_cursor_t append;
- fr_pair_t *vp;
-
- /*
- * If we get a '\n' by itself, we assume that's
- * the end of that VP list.
- */
- if (buf[0] == '\n') {
- if (found) {
- *pfiledone = false;
- return 0;
- }
- continue;
- }
-
- /*
- * Comments get ignored
- */
- if (buf[0] == '#') continue;
-
- /*
- * Read all of the attributes on the current line.
- *
- * If we get nothing but an EOL, it's likely OK.
- */
- vp = NULL;
- last_token = fr_pair_list_afrom_str(ctx, dict, buf, &vp);
- if (!vp) {
- if (last_token == T_EOL) break;
-
- /*
- * Didn't read anything, but the previous
- * line wasn't EOL. The input file has a
- * format error.
- */
- *pfiledone = false;
- vp = fr_cursor_head(&cursor);
- if (vp) fr_pair_list_free(&vp);
- *out = NULL;
- return -1;
- }
-
- found = true;
- fr_cursor_init(&append, &vp);
- fr_cursor_merge(&cursor, &append);
- (void) fr_cursor_tail(&cursor);
- }
-
- *pfiledone = true;
- return 0;
-}
-
-
-/** Move pairs from source list to destination list respecting operator
- *
- * @note This function does some additional magic that's probably not needed
- * in most places. Consider using radius_pairmove in server code.
- *
- * @note fr_pair_list_free should be called on the head of the source list to free
- * unmoved attributes (if they're no longer needed).
- *
- * @param[in,out] to destination list.
- * @param[in,out] from source list.
- *
- * @see radius_pairmove
- */
-void fr_pair_list_move(fr_pair_list_t *to, fr_pair_list_t *from)
-{
- fr_pair_t *i, *found;
- fr_pair_t *head_new, **tail_new;
- fr_pair_t **tail_from;
-
- if (!to || !from || !*from) return;
-
- /*
- * We're editing the "to" list while we're adding new
- * attributes to it. We don't want the new attributes to
- * be edited, so we create an intermediate list to hold
- * them during the editing process.
- */
- head_new = NULL;
- tail_new = &head_new;
-
- /*
- * We're looping over the "from" list, moving some
- * attributes out, but leaving others in place.
- */
- tail_from = from;
- while ((i = *tail_from) != NULL) {
- fr_pair_t *j;
-
- VP_VERIFY(i);
-
- /*
- * We never move Fall-Through.
- */
- if (fr_dict_attr_is_top_level(i->da) && (i->da->attr == FR_FALL_THROUGH)) {
- tail_from = &(i->next);
- continue;
- }
-
- /*
- * Unlike previous versions, we treat all other
- * attributes as normal. i.e. there's no special
- * treatment for passwords or Hint.
- */
-
- switch (i->op) {
- /*
- * Anything else are operators which
- * shouldn't occur. We ignore them, and
- * leave them in place.
- */
- default:
- tail_from = &(i->next);
- continue;
-
- /*
- * Add it to the "to" list, but only if
- * it doesn't already exist.
- */
- case T_OP_EQ:
- found = fr_pair_find_by_da(to, i->da);
- if (!found) goto do_add;
-
- tail_from = &(i->next);
- continue;
-
- /*
- * Add it to the "to" list, and delete any attribute
- * of the same vendor/attr which already exists.
- */
- case T_OP_SET:
- found = fr_pair_find_by_da(to, i->da);
- if (!found) goto do_add;
-
- switch (found->vp_type) {
- default:
- j = found->next;
- memcpy(found, i, sizeof(*found));
- found->next = j;
- break;
-
- case FR_TYPE_OCTETS:
- fr_pair_value_memdup_buffer(found, i->vp_octets, i->vp_tainted);
- fr_pair_value_clear(i);
- break;
-
- case FR_TYPE_STRING:
- fr_pair_value_bstrdup_buffer(found, i->vp_strvalue, i->vp_tainted);
- fr_pair_value_clear(i);
- break;
- }
-
- /*
- * Delete *all* of the attributes
- * of the same number.
- */
- fr_pair_delete_by_da(&found->next, found->da);
-
- /*
- * Remove this attribute from the
- * "from" list.
- */
- *tail_from = i->next;
- i->next = NULL;
- fr_pair_list_free(&i);
- continue;
-
- /*
- * Move it from the old list and add it
- * to the new list.
- */
- case T_OP_ADD:
- do_add:
- *tail_from = i->next;
- i->next = NULL;
- *tail_new = i;
- tail_new = &(i->next);
- continue;
- }
- } /* loop over the "from" list. */
-
- /*
- * Take the "new" list, and append it to the "to" list.
- */
- fr_pair_add(to, head_new);
-}