1 /*********************************************************************************************************
2 * Software License Agreement (BSD License)                                                               *
3 * Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
4 *													 *
5 * Copyright (c) 2020, 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 "fdcore-internal.h"
37 
38 static struct dict_object * dict_avp_SI  = NULL; /* Session-Id */
39 static struct dict_object * dict_avp_OH  = NULL; /* Origin-Host */
40 static struct dict_object * dict_avp_OR  = NULL; /* Origin-Realm */
41 static struct dict_object * dict_avp_EM  = NULL; /* Error-Message */
42 static struct dict_object * dict_avp_ERH = NULL; /* Error-Reporting-Host */
43 static struct dict_object * dict_avp_FAVP= NULL; /* Failed-AVP */
44 static struct dict_object * dict_avp_RC  = NULL; /* Result-Code */
45 static struct dict_object * dict_avp_ER  = NULL; /* Experimental-Result */
46 static struct dict_object * dict_avp_VI  = NULL; /* Vendor-Id */
47 static struct dict_object * dict_avp_ERC = NULL; /* Experimental-Result-Code */
48 struct dict_object * fd_dict_avp_OSI = NULL; /* Origin-State-Id */
49 struct dict_object * fd_dict_cmd_CER = NULL; /* Capabilities-Exchange-Request */
50 struct dict_object * fd_dict_cmd_DWR = NULL; /* Device-Watchdog-Request */
51 struct dict_object * fd_dict_avp_DC  = NULL; /* Disconnect-Cause */
52 struct dict_object * fd_dict_cmd_DPR = NULL; /* Disconnect-Peer-Request */
53 
54 /* Resolve the dictionary objects */
fd_msg_init(void)55 int fd_msg_init(void)
56 {
57 	TRACE_ENTRY("");
58 
59 	/* Initialize the dictionary objects that we may use frequently */
60 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", 	&dict_avp_SI , ENOENT)  );
61 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host",     	&dict_avp_OH  , ENOENT)  );
62 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm",    	&dict_avp_OR  , ENOENT)  );
63 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-State-Id", 	&fd_dict_avp_OSI , ENOENT)  );
64 
65 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code",     	&dict_avp_RC  , ENOENT)  );
66 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Message",   	&dict_avp_EM  , ENOENT)  );
67 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &dict_avp_ERH , ENOENT)  );
68 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Failed-AVP",      	&dict_avp_FAVP, ENOENT)  );
69 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Experimental-Result", &dict_avp_ER, ENOENT)  );
70 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Id",		&dict_avp_VI, ENOENT)  );
71 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Experimental-Result-Code", &dict_avp_ERC, ENOENT)  );
72 
73 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Disconnect-Cause", 	&fd_dict_avp_DC , ENOENT)  );
74 
75 	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &fd_dict_cmd_CER, ENOENT ) );
76 	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request", &fd_dict_cmd_DWR, ENOENT ) );
77 	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Disconnect-Peer-Request", &fd_dict_cmd_DPR, ENOENT ) );
78 
79 
80 	return 0;
81 }
82 
83 /* Add Origin-Host, Origin-Realm, Origin-State-Id AVPS at the end of the message */
fd_msg_add_origin(struct msg * msg,int osi)84 int fd_msg_add_origin ( struct msg * msg, int osi )
85 {
86 	union avp_value val;
87 	struct avp * avp_OH  = NULL;
88 	struct avp * avp_OR  = NULL;
89 	struct avp * avp_OSI = NULL;
90 
91 	TRACE_ENTRY("%p", msg);
92 	CHECK_PARAMS(  msg  );
93 
94 	/* Create the Origin-Host AVP */
95 	CHECK_FCT( fd_msg_avp_new( dict_avp_OH, 0, &avp_OH ) );
96 
97 	/* Set its value */
98 	memset(&val, 0, sizeof(val));
99 	val.os.data = (os0_t)fd_g_config->cnf_diamid;
100 	val.os.len  = fd_g_config->cnf_diamid_len;
101 	CHECK_FCT( fd_msg_avp_setvalue( avp_OH, &val ) );
102 
103 	/* Add it to the message */
104 	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OH ) );
105 
106 
107 	/* Create the Origin-Realm AVP */
108 	CHECK_FCT( fd_msg_avp_new( dict_avp_OR, 0, &avp_OR ) );
109 
110 	/* Set its value */
111 	memset(&val, 0, sizeof(val));
112 	val.os.data = (os0_t)fd_g_config->cnf_diamrlm;
113 	val.os.len  = fd_g_config->cnf_diamrlm_len;
114 	CHECK_FCT( fd_msg_avp_setvalue( avp_OR, &val ) );
115 
116 	/* Add it to the message */
117 	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OR ) );
118 
119 	if (osi) {
120 		/* Create the Origin-State-Id AVP */
121 		CHECK_FCT( fd_msg_avp_new( fd_dict_avp_OSI, 0, &avp_OSI ) );
122 
123 		/* Set its value */
124 		memset(&val, 0, sizeof(val));
125 		val.u32 = fd_g_config->cnf_orstateid;
126 		CHECK_FCT( fd_msg_avp_setvalue( avp_OSI, &val ) );
127 
128 		/* Add it to the message */
129 		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OSI ) );
130 	}
131 
132 	return 0;
133 }
134 
135 /* Create a new Session-Id and add at the beginning of the message. */
fd_msg_new_session(struct msg * msg,os0_t opt,size_t optlen)136 int fd_msg_new_session( struct msg * msg, os0_t opt, size_t optlen )
137 {
138 	union avp_value val;
139 	struct avp * avp  = NULL;
140 	struct session * sess = NULL;
141 	os0_t sid;
142 	size_t sidlen;
143 
144 	TRACE_ENTRY("%p %p %zd", msg, opt, optlen);
145 	CHECK_PARAMS(  msg  );
146 
147 	/* Check there is not already a session in the message */
148 	CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, msg, &sess, NULL) );
149 	CHECK_PARAMS( sess == NULL );
150 
151 	/* Ok, now create the session */
152 	CHECK_FCT( fd_sess_new ( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, opt, optlen ) );
153 	CHECK_FCT( fd_sess_getsid( sess, &sid, &sidlen) );
154 
155 	/* Create an AVP to hold it */
156 	CHECK_FCT( fd_msg_avp_new( dict_avp_SI, 0, &avp ) );
157 
158 	/* Set its value */
159 	memset(&val, 0, sizeof(val));
160 	val.os.data = sid;
161 	val.os.len  = sidlen;
162 	CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
163 
164 	/* Add it to the message */
165 	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_FIRST_CHILD, avp ) );
166 
167 	/* Save the session associated with the message */
168 	CHECK_FCT( fd_msg_sess_set( msg, sess) );
169 
170 	/* Done! */
171 	return 0;
172 }
173 
174 
175 /* Add Result-Code or Experimental-Result, and eventually Failed-AVP, Error-Message and Error-Reporting-Host AVPs */
fd_msg_add_result(struct msg * msg,vendor_id_t vendor,struct dict_object * restype,char * rescode,char * errormsg,struct avp * optavp,int type_id)176 int fd_msg_add_result( struct msg * msg, vendor_id_t vendor, struct dict_object * restype, char * rescode, char * errormsg, struct avp * optavp, int type_id )
177 {
178 	union avp_value val;
179 	uint32_t rc_val = 0;
180 	int set_e_bit=0;
181 	int std_err_msg=0;
182 
183 	TRACE_ENTRY("%p %d %p %s %p %p %d", msg, vendor, restype, rescode, errormsg, optavp, type_id);
184 
185 	CHECK_PARAMS(  msg && restype && rescode  );
186 
187 	/* Find the enum value corresponding to the rescode string, this will give the class of error */
188 	{
189 		struct dict_object * enum_obj = NULL;
190 
191 		/* Search in the restype */
192 		struct dict_enumval_request req;
193 		memset(&req, 0, sizeof(struct dict_enumval_request));
194 		req.type_obj = restype;
195 
196 		/* Now search for the value given as parameter */
197 		req.search.enum_name = rescode;
198 		CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &req, &enum_obj, ENOTSUP)  );
199 
200 		/* finally retrieve its data */
201 		CHECK_FCT_DO(  fd_dict_getval( enum_obj, &(req.search) ), return EINVAL );
202 
203 		/* copy the found value, we're done */
204 		rc_val = req.search.enum_value.u32;
205 	}
206 
207 	if (type_id == 1) {
208 		/* Add the Origin-Host and Origin-Realm AVP */
209 		CHECK_FCT( fd_msg_add_origin ( msg, 0 ) );
210 	}
211 
212 	if (vendor == 0) {
213 		/* Vendor 0; create the Result-Code AVP */
214 		struct avp * avp_RC  = NULL;
215 		CHECK_FCT( fd_msg_avp_new( dict_avp_RC, 0, &avp_RC ) );
216 
217 		/* Set its value */
218 		memset(&val, 0, sizeof(val));
219 		val.u32  = rc_val;
220 		CHECK_FCT( fd_msg_avp_setvalue( avp_RC, &val ) );
221 
222 		/* Add it to the message */
223 		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_RC ) );
224 	} else {
225 		/* Vendor !0; create the Experimental-Result AVP */
226 		struct avp * avp_ER  = NULL;
227 		CHECK_FCT( fd_msg_avp_new( dict_avp_ER, 0, &avp_ER ) );
228 
229 		/* Create the Vendor-Id AVP and add to Experimental-Result */
230 		{
231 			struct avp * avp_VI  = NULL;
232 			CHECK_FCT( fd_msg_avp_new( dict_avp_VI, 0, &avp_VI ) );
233 
234 			/* Set Vendor-Id value to vendor */
235 			memset(&val, 0, sizeof(val));
236 			val.u32  = vendor;
237 			CHECK_FCT( fd_msg_avp_setvalue( avp_VI, &val ) );
238 
239 			/* Add it to Experimental-Result */
240 			CHECK_FCT( fd_msg_avp_add( avp_ER, MSG_BRW_LAST_CHILD, avp_VI ) );
241 		}
242 
243 		/* Create the Experimental-Result-Code AVP and add to Experimental-Result */
244 		{
245 			struct avp * avp_ERC  = NULL;
246 			CHECK_FCT( fd_msg_avp_new( dict_avp_ERC, 0, &avp_ERC ) );
247 
248 			/* Set Experimental-Result-Code value to rc_val */
249 			memset(&val, 0, sizeof(val));
250 			val.u32  = rc_val;
251 			CHECK_FCT( fd_msg_avp_setvalue( avp_ERC, &val ) );
252 
253 			/* Add it to Experimental-Result */
254 			CHECK_FCT( fd_msg_avp_add( avp_ER, MSG_BRW_LAST_CHILD, avp_ERC ) );
255 		}
256 
257 		/* Add it to the message */
258 		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_ER ) );
259 	}
260 
261 	if (type_id == 2) {
262 		/* Add the Error-Reporting-Host AVP */
263 		struct avp * avp_ERH = NULL;
264 		CHECK_FCT( fd_msg_avp_new( dict_avp_ERH, 0, &avp_ERH ) );
265 
266 		/* Set its value */
267 		memset(&val, 0, sizeof(val));
268 		val.os.data = (uint8_t *)fd_g_config->cnf_diamid;
269 		val.os.len  = fd_g_config->cnf_diamid_len;
270 		CHECK_FCT( fd_msg_avp_setvalue( avp_ERH, &val ) );
271 
272 		/* Add it to the message */
273 		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_ERH ) );
274 	}
275 
276 	/* Now add the optavp in a Failed-AVP if provided */
277 	if (optavp) {
278 		struct avp * avp_FAVP= NULL;
279 		struct avp * optavp_cpy = NULL;
280 		struct avp_hdr *opt_hdr, *optcpy_hdr;
281 		struct dict_object * opt_model = NULL;
282 		int is_grouped = 0;
283 
284 		/* Create the Failed-AVP AVP */
285 		CHECK_FCT( fd_msg_avp_new( dict_avp_FAVP, 0, &avp_FAVP ) );
286 
287 		/* Was this AVP a grouped one? Best effort only here */
288 		if (!fd_msg_model ( optavp, &opt_model ) && (opt_model != NULL)) {
289 			struct dict_avp_data  dictdata;
290 			CHECK_FCT(  fd_dict_getval(opt_model, &dictdata)  );
291 			if (dictdata.avp_basetype == AVP_TYPE_GROUPED)
292 				is_grouped = 1;
293 		}
294 
295 		/* Create a new AVP with a copy of the data of the invalid or missing AVP */
296 		optavp_cpy = optavp;
297 
298 		if (is_grouped) {
299 			CHECK_FCT( fd_msg_avp_new( opt_model, 0, &optavp_cpy) );
300 		} else {
301 			CHECK_FCT( fd_msg_avp_new( NULL, AVPFL_SET_BLANK_VALUE | AVPFL_SET_RAWDATA_FROM_AVP, &optavp_cpy) );
302 
303 			CHECK_FCT( fd_msg_avp_hdr(optavp, &opt_hdr) );
304 			CHECK_FCT( fd_msg_avp_hdr(optavp_cpy, &optcpy_hdr) );
305 			memcpy(optcpy_hdr, opt_hdr, sizeof(struct avp_hdr));
306 		}
307 
308 		/* Add the passed AVP inside it */
309 		CHECK_FCT( fd_msg_avp_add( avp_FAVP, MSG_BRW_LAST_CHILD, optavp_cpy ) );
310 
311 		/* And add to the message */
312 		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_FAVP ) );
313 	}
314 
315 
316 	/* Deal with the 'E' bit and the error message */
317 	switch (rc_val / 1000) {
318 		case 1:	/* Informational */
319 		case 2: /* Success */
320 			/* Nothing special here: no E bit, no error message unless one is specified */
321 			break;
322 
323 		case 3: /* Protocol Errors */
324 			set_e_bit = 1;
325 			std_err_msg = 1;
326 			break;
327 
328 		case 4: /* Transcient Failure */
329 		case 5: /* Permanent Failure */
330 			if (rc_val == 5017) /* DIAMETER_NO_COMMON_SECURITY */ {
331 				set_e_bit = 1;
332 			}
333 		default:
334 			std_err_msg = 1;
335 			break;
336 
337 	}
338 
339 	{
340 		struct msg_hdr * hdr = NULL;
341 
342 		CHECK_FCT(  fd_msg_hdr( msg, &hdr )  );
343 
344 		if (set_e_bit)
345 			hdr->msg_flags |= CMD_FLAG_ERROR;
346 		else
347 			hdr->msg_flags &= ~ CMD_FLAG_ERROR;
348 	}
349 
350 	if (std_err_msg || errormsg) {
351 		/* Add the Error-Message AVP */
352 		struct avp * avp_EM  = NULL;
353 		CHECK_FCT( fd_msg_avp_new( dict_avp_EM, 0, &avp_EM ) );
354 
355 		/* Set its value */
356 		memset(&val, 0, sizeof(val));
357 
358 		if (errormsg) {
359 			val.os.data = (uint8_t *)errormsg;
360 			val.os.len  = strlen(errormsg);
361 		} else {
362 			val.os.data = (uint8_t *)rescode;
363 			val.os.len  = strlen(rescode);
364 		}
365 		CHECK_FCT( fd_msg_avp_setvalue( avp_EM, &val ) );
366 
367 		/* Add it to the message */
368 		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_EM ) );
369 	}
370 
371 	return 0;
372 }
373 
fd_msg_rescode_set(struct msg * msg,char * rescode,char * errormsg,struct avp * optavp,int type_id)374 int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id )
375 {
376 	struct dict_object * restype = NULL;
377 	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, dict_avp_RC, &restype, ENOENT ) );
378 	return fd_msg_add_result(msg, 0, restype, rescode, errormsg, optavp, type_id);
379 }
380 
fd_msg_send_int(struct msg ** pmsg,void (* anscb)(void *,struct msg **),void * data,void (* expirecb)(void *,DiamId_t,size_t,struct msg **),const struct timespec * timeout)381 static int fd_msg_send_int( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, void (*expirecb)(void *, DiamId_t, size_t, struct msg **), const struct timespec *timeout )
382 {
383 	struct msg_hdr *hdr;
384 	DiamId_t diamid;
385 
386 	/* Save the callback in the message, with the timeout */
387 	CHECK_FCT(  fd_msg_anscb_associate( *pmsg, anscb, data, expirecb, timeout )  );
388 
389 	/* If this is a new request, call the HOOK_MESSAGE_LOCAL hook */
390 	if ( (fd_msg_hdr(*pmsg, &hdr) == 0)
391 	 &&  (hdr->msg_flags & CMD_FLAG_REQUEST)
392 	 &&  (fd_msg_source_get(*pmsg, &diamid, NULL) == 0)
393 	 &&  (diamid == NULL)) {
394 		fd_hook_call(HOOK_MESSAGE_LOCAL, *pmsg, NULL, NULL, fd_msg_pmdl_get(*pmsg));
395 	}
396 
397 	/* Post the message in the outgoing queue */
398 	CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
399 
400 	return 0;
401 }
402 
403 /* Send a message and optionally register a callback for an answer */
fd_msg_send(struct msg ** pmsg,void (* anscb)(void *,struct msg **),void * data)404 int fd_msg_send ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data )
405 {
406 	TRACE_ENTRY("%p %p %p", pmsg, anscb, data);
407 	CHECK_PARAMS( pmsg );
408 
409 	return fd_msg_send_int(pmsg, anscb, data, NULL, NULL);
410 }
411 
412 /* The variation of the same function with a timeout callback */
fd_msg_send_timeout(struct msg ** pmsg,void (* anscb)(void *,struct msg **),void * data,void (* expirecb)(void *,DiamId_t,size_t,struct msg **),const struct timespec * timeout)413 int fd_msg_send_timeout ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, void (*expirecb)(void *, DiamId_t, size_t, struct msg **), const struct timespec *timeout )
414 {
415 	TRACE_ENTRY("%p %p %p %p %p", pmsg, anscb, data, expirecb, timeout);
416 	CHECK_PARAMS( pmsg && expirecb && timeout );
417 
418 	return fd_msg_send_int(pmsg, anscb, data, expirecb, timeout);
419 }
420 
421 
422 /* Parse a message against our dictionary, and in case of error log and eventually build the error reply -- returns the parsing status */
fd_msg_parse_or_error(struct msg ** msg,struct msg ** error)423 int fd_msg_parse_or_error( struct msg ** msg, struct msg **error)
424 {
425 	int ret = 0;
426 	struct msg * m;
427 	struct msg_hdr * hdr = NULL;
428 	struct fd_pei	pei;
429 
430 	TRACE_ENTRY("%p", msg);
431 
432 	CHECK_PARAMS(msg && *msg && error);
433 	m = *msg;
434 	*error = NULL;
435 
436 	/* Parse the message against our dictionary */
437 	ret = fd_msg_parse_rules ( m, fd_g_config->cnf_dict, &pei);
438 	if 	((ret != EBADMSG) 	/* Parsing grouped AVP failed / Conflicting rule found */
439 		&& (ret != ENOTSUP))	/* Command is not supported / Mandatory AVP is not supported */
440 		return ret; /* 0 or another error */
441 
442 	/* Log */
443 	fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, m, NULL, pei.pei_message ?: pei.pei_errcode, fd_msg_pmdl_get(m));
444 
445 	CHECK_FCT( fd_msg_hdr(m, &hdr) );
446 
447 	/* Now create an answer error if the message is a query */
448 	if (hdr->msg_flags & CMD_FLAG_REQUEST) {
449 
450 		/* Create the error message */
451 		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &m, pei.pei_protoerr ? MSGFL_ANSW_ERROR : 0 ) );
452 
453 		/* Set the error code */
454 		CHECK_FCT( fd_msg_rescode_set(m, pei.pei_errcode, pei.pei_message, pei.pei_avp, 1 ) );
455 
456 		/* free the pei AVP to avoid memory leak */
457 		if (pei.pei_avp_free) {
458 			fd_msg_free(pei.pei_avp);
459 		}
460 
461 		*msg = NULL;
462 		*error = m;
463 
464 	} else {
465 		do { /* Rescue error messages */
466 			struct avp * avp;
467 			union avp_value * rc = NULL;
468 
469 			/* Search the Result-Code AVP */
470 			CHECK_FCT_DO(  fd_msg_browse(*msg, MSG_BRW_FIRST_CHILD, &avp, NULL), break  );
471 			while (avp) {
472 				struct avp_hdr * ahdr;
473 				CHECK_FCT_DO(  fd_msg_avp_hdr( avp, &ahdr ), break  );
474 
475 				if ((ahdr->avp_code == AC_RESULT_CODE) && (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) ) {
476 					/* Parse this AVP */
477 					if (fd_msg_parse_dict(avp, fd_g_config->cnf_dict, &pei) < 0) {
478 						TRACE_DEBUG(INFO, "error parsing Result-Code AVP");
479 						rc = NULL;
480 						break;
481 					}
482 					rc = ahdr->avp_value;
483 					if (rc == NULL) {
484 						TRACE_DEBUG(INFO, "invalid Result-Code AVP");
485 						break;
486 					}
487 					break;
488 				}
489 
490 				/* Go to next AVP */
491 				CHECK_FCT_DO(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), break  );
492 			}
493 
494 			if (rc) {
495 				switch (rc->u32 / 1000) {
496 					case 1:	/* 1xxx : Informational */
497 					case 2:	/* 2xxx : Sucess */
498 						/* In these cases, we want the message to validate the ABNF, so we will discard the bad message */
499 						break;
500 
501 					default: /* Other errors */
502 						/* We let the application decide what to do with the message, we rescue it */
503 						*error = m;
504 				}
505 			}
506 		} while (0);
507 	}
508 
509 	return EBADMSG; /* We convert ENOTSUP to EBADMSG as well */
510 }
511