The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
filter.c
Go to the documentation of this file.
1/*
2 * This program is 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: f62246f98c32488ea3287c3b5732984cd1033607 $
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
34static bool const fr_ldap_attr_allowed_chars[UINT8_MAX + 1] = {
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_typed_strdup(node, "&");
67 break;
68
69 case '|':
70 node->logic_op = LDAP_FILTER_LOGIC_OR;
71 node->orig = talloc_typed_strdup(node, "|");
72 break;
73
74 case '!':
75 node->logic_op = LDAP_FILTER_LOGIC_NOT;
76 node->orig = talloc_typed_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 if (len == 0) {
140 fr_strerror_const("Missing attribute name");
142 }
143
144 MEM(node->attr = talloc_zero_array(node, char, len+1));
145 memcpy(node->attr, attr_buffer, len);
146
147 /*
148 * Check for the attribute needed for the filter using the
149 * provided callback.
150 */
151 if (attr_check) attr_check(node->attr, uctx);
152
153 /*
154 * If the attribute name is followed by ':' there is an
155 * extended match rule. We only support two of them.
156 */
157 if (fr_sbuff_next_if_char(sbuff, ':')) {
159 node->op = LDAP_FILTER_OP_BIT_AND;
160 goto found_op;
161 }
163 node->op = LDAP_FILTER_OP_BIT_OR;
164 goto found_op;
165 }
166
167 fr_strerror_const("Unsupported extended match rule");
169
170 found_op:
171 if(!(fr_sbuff_next_if_char(sbuff, ':'))) {
172 fr_strerror_const("Missing ':' after extended match rule");
174 }
175 }
176
178
179 switch(op) {
181 if (node->op == LDAP_FILTER_OP_UNSET) node->op = op;
182 break;
183
186 node->op = op;
187 break;
188
189 default:
190 fr_strerror_const("Incorrect operator");
192 }
193
194 if (((node->op == LDAP_FILTER_OP_BIT_AND) || (node->op == LDAP_FILTER_OP_BIT_OR)) &&
195 (op != LDAP_FILTER_OP_EQ)) {
196 fr_strerror_const("Extended match rule only valid with '=' operator");
198 }
199
200 /*
201 * Capture everything up to the next ')' as the value, blanking the buffer first.
202 */
203 memset(val_buffer, 0, FILTER_VALUE_MAX_LEN);
204 len = fr_sbuff_out_bstrncpy_until(&val_sbuff, sbuff, FILTER_VALUE_MAX_LEN - 1, &FR_SBUFF_TERM(")"), NULL);
205
206 if (len == 0) {
207 fr_strerror_const("Missing filter value");
209 }
210
211 /*
212 * An equality test with a value of '*' is a present test
213 */
214 if ((len == 1) && (*val_buffer == '*') && (node->op == LDAP_FILTER_OP_EQ)) node->op = LDAP_FILTER_OP_PRESENT;
215
216 /*
217 * Equality tests with '*' in the value are substring matches
218 */
219 fr_sbuff_set_to_start(&val_sbuff);
220 if ((node->op == LDAP_FILTER_OP_EQ) && (fr_sbuff_adv_to_chr(&val_sbuff, SIZE_MAX, '*'))) {
221 node->op = LDAP_FILTER_OP_SUBSTR;
222 }
223
224 MEM(node->value = fr_value_box_alloc_null(node));
225
226 switch (node->op) {
230 if (fr_value_box_bstrndup(node, node->value, NULL, val_buffer, len, false) < 0) {
231 fr_strerror_const("Failed parsing value for filter");
233 }
234 break;
235
236 /*
237 * Since we don't have the LDAP schema, we make an assumption that <=, >= and
238 * bitwise operators are going to be used with numeric attributes
239 */
244 if (fr_value_box_from_str(node, node->value, FR_TYPE_UINT32, NULL,
245 val_buffer, len, NULL, false) < 0) {
246 fr_strerror_const("Failed parsing value for filter");
248 }
249 break;
250
251 /*
252 * Operator should not be unset at the end of a filter
253 */
255 fr_assert(0);
256 break;
257 }
258
259 /*
260 * Take a copy of the original filter for debug output
261 */
262 MEM(node->orig = talloc_zero_array(node, char, fr_sbuff_diff(sbuff, &marker) + 1));
263 memcpy(node->orig, fr_sbuff_current(&marker), fr_sbuff_diff(sbuff, &marker));
264 DEBUG3("%*sParsed LDAP filter (%s)", depth, "", node->orig);
265
266 return fr_sbuff_diff(sbuff, &marker);
267}
268
269/** Parse individual LDAP filter nodes
270 *
271 * A node can either be a group of nodes joined with a logical operator
272 * or an individual filter.
273 *
274 * @param[in,out] node to populate with parsed filter.
275 * @param[in] sbuff pointing to filter to parse.
276 * @param[in] depth to indent debug output, indicating nesting of groups.
277 * @param[in] attr_check callback to check if required attributes are in the query.
278 * @param[in] uctx passed to attribute check callback.
279 * @return
280 * - number of bytes parsed on success
281 * - < 0 on error
282 */
285{
286 fr_sbuff_marker_t marker;
287 fr_slen_t ret;
288 fr_slen_t parsed = 0;
289
290 static bool const logical_op_chars[UINT8_MAX +1] = {
291 ['!'] = true, ['&'] = true, ['|'] = true,
292 };
293
294 if (!fr_sbuff_next_if_char(sbuff, '(')) {
295 fr_strerror_const("Missing '('");
297 }
298
299 /*
300 * Firstly, look for the characters which indicate the start of a group of filters
301 * to be combined with a logical operator.
302 */
303 fr_sbuff_marker(&marker, sbuff);
304 if (fr_sbuff_adv_past_allowed(sbuff, 1, logical_op_chars, NULL)) {
305 fr_sbuff_set(sbuff, &marker);
306 ret = ldap_filter_parse_logic(node, sbuff, depth, attr_check, uctx);
307 } else {
308 ret = ldap_filter_parse_filter(node, sbuff, depth, attr_check, uctx);
309 }
310
311 if (ret < 0) return ret;
312 parsed += ret;
313
314 if (!fr_sbuff_next_if_char(sbuff, ')')) {
315 fr_strerror_const("Missing ')'");
317 }
318 parsed ++;
319
320 /*
321 * If we're at the very top level we should be at the end
322 * of the buffer
323 */
324 if ((depth == 0) && (fr_sbuff_extend(sbuff))) {
325 fr_strerror_const("Extra characters at the end of LDAP filter");
327 }
328
329 return parsed;
330}
331
332/** Parse an LDAP filter into its component nodes
333 *
334 * @param[in] ctx to allocate nodes in.
335 * @param[in,out] root where to allocate the root of the parsed filter.
336 * @param[in] filter to parse.
337 * @param[in] attr_check callback to check if required attributes are in the query.
338 * @param[in] uctx passed to attribute check callback.
339 * @return
340 * - number of bytes parsed on success
341 * < 0 on failure
342 */
345{
346 ldap_filter_t *node;
347 fr_slen_t ret;
348
349 MEM(*root = talloc_zero(ctx, fr_dlist_head_t));
350 fr_dlist_init(*root, ldap_filter_t, entry);
351
352 MEM(node = talloc_zero(*root, ldap_filter_t));
353 fr_dlist_insert_head(*root, node);
354
355 ret = ldap_filter_parse_node(node, filter, 0, attr_check, uctx);
356 if (ret < 0) {
357 talloc_free(*root);
358 *root = NULL;
359 return ret;
360 }
361
362 return ret;
363}
364
365static bool ldap_filter_node_eval(ldap_filter_t *node, fr_ldap_connection_t *conn, LDAPMessage *msg, int depth);
366
367/** Evaluate a group of LDAP filters
368 *
369 * Groups have a logical operator of &, | or !
370 *
371 * @param[in] group to evaluate.
372 * @param[in] conn LDAP connection the message being filtered was returned on
373 * @param[in] msg to filter
374 * @param[in] depth to indent debug messages, reflecting group nesting
375 * @return true or false result of the group evaluation
376 */
377static bool ldap_filter_group_eval(ldap_filter_t *group, fr_ldap_connection_t *conn, LDAPMessage *msg, int depth)
378{
379 ldap_filter_t *node = NULL;
380 bool filter_state = false;
381
382 DEBUG3("%*sEvaluating LDAP filter group %s", depth, "", group->orig);
383 depth += 2;
384 while ((node = fr_dlist_next(&group->children, node))) {
385 switch (node->filter_type) {
387 filter_state = ldap_filter_group_eval(node, conn, msg, depth);
388 break;
389 case LDAP_FILTER_NODE:
390 filter_state = ldap_filter_node_eval(node, conn, msg, depth);
391 break;
392 }
393
394 /*
395 * Short circuit the group depending on the logical operator
396 * and the return state of the last node
397 */
398 if (((group->logic_op == LDAP_FILTER_LOGIC_OR) && filter_state) ||
399 ((group->logic_op == LDAP_FILTER_LOGIC_AND) && !filter_state)) {
400 break;
401 }
402 }
403
404 filter_state = (group->logic_op == LDAP_FILTER_LOGIC_NOT ? !filter_state : filter_state);
405
406 depth -= 2;
407 DEBUG3("%*sLDAP filter group %s results in %s", depth, "", group->orig, (filter_state ? "TRUE" : "FALSE"));
408 return filter_state;
409}
410
411#define DEBUG_LDAP_ATTR_VAL if (DEBUG_ENABLED3) { \
412 fr_value_box_t value_box; \
413 fr_ldap_berval_to_value_str_shallow(&value_box, values[i]); \
414 DEBUG3("%*s Evaluating attribute \"%s\", value \"%pV\"", depth, "", node->attr, &value_box); \
415}
416
417/** Evaluate a single LDAP filter node
418 *
419 * @param[in] node to evaluate.
420 * @param[in] conn LDAP connection the message being filtered was returned on.
421 * @param[in] msg to filter.
422 * @param[in] depth to indent debug messages, reflecting group nesting.
423 * @return true or false result of the node evaluation.
424 */
425static bool ldap_filter_node_eval(ldap_filter_t *node, fr_ldap_connection_t *conn, LDAPMessage *msg, int depth)
426{
427 struct berval **values;
428 int count, i;
429 bool filter_state = false;
430
431 switch (node->filter_type) {
433 return ldap_filter_group_eval(node, conn, msg, depth);
434
435 case LDAP_FILTER_NODE:
436 DEBUG3("%*sEvaluating LDAP filter (%s)", depth, "", node->orig);
437 values = ldap_get_values_len(conn->handle, msg, node->attr);
438 count = ldap_count_values_len(values);
439
440 switch (node->op) {
442 filter_state = (count > 0 ? true : false);
443 break;
444
446 for (i = 0; i < count; i++) {
448 if ((node->value->vb_length == values[i]->bv_len) &&
449 (strncasecmp(values[i]->bv_val, node->value->vb_strvalue, values[i]->bv_len) == 0)) {
450 filter_state = true;
451 break;
452 }
453 }
454 break;
455
456 /*
457 * LDAP filters only use one wildcard character '*' for zero or more
458 * character matches.
459 */
461 {
462 char const *v, *t, *v_end, *t_end;
463 bool skip;
464
465 /*
466 * Point t_end at the final character of the filter value
467 * - not the NULL - so we can check for trailing '*'
468 */
469 t_end = node->value->vb_strvalue + node->value->vb_length - 1;
470
471 for (i = 0; i < count; i++) {
473 t = node->value->vb_strvalue;
474 v = values[i]->bv_val;
475 v_end = values[i]->bv_val + values[i]->bv_len - 1;
476 skip = false;
477
478 /*
479 * Walk the value (v) and test (t), comparing until
480 * there is a mismatch or the end of one is reached.
481 */
482 while ((v <= v_end) && (t <= t_end)) {
483 /*
484 * If a wildcard is found in the test,
485 * indicate that we can skip non-matching
486 * characters in the value
487 */
488 if (*t == '*'){
489 skip = true;
490 t++;
491 continue;
492 }
493 if (skip) {
494 while ((tolower((uint8_t) *t) != tolower((uint8_t) *v)) && (v <= v_end)) v++;
495 }
496 if (tolower((uint8_t) *t) != tolower((uint8_t) *v)) break;
497 skip = false;
498 t++;
499 v++;
500 }
501
502 /*
503 * If we've got to the end of both the test and value,
504 * or we've used all of the test and the last character is '*'
505 * then we've matched the pattern.
506 */
507 if (((v > v_end) && (t > t_end)) || ((t >= t_end) && (*t_end == '*'))) {
508 filter_state = true;
509 break;
510 }
511 }
512 }
513 break;
514
515 /*
516 * For >=, <= and bitwise operators, we assume numeric values
517 */
522 {
523 char buffer[11]; /* Max uint32_t + 1 */
525 for (i = 0; i < count; i++) {
527 /*
528 * String too long for max uint32
529 */
530 if (values[i]->bv_len > 10) continue;
531
532 /*
533 * bv_val is not NULL terminated - so copy to a
534 * NULL terminated string before parsing.
535 */
536 memcpy(buffer, values[i]->bv_val, values[i]->bv_len);
537 buffer[values[i]->bv_len] = '\0';
538
539 value = (uint32_t)strtol(buffer, NULL, 10);
540 switch (node->op) {
542 if (value >= node->value->vb_uint32) filter_state = true;
543 break;
545 if (value <= node->value->vb_uint32) filter_state = true;
546 break;
548 if (value & node->value->vb_uint32) filter_state = true;
549 break;
551 if (value | node->value->vb_uint32) filter_state = true;
552 break;
553 default:
554 fr_assert(0);
555 break;
556 }
557 if (filter_state) break;
558 }
559 }
560 break;
561
562 default:
563 fr_assert(0);
564 break;
565
566 }
567
568 ldap_value_free_len(values);
569 }
570
571 DEBUG3("%*sLDAP filter returns %s", depth, "", (filter_state ? "TRUE" : "FALSE"));
572
573 return filter_state;
574}
575
576/** Evaluate an LDAP filter
577 *
578 * @param[in] root of the LDAP filter to evaluate.
579 * @param[in] conn LDAP connection the message being filtered was returned on.
580 * @param[in] msg to filter.
581 * @return true or false result of the node evaluation.
582 */
584 return ldap_filter_node_eval(fr_dlist_head(root), conn, msg, 0);
585}
static int const char char buffer[256]
Definition acutest.h:576
log_entry msg
Definition acutest.h:794
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define NUM_ELEMENTS(_t)
Definition build.h:337
#define MEM(x)
Definition debug.h:36
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:260
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:486
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:378
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:338
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:555
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:343
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:583
static bool const fr_ldap_attr_allowed_chars[UINT8_MAX+1]
Definition filter.c:34
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:377
#define FILTER_VALUE_MAX_LEN
Definition filter.c:40
#define DEBUG_LDAP_ATTR_VAL
Definition filter.c:411
#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:283
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:425
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
@ LDAP_FILTER_LOGIC_NOT
Definition base.h:544
@ LDAP_FILTER_LOGIC_OR
Definition base.h:543
@ LDAP_FILTER_LOGIC_AND
Definition base.h:542
LDAP * handle
libldap handle.
Definition base.h:333
#define LDAP_MATCHING_RULE_BIT_OR
OID of bit-wise OR LDAP match rule.
Definition base.h:120
@ LDAP_FILTER_GROUP
The filter node is a parent of a group which will be combined using a logical operator.
Definition base.h:535
@ LDAP_FILTER_NODE
The filter node is an individual one to be evaluated against an attribute.
Definition base.h:533
ldap_filter_op_t
Operators for use in LDAP filters.
Definition base.h:549
@ LDAP_FILTER_OP_BIT_AND
Bitwise AND comparison.
Definition base.h:556
@ LDAP_FILTER_OP_PRESENT
Attribute present.
Definition base.h:553
@ LDAP_FILTER_OP_SUBSTR
Attribute matches string with wildcards.
Definition base.h:552
@ LDAP_FILTER_OP_EQ
Attribute equals value.
Definition base.h:551
@ LDAP_FILTER_OP_LE
Attribute less than or equal to value.
Definition base.h:555
@ LDAP_FILTER_OP_BIT_OR
Bitwise OR comparison.
Definition base.h:557
@ LDAP_FILTER_OP_GE
Attribute greater than or equal to value.
Definition base.h:554
@ LDAP_FILTER_OP_UNSET
Attribute not set yet.
Definition base.h:550
char * orig
Text representation of filter for debug messages,.
Definition base.h:565
ldap_filter_type_t filter_type
Type of this filter node.
Definition base.h:564
#define LDAP_MATCHING_RULE_BIT_AND
OID of bit-wise AND LDAP match rule.
Definition base.h:119
int(* filter_attr_check_t)(char const *attr, void *uctx)
Definition base.h:971
Tracks the state of a libldap connection handle.
Definition base.h:332
Structure to hold parsed details of LDAP filters.
Definition base.h:562
#define DEBUG3(_fmt,...)
Definition log.h:266
talloc_free(reap)
@ FR_TYPE_UINT32
32 Bit unsigned integer.
unsigned int uint32_t
long int ssize_t
size_t fr_sbuff_out_bstrncpy_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, 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
size_t fr_sbuff_out_bstrncpy_allowed(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, bool const allowed[static UINT8_MAX+1])
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:36
#define fr_assert(_expr)
Definition rad_assert.h:38
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 UINT8_MAX+1], fr_sbuff_term_t const *tt)
Wind position past characters in the allowed set.
Definition sbuff.c:1777
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:1956
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:2088
#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:180
return count
Definition module.c:163
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:445
#define fr_strerror_const(_msg)
Definition strerror.h:223
ssize_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, bool tainted)
Definition value.c:5315
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:4148
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition value.h:632