dictionary.c 71.7 KB
Newer Older
Sebastien Decugis's avatar
Sebastien Decugis committed
1
2
/*********************************************************************************************************
* Software License Agreement (BSD License)                                                               *
Sebastien Decugis's avatar
Sebastien Decugis committed
3
* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
Sebastien Decugis's avatar
Sebastien Decugis committed
4
*													 *
5
* Copyright (c) 2011, WIDE Project and NICT								 *
Sebastien Decugis's avatar
Sebastien Decugis committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
* All rights reserved.											 *
* 													 *
* Redistribution and use of this software in source and binary forms, with or without modification, are  *
* permitted provided that the following conditions are met:						 *
* 													 *
* * Redistributions of source code must retain the above 						 *
*   copyright notice, this list of conditions and the 							 *
*   following disclaimer.										 *
*    													 *
* * Redistributions in binary form must reproduce the above 						 *
*   copyright notice, this list of conditions and the 							 *
*   following disclaimer in the documentation and/or other						 *
*   materials provided with the distribution.								 *
* 													 *
* * Neither the name of the WIDE Project or NICT nor the 						 *
*   names of its contributors may be used to endorse or 						 *
*   promote products derived from this software without 						 *
*   specific prior written permission of WIDE Project and 						 *
*   NICT.												 *
* 													 *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
*********************************************************************************************************/

Sebastien Decugis's avatar
Sebastien Decugis committed
36
#include "fdproto-internal.h"
Sebastien Decugis's avatar
Sebastien Decugis committed
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

/* Names of the base types */
const char * type_base_name[] = { /* must keep in sync with dict_avp_basetype */
	"GROUPED", 	/* AVP_TYPE_GROUPED */
	"OCTETSTRING", 	/* AVP_TYPE_OCTETSTRING */
	"INTEGER32", 	/* AVP_TYPE_INTEGER32 */
	"INTEGER64", 	/* AVP_TYPE_INTEGER64 */
	"UNSIGNED32", 	/* AVP_TYPE_UNSIGNED32 */
	"UNSIGNED64", 	/* AVP_TYPE_UNSIGNED64 */
	"FLOAT32", 	/* AVP_TYPE_FLOAT32 */
	"FLOAT64"	/* AVP_TYPE_FLOAT64 */
	};

/* The number of lists in an object */
#define NB_LISTS_PER_OBJ	3

/* Some eye catchers definitions */
#define OBJECT_EYECATCHER	(0x0b13c7)
#define DICT_EYECATCHER		(0x00d1c7)

/* Definition of the dictionary objects */
struct dict_object {
	enum dict_object_type	type;	/* What type of object is this? */
	int			objeyec;/* eyecatcher for this object */
	int			typeyec;/* eyecatcher for this type of object */
	struct dictionary	*dico;  /* The dictionary this object belongs to */
	
	union {
65
66
67
68
69
70
71
		struct dict_vendor_data		vendor;		/* datastr_len = strlen(vendor_name) */
		struct dict_application_data	application;	/* datastr_len = strlen(application_name) */
		struct dict_type_data		type;		/* datastr_len = strlen(type_name) */
		struct dict_enumval_data	enumval;	/* datastr_len = strlen(enum_name) */
		struct dict_avp_data		avp;		/* datastr_len = strlen(avp_name) */
		struct dict_cmd_data		cmd;		/* datastr_len = strlen(cmd_name) */
		struct dict_rule_data		rule;		/* datastr_len = 0 */
Sebastien Decugis's avatar
Sebastien Decugis committed
72
73
	} data;				/* The data of this object */
	
74
75
	size_t			datastr_len; /* cached length of the string inside the data. Saved when the object is created. */
	
Sebastien Decugis's avatar
Sebastien Decugis committed
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
	struct dict_object *	parent; /* The parent of this object, if any */
	
	struct fd_list		list[NB_LISTS_PER_OBJ];/* used to chain objects.*/
	/* More information about the lists :
	
	 - the use for each list depends on the type of object. See detail bellow.
	 
	 - a sentinel for a list has its 'o' field cleared. (this is the criteria to detect end of a loop)
	 
	 - The lists are always ordered. The criteria are described bellow. the functions to order them are referenced in dict_obj_info
	 
	 - The dict_lock must be held for any list operation.
	 
	 => VENDORS:
	 list[0]: list of the vendors, ordered by their id. The sentinel is g_dict_vendors (vendor with id 0)
	 list[1]: sentinel for the list of AVPs from this vendor, ordered by AVP code.
92
	 list[2]: sentinel for the list of AVPs from this vendor, ordered by AVP name (fd_os_cmp).
Sebastien Decugis's avatar
Sebastien Decugis committed
93
94
95
96
97
98
99
100
	 
	 => APPLICATIONS:
	 list[0]: list of the applications, ordered by their id. The sentinel is g_dict_applications (application with id 0)
	 list[1]: not used
	 list[2]: not used.
	 
	 => TYPES:
	 list[0]: list of the types, ordered by their names. The sentinel is g_list_types.
101
	 list[1]: sentinel for the type_enum list of this type, ordered by their constant name (fd_os_cmp).
Sebastien Decugis's avatar
Sebastien Decugis committed
102
103
104
	 list[2]: sentinel for the type_enum list of this type, ordered by their constant value.
	 
	 => TYPE_ENUMS:
105
	 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.
Sebastien Decugis's avatar
Sebastien Decugis committed
106
107
108
109
110
	 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.
	 list[2]: not used
	 
	 => AVPS:
	 list[0]: list of the AVP from a given vendor, ordered by avp code. Sentinel is a list[1] element of a VENDOR object.
111
	 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.
Sebastien Decugis's avatar
Sebastien Decugis committed
112
113
114
	 list[2]: sentinel for the rule list that apply to this AVP.
	 
	 => COMMANDS:
115
	 list[0]: list of the commands, ordered by their names (fd_os_cmp). The sentinel is g_list_cmd_name.
Sebastien Decugis's avatar
Sebastien Decugis committed
116
117
118
119
	 list[1]: list of the commands, ordered by their command code and 'R' flag. The sentinel is g_list_cmd_code.
	 list[2]: sentinel for the rule list that apply to this command.
	 
	 => RULES:
120
	 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.
Sebastien Decugis's avatar
Sebastien Decugis committed
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
	 list[1]: not used
	 list[2]: not used.
	 
	 */
	 
	 /* Sentinel for the dispatch callbacks */
	 struct fd_list		disp_cbs;
	
};

/* Definition of the dictionary structure */
struct dictionary {
	int		 	dict_eyec;		/* Eye-catcher for the dictionary (DICT_EYECATCHER) */
	
	pthread_rwlock_t 	dict_lock;		/* The global rwlock for the dictionary */
	
	struct dict_object	dict_vendors;		/* Sentinel for the list of vendors, corresponding to vendor 0 */
	struct dict_object	dict_applications;	/* Sentinel for the list of applications, corresponding to app 0 */
	struct fd_list		dict_types;		/* Sentinel for the list of types */
	struct fd_list		dict_cmd_name;		/* Sentinel for the list of commands, ordered by names */
	struct fd_list		dict_cmd_code;		/* Sentinel for the list of commands, ordered by codes */
	
	struct dict_object	dict_cmd_error;		/* Special command object for answers with the 'E' bit set */
	
145
	int			dict_count[DICT_TYPE_MAX + 1]; /* Number of objects of each type */
Sebastien Decugis's avatar
Sebastien Decugis committed
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
};

/* Forward declarations of dump functions */
static void dump_vendor_data 	  ( void * data );
static void dump_application_data ( void * data );
static void dump_type_data 	  ( void * data );
  /* the dump function for enum has a different prototype since it need the datatype */
static void dump_avp_data 	  ( void * data );
static void dump_command_data 	  ( void * data );
static void dump_rule_data 	  ( void * data );

/* Forward declarations of search functions */
static int search_vendor 	( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
static int search_application   ( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
static int search_type 		( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
static int search_enumval 	( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
static int search_avp		( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
static int search_cmd		( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
static int search_rule		( struct dictionary * dict, int criteria, void * what, struct dict_object **result );

/* The following array contains lot of data about the different types of objects, for automated handling */
static struct {
	enum dict_object_type 	type; 		/* information for this type */
	char *			name;		/* string describing this object, for debug */
	size_t			datasize;	/* The size of the data structure */
	int			parent;		/* 0: never; 1: may; 2: must */
	enum dict_object_type	parenttype;	/* The type of the parent, when relevant */
	int			eyecatcher;	/* A kind of signature for this object */
	void 		      (*dump_data)(void * data );	/* The function to dump the data section */
	int 		      (*search_fct)(struct dictionary * dict, int criteria, void * what, struct dict_object **result );;	/* The function to search an object of this type */
	int			haslist[NB_LISTS_PER_OBJ];	/* Tell if this list is used */
} dict_obj_info[] = { { 0, "(error)", 0, 0, 0, 0, NULL, NULL, {0, 0, 0} }

	/* type			 name		datasize		   	  parent  	parenttype 
			eyecatcher		dump_data	  	search_fct,		haslist[] 	*/

	,{ DICT_VENDOR,		"VENDOR",	sizeof(struct dict_vendor_data),	0, 	0,
			OBJECT_EYECATCHER + 1, 	dump_vendor_data, 	search_vendor, 		{ 1, 0, 0 } }
	
	,{ DICT_APPLICATION,	"APPLICATION",	sizeof(struct dict_application_data),	1, 	DICT_VENDOR,
			OBJECT_EYECATCHER + 2,	dump_application_data,	search_application,	{ 1, 0, 0 } }
	
	,{ DICT_TYPE,		"TYPE",		sizeof(struct dict_type_data),		1, 	DICT_APPLICATION,
			OBJECT_EYECATCHER + 3,	dump_type_data,		search_type,		{ 1, 0, 0 } }
	
	,{ DICT_ENUMVAL,	"ENUMVAL",	sizeof(struct dict_enumval_data),	2, 	DICT_TYPE,
			OBJECT_EYECATCHER + 4,	NULL,			search_enumval,	{ 1, 1, 0 } }
	
	,{ DICT_AVP,		"AVP",		sizeof(struct dict_avp_data),		1, 	DICT_TYPE,
			OBJECT_EYECATCHER + 5,	dump_avp_data,		search_avp,		{ 1, 1,	0 } }
	
	,{ DICT_COMMAND,	"COMMAND",	sizeof(struct dict_cmd_data),		1, 	DICT_APPLICATION,
			OBJECT_EYECATCHER + 6,	dump_command_data,	search_cmd,		{ 1, 1, 0 } }
	
	,{ DICT_RULE,		"RULE",		sizeof(struct dict_rule_data),		2, 	-1 /* special case: grouped avp or command */,
			OBJECT_EYECATCHER + 7,	dump_rule_data,		search_rule,		{ 1, 0, 0 } }
	
};
	
/* Macro to verify a "type" value */
#define CHECK_TYPE( type ) ( ((type) > 0) && ((type) <= DICT_TYPE_MAX) )

/* Cast macro */
#define _O( object ) ((struct dict_object *) (object))

/* Get information line for a given object */
#define _OBINFO(object) (dict_obj_info[CHECK_TYPE(_O(object)->type) ? _O(object)->type : 0])




/*******************************************************************************************************/
/*******************************************************************************************************/
/*                                                                                                     */
/*                                  Objects management                                                 */
/*                                                                                                     */
/*******************************************************************************************************/
/*******************************************************************************************************/

/* Functions to manage the objects creation and destruction. */

227
228
229
230
/* Duplicate a string inplace, save its length */
#define DUP_string_len( str, plen ) {		\
	*(plen) = strlen((str));		\
	str = os0dup( str, *(plen));		\
Sebastien Decugis's avatar
Sebastien Decugis committed
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
}
	
/* Initialize an object */
static void init_object( struct dict_object * obj, enum dict_object_type type )
{
	int i;
	
	TRACE_ENTRY("%p %d", obj, type);
	
	/* Clean the object first */
	memset ( obj, 0, sizeof(struct dict_object));
	
	CHECK_PARAMS_DO(  CHECK_TYPE(type),  return  );

	obj->type = type;
	obj->objeyec = OBJECT_EYECATCHER;
	obj->typeyec = _OBINFO(obj).eyecatcher;

	/* We don't initialize the data nor the parent here */
	
	/* Now init the lists */
	for (i=0; i<NB_LISTS_PER_OBJ; i++) {
		if (_OBINFO(obj).haslist[i] != 0) 
			fd_list_init(&obj->list[i], obj);
		else
			fd_list_init(&obj->list[i], NULL);
	}
	
	fd_list_init(&obj->disp_cbs, NULL);
}

/* Initialize the "data" part of an object */
263
static int init_object_data(struct dict_object * dest, void * source, enum dict_object_type type)
Sebastien Decugis's avatar
Sebastien Decugis committed
264
265
266
267
268
{
	TRACE_ENTRY("%p %p %d", dest, source, type);
	CHECK_PARAMS( dest && source && CHECK_TYPE(type) );
	
	/* Generic: copy the full data structure */	
269
	memcpy( &dest->data, source, dict_obj_info[type].datasize );
Sebastien Decugis's avatar
Sebastien Decugis committed
270
271
272
273
274
	
	/* Then strings must be duplicated, not copied */
	/* This function might be simplified by always defining the "name" field as the first field of the structures, but... it's error-prone */
	switch (type) {
		case DICT_VENDOR:
275
			DUP_string_len( dest->data.vendor.vendor_name, &dest->datastr_len );
Sebastien Decugis's avatar
Sebastien Decugis committed
276
277
278
			break;
		
		case DICT_APPLICATION:
279
			DUP_string_len( dest->data.application.application_name, &dest->datastr_len );
Sebastien Decugis's avatar
Sebastien Decugis committed
280
281
282
			break;
			
		case DICT_TYPE:
283
			DUP_string_len( dest->data.type.type_name, &dest->datastr_len );
Sebastien Decugis's avatar
Sebastien Decugis committed
284
285
286
			break;
			
		case DICT_ENUMVAL:
287
			DUP_string_len( dest->data.enumval.enum_name, &dest->datastr_len );
Sebastien Decugis's avatar
Sebastien Decugis committed
288
289
290
			break;

		case DICT_AVP:
291
			DUP_string_len( dest->data.avp.avp_name, &dest->datastr_len );
Sebastien Decugis's avatar
Sebastien Decugis committed
292
293
294
			break;
			
		case DICT_COMMAND:
295
			DUP_string_len( dest->data.cmd.cmd_name, &dest->datastr_len );
Sebastien Decugis's avatar
Sebastien Decugis committed
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
			break;
		
		default:
			/* Nothing to do for RULES */
			;
	}
	
	return 0;
}

/* Check that an object is valid (1: OK, 0: error) */
static int verify_object( struct dict_object * obj )
{
	TRACE_ENTRY("%p", obj);
	
	CHECK_PARAMS_DO(  obj
			&& (obj->objeyec == OBJECT_EYECATCHER)
			&& CHECK_TYPE(obj->type)
			&& (obj->typeyec == dict_obj_info[obj->type].eyecatcher),
		{
			if (obj) {
				TRACE_DEBUG(FULL, "Invalid object : %p\n"
						  "     obj->objeyec : %x / %x\n"
						  "     obj->type    : %d\n"
						  "     obj->objeyec : %x / %x\n"
						  "     obj->typeyec : %x / %x", 
						obj,
						obj->objeyec, OBJECT_EYECATCHER,
						obj->type,
						obj->objeyec, OBJECT_EYECATCHER,
						obj->typeyec, _OBINFO(obj).eyecatcher);
Sebastien Decugis's avatar
Sebastien Decugis committed
327
328
			} else {
				TRACE_DEBUG(FULL, "Invalid object : NULL pointer");
Sebastien Decugis's avatar
Sebastien Decugis committed
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
			}
			return 0;
		}  );
	
	/* The object is probably valid. */
	return 1;
}

/* Free the data associated to an object */
static void destroy_object_data(struct dict_object * obj)
{
	/* TRACE_ENTRY("%p", obj); */
	
	switch (obj->type) {
		case DICT_VENDOR:
			free( obj->data.vendor.vendor_name );
			break;
		
		case DICT_APPLICATION:
			free( obj->data.application.application_name );
			break;
			
		case DICT_TYPE:
			free( obj->data.type.type_name );
			break;
			
		case DICT_ENUMVAL:
			free( obj->data.enumval.enum_name );
			break;

		case DICT_AVP:
			free( obj->data.avp.avp_name );
			break;
			
		case DICT_COMMAND:
			free( obj->data.cmd.cmd_name );
			break;
		
		default:
			/* nothing to do */
			;
	}
}

/* Forward declaration */
static void destroy_object(struct dict_object * obj);

/* Destroy all objects in a list - the lock must be held */
static void destroy_list(struct fd_list * head) 
{
	/* TRACE_ENTRY("%p", head); */
	
	/* loop in the list */
	while (!FD_IS_LIST_EMPTY(head))
	{
		/* When destroying the object, it is unlinked from the list */
		destroy_object(_O(head->next->o));
	}
}
	
/* Free an object and its sublists */
static void destroy_object(struct dict_object * obj)
{
	int i;
	
	/* TRACE_ENTRY("%p", obj); */
	
	/* Update global count */
	if (obj->dico) 
		obj->dico->dict_count[obj->type]--;
	
	/* Mark the object as invalid */
	obj->objeyec = 0xdead;
	
	/* First, destroy the data associated to the object */
	destroy_object_data(obj);
	
	for (i=0; i<NB_LISTS_PER_OBJ; i++) {
		if (_OBINFO(obj).haslist[i])
			/* unlink the element from the list */
			fd_list_unlink( &obj->list[i] );
		else
			/* This is either a sentinel or unused (=emtpy) list, let's destroy it */
			destroy_list( &obj->list[i] );
	}
	
	/* Unlink all elements from the dispatch list; they will be freed when callback is unregistered */
416
	CHECK_POSIX_DO( pthread_rwlock_wrlock(&fd_disp_lock), /* continue */ );
Sebastien Decugis's avatar
Sebastien Decugis committed
417
418
419
	while (!FD_IS_LIST_EMPTY(&obj->disp_cbs)) {
		fd_list_unlink( obj->disp_cbs.next );
	}
420
	CHECK_POSIX_DO( pthread_rwlock_unlock(&fd_disp_lock), /* continue */ );
Sebastien Decugis's avatar
Sebastien Decugis committed
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
	
	/* Last, destroy the object */
	free(obj);
}

/*******************************************************************************************************/
/*******************************************************************************************************/
/*                                                                                                     */
/*                                  Compare functions                                                  */
/*                                                                                                     */
/*******************************************************************************************************/
/*******************************************************************************************************/

/* Compare two values */
#define ORDER_scalar( i1, i2 ) \
	((i1 < i2 ) ? -1 : ( i1 > i2 ? 1 : 0 )) 


/* Compare two vendor objects by their id (checks already performed) */
static int order_vendor_by_id ( struct dict_object *o1, struct dict_object *o2 )
{
	TRACE_ENTRY("%p %p", o1, o2);
	
	return ORDER_scalar( o1->data.vendor.vendor_id, o2->data.vendor.vendor_id );
}

/* Compare two application objects by their id (checks already performed) */
static int order_appli_by_id  ( struct dict_object *o1, struct dict_object *o2 )
{
	TRACE_ENTRY("%p %p", o1, o2);
	
	return ORDER_scalar( o1->data.application.application_id, o2->data.application.application_id );
}

/* Compare two type objects by their name (checks already performed) */
static int order_type_by_name ( struct dict_object *o1, struct dict_object *o2 )
{
	TRACE_ENTRY("%p %p", o1, o2);
	
460
	return fd_os_cmp( o1->data.type.type_name, o1->datastr_len, o2->data.type.type_name, o2->datastr_len );
Sebastien Decugis's avatar
Sebastien Decugis committed
461
462
463
464
465
466
467
}

/* Compare two type_enum objects by their names (checks already performed) */
static int order_enum_by_name ( struct dict_object *o1, struct dict_object *o2 )
{
	TRACE_ENTRY("%p %p", o1, o2);
	
468
	return fd_os_cmp( o1->data.enumval.enum_name, o1->datastr_len, o2->data.enumval.enum_name, o2->datastr_len );
Sebastien Decugis's avatar
Sebastien Decugis committed
469
470
471
472
473
474
475
476
477
478
}

/* Compare two type_enum objects by their values (checks already performed) */
static int order_enum_by_val  ( struct dict_object *o1, struct dict_object *o2 )
{
	TRACE_ENTRY("%p %p", o1, o2);
	
	/* The comparison function depends on the type of data */
	switch ( o1->parent->data.type.type_base ) {
		case AVP_TYPE_OCTETSTRING:
479
480
			return fd_os_cmp( o1->data.enumval.enum_value.os.data, o1->data.enumval.enum_value.os.len, 
					  o2->data.enumval.enum_value.os.data, o2->data.enumval.enum_value.os.len);
Sebastien Decugis's avatar
Sebastien Decugis committed
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
		
		case AVP_TYPE_INTEGER32:
			return ORDER_scalar( o1->data.enumval.enum_value.i32, o2->data.enumval.enum_value.i32 );

		case AVP_TYPE_INTEGER64:
			return ORDER_scalar( o1->data.enumval.enum_value.i64, o2->data.enumval.enum_value.i64 );

		case AVP_TYPE_UNSIGNED32:
			return ORDER_scalar( o1->data.enumval.enum_value.u32, o2->data.enumval.enum_value.u32 );

		case AVP_TYPE_UNSIGNED64:
			return ORDER_scalar( o1->data.enumval.enum_value.u64, o2->data.enumval.enum_value.u64 );

		case AVP_TYPE_FLOAT32:
			return ORDER_scalar( o1->data.enumval.enum_value.f32, o2->data.enumval.enum_value.f32 );

		case AVP_TYPE_FLOAT64:
			return ORDER_scalar( o1->data.enumval.enum_value.f64, o2->data.enumval.enum_value.f64 );

		case AVP_TYPE_GROUPED:
		default:
			ASSERT(0);
	}
	return 0;
}

/* Compare two avp objects by their codes (checks already performed) */
static int order_avp_by_code  ( struct dict_object *o1, struct dict_object *o2 )
{
	TRACE_ENTRY("%p %p", o1, o2);
	
	return ORDER_scalar( o1->data.avp.avp_code, o2->data.avp.avp_code );
}

/* Compare two avp objects by their names (checks already performed) */
static int order_avp_by_name  ( struct dict_object *o1, struct dict_object *o2 )
{
	TRACE_ENTRY("%p %p", o1, o2);
	
520
	return fd_os_cmp( o1->data.avp.avp_name, o1->datastr_len, o2->data.avp.avp_name, o2->datastr_len );
Sebastien Decugis's avatar
Sebastien Decugis committed
521
522
523
524
525
526
527
}

/* Compare two command objects by their names (checks already performed) */
static int order_cmd_by_name  ( struct dict_object *o1, struct dict_object *o2 )
{
	TRACE_ENTRY("%p %p", o1, o2);
	
528
	return fd_os_cmp( o1->data.cmd.cmd_name, o1->datastr_len, o2->data.cmd.cmd_name, o2->datastr_len );
Sebastien Decugis's avatar
Sebastien Decugis committed
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
}

/* Compare two command objects by their codes and flags (request or answer) (checks already performed) */
static int order_cmd_by_codefl( struct dict_object *o1, struct dict_object *o2 )
{
	uint8_t fl1, fl2;
	int cmp = 0;
	
	TRACE_ENTRY("%p %p", o1, o2);
	
	cmp = ORDER_scalar( o1->data.cmd.cmd_code, o2->data.cmd.cmd_code );
	if (cmp) 
		return cmp;
	
	/* Same command code, we must compare the value of the 'R' flag */
	fl1 = o1->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST;
	fl2 = o2->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST;
	
	/* We want requests first, so we reverse the operators here */
	return ORDER_scalar(fl2, fl1);
		
}

Sebastien Decugis's avatar
Sebastien Decugis committed
552
/* Compare two rule object by the AVP vendor & code that they refer (checks already performed) */
553
static int order_rule_by_avpvc ( struct dict_object *o1, struct dict_object *o2 )
Sebastien Decugis's avatar
Sebastien Decugis committed
554
555
556
{
	TRACE_ENTRY("%p %p", o1, o2);
	
Sebastien Decugis's avatar
Sebastien Decugis committed
557
558
	return ORDER_scalar(o1->data.rule.rule_avp->data.avp.avp_vendor, o2->data.rule.rule_avp->data.avp.avp_vendor) 
		?: ORDER_scalar(o1->data.rule.rule_avp->data.avp.avp_code, o2->data.rule.rule_avp->data.avp.avp_code) ;
Sebastien Decugis's avatar
Sebastien Decugis committed
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
}

/*******************************************************************************************************/
/*******************************************************************************************************/
/*                                                                                                     */
/*                                  Search  functions                                                  */
/*                                                                                                     */
/*******************************************************************************************************/
/*******************************************************************************************************/

/* Functions used to search for objects in the lists, according to some criteria */

/* On a general note, if result is not NULL, ENOENT is not returned but *result is NULL. */

/* The following macros assume that "what", "ret", "result" (variables), and "end" (label) exist
in the local context where they are called. They are meant to be called only from the functions that follow. */

/* For searchs of type "xxx_OF_xxx": children's parent or default parent */
#define SEARCH_childs_parent( type_of_child, default_parent ) {			\
	struct dict_object *__child = (struct dict_object *) what;		\
	CHECK_PARAMS_DO( verify_object(__child) && 				\
		(__child->type == (type_of_child)), 				\
		   {  ret = EINVAL; goto end;  }  );				\
	ret = 0;								\
	if (result)								\
		*result = (__child->parent ? __child->parent :(default_parent));\
}

/* For search of strings in lists. isindex= 1 if the string is the ordering key of the list */
588
589
590
591
/* it is expected that object->datastr_len is the length of the datafield parameter */
#define SEARCH_os0_l( str, len, sentinel, datafield, isindex ) {		\
	char *  __str = (char *) (str);						\
	size_t __strlen = (size_t)(len);					\
Sebastien Decugis's avatar
Sebastien Decugis committed
592
593
594
	int __cmp;								\
	struct fd_list * __li;							\
	ret = 0;								\
595
	for  (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) {	\
596
597
		__cmp = fd_os_cmp(__str, __strlen,				\
			_O(__li->o)->data. datafield, _O(__li->o)->datastr_len);\
Sebastien Decugis's avatar
Sebastien Decugis committed
598
599
		if (__cmp == 0) {						\
			if (result)						\
600
				*result = _O(__li->o);				\
Sebastien Decugis's avatar
Sebastien Decugis committed
601
602
603
604
605
606
607
608
609
610
611
			goto end;						\
		}								\
		if ((isindex) && (__cmp < 0))					\
			break;							\
	}									\
	if (result)								\
		*result = NULL;							\
	else									\
		ret = ENOENT;							\
}

612
613
614
615
616
617
618
619
620
621
622
623
/* When len is not provided */
#define SEARCH_os0( str, sentinel, datafield, isindex ) {			\
	char *  _str = (char *) (str);						\
	size_t  _strlen = strlen(_str);						\
	SEARCH_os0_l( _str, _strlen, sentinel, datafield, isindex );		\
}


/* For search of octetstrings in lists. */
#define SEARCH_os(  str, strlen, sentinel, osdatafield, isindex ) {		\
	uint8_t * __str = (uint8_t *) (str);					\
	size_t __strlen = (size_t)(strlen);					\
Sebastien Decugis's avatar
Sebastien Decugis committed
624
625
626
	int __cmp;								\
	struct fd_list * __li;							\
	ret = 0;								\
627
628
629
630
	for  (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) {	\
		__cmp = fd_os_cmp(__str, __strlen,				\
			_O(__li->o)->data. osdatafield .data,			\
			_O(__li->o)->data. osdatafield .len);			\
Sebastien Decugis's avatar
Sebastien Decugis committed
631
632
		if (__cmp == 0) {						\
			if (result)						\
633
				*result = _O(__li->o);				\
Sebastien Decugis's avatar
Sebastien Decugis committed
634
635
636
637
638
639
640
641
642
643
644
645
			goto end;						\
		}								\
		if ((isindex) && (__cmp < 0))					\
			break;							\
	}									\
	if (result)								\
		*result = NULL;							\
	else									\
		ret = ENOENT;							\
}

/* For search of AVP name in rule lists. */
646
647
648
#define SEARCH_ruleavpname( str, strlen, sentinel ) {				\
	char * __str = (char *) (str);						\
	size_t __strlen = (size_t) (strlen);					\
Sebastien Decugis's avatar
Sebastien Decugis committed
649
650
651
	int __cmp;								\
	struct fd_list * __li;							\
	ret = 0;								\
652
653
654
655
	for  (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) {	\
		__cmp = fd_os_cmp(__str, __strlen, 				\
		  	_O(__li->o)->data.rule.rule_avp->data.avp.avp_name,	\
			_O(__li->o)->data.rule.rule_avp->datastr_len);		\
Sebastien Decugis's avatar
Sebastien Decugis committed
656
657
		if (__cmp == 0) {						\
			if (result)						\
658
				*result = _O(__li->o);				\
Sebastien Decugis's avatar
Sebastien Decugis committed
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
			goto end;						\
		}								\
		if (__cmp < 0)							\
			break;							\
	}									\
	if (result)								\
		*result = NULL;							\
	else									\
		ret = ENOENT;							\
}

/* For search of scalars in lists. isindex= 1 if the value is the ordering key of the list */
#define SEARCH_scalar( value, sentinel, datafield, isindex, defaultobj ) {	\
	int __cmp;								\
	struct fd_list * __li;							\
	ret = 0;								\
	if (  ((defaultobj) != NULL) 						\
		   && (_O(defaultobj)->data. datafield  == value)) {		\
		if (result)							\
			*result = _O(defaultobj);				\
		goto end;							\
	}									\
681
682
	for  (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) {	\
		__cmp= ORDER_scalar(value, _O(__li->o)->data. datafield );	\
Sebastien Decugis's avatar
Sebastien Decugis committed
683
684
		if (__cmp == 0) {						\
			if (result)						\
685
				*result = _O(__li->o);				\
Sebastien Decugis's avatar
Sebastien Decugis committed
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
			goto end;						\
		}								\
		if ((isindex) && (__cmp < 0))					\
			break;							\
	}									\
	if (result)								\
		*result = NULL;							\
	else									\
		ret = ENOENT;							\
}

/* For search of commands in lists by code and flag. R_flag_val = 0 or CMD_FLAG_REQUEST */
#define SEARCH_codefl( value, R_flag_val, sentinel) {					\
	int __cmp;								\
	struct fd_list * __li;							\
	ret = 0;								\
702
	for  (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) {	\
Sebastien Decugis's avatar
Sebastien Decugis committed
703
		__cmp = ORDER_scalar(value, 					\
704
				_O(__li->o)->data.cmd.cmd_code );		\
Sebastien Decugis's avatar
Sebastien Decugis committed
705
706
		if (__cmp == 0) {						\
			uint8_t __mask, __val;					\
707
708
			__mask = _O(__li->o)->data.cmd.cmd_flag_mask;		\
			__val  = _O(__li->o)->data.cmd.cmd_flag_val;		\
Sebastien Decugis's avatar
Sebastien Decugis committed
709
710
711
712
713
			if ( ! (__mask & CMD_FLAG_REQUEST) )			\
				continue;					\
			if ( ( __val & CMD_FLAG_REQUEST ) != R_flag_val )	\
				continue;					\
			if (result)						\
714
				*result = _O(__li->o);				\
Sebastien Decugis's avatar
Sebastien Decugis committed
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
			goto end;						\
		}								\
		if (__cmp < 0)							\
			break;							\
	}									\
	if (result)								\
		*result = NULL;							\
	else									\
		ret = ENOENT;							\
}

static int search_vendor ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
{
	int ret = 0;
	vendor_id_t id;
	
	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
	
	switch (criteria) {
		case VENDOR_BY_ID:
			id = *(vendor_id_t *) what;
			SEARCH_scalar( id, &dict->dict_vendors.list[0], vendor.vendor_id, 1, &dict->dict_vendors );
			break;
				
		case VENDOR_BY_NAME:
			/* "what" is a vendor name */
741
			SEARCH_os0( what, &dict->dict_vendors.list[0], vendor.vendor_name, 0);
Sebastien Decugis's avatar
Sebastien Decugis committed
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
			break;
			
		case VENDOR_OF_APPLICATION:
			/* "what" should be an application object */
			SEARCH_childs_parent( DICT_APPLICATION, &dict->dict_vendors );
			break;
		
		default:
			/* Invalid criteria */
			CHECK_PARAMS( criteria = 0 );
	}
end:
	return ret;
}

static int search_application ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
{
	int ret = 0;
	application_id_t id;
	
	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
	
	switch (criteria) {
		case APPLICATION_BY_ID:
			id = *(application_id_t *) what;

			SEARCH_scalar( id, &dict->dict_applications.list[0],  application.application_id, 1, &dict->dict_applications );
			break;
				
		case APPLICATION_BY_NAME:
			/* "what" is an application name */
773
			SEARCH_os0( what, &dict->dict_applications.list[0], application.application_name, 0);
Sebastien Decugis's avatar
Sebastien Decugis committed
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
			break;
			
		case APPLICATION_OF_TYPE:
			/* "what" should be a type object */
			SEARCH_childs_parent( DICT_TYPE, &dict->dict_applications );
			break;
		
		case APPLICATION_OF_COMMAND:
			/* "what" should be a command object */
			SEARCH_childs_parent( DICT_COMMAND, &dict->dict_applications );
			break;
		
		default:
			/* Invalid criteria */
			CHECK_PARAMS( criteria = 0 );
	}
end:
	return ret;
}

static int search_type ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
{
	int ret = 0;
	
	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
	
	switch (criteria) {
		case TYPE_BY_NAME:
			/* "what" is a type name */
803
			SEARCH_os0( what, &dict->dict_types, type.type_name, 1);
Sebastien Decugis's avatar
Sebastien Decugis committed
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
			break;
			
		case TYPE_OF_ENUMVAL:
			/* "what" should be a type_enum object */
			SEARCH_childs_parent( DICT_ENUMVAL, NULL );
			break;
		
		case TYPE_OF_AVP:
			/* "what" should be an avp object */
			SEARCH_childs_parent( DICT_AVP, NULL );
			break;
		
				
		default:
			/* Invalid criteria */
			CHECK_PARAMS( criteria = 0 );
	}
end:
	return ret;
}

static int search_enumval ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
{
	int ret = 0;
	
	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
	
	switch (criteria) {
		case ENUMVAL_BY_STRUCT:
			{
				struct dict_object * parent = NULL;
				struct dict_enumval_request * _what = (struct dict_enumval_request *) what;
				
				CHECK_PARAMS(  _what  &&  ( _what->type_obj || _what->type_name )  );
				
				if (_what->type_obj != NULL) {
					parent = _what->type_obj;
					CHECK_PARAMS(  verify_object(parent)  &&  (parent->type == DICT_TYPE)  );
				} else {
					/* We received only the type name, we must find it first */
					CHECK_FCT_DO( search_type( dict, TYPE_BY_NAME, _what->type_name, &parent ),
							CHECK_PARAMS( 0 ) );
				}
				
				/* From here the "parent" object is valid */
				
				if ( _what->search.enum_name != NULL ) {
					/* We are looking for this string */
852
					SEARCH_os0(  _what->search.enum_name, &parent->list[1], enumval.enum_name, 1 );
Sebastien Decugis's avatar
Sebastien Decugis committed
853
854
855
856
				} else {
					/* We are looking for the value in enum_value */
					switch (parent->data.type.type_base) {
						case AVP_TYPE_OCTETSTRING:
857
							SEARCH_os(	 _what->search.enum_value.os.data, 
Sebastien Decugis's avatar
Sebastien Decugis committed
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
									 _what->search.enum_value.os.len, 
									 &parent->list[2], 
									 enumval.enum_value.os , 
									 1 );
							break;

						case AVP_TYPE_INTEGER32:
							SEARCH_scalar(	_what->search.enum_value.i32,
									&parent->list[2],
									enumval.enum_value.i32,
									1,
									(struct dict_object *)NULL);
							break;
							
						case AVP_TYPE_INTEGER64:
							SEARCH_scalar(	_what->search.enum_value.i64,
									&parent->list[2],
									enumval.enum_value.i64,
									1,
									(struct dict_object *)NULL);
							break;
							
						case AVP_TYPE_UNSIGNED32:
							SEARCH_scalar(	_what->search.enum_value.u32,
									&parent->list[2],
									enumval.enum_value.u32,
									1,
									(struct dict_object *)NULL);
							break;
							
						case AVP_TYPE_UNSIGNED64:
							SEARCH_scalar(	_what->search.enum_value.u64,
									&parent->list[2],
									enumval.enum_value.u64,
									1,
									(struct dict_object *)NULL);
							break;
							
						case AVP_TYPE_FLOAT32:
							SEARCH_scalar(	_what->search.enum_value.f32,
									&parent->list[2],
									enumval.enum_value.f32,
									1,
									(struct dict_object *)NULL);
							break;
							
						case AVP_TYPE_FLOAT64:
							SEARCH_scalar(	_what->search.enum_value.f64,
									&parent->list[2],
									enumval.enum_value.f64,
									1,
									(struct dict_object *)NULL);
							break;
							
						default:
							/* Invalid parent type basetype */
							CHECK_PARAMS( parent = NULL );
					}
				}
				
			}
			break;
		
				
		default:
			/* Invalid criteria */
			CHECK_PARAMS( criteria = 0 );
	}
end:
	return ret;
}

static int search_avp ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
{
	int ret = 0;
	
	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
	
	switch (criteria) {
		case AVP_BY_CODE:
			{
				avp_code_t code;
				code = *(avp_code_t *) what;

				SEARCH_scalar( code, &dict->dict_vendors.list[1],  avp.avp_code, 1, (struct dict_object *)NULL );
			}
			break;
				
		case AVP_BY_NAME:
			/* "what" is the AVP name, vendor 0 */
948
			SEARCH_os0( what, &dict->dict_vendors.list[2], avp.avp_name, 1);
Sebastien Decugis's avatar
Sebastien Decugis committed
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
			break;
			
		case AVP_BY_CODE_AND_VENDOR:
		case AVP_BY_NAME_AND_VENDOR:
			{
				struct dict_avp_request * _what = (struct dict_avp_request *) what;
				struct dict_object * vendor = NULL;
				
				CHECK_PARAMS( (criteria != AVP_BY_NAME_AND_VENDOR) || _what->avp_name  );
				
				/* Now look for the vendor first */
				CHECK_FCT( search_vendor( dict, VENDOR_BY_ID, &_what->avp_vendor, &vendor ) );
				if (vendor == NULL) {
					if (result)
						*result = NULL;
					else
						ret = ENOENT;
					goto end;
				}
				
				/* We now have our vendor = head of the appropriate avp list */
				if (criteria == AVP_BY_NAME_AND_VENDOR) {
971
					SEARCH_os0( _what->avp_name, &vendor->list[2], avp.avp_name, 1);
Sebastien Decugis's avatar
Sebastien Decugis committed
972
973
				} else {
					/* AVP_BY_CODE_AND_VENDOR */
974
					SEARCH_scalar( _what->avp_code, &vendor->list[1], avp.avp_code, 1, (struct dict_object *)NULL );
Sebastien Decugis's avatar
Sebastien Decugis committed
975
976
977
978
				}
			}
			break;
		
979
980
981
		case AVP_BY_NAME_ALL_VENDORS:
			{
				struct fd_list * li;
982
				size_t wl = strlen((char *)what);
983
984
				
				/* First, search for vendor 0 */
985
				SEARCH_os0_l( what, wl, &dict->dict_vendors.list[2], avp.avp_name, 1);
986
987
988
				
				/* If not found, loop for all vendors, until found */
				for (li = dict->dict_vendors.list[0].next; li != &dict->dict_vendors.list[0]; li = li->next) {
989
					SEARCH_os0_l( what, wl, &_O(li->o)->list[2], avp.avp_name, 1);
990
991
992
993
				}
			}
			break;
		
Sebastien Decugis's avatar
Sebastien Decugis committed
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
		default:
			/* Invalid criteria */
			CHECK_PARAMS( criteria = 0 );
	}
end:
	return ret;
}

static int search_cmd ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
{
	int ret = 0;
	
	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
	
	switch (criteria) {
		case CMD_BY_NAME:
			/* "what" is a command name */
1011
			SEARCH_os0( what, &dict->dict_cmd_name, cmd.cmd_name, 1);
Sebastien Decugis's avatar
Sebastien Decugis committed
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
			break;
			
		case CMD_BY_CODE_R:
		case CMD_BY_CODE_A:
			{
				command_code_t code;
				uint8_t searchfl = 0;
				
				/* The command code that we are searching */
				code = *(command_code_t *) what;
				
				/* The flag (request or answer) of the command we are searching */
				if (criteria == CMD_BY_CODE_R) {
					searchfl = CMD_FLAG_REQUEST;
				}
				
				/* perform the search */
				SEARCH_codefl( code, searchfl, &dict->dict_cmd_code );
			}
			break;
				
		case CMD_ANSWER:
			{
				/* "what" is a command object of type "request" */
				struct dict_object * req = (struct dict_object *) what;
				struct dict_object * ans = NULL;
				
				CHECK_PARAMS( verify_object(req) 
						&& (req->type == DICT_COMMAND)
						&& (req->data.cmd.cmd_flag_mask & CMD_FLAG_REQUEST)
						&& (req->data.cmd.cmd_flag_val  & CMD_FLAG_REQUEST) );
				
				/* The answer is supposed to be the next element in the list, if it exists */
				ans = req->list[1].next->o;
				if ( ans == NULL ) {
					TRACE_DEBUG( FULL, "the request was the last element in the list" );
					ret = ENOENT;
					goto end;
				}
				
				/* Now check that the ans element is really the correct one */
				if (  (ans->data.cmd.cmd_code != req->data.cmd.cmd_code)
				   || (!(ans->data.cmd.cmd_flag_mask & CMD_FLAG_REQUEST))
				   || (  ans->data.cmd.cmd_flag_val  & CMD_FLAG_REQUEST ) ) {
					TRACE_DEBUG( FULL, "the answer does not follow the request in the list" );
					ret = ENOENT;
					goto end;
				}
				
				if (result)
					*result = ans;
				ret = 0;
			}						
			break;
			
		default:
			/* Invalid criteria */
			CHECK_PARAMS( criteria = 0 );
	}
end:
	return ret;
}

static int search_rule ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
{
	int ret = 0;
	
	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
	
	switch (criteria) {
		case RULE_BY_AVP_AND_PARENT:
			{
				struct dict_object * parent = NULL;
				struct dict_object * avp = NULL;
				struct dict_rule_request * _what = (struct dict_rule_request *) what;
				
				CHECK_PARAMS( _what 
						&& (parent = _what->rule_parent)
						&& (avp    = _what->rule_avp   ) );
				
				CHECK_PARAMS( verify_object(parent) 
						&& ((parent->type == DICT_COMMAND) 
						 || ((parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED))) );
				
				CHECK_PARAMS( verify_object(avp) && (avp->type == DICT_AVP) );
				
				/* Perform the search */
1099
				SEARCH_ruleavpname( avp->data.avp.avp_name, avp->datastr_len, &parent->list[2]);
Sebastien Decugis's avatar
Sebastien Decugis committed
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
				
			}
			break;
			
		default:
			/* Invalid criteria */
			CHECK_PARAMS( criteria = 0 );
	}
end:
	return ret;
}

/*******************************************************************************************************/
/*******************************************************************************************************/
/*                                                                                                     */
/*                                  Dump / debug functions                                             */
/*                                                                                                     */
/*******************************************************************************************************/
/*******************************************************************************************************/
/* The following functions are used to debug the module, and allow to print out the content of the dictionary */
static void dump_vendor_data ( void * data )
{
	struct dict_vendor_data * vendor = (struct dict_vendor_data *)data;
	
	fd_log_debug("data: %-6u \"%s\"", vendor->vendor_id, vendor->vendor_name);
}
static void dump_application_data ( void * data )
{
	struct dict_application_data * appli = (struct dict_application_data *) data;
	fd_log_debug("data: %-6u \"%s\"", appli->application_id, appli->application_name);
}
static void dump_type_data ( void * data )
{
	struct dict_type_data * type = ( struct dict_type_data * ) data;
	
	fd_log_debug("data: %-12s \"%s\"", 
			type_base_name[type->type_base], 
			type->type_name);
}
static void dump_enumval_data ( struct dict_enumval_data * enumval, enum dict_avp_basetype type )
{
	const int LEN_MAX = 20;
	fd_log_debug("data: (%-12s) \"%s\" -> ", type_base_name[type], enumval->enum_name);
	switch (type) {
		case AVP_TYPE_OCTETSTRING:
			{
				int i, n=LEN_MAX;
				if (enumval->enum_value.os.len < LEN_MAX)
					n = enumval->enum_value.os.len;
				for (i=0; i < n; i++)
					fd_log_debug("0x%02.2X/'%c' ", enumval->enum_value.os.data[i], ASCII(enumval->enum_value.os.data[i]));
				if (n == LEN_MAX)
					fd_log_debug("...");
			}
			break;
		
		case AVP_TYPE_INTEGER32:
			fd_log_debug("%i", enumval->enum_value.i32);
			break;

		case AVP_TYPE_INTEGER64:
			fd_log_debug("%lli", enumval->enum_value.i64);
			break;

		case AVP_TYPE_UNSIGNED32:
			fd_log_debug("%u", enumval->enum_value.u32);
			break;

		case AVP_TYPE_UNSIGNED64:
			fd_log_debug("%llu", enumval->enum_value.u64);
			break;

		case AVP_TYPE_FLOAT32:
			fd_log_debug("%f", enumval->enum_value.f32);
			break;

		case AVP_TYPE_FLOAT64:
			fd_log_debug("%g", enumval->enum_value.f64);
			break;
		
		default:
			fd_log_debug("??? (ERROR unknown type %d)", type);
	}
}
static void dump_avp_data ( void * data )
{
	struct dict_avp_data * avp = (struct dict_avp_data * ) data;
	fd_log_debug("data: v/m:" DUMP_AVPFL_str "/" DUMP_AVPFL_str ", %12s, %-6u \"%s\"", 
			DUMP_AVPFL_val(avp->avp_flag_val), 
			DUMP_AVPFL_val(avp->avp_flag_mask), 
			type_base_name[avp->avp_basetype], 
			avp->avp_code, 
			avp->avp_name );
}
static void dump_command_data ( void * data )
{
	struct dict_cmd_data * cmd = (struct dict_cmd_data *) data;
	fd_log_debug("data: v/m:" DUMP_CMDFL_str "/" DUMP_CMDFL_str ", %-6u \"%s\"", 
			DUMP_CMDFL_val(cmd->cmd_flag_val), DUMP_CMDFL_val(cmd->cmd_flag_mask), cmd->cmd_code, cmd->cmd_name);
}
static void dump_rule_data ( void * data )
{
	struct dict_rule_data * rule = (struct dict_rule_data * )data;
	fd_log_debug("data: pos:%d ord:%d m/M:%2d/%2d avp:\"%s\"", 
			rule->rule_position, 
			rule->rule_order, 
			rule->rule_min, 
			rule->rule_max,
			rule->rule_avp->data.avp.avp_name);
}

static void dump_object ( struct dict_object * obj, int parents, int depth, int indent );

static void dump_list ( struct fd_list * sentinel, int parents, int depth, int indent )
{
	struct fd_list * li = sentinel;
	/* We don't lock here, the caller must have taken the dictionary lock for reading already */
	while (li->next != sentinel)
	{
		li = li->next;
		dump_object( _O(li->o), parents, depth, indent );
	}
}

static void dump_object ( struct dict_object * obj, int parents, int depth, int indent )
{
	if (obj == NULL)
		return;
	
	if (parents)
		dump_object (obj->parent, parents-1, 0, indent + 1 );
	
	fd_log_debug("%*s@%p: %s%s (p:%-9p) ", 
			indent,
			"",
			obj, 
			verify_object(obj) ? "" : "INVALID ", 
			_OBINFO(obj).name, 
			obj->parent);
	
	if (obj->type == DICT_ENUMVAL)
		dump_enumval_data ( &obj->data.enumval, obj->parent->data.type.type_base );
	else
		_OBINFO(obj).dump_data(&obj->data);
	
	fd_log_debug("\n");
	
	if (depth) {
		int i;
		for (i=0; i<NB_LISTS_PER_OBJ; i++) {
			if ((obj->list[i].o == NULL) && (obj->list[i].next != &obj->list[i])) {
				fd_log_debug("%*s>%p: list[%d]:\n", indent, "", obj, i);
				dump_list(&obj->list[i], parents, depth - 1, indent + 2);
			}
		}
	}
}

void fd_dict_dump_object(struct dict_object * obj)
{
	fd_log_debug("Dictionary object %p dump:\n", obj);
	dump_object( obj, 1, 2, 2 );
}

void fd_dict_dump(struct dictionary * dict)
{
	int i;
	
	CHECK_PARAMS_DO(dict && (dict->dict_eyec == DICT_EYECATCHER), return);
	
	CHECK_POSIX_DO(  pthread_rwlock_rdlock( &dict->dict_lock ), /* ignore */  );
	
	fd_log_debug("######################################################\n");
	fd_log_debug("###### Dumping vendors, AVPs and related rules #######\n");
	
	dump_object( &dict->dict_vendors, 0, 3, 0 );
	
1277
	fd_log_debug("######          Dumping applications           #######\n");
Sebastien Decugis's avatar
Sebastien Decugis committed
1278
1279
1280

	dump_object( &dict->dict_applications, 0, 1, 0 );
	
1281
	fd_log_debug("######             Dumping types               #######\n");
Sebastien Decugis's avatar
Sebastien Decugis committed
1282
1283
1284

	dump_list( &dict->dict_types, 0, 2, 0 );
	
1285
	fd_log_debug("######      Dumping commands per name          #######\n");
Sebastien Decugis's avatar
Sebastien Decugis committed
1286
1287
1288

	dump_list( &dict->dict_cmd_name, 0, 2, 0 );
	
1289
	fd_log_debug("######   Dumping commands per code and flags   #######\n");
Sebastien Decugis's avatar
Sebastien Decugis committed
1290
1291
1292

	dump_list( &dict->dict_cmd_code, 0, 0, 0 );
	
1293
	fd_log_debug("######             Statistics                  #######\n");
Sebastien Decugis's avatar
Sebastien Decugis committed
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303

	for (i=1; i<=DICT_TYPE_MAX; i++)
		fd_log_debug(" %5d objects of type %s\n", dict->dict_count[i], dict_obj_info[i].name);
	
	fd_log_debug("######################################################\n");
	
	/* Free the rwlock */
	CHECK_POSIX_DO(  pthread_rwlock_unlock( &dict->dict_lock ), /* ignore */  );
}

1304
1305
1306
/**************************** Dump AVP values ********************************/

/* Default dump functions */
1307
static void dump_val_os(union avp_value * value, FILE * fstr)
1308
1309
1310
{
	int i;
	for (i = 0; i < value->os.len; i++) {
1311
		if (i == 24) { /* Dump only up to 24 bytes of the buffer */
1312
			fd_log_debug_fstr(fstr, "[...] (len=%zd)", value->os.len);
1313
1314
			break;
		}
1315
		fd_log_debug_fstr(fstr, "%02.2X ", value->os.data[i]);
1316
1317
1318
	}
}

1319
static void dump_val_i32(union avp_value * value, FILE * fstr)
1320
{
1321
	fd_log_debug_fstr(fstr, "%i (0x%x)", value->i32, value->i32);
1322
1323
}

1324
static void dump_val_i64(union avp_value * value, FILE * fstr)
1325
{
1326
	fd_log_debug_fstr(fstr, "%lli (0x%llx)", value->i64, value->i64);
1327
1328
}

1329
static void dump_val_u32(union avp_value * value, FILE * fstr)
1330
{
1331
	fd_log_debug_fstr(fstr, "%u (0x%x)", value->u32, value->u32);
1332
1333
}

1334
static void dump_val_u64(union avp_value * value, FILE * fstr)
1335
{
1336
	fd_log_debug_fstr(fstr, "%llu (0x%llx)", value->u64, value->u64);
1337
1338
}

1339
static void dump_val_f32(union avp_value * value, FILE * fstr)
1340
{
1341
	fd_log_debug_fstr(fstr, "%f", value->f32);
1342
1343
}

1344
static void dump_val_f64(union avp_value * value, FILE * fstr)
1345
{
1346
	fd_log_debug_fstr(fstr, "%g", value->f64);
1347
1348
1349
}

/* Get the dump function for basic dict_avp_basetype */
1350
static void (*get_default_dump_val_cb(enum dict_avp_basetype datatype))(union avp_value *, FILE *)
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
{
	switch (datatype) {
		case AVP_TYPE_OCTETSTRING:
			return &dump_val_os;
		
		case AVP_TYPE_INTEGER32:
			return &dump_val_i32;

		case AVP_TYPE_INTEGER64:
			return &dump_val_i64;

		case AVP_TYPE_UNSIGNED32:
			return &dump_val_u32;

		case AVP_TYPE_UNSIGNED64:
			return &dump_val_u64;

		case AVP_TYPE_FLOAT32:
			return &dump_val_f32;

		case AVP_TYPE_FLOAT64:
			return &dump_val_f64;
		
		case AVP_TYPE_GROUPED:
			TRACE_DEBUG(FULL, "error: grouped AVP with a value!");
	}
	return NULL;
}

/* indent inside an object (duplicate from messages.c) */
#define INOBJHDR 	"%*s   "
#define INOBJHDRVAL 	indent<0 ? 1 : indent, indent<0 ? "-" : "|"

/* Formater for the AVP value dump line */
1385
static void dump_avp_val(union avp_value *avp_value, void (*dump_val_cb)(union avp_value *, FILE *), enum dict_avp_basetype datatype, char * type_name, char * const_name, int indent, FILE * fstr)
1386
1387
{
	/* Header for all AVP values dumps: */
1388
	fd_log_debug_fstr(fstr, INOBJHDR "value ", INOBJHDRVAL);
1389
1390
1391
	
	/* If the type is provided, write it */
	if (type_name)
1392
		fd_log_debug_fstr(fstr, "t: '%s' ", type_name);
1393
1394
	
	/* Always give the base datatype anyway */
1395
	fd_log_debug_fstr(fstr, "(%s) ", type_base_name[datatype]);
1396
1397
	
	/* Now, the value */
1398
	fd_log_debug_fstr(fstr, "v: ");
1399
	if (const_name)
1400
1401
		fd_log_debug_fstr(fstr, "'%s' (", const_name);
	(*dump_val_cb)(avp_value, fstr);
1402
	if (const_name)
1403
		fd_log_debug_fstr(fstr, ")");
1404
1405
	
	/* Done! */
1406
	fd_log_debug_fstr(fstr, "\n");
1407
1408
1409
}

/* Dump the value of an AVP of known type */
1410
void fd_dict_dump_avp_value(union avp_value *avp_value, struct dict_object * model, int indent, FILE * fstr)
1411
{
1412
	void (*dump_val_cb)(union avp_value *avp_value, FILE * fstr);
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
	struct dict_object * type = NULL;
	char * type_name = NULL;
	char * const_name = NULL;
	
	/* Check the parameters are correct */
	CHECK_PARAMS_DO( avp_value && verify_object(model) && (model->type == DICT_AVP), return );
	
	/* Default: display the value with the formatter for the AVP datatype */
	CHECK_PARAMS_DO( dump_val_cb = get_default_dump_val_cb(model->data.avp.avp_basetype), return );
	
	/* Get the type definition of this AVP */
	type = model->parent;
	if (type) {
		struct dict_enumval_request  request;
		struct dict_object * enumval = NULL;
		
		type_name = type->data.type.type_name;
		
		/* overwrite the dump function ? */
		if (type->data.type.type_dump)
			dump_val_cb = type->data.type.type_dump;
		
		/* Now check if the AVP value matches a constant */
		memset(&request, 0, sizeof(request));
		request.type_obj = type;
		memcpy(&request.search.enum_value, avp_value, sizeof(union avp_value));
		/* bypass checks */
		if ((search_enumval( type->dico, ENUMVAL_BY_STRUCT, &request, &enumval ) == 0) && (enumval)) {
			/* We found a cosntant, get its name */
			const_name = enumval->data.enumval.enum_name;
		}
	}
	
	/* And finally, dump the value */
1447
	dump_avp_val(avp_value, dump_val_cb, model->data.avp.avp_basetype, type_name, const_name, indent, fstr);
1448
1449
}

Sebastien Decugis's avatar
Sebastien Decugis committed
1450
1451
1452
1453
1454
1455
1456
1457
/*******************************************************************************************************/
/*******************************************************************************************************/
/*                                                                                                     */
/*                                  Exported functions                                                 */
/*                                                                                                     */
/*******************************************************************************************************/
/*******************************************************************************************************/

1458
/* These are the functions exported outside libfreeDiameter. */
Sebastien Decugis's avatar
Sebastien Decugis committed
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501

/* Get the data associated to an object */
int fd_dict_gettype ( struct dict_object * object, enum dict_object_type * type)
{
	TRACE_ENTRY("%p %p", object, type);
	
	CHECK_PARAMS( type && verify_object(object) );
	
	/* Copy the value and return */
	*type = object->type;
	return 0;
}

int fd_dict_getdict ( struct dict_object * object, struct dictionary ** dict)
{
	TRACE_ENTRY("%p %p", object, dict);
	
	CHECK_PARAMS( dict && verify_object(object) );
	
	/* Copy the value and return */
	*dict = object->dico;
	return 0;
}


/* Get the data associated to an object */
int fd_dict_getval ( struct dict_object * object, void * val)
{
	TRACE_ENTRY("%p %p", object, val);
	
	CHECK_PARAMS( val && verify_object(object) );
	
	/* Copy the value and return */
	memcpy(val, &object->data, _OBINFO(object).datasize);;
	return 0;
}

/* Add a new object in the dictionary */
int fd_dict_new ( struct dictionary * dict, enum dict_object_type type, void * data, struct dict_object * parent, struct dict_object **ref )
{
	int ret = 0;
	struct dict_object * new = NULL;
	struct dict_object * vendor = NULL;
Sebastien Decugis's avatar
Sebastien Decugis committed
1502
	struct dict_object * locref = NULL;
Sebastien Decugis's avatar
Sebastien Decugis committed
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
	
	TRACE_ENTRY("%p %d(%s) %p %p %p", dict, type, dict_obj_info[CHECK_TYPE(type) ? type : 0].name, data, parent, ref);
	
	/* Check parameters */
	CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && CHECK_TYPE(type) && data  );
	
	/* Check the "parent" parameter */
	switch (dict_obj_info[type].parent) {
		case 0:	/* parent is forbidden */
			CHECK_PARAMS( parent == NULL );
		
		case 1:	/* parent is optional */
			if (parent == NULL)
				break;
		
		case 2: /* parent is mandatory */
			CHECK_PARAMS(  verify_object(parent)  );
			
			if (type == DICT_RULE ) { /* Special case : grouped AVP or Command parents are allowed */
				CHECK_PARAMS( (parent->type == DICT_COMMAND ) 
						|| ( (parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED ) ) );
			} else {
				CHECK_PARAMS( parent->type == dict_obj_info[type].parenttype );
			}
	}
	
	/* For AVP object, we must also check that the "vendor" referenced exists */
	if (type == DICT_AVP) {
		CHECK_FCT_DO(  fd_dict_search( dict, DICT_VENDOR, VENDOR_BY_ID, &(((struct dict_avp_data *)data)->avp_vendor), (void*)&vendor, ENOENT ),
Sebastien Decugis's avatar
Sebastien Decugis committed
1532
			{ TRACE_DEBUG(INFO, "Unable to find vendor '%d' referenced in the AVP data", ((struct dict_avp_data *)data)->avp_vendor); return EINVAL; }  );
Sebastien Decugis's avatar
Sebastien Decugis committed
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
		
		/* Also check if a parent is provided, that the type are the same */
		if (parent) {
			CHECK_PARAMS(  parent->data.type.type_base == ((struct dict_avp_data *)data)->avp_basetype  );
		}
	}
	
	/* For RULE object, we must also check that the "avp" referenced exists */
	if (type == DICT_RULE) {
		CHECK_PARAMS(  verify_object(((struct dict_rule_data *)data)->rule_avp)  );
		CHECK_PARAMS(  ((struct dict_rule_data *)data)->rule_avp->type == DICT_AVP  );
	}
	
	/* For COMMAND object, check that the 'R' flag is fixed */
	if (type == DICT_COMMAND) {
		CHECK_PARAMS( ((struct dict_cmd_data *)data)->cmd_flag_mask & CMD_FLAG_REQUEST   );
	}
	
1551
1552
1553
1554
1555
1556
1557
1558
	/* We have to check that the new values are not equal to the sentinels */
	if (type == DICT_VENDOR) {
		CHECK_PARAMS( ((struct dict_vendor_data *)data)->vendor_id != 0   );
	}
	if (type == DICT_APPLICATION) {
		CHECK_PARAMS( ((struct dict_application_data *)data)->application_id != 0   );
	}
	
Sebastien Decugis's avatar
Sebastien Decugis committed
1559
1560
1561
1562
1563
	/* Parameters are valid, create the new object */
	CHECK_MALLOC(  new = malloc(sizeof(struct dict_object))  );
	
	/* Initialize the data of the new object */
	init_object(new, type);
1564
	init_object_data(new, data, type);
Sebastien Decugis's avatar
Sebastien Decugis committed
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
	new->dico = dict;
	new->parent = parent;
	
	/* We will change the dictionary => acquire the write lock */
	CHECK_POSIX_DO(  ret = pthread_rwlock_wrlock(&dict->dict_lock),  goto error_free  );
	
	/* Now link the object -- this also checks that no object with same keys already exists */
	switch (type) {
		case DICT_VENDOR:
			/* A vendor object is linked in the g_dict_vendors.list[0], by their id */
Sebastien Decugis's avatar
Sebastien Decugis committed
1575
1576
1577
			ret = fd_list_insert_ordered ( &dict->dict_vendors.list[0], &new->list[0], (int (*)(void*, void *))order_vendor_by_id, (void **)&locref );
			if (ret)
				goto error_unlock;
Sebastien Decugis's avatar
Sebastien Decugis committed
1578
1579
1580
1581
			break;
		
		case DICT_APPLICATION:
			/* An application object is linked in the g_dict_applciations.list[0], by their id */
Sebastien Decugis's avatar
Sebastien Decugis committed
1582
1583
1584
			ret = fd_list_insert_ordered ( &dict->dict_applications.list[0], &new->list[0], (int (*)(void*, void *))order_appli_by_id, (void **)&locref );
			if (ret)
				goto error_unlock;
Sebastien Decugis's avatar
Sebastien Decugis committed
1585
1586
1587
1588
			break;
		
		case DICT_TYPE:
			/* A type object is linked in g_list_types by its name */
Sebastien Decugis's avatar
Sebastien Decugis committed
1589
1590
1591
			ret = fd_list_insert_ordered ( &dict->dict_types, &new->list[0], (int (*)(void*, void *))order_type_by_name, (void **)&locref );
			if (ret)
				goto error_unlock;
Sebastien Decugis's avatar
Sebastien Decugis committed
1592
1593
1594
1595
			break;
		
		case DICT_ENUMVAL:
			/* A type_enum object is linked in it's parent 'type' object lists 1 and 2 by its name and values */
Sebastien Decugis's avatar
Sebastien Decugis committed
1596
1597
1598
1599
1600
1601
1602
1603
1604
			ret = fd_list_insert_ordered ( &parent->list[1], &new->list[0], (int (*)(void*, void *))order_enum_by_name, (void **)&locref );
			if (ret)
				goto error_unlock;
			
			ret = fd_list_insert_ordered ( &parent->list[2], &new->list[1], (int (*)(void*, void *))order_enum_by_val, (void **)&locref );
			if (ret) { 
				fd_list_unlink(&new->list[0]); 
				goto error_unlock; 
			}
Sebastien Decugis's avatar
Sebastien Decugis committed
1605
1606
1607
1608
			break;
		
		case DICT_AVP:
			/* An avp object is linked in lists 1 and 2 of its vendor, by code and name */
Sebastien Decugis's avatar
Sebastien Decugis committed
1609
1610
1611
1612
1613
1614
1615
1616
1617
			ret = fd_list_insert_ordered ( &vendor->list[1], &new->list[0], (int (*)(void*, void *))order_avp_by_code, (void **)&locref );
			if (ret)
				goto error_unlock;
			
			ret = fd_list_insert_ordered ( &vendor->list[2], &new->list[1], (int (*)(void*, void *))order_avp_by_name, (void **)&locref );
			if (ret) {
				fd_list_unlink(&new->list[0]);
				goto error_unlock;
			}
Sebastien Decugis's avatar
Sebastien Decugis committed
1618
1619
1620
1621
			break;
			
		case DICT_COMMAND:
			/* A command object is linked in g_list_cmd_name and g_list_cmd_code by its name and code */
Sebastien Decugis's avatar
Sebastien Decugis committed
1622
1623
1624
1625
1626
1627
1628
1629
1630
			ret = fd_list_insert_ordered ( &dict->dict_cmd_code, &new->list[1], (int (*)(void*, void *))order_cmd_by_codefl, (void **)&locref );
			if (ret)
				goto error_unlock;
			
			ret = fd_list_insert_ordered ( &dict->dict_cmd_name, &new->list[0], (int (*)(void*, void *))order_cmd_by_name, (void **)&locref );
			if (ret) {
				fd_list_unlink(&new->list[1]);
				goto error_unlock;
			}
Sebastien Decugis's avatar
Sebastien Decugis committed
1631
1632
1633
1634
			break;
		
		case DICT_RULE:
			/* A rule object is linked in list[2] of its parent command or AVP by the name of the AVP it refers */
1635
			ret = fd_list_insert_ordered ( &parent->list[2], &new->list[0], (int (*)(void*, void *))order_rule_by_avpvc, (void **)&locref );
Sebastien Decugis's avatar
Sebastien Decugis committed
1636
1637
			if (ret)
				goto error_unlock;
Sebastien Decugis's avatar
Sebastien Decugis committed
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
			break;
			
		default:
			ASSERT(0);
	}
	
	/* A new object has been created, increment the global counter */
	dict->dict_count[type]++;
	
	/* Unlock the dictionary */
	CHECK_POSIX_DO(  ret = pthread_rwlock_unlock(&dict->dict_lock),  goto error_free  );
	
	/* Save the pointer to the new object */
	if (ref)
		*ref = new;
	
	return 0;
	
error_unlock:
	CHECK_POSIX_DO(  pthread_rwlock_unlock(&dict->dict_lock),  /* continue */  );
Sebastien Decugis's avatar
Sebastien Decugis committed
1658
1659
1660
1661
	if (ret == EEXIST) {
		/* We have a duplicate key in locref. Check if the pointed object is the same or not */
		switch (type) {
			case DICT_VENDOR:
1662
1663
1664
				/* if we are here, it means the two vendors id are identical */
				if (fd_os_cmp(locref->data.vendor.vendor_name, locref->datastr_len, 
						new->data.vendor.vendor_name, new->datastr_len)) {
Sebastien Decugis's avatar
Sebastien Decugis committed
1665
1666
1667
1668
1669
1670
1671
1672
1673
					TRACE_DEBUG(FULL, "Conflicting vendor name");
					break;
				}
				/* Otherwise (same name), we consider the function succeeded, since the (same) object is in the dictionary */
				ret = 0; 
				break;

			case DICT_APPLICATION:
				/* got same id */
1674
1675
				if (fd_os_cmp(locref->data.application.application_name, locref->datastr_len, 
						new->data.application.application_name, new->datastr_len)) {
Sebastien Decugis's avatar
Sebastien Decugis committed
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
					TRACE_DEBUG(FULL, "Conflicting application name");
					break;
				}
				ret = 0;
				break;

			case DICT_TYPE:
				/* got same name */
				if (locref->data.type.type_base != new->data.type.type_base) {
					TRACE_DEBUG(FULL, "Conflicting base type");
					break;
				}
				/* discard new definition only it a callback is provided and different from the previous one */
				if ((new->data.type.type_interpret) && (locref->data.type.type_interpret != new->data.type.type_interpret)) {
					TRACE_DEBUG(FULL, "Conflicting interpret cb");
					break;
				}
				if ((new->data.type.type_encode) && (locref->data.type.type_encode != new->data.type.type_encode)) {
					TRACE_DEBUG(FULL, "Conflicting encode cb");
					break;
				}
				if ((new->data.type.type_dump) && (locref->data.type.type_dump != new->data.type.type_dump)) {
					TRACE_DEBUG(FULL, "Conflicting dump cb");
					break;
				}
				ret = 0;
				break;

			case DICT_ENUMVAL:
				/* got either same name or same value. We check that both are true */
				if (order_enum_by_name(locref, new)) {
					TRACE_DEBUG(FULL, "Conflicting enum name");
					break;
				}
				if (order_enum_by_val(locref, new)) {
					TRACE_DEBUG(FULL, "Conflicting enum value");
					break;
				}
				ret = 0;
				break;

			case DICT_AVP:
				/* got either same name or code */
				if (order_avp_by_code(locref, new)) {
					TRACE_DEBUG(FULL, "Conflicting AVP code");
					break;
				}
				if (order_avp_by_name(locref, new)) {
					TRACE_DEBUG(FULL, "Conflicting AVP name");
					break;
				}
				if  (locref->data.avp.avp_vendor != new->data.avp.avp_vendor) {
					TRACE_DEBUG(FULL, "Conflicting AVP vendor");
					break;
				}
				if  (locref->data.avp.avp_flag_mask != new->data.avp.avp_flag_mask) {
					TRACE_DEBUG(FULL, "Conflicting AVP flags mask");
					break;
				}
				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)) {
					TRACE_DEBUG(FULL, "Conflicting AVP flags value");
					break;
				}
				if  (locref->data.avp.avp_basetype != new->data.avp.avp_basetype) {
					TRACE_DEBUG(FULL, "Conflicting AVP base type");
					break;
				}
				ret = 0;
				break;

			case DICT_COMMAND:
				/* We got either same name, or same code + R flag */
				if (order_cmd_by_name(locref, new)) {
					TRACE_DEBUG(FULL, "Conflicting command name");
					break;
				}
				if (locref->data.cmd.cmd_code != new->data.cmd.cmd_code) {
					TRACE_DEBUG(FULL, "Conflicting command code");
					break;
				}
				if (locref->data.cmd.cmd_flag_mask != new->data.cmd.cmd_flag_mask) {
					TRACE_DEBUG(FULL, "Conflicting command flags mask %hhx:%hhx", locref->data.cmd.cmd_flag_mask, new->data.cmd.cmd_flag_mask);
					break;
				}
				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)) {
					TRACE_DEBUG(FULL, "Conflicting command flags value");
					break;
				}
				ret = 0;
				break;

			case DICT_RULE:
1768
				/* Both rules point to the same AVPs (code & vendor) */
Sebastien Decugis's avatar
Sebastien Decugis committed
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
				if (locref->data.rule.rule_position != new->data.rule.rule_position) {
					TRACE_DEBUG(FULL, "Conflicting rule position");
					break;
				}
				if ( ((locref->data.rule.rule_position == RULE_FIXED_HEAD) ||
					(locref->data.rule.rule_position == RULE_FIXED_TAIL))
				    && (locref->data.rule.rule_order != new->data.rule.rule_order)) {
					TRACE_DEBUG(FULL, "Conflicting rule order");
					break;
				}
				if (locref->data.rule.rule_min != new->data.rule.rule_min) {
					int r1 = locref->data.rule.rule_min;
					int r2 = new->data.rule.rule_min;
					int p  = locref->data.rule.rule_position;
					if (  ((r1 != -1) && (r2 != -1)) /* none of the definitions contains the "default" value */
					   || ((p == RULE_OPTIONAL) && (r1 != 0) && (r2 != 0)) /* the other value is not 0 for an optional rule */
					   || ((r1 != 1) && (r2 != 1)) /* the other value is not 1 for another rule */
					) {
						TRACE_DEBUG(FULL, "Conflicting rule min");
						break;
					}
				}
				if (locref->data.rule.rule_max != new->data.rule.rule_max) {
					TRACE_DEBUG(FULL, "Conflicting rule max");
					break;
				}
				ret = 0;
				break;
		}
		if (ret) {
			TRACE_DEBUG(INFO, "An existing object with different non-key data was found: EEXIST");
		} else {
			TRACE_DEBUG(FULL, "An existing object with the same data was found, ignoring the error...");
		}
		if (ref)
			*ref = locref;
	} else {
		CHECK_FCT_DO( ret, ); /* log the error */ 
	}

Sebastien Decugis's avatar
Sebastien Decugis committed
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
error_free:
	free(new);
	return ret;
}

int fd_dict_search ( struct dictionary * dict, enum dict_object_type type, int criteria, void * what, struct dict_object **result, int retval )
{
	int ret = 0;
	
	TRACE_ENTRY("%p %d(%s) %d %p %p %d", dict, type, dict_obj_info[CHECK_TYPE(type) ? type : 0].name, criteria, what, result, retval);
	
	/* Check param */
	CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && CHECK_TYPE(type) );
	
	/* Lock the dictionary for reading */
	CHECK_POSIX(  pthread_rwlock_rdlock(&dict->dict_lock)  );
	
	/* Now call the type-specific search function */
	ret = dict_obj_info[type].search_fct (dict, criteria, what, result);
	
	/* Unlock */
	CHECK_POSIX(  pthread_rwlock_unlock(&dict->dict_lock)  );
	
	/* Update the return value as needed */
	if ((result != NULL) && (*result == NULL))
		ret = retval;
	
	return ret;
}

1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
/* Function to retrieve list of objects in the dictionary. Use with care (read only).

All returned list must be accessed like this:

  for (li = sentinel->next; li=li->next; li != sentinel) {
	struct dict_object * obj = li->o;
	...
  }

The following criteria are allowed, with corresponding parent. 
The parent is either struct dictionary * or struct dict_object *
		
VENDOR_BY_ID : (parent = dictionary) returns list of vendors ordered by ID
APPLICATION_BY_ID : (parent = dictionary) returns list of applications ordered by ID
  ** for these two lists, the Vendor with id 0 and applciation with id 0 are excluded. 
     You must resolve them separatly with dict_search.
		
TYPE_BY_NAME : (parent = dictionary) returns list of types ordered by name
ENUMVAL_BY_NAME : (parent = type object) return list of constants for this type ordered by name
ENUMVAL_BY_VALUE : (parent = type object) return list of constants for this type ordered by values
AVP_BY_NAME : (parent = vendor object) return list of AVP for this vendor ordered by name
AVP_BY_CODE : (parent = vendor object) return list of AVP for this vendor ordered by code
CMD_BY_NAME : (parent = dictionary) returns list of commands ordered by name
CMD_BY_CODE_R : (parent = dictionary) returns list of commands ordered by code
RULE_BY_AVP_AND_PARENT: (parent = command or grouped AVP object) return list of rules for this object ordered by AVP vendor/code

All other criteria are rejected.
 */
int fd_dict_getlistof(int criteria, void * parent, struct fd_list * sentinel)
{
	struct dictionary * dict = parent;
	struct dict_object * obj_parent = parent;
	
	TRACE_ENTRY("%i %p %p", criteria, parent, sentinel);
	
	CHECK_PARAMS(sentinel && parent);
	
	switch(criteria) {
		case VENDOR_BY_ID: /* parent must be the dictionary */
			CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER);
			sentinel = &dict->dict_vendors.list[0];
			break;
			
		case APPLICATION_BY_ID: /* parent must be the dictionary */
			CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER);
			sentinel = &dict->dict_applications.list[0];
			break;
			
		case TYPE_BY_NAME: /* parent must be the dictionary */
			CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER);
			sentinel = &dict->dict_types;
			break;
			
		case ENUMVAL_BY_NAME: /* parent must be a type object */
			CHECK_PARAMS(verify_object(obj_parent) && (obj_parent->type == DICT_TYPE));
			sentinel = &obj_parent->list[1];
			break;
			
		case ENUMVAL_BY_VALUE: /* parent must be a type object */
			CHECK_PARAMS(verify_object(obj_parent) && (obj_parent->type == DICT_TYPE));
			sentinel = &obj_parent->list[2];
			break;
			
		case AVP_BY_NAME: /* parent must be a AVP object */
			CHECK_PARAMS(verify_object(obj_parent) && (obj_parent->type == DICT_AVP));
			sentinel = &obj_parent->list[2];
			break;
			
		case AVP_BY_CODE: /* parent must be a AVP object */
			CHECK_PARAMS(verify_object(obj_parent) && (obj_parent->type == DICT_AVP));
			sentinel = &obj_parent->list[1];
			break;
			
		case CMD_BY_NAME: /* parent must be the dictionary */
			CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER);
			sentinel = &dict->dict_cmd_name;
			break;
			
		case CMD_BY_CODE_R: /* parent must be the dictionary */
			CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER);
			sentinel = &dict->dict_cmd_code;
			break;
			
		case RULE_BY_AVP_AND_PARENT: /* parent must be command or grouped AVP */
			CHECK_PARAMS(verify_object(obj_parent));
			CHECK_PARAMS(	(obj_parent->type == DICT_COMMAND) ||
					((obj_parent->type == DICT_AVP) 
					  && (obj_parent->data.avp.avp_basetype == AVP_TYPE_GROUPED)) );
			sentinel = &obj_parent->list[2];
			break;
			
		default:
			CHECK_PARAMS(0);
	}