blob: daf706667780090be93970abf6f527edbc88d48d [file] [log] [blame]
Brian Waters13d96012017-12-08 16:53:31 -06001/*********************************************************************************************************
2* Software License Agreement (BSD License) *
3* Author: Sebastien Decugis <sdecugis@freediameter.net> *
4* *
5* Copyright (c) 2015, WIDE Project and NICT *
6* All rights reserved. *
7* *
8* Redistribution and use of this software in source and binary forms, with or without modification, are *
9* permitted provided that the following conditions are met: *
10* *
11* * Redistributions of source code must retain the above *
12* copyright notice, this list of conditions and the *
13* following disclaimer. *
14* *
15* * Redistributions in binary form must reproduce the above *
16* copyright notice, this list of conditions and the *
17* following disclaimer in the documentation and/or other *
18* materials provided with the distribution. *
19* *
20* * Neither the name of the WIDE Project or NICT nor the *
21* names of its contributors may be used to endorse or *
22* promote products derived from this software without *
23* specific prior written permission of WIDE Project and *
24* NICT. *
25* *
26* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
27* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
28* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
29* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
30* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS *
31* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
32* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF *
33* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
34*********************************************************************************************************/
35
36#include "fdproto-internal.h"
37#include <inttypes.h>
38
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -050039#define ENABLE_LOCK_BYPASS 1
40#define USE_HASHLIST 1
41
42#if USE_HASHLIST
43 int initInt32HashList(void **hl);
44 void deleteInt32HashList(void *hl);
45 void deleteEntryInt32HashList(int32_t k, void *hl);
46 int insertInt32HashList(int32_t k, void *v, void *hl, void **duplicate);
47 int findInt32HashList(int32_t k, void *hl, void **result);
48
49 int initInt64HashList(void **hl);
50 void deleteInt64HashList(void *hl);
51 void deleteEntryInt64HashList(int64_t k, void *hl);
52 int insertInt64HashList(int64_t k, void *v, void *hl, void **duplicate);
53 int findInt64HashList(int64_t k, void *hl, void **result);
54
55 int initUInt32HashList(void **hl);
56 void deleteUInt32HashList(void *hl);
57 void deleteEntryUInt32HashList(uint32_t k, void *hl);
58 int insertUInt32HashList(uint32_t k, void *v, void *hl, void **duplicate);
59 int findUInt32HashList(uint32_t k, void *hl, void **result);
60
61 int initUInt64HashList(void **hl);
62 void deleteUInt64HashList(void *hl);
63 void deleteEntryUInt64HashList(uint64_t k, void *hl);
64 int insertUInt64HashList(uint64_t k, void *v, void *hl, void **duplicate);
65 int findUInt64HashList(uint64_t k, void *hl, void **result);
66
67 int initFloat32HashList(void **hl);
68 void deleteFloat32HashList(void *hl);
69 void deleteEntryFloat32HashList(float k, void *hl);
70 int insertFloat32HashList(float k, void *v, void *hl, void **duplicate);
71 int findFloat32HashList(float k, void *hl, void **result);
72
73 int initFloat64HashList(void **hl);
74 void deleteFloat64HashList(void *hl);
75 void deleteEntryFloat64HashList(double k, void *hl);
76 int insertFloat64HashList(double k, void *v, void *hl, void **duplicate);
77 int findFloat64HashList(double k, void *hl, void **result);
78
79 int initStringHashList(void **hl);
80 void deleteStringHashList(void *hl);
81 void deleteEntryStringHashList(const char *k, void *hl);
82 int insertStringHashList(const char *k, void *v, void *hl, void **duplicate);
83 int findStringHashList(const char *k, void *hl, void **result);
84#endif
85
Brian Waters13d96012017-12-08 16:53:31 -060086/* Names of the base types */
87const char * type_base_name[] = { /* must keep in sync with dict_avp_basetype */
88 "GROUPED", /* AVP_TYPE_GROUPED */
89 "OCTETSTRING", /* AVP_TYPE_OCTETSTRING */
90 "INTEGER32", /* AVP_TYPE_INTEGER32 */
91 "INTEGER64", /* AVP_TYPE_INTEGER64 */
92 "UNSIGNED32", /* AVP_TYPE_UNSIGNED32 */
93 "UNSIGNED64", /* AVP_TYPE_UNSIGNED64 */
94 "FLOAT32", /* AVP_TYPE_FLOAT32 */
95 "FLOAT64" /* AVP_TYPE_FLOAT64 */
96 };
97
98/* The number of lists in an object */
99#define NB_LISTS_PER_OBJ 3
100
101/* Some eye catchers definitions */
102#define OBJECT_EYECATCHER (0x0b13c7)
103#define DICT_EYECATCHER (0x00d1c7)
104
105/* Definition of the dictionary objects */
106struct dict_object {
107 enum dict_object_type type; /* What type of object is this? */
108 int objeyec;/* eyecatcher for this object */
109 int typeyec;/* eyecatcher for this type of object */
110 struct dictionary *dico; /* The dictionary this object belongs to */
111
112 union {
113 struct dict_vendor_data vendor; /* datastr_len = strlen(vendor_name) */
114 struct dict_application_data application; /* datastr_len = strlen(application_name) */
115 struct dict_type_data type; /* datastr_len = strlen(type_name) */
116 struct dict_enumval_data enumval; /* datastr_len = strlen(enum_name) */
117 struct dict_avp_data avp; /* datastr_len = strlen(avp_name) */
118 struct dict_cmd_data cmd; /* datastr_len = strlen(cmd_name) */
119 struct dict_rule_data rule; /* datastr_len = 0 */
120 } data; /* The data of this object */
121
122 size_t datastr_len; /* cached length of the string inside the data. Saved when the object is created. */
123
124 struct dict_object * parent; /* The parent of this object, if any */
125
126 struct fd_list list[NB_LISTS_PER_OBJ];/* used to chain objects.*/
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -0500127#ifdef USE_HASHLIST
128 void * hashlist[NB_LISTS_PER_OBJ];
129#endif
130
Brian Waters13d96012017-12-08 16:53:31 -0600131 /* More information about the lists :
132
133 - the use for each list depends on the type of object. See detail below.
134
135 - a sentinel for a list has its 'o' field cleared. (this is the criteria to detect end of a loop)
136
137 - The lists are always ordered. The criteria are described below. the functions to order them are referenced in dict_obj_info
138
139 - The dict_lock must be held for any list operation.
140
141 => VENDORS:
142 list[0]: list of the vendors, ordered by their id. The sentinel is g_dict_vendors (vendor with id 0)
143 list[1]: sentinel for the list of AVPs from this vendor, ordered by AVP code.
144 list[2]: sentinel for the list of AVPs from this vendor, ordered by AVP name (fd_os_cmp).
145
146 => APPLICATIONS:
147 list[0]: list of the applications, ordered by their id. The sentinel is g_dict_applications (application with id 0)
148 list[1]: not used
149 list[2]: not used.
150
151 => TYPES:
152 list[0]: list of the types, ordered by their names. The sentinel is g_list_types.
153 list[1]: sentinel for the type_enum list of this type, ordered by their constant name (fd_os_cmp).
154 list[2]: sentinel for the type_enum list of this type, ordered by their constant value.
155
156 => TYPE_ENUMS:
157 list[0]: list of the contants for a given type, ordered by the constant name (fd_os_cmp). Sentinel is a (list[1]) element of a TYPE object.
158 list[1]: list of the contants for a given type, ordered by the constant value. Sentinel is a (list[2]) element of a TYPE object.
159 list[2]: not used
160
161 => AVPS:
162 list[0]: list of the AVP from a given vendor, ordered by avp code. Sentinel is a list[1] element of a VENDOR object.
163 list[1]: list of the AVP from a given vendor, ordered by avp name (fd_os_cmp). Sentinel is a list[2] element of a VENDOR object.
164 list[2]: sentinel for the rule list that apply to this AVP.
165
166 => COMMANDS:
167 list[0]: list of the commands, ordered by their names (fd_os_cmp). The sentinel is g_list_cmd_name.
168 list[1]: list of the commands, ordered by their command code and 'R' flag. The sentinel is g_list_cmd_code.
169 list[2]: sentinel for the rule list that apply to this command.
170
171 => RULES:
172 list[0]: list of the rules for a given (grouped) AVP or Command, ordered by the AVP vendor & code to which they refer. sentinel is list[2] of a command or (grouped) avp.
173 list[1]: not used
174 list[2]: not used.
175
176 */
177
178 /* Sentinel for the dispatch callbacks */
179 struct fd_list disp_cbs;
180
181};
182
183/* Definition of the dictionary structure */
184struct dictionary {
185 int dict_eyec; /* Eye-catcher for the dictionary (DICT_EYECATCHER) */
186
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -0500187#if ENABLE_LOCK_BYPASS
188 int dict_bypass_lock; /* When true, don't use the dict_lock */
189#endif
Brian Waters13d96012017-12-08 16:53:31 -0600190 pthread_rwlock_t dict_lock; /* The global rwlock for the dictionary */
191
192 struct dict_object dict_vendors; /* Sentinel for the list of vendors, corresponding to vendor 0 */
193 struct dict_object dict_applications; /* Sentinel for the list of applications, corresponding to app 0 */
194 struct fd_list dict_types; /* Sentinel for the list of types */
195 struct fd_list dict_cmd_name; /* Sentinel for the list of commands, ordered by names */
196 struct fd_list dict_cmd_code; /* Sentinel for the list of commands, ordered by codes */
197
198 struct dict_object dict_cmd_error; /* Special command object for answers with the 'E' bit set */
199
200 int dict_count[DICT_TYPE_MAX + 1]; /* Number of objects of each type */
201};
202
203/* Forward declarations of dump functions */
204static DECLARE_FD_DUMP_PROTOTYPE(dump_vendor_data, void * data );
205static DECLARE_FD_DUMP_PROTOTYPE(dump_application_data, void * data );
206static DECLARE_FD_DUMP_PROTOTYPE(dump_type_data, void * data );
207 /* the dump function for enum has a different prototype since it need the datatype */
208static DECLARE_FD_DUMP_PROTOTYPE(dump_avp_data, void * data );
209static DECLARE_FD_DUMP_PROTOTYPE(dump_command_data, void * data );
210static DECLARE_FD_DUMP_PROTOTYPE(dump_rule_data, void * data );
211
212/* Forward declarations of search functions */
213static int search_vendor ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result );
214static int search_application ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result );
215static int search_type ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result );
216static int search_enumval ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result );
217static int search_avp ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result );
218static int search_cmd ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result );
219static int search_rule ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result );
220
221/* The following array contains lot of data about the different types of objects, for automated handling */
222static struct {
223 enum dict_object_type type; /* information for this type */
224 char * name; /* string describing this object, for debug */
225 size_t datasize; /* The size of the data structure */
226 int parent; /* 0: never; 1: may; 2: must */
227 enum dict_object_type parenttype; /* The type of the parent, when relevant */
228 int eyecatcher; /* A kind of signature for this object */
229 DECLARE_FD_DUMP_PROTOTYPE( (*dump_data), void * data ); /* The function to dump the data section */
230 int (*search_fct)(struct dictionary * dict, int criteria, const void * what, struct dict_object **result );; /* The function to search an object of this type */
231 int haslist[NB_LISTS_PER_OBJ]; /* Tell if this list is used */
232} dict_obj_info[] = { { 0, "(error)", 0, 0, 0, 0, NULL, NULL, {0, 0, 0} }
233
234 /* type name datasize parent parenttype
235 eyecatcher dump_data search_fct, haslist[] */
236
237 ,{ DICT_VENDOR, "VENDOR", sizeof(struct dict_vendor_data), 0, 0,
238 OBJECT_EYECATCHER + 1, dump_vendor_data, search_vendor, { 1, 0, 0 } }
239
240 ,{ DICT_APPLICATION, "APPLICATION", sizeof(struct dict_application_data), 1, DICT_VENDOR,
241 OBJECT_EYECATCHER + 2, dump_application_data, search_application, { 1, 0, 0 } }
242
243 ,{ DICT_TYPE, "TYPE", sizeof(struct dict_type_data), 1, DICT_APPLICATION,
244 OBJECT_EYECATCHER + 3, dump_type_data, search_type, { 1, 0, 0 } }
245
246 ,{ DICT_ENUMVAL, "ENUMVAL", sizeof(struct dict_enumval_data), 2, DICT_TYPE,
247 OBJECT_EYECATCHER + 4, NULL, search_enumval, { 1, 1, 0 } }
248
249 ,{ DICT_AVP, "AVP", sizeof(struct dict_avp_data), 1, DICT_TYPE,
250 OBJECT_EYECATCHER + 5, dump_avp_data, search_avp, { 1, 1, 0 } }
251
252 ,{ DICT_COMMAND, "COMMAND", sizeof(struct dict_cmd_data), 1, DICT_APPLICATION,
253 OBJECT_EYECATCHER + 6, dump_command_data, search_cmd, { 1, 1, 0 } }
254
255 ,{ DICT_RULE, "RULE", sizeof(struct dict_rule_data), 2, -1 /* special case: grouped avp or command */,
256 OBJECT_EYECATCHER + 7, dump_rule_data, search_rule, { 1, 0, 0 } }
257
258};
259
260/* Macro to verify a "type" value */
261#define CHECK_TYPE( type ) ( ((type) > 0) && ((type) <= DICT_TYPE_MAX) )
262
263/* Cast macro */
264#define _O( object ) ((struct dict_object *) (object))
265
266/* Get information line for a given object */
267#define _OBINFO(object) (dict_obj_info[CHECK_TYPE(_O(object)->type) ? _O(object)->type : 0])
268
269
270
271
272/*******************************************************************************************************/
273/*******************************************************************************************************/
274/* */
275/* Objects management */
276/* */
277/*******************************************************************************************************/
278/*******************************************************************************************************/
279
280/* Functions to manage the objects creation and destruction. */
281
282/* Duplicate a string inplace, save its length */
283#define DUP_string_len( str, plen ) { \
284 *(plen) = strlen((str)); \
285 str = os0dup( str, *(plen)); \
286}
287
288/* Initialize an object */
289static void init_object( struct dict_object * obj, enum dict_object_type type )
290{
291 int i;
292
293 TRACE_ENTRY("%p %d", obj, type);
294
295 /* Clean the object first */
296 memset ( obj, 0, sizeof(struct dict_object));
297
298 CHECK_PARAMS_DO( CHECK_TYPE(type), return );
299
300 obj->type = type;
301 obj->objeyec = OBJECT_EYECATCHER;
302 obj->typeyec = _OBINFO(obj).eyecatcher;
303
304 /* We don't initialize the data nor the parent here */
305
306 /* Now init the lists */
307 for (i=0; i<NB_LISTS_PER_OBJ; i++) {
308 if (_OBINFO(obj).haslist[i] != 0)
309 fd_list_init(&obj->list[i], obj);
310 else
311 fd_list_init(&obj->list[i], NULL);
312 }
313
314 fd_list_init(&obj->disp_cbs, NULL);
315}
316
317/* Initialize the "data" part of an object */
318static int init_object_data(struct dict_object * dest, void * source, enum dict_object_type type, int dupos)
319{
320 TRACE_ENTRY("%p %p %d", dest, source, type);
321 CHECK_PARAMS( dest && source && CHECK_TYPE(type) );
322
323 /* Generic: copy the full data structure */
324 memcpy( &dest->data, source, dict_obj_info[type].datasize );
325
326 /* Then strings must be duplicated, not copied */
327 /* This function might be simplified by always defining the "name" field as the first field of the structures, but... it's error-prone */
328 switch (type) {
329 case DICT_VENDOR:
330 DUP_string_len( dest->data.vendor.vendor_name, &dest->datastr_len );
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -0500331#if USE_HASHLIST
332 initUInt32HashList(&dest->hashlist[0]);
333 initStringHashList(&dest->hashlist[1]);
334#endif
Brian Waters13d96012017-12-08 16:53:31 -0600335 break;
336
337 case DICT_APPLICATION:
338 DUP_string_len( dest->data.application.application_name, &dest->datastr_len );
339 break;
340
341 case DICT_TYPE:
342 DUP_string_len( dest->data.type.type_name, &dest->datastr_len );
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -0500343#if USE_HASHLIST
344 switch (dest->data.type.type_base)
345 {
346 case AVP_TYPE_OCTETSTRING:
347 break;
348 case AVP_TYPE_INTEGER32:
349 initInt32HashList(&dest->hashlist[0]);
350 break;
351 case AVP_TYPE_INTEGER64:
352 initInt64HashList(&dest->hashlist[0]);
353 break;
354 case AVP_TYPE_UNSIGNED32:
355 initUInt32HashList(&dest->hashlist[0]);
356 break;
357 case AVP_TYPE_UNSIGNED64:
358 initUInt64HashList(&dest->hashlist[0]);
359 break;
360 case AVP_TYPE_FLOAT32:
361 initFloat32HashList(&dest->hashlist[0]);
362 break;
363 case AVP_TYPE_FLOAT64:
364 initFloat64HashList(&dest->hashlist[0]);
365 break;
366 case AVP_TYPE_GROUPED:
367 default:
368 break;
369 }
370 initStringHashList(&dest->hashlist[1]);
371#endif
Brian Waters13d96012017-12-08 16:53:31 -0600372 break;
373
374 case DICT_ENUMVAL:
375 DUP_string_len( dest->data.enumval.enum_name, &dest->datastr_len );
376 if (dupos) {
377 // we also need to duplicate the octetstring constant value since it is a pointer.
378 dest->data.enumval.enum_value.os.data = os0dup(
379 ((struct dict_enumval_data *)source)->enum_value.os.data,
380 ((struct dict_enumval_data *)source)->enum_value.os.len
381 );
382 }
383 break;
384
385 case DICT_AVP:
386 DUP_string_len( dest->data.avp.avp_name, &dest->datastr_len );
387 break;
388
389 case DICT_COMMAND:
390 DUP_string_len( dest->data.cmd.cmd_name, &dest->datastr_len );
391 break;
392
393 default:
394 /* Nothing to do for RULES */
395 ;
396 }
397
398 return 0;
399}
400
401/* Check that an object is valid (1: OK, 0: error) */
402static int verify_object( struct dict_object * obj )
403{
404 TRACE_ENTRY("%p", obj);
405
406 CHECK_PARAMS_DO( obj
407 && (obj->objeyec == OBJECT_EYECATCHER)
408 && CHECK_TYPE(obj->type)
409 && (obj->typeyec == dict_obj_info[obj->type].eyecatcher),
410 {
411 if (obj) {
412 TRACE_DEBUG(FULL, "Invalid object: %p, obj->objeyec: %x/%x, obj->type: %d, obj->objeyec: %x/%x, obj->typeyec: %x/%x",
413 obj,
414 obj->objeyec, OBJECT_EYECATCHER,
415 obj->type,
416 obj->objeyec, OBJECT_EYECATCHER,
417 obj->typeyec, _OBINFO(obj).eyecatcher);
418 } else {
419 TRACE_DEBUG(FULL, "Invalid object : NULL pointer");
420 }
421 return 0;
422 } );
423
424 /* The object is probably valid. */
425 return 1;
426}
427
428/* Free the data associated to an object */
429static void destroy_object_data(struct dict_object * obj)
430{
431 /* TRACE_ENTRY("%p", obj); */
432
433 switch (obj->type) {
434 case DICT_VENDOR:
435 free( obj->data.vendor.vendor_name );
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -0500436#if USE_HASHLIST
437 deleteUInt32HashList(obj->hashlist[0]);
438 deleteStringHashList(obj->hashlist[1]);
439#endif
Brian Waters13d96012017-12-08 16:53:31 -0600440 break;
441
442 case DICT_APPLICATION:
443 free( obj->data.application.application_name );
444 break;
445
446 case DICT_TYPE:
447 free( obj->data.type.type_name );
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -0500448#if USE_HASHLIST
449 switch (obj->data.type.type_base)
450 {
451 case AVP_TYPE_OCTETSTRING:
452 break;
453 case AVP_TYPE_INTEGER32:
454 deleteInt32HashList(obj->hashlist[0]);
455 break;
456 case AVP_TYPE_INTEGER64:
457 deleteInt64HashList(obj->hashlist[0]);
458 break;
459 case AVP_TYPE_UNSIGNED32:
460 deleteUInt32HashList(obj->hashlist[0]);
461 break;
462 case AVP_TYPE_UNSIGNED64:
463 deleteUInt64HashList(obj->hashlist[0]);
464 break;
465 case AVP_TYPE_FLOAT32:
466 deleteFloat32HashList(obj->hashlist[0]);
467 break;
468 case AVP_TYPE_FLOAT64:
469 deleteFloat64HashList(obj
470
471
472 ->hashlist[0]);
473 break;
474 default:
475 break;
476 }
477 deleteStringHashList(obj->hashlist[1]);
478#endif
Brian Waters13d96012017-12-08 16:53:31 -0600479 break;
480
481 case DICT_ENUMVAL:
482 free( obj->data.enumval.enum_name );
483 break;
484
485 case DICT_AVP:
486 free( obj->data.avp.avp_name );
487 break;
488
489 case DICT_COMMAND:
490 free( obj->data.cmd.cmd_name );
491 break;
492
493 default:
494 /* nothing to do */
495 ;
496 }
497}
498
499/* Forward declaration */
500static void destroy_object(struct dict_object * obj);
501
502/* Destroy all objects in a list - the lock must be held */
503static void destroy_list(struct fd_list * head)
504{
505 /* TRACE_ENTRY("%p", head); */
506
507 /* loop in the list */
508 while (!FD_IS_LIST_EMPTY(head))
509 {
510 /* When destroying the object, it is unlinked from the list */
511 destroy_object(_O(head->next->o));
512 }
513}
514
515/* Free an object and its sublists */
516static void destroy_object(struct dict_object * obj)
517{
518 int i;
519
520 /* TRACE_ENTRY("%p", obj); */
521
522 /* Update global count */
523 if (obj->dico)
524 obj->dico->dict_count[obj->type]--;
525
526 /* Mark the object as invalid */
527 obj->objeyec = 0xdead;
528
529 /* First, destroy the data associated to the object */
530 destroy_object_data(obj);
531
532 for (i=0; i<NB_LISTS_PER_OBJ; i++) {
533 if (_OBINFO(obj).haslist[i])
534 /* unlink the element from the list */
535 fd_list_unlink( &obj->list[i] );
536 else
537 /* This is either a sentinel or unused (=emtpy) list, let's destroy it */
538 destroy_list( &obj->list[i] );
539 }
540
541 /* Unlink all elements from the dispatch list; they will be freed when callback is unregistered */
542 CHECK_POSIX_DO( pthread_rwlock_wrlock(&fd_disp_lock), /* continue */ );
543 while (!FD_IS_LIST_EMPTY(&obj->disp_cbs)) {
544 fd_list_unlink( obj->disp_cbs.next );
545 }
546 CHECK_POSIX_DO( pthread_rwlock_unlock(&fd_disp_lock), /* continue */ );
547
548 /* Last, destroy the object */
549 free(obj);
550}
551
552/*******************************************************************************************************/
553/*******************************************************************************************************/
554/* */
555/* Compare functions */
556/* */
557/*******************************************************************************************************/
558/*******************************************************************************************************/
559
560/* Compare two values */
561#define ORDER_scalar( i1, i2 ) \
562 ((i1 < i2 ) ? -1 : ( i1 > i2 ? 1 : 0 ))
563
564
565/* Compare two vendor objects by their id (checks already performed) */
566static int order_vendor_by_id ( struct dict_object *o1, struct dict_object *o2 )
567{
568 TRACE_ENTRY("%p %p", o1, o2);
569
570 return ORDER_scalar( o1->data.vendor.vendor_id, o2->data.vendor.vendor_id );
571}
572
573/* Compare two application objects by their id (checks already performed) */
574static int order_appli_by_id ( struct dict_object *o1, struct dict_object *o2 )
575{
576 TRACE_ENTRY("%p %p", o1, o2);
577
578 return ORDER_scalar( o1->data.application.application_id, o2->data.application.application_id );
579}
580
581/* Compare two type objects by their name (checks already performed) */
582static int order_type_by_name ( struct dict_object *o1, struct dict_object *o2 )
583{
584 TRACE_ENTRY("%p %p", o1, o2);
585
586 return fd_os_cmp( o1->data.type.type_name, o1->datastr_len, o2->data.type.type_name, o2->datastr_len );
587}
588
589/* Compare two type_enum objects by their names (checks already performed) */
590static int order_enum_by_name ( struct dict_object *o1, struct dict_object *o2 )
591{
592 TRACE_ENTRY("%p %p", o1, o2);
593
594 return fd_os_cmp( o1->data.enumval.enum_name, o1->datastr_len, o2->data.enumval.enum_name, o2->datastr_len );
595}
596
597/* Compare two type_enum objects by their values (checks already performed) */
598static int order_enum_by_val ( struct dict_object *o1, struct dict_object *o2 )
599{
600 TRACE_ENTRY("%p %p", o1, o2);
601
602 /* The comparison function depends on the type of data */
603 switch ( o1->parent->data.type.type_base ) {
604 case AVP_TYPE_OCTETSTRING:
605 return fd_os_cmp( o1->data.enumval.enum_value.os.data, o1->data.enumval.enum_value.os.len,
606 o2->data.enumval.enum_value.os.data, o2->data.enumval.enum_value.os.len);
607
608 case AVP_TYPE_INTEGER32:
609 return ORDER_scalar( o1->data.enumval.enum_value.i32, o2->data.enumval.enum_value.i32 );
610
611 case AVP_TYPE_INTEGER64:
612 return ORDER_scalar( o1->data.enumval.enum_value.i64, o2->data.enumval.enum_value.i64 );
613
614 case AVP_TYPE_UNSIGNED32:
615 return ORDER_scalar( o1->data.enumval.enum_value.u32, o2->data.enumval.enum_value.u32 );
616
617 case AVP_TYPE_UNSIGNED64:
618 return ORDER_scalar( o1->data.enumval.enum_value.u64, o2->data.enumval.enum_value.u64 );
619
620 case AVP_TYPE_FLOAT32:
621 return ORDER_scalar( o1->data.enumval.enum_value.f32, o2->data.enumval.enum_value.f32 );
622
623 case AVP_TYPE_FLOAT64:
624 return ORDER_scalar( o1->data.enumval.enum_value.f64, o2->data.enumval.enum_value.f64 );
625
626 case AVP_TYPE_GROUPED:
627 default:
628 ASSERT(0);
629 }
630 return 0;
631}
632
633/* Compare two avp objects by their codes (checks already performed) */
634static int order_avp_by_code ( struct dict_object *o1, struct dict_object *o2 )
635{
636 TRACE_ENTRY("%p %p", o1, o2);
637
638 return ORDER_scalar( o1->data.avp.avp_code, o2->data.avp.avp_code );
639}
640
641/* Compare two avp objects by their names (checks already performed) */
642static int order_avp_by_name ( struct dict_object *o1, struct dict_object *o2 )
643{
644 TRACE_ENTRY("%p %p", o1, o2);
645
646 return fd_os_cmp( o1->data.avp.avp_name, o1->datastr_len, o2->data.avp.avp_name, o2->datastr_len );
647}
648
649/* Compare two command objects by their names (checks already performed) */
650static int order_cmd_by_name ( struct dict_object *o1, struct dict_object *o2 )
651{
652 TRACE_ENTRY("%p %p", o1, o2);
653
654 return fd_os_cmp( o1->data.cmd.cmd_name, o1->datastr_len, o2->data.cmd.cmd_name, o2->datastr_len );
655}
656
657/* Compare two command objects by their codes and flags (request or answer) (checks already performed) */
658static int order_cmd_by_codefl( struct dict_object *o1, struct dict_object *o2 )
659{
660 uint8_t fl1, fl2;
661 int cmp = 0;
662
663 TRACE_ENTRY("%p %p", o1, o2);
664
665 cmp = ORDER_scalar( o1->data.cmd.cmd_code, o2->data.cmd.cmd_code );
666 if (cmp)
667 return cmp;
668
669 /* Same command code, we must compare the value of the 'R' flag */
670 fl1 = o1->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST;
671 fl2 = o2->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST;
672
673 /* We want requests first, so we reverse the operators here */
674 return ORDER_scalar(fl2, fl1);
675
676}
677
678/* Compare two rule object by the AVP vendor & code that they refer (checks already performed) */
679static int order_rule_by_avpvc ( struct dict_object *o1, struct dict_object *o2 )
680{
681 TRACE_ENTRY("%p %p", o1, o2);
682
683 return ORDER_scalar(o1->data.rule.rule_avp->data.avp.avp_vendor, o2->data.rule.rule_avp->data.avp.avp_vendor)
684 ?: ORDER_scalar(o1->data.rule.rule_avp->data.avp.avp_code, o2->data.rule.rule_avp->data.avp.avp_code) ;
685}
686
687/*******************************************************************************************************/
688/*******************************************************************************************************/
689/* */
690/* Search functions */
691/* */
692/*******************************************************************************************************/
693/*******************************************************************************************************/
694
695/* Functions used to search for objects in the lists, according to some criteria */
696
697/* On a general note, if result is not NULL, ENOENT is not returned but *result is NULL. */
698
699/* The following macros assume that "what", "ret", "result" (variables), and "end" (label) exist
700in the local context where they are called. They are meant to be called only from the functions that follow. */
701
702/* For searchs of type "xxx_OF_xxx": children's parent or default parent */
703#define SEARCH_childs_parent( type_of_child, default_parent ) { \
704 struct dict_object *__child = (struct dict_object *) what; \
705 CHECK_PARAMS_DO( verify_object(__child) && \
706 (__child->type == (type_of_child)), \
707 { ret = EINVAL; goto end; } ); \
708 ret = 0; \
709 if (result) \
710 *result = (__child->parent ? __child->parent :(default_parent));\
711}
712
713/* For search of strings in lists. isindex= 1 if the string is the ordering key of the list */
714/* it is expected that object->datastr_len is the length of the datafield parameter */
715#define SEARCH_os0_l( str, len, sentinel, datafield, isindex ) { \
716 char * __str = (char *) (str); \
717 size_t __strlen = (size_t)(len); \
718 int __cmp; \
719 struct fd_list * __li; \
720 ret = 0; \
721 for (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) { \
722 __cmp = fd_os_cmp(__str, __strlen, \
723 _O(__li->o)->data. datafield, _O(__li->o)->datastr_len);\
724 if (__cmp == 0) { \
725 if (result) \
726 *result = _O(__li->o); \
727 goto end; \
728 } \
729 if ((isindex) && (__cmp < 0)) \
730 break; \
731 } \
732 if (result) \
733 *result = NULL; \
734 else \
735 ret = ENOENT; \
736}
737
738/* When len is not provided */
739#define SEARCH_os0( str, sentinel, datafield, isindex ) { \
740 char * _str = (char *) (str); \
741 size_t _strlen = strlen(_str); \
742 SEARCH_os0_l( _str, _strlen, sentinel, datafield, isindex ); \
743}
744
745
746/* For search of octetstrings in lists. */
747#define SEARCH_os( str, strlen, sentinel, osdatafield, isindex ) { \
748 uint8_t * __str = (uint8_t *) (str); \
749 size_t __strlen = (size_t)(strlen); \
750 int __cmp; \
751 struct fd_list * __li; \
752 ret = 0; \
753 for (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) { \
754 __cmp = fd_os_cmp(__str, __strlen, \
755 _O(__li->o)->data. osdatafield .data, \
756 _O(__li->o)->data. osdatafield .len); \
757 if (__cmp == 0) { \
758 if (result) \
759 *result = _O(__li->o); \
760 goto end; \
761 } \
762 if ((isindex) && (__cmp < 0)) \
763 break; \
764 } \
765 if (result) \
766 *result = NULL; \
767 else \
768 ret = ENOENT; \
769}
770
771/* For search of AVP name in rule lists -- the list is not ordered by AVP names! */
772#define SEARCH_ruleavpname( str, strlen, sentinel ) { \
773 char * __str = (char *) (str); \
774 size_t __strlen = (size_t) (strlen); \
775 int __cmp; \
776 struct fd_list * __li; \
777 ret = 0; \
778 for (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) { \
779 __cmp = fd_os_cmp(__str, __strlen, \
780 _O(__li->o)->data.rule.rule_avp->data.avp.avp_name, \
781 _O(__li->o)->data.rule.rule_avp->datastr_len); \
782 if (__cmp == 0) { \
783 if (result) \
784 *result = _O(__li->o); \
785 goto end; \
786 } \
787 } \
788 if (result) \
789 *result = NULL; \
790 else \
791 ret = ENOENT; \
792}
793
794/* For search of scalars in lists. isindex= 1 if the value is the ordering key of the list */
795#define SEARCH_scalar( value, sentinel, datafield, isindex, defaultobj ) { \
796 int __cmp; \
797 struct fd_list * __li; \
798 ret = 0; \
799 if ( ((defaultobj) != NULL) \
800 && (_O(defaultobj)->data. datafield == value)) { \
801 if (result) \
802 *result = _O(defaultobj); \
803 goto end; \
804 } \
805 for (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) { \
806 __cmp= ORDER_scalar(value, _O(__li->o)->data. datafield ); \
807 if (__cmp == 0) { \
808 if (result) \
809 *result = _O(__li->o); \
810 goto end; \
811 } \
812 if ((isindex) && (__cmp < 0)) \
813 break; \
814 } \
815 if (result) \
816 *result = NULL; \
817 else \
818 ret = ENOENT; \
819}
820
821/* For search of commands in lists by code and flag. R_flag_val = 0 or CMD_FLAG_REQUEST */
822#define SEARCH_codefl( value, R_flag_val, sentinel) { \
823 int __cmp; \
824 struct fd_list * __li; \
825 ret = 0; \
826 for (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) { \
827 __cmp = ORDER_scalar(value, \
828 _O(__li->o)->data.cmd.cmd_code ); \
829 if (__cmp == 0) { \
830 uint8_t __mask, __val; \
831 __mask = _O(__li->o)->data.cmd.cmd_flag_mask; \
832 __val = _O(__li->o)->data.cmd.cmd_flag_val; \
833 if ( ! (__mask & CMD_FLAG_REQUEST) ) \
834 continue; \
835 if ( ( __val & CMD_FLAG_REQUEST ) != R_flag_val ) \
836 continue; \
837 if (result) \
838 *result = _O(__li->o); \
839 goto end; \
840 } \
841 if (__cmp < 0) \
842 break; \
843 } \
844 if (result) \
845 *result = NULL; \
846 else \
847 ret = ENOENT; \
848}
849
850/* For searchs of type "xxx_OF_xxx": if the search object is sentinel list for the "what" object */
851#define SEARCH_sentinel( type_of_what, what_list_nr, sentinel_list_nr ) { \
852 struct dict_object *__what = (struct dict_object *) what; \
853 CHECK_PARAMS_DO( verify_object(__what) && \
854 (__what->type == (type_of_what)), \
855 { ret = EINVAL; goto end; } ); \
856 ret = 0; \
857 if (result) { \
858 /* this is similar to the "container_of" */ \
859 *result = (struct dict_object *)((char *)(__what->list[what_list_nr].head) - \
860 (size_t)&(((struct dict_object *)0)->list[sentinel_list_nr])); \
861 } \
862}
863
864
865static int search_vendor ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result )
866{
867 int ret = 0;
868 vendor_id_t id;
869
870 TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
871
872 switch (criteria) {
873 case VENDOR_BY_ID:
874 id = *(vendor_id_t *) what;
875 SEARCH_scalar( id, &dict->dict_vendors.list[0], vendor.vendor_id, 1, &dict->dict_vendors );
876 break;
877
878 case VENDOR_BY_NAME:
879 /* "what" is a vendor name */
880 SEARCH_os0( what, &dict->dict_vendors.list[0], vendor.vendor_name, 0);
881 break;
882
883 case VENDOR_OF_APPLICATION:
884 /* "what" should be an application object */
885 SEARCH_childs_parent( DICT_APPLICATION, &dict->dict_vendors );
886 break;
887
888 case VENDOR_OF_AVP:
889 /* "what" should be an avp object */
890 SEARCH_sentinel( DICT_AVP, 0, 1 );
891 break;
892
893 default:
894 /* Invalid criteria */
895 CHECK_PARAMS( criteria = 0 );
896 }
897end:
898 return ret;
899}
900
901static int search_application ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result )
902{
903 int ret = 0;
904 application_id_t id;
905
906 TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
907
908 switch (criteria) {
909 case APPLICATION_BY_ID:
910 id = *(application_id_t *) what;
911
912 SEARCH_scalar( id, &dict->dict_applications.list[0], application.application_id, 1, &dict->dict_applications );
913 break;
914
915 case APPLICATION_BY_NAME:
916 /* "what" is an application name */
917 SEARCH_os0( what, &dict->dict_applications.list[0], application.application_name, 0);
918 break;
919
920 case APPLICATION_OF_TYPE:
921 /* "what" should be a type object */
922 SEARCH_childs_parent( DICT_TYPE, &dict->dict_applications );
923 break;
924
925 case APPLICATION_OF_COMMAND:
926 /* "what" should be a command object */
927 SEARCH_childs_parent( DICT_COMMAND, &dict->dict_applications );
928 break;
929
930 default:
931 /* Invalid criteria */
932 CHECK_PARAMS( criteria = 0 );
933 }
934end:
935 return ret;
936}
937
938static int search_type ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result )
939{
940 int ret = 0;
941
942 TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
943
944 switch (criteria) {
945 case TYPE_BY_NAME:
946 /* "what" is a type name */
947 SEARCH_os0( what, &dict->dict_types, type.type_name, 1);
948 break;
949
950 case TYPE_OF_ENUMVAL:
951 /* "what" should be a type_enum object */
952 SEARCH_childs_parent( DICT_ENUMVAL, NULL );
953 break;
954
955 case TYPE_OF_AVP:
956 /* "what" should be an avp object */
957 SEARCH_childs_parent( DICT_AVP, NULL );
958 break;
959
960
961 default:
962 /* Invalid criteria */
963 CHECK_PARAMS( criteria = 0 );
964 }
965end:
966 return ret;
967}
968
969static int search_enumval ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result )
970{
971 int ret = 0;
972
973 TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
974
975 switch (criteria) {
976 case ENUMVAL_BY_STRUCT:
977 {
978 struct dict_object * parent = NULL;
979 struct dict_enumval_request * _what = (struct dict_enumval_request *) what;
980
981 CHECK_PARAMS( _what && ( _what->type_obj || _what->type_name ) );
982
983 if (_what->type_obj != NULL) {
984 parent = _what->type_obj;
985 CHECK_PARAMS( verify_object(parent) && (parent->type == DICT_TYPE) );
986 } else {
987 /* We received only the type name, we must find it first */
988 CHECK_FCT_DO( search_type( dict, TYPE_BY_NAME, _what->type_name, &parent ),
989 CHECK_PARAMS( 0 ) );
990 }
991
992 /* From here the "parent" object is valid */
993
994 if ( _what->search.enum_name != NULL ) {
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -0500995#if USE_HASHLIST
996 ret = findStringHashList(_what->search.enum_name, parent->hashlist[1], (void**)result);
997#else
Brian Waters13d96012017-12-08 16:53:31 -0600998 /* We are looking for this string */
999 SEARCH_os0( _what->search.enum_name, &parent->list[1], enumval.enum_name, 1 );
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001000#endif
Brian Waters13d96012017-12-08 16:53:31 -06001001 } else {
1002 /* We are looking for the value in enum_value */
1003 switch (parent->data.type.type_base) {
1004 case AVP_TYPE_OCTETSTRING:
1005 SEARCH_os( _what->search.enum_value.os.data,
1006 _what->search.enum_value.os.len,
1007 &parent->list[2],
1008 enumval.enum_value.os ,
1009 1 );
1010 break;
1011
1012 case AVP_TYPE_INTEGER32:
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001013#if USE_HASHLIST
1014 ret = findInt32HashList(_what->search.enum_value.i32, parent->hashlist[0], (void**)result);
1015#else
Brian Waters13d96012017-12-08 16:53:31 -06001016 SEARCH_scalar( _what->search.enum_value.i32,
1017 &parent->list[2],
1018 enumval.enum_value.i32,
1019 1,
1020 (struct dict_object *)NULL);
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001021#endif
Brian Waters13d96012017-12-08 16:53:31 -06001022 break;
1023
1024 case AVP_TYPE_INTEGER64:
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001025#if USE_HASHLIST
1026 ret = findInt64HashList(_what->search.enum_value.i64, parent->hashlist[0], (void**)result);
1027#else
Brian Waters13d96012017-12-08 16:53:31 -06001028 SEARCH_scalar( _what->search.enum_value.i64,
1029 &parent->list[2],
1030 enumval.enum_value.i64,
1031 1,
1032 (struct dict_object *)NULL);
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001033#endif
Brian Waters13d96012017-12-08 16:53:31 -06001034 break;
1035
1036 case AVP_TYPE_UNSIGNED32:
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001037#if USE_HASHLIST
1038 ret = findUInt32HashList(_what->search.enum_value.u32, parent->hashlist[0], (void**)result);
1039#else
Brian Waters13d96012017-12-08 16:53:31 -06001040 SEARCH_scalar( _what->search.enum_value.u32,
1041 &parent->list[2],
1042 enumval.enum_value.u32,
1043 1,
1044 (struct dict_object *)NULL);
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001045#endif
Brian Waters13d96012017-12-08 16:53:31 -06001046 break;
1047
1048 case AVP_TYPE_UNSIGNED64:
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001049#if USE_HASHLIST
1050 ret = findUInt64HashList(_what->search.enum_value.u64, parent->hashlist[0], (void**)result);
1051#else
Brian Waters13d96012017-12-08 16:53:31 -06001052 SEARCH_scalar( _what->search.enum_value.u64,
1053 &parent->list[2],
1054 enumval.enum_value.u64,
1055 1,
1056 (struct dict_object *)NULL);
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001057#endif
Brian Waters13d96012017-12-08 16:53:31 -06001058 break;
1059
1060 case AVP_TYPE_FLOAT32:
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001061#if USE_HASHLIST
1062 ret = findFloat32HashList(_what->search.enum_value.f32, parent->hashlist[0], (void**)result);
1063#else
Brian Waters13d96012017-12-08 16:53:31 -06001064 SEARCH_scalar( _what->search.enum_value.f32,
1065 &parent->list[2],
1066 enumval.enum_value.f32,
1067 1,
1068 (struct dict_object *)NULL);
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001069#endif
Brian Waters13d96012017-12-08 16:53:31 -06001070 break;
1071
1072 case AVP_TYPE_FLOAT64:
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001073#if USE_HASHLIST
1074 ret = findFloat64HashList(_what->search.enum_value.f64, parent->hashlist[0], (void**)result);
1075#else
Brian Waters13d96012017-12-08 16:53:31 -06001076 SEARCH_scalar( _what->search.enum_value.f64,
1077 &parent->list[2],
1078 enumval.enum_value.f64,
1079 1,
1080 (struct dict_object *)NULL);
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001081#endif
Brian Waters13d96012017-12-08 16:53:31 -06001082 break;
1083
1084 default:
1085 /* Invalid parent type basetype */
1086 CHECK_PARAMS( parent = NULL );
1087 }
1088 }
1089
1090 }
1091 break;
1092
1093
1094 default:
1095 /* Invalid criteria */
1096 CHECK_PARAMS( criteria = 0 );
1097 }
1098end:
1099 return ret;
1100}
1101
1102static int search_avp ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result )
1103{
1104 int ret = 0;
1105
1106 TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
1107
1108 switch (criteria) {
1109 case AVP_BY_CODE:
1110 {
1111 avp_code_t code;
1112 code = *(avp_code_t *) what;
1113
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001114#if USE_HASHLIST
1115 ret = findUInt32HashList(code, dict->dict_vendors.hashlist[0], (void**)result);
1116#else
Brian Waters13d96012017-12-08 16:53:31 -06001117 SEARCH_scalar( code, &dict->dict_vendors.list[1], avp.avp_code, 1, (struct dict_object *)NULL );
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001118#endif
Brian Waters13d96012017-12-08 16:53:31 -06001119 }
1120 break;
1121
1122 case AVP_BY_NAME:
1123 /* "what" is the AVP name, vendor 0 */
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001124#if USE_HASHLIST
1125 ret = findStringHashList((const char *)what, dict->dict_vendors.hashlist[1], (void**)result);
1126#else
Brian Waters13d96012017-12-08 16:53:31 -06001127 SEARCH_os0( what, &dict->dict_vendors.list[2], avp.avp_name, 1);
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001128#endif
Brian Waters13d96012017-12-08 16:53:31 -06001129 break;
1130
1131 case AVP_BY_CODE_AND_VENDOR:
1132 case AVP_BY_NAME_AND_VENDOR:
1133 {
1134 struct dict_avp_request * _what = (struct dict_avp_request *) what;
1135 struct dict_object * vendor = NULL;
1136
1137 CHECK_PARAMS( (criteria != AVP_BY_NAME_AND_VENDOR) || _what->avp_name );
1138
1139 /* Now look for the vendor first */
1140 CHECK_FCT( search_vendor( dict, VENDOR_BY_ID, &_what->avp_vendor, &vendor ) );
1141 if (vendor == NULL) {
1142 if (result)
1143 *result = NULL;
1144 else
1145 ret = ENOENT;
1146 goto end;
1147 }
1148
1149 /* We now have our vendor = head of the appropriate avp list */
1150 if (criteria == AVP_BY_NAME_AND_VENDOR) {
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001151#if USE_HASHLIST
1152 ret = findStringHashList(_what->avp_name, vendor->hashlist[1], (void**)result);
1153#else
Brian Waters13d96012017-12-08 16:53:31 -06001154 SEARCH_os0( _what->avp_name, &vendor->list[2], avp.avp_name, 1);
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001155#endif
Brian Waters13d96012017-12-08 16:53:31 -06001156 } else {
1157 /* AVP_BY_CODE_AND_VENDOR */
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001158#if USE_HASHLIST
1159 ret = findUInt32HashList(_what->avp_code, vendor->hashlist[0], (void**)result);
1160#else
Brian Waters13d96012017-12-08 16:53:31 -06001161 SEARCH_scalar( _what->avp_code, &vendor->list[1], avp.avp_code, 1, (struct dict_object *)NULL );
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001162#endif
Brian Waters13d96012017-12-08 16:53:31 -06001163 }
1164 }
1165 break;
1166
1167 case AVP_BY_STRUCT:
1168 {
1169 struct dict_avp_request_ex * _what = (struct dict_avp_request_ex *) what;
1170 struct dict_object * vendor = NULL;
1171
1172 CHECK_PARAMS( _what->avp_vendor.vendor || _what->avp_vendor.vendor_id || _what->avp_vendor.vendor_name );
1173 CHECK_PARAMS( _what->avp_data.avp_code || _what->avp_data.avp_name );
1174
1175 /* Now look for the vendor first */
1176 if (_what->avp_vendor.vendor) {
1177 CHECK_PARAMS( ! _what->avp_vendor.vendor_id && ! _what->avp_vendor.vendor_name );
1178 vendor = _what->avp_vendor.vendor;
1179 } else if (_what->avp_vendor.vendor_id) {
1180 CHECK_PARAMS( ! _what->avp_vendor.vendor_name );
1181 CHECK_FCT( search_vendor( dict, VENDOR_BY_ID, &_what->avp_vendor.vendor_id, &vendor ) );
1182 } else {
1183 CHECK_FCT( search_vendor( dict, VENDOR_BY_NAME, _what->avp_vendor.vendor_name, &vendor ) );
1184 }
1185
1186 if (vendor == NULL) {
1187 if (result)
1188 *result = NULL;
1189 else
1190 ret = ENOENT;
1191 goto end;
1192 }
1193
1194 /* We now have our vendor = head of the appropriate avp list */
1195 if (_what->avp_data.avp_code) {
1196 CHECK_PARAMS( ! _what->avp_data.avp_name );
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001197#if USE_HASHLIST
1198 ret = findUInt32HashList(_what->avp_data.avp_code, vendor->hashlist[0], (void**)result);
1199#else
Brian Waters13d96012017-12-08 16:53:31 -06001200 SEARCH_scalar( _what->avp_data.avp_code, &vendor->list[1], avp.avp_code, 1, (struct dict_object *)NULL );
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001201#endif
Brian Waters13d96012017-12-08 16:53:31 -06001202 } else {
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001203#if USE_HASHLIST
1204 ret = findStringHashList(_what->avp_data.avp_name, vendor->hashlist[1], (void**)result);
1205#else
Brian Waters13d96012017-12-08 16:53:31 -06001206 SEARCH_os0( _what->avp_data.avp_name, &vendor->list[2], avp.avp_name, 1);
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001207#endif
Brian Waters13d96012017-12-08 16:53:31 -06001208 }
1209 }
1210 break;
1211
1212 case AVP_BY_NAME_ALL_VENDORS:
1213 {
1214 struct fd_list * li;
1215 size_t wl = strlen((char *)what);
1216
1217 /* First, search for vendor 0 */
1218 SEARCH_os0_l( what, wl, &dict->dict_vendors.list[2], avp.avp_name, 1);
1219
1220 /* If not found, loop for all vendors, until found */
1221 for (li = dict->dict_vendors.list[0].next; li != &dict->dict_vendors.list[0]; li = li->next) {
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001222#if USE_HASHLIST
1223 ret = findStringHashList(what, _O(li->o)->hashlist[1], (void**)result);
1224 if (ret == 0 && *result)
1225 goto end;
1226#else
Brian Waters13d96012017-12-08 16:53:31 -06001227 SEARCH_os0_l( what, wl, &_O(li->o)->list[2], avp.avp_name, 1);
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001228#endif
Brian Waters13d96012017-12-08 16:53:31 -06001229 }
1230 }
1231 break;
1232
1233 default:
1234 /* Invalid criteria */
1235 CHECK_PARAMS( criteria = 0 );
1236 }
1237end:
1238 return ret;
1239}
1240
1241static int search_cmd ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result )
1242{
1243 int ret = 0;
1244
1245 TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
1246
1247 switch (criteria) {
1248 case CMD_BY_NAME:
1249 /* "what" is a command name */
1250 SEARCH_os0( what, &dict->dict_cmd_name, cmd.cmd_name, 1);
1251 break;
1252
1253 case CMD_BY_CODE_R:
1254 case CMD_BY_CODE_A:
1255 {
1256 command_code_t code;
1257 uint8_t searchfl = 0;
1258
1259 /* The command code that we are searching */
1260 code = *(command_code_t *) what;
1261
1262 /* The flag (request or answer) of the command we are searching */
1263 if (criteria == CMD_BY_CODE_R) {
1264 searchfl = CMD_FLAG_REQUEST;
1265 }
1266
1267 /* perform the search */
1268 SEARCH_codefl( code, searchfl, &dict->dict_cmd_code );
1269 }
1270 break;
1271
1272 case CMD_ANSWER:
1273 {
1274 /* "what" is a command object of type "request" */
1275 struct dict_object * req = (struct dict_object *) what;
1276 struct dict_object * ans = NULL;
1277
1278 CHECK_PARAMS( verify_object(req)
1279 && (req->type == DICT_COMMAND)
1280 && (req->data.cmd.cmd_flag_mask & CMD_FLAG_REQUEST)
1281 && (req->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST) );
1282
1283 /* The answer is supposed to be the next element in the list, if it exists */
1284 ans = req->list[1].next->o;
1285 if ( ans == NULL ) {
1286 TRACE_DEBUG( FULL, "the request was the last element in the list" );
1287 ret = ENOENT;
1288 goto end;
1289 }
1290
1291 /* Now check that the ans element is really the correct one */
1292 if ( (ans->data.cmd.cmd_code != req->data.cmd.cmd_code)
1293 || (!(ans->data.cmd.cmd_flag_mask & CMD_FLAG_REQUEST))
1294 || ( ans->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST ) ) {
1295 TRACE_DEBUG( FULL, "the answer does not follow the request in the list" );
1296 ret = ENOENT;
1297 goto end;
1298 }
1299
1300 if (result)
1301 *result = ans;
1302 ret = 0;
1303 }
1304 break;
1305
1306 default:
1307 /* Invalid criteria */
1308 CHECK_PARAMS( criteria = 0 );
1309 }
1310end:
1311 return ret;
1312}
1313
1314static int search_rule ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result )
1315{
1316 int ret = 0;
1317
1318 TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
1319
1320 switch (criteria) {
1321 case RULE_BY_AVP_AND_PARENT:
1322 {
1323 struct dict_object * parent = NULL;
1324 struct dict_object * avp = NULL;
1325 struct dict_rule_request * _what = (struct dict_rule_request *) what;
1326
1327 CHECK_PARAMS( _what
1328 && (parent = _what->rule_parent)
1329 && (avp = _what->rule_avp ) );
1330
1331 CHECK_PARAMS( verify_object(parent)
1332 && ((parent->type == DICT_COMMAND)
1333 || ((parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED))) );
1334
1335 CHECK_PARAMS( verify_object(avp) && (avp->type == DICT_AVP) );
1336
1337 /* Perform the search */
1338 SEARCH_ruleavpname( avp->data.avp.avp_name, avp->datastr_len, &parent->list[2]);
1339
1340 }
1341 break;
1342
1343 default:
1344 /* Invalid criteria */
1345 CHECK_PARAMS( criteria = 0 );
1346 }
1347end:
1348 return ret;
1349}
1350
1351/*******************************************************************************************************/
1352/*******************************************************************************************************/
1353/* */
1354/* Dump / debug functions */
1355/* */
1356/*******************************************************************************************************/
1357/*******************************************************************************************************/
1358/* The following functions are used to debug the module, and allow to print out the content of the dictionary */
1359static DECLARE_FD_DUMP_PROTOTYPE(dump_vendor_data, void * data )
1360{
1361 struct dict_vendor_data * vendor = (struct dict_vendor_data *)data;
1362
1363 return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: %-6u \"%s\"", vendor->vendor_id, vendor->vendor_name);
1364}
1365static DECLARE_FD_DUMP_PROTOTYPE(dump_application_data, void * data )
1366{
1367 struct dict_application_data * appli = (struct dict_application_data *) data;
1368 return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: %-6u \"%s\"", appli->application_id, appli->application_name);
1369}
1370static DECLARE_FD_DUMP_PROTOTYPE(dump_type_data, void * data )
1371{
1372 struct dict_type_data * type = ( struct dict_type_data * ) data;
1373
1374 return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: %-12s \"%s\"",
1375 type_base_name[type->type_base],
1376 type->type_name);
1377}
1378static DECLARE_FD_DUMP_PROTOTYPE(dump_enumval_data, struct dict_enumval_data * enumval, enum dict_avp_basetype type )
1379{
1380 const int LEN_MAX = 20;
1381 CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "data: (%-12s) \"%s\" -> ", type_base_name[type], enumval->enum_name), return NULL);
1382 switch (type) {
1383 case AVP_TYPE_OCTETSTRING:
1384 {
1385 int i, n=LEN_MAX;
1386 if (enumval->enum_value.os.len < LEN_MAX)
1387 n = enumval->enum_value.os.len;
1388 for (i=0; i < n; i++)
1389 CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "0x%2hhX/'%c' ", enumval->enum_value.os.data[i], ASCII(enumval->enum_value.os.data[i])), return NULL);
1390 if (n == LEN_MAX)
1391 CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "..."), return NULL);
1392 }
1393 break;
1394
1395 case AVP_TYPE_INTEGER32:
1396 CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "%i", enumval->enum_value.i32), return NULL);
1397 break;
1398
1399 case AVP_TYPE_INTEGER64:
1400 CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "%"PRId64, enumval->enum_value.i64), return NULL);
1401 break;
1402
1403 case AVP_TYPE_UNSIGNED32:
1404 CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "%u", enumval->enum_value.u32), return NULL);
1405 break;
1406
1407 case AVP_TYPE_UNSIGNED64:
1408 CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "%"PRIu64, enumval->enum_value.u64), return NULL);
1409 break;
1410
1411 case AVP_TYPE_FLOAT32:
1412 CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "%f", enumval->enum_value.f32), return NULL);
1413 break;
1414
1415 case AVP_TYPE_FLOAT64:
1416 CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "%g", enumval->enum_value.f64), return NULL);
1417 break;
1418
1419 default:
1420 CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "??? (ERROR unknown type %d)", type), return NULL);
1421 }
1422 return *buf;
1423}
1424static DECLARE_FD_DUMP_PROTOTYPE(dump_avp_data, void * data )
1425{
1426 struct dict_avp_data * avp = (struct dict_avp_data * ) data;
1427 return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: v/m:" DUMP_AVPFL_str "/" DUMP_AVPFL_str ", %12s, %-6u \"%s\"",
1428 DUMP_AVPFL_val(avp->avp_flag_val),
1429 DUMP_AVPFL_val(avp->avp_flag_mask),
1430 type_base_name[avp->avp_basetype],
1431 avp->avp_code,
1432 avp->avp_name );
1433}
1434static DECLARE_FD_DUMP_PROTOTYPE(dump_command_data, void * data )
1435{
1436 struct dict_cmd_data * cmd = (struct dict_cmd_data *) data;
1437 return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: v/m:" DUMP_CMDFL_str "/" DUMP_CMDFL_str ", %-6u \"%s\"",
1438 DUMP_CMDFL_val(cmd->cmd_flag_val), DUMP_CMDFL_val(cmd->cmd_flag_mask), cmd->cmd_code, cmd->cmd_name);
1439}
1440static DECLARE_FD_DUMP_PROTOTYPE(dump_rule_data, void * data )
1441{
1442 struct dict_rule_data * rule = (struct dict_rule_data * )data;
1443 return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: pos:%d ord:%d m/M:%2d/%2d avp:\"%s\"",
1444 rule->rule_position,
1445 rule->rule_order,
1446 rule->rule_min,
1447 rule->rule_max,
1448 rule->rule_avp->data.avp.avp_name);
1449}
1450
1451static DECLARE_FD_DUMP_PROTOTYPE(dump_object, struct dict_object * obj, int parents, int depth, int indent );
1452
1453static DECLARE_FD_DUMP_PROTOTYPE(dump_list, struct fd_list * sentinel, int parents, int depth, int indent )
1454{
1455 struct fd_list * li = sentinel;
1456 /* We don't lock here, the caller must have taken the dictionary lock for reading already */
1457 if (FD_IS_LIST_EMPTY(sentinel)) {
1458 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n%*s{empty list}", indent, ""), return NULL);
1459 } else {
1460 while (li->next != sentinel)
1461 {
1462 li = li->next;
1463 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL);
1464 CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, _O(li->o), parents, depth, indent ), return NULL);
1465 }
1466 }
1467 return *buf;
1468}
1469
1470static DECLARE_FD_DUMP_PROTOTYPE(dump_object, struct dict_object * obj, int parents, int depth, int indent )
1471{
1472 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%*s{dictobj}(@%p): ", indent, "", obj), return NULL);
1473
1474 if (!verify_object(obj)) {
1475 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID/NULL"), return NULL);
1476 return *buf;
1477 }
1478
1479 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%s p:%p ",
1480 _OBINFO(obj).name,
1481 obj->parent), return NULL);
1482
1483 if (obj->type == DICT_ENUMVAL) {
1484 CHECK_MALLOC_DO( dump_enumval_data ( FD_DUMP_STD_PARAMS, &obj->data.enumval, obj->parent->data.type.type_base ), return NULL);
1485 } else {
1486 CHECK_MALLOC_DO( _OBINFO(obj).dump_data(FD_DUMP_STD_PARAMS, &obj->data), return NULL);
1487 }
1488
1489 if (parents) {
1490 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n%*sparent:", indent + 1, ""), return NULL);
1491 CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, obj->parent, parents-1, 0, 0 ), return NULL);
1492 }
1493
1494 if (depth) {
1495 int i;
1496 for (i=0; i<NB_LISTS_PER_OBJ; i++) {
1497 if ((obj->list[i].o == NULL) && (obj->list[i].next != &obj->list[i])) {
1498 CHECK_MALLOC_DO( dump_list(FD_DUMP_STD_PARAMS, &obj->list[i], 0, depth - 1, indent + 2), return NULL);
1499 break; /* we get duplicate information sorted by another criteria otherwise, which is not very useful */
1500 }
1501 }
1502 }
1503
1504 return *buf;
1505}
1506
1507DECLARE_FD_DUMP_PROTOTYPE(fd_dict_dump_object, struct dict_object * obj)
1508{
1509 FD_DUMP_HANDLE_OFFSET();
1510
1511 CHECK_MALLOC_DO( dump_object(FD_DUMP_STD_PARAMS, obj, 1, 2, 0), return NULL);
1512
1513 return *buf;
1514}
1515
1516DECLARE_FD_DUMP_PROTOTYPE(fd_dict_dump, struct dictionary * dict)
1517{
1518 int i;
1519 struct fd_list * li;
1520
1521 FD_DUMP_HANDLE_OFFSET();
1522
1523 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{dictionary}(@%p): ", dict), return NULL);
1524
1525 if ((dict == NULL) || (dict->dict_eyec != DICT_EYECATCHER)) {
1526 return fd_dump_extend(FD_DUMP_STD_PARAMS, "INVALID/NULL");
1527 }
1528
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001529#if ENABLE_LOCK_BYPASS
1530 if (!dict->dict_bypass_lock)
1531#endif
Brian Waters13d96012017-12-08 16:53:31 -06001532 CHECK_POSIX_DO( pthread_rwlock_rdlock( &dict->dict_lock ), /* ignore */ );
1533
1534 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict(%p) : VENDORS / AVP / RULES}\n", dict), goto error);
1535 CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, &dict->dict_vendors, 0, 3, 3 ), goto error);
1536 for (li = dict->dict_vendors.list[0].next; li != &dict->dict_vendors.list[0]; li = li->next) {
1537 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL);
1538 CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, li->o, 0, 3, 3 ), goto error);
1539 }
1540
1541 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict(%p) : APPLICATIONS}\n", dict), goto error);
1542 CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, &dict->dict_applications, 0, 1, 3 ), goto error);
1543 for (li = dict->dict_applications.list[0].next; li != &dict->dict_applications.list[0]; li = li->next) {
1544 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL);
1545 CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, li->o, 0, 1, 3 ), goto error);
1546 }
1547
1548 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict(%p) : TYPES / ENUMVAL}", dict), goto error);
1549 CHECK_MALLOC_DO( dump_list(FD_DUMP_STD_PARAMS, &dict->dict_types, 0, 2, 3 ), goto error);
1550
1551 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict(%p) : COMMANDS / RULES}", dict), goto error);
1552 CHECK_MALLOC_DO( dump_list(FD_DUMP_STD_PARAMS, &dict->dict_cmd_code, 0, 0, 3 ), goto error);
1553
1554 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict(%p) : statistics}", dict), goto error);
1555 for (i=1; i<=DICT_TYPE_MAX; i++)
1556 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n %5d: %s", dict->dict_count[i], dict_obj_info[i].name), goto error);
1557
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001558#if ENABLE_LOCK_BYPASS
1559 if (!dict->dict_bypass_lock)
1560#endif
Brian Waters13d96012017-12-08 16:53:31 -06001561 CHECK_POSIX_DO( pthread_rwlock_unlock( &dict->dict_lock ), /* ignore */ );
1562 return *buf;
1563error:
1564 /* Free the rwlock */
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001565#if ENABLE_LOCK_BYPASS
1566 if (!dict->dict_bypass_lock)
1567#endif
Brian Waters13d96012017-12-08 16:53:31 -06001568 CHECK_POSIX_DO( pthread_rwlock_unlock( &dict->dict_lock ), /* ignore */ );
1569 return NULL;
1570}
1571
1572/**************************** Dump AVP values ********************************/
1573
1574/* Default dump functions */
1575static DECLARE_FD_DUMP_PROTOTYPE(dump_val_os, union avp_value * value)
1576{
1577 int i;
1578
1579 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "<"), return NULL);
1580 for (i = 0; i < value->os.len; i++) {
1581 if (i == 1024) { /* Dump only up to 1024 bytes of the buffer */
1582 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "[...] (len=%zd)", value->os.len), return NULL);
1583 break;
1584 }
1585 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%s%02hhX", (i==0 ? "" : " "), value->os.data[i]), return NULL);
1586 }
1587 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, ">"), return NULL);
1588 return *buf;
1589}
1590
1591static DECLARE_FD_DUMP_PROTOTYPE(dump_val_i32, union avp_value * value)
1592{
1593 return fd_dump_extend( FD_DUMP_STD_PARAMS, "%i (0x%x)", value->i32, value->i32);
1594}
1595
1596static DECLARE_FD_DUMP_PROTOTYPE(dump_val_i64, union avp_value * value)
1597{
1598 return fd_dump_extend( FD_DUMP_STD_PARAMS, "%" PRId64 " (0x%" PRIx64 ")", value->i64, value->i64);
1599}
1600
1601static DECLARE_FD_DUMP_PROTOTYPE(dump_val_u32, union avp_value * value)
1602{
1603 return fd_dump_extend( FD_DUMP_STD_PARAMS, "%u (0x%x)", value->u32, value->u32);
1604}
1605
1606static DECLARE_FD_DUMP_PROTOTYPE(dump_val_u64, union avp_value * value)
1607{
1608 return fd_dump_extend( FD_DUMP_STD_PARAMS, "%" PRIu64 " (0x%" PRIx64 ")", value->u64, value->u64);
1609}
1610
1611static DECLARE_FD_DUMP_PROTOTYPE(dump_val_f32, union avp_value * value)
1612{
1613 return fd_dump_extend( FD_DUMP_STD_PARAMS, "%f", value->f32);
1614}
1615
1616static DECLARE_FD_DUMP_PROTOTYPE(dump_val_f64, union avp_value * value)
1617{
1618 return fd_dump_extend( FD_DUMP_STD_PARAMS, "%g", value->f64);
1619}
1620
1621/* Get the dump function for basic dict_avp_basetype */
1622static DECLARE_FD_DUMP_PROTOTYPE((*get_default_dump_val_cb(enum dict_avp_basetype datatype)), union avp_value *)
1623{
1624 switch (datatype) {
1625 case AVP_TYPE_OCTETSTRING:
1626 return &dump_val_os;
1627
1628 case AVP_TYPE_INTEGER32:
1629 return &dump_val_i32;
1630
1631 case AVP_TYPE_INTEGER64:
1632 return &dump_val_i64;
1633
1634 case AVP_TYPE_UNSIGNED32:
1635 return &dump_val_u32;
1636
1637 case AVP_TYPE_UNSIGNED64:
1638 return &dump_val_u64;
1639
1640 case AVP_TYPE_FLOAT32:
1641 return &dump_val_f32;
1642
1643 case AVP_TYPE_FLOAT64:
1644 return &dump_val_f64;
1645
1646 case AVP_TYPE_GROUPED:
1647 TRACE_DEBUG(FULL, "error: grouped AVP with a value!");
1648 }
1649 return NULL;
1650}
1651
1652/* indent inside an object (duplicate from messages.c) */
1653#define INOBJHDR "%*s "
1654#define INOBJHDRVAL indent<0 ? 1 : indent, indent<0 ? "-" : "|"
1655
1656typedef DECLARE_FD_DUMP_PROTOTYPE((*dump_val_cb_t), union avp_value *);
1657
1658/* Formatter for the AVP value dump line */
1659static DECLARE_FD_DUMP_PROTOTYPE(dump_avp_val, union avp_value *avp_value,
1660 dump_val_cb_t def_dump_val_cb,
1661 dump_val_cb_t dump_val_cb,
1662 enum dict_avp_basetype datatype,
1663 char * type_name,
1664 char * const_name,
1665 int indent,
1666 int header)
1667{
1668 if (header) {
1669 /* Header for all AVP values dumps: */
1670 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, INOBJHDR "value ", INOBJHDRVAL), return NULL);
1671
1672 /* If the type is provided, write it */
1673 if (type_name) {
1674 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "t: '%s' ", type_name), return NULL);
1675 }
1676
1677 /* Always give the base datatype anyway */
1678 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(%s) ", type_base_name[datatype]), return NULL);
1679
1680 /* Now, the value */
1681 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "v: "), return NULL);
1682 }
1683 if (const_name) {
1684 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s' (", const_name), return NULL);
1685 }
1686 if (dump_val_cb) {
1687 CHECK_MALLOC_DO( (*dump_val_cb)( FD_DUMP_STD_PARAMS, avp_value), fd_dump_extend( FD_DUMP_STD_PARAMS, "(dump failed)"));
1688 } else {
1689 CHECK_MALLOC_DO( (*def_dump_val_cb)( FD_DUMP_STD_PARAMS, avp_value), return NULL);
1690 }
1691 if (const_name) {
1692 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, ")"), return NULL);
1693 }
1694
1695 /* Done! */
1696 return *buf;
1697}
1698
1699/* Dump the value of an AVP of known type into the returned str */
1700DECLARE_FD_DUMP_PROTOTYPE(fd_dict_dump_avp_value, union avp_value *avp_value, struct dict_object * model, int indent, int header)
1701{
1702 DECLARE_FD_DUMP_PROTOTYPE((*dump_val_cb), union avp_value *avp_value) = NULL;
1703 struct dict_object * type = NULL;
1704 char * type_name = NULL;
1705 char * const_name = NULL;
1706
1707 FD_DUMP_HANDLE_OFFSET();
1708
1709 /* Handle invalid parameters */
1710 if (!avp_value) {
1711 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(avp value not set)"), return NULL);
1712 return *buf;
1713 }
1714
1715 if (!model) {
1716 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(model not set)"), return NULL);
1717 return *buf;
1718 }
1719
1720 if (! ( verify_object(model) && (model->type == DICT_AVP) )) {
1721 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(invalid model)"), return NULL);
1722 return *buf;
1723 }
1724
1725 /* Get the type definition of this AVP */
1726 type = model->parent;
1727 if (type) {
1728 struct dict_enumval_request request;
1729 struct dict_object * enumval = NULL;
1730
1731 type_name = type->data.type.type_name;
1732
1733 /* overwrite the dump function ? */
1734 if (type->data.type.type_dump)
1735 dump_val_cb = type->data.type.type_dump;
1736
1737 /* Now check if the AVP value matches a constant */
1738 memset(&request, 0, sizeof(request));
1739 request.type_obj = type;
1740 memcpy(&request.search.enum_value, avp_value, sizeof(union avp_value));
1741 /* bypass checks */
1742 if ((search_enumval( type->dico, ENUMVAL_BY_STRUCT, &request, &enumval ) == 0) && (enumval)) {
1743 /* We found a constant, get its name */
1744 const_name = enumval->data.enumval.enum_name;
1745 }
1746 }
1747
1748 /* And finally, dump the value */
1749 CHECK_MALLOC_DO( dump_avp_val(FD_DUMP_STD_PARAMS, avp_value, get_default_dump_val_cb(model->data.avp.avp_basetype), dump_val_cb, model->data.avp.avp_basetype, type_name, const_name, indent, header), return NULL );
1750 return *buf;
1751}
1752
1753/*******************************************************************************************************/
1754/*******************************************************************************************************/
1755/* */
1756/* Exported functions */
1757/* */
1758/*******************************************************************************************************/
1759/*******************************************************************************************************/
1760
1761/* These are the functions exported outside libfreeDiameter. */
1762
1763/* Get the data associated to an object */
1764int fd_dict_gettype ( struct dict_object * object, enum dict_object_type * type)
1765{
1766 TRACE_ENTRY("%p %p", object, type);
1767
1768 CHECK_PARAMS( type && verify_object(object) );
1769
1770 /* Copy the value and return */
1771 *type = object->type;
1772 return 0;
1773}
1774
1775int fd_dict_getdict ( struct dict_object * object, struct dictionary ** dict)
1776{
1777 TRACE_ENTRY("%p %p", object, dict);
1778
1779 CHECK_PARAMS( dict && verify_object(object) );
1780
1781 /* Copy the value and return */
1782 *dict = object->dico;
1783 return 0;
1784}
1785
1786
1787/* Get the data associated to an object */
1788int fd_dict_getval ( struct dict_object * object, void * val)
1789{
1790 TRACE_ENTRY("%p %p", object, val);
1791
1792 CHECK_PARAMS( val && verify_object(object) );
1793
1794 /* Copy the value and return */
1795 memcpy(val, &object->data, _OBINFO(object).datasize);;
1796 return 0;
1797}
1798
1799/* Add a new object in the dictionary */
1800int fd_dict_new ( struct dictionary * dict, enum dict_object_type type, void * data, struct dict_object * parent, struct dict_object **ref )
1801{
1802 int ret = 0;
1803 int dupos = 0;
1804 struct dict_object * new = NULL;
1805 struct dict_object * vendor = NULL;
1806 struct dict_object * locref = NULL;
1807
1808 TRACE_ENTRY("%p %d(%s) %p %p %p", dict, type, dict_obj_info[CHECK_TYPE(type) ? type : 0].name, data, parent, ref);
1809
1810 /* Check parameters */
1811 CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && CHECK_TYPE(type) && data );
1812
1813 /* Check the "parent" parameter */
1814 switch (dict_obj_info[type].parent) {
1815 case 0: /* parent is forbidden */
1816 CHECK_PARAMS_DO( parent == NULL, goto error_param );
1817
1818 case 1: /* parent is optional */
1819 if (parent == NULL)
1820 break;
1821
1822 case 2: /* parent is mandatory */
1823 CHECK_PARAMS_DO( verify_object(parent), goto error_param );
1824
1825 if (type == DICT_RULE ) { /* Special case : grouped AVP or Command parents are allowed */
1826 CHECK_PARAMS_DO( (parent->type == DICT_COMMAND )
1827 || ( (parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED ) ), goto error_param );
1828 } else {
1829 CHECK_PARAMS_DO( parent->type == dict_obj_info[type].parenttype, goto error_param );
1830 }
1831 }
1832
1833 /* For AVP object, we must also check that the "vendor" referenced exists */
1834 if (type == DICT_AVP) {
1835 CHECK_FCT_DO( fd_dict_search( dict, DICT_VENDOR, VENDOR_BY_ID, &(((struct dict_avp_data *)data)->avp_vendor), (void*)&vendor, ENOENT ),
1836 { TRACE_DEBUG(INFO, "Unable to find vendor '%d' referenced in the AVP data", ((struct dict_avp_data *)data)->avp_vendor); goto error_param; } );
1837
1838 /* Also check if a parent is provided, that the type are the same */
1839 if (parent) {
1840 CHECK_PARAMS_DO( parent->data.type.type_base == ((struct dict_avp_data *)data)->avp_basetype, goto error_param );
1841 }
1842 }
1843
1844 /* For RULE object, we must also check that the "avp" referenced exists */
1845 if (type == DICT_RULE) {
1846 CHECK_PARAMS_DO( verify_object(((struct dict_rule_data *)data)->rule_avp), goto error_param );
1847 CHECK_PARAMS_DO( ((struct dict_rule_data *)data)->rule_avp->type == DICT_AVP, goto error_param );
1848 }
1849
1850 /* For COMMAND object, check that the 'R' flag is fixed */
1851 if (type == DICT_COMMAND) {
1852 CHECK_PARAMS_DO( ((struct dict_cmd_data *)data)->cmd_flag_mask & CMD_FLAG_REQUEST, goto error_param );
1853 }
1854
1855 /* For ENUMVAL object, check if the parent type is an OctetString */
1856 if (type == DICT_ENUMVAL) {
1857 if (parent->data.type.type_base == AVP_TYPE_OCTETSTRING)
1858 dupos = 1;
1859 }
1860
1861 /* We have to check that the new values are not equal to the sentinels */
1862 if (type == DICT_VENDOR) {
1863 CHECK_PARAMS_DO( ((struct dict_vendor_data *)data)->vendor_id != 0, goto error_param );
1864 }
1865 if (type == DICT_APPLICATION) {
1866 CHECK_PARAMS_DO( ((struct dict_application_data *)data)->application_id != 0, goto error_param );
1867 }
1868
1869 /* Parameters are valid, create the new object */
1870 CHECK_MALLOC( new = malloc(sizeof(struct dict_object)) );
1871
1872 /* Initialize the data of the new object */
1873 init_object(new, type);
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001874 new->dico = dict;
1875 new->parent = parent;
Brian Waters13d96012017-12-08 16:53:31 -06001876 init_object_data(new, data, type, dupos);
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001877
Brian Waters13d96012017-12-08 16:53:31 -06001878 /* We will change the dictionary => acquire the write lock */
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001879#if ENABLE_LOCK_BYPASS
1880 if (!dict->dict_bypass_lock)
1881#endif
Brian Waters13d96012017-12-08 16:53:31 -06001882 CHECK_POSIX_DO( ret = pthread_rwlock_wrlock(&dict->dict_lock), goto error_free );
1883
1884 /* Now link the object -- this also checks that no object with same keys already exists */
1885 switch (type) {
1886 case DICT_VENDOR:
1887 /* A vendor object is linked in the g_dict_vendors.list[0], by their id */
1888 ret = fd_list_insert_ordered ( &dict->dict_vendors.list[0], &new->list[0], (int (*)(void*, void *))order_vendor_by_id, (void **)&locref );
1889 if (ret)
1890 goto error_unlock;
1891 break;
1892
1893 case DICT_APPLICATION:
1894 /* An application object is linked in the g_dict_applciations.list[0], by their id */
1895 ret = fd_list_insert_ordered ( &dict->dict_applications.list[0], &new->list[0], (int (*)(void*, void *))order_appli_by_id, (void **)&locref );
1896 if (ret)
1897 goto error_unlock;
1898 break;
1899
1900 case DICT_TYPE:
1901 /* A type object is linked in g_list_types by its name */
1902 ret = fd_list_insert_ordered ( &dict->dict_types, &new->list[0], (int (*)(void*, void *))order_type_by_name, (void **)&locref );
1903 if (ret)
1904 goto error_unlock;
1905 break;
1906
1907 case DICT_ENUMVAL:
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001908#if USE_HASHLIST
1909 switch (parent->data.type.type_base)
1910 {
1911 case AVP_TYPE_INTEGER32:
1912 ret = insertInt32HashList(new->data.enumval.enum_value.i32, new, parent->hashlist[0], (void**)&locref);
1913 break;
1914
1915 case AVP_TYPE_INTEGER64:
1916 ret = insertInt64HashList(new->data.enumval.enum_value.i64, new, parent->hashlist[0], (void**)&locref);
1917 break;
1918
1919 case AVP_TYPE_UNSIGNED32:
1920 ret = insertUInt32HashList(new->data.enumval.enum_value.u32, new, parent->hashlist[0], (void**)&locref);
1921 break;
1922
1923 case AVP_TYPE_UNSIGNED64:
1924 ret = insertUInt64HashList(new->data.enumval.enum_value.u64, new, parent->hashlist[0], (void**)&locref);
1925 break;
1926
1927 case AVP_TYPE_FLOAT32:
1928 ret = insertFloat32HashList(new->data.enumval.enum_value.f32, new, parent->hashlist[0], (void**)&locref);
1929 break;
1930
1931 case AVP_TYPE_FLOAT64:
1932 ret = insertFloat64HashList(new->data.enumval.enum_value.f64, new, parent->hashlist[0], (void**)&locref);
1933 break;
1934
1935 default:
1936 /* Invalid parent type basetype */
1937 CHECK_PARAMS( parent = NULL );
1938 }
1939 if (ret)
1940 goto error_unlock;
1941
1942 ret = insertStringHashList(new->data.enumval.enum_name, new, parent->hashlist[1], (void **)&locref);
1943 if (ret) {
1944 switch (parent->data.type.type_base)
1945 {
1946 case AVP_TYPE_INTEGER32:
1947 deleteEntryInt32HashList(new->data.enumval.enum_value.i32, parent->hashlist[0]);
1948 break;
1949
1950 case AVP_TYPE_INTEGER64:
1951 deleteEntryInt64HashList(new->data.enumval.enum_value.i64, parent->hashlist[0]);
1952 break;
1953
1954 case AVP_TYPE_UNSIGNED32:
1955 deleteEntryUInt32HashList(new->data.enumval.enum_value.u32, parent->hashlist[0]);
1956 break;
1957
1958 case AVP_TYPE_UNSIGNED64:
1959 deleteEntryUInt64HashList(new->data.enumval.enum_value.u64, parent->hashlist[0]);
1960 break;
1961
1962 case AVP_TYPE_FLOAT32:
1963 deleteEntryFloat32HashList(new->data.enumval.enum_value.f32, parent->hashlist[0]);
1964 break;
1965
1966 case AVP_TYPE_FLOAT64:
1967 deleteEntryFloat64HashList(new->data.enumval.enum_value.f64, parent->hashlist[0]);
1968 break;
1969
1970 default:
1971 /* Invalid parent type basetype */
1972 CHECK_PARAMS( parent = NULL );
1973 }
1974 goto error_unlock;
1975 }
1976#endif
Brian Waters13d96012017-12-08 16:53:31 -06001977 /* A type_enum object is linked in it's parent 'type' object lists 1 and 2 by its name and values */
1978 ret = fd_list_insert_ordered ( &parent->list[1], &new->list[0], (int (*)(void*, void *))order_enum_by_name, (void **)&locref );
1979 if (ret)
1980 goto error_unlock;
1981
1982 ret = fd_list_insert_ordered ( &parent->list[2], &new->list[1], (int (*)(void*, void *))order_enum_by_val, (void **)&locref );
1983 if (ret) {
1984 fd_list_unlink(&new->list[0]);
1985 goto error_unlock;
1986 }
1987 break;
1988
1989 case DICT_AVP:
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05001990#if USE_HASHLIST
1991 ret = insertUInt32HashList(new->data.avp.avp_code, new, vendor->hashlist[0], (void **)&locref);
1992 if (ret)
1993 goto error_unlock;
1994
1995 ret = insertStringHashList(new->data.avp.avp_name, new, vendor->hashlist[1], (void **)&locref);
1996 if (ret) {
1997 deleteEntryUInt32HashList(new->data.avp.avp_code, vendor->hashlist[0]);
1998 goto error_unlock;
1999 }
2000#endif
Brian Waters13d96012017-12-08 16:53:31 -06002001 /* An avp object is linked in lists 1 and 2 of its vendor, by code and name */
2002 ret = fd_list_insert_ordered ( &vendor->list[1], &new->list[0], (int (*)(void*, void *))order_avp_by_code, (void **)&locref );
2003 if (ret)
2004 goto error_unlock;
2005
2006 ret = fd_list_insert_ordered ( &vendor->list[2], &new->list[1], (int (*)(void*, void *))order_avp_by_name, (void **)&locref );
2007 if (ret) {
2008 fd_list_unlink(&new->list[0]);
2009 goto error_unlock;
2010 }
2011 break;
2012
2013 case DICT_COMMAND:
2014 /* A command object is linked in g_list_cmd_name and g_list_cmd_code by its name and code */
2015 ret = fd_list_insert_ordered ( &dict->dict_cmd_code, &new->list[1], (int (*)(void*, void *))order_cmd_by_codefl, (void **)&locref );
2016 if (ret)
2017 goto error_unlock;
2018
2019 ret = fd_list_insert_ordered ( &dict->dict_cmd_name, &new->list[0], (int (*)(void*, void *))order_cmd_by_name, (void **)&locref );
2020 if (ret) {
2021 fd_list_unlink(&new->list[1]);
2022 goto error_unlock;
2023 }
2024 break;
2025
2026 case DICT_RULE:
2027 /* A rule object is linked in list[2] of its parent command or AVP by the name of the AVP it refers */
2028 ret = fd_list_insert_ordered ( &parent->list[2], &new->list[0], (int (*)(void*, void *))order_rule_by_avpvc, (void **)&locref );
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002029 if (ret){
2030 /*If the new rule is optional and the existing one is mandatory ==> override the existing rule to optional */
2031 if(locref->data.rule.rule_position == RULE_REQUIRED && new->data.rule.rule_position == RULE_OPTIONAL){
2032 TRACE_DEBUG(INFO, "Overriding rule to optional for AVP: %s", locref->data.rule.rule_avp->data.avp.avp_name);
2033 locref->data.rule.rule_position = RULE_OPTIONAL;
2034 }
Brian Waters13d96012017-12-08 16:53:31 -06002035 goto error_unlock;
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002036 }
Brian Waters13d96012017-12-08 16:53:31 -06002037 break;
2038
2039 default:
2040 ASSERT(0);
2041 }
2042
2043 /* A new object has been created, increment the global counter */
2044 dict->dict_count[type]++;
2045
2046 /* Unlock the dictionary */
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002047#if ENABLE_LOCK_BYPASS
2048 if (!dict->dict_bypass_lock)
2049#endif
Brian Waters13d96012017-12-08 16:53:31 -06002050 CHECK_POSIX_DO( ret = pthread_rwlock_unlock(&dict->dict_lock), goto error_free );
2051
2052 /* Save the pointer to the new object */
2053 if (ref)
2054 *ref = new;
2055
2056 return 0;
2057
2058error_param:
2059 ret = EINVAL;
2060 goto all_errors;
2061
2062error_unlock:
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002063#if ENABLE_LOCK_BYPASS
2064 if (!dict->dict_bypass_lock)
2065#endif
Brian Waters13d96012017-12-08 16:53:31 -06002066 CHECK_POSIX_DO( pthread_rwlock_unlock(&dict->dict_lock), /* continue */ );
2067 if (ret == EEXIST) {
2068 /* We have a duplicate key in locref. Check if the pointed object is the same or not */
2069 switch (type) {
2070 case DICT_VENDOR:
2071 TRACE_DEBUG(FULL, "Vendor %s already in dictionary", new->data.vendor.vendor_name);
2072 /* if we are here, it means the two vendors id are identical */
2073 if (fd_os_cmp(locref->data.vendor.vendor_name, locref->datastr_len,
2074 new->data.vendor.vendor_name, new->datastr_len)) {
2075 TRACE_DEBUG(INFO, "Conflicting vendor name: %s", new->data.vendor.vendor_name);
2076 break;
2077 }
2078 /* Otherwise (same name), we consider the function succeeded, since the (same) object is in the dictionary */
2079 ret = 0;
2080 break;
2081
2082 case DICT_APPLICATION:
2083 TRACE_DEBUG(FULL, "Application %s already in dictionary", new->data.application.application_name);
2084 /* got same id */
2085 if (fd_os_cmp(locref->data.application.application_name, locref->datastr_len,
2086 new->data.application.application_name, new->datastr_len)) {
2087 TRACE_DEBUG(FULL, "Conflicting application name");
2088 break;
2089 }
2090 ret = 0;
2091 break;
2092
2093 case DICT_TYPE:
2094 TRACE_DEBUG(FULL, "Type %s already in dictionary", new->data.type.type_name);
2095 /* got same name */
2096 if (locref->data.type.type_base != new->data.type.type_base) {
2097 TRACE_DEBUG(FULL, "Conflicting base type");
2098 break;
2099 }
2100 /* discard new definition only it a callback is provided and different from the previous one */
2101 if ((new->data.type.type_interpret) && (locref->data.type.type_interpret != new->data.type.type_interpret)) {
2102 TRACE_DEBUG(FULL, "Conflicting interpret cb");
2103 break;
2104 }
2105 if ((new->data.type.type_encode) && (locref->data.type.type_encode != new->data.type.type_encode)) {
2106 TRACE_DEBUG(FULL, "Conflicting encode cb");
2107 break;
2108 }
2109 if ((new->data.type.type_dump) && (locref->data.type.type_dump != new->data.type.type_dump)) {
2110 TRACE_DEBUG(FULL, "Conflicting dump cb");
2111 break;
2112 }
2113 ret = 0;
2114 break;
2115
2116 case DICT_ENUMVAL:
2117 TRACE_DEBUG(FULL, "Enum %s already in dictionary", new->data.enumval.enum_name);
2118 /* got either same name or same value. We check that both are true */
2119 if (order_enum_by_name(locref, new)) {
2120 TRACE_DEBUG(FULL, "Conflicting enum name");
2121 break;
2122 }
2123 if (order_enum_by_val(locref, new)) {
2124 TRACE_DEBUG(FULL, "Conflicting enum value");
2125 break;
2126 }
2127 ret = 0;
2128 break;
2129
2130 case DICT_AVP:
2131 TRACE_DEBUG(FULL, "AVP %s already in dictionary", new->data.avp.avp_name);
2132 /* got either same name or code */
2133 if (order_avp_by_code(locref, new)) {
2134 TRACE_DEBUG(FULL, "Conflicting AVP code");
2135 break;
2136 }
2137 if (order_avp_by_name(locref, new)) {
2138 TRACE_DEBUG(FULL, "Conflicting AVP name");
2139 break;
2140 }
2141 if (locref->data.avp.avp_vendor != new->data.avp.avp_vendor) {
2142 TRACE_DEBUG(FULL, "Conflicting AVP vendor");
2143 break;
2144 }
2145 if (locref->data.avp.avp_flag_mask != new->data.avp.avp_flag_mask) {
2146 TRACE_DEBUG(FULL, "Conflicting AVP flags mask");
2147 break;
2148 }
2149 if ((locref->data.avp.avp_flag_val & locref->data.avp.avp_flag_mask) != (new->data.avp.avp_flag_val & new->data.avp.avp_flag_mask)) {
2150 TRACE_DEBUG(FULL, "Conflicting AVP flags value");
2151 break;
2152 }
2153 if (locref->data.avp.avp_basetype != new->data.avp.avp_basetype) {
2154 TRACE_DEBUG(FULL, "Conflicting AVP base type");
2155 break;
2156 }
2157 ret = 0;
2158 break;
2159
2160 case DICT_COMMAND:
2161 TRACE_DEBUG(FULL, "Command %s already in dictionary", new->data.cmd.cmd_name);
2162 /* We got either same name, or same code + R flag */
2163 if (order_cmd_by_name(locref, new)) {
2164 TRACE_DEBUG(FULL, "Conflicting command name");
2165 break;
2166 }
2167 if (locref->data.cmd.cmd_code != new->data.cmd.cmd_code) {
2168 TRACE_DEBUG(FULL, "Conflicting command code");
2169 break;
2170 }
2171 if (locref->data.cmd.cmd_flag_mask != new->data.cmd.cmd_flag_mask) {
2172 TRACE_DEBUG(FULL, "Conflicting command flags mask %hhx:%hhx", locref->data.cmd.cmd_flag_mask, new->data.cmd.cmd_flag_mask);
2173 break;
2174 }
2175 if ((locref->data.cmd.cmd_flag_val & locref->data.cmd.cmd_flag_mask) != (new->data.cmd.cmd_flag_val & new->data.cmd.cmd_flag_mask)) {
2176 TRACE_DEBUG(FULL, "Conflicting command flags value");
2177 break;
2178 }
2179 ret = 0;
2180 break;
2181
2182 case DICT_RULE:
2183 /* Both rules point to the same AVPs (code & vendor) */
2184 if (locref->data.rule.rule_position != new->data.rule.rule_position) {
2185 TRACE_DEBUG(FULL, "Conflicting rule position");
2186 break;
2187 }
2188 if ( ((locref->data.rule.rule_position == RULE_FIXED_HEAD) ||
2189 (locref->data.rule.rule_position == RULE_FIXED_TAIL))
2190 && (locref->data.rule.rule_order != new->data.rule.rule_order)) {
2191 TRACE_DEBUG(FULL, "Conflicting rule order");
2192 break;
2193 }
2194 if (locref->data.rule.rule_min != new->data.rule.rule_min) {
2195 int r1 = locref->data.rule.rule_min;
2196 int r2 = new->data.rule.rule_min;
2197 int p = locref->data.rule.rule_position;
2198 if ( ((r1 != -1) && (r2 != -1)) /* none of the definitions contains the "default" value */
2199 || ((p == RULE_OPTIONAL) && (r1 != 0) && (r2 != 0)) /* the other value is not 0 for an optional rule */
2200 || ((r1 != 1) && (r2 != 1)) /* the other value is not 1 for another rule */
2201 ) {
2202 TRACE_DEBUG(FULL, "Conflicting rule min");
2203 break;
2204 }
2205 }
2206 if (locref->data.rule.rule_max != new->data.rule.rule_max) {
2207 TRACE_DEBUG(FULL, "Conflicting rule max");
2208 break;
2209 }
2210 ret = 0;
2211 break;
2212 }
2213 if (!ret) {
2214 TRACE_DEBUG(FULL, "An existing object with the same data was found, ignoring the error...");
2215 }
2216 if (ref)
2217 *ref = locref;
2218 }
2219all_errors:
2220 if (ret != 0) {
2221 char * buf = NULL;
2222 size_t len = 0, offset=0;
2223
2224 if (type == DICT_ENUMVAL) {
2225 CHECK_MALLOC( dump_enumval_data ( &buf, &len, &offset, data, parent->data.type.type_base ));
2226 } else {
2227 CHECK_MALLOC( dict_obj_info[CHECK_TYPE(type) ? type : 0].dump_data(&buf, &len, &offset, data) );
2228 }
2229
2230 TRACE_DEBUG(INFO, "An error occurred while adding the following data in the dictionary: %s", buf);
2231
2232 if (ret == EEXIST) {
2233 offset=0;
2234 CHECK_MALLOC( dump_object(&buf, &len, &offset, locref, 0, 0, 0) );
2235 TRACE_DEBUG(INFO, "Conflicting entry in the dictionary: %s", buf);
2236 }
2237 free(buf);
2238 }
2239error_free:
2240 free(new);
2241 return ret;
2242}
2243
2244
2245int fd_dict_delete(struct dict_object * obj)
2246{
2247 int i;
2248 struct dictionary * dict;
2249 int ret=0;
2250
2251 /* check params */
2252 CHECK_PARAMS( verify_object(obj) && obj->dico);
2253 dict = obj->dico;
2254
2255 /* Lock the dictionary for change */
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002256#if ENABLE_LOCK_BYPASS
2257 if (!dict->dict_bypass_lock)
2258#endif
Brian Waters13d96012017-12-08 16:53:31 -06002259 CHECK_POSIX( pthread_rwlock_wrlock(&dict->dict_lock) );
2260
2261 /* check the object is not sentinel for another list */
2262 for (i=0; i<NB_LISTS_PER_OBJ; i++) {
2263 if (!_OBINFO(obj).haslist[i] && !(FD_IS_LIST_EMPTY(&obj->list[i]))) {
2264 /* There are children, this is not good */
2265 ret = EINVAL;
2266 TRACE_DEBUG (FULL, "Cannot delete object, list %d not empty:", i);
2267 #if 0
2268 dump_list(&obj->list[i], 0,0,0);
2269 #endif
2270 break;
2271 }
2272 }
2273
2274 /* ok, now destroy the object */
2275 if (!ret)
2276 destroy_object(obj);
2277
2278 /* Unlock */
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002279#if ENABLE_LOCK_BYPASS
2280 if (!dict->dict_bypass_lock)
2281#endif
Brian Waters13d96012017-12-08 16:53:31 -06002282 CHECK_POSIX( pthread_rwlock_unlock(&dict->dict_lock) );
2283
2284 return ret;
2285}
2286
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002287void fd_dict_bypass_lock( struct dictionary *dict, int bypass )
2288{
2289#if ENABLE_LOCK_BYPASS
2290 if (dict && dict->dict_eyec == DICT_EYECATCHER)
2291 dict->dict_bypass_lock = bypass;
2292#endif
2293}
Brian Waters13d96012017-12-08 16:53:31 -06002294
2295int fd_dict_search ( struct dictionary * dict, enum dict_object_type type, int criteria, const void * what, struct dict_object **result, int retval )
2296{
2297 int ret = 0;
2298
2299 TRACE_ENTRY("%p %d(%s) %d %p %p %d", dict, type, dict_obj_info[CHECK_TYPE(type) ? type : 0].name, criteria, what, result, retval);
2300
2301 /* Check param */
2302 CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && CHECK_TYPE(type) );
2303
2304 /* Lock the dictionary for reading */
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002305#if ENABLE_LOCK_BYPASS
2306 if (!dict->dict_bypass_lock)
2307#endif
Brian Waters13d96012017-12-08 16:53:31 -06002308 CHECK_POSIX( pthread_rwlock_rdlock(&dict->dict_lock) );
2309
2310 /* Now call the type-specific search function */
2311 ret = dict_obj_info[type].search_fct (dict, criteria, what, result);
2312
2313 /* Unlock */
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002314#if ENABLE_LOCK_BYPASS
2315 if (!dict->dict_bypass_lock)
2316#endif
Brian Waters13d96012017-12-08 16:53:31 -06002317 CHECK_POSIX( pthread_rwlock_unlock(&dict->dict_lock) );
2318
2319 /* Update the return value as needed */
2320 if ((result != NULL) && (*result == NULL))
2321 ret = retval;
2322
2323 return ret;
2324}
2325
2326/* Function to retrieve list of objects in the dictionary. Use with care (read only).
2327
2328All returned list must be accessed like this:
2329
2330 for (li = sentinel->next; li != sentinel; li=li->next) {
2331 struct dict_object * obj = li->o;
2332 ...
2333 }
2334
2335The following criteria are allowed, with corresponding parent.
2336The parent is either struct dictionary * or struct dict_object *
2337
2338VENDOR_BY_ID : (parent = dictionary) returns list of vendors ordered by ID
2339APPLICATION_BY_ID : (parent = dictionary) returns list of applications ordered by ID
2340 ** for these two lists, the Vendor with id 0 and applciation with id 0 are excluded.
2341 You must resolve them separatly with dict_search.
2342
2343TYPE_BY_NAME : (parent = dictionary) returns list of types ordered by name (osstring order)
2344ENUMVAL_BY_NAME : (parent = type object) return list of constants for this type ordered by name (osstring order)
2345ENUMVAL_BY_VALUE : (parent = type object) return list of constants for this type ordered by values
2346AVP_BY_NAME : (parent = vendor object) return list of AVP for this vendor ordered by name (osstring order)
2347AVP_BY_CODE : (parent = vendor object) return list of AVP for this vendor ordered by code
2348CMD_BY_NAME : (parent = dictionary) returns list of commands ordered by name (osstring order)
2349CMD_BY_CODE_R : (parent = dictionary) returns list of commands ordered by code
2350RULE_BY_AVP_AND_PARENT: (parent = command or grouped AVP object) return list of rules for this object ordered by AVP vendor/code
2351
2352All other criteria are rejected.
2353 */
2354int fd_dict_getlistof(int criteria, void * parent, struct fd_list ** sentinel)
2355{
2356 struct dictionary * dict = parent;
2357 struct dict_object * obj_parent = parent;
2358
2359 TRACE_ENTRY("%i %p %p", criteria, parent, sentinel);
2360
2361 CHECK_PARAMS(sentinel && parent);
2362
2363 switch(criteria) {
2364 case VENDOR_BY_ID: /* parent must be the dictionary */
2365 CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER);
2366 *sentinel = &dict->dict_vendors.list[0];
2367 break;
2368
2369 case APPLICATION_BY_ID: /* parent must be the dictionary */
2370 CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER);
2371 *sentinel = &dict->dict_applications.list[0];
2372 break;
2373
2374 case TYPE_BY_NAME: /* parent must be the dictionary */
2375 CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER);
2376 *sentinel = &dict->dict_types;
2377 break;
2378
2379 case ENUMVAL_BY_NAME: /* parent must be a type object */
2380 CHECK_PARAMS(verify_object(obj_parent) && (obj_parent->type == DICT_TYPE));
2381 *sentinel = &obj_parent->list[1];
2382 break;
2383
2384 case ENUMVAL_BY_VALUE: /* parent must be a type object */
2385 CHECK_PARAMS(verify_object(obj_parent) && (obj_parent->type == DICT_TYPE));
2386 *sentinel = &obj_parent->list[2];
2387 break;
2388
2389 case AVP_BY_NAME: /* parent must be a VENDOR object */
2390 CHECK_PARAMS(verify_object(obj_parent) && (obj_parent->type == DICT_VENDOR));
2391 *sentinel = &obj_parent->list[2];
2392 break;
2393
2394 case AVP_BY_CODE: /* parent must be a VENDOR object */
2395 CHECK_PARAMS(verify_object(obj_parent) && (obj_parent->type == DICT_VENDOR));
2396 *sentinel = &obj_parent->list[1];
2397 break;
2398
2399 case CMD_BY_NAME: /* parent must be the dictionary */
2400 CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER);
2401 *sentinel = &dict->dict_cmd_name;
2402 break;
2403
2404 case CMD_BY_CODE_R: /* parent must be the dictionary */
2405 CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER);
2406 *sentinel = &dict->dict_cmd_code;
2407 break;
2408
2409 case RULE_BY_AVP_AND_PARENT: /* parent must be command or grouped AVP */
2410 CHECK_PARAMS(verify_object(obj_parent));
2411 CHECK_PARAMS( (obj_parent->type == DICT_COMMAND) ||
2412 ((obj_parent->type == DICT_AVP)
2413 && (obj_parent->data.avp.avp_basetype == AVP_TYPE_GROUPED)) );
2414 *sentinel = &obj_parent->list[2];
2415 break;
2416
2417 default:
2418 CHECK_PARAMS(0);
2419 }
2420
2421 return 0;
2422}
2423
2424/*******************************************************************************************************/
2425/*******************************************************************************************************/
2426/* */
2427/* The init/fini functions */
2428/* */
2429/*******************************************************************************************************/
2430/*******************************************************************************************************/
2431
2432/* Initialize the dictionary */
2433int fd_dict_init ( struct dictionary ** dict)
2434{
2435 struct dictionary * new = NULL;
2436
2437 TRACE_ENTRY("%p", dict);
2438
2439 /* Sanity checks */
2440 ASSERT( (sizeof(type_base_name) / sizeof(type_base_name[0])) == (AVP_TYPE_MAX + 1) );
2441 ASSERT( (sizeof(dict_obj_info) / sizeof(dict_obj_info[0])) == (DICT_TYPE_MAX + 1) );
2442 CHECK_PARAMS(dict);
2443
2444 /* Allocate the memory for the dictionary */
2445 CHECK_MALLOC( new = malloc(sizeof(struct dictionary)) );
2446 memset(new, 0, sizeof(struct dictionary));
2447
2448 new->dict_eyec = DICT_EYECATCHER;
2449
2450 /* Initialize the lock for the dictionary */
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002451#if ENABLE_LOCK_BYPASS
2452 new->dict_bypass_lock = 0;
2453#endif
Brian Waters13d96012017-12-08 16:53:31 -06002454 CHECK_POSIX( pthread_rwlock_init(&new->dict_lock, NULL) );
2455
2456 /* Initialize the sentinel for vendors and AVP lists */
2457 init_object( &new->dict_vendors, DICT_VENDOR );
2458 #define NO_VENDOR_NAME "(no vendor)"
2459 new->dict_vendors.data.vendor.vendor_name = NO_VENDOR_NAME;
2460 new->dict_vendors.datastr_len = CONSTSTRLEN(NO_VENDOR_NAME);
2461 /* new->dict_vendors.list[0].o = NULL; *//* overwrite since element is also sentinel for this list. */
2462 new->dict_vendors.dico = new;
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002463#if USE_HASHLIST
2464 initUInt32HashList(&new->dict_vendors.hashlist[0]);
2465 initStringHashList(&new->dict_vendors.hashlist[1]);
2466 LOG_N("HASHLIST is enabled");
2467#else
2468 LOG_N("HASHLIST is disabled");
2469#endif
Brian Waters13d96012017-12-08 16:53:31 -06002470
2471 /* Initialize the sentinel for applications */
2472 init_object( &new->dict_applications, DICT_APPLICATION );
2473 #define APPLICATION_0_NAME "Diameter Common Messages"
2474 new->dict_applications.data.application.application_name = APPLICATION_0_NAME;
2475 new->dict_applications.datastr_len = CONSTSTRLEN(APPLICATION_0_NAME);
2476 /* new->dict_applications.list[0].o = NULL; *//* overwrite since since element is also sentinel for this list. */
2477 new->dict_applications.dico = new;
2478
2479 /* Initialize the sentinel for types */
2480 fd_list_init ( &new->dict_types, NULL );
2481
2482 /* Initialize the sentinels for commands */
2483 fd_list_init ( &new->dict_cmd_name, NULL );
2484 fd_list_init ( &new->dict_cmd_code, NULL );
2485
2486 /* Initialize the error command object */
2487 init_object( &new->dict_cmd_error, DICT_COMMAND );
2488 #define GENERIC_ERROR_NAME "(generic error format)"
2489 new->dict_cmd_error.data.cmd.cmd_name = GENERIC_ERROR_NAME;
2490 new->dict_cmd_error.datastr_len = CONSTSTRLEN(GENERIC_ERROR_NAME);
2491 new->dict_cmd_error.data.cmd.cmd_flag_mask=CMD_FLAG_ERROR | CMD_FLAG_REQUEST | CMD_FLAG_RETRANSMIT;
2492 new->dict_cmd_error.data.cmd.cmd_flag_val =CMD_FLAG_ERROR;
2493 new->dict_cmd_error.dico = new;
2494
2495 *dict = new;
2496
2497 /* Done */
2498 return 0;
2499}
2500
2501/* Destroy a dictionary */
2502int fd_dict_fini ( struct dictionary ** dict)
2503{
2504 int i;
2505
2506 TRACE_ENTRY("");
2507 CHECK_PARAMS( dict && *dict && ((*dict)->dict_eyec == DICT_EYECATCHER) );
2508
2509 /* Acquire the write lock to make sure no other operation is ongoing */
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002510#if ENABLE_LOCK_BYPASS
2511 if (!(*dict)->dict_bypass_lock)
2512#endif
Brian Waters13d96012017-12-08 16:53:31 -06002513 CHECK_POSIX( pthread_rwlock_wrlock(&(*dict)->dict_lock) );
2514
2515 /* Empty all the lists, free the elements */
2516 destroy_list ( &(*dict)->dict_cmd_error.list[2] );
2517 destroy_list ( &(*dict)->dict_cmd_code );
2518 destroy_list ( &(*dict)->dict_cmd_name );
2519 destroy_list ( &(*dict)->dict_types );
2520 for (i=0; i< NB_LISTS_PER_OBJ; i++) {
2521 destroy_list ( &(*dict)->dict_applications.list[i] );
2522 destroy_list ( &(*dict)->dict_vendors.list[i] );
2523 }
2524
2525 /* Dictionary is empty, now destroy the lock */
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002526#if ENABLE_LOCK_BYPASS
2527 if (!(*dict)->dict_bypass_lock)
2528#endif
Brian Waters13d96012017-12-08 16:53:31 -06002529 CHECK_POSIX( pthread_rwlock_unlock(&(*dict)->dict_lock) );
2530 CHECK_POSIX( pthread_rwlock_destroy(&(*dict)->dict_lock) );
2531
2532 free(*dict);
2533 *dict = NULL;
2534
2535 return 0;
2536}
2537
2538/*******************************************************************************************************/
2539/*******************************************************************************************************/
2540/* */
2541/* Other functions */
2542/* */
2543/*******************************************************************************************************/
2544/*******************************************************************************************************/
2545
2546/* Iterate a callback on the rules for an object */
2547int fd_dict_iterate_rules ( struct dict_object *parent, void * data, int (*cb)(void *, struct dict_rule_data *) )
2548{
2549 int ret = 0;
2550 struct fd_list * li;
2551
2552 TRACE_ENTRY("%p %p %p", parent, data, cb);
2553
2554 /* Check parameters */
2555 CHECK_PARAMS( verify_object(parent) );
2556 CHECK_PARAMS( (parent->type == DICT_COMMAND)
2557 || ((parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED)) );
2558 TRACE_DEBUG (FULL, "Iterating on rules of %s: '%s'.",
2559 _OBINFO(parent).name,
2560 parent->type == DICT_COMMAND ?
2561 parent->data.cmd.cmd_name
2562 : parent->data.avp.avp_name);
2563
2564 /* Acquire the read lock */
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002565#if ENABLE_LOCK_BYPASS
2566 if (!parent->dico->dict_bypass_lock)
2567#endif
Brian Waters13d96012017-12-08 16:53:31 -06002568 CHECK_POSIX( pthread_rwlock_rdlock(&parent->dico->dict_lock) );
2569
2570 /* go through the list and call the cb on each rule data */
2571 for (li = &(parent->list[2]); li->next != &(parent->list[2]); li = li->next) {
2572 ret = (*cb)(data, &(_O(li->next->o)->data.rule));
2573 if (ret != 0)
2574 break;
2575 }
2576
2577 /* Release the lock */
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002578#if ENABLE_LOCK_BYPASS
2579 if (!parent->dico->dict_bypass_lock)
2580#endif
Brian Waters13d96012017-12-08 16:53:31 -06002581 CHECK_POSIX( pthread_rwlock_unlock(&parent->dico->dict_lock) );
2582
2583 return ret;
2584}
2585
2586/* Create the list of vendors. Returns a 0-terminated array, that must be freed after use. Returns NULL on error. */
2587uint32_t * fd_dict_get_vendorid_list(struct dictionary * dict)
2588{
2589 uint32_t * ret = NULL;
2590 int i = 0;
2591 struct fd_list * li;
2592
2593 TRACE_ENTRY();
2594
2595 /* Acquire the read lock */
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002596#if ENABLE_LOCK_BYPASS
2597 if (!dict->dict_bypass_lock)
2598#endif
Brian Waters13d96012017-12-08 16:53:31 -06002599 CHECK_POSIX_DO( pthread_rwlock_rdlock(&dict->dict_lock), return NULL );
2600
2601 /* Allocate an array to contain all the elements */
2602 CHECK_MALLOC_DO( ret = calloc( dict->dict_count[DICT_VENDOR] + 1, sizeof(uint32_t) ), goto out );
2603
2604 /* Copy the vendors IDs */
2605 for (li = dict->dict_vendors.list[0].next; li != &(dict->dict_vendors.list[0]); li = li->next) {
2606 ret[i] = _O(li->o)->data.vendor.vendor_id;
2607 i++;
2608 ASSERT( i <= dict->dict_count[DICT_VENDOR] );
2609 }
2610out:
2611 /* Release the lock */
Javier Bravo Conde13b0e7d2018-07-09 17:08:51 -05002612#if ENABLE_LOCK_BYPASS
2613 if (!dict->dict_bypass_lock)
2614#endif
Brian Waters13d96012017-12-08 16:53:31 -06002615 CHECK_POSIX_DO( pthread_rwlock_unlock(&dict->dict_lock), return NULL );
2616
2617 return ret;
2618}
2619
2620/* Return the location of the cb list for an object, after checking its type */
2621int fd_dict_disp_cb(enum dict_object_type type, struct dict_object *obj, struct fd_list ** cb_list)
2622{
2623 TRACE_ENTRY("%d %p %p", type, obj, cb_list);
2624 CHECK_PARAMS( verify_object(obj) );
2625 CHECK_PARAMS( _OBINFO(obj).type == type );
2626 CHECK_PARAMS( cb_list );
2627 *cb_list = &obj->disp_cbs;
2628 return 0;
2629}
2630
2631int fd_dict_get_error_cmd(struct dictionary * dict, struct dict_object **obj)
2632{
2633 TRACE_ENTRY("%p %p", dict, obj);
2634 CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && obj );
2635 *obj = &dict->dict_cmd_error;
2636 return 0;
2637}