The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
filter.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: f9b13c8f9898966a3174180d8049570eed0b332c $
19 * @file lib/ldap/filter.c
20 * @brief Functions to handle basic LDAP filter parsing and filtering
21 *
22 * @copyright 2022 Network RADIUS SAS (legal@networkradius.com)
23 */
24
25#include <freeradius-devel/ldap/base.h>
26
28 { L("<="), LDAP_FILTER_OP_LE },
29 { L("="), LDAP_FILTER_OP_EQ },
30 { L(">="), LDAP_FILTER_OP_GE }
31};
33
35 ['-'] = true,
37};
38
39#define FILTER_ATTR_MAX_LEN 256
40#define FILTER_VALUE_MAX_LEN 256
41
44
45/** Parse LDAP filter logic group
46 *
47 * @param[in,out] node to populate with parsed filter.
48 * @param[in] sbuff pointing to filter to parse.
49 * @param[in] depth to indent debug output, indicating nesting of groups.
50 * @param[in] attr_check callback to check if required attributes are in the query.
51 * @param[in] uctx passed to attribute check callback.
52 * @return
53 * - number of bytes parsed on success
54 * - < 0 on error
55 */
58{
59 ldap_filter_t *child_node;
60 fr_slen_t ret = 0;
61 fr_slen_t parsed = 0;
62
63 fr_sbuff_switch(sbuff, '\0') {
64 case '&':
65 node->logic_op = LDAP_FILTER_LOGIC_AND;
66 node->orig = talloc_strdup(node, "&");
67 break;
68
69 case '|':
70 node->logic_op = LDAP_FILTER_LOGIC_OR;
71 node->orig = talloc_strdup(node, "|");
72 break;
73
74 case '!':
75 node->logic_op = LDAP_FILTER_LOGIC_NOT;
76 node->orig = talloc_strdup(node, "!");
77 break;
78 }
79 parsed += fr_sbuff_advance(sbuff, 1);
80
81 DEBUG3("%*sCreating LDAP filter group %s", depth, "", node->orig);
83 fr_dlist_init(&node->children, ldap_filter_t, entry);
84 MEM(child_node = talloc_zero(node, ldap_filter_t));
85 fr_dlist_insert_head(&node->children, child_node);
86
87 depth += 2;
88 ret = ldap_filter_parse_node(child_node, sbuff, depth, attr_check, uctx);
89 if (ret < 0) return ret;
90 parsed += ret;
91
92 /*
93 * Look for sibling nodes to the child just processed
94 */
95 while (fr_sbuff_is_char(sbuff, '(')) {
96 if (node->logic_op == LDAP_FILTER_LOGIC_NOT) {
97 fr_strerror_const("'!' operator can only apply to one filter");
99 }
100 MEM(child_node = talloc_zero(node, ldap_filter_t));
101 fr_dlist_insert_tail(&node->children, child_node);
102 ret = ldap_filter_parse_node(child_node, sbuff, depth, attr_check, uctx);
103 if (ret < 0) return ret;
104 parsed += ret;
105 }
106
107 return parsed;
108}
109
110/** Parse individual LDAP filter
111 *
112 * @param[in,out] node to populate with parsed filter.
113 * @param[in] sbuff pointing to filter to parse.
114 * @param[in] depth to indent debug output, indicating nesting of groups.
115 * @param[in] attr_check callback to check if required attributes are in the query.
116 * @param[in] uctx passed to attribute check callback.
117 * @return
118 * - number of bytes parsed on success
119 * - < 0 on error
120 */
123{
124 char attr_buffer[FILTER_ATTR_MAX_LEN], val_buffer[FILTER_VALUE_MAX_LEN];
125 fr_sbuff_t attr_sbuff = FR_SBUFF_IN(attr_buffer, FILTER_ATTR_MAX_LEN);
126 fr_sbuff_t val_sbuff = FR_SBUFF_IN(val_buffer, FILTER_VALUE_MAX_LEN);
127 size_t len;
128 ssize_t slen;
130 fr_sbuff_marker_t marker;
131
132 fr_sbuff_marker(&marker, sbuff);
133
134 /*
135 * Extract the attribute name, blanking the buffer first.
136 */
137 memset(attr_buffer, 0, FILTER_ATTR_MAX_LEN);
139 fr_strerror_const("Failed reading attribute name");
141 }
142 if (len == 0) {
143 fr_strerror_const("Missing attribute name");
145 }
146
147 MEM(node->attr = talloc_zero_array(node, char, len+1));
148 memcpy(node->attr, attr_buffer, len);
149
150 /*
151 * Check for the attribute needed for the filter using the
152 * provided callback.
153 */
154 if (attr_check) attr_check(node->attr, uctx);
155
156 /*
157 * If the attribute name is followed by ':' there is an
158 * extended match rule. We only support two of them.
159 */
160 if (fr_sbuff_next_if_char(sbuff, ':')) {
162 node->op = LDAP_FILTER_OP_BIT_AND;
163 goto found_op;
164 }
166 node->op = LDAP_FILTER_OP_BIT_OR;
167 goto found_op;
168 }
169
170 fr_strerror_const("Unsupported extended match rule");
172
173 found_op:
174 if(!(fr_sbuff_next_if_char(sbuff, ':'))) {
175 fr_strerror_const("Missing ':' after extended match rule");
177 }
178 }
179
181
182 switch(op) {
184 if (node->op == LDAP_FILTER_OP_UNSET) node->op = op;
185 break;
186
189 node->op = op;
190 break;
191
192 default:
193 fr_strerror_const("Incorrect operator");
195 }
196
197 if (((node->op == LDAP_FILTER_OP_BIT_AND) || (node->op == LDAP_FILTER_OP_BIT_OR)) &&
198 (op != LDAP_FILTER_OP_EQ)) {
199 fr_strerror_const("Extended match rule only valid with '=' operator");
201 }
202
203 /*
204 * Capture everything up to the next ')' as the value, blanking the buffer first.
205 */
206 memset(val_buffer, 0, FILTER_VALUE_MAX_LEN);
207 if (fr_sbuff_out_bstrncpy_until(&len, &val_sbuff, sbuff, FILTER_VALUE_MAX_LEN - 1, &FR_SBUFF_TERM(")"), NULL) < 0) {
208 fr_strerror_const("Failed reading filter value");
210 }
211 if (len == 0) {
212 fr_strerror_const("Missing filter value");
214 }
215
216 /*
217 * An equality test with a value of '*' is a present test
218 */
219 if ((len == 1) && (*val_buffer == '*') && (node->op == LDAP_FILTER_OP_EQ)) node->op = LDAP_FILTER_OP_PRESENT;
220
221 /*
222 * Equality tests with '*' in the value are substring matches
223 */
224 fr_sbuff_set_to_start(&val_sbuff);
225 if ((node->op == LDAP_FILTER_OP_EQ) && (fr_sbuff_adv_to_chr(&val_sbuff, SIZE_MAX, '*'))) {
226 node->op = LDAP_FILTER_OP_SUBSTR;
227 }
228
229 MEM(node->value = fr_value_box_alloc_null(node));
230
231 switch (node->op) {
235 if (fr_value_box_bstrndup(node, node->value, NULL, val_buffer, len, false) < 0) {
236 fr_strerror_const("Failed parsing value for filter");
238 }
239 break;
240
241 /*
242 * Since we don't have the LDAP schema, we make an assumption that <=, >= and
243 * bitwise operators are going to be used with numeric attributes
244 */
249 if (fr_value_box_from_str(node, node->value, FR_TYPE_UINT32, NULL,
250 val_buffer, len, NULL) < 0) {
251 fr_strerror_const("Failed parsing value for filter");
253 }
254 break;
255
256 /*
257 * Operator should not be unset at the end of a filter
258 */
260 fr_assert(0);
261 break;
262 }
263
264 /*
265 * Take a copy of the original filter for debug output
266 */
267 MEM(node->orig = talloc_zero_array(node, char, fr_sbuff_diff(sbuff, &marker) + 1));
268 memcpy(node->orig, fr_sbuff_current(&marker), fr_sbuff_diff(sbuff, &marker));
269 DEBUG3("%*sParsed LDAP filter (%s)", depth, "", node->orig);
270
271 return fr_sbuff_diff(sbuff, &marker);
272}
273
274/** Parse individual LDAP filter nodes
275 *
276 * A node can either be a group of nodes joined with a logical operator
277 * or an individual filter.
278 *
279 * @param[in,out] node to populate with parsed filter.
280 * @param[in] sbuff pointing to filter to parse.
281 * @param[in] depth to indent debug output, indicating nesting of groups.
282 * @param[in] attr_check callback to check if required attributes are in the query.
283 * @param[in] uctx passed to attribute check callback.
284 * @return
285 * - number of bytes parsed on success
286 * - < 0 on error
287 */
290{
291 fr_sbuff_marker_t marker;
292 fr_slen_t ret;
293 fr_slen_t parsed = 0;
294
295 static bool const logical_op_chars[UINT8_MAX +1] = {
296 ['!'] = true, ['&'] = true, ['|'] = true,
297 };
298
299 if (!fr_sbuff_next_if_char(sbuff, '(')) {
300 fr_strerror_const("Missing '('");
302 }
303
304 /*
305 * Firstly, look for the characters which indicate the start of a group of filters
306 * to be combined with a logical operator.
307 */
308 fr_sbuff_marker(&marker, sbuff);
309 if (fr_sbuff_adv_past_allowed(sbuff, 1, logical_op_chars, NULL)) {
310 fr_sbuff_set(sbuff, &marker);
311 ret = ldap_filter_parse_logic(node, sbuff, depth, attr_check, uctx);
312 } else {
313 ret = ldap_filter_parse_filter(node, sbuff, depth, attr_check, uctx);
314 }
315
316 if (ret < 0) return ret;
317 parsed += ret;
318
319 if (!fr_sbuff_next_if_char(sbuff, ')')) {
320 fr_strerror_const("Missing ')'");
322 }
323 parsed ++;
324
325 /*
326 * If we're at the very top level we should be at the end
327 * of the buffer
328 */
329 if ((depth == 0) && (fr_sbuff_extend(sbuff))) {
330 fr_strerror_const("Extra characters at the end of LDAP filter");
332 }
333
334 return parsed;
335}
336
337/** Parse an LDAP filter into its component nodes
338 *
339 * @param[in] ctx to allocate nodes in.
340 * @param[in,out] root where to allocate the root of the parsed filter.
341 * @param[in] filter to parse.
342 * @param[in] attr_check callback to check if required attributes are in the query.
343 * @param[in] uctx passed to attribute check callback.
344 * @return
345 * - number of bytes parsed on success
346 * < 0 on failure
347 */
350{
351 ldap_filter_t *node;
352 fr_slen_t ret;
353
354 MEM(*root = talloc_zero(ctx, fr_dlist_head_t));
355 fr_dlist_init(*root, ldap_filter_t, entry);
356
357 MEM(node = talloc_zero(*root, ldap_filter_t));
358 fr_dlist_insert_head(*root, node);
359
360 ret = ldap_filter_parse_node(node, filter, 0, attr_check, uctx);
361 if (ret < 0) {
362 talloc_free(*root);
363 *root = NULL;
364 return ret;
365 }
366
367 return ret;
368}
369
370static bool ldap_filter_node_eval(ldap_filter_t *node, fr_ldap_connection_t *conn, LDAPMessage *msg, int depth);
371
372/** Evaluate a group of LDAP filters
373 *
374 * Groups have a logical operator of &, | or !
375 *
376 * @param[in] group to evaluate.
377 * @param[in] conn LDAP connection the message being filtered was returned on
378 * @param[in] msg to filter
379 * @param[in] depth to indent debug messages, reflecting group nesting
380 * @return true or false result of the group evaluation
381 */
382static bool ldap_filter_group_eval(ldap_filter_t *group, fr_ldap_connection_t *conn, LDAPMessage *msg, int depth)
383{
384 ldap_filter_t *node = NULL;
385 bool filter_state = false;
386
387 DEBUG3("%*sEvaluating LDAP filter group %s", depth, "", group->orig);
388 depth += 2;
389 while ((node = fr_dlist_next(&group->children, node))) {
390 switch (node->filter_type) {
392 filter_state = ldap_filter_group_eval(node, conn, msg, depth);
393 break;
394 case LDAP_FILTER_NODE:
395 filter_state = ldap_filter_node_eval(node, conn, msg, depth);
396 break;
397 }
398
399 /*
400 * Short circuit the group depending on the logical operator
401 * and the return state of the last node
402 */
403 if (((group->logic_op == LDAP_FILTER_LOGIC_OR) && filter_state) ||
404 ((group->logic_op == LDAP_FILTER_LOGIC_AND) && !filter_state)) {
405 break;
406 }
407 }
408
409 filter_state = (group->logic_op == LDAP_FILTER_LOGIC_NOT ? !filter_state : filter_state);
410
411 depth -= 2;
412 DEBUG3("%*sLDAP filter group %s results in %s", depth, "", group->orig, (filter_state ? "TRUE" : "FALSE"));
413 return filter_state;
414}
415
416#define DEBUG_LDAP_ATTR_VAL if (DEBUG_ENABLED3) { \
417 fr_value_box_t value_box; \
418 fr_ldap_berval_to_value_str_shallow(&value_box, values[i]); \
419 DEBUG3("%*s Evaluating attribute \"%s\", value \"%pV\"", depth, "", node->attr, &value_box); \
420}
421
422/** Evaluate a single LDAP filter node
423 *
424 * @param[in] node to evaluate.
425 * @param[in] conn LDAP connection the message being filtered was returned on.
426 * @param[in] msg to filter.
427 * @param[in] depth to indent debug messages, reflecting group nesting.
428 * @return true or false result of the node evaluation.
429 */
430static bool ldap_filter_node_eval(ldap_filter_t *node, fr_ldap_connection_t *conn, LDAPMessage *msg, int depth)
431{
432 struct berval **values;
433 int count, i;
434 bool filter_state = false;
435
436 switch (node->filter_type) {
438 return ldap_filter_group_eval(node, conn, msg, depth);
439
440 case LDAP_FILTER_NODE:
441 DEBUG3("%*sEvaluating LDAP filter (%s)", depth, "", node->orig);
442 values = ldap_get_values_len(conn->handle, msg, node->attr);
443 count = ldap_count_values_len(values);
444
445 switch (node->op) {
447 filter_state = (count > 0 ? true : false);
448 break;
449
451 for (i = 0; i < count; i++) {
453 if ((node->value->vb_length == values[i]->bv_len) &&
454 (strncasecmp(values[i]->bv_val, node->value->vb_strvalue, values[i]->bv_len) == 0)) {
455 filter_state = true;
456 break;
457 }
458 }
459 break;
460
461 /*
462 * LDAP filters only use one wildcard character '*' for zero or more
463 * character matches.
464 */
466 {
467 char const *v, *t, *v_end, *t_end;
468 bool skip;
469
470 /*
471 * Point t_end at the final character of the filter value
472 * - not the NULL - so we can check for trailing '*'
473 */
474 t_end = node->value->vb_strvalue + node->value->vb_length - 1;
475
476 for (i = 0; i < count; i++) {
478 t = node->value->vb_strvalue;
479 v = values[i]->bv_val;
480 v_end = values[i]->bv_val + values[i]->bv_len - 1;
481 skip = false;
482
483 /*
484 * Walk the value (v) and test (t), comparing until
485 * there is a mismatch or the end of one is reached.
486 */
487 while ((v <= v_end) && (t <= t_end)) {
488 /*
489 * If a wildcard is found in the test,
490 * indicate that we can skip non-matching
491 * characters in the value
492 */
493 if (*t == '*'){
494 skip = true;
495 t++;
496 continue;
497 }
498 if (skip) {
499 while ((tolower((uint8_t) *t) != tolower((uint8_t) *v)) && (v <= v_end)) v++;
500 }
501 if (tolower((uint8_t) *t) != tolower((uint8_t) *v)) break;
502 skip = false;
503 t++;
504 v++;
505 }
506
507 /*
508 * If we've got to the end of both the test and value,
509 * or we've used all of the test and the last character is '*'
510 * then we've matched the pattern.
511 */
512 if (((v > v_end) && (t > t_end)) || ((t >= t_end) && (*t_end == '*'))) {
513 filter_state = true;
514 break;
515 }
516 }
517 }
518 break;
519
520 /*
521 * For >=, <= and bitwise operators, we assume numeric values
522 */
527 {
528 char buffer[11]; /* Max uint32_t + 1 */
530 for (i = 0; i < count; i++) {
532 /*
533 * String too long for max uint32
534 */
535 if (values[i]->bv_len > 10) continue;
536
537 /*
538 * bv_val is not NULL terminated - so copy to a
539 * NULL terminated string before parsing.
540 */
541 memcpy(buffer, values[i]->bv_val, values[i]->bv_len);
542 buffer[values[i]->bv_len] = '\0';
543
544 value = (uint32_t)strtol(buffer, NULL, 10);
545 switch (node->op) {
547 if (value >= node->value->vb_uint32) filter_state = true;
548 break;
550 if (value <= node->value->vb_uint32) filter_state = true;
551 break;
553 if (value & node->value->vb_uint32) filter_state = true;
554 break;
556 if (value | node->value->vb_uint32) filter_state = true;
557 break;
558 default:
559 fr_assert(0);
560 break;
561 }
562 if (filter_state) break;
563 }
564 }
565 break;
566
567 default:
568 fr_assert(0);
569 break;
570
571 }
572
573 ldap_value_free_len(values);
574 }
575
576 DEBUG3("%*sLDAP filter returns %s", depth, "", (filter_state ? "TRUE" : "FALSE"));
577
578 return filter_state;
579}
580
581/** Evaluate an LDAP filter
582 *
583 * @param[in] root of the LDAP filter to evaluate.
584 * @param[in] conn LDAP connection the message being filtered was returned on.
585 * @param[in] msg to filter.
586 * @return true or false result of the node evaluation.
587 */
589 return ldap_filter_node_eval(fr_dlist_head(root), conn, msg, 0);
590}
static int const char char buffer[256]
Definition acutest.h:635
log_entry msg
Definition acutest.h:941
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define NUM_ELEMENTS(_t)
Definition build.h:406
#define MEM(x)
Definition debug.h:38
Test enumeration values.
Definition dict_test.h:92
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:242
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition dlist.h:468
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:360
static int fr_dlist_insert_head(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the head of a list.
Definition dlist.h:320
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:537
Head of a doubly linked list.
Definition dlist.h:51
fr_slen_t fr_ldap_filter_parse(TALLOC_CTX *ctx, fr_dlist_head_t **root, fr_sbuff_t *filter, filter_attr_check_t attr_check, void *uctx)
Parse an LDAP filter into its component nodes.
Definition filter.c:348
static fr_slen_t ldap_filter_parse_filter(ldap_filter_t *node, fr_sbuff_t *sbuff, int depth, filter_attr_check_t attr_check, void *uctx)
Parse individual LDAP filter.
Definition filter.c:121
static size_t ldap_filter_op_table_len
Definition filter.c:32
bool fr_ldap_filter_eval(fr_dlist_head_t *root, fr_ldap_connection_t *conn, LDAPMessage *msg)
Evaluate an LDAP filter.
Definition filter.c:588
static bool ldap_filter_group_eval(ldap_filter_t *group, fr_ldap_connection_t *conn, LDAPMessage *msg, int depth)
Evaluate a group of LDAP filters.
Definition filter.c:382
#define FILTER_VALUE_MAX_LEN
Definition filter.c:40
#define DEBUG_LDAP_ATTR_VAL
Definition filter.c:416
static bool const fr_ldap_attr_allowed_chars[SBUFF_CHAR_CLASS]
Definition filter.c:34
#define FILTER_ATTR_MAX_LEN
Definition filter.c:39
static fr_slen_t ldap_filter_parse_node(ldap_filter_t *node, fr_sbuff_t *sbuff, int depth, filter_attr_check_t attr_check, void *uctx)
Parse individual LDAP filter nodes.
Definition filter.c:288
static fr_table_num_sorted_t const ldap_filter_op_table[]
Definition filter.c:27
static bool ldap_filter_node_eval(ldap_filter_t *node, fr_ldap_connection_t *conn, LDAPMessage *msg, int depth)
Evaluate a single LDAP filter node.
Definition filter.c:430
static fr_slen_t ldap_filter_parse_logic(ldap_filter_t *node, fr_sbuff_t *sbuff, int depth, filter_attr_check_t attr_check, void *uctx)
Parse LDAP filter logic group.
Definition filter.c:56
talloc_free(hp)
@ LDAP_FILTER_LOGIC_NOT
Definition base.h:546
@ LDAP_FILTER_LOGIC_OR
Definition base.h:545
@ LDAP_FILTER_LOGIC_AND
Definition base.h:544
LDAP * handle
libldap handle.
Definition base.h:342
#define LDAP_MATCHING_RULE_BIT_OR
OID of bit-wise OR LDAP match rule.
Definition base.h:119
@ LDAP_FILTER_GROUP
The filter node is a parent of a group which will be combined using a logical operator.
Definition base.h:537
@ LDAP_FILTER_NODE
The filter node is an individual one to be evaluated against an attribute.
Definition base.h:535
ldap_filter_op_t
Operators for use in LDAP filters.
Definition base.h:551
@ LDAP_FILTER_OP_BIT_AND
Bitwise AND comparison.
Definition base.h:558
@ LDAP_FILTER_OP_PRESENT
Attribute present.
Definition base.h:555
@ LDAP_FILTER_OP_SUBSTR
Attribute matches string with wildcards.
Definition base.h:554
@ LDAP_FILTER_OP_EQ
Attribute equals value.
Definition base.h:553
@ LDAP_FILTER_OP_LE
Attribute less than or equal to value.
Definition base.h:557
@ LDAP_FILTER_OP_BIT_OR
Bitwise OR comparison.
Definition base.h:559
@ LDAP_FILTER_OP_GE
Attribute greater than or equal to value.
Definition base.h:556
@ LDAP_FILTER_OP_UNSET
Attribute not set yet.
Definition base.h:552
char * orig
Text representation of filter for debug messages,.
Definition base.h:567
ldap_filter_type_t filter_type
Type of this filter node.
Definition base.h:566
#define LDAP_MATCHING_RULE_BIT_AND
OID of bit-wise AND LDAP match rule.
Definition base.h:118
int(* filter_attr_check_t)(char const *attr, void *uctx)
Definition base.h:1036
Tracks the state of a libldap connection handle.
Definition base.h:341
Structure to hold parsed details of LDAP filters.
Definition base.h:564
#define DEBUG3(_fmt,...)
Definition log.h:271
#define SBUFF_CHAR_CLASS
@ FR_TYPE_UINT32
32 Bit unsigned integer.
unsigned int uint32_t
long int ssize_t
fr_sbuff_err_t fr_sbuff_out_bstrncpy_allowed(size_t *len, fr_sbuff_t *out, fr_sbuff_t *in, size_t max, bool const allowed[static SBUFF_CHAR_CLASS])
fr_sbuff_err_t fr_sbuff_out_bstrncpy_until(size_t *len, fr_sbuff_t *out, fr_sbuff_t *in, size_t max, fr_sbuff_term_t const *tt, fr_sbuff_unescape_rules_t const *u_rules)
unsigned char uint8_t
ssize_t fr_slen_t
#define UINT8_MAX
static uint8_t depth(fr_minmax_heap_index_t i)
Definition minmax_heap.c:83
int strncasecmp(char *s1, char *s2, int n)
Definition missing.c:35
#define fr_assert(_expr)
Definition rad_assert.h:37
static int attr_check(CONF_SECTION *conf, tmpl_t *tmpl, char const *name, fr_dict_attr_flags_t *flags)
size_t fr_sbuff_adv_past_allowed(fr_sbuff_t *sbuff, size_t len, bool const allowed[static SBUFF_CHAR_CLASS], fr_sbuff_term_t const *tt)
Wind position past characters in the allowed set.
Definition sbuff.c:1936
char * fr_sbuff_adv_to_chr(fr_sbuff_t *sbuff, size_t len, char c)
Wind position to first instance of specified char.
Definition sbuff.c:2115
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2247
#define fr_sbuff_out_by_longest_prefix(_match_len, _out, _table, _sbuff, _def)
#define fr_sbuff_adv_past_str_literal(_sbuff, _needle)
#define fr_sbuff_set(_dst, _src)
#define fr_sbuff_diff(_a, _b)
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_current(_sbuff_or_marker)
#define fr_sbuff_extend(_sbuff_or_marker)
#define SBUFF_CHAR_CLASS_ALPHA_NUM
#define fr_sbuff_is_char(_sbuff_or_marker, _c)
#define FR_SBUFF_ERROR_RETURN(_sbuff_or_marker)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define fr_sbuff_switch(_sbuff_or_marker, _eob)
#define FR_SBUFF_TERM(_str)
Initialise a terminal structure with a single string.
Definition sbuff.h:178
return count
Definition module.c:155
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
#define fr_strerror_const(_msg)
Definition strerror.h:223
fr_slen_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules)
Definition value.c:6164
int fr_value_box_bstrndup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Copy a string to to a fr_value_box_t.
Definition value.c:4922
static fr_sbuff_err_t char size_t * len
Definition value.h:1061
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition value.h:679