1 /*********************************************************************************************************
2 * Software License Agreement (BSD License)                                                               *
3 * Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
4 *													 *
5 * Copyright (c) 2017, 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 "dict_lxml.h"
37 
38 /*
39 The internal freeDiameter dictionary has strong dependency relations between
40 the different objects, as follow:
41 
42            vendor
43            /    \
44    application   \
45      /    \      |
46  command   \     |
47   |       type   |
48   |       /   \  |
49    \  enumval  \ |
50     \          avp
51      \    _____/
52       \  /
53       rule
54 
55 It means an AVP cannot be defined unless the parent TYPE has already been defined,
56 in turn depending on parent APPLICATION, etc. (top-to-bottom dependencies on the graph)
57 
58 On the other hand, the hierarchy of the XML format described in draft-frascone-xml-dictionary-00
59 does not enforce most of these dependencies, the structure is as follows:
60 
61  vendor     application
62 	   /     |     \
63       command    |     avp
64 	 /      type     \
65        rule	        enumval
66 
67 (in addition if DTD validation was performed, command and avp refer to vendor, avp refers to type,
68 but we do not do it for larger compatibility -- we just report when errors are found)
69 
70 As a consequence of this difference, it is impossible to parse the XML tree and create the dictionary objects in freeDiameter
71 in only 1 pass. To avoid parsing the tree several times, we use a temporary structure in memory to contain all the data
72 from the XML file, and when the parsing is complete we store all the objects in the dictionary.
73 */
74 
75 /* We use the SAX interface of libxml2 (from GNOME) to parse the XML file. */
76 #include <libxml/parser.h>
77 
78 /*******************************************/
79  /* Helper functions */
xmltoint(xmlChar * xmlinteger,uint32_t * conv)80 static int xmltoint(xmlChar * xmlinteger, uint32_t * conv) {
81 	TRACE_ENTRY("%p %p", xmlinteger, conv);
82 
83 	/* Attempt at converting the string to an integer */
84 	if (sscanf((char *)xmlinteger, "%u", conv) != 1) {
85 		TRACE_DEBUG(INFO, "Unable to convert '%s' to integer.", (char *)xmlinteger)
86 		return EINVAL;
87 	}
88 
89 	return 0;
90 }
91 
92 
93 /*******************************************
94  The temporary structure that is being built when the XML file is parsed
95  *******************************************/
96 
97 /* VENDOR */
98 struct t_vend {
99 	struct fd_list 	chain; /* link in the t_dictionary->vendors */
100 	uint32_t	id;
101 	uint8_t *	name;
102 };
103 
new_vendor(struct fd_list * parent,xmlChar * xmlid,xmlChar * xmlname)104 static int new_vendor(struct fd_list * parent, xmlChar * xmlid, xmlChar * xmlname) {
105 	struct t_vend * new;
106 	uint32_t id = 0;
107 
108 	TRACE_ENTRY("%p %p %p", parent, xmlid, xmlname);
109 	CHECK_PARAMS( parent && xmlid && xmlname );
110 
111 	CHECK_FCT( xmltoint(xmlid, &id) );
112 
113 	CHECK_MALLOC( new = malloc(sizeof(struct t_vend)) );
114 	memset(new, 0, sizeof(struct t_vend));
115 	fd_list_init(&new->chain, NULL);
116 	new->id = id;
117 	CHECK_MALLOC( new->name = (uint8_t *)strdup((char *)xmlname) );
118 
119 	fd_list_insert_before(parent, &new->chain);
120 
121 	return 0;
122 }
123 
dump_vendor(struct t_vend * v)124 static void dump_vendor(struct t_vend * v) {
125 	fd_log_debug(" Vendor %d:'%s'", v->id, (char *)v->name);
126 }
127 
del_vendor_contents(struct t_vend * v)128 static void del_vendor_contents(struct t_vend * v) {
129 	TRACE_ENTRY("%p", v);
130 	free(v->name);
131 }
132 
133 
134 /* RULE */
135 struct t_rule {
136 	struct fd_list  chain;	/* link in either t_cmd or t_avp */
137 	uint8_t *	avpname;
138 	int		max;
139 	int		min;
140 };
141 
new_rule(struct fd_list * parent,xmlChar * xmlname,xmlChar * xmlmaximum,xmlChar * xmlminimum)142 static int new_rule(struct fd_list * parent, xmlChar * xmlname, /* position is never used */ xmlChar * xmlmaximum, xmlChar * xmlminimum) {
143 	struct t_rule * new;
144 	uint32_t min, max;
145 
146 	TRACE_ENTRY("%p %p %p %p", parent, xmlname, xmlmaximum, xmlminimum);
147 	CHECK_PARAMS( parent && xmlname );
148 
149 	CHECK_MALLOC( new = malloc(sizeof(struct t_rule)) );
150 	memset(new, 0, sizeof(struct t_rule));
151 	fd_list_init(&new->chain, NULL);
152 	if (xmlminimum) {
153 		CHECK_FCT( xmltoint(xmlminimum, &min) );
154 		new->min = (int) min;
155 	} else {
156 		new->min = -1;
157 	}
158 	if (xmlmaximum) {
159 		CHECK_FCT( xmltoint(xmlmaximum, &max) );
160 		new->max = (int) max;
161 	} else {
162 		new->max = -1;
163 	}
164 	CHECK_MALLOC( new->avpname = (uint8_t *)strdup((char *)xmlname) );
165 
166 	fd_list_insert_before(parent, &new->chain);
167 
168 	return 0;
169 }
170 
dump_rule(struct t_rule * r,char * prefix)171 static void dump_rule(struct t_rule * r, char * prefix) {
172 	fd_log_debug("%s ", prefix);
173 	if (r->min != -1)
174 		fd_log_debug("m:%d ", r->min);
175 	if (r->max != -1)
176 		fd_log_debug("M:%d ", r->max);
177 	fd_log_debug("%s", (char *)r->avpname);
178 }
179 
del_rule_contents(struct t_rule * r)180 static void del_rule_contents(struct t_rule * r) {
181 	TRACE_ENTRY("%p",r);
182 	free(r->avpname);
183 }
184 
185 
186 /* COMMAND */
187 struct t_cmd {
188 	struct fd_list  chain;    /* link in t_appl->commands */
189 	uint32_t	code;
190 	uint8_t *	name;
191 	uint8_t		flags;
192 	uint8_t		fmask;
193 	struct fd_list  reqrules_fixed;     /* list of t_rule */
194 	struct fd_list  reqrules_required;  /* list of t_rule */
195 	struct fd_list  reqrules_optional;  /* list of t_rule */
196 	struct fd_list  ansrules_fixed;     /* list of t_rule */
197 	struct fd_list  ansrules_required;  /* list of t_rule */
198 	struct fd_list  ansrules_optional;  /* list of t_rule */
199 };
200 
new_cmd(struct fd_list * parent,xmlChar * xmlcode,xmlChar * xmlname,xmlChar * xmlpbit,struct t_cmd ** ret)201 static int new_cmd(struct fd_list * parent, xmlChar * xmlcode, xmlChar * xmlname /*, ignore the vendor id because we don't use it */, xmlChar * xmlpbit, struct t_cmd **ret) {
202 	struct t_cmd * new;
203 	uint32_t code;
204 	uint32_t flag = 0;
205 	uint32_t fmask = 0;
206 
207 	TRACE_ENTRY("%p %p %p %p", parent, xmlcode, xmlname, xmlpbit);
208 	CHECK_PARAMS( parent && xmlcode && xmlname );
209 
210 	CHECK_FCT( xmltoint(xmlcode, &code) );
211 
212 	if (xmlpbit) {
213 		uint32_t val;
214 		CHECK_FCT( xmltoint(xmlpbit, &val) );
215 		fmask |= CMD_FLAG_PROXIABLE;
216 		if (val)
217 			flag |= CMD_FLAG_PROXIABLE;
218 	}
219 
220 	CHECK_MALLOC( new = malloc(sizeof(struct t_cmd)) );
221 	memset(new, 0, sizeof(struct t_cmd));
222 	fd_list_init(&new->chain, NULL);
223 	new->code = code;
224 	CHECK_MALLOC( new->name = (uint8_t *)strdup((char *)xmlname) );
225 	new->flags = flag;
226 	new->fmask = fmask;
227 	fd_list_init(&new->reqrules_fixed, NULL);
228 	fd_list_init(&new->reqrules_required, NULL);
229 	fd_list_init(&new->reqrules_optional, NULL);
230 	fd_list_init(&new->ansrules_fixed, NULL);
231 	fd_list_init(&new->ansrules_required, NULL);
232 	fd_list_init(&new->ansrules_optional, NULL);
233 
234 	fd_list_insert_before(parent, &new->chain);
235 
236 	*ret = new;
237 
238 	return 0;
239 }
240 
dump_cmd(struct t_cmd * c)241 static void dump_cmd(struct t_cmd * c) {
242 	struct fd_list * li;
243 	fd_log_debug("  Command %d %s: %s", c->code,
244 		c->fmask ? ( c->flags ? "[P=1]" : "[P=0]") : "", c->name);
245 	for (li = c->reqrules_fixed.next; li != &c->reqrules_fixed; li = li->next)
246 		dump_rule((struct t_rule *)li, "    Request fixed    AVP:");
247 	for (li = c->reqrules_required.next; li != &c->reqrules_required; li = li->next)
248 		dump_rule((struct t_rule *)li, "    Request required AVP:");
249 	for (li = c->reqrules_optional.next; li != &c->reqrules_optional; li = li->next)
250 		dump_rule((struct t_rule *)li, "    Request optional AVP:");
251 	for (li = c->ansrules_fixed.next; li != &c->ansrules_fixed; li = li->next)
252 		dump_rule((struct t_rule *)li, "    Answer fixed    AVP:");
253 	for (li = c->ansrules_required.next; li != &c->ansrules_required; li = li->next)
254 		dump_rule((struct t_rule *)li, "    Answer required AVP:");
255 	for (li = c->ansrules_optional.next; li != &c->ansrules_optional; li = li->next)
256 		dump_rule((struct t_rule *)li, "    Answer optional AVP:");
257 }
258 
del_cmd_contents(struct t_cmd * c)259 static void del_cmd_contents(struct t_cmd * c) {
260 	TRACE_ENTRY("%p", c);
261 	free(c->name);
262 	while (!FD_IS_LIST_EMPTY(&c->reqrules_fixed)) {
263 		struct fd_list * li = c->reqrules_fixed.next;
264 		fd_list_unlink(li);
265 		del_rule_contents((struct t_rule *)li);
266 		free(li);
267 	}
268 	while (!FD_IS_LIST_EMPTY(&c->reqrules_required)) {
269 		struct fd_list * li = c->reqrules_required.next;
270 		fd_list_unlink(li);
271 		del_rule_contents((struct t_rule *)li);
272 		free(li);
273 	}
274 	while (!FD_IS_LIST_EMPTY(&c->reqrules_optional)) {
275 		struct fd_list * li = c->reqrules_optional.next;
276 		fd_list_unlink(li);
277 		del_rule_contents((struct t_rule *)li);
278 		free(li);
279 	}
280 	while (!FD_IS_LIST_EMPTY(&c->ansrules_fixed)) {
281 		struct fd_list * li = c->ansrules_fixed.next;
282 		fd_list_unlink(li);
283 		del_rule_contents((struct t_rule *)li);
284 		free(li);
285 	}
286 	while (!FD_IS_LIST_EMPTY(&c->ansrules_required)) {
287 		struct fd_list * li = c->ansrules_required.next;
288 		fd_list_unlink(li);
289 		del_rule_contents((struct t_rule *)li);
290 		free(li);
291 	}
292 	while (!FD_IS_LIST_EMPTY(&c->ansrules_optional)) {
293 		struct fd_list * li = c->ansrules_optional.next;
294 		fd_list_unlink(li);
295 		del_rule_contents((struct t_rule *)li);
296 		free(li);
297 	}
298 }
299 
300 /* TYPE */
301 struct t_typedefn {
302 	struct fd_list  chain;	/* link in t_appl->types */
303 	uint8_t *	name;
304 	uint8_t *	parent_name;
305 };
306 
new_type(struct fd_list * parent,xmlChar * xmlname,xmlChar * xmlparent)307 static int new_type(struct fd_list * parent, xmlChar * xmlname, xmlChar * xmlparent /*, xmlChar * xmldescription -- ignore */) {
308 	struct t_typedefn * new;
309 
310 	TRACE_ENTRY("%p %p %p", parent, xmlname, xmlparent);
311 	CHECK_PARAMS( parent && xmlname );
312 
313 	CHECK_MALLOC( new = malloc(sizeof(struct t_typedefn)) );
314 	memset(new, 0, sizeof(struct t_typedefn));
315 	fd_list_init(&new->chain, NULL);
316 	CHECK_MALLOC( new->name = (uint8_t *)strdup((char *)xmlname) );
317 	if (xmlparent) {
318 		CHECK_MALLOC( new->parent_name = (uint8_t *)strdup((char *)xmlparent) );
319 	}
320 
321 	fd_list_insert_before(parent, &new->chain);
322 
323 	return 0;
324 }
325 
dump_type(struct t_typedefn * t)326 static void dump_type(struct t_typedefn * t) {
327 	fd_log_debug("  Type %s%s%s%s", (char *)t->name,
328 		     t->parent_name ? "(parent: " : "",
329 		     t->parent_name ? (char *)t->parent_name : "",
330 		     t->parent_name ? ")" : "");
331 }
332 
del_type_contents(struct t_typedefn * t)333 static void del_type_contents(struct t_typedefn * t) {
334 	TRACE_ENTRY("%p", t);
335 	free(t->name);
336 	free(t->parent_name);
337 }
338 
339 
340 /* TYPE INSIDE AVP */
341 struct t_avptype {
342 	struct fd_list  chain;  /* link in t_avp->type */
343 	uint8_t *	type_name;
344 };
345 
new_avptype(struct fd_list * parent,xmlChar * xmlname)346 static int new_avptype(struct fd_list * parent, xmlChar * xmlname) {
347 	struct t_avptype * new;
348 
349 	TRACE_ENTRY("%p %p", parent, xmlname);
350 	CHECK_PARAMS( parent && xmlname );
351 
352 	CHECK_MALLOC( new = malloc(sizeof(struct t_avptype)) );
353 	memset(new, 0, sizeof(struct t_avptype));
354 	fd_list_init(&new->chain, NULL);
355 	CHECK_MALLOC( new->type_name = (uint8_t *)strdup((char *)xmlname) );
356 
357 	fd_list_insert_before(parent, &new->chain);
358 
359 	return 0;
360 }
361 
dump_avptype(struct t_avptype * t)362 static void dump_avptype(struct t_avptype * t) {
363 	fd_log_debug("    data type: %s", t->type_name);
364 }
365 
del_avptype_contents(struct t_avptype * t)366 static void del_avptype_contents(struct t_avptype * t) {
367 	TRACE_ENTRY("%p", t);
368 	free(t->type_name);
369 }
370 
371 
372 /* ENUM */
373 struct t_enum {
374 	struct fd_list  chain;  /* link in t_avp->enums */
375 	uint32_t	code;
376 	uint8_t *	name;
377 };
378 
new_enum(struct fd_list * parent,xmlChar * xmlcode,xmlChar * xmlname)379 static int new_enum(struct fd_list * parent, xmlChar * xmlcode, xmlChar * xmlname) {
380 	struct t_enum * new;
381 	uint32_t code = 0;
382 
383 	TRACE_ENTRY("%p %p %p", parent, xmlcode, xmlname);
384 	CHECK_PARAMS( parent && xmlcode && xmlname );
385 
386 	CHECK_FCT( xmltoint(xmlcode, &code) );
387 
388 	CHECK_MALLOC( new = malloc(sizeof(struct t_enum)) );
389 	memset(new, 0, sizeof(struct t_enum));
390 	fd_list_init(&new->chain, NULL);
391 	new->code = code;
392 	CHECK_MALLOC( new->name = (uint8_t *)strdup((char *)xmlname) );
393 
394 	fd_list_insert_before(parent, &new->chain);
395 
396 	return 0;
397 }
398 
dump_enum(struct t_enum * e)399 static void dump_enum(struct t_enum * e) {
400 	fd_log_debug("    Value: %d == %s", e->code, e->name);
401 }
402 
del_enum_contents(struct t_enum * e)403 static void del_enum_contents(struct t_enum * e) {
404 	TRACE_ENTRY("%p", e);
405 	free(e->name);
406 }
407 
408 /* AVP */
409 struct t_avp {
410 	struct fd_list  chain;  /* link in t_appl->avps */
411 	uint32_t	code;
412 	uint8_t *	name;
413 	uint8_t		flags;
414 	uint8_t		fmask;
415 	uint32_t	vendor;
416 	struct fd_list  type;             /* list of t_avptype -- there must be at max 1 item in the list */
417 	struct fd_list  enums;            /* list of t_enum */
418 	struct fd_list  grouped_fixed;    /* list of t_rule */
419 	struct fd_list  grouped_required; /* list of t_rule */
420 	struct fd_list  grouped_optional; /* list of t_rule */
421 };
422 
new_avp(struct fd_list * parent,xmlChar * xmlcode,xmlChar * xmlname,xmlChar * xmlmandatory,xmlChar * xmlvendor,struct t_avp ** ret)423 static int new_avp(struct fd_list * parent, xmlChar * xmlcode, xmlChar * xmlname, xmlChar * xmlmandatory, xmlChar * xmlvendor, struct t_avp **ret) {
424 	/* we ignore description, may-encrypt, protected, ... */
425 	struct t_avp * new;
426 	uint32_t code;
427 	uint32_t vendor = 0;
428 	uint32_t flag = 0;
429 	uint32_t fmask = 0;
430 
431 	TRACE_ENTRY("%p %p %p %p %p", parent, xmlcode, xmlname, xmlmandatory, xmlvendor);
432 	CHECK_PARAMS( parent && xmlcode && xmlname );
433 
434 	CHECK_FCT( xmltoint(xmlcode, &code) );
435 
436 	if (xmlmandatory && !strcasecmp((char *)xmlmandatory, "must")) {
437 		flag |= AVP_FLAG_MANDATORY;
438 		fmask |= AVP_FLAG_MANDATORY;
439 	}
440 
441 	if (xmlvendor) {
442 		CHECK_FCT( xmltoint(xmlvendor, &vendor) );
443 		if (vendor)
444 			flag |= AVP_FLAG_VENDOR;
445 		fmask |= AVP_FLAG_VENDOR;
446 	}
447 
448 	CHECK_MALLOC( new = malloc(sizeof(struct t_avp)) );
449 	memset(new, 0, sizeof(struct t_avp));
450 	fd_list_init(&new->chain, NULL);
451 	new->code = code;
452 	CHECK_MALLOC( new->name = (uint8_t *)strdup((char *)xmlname) );
453 	new->flags = flag;
454 	new->fmask = fmask;
455 	new->vendor= vendor;
456 	fd_list_init(&new->type, NULL);
457 	fd_list_init(&new->enums, NULL);
458 	fd_list_init(&new->grouped_fixed, NULL);
459 	fd_list_init(&new->grouped_required, NULL);
460 	fd_list_init(&new->grouped_optional, NULL);
461 
462 	fd_list_insert_before(parent, &new->chain);
463 
464 	*ret = new;
465 
466 	return 0;
467 }
468 
dump_avp(struct t_avp * a)469 static void dump_avp(struct t_avp * a) {
470 	struct fd_list * li;
471 	fd_log_debug("  AVP %d %s%s: %s", a->code,
472 		a->fmask & AVP_FLAG_MANDATORY ? ( a->flags & AVP_FLAG_MANDATORY ? "[M=1]" : "[M=0]") : "",
473 		a->fmask & AVP_FLAG_VENDOR ? ( a->flags & AVP_FLAG_VENDOR ? "[V=1]" : "[V=0]") : "",
474 		a->name);
475 	if (a->fmask & AVP_FLAG_VENDOR)
476 		fd_log_debug("    vendor: %d", a->vendor);
477 	for (li = a->type.next; li != &a->type; li = li->next)
478 		dump_avptype((struct t_avptype *)li);
479 	for (li = a->enums.next; li != &a->enums; li = li->next)
480 		dump_enum((struct t_enum *)li);
481 	for (li = a->grouped_fixed.next; li != &a->grouped_fixed; li = li->next)
482 		dump_rule((struct t_rule *)li, "    Grouped, fixed    AVP:");
483 	for (li = a->grouped_required.next; li != &a->grouped_required; li = li->next)
484 		dump_rule((struct t_rule *)li, "    Grouped, required AVP:");
485 	for (li = a->grouped_optional.next; li != &a->grouped_optional; li = li->next)
486 		dump_rule((struct t_rule *)li, "    Grouped, optional AVP:");
487 }
488 
del_avp_contents(struct t_avp * a)489 static void del_avp_contents(struct t_avp * a) {
490 	TRACE_ENTRY("%p", a);
491 	free(a->name);
492 	while (!FD_IS_LIST_EMPTY(&a->type)) {
493 		struct fd_list * li = a->type.next;
494 		fd_list_unlink(li);
495 		del_avptype_contents((struct t_avptype *)li);
496 		free(li);
497 	}
498 	while (!FD_IS_LIST_EMPTY(&a->enums)) {
499 		struct fd_list * li = a->enums.next;
500 		fd_list_unlink(li);
501 		del_enum_contents((struct t_enum *)li);
502 		free(li);
503 	}
504 	while (!FD_IS_LIST_EMPTY(&a->grouped_fixed)) {
505 		struct fd_list * li = a->grouped_fixed.next;
506 		fd_list_unlink(li);
507 		del_rule_contents((struct t_rule *)li);
508 		free(li);
509 	}
510 	while (!FD_IS_LIST_EMPTY(&a->grouped_required)) {
511 		struct fd_list * li = a->grouped_required.next;
512 		fd_list_unlink(li);
513 		del_rule_contents((struct t_rule *)li);
514 		free(li);
515 	}
516 	while (!FD_IS_LIST_EMPTY(&a->grouped_optional)) {
517 		struct fd_list * li = a->grouped_optional.next;
518 		fd_list_unlink(li);
519 		del_rule_contents((struct t_rule *)li);
520 		free(li);
521 	}
522 }
523 
524 
525 /* APPLICATION */
526 struct t_appl {
527 	struct fd_list 	chain; /* link in the t_dictionary->base_and_applications, the sentinel corresponds to "base" */
528 	uint32_t	id;
529 	uint8_t *	name;
530 	struct fd_list  commands; /* list of t_cmd */
531 	struct fd_list	types;    /* list of t_typedefn */
532 	struct fd_list  avps;     /* list of t_avp */
533 };
534 
new_appl(struct fd_list * parent,xmlChar * xmlid,xmlChar * xmlname,struct t_appl ** ret)535 static int new_appl(struct fd_list * parent, xmlChar * xmlid, xmlChar * xmlname /* We ignore the URI */, struct t_appl **ret) {
536 	struct t_appl * new;
537 	uint32_t id = 0;
538 
539 	TRACE_ENTRY("%p %p %p", parent, xmlid, xmlname);
540 	CHECK_PARAMS( parent && xmlid && xmlname );
541 
542 	CHECK_FCT( xmltoint(xmlid, &id) );
543 
544 	CHECK_MALLOC( new = malloc(sizeof(struct t_appl)) );
545 	memset(new, 0, sizeof(struct t_appl));
546 	fd_list_init(&new->chain, NULL);
547 	new->id = id;
548 	CHECK_MALLOC( new->name = (uint8_t *)strdup((char *)xmlname) );
549 
550 	fd_list_init(&new->commands, NULL);
551 	fd_list_init(&new->types, NULL);
552 	fd_list_init(&new->avps, NULL);
553 
554 	fd_list_insert_before(parent, &new->chain);
555 
556 	*ret = new;
557 
558 	return 0;
559 }
560 
dump_appl(struct t_appl * a)561 static void dump_appl(struct t_appl * a) {
562 	struct fd_list * li;
563 	fd_log_debug(" Application %d: %s", a->id, a->name);
564 	for (li = a->commands.next; li != &a->commands; li = li->next)
565 		dump_cmd((struct t_cmd *)li);
566 	for (li = a->types.next; li != &a->types; li = li->next)
567 		dump_type((struct t_typedefn *)li);
568 	for (li = a->avps.next; li != &a->avps; li = li->next)
569 		dump_avp((struct t_avp *)li);
570 }
571 
del_appl_contents(struct t_appl * a)572 static void del_appl_contents(struct t_appl * a) {
573 	TRACE_ENTRY("%p", a);
574 	free(a->name);
575 	while (!FD_IS_LIST_EMPTY(&a->commands)) {
576 		struct fd_list * li = a->commands.next;
577 		fd_list_unlink(li);
578 		del_cmd_contents((struct t_cmd *)li);
579 		free(li);
580 	}
581 	while (!FD_IS_LIST_EMPTY(&a->types)) {
582 		struct fd_list * li = a->types.next;
583 		fd_list_unlink(li);
584 		del_type_contents((struct t_typedefn *)li);
585 		free(li);
586 	}
587 	while (!FD_IS_LIST_EMPTY(&a->avps)) {
588 		struct fd_list * li = a->avps.next;
589 		fd_list_unlink(li);
590 		del_avp_contents((struct t_avp *)li);
591 		free(li);
592 	}
593 }
594 
595 /* DICTIONARY */
596 struct t_dictionary {
597 	struct fd_list  vendors;
598 	struct t_appl   base_and_applications;
599 };
600 
dump_dict(struct t_dictionary * d)601 static void dump_dict(struct t_dictionary * d) {
602 	struct fd_list * li;
603 	for (li = d->vendors.next; li != &d->vendors; li = li->next)
604 		dump_vendor((struct t_vend *)li);
605 	dump_appl(&d->base_and_applications);
606 	for (li = d->base_and_applications.chain.next; li != &d->base_and_applications.chain; li = li->next)
607 		dump_appl((struct t_appl *)li);
608 }
609 
del_dict_contents(struct t_dictionary * d)610 static void del_dict_contents(struct t_dictionary * d) {
611 	TRACE_ENTRY("%p", d);
612 	while (!FD_IS_LIST_EMPTY(&d->vendors)) {
613 		struct fd_list * li = d->vendors.next;
614 		fd_list_unlink(li);
615 		del_vendor_contents((struct t_vend *)li);
616 		free(li);
617 	}
618 	while (!FD_IS_LIST_EMPTY(&d->base_and_applications.chain)) {
619 		struct fd_list * li = d->base_and_applications.chain.next;
620 		fd_list_unlink(li);
621 		del_appl_contents((struct t_appl *)li);
622 		free(li);
623 	}
624 	d->base_and_applications.name = NULL;
625 	del_appl_contents(&d->base_and_applications);
626 }
627 
628 /*********************************************/
629 
630 /* The states for the SAX parser, corresponding roughly to the expected structure of the XML file.
631 We use the states mostly to validate the XML file. */
632 enum state {
633 	INIT = 0,
634 	START, /* In "dictionary" */
635 	 IN_VENDOR,
636 	 IN_APPLICATION,        /* note that "base" is equivalent to "application" for our state machine */
637 	  IN_COMMAND,
638 	   IN_REQRULES,
639 	    IN_REQRULES_FIXED,
640 	    IN_REQRULES_REQUIRED,
641 	    IN_REQRULES_OPTIONAL,
642 	   IN_ANSRULES,
643 	    IN_ANSRULES_FIXED,
644 	    IN_ANSRULES_REQUIRED,
645 	    IN_ANSRULES_OPTIONAL,
646 	  IN_TYPEDEFN,
647 	  IN_AVP,
648 	   IN_AVP_TYPE,
649 	   IN_AVP_ENUM,
650 	   IN_AVP_GROUPED,
651 	    IN_AVP_GROUPED_FIXED,
652 	    IN_AVP_GROUPED_REQUIRED,
653 	    IN_AVP_GROUPED_OPTIONAL
654 };
655 
656 
657 /* The context passed to the SAX parser */
658 struct parser_ctx {
659 	enum state 		state; 	     /* the current state */
660 	int        		error_depth; /* if non 0, we are in an unexpected element, wait until the count goes back to 0 to resume normal parsing. */
661 	struct t_dictionary 	dict;        /* The dictionary being built */
662 	struct t_appl *		cur_app;
663 	struct t_cmd  *		cur_cmd;
664 	struct t_avp  *		cur_avp;
665 	char * 			xmlfilename; /* Name of the file, for error messages */
666 };
667 
668 /* Find an attribute with given name in the list */
get_attr(const xmlChar ** atts_array,const char * attr_name,xmlChar ** attr_val)669 static void get_attr(const xmlChar ** atts_array, const char * attr_name, xmlChar ** attr_val) {
670 	int i;
671 	*attr_val = NULL;
672 	if (atts_array == NULL)
673 		return;
674 	for (i=0; atts_array[i] != NULL; i+=2) {
675 		if (!strcasecmp((char *)atts_array[i], attr_name)) {
676 			/* found */
677 			*attr_val = (xmlChar *)atts_array[i+1];
678 			return;
679 		}
680 	}
681 	/* not found */
682 	return;
683 }
684 
685 /* The following macro avoids duplicating a lot of code in the state machine */
686 #define ADD_RULE( _parent_list ) { 				\
687 	xmlChar *xname, *xmin, *xmax;				\
688 	/* We are expecting an <avprule> tag at this point */	\
689 	if (strcasecmp((char *)name, "avprule"))		\
690 		goto xml_tree_error;				\
691 	/* Search the expected attributes */			\
692 	get_attr(atts, "name", &xname);				\
693 	get_attr(atts, "maximum", &xmax);			\
694 	get_attr(atts, "minimum", &xmin);			\
695 	/* Check the mandatory name is here */			\
696 	CHECK_PARAMS_DO(xname, 					\
697 		{ TRACE_DEBUG(INFO, "Invalid 'avprule' tag found without 'name' attribute."); goto xml_tree_error; } );	\
698 	/* Create the rule and add into the parent list */	\
699 	CHECK_FCT_DO( new_rule((_parent_list), xname, xmax, xmin),\
700 		{ TRACE_DEBUG(INFO, "An error occurred while parsing an avprule tag. Entry ignored."); goto xml_tree_error; } ); \
701 	/* Done. we don't change the state */			\
702 }
703 
704 
705 /* The function called on each XML element start tag (startElementSAXFunc) */
SAXstartelem(void * ctx,const xmlChar * name,const xmlChar ** atts)706 static void SAXstartelem (void * ctx, const xmlChar * name, const xmlChar ** atts)
707 {
708 	struct parser_ctx * data = ctx;
709 	TRACE_ENTRY("%p %p %p", ctx, name, atts);
710 	CHECK_PARAMS_DO( ctx && name, { return; } );
711 
712 	TRACE_DEBUG(CALL, "Tag: <%s>", (char *)name);
713 
714 	if (data->error_depth) /* we are in an unknown element, just skip until it is closed */
715 		goto xml_tree_error;
716 
717 	switch (data->state) {
718 		case INIT: /* we are just starting. We only expect a <dictionary> tag, reject anything else. */
719 			if (strcasecmp((char *)name, "dictionary"))
720 				goto xml_tree_error;
721 
722 			data->state = START;
723 			break;
724 
725 		case START:
726 			/* We are in <dictionary>
727 				Valid tags are: <vendor>, <base>, <application> */
728 			if (!strcasecmp((char *)name, "vendor")) {
729 				xmlChar *xid, *xname;
730 
731 				get_attr(atts, "id", &xid);
732 				get_attr(atts, "name", &xname);
733 
734 				/* id and name are required */
735 				CHECK_PARAMS_DO(xid && xname,
736 					{ TRACE_DEBUG(INFO, "Invalid 'vendor' tag found without 'id' or 'name' attribute."); goto xml_tree_error; } );
737 
738 
739 				CHECK_FCT_DO( new_vendor(&data->dict.vendors, xid, xname),
740 					{ TRACE_DEBUG(INFO, "An error occurred while parsing a vendor tag. Entry ignored."); goto xml_tree_error; } )
741 
742 				data->state = IN_VENDOR;
743 				break;
744 			}
745 
746 			if (!strcasecmp((char *)name, "base")) {
747 				/* we don't care for the 'uri' attribute */
748 				data->cur_app = &data->dict.base_and_applications;
749 				data->state = IN_APPLICATION;
750 				break;
751 			}
752 
753 			if (!strcasecmp((char *)name, "application")) {
754 				/* we don't care for the 'uri' attribute */
755 				xmlChar *xid, *xname;
756 				char buf[50];
757 
758 				get_attr(atts, "id", &xid);
759 				get_attr(atts, "name", &xname);
760 
761 				CHECK_PARAMS_DO(xid,
762 					{ TRACE_DEBUG(INFO, "Invalid 'application' tag found without 'id' attribute."); goto xml_tree_error; } );
763 
764 				/* Name is optional, if not provided we create a name */
765 				if (!xname) {
766 					snprintf(buf, sizeof(buf), "Application %s", xid);
767 					xname = (xmlChar *)buf;
768 				}
769 
770 				CHECK_FCT_DO( new_appl(&data->dict.base_and_applications.chain, xid, xname, &data->cur_app),
771 					{ TRACE_DEBUG(INFO, "An error occurred while parsing an application tag. Entry ignored."); goto xml_tree_error; } )
772 
773 				data->state = IN_APPLICATION;
774 				break;
775 			}
776 
777 			/* Other tags are errors */
778 			goto xml_tree_error;
779 
780 
781 		case IN_VENDOR: /* nothing is allowed inside <vendor> */
782 			goto xml_tree_error;
783 
784 		case IN_APPLICATION:
785 			/* We are in <base> or <application>
786 				Valid tags are: <command>, <typedefn>, <avp> */
787 			if (!strcasecmp((char *)name, "command")) {
788 				/* we don't care for the 'vendor-id' attribute. */
789 				xmlChar *xcode, *xname, *xpbit;
790 
791 				get_attr(atts, "code", &xcode);
792 				get_attr(atts, "name", &xname);
793 				get_attr(atts, "pbit", &xpbit);
794 
795 				/* code and name are required */
796 				CHECK_PARAMS_DO(xcode && xname,
797 					{ TRACE_DEBUG(INFO, "Invalid 'command' tag found without 'code' or 'name' attribute."); goto xml_tree_error; } );
798 
799 				CHECK_FCT_DO( new_cmd( &data->cur_app->commands, xcode, xname, xpbit, &data->cur_cmd),
800 					{ TRACE_DEBUG(INFO, "An error occurred while parsing a command tag. Entry ignored."); goto xml_tree_error; } )
801 
802 				data->state = IN_COMMAND;
803 				break;
804 			}
805 
806 			if (!strcasecmp((char *)name, "typedefn")) {
807 				/* we don't care for the 'description' attribute. */
808 				xmlChar *xname, *xparent;
809 
810 				get_attr(atts, "type-name", &xname);
811 				get_attr(atts, "type-parent", &xparent);
812 
813 				/* name is required */
814 				CHECK_PARAMS_DO(xname,
815 					{ TRACE_DEBUG(INFO, "Invalid 'typedefn' tag found without 'name' attribute."); goto xml_tree_error; } );
816 
817 				CHECK_FCT_DO( new_type( &data->cur_app->types, xname, xparent),
818 					{ TRACE_DEBUG(INFO, "An error occurred while parsing a typedefn tag. Entry ignored."); goto xml_tree_error; } )
819 
820 				data->state = IN_TYPEDEFN;
821 				break;
822 			}
823 
824 			if (!strcasecmp((char *)name, "avp")) {
825 				/* we don't care for the description, may-encrypt, and protected attributes */
826 				xmlChar *xname, *xcode, *xmandatory, *xvendor;
827 
828 				get_attr(atts, "name", &xname);
829 				get_attr(atts, "code", &xcode);
830 				get_attr(atts, "mandatory", &xmandatory);
831 				get_attr(atts, "vendor-id", &xvendor);
832 
833 				/* code and name are required */
834 				CHECK_PARAMS_DO(xcode && xname,
835 					{ TRACE_DEBUG(INFO, "Invalid 'avp' tag found without 'code' or 'name' attribute."); goto xml_tree_error; } );
836 
837 				CHECK_FCT_DO( new_avp(&data->cur_app->avps, xcode, xname, xmandatory, xvendor, &data->cur_avp),
838 					{ TRACE_DEBUG(INFO, "An error occurred while parsing an avp tag. Entry ignored."); goto xml_tree_error; } )
839 
840 				data->state = IN_AVP;
841 				break;
842 			}
843 			/* Other tags are errors */
844 			goto xml_tree_error;
845 
846 
847 		case IN_COMMAND:
848 			/* We are in <command>
849 				Valid tags are: <requestrules>, <answerrules> */
850 			if (!strcasecmp((char *)name, "requestrules")) {
851 				data->state = IN_REQRULES;
852 				break;
853 			}
854 			if (!strcasecmp((char *)name, "answerrules")) {
855 				data->state = IN_ANSRULES;
856 				break;
857 			}
858 			/* Other tags are errors */
859 			goto xml_tree_error;
860 
861 		case IN_REQRULES:
862 			/* We are in <requestrules>
863 				Valid tags are: <fixed>, <required>, <optional> */
864 			if (!strcasecmp((char *)name, "fixed")) {
865 				data->state = IN_REQRULES_FIXED;
866 				break;
867 			}
868 			if (!strcasecmp((char *)name, "required")) {
869 				data->state = IN_REQRULES_REQUIRED;
870 				break;
871 			}
872 			if (!strcasecmp((char *)name, "optional")) {
873 				data->state = IN_REQRULES_OPTIONAL;
874 				break;
875 			}
876 			/* Other tags are errors */
877 			goto xml_tree_error;
878 
879 		case IN_ANSRULES:
880 			/* We are in <answerrules>
881 				Valid tags are: <fixed>, <required>, <optional> */
882 			if (!strcasecmp((char *)name, "fixed")) {
883 				data->state = IN_ANSRULES_FIXED;
884 				break;
885 			}
886 			if (!strcasecmp((char *)name, "required")) {
887 				data->state = IN_ANSRULES_REQUIRED;
888 				break;
889 			}
890 			if (!strcasecmp((char *)name, "optional")) {
891 				data->state = IN_ANSRULES_OPTIONAL;
892 				break;
893 			}
894 			/* Other tags are errors */
895 			goto xml_tree_error;
896 
897 		case IN_REQRULES_FIXED:
898 			/* We are in <command><answerrules><fixed>
899 				Valid tags are: <avprule> */
900 			ADD_RULE( &data->cur_cmd->reqrules_fixed );
901 			break;
902 		case IN_REQRULES_REQUIRED:
903 			ADD_RULE( &data->cur_cmd->reqrules_required );
904 			break;
905 		case IN_REQRULES_OPTIONAL:
906 			ADD_RULE( &data->cur_cmd->reqrules_optional );
907 			break;
908 		case IN_ANSRULES_FIXED:
909 			ADD_RULE( &data->cur_cmd->ansrules_fixed );
910 			break;
911 		case IN_ANSRULES_REQUIRED:
912 			ADD_RULE( &data->cur_cmd->ansrules_required );
913 			break;
914 		case IN_ANSRULES_OPTIONAL:
915 			ADD_RULE( &data->cur_cmd->ansrules_optional );
916 			break;
917 
918 
919 		case IN_TYPEDEFN: /* nothing is allowed inside <typedefn> */
920 			goto xml_tree_error;
921 
922 
923 		case IN_AVP:
924 			/* We are in <avp>
925 				Valid tags are: <type>, <enum>, <grouped> */
926 			if (!strcasecmp((char *)name, "type")) {
927 				xmlChar *xname;
928 
929 				get_attr(atts, "type-name", &xname);
930 
931 				/* name is required */
932 				CHECK_PARAMS_DO(xname,
933 					{ TRACE_DEBUG(INFO, "Invalid 'type' tag found without 'name' attribute."); goto xml_tree_error; } );
934 
935 				/* Check there is only 1 type */
936 				if (!FD_IS_LIST_EMPTY(&data->cur_avp->type)) {
937 					TRACE_DEBUG(INFO, "Multiple 'type' tags found for AVP.");
938 					goto xml_tree_error;
939 				}
940 
941 				/* Add the new type */
942 				CHECK_FCT_DO( new_avptype(&data->cur_avp->type, xname),
943 					{ TRACE_DEBUG(INFO, "An error occurred while parsing a type tag. Entry ignored."); goto xml_tree_error; } )
944 
945 				data->state = IN_AVP_TYPE;
946 				break;
947 			}
948 			if (!strcasecmp((char *)name, "enum")) {
949 				xmlChar *xcode, *xname;
950 
951 				get_attr(atts, "code", &xcode);
952 				get_attr(atts, "name", &xname);
953 
954 				/* code and name are required */
955 				CHECK_PARAMS_DO(xcode && xname,
956 					{ TRACE_DEBUG(INFO, "Invalid 'enum' tag found without 'code' or 'name' attribute."); goto xml_tree_error; } );
957 
958 				CHECK_FCT_DO( new_enum(&data->cur_avp->enums, xcode, xname),
959 					{ TRACE_DEBUG(INFO, "An error occurred while parsing a command tag. Entry ignored."); goto xml_tree_error; } )
960 
961 				data->state = IN_AVP_ENUM;
962 				break;
963 			}
964 			if (!strcasecmp((char *)name, "grouped")) {
965 				/* no attribute for this one */
966 				data->state = IN_AVP_GROUPED;
967 				break;
968 			}
969 			/* Other tags are errors */
970 			goto xml_tree_error;
971 
972 		case IN_AVP_TYPE: /* nothing is allowed inside <type> */
973 			goto xml_tree_error;
974 
975 		case IN_AVP_ENUM: /* nothing is allowed inside <enum> */
976 			goto xml_tree_error;
977 
978 		case IN_AVP_GROUPED:
979 			/* We are in <avp><grouped>
980 				Valid tags are: <fixed>, <required>, <optional> */
981 			if (!strcasecmp((char *)name, "fixed")) {
982 				data->state = IN_AVP_GROUPED_FIXED;
983 				break;
984 			}
985 			if (!strcasecmp((char *)name, "required")) {
986 				data->state = IN_AVP_GROUPED_REQUIRED;
987 				break;
988 			}
989 			if (!strcasecmp((char *)name, "optional")) {
990 				data->state = IN_AVP_GROUPED_OPTIONAL;
991 				break;
992 			}
993 			/* Other tags are errors */
994 			goto xml_tree_error;
995 
996 		case IN_AVP_GROUPED_FIXED:
997 			/* We are in <avp><grouped><fixed>
998 				Valid tags are: <avprule> */
999 			ADD_RULE( &data->cur_avp->grouped_fixed );
1000 			break;
1001 		case IN_AVP_GROUPED_REQUIRED:
1002 			ADD_RULE( &data->cur_avp->grouped_required );
1003 			break;
1004 		case IN_AVP_GROUPED_OPTIONAL:
1005 			ADD_RULE( &data->cur_avp->grouped_optional );
1006 			break;
1007 
1008 
1009 		default:
1010 			TRACE_DEBUG(INFO, "Internal parsing error, unexpected state %d.", data->state);
1011 	}
1012 
1013 	return;
1014 
1015 xml_tree_error:
1016 	if (!data->error_depth) {
1017 		TRACE_DEBUG(INFO, "Unexpected XML element found: '%s'. Ignoring...", name);
1018 	}
1019 	data->error_depth += 1;
1020 	if (data->cur_app || data->cur_cmd || data->cur_avp) {
1021 		TRACE_DEBUG(INFO, "Error encountered while parsing tag of:");
1022 		if (data->cur_app)
1023 			fd_log_debug("  Application: '%s'", data->cur_app->name);
1024 		if (data->cur_cmd)
1025 			fd_log_debug("  Command    : '%s'", data->cur_cmd->name);
1026 		if (data->cur_avp)
1027 			fd_log_debug("  AVP        : '%s'", data->cur_avp->name);
1028 	}
1029 	return;
1030 }
1031 
1032 /* The function called on each XML element end tag (endElementSAXFunc) */
SAXendelem(void * ctx,const xmlChar * name)1033 static void SAXendelem (void * ctx, const xmlChar * name)
1034 {
1035 	struct parser_ctx * data = ctx;
1036 	TRACE_ENTRY("%p %p", ctx, name);
1037 	CHECK_PARAMS_DO( ctx && name, { return; } );
1038 
1039 	TRACE_DEBUG(CALL, "Tag: </%s>", (char *)name);
1040 
1041 	if (data->error_depth) {
1042 		/* we are recovering from an erroneous element */
1043 		data->error_depth -= 1;
1044 		return;
1045 	}
1046 
1047 	switch (data->state) {
1048 		case INIT:
1049 			goto state_machine_error;
1050 
1051 		case START:
1052 			if (strcasecmp((char *)name, "dictionary"))
1053 				goto state_machine_error;
1054 
1055 			data->state = 0;
1056 			break;
1057 
1058 		case IN_VENDOR:
1059 			if (strcasecmp((char *)name, "vendor"))
1060 				goto state_machine_error;
1061 
1062 			data->state = START;
1063 			break;
1064 
1065 		case IN_APPLICATION:
1066 			if (strcasecmp((char *)name, "base") && strcasecmp((char *)name, "application"))
1067 				goto state_machine_error;
1068 
1069 			data->cur_app = NULL;
1070 			data->state = START;
1071 			break;
1072 
1073 		case IN_COMMAND:
1074 			if (strcasecmp((char *)name, "command"))
1075 				goto state_machine_error;
1076 
1077 			data->cur_cmd = NULL;
1078 			data->state = IN_APPLICATION;
1079 			break;
1080 
1081 		case IN_REQRULES:
1082 			if (strcasecmp((char *)name, "requestrules"))
1083 				goto state_machine_error;
1084 
1085 			data->state = IN_COMMAND;
1086 			break;
1087 
1088 		case IN_REQRULES_FIXED:
1089 			if (!strcasecmp((char *)name, "avprule"))
1090 				/* we don't have a special state for these, just ignore */
1091 				return;
1092 			if (strcasecmp((char *)name, "fixed"))
1093 				goto state_machine_error;
1094 			data->state = IN_REQRULES;
1095 			break;
1096 		case IN_REQRULES_REQUIRED:
1097 			if (!strcasecmp((char *)name, "avprule"))
1098 				/* we don't have a special state for these, just ignore */
1099 				return;
1100 			if (strcasecmp((char *)name, "required"))
1101 				goto state_machine_error;
1102 			data->state = IN_REQRULES;
1103 			break;
1104 		case IN_REQRULES_OPTIONAL:
1105 			if (!strcasecmp((char *)name, "avprule"))
1106 				/* we don't have a special state for these, just ignore */
1107 				return;
1108 			if (strcasecmp((char *)name, "optional"))
1109 				goto state_machine_error;
1110 			data->state = IN_REQRULES;
1111 			break;
1112 
1113 		case IN_ANSRULES:
1114 			if (strcasecmp((char *)name, "answerrules"))
1115 				goto state_machine_error;
1116 
1117 			data->state = IN_COMMAND;
1118 			break;
1119 		case IN_ANSRULES_FIXED:
1120 			if (!strcasecmp((char *)name, "avprule"))
1121 				/* we don't have a special state for these, just ignore */
1122 				return;
1123 			if (strcasecmp((char *)name, "fixed"))
1124 				goto state_machine_error;
1125 			data->state = IN_ANSRULES;
1126 			break;
1127 		case IN_ANSRULES_REQUIRED:
1128 			if (!strcasecmp((char *)name, "avprule"))
1129 				/* we don't have a special state for these, just ignore */
1130 				return;
1131 			if (strcasecmp((char *)name, "required"))
1132 				goto state_machine_error;
1133 			data->state = IN_ANSRULES;
1134 			break;
1135 		case IN_ANSRULES_OPTIONAL:
1136 			if (!strcasecmp((char *)name, "avprule"))
1137 				/* we don't have a special state for these, just ignore */
1138 				return;
1139 			if (strcasecmp((char *)name, "optional"))
1140 				goto state_machine_error;
1141 			data->state = IN_ANSRULES;
1142 			break;
1143 
1144 
1145 		case IN_TYPEDEFN:
1146 			if (strcasecmp((char *)name, "typedefn"))
1147 				goto state_machine_error;
1148 
1149 			data->state = IN_APPLICATION;
1150 			break;
1151 
1152 		case IN_AVP:
1153 			if (strcasecmp((char *)name, "avp"))
1154 				goto state_machine_error;
1155 
1156 			data->cur_avp = NULL;
1157 			data->state = IN_APPLICATION;
1158 			break;
1159 
1160 		case IN_AVP_TYPE:
1161 			if (strcasecmp((char *)name, "type"))
1162 				goto state_machine_error;
1163 
1164 			data->state = IN_AVP;
1165 			break;
1166 
1167 		case IN_AVP_ENUM:
1168 			if (strcasecmp((char *)name, "enum"))
1169 				goto state_machine_error;
1170 
1171 			data->state = IN_AVP;
1172 			break;
1173 
1174 		case IN_AVP_GROUPED:
1175 			if (strcasecmp((char *)name, "grouped"))
1176 				goto state_machine_error;
1177 
1178 			data->state = IN_AVP;
1179 			break;
1180 
1181 		case IN_AVP_GROUPED_FIXED:
1182 			if (!strcasecmp((char *)name, "avprule"))
1183 				/* we don't have a special state for these, just ignore */
1184 				return;
1185 			if (strcasecmp((char *)name, "fixed"))
1186 				goto state_machine_error;
1187 			data->state = IN_AVP_GROUPED;
1188 			break;
1189 		case IN_AVP_GROUPED_REQUIRED:
1190 			if (!strcasecmp((char *)name, "avprule"))
1191 				return;
1192 			if (strcasecmp((char *)name, "required"))
1193 				goto state_machine_error;
1194 			data->state = IN_AVP_GROUPED;
1195 			break;
1196 		case IN_AVP_GROUPED_OPTIONAL:
1197 			if (!strcasecmp((char *)name, "avprule"))
1198 				return;
1199 			if (strcasecmp((char *)name, "optional"))
1200 				goto state_machine_error;
1201 			data->state = IN_AVP_GROUPED;
1202 			break;
1203 
1204 		default:
1205 			TRACE_DEBUG(INFO, "Internal parsing error, unexpected state %d.", data->state);
1206 	}
1207 
1208 	return;
1209 
1210 state_machine_error:
1211 	TRACE_DEBUG(INFO, "Internal parsing error, ignored [state %d, closing tag '%s'].", data->state, name);
1212 	return;
1213 }
1214 
1215 /* The SAX parser sends a warning, error, fatalerror -- do we need these ?
1216 static void SAXwarning (void * ctx, const char * msg, ...)
1217 {
1218 
1219 }
1220 static void SAXerror (void * ctx, const char * msg, ...)
1221 {
1222 
1223 }
1224 static void SAXfatal (void * ctx, const char * msg, ...)
1225 {
1226 
1227 }
1228 */
1229 
1230 
1231 
1232 
1233 /*********************************************/
1234  /* 2nd pass: from memory to fD dictionary */
1235 /*********************************************/
1236 
1237 /* Find or create a vendor */
vend_to_fD(struct t_vend * v,struct dictionary * fD_dict,struct dict_object ** fd_v,int * nb_added)1238 static int vend_to_fD(struct t_vend * v, struct dictionary * fD_dict, struct dict_object ** fd_v, int * nb_added)
1239 {
1240 	int ret;
1241 	struct dict_object * prev = NULL;
1242 	struct dict_vendor_data vd;
1243 
1244 	TRACE_ENTRY("%p %p %p %p", v, fD_dict, fd_v, nb_added);
1245 
1246 	CHECK_PARAMS(v && fD_dict);
1247 
1248 	/* Prepare the data in fD's format */
1249 	memset(&vd, 0, sizeof(vd));
1250 	vd.vendor_id   = v->id;
1251 	vd.vendor_name = (char *)v->name;
1252 
1253 	/* Create or search in the dictionary */
1254 	ret = fd_dict_new ( fD_dict, DICT_VENDOR, &vd, NULL, &prev );
1255 	if (fd_v)
1256 		*fd_v = prev;
1257 	if (ret == EEXIST) {
1258 		/* Conflict with existing entry */
1259 		CHECK_FCT( fd_dict_getval(prev, &vd) );
1260 		TRACE_DEBUG(INFO, "[dict_legacy_xml] Warning: Conflicting entry.");
1261 		TRACE_DEBUG(INFO, "[dict_legacy_xml]   New entry (ignored): %u - '%s'", v->id, (char *)v->name);
1262 		TRACE_DEBUG(INFO, "[dict_legacy_xml]   Old entry          : %u - '%s'", vd.vendor_id, vd.vendor_name);
1263 		return 0;
1264 	} else {
1265 		/* other errors are stoppers */
1266 		CHECK_FCT(ret);
1267 	}
1268 
1269 	/* Update count */
1270 	if (nb_added)
1271 		*nb_added += 1;
1272 
1273 	/* Done */
1274 	return 0;
1275 }
1276 
1277 /* Find the base fD type from a type name */
resolve_base_type(struct dictionary * fD_dict,uint8_t * type_name,enum dict_avp_basetype * basetype,struct dict_object ** type)1278 static int resolve_base_type(struct dictionary * fD_dict, uint8_t * type_name, enum dict_avp_basetype * basetype, struct dict_object **type)
1279 {
1280 	int ret;
1281 	struct dict_type_data td;
1282 	struct dict_object *t;
1283 
1284 	TRACE_ENTRY("%p, %p %p", fD_dict, type_name, basetype);
1285 	CHECK_PARAMS( fD_dict && type_name && basetype );
1286 
1287 	/* First, check if the type is already in the dictionary */
1288 	ret = fd_dict_search ( fD_dict, DICT_TYPE, TYPE_BY_NAME, type_name, &t, ENOENT);
1289 	switch (ret) {
1290 		case 0: /* the type is already in the dictionary */
1291 			CHECK_FCT( fd_dict_getval(t, &td) );
1292 			*basetype = td.type_base;
1293 			if (type)
1294 				*type = t;
1295 			return 0;
1296 
1297 		case ENOENT: /* We did not find it, it is maybe normal */
1298 			break;
1299 
1300 		default:
1301 			/* An unexpected error occurred */
1302 			CHECK_FCT(ret);
1303 	}
1304 
1305 	/* at this point we did not find the type in the dictionary */
1306 #define PREDEF_TYPES( _typename_, _basetype_ )			\
1307 	if (!strcasecmp((char *)type_name, (_typename_))) {	\
1308 		*basetype = (_basetype_);			\
1309 		return 0;					\
1310 	}
1311 
1312 	PREDEF_TYPES( "OctetString", AVP_TYPE_OCTETSTRING );
1313 	PREDEF_TYPES( "Integer32",   AVP_TYPE_INTEGER32   );
1314 	PREDEF_TYPES( "Integer64",   AVP_TYPE_INTEGER64   );
1315 	PREDEF_TYPES( "Unsigned32",  AVP_TYPE_UNSIGNED32  );
1316 	PREDEF_TYPES( "Enumerated",  AVP_TYPE_INTEGER32  );
1317 	PREDEF_TYPES( "Unsigned64",  AVP_TYPE_UNSIGNED64  );
1318 	PREDEF_TYPES( "Float32",     AVP_TYPE_FLOAT32     );
1319 	PREDEF_TYPES( "Float64",     AVP_TYPE_FLOAT64     );
1320 
1321 	/* When we reach this point, we have not yet found this type anywhere. */
1322 	TODO("Type not found. Maybe search in whole xmldictionary if it is defined later?");
1323 	TRACE_DEBUG(INFO, "The type '%s' could not be resolved. Please check it is defined before use.", type_name);
1324 	return ENOENT;
1325 }
1326 
1327 /* Find or create a type. */
typdefn_to_fD(struct t_typedefn * t,struct dictionary * fD_dict,struct dict_object * fd_appl,struct dict_object ** fd_t,int * nb_added)1328 static int typdefn_to_fD(struct t_typedefn * t, struct dictionary * fD_dict, struct dict_object * fd_appl, struct dict_object ** fd_t, int * nb_added)
1329 {
1330 	int ret;
1331 	struct dict_object * prev = NULL;
1332 	struct dict_type_data td;
1333 
1334 	TRACE_ENTRY("%p %p %p %p %p", t, fD_dict, fd_appl, fd_t, nb_added);
1335 
1336 	CHECK_PARAMS(t && fD_dict);
1337 
1338 	/* Prepare the data in fD's format */
1339 	memset(&td, 0, sizeof(td));
1340 	td.type_name = (char *)t->name;
1341 
1342 	/* infer td.type_base from t->parent_name */
1343 	CHECK_FCT( resolve_base_type(fD_dict, t->parent_name, &td.type_base, NULL) );
1344 
1345 	/* Create or search in the dictionary */
1346 	ret = fd_dict_new ( fD_dict, DICT_TYPE, &td, fd_appl, &prev );
1347 	if (fd_t)
1348 		*fd_t = prev;
1349 	if (ret == EEXIST) {
1350 		/* Conflict with existing entry */
1351 		enum dict_avp_basetype xmlbt = td.type_base;
1352 		extern const char * type_base_name[]; /* in libfreeDiameter/dictionary.c */
1353 		CHECK_FCT( fd_dict_getval(prev, &td) );
1354 		TRACE_DEBUG(INFO, "[dict_legacy_xml] Warning: Conflicting entry.");
1355 		TRACE_DEBUG(INFO, "[dict_legacy_xml]   New entry (ignored): '%s' (%d - %s)", t->name, xmlbt, type_base_name[xmlbt] );
1356 		TRACE_DEBUG(INFO, "[dict_legacy_xml]   Old entry          : '%s' (%d - %s)", td.type_name, td.type_base, type_base_name[td.type_base]);
1357 		return 0;
1358 	} else {
1359 		/* other errors are stoppers */
1360 		CHECK_FCT(ret);
1361 	}
1362 
1363 	/* Update count */
1364 	if (nb_added)
1365 		*nb_added += 1;
1366 
1367 	/* Done */
1368 	return 0;
1369 }
1370 
1371 /* Process one list of rules */
rules_to_fD_onelist(struct dictionary * fD_dict,struct dict_object * parent,enum rule_position position,struct fd_list * list,int * nb_added)1372 static int rules_to_fD_onelist(struct dictionary * fD_dict, struct dict_object * parent, enum rule_position position, struct fd_list * list, int * nb_added)
1373 {
1374 	struct dict_rule_data rd;
1375 	struct fd_list * li;
1376 	int order = 0;
1377 	int ret;
1378 
1379 	TRACE_ENTRY("%p %p %d %p %p", fD_dict, parent, position, list, nb_added);
1380 
1381 	CHECK_PARAMS(fD_dict && parent && position && list);
1382 
1383 	for (li = list->next; li != list; li = li->next) {
1384 		struct t_rule * r = (struct t_rule *)li;
1385 
1386 		/* The [AVP] rule in all ABNF definitions is implicit in freeDiameter, skip it */
1387 		if (!strcmp((char *)r->avpname, "AVP"))
1388 			continue;
1389 
1390 		/* Prepare rule data */
1391 		memset(&rd, 0, sizeof(rd));
1392 		rd.rule_position = position;
1393 		rd.rule_order = ++order; /* actually only used for fixed rules, but no harm for others */
1394 		rd.rule_min = r->min;
1395 		rd.rule_max = r->max;
1396 
1397 		/* Resolve the AVP */
1398 		ret = fd_dict_search(fD_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, r->avpname, &rd.rule_avp, ENOENT);
1399 		if (ret == ENOENT) {
1400 			TRACE_DEBUG(INFO, "[dict_legacy_xml] Error: AVP '%s' used in a rule before being defined.", r->avpname);
1401 		}
1402 		CHECK_FCT(ret);
1403 
1404 		/* Now create the new rule */
1405 		CHECK_FCT_DO( ret = fd_dict_new ( fD_dict, DICT_RULE, &rd, parent, NULL ),
1406 			{ TRACE_DEBUG(INFO, "Error creating rule for sub-AVP '%s'", r->avpname); return ret; } );
1407 		if (nb_added)
1408 			*nb_added += 1;
1409 	}
1410 
1411 	return 0;
1412 }
1413 
1414 /* Process lists of rules */
rules_to_fD(struct dictionary * fD_dict,struct dict_object * parent,struct fd_list * fixed,struct fd_list * required,struct fd_list * optional,int * nb_added)1415 static int rules_to_fD(struct dictionary * fD_dict, struct dict_object * parent, struct fd_list * fixed, struct fd_list * required, struct fd_list * optional, int * nb_added)
1416 {
1417 	int ret;
1418 
1419 	TRACE_ENTRY("%p %p %p %p %p %p", fD_dict, parent, fixed, required, optional, nb_added);
1420 
1421 	/* Process the rules */
1422 	CHECK_FCT_DO( ret = rules_to_fD_onelist(fD_dict, parent, RULE_FIXED_HEAD, fixed, nb_added),
1423 		{ TRACE_DEBUG(INFO, "Error processing FIXED rules"); return ret; } );
1424 	CHECK_FCT_DO( ret = rules_to_fD_onelist(fD_dict, parent, RULE_REQUIRED, required, nb_added),
1425 		{ TRACE_DEBUG(INFO, "Error processing REQUIRED rules"); return ret; } );
1426 	CHECK_FCT_DO( ret = rules_to_fD_onelist(fD_dict, parent, RULE_OPTIONAL, optional, nb_added),
1427 		{ TRACE_DEBUG(INFO, "Error processing OPTIONAL rules"); return ret; } );
1428 
1429 	return 0;
1430 }
1431 
1432 /* Find or create an AVP (and dependent objects) */
avp_to_fD(struct t_avp * a,struct dictionary * fD_dict,struct dict_object * fd_appl,struct dict_object ** fd_a,int * nb_added)1433 static int avp_to_fD(struct t_avp * a, struct dictionary * fD_dict, struct dict_object * fd_appl, struct dict_object ** fd_a, int * nb_added)
1434 {
1435 	int ret;
1436 	struct dict_object * prev = NULL, *type = NULL;
1437 	struct dict_avp_data ad;
1438 	struct fd_list * li;
1439 
1440 	TRACE_ENTRY("%p %p %p %p %p", a, fD_dict, fd_appl, fd_a, nb_added);
1441 
1442 	CHECK_PARAMS(a && fD_dict);
1443 
1444 	/* Prepare the data in fD's format */
1445 	memset(&ad, 0, sizeof(ad));
1446 	ad.avp_code   = a->code;
1447 	ad.avp_vendor = a->vendor;
1448 	ad.avp_name   = (char *)a->name;
1449 	ad.avp_flag_mask = a->fmask | AVP_FLAG_VENDOR;
1450 	ad.avp_flag_val  = a->flags;
1451 
1452 	if (!FD_IS_LIST_EMPTY(&a->type)) {
1453 		/* special exception: we use per-AVP enumerated types in fD */
1454 		if (!strcasecmp("Enumerated", (char *)((struct t_avptype *)a->type.next)->type_name))
1455 			goto enumerated;
1456 		/* Let's allow "Integer32" instead of "Enumerated" also... */
1457 		if ((!FD_IS_LIST_EMPTY(&a->enums)) && (!strcasecmp("Integer32", (char *)((struct t_avptype *)a->type.next)->type_name)))
1458 			goto enumerated;
1459 
1460 		/* The type was explicitly specified, resolve it */
1461 		CHECK_FCT( resolve_base_type(fD_dict, ((struct t_avptype *)a->type.next)->type_name, &ad.avp_basetype, &type) );
1462 	} else {
1463 		/* The type was not specified, try to infer it from provided data */
1464 		if (       !FD_IS_LIST_EMPTY(&a->grouped_optional)
1465 			|| !FD_IS_LIST_EMPTY(&a->grouped_required)
1466 			|| !FD_IS_LIST_EMPTY(&a->grouped_fixed) ) {
1467 			/* The AVP has rules, it is a grouped AVP */
1468 			CHECK_PARAMS_DO( FD_IS_LIST_EMPTY(&a->enums),
1469 				{ TRACE_DEBUG(INFO, "Conflict: The AVP '%s' has both enum values and rules.", ad.avp_name); return EINVAL; } );
1470 			ad.avp_basetype = AVP_TYPE_GROUPED;
1471 		} else {
1472 			/* It should be an enumerated AVP... */
1473 			if (FD_IS_LIST_EMPTY(&a->enums)) {
1474 				TRACE_DEBUG(INFO, "Error: Missing type information for AVP '%s'", ad.avp_name);
1475 				return EINVAL;
1476 			} else {
1477 				/* We create a new type to hold the enumerated values -- fD specifics */
1478 				char typename[256];
1479 				struct dict_type_data 	tdata;
1480 
1481 enumerated:
1482 				snprintf(typename, sizeof(typename), "Enumerated(%s)", ad.avp_name);
1483 				memset(&tdata, 0, sizeof(tdata));
1484 				tdata.type_base = AVP_TYPE_INTEGER32;
1485 				tdata.type_name = &typename[0];
1486 				CHECK_FCT( fd_dict_new ( fD_dict, DICT_TYPE, &tdata, fd_appl, &type ) );
1487 				if (nb_added)
1488 					*nb_added += 1;
1489 
1490 				ad.avp_basetype = AVP_TYPE_INTEGER32;
1491 			}
1492 		}
1493 	}
1494 
1495 	/* At this point, ad.avp_basetype is defined and type might also be */
1496 
1497 	/* Create or search in the dictionary */
1498 	ret = fd_dict_new ( fD_dict, DICT_AVP, &ad, type, &prev );
1499 	if (fd_a)
1500 		*fd_a = prev;
1501 	if (ret == EEXIST) {
1502 		/* Conflict with existing entry */
1503 		CHECK_FCT( fd_dict_getval(prev, &ad) );
1504 		TRACE_DEBUG(INFO, "[dict_legacy_xml] Warning: Conflicting entry.");
1505 		TRACE_DEBUG(INFO, "[dict_legacy_xml]   New entry (ignored): %u - '%s'", a->code, (char *)a->name);
1506 		TRACE_DEBUG(INFO, "[dict_legacy_xml]   Old entry          : %u - '%s'", ad.avp_code, ad.avp_name);
1507 		goto inside;
1508 	} else {
1509 		/* other errors are stoppers */
1510 		CHECK_FCT(ret);
1511 	}
1512 
1513 	/* Update count */
1514 	if (nb_added)
1515 		*nb_added += 1;
1516 
1517 inside:
1518 	/* Now, the inner elements, if any */
1519 
1520 	if ( (!FD_IS_LIST_EMPTY(&a->enums)) && (ad.avp_basetype != AVP_TYPE_UNSIGNED32)) {
1521 		TRACE_DEBUG(INFO, "AVP '%s' type is not an Unsigned32 but it has enum values (invalid in this extension).", ad.avp_name);
1522 		return EINVAL;
1523 	}
1524 
1525 	/* In case of enumeration, define the enum values */
1526 	for (li = a->enums.next; li != &a->enums; li = li->next) {
1527 		struct t_enum * e = (struct t_enum *)li;
1528 		struct dict_enumval_data ed;
1529 
1530 		memset(&ed, 0, sizeof(ed));
1531 		ed.enum_name = (char *)e->name;
1532 		ed.enum_value.u32 = e->code;
1533 
1534 		CHECK_FCT_DO( ret = fd_dict_new ( fD_dict, DICT_ENUMVAL, &ed, type, NULL ),
1535 			{
1536 				TRACE_DEBUG(INFO, "Error defining constant value '%s' for AVP '%s': %s", ed.enum_name, ad.avp_name, strerror(ret));
1537 				return ret;
1538 			} );
1539 		if (nb_added)
1540 			*nb_added += 1;
1541 	}
1542 
1543 	/* In case of grouped AVP, check the type is really grouped */
1544 	if ( !FD_IS_LIST_EMPTY(&a->grouped_optional)
1545 	  || !FD_IS_LIST_EMPTY(&a->grouped_required)
1546 	  || !FD_IS_LIST_EMPTY(&a->grouped_fixed) ) {
1547 		CHECK_PARAMS_DO( ad.avp_basetype == AVP_TYPE_GROUPED,
1548 			{ TRACE_DEBUG(INFO, "Got rules for non-grouped AVP '%s'", ad.avp_name); return EINVAL;} );
1549 		CHECK_FCT_DO( ret = rules_to_fD(fD_dict, prev, &a->grouped_fixed, &a->grouped_required, &a->grouped_optional, nb_added),
1550 			{ TRACE_DEBUG(INFO, "Error processing rules for AVP '%s': %s", ad.avp_name, strerror(ret)); return ret; } );
1551 	}
1552 
1553 	/* done! */
1554 	return 0;
1555 }
1556 
1557 /* Find or create a command. */
cmd_to_fD(struct t_cmd * c,struct dictionary * fD_dict,struct dict_object * fd_appl,struct dict_object ** fd_req,int * nb_added)1558 static int cmd_to_fD(struct t_cmd * c, struct dictionary * fD_dict, struct dict_object * fd_appl, struct dict_object ** fd_req, int * nb_added)
1559 {
1560 	int ret;
1561 	struct dict_object * req = NULL, *ans = NULL;
1562 	struct dict_cmd_data cd;
1563 	char cmdname[512];
1564 
1565 	TRACE_ENTRY("%p %p %p %p %p", c, fD_dict, fd_appl, fd_req, nb_added);
1566 
1567 	CHECK_PARAMS(c && fD_dict);
1568 
1569 	/* Prepare the request data in fD's format */
1570 	memset(&cd, 0, sizeof(cd));
1571 	cd.cmd_code = c->code;
1572 	snprintf(cmdname, sizeof(cmdname), "%s-Request", (char *)c->name);
1573 	cd.cmd_name = &cmdname[0];
1574 	cd.cmd_flag_mask = c->fmask | CMD_FLAG_REQUEST | CMD_FLAG_ERROR;
1575 	cd.cmd_flag_val  = c->flags | CMD_FLAG_REQUEST;
1576 
1577 	/* Create or search in the dictionary */
1578 	ret = fd_dict_new ( fD_dict, DICT_COMMAND, &cd, fd_appl, &req );
1579 	if (fd_req)
1580 		*fd_req = req;
1581 	if (ret == EEXIST) {
1582 		struct dict_cmd_data prevcd;
1583 		/* Conflict with existing entry */
1584 		CHECK_FCT( fd_dict_getval(req, &prevcd) );
1585 		TRACE_DEBUG(INFO, "[dict_legacy_xml] Warning: Conflicting entry.");
1586 		TRACE_DEBUG(INFO, "[dict_legacy_xml]   New entry (ignored): %u - '%s'", cd.cmd_code, cd.cmd_name);
1587 		TRACE_DEBUG(INFO, "[dict_legacy_xml]   Old entry          : %u - '%s'", prevcd.cmd_code, prevcd.cmd_name);
1588 		goto answer;
1589 	} else {
1590 		/* other errors are stoppers */
1591 		CHECK_FCT(ret);
1592 	}
1593 
1594 	/* Update count */
1595 	if (nb_added)
1596 		*nb_added += 1;
1597 
1598 answer:
1599 	/* update data for the answer */
1600 	snprintf(cmdname, sizeof(cmdname), "%s-Answer", (char *)c->name);
1601 	cd.cmd_flag_val &= ~CMD_FLAG_REQUEST;
1602 	cd.cmd_flag_mask &= ~CMD_FLAG_ERROR;
1603 
1604 	ret = fd_dict_new ( fD_dict, DICT_COMMAND, &cd, fd_appl, &ans );
1605 	if (ret == EEXIST) {
1606 		struct dict_cmd_data prevcd;
1607 		/* Conflict with existing entry */
1608 		CHECK_FCT( fd_dict_getval(ans, &prevcd) );
1609 		TRACE_DEBUG(INFO, "[dict_legacy_xml] Warning: Conflicting entry.");
1610 		TRACE_DEBUG(INFO, "[dict_legacy_xml]   New entry (ignored): %u - '%s'", cd.cmd_code, cd.cmd_name);
1611 		TRACE_DEBUG(INFO, "[dict_legacy_xml]   Old entry          : %u - '%s'", prevcd.cmd_code, prevcd.cmd_name);
1612 		goto rules;
1613 	} else {
1614 		/* other errors are stoppers */
1615 		CHECK_FCT(ret);
1616 	}
1617 
1618 	/* Update count */
1619 	if (nb_added)
1620 		*nb_added += 1;
1621 
1622 rules:
1623 	/* Now process the rules inside the command */
1624 	CHECK_FCT_DO( ret = rules_to_fD(fD_dict, req, &c->reqrules_fixed, &c->reqrules_required, &c->reqrules_optional, nb_added),
1625 			{
1626 				TRACE_DEBUG(INFO, "Error converting data from request rules: %s", strerror(ret));
1627 				return ret;
1628 			}   );
1629 	CHECK_FCT_DO( ret = rules_to_fD(fD_dict, ans, &c->ansrules_fixed, &c->ansrules_required, &c->ansrules_optional, nb_added),
1630 			{
1631 				TRACE_DEBUG(INFO, "Error converting data from answer rules: %s", strerror(ret));
1632 				return ret;
1633 			}   );
1634 
1635 	/* Done */
1636 	return 0;
1637 }
1638 
1639 /* Find or create an application (and dependent objects) */
appl_to_fD(struct t_appl * a,struct dictionary * fD_dict,struct dict_object ** fd_a,int * nb_added)1640 static int appl_to_fD(struct t_appl * a, struct dictionary * fD_dict, struct dict_object ** fd_a, int * nb_added)
1641 {
1642 	int ret;
1643 	struct dict_object * prev = NULL;
1644 	struct dict_application_data ad;
1645 	struct fd_list * li;
1646 
1647 	TRACE_ENTRY("%p %p %p %p", a, fD_dict, fd_a, nb_added);
1648 
1649 	CHECK_PARAMS(a && fD_dict);
1650 
1651 	if (a->id) { /* skip app 0 */
1652 
1653 		/* Prepare the data in fD's format */
1654 		memset(&ad, 0, sizeof(ad));
1655 		ad.application_id   = a->id;
1656 		ad.application_name = (char *)a->name;
1657 
1658 		/* Create or search in the dictionary */
1659 		ret = fd_dict_new ( fD_dict,
1660 					DICT_APPLICATION,
1661 					&ad,
1662 					NULL /* we don't have a parent vendor in XML files, so currently everything links to no vendor */,
1663 					&prev );
1664 		if (fd_a)
1665 			*fd_a = prev;
1666 		if (ret == EEXIST) {
1667 			/* Conflict with existing entry */
1668 			CHECK_FCT( fd_dict_getval(prev, &ad) );
1669 			TRACE_DEBUG(INFO, "[dict_legacy_xml] Warning: Conflicting entry.");
1670 			TRACE_DEBUG(INFO, "[dict_legacy_xml]   New entry (ignored): %u - '%s'", a->id, (char *)a->name);
1671 			TRACE_DEBUG(INFO, "[dict_legacy_xml]   Old entry          : %u - '%s'", ad.application_id, ad.application_name);
1672 			goto inside;
1673 		} else {
1674 			/* other errors are stoppers */
1675 			CHECK_FCT(ret);
1676 		}
1677 
1678 		/* Update count */
1679 		if (nb_added)
1680 			*nb_added += 1;
1681 	}
1682 
1683 inside:
1684 	/* Now, the inner elements */
1685 
1686 	/* First, define all the types */
1687 	for (li = a->types.next; li != &a->types; li = li->next) {
1688 		CHECK_FCT_DO( ret = typdefn_to_fD((struct t_typedefn *)li, fD_dict, prev, NULL, nb_added),
1689 			{
1690 				TRACE_DEBUG(INFO, "Error converting data from typedefn '%s': %s", ((struct t_typedefn *)li)->name, strerror(ret));
1691 				return ret;
1692 			}  );
1693 	}
1694 
1695 	/* Then, AVPs, enums, and grouped AVP rules */
1696 	for (li = a->avps.next; li != &a->avps; li = li->next) {
1697 		CHECK_FCT_DO( ret = avp_to_fD((struct t_avp *)li, fD_dict, prev, NULL, nb_added),
1698 			{
1699 				TRACE_DEBUG(INFO, "Error converting data from AVP '%s': %s", ((struct t_avp *)li)->name, strerror(ret));
1700 				return ret;
1701 			}  );
1702 	}
1703 
1704 	/* Finally, the commands and rules */
1705 	for (li = a->commands.next; li != &a->commands; li = li->next) {
1706 		CHECK_FCT_DO( ret = cmd_to_fD((struct t_cmd *)li, fD_dict, prev, NULL, nb_added),
1707 			{
1708 				TRACE_DEBUG(INFO, "Error converting data from command '%s': %s", ((struct t_cmd *)li)->name, strerror(ret));
1709 				return ret;
1710 			}  );
1711 	}
1712 
1713 	/* done! */
1714 	return 0;
1715 }
1716 
1717 
dict_to_fD(struct dictionary * fD_dict,struct t_dictionary * xmldict,int * nb_added)1718 static int dict_to_fD(struct dictionary * fD_dict, struct t_dictionary * xmldict, int * nb_added)
1719 {
1720 	struct fd_list * li;
1721 	int ret;
1722 
1723 	TRACE_ENTRY("%p %p %p", fD_dict, xmldict, nb_added);
1724 
1725 	CHECK_PARAMS(fD_dict && xmldict && nb_added);
1726 
1727 	*nb_added = 0;
1728 
1729 	/* Create all the vendors */
1730 	for (li = xmldict->vendors.next; li != &xmldict->vendors; li = li->next) {
1731 		CHECK_FCT_DO( ret = vend_to_fD((struct t_vend *)li, fD_dict, NULL, nb_added),
1732 			{
1733 				TRACE_DEBUG(INFO, "Error converting data from vendor '%s': %s", ((struct t_vend *)li)->name, strerror(ret));
1734 				return ret;
1735 			} );
1736 	}
1737 
1738 	/* Now, process each application */
1739 	CHECK_FCT_DO( ret = appl_to_fD(&xmldict->base_and_applications, fD_dict, NULL, nb_added),
1740 			{
1741 				TRACE_DEBUG(INFO, "Error converting data from Base application: %s", strerror(ret));
1742 				return ret;
1743 			} );
1744 	for (li = xmldict->base_and_applications.chain.next; li != &xmldict->base_and_applications.chain; li = li->next) {
1745 		CHECK_FCT_DO( ret = appl_to_fD((struct t_appl *) li, fD_dict, NULL, nb_added),
1746 			{
1747 				TRACE_DEBUG(INFO, "Error converting data from application '%s': %s", ((struct t_appl *)li)->name, strerror(ret));
1748 				return ret;
1749 			}  );
1750 	}
1751 
1752 	/* Complete! */
1753 	return 0;
1754 }
1755 
1756 
1757 
1758 
1759 
1760 /*********************************************/
1761 
dict_lxml_parse(char * xmlfilename)1762 int dict_lxml_parse(char * xmlfilename)
1763 {
1764 	xmlSAXHandler handler;
1765 	struct parser_ctx data;
1766 	int ret;
1767 
1768 	TRACE_ENTRY("%p", xmlfilename);
1769 
1770 	CHECK_PARAMS_DO(xmlfilename, { return -1; } );
1771 
1772 	TRACE_DEBUG(FULL, "Parsing next XML file: %s...", xmlfilename);
1773 
1774 	/* Initialize the parser */
1775 	memset(&handler, 0, sizeof(handler));
1776 	handler.startElement = SAXstartelem;
1777 	handler.endElement   = SAXendelem;
1778 
1779 	/* Initialize the data */
1780 	memset(&data, 0, sizeof(data));
1781 	fd_list_init( &data.dict.vendors, NULL );
1782 	fd_list_init( &data.dict.base_and_applications.chain, NULL );
1783 	data.dict.base_and_applications.name = (uint8_t *)"[Diameter Base Protocol]";
1784 	fd_list_init( &data.dict.base_and_applications.commands, NULL );
1785 	fd_list_init( &data.dict.base_and_applications.types, NULL );
1786 	fd_list_init( &data.dict.base_and_applications.avps, NULL );
1787 	data.xmlfilename = xmlfilename;
1788 
1789 	/* Parse the file */
1790 	ret = xmlSAXUserParseFile(&handler, &data, xmlfilename);
1791 	if (ret < 0) {
1792 		TRACE_DEBUG(INFO, "An error occurred while parsing %s, aborting.", xmlfilename);
1793 		del_dict_contents(&data.dict);
1794 		return -1;
1795 	}
1796 
1797 	TRACE_DEBUG(FULL, "XML file parsing, 1st pass completed.");
1798 	if (TRACE_BOOL(ANNOYING)) {
1799 		dump_dict(&data.dict);
1800 	}
1801 
1802 	/* Now, convert all the objects from the temporary tree into the freeDiameter dictionary */
1803 	CHECK_FCT_DO( dict_to_fD(fd_g_config->cnf_dict, &data.dict, &ret),
1804 		{
1805 			TRACE_DEBUG(INFO, "Error while converting data read from file '%s'", xmlfilename);
1806 			del_dict_contents(&data.dict);
1807 			return -1;
1808 		} );
1809 
1810 	TRACE_DEBUG(FULL, "Conversion from '%s' to freeDiameter internal format complete.", xmlfilename);
1811 
1812 	/* Done */
1813 	del_dict_contents(&data.dict);
1814 
1815 	return ret;
1816 }
1817