/src/freeradius-server/src/lib/util/dict_util.c
Line | Count | Source (jump to first uncovered line) |
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 |
5 | | * (at 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 | | /** Multi-protocol AVP dictionary API |
18 | | * |
19 | | * @file src/lib/util/dict_util.c |
20 | | * |
21 | | * @copyright 2000,2006 The FreeRADIUS server project |
22 | | */ |
23 | | RCSID("$Id: be45c6c79b638a31de8dc2b9a2fe27043983bc87 $") |
24 | | |
25 | | #define _DICT_PRIVATE 1 |
26 | | |
27 | | #include <freeradius-devel/util/atexit.h> |
28 | | #include <freeradius-devel/util/conf.h> |
29 | | #include <freeradius-devel/util/dict.h> |
30 | | #include <freeradius-devel/util/dict_fixup_priv.h> |
31 | | #include <freeradius-devel/util/proto.h> |
32 | | #include <freeradius-devel/util/rand.h> |
33 | | #include <freeradius-devel/util/sbuff.h> |
34 | | #include <freeradius-devel/util/syserror.h> |
35 | | |
36 | | #ifdef HAVE_SYS_STAT_H |
37 | | # include <sys/stat.h> |
38 | | #endif |
39 | | |
40 | | fr_dict_gctx_t *dict_gctx = NULL; //!< Top level structure containing global dictionary state. |
41 | | |
42 | | /** Characters allowed in dictionary names |
43 | | * |
44 | | */ |
45 | | bool const fr_dict_attr_allowed_chars[UINT8_MAX + 1] = { |
46 | | ['-'] = true, ['/'] = true, ['_'] = true, |
47 | | ['0'] = true, ['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true, |
48 | | ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true, |
49 | | ['A'] = true, ['B'] = true, ['C'] = true, ['D'] = true, ['E'] = true, |
50 | | ['F'] = true, ['G'] = true, ['H'] = true, ['I'] = true, ['J'] = true, |
51 | | ['K'] = true, ['L'] = true, ['M'] = true, ['N'] = true, ['O'] = true, |
52 | | ['P'] = true, ['Q'] = true, ['R'] = true, ['S'] = true, ['T'] = true, |
53 | | ['U'] = true, ['V'] = true, ['W'] = true, ['X'] = true, ['Y'] = true, |
54 | | ['Z'] = true, |
55 | | ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true, |
56 | | ['f'] = true, ['g'] = true, ['h'] = true, ['i'] = true, ['j'] = true, |
57 | | ['k'] = true, ['l'] = true, ['m'] = true, ['n'] = true, ['o'] = true, |
58 | | ['p'] = true, ['q'] = true, ['r'] = true, ['s'] = true, ['t'] = true, |
59 | | ['u'] = true, ['v'] = true, ['w'] = true, ['x'] = true, ['y'] = true, |
60 | | ['z'] = true |
61 | | }; |
62 | | |
63 | | /** Characters allowed in enumeration value names |
64 | | * |
65 | | */ |
66 | | bool const fr_dict_enum_allowed_chars[UINT8_MAX + 1] = { |
67 | | ['+'] = true, ['-'] = true, ['.'] = true, ['/'] = true, ['_'] = true, |
68 | | ['0'] = true, ['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true, |
69 | | ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true, |
70 | | ['A'] = true, ['B'] = true, ['C'] = true, ['D'] = true, ['E'] = true, |
71 | | ['F'] = true, ['G'] = true, ['H'] = true, ['I'] = true, ['J'] = true, |
72 | | ['K'] = true, ['L'] = true, ['M'] = true, ['N'] = true, ['O'] = true, |
73 | | ['P'] = true, ['Q'] = true, ['R'] = true, ['S'] = true, ['T'] = true, |
74 | | ['U'] = true, ['V'] = true, ['W'] = true, ['X'] = true, ['Y'] = true, |
75 | | ['Z'] = true, |
76 | | ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true, |
77 | | ['f'] = true, ['g'] = true, ['h'] = true, ['i'] = true, ['j'] = true, |
78 | | ['k'] = true, ['l'] = true, ['m'] = true, ['n'] = true, ['o'] = true, |
79 | | ['p'] = true, ['q'] = true, ['r'] = true, ['s'] = true, ['t'] = true, |
80 | | ['u'] = true, ['v'] = true, ['w'] = true, ['x'] = true, ['y'] = true, |
81 | | ['z'] = true |
82 | | }; |
83 | | |
84 | | /* |
85 | | * Create the hash of the name. |
86 | | * |
87 | | * We copy the hash function here because it's substantially faster. |
88 | | */ |
89 | 439k | #define FNV_MAGIC_INIT (0x811c9dc5) |
90 | 5.47M | #define FNV_MAGIC_PRIME (0x01000193) |
91 | | |
92 | | static void hash_pool_free(void *to_free) |
93 | 37.3k | { |
94 | 37.3k | talloc_free(to_free); |
95 | 37.3k | } |
96 | | |
97 | | /** Apply a simple (case insensitive) hashing function to the name of an attribute, vendor or protocol |
98 | | * |
99 | | * @param[in] name of the attribute, vendor or protocol. |
100 | | * @param[in] len length of the input string. |
101 | | * |
102 | | * @return the hashed derived from the name. |
103 | | */ |
104 | | static uint32_t dict_hash_name(char const *name, size_t len) |
105 | 439k | { |
106 | 439k | uint32_t hash = FNV_MAGIC_INIT; |
107 | | |
108 | 439k | char const *p = name, *q = name + len; |
109 | | |
110 | 5.91M | while (p < q) { |
111 | 5.47M | int c = *(unsigned char const *)p; |
112 | 5.47M | if (isalpha(c)) c = tolower(c); |
113 | | |
114 | 5.47M | hash *= FNV_MAGIC_PRIME; |
115 | 5.47M | hash ^= (uint32_t)(c & 0xff); |
116 | 5.47M | p++; |
117 | 5.47M | } |
118 | | |
119 | 439k | return hash; |
120 | 439k | } |
121 | | |
122 | | /** Wrap name hash function for fr_dict_protocol_t |
123 | | * |
124 | | * @param[in] data fr_dict_attr_t to hash. |
125 | | * @return the hash derived from the name of the attribute. |
126 | | */ |
127 | | static uint32_t dict_protocol_name_hash(void const *data) |
128 | 5.67k | { |
129 | 5.67k | char const *name; |
130 | | |
131 | 5.67k | name = ((fr_dict_t const *)data)->root->name; |
132 | | |
133 | 5.67k | return dict_hash_name(name, strlen(name)); |
134 | 5.67k | } |
135 | | |
136 | | /** Compare two protocol names |
137 | | * |
138 | | */ |
139 | | static int8_t dict_protocol_name_cmp(void const *one, void const *two) |
140 | 5.59k | { |
141 | 5.59k | fr_dict_t const *a = one; |
142 | 5.59k | fr_dict_t const *b = two; |
143 | 5.59k | int ret; |
144 | | |
145 | 5.59k | ret = strcasecmp(a->root->name, b->root->name); |
146 | 5.59k | return CMP(ret, 0); |
147 | 5.59k | } |
148 | | |
149 | | /** Hash a protocol number |
150 | | * |
151 | | */ |
152 | | static uint32_t dict_protocol_num_hash(void const *data) |
153 | 63 | { |
154 | 63 | return fr_hash(&(((fr_dict_t const *)data)->root->attr), sizeof(((fr_dict_t const *)data)->root->attr)); |
155 | 63 | } |
156 | | |
157 | | /** Compare two protocol numbers |
158 | | * |
159 | | */ |
160 | | static int8_t dict_protocol_num_cmp(void const *one, void const *two) |
161 | 21 | { |
162 | 21 | fr_dict_t const *a = one; |
163 | 21 | fr_dict_t const *b = two; |
164 | | |
165 | 21 | return CMP(a->root->attr, b->root->attr); |
166 | 21 | } |
167 | | |
168 | | /** Wrap name hash function for fr_dict_attr_t |
169 | | * |
170 | | * @param data fr_dict_attr_t to hash. |
171 | | * @return the hash derived from the name of the attribute. |
172 | | */ |
173 | | static uint32_t dict_attr_name_hash(void const *data) |
174 | 391k | { |
175 | 391k | char const *name; |
176 | | |
177 | 391k | name = ((fr_dict_attr_t const *)data)->name; |
178 | | |
179 | 391k | return dict_hash_name(name, strlen(name)); |
180 | 391k | } |
181 | | |
182 | | /** Compare two attribute names |
183 | | * |
184 | | */ |
185 | | static int8_t dict_attr_name_cmp(void const *one, void const *two) |
186 | 267k | { |
187 | 267k | fr_dict_attr_t const *a = one, *b = two; |
188 | 267k | int ret; |
189 | | |
190 | 267k | ret = strcasecmp(a->name, b->name); |
191 | 267k | return CMP(ret, 0); |
192 | 267k | } |
193 | | |
194 | | /** Wrap name hash function for fr_dict_vendor_t |
195 | | * |
196 | | * @param data fr_dict_vendor_t to hash. |
197 | | * @return the hash derived from the name of the attribute. |
198 | | */ |
199 | | static uint32_t dict_vendor_name_hash(void const *data) |
200 | 2.26k | { |
201 | 2.26k | char const *name; |
202 | | |
203 | 2.26k | name = ((fr_dict_vendor_t const *)data)->name; |
204 | | |
205 | 2.26k | return dict_hash_name(name, strlen(name)); |
206 | 2.26k | } |
207 | | |
208 | | /** Compare two attribute names |
209 | | * |
210 | | */ |
211 | | static int8_t dict_vendor_name_cmp(void const *one, void const *two) |
212 | 1.51k | { |
213 | 1.51k | fr_dict_vendor_t const *a = one; |
214 | 1.51k | fr_dict_vendor_t const *b = two; |
215 | 1.51k | int ret; |
216 | | |
217 | 1.51k | ret = strcasecmp(a->name, b->name); |
218 | 1.51k | return CMP(ret, 0); |
219 | 1.51k | } |
220 | | |
221 | | /** Hash a vendor number |
222 | | * |
223 | | */ |
224 | | static uint32_t dict_vendor_pen_hash(void const *data) |
225 | 16.4k | { |
226 | 16.4k | return fr_hash(&(((fr_dict_vendor_t const *)data)->pen), |
227 | 16.4k | sizeof(((fr_dict_vendor_t const *)data)->pen)); |
228 | 16.4k | } |
229 | | |
230 | | /** Compare two vendor numbers |
231 | | * |
232 | | */ |
233 | | static int8_t dict_vendor_pen_cmp(void const *one, void const *two) |
234 | 14.9k | { |
235 | 14.9k | fr_dict_vendor_t const *a = one; |
236 | 14.9k | fr_dict_vendor_t const *b = two; |
237 | | |
238 | 14.9k | return CMP(a->pen, b->pen); |
239 | 14.9k | } |
240 | | |
241 | | /** Hash a enumeration name |
242 | | * |
243 | | */ |
244 | | static uint32_t dict_enum_name_hash(void const *data) |
245 | 39.9k | { |
246 | 39.9k | fr_dict_enum_value_t const *enumv = data; |
247 | | |
248 | 39.9k | return dict_hash_name((void const *)enumv->name, enumv->name_len); |
249 | 39.9k | } |
250 | | |
251 | | /** Compare two dictionary attribute enum values |
252 | | * |
253 | | */ |
254 | | static int8_t dict_enum_name_cmp(void const *one, void const *two) |
255 | 1.52k | { |
256 | 1.52k | fr_dict_enum_value_t const *a = one; |
257 | 1.52k | fr_dict_enum_value_t const *b = two; |
258 | 1.52k | size_t len; |
259 | 1.52k | int ret; |
260 | | |
261 | 1.52k | if (a->name_len >= b->name_len) { |
262 | 1.52k | len = a->name_len; |
263 | 1.52k | } else { |
264 | 1 | len = b->name_len; |
265 | 1 | } |
266 | | |
267 | 1.52k | ret = strncasecmp(a->name, b->name, len); |
268 | 1.52k | return CMP(ret, 0); |
269 | 1.52k | } |
270 | | |
271 | | /** Hash a dictionary enum value |
272 | | * |
273 | | */ |
274 | | static uint32_t dict_enum_value_hash(void const *data) |
275 | 80.0k | { |
276 | 80.0k | fr_dict_enum_value_t const *enumv = data; |
277 | | |
278 | 80.0k | return fr_value_box_hash(enumv->value); |
279 | 80.0k | } |
280 | | |
281 | | /** Compare two dictionary enum values |
282 | | * |
283 | | */ |
284 | | static int8_t dict_enum_value_cmp(void const *one, void const *two) |
285 | 7.40k | { |
286 | 7.40k | fr_dict_enum_value_t const *a = one; |
287 | 7.40k | fr_dict_enum_value_t const *b = two; |
288 | 7.40k | int ret; |
289 | | |
290 | 7.40k | ret = fr_value_box_cmp(a->value, b->value); /* not yet int8_t! */ |
291 | 7.40k | return CMP(ret, 0); |
292 | 7.40k | } |
293 | | |
294 | | /** Resolve an alias attribute to the concrete attribute it points to |
295 | | * |
296 | | * @param[out] err where to write the error (if any). |
297 | | * @param[in] da to resolve. |
298 | | * @return |
299 | | * - NULL on error. |
300 | | * - The concrete attribute on success. |
301 | | */ |
302 | | static inline fr_dict_attr_t const *dict_attr_alias(fr_dict_attr_err_t *err, fr_dict_attr_t const *da) |
303 | 890k | { |
304 | 890k | fr_dict_attr_t const *ref; |
305 | | |
306 | 890k | if (!da->flags.is_alias) return da; |
307 | | |
308 | 0 | ref = fr_dict_attr_ref(da); |
309 | 0 | if (unlikely(!ref)) { |
310 | 0 | fr_strerror_printf("ALIAS attribute '%s' missing reference", da->name); |
311 | 0 | if (err) *err = FR_DICT_ATTR_INTERNAL_ERROR; |
312 | 0 | return NULL; |
313 | 0 | } else { |
314 | 0 | if (err) *err = FR_DICT_ATTR_OK; |
315 | 0 | } |
316 | | |
317 | 0 | return ref; |
318 | 0 | } |
319 | | |
320 | | /** Set a dictionary attribute's name |
321 | | * |
322 | | * @note This function can only be used _before_ the attribute is inserted into the dictionary. |
323 | | * |
324 | | * @param[in] da_p to set name for. |
325 | | * @param[in] name to set. If NULL a name will be automatically generated. |
326 | | */ |
327 | | static inline CC_HINT(always_inline) int dict_attr_name_set(fr_dict_attr_t **da_p, char const *name) |
328 | 2.08M | { |
329 | 2.08M | char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1]; |
330 | 2.08M | size_t name_len; |
331 | 2.08M | char *name_start, *name_end; |
332 | 2.08M | fr_dict_attr_t *da = *da_p; |
333 | | |
334 | | /* |
335 | | * Generate a name if none is specified |
336 | | */ |
337 | 2.08M | if (!name) { |
338 | 1.43M | fr_sbuff_t unknown_name = FR_SBUFF_OUT(buffer, sizeof(buffer)); |
339 | | |
340 | | |
341 | 1.43M | (void) fr_sbuff_in_sprintf(&unknown_name, "%u", da->attr); |
342 | | |
343 | 1.43M | name = fr_sbuff_buff(&unknown_name); |
344 | 1.43M | name_len = fr_sbuff_used(&unknown_name); |
345 | 1.43M | } else { |
346 | 650k | name_len = strlen(name); |
347 | 650k | } |
348 | | |
349 | | /* |
350 | | * Grow the structure to hold the name |
351 | | * |
352 | | * We add the name as an extension because it makes |
353 | | * the code less complex, and means the name value |
354 | | * is copied automatically when if the fr_dict_attr_t |
355 | | * is copied. |
356 | | * |
357 | | * We do still need to fixup the da->name pointer |
358 | | * though. |
359 | | */ |
360 | 2.08M | name_start = dict_attr_ext_alloc_size(da_p, FR_DICT_ATTR_EXT_NAME, name_len + 1); |
361 | 2.08M | if (!name_start) return -1; |
362 | | |
363 | 2.08M | name_end = name_start + name_len; |
364 | | |
365 | 2.08M | memcpy(name_start, name, name_len); |
366 | 2.08M | *name_end = '\0'; |
367 | | |
368 | 2.08M | (*da_p)->name = name_start; |
369 | 2.08M | (*da_p)->name_len = name_len; |
370 | | |
371 | 2.08M | return 0; |
372 | 2.08M | } |
373 | | |
374 | | /** Add a child/nesting extension to an attribute |
375 | | * |
376 | | * @note This function can only be used _before_ the attribute is inserted into the dictionary. |
377 | | * |
378 | | * @param[in] da_p to set a group reference for. |
379 | | */ |
380 | | static inline CC_HINT(always_inline) int dict_attr_children_init(fr_dict_attr_t **da_p) |
381 | 59.9k | { |
382 | 59.9k | fr_dict_attr_ext_children_t *ext; |
383 | | |
384 | 59.9k | ext = dict_attr_ext_alloc(da_p, FR_DICT_ATTR_EXT_CHILDREN); |
385 | 59.9k | if (unlikely(!ext)) return -1; |
386 | | |
387 | 59.9k | return 0; |
388 | 59.9k | } |
389 | | |
390 | | /** Set a reference for a grouping attribute or an alias attribute |
391 | | * |
392 | | * @note This function can only be used _before_ the attribute is inserted into the dictionary. |
393 | | * |
394 | | * @param[in] da_p to set reference for. |
395 | | * @param[in] ref The attribute referred to by this attribute. |
396 | | */ |
397 | | static inline CC_HINT(always_inline) int dict_attr_ref_init(fr_dict_attr_t **da_p, fr_dict_attr_t const *ref) |
398 | 1.29k | { |
399 | 1.29k | fr_dict_attr_ext_ref_t *ext; |
400 | | |
401 | 1.29k | ext = dict_attr_ext_alloc(da_p, FR_DICT_ATTR_EXT_REF); |
402 | 1.29k | if (unlikely(!ext)) return -1; |
403 | | |
404 | 1.29k | ext->ref = ref; |
405 | | |
406 | 1.29k | return 0; |
407 | 1.29k | } |
408 | | |
409 | | /** Cache the vendor pointer for an attribute |
410 | | * |
411 | | * @note This function can only be used _before_ the attribute is inserted into the dictionary. |
412 | | * |
413 | | * @param[in] da_p to set a group reference for. |
414 | | * @param[in] vendor to set. |
415 | | */ |
416 | | static inline CC_HINT(always_inline) int dict_attr_vendor_set(fr_dict_attr_t **da_p, fr_dict_attr_t const *vendor) |
417 | 58.9k | { |
418 | 58.9k | fr_dict_attr_ext_vendor_t *ext; |
419 | | |
420 | 58.9k | ext = dict_attr_ext_alloc(da_p, FR_DICT_ATTR_EXT_VENDOR); |
421 | 58.9k | if (unlikely(!ext)) return -1; |
422 | | |
423 | 58.9k | ext->vendor = vendor; |
424 | | |
425 | 58.9k | return 0; |
426 | 58.9k | } |
427 | | |
428 | | /** Initialise an attribute's da stack from its parent |
429 | | * |
430 | | * @note This function can only be used _before_ the attribute is inserted into the dictionary. |
431 | | * |
432 | | * @param[in] da_p to populate the da_stack for. |
433 | | */ |
434 | | static inline CC_HINT(always_inline) int dict_attr_da_stack_set(fr_dict_attr_t **da_p) |
435 | 2.08M | { |
436 | 2.08M | fr_dict_attr_ext_da_stack_t *ext, *p_ext; |
437 | 2.08M | fr_dict_attr_t *da = *da_p; |
438 | 2.08M | fr_dict_attr_t const *parent = da->parent; |
439 | | |
440 | 2.08M | if (!parent) return 1; |
441 | 2.08M | if (da->depth > FR_DICT_DA_STACK_CACHE_MAX) return 1; |
442 | 2.05M | if (fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_DA_STACK)) return 1; |
443 | | |
444 | 2.05M | p_ext = fr_dict_attr_ext(parent, FR_DICT_ATTR_EXT_DA_STACK); |
445 | 2.05M | if (!p_ext) return 1; |
446 | | |
447 | 0 | ext = dict_attr_ext_alloc_size(da_p, FR_DICT_ATTR_EXT_DA_STACK, sizeof(ext->da_stack[0]) * (da->depth + 1)); |
448 | 0 | if (unlikely(!ext)) return -1; |
449 | | |
450 | 0 | memcpy(ext->da_stack, p_ext->da_stack, sizeof(ext->da_stack[0]) * parent->depth); |
451 | | |
452 | | /* |
453 | | * Always set the last stack entry to ourselves. |
454 | | */ |
455 | 0 | ext->da_stack[da->depth] = da; |
456 | |
|
457 | 0 | return 0; |
458 | 0 | } |
459 | | |
460 | | /** Initialise a per-attribute enumeration table |
461 | | * |
462 | | * @note This function can only be used _before_ the attribute is inserted into the dictionary. |
463 | | * |
464 | | * @param[in] da_p to set a group reference for. |
465 | | */ |
466 | | static inline CC_HINT(always_inline) int dict_attr_enumv_init(fr_dict_attr_t **da_p) |
467 | 2.02M | { |
468 | 2.02M | fr_dict_attr_ext_enumv_t *ext; |
469 | | |
470 | 2.02M | ext = dict_attr_ext_alloc(da_p, FR_DICT_ATTR_EXT_ENUMV); |
471 | 2.02M | if (unlikely(!ext)) return -1; |
472 | | |
473 | 2.02M | return 0; |
474 | 2.02M | } |
475 | | |
476 | | /** Initialise a per-attribute namespace |
477 | | * |
478 | | * @note This function can only be used _before_ the attribute is inserted into the dictionary. |
479 | | * |
480 | | * @param[in] da_p to set a group reference for. |
481 | | */ |
482 | | static inline CC_HINT(always_inline) int dict_attr_namespace_init(fr_dict_attr_t **da_p) |
483 | 59.9k | { |
484 | 59.9k | fr_dict_attr_ext_namespace_t *ext; |
485 | | |
486 | 59.9k | ext = dict_attr_ext_alloc(da_p, FR_DICT_ATTR_EXT_NAMESPACE); |
487 | 59.9k | if (unlikely(!ext)) return -1; |
488 | | |
489 | | /* |
490 | | * Create the table of attributes by name. |
491 | | * There MAY NOT be multiple attributes of the same name. |
492 | | * |
493 | | * If the attribute already has extensions |
494 | | * then we don't want to leak the old |
495 | | * namespace hash table. |
496 | | */ |
497 | 59.9k | if (!ext->namespace) { |
498 | 59.9k | ext->namespace = fr_hash_table_talloc_alloc(*da_p, fr_dict_attr_t, |
499 | 59.9k | dict_attr_name_hash, dict_attr_name_cmp, NULL); |
500 | 59.9k | if (!ext->namespace) { |
501 | 0 | fr_strerror_printf("Failed allocating \"namespace\" table"); |
502 | 0 | return -1; |
503 | 0 | } |
504 | 59.9k | } |
505 | | |
506 | 59.9k | return 0; |
507 | 59.9k | } |
508 | | |
509 | | /** Initialise fields in a dictionary attribute structure |
510 | | * |
511 | | * @note This function can only be used _before_ the attribute is inserted into the dictionary. |
512 | | * |
513 | | * @param[in] da_p to initialise. |
514 | | * @param[in] parent of the attribute, if none, should be |
515 | | * the dictionary root. |
516 | | * @param[in] name of attribute. Pass NULL for auto-generated name. |
517 | | * @param[in] attr number. |
518 | | * @param[in] type of the attribute. |
519 | | * @param[in] args optional initialisation arguments. |
520 | | */ |
521 | | int dict_attr_init(fr_dict_attr_t **da_p, |
522 | | fr_dict_attr_t const *parent, |
523 | | char const *name, int attr, |
524 | | fr_type_t type, dict_attr_args_t const *args) |
525 | 2.08M | { |
526 | 2.08M | static dict_attr_args_t default_args; |
527 | | |
528 | 2.08M | if (!args) args = &default_args; |
529 | | |
530 | 2.08M | **da_p = (fr_dict_attr_t) { |
531 | 2.08M | .attr = attr, |
532 | 2.08M | .last_child_attr = (1 << 24), |
533 | 2.08M | .type = type, |
534 | 2.08M | .flags = *args->flags, |
535 | 2.08M | .parent = parent, |
536 | 2.08M | }; |
537 | | |
538 | | /* |
539 | | * Record the parent |
540 | | */ |
541 | 2.08M | if (parent) { |
542 | 2.08M | (*da_p)->dict = parent->dict; |
543 | 2.08M | (*da_p)->depth = parent->depth + 1; |
544 | | |
545 | | /* |
546 | | * Point to the vendor definition. Since ~90% of |
547 | | * attributes are VSAs, caching this pointer will help. |
548 | | */ |
549 | 2.08M | if (parent->type == FR_TYPE_VENDOR) { |
550 | 58.9k | if (dict_attr_vendor_set(da_p, parent) < 0) return -1; |
551 | 2.02M | } else { |
552 | 2.02M | dict_attr_ext_copy(da_p, parent, FR_DICT_ATTR_EXT_VENDOR); /* Noop if no vendor extension */ |
553 | 2.02M | } |
554 | 2.08M | } else { |
555 | 39 | (*da_p)->depth = 0; |
556 | 39 | } |
557 | | |
558 | | /* |
559 | | * Cache the da_stack so we don't need |
560 | | * to generate it at runtime. |
561 | | */ |
562 | 2.08M | dict_attr_da_stack_set(da_p); |
563 | | |
564 | | /* |
565 | | * Structural types can have children |
566 | | * so add the extension for them. |
567 | | */ |
568 | 2.08M | switch (type) { |
569 | 95.8k | case FR_TYPE_STRUCTURAL: |
570 | 95.8k | structural: |
571 | | /* |
572 | | * Groups don't have children or namespaces. But |
573 | | * they always have refs. Either to the root of |
574 | | * the current dictionary, or to another dictionary, |
575 | | * via its top-level TLV. |
576 | | * |
577 | | * Note that when multiple TLVs have the same |
578 | | * children, the dictionary has to use "clone=" |
579 | | * instead of "ref=". That's because the |
580 | | * children of the TLVs all require the correct |
581 | | * parentage. Perhaps that can be changed when |
582 | | * the encoders / decoders are updated. It would be good to just reference the DAs instead of cloning an entire subtree. |
583 | | */ |
584 | 60.5k | if (type == FR_TYPE_GROUP) { |
585 | 581 | if (dict_attr_ref_init(da_p, NULL) < 0) return -1; |
586 | 581 | break; |
587 | 581 | } |
588 | | |
589 | 59.9k | if (dict_attr_children_init(da_p) < 0) return -1; |
590 | 59.9k | if (dict_attr_namespace_init(da_p) < 0) return -1; /* Needed for all TLV style attributes */ |
591 | 59.9k | break; |
592 | | |
593 | | /* |
594 | | * Keying types *sigh* |
595 | | */ |
596 | 59.9k | case FR_TYPE_UINT8: /* Hopefully temporary until unions are done properly */ |
597 | 2.03k | case FR_TYPE_UINT16: /* Same here */ |
598 | 2.03k | if (dict_attr_enumv_init(da_p) < 0) return -1; |
599 | 2.03k | goto structural; |
600 | | |
601 | | /* |
602 | | * Leaf types |
603 | | */ |
604 | 2.02M | default: |
605 | 2.02M | if (dict_attr_enumv_init(da_p) < 0) return -1; |
606 | 2.02M | break; |
607 | 2.08M | } |
608 | | |
609 | | /* |
610 | | * This attribute is just a reference to another. |
611 | | */ |
612 | 2.08M | if (args->ref) if (dict_attr_ref_init(da_p, args->ref) < 0) return -1; |
613 | | |
614 | | /* |
615 | | * Name is a separate talloc chunk. We allocate |
616 | | * it last because we cache the pointer value. |
617 | | */ |
618 | 2.08M | if (dict_attr_name_set(da_p, name) < 0) return -1; |
619 | | |
620 | 2.08M | DA_VERIFY(*da_p); |
621 | | |
622 | 2.08M | return 0; |
623 | 2.08M | } |
624 | | |
625 | | static int _dict_attr_free(fr_dict_attr_t *da) |
626 | 2.08M | { |
627 | 2.08M | fr_dict_attr_ext_enumv_t *ext; |
628 | | |
629 | | #if 0 |
630 | | #ifdef WITH_VERIFY_PTR |
631 | | /* |
632 | | * Check that any attribute we reference is still valid |
633 | | * when we're being freed. |
634 | | */ |
635 | | fr_dict_attr_t const *ref = fr_dict_attr_ref(da); |
636 | | |
637 | | if (ref) (void)talloc_get_type_abort_const(ref, fr_dict_attr_t); |
638 | | #endif |
639 | | #endif |
640 | | |
641 | 2.08M | ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_ENUMV); |
642 | 2.08M | if (ext) talloc_free(ext->value_by_name); /* Ensure this is freed before the enumvs */ |
643 | | |
644 | 2.08M | return 0; |
645 | 2.08M | } |
646 | | /** Allocate a partially completed attribute |
647 | | * |
648 | | * This is useful in some instances where we need to pre-allocate the attribute |
649 | | * for talloc hierarchy reasons, but want to finish initialising it |
650 | | * with #dict_attr_init later. |
651 | | * |
652 | | * @param[in] ctx to allocate attribute in. |
653 | | * @return |
654 | | * - A new, partially completed, fr_dict_attr_t on success. |
655 | | * - NULL on failure (memory allocation error). |
656 | | */ |
657 | | fr_dict_attr_t *dict_attr_alloc_null(TALLOC_CTX *ctx) |
658 | 2.08M | { |
659 | 2.08M | fr_dict_attr_t *da; |
660 | | |
661 | | /* |
662 | | * Do not use talloc zero, the caller |
663 | | * always initialises memory allocated |
664 | | * here. |
665 | | */ |
666 | 2.08M | da = talloc(ctx, fr_dict_attr_t); |
667 | 2.08M | if (unlikely(!da)) return NULL; |
668 | | |
669 | | /* |
670 | | * On error paths in the caller, the |
671 | | * caller may free the attribute |
672 | | * allocated here without initialising |
673 | | * the ext array, which is then |
674 | | * accessed in the destructor. |
675 | | */ |
676 | 2.08M | memset(da->ext, 0, sizeof(da->ext)); |
677 | | |
678 | 2.08M | talloc_set_destructor(da, _dict_attr_free); |
679 | | |
680 | 2.08M | return da; |
681 | 2.08M | } |
682 | | |
683 | | /** Allocate a dictionary attribute on the heap |
684 | | * |
685 | | * @param[in] ctx to allocate the attribute in. |
686 | | * @param[in] parent of the attribute, if none, should be |
687 | | * the dictionary root. |
688 | | * @param[in] name of the attribute. If NULL an OID string |
689 | | * will be created and set as the name. |
690 | | * @param[in] attr number. |
691 | | * @param[in] type of the attribute. |
692 | | * @param[in] args optional initialisation arguments. |
693 | | * @return |
694 | | * - A new fr_dict_attr_t on success. |
695 | | * - NULL on failure. |
696 | | */ |
697 | | fr_dict_attr_t *dict_attr_alloc(TALLOC_CTX *ctx, |
698 | | fr_dict_attr_t const *parent, |
699 | | char const *name, int attr, |
700 | | fr_type_t type, dict_attr_args_t const *args) |
701 | 1.47M | { |
702 | 1.47M | fr_dict_attr_t *n; |
703 | | |
704 | 1.47M | n = dict_attr_alloc_null(ctx); |
705 | 1.47M | if (unlikely(!n)) return NULL; |
706 | | |
707 | 1.47M | if (dict_attr_init(&n, parent, name, attr, type, args) < 0) { |
708 | 0 | talloc_free(n); |
709 | 0 | return NULL; |
710 | 0 | } |
711 | | |
712 | 1.47M | return n; |
713 | 1.47M | } |
714 | | |
715 | | /** Copy a an existing attribute |
716 | | * |
717 | | * @param[in] ctx to allocate new attribute in. |
718 | | * @param[in] in attribute to copy. |
719 | | * @param[in] new_name to assign to the attribute. |
720 | | * If NULL the existing name will be used. |
721 | | * @return |
722 | | * - A copy of the input fr_dict_attr_t on success. |
723 | | * - NULL on failure. |
724 | | */ |
725 | | fr_dict_attr_t *dict_attr_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *in, char const *new_name) |
726 | 878 | { |
727 | 878 | fr_dict_attr_t *n; |
728 | | |
729 | 878 | n = dict_attr_alloc(ctx, in->parent, new_name ? new_name : in->name, |
730 | 878 | in->attr, in->type, &(dict_attr_args_t){ .flags = &in->flags }); |
731 | 878 | if (unlikely(!n)) return NULL; |
732 | | |
733 | 878 | if (dict_attr_ext_copy_all(&n, in) < 0) { |
734 | 0 | talloc_free(n); |
735 | 0 | return NULL; |
736 | 0 | } |
737 | 878 | DA_VERIFY(n); |
738 | | |
739 | 878 | return n; |
740 | 878 | } |
741 | | |
742 | | /** Copy the children of an existing attribute |
743 | | * |
744 | | * @param[in] dict to allocate the children in |
745 | | * @param[in] dst where to copy the children to |
746 | | * @param[in] src where to copy the children from |
747 | | * @return |
748 | | * - 0 on success |
749 | | * - <0 on error |
750 | | */ |
751 | | int dict_attr_acopy_children(fr_dict_t *dict, fr_dict_attr_t *dst, fr_dict_attr_t const *src) |
752 | 270 | { |
753 | 270 | fr_dict_attr_t const *child = NULL; |
754 | 270 | fr_dict_attr_t *copy; |
755 | 270 | fr_dict_attr_ext_enumv_t *ext; |
756 | | |
757 | 270 | fr_assert(fr_dict_attr_has_ext(dst, FR_DICT_ATTR_EXT_CHILDREN)); |
758 | 270 | fr_assert(dst->type == src->type); |
759 | 270 | fr_assert(fr_dict_attr_is_key_field(src) == fr_dict_attr_is_key_field(dst)); |
760 | | |
761 | 270 | for (child = fr_dict_attr_iterate_children(src, &child); |
762 | 1.02k | child != NULL; |
763 | 756 | child = fr_dict_attr_iterate_children(src, &child)) { |
764 | 756 | copy = dict_attr_acopy(dict->pool, child, NULL); |
765 | 756 | if (!copy) { |
766 | 0 | fr_strerror_printf("Failed cloning child %s", child->name); |
767 | 0 | return -1; |
768 | 0 | } |
769 | | |
770 | 756 | copy->parent = dst; |
771 | | |
772 | 756 | if (dict_attr_child_add(dst, copy) < 0) return -1; |
773 | | |
774 | 756 | if (dict_attr_add_to_namespace(dst, copy) < 0) return -1; |
775 | | |
776 | 756 | if (!dict_attr_children(child)) continue; |
777 | | |
778 | 140 | if (dict_attr_acopy_children(dict, copy, child) < 0) return -1; |
779 | 140 | } |
780 | | |
781 | | /* |
782 | | * Copy VALUEs, too. |
783 | | */ |
784 | 270 | ext = fr_dict_attr_ext(src, FR_DICT_ATTR_EXT_ENUMV); |
785 | 270 | if (ext && ext->name_by_value) { |
786 | 24 | int cloned; |
787 | | |
788 | 24 | cloned = dict_attr_acopy_enumv(dst, src); |
789 | 24 | if (cloned < 0) return -1; |
790 | 24 | } |
791 | | |
792 | 270 | return 0; |
793 | 270 | } |
794 | | |
795 | | /** Copy the VALUEs of an existing attribute, by casting them |
796 | | * |
797 | | * @param[in] dst where to cast the VALUEs to |
798 | | * @param[in] src where to cast the VALUEs from |
799 | | * @return |
800 | | * - 0 on success (but copied no values) |
801 | | * - 1 on success (but copied at least one value) |
802 | | * - <0 on error |
803 | | */ |
804 | | int dict_attr_acopy_enumv(fr_dict_attr_t *dst, fr_dict_attr_t const *src) |
805 | 28 | { |
806 | 28 | fr_dict_enum_value_t const *enumv; |
807 | 28 | fr_dict_attr_ext_enumv_t *ext; |
808 | 28 | fr_hash_iter_t iter; |
809 | 28 | int copied = 0; |
810 | | |
811 | 28 | fr_assert(!fr_type_is_non_leaf(dst->type)); |
812 | 28 | fr_assert(!fr_type_is_non_leaf(src->type)); |
813 | | |
814 | 28 | fr_assert(fr_dict_attr_has_ext(dst, FR_DICT_ATTR_EXT_ENUMV)); |
815 | 28 | fr_assert(fr_dict_attr_has_ext(src, FR_DICT_ATTR_EXT_ENUMV)); |
816 | | |
817 | 28 | ext = fr_dict_attr_ext(src, FR_DICT_ATTR_EXT_ENUMV); |
818 | 28 | if (!ext) { |
819 | 0 | fr_assert(0); |
820 | 0 | return -1; |
821 | 0 | } |
822 | | |
823 | 28 | if (!ext->name_by_value) { |
824 | 0 | fr_strerror_printf("Reference enum %s does not have any VALUEs to copy", src->name); |
825 | 0 | return -1; |
826 | 0 | } |
827 | | |
828 | | /* |
829 | | * Loop over the VALUEs, adding names from the old |
830 | | * attribute to the new one. |
831 | | * |
832 | | * If a value can't be cast, then just ignore it. |
833 | | */ |
834 | 28 | for (enumv = fr_hash_table_iter_init(ext->name_by_value, &iter); |
835 | 122 | enumv; |
836 | 94 | enumv = fr_hash_table_iter_next(ext->name_by_value, &iter)) { |
837 | 94 | if (dict_attr_enum_add_name(dst, enumv->name, enumv->value, true, |
838 | 94 | false, NULL) < 0) { |
839 | 0 | continue; |
840 | 0 | } |
841 | | |
842 | 94 | copied++; |
843 | 94 | } |
844 | | |
845 | 28 | return copied; |
846 | 28 | } |
847 | | |
848 | | /** Add a protocol to the global protocol table |
849 | | * |
850 | | * Inserts a protocol into the global protocol table. Uses the root attributes |
851 | | * of the dictionary for comparisons. |
852 | | * |
853 | | * @param[in] dict of protocol we're inserting. |
854 | | * @return |
855 | | * - 0 on success. |
856 | | * - -1 on failure. |
857 | | */ |
858 | | int dict_protocol_add(fr_dict_t *dict) |
859 | 21 | { |
860 | 21 | if (!dict->root) return -1; /* Should always have root */ |
861 | | |
862 | 21 | if (!fr_hash_table_insert(dict_gctx->protocol_by_name, dict)) { |
863 | 0 | fr_dict_t *old_proto; |
864 | |
|
865 | 0 | old_proto = fr_hash_table_find(dict_gctx->protocol_by_name, dict); |
866 | 0 | if (!old_proto) { |
867 | 0 | fr_strerror_printf("%s: Failed inserting protocol name %s", __FUNCTION__, dict->root->name); |
868 | 0 | return -1; |
869 | 0 | } |
870 | | |
871 | 0 | if ((strcmp(old_proto->root->name, dict->root->name) == 0) && |
872 | 0 | (old_proto->root->name == dict->root->name)) { |
873 | 0 | fr_strerror_printf("%s: Duplicate protocol name %s", __FUNCTION__, dict->root->name); |
874 | 0 | return -1; |
875 | 0 | } |
876 | | |
877 | 0 | return 0; |
878 | 0 | } |
879 | 21 | dict->in_protocol_by_name = true; |
880 | | |
881 | 21 | if (!fr_hash_table_insert(dict_gctx->protocol_by_num, dict)) { |
882 | 0 | fr_strerror_printf("%s: Duplicate protocol number %i", __FUNCTION__, dict->root->attr); |
883 | 0 | return -1; |
884 | 0 | } |
885 | 21 | dict->in_protocol_by_num = true; |
886 | | |
887 | 21 | dict_dependent_add(dict, "global"); |
888 | | |
889 | | /* |
890 | | * Create and add sub-attributes which allow other |
891 | | * protocols to be encapsulated in the internal |
892 | | * namespace. |
893 | | */ |
894 | 21 | if (dict_gctx->internal && (dict != dict_gctx->internal)) { |
895 | 21 | fr_dict_attr_t const *da; |
896 | 21 | fr_dict_attr_flags_t flags = { 0 }; |
897 | | |
898 | 21 | if (!dict_gctx->attr_protocol_encapsulation) dict_gctx->attr_protocol_encapsulation = fr_dict_attr_by_name(NULL, dict_gctx->internal->root, "Proto"); |
899 | 21 | fr_assert(dict_gctx->attr_protocol_encapsulation != NULL); |
900 | | |
901 | 21 | da = fr_dict_attr_child_by_num(dict_gctx->attr_protocol_encapsulation, dict->root->attr); |
902 | 21 | if (!da) { |
903 | 21 | if (fr_dict_attr_add(dict_gctx->internal, dict_gctx->attr_protocol_encapsulation, dict->root->name, dict->root->attr, FR_TYPE_GROUP, &flags) < 0) { |
904 | 0 | return -1; |
905 | 0 | } |
906 | | |
907 | 21 | da = fr_dict_attr_child_by_num(dict_gctx->attr_protocol_encapsulation, dict->root->attr); |
908 | 21 | fr_assert(da != NULL); |
909 | 21 | } |
910 | | |
911 | 21 | dict_attr_ref_set(da, dict->root); |
912 | 21 | } |
913 | | |
914 | 21 | return 0; |
915 | 21 | } |
916 | | |
917 | | /** Add a vendor to the dictionary |
918 | | * |
919 | | * Inserts a vendor entry into the vendor hash table. This must be done before adding |
920 | | * attributes under a VSA. |
921 | | * |
922 | | * @param[in] dict of protocol context we're operating in. |
923 | | * If NULL the internal dictionary will be used. |
924 | | * @param[in] name of the vendor. |
925 | | * @param[in] num Vendor's Private Enterprise Number. |
926 | | * @return |
927 | | * - 0 on success. |
928 | | * - -1 on failure. |
929 | | */ |
930 | | int dict_vendor_add(fr_dict_t *dict, char const *name, unsigned int num) |
931 | 751 | { |
932 | 751 | size_t len; |
933 | 751 | fr_dict_vendor_t *vendor; |
934 | | |
935 | 751 | INTERNAL_IF_NULL(dict, -1); |
936 | | |
937 | 751 | len = strlen(name); |
938 | 751 | if (len >= FR_DICT_VENDOR_MAX_NAME_LEN) { |
939 | 0 | fr_strerror_printf("%s: Vendor name too long", __FUNCTION__); |
940 | 0 | return -1; |
941 | 0 | } |
942 | | |
943 | 751 | vendor = talloc_zero(dict, fr_dict_vendor_t); |
944 | 751 | if (!vendor) { |
945 | 0 | oom: |
946 | 0 | fr_strerror_const("Out of memory"); |
947 | 0 | return -1; |
948 | 0 | } |
949 | | |
950 | 751 | vendor->name = talloc_typed_strdup(vendor, name); |
951 | 751 | if (!vendor->name) { |
952 | 0 | talloc_free(vendor); |
953 | 0 | goto oom; |
954 | 0 | } |
955 | 751 | vendor->pen = num; |
956 | 751 | vendor->type = vendor->length = 1; /* defaults */ |
957 | | |
958 | 751 | if (!fr_hash_table_insert(dict->vendors_by_name, vendor)) { |
959 | 0 | fr_dict_vendor_t const *old_vendor; |
960 | |
|
961 | 0 | old_vendor = fr_hash_table_find(dict->vendors_by_name, vendor); |
962 | 0 | if (!old_vendor) { |
963 | 0 | fr_strerror_printf("%s: Failed inserting vendor name %s", __FUNCTION__, name); |
964 | 0 | return -1; |
965 | 0 | } |
966 | 0 | if ((strcmp(old_vendor->name, vendor->name) == 0) && (old_vendor->pen != vendor->pen)) { |
967 | 0 | fr_strerror_printf("%s: Duplicate vendor name %s", __FUNCTION__, name); |
968 | 0 | return -1; |
969 | 0 | } |
970 | | |
971 | | /* |
972 | | * Already inserted. Discard the duplicate entry. |
973 | | */ |
974 | 0 | talloc_free(vendor); |
975 | |
|
976 | 0 | return 0; |
977 | 0 | } |
978 | | |
979 | | /* |
980 | | * Insert the SAME pointer (not free'd when this table is |
981 | | * deleted), into another table. |
982 | | * |
983 | | * We want this behaviour because we want OLD names for |
984 | | * the attributes to be read from the configuration |
985 | | * files, but when we're printing them, (and looking up |
986 | | * by value) we want to use the NEW name. |
987 | | */ |
988 | 751 | if (fr_hash_table_replace(NULL, dict->vendors_by_num, vendor) < 0) { |
989 | 0 | fr_strerror_printf("%s: Failed inserting vendor %s", __FUNCTION__, name); |
990 | 0 | return -1; |
991 | 0 | } |
992 | | |
993 | 751 | return 0; |
994 | 751 | } |
995 | | |
996 | | /** See if a #fr_dict_attr_t can have children |
997 | | * |
998 | | * The check for children is complicated by the need for "int" types |
999 | | * to have children, when they are `key` fields in a `struct`. This |
1000 | | * situation occurs when a struct has multiple sub-structures, which |
1001 | | * are selected based on a `key` field. |
1002 | | * |
1003 | | * There is no other place for the sub-structures to go. In the |
1004 | | * future, we may extend the functionality of the `key` field, by |
1005 | | * allowing non-integer data types. That would require storing keys |
1006 | | * as #fr_dict_enum_value_t, and then placing the child (i.e. sub) |
1007 | | * structures there. But that would involve adding children to |
1008 | | * enums, which is currently not supported. |
1009 | | * |
1010 | | * @param da the dictionary attribute to check. |
1011 | | */ |
1012 | | bool dict_attr_can_have_children(fr_dict_attr_t const *da) |
1013 | 45.6k | { |
1014 | 45.6k | switch (da->type) { |
1015 | 12.0k | case FR_TYPE_TLV: |
1016 | 37.8k | case FR_TYPE_VENDOR: |
1017 | 42.9k | case FR_TYPE_VSA: |
1018 | 45.3k | case FR_TYPE_STRUCT: |
1019 | 45.3k | return true; |
1020 | | |
1021 | 52 | case FR_TYPE_UINT8: |
1022 | 324 | case FR_TYPE_UINT16: |
1023 | 324 | case FR_TYPE_UINT32: |
1024 | | /* |
1025 | | * Children are allowed here, but ONLY if this |
1026 | | * attribute is a key field. |
1027 | | */ |
1028 | 324 | if (da->parent && (da->parent->type == FR_TYPE_STRUCT) && fr_dict_attr_is_key_field(da)) return true; |
1029 | 0 | break; |
1030 | | |
1031 | 0 | default: |
1032 | 0 | break; |
1033 | 45.6k | } |
1034 | | |
1035 | 0 | return false; |
1036 | 45.6k | } |
1037 | | |
1038 | | /** Add a child to a parent. |
1039 | | * |
1040 | | * @param[in] parent we're adding a child to. |
1041 | | * @param[in] child to add to parent. |
1042 | | * @return |
1043 | | * - 0 on success. |
1044 | | * - -1 on failure (memory allocation error). |
1045 | | */ |
1046 | | int dict_attr_child_add(fr_dict_attr_t *parent, fr_dict_attr_t *child) |
1047 | 45.6k | { |
1048 | 45.6k | fr_dict_attr_t const * const *bin; |
1049 | 45.6k | fr_dict_attr_t **this; |
1050 | 45.6k | fr_dict_attr_t const **children; |
1051 | | |
1052 | | /* |
1053 | | * Setup fields in the child |
1054 | | */ |
1055 | 45.6k | fr_assert(child->parent == parent); |
1056 | | |
1057 | 45.6k | DA_VERIFY(child); |
1058 | | |
1059 | 45.6k | if (fr_dict_attr_ref(parent)) { |
1060 | 0 | fr_strerror_printf("Cannot add children to attribute '%s' which has 'ref=%s'", |
1061 | 0 | parent->name, fr_dict_attr_ref(parent)->name); |
1062 | 0 | return -1; |
1063 | 0 | } |
1064 | | |
1065 | 45.6k | if (!dict_attr_can_have_children(parent)) { |
1066 | 0 | fr_strerror_printf("Cannot add children to attribute '%s' of type %s", |
1067 | 0 | parent->name, |
1068 | 0 | fr_type_to_str(parent->type)); |
1069 | 0 | return -1; |
1070 | 0 | } |
1071 | | |
1072 | 45.6k | if ((parent->type == FR_TYPE_VSA) && (child->type != FR_TYPE_VENDOR)) { |
1073 | 0 | fr_strerror_printf("Cannot add non-vendor children to attribute '%s' of type %s", |
1074 | 0 | parent->name, |
1075 | 0 | fr_type_to_str(parent->type)); |
1076 | 0 | return -1; |
1077 | 0 | } |
1078 | | |
1079 | | /* |
1080 | | * The parent has children by name only, not by number. Don't even bother trying to track |
1081 | | * numbers, except for VENDOR in root, and MEMBER of a struct. |
1082 | | */ |
1083 | 45.6k | if (!parent->flags.is_root && parent->flags.name_only && |
1084 | 45.6k | (parent->type != FR_TYPE_STRUCT) && (parent->type != FR_TYPE_TLV)) { |
1085 | 0 | return 0; |
1086 | 0 | } |
1087 | | |
1088 | | /* |
1089 | | * We only allocate the pointer array *if* the parent has children. |
1090 | | */ |
1091 | 45.6k | children = dict_attr_children(parent); |
1092 | 45.6k | if (!children) { |
1093 | 2.36k | children = talloc_zero_array(parent, fr_dict_attr_t const *, UINT8_MAX + 1); |
1094 | 2.36k | if (!children) { |
1095 | 0 | fr_strerror_const("Out of memory"); |
1096 | 0 | return -1; |
1097 | 0 | } |
1098 | 2.36k | if (dict_attr_children_set(parent, children) < 0) return -1; |
1099 | 2.36k | } |
1100 | | |
1101 | | /* |
1102 | | * Treat the array as a hash of 255 bins, with attributes |
1103 | | * sorted into bins using num % 255. |
1104 | | * |
1105 | | * Although the various protocols may define numbers higher than 255: |
1106 | | * |
1107 | | * RADIUS/DHCPv4 - 1-255 |
1108 | | * Diameter/Internal - 1-4294967295 |
1109 | | * DHCPv6 - 1-65535 |
1110 | | * |
1111 | | * In reality very few will ever use attribute numbers > 500, so for |
1112 | | * the majority of lookups we get O(1) performance. |
1113 | | * |
1114 | | * Attributes are inserted into the bin in order of their attribute |
1115 | | * numbers to allow slightly more efficient lookups. |
1116 | | */ |
1117 | 46.6k | for (bin = &children[child->attr & 0xff]; *bin; bin = &(*bin)->next) { |
1118 | | /* |
1119 | | * Workaround for vendors that overload the RFC space. |
1120 | | * Structural attributes always take priority. |
1121 | | */ |
1122 | 6.94k | bool child_is_struct = fr_type_is_structural(child->type); |
1123 | 6.94k | bool bin_is_struct = fr_type_is_structural((*bin)->type); |
1124 | | |
1125 | 6.94k | if (child_is_struct && !bin_is_struct) break; |
1126 | 6.70k | if (fr_dict_vendor_num_by_da(child) <= fr_dict_vendor_num_by_da(*bin)) break; /* Prioritise RFC attributes */ |
1127 | 1.05k | if (child->attr <= (*bin)->attr) break; |
1128 | 1.05k | } |
1129 | | |
1130 | 45.6k | memcpy(&this, &bin, sizeof(this)); |
1131 | 45.6k | child->next = *this; |
1132 | 45.6k | *this = child; |
1133 | | |
1134 | 45.6k | return 0; |
1135 | 45.6k | } |
1136 | | |
1137 | | /** Add an attribute to the name table for an attribute |
1138 | | * |
1139 | | * @param[in] parent containing the namespace to add this attribute to. |
1140 | | * @param[in] da to add to the name lookup tables. |
1141 | | * @return |
1142 | | * - 0 on success. |
1143 | | * - -1 on failure. |
1144 | | */ |
1145 | | int dict_attr_add_to_namespace(fr_dict_attr_t const *parent, fr_dict_attr_t *da) |
1146 | 45.6k | { |
1147 | 45.6k | fr_hash_table_t *namespace; |
1148 | | |
1149 | 45.6k | namespace = dict_attr_namespace(parent); |
1150 | 45.6k | if (unlikely(!namespace)) { |
1151 | 0 | fr_strerror_printf("Parent \"%s\" has no namespace", parent->name); |
1152 | 0 | error: |
1153 | 0 | return -1; |
1154 | 0 | } |
1155 | | |
1156 | | /* |
1157 | | * Sanity check to stop children of vendors ending |
1158 | | * up in the Vendor-Specific or root namespace. |
1159 | | */ |
1160 | 45.6k | if ((fr_dict_vendor_num_by_da(da) != 0) && (da->type != FR_TYPE_VENDOR) && |
1161 | 45.6k | ((parent->type == FR_TYPE_VSA) || parent->flags.is_root)) { |
1162 | 0 | fr_strerror_printf("Cannot insert attribute '%s' of type %s into %s", |
1163 | 0 | da->name, |
1164 | 0 | fr_type_to_str(da->type), |
1165 | 0 | parent->name); |
1166 | 0 | goto error; |
1167 | 0 | } |
1168 | | |
1169 | | /* |
1170 | | * Insert the attribute, only if it's not a duplicate. |
1171 | | */ |
1172 | 45.6k | if (!fr_hash_table_insert(namespace, da)) { |
1173 | 74 | fr_dict_attr_t *a; |
1174 | | |
1175 | | /* |
1176 | | * Find the old name. If it's the same name and |
1177 | | * but the parent, or number, or type are |
1178 | | * different, that's an error. |
1179 | | */ |
1180 | 74 | a = fr_hash_table_find(namespace, da); |
1181 | 74 | if (a && (strcasecmp(a->name, da->name) == 0)) { |
1182 | 74 | if ((a->attr != da->attr) || (a->type != da->type) || (a->parent != da->parent)) { |
1183 | 0 | fr_strerror_printf("Duplicate attribute name \"%s\"", da->name); |
1184 | 0 | goto error; |
1185 | 0 | } |
1186 | 74 | } |
1187 | | |
1188 | | /* |
1189 | | * Otherwise the attribute has been redefined later |
1190 | | * in the dictionary. |
1191 | | * |
1192 | | * The original fr_dict_attr_t remains in the |
1193 | | * dictionary but entry in the name hash table is |
1194 | | * updated to point to the new definition. |
1195 | | */ |
1196 | 74 | if (fr_hash_table_replace(NULL, namespace, da) < 0) { |
1197 | 0 | fr_strerror_const("Internal error storing attribute"); |
1198 | 0 | goto error; |
1199 | 0 | } |
1200 | 74 | } |
1201 | | |
1202 | 45.6k | return 0; |
1203 | 45.6k | } |
1204 | | |
1205 | | static int dict_attr_compatible(fr_dict_attr_t const *parent, fr_dict_attr_t const *old, fr_dict_attr_t const *n) |
1206 | 214 | { |
1207 | 214 | if (old->parent != parent) { |
1208 | 0 | fr_strerror_printf_push("Cannot add duplicate attribute \"%s\" with different parent (old %s, new %s)", |
1209 | 0 | n->name, old->parent->name, parent->name); |
1210 | 0 | return -1; |
1211 | 0 | } |
1212 | | |
1213 | 214 | if (old->attr != n->attr) { |
1214 | 0 | fr_strerror_printf_push("Cannot add duplicate attribute name \"%s\" with different number (old %u, new %d)", |
1215 | 0 | n->name, old->attr, n->attr); |
1216 | 0 | return -1; |
1217 | 0 | } |
1218 | | |
1219 | 214 | if (old->type != n->type) { |
1220 | 0 | fr_strerror_printf_push("Cannot add duplicate attribute with different type " |
1221 | 0 | "(old attribute \"%s\" has type %s, new attribute \"%s\" has type %s)", |
1222 | 0 | old->name, |
1223 | 0 | fr_type_to_str(old->type), |
1224 | 0 | n->name, |
1225 | 0 | fr_type_to_str(n->type)); |
1226 | 0 | return -1; |
1227 | 0 | } |
1228 | | |
1229 | 214 | return 0; |
1230 | 214 | } |
1231 | | |
1232 | | /** Add an attribute to the dictionary |
1233 | | * |
1234 | | * @param[in] dict of protocol context we're operating in. |
1235 | | * If NULL the internal dictionary will be used. |
1236 | | * @param[in] parent to add attribute under. |
1237 | | * @param[in] name of the attribute. |
1238 | | * @param[in] attr number. |
1239 | | * @param[in] type of attribute. |
1240 | | * @param[in] flags to set in the attribute. |
1241 | | * @return |
1242 | | * - 0 on success. |
1243 | | * - -1 on failure. |
1244 | | */ |
1245 | | int fr_dict_attr_add(fr_dict_t *dict, fr_dict_attr_t const *parent, |
1246 | | char const *name, int attr, fr_type_t type, fr_dict_attr_flags_t const *flags) |
1247 | 43.4k | { |
1248 | 43.4k | fr_dict_attr_t *n; |
1249 | 43.4k | fr_dict_attr_t const *old; |
1250 | 43.4k | fr_dict_attr_flags_t our_flags = *flags; |
1251 | 43.4k | bool self_allocated = false; |
1252 | 43.4k | #ifndef NDEBUG |
1253 | 43.4k | fr_dict_attr_t const *da; |
1254 | 43.4k | #endif |
1255 | | |
1256 | 43.4k | if (unlikely(dict->read_only)) { |
1257 | 0 | fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root(dict)->name); |
1258 | 0 | return -1; |
1259 | 0 | } |
1260 | | |
1261 | 43.4k | self_allocated = (attr < 0); |
1262 | | |
1263 | | /* |
1264 | | * Check that the definition is valid. |
1265 | | */ |
1266 | 43.4k | if (!dict_attr_fields_valid(dict, parent, name, &attr, type, &our_flags)) return -1; |
1267 | | |
1268 | 43.4k | n = dict_attr_alloc(dict->pool, parent, name, attr, type, &(dict_attr_args_t){ .flags = &our_flags}); |
1269 | 43.4k | if (!n) return -1; |
1270 | | |
1271 | 43.4k | #define FLAGS_EQUAL(_x) (old->flags._x == flags->_x) |
1272 | | |
1273 | 43.4k | old = fr_dict_attr_by_name(NULL, parent, name); |
1274 | 43.4k | if (old) { |
1275 | | /* |
1276 | | * Don't bother inserting exact duplicates. |
1277 | | */ |
1278 | 16 | if ((old->parent == parent) && (old->type == type) && |
1279 | 16 | FLAGS_EQUAL(array) && FLAGS_EQUAL(subtype) && |
1280 | 16 | ((old->attr == (unsigned int) attr) || self_allocated)) { |
1281 | 16 | return 0; |
1282 | 16 | } |
1283 | | |
1284 | | /* |
1285 | | * We have the same name, but different |
1286 | | * properties. That's an error. |
1287 | | */ |
1288 | 0 | if (dict_attr_compatible(parent, old, n) < 0) goto error; |
1289 | | |
1290 | | /* |
1291 | | * We have the same name, and same (enough) |
1292 | | * properties. Discard the duplicate. |
1293 | | */ |
1294 | 0 | talloc_free(n); |
1295 | 0 | return 0; |
1296 | 0 | } |
1297 | | |
1298 | | /* |
1299 | | * Attributes can also be indexed by number. Ensure that |
1300 | | * all attributes of the same number have the same |
1301 | | * properties. |
1302 | | */ |
1303 | 43.4k | old = fr_dict_attr_child_by_num(parent, n->attr); |
1304 | 43.4k | if (old && (dict_attr_compatible(parent, old, n) < 0)) goto error; |
1305 | | |
1306 | 43.4k | if (dict_attr_add_to_namespace(parent, n) < 0) { |
1307 | 0 | error: |
1308 | 0 | talloc_free(n); |
1309 | 0 | return -1; |
1310 | 0 | } |
1311 | | |
1312 | | /* |
1313 | | * Add in by number |
1314 | | */ |
1315 | 43.4k | if (dict_attr_child_add(UNCONST(fr_dict_attr_t *, parent), n) < 0) goto error; |
1316 | | |
1317 | 43.4k | #ifndef NDEBUG |
1318 | | /* |
1319 | | * Check if we added the attribute |
1320 | | */ |
1321 | 43.4k | da = dict_attr_child_by_num(parent, n->attr); |
1322 | 43.4k | if (!da) { |
1323 | 0 | fr_strerror_printf("FATAL - Failed to find attribute number %u we just added to parent %s.", n->attr, parent->name); |
1324 | 0 | return -1; |
1325 | 0 | } |
1326 | | |
1327 | 43.4k | if (!dict_attr_by_name(NULL, parent, n->name)) { |
1328 | 0 | fr_strerror_printf("FATAL - Failed to find attribute '%s' we just added to parent %s.", n->name, parent->name); |
1329 | 0 | return -1; |
1330 | 0 | } |
1331 | 43.4k | #endif |
1332 | | |
1333 | | /* |
1334 | | * If it's a group attribute, the default |
1335 | | * reference goes to the root of the |
1336 | | * dictionary as that's where the default |
1337 | | * name/numberspace is. |
1338 | | * |
1339 | | * This may be updated by the caller. |
1340 | | */ |
1341 | 43.4k | if (type == FR_TYPE_GROUP) dict_attr_ref_set(n, fr_dict_root(dict)); |
1342 | | |
1343 | 43.4k | return 0; |
1344 | 43.4k | } |
1345 | | |
1346 | | int dict_attr_enum_add_name(fr_dict_attr_t *da, char const *name, |
1347 | | fr_value_box_t const *value, |
1348 | | bool coerce, bool takes_precedence, |
1349 | | fr_dict_attr_t const *child_struct) |
1350 | 37.3k | { |
1351 | 37.3k | size_t len; |
1352 | 37.3k | fr_dict_enum_value_t *enumv = NULL; |
1353 | 37.3k | fr_value_box_t *enum_value = NULL; |
1354 | 37.3k | fr_dict_attr_ext_enumv_t *ext; |
1355 | | |
1356 | 37.3k | if (!da) { |
1357 | 0 | fr_strerror_printf("%s: Dictionary attribute not specified", __FUNCTION__); |
1358 | 0 | return -1; |
1359 | 0 | } |
1360 | | |
1361 | 37.3k | if (!*name) { |
1362 | 0 | fr_strerror_printf("%s: Empty names are not permitted", __FUNCTION__); |
1363 | 0 | return -1; |
1364 | 0 | } |
1365 | | |
1366 | 37.3k | len = strlen(name); |
1367 | 37.3k | if (len >= FR_DICT_ENUM_MAX_NAME_LEN) { |
1368 | 0 | fr_strerror_printf("VALUE name is too long"); |
1369 | 0 | return -1; |
1370 | 0 | } |
1371 | | |
1372 | | /* |
1373 | | * If the parent isn't a key field, then we CANNOT add a child struct. |
1374 | | */ |
1375 | 37.3k | if (!fr_dict_attr_is_key_field(da) && child_struct) { |
1376 | 0 | fr_strerror_const("Child structures cannot be defined for VALUEs which are not for 'key' attributes"); |
1377 | 0 | return -1; |
1378 | 0 | } |
1379 | | |
1380 | 37.3k | if (fr_type_is_structural(da->type) || (da->type == FR_TYPE_STRING)) { |
1381 | 0 | fr_strerror_printf("Enumeration names cannot be added for data type '%s'", fr_type_to_str(da->type)); |
1382 | 0 | return -1; |
1383 | 0 | } |
1384 | | |
1385 | 37.3k | if (da->flags.is_alias) { |
1386 | 0 | fr_strerror_printf("Enumeration names cannot be added for ALIAS '%s'", da->name); |
1387 | 0 | return -1; |
1388 | 0 | } |
1389 | | |
1390 | 37.3k | ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_ENUMV); |
1391 | 37.3k | if (!ext) { |
1392 | 0 | fr_strerror_printf("VALUE cannot be defined for %s", da->name); |
1393 | 0 | return -1; |
1394 | 0 | } |
1395 | | |
1396 | | /* |
1397 | | * Initialise enumv hash tables |
1398 | | */ |
1399 | 37.3k | if (!ext->value_by_name || !ext->name_by_value) { |
1400 | 3.68k | ext->value_by_name = fr_hash_table_talloc_alloc(da, fr_dict_enum_value_t, dict_enum_name_hash, |
1401 | 3.68k | dict_enum_name_cmp, hash_pool_free); |
1402 | 3.68k | if (!ext->value_by_name) { |
1403 | 0 | fr_strerror_printf("Failed allocating \"value_by_name\" table"); |
1404 | 0 | return -1; |
1405 | 0 | } |
1406 | | |
1407 | 3.68k | ext->name_by_value = fr_hash_table_talloc_alloc(da, fr_dict_enum_value_t, dict_enum_value_hash, |
1408 | 3.68k | dict_enum_value_cmp, NULL); |
1409 | 3.68k | if (!ext->name_by_value) { |
1410 | 0 | fr_strerror_printf("Failed allocating \"name_by_value\" table"); |
1411 | 0 | return -1; |
1412 | 0 | } |
1413 | 3.68k | } |
1414 | | |
1415 | | /* |
1416 | | * Allocate a structure to map between |
1417 | | * the name and value. |
1418 | | */ |
1419 | 37.3k | enumv = talloc_zero_size(da, sizeof(fr_dict_enum_value_t) + sizeof(enumv->child_struct[0]) * fr_dict_attr_is_key_field(da)); |
1420 | 37.3k | if (!enumv) { |
1421 | 0 | oom: |
1422 | 0 | fr_strerror_printf("%s: Out of memory", __FUNCTION__); |
1423 | 0 | return -1; |
1424 | 0 | } |
1425 | 37.3k | talloc_set_type(enumv, fr_dict_enum_value_t); |
1426 | | |
1427 | 37.3k | enumv->name = talloc_typed_strdup(enumv, name); |
1428 | 37.3k | enumv->name_len = len; |
1429 | | |
1430 | 37.3k | if (child_struct) enumv->child_struct[0] = child_struct; |
1431 | 37.3k | enum_value = fr_value_box_alloc(enumv, da->type, NULL); |
1432 | 37.3k | if (!enum_value) goto oom; |
1433 | | |
1434 | 37.3k | if (da->type != value->type) { |
1435 | 12 | if (!coerce) { |
1436 | 0 | fr_strerror_printf("Type mismatch between attribute (%s) and enum (%s)", |
1437 | 0 | fr_type_to_str(da->type), |
1438 | 0 | fr_type_to_str(value->type)); |
1439 | 0 | return -1; |
1440 | 0 | } |
1441 | | |
1442 | 12 | if (fr_value_box_cast(enumv, enum_value, da->type, NULL, value) < 0) { |
1443 | 0 | fr_strerror_printf_push("Failed coercing enum type (%s) to attribute type (%s)", |
1444 | 0 | fr_type_to_str(value->type), |
1445 | 0 | fr_type_to_str(da->type)); |
1446 | |
|
1447 | 0 | return -1; |
1448 | 0 | } |
1449 | 37.3k | } else { |
1450 | 37.3k | if (fr_value_box_copy(enum_value, enum_value, value) < 0) { |
1451 | 0 | fr_strerror_printf_push("%s: Failed copying value into enum", __FUNCTION__); |
1452 | 0 | return -1; |
1453 | 0 | } |
1454 | 37.3k | } |
1455 | | |
1456 | 37.3k | enumv->value = enum_value; |
1457 | | |
1458 | | /* |
1459 | | * Add the value into the dictionary. |
1460 | | */ |
1461 | 37.3k | { |
1462 | 37.3k | fr_dict_attr_t *tmp; |
1463 | 37.3k | memcpy(&tmp, &enumv, sizeof(tmp)); |
1464 | | |
1465 | 37.3k | if (!fr_hash_table_insert(ext->value_by_name, tmp)) { |
1466 | 756 | fr_dict_enum_value_t *old; |
1467 | | |
1468 | | /* |
1469 | | * Suppress duplicates with the same |
1470 | | * name and value. There are lots in |
1471 | | * dictionary.ascend. |
1472 | | */ |
1473 | 756 | old = fr_dict_enum_by_name(da, name, -1); |
1474 | 756 | if (!fr_cond_assert(old)) return -1; |
1475 | | |
1476 | 756 | if (fr_value_box_cmp(old->value, enumv->value) == 0) { |
1477 | 756 | talloc_free(enumv); |
1478 | 756 | return 0; |
1479 | 756 | } |
1480 | | |
1481 | 0 | fr_strerror_printf("Duplicate VALUE name \"%s\" for Attribute '%s'. " |
1482 | 0 | "Old value was \"%pV\", new value was \"%pV\"", name, da->name, |
1483 | 0 | old->value, enumv->value); |
1484 | 0 | talloc_free(enumv); |
1485 | 0 | return -1; |
1486 | 756 | } |
1487 | | |
1488 | 36.5k | if (enumv->name_len > ext->max_name_len) ext->max_name_len = enumv->name_len; |
1489 | 36.5k | } |
1490 | | |
1491 | | /* |
1492 | | * There are multiple VALUE's, keyed by attribute, so we |
1493 | | * take care of that here. |
1494 | | */ |
1495 | 36.5k | if (takes_precedence) { |
1496 | 34.9k | if (fr_hash_table_replace(NULL, ext->name_by_value, enumv) < 0) { |
1497 | 0 | fr_strerror_printf("%s: Failed inserting value %s", __FUNCTION__, name); |
1498 | 0 | return -1; |
1499 | 0 | } |
1500 | 34.9k | } else { |
1501 | 1.60k | (void) fr_hash_table_insert(ext->name_by_value, enumv); |
1502 | 1.60k | } |
1503 | | |
1504 | | /* |
1505 | | * Mark the attribute up as having an enumv |
1506 | | */ |
1507 | 36.5k | UNCONST(fr_dict_attr_t *, da)->flags.has_value = 1; |
1508 | | |
1509 | 36.5k | return 0; |
1510 | 36.5k | } |
1511 | | |
1512 | | /** Add a value name |
1513 | | * |
1514 | | * Aliases are textual (string) names for a given value. |
1515 | | * |
1516 | | * Value names are not limited to integers, and may be added for any non-structural |
1517 | | * attribute type. |
1518 | | * |
1519 | | * @param[in] da to add enumeration value to. |
1520 | | * @param[in] name Name of value name. |
1521 | | * @param[in] value to associate with name. |
1522 | | * @param[in] coerce if the type of the value does not match the |
1523 | | * type of the da, attempt to cast it to match |
1524 | | * the type of the da. If this is false and there's |
1525 | | * a type mismatch, we fail. |
1526 | | * We also fail if the value cannot be coerced to |
1527 | | * the attribute type. |
1528 | | * @param[in] takes_precedence This name should take precedence over previous |
1529 | | * names for the same value, when resolving value |
1530 | | * to name. |
1531 | | * @return |
1532 | | * - 0 on success. |
1533 | | * - -1 on failure. |
1534 | | */ |
1535 | | int fr_dict_enum_add_name(fr_dict_attr_t *da, char const *name, |
1536 | | fr_value_box_t const *value, |
1537 | | bool coerce, bool takes_precedence) |
1538 | 36.1k | { |
1539 | 36.1k | return dict_attr_enum_add_name(da, name, value, coerce, takes_precedence, NULL); |
1540 | 36.1k | } |
1541 | | |
1542 | | /** Add an name to an integer attribute hashing the name for the integer value |
1543 | | * |
1544 | | * If the integer value conflicts with an existing name, it's incremented |
1545 | | * until we find a free value. |
1546 | | */ |
1547 | | int fr_dict_enum_add_name_next(fr_dict_attr_t *da, char const *name) |
1548 | 0 | { |
1549 | 0 | fr_value_box_t v = { |
1550 | 0 | .type = da->type |
1551 | 0 | }; |
1552 | 0 | fr_value_box_t s = { |
1553 | 0 | .type = da->type |
1554 | 0 | }; |
1555 | |
|
1556 | 0 | if (fr_dict_enum_by_name(da, name, -1)) return 0; |
1557 | | |
1558 | 0 | switch (da->type) { |
1559 | 0 | case FR_TYPE_INT8: |
1560 | 0 | v.vb_int8 = s.vb_int8 = fr_hash_string(name) & INT8_MAX; |
1561 | 0 | break; |
1562 | | |
1563 | 0 | case FR_TYPE_INT16: |
1564 | 0 | v.vb_int16 = s.vb_int16 = fr_hash_string(name) & INT16_MAX; |
1565 | 0 | break; |
1566 | | |
1567 | 0 | case FR_TYPE_INT32: |
1568 | 0 | v.vb_int32 = s.vb_int32 = fr_hash_string(name) & INT32_MAX; |
1569 | 0 | break; |
1570 | | |
1571 | 0 | case FR_TYPE_INT64: |
1572 | 0 | v.vb_int64 = s.vb_int64 = fr_hash_string(name) & INT64_MAX; |
1573 | 0 | break; |
1574 | | |
1575 | 0 | case FR_TYPE_UINT8: |
1576 | 0 | v.vb_uint8 = s.vb_uint8 = fr_hash_string(name) & UINT8_MAX; |
1577 | 0 | break; |
1578 | | |
1579 | 0 | case FR_TYPE_UINT16: |
1580 | 0 | v.vb_uint16 = s.vb_uint16 = fr_hash_string(name) & UINT16_MAX; |
1581 | 0 | break; |
1582 | | |
1583 | 0 | case FR_TYPE_UINT32: |
1584 | 0 | v.vb_uint32 = s.vb_uint32 = fr_hash_string(name) & UINT32_MAX; |
1585 | 0 | break; |
1586 | | |
1587 | 0 | case FR_TYPE_UINT64: |
1588 | 0 | v.vb_uint64 = s.vb_uint64 = fr_hash_string(name) & UINT64_MAX; |
1589 | 0 | break; |
1590 | | |
1591 | 0 | default: |
1592 | 0 | fr_strerror_printf("Attribute is wrong type for auto-numbering, expected numeric type, got %s", |
1593 | 0 | fr_type_to_str(da->type)); |
1594 | 0 | return -1; |
1595 | 0 | } |
1596 | | |
1597 | | /* |
1598 | | * If there's no existing value, add an enum |
1599 | | * with the hash value of the name. |
1600 | | * |
1601 | | * This helps with debugging as the values |
1602 | | * are consistent. |
1603 | | */ |
1604 | 0 | if (!fr_dict_enum_by_value(da, &v)) { |
1605 | 0 | add: |
1606 | 0 | return fr_dict_enum_add_name(da, name, &v, false, false); |
1607 | 0 | } |
1608 | | |
1609 | 0 | for (;;) { |
1610 | 0 | fr_value_box_increment(&v); |
1611 | |
|
1612 | 0 | if (fr_value_box_cmp_op(T_OP_CMP_EQ, &v, &s) == 0) { |
1613 | 0 | fr_strerror_const("No free integer values for enumeration"); |
1614 | 0 | return -1; |
1615 | 0 | } |
1616 | | |
1617 | 0 | if (!fr_dict_enum_by_value(da, &v)) goto add; |
1618 | 0 | } |
1619 | | /* NEVER REACHED */ |
1620 | 0 | } |
1621 | | |
1622 | | /** Find a common ancestor that two TLV type attributes share |
1623 | | * |
1624 | | * @param[in] a first TLV attribute. |
1625 | | * @param[in] b second TLV attribute. |
1626 | | * @param[in] is_ancestor Enforce a->b relationship (a is parent or ancestor of b). |
1627 | | * @return |
1628 | | * - Common ancestor if one exists. |
1629 | | * - NULL if no common ancestor exists. |
1630 | | */ |
1631 | | fr_dict_attr_t const *fr_dict_attr_common_parent(fr_dict_attr_t const *a, fr_dict_attr_t const *b, bool is_ancestor) |
1632 | 72 | { |
1633 | 72 | unsigned int i; |
1634 | 72 | fr_dict_attr_t const *p_a, *p_b; |
1635 | | |
1636 | 72 | if (!a || !b) return NULL; |
1637 | | |
1638 | 72 | if (is_ancestor && (b->depth <= a->depth)) return NULL; /* fast_path */ |
1639 | | |
1640 | | /* |
1641 | | * Find a common depth to work back from |
1642 | | */ |
1643 | 72 | if (a->depth > b->depth) { |
1644 | 0 | p_b = b; |
1645 | 0 | for (p_a = a, i = a->depth - b->depth; p_a && (i > 0); p_a = p_a->parent, i--); |
1646 | 0 | if (is_ancestor && (p_a != p_b)) return NULL; |
1647 | 72 | } else if (a->depth < b->depth) { |
1648 | 72 | p_a = a; |
1649 | 144 | for (p_b = b, i = b->depth - a->depth; p_b && (i > 0); p_b = p_b->parent, i--); |
1650 | 72 | if (is_ancestor && (p_a != p_b)) return NULL; |
1651 | 72 | } else { |
1652 | 0 | p_a = a; |
1653 | 0 | p_b = b; |
1654 | 0 | } |
1655 | | |
1656 | 72 | while (p_a && p_b) { |
1657 | 72 | if (p_a == p_b) return p_a; |
1658 | | |
1659 | 0 | p_a = p_a->parent; |
1660 | 0 | p_b = p_b->parent; |
1661 | 0 | } |
1662 | | |
1663 | 0 | return NULL; |
1664 | 72 | } |
1665 | | |
1666 | | /** Process a single OID component |
1667 | | * |
1668 | | * @param[out] out Value of component. |
1669 | | * @param[in] oid string to parse. |
1670 | | * @return |
1671 | | * - 0 on success. |
1672 | | * - -1 on format error. |
1673 | | */ |
1674 | | int fr_dict_oid_component_legacy(unsigned int *out, char const **oid) |
1675 | 3.35k | { |
1676 | 3.35k | char const *p = *oid; |
1677 | 3.35k | char *q; |
1678 | 3.35k | unsigned long num; |
1679 | | |
1680 | 3.35k | *out = 0; |
1681 | | |
1682 | 3.35k | num = strtoul(p, &q, 10); |
1683 | 3.35k | if ((p == q) || (num == ULONG_MAX)) { |
1684 | 0 | fr_strerror_printf("Invalid OID component \"%s\" (%lu)", p, num); |
1685 | 0 | return -1; |
1686 | 0 | } |
1687 | | |
1688 | 3.35k | switch (*q) { |
1689 | 2.32k | case '\0': |
1690 | 3.35k | case '.': |
1691 | 3.35k | *oid = q; |
1692 | 3.35k | *out = (unsigned int)num; |
1693 | | |
1694 | 3.35k | return 0; |
1695 | | |
1696 | 0 | default: |
1697 | 0 | fr_strerror_const("Unexpected text after OID component"); |
1698 | 0 | *out = 0; |
1699 | 0 | return -1; |
1700 | 3.35k | } |
1701 | 3.35k | } |
1702 | | |
1703 | | /** Get the leaf attribute of an OID string |
1704 | | * |
1705 | | * @note On error, vendor will be set (if present), parent will be the |
1706 | | * maximum depth we managed to resolve to, and attr will be the child |
1707 | | * we failed to resolve. |
1708 | | * |
1709 | | * @param[in] dict of protocol context we're operating in. |
1710 | | * If NULL the internal dictionary will be used. |
1711 | | * @param[out] attr Number we parsed. |
1712 | | * @param[in,out] parent attribute (or root of dictionary). |
1713 | | * Will be updated to the parent directly beneath the leaf. |
1714 | | * @param[in] oid string to parse. |
1715 | | * @return |
1716 | | * - > 0 on success (number of bytes parsed). |
1717 | | * - <= 0 on parse error (negative offset of parse error). |
1718 | | */ |
1719 | | ssize_t fr_dict_attr_by_oid_legacy(fr_dict_t const *dict, fr_dict_attr_t const **parent, unsigned int *attr, char const *oid) |
1720 | 3.35k | { |
1721 | 3.35k | char const *p = oid; |
1722 | 3.35k | unsigned int num = 0; |
1723 | 3.35k | ssize_t slen; |
1724 | | |
1725 | 3.35k | if (!*parent) return -1; |
1726 | | |
1727 | | /* |
1728 | | * It's a partial OID. Grab it, and skip to the next bit. |
1729 | | */ |
1730 | 3.35k | if (p[0] == '.') { |
1731 | 1.99k | p++; |
1732 | 1.99k | } |
1733 | | |
1734 | 3.35k | *attr = 0; |
1735 | | |
1736 | 3.35k | if (fr_dict_oid_component_legacy(&num, &p) < 0) return oid - p; |
1737 | | |
1738 | | /* |
1739 | | * Record progress even if we error out. |
1740 | | * |
1741 | | * Don't change this, you will break things. |
1742 | | */ |
1743 | 3.35k | *attr = num; |
1744 | | |
1745 | 3.35k | switch ((*parent)->type) { |
1746 | 3.35k | case FR_TYPE_STRUCTURAL: |
1747 | 3.35k | break; |
1748 | | |
1749 | 0 | default: |
1750 | 0 | if (dict_attr_can_have_children(*parent)) break; |
1751 | 0 | fr_strerror_printf("Attribute %s (%i) is not a TLV, so cannot contain a child attribute. " |
1752 | 0 | "Error at sub OID \"%s\"", (*parent)->name, (*parent)->attr, oid); |
1753 | 0 | return 0; /* We parsed nothing */ |
1754 | 3.35k | } |
1755 | | |
1756 | | /* |
1757 | | * If it's not a vendor type, it must be between 0..8*type_size |
1758 | | * |
1759 | | * @fixme: find the TLV parent, and check it's size |
1760 | | */ |
1761 | 3.35k | if (((*parent)->type != FR_TYPE_VENDOR) && ((*parent)->type != FR_TYPE_VSA) && !(*parent)->flags.is_root && |
1762 | 3.35k | (num > UINT8_MAX)) { |
1763 | 0 | fr_strerror_const("TLV attributes must be between 0..255 inclusive"); |
1764 | 0 | return 0; |
1765 | 0 | } |
1766 | | |
1767 | 3.35k | switch (p[0]) { |
1768 | | /* |
1769 | | * We've not hit the leaf yet, so the attribute must be |
1770 | | * defined already. |
1771 | | */ |
1772 | 1.02k | case '.': |
1773 | 1.02k | { |
1774 | 1.02k | fr_dict_attr_t const *child; |
1775 | 1.02k | p++; |
1776 | | |
1777 | 1.02k | child = dict_attr_child_by_num(*parent, num); |
1778 | 1.02k | if (!child) { |
1779 | 0 | fr_strerror_printf("Unknown attribute '%i' in OID string \"%s\" for parent %s", |
1780 | 0 | num, oid, (*parent)->name); |
1781 | 0 | return 0; /* We parsed nothing */ |
1782 | 0 | } |
1783 | | |
1784 | | /* |
1785 | | * Record progress even if we error out. |
1786 | | * |
1787 | | * Don't change this, you will break things. |
1788 | | */ |
1789 | 1.02k | *parent = child; |
1790 | | |
1791 | 1.02k | slen = fr_dict_attr_by_oid_legacy(dict, parent, attr, p); |
1792 | 1.02k | if (slen <= 0) return slen - (p - oid); |
1793 | 1.02k | return slen + (p - oid); |
1794 | 1.02k | } |
1795 | | |
1796 | | /* |
1797 | | * Hit the leaf, this is the attribute we need to define. |
1798 | | */ |
1799 | 2.32k | case '\0': |
1800 | 2.32k | *attr = num; |
1801 | 2.32k | return p - oid; |
1802 | | |
1803 | 0 | default: |
1804 | 0 | fr_strerror_printf("Malformed OID string, got trailing garbage '%s'", p); |
1805 | 0 | return oid - p; |
1806 | 3.35k | } |
1807 | 3.35k | } |
1808 | | |
1809 | | /** Parse an OID component, resolving it to a defined attribute |
1810 | | * |
1811 | | * @note Will leave the sbuff pointing at the component the error occurred at |
1812 | | * so that the caller can attempt to process the component in another way. |
1813 | | * |
1814 | | * @param[out] err The parsing error that occurred. |
1815 | | * @param[out] out The deepest attribute we resolved. |
1816 | | * @param[in] parent Where to resolve relative attributes from. |
1817 | | * @param[in] in string to parse. |
1818 | | * @param[in] tt Terminal strings. |
1819 | | * @return |
1820 | | * - >0 the number of bytes consumed. |
1821 | | * - <0 Parse error occurred here. |
1822 | | */ |
1823 | | fr_slen_t fr_dict_oid_component(fr_dict_attr_err_t *err, |
1824 | | fr_dict_attr_t const **out, fr_dict_attr_t const *parent, |
1825 | | fr_sbuff_t *in, fr_sbuff_term_t const *tt) |
1826 | 202k | { |
1827 | 202k | fr_sbuff_t our_in = FR_SBUFF(in); |
1828 | 202k | uint32_t num = 0; |
1829 | 202k | fr_sbuff_parse_error_t sberr; |
1830 | 202k | fr_dict_attr_t const *child; |
1831 | | |
1832 | 202k | if (err) *err = FR_DICT_ATTR_OK; |
1833 | | |
1834 | 202k | *out = NULL; |
1835 | | |
1836 | 202k | switch (parent->type) { |
1837 | 202k | case FR_TYPE_STRUCTURAL: |
1838 | 202k | break; |
1839 | | |
1840 | 0 | default: |
1841 | 0 | if (dict_attr_can_have_children(parent)) break; |
1842 | 0 | fr_strerror_printf("Attribute '%s' is type %s and cannot contain child attributes. " |
1843 | 0 | "Error at OID \"%.*s\"", |
1844 | 0 | parent->name, |
1845 | 0 | fr_type_to_str(parent->type), |
1846 | 0 | (int)fr_sbuff_remaining(&our_in), |
1847 | 0 | fr_sbuff_current(&our_in)); |
1848 | 0 | if (err) *err =FR_DICT_ATTR_NO_CHILDREN; |
1849 | 0 | FR_SBUFF_ERROR_RETURN(&our_in); |
1850 | 202k | } |
1851 | | |
1852 | 202k | fr_sbuff_out(&sberr, &num, &our_in); |
1853 | 202k | switch (sberr) { |
1854 | | /* |
1855 | | * Lookup by number |
1856 | | */ |
1857 | 12 | case FR_SBUFF_PARSE_OK: |
1858 | 12 | if (!fr_sbuff_is_char(&our_in, '.') && !fr_sbuff_is_terminal(&our_in, tt)) { |
1859 | 12 | fr_sbuff_set_to_start(&our_in); |
1860 | 12 | goto oid_str; |
1861 | 12 | } |
1862 | | |
1863 | 0 | child = dict_attr_child_by_num(parent, num); |
1864 | 0 | if (!child) { |
1865 | 0 | fr_strerror_printf("Failed resolving child %u in context %s", |
1866 | 0 | num, parent->name); |
1867 | 0 | if (err) *err = FR_DICT_ATTR_NOTFOUND; |
1868 | 0 | FR_SBUFF_ERROR_RETURN(&our_in); |
1869 | 0 | } |
1870 | 0 | break; |
1871 | | |
1872 | | /* |
1873 | | * Lookup by name |
1874 | | */ |
1875 | 202k | case FR_SBUFF_PARSE_ERROR_NOT_FOUND: |
1876 | 202k | case FR_SBUFF_PARSE_ERROR_TRAILING: |
1877 | 202k | { |
1878 | 202k | fr_dict_attr_err_t our_err; |
1879 | 202k | oid_str: |
1880 | 202k | if (fr_dict_attr_by_name_substr(&our_err, &child, parent, &our_in, tt) < 0) { |
1881 | 18 | fr_strerror_printf("Failed resolving \"%.*s\" in context %s", |
1882 | 18 | (int)fr_sbuff_remaining(&our_in), |
1883 | 18 | fr_sbuff_current(&our_in), |
1884 | 18 | parent->name); |
1885 | 18 | if (err) *err = our_err; |
1886 | 18 | FR_SBUFF_ERROR_RETURN(&our_in); |
1887 | 18 | } |
1888 | 202k | } |
1889 | 202k | break; |
1890 | | |
1891 | 202k | default: |
1892 | 0 | fr_strerror_printf("Invalid OID component (%s) \"%.*s\"", |
1893 | 0 | fr_table_str_by_value(sbuff_parse_error_table, sberr, "<INVALID>"), |
1894 | 0 | (int)fr_sbuff_remaining(&our_in), fr_sbuff_current(&our_in)); |
1895 | 0 | if (err) *err = FR_DICT_ATTR_PARSE_ERROR; |
1896 | 0 | FR_SBUFF_ERROR_RETURN(&our_in); |
1897 | 202k | } |
1898 | | |
1899 | 202k | child = dict_attr_alias(err, child); |
1900 | 202k | if (unlikely(!child)) FR_SBUFF_ERROR_RETURN(&our_in); |
1901 | | |
1902 | 202k | *out = child; |
1903 | | |
1904 | 202k | FR_SBUFF_SET_RETURN(in, &our_in); |
1905 | 202k | } |
1906 | | |
1907 | | /** Resolve an attribute using an OID string |
1908 | | * |
1909 | | * @note Will leave the sbuff pointing at the component the error occurred at |
1910 | | * so that the caller can attempt to process the component in another way. |
1911 | | * An err pointer should be provided in order to determine if an error |
1912 | | * occurred. |
1913 | | * |
1914 | | * @param[out] err The parsing error that occurred. |
1915 | | * @param[out] out The deepest attribute we resolved. |
1916 | | * @param[in] parent Where to resolve relative attributes from. |
1917 | | * @param[in] in string to parse. |
1918 | | * @param[in] tt Terminal strings. |
1919 | | * @return The number of bytes of name consumed. |
1920 | | */ |
1921 | | fr_slen_t fr_dict_attr_by_oid_substr(fr_dict_attr_err_t *err, |
1922 | | fr_dict_attr_t const **out, fr_dict_attr_t const *parent, |
1923 | | fr_sbuff_t *in, fr_sbuff_term_t const *tt) |
1924 | 172k | { |
1925 | 172k | fr_sbuff_t our_in = FR_SBUFF(in); |
1926 | 172k | fr_sbuff_marker_t m_c; |
1927 | 172k | fr_dict_attr_t const *our_parent = parent; |
1928 | | |
1929 | 172k | fr_sbuff_marker(&m_c, &our_in); |
1930 | | |
1931 | | /* |
1932 | | * If the OID doesn't begin with '.' we |
1933 | | * resolve it from the root. |
1934 | | */ |
1935 | | #if 0 |
1936 | | if (!fr_sbuff_next_if_char(&our_in, '.')) our_parent = fr_dict_root(fr_dict_by_da(parent)); |
1937 | | #else |
1938 | 172k | (void) fr_sbuff_next_if_char(&our_in, '.'); |
1939 | 172k | #endif |
1940 | 172k | *out = NULL; |
1941 | | |
1942 | 202k | for (;;) { |
1943 | 202k | fr_dict_attr_t const *child; |
1944 | | |
1945 | 202k | if ((fr_dict_oid_component(err, &child, our_parent, &our_in, tt) < 0) || !child) { |
1946 | 18 | *out = our_parent; |
1947 | 18 | fr_sbuff_set(&our_in, &m_c); /* Reset to the start of the last component */ |
1948 | 18 | break; /* Resolved as much as we can */ |
1949 | 18 | } |
1950 | | |
1951 | 202k | our_parent = child; |
1952 | 202k | *out = child; |
1953 | | |
1954 | 202k | fr_sbuff_set(&m_c, &our_in); |
1955 | 202k | if (!fr_sbuff_next_if_char(&our_in, '.')) break; |
1956 | 202k | } |
1957 | | |
1958 | 172k | FR_SBUFF_SET_RETURN(in, &our_in); |
1959 | 172k | } |
1960 | | |
1961 | | /** Resolve an attribute using an OID string |
1962 | | * |
1963 | | * @param[out] err The parsing error that occurred. |
1964 | | * @param[in] parent Where to resolve relative attributes from. |
1965 | | * @param[in] oid string to parse. |
1966 | | * @return |
1967 | | * - NULL if we couldn't resolve the attribute. |
1968 | | * - The resolved attribute. |
1969 | | */ |
1970 | | fr_dict_attr_t const *fr_dict_attr_by_oid(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *oid) |
1971 | 172k | { |
1972 | 172k | fr_sbuff_t sbuff = FR_SBUFF_IN(oid, strlen(oid)); |
1973 | 172k | fr_dict_attr_t const *da; |
1974 | | |
1975 | 172k | if (fr_dict_attr_by_oid_substr(err, &da, parent, &sbuff, NULL) <= 0) return NULL; |
1976 | 172k | if (err && *err != FR_DICT_ATTR_OK) return NULL; |
1977 | | |
1978 | | /* |
1979 | | * If we didn't parse the entire string, then the parsing stopped at an unknown child. |
1980 | | * e.g. Vendor-Specific.Cisco.Foo. In that case, the full attribute wasn't found. |
1981 | | */ |
1982 | 172k | if (fr_sbuff_remaining(&sbuff) > 0) { |
1983 | 0 | if (err) *err = FR_DICT_ATTR_NOTFOUND; |
1984 | 0 | return NULL; |
1985 | 0 | } |
1986 | | |
1987 | 172k | return da; |
1988 | 172k | } |
1989 | | |
1990 | | /** Return the root attribute of a dictionary |
1991 | | * |
1992 | | * @param dict to return root for. |
1993 | | * @return the root attribute of the dictionary. |
1994 | | * |
1995 | | * @hidecallergraph |
1996 | | */ |
1997 | | fr_dict_attr_t const *fr_dict_root(fr_dict_t const *dict) |
1998 | 1.39M | { |
1999 | 1.39M | return dict->root; |
2000 | 1.39M | } |
2001 | | |
2002 | | bool fr_dict_is_read_only(fr_dict_t const *dict) |
2003 | 2.19M | { |
2004 | 2.19M | return dict->read_only; |
2005 | 2.19M | } |
2006 | | |
2007 | | dl_t *fr_dict_dl(fr_dict_t const *dict) |
2008 | 0 | { |
2009 | 0 | return dict->dl; |
2010 | 0 | } |
2011 | | |
2012 | | fr_slen_t dict_by_protocol_substr(fr_dict_attr_err_t *err, |
2013 | | fr_dict_t **out, fr_sbuff_t *name, fr_dict_t const *dict_def) |
2014 | 44 | { |
2015 | 44 | fr_dict_attr_t root; |
2016 | | |
2017 | 44 | fr_sbuff_t our_name; |
2018 | 44 | fr_dict_t *dict; |
2019 | 44 | fr_slen_t slen; |
2020 | 44 | char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1 + 1]; /* +1 \0 +1 for "too long" */ |
2021 | | |
2022 | 44 | if (!dict_gctx || !name || !out) { |
2023 | 0 | if (err) *err = FR_DICT_ATTR_EINVAL; |
2024 | 0 | if (name) FR_SBUFF_ERROR_RETURN(name); |
2025 | 0 | return 0; |
2026 | 0 | } |
2027 | | |
2028 | 44 | our_name = FR_SBUFF(name); |
2029 | 44 | memset(&root, 0, sizeof(root)); |
2030 | | |
2031 | | /* |
2032 | | * Advance p until we get something that's not part of |
2033 | | * the dictionary attribute name. |
2034 | | */ |
2035 | 44 | slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(buffer, sizeof(buffer)), |
2036 | 44 | &our_name, SIZE_MAX, |
2037 | 44 | fr_dict_attr_allowed_chars); |
2038 | 44 | if (slen == 0) { |
2039 | 0 | fr_strerror_const("Zero length attribute name"); |
2040 | 0 | if (err) *err = FR_DICT_ATTR_PARSE_ERROR; |
2041 | 0 | FR_SBUFF_ERROR_RETURN(&our_name); |
2042 | 0 | } |
2043 | 44 | if (slen > FR_DICT_ATTR_MAX_NAME_LEN) { |
2044 | 0 | fr_strerror_const("Attribute name too long"); |
2045 | 0 | if (err) *err = FR_DICT_ATTR_PARSE_ERROR; |
2046 | 0 | FR_SBUFF_ERROR_RETURN(&our_name); |
2047 | 0 | } |
2048 | | |
2049 | | /* |
2050 | | * The remaining operations don't generate errors |
2051 | | */ |
2052 | 44 | if (err) *err = FR_DICT_ATTR_OK; |
2053 | | |
2054 | | /* |
2055 | | * If what we stopped at wasn't a '.', then there |
2056 | | * can't be a protocol name in this string. |
2057 | | */ |
2058 | 44 | if (*(our_name.p) && (*(our_name.p) != '.')) { |
2059 | 0 | memcpy(out, &dict_def, sizeof(*out)); |
2060 | 0 | return 0; |
2061 | 0 | } |
2062 | | |
2063 | 44 | root.name = buffer; |
2064 | 44 | dict = fr_hash_table_find(dict_gctx->protocol_by_name, &(fr_dict_t){ .root = &root }); |
2065 | | |
2066 | 44 | if (!dict) { |
2067 | 8 | if (strcasecmp(root.name, "internal") != 0) { |
2068 | 8 | fr_strerror_printf("Unknown protocol '%s'", root.name); |
2069 | 8 | memcpy(out, &dict_def, sizeof(*out)); |
2070 | 8 | fr_sbuff_set_to_start(&our_name); |
2071 | 8 | FR_SBUFF_ERROR_RETURN(&our_name); |
2072 | 8 | } |
2073 | | |
2074 | 0 | dict = dict_gctx->internal; |
2075 | 0 | } |
2076 | | |
2077 | 36 | *out = dict; |
2078 | | |
2079 | 36 | FR_SBUFF_SET_RETURN(name, &our_name); |
2080 | 44 | } |
2081 | | |
2082 | | /** Look up a protocol name embedded in another string |
2083 | | * |
2084 | | * @param[out] err Parsing error. |
2085 | | * @param[out] out the resolve dictionary or NULL if the dictionary |
2086 | | * couldn't be resolved. |
2087 | | * @param[in] name string start. |
2088 | | * @param[in] dict_def The dictionary to return if no dictionary qualifier was found. |
2089 | | * @return |
2090 | | * - 0 and *out != NULL. Couldn't find a dictionary qualifier, so returned dict_def. |
2091 | | * - < 0 on error and (*out == NULL) (offset as negative integer) |
2092 | | * - > 0 on success (number of bytes parsed). |
2093 | | */ |
2094 | | fr_slen_t fr_dict_by_protocol_substr(fr_dict_attr_err_t *err, fr_dict_t const **out, fr_sbuff_t *name, fr_dict_t const *dict_def) |
2095 | 0 | { |
2096 | 0 | return dict_by_protocol_substr(err, UNCONST(fr_dict_t **, out), name, dict_def); |
2097 | 0 | } |
2098 | | |
2099 | | /** Internal version of #fr_dict_by_protocol_name |
2100 | | * |
2101 | | * @note For internal use by the dictionary API only. |
2102 | | * |
2103 | | * @copybrief fr_dict_by_protocol_name |
2104 | | */ |
2105 | | fr_dict_t *dict_by_protocol_name(char const *name) |
2106 | 5.58k | { |
2107 | 5.58k | if (!dict_gctx || !name) return NULL; |
2108 | | |
2109 | 5.58k | return fr_hash_table_find(dict_gctx->protocol_by_name, |
2110 | 5.58k | &(fr_dict_t){ .root = &(fr_dict_attr_t){ .name = name } }); |
2111 | 5.58k | } |
2112 | | |
2113 | | /** Internal version of #fr_dict_by_protocol_num |
2114 | | * |
2115 | | * @note For internal use by the dictionary API only. |
2116 | | * |
2117 | | * @copybrief fr_dict_by_protocol_num |
2118 | | */ |
2119 | | fr_dict_t *dict_by_protocol_num(unsigned int num) |
2120 | 21 | { |
2121 | 21 | if (!dict_gctx) return NULL; |
2122 | | |
2123 | 21 | return fr_hash_table_find(dict_gctx->protocol_by_num, |
2124 | 21 | &(fr_dict_t) { .root = &(fr_dict_attr_t){ .attr = num } }); |
2125 | 21 | } |
2126 | | |
2127 | | /** Internal version of #fr_dict_by_da |
2128 | | * |
2129 | | * @note For internal use by the dictionary API only. |
2130 | | * |
2131 | | * @copybrief fr_dict_by_da |
2132 | | */ |
2133 | | fr_dict_t *dict_by_da(fr_dict_attr_t const *da) |
2134 | 47.0k | { |
2135 | 47.0k | #ifndef NDEBUG |
2136 | 47.0k | { |
2137 | 47.0k | fr_dict_attr_t const *da_p = da; |
2138 | 47.0k | fr_dict_t const *dict; |
2139 | | |
2140 | 47.0k | dict = da->dict; |
2141 | 57.7k | while (da_p->parent) { |
2142 | 10.7k | da_p = da_p->parent; |
2143 | 10.7k | fr_cond_assert_msg(da_p->dict == dict, "Inconsistent dict membership. " |
2144 | 10.7k | "Expected %s, got %s", |
2145 | 10.7k | !da_p->dict ? "(null)" : fr_dict_root(da_p->dict)->name, |
2146 | 10.7k | !dict ? "(null)" : fr_dict_root(dict)->name); |
2147 | 10.7k | DA_VERIFY(da_p); |
2148 | 10.7k | } |
2149 | | |
2150 | 47.0k | if (!da_p->flags.is_root) { |
2151 | 0 | fr_strerror_printf("%s: Attribute %s has not been inserted into a dictionary", |
2152 | 0 | __FUNCTION__, da->name); |
2153 | 0 | return NULL; |
2154 | 0 | } |
2155 | 47.0k | } |
2156 | 47.0k | #endif |
2157 | | |
2158 | | /* |
2159 | | * Parent of the root attribute must |
2160 | | * be the dictionary. |
2161 | | */ |
2162 | 47.0k | return talloc_get_type_abort(da->dict, fr_dict_t); |
2163 | 47.0k | } |
2164 | | |
2165 | | /** Lookup a protocol by its name |
2166 | | * |
2167 | | * @note For internal use by the dictionary API only. |
2168 | | * |
2169 | | * @param[in] name of the protocol to locate. |
2170 | | * @return |
2171 | | * - Attribute matching name. |
2172 | | * - NULL if no matching protocol could be found. |
2173 | | */ |
2174 | | fr_dict_t const *fr_dict_by_protocol_name(char const *name) |
2175 | 0 | { |
2176 | 0 | return dict_by_protocol_name(name); |
2177 | 0 | } |
2178 | | |
2179 | | /** Lookup a protocol by its number |
2180 | | * |
2181 | | * Returns the #fr_dict_t belonging to the protocol with the specified number |
2182 | | * if any have been registered. |
2183 | | * |
2184 | | * @param[in] num to search for. |
2185 | | * @return dictionary representing the protocol (if it exists). |
2186 | | */ |
2187 | | fr_dict_t const *fr_dict_by_protocol_num(unsigned int num) |
2188 | 0 | { |
2189 | 0 | return dict_by_protocol_num(num); |
2190 | 0 | } |
2191 | | |
2192 | | /** Attempt to locate the protocol dictionary containing an attribute |
2193 | | * |
2194 | | * @note Unlike fr_dict_by_attr_name, doesn't search through all the dictionaries, |
2195 | | * just uses the fr_dict_attr_t hierarchy and the talloc hierarchy to locate |
2196 | | * the dictionary (much much faster and more scalable). |
2197 | | * |
2198 | | * @param[in] da To get the containing dictionary for. |
2199 | | * @return |
2200 | | * - The dictionary containing da. |
2201 | | * - NULL. |
2202 | | */ |
2203 | | fr_dict_t const *fr_dict_by_da(fr_dict_attr_t const *da) |
2204 | 42.2k | { |
2205 | 42.2k | return dict_by_da(da); |
2206 | 42.2k | } |
2207 | | |
2208 | | /** See if two dictionaries have the same end parent |
2209 | | * |
2210 | | * @param[in] dict1 one dictionary |
2211 | | * @param[in] dict2 two dictionary |
2212 | | * @return |
2213 | | * - true the dictionaries have the same end parent |
2214 | | * - false the dictionaries do not have the same end parent. |
2215 | | */ |
2216 | | bool fr_dict_compatible(fr_dict_t const *dict1, fr_dict_t const *dict2) |
2217 | 0 | { |
2218 | 0 | while (dict1->next) dict1 = dict1->next; |
2219 | |
|
2220 | 0 | while (dict2->next) dict2 = dict2->next; |
2221 | |
|
2222 | 0 | return (dict1 == dict2); |
2223 | 0 | } |
2224 | | |
2225 | | /** Look up a vendor by one of its child attributes |
2226 | | * |
2227 | | * @param[in] da The vendor attribute. |
2228 | | * @return |
2229 | | * - The vendor. |
2230 | | * - NULL if no vendor with that number was registered for this protocol. |
2231 | | */ |
2232 | | fr_dict_vendor_t const *fr_dict_vendor_by_da(fr_dict_attr_t const *da) |
2233 | 0 | { |
2234 | 0 | fr_dict_t *dict; |
2235 | 0 | fr_dict_vendor_t dv; |
2236 | |
|
2237 | 0 | dv.pen = fr_dict_vendor_num_by_da(da); |
2238 | 0 | if (!dv.pen) return NULL; |
2239 | | |
2240 | 0 | dict = dict_by_da(da); |
2241 | |
|
2242 | 0 | return fr_hash_table_find(dict->vendors_by_num, &dv); |
2243 | 0 | } |
2244 | | |
2245 | | /** Look up a vendor by its name |
2246 | | * |
2247 | | * @param[in] dict of protocol context we're operating in. |
2248 | | * If NULL the internal dictionary will be used. |
2249 | | * @param[in] name to search for. |
2250 | | * @return |
2251 | | * - The vendor. |
2252 | | * - NULL if no vendor with that name was registered for this protocol. |
2253 | | */ |
2254 | | fr_dict_vendor_t const *fr_dict_vendor_by_name(fr_dict_t const *dict, char const *name) |
2255 | 1.51k | { |
2256 | 1.51k | fr_dict_vendor_t *found; |
2257 | | |
2258 | 1.51k | INTERNAL_IF_NULL(dict, NULL); |
2259 | | |
2260 | 1.51k | if (!name) return 0; |
2261 | | |
2262 | 1.51k | found = fr_hash_table_find(dict->vendors_by_name, &(fr_dict_vendor_t) { .name = name }); |
2263 | 1.51k | if (!found) return 0; |
2264 | | |
2265 | 1.51k | return found; |
2266 | 1.51k | } |
2267 | | |
2268 | | /** Look up a vendor by its PEN |
2269 | | * |
2270 | | * @param[in] dict of protocol context we're operating in. |
2271 | | * If NULL the internal dictionary will be used. |
2272 | | * @param[in] vendor_pen to search for. |
2273 | | * @return |
2274 | | * - The vendor. |
2275 | | * - NULL if no vendor with that number was registered for this protocol. |
2276 | | */ |
2277 | | fr_dict_vendor_t const *fr_dict_vendor_by_num(fr_dict_t const *dict, uint32_t vendor_pen) |
2278 | 14.9k | { |
2279 | 14.9k | INTERNAL_IF_NULL(dict, NULL); |
2280 | | |
2281 | 14.9k | return fr_hash_table_find(dict->vendors_by_num, &(fr_dict_vendor_t) { .pen = vendor_pen }); |
2282 | 14.9k | } |
2283 | | |
2284 | | /** Return vendor attribute for the specified dictionary and pen |
2285 | | * |
2286 | | * @param[in] vendor_root of the vendor root attribute. Could be 26 (for example) in RADIUS. |
2287 | | * @param[in] vendor_pen to find. |
2288 | | * @return |
2289 | | * - NULL if vendor does not exist. |
2290 | | * - A fr_dict_attr_t representing the vendor in the dictionary hierarchy. |
2291 | | */ |
2292 | | fr_dict_attr_t const *fr_dict_vendor_da_by_num(fr_dict_attr_t const *vendor_root, uint32_t vendor_pen) |
2293 | 0 | { |
2294 | 0 | fr_dict_attr_t const *vendor; |
2295 | |
|
2296 | 0 | switch (vendor_root->type) { |
2297 | 0 | case FR_TYPE_VSA: /* Vendor specific attribute */ |
2298 | 0 | break; |
2299 | | |
2300 | 0 | default: |
2301 | 0 | fr_strerror_printf("Wrong type for vendor root, expected '%s', got '%s'", |
2302 | 0 | fr_type_to_str(FR_TYPE_VSA), |
2303 | 0 | fr_type_to_str(vendor_root->type)); |
2304 | 0 | return NULL; |
2305 | 0 | } |
2306 | | |
2307 | 0 | vendor = dict_attr_child_by_num(vendor_root, vendor_pen); |
2308 | 0 | if (!vendor) { |
2309 | 0 | fr_strerror_printf("Vendor %i not defined", vendor_pen); |
2310 | 0 | return NULL; |
2311 | 0 | } |
2312 | | |
2313 | 0 | if (vendor->type != FR_TYPE_VENDOR) { |
2314 | 0 | fr_strerror_printf("Wrong type for vendor, expected '%s' got '%s'", |
2315 | 0 | fr_type_to_str(vendor->type), |
2316 | 0 | fr_type_to_str(FR_TYPE_VENDOR)); |
2317 | 0 | return NULL; |
2318 | 0 | } |
2319 | | |
2320 | 0 | return vendor; |
2321 | 0 | } |
2322 | | |
2323 | | /** Callback function for resolving dictionary attributes |
2324 | | * |
2325 | | * @param[out] err Where to write error codes. Any error |
2326 | | * other than FR_DICT_ATTR_NOTFOUND will |
2327 | | * prevent resolution from continuing. |
2328 | | * @param[out] out Where to write resolved DA. |
2329 | | * @param[in] parent The dictionary root or other attribute to search from. |
2330 | | * @param[in] in Contains the string to resolve. |
2331 | | * @param[in] tt Terminal sequences to use to determine the portion |
2332 | | * of in to search. |
2333 | | * @return |
2334 | | * - < 0 on failure. |
2335 | | * - The number of bytes of name consumed on success. |
2336 | | */ |
2337 | | typedef fr_slen_t (*dict_attr_resolve_func_t)(fr_dict_attr_err_t *err, |
2338 | | fr_dict_attr_t const **out, fr_dict_attr_t const *parent, |
2339 | | fr_sbuff_t *in, fr_sbuff_term_t const *tt); |
2340 | | |
2341 | | /** Internal function for searching for attributes in multiple dictionaries |
2342 | | * |
2343 | | * @param[out] err Any errors that occurred searching. |
2344 | | * @param[out] out The attribute we found. |
2345 | | * @param[in] dict_def The default dictionary to search in. |
2346 | | * @param[in] in string to resolve to an attribute. |
2347 | | * @param[in] tt terminals that indicate the end of the string. |
2348 | | * @param[in] internal Resolve the attribute in the internal dictionary. |
2349 | | * @param[in] foreign Resolve attribute in a foreign dictionary, |
2350 | | * i.e. one other than dict_def. |
2351 | | * @param[in] func to use for resolution. |
2352 | | * @return |
2353 | | * - <=0 on error (the offset of the error). |
2354 | | * - >0 on success. |
2355 | | */ |
2356 | | static inline CC_HINT(always_inline) |
2357 | | fr_slen_t dict_attr_search(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, |
2358 | | fr_dict_t const *dict_def, |
2359 | | fr_sbuff_t *in, fr_sbuff_term_t const *tt, |
2360 | | bool internal, bool foreign, |
2361 | | dict_attr_resolve_func_t func) |
2362 | 0 | { |
2363 | 0 | fr_dict_attr_err_t our_err = FR_DICT_ATTR_OK; |
2364 | 0 | fr_hash_iter_t iter; |
2365 | 0 | fr_dict_t *dict = NULL; |
2366 | 0 | fr_sbuff_t our_in = FR_SBUFF(in); |
2367 | |
|
2368 | 0 | if (internal && !dict_gctx->internal) internal = false; |
2369 | | |
2370 | | /* |
2371 | | * Always going to fail... |
2372 | | */ |
2373 | 0 | if (unlikely(!internal && !foreign && !dict_def)) { |
2374 | 0 | if (err) *err = FR_DICT_ATTR_EINVAL; |
2375 | 0 | *out = NULL; |
2376 | 0 | return 0; |
2377 | 0 | } |
2378 | | |
2379 | | /* |
2380 | | * dict_def search in the specified dictionary |
2381 | | */ |
2382 | 0 | if (dict_def) { |
2383 | 0 | (void)func(&our_err, out, fr_dict_root(dict_def), &our_in, tt); |
2384 | 0 | switch (our_err) { |
2385 | 0 | case FR_DICT_ATTR_OK: |
2386 | 0 | FR_SBUFF_SET_RETURN(in, &our_in); |
2387 | | |
2388 | 0 | case FR_DICT_ATTR_NOTFOUND: |
2389 | 0 | if (!internal && !foreign) goto error; |
2390 | 0 | break; |
2391 | | |
2392 | 0 | default: |
2393 | 0 | goto error; |
2394 | 0 | } |
2395 | 0 | } |
2396 | | |
2397 | | /* |
2398 | | * Next in the internal dictionary |
2399 | | */ |
2400 | 0 | if (internal) { |
2401 | 0 | (void)func(&our_err, out, fr_dict_root(dict_gctx->internal), &our_in, tt); |
2402 | 0 | switch (our_err) { |
2403 | 0 | case FR_DICT_ATTR_OK: |
2404 | 0 | FR_SBUFF_SET_RETURN(in, &our_in); |
2405 | | |
2406 | 0 | case FR_DICT_ATTR_NOTFOUND: |
2407 | 0 | if (!foreign) goto error; |
2408 | 0 | break; |
2409 | | |
2410 | 0 | default: |
2411 | 0 | goto error; |
2412 | 0 | } |
2413 | 0 | } |
2414 | | |
2415 | | /* |
2416 | | * Now loop over the protocol dictionaries |
2417 | | */ |
2418 | 0 | for (dict = fr_hash_table_iter_init(dict_gctx->protocol_by_num, &iter); |
2419 | 0 | dict; |
2420 | 0 | dict = fr_hash_table_iter_next(dict_gctx->protocol_by_num, &iter)) { |
2421 | 0 | if (dict == dict_def) continue; |
2422 | 0 | if (dict == dict_gctx->internal) continue; |
2423 | | |
2424 | 0 | (void)func(&our_err, out, fr_dict_root(dict), &our_in, tt); |
2425 | 0 | switch (our_err) { |
2426 | 0 | case FR_DICT_ATTR_OK: |
2427 | 0 | FR_SBUFF_SET_RETURN(in, &our_in); |
2428 | | |
2429 | 0 | case FR_DICT_ATTR_NOTFOUND: |
2430 | 0 | continue; |
2431 | | |
2432 | 0 | default: |
2433 | 0 | break; |
2434 | 0 | } |
2435 | 0 | } |
2436 | | |
2437 | 0 | error: |
2438 | | /* |
2439 | | * Add a more helpful error message about |
2440 | | * which dictionaries we tried to locate |
2441 | | * the attribute in. |
2442 | | */ |
2443 | 0 | if (our_err == FR_DICT_ATTR_NOTFOUND) { |
2444 | 0 | fr_sbuff_marker_t start; |
2445 | 0 | char *list = NULL; |
2446 | |
|
2447 | 0 | #define DICT_NAME_APPEND(_in, _dict) \ |
2448 | 0 | do { \ |
2449 | 0 | char *_n; \ |
2450 | 0 | _n = talloc_strdup_append_buffer(_in, fr_dict_root(_dict)->name); \ |
2451 | 0 | if (unlikely(!_n)) { \ |
2452 | 0 | talloc_free(_in); \ |
2453 | 0 | goto done; \ |
2454 | 0 | } \ |
2455 | 0 | _in = _n; \ |
2456 | 0 | _n = talloc_strdup_append_buffer(_in, ", "); \ |
2457 | 0 | if (unlikely(!_n)) { \ |
2458 | 0 | talloc_free(_in); \ |
2459 | 0 | goto done; \ |
2460 | 0 | } \ |
2461 | 0 | _in = _n; \ |
2462 | 0 | } while (0) |
2463 | |
|
2464 | 0 | our_in = FR_SBUFF(in); |
2465 | 0 | fr_sbuff_marker(&start, &our_in); |
2466 | |
|
2467 | 0 | list = talloc_strdup(NULL, ""); |
2468 | 0 | if (unlikely(!list)) goto done; |
2469 | | |
2470 | 0 | if (dict_def) DICT_NAME_APPEND(list, dict_def); |
2471 | 0 | if (internal) DICT_NAME_APPEND(list, dict_gctx->internal); |
2472 | | |
2473 | 0 | if (foreign) { |
2474 | 0 | for (dict = fr_hash_table_iter_init(dict_gctx->protocol_by_num, &iter); |
2475 | 0 | dict; |
2476 | 0 | dict = fr_hash_table_iter_next(dict_gctx->protocol_by_num, &iter)) { |
2477 | 0 | if (dict == dict_def) continue; |
2478 | 0 | if (dict == dict_gctx->internal) continue; |
2479 | | |
2480 | 0 | if (internal) DICT_NAME_APPEND(list, dict); |
2481 | 0 | } |
2482 | 0 | } |
2483 | | |
2484 | 0 | fr_strerror_printf("Attribute '%pV' not found. Searched in: %pV", |
2485 | 0 | fr_box_strvalue_len(fr_sbuff_current(&start), |
2486 | 0 | fr_sbuff_adv_until(&our_in, SIZE_MAX, tt, '\0')), |
2487 | 0 | fr_box_strvalue_len(list, talloc_array_length(list) - 3)); |
2488 | |
|
2489 | 0 | talloc_free(list); |
2490 | 0 | } |
2491 | | |
2492 | 0 | done: |
2493 | 0 | if (err) *err = our_err; |
2494 | 0 | *out = NULL; |
2495 | |
|
2496 | 0 | FR_SBUFF_ERROR_RETURN(&our_in); |
2497 | 0 | } |
2498 | | |
2499 | | /** Internal function for searching for attributes in multiple dictionaries |
2500 | | * |
2501 | | * Unlike #dict_attr_search this function searches for a protocol name preceding |
2502 | | * the attribute identifier. |
2503 | | */ |
2504 | | static inline CC_HINT(always_inline) |
2505 | | fr_slen_t dict_attr_search_qualified(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, |
2506 | | fr_dict_t const *dict_def, |
2507 | | fr_sbuff_t *in, fr_sbuff_term_t const *tt, |
2508 | | bool internal, bool foreign, |
2509 | | dict_attr_resolve_func_t func) |
2510 | 0 | { |
2511 | 0 | fr_sbuff_t our_in = FR_SBUFF(in); |
2512 | 0 | fr_dict_attr_err_t our_err; |
2513 | 0 | fr_dict_t *initial; |
2514 | 0 | fr_slen_t slen; |
2515 | | |
2516 | | /* |
2517 | | * Check for dictionary prefix |
2518 | | */ |
2519 | 0 | slen = dict_by_protocol_substr(&our_err, &initial, &our_in, dict_def); |
2520 | 0 | if (our_err != FR_DICT_ATTR_OK) { |
2521 | 0 | error: |
2522 | 0 | if (err) *err = our_err; |
2523 | 0 | *out = NULL; |
2524 | 0 | FR_SBUFF_ERROR_RETURN(&our_in); |
2525 | 0 | } |
2526 | | |
2527 | | /* |
2528 | | * Has dictionary qualifier, can't fallback |
2529 | | */ |
2530 | 0 | if (slen > 0) { |
2531 | | /* |
2532 | | * Next thing SHOULD be a '.' |
2533 | | */ |
2534 | 0 | if (!fr_sbuff_next_if_char(&our_in, '.')) { |
2535 | 0 | if (err) *err = FR_DICT_ATTR_PARSE_ERROR; |
2536 | 0 | *out = NULL; |
2537 | 0 | FR_SBUFF_ERROR_RETURN(&our_in); |
2538 | 0 | } |
2539 | | |
2540 | 0 | internal = foreign = false; |
2541 | 0 | } |
2542 | | |
2543 | 0 | if (dict_attr_search(&our_err, out, initial, &our_in, tt, internal, foreign, func) < 0) goto error; |
2544 | 0 | if (err) *err = FR_DICT_ATTR_OK; |
2545 | |
|
2546 | 0 | FR_SBUFF_SET_RETURN(in, &our_in); |
2547 | 0 | } |
2548 | | |
2549 | | /** Locate a qualified #fr_dict_attr_t by its name and a dictionary qualifier |
2550 | | * |
2551 | | * This function will search through all loaded dictionaries, or a subset of |
2552 | | * loaded dictionaries, for a matching attribute in the top level namespace. |
2553 | | * |
2554 | | * This attribute may be qualified with `<protocol>.` to selection an attribute |
2555 | | * in a specific case. |
2556 | | * |
2557 | | * @note If calling this function from the server any list or request qualifiers |
2558 | | * should be stripped first. |
2559 | | * |
2560 | | * @param[out] err Why parsing failed. May be NULL. |
2561 | | * @see fr_dict_attr_err_t |
2562 | | * @param[out] out Dictionary found attribute. |
2563 | | * @param[in] dict_def Default dictionary for non-qualified dictionaries. |
2564 | | * @param[in] name Dictionary/Attribute name. |
2565 | | * @param[in] tt Terminal strings. |
2566 | | * @param[in] internal If true, fallback to the internal dictionary. |
2567 | | * @param[in] foreign If true, fallback to foreign dictionaries. |
2568 | | * @return |
2569 | | * - < 0 on failure. |
2570 | | * - The number of bytes of name consumed on success. |
2571 | | */ |
2572 | | fr_slen_t fr_dict_attr_search_by_qualified_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, |
2573 | | fr_dict_t const *dict_def, |
2574 | | fr_sbuff_t *name, fr_sbuff_term_t const *tt, |
2575 | | bool internal, bool foreign) |
2576 | 0 | { |
2577 | 0 | return dict_attr_search_qualified(err, out, dict_def, name, tt, |
2578 | 0 | internal, foreign, fr_dict_attr_by_name_substr); |
2579 | 0 | } |
2580 | | |
2581 | | /** Locate a #fr_dict_attr_t by its name in the top level namespace of a dictionary |
2582 | | * |
2583 | | * This function will search through all loaded dictionaries, or a subset of |
2584 | | * loaded dictionaries, for a matching attribute in the top level namespace. |
2585 | | * |
2586 | | * @note If calling this function from the server any list or request qualifiers |
2587 | | * should be stripped first. |
2588 | | * |
2589 | | * @param[out] err Why parsing failed. May be NULL. |
2590 | | * @see fr_dict_attr_err_t |
2591 | | * @param[out] out Dictionary found attribute. |
2592 | | * @param[in] dict_def Default dictionary for non-qualified dictionaries. |
2593 | | * @param[in] name Dictionary/Attribute name. |
2594 | | * @param[in] tt Terminal strings. |
2595 | | * @param[in] internal If true, fallback to the internal dictionary. |
2596 | | * @param[in] foreign If true, fallback to foreign dictionaries. |
2597 | | * @return |
2598 | | * - < 0 on failure. |
2599 | | * - The number of bytes of name consumed on success. |
2600 | | */ |
2601 | | fr_slen_t fr_dict_attr_search_by_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, |
2602 | | fr_dict_t const *dict_def, |
2603 | | fr_sbuff_t *name, fr_sbuff_term_t const *tt, |
2604 | | bool internal, bool foreign) |
2605 | 0 | { |
2606 | 0 | return dict_attr_search_qualified(err, out, dict_def, name, tt, |
2607 | 0 | internal, foreign, fr_dict_attr_by_name_substr); |
2608 | 0 | } |
2609 | | |
2610 | | /** Locate a qualified #fr_dict_attr_t by a dictionary qualified OID string |
2611 | | * |
2612 | | * This function will search through all loaded dictionaries, or a subset of |
2613 | | * loaded dictionaries, for a matching attribute. |
2614 | | * |
2615 | | * @note If calling this function from the server any list or request qualifiers |
2616 | | * should be stripped first. |
2617 | | * |
2618 | | * @note err should be checked to determine if a parse error occurred. |
2619 | | * |
2620 | | * @param[out] err Why parsing failed. May be NULL. |
2621 | | * @see fr_dict_attr_err_t |
2622 | | * @param[out] out Dictionary found attribute. |
2623 | | * @param[in] dict_def Default dictionary for non-qualified dictionaries. |
2624 | | * @param[in] in Dictionary/Attribute name. |
2625 | | * @param[in] tt Terminal strings. |
2626 | | * @param[in] internal If true, fallback to the internal dictionary. |
2627 | | * @param[in] foreign If true, fallback to foreign dictionaries. |
2628 | | * @return The number of bytes of name consumed. |
2629 | | */ |
2630 | | fr_slen_t fr_dict_attr_search_by_qualified_oid_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, |
2631 | | fr_dict_t const *dict_def, |
2632 | | fr_sbuff_t *in, fr_sbuff_term_t const *tt, |
2633 | | bool internal, bool foreign) |
2634 | 0 | { |
2635 | 0 | return dict_attr_search_qualified(err, out, dict_def, in, tt, |
2636 | 0 | internal, foreign, fr_dict_attr_by_oid_substr); |
2637 | 0 | } |
2638 | | |
2639 | | /** Locate a qualified #fr_dict_attr_t by a dictionary using a non-qualified OID string |
2640 | | * |
2641 | | * This function will search through all loaded dictionaries, or a subset of |
2642 | | * loaded dictionaries, for a matching attribute. |
2643 | | * |
2644 | | * @note If calling this function from the server any list or request qualifiers |
2645 | | * should be stripped first. |
2646 | | * |
2647 | | * @note err should be checked to determine if a parse error occurred. |
2648 | | * |
2649 | | * @param[out] err Why parsing failed. May be NULL. |
2650 | | * @see fr_dict_attr_err_t |
2651 | | * @param[out] out Dictionary found attribute. |
2652 | | * @param[in] dict_def Default dictionary for non-qualified dictionaries. |
2653 | | * @param[in] in Dictionary/Attribute name. |
2654 | | * @param[in] tt Terminal strings. |
2655 | | * @param[in] internal If true, fallback to the internal dictionary. |
2656 | | * @param[in] foreign If true, fallback to foreign dictionaries. |
2657 | | * @return The number of bytes of name consumed. |
2658 | | */ |
2659 | | fr_slen_t fr_dict_attr_search_by_oid_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, |
2660 | | fr_dict_t const *dict_def, |
2661 | | fr_sbuff_t *in, fr_sbuff_term_t const *tt, |
2662 | | bool internal, bool foreign) |
2663 | 0 | { |
2664 | 0 | return dict_attr_search_qualified(err, out, dict_def, in, tt, |
2665 | 0 | internal, foreign, fr_dict_attr_by_oid_substr); |
2666 | 0 | } |
2667 | | |
2668 | | /** Locate a qualified #fr_dict_attr_t by its name and a dictionary qualifier |
2669 | | * |
2670 | | * @param[out] err Why parsing failed. May be NULL. |
2671 | | * @see fr_dict_attr_err_t. |
2672 | | * @param[in] dict_def Default dictionary for non-qualified dictionaries. |
2673 | | * @param[in] name Dictionary/Attribute name. |
2674 | | * @param[in] internal If true, fallback to the internal dictionary. |
2675 | | * @param[in] foreign If true, fallback to foreign dictionaries. |
2676 | | * @return an #fr_dict_attr_err_t value. |
2677 | | */ |
2678 | | fr_dict_attr_t const *fr_dict_attr_search_by_qualified_oid(fr_dict_attr_err_t *err, fr_dict_t const *dict_def, |
2679 | | char const *name, |
2680 | | bool internal, bool foreign) |
2681 | 0 | { |
2682 | 0 | ssize_t slen; |
2683 | 0 | fr_sbuff_t our_name; |
2684 | 0 | fr_dict_attr_t const *da; |
2685 | 0 | fr_dict_attr_err_t our_err; |
2686 | |
|
2687 | 0 | fr_sbuff_init_in(&our_name, name, strlen(name)); |
2688 | |
|
2689 | 0 | slen = fr_dict_attr_search_by_qualified_oid_substr(&our_err, &da, dict_def, &our_name, NULL, internal, foreign); |
2690 | 0 | if (our_err != FR_DICT_ATTR_OK) { |
2691 | 0 | if (err) *err = our_err; |
2692 | 0 | return NULL; |
2693 | 0 | } |
2694 | 0 | if ((size_t)slen != fr_sbuff_len(&our_name)) { |
2695 | 0 | fr_strerror_printf("Trailing garbage after attr string \"%s\"", name); |
2696 | 0 | if (err) *err = FR_DICT_ATTR_PARSE_ERROR; |
2697 | 0 | return NULL; |
2698 | 0 | } |
2699 | | |
2700 | 0 | return da; |
2701 | 0 | } |
2702 | | |
2703 | | /** Look up a dictionary attribute by a name embedded in another string |
2704 | | * |
2705 | | * Find the first invalid attribute name char in the string pointed |
2706 | | * to by name. |
2707 | | * |
2708 | | * Copy the characters between the start of the name string and the first |
2709 | | * none #fr_dict_attr_allowed_chars char to a buffer and perform a dictionary lookup |
2710 | | * using that value. |
2711 | | * |
2712 | | * If the attribute exists, advance the pointer pointed to by name |
2713 | | * to the first none #fr_dict_attr_allowed_chars char, and return the DA. |
2714 | | * |
2715 | | * If the attribute does not exist, don't advance the pointer and return |
2716 | | * NULL. |
2717 | | * |
2718 | | * @param[out] err Why parsing failed. May be NULL. |
2719 | | * @see fr_dict_attr_err_t |
2720 | | * @param[out] out Where to store the resolve attribute. |
2721 | | * @param[in] parent containing the namespace to search in. |
2722 | | * @param[in] name string start. |
2723 | | * @param[in] tt Terminal sequences to use to determine the portion |
2724 | | * of in to search. |
2725 | | * @return |
2726 | | * - <= 0 on failure. |
2727 | | * - The number of bytes of name consumed on success. |
2728 | | */ |
2729 | | fr_slen_t fr_dict_attr_by_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, |
2730 | | fr_dict_attr_t const *parent, fr_sbuff_t *name, UNUSED fr_sbuff_term_t const *tt) |
2731 | 202k | { |
2732 | 202k | fr_dict_attr_t const *da; |
2733 | 202k | size_t len; |
2734 | 202k | fr_dict_attr_t const *ref; |
2735 | 202k | char const *p; |
2736 | 202k | char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1 + 1]; /* +1 \0 +1 for "too long" */ |
2737 | 202k | fr_sbuff_t our_name = FR_SBUFF(name); |
2738 | 202k | fr_hash_table_t *namespace; |
2739 | | |
2740 | 202k | *out = NULL; |
2741 | | |
2742 | | #ifdef STATIC_ANALYZER |
2743 | | memset(buffer, 0, sizeof(buffer)); |
2744 | | #endif |
2745 | | |
2746 | 202k | len = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(buffer, sizeof(buffer)), |
2747 | 202k | &our_name, SIZE_MAX, |
2748 | 202k | fr_dict_attr_allowed_chars); |
2749 | 202k | if (len == 0) { |
2750 | 0 | fr_strerror_const("Zero length attribute name"); |
2751 | 0 | if (err) *err = FR_DICT_ATTR_PARSE_ERROR; |
2752 | 0 | FR_SBUFF_ERROR_RETURN(&our_name); |
2753 | 0 | } |
2754 | 202k | if (len > FR_DICT_ATTR_MAX_NAME_LEN) { |
2755 | 0 | fr_strerror_const("Attribute name too long"); |
2756 | 0 | if (err) *err = FR_DICT_ATTR_PARSE_ERROR; |
2757 | 0 | FR_SBUFF_ERROR_RETURN(&our_name); |
2758 | 0 | } |
2759 | | |
2760 | | /* |
2761 | | * Do a second pass, ensuring that the name has at least one alphanumeric character. |
2762 | | */ |
2763 | 202k | for (p = buffer; p < (buffer + len); p++) { |
2764 | 202k | if (sbuff_char_alpha_num[(uint8_t) *p]) break; |
2765 | 202k | } |
2766 | | |
2767 | 202k | if ((size_t) (p - buffer) == len) { |
2768 | 0 | fr_strerror_const("Invalid attribute name"); |
2769 | 0 | if (err) *err = FR_DICT_ATTR_PARSE_ERROR; |
2770 | 0 | FR_SBUFF_ERROR_RETURN(&our_name); |
2771 | 0 | } |
2772 | | |
2773 | 202k | ref = fr_dict_attr_ref(parent); |
2774 | 202k | if (ref) parent = ref; |
2775 | | |
2776 | 202k | redo: |
2777 | 202k | namespace = dict_attr_namespace(parent); |
2778 | 202k | if (!namespace) { |
2779 | 0 | fr_strerror_printf("Attribute '%s' does not contain a namespace", parent->name); |
2780 | 0 | if (err) *err = FR_DICT_ATTR_NO_CHILDREN; |
2781 | 0 | fr_sbuff_set_to_start(&our_name); |
2782 | 0 | FR_SBUFF_ERROR_RETURN(&our_name); |
2783 | 0 | } |
2784 | | |
2785 | 202k | da = fr_hash_table_find(namespace, &(fr_dict_attr_t){ .name = buffer }); |
2786 | 202k | if (!da) { |
2787 | 18 | if (parent->flags.is_root) { |
2788 | 18 | fr_dict_t const *dict = fr_dict_by_da(parent); |
2789 | | |
2790 | 18 | if (dict->next) { |
2791 | 0 | parent = dict->next->root; |
2792 | 0 | goto redo; |
2793 | 0 | } |
2794 | 18 | } |
2795 | | |
2796 | 18 | if (err) *err = FR_DICT_ATTR_NOTFOUND; |
2797 | 18 | fr_strerror_printf("Attribute '%s' not found in namespace '%s'", buffer, parent->name); |
2798 | 18 | fr_sbuff_set_to_start(&our_name); |
2799 | 18 | FR_SBUFF_ERROR_RETURN(&our_name); |
2800 | 18 | } |
2801 | | |
2802 | 202k | da = dict_attr_alias(err, da); |
2803 | 202k | if (unlikely(!da)) FR_SBUFF_ERROR_RETURN(&our_name); |
2804 | | |
2805 | 202k | *out = da; |
2806 | 202k | if (err) *err = FR_DICT_ATTR_OK; |
2807 | | |
2808 | 202k | FR_SBUFF_SET_RETURN(name, &our_name); |
2809 | 202k | } |
2810 | | |
2811 | | /* Internal version of fr_dict_attr_by_name |
2812 | | * |
2813 | | */ |
2814 | | fr_dict_attr_t *dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *name) |
2815 | 142k | { |
2816 | 142k | fr_hash_table_t *namespace; |
2817 | 142k | fr_dict_attr_t *da; |
2818 | | |
2819 | 142k | DA_VERIFY(parent); |
2820 | | |
2821 | 142k | redo: |
2822 | 142k | namespace = dict_attr_namespace(parent); |
2823 | 142k | if (!namespace) { |
2824 | 0 | fr_strerror_printf("Attribute '%s' does not contain a namespace", parent->name); |
2825 | 0 | if (err) *err = FR_DICT_ATTR_NO_CHILDREN; |
2826 | 0 | return NULL; |
2827 | 0 | } |
2828 | | |
2829 | 142k | da = fr_hash_table_find(namespace, &(fr_dict_attr_t) { .name = name }); |
2830 | 142k | if (!da) { |
2831 | 77.9k | if (parent->flags.is_root) { |
2832 | 42.0k | fr_dict_t const *dict = fr_dict_by_da(parent); |
2833 | | |
2834 | 42.0k | if (dict->next) { |
2835 | 0 | parent = dict->next->root; |
2836 | 0 | goto redo; |
2837 | 0 | } |
2838 | 42.0k | } |
2839 | | |
2840 | 77.9k | if (err) *err = FR_DICT_ATTR_NOTFOUND; |
2841 | 77.9k | fr_strerror_printf("Attribute '%s' not found in namespace '%s'", name, parent->name); |
2842 | 77.9k | return NULL; |
2843 | 77.9k | } |
2844 | | |
2845 | 64.8k | if (err) *err = FR_DICT_ATTR_OK; |
2846 | | |
2847 | 64.8k | return da; |
2848 | 142k | } |
2849 | | |
2850 | | /** Locate a #fr_dict_attr_t by its name |
2851 | | * |
2852 | | * @param[out] err Why the lookup failed. May be NULL. |
2853 | | * @see fr_dict_attr_err_t. |
2854 | | * @param[in] parent containing the namespace we're searching in. |
2855 | | * @param[in] name of the attribute to locate. |
2856 | | * @return |
2857 | | * - Attribute matching name. |
2858 | | * - NULL if no matching attribute could be found. |
2859 | | */ |
2860 | | fr_dict_attr_t const *fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *name) |
2861 | 97.9k | { |
2862 | 97.9k | fr_dict_attr_t const *da; |
2863 | | |
2864 | 97.9k | DA_VERIFY(parent); |
2865 | | |
2866 | 97.9k | da = dict_attr_by_name(err, parent, name); |
2867 | 97.9k | if (!da) return NULL; |
2868 | | |
2869 | 20.8k | da = dict_attr_alias(err, da); |
2870 | 20.8k | if (unlikely(!da)) return NULL; |
2871 | | |
2872 | 20.8k | return da; |
2873 | 20.8k | } |
2874 | | |
2875 | | /** Internal version of fr_dict_attr_child_by_num |
2876 | | * |
2877 | | */ |
2878 | | fr_dict_attr_t *dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr) |
2879 | 2.08M | { |
2880 | 2.08M | fr_dict_attr_t const *bin; |
2881 | 2.08M | fr_dict_attr_t const **children; |
2882 | 2.08M | fr_dict_attr_t const *ref; |
2883 | | |
2884 | 2.08M | DA_VERIFY(parent); |
2885 | | |
2886 | | /* |
2887 | | * Do any necessary dereferencing |
2888 | | */ |
2889 | 2.08M | ref = fr_dict_attr_ref(parent); |
2890 | 2.08M | if (ref) parent = ref; |
2891 | | |
2892 | 2.08M | children = dict_attr_children(parent); |
2893 | 2.08M | if (!children) return NULL; |
2894 | | |
2895 | | /* |
2896 | | * Child arrays may be trimmed back to save memory. |
2897 | | * Check that so we don't SEGV. |
2898 | | */ |
2899 | 2.07M | if ((attr & 0xff) > talloc_array_length(children)) return NULL; |
2900 | | |
2901 | 2.07M | bin = children[attr & 0xff]; |
2902 | 2.11M | for (;;) { |
2903 | 2.11M | if (!bin) return NULL; |
2904 | 585k | if (bin->attr == attr) { |
2905 | 551k | fr_dict_attr_t *out; |
2906 | | |
2907 | 551k | memcpy(&out, &bin, sizeof(bin)); |
2908 | | |
2909 | 551k | return out; |
2910 | 551k | } |
2911 | 34.3k | bin = bin->next; |
2912 | 34.3k | } |
2913 | | |
2914 | 0 | return NULL; |
2915 | 2.07M | } |
2916 | | |
2917 | | /** Check if a child attribute exists in a parent using an attribute number |
2918 | | * |
2919 | | * @param[in] parent to check for child in. |
2920 | | * @param[in] attr number to look for. |
2921 | | * @return |
2922 | | * - The child attribute on success. |
2923 | | * - NULL if the child attribute does not exist. |
2924 | | */ |
2925 | | fr_dict_attr_t const *fr_dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr) |
2926 | 1.99M | { |
2927 | 1.99M | fr_dict_attr_t const *da; |
2928 | | |
2929 | 1.99M | da = dict_attr_child_by_num(parent, attr); |
2930 | 1.99M | if (!da) return NULL; |
2931 | | |
2932 | 465k | da = dict_attr_alias(NULL, da); |
2933 | 465k | if (unlikely(!da)) return NULL; |
2934 | | |
2935 | 465k | return da; |
2936 | 465k | } |
2937 | | |
2938 | | /** Lookup the structure representing an enum value in a #fr_dict_attr_t |
2939 | | * |
2940 | | * @param[in] da to search in. |
2941 | | * @param[in] value to search for. |
2942 | | * @return |
2943 | | * - Matching #fr_dict_enum_value_t. |
2944 | | * - NULL if no matching #fr_dict_enum_value_t could be found. |
2945 | | */ |
2946 | | fr_dict_enum_value_t *fr_dict_enum_by_value(fr_dict_attr_t const *da, fr_value_box_t const *value) |
2947 | 8.73k | { |
2948 | 8.73k | fr_dict_attr_ext_enumv_t *ext; |
2949 | | |
2950 | 8.73k | ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_ENUMV); |
2951 | 8.73k | if (!ext) { |
2952 | 0 | fr_strerror_printf("VALUE cannot be defined for %s attributes", |
2953 | 0 | fr_type_to_str(da->type)); |
2954 | 0 | return NULL; |
2955 | 0 | } |
2956 | | |
2957 | | /* |
2958 | | * No values associated with this attribute |
2959 | | */ |
2960 | 8.73k | if (!ext->name_by_value) return NULL; |
2961 | | |
2962 | | /* |
2963 | | * Could be NULL or an unknown attribute, in which case |
2964 | | * we want to avoid the lookup gracefully... |
2965 | | */ |
2966 | 8.73k | if (value->type != da->type) return NULL; |
2967 | | |
2968 | 8.73k | return fr_hash_table_find(ext->name_by_value, &(fr_dict_enum_value_t){ .value = value }); |
2969 | 8.73k | } |
2970 | | |
2971 | | /** Lookup the name of an enum value in a #fr_dict_attr_t |
2972 | | * |
2973 | | * @param[in] da to search in. |
2974 | | * @param[in] value number to search for. |
2975 | | * @return |
2976 | | * - Name of value. |
2977 | | * - NULL if no matching value could be found. |
2978 | | */ |
2979 | | char const *fr_dict_enum_name_by_value(fr_dict_attr_t const *da, fr_value_box_t const *value) |
2980 | 0 | { |
2981 | 0 | fr_dict_enum_value_t *dv; |
2982 | |
|
2983 | 0 | dv = fr_dict_enum_by_value(da, value); |
2984 | 0 | if (!dv) return NULL; |
2985 | | |
2986 | 0 | return dv->name; |
2987 | 0 | } |
2988 | | |
2989 | | /* |
2990 | | * Get a value by its name, keyed off of an attribute. |
2991 | | */ |
2992 | | fr_dict_enum_value_t *fr_dict_enum_by_name(fr_dict_attr_t const *da, char const *name, ssize_t len) |
2993 | 2.59k | { |
2994 | 2.59k | fr_dict_attr_ext_enumv_t *ext; |
2995 | | |
2996 | 2.59k | if (!name) return NULL; |
2997 | | |
2998 | 2.59k | ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_ENUMV); |
2999 | 2.59k | if (!ext) { |
3000 | 0 | fr_strerror_printf("VALUE cannot be defined for %s attributes", |
3001 | 0 | fr_type_to_str(da->type)); |
3002 | 0 | return NULL; |
3003 | 0 | } |
3004 | | |
3005 | | /* |
3006 | | * No values associated with this attribute |
3007 | | */ |
3008 | 2.59k | if (!ext->value_by_name) return NULL; |
3009 | | |
3010 | 2.59k | if (len < 0) len = strlen(name); |
3011 | | |
3012 | 2.59k | return fr_hash_table_find(ext->value_by_name, &(fr_dict_enum_value_t){ .name = name, .name_len = len}); |
3013 | 2.59k | } |
3014 | | |
3015 | | /* |
3016 | | * Get a value by its name, keyed off of an attribute, from an sbuff |
3017 | | */ |
3018 | | fr_slen_t fr_dict_enum_by_name_substr(fr_dict_enum_value_t **out, fr_dict_attr_t const *da, fr_sbuff_t *in) |
3019 | 0 | { |
3020 | 0 | fr_dict_attr_ext_enumv_t *ext; |
3021 | 0 | fr_sbuff_t our_in = FR_SBUFF(in); |
3022 | 0 | fr_dict_enum_value_t *found = NULL; |
3023 | 0 | size_t found_len = 0; |
3024 | 0 | uint8_t *p; |
3025 | 0 | uint8_t name[FR_DICT_ENUM_MAX_NAME_LEN + 1]; |
3026 | | |
3027 | | /* |
3028 | | * No values associated with this attribute, do nothing. |
3029 | | */ |
3030 | 0 | ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_ENUMV); |
3031 | 0 | if (!ext || !ext->value_by_name) return 0; |
3032 | | |
3033 | | /* |
3034 | | * Loop until we exhaust all of the possibilities. |
3035 | | */ |
3036 | 0 | for (p = name; (size_t) (p - name) < ext->max_name_len; p++) { |
3037 | 0 | int len = (p - name) + 1; |
3038 | 0 | fr_dict_enum_value_t *enumv; |
3039 | |
|
3040 | 0 | *p = fr_sbuff_char(&our_in, '\0'); |
3041 | 0 | if (!fr_dict_enum_allowed_chars[*p]) { |
3042 | 0 | break; |
3043 | 0 | } |
3044 | 0 | fr_sbuff_next(&our_in); |
3045 | |
|
3046 | 0 | enumv = fr_hash_table_find(ext->value_by_name, &(fr_dict_enum_value_t){ .name = (char const *) name, |
3047 | 0 | .name_len = len}); |
3048 | | |
3049 | | /* |
3050 | | * Return the LONGEST match, as there may be |
3051 | | * overlaps. e.g. "Framed", and "Framed-User". |
3052 | | */ |
3053 | 0 | if (enumv) { |
3054 | 0 | found = enumv; |
3055 | 0 | found_len = len; |
3056 | 0 | } |
3057 | 0 | } |
3058 | |
|
3059 | 0 | if (found) { |
3060 | 0 | *out = found; |
3061 | 0 | FR_SBUFF_SET_RETURN(in, found_len); |
3062 | 0 | } |
3063 | | |
3064 | 0 | return 0; |
3065 | 0 | } |
3066 | | |
3067 | | /** Extract an enumeration name from a string |
3068 | | * |
3069 | | * This function defines the canonical format for an enumeration name. |
3070 | | * |
3071 | | * An enumeration name is made up of one or more fr_dict_attr_allowed_chars |
3072 | | * with at least one character in the sequence not being a special character |
3073 | | * i.e. [-+/_] or a number. |
3074 | | * |
3075 | | * This disambiguates enumeration identifiers from mathematical expressions. |
3076 | | * |
3077 | | * If we allowed enumeration names consisting of sequences of numbers separated |
3078 | | * by special characters it would not be possible to determine if the special |
3079 | | * character were an operator in a subexpression. |
3080 | | * |
3081 | | * For example take: |
3082 | | * |
3083 | | * &My-Enum-Attr == 01234-5678 |
3084 | | * |
3085 | | * Without having access to the enumeration values of My-Enum-Attr (which we |
3086 | | * might not have during tokenisation), we cannot tell if this is: |
3087 | | * |
3088 | | * (&My-Enum-Attr == 01234-5678) |
3089 | | * |
3090 | | * OR |
3091 | | * |
3092 | | * ((&My-Enum-Attr == 01234) - 5678) |
3093 | | * |
3094 | | * If an alpha character occurs anywhere in the string i.e: |
3095 | | * |
3096 | | * (&My-Enum-Attr == 01234-A5678) |
3097 | | * |
3098 | | * we know 01234-A5678 can't be a mathematical sub-expression because the |
3099 | | * second potential operand can no longer be parsed as an integer constant. |
3100 | | * |
3101 | | * @param[out] out The name string we managed to extract. |
3102 | | * May be NULL in which case only the length of the name |
3103 | | * will be returned. |
3104 | | * @param[out] err Type of parsing error which occurred. May be NULL. |
3105 | | * @param[in] in The string containing the enum identifier. |
3106 | | * @param[in] tt If non-null verify that a terminal sequence occurs |
3107 | | * after the enumeration name. |
3108 | | * @return |
3109 | | * - <0 the offset at which the parse error occurred. |
3110 | | * - >1 the number of bytes parsed. |
3111 | | */ |
3112 | | fr_slen_t fr_dict_enum_name_from_substr(fr_sbuff_t *out, fr_sbuff_parse_error_t *err, |
3113 | | fr_sbuff_t *in, fr_sbuff_term_t const *tt) |
3114 | 34.5k | { |
3115 | 34.5k | fr_sbuff_t our_in = FR_SBUFF(in); |
3116 | 34.5k | bool seen_alpha = false; |
3117 | | |
3118 | 485k | while (fr_sbuff_is_in_charset(&our_in, fr_dict_enum_allowed_chars)) { |
3119 | 451k | if (fr_sbuff_is_alpha(&our_in)) seen_alpha = true; |
3120 | 451k | fr_sbuff_next(&our_in); |
3121 | 451k | } |
3122 | | |
3123 | 34.5k | if (!seen_alpha) { |
3124 | 0 | if (fr_sbuff_used(&our_in) == 0) { |
3125 | 0 | fr_strerror_const("VALUE name is empty"); |
3126 | 0 | if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; |
3127 | 0 | FR_SBUFF_ERROR_RETURN(&our_in); |
3128 | 0 | } |
3129 | | |
3130 | 0 | fr_strerror_const("VALUE name must contain at least one alpha character"); |
3131 | 0 | if (err) *err = FR_SBUFF_PARSE_ERROR_FORMAT; |
3132 | 0 | fr_sbuff_set_to_start(&our_in); /* Marker should be at the start of the enum */ |
3133 | 0 | FR_SBUFF_ERROR_RETURN(&our_in); |
3134 | 0 | } |
3135 | | |
3136 | | /* |
3137 | | * Check that the sequence is correctly terminated |
3138 | | */ |
3139 | 34.5k | if (tt && !fr_sbuff_is_terminal(&our_in, tt)) { |
3140 | 0 | fr_strerror_const("VALUE name has trailing text"); |
3141 | 0 | if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING; |
3142 | 0 | FR_SBUFF_ERROR_RETURN(&our_in); |
3143 | 0 | } |
3144 | | |
3145 | 34.5k | if (out) return fr_sbuff_out_bstrncpy_exact(out, in, fr_sbuff_used(&our_in)); |
3146 | | |
3147 | 34.5k | if (err) *err = FR_SBUFF_PARSE_OK; |
3148 | | |
3149 | 34.5k | FR_SBUFF_SET_RETURN(in, &our_in); |
3150 | 34.5k | } |
3151 | | |
3152 | | int dict_dlopen(fr_dict_t *dict, char const *name) |
3153 | 39 | { |
3154 | 39 | char *module_name; |
3155 | 39 | char *p, *q; |
3156 | | |
3157 | 39 | if (!name) return 0; |
3158 | | |
3159 | 39 | module_name = talloc_typed_asprintf(NULL, "libfreeradius-%s", name); |
3160 | 839 | for (p = module_name, q = p + talloc_array_length(p) - 1; p < q; p++) *p = tolower((uint8_t) *p); |
3161 | | |
3162 | | /* |
3163 | | * Pass in dict as the uctx so that we can get at it in |
3164 | | * any callbacks. |
3165 | | * |
3166 | | * Not all dictionaries have validation functions. It's |
3167 | | * a soft error if they don't exist. |
3168 | | */ |
3169 | 39 | dict->dl = dl_by_name(dict_gctx->dict_loader, module_name, dict, false); |
3170 | 39 | if (!dict->dl) { |
3171 | 0 | fr_strerror_printf_push("Failed loading dictionary validation library \"%s\"", module_name); |
3172 | 0 | talloc_free(module_name); |
3173 | 0 | return -1; |
3174 | 0 | } |
3175 | | |
3176 | 39 | talloc_free(module_name); |
3177 | 39 | return 0; |
3178 | 39 | } |
3179 | | |
3180 | | /** Find a dependent in the tree of dependents |
3181 | | * |
3182 | | */ |
3183 | | static int8_t _dict_dependent_cmp(void const *a, void const *b) |
3184 | 38.6k | { |
3185 | 38.6k | fr_dict_dependent_t const *dep_a = a; |
3186 | 38.6k | fr_dict_dependent_t const *dep_b = b; |
3187 | 38.6k | int ret; |
3188 | | |
3189 | 38.6k | ret = strcmp(dep_a->dependent, dep_b->dependent); |
3190 | 38.6k | return CMP(ret, 0); |
3191 | 38.6k | } |
3192 | | |
3193 | | /** Record a new dependency on a dictionary |
3194 | | * |
3195 | | * These are used to determine what is currently depending on a dictionary. |
3196 | | * |
3197 | | * @param[in] dict to record dependency on. |
3198 | | * @param[in] dependent Either C src file, or another dictionary. |
3199 | | * @return |
3200 | | * - 0 on success. |
3201 | | * - -1 on failure. |
3202 | | */ |
3203 | | int dict_dependent_add(fr_dict_t *dict, char const *dependent) |
3204 | 5.56k | { |
3205 | 5.56k | fr_dict_dependent_t *found; |
3206 | | |
3207 | 5.56k | found = fr_rb_find(dict->dependents, &(fr_dict_dependent_t){ .dependent = dependent } ); |
3208 | 5.56k | if (!found) { |
3209 | 5.56k | fr_dict_dependent_t *new; |
3210 | | |
3211 | 5.56k | new = talloc_zero(dict->dependents, fr_dict_dependent_t); |
3212 | 5.56k | if (unlikely(!new)) return -1; |
3213 | | |
3214 | | /* |
3215 | | * If the dependent is in a module that gets |
3216 | | * unloaded, any strings in the text area also |
3217 | | * get unloaded (including dependent locations). |
3218 | | * |
3219 | | * Strdup the string here so we don't get |
3220 | | * random segfaults if a module forgets to unload |
3221 | | * a dictionary. |
3222 | | */ |
3223 | 5.56k | new->dependent = talloc_typed_strdup(new, dependent); |
3224 | 5.56k | fr_rb_insert(dict->dependents, new); |
3225 | | |
3226 | 5.56k | new->count = 1; |
3227 | | |
3228 | 5.56k | return 0; |
3229 | 5.56k | } |
3230 | | |
3231 | 0 | found->count++; /* Increase ref count */ |
3232 | |
|
3233 | 0 | return 0; |
3234 | 5.56k | } |
3235 | | |
3236 | | /** Manually increase the reference count for a dictionary |
3237 | | * |
3238 | | * This is useful if a previously loaded dictionary needs to |
3239 | | * be bound to the lifetime of an additional object. |
3240 | | * |
3241 | | * @param[in] dict to increase the reference count for. |
3242 | | * @param[in] dependent requesting the loading of the dictionary. |
3243 | | * @return |
3244 | | * - 0 on success. |
3245 | | * - -1 on error. |
3246 | | */ |
3247 | | int fr_dict_dependent_add(fr_dict_t const *dict, char const *dependent) |
3248 | 0 | { |
3249 | 0 | fr_dict_t *m_dict = fr_dict_unconst(dict); |
3250 | |
|
3251 | 0 | if (unlikely(!m_dict)) return -1; |
3252 | | |
3253 | 0 | return dict_dependent_add(m_dict, dependent); |
3254 | 0 | } |
3255 | | |
3256 | | /** Decrement ref count for a dependent in a dictionary |
3257 | | * |
3258 | | * @param[in] dict to remove dependency from. |
3259 | | * @param[in] dependent Either C src, or another dictionary dependent. |
3260 | | * What depends on this dictionary. |
3261 | | */ |
3262 | | int dict_dependent_remove(fr_dict_t *dict, char const *dependent) |
3263 | 5.56k | { |
3264 | 5.56k | fr_dict_dependent_t *found; |
3265 | | |
3266 | 5.56k | found = fr_rb_find(dict->dependents, &(fr_dict_dependent_t){ .dependent = dependent } ); |
3267 | 5.56k | if (!found) { |
3268 | 0 | fr_strerror_printf("Dependent \"%s\" not found in dictionary \"%s\"", dependent, dict->root->name); |
3269 | 0 | return -1; |
3270 | 0 | } |
3271 | | |
3272 | 5.56k | if (found->count == 0) { |
3273 | 0 | fr_strerror_printf("Zero ref count invalid for dependent \"%s\", dictionary \"%s\"", |
3274 | 0 | dependent, dict->root->name); |
3275 | 0 | return -1; |
3276 | 0 | } |
3277 | | |
3278 | 5.56k | if (--found->count == 0) { |
3279 | 5.56k | fr_rb_delete(dict->dependents, found); |
3280 | 5.56k | talloc_free(found); |
3281 | 5.56k | return 0; |
3282 | 5.56k | } |
3283 | | |
3284 | 0 | return 1; |
3285 | 5.56k | } |
3286 | | |
3287 | | /** Check if a dictionary still has dependents |
3288 | | * |
3289 | | * @param[in] dict to check |
3290 | | * @return |
3291 | | * - true if there's still at least one dependent. |
3292 | | * - false if there are no dependents. |
3293 | | */ |
3294 | | bool dict_has_dependents(fr_dict_t *dict) |
3295 | 5.56k | { |
3296 | 5.56k | return (fr_rb_num_elements(dict->dependents) > 0); |
3297 | 5.56k | } |
3298 | | |
3299 | | #ifndef NDEBUG |
3300 | | static void dependent_debug(fr_dict_t *dict) |
3301 | 0 | { |
3302 | 0 | fr_rb_iter_inorder_t iter; |
3303 | 0 | fr_dict_dependent_t *dep; |
3304 | 0 |
|
3305 | 0 | if (!dict_has_dependents(dict)) return; |
3306 | 0 |
|
3307 | 0 | fprintf(stderr, "DEPENDENTS FOR %s\n", dict->root->name); |
3308 | 0 |
|
3309 | 0 | for (dep = fr_rb_iter_init_inorder(&iter, dict->dependents); |
3310 | 0 | dep; |
3311 | 0 | dep = fr_rb_iter_next_inorder(&iter)) { |
3312 | 0 | fprintf(stderr, "\t<- %s (%u)\n", dep->dependent, dep->count); |
3313 | 0 | } |
3314 | 0 | } |
3315 | | #endif |
3316 | | |
3317 | | |
3318 | | static int dict_autoref_free(fr_dict_t *dict) |
3319 | 21 | { |
3320 | 21 | fr_dict_t **refd_list; |
3321 | 21 | unsigned int i; |
3322 | | |
3323 | 21 | if (!dict->autoref) return 0; |
3324 | | |
3325 | 21 | if (fr_hash_table_flatten(dict->autoref, (void ***)&refd_list, dict->autoref) < 0) { |
3326 | 0 | fr_strerror_const("failed flattening autoref hash table"); |
3327 | 0 | return -1; |
3328 | 0 | } |
3329 | | |
3330 | | /* |
3331 | | * Free the dictionary. It will call proto->free() if there's nothing more to do. |
3332 | | */ |
3333 | 29 | for (i = 0; i < talloc_array_length(refd_list); i++) { |
3334 | 8 | if (fr_dict_free(&refd_list[i], dict->root->name) < 0) { |
3335 | 0 | fr_strerror_printf("failed freeing autoloaded protocol %s", refd_list[i]->root->name); |
3336 | 0 | return -1; |
3337 | 0 | } |
3338 | 8 | } |
3339 | | |
3340 | 21 | TALLOC_FREE(dict->autoref); |
3341 | | |
3342 | 21 | return 0; |
3343 | 21 | } |
3344 | | |
3345 | | static int _dict_free(fr_dict_t *dict) |
3346 | 39 | { |
3347 | | /* |
3348 | | * We don't necessarily control the order of freeing |
3349 | | * children. |
3350 | | */ |
3351 | 39 | if (dict != dict->gctx->internal) { |
3352 | 21 | fr_dict_attr_t const *da; |
3353 | | |
3354 | 21 | if (dict->gctx->attr_protocol_encapsulation && dict->root) { |
3355 | 21 | da = fr_dict_attr_child_by_num(dict->gctx->attr_protocol_encapsulation, dict->root->attr); |
3356 | 21 | if (da && fr_dict_attr_ref(da)) dict_attr_ref_set(da, NULL); |
3357 | 21 | } |
3358 | 21 | } |
3359 | | |
3360 | | #ifdef STATIC_ANALYZER |
3361 | | if (!dict->root) { |
3362 | | fr_strerror_const("dict root is missing"); |
3363 | | return -1; |
3364 | | } |
3365 | | #endif |
3366 | | |
3367 | | /* |
3368 | | * If we called init(), then call free() |
3369 | | */ |
3370 | 39 | if (dict->proto && dict->proto->free) { |
3371 | 18 | dict->proto->free(); |
3372 | 18 | } |
3373 | | |
3374 | 39 | if (!fr_cond_assert(!dict->in_protocol_by_name || fr_hash_table_delete(dict->gctx->protocol_by_name, dict))) { |
3375 | 0 | fr_strerror_printf("Failed removing dictionary from protocol hash \"%s\"", dict->root->name); |
3376 | 0 | return -1; |
3377 | 0 | } |
3378 | 39 | dict->in_protocol_by_name = false; |
3379 | | |
3380 | 39 | if (!fr_cond_assert(!dict->in_protocol_by_num || fr_hash_table_delete(dict->gctx->protocol_by_num, dict))) { |
3381 | 0 | fr_strerror_printf("Failed removing dictionary from protocol number_hash \"%s\"", dict->root->name); |
3382 | 0 | return -1; |
3383 | 0 | } |
3384 | 39 | dict->in_protocol_by_num = false; |
3385 | | |
3386 | 39 | if (dict_has_dependents(dict)) { |
3387 | 0 | fr_rb_iter_inorder_t iter; |
3388 | 0 | fr_dict_dependent_t *dep; |
3389 | |
|
3390 | 0 | fr_strerror_printf("Refusing to free dictionary \"%s\", still has dependents", dict->root->name); |
3391 | |
|
3392 | 0 | for (dep = fr_rb_iter_init_inorder(&iter, dict->dependents); |
3393 | 0 | dep; |
3394 | 0 | dep = fr_rb_iter_next_inorder(&iter)) { |
3395 | 0 | fr_strerror_printf_push("%s (%u)", dep->dependent, dep->count); |
3396 | 0 | } |
3397 | |
|
3398 | 0 | return -1; |
3399 | 0 | } |
3400 | | |
3401 | | /* |
3402 | | * Free the hash tables with free functions first |
3403 | | * so that the things the hash tables reference |
3404 | | * are still there. |
3405 | | */ |
3406 | 39 | talloc_free(dict->vendors_by_name); |
3407 | | |
3408 | | /* |
3409 | | * Decrease the reference count on the validation |
3410 | | * library we loaded. |
3411 | | */ |
3412 | 39 | dl_free(dict->dl); |
3413 | | |
3414 | 39 | if (dict == dict->gctx->internal) { |
3415 | 18 | dict->gctx->internal = NULL; |
3416 | 18 | dict->gctx->attr_protocol_encapsulation = NULL; |
3417 | 18 | } |
3418 | | |
3419 | 39 | return 0; |
3420 | 39 | } |
3421 | | |
3422 | | /** Allocate a new dictionary |
3423 | | * |
3424 | | * @param[in] ctx to allocate dictionary in. |
3425 | | * @return |
3426 | | * - NULL on memory allocation error. |
3427 | | */ |
3428 | | fr_dict_t *dict_alloc(TALLOC_CTX *ctx) |
3429 | 39 | { |
3430 | 39 | fr_dict_t *dict; |
3431 | | |
3432 | 39 | if (!dict_gctx) { |
3433 | 0 | fr_strerror_const("Initialise global dictionary ctx with fr_dict_global_ctx_init()"); |
3434 | 0 | return NULL; |
3435 | 0 | } |
3436 | | |
3437 | 39 | dict = talloc_zero(ctx, fr_dict_t); |
3438 | 39 | if (!dict) { |
3439 | 0 | fr_strerror_const("Failed allocating memory for dictionary"); |
3440 | 0 | error: |
3441 | 0 | talloc_free(dict); |
3442 | 0 | return NULL; |
3443 | 0 | } |
3444 | 39 | dict->gctx = dict_gctx; /* Record which global context this was allocated in */ |
3445 | 39 | talloc_set_destructor(dict, _dict_free); |
3446 | | |
3447 | | /* |
3448 | | * Pre-Allocate pool memory for rapid startup |
3449 | | * As that's the working memory required during |
3450 | | * dictionary initialisation. |
3451 | | */ |
3452 | 39 | dict->pool = talloc_pool(dict, DICT_POOL_SIZE); |
3453 | 39 | if (!dict->pool) { |
3454 | 0 | fr_strerror_const("Failed allocating talloc pool for dictionary"); |
3455 | 0 | goto error; |
3456 | 0 | } |
3457 | | |
3458 | | /* |
3459 | | * Create the table of vendor by name. There MAY NOT |
3460 | | * be multiple vendors of the same name. |
3461 | | */ |
3462 | 39 | dict->vendors_by_name = fr_hash_table_alloc(dict, dict_vendor_name_hash, dict_vendor_name_cmp, hash_pool_free); |
3463 | 39 | if (!dict->vendors_by_name) { |
3464 | 0 | fr_strerror_printf("Failed allocating \"vendors_by_name\" table"); |
3465 | 0 | goto error; |
3466 | 0 | } |
3467 | | /* |
3468 | | * Create the table of vendors by value. There MAY |
3469 | | * be vendors of the same value. If there are, we |
3470 | | * pick the latest one. |
3471 | | */ |
3472 | 39 | dict->vendors_by_num = fr_hash_table_alloc(dict, dict_vendor_pen_hash, dict_vendor_pen_cmp, NULL); |
3473 | 39 | if (!dict->vendors_by_num) { |
3474 | 0 | fr_strerror_printf("Failed allocating \"vendors_by_num\" table"); |
3475 | 0 | goto error; |
3476 | 0 | } |
3477 | | |
3478 | | /* |
3479 | | * Inter-dictionary reference caching |
3480 | | */ |
3481 | 39 | dict->autoref = fr_hash_table_alloc(dict, dict_protocol_name_hash, dict_protocol_name_cmp, NULL); |
3482 | 39 | if (!dict->autoref) { |
3483 | 0 | fr_strerror_printf("Failed allocating \"autoref\" table"); |
3484 | 0 | goto error; |
3485 | 0 | } |
3486 | | |
3487 | | /* |
3488 | | * Who/what depends on this dictionary |
3489 | | */ |
3490 | 39 | dict->dependents = fr_rb_inline_alloc(dict, fr_dict_dependent_t, node, _dict_dependent_cmp, NULL); |
3491 | | |
3492 | | /* |
3493 | | * Set default type size and length. |
3494 | | */ |
3495 | 39 | dict->default_type_size = 1; |
3496 | 39 | dict->default_type_length = 1; |
3497 | | |
3498 | 39 | return dict; |
3499 | 39 | } |
3500 | | |
3501 | | /** Allocate a new local dictionary |
3502 | | * |
3503 | | * @param[in] parent parent dictionary and talloc ctx |
3504 | | * @return |
3505 | | * - NULL on memory allocation error. |
3506 | | * |
3507 | | * This dictionary cannot define vendors, or inter-dictionary |
3508 | | * dependencies. However, we initialize the relevant fields just in |
3509 | | * case. We should arguably just skip initializing those fields, and |
3510 | | * just allow the server to crash if programmers do something stupid with it. |
3511 | | */ |
3512 | | fr_dict_t *fr_dict_protocol_alloc(fr_dict_t const *parent) |
3513 | 0 | { |
3514 | 0 | fr_dict_t *dict; |
3515 | 0 | fr_dict_attr_t *da; |
3516 | |
|
3517 | 0 | fr_dict_attr_flags_t flags = { |
3518 | 0 | .is_root = true, |
3519 | 0 | .local = true, |
3520 | 0 | .type_size = 2, |
3521 | 0 | .length = 2 |
3522 | 0 | }; |
3523 | | |
3524 | |
|
3525 | 0 | dict = dict_alloc(UNCONST(fr_dict_t *, parent)); |
3526 | 0 | if (!dict) return NULL; |
3527 | | |
3528 | | /* |
3529 | | * Allow for 64k local attributes. |
3530 | | */ |
3531 | 0 | dict->default_type_size = 2; |
3532 | 0 | dict->default_type_length = 2; |
3533 | | |
3534 | | /* |
3535 | | * Allocate the root attribute. This dictionary is |
3536 | | * always protocol "local", and number "0". |
3537 | | */ |
3538 | 0 | da = dict_attr_alloc(dict->pool, NULL, "local", 0, FR_TYPE_TLV, |
3539 | 0 | &(dict_attr_args_t){ .flags = &flags }); |
3540 | 0 | if (unlikely(!da)) { |
3541 | 0 | talloc_free(dict); |
3542 | 0 | return NULL; |
3543 | 0 | } |
3544 | | |
3545 | 0 | da->last_child_attr = fr_dict_root(parent)->last_child_attr; |
3546 | |
|
3547 | 0 | dict->root = da; |
3548 | 0 | dict->root->dict = dict; |
3549 | 0 | dict->next = parent; |
3550 | |
|
3551 | 0 | DA_VERIFY(dict->root); |
3552 | |
|
3553 | 0 | return dict; |
3554 | 0 | } |
3555 | | |
3556 | | /** Decrement the reference count on a previously loaded dictionary |
3557 | | * |
3558 | | * @param[in] dict to free. |
3559 | | * @param[in] dependent that originally allocated this dictionary. |
3560 | | * @return |
3561 | | * - 0 on success (dictionary freed). |
3562 | | * - 1 if other things still depend on the dictionary. |
3563 | | * - -1 on error (dependent doesn't exist) |
3564 | | */ |
3565 | | int fr_dict_const_free(fr_dict_t const **dict, char const *dependent) |
3566 | 5.49k | { |
3567 | 5.49k | fr_dict_t **our_dict = UNCONST(fr_dict_t **, dict); |
3568 | | |
3569 | 5.49k | return fr_dict_free(our_dict, dependent); |
3570 | 5.49k | } |
3571 | | |
3572 | | /** Decrement the reference count on a previously loaded dictionary |
3573 | | * |
3574 | | * @param[in] dict to free. |
3575 | | * @param[in] dependent that originally allocated this dictionary. |
3576 | | * @return |
3577 | | * - 0 on success (dictionary freed). |
3578 | | * - 1 if other things still depend on the dictionary. |
3579 | | * - -1 on error (dependent doesn't exist) |
3580 | | */ |
3581 | | int fr_dict_free(fr_dict_t **dict, char const *dependent) |
3582 | 5.54k | { |
3583 | 5.54k | if (!*dict) return 0; |
3584 | | |
3585 | 5.52k | switch (dict_dependent_remove(*dict, dependent)) { |
3586 | 5.52k | case 0: /* dependent has no more refs */ |
3587 | 5.52k | if (!dict_has_dependents(*dict)) { |
3588 | 18 | talloc_free(*dict); |
3589 | 18 | return 0; |
3590 | 18 | } |
3591 | 5.52k | FALL_THROUGH; |
3592 | | |
3593 | 5.50k | case 1: /* dependent has more refs */ |
3594 | 5.50k | return 1; |
3595 | | |
3596 | 0 | default: /* error */ |
3597 | 0 | return -1; |
3598 | 5.52k | } |
3599 | 5.52k | } |
3600 | | |
3601 | | /** Process a dict_attr_autoload element to load/verify a dictionary attribute |
3602 | | * |
3603 | | * @param[in] to_load attribute definition |
3604 | | * @return |
3605 | | * - 0 on success. |
3606 | | * - -1 on failure. |
3607 | | */ |
3608 | | int fr_dict_enum_autoload(fr_dict_enum_autoload_t const *to_load) |
3609 | 0 | { |
3610 | 0 | fr_dict_enum_autoload_t const *p = to_load; |
3611 | 0 | fr_dict_enum_value_t const *enumv; |
3612 | |
|
3613 | 0 | for (p = to_load; p->out; p++) { |
3614 | 0 | if (unlikely(!p->attr)) { |
3615 | 0 | fr_strerror_printf("Invalid attribute autoload entry for \"%s\", missing attribute pointer", p->name); |
3616 | 0 | return -1; |
3617 | 0 | } |
3618 | | |
3619 | 0 | if (unlikely(!*p->attr)) { |
3620 | 0 | fr_strerror_printf("Can't resolve value \"%s\", attribute not loaded", p->name); |
3621 | 0 | fr_strerror_printf_push("Check fr_dict_attr_autoload_t struct has " |
3622 | 0 | "an entry to load the attribute \"%s\" is located in, and that " |
3623 | 0 | "the fr_dict_autoload_attr_t symbol name is correct", p->name); |
3624 | 0 | return -1; |
3625 | 0 | } |
3626 | | |
3627 | 0 | enumv = fr_dict_enum_by_name(*(p->attr), p->name, -1); |
3628 | 0 | if (!enumv) { |
3629 | 0 | fr_strerror_printf("Value '%s' not found in \"%s\" attribute", |
3630 | 0 | p->name, (*(p->attr))->name); |
3631 | 0 | return -1; |
3632 | 0 | } |
3633 | | |
3634 | 0 | if (p->out) *(p->out) = enumv->value; |
3635 | 0 | } |
3636 | | |
3637 | 0 | return 0; |
3638 | 0 | } |
3639 | | |
3640 | | /** Process a dict_attr_autoload element to load/verify a dictionary attribute |
3641 | | * |
3642 | | * @param[in] to_load attribute definition |
3643 | | * @return |
3644 | | * - 0 on success. |
3645 | | * - -1 on failure. |
3646 | | */ |
3647 | | int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load) |
3648 | 5.48k | { |
3649 | 5.48k | fr_dict_attr_t const *da; |
3650 | 5.48k | fr_dict_attr_autoload_t const *p = to_load; |
3651 | | |
3652 | 174k | for (p = to_load; p->out; p++) { |
3653 | 168k | if (!p->dict) { |
3654 | 0 | fr_strerror_printf("Invalid attribute autoload entry for \"%s\", missing dictionary pointer", p->name); |
3655 | 0 | return -1; |
3656 | 0 | } |
3657 | | |
3658 | 168k | if (!*p->dict) { |
3659 | 0 | fr_strerror_printf("Autoloader autoloader can't resolve attribute \"%s\", dictionary not loaded", p->name); |
3660 | 0 | fr_strerror_printf_push("Check fr_dict_autoload_t struct has " |
3661 | 0 | "an entry to load the dictionary \"%s\" is located in, and that " |
3662 | 0 | "the fr_dict_autoload_t symbol name is correct", p->name); |
3663 | 0 | return -1; |
3664 | 0 | } |
3665 | | |
3666 | 168k | da = fr_dict_attr_by_oid(NULL, fr_dict_root(*p->dict), p->name); |
3667 | 168k | if (!da) { |
3668 | 0 | fr_strerror_printf("Autoloader attribute \"%s\" not found in \"%s\" dictionary", p->name, |
3669 | 0 | *p->dict ? (*p->dict)->root->name : "internal"); |
3670 | 0 | return -1; |
3671 | 0 | } |
3672 | | |
3673 | 168k | if (da->type != p->type) { |
3674 | 0 | fr_strerror_printf("Autoloader attribute \"%s\" should be type %s, but defined as type %s", da->name, |
3675 | 0 | fr_type_to_str(p->type), |
3676 | 0 | fr_type_to_str(da->type)); |
3677 | 0 | return -1; |
3678 | 0 | } |
3679 | | |
3680 | 168k | DA_VERIFY(da); |
3681 | | |
3682 | 168k | if (p->out) *(p->out) = da; |
3683 | 168k | } |
3684 | | |
3685 | 5.48k | return 0; |
3686 | 5.48k | } |
3687 | | |
3688 | | /** Process a dict_autoload element to load a protocol |
3689 | | * |
3690 | | * @param[in] to_load dictionary definition. |
3691 | | * @param[in] dependent that is loading this dictionary. |
3692 | | * @return |
3693 | | * - 0 on success. |
3694 | | * - -1 on failure. |
3695 | | */ |
3696 | | int _fr_dict_autoload(fr_dict_autoload_t const *to_load, char const *dependent) |
3697 | 5.48k | { |
3698 | 5.48k | fr_dict_autoload_t const *p; |
3699 | | |
3700 | 10.9k | for (p = to_load; p->out; p++) { |
3701 | 5.49k | fr_dict_t *dict = NULL; |
3702 | | |
3703 | 5.49k | if (unlikely(!p->proto)) { |
3704 | 0 | fr_strerror_const("autoload missing parameter proto"); |
3705 | 0 | return -1; |
3706 | 0 | } |
3707 | | |
3708 | | /* |
3709 | | * Load the internal dictionary |
3710 | | */ |
3711 | 5.49k | if (strcmp(p->proto, "freeradius") == 0) { |
3712 | 6 | if (fr_dict_internal_afrom_file(&dict, p->proto, dependent) < 0) return -1; |
3713 | 5.48k | } else { |
3714 | 5.48k | if (fr_dict_protocol_afrom_file(&dict, p->proto, p->base_dir, dependent) < 0) return -1; |
3715 | 5.48k | } |
3716 | | |
3717 | 5.49k | *(p->out) = dict; |
3718 | 5.49k | } |
3719 | | |
3720 | 5.48k | return 0; |
3721 | 5.48k | } |
3722 | | |
3723 | | |
3724 | | /** Decrement the reference count on a previously loaded dictionary |
3725 | | * |
3726 | | * @param[in] to_free previously loaded dictionary to free. |
3727 | | * @param[in] dependent that originally allocated this dictionary |
3728 | | */ |
3729 | | int _fr_dict_autofree(fr_dict_autoload_t const *to_free, char const *dependent) |
3730 | 5.48k | { |
3731 | 5.48k | fr_dict_autoload_t const *p; |
3732 | | |
3733 | 10.9k | for (p = to_free; p->out; p++) { |
3734 | 5.49k | int ret; |
3735 | | |
3736 | 5.49k | if (!*p->out) continue; |
3737 | 5.49k | ret = fr_dict_const_free(p->out, dependent); |
3738 | | |
3739 | 5.49k | if (ret == 0) *p->out = NULL; |
3740 | 5.49k | if (ret < 0) return -1; |
3741 | 5.49k | } |
3742 | | |
3743 | 5.48k | return 0; |
3744 | 5.48k | } |
3745 | | |
3746 | | /** Structure used to managed the lifetime of a dictionary |
3747 | | * |
3748 | | * This should only be used when dictionaries are being dynamically loaded during |
3749 | | * compilation. It should not be used to load dictionaries at runtime, or if |
3750 | | * modules need to load dictionaries (use static fr_dict_autoload_t defs). |
3751 | | |
3752 | | */ |
3753 | | struct fr_dict_autoload_talloc_s { |
3754 | | fr_dict_autoload_t load[2]; //!< Autoloader def. |
3755 | | char const *dependent; //!< Dependent that loaded the dictionary. |
3756 | | }; |
3757 | | |
3758 | | /** Talloc destructor to automatically free dictionaries |
3759 | | * |
3760 | | * @param[in] to_free dictionary autoloader definition describing the dictionary to free. |
3761 | | */ |
3762 | | static int _fr_dict_autoload_talloc_free(fr_dict_autoload_talloc_t const *to_free) |
3763 | 0 | { |
3764 | 0 | return _fr_dict_autofree(to_free->load, to_free->dependent); |
3765 | 0 | } |
3766 | | |
3767 | | /** Autoload a dictionary and bind the lifetime to a talloc chunk |
3768 | | * |
3769 | | * Mainly useful for resolving "forward" references from unlang immediately. |
3770 | | * |
3771 | | * @note If the talloc chunk is freed it does not mean the dictionary will |
3772 | | * be immediately freed. It will be freed when all other references |
3773 | | * to the dictionary are gone. |
3774 | | * |
3775 | | * @param[in] ctx to bind the dictionary lifetime to. |
3776 | | * @param[out] out pointer to the loaded dictionary. |
3777 | | * @param[in] proto to load. |
3778 | | * @param[in] dependent to register this reference to. Will be dupd. |
3779 | | */ |
3780 | | fr_dict_autoload_talloc_t *_fr_dict_autoload_talloc(TALLOC_CTX *ctx, fr_dict_t const **out, char const *proto, char const *dependent) |
3781 | 0 | { |
3782 | 0 | fr_dict_autoload_talloc_t *dict_ref; |
3783 | 0 | int ret; |
3784 | |
|
3785 | 0 | dict_ref = talloc(ctx, fr_dict_autoload_talloc_t); |
3786 | 0 | if (unlikely(dict_ref == NULL)) { |
3787 | 0 | oom: |
3788 | 0 | fr_strerror_const("Out of memory"); |
3789 | 0 | return NULL; |
3790 | 0 | } |
3791 | | |
3792 | 0 | dict_ref->load[0] = (fr_dict_autoload_t){ .proto = proto, .out = out}; |
3793 | 0 | dict_ref->load[1] = (fr_dict_autoload_t){ NULL }; |
3794 | 0 | dict_ref->dependent = talloc_strdup(dict_ref, dependent); |
3795 | 0 | if (unlikely(dict_ref->dependent == NULL)) { |
3796 | 0 | talloc_free(dict_ref); |
3797 | 0 | goto oom; |
3798 | 0 | } |
3799 | | |
3800 | 0 | ret = _fr_dict_autoload(dict_ref->load, dependent); |
3801 | 0 | if (ret < 0) { |
3802 | 0 | talloc_free(dict_ref); |
3803 | 0 | return NULL; |
3804 | 0 | } |
3805 | | |
3806 | 0 | return dict_ref; |
3807 | 0 | } |
3808 | | |
3809 | | /** Callback to automatically resolve enum values |
3810 | | * |
3811 | | * @param[in] module being loaded. |
3812 | | * @param[in] symbol An array of fr_dict_enum_autoload_t to load. |
3813 | | * @param[in] user_ctx unused. |
3814 | | * @return |
3815 | | * - 0 on success. |
3816 | | * - -1 on failure. |
3817 | | */ |
3818 | | int fr_dl_dict_enum_autoload(UNUSED dl_t const *module, void *symbol, UNUSED void *user_ctx) |
3819 | 0 | { |
3820 | 0 | if (fr_dict_enum_autoload((fr_dict_enum_autoload_t *)symbol) < 0) return -1; |
3821 | | |
3822 | 0 | return 0; |
3823 | 0 | } |
3824 | | |
3825 | | /** Callback to automatically resolve attributes and check the types are correct |
3826 | | * |
3827 | | * @param[in] module being loaded. |
3828 | | * @param[in] symbol An array of fr_dict_attr_autoload_t to load. |
3829 | | * @param[in] user_ctx unused. |
3830 | | * @return |
3831 | | * - 0 on success. |
3832 | | * - -1 on failure. |
3833 | | */ |
3834 | | int fr_dl_dict_attr_autoload(UNUSED dl_t const *module, void *symbol, UNUSED void *user_ctx) |
3835 | 0 | { |
3836 | 0 | if (fr_dict_attr_autoload((fr_dict_attr_autoload_t *)symbol) < 0) return -1; |
3837 | | |
3838 | 0 | return 0; |
3839 | 0 | } |
3840 | | |
3841 | | /** Callback to automatically load dictionaries required by modules |
3842 | | * |
3843 | | * @param[in] module being loaded. |
3844 | | * @param[in] symbol An array of fr_dict_autoload_t to load. |
3845 | | * @param[in] user_ctx unused. |
3846 | | * @return |
3847 | | * - 0 on success. |
3848 | | * - -1 on failure. |
3849 | | */ |
3850 | | int fr_dl_dict_autoload(UNUSED dl_t const *module, void *symbol, UNUSED void *user_ctx) |
3851 | 0 | { |
3852 | 0 | if (fr_dict_autoload((fr_dict_autoload_t const *)symbol) < 0) return -1; |
3853 | | |
3854 | 0 | return 0; |
3855 | 0 | } |
3856 | | |
3857 | | /** Callback to automatically free a dictionary when the module is unloaded |
3858 | | * |
3859 | | * @param[in] module being loaded. |
3860 | | * @param[in] symbol An array of fr_dict_autoload_t to load. |
3861 | | * @param[in] user_ctx unused. |
3862 | | */ |
3863 | | void fr_dl_dict_autofree(UNUSED dl_t const *module, void *symbol, UNUSED void *user_ctx) |
3864 | 0 | { |
3865 | 0 | fr_dict_autofree(((fr_dict_autoload_t *)symbol)); |
3866 | 0 | } |
3867 | | |
3868 | | /** Callback to automatically load validation routines for dictionaries. |
3869 | | * |
3870 | | * @param[in] dl the library we just loaded |
3871 | | * @param[in] symbol pointer to a fr_dict_protocol_t table |
3872 | | * @param[in] user_ctx the global context which we don't need |
3873 | | * @return |
3874 | | * - 0 on success. |
3875 | | * - -1 on failure. |
3876 | | */ |
3877 | | static int _dict_validation_onload(dl_t const *dl, void *symbol, UNUSED void *user_ctx) |
3878 | 18 | { |
3879 | 18 | fr_dict_t *dict = talloc_get_type_abort(dl->uctx, fr_dict_t); |
3880 | 18 | fr_dict_protocol_t const *proto = symbol; |
3881 | | |
3882 | | |
3883 | | /* |
3884 | | * Set the protocol-specific callbacks. |
3885 | | */ |
3886 | 18 | dict->proto = proto; |
3887 | | |
3888 | | /* |
3889 | | * @todo - just use dict->proto->foo, once we get the |
3890 | | * rest of the code cleaned up. |
3891 | | */ |
3892 | 18 | #undef COPY |
3893 | 90 | #define COPY(_x) dict->_x = proto->_x |
3894 | 18 | COPY(default_type_size); |
3895 | 18 | COPY(default_type_length); |
3896 | 18 | COPY(subtype_table); |
3897 | 18 | COPY(subtype_table_len); |
3898 | 18 | COPY(attr_valid); |
3899 | | |
3900 | 18 | return 0; |
3901 | 18 | } |
3902 | | |
3903 | | static int _dict_global_free_at_exit(void *uctx) |
3904 | 18 | { |
3905 | 18 | return talloc_free(uctx); |
3906 | 18 | } |
3907 | | |
3908 | | static int _dict_global_free(fr_dict_gctx_t *gctx) |
3909 | 18 | { |
3910 | 18 | fr_hash_iter_t iter; |
3911 | 18 | fr_dict_t *dict; |
3912 | 18 | bool still_loaded = false; |
3913 | | |
3914 | | /* |
3915 | | * Make sure this doesn't fire later and mess |
3916 | | * things up... |
3917 | | */ |
3918 | 18 | if (gctx->free_at_exit) fr_atexit_global_disarm(true, _dict_global_free_at_exit, gctx); |
3919 | | |
3920 | | /* |
3921 | | * Free up autorefs first, which will free up inter-dictionary dependencies. |
3922 | | */ |
3923 | 18 | for (dict = fr_hash_table_iter_init(gctx->protocol_by_name, &iter); |
3924 | 39 | dict; |
3925 | 21 | dict = fr_hash_table_iter_next(gctx->protocol_by_name, &iter)) { |
3926 | 21 | (void)talloc_get_type_abort(dict, fr_dict_t); |
3927 | | |
3928 | 21 | if (dict_autoref_free(dict) < 0) return -1; |
3929 | 21 | } |
3930 | | |
3931 | 18 | for (dict = fr_hash_table_iter_init(gctx->protocol_by_name, &iter); |
3932 | 39 | dict; |
3933 | 21 | dict = fr_hash_table_iter_next(gctx->protocol_by_name, &iter)) { |
3934 | 21 | (void)talloc_get_type_abort(dict, fr_dict_t); |
3935 | 21 | dict_dependent_remove(dict, "global"); /* remove our dependency */ |
3936 | | |
3937 | 21 | if (talloc_free(dict) < 0) { |
3938 | 0 | #ifndef NDEBUG |
3939 | 0 | FR_FAULT_LOG("gctx failed to free dictionary %s - %s", dict->root->name, fr_strerror()); |
3940 | 0 | #endif |
3941 | 0 | still_loaded = true; |
3942 | 0 | } |
3943 | 21 | } |
3944 | | |
3945 | | /* |
3946 | | * Free the internal dictionary as the last step, after all of the protocol dictionaries and |
3947 | | * libraries have freed their references to it. |
3948 | | */ |
3949 | 18 | if (gctx->internal) { |
3950 | 18 | dict_dependent_remove(gctx->internal, "global"); /* remove our dependency */ |
3951 | | |
3952 | 18 | if (talloc_free(gctx->internal) < 0) still_loaded = true; |
3953 | 18 | } |
3954 | | |
3955 | 18 | if (still_loaded) { |
3956 | 0 | #ifndef NDEBUG |
3957 | 0 | fr_dict_global_ctx_debug(gctx); |
3958 | 0 | #endif |
3959 | 0 | return -1; |
3960 | 0 | } |
3961 | | |
3962 | | /* |
3963 | | * Set this to NULL just in case the caller tries to use |
3964 | | * dict_global_init() again. |
3965 | | */ |
3966 | 18 | if (gctx == dict_gctx) dict_gctx = NULL; /* In case the active context isn't this one */ |
3967 | | |
3968 | 18 | return 0; |
3969 | 18 | } |
3970 | | |
3971 | | /** Initialise the global protocol hashes |
3972 | | * |
3973 | | * @note Must be called before any other dictionary functions. |
3974 | | * |
3975 | | * @param[in] ctx to allocate global resources in. |
3976 | | * @param[in] free_at_exit Install an at_exit handler to free the global ctx. |
3977 | | * This is useful when dictionaries are held by other |
3978 | | * libraries which free them using atexit handlers. |
3979 | | * @param[in] dict_dir the default location for the dictionaries. |
3980 | | * @return |
3981 | | * - A pointer to the new global context on success. |
3982 | | * - NULL on failure. |
3983 | | */ |
3984 | | fr_dict_gctx_t *fr_dict_global_ctx_init(TALLOC_CTX *ctx, bool free_at_exit, char const *dict_dir) |
3985 | 38 | { |
3986 | 38 | fr_dict_gctx_t *new_ctx; |
3987 | | |
3988 | 38 | if (!dict_dir) { |
3989 | 0 | fr_strerror_const("No dictionary location provided"); |
3990 | 0 | return NULL; |
3991 | 0 | } |
3992 | | |
3993 | 38 | new_ctx = talloc_zero(ctx, fr_dict_gctx_t); |
3994 | 38 | if (!new_ctx) { |
3995 | 0 | fr_strerror_const("Out of Memory"); |
3996 | 0 | return NULL; |
3997 | 0 | } |
3998 | 38 | new_ctx->perm_check = true; /* Check file permissions by default */ |
3999 | | |
4000 | 38 | new_ctx->protocol_by_name = fr_hash_table_alloc(new_ctx, dict_protocol_name_hash, dict_protocol_name_cmp, NULL); |
4001 | 38 | if (!new_ctx->protocol_by_name) { |
4002 | 0 | fr_strerror_const("Failed initializing protocol_by_name hash"); |
4003 | 0 | error: |
4004 | 0 | talloc_free(new_ctx); |
4005 | 0 | return NULL; |
4006 | 0 | } |
4007 | | |
4008 | 38 | new_ctx->protocol_by_num = fr_hash_table_alloc(new_ctx, dict_protocol_num_hash, dict_protocol_num_cmp, NULL); |
4009 | 38 | if (!new_ctx->protocol_by_num) { |
4010 | 0 | fr_strerror_const("Failed initializing protocol_by_num hash"); |
4011 | 0 | goto error; |
4012 | 0 | } |
4013 | | |
4014 | 38 | new_ctx->dict_dir_default = talloc_strdup(new_ctx, dict_dir); |
4015 | 38 | if (!new_ctx->dict_dir_default) goto error; |
4016 | | |
4017 | 38 | new_ctx->dict_loader = dl_loader_init(new_ctx, NULL, false, false); |
4018 | 38 | if (!new_ctx->dict_loader) goto error; |
4019 | | |
4020 | 38 | if (dl_symbol_init_cb_register(new_ctx->dict_loader, 0, "dict_protocol", |
4021 | 38 | _dict_validation_onload, NULL) < 0) goto error; |
4022 | 38 | new_ctx->free_at_exit = free_at_exit; |
4023 | | |
4024 | 38 | talloc_set_destructor(new_ctx, _dict_global_free); |
4025 | | |
4026 | 38 | if (!dict_gctx) dict_gctx = new_ctx; /* Set as the default */ |
4027 | | |
4028 | 38 | if (free_at_exit) fr_atexit_global(_dict_global_free_at_exit, new_ctx); |
4029 | | |
4030 | 38 | return new_ctx; |
4031 | 38 | } |
4032 | | |
4033 | | /** Set whether we check dictionary file permissions |
4034 | | * |
4035 | | * @param[in] gctx to alter. |
4036 | | * @param[in] enable Whether we should check file permissions as they're loaded. |
4037 | | */ |
4038 | | void fr_dict_global_ctx_perm_check(fr_dict_gctx_t *gctx, bool enable) |
4039 | 0 | { |
4040 | 0 | gctx->perm_check = enable; |
4041 | 0 | } |
4042 | | |
4043 | | /** Set a new, active, global dictionary context |
4044 | | * |
4045 | | * @param[in] gctx To set. |
4046 | | */ |
4047 | | void fr_dict_global_ctx_set(fr_dict_gctx_t const *gctx) |
4048 | 0 | { |
4049 | 0 | memcpy(&dict_gctx, &gctx, sizeof(dict_gctx)); |
4050 | 0 | } |
4051 | | |
4052 | | /** Explicitly free all data associated with a global dictionary context |
4053 | | * |
4054 | | * @note You should *NOT* ignore the return code of this function. |
4055 | | * You should use perror() or PERROR() to print out the reason |
4056 | | * why freeing failed. |
4057 | | * |
4058 | | * @param[in] gctx To set. |
4059 | | * @return |
4060 | | * - 0 on success. |
4061 | | * - -1 on failure. |
4062 | | */ |
4063 | | int fr_dict_global_ctx_free(fr_dict_gctx_t const *gctx) |
4064 | 0 | { |
4065 | 0 | if (dict_gctx == gctx) dict_gctx = NULL; |
4066 | |
|
4067 | 0 | return talloc_const_free(gctx); |
4068 | 0 | } |
4069 | | |
4070 | | /** Allow the default dict dir to be changed after initialisation |
4071 | | * |
4072 | | * @param[in] dict_dir New default dict dir to use. |
4073 | | * @return |
4074 | | * - 0 on success. |
4075 | | * - -1 on failure. |
4076 | | */ |
4077 | | int fr_dict_global_ctx_dir_set(char const *dict_dir) |
4078 | 0 | { |
4079 | 0 | if (!dict_gctx) return -1; |
4080 | | |
4081 | 0 | talloc_free(dict_gctx->dict_dir_default); /* Free previous value */ |
4082 | 0 | dict_gctx->dict_dir_default = talloc_strdup(dict_gctx, dict_dir); |
4083 | 0 | if (!dict_gctx->dict_dir_default) return -1; |
4084 | | |
4085 | 0 | return 0; |
4086 | 0 | } |
4087 | | |
4088 | | char const *fr_dict_global_ctx_dir(void) |
4089 | 39 | { |
4090 | 39 | return dict_gctx->dict_dir_default; |
4091 | 39 | } |
4092 | | |
4093 | | /** Mark all dictionaries and the global dictionary ctx as read only |
4094 | | * |
4095 | | * Any attempts to add new attributes will now fail. |
4096 | | */ |
4097 | | void fr_dict_global_ctx_read_only(void) |
4098 | 0 | { |
4099 | 0 | fr_hash_iter_t iter; |
4100 | 0 | fr_dict_t *dict; |
4101 | |
|
4102 | 0 | if (!dict_gctx) return; |
4103 | | |
4104 | | /* |
4105 | | * Set everything to read only |
4106 | | */ |
4107 | 0 | for (dict = fr_hash_table_iter_init(dict_gctx->protocol_by_num, &iter); |
4108 | 0 | dict; |
4109 | 0 | dict = fr_hash_table_iter_next(dict_gctx->protocol_by_num, &iter)) { |
4110 | 0 | dict_hash_tables_finalise(dict); |
4111 | 0 | dict->read_only = true; |
4112 | 0 | } |
4113 | |
|
4114 | 0 | dict = dict_gctx->internal; |
4115 | 0 | dict_hash_tables_finalise(dict); |
4116 | 0 | dict->read_only = true; |
4117 | 0 | dict_gctx->read_only = true; |
4118 | 0 | } |
4119 | | |
4120 | | /** Dump information about currently loaded dictionaries |
4121 | | * |
4122 | | * Intended to be called from a debugger |
4123 | | */ |
4124 | | void fr_dict_global_ctx_debug(fr_dict_gctx_t const *gctx) |
4125 | 0 | { |
4126 | 0 | fr_hash_iter_t dict_iter; |
4127 | 0 | fr_dict_t *dict; |
4128 | 0 | fr_rb_iter_inorder_t dep_iter; |
4129 | 0 | fr_dict_dependent_t *dep; |
4130 | |
|
4131 | 0 | if (gctx == NULL) gctx = dict_gctx; |
4132 | |
|
4133 | 0 | if (!gctx) { |
4134 | 0 | FR_FAULT_LOG("gctx not initialised"); |
4135 | 0 | return; |
4136 | 0 | } |
4137 | | |
4138 | 0 | FR_FAULT_LOG("gctx %p report", dict_gctx); |
4139 | 0 | for (dict = fr_hash_table_iter_init(gctx->protocol_by_num, &dict_iter); |
4140 | 0 | dict; |
4141 | 0 | dict = fr_hash_table_iter_next(gctx->protocol_by_num, &dict_iter)) { |
4142 | 0 | for (dep = fr_rb_iter_init_inorder(&dep_iter, dict->dependents); |
4143 | 0 | dep; |
4144 | 0 | dep = fr_rb_iter_next_inorder(&dep_iter)) { |
4145 | 0 | FR_FAULT_LOG("\t%s is referenced from %s count (%u)", dict->root->name, dep->dependent, dep->count); |
4146 | 0 | } |
4147 | 0 | } |
4148 | |
|
4149 | 0 | if (gctx->internal) { |
4150 | 0 | for (dep = fr_rb_iter_init_inorder(&dep_iter, gctx->internal->dependents); |
4151 | 0 | dep; |
4152 | 0 | dep = fr_rb_iter_next_inorder(&dep_iter)) { |
4153 | 0 | FR_FAULT_LOG("\t%s is referenced from %s count (%u)", gctx->internal->root->name, dep->dependent, dep->count); |
4154 | 0 | } |
4155 | 0 | } |
4156 | 0 | } |
4157 | | |
4158 | | /** Iterate protocols by name |
4159 | | * |
4160 | | */ |
4161 | | fr_dict_t *fr_dict_global_ctx_iter_init(fr_dict_global_ctx_iter_t *iter) |
4162 | 0 | { |
4163 | 0 | if (!dict_gctx) return NULL; |
4164 | | |
4165 | 0 | return fr_hash_table_iter_init(dict_gctx->protocol_by_name, iter); |
4166 | 0 | } |
4167 | | |
4168 | | fr_dict_t *fr_dict_global_ctx_iter_next(fr_dict_global_ctx_iter_t *iter) |
4169 | 0 | { |
4170 | 0 | if (!dict_gctx) return NULL; |
4171 | | |
4172 | 0 | return fr_hash_table_iter_next(dict_gctx->protocol_by_name, iter); |
4173 | 0 | } |
4174 | | |
4175 | | |
4176 | | /** Coerce to non-const |
4177 | | * |
4178 | | */ |
4179 | | fr_dict_t *fr_dict_unconst(fr_dict_t const *dict) |
4180 | 136 | { |
4181 | 136 | if (unlikely(dict->read_only)) { |
4182 | 0 | fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root(dict)->name); |
4183 | 0 | return NULL; |
4184 | 0 | } |
4185 | 136 | return UNCONST(fr_dict_t *, dict); |
4186 | 136 | } |
4187 | | |
4188 | | /** Coerce to non-const |
4189 | | * |
4190 | | */ |
4191 | | fr_dict_attr_t *fr_dict_attr_unconst(fr_dict_attr_t const *da) |
4192 | 4.74k | { |
4193 | 4.74k | fr_dict_t *dict; |
4194 | | |
4195 | 4.74k | dict = dict_by_da(da); |
4196 | 4.74k | if (unlikely(dict->read_only)) { |
4197 | 0 | fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root(dict)->name); |
4198 | 0 | return NULL; |
4199 | 0 | } |
4200 | | |
4201 | 4.74k | return UNCONST(fr_dict_attr_t *, da); |
4202 | 4.74k | } |
4203 | | |
4204 | | fr_dict_t const *fr_dict_internal(void) |
4205 | 97 | { |
4206 | 97 | if (!dict_gctx) return NULL; |
4207 | | |
4208 | 97 | return dict_gctx->internal; |
4209 | 97 | } |
4210 | | |
4211 | | /* |
4212 | | * Check for the allowed characters. |
4213 | | */ |
4214 | | ssize_t fr_dict_valid_name(char const *name, ssize_t len) |
4215 | 43.4k | { |
4216 | 43.4k | char const *p = name, *end; |
4217 | 43.4k | bool unknown = false; |
4218 | 43.4k | bool alnum = false; |
4219 | | |
4220 | 43.4k | if (len < 0) len = strlen(name); |
4221 | | |
4222 | 43.4k | if (len > FR_DICT_ATTR_MAX_NAME_LEN) { |
4223 | 0 | fr_strerror_const("Attribute name is too long"); |
4224 | 0 | return -1; |
4225 | 0 | } |
4226 | | |
4227 | 43.4k | end = p + len; |
4228 | | |
4229 | | /* |
4230 | | * Unknown attributes can have '.' in their name. |
4231 | | */ |
4232 | 43.4k | if ((len > 5) && (memcmp(name, "Attr-", 5) == 0)) unknown = true; |
4233 | | |
4234 | 682k | while (p < end) { |
4235 | 638k | if ((*p == '.') && unknown) p++; |
4236 | | |
4237 | 638k | if (!fr_dict_attr_allowed_chars[(uint8_t)*p]) { |
4238 | 0 | fr_strerror_printf("Invalid character '%pV' in attribute name \"%pV\"", |
4239 | 0 | fr_box_strvalue_len(p, 1), fr_box_strvalue_len(name, len)); |
4240 | |
|
4241 | 0 | return -(p - name); |
4242 | 0 | } |
4243 | | |
4244 | 638k | alnum |= sbuff_char_alpha_num[(uint8_t)*p]; |
4245 | | |
4246 | 638k | p++; |
4247 | 638k | } |
4248 | | |
4249 | 43.4k | if (!alnum) { |
4250 | 0 | fr_strerror_const("Invalid attribute name"); |
4251 | 0 | return -1; |
4252 | 0 | } |
4253 | | |
4254 | 43.4k | return len; |
4255 | 43.4k | } |
4256 | | |
4257 | | ssize_t fr_dict_valid_oid_str(char const *name, ssize_t len) |
4258 | 0 | { |
4259 | 0 | char const *p = name, *end; |
4260 | 0 | bool alnum = false; |
4261 | |
|
4262 | 0 | if (len < 0) len = strlen(name); |
4263 | 0 | end = p + len; |
4264 | |
|
4265 | 0 | do { |
4266 | 0 | if (!fr_dict_attr_allowed_chars[(uint8_t)*p] && (*p != '.')) { |
4267 | 0 | fr_strerror_printf("Invalid character '%pV' in oid string \"%pV\"", |
4268 | 0 | fr_box_strvalue_len(p, 1), fr_box_strvalue_len(name, len)); |
4269 | |
|
4270 | 0 | return -(p - name); |
4271 | 0 | } |
4272 | | |
4273 | 0 | alnum |= sbuff_char_alpha_num[(uint8_t)*p]; |
4274 | 0 | p++; |
4275 | 0 | } while (p < end); |
4276 | | |
4277 | 0 | if (!alnum) return 0; |
4278 | | |
4279 | 0 | return len; |
4280 | 0 | } |
4281 | | |
4282 | | /** Iterate over children of a DA. |
4283 | | * |
4284 | | * @param[in] parent the parent da to iterate over |
4285 | | * @param[in,out] prev pointer to NULL to start, otherwise pointer to the previously returned child |
4286 | | * @return |
4287 | | * - NULL for end of iteration |
4288 | | * - !NULL for a valid child. This child MUST be passed to the next loop. |
4289 | | */ |
4290 | | fr_dict_attr_t const *fr_dict_attr_iterate_children(fr_dict_attr_t const *parent, fr_dict_attr_t const **prev) |
4291 | 1.57k | { |
4292 | 1.57k | fr_dict_attr_t const * const *bin; |
4293 | 1.57k | fr_dict_attr_t const **children; |
4294 | 1.57k | fr_dict_attr_t const *ref; |
4295 | 1.57k | size_t len, i, start; |
4296 | | |
4297 | 1.57k | if (!parent || !prev) return NULL; |
4298 | | |
4299 | 1.57k | ref = fr_dict_attr_ref(parent); |
4300 | 1.57k | if (ref) parent = ref; |
4301 | | |
4302 | 1.57k | children = dict_attr_children(parent); |
4303 | 1.57k | if (!children) return NULL; |
4304 | | |
4305 | 1.57k | if (!*prev) { |
4306 | 274 | start = 0; |
4307 | | |
4308 | 1.30k | } else if ((*prev)->next) { |
4309 | | /* |
4310 | | * There are more children in this bin, return |
4311 | | * the next one. |
4312 | | */ |
4313 | 32 | return (*prev)->next; |
4314 | | |
4315 | 1.27k | } else { |
4316 | | /* |
4317 | | * Figure out which bin we were in. If it was |
4318 | | * the last one, we're done. |
4319 | | */ |
4320 | 1.27k | start = (*prev)->attr & 0xff; |
4321 | 1.27k | if (start == 255) return NULL; |
4322 | | |
4323 | | /* |
4324 | | * Start at the next bin. |
4325 | | */ |
4326 | 1.27k | start++; |
4327 | 1.27k | } |
4328 | | |
4329 | | /* |
4330 | | * Look for a non-empty bin, and return the first child |
4331 | | * from there. |
4332 | | */ |
4333 | 1.54k | len = talloc_array_length(children); |
4334 | 70.4k | for (i = start; i < len; i++) { |
4335 | 70.1k | bin = &children[i & 0xff]; |
4336 | | |
4337 | 70.1k | if (*bin) return *bin; |
4338 | 70.1k | } |
4339 | | |
4340 | 274 | return NULL; |
4341 | 1.54k | } |
4342 | | |
4343 | | /** Call the specified callback for da and then for all its children |
4344 | | * |
4345 | | */ |
4346 | | static int dict_walk(fr_dict_attr_t const *da, fr_dict_walk_t callback, void *uctx) |
4347 | 0 | { |
4348 | 0 | size_t i, len; |
4349 | 0 | fr_dict_attr_t const **children; |
4350 | |
|
4351 | 0 | children = dict_attr_children(da); |
4352 | |
|
4353 | 0 | if (fr_dict_attr_ref(da) || !children) return callback(da, uctx); |
4354 | | |
4355 | 0 | len = talloc_array_length(children); |
4356 | 0 | for (i = 0; i < len; i++) { |
4357 | 0 | int ret; |
4358 | 0 | fr_dict_attr_t const *bin; |
4359 | |
|
4360 | 0 | if (!children[i]) continue; |
4361 | | |
4362 | 0 | for (bin = children[i]; bin; bin = bin->next) { |
4363 | 0 | ret = dict_walk(bin, callback, uctx); |
4364 | 0 | if (ret < 0) return ret; |
4365 | 0 | } |
4366 | 0 | } |
4367 | | |
4368 | 0 | return 0; |
4369 | 0 | } |
4370 | | |
4371 | | int fr_dict_walk(fr_dict_attr_t const *da, fr_dict_walk_t callback, void *uctx) |
4372 | 0 | { |
4373 | 0 | return dict_walk(da, callback, uctx); |
4374 | 0 | } |
4375 | | |
4376 | | |
4377 | | void fr_dict_attr_verify(char const *file, int line, fr_dict_attr_t const *da) |
4378 | 11.7M | { |
4379 | 11.7M | int i; |
4380 | 11.7M | fr_dict_attr_t const *da_p; |
4381 | | |
4382 | 11.7M | if (!da) fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t pointer was NULL", file, line); |
4383 | | |
4384 | 11.7M | (void) talloc_get_type_abort_const(da, fr_dict_attr_t); |
4385 | | |
4386 | 11.7M | if ((!da->flags.is_root) && (da->depth == 0)) { |
4387 | 0 | fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t %s vendor: %i, attr %i: " |
4388 | 0 | "Is not root, but depth is 0", |
4389 | 0 | file, line, da->name, fr_dict_vendor_num_by_da(da), da->attr); |
4390 | 0 | } |
4391 | | |
4392 | 11.7M | if (da->depth > FR_DICT_MAX_TLV_STACK) { |
4393 | 0 | fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t %s vendor: %i, attr %i: " |
4394 | 0 | "Indicated depth (%u) greater than TLV stack depth (%u)", |
4395 | 0 | file, line, da->name, fr_dict_vendor_num_by_da(da), da->attr, |
4396 | 0 | da->depth, FR_DICT_MAX_TLV_STACK); |
4397 | 0 | } |
4398 | | |
4399 | 23.6M | for (da_p = da; da_p; da_p = da_p->next) { |
4400 | 11.9M | (void) talloc_get_type_abort_const(da_p, fr_dict_attr_t); |
4401 | 11.9M | } |
4402 | | |
4403 | 36.7M | for (i = da->depth, da_p = da; (i >= 0) && da; i--, da_p = da_p->parent) { |
4404 | 25.0M | if (i != (int)da_p->depth) { |
4405 | 0 | fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t %s vendor: %i, attr %i: " |
4406 | 0 | "Depth out of sequence, expected %i, got %u", |
4407 | 0 | file, line, da->name, fr_dict_vendor_num_by_da(da), da->attr, i, da_p->depth); |
4408 | 0 | } |
4409 | | |
4410 | 25.0M | } |
4411 | | |
4412 | 11.7M | if ((i + 1) < 0) { |
4413 | 0 | fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t top of hierarchy was not at depth 0", |
4414 | 0 | file, line); |
4415 | 0 | } |
4416 | | |
4417 | 11.7M | if (da->parent && (da->parent->type == FR_TYPE_VENDOR) && !fr_dict_attr_has_ext(da, FR_DICT_ATTR_EXT_VENDOR)) { |
4418 | 0 | fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: VSA missing 'vendor' extension", file, line); |
4419 | 0 | } |
4420 | | |
4421 | 11.7M | switch (da->type) { |
4422 | 5.67M | case FR_TYPE_STRUCTURAL: |
4423 | 5.67M | { |
4424 | 5.67M | fr_hash_table_t *ht; |
4425 | | |
4426 | 5.67M | if (da->type == FR_TYPE_GROUP) break; |
4427 | | |
4428 | 2.64M | fr_assert_msg(fr_dict_attr_has_ext(da, FR_DICT_ATTR_EXT_CHILDREN), |
4429 | 2.64M | "CONSISTENCY CHECK FAILED %s[%u]: %s missing 'children' extension", |
4430 | 2.64M | file, line, |
4431 | 2.64M | fr_type_to_str(da->type)); |
4432 | | |
4433 | 2.64M | fr_assert_msg(fr_dict_attr_has_ext(da, FR_DICT_ATTR_EXT_NAMESPACE), |
4434 | 2.64M | "CONSISTENCY CHECK FAILED %s[%u]: %s missing 'namespace' extension", |
4435 | 2.64M | file, line, |
4436 | 2.64M | fr_type_to_str(da->type)); |
4437 | | |
4438 | | /* |
4439 | | * Check the namespace hash table is ok |
4440 | | */ |
4441 | 2.64M | ht = dict_attr_namespace(da); |
4442 | 2.64M | if (unlikely(!ht)) break; |
4443 | 2.63M | fr_hash_table_verify(ht); |
4444 | 2.63M | } |
4445 | 0 | break; |
4446 | | |
4447 | 9.04M | default: |
4448 | 9.04M | break; |
4449 | 11.7M | } |
4450 | 11.7M | } |
4451 | | |
4452 | | /** See if a structural da is allowed to contain another da |
4453 | | * |
4454 | | * We have some complex rules with different structural types, |
4455 | | * different protocol dictionaries, references to other protocols, |
4456 | | * etc. |
4457 | | * |
4458 | | * @param[in] parent The parent da, must be structural |
4459 | | * @param[in] child The alleged child |
4460 | | * @return |
4461 | | * - false - the child is not allowed to be contained by the parent |
4462 | | * - true - the child is allowed to be contained by the parent |
4463 | | */ |
4464 | | bool fr_dict_attr_can_contain(fr_dict_attr_t const *parent, fr_dict_attr_t const *child) |
4465 | 411k | { |
4466 | | /* |
4467 | | * This is the common case: child is from the parent. |
4468 | | */ |
4469 | 411k | if (child->parent == parent) return true; |
4470 | | |
4471 | 80.7k | if (child->flags.is_raw) return true; /* let people do stupid things */ |
4472 | | |
4473 | | /* |
4474 | | * Child is a STRUCT which has a parent key field. The |
4475 | | * child pair nesting, though, is in the grandparent. |
4476 | | */ |
4477 | 25.7k | if (fr_dict_attr_is_key_field(child->parent)) { |
4478 | 0 | fr_assert(child->parent->parent == parent); |
4479 | |
|
4480 | 0 | return (child->parent->parent == parent); |
4481 | 0 | } |
4482 | | |
4483 | | /* |
4484 | | * Only structural types or key fields can have children. |
4485 | | */ |
4486 | 25.7k | if (!fr_type_structural[parent->type]) return false; |
4487 | | |
4488 | | /* |
4489 | | * An internal attribute can go into any other container. |
4490 | | * |
4491 | | * Any other attribute can go into an internal structural |
4492 | | * attribute, because why not? |
4493 | | */ |
4494 | 25.7k | if (dict_gctx) { |
4495 | 25.7k | if (child->dict == dict_gctx->internal) return true; |
4496 | | |
4497 | 25.7k | if (parent->dict == dict_gctx->internal) return true; |
4498 | 25.7k | } |
4499 | | |
4500 | | /* |
4501 | | * Anything can go into internal groups. |
4502 | | */ |
4503 | 25.7k | if ((parent->type == FR_TYPE_GROUP) && parent->flags.internal) return true; |
4504 | | |
4505 | | /* |
4506 | | * Protocol attributes have to be in the same dictionary. |
4507 | | * |
4508 | | * Unless they're a cross-protocol grouping attribute. |
4509 | | * In which case we check if the ref is the same. |
4510 | | */ |
4511 | 23.2k | if (child->dict != parent->dict) { |
4512 | 23.2k | fr_dict_attr_t const *ref; |
4513 | | |
4514 | 23.2k | ref = fr_dict_attr_ref(parent); |
4515 | | |
4516 | 23.2k | return (ref && (ref->dict == child->dict)); |
4517 | 23.2k | } |
4518 | | |
4519 | | /* |
4520 | | * Key fields can have children, but everyone else thinks |
4521 | | * that the struct is the parent. <sigh> |
4522 | | */ |
4523 | 0 | if ((parent->type == FR_TYPE_STRUCT) && child->parent->parent == parent) return true; |
4524 | | |
4525 | | /* |
4526 | | * We're in the same protocol dictionary, but the child |
4527 | | * isn't directly from the parent. Therefore the only |
4528 | | * type of same-protocol structure it can go into is a |
4529 | | * group. |
4530 | | */ |
4531 | 0 | return (parent->type == FR_TYPE_GROUP); |
4532 | 0 | } |
4533 | | |
4534 | | /** Return the protocol descriptor for the dictionary. |
4535 | | * |
4536 | | */ |
4537 | | fr_dict_protocol_t const *fr_dict_protocol(fr_dict_t const *dict) |
4538 | 10.7k | { |
4539 | 10.7k | return dict->proto; |
4540 | 10.7k | } |