From: Nick Porter Date: Mon, 25 Jan 2021 11:46:26 +0000 (+0000) Subject: Remove util/pair.c and util/pair_legacy.c X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0379bbb0decd5b91a194de063b3614da32e6cc41;p=thirdparty%2Ffreeradius-server.git Remove util/pair.c and util/pair_legacy.c --- diff --git a/src/lib/util/pair.c b/src/lib/util/pair.c deleted file mode 100644 index 18ab3fae97..0000000000 --- a/src/lib/util/pair.c +++ /dev/null @@ -1,2504 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include - -/** 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, "")); - 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, ""), 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, "")); - } - } - - 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, ""), - da, da->name, - fr_table_str_by_value(fr_value_box_type_table, da->type, "")); - } - } - - 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, ""), - fr_table_str_by_value(fr_value_box_type_table, vp->data.type, "")); - } - } 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; -} diff --git a/src/lib/util/pair.h b/src/lib/util/pair.h deleted file mode 100644 index d5a3282747..0000000000 --- a/src/lib/util/pair.h +++ /dev/null @@ -1,424 +0,0 @@ -#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 -#else -RCSIDH(pair_h, "$Id$") - -#include -#include -#include -#include -#include - -#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 diff --git a/src/lib/util/pair_legacy.c b/src/lib/util/pair_legacy.c deleted file mode 100644 index 4dbcebf184..0000000000 --- a/src/lib/util/pair_legacy.c +++ /dev/null @@ -1,774 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -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); -} diff --git a/src/lib/util/pair_legacy.h b/src/lib/util/pair_legacy.h deleted file mode 100644 index 19689299aa..0000000000 --- a/src/lib/util/pair_legacy.h +++ /dev/null @@ -1,55 +0,0 @@ -#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 - */ - -/** Legacy API functions - DO NOT USE IN NEW CODE - * - * @file src/lib/util/pair.h - * - * @copyright 2015 The FreeRADIUS server project - */ -#ifdef USE_DOUBLE_LIST -#include -#else -RCSIDH(pair_legacy_h, "$Id$") - -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -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); - -int fr_pair_mark_xlat(fr_pair_t *vp, char const *value); - -fr_token_t fr_pair_list_afrom_str(TALLOC_CTX *ctx, fr_dict_t const *dict, - char const *buffer, fr_pair_list_t *head); -int fr_pair_list_afrom_file(TALLOC_CTX *ctx, fr_dict_t const *dict, - fr_pair_list_t *out, FILE *fp, bool *pfiledone); - -void fr_pair_list_move(fr_pair_list_t *to, fr_pair_list_t *from); - -#ifdef __cplusplus -} -#endif -#endif