/** Setup a cursor to iterate over attribute pairs * * @param cursor Where to initialise the cursor (uses existing structure). * @param const_vp to start from. * @return the attribute pointed to by vp. */ VALUE_PAIR *fr_pair_cursor_init(vp_cursor_t *cursor, VALUE_PAIR * const *const_vp) { VALUE_PAIR **vp; if (!const_vp || !cursor) { return NULL; } memset(cursor, 0, sizeof(*cursor)); memcpy(&vp, &const_vp, sizeof(vp)); /* stupid const hacks */ /* * Useful check to see if uninitialised memory is pointed * to by vp */ #ifndef NDEBUG if (*vp) VP_VERIFY(*vp); #endif memcpy(&cursor->first, &vp, sizeof(cursor->first)); cursor->current = *cursor->first; if (cursor->current) { VP_VERIFY(cursor->current); cursor->next = cursor->current->next; } return cursor->current; }
/** Iterate over a collection of VALUE_PAIRs of a given type in the pairlist * * Find the next attribute of a given type. If no fr_pair_cursor_next_by_* function * has been called on a cursor before, or the previous call returned * NULL, the search will start with the current attribute. Subsequent calls to * fr_pair_cursor_next_by_* functions will start the search from the previously * matched attribute. * * @param cursor to operate on. * @param attr number to match. * @param vendor number to match (0 for none vendor attribute). * @param tag to match. Either a tag number or TAG_ANY to match any tagged or * untagged attribute, TAG_NONE to match attributes without tags. * @return * - The next matching #VALUE_PAIR. * - NULL if no #VALUE_PAIR (s) match. */ VALUE_PAIR *fr_pair_cursor_next_by_num(vp_cursor_t *cursor, unsigned int vendor, unsigned int attr, int8_t tag) { VALUE_PAIR *i; if (!cursor->first) return NULL; if (!vendor) { /* * Find top-level attributes. */ for (i = cursor->found ? cursor->found->next : cursor->current; i != NULL; i = i->next) { VP_VERIFY(i); if (fr_dict_attr_is_top_level(i->da) && (i->da->attr == attr) && ATTR_TAG_MATCH(i, tag)) { break; } } } else { for (i = cursor->found ? cursor->found->next : cursor->current; i != NULL; i = i->next) { VP_VERIFY(i); if ((i->da->parent->type == FR_TYPE_VENDOR) && (i->da->attr == attr) && (fr_dict_vendor_num_by_da(i->da) == vendor) && ATTR_TAG_MATCH(i, tag)) { break; } } } return fr_pair_cursor_update(cursor, i); }
/** Rewind cursor to the start of the list * * @param cursor to operate on. * @return #VALUE_PAIR at the start of the list. */ VALUE_PAIR *fr_pair_cursor_head(vp_cursor_t *cursor) { if (!cursor->first) return NULL; cursor->current = *cursor->first; if (cursor->current) { VP_VERIFY(cursor->current); cursor->next = cursor->current->next; if (cursor->next) VP_VERIFY(cursor->next); cursor->found = NULL; } return cursor->current; }
/** Compare check and vp. May call the attribute comparison function. * * Unlike paircmp_pairs() this function will call any attribute-specific * comparison functions registered. * * @param[in] request Current request. * @param[in] request_list list pairs. * @param[in] check item to compare. * @param[in] check_list list. * @param[in] reply_list list. * @return * - 0 if check and vp are equal. * - -1 if vp value is less than check value. * - 1 is vp value is more than check value. */ static int paircmp_func(REQUEST *request, VALUE_PAIR *request_list, VALUE_PAIR *check, VALUE_PAIR *check_list, VALUE_PAIR **reply_list) { paircmp_t *c; VP_VERIFY(check); /* * Check for =* and !* and return appropriately */ if (check->op == T_OP_CMP_TRUE) return 0; if (check->op == T_OP_CMP_FALSE) return 1; /* * See if there is a special compare function. * * FIXME: use new RB-Tree code. */ for (c = cmp; c; c = c->next) { if (c->da == check->da) { return (c->compare)(c->instance, request, request_list, check, check_list, reply_list); } } if (!request) return -1; /* doesn't exist, don't compare it */ return paircmp_pairs(request, check, request_list); }
/* * Compare prefix/suffix. * * If they compare: * - if FR_STRIP_USER_NAME is present in check_list, * strip the username of prefix/suffix. * - if FR_STRIP_USER_NAME is not present in check_list, * add a FR_STRIPPED_USER_NAME to the request. */ static int prefix_suffix_cmp(UNUSED void *instance, REQUEST *request, VALUE_PAIR *req, VALUE_PAIR *check, VALUE_PAIR *check_list, UNUSED VALUE_PAIR **reply_list) { VALUE_PAIR *vp; char const *name; char rest[FR_MAX_STRING_LEN]; int len, namelen; int ret = -1; if (!request || !request->username) return -1; VP_VERIFY(check); name = request->username->vp_strvalue; RDEBUG3("Comparing name \"%s\" and check value \"%s\"", name, check->vp_strvalue); len = strlen(check->vp_strvalue); if (check->da == attr_prefix) { ret = strncmp(name, check->vp_strvalue, len); if (ret == 0) strlcpy(rest, name + len, sizeof(rest)); } else if (check->da == attr_suffix) { namelen = strlen(name); if (namelen >= len) { ret = strcmp(name + namelen - len, check->vp_strvalue); if (ret == 0) strlcpy(rest, name, namelen - len + 1); } } if (ret != 0) return ret; /* * If Strip-User-Name == No, then don't do any more. */ vp = fr_pair_find_by_da(check_list, attr_strip_user_name, TAG_ANY); if (vp && !vp->vp_uint32) return ret; /* * See where to put the stripped user name. */ vp = fr_pair_find_by_da(check_list, attr_stripped_user_name, TAG_ANY); if (!vp) { /* * If "request" is NULL, then the memory will be * lost! */ MEM(vp = fr_pair_afrom_da(request->packet, attr_stripped_user_name)); fr_pair_add(&req, vp); request->username = vp; } fr_pair_value_strcpy(vp, rest); return ret; }
static void dhcp_packet_debug(RADIUS_PACKET *packet, bool received) { fr_cursor_t cursor; char buffer[256]; char src_ipaddr[INET6_ADDRSTRLEN]; char dst_ipaddr[INET6_ADDRSTRLEN]; #if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION) char if_name[IFNAMSIZ]; #endif VALUE_PAIR *vp; if (!packet) return; /* * Client-specific debugging re-prints the input * packet into the client log. * * This really belongs in a utility library */ printf("%s %s Id %08x from %s%s%s:%i to %s%s%s:%i " #if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION) "%s%s%s" #endif "length %zu\n", received ? "Received" : "Sending", dhcp_message_types[packet->code], packet->id, packet->src_ipaddr.af == AF_INET6 ? "[" : "", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.addr, src_ipaddr, sizeof(src_ipaddr)), packet->src_ipaddr.af == AF_INET6 ? "]" : "", packet->src_port, packet->dst_ipaddr.af == AF_INET6 ? "[" : "", inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.addr, dst_ipaddr, sizeof(dst_ipaddr)), packet->dst_ipaddr.af == AF_INET6 ? "]" : "", packet->dst_port, #if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION) packet->if_index ? "via " : "", packet->if_index ? fr_ifname_from_ifindex(if_name, packet->if_index) : "", packet->if_index ? " " : "", #endif packet->data_len); for (vp = fr_cursor_init(&cursor, &packet->vps); vp; vp = fr_cursor_next(&cursor)) { VP_VERIFY(vp); fr_pair_snprint(buffer, sizeof(buffer), vp); printf("\t%s\n", buffer); } }
/* * Generic comparisons, via xlat. */ static int generic_cmp(UNUSED void *instance, REQUEST *request, VALUE_PAIR *req, VALUE_PAIR *check, UNUSED VALUE_PAIR *check_list, UNUSED VALUE_PAIR **reply_list) { VP_VERIFY(check); if ((check->op != T_OP_REG_EQ) && (check->op != T_OP_REG_NE)) { int rcode; char name[1024]; char value[1024]; VALUE_PAIR *vp; snprintf(name, sizeof(name), "%%{%s}", check->da->name); if (xlat_eval(value, sizeof(value), request, name, NULL, NULL) < 0) return 0; vp = fr_pair_afrom_da(req, check->da); vp->op = check->op; fr_pair_value_from_str(vp, value, -1, '"', false); /* * Paircmp returns 0 for failed comparison, 1 for succeeded -1 for error. */ rcode = fr_pair_cmp(check, vp); /* * We're being called from paircmp_func, * which wants 0 for success, and 1 for fail (sigh) * * We should really fix the API so that it is * consistent. i.e. the comparison callbacks should * return ONLY the resut of comparing A to B. * The radius_callback_cmp function should then * take care of using the operator to see if the * condition (A OP B) is true or not. * * This would also allow "<", etc. to work in the * callback functions... * * See rlm_ldap, ...groupcmp() for something that * returns 0 for matched, and 1 for didn't match. */ rcode = !rcode; fr_pair_list_free(&vp); return rcode; } /* * Will do the xlat for us */ return paircmp_pairs(request, check, NULL); }
/* * Compare the request packet type. */ static int packet_cmp(UNUSED void *instance, REQUEST *request, UNUSED VALUE_PAIR *req, VALUE_PAIR *check, UNUSED VALUE_PAIR *check_list, UNUSED VALUE_PAIR **reply_list) { VP_VERIFY(check); if (request->packet->code == check->vp_uint32) return 0; return 1; }
int8_t fr_dhcpv4_attr_cmp(void const *a, void const *b) { VALUE_PAIR const *my_a = a, *my_b = b; fr_dict_attr_t const *a_82, *b_82; VP_VERIFY(my_a); VP_VERIFY(my_b); /* * We can only use attribute numbers if we know they're * not nested attributes. * * @fixme We should be able to use my_a->da->parent->flags.is_root, * but the DHCP attributes are hacked into the server under a vendor * dictionary, so we can't. */ /* * DHCP-Message-Type is first, for simplicity. */ if (((my_a->da->parent->type != FR_TYPE_TLV) && (my_a->da == attr_dhcp_message_type)) && ((my_b->da->parent->type == FR_TYPE_TLV) || (my_b->da != attr_dhcp_message_type))) return -1; if (((my_a->da->parent->type == FR_TYPE_TLV) || (my_a->da != attr_dhcp_message_type)) && ((my_b->da->parent->type != FR_TYPE_TLV) && (my_b->da == attr_dhcp_message_type))) return +1; /* * Relay-Agent is last. * * Check if either of the options are descended from option 82. */ a_82 = fr_dict_parent_common(dhcp_option_82, my_a->da, true); b_82 = fr_dict_parent_common(dhcp_option_82, my_b->da, true); if (a_82 && !b_82) return +1; if (!a_82 && !b_82) return -1; return fr_pair_cmp_by_parent_num_tag(my_a, my_b); }
/** Insert a single VALUE_PAIR at the start of the list * * @note Will not advance cursor position to new attribute, but will set cursor * to this attribute, if it's the first one in the list. * * Insert a VALUE_PAIR at the start of the list. * * @param cursor to operate on. * @param vp to insert. */ void fr_pair_cursor_prepend(vp_cursor_t *cursor, VALUE_PAIR *vp) { if (!fr_cond_assert(cursor->first)) return; /* cursor must have been initialised */ if (!vp) return; VP_VERIFY(vp); LIST_VERIFY(*(cursor->first)); /* * Only allow one VP to by inserted at a time */ vp->next = NULL; /* * Cursor was initialised with a pointer to a NULL value_pair */ if (!*(cursor->first)) { *cursor->first = vp; cursor->current = vp; return; } /* * Append to the head of the list */ vp->next = *cursor->first; *cursor->first = vp; /* * Either current was never set, or something iterated to the * end of the attribute list. In both cases the newly inserted * VALUE_PAIR should be set as the current VALUE_PAIR. */ if (!cursor->current) cursor->current = vp; /* * If the next pointer was NULL, and the VALUE_PAIR * just added has a next pointer value, set the cursor's next * pointer to the VALUE_PAIR's next pointer. */ if (!cursor->next) cursor->next = cursor->current->next; LIST_VERIFY(*(cursor->first)); }
/** Iterate over attributes of a given DA in the pairlist * * Find the next attribute of a given type. If no fr_pair_cursor_next_by_* function * has been called on a cursor before, or the previous call returned * NULL, the search will start with the current attribute. Subsequent calls to * fr_pair_cursor_next_by_* functions will start the search from the previously * matched attribute. * * @note fr_dict_attr_t pointers are compared, not the attribute numbers and vendors. * * @param cursor to operate on. * @param da to match. * @param tag to match. Either a tag number or TAG_ANY to match any tagged or * untagged attribute, TAG_NONE to match attributes without tags. * @return * - Next matching #VALUE_PAIR. * - NULL if no #VALUE_PAIR (s) match. */ VALUE_PAIR *fr_pair_cursor_next_by_da(vp_cursor_t *cursor, fr_dict_attr_t const *da, int8_t tag) { VALUE_PAIR *i; if (!cursor->first) return NULL; for (i = cursor->found ? cursor->found->next : cursor->current; i != NULL; i = i->next) { VP_VERIFY(i); if ((i->da == da) && ATTR_TAG_MATCH(i, tag)) { break; } } return fr_pair_cursor_update(cursor, i); }
/** Print a list of VALUE_PAIRs. * * @param[in] lvl Debug lvl (1-4). * @param[in] request to read logging params from. * @param[in] vp to print. * @param[in] prefix (optional). */ void log_request_pair_list(fr_log_lvl_t lvl, REQUEST *request, VALUE_PAIR *vp, char const *prefix) { fr_cursor_t cursor; if (!vp || !request || !request->log.dst) return; if (!log_debug_enabled(L_DBG, lvl, request)) return; RINDENT(); for (vp = fr_cursor_init(&cursor, &vp); vp; vp = fr_cursor_next(&cursor)) { VP_VERIFY(vp); RDEBUGX(lvl, "%s%pP", prefix ? prefix : "&", vp); } REXDENT(); }
/** Iterate over a collection of VALUE_PAIRs of a given type in the pairlist * * Find the next attribute of a given type. If no fr_pair_cursor_next_by_* function * has been called on a cursor before, or the previous call returned * NULL, the search will start with the current attribute. Subsequent calls to * fr_pair_cursor_next_by_* functions will start the search from the previously * matched attribute. * * @note If the attribute specified by attr is not a child of the parent, NULL will be returned. * * @param cursor to operate on. * @param parent to search for attr in. * @param attr number to match. * @param tag to match. Either a tag number or TAG_ANY to match any tagged or * untagged attribute, TAG_NONE to match attributes without tags. * @return * - The next matching #VALUE_PAIR. - NULL if no #VALUE_PAIR (s) match (or attr doesn't exist). */ VALUE_PAIR *fr_pair_cursor_next_by_child_num(vp_cursor_t *cursor, fr_dict_attr_t const *parent, unsigned int attr, int8_t tag) { fr_dict_attr_t const *da; VALUE_PAIR *i; if (!cursor->first) return NULL; da = fr_dict_attr_child_by_num(parent, attr); if (!da) return NULL; for (i = cursor->found ? cursor->found->next : cursor->current; i != NULL; i = i->next) { VP_VERIFY(i); if ((i->da == da) && ATTR_TAG_MATCH(i, tag)) { break; } } return fr_pair_cursor_update(cursor, i); }
/** Advanced the cursor to the next VALUE_PAIR * * @param cursor to operate on. * @return * - Next #VALUE_PAIR. * - NULL if no more #VALUE_PAIR in the collection. */ VALUE_PAIR *fr_pair_cursor_next(vp_cursor_t *cursor) { if (!cursor->first) return NULL; cursor->current = cursor->next; if (cursor->current) { VP_VERIFY(cursor->current); /* * Set this now in case 'current' gets freed before * fr_pair_cursor_next is called again. */ cursor->next = cursor->current->next; /* * Next call to fr_pair_cursor_next_by_num will start from the current * position in the list, not the last found instance. */ cursor->found = NULL; } return cursor->current; }
/* * * Verify that a Perl SV is a string and save it in FreeRadius * Value Pair Format * */ static int pairadd_sv(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vps, char *key, SV *sv, FR_TOKEN op, const char *hash_name, const char *list_name) { char *val; VALUE_PAIR *vp; STRLEN len; if (!SvOK(sv)) return -1; val = SvPV(sv, len); vp = fr_pair_make(ctx, request->dict, vps, key, NULL, op); if (!vp) { fail: REDEBUG("Failed to create pair %s:%s %s %s", list_name, key, fr_int2str(fr_tokens_table, op, "<INVALID>"), val); return -1; } switch (vp->vp_type) { case FR_TYPE_STRING: fr_pair_value_bstrncpy(vp, val, len); break; case FR_TYPE_OCTETS: fr_pair_value_memcpy(vp, (uint8_t const *)val, len); break; default: if (fr_pair_value_from_str(vp, val, len, '\0', false) < 0) goto fail; } VP_VERIFY(vp); RDEBUG2("&%s:%s %s $%s{'%s'} -> '%s'", list_name, key, fr_int2str(fr_tokens_table, op, "<INVALID>"), hash_name, key, val); return 0; }
/** Insert a single VALUE_PAIR at the end of the list * * @note Will not advance cursor position to new attribute, but will set cursor * to this attribute, if it's the first one in the list. * * Insert a VALUE_PAIR at the end of the list. * * @param cursor to operate on. * @param vp to insert. */ void fr_pair_cursor_append(vp_cursor_t *cursor, VALUE_PAIR *vp) { VALUE_PAIR *i; if (!fr_cond_assert(cursor->first)) return; /* cursor must have been initialised */ if (!vp) return; VP_VERIFY(vp); LIST_VERIFY(*(cursor->first)); /* * Only allow one VP to by inserted at a time */ vp->next = NULL; /* * Cursor was initialised with a pointer to a NULL value_pair */ if (!*(cursor->first)) { *cursor->first = vp; cursor->current = vp; return; } /* * We don't yet know where the last VALUE_PAIR is * * Assume current is closer to the end of the list and * use that if available. */ if (!cursor->last) cursor->last = cursor->current ? cursor->current : *cursor->first; VP_VERIFY(cursor->last); /* * Wind last to the end of the list. */ if (cursor->last->next) { for (i = cursor->last; i; i = i->next) { VP_VERIFY(i); cursor->last = i; } } /* * Either current was never set, or something iterated to the * end of the attribute list. In both cases the newly inserted * VALUE_PAIR should be set as the current VALUE_PAIR. */ if (!cursor->current) cursor->current = vp; /* * Add the VALUE_PAIR to the end of the list */ cursor->last->next = vp; cursor->last = vp; /* Wind it forward a little more */ /* * If the next pointer was NULL, and the VALUE_PAIR * just added has a next pointer value, set the cursor's next * pointer to the VALUE_PAIR's next pointer. */ if (!cursor->next) cursor->next = cursor->current->next; LIST_VERIFY(*(cursor->first)); }
/** Return the VALUE_PAIR the cursor current points to * * @param cursor to operate on. * @return the #VALUE_PAIR the cursor currently points to. */ VALUE_PAIR *fr_pair_cursor_current(vp_cursor_t *cursor) { if (cursor->current) VP_VERIFY(cursor->current); return cursor->current; }