all: updated FSF address in GPL text
[sip-router] / modules / cdp / diameter_avp.c
1 /*
2  * $Id$
3  *
4  * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
5  * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
6  * 
7  * The initial version of this code was written by Dragos Vingarzan
8  * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
9  * Fruanhofer Institute. It was and still is maintained in a separate
10  * branch of the original SER. We are therefore migrating it to
11  * Kamailio/SR and look forward to maintaining it from here on out.
12  * 2011/2012 Smile Communications, Pty. Ltd.
13  * ported/maintained/improved by 
14  * Jason Penton (jason(dot)penton(at)smilecoms.com and
15  * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
16  * effort to add full IMS support to Kamailio/SR using a new and
17  * improved architecture
18  * 
19  * NB: Alot of this code was originally part of OpenIMSCore,
20  * FhG Fokus. 
21  * Copyright (C) 2004-2006 FhG Fokus
22  * Thanks for great work! This is an effort to 
23  * break apart the various CSCF functions into logically separate
24  * components. We hope this will drive wider use. We also feel
25  * that in this way the architecture is more complete and thereby easier
26  * to manage in the Kamailio/SR environment
27  *
28  * This file is part of Kamailio, a free SIP server.
29  *
30  * Kamailio is free software; you can redistribute it and/or modify
31  * it under the terms of the GNU General Public License as published by
32  * the Free Software Foundation; either version 2 of the License, or
33  * (at your option) any later version
34  *
35  * Kamailio is distributed in the hope that it will be useful,
36  * but WITHOUT ANY WARRANTY; without even the implied warranty of
37  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
38  * GNU General Public License for more details.
39  *
40  * You should have received a copy of the GNU General Public License 
41  * along with this program; if not, write to the Free Software 
42  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
43  * 
44  */
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <netinet/in.h>
50
51 #include "diameter.h"
52 #include "utils.h"
53
54
55 /* Start of disc implementation */
56
57 /**
58  * Takes care that each AVP type has the default flags set/reset and a proper data type.
59  * All this default values (for flags and data-type) are correct/set by this
60  * function.
61  * @param code - code of the AVP
62  * @param avp - the actual AVP to set flags
63  * \note This function is taken from DISC http://developer.berlios.de/projects/disc/
64  */
65 inline void set_avp_fields( AAA_AVPCode code, AAA_AVP *avp)
66 {
67         switch (code) {
68                 case   1: /*AVP_User_Name*/
69                 case  25: /*AVP_Class*/
70                 case 263: /*AVP_Session_Id*/
71                 case 283: /*AVP_Destination_Realm*/
72                 case 293: /*AVP Destination Host*/
73                 case 264: /*AVP_Origin_Host*/
74                 case 296: /*AVP Origin_Realm*/
75                         avp->flags = 0x40|(0x20&avp->flags);
76                         avp->type = AAA_AVP_STRING_TYPE;
77                         break;
78                 case  27: /*AVP_Session_Timeout*/
79                 case 258: /*AVP_Auth_Aplication_Id*/
80                 case 262: /*AVP_Redirect_Max_Cache_Time*/
81                 case 265: /*AVP_Supported_Vendor_Id*/
82                 case 266: /*AVP_Vendor_Id*/
83                 case 268: /*AVP_Result_Code*/
84                 case 270: /*AVP_Session_Binding*/
85                 case 276: /*AVP_Auth_Grace_Period*/
86                 case 278: /*AVP_Origin_State_Id*/
87                 case 291: /*AVP_Authorization_Lifetime*/
88                         avp->flags = 0x40|(0x20&avp->flags);
89                         avp->type = AAA_AVP_INTEGER32_TYPE;
90                         break;
91                 case 33: /*AVP_Proxy_State*/
92                         avp->flags = 0x40;
93                         avp->type = AAA_AVP_STRING_TYPE;
94                         break;
95                 case 257: /*AVP_Host_IP_Address*/
96                         avp->flags = 0x40|(0x20&avp->flags);
97                         avp->type = AAA_AVP_ADDRESS_TYPE;
98                         break;
99                 case 269: /*AVP_Product_Name*/
100                         avp->flags = 0x00;
101                         avp->type = AAA_AVP_STRING_TYPE;
102                         break;
103                 case 281: /*AVP_Error_Message*/
104                         avp->flags = (0x20&avp->flags);
105                         avp->type = AAA_AVP_STRING_TYPE;
106                         break;
107                 default:
108                         avp->type = AAA_AVP_DATA_TYPE;
109         };
110 }
111
112
113
114 /** 
115  * This function creates an AVP and returns a pointer to it.
116  * @param code - the code of the new AVP
117  * @param flags - the flags to set
118  * @param vendorId - vendor id
119  * @param data - the generic payload data
120  * @param length - length of the payload
121  * @param data_status - what to do with the payload: duplicate, free with the message, etc
122  * @returns the AAA_AVP* or null on error
123  * \note This function is taken from DISC http://developer.berlios.de/projects/disc/
124  */
125 AAA_AVP*  AAACreateAVP(
126         AAA_AVPCode code,
127         AAA_AVPFlag flags,
128         AAAVendorId vendorId,
129         char   *data,
130         size_t length,
131         AVPDataStatus data_status)
132 {
133         AAA_AVP *avp;
134
135         /* first check the params */
136         if( data==0 || length==0) {
137                 LM_ERR("AAACreateAVP: NULL value received for"
138                         " param data/length (AVP Code %d, VendorId %d)!!\n",code,vendorId);
139                 return 0;
140         }
141
142         /* allocated a new AVP struct */
143         avp = 0;
144         avp = (AAA_AVP*)shm_malloc(sizeof(AAA_AVP));
145         if (!avp)
146                 goto error;
147         memset( avp, 0, sizeof(AAA_AVP) );
148
149         /* set some fields */
150         //avp->free_it = free_it;
151         avp->code=code;
152         avp->flags=flags;
153         avp->vendorId=vendorId;
154         set_avp_fields( code, avp);
155
156         if ( data_status==AVP_DUPLICATE_DATA ) {
157                 /* make a duplicate for data */
158                 avp->data.len = length;
159                 avp->data.s = (void*)shm_malloc(length);
160                 if(!avp->data.s)
161                         goto error;
162                 memcpy( avp->data.s, data, length);
163                 avp->free_it = 1;
164         } else {
165                 avp->data.s = data;
166                 avp->data.len = length;
167                 avp->free_it = (data_status==AVP_FREE_DATA)?1:0;
168         }
169
170         return avp;
171 error:
172         LM_ERR("AAACreateAVP: no more free memory!\n");
173         return 0;
174 }
175
176
177
178 /**
179  *  Insert the AVP avp into the avpList of a message, after a certain position.
180  * @param msg - the AAAMessage to add to
181  * @param avp - the AAA_AVP to add
182  * @param position - AAA_AVP to add after. If NULL, will add at the beginning.
183  * @returns AAA_ERR_SUCCESS on success or AAA_ERR_PARAMETER if the position is not found
184  * \note This function is taken from DISC http://developer.berlios.de/projects/disc/
185  */
186 AAAReturnCode  AAAAddAVPToMessage(
187         AAAMessage *msg,
188         AAA_AVP *avp,
189         AAA_AVP *position)
190 {
191         AAA_AVP *avp_t;
192
193         if ( !msg || !avp ) {
194                 LM_ERR("AAAAddAVPToMessage: param msg or avp passed null"
195                         " or *avpList=NULL and position!=NULL !!\n");
196                 return AAA_ERR_PARAMETER;
197         }
198
199         if (!position) {
200                 /* insert at the begining */
201                 avp->next = msg->avpList.head;
202                 avp->prev = 0;
203                 msg->avpList.head = avp;
204                 if (avp->next)
205                         avp->next->prev = avp;
206                 else
207                         msg->avpList.tail = avp;
208         } else {
209                 /* look after avp from position */
210                 for(avp_t=msg->avpList.head;avp_t&&avp_t!=position;avp_t=avp_t->next);
211                 if (!avp_t) {
212                         LM_ERR("AAAAddAVPToMessage: the \"position\" avp is not in"
213                                 "\"msg\" message!!\n");
214                         return AAA_ERR_PARAMETER;
215                 }
216                 /* insert after position */
217                 avp->next = position->next;
218                 position->next = avp;
219                 if (avp->next)
220                         avp->next->prev = avp;
221                 else
222                         msg->avpList.tail = avp;
223                 avp->prev = position;
224         }
225
226         /* update the short-cuts */
227         switch (avp->code) {
228                 case AVP_Session_Id: msg->sessionId = avp;break;
229                 case AVP_Origin_Host: msg->orig_host = avp;break;
230                 case AVP_Origin_Realm: msg->orig_realm = avp;break;
231                 case AVP_Destination_Host: msg->dest_host = avp;break;
232                 case AVP_Destination_Realm: msg->dest_realm = avp;break;
233                 case AVP_Result_Code: msg->res_code = avp;break;
234                 case AVP_Auth_Session_State: msg->auth_ses_state = avp;break;
235         }
236
237         return AAA_ERR_SUCCESS;
238 }
239
240
241
242 /**
243  *  This function finds an AVP with matching code and vendor id.
244  * @param msg - message to look into
245  * @param startAvp - where to start the search. Usefull when you want to find the next one.
246  *      Even this one will be checked and can be returned if it fits.
247  * @param avpCode - code of the AVP to match
248  * @param vendorId - vendor id to match
249  * @param searchType - whether to look forward or backward
250  * @returns the AAA_AVP* if found, NULL if not 
251  * \note This function is taken from DISC http://developer.berlios.de/projects/disc/
252  */
253 AAA_AVP  *AAAFindMatchingAVP(
254         AAAMessage *msg,
255         AAA_AVP *startAvp,
256         AAA_AVPCode avpCode,
257         AAAVendorId vendorId,
258         AAASearchType searchType)
259 {
260         AAA_AVP *avp_t;
261
262         /* param checking */
263         if (!msg) {
264                 LM_ERR("FindMatchingAVP: param msg passed null !!\n");
265                 goto error;
266         }
267
268         /* where should I start searching from ? */
269         if (startAvp) {
270                 /* double-check the startAVP avp */
271                 for(avp_t=msg->avpList.head;avp_t&&avp_t!=startAvp;avp_t=avp_t->next);
272                 if (!avp_t) {
273                         LM_ERR("AAAFindMatchingAVP: the \"position\" avp is not "
274                                 "in \"avpList\" list!!\n");
275                         goto error;
276                 }
277                 avp_t=startAvp;
278         } else {
279                 /* if no startAVP -> start from one of the ends */
280                 avp_t=(searchType==AAA_FORWARD_SEARCH)?(msg->avpList.head):
281                         (msg->avpList.tail);
282         }
283
284         /* start searching */
285         while(avp_t) {
286                 if (avp_t->code==avpCode && avp_t->vendorId==vendorId)
287                         return avp_t;
288                 avp_t = (searchType==AAA_FORWARD_SEARCH)?(avp_t->next):(avp_t->prev);
289         }
290
291 error:
292         return 0;
293 }
294
295
296
297 /**
298  *  This function removes an AVP from a message.
299  * @param msg - the diameter message 
300  * @param avp - the AVP to remove
301  * @returns AAA_ERR_SUCCESS on success or AAA_ERR_PARAMETER if not found
302  * \note This function is taken from DISC http://developer.berlios.de/projects/disc/
303  */
304 AAAReturnCode  AAARemoveAVPFromMessage(
305         AAAMessage *msg,
306         AAA_AVP *avp)
307 {
308         AAA_AVP *avp_t;
309
310         /* param check */
311         if ( !msg || !avp ) {
312                 LM_ERR("AAARemoveAVPFromMessage: param AVP_LIST \"avpList\" or AVP "
313                         "\"avp\" passed null !!\n");
314                 return AAA_ERR_PARAMETER;
315         }
316
317         /* search the "avp" avp */
318         for(avp_t=msg->avpList.head;avp_t&&avp_t!=avp;avp_t=avp_t->next);
319         if (!avp_t) {
320                 LM_ERR("AAARemoveAVPFromMessage: the \"avp\" avp is not in "
321                         "\"avpList\" avp list!!\n");
322                 return AAA_ERR_PARAMETER;
323         }
324
325         /* remove the avp from list */
326         if (msg->avpList.head==avp)
327                 msg->avpList.head = avp->next;
328         else
329                 avp->prev->next = avp->next;
330         if (avp->next)
331                 avp->next->prev = avp->prev;
332         else
333                 msg->avpList.tail = avp->prev;
334         avp->next = avp->prev = 0;
335
336         /* update short-cuts */
337         switch (avp->code) {
338                 case AVP_Session_Id: msg->sessionId = 0;break;
339                 case AVP_Origin_Host: msg->orig_host = 0;break;
340                 case AVP_Origin_Realm: msg->orig_realm = 0;break;
341                 case AVP_Destination_Host: msg->dest_host = 0;break;
342                 case AVP_Destination_Realm: msg->dest_realm = 0;break;
343                 case AVP_Result_Code: msg->res_code = 0;break;
344                 case AVP_Auth_Session_State: msg->auth_ses_state = 0;break;
345         }
346
347         return AAA_ERR_SUCCESS;
348 }
349
350
351
352 /**
353  *  The function frees the memory allocated to an AVP 
354  * \note This function is taken from DISC http://developer.berlios.de/projects/disc/
355  */
356 AAAReturnCode  AAAFreeAVP(AAA_AVP **avp)
357 {
358         /* some checks */
359         if (!avp || !(*avp)) {
360                 LM_ERR("AAAFreeAVP: param avp cannot be null!!\n");
361                 return AAA_ERR_PARAMETER;
362         }
363
364         /* free all the mem */
365         if ( (*avp)->free_it && (*avp)->data.s )
366                 shm_free((*avp)->data.s);
367
368         shm_free( *avp );
369         avp = 0;
370
371         return AAA_ERR_SUCCESS;
372 }
373
374
375
376 /**
377  *  This function returns the first AVP in the list.
378  * @param avpList - the list 
379  * \note This function is taken from DISC http://developer.berlios.de/projects/disc/
380  */
381 AAA_AVP*  AAAGetFirstAVP(AAA_AVP_LIST *avpList){
382         return avpList->head;
383 }
384
385
386
387 /**
388  *  This function returns the last AVP in the list.
389  * @param avpList - the list  
390  * \note This function is taken from DISC http://developer.berlios.de/projects/disc/
391  */
392 AAA_AVP*  AAAGetLastAVP(AAA_AVP_LIST *avpList)
393 {
394         return avpList->tail;
395 }
396
397
398
399
400 /**
401  *  This function returns the next AVP in the list that this AVP was extracted from
402  * @param avp - reference avp
403  * @returns  the next AAA_AVP or NULL if this was the last one
404  * \note This function is taken from DISC http://developer.berlios.de/projects/disc/
405  */
406 AAA_AVP*  AAAGetNextAVP(AAA_AVP *avp)
407 {
408         return avp->next;
409 }
410
411
412
413 /**
414  *  This function returns a the previous AVP in the list  that this AVP was extracted from
415  * @param avp - reference avp
416  * @returns  the next AAA_AVP or NULL if this was the first one
417  * \note This function is taken from DISC http://developer.berlios.de/projects/disc/
418  */
419 AAA_AVP*  AAAGetPrevAVP(AAA_AVP *avp)
420 {
421         return avp->prev;
422 }
423
424
425
426 /**
427  *  This function converts the data in the AVP to a format suitable for
428  * print, log or display.
429  * @param avp - the AAA_AVP to print
430  * @param dest - preallocated destination buffer. If too short, message will be truncated 
431  * @param destLen - length of the destipation buffer 
432  * @returns dest on success, NULL on failure 
433  * \note This function is taken from DISC http://developer.berlios.de/projects/disc/
434  */
435 char*  AAAConvertAVPToString(AAA_AVP *avp, char *dest, unsigned int destLen)
436 {
437         int l;
438         int i;
439
440         if (!avp || !dest || !destLen) {
441                 LM_ERR("AAAConvertAVPToString: param AVP, DEST or DESTLEN "
442                         "passed as null!!!\n");
443                 return 0;
444         }
445         l = snprintf(dest,destLen,"AVP(%p < %p >%p);code=%u,"
446                 "flags=%x;\nDataType=%u;VendorID=%u;DataLen=%u;\n",
447                 avp->prev,avp,avp->next,avp->code,avp->flags,
448                 avp->type,avp->vendorId,avp->data.len);
449         switch(avp->type) {
450                 case AAA_AVP_STRING_TYPE:
451                         l+=snprintf(dest+l,destLen-l,"String: <%.*s>",avp->data.len,
452                                 avp->data.s);
453                         break;
454                 case AAA_AVP_INTEGER32_TYPE:
455                         l+=snprintf(dest+l,destLen-l,"Int32: <%u>(%x)",
456                                 htonl(*((unsigned int*)avp->data.s)),
457                                 htonl(*((unsigned int*)avp->data.s)));
458                         break;
459                 case AAA_AVP_ADDRESS_TYPE:
460                         i = 1;
461                         switch (avp->data.len) {
462                                 case 4: i=i*0;
463                                 case 6: i=i*2;
464                                         l+=snprintf(dest+l,destLen-l,"Address IPv4: <%d.%d.%d.%d>",
465                                                 (unsigned char)avp->data.s[i+0],
466                                                 (unsigned char)avp->data.s[i+1],
467                                                 (unsigned char)avp->data.s[i+2],
468                                                 (unsigned char)avp->data.s[i+3]);
469                                         break;
470                                 case 16: i=i*0;
471                                 case 18: i=i*2;
472                                         l+=snprintf(dest+l,destLen-l,
473                                                 "Address IPv6: <%x.%x.%x.%x.%x.%x.%x.%x>",
474                                                 ((avp->data.s[i+0]<<8)+avp->data.s[i+1]),
475                                                 ((avp->data.s[i+2]<<8)+avp->data.s[i+3]),
476                                                 ((avp->data.s[i+4]<<8)+avp->data.s[i+5]),
477                                                 ((avp->data.s[i+6]<<8)+avp->data.s[i+7]),
478                                                 ((avp->data.s[i+8]<<8)+avp->data.s[i+9]),
479                                                 ((avp->data.s[i+10]<<8)+avp->data.s[i+11]),
480                                                 ((avp->data.s[i+12]<<8)+avp->data.s[i+13]),
481                                                 ((avp->data.s[i+14]<<8)+avp->data.s[i+15]));
482                                         break;
483                         break;
484                         }
485                         break;
486                 //case AAA_AVP_INTEGER64_TYPE:
487                 case AAA_AVP_TIME_TYPE:
488                 default:
489                         LM_WARN("AAAConvertAVPToString: don't know how to print"
490                                 " this data type [%d] -> tryng hexa\n",avp->type);
491                 case AAA_AVP_DATA_TYPE:
492                         for (i=0;i<avp->data.len&&l<destLen-1;i++)
493                         l+=snprintf(dest+l,destLen-l-1,"%x",
494                                 ((unsigned char*)avp->data.s)[i]);
495         }
496         return dest;
497 }
498
499
500 /**
501  * Duplicate a whole AAA_AVP.
502  * @param avp - original avp
503  * @param clone_data - whether to duplicate also the data payload
504  * @returns the new AAA_AVP* or NULL on error
505  * \note This function is taken from DISC http://developer.berlios.de/projects/disc/
506  */
507 AAA_AVP* AAACloneAVP( AAA_AVP *avp , unsigned char clone_data)
508 {
509         AAA_AVP *n_avp;
510
511         if (!avp || !(avp->data.s) || !(avp->data.len) )
512                 goto error;
513
514         /* clone the avp structure */
515         n_avp = (AAA_AVP*)shm_malloc( sizeof(AAA_AVP) );
516         if (!n_avp) {
517                 LM_ERR("clone_avp: cannot get free memory!!\n");
518                 goto error;
519         }
520         memcpy( n_avp, avp, sizeof(AAA_AVP));
521         n_avp->next = n_avp->prev = 0;
522
523         if (clone_data) {
524                 /* clone the avp data */
525                 n_avp->data.s = (char*)shm_malloc( avp->data.len );
526                 if (!(n_avp->data.s)) {
527                         LM_ERR("clone_avp: cannot get free memory!!\n");
528                         shm_free( n_avp );
529                         goto error;
530                 }
531                 memcpy( n_avp->data.s, avp->data.s, avp->data.len);
532                 n_avp->free_it = 1;
533         } else {
534                 /* link the clone's data to the original's data */
535                 n_avp->data.s = avp->data.s;
536                 n_avp->data.len = avp->data.len;
537                 n_avp->free_it = 0;
538         }
539
540         return n_avp;
541 error:
542         return 0;
543 }
544
545 /* End of disc implementation */
546
547 /* Simple extension for grouped avp based on the above implementation  */
548
549 /**
550  * Adds an AVP to a list of AVPs, at the end.
551  * @param list - the list to add to
552  * @param avp - the avp to add 
553  */ 
554 void AAAAddAVPToList(AAA_AVP_LIST *list,AAA_AVP *avp)
555 {
556         if (list->tail) {
557                 avp->prev=list->tail;
558                 avp->next=0;    
559                 list->tail->next = avp;
560                 list->tail=avp;
561         } else {
562                 list->head = avp;
563                 list->tail = avp;
564                 avp->next=0;
565                 avp->prev=0;
566         }       
567 }
568  
569 /** 
570  * Groups a list of avps into a data buffer
571  * @param avps 
572  */
573 str AAAGroupAVPS(AAA_AVP_LIST avps)
574  {
575         AAA_AVP *avp;
576         unsigned char *p;
577         str buf={0,0};
578
579         /* count and add the avps */
580         for(avp=avps.head;avp;avp=avp->next) {
581                 buf.len += AVP_HDR_SIZE(avp->flags)+ to_32x_len( avp->data.len );
582         }
583
584         if (!buf.len) return buf;
585         /* allocate some memory */
586         buf.s = (char*)shm_malloc( buf.len );
587         if (!buf.s) {
588                 LM_ERR("hss3g_group_avps: no more free memory!\n");
589                 buf.len=0;
590                 return buf;
591         }
592         memset(buf.s, 0, buf.len);
593         /* fill in the buffer */
594         p = (unsigned char*) buf.s;
595         for(avp=avps.head;avp;avp=avp->next) {
596                 /* AVP HEADER */
597                 /* avp code */
598                 set_4bytes(p,avp->code);
599                 p +=4;
600                 /* flags */
601                 (*p++) = (unsigned char)avp->flags;
602                 /* avp length */
603                 set_3bytes(p, (AVP_HDR_SIZE(avp->flags)+avp->data.len) );
604                 p += 3;
605                 /* vendor id */
606                 if ((avp->flags&0x80)!=0) {
607                         set_4bytes(p,avp->vendorId);
608                         p +=4;
609                 }
610                 /* data */
611                 memcpy( p, avp->data.s, avp->data.len);
612                 p += to_32x_len( avp->data.len );
613         }
614         if ((char*)p-buf.s!=buf.len) {
615                 LM_ERR("BUG:hss3g_group_avps: mismatch between len and buf!\n");
616                 shm_free( buf.s );
617                 buf.s = 0;
618                 buf.len = 0;
619                 return buf;
620         }
621         return buf;
622 }
623
624 /** 
625  * Ungroup from a data buffer a list of avps
626  * @param buf - payload to ungroup the list from
627  * @returns the AAA_AVP_LIST or an empty one on error 
628  */
629 AAA_AVP_LIST AAAUngroupAVPS(str buf)
630 {
631         char *ptr;
632         AAA_AVP       *avp;
633         unsigned int  avp_code;
634         unsigned char avp_flags;
635         unsigned int  avp_len;
636         unsigned int  avp_vendorID;
637         unsigned int  avp_data_len;
638         AAA_AVP_LIST    lh;
639
640         lh.head=0;
641         lh.tail=0;
642         ptr = buf.s;
643
644         /* start decoding the AVPS */
645         while (ptr < buf.s+buf.len) {
646                 if (ptr+AVP_HDR_SIZE(0x80)>buf.s+buf.len){
647                         LM_ERR("hss3g_ungroup_avps: source buffer to short!! "
648                                 "Cannot read the whole AVP header!\n");
649                         goto error;
650                 }
651                 /* avp code */
652                 avp_code = get_4bytes( ptr );
653                 ptr += AVP_CODE_SIZE;
654                 /* avp flags */
655                 avp_flags = (unsigned char)*ptr;
656                 ptr += AVP_FLAGS_SIZE;
657                 /* avp length */
658                 avp_len = get_3bytes( ptr );
659                 ptr += AVP_LENGTH_SIZE;
660                 if (avp_len<1) {
661                         LM_ERR("hss3g_ungroup_avps: invalid AVP len [%d]\n",
662                                 avp_len);
663                         goto error;
664                 }
665                 /* avp vendor-ID */
666                 avp_vendorID = 0;
667                 if (avp_flags&AAA_AVP_FLAG_VENDOR_SPECIFIC) {
668                         avp_vendorID = get_4bytes( ptr );
669                         ptr += AVP_VENDOR_ID_SIZE;
670                 }
671
672                 /* data length */
673                 avp_data_len = avp_len-AVP_HDR_SIZE(avp_flags);
674                 /*check the data length */
675                 if ( buf.s+buf.len<ptr+avp_data_len) {
676                         LM_ERR("hss3g_ungroup_avps: source buffer to short!! "
677                                 "Cannot read a whole data for AVP!\n");
678                         goto error;
679                 }
680
681                 /* create the AVP */
682                 avp = AAACreateAVP( avp_code, avp_flags, avp_vendorID, ptr,
683                         avp_data_len, AVP_DONT_FREE_DATA);
684                 if (!avp) {
685                         LM_ERR("hss3g_ungroup_avps: can't create avp for member of list\n");
686                         goto error;
687                 }
688
689                 /* link the avp into aaa message to the end */
690                 avp->next = 0;
691                 avp->prev = lh.tail;
692                 if (lh.tail) {
693                         lh.tail->next=avp;
694                         lh.tail=avp;
695                 }
696                 else {
697                         lh.tail=avp;
698                         lh.head=avp;
699                 }
700
701                 ptr += to_32x_len( avp_data_len );
702         }
703         return lh;
704
705 error:
706         LM_CRIT("AVP:<%.*s>\n",buf.len,buf.s);
707         return lh;
708 }
709
710 /**
711  * Find an avp into a list of avps.
712  * @param avpList - the list to look into
713  * @param startAvp - where to start the search. Usefull when you want to find the next one. 
714  * Even this one will be checked and can be returned if it fits.
715  * @param avpCode - the AVP code to match
716  * @param vendorId - the vendor id to match
717  * @param searchType - direction of search
718  * @returns the AAA_AVP* if found, NULL if not
719  */
720 AAA_AVP  *AAAFindMatchingAVPList(
721         AAA_AVP_LIST avpList,
722         AAA_AVP *startAvp,
723         AAA_AVPCode avpCode,
724         AAAVendorId vendorId,
725         AAASearchType searchType)
726 {
727         AAA_AVP *avp_t;
728
729         /* param checking */
730
731         /* where should I start searching from ? */
732         if (startAvp) {
733                 /* double-check the startAVP avp */
734                 for(avp_t=avpList.head;avp_t&&avp_t!=startAvp;avp_t=avp_t->next);
735                 if (!avp_t) {
736                         LM_ERR("ndMatchingAVP: the \"position\" avp is not "
737                                 "in \"avpList\" list!!\n");
738                         goto error;
739                 }
740                 avp_t=startAvp;
741         } else {
742                 /* if no startAVP -> start from one of the ends */
743                 avp_t=(searchType==AAA_FORWARD_SEARCH)?(avpList.head):
744                         (avpList.tail);
745         }
746
747         /* start searching */
748         while(avp_t) {
749                 if (avp_t->code==avpCode && avp_t->vendorId==vendorId)
750                         return avp_t;
751                 avp_t = (searchType==AAA_FORWARD_SEARCH)?(avp_t->next):(avp_t->prev);
752         }
753
754 error:
755         return 0;
756 }
757