changeset 360:1740bee6c821

Initial App_SIP import
author Alexandre Westfahl <awestfahl@freediameter.net>
date Fri, 02 Jul 2010 12:00:58 +0900
parents 9fa49025743f
children 22e7110bf46d
files extensions/app_sip/CMakeLists.txt extensions/app_sip/diamsip.c extensions/app_sip/diamsip.h extensions/app_sip/libdiamsip.c extensions/app_sip/md5.c extensions/app_sip/md5.h extensions/app_sip/multimediaauth.c
diffstat 7 files changed, 1767 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extensions/app_sip/CMakeLists.txt	Fri Jul 02 12:00:58 2010 +0900
@@ -0,0 +1,19 @@
+# The dict_nasreq extension
+PROJECT("Diameter SIP Application (RFC4740)" C)
+
+FIND_PACKAGE(MYSQL REQUIRED)
+INCLUDE_DIRECTORIES(${MYSQL_INCLUDE_DIR}) 
+
+# List of source files
+SET( DIAM_SIP_SRC
+	diamsip.c
+	diamsip.h
+	libdiamsip.c
+	md5.c
+	multimediaauth.c
+)
+
+# Compile as a module
+FD_ADD_EXTENSION(app_sip ${DIAM_SIP_SRC})
+
+TARGET_LINK_LIBRARIES(app_sip ${MYSQL_LIBRARY})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extensions/app_sip/diamsip.c	Fri Jul 02 12:00:58 2010 +0900
@@ -0,0 +1,153 @@
+#include "diamsip.h"
+
+static struct disp_hdl * diamsip_MAR_hdl=NULL;
+static struct disp_hdl * diamsip_default_hdl=NULL;
+
+int diamsip_default_cb( struct msg ** msg, struct avp * avp, struct session * sess, enum disp_action * act)
+{
+
+	TRACE_ENTRY("%p %p %p %p", msg, avp, sess, act);
+
+	
+	return 0;
+}
+
+
+
+
+/* entry point */
+static int ds_entry()
+{
+	struct dict_object * app=NULL;
+	struct disp_when data;
+	
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Session Initiation Protocol (SIP) Application", &app, ENOENT) );
+	CHECK_FCT( fd_disp_app_support ( app, NULL, 1, 0 ) );
+	
+	
+	
+	//We set usefull AVPs 
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Session-State", &sip_dict.Auth_Session_State, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &sip_dict.Auth_Application_Id, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Auth-Data-Item", &sip_dict.SIP_Auth_Data_Item, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Authorization", &sip_dict.SIP_Authorization, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Authenticate", &sip_dict.SIP_Authenticate, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Number-Auth-Items", &sip_dict.SIP_Number_Auth_Items, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Authentication-Scheme", &sip_dict.SIP_Authentication_Scheme, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Authentication-Info", &sip_dict.SIP_Authentication_Info, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Server-URI", &sip_dict.SIP_Server_URI, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Method", &sip_dict.SIP_Method, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-AOR", &sip_dict.SIP_AOR, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Realm", &sip_dict.Digest_Realm, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-URI", &sip_dict.Digest_URI, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Nonce", &sip_dict.Digest_Nonce, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-CNonce", &sip_dict.Digest_CNonce, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Method", &sip_dict.Digest_Method, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Nonce-Count", &sip_dict.Digest_Nonce_Count, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Realm", &sip_dict.Digest_Realm, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Response", &sip_dict.Digest_Response, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Response-Auth", &sip_dict.Digest_Response_Auth, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Username", &sip_dict.Digest_Username, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Algorithm", &sip_dict.Digest_Algorithm, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-QoP", &sip_dict.Digest_QOP, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Name", &sip_dict.User_Name, ENOENT) );
+	
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-HA1", &sip_dict.Digest_HA1, ENOENT) );
+	
+	
+	//Register Application
+	memset(&data, 0, sizeof(data));
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Session Initiation Protocol (SIP) Application", &data.app, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Multimedia-Auth-Request", &data.command, ENOENT) );
+	
+	//Callback for unexpected messages
+	CHECK_FCT( fd_disp_register( diamsip_MAR_cb, DISP_HOW_APPID, &data, &diamsip_default_hdl ) );
+	
+	//**Command Codes
+	//MAR
+	CHECK_FCT( fd_disp_register( diamsip_MAR_cb, DISP_HOW_CC, &data, &diamsip_MAR_hdl ) );
+	
+	//TRACE_DEBUG(INFO,"*%s*%s*%s*%s*",DB_SERVER,DB_USERNAME, DB_PASSWORD, DB_DATABASE);
+	//We start database connection
+	if(start_mysql_connection(DB_SERVER,DB_USERNAME, DB_PASSWORD, DB_DATABASE))
+		return 1;
+	
+	CHECK_FCT(fd_sess_handler_create(&ds_sess_hdl, free));
+	
+
+	//listnonce=NULL;
+	return 0;
+}
+
+//Cleanup callback
+void fd_ext_fini(void)
+{
+	
+	if (diamsip_MAR_cb) {
+		(void) fd_disp_unregister(&diamsip_MAR_hdl);
+		CHECK_FCT_DO( fd_sess_handler_destroy(&ds_sess_hdl),return);
+	}
+	
+	//We close database connection
+	close_mysql_connection();
+	
+	//We delete the chained list of nonces
+	//nonce_deletelistnonce();
+	//TODO:NONCE
+	
+	TRACE_ENTRY();
+	return ;
+}
+
+EXTENSION_ENTRY("diam_sip", ds_entry);
+
+
+/*
+
+
+
+
+
+
+test set for digest calculate
+
+TRACE_DEBUG(FULL,"TEST");
+									DigestCalcHA1("MD5", "12345678", "example.com", "secret", "3bada1a0","56593a80", HA1);
+									TRACE_DEBUG(FULL,"TEST->HA1 done: *%s*",HA1);
+      									DigestCalcResponse(HA1, "3bada1a0", "00000001", "56593a80", "auth","INVITE", "sip:97226491335@example.com", HA2, response);
+      									DigestCalcResponseAuth(HA1, "3bada1a0", "00000001", "56593a80", "auth","INVITE", "sip:97226491335@example.com", HA2, responseauth);
+	
+	
+	
+	
+	
+	
+old digest reponse check
+
+						struct avp_hdr * tempavphdr=NULL;
+						
+						
+						CHECK_FCT(fd_msg_browse ( avp, MSG_BRW_WALK, &tempavp, NULL) );
+						
+						while(tempavp)
+						{
+							CHECK_FCT( fd_msg_avp_hdr( tempavp, &tempavphdr )  );
+							
+							if(tempavphdr->avp_code==380)
+							{
+								found_response=0;
+								//We have not found it but we finished looking in this Auth-Data-Item
+								tempavp=NULL; 
+							}
+							else if(tempavphdr->avp_code==103)
+							{
+								found_response=1;
+								//We found it, we can leave the loop
+								tempavp=NULL; 
+							}
+							else
+							{
+								CHECK_FCT(fd_msg_browse ( tempavp, MSG_BRW_WALK, &tempavp, NULL) );
+							}
+						}
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extensions/app_sip/diamsip.h	Fri Jul 02 12:00:58 2010 +0900
@@ -0,0 +1,106 @@
+#include <freeDiameter/extension.h>
+#include <sys/time.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <gcrypt.h>
+#include <string.h>
+#include <mysql.h>
+#include "md5.h"
+
+
+#define NONCE_SIZE 16
+#define DIGEST_LEN 16
+
+//SQL configuration
+#define DB_USERNAME "diamsip"
+#define DB_PASSWORD "BAVpzCUhULVHayFr"
+#define DB_SERVER "pineapple.tera.ics.keio.ac.jp"
+#define DB_DATABASE "diamsip"
+
+extern MYSQL *conn;
+
+
+
+void calc_md5(char *buffer, char * data);
+void clear_digest(char * digest, char * readable_digest, int digestlength);
+struct avp_hdr * walk_digest(struct avp *avp, int avp_code);
+int start_mysql_connection(char *server,char *user, char *password, char *database);
+void request_mysql(char *query);
+void close_mysql_connection();
+/*
+typedef struct noncechain noncechain;
+struct noncechain
+{
+    int timestamp;
+    char * nonce;
+    noncechain *next;
+};
+
+
+//Global variable which points to chained list of nonce
+noncechain* listnonce;
+
+void nonce_add_element(char * nonce);
+int nonce_check_element(char * nonce);
+void nonce_deletelistnonce();
+*/
+
+
+
+static int ds_entry();
+void fd_ext_fini(void);
+int diamsip_default_cb( struct msg ** msg, struct avp * avp, struct session * sess, enum disp_action * act);
+int diamsip_MAR_cb( struct msg ** msg, struct avp * avp, struct session * sess, enum disp_action * act);
+#define SQL_GETPASSWORD "SELECT `password` FROM ds_users WHERE `username` ='%s'"
+#define SQL_GETPASSWORD_LEN 52
+
+#define SQL_GETSIPURI "SELECT `sip_server_uri` FROM ds_users WHERE `username` ='%s'"
+#define SQL_GETSIPURI_LEN 60
+
+#define SQL_SETSIPURI "UPDATE ds_users SET `sip_server_uri`='%s', `flag`=1 WHERE `username` ='%s'"
+#define SQL_SETSIPURI_LEN 74
+
+#define SQL_GETSIPAOR "SELECT `sip_aor` FROM `ds_sip_aor`, `ds_users` WHERE `ds_sip_aor`.`id_user` = `ds_users`.`id_user` AND `ds_users`.`username` = '%s'"
+#define SQL_GETSIPAOR_LEN 131
+
+#define SQL_CLEARFLAG "UPDATE ds_users SET `flag`=0 WHERE `username` ='%s'"
+#define SQL_CLEARFLAG_LEN 74
+
+static struct session_handler * ds_sess_hdl;
+static struct session *dssess;
+
+
+
+struct ds_nonce
+{
+	char *nonce;
+};
+
+//Storage for some usefull AVPs
+static struct {
+	struct dict_object * Auth_Session_State;
+	struct dict_object * Auth_Application_Id;
+	struct dict_object * User_Name;
+	struct dict_object * SIP_Auth_Data_Item;
+	struct dict_object * SIP_Authorization;
+	struct dict_object * SIP_Authenticate;
+	struct dict_object * SIP_Number_Auth_Items;	
+	struct dict_object * SIP_Authentication_Scheme;
+	struct dict_object * SIP_Authentication_Info;	
+	struct dict_object * SIP_Server_URI;
+	struct dict_object * SIP_Method;
+	struct dict_object * SIP_AOR;
+	struct dict_object * Digest_URI;		
+	struct dict_object * Digest_Nonce;
+	struct dict_object * Digest_Nonce_Count;
+	struct dict_object * Digest_CNonce;		
+	struct dict_object * Digest_Realm;		
+	struct dict_object * Digest_Response;	
+	struct dict_object * Digest_Response_Auth;	
+	struct dict_object * Digest_Username;	
+	struct dict_object * Digest_Method;
+	struct dict_object * Digest_QOP;	
+	struct dict_object * Digest_Algorithm;
+	struct dict_object * Digest_HA1;
+} sip_dict;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extensions/app_sip/libdiamsip.c	Fri Jul 02 12:00:58 2010 +0900
@@ -0,0 +1,262 @@
+#include <mysql.h>
+#include "diamsip.h"
+
+/*
+void calculate_nonce(u8 * nonce)
+{
+
+	nonce="lkgbsljhsdjdsgj";
+	return;
+}*/
+
+void clear_digest(char * digest, char * readable_digest, int digestlength)
+{
+	int i=0;
+	for(i=0;i<digestlength * 2;i++)
+		sprintf(&readable_digest[2 * i], "%2.2hhx", digest[i]);
+	readable_digest[2 * digestlength]='\0';
+	
+	return;
+}
+
+// You must create a table like this "char  clearDigest[DIGEST_LEN*2+1];"
+void calc_md5(char *clearDigest, char * data)
+{
+	gcry_md_hd_t md5;
+	uint8_t * binDigest=NULL;
+	
+	CHECK_MALLOC_DO(binDigest=malloc(DIGEST_LEN),return);
+	
+	gcry_md_open(&md5,GCRY_MD_MD5, 0); 
+	gcry_md_write(md5, (char *)data, sizeof(data));
+	memcpy(binDigest, gcry_md_read(md5,  GCRY_MD_MD5),gcry_md_get_algo_dlen(GCRY_MD_MD5));
+	gcry_md_close(md5);
+	
+	clear_digest(binDigest, clearDigest, DIGEST_LEN);
+	free(binDigest);
+	return;
+}
+
+
+/* Search a given AVP model in an AVP (extracted from libfreediameter/message.c ) */
+int fd_avp_search_avp ( struct avp * groupedavp, struct dict_object * what, struct avp ** avp )
+{
+	struct avp * nextavp;
+	struct avp_hdr * nextavphdr;
+	struct dict_avp_data 	dictdata;
+	enum dict_object_type 	dicttype;
+	
+	TRACE_ENTRY("%p %p %p", groupedavp, what, avp);
+	
+	CHECK_FCT(  fd_dict_getval(what, &dictdata)  );
+	
+	// Loop only in the group AVP 
+	CHECK_FCT(  fd_msg_browse(groupedavp, MSG_BRW_FIRST_CHILD, (void *)&nextavp, NULL)  );
+	CHECK_FCT( fd_msg_avp_hdr( nextavp, &nextavphdr )  );
+	
+	while (nextavphdr) {
+		
+		if ( (nextavphdr->avp_code   == dictdata.avp_code) && (nextavphdr->avp_vendor == dictdata.avp_vendor) ) // always 0 if no Vendor flag
+		{
+			break;
+		}
+		
+		// Otherwise move to next AVP in the grouped AVP 
+		CHECK_FCT( fd_msg_browse(nextavp, MSG_BRW_NEXT, (void *)&nextavp, NULL) );
+		
+		if(nextavp!=NULL)
+		{
+			CHECK_FCT( fd_msg_avp_hdr( nextavp, &nextavphdr )  );
+		}
+		else
+			nextavphdr=NULL;
+	}
+	if (avp)
+		*avp = nextavp;
+	
+	if (avp && nextavp) {
+		struct dictionary * dict;
+		CHECK_FCT( fd_dict_getdict( what, &dict) );
+		CHECK_FCT_DO( fd_msg_parse_dict( nextavp, dict, NULL ),  );
+	}
+	
+	if (avp || nextavp)
+		return 0;
+	else
+		return ENOENT;
+}
+struct avp_hdr *walk_digest(struct avp *avp, int avp_code)
+{
+	struct avp_hdr *temphdr=NULL;
+	CHECK_FCT_DO(fd_msg_browse ( avp, MSG_BRW_WALK, &avp, NULL),0 );
+	
+	while(avp!=NULL)
+	{
+		
+		CHECK_FCT_DO( fd_msg_avp_hdr( avp,&temphdr ),0);
+
+		if(temphdr->avp_code==avp_code)
+		{
+			//We found the AVP so we set avp to NULL to exit the loop
+			avp=NULL;
+			return temphdr;
+			
+		}
+		else if(temphdr->avp_code==380)//SIP-Authorization AVP
+		{
+			//We didn't found the AVP but we finished browsing the Authentication AVP
+			avp=NULL;
+			temphdr=NULL;
+			
+			return temphdr;
+		}
+		else
+		{
+			CHECK_FCT_DO(fd_msg_browse ( avp, MSG_BRW_WALK, &avp, NULL),0 );
+			temphdr=NULL;
+			
+		}
+	}
+	
+	return temphdr;
+}
+
+int start_mysql_connection(char *server,char *user, char *password, char *database)
+{
+	conn = mysql_init(NULL);
+	
+	mysql_options(conn, MYSQL_OPT_RECONNECT, "true");
+	
+	if (!mysql_real_connect(conn, server,user, password, database, 0, NULL, 0)) 
+	{
+		TRACE_DEBUG(INFO,"Unable to connect to database (%s) with login:%s",database,user);
+		return 1;
+	}
+	return 0;
+	
+}
+
+//You must free ""result"" after using this function
+void request_mysql(char *query)
+{
+	//We check if the connection is still up
+	mysql_ping(conn);
+	
+	if (mysql_query(conn, query)) 
+	{
+		TRACE_DEBUG(INFO,"Query %s failed", query);
+		
+	}
+	
+}
+
+void close_mysql_connection()
+{
+	mysql_close(conn);
+	
+}
+/*
+void nonce_add_element(char * nonce)
+{
+	noncechain *newelt=malloc(sizeof(noncechain));
+	
+	newelt->nonce=nonce;
+	
+	newelt->timestamp=(int)time(NULL);
+	newelt->next=NULL;
+	
+	if(listnonce==NULL)
+	{
+		listnonce=newelt;
+	}
+	else
+	{
+		noncechain* temp=listnonce;
+		
+		while(temp->next != NULL)
+		{
+			if(temp->timestamp < ((int)time(NULL)-300))
+			{
+				listnonce=temp->next;
+				free(temp);
+				temp=listnonce;
+			}
+			temp = temp->next;
+		}
+		temp->next = newelt;
+	}
+	
+}
+void nonce_del_element(char * nonce)
+{
+	if(listnonce!=NULL)
+	{
+		noncechain *temp=listnonce, *tempbefore=NULL;
+		
+		if(listnonce->next==NULL && strcmp(listnonce->nonce,nonce)==0)
+		{
+			free(listnonce);
+			listnonce=NULL;
+			return;
+		}
+		while(temp->next != NULL)
+		{
+			if(strcmp(temp->nonce,nonce)==0)
+			{
+				if(tempbefore==NULL)
+				{
+					listnonce=temp->next;
+					free(temp);
+					return;
+				}
+				tempbefore->next=temp->next;
+				free(temp);
+				break;
+			}
+			tempbefore=temp;
+			temp = temp->next;
+		}
+		
+	}
+	
+}
+int nonce_check_element(char * nonce)
+{
+	if(listnonce==NULL)
+	{
+		//Not found
+		return 0;
+	}
+	else
+	{
+		noncechain* temp=listnonce;
+		
+		while(temp->next != NULL)
+		{
+			if(strcmp(temp->nonce,nonce)==0)
+				return 1;
+			else
+				temp = temp->next;
+		}
+	}
+	return 0;
+}
+
+void nonce_deletelistnonce()
+{
+	if(listnonce !=NULL)
+	{
+		noncechain* temp=listnonce;
+	
+		while(listnonce->next != NULL)
+		{
+			temp = listnonce->next;
+		
+			free(listnonce);
+		
+			listnonce=temp;
+		}
+		free(listnonce);
+	}
+}
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extensions/app_sip/md5.c	Fri Jul 02 12:00:58 2010 +0900
@@ -0,0 +1,421 @@
+/*********************************************************************************/
+/* freeDiameter author note:
+ *  The content from this file comes directly from the hostap project.
+ * It is redistributed under the terms of the BSD license, as allowed
+ * by the original copyright reproduced bellow.
+ *  In addition to this notice, only the #include directives have been modified.
+ */
+
+/*********************************************************************************/
+#include"diamsip.h"
+ 
+/*
+ * MD5 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+void CvtHex(
+    IN HASH Bin,
+    OUT HASHHEX Hex
+    )
+{
+    unsigned short i;
+    unsigned char j;
+
+    for (i = 0; i < HASHLEN; i++) {
+        j = (Bin[i] >> 4) & 0xf;
+        if (j <= 9)
+            Hex[i*2] = (j + '0');
+         else
+            Hex[i*2] = (j + 'a' - 10);
+        j = Bin[i] & 0xf;
+        if (j <= 9)
+            Hex[i*2+1] = (j + '0');
+         else
+            Hex[i*2+1] = (j + 'a' - 10);
+    }
+    Hex[HASHHEXLEN] = '\0';
+}
+
+// calculate H(A1) as per spec 
+void DigestCalcHA1(char * pszAlg,char * pszUserName,char * pszRealm,char * pszPassword,char * pszNonce,char * pszCNonce,HASHHEX SessionKey)
+{
+      MD5_CTX Md5Ctx;
+      HASH HA1;
+
+      MD5Init(&Md5Ctx);
+      MD5Update(&Md5Ctx, pszUserName, strlen(pszUserName));
+      MD5Update(&Md5Ctx, ":", 1);
+      MD5Update(&Md5Ctx, pszRealm, strlen(pszRealm));
+      MD5Update(&Md5Ctx, ":", 1);
+      MD5Update(&Md5Ctx, pszPassword, strlen(pszPassword));
+      MD5Final(HA1, &Md5Ctx);
+      if (strcmp(pszAlg, "md5-sess") == 0) {	
+		MD5Init(&Md5Ctx);
+		MD5Update(&Md5Ctx, HA1, HASHLEN);
+		MD5Update(&Md5Ctx, ":", 1);
+		MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+		MD5Update(&Md5Ctx, ":", 1);
+		MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+		MD5Final(HA1, &Md5Ctx);
+      }
+      CvtHex(HA1, SessionKey);
+}
+
+// calculate request-digest as  SIP Digest spec RFC5090
+void DigestCalcResponse(HASHHEX HA1,char * pszNonce,char * pszNonceCount,char * pszCNonce,char * pszQop,char * pszMethod,char * pszDigestUri,HASHHEX HEntity,HASHHEX Response)
+{
+      MD5_CTX Md5Ctx;
+      HASH HA2;
+      HASH RespHash;
+       HASHHEX HA2Hex;
+
+      // calculate H(A2)
+      MD5Init(&Md5Ctx);
+      MD5Update(&Md5Ctx, pszMethod, strlen(pszMethod));
+      MD5Update(&Md5Ctx, ":", 1);
+      MD5Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri));
+      if (strcmp(pszQop, "auth-int") == 0) {
+            MD5Update(&Md5Ctx, ":", 1);
+            MD5Update(&Md5Ctx, HEntity, HASHHEXLEN);
+      }
+      MD5Final(HA2, &Md5Ctx);
+       CvtHex(HA2, HA2Hex);
+       
+      // calculate response
+      MD5Init(&Md5Ctx);
+      MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
+      MD5Update(&Md5Ctx, ":", 1);
+      MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+      MD5Update(&Md5Ctx, ":", 1);
+      if (*pszQop) {
+          MD5Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount));
+          MD5Update(&Md5Ctx, ":", 1);
+          MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+          MD5Update(&Md5Ctx, ":", 1);
+          MD5Update(&Md5Ctx, pszQop, strlen(pszQop));
+          MD5Update(&Md5Ctx, ":", 1);
+      }
+      MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
+      MD5Final(RespHash, &Md5Ctx);
+      CvtHex(RespHash, Response);
+}
+// calculate Digest_response_Auth as per SIP Digest spec RFC5090
+void DigestCalcResponseAuth(HASHHEX HA1,char * pszNonce,char * pszNonceCount,char * pszCNonce,char * pszQop,char * pszMethod,char * pszDigestUri,HASHHEX HEntity,HASHHEX Response)
+{
+      MD5_CTX Md5Ctx;
+      HASH HA2;
+      HASH RespHash;
+       HASHHEX HA2Hex;
+
+      // calculate H(A2)
+      MD5Init(&Md5Ctx);
+      MD5Update(&Md5Ctx, ":", 1);
+      MD5Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri));
+      if (strcmp(pszQop, "auth-int") == 0) {
+            MD5Update(&Md5Ctx, ":", 1);
+            MD5Update(&Md5Ctx, HEntity, HASHHEXLEN);
+      }
+      MD5Final(HA2, &Md5Ctx);
+       CvtHex(HA2, HA2Hex);
+       
+      // calculate response
+      MD5Init(&Md5Ctx);
+      MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
+      MD5Update(&Md5Ctx, ":", 1);
+      MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+      MD5Update(&Md5Ctx, ":", 1);
+      if (*pszQop) {
+          MD5Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount));
+          MD5Update(&Md5Ctx, ":", 1);
+          MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+          MD5Update(&Md5Ctx, ":", 1);
+          MD5Update(&Md5Ctx, pszQop, strlen(pszQop));
+          MD5Update(&Md5Ctx, ":", 1);
+      }
+      MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
+      MD5Final(RespHash, &Md5Ctx);
+      CvtHex(RespHash, Response);
+}
+
+
+
+
+
+static void MD5Transform(u32 buf[4], u32 const in[16]);
+
+
+
+
+/**
+ * md5_vector - MD5 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ */
+void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	MD5_CTX ctx;
+	size_t i;
+
+	MD5Init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		MD5Update(&ctx, addr[i], len[i]);
+	MD5Final(mac, &ctx);
+}
+
+
+/* ===== start - public domain MD5 implementation ===== */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#ifndef WORDS_BIGENDIAN
+#define byteReverse(buf, len)	/* Nothing */
+#else
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+    u32 t;
+    do {
+	t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+	    ((unsigned) buf[1] << 8 | buf[0]);
+	*(u32 *) buf = t;
+	buf += 4;
+    } while (--longs);
+}
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+    u32 t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((u32) len << 3)) < t)
+	ctx->bits[1]++;		/* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+	unsigned char *p = (unsigned char *) ctx->in + t;
+
+	t = 64 - t;
+	if (len < t) {
+	    os_memcpy(p, buf, len);
+	    return;
+	}
+	os_memcpy(p, buf, t);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (u32 *) ctx->in);
+	buf += t;
+	len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+	os_memcpy(ctx->in, buf, 64);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (u32 *) ctx->in);
+	buf += 64;
+	len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    os_memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+	/* Two lots of padding:  Pad the first block to 64 bytes */
+	os_memset(p, 0, count);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (u32 *) ctx->in);
+
+	/* Now fill the next block with 56 bytes */
+	os_memset(ctx->in, 0, 56);
+    } else {
+	/* Pad block to 56 bytes */
+	os_memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((u32 *) ctx->in)[14] = ctx->bits[0];
+    ((u32 *) ctx->in)[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (u32 *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    os_memcpy(digest, ctx->buf, 16);
+    os_memset(ctx, 0, sizeof(ctx));	/* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(u32 buf[4], u32 const in[16])
+{
+    register u32 a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+/* ===== end - public domain MD5 implementation ===== */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extensions/app_sip/md5.h	Fri Jul 02 12:00:58 2010 +0900
@@ -0,0 +1,59 @@
+/*********************************************************************************/
+/* freeDiameter author note:
+ *  The content from this file comes directly from the hostap project.
+ * It is redistributed under the terms of the BSD license, as allowed
+ * by the original copyright reproduced bellow.
+ *  The file has not been modified, except for this notice.
+ */
+/*********************************************************************************/
+
+/*
+ * MD5 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+#define MD5_MAC_LEN 16
+
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+typedef int64_t s64;
+typedef int32_t s32;
+typedef int16_t s16;
+typedef int8_t s8;
+
+#define HASHLEN 16
+typedef char HASH[HASHLEN];
+#define HASHHEXLEN 32
+typedef char HASHHEX[HASHHEXLEN+1];
+#define IN
+#define OUT
+
+struct MD5Context {
+	u32 buf[4];
+	u32 bits[2];
+	u8 in[64];
+};
+typedef struct MD5Context MD5_CTX;
+#define os_memcpy(d, s, n) memcpy((d), (s), (n))
+#define os_memset(s, c, n) memset(s, c, n)
+
+
+void MD5Init(struct MD5Context *ctx);
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx);
+
+#endif /* MD5_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extensions/app_sip/multimediaauth.c	Fri Jul 02 12:00:58 2010 +0900
@@ -0,0 +1,747 @@
+#include "diamsip.h"
+
+
+int diamsip_MAR_cb( struct msg ** msg, struct avp * avp, struct session * sess, enum disp_action * act)
+{
+	struct msg *ans, *qry;
+	struct avp *a2, *authdataitem;
+	struct msg_hdr * header = NULL;
+	struct avp_hdr * avphdr=NULL, *avpheader=NULL, *avpheader_auth=NULL,*digestheader=NULL;
+	union avp_value val;
+	int found_cnonce=0;
+	struct avp * tempavp=NULL,*sipAuthentication=NULL,*sipAuthenticate=NULL;
+	char * result;
+	char password[51];
+	int idx=0, idx2=0, number_of_auth_items=0,i=0;;
+	//Flags and variables for Database
+	int sipurinotstored=0, authenticationpending=0, querylen=0, usernamelen=0;
+	char *query=NULL,*username=NULL;
+	
+	//The nonce we will store and retrieve in session
+	struct ds_nonce *storednonce=NULL;
+	
+	
+	TRACE_ENTRY("%p %p %p %p", msg, avp, sess, act);
+	
+	if (msg == NULL)
+		return EINVAL;
+
+	
+	/* Create answer header */
+	qry = *msg;
+	CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) );
+	ans = *msg;
+	
+
+
+	/* Add the appropriate command code & Auth-Application-Id */
+	{
+		
+		CHECK_FCT( fd_msg_hdr ( *msg, &header ) );
+		header->msg_flags = CMD_FLAG_PROXIABLE;
+		header->msg_code = 286;
+		header->msg_appl = 6;
+		
+		
+		/* Add the Auth-Application-Id */
+		{
+			CHECK_FCT( fd_msg_avp_new ( sip_dict.Auth_Application_Id, 0, &avp ) );
+			val.i32 = header->msg_appl;
+			CHECK_FCT( fd_msg_avp_setvalue ( avp, &val ) );
+			CHECK_FCT( fd_msg_avp_add ( ans, MSG_BRW_LAST_CHILD, avp) );
+		}
+	}
+	
+	
+	/* Add the Auth-Session-State AVP */
+	{
+		
+		CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.Auth_Session_State, &avp) );
+		CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr )  );
+		
+		CHECK_FCT( fd_msg_avp_new ( sip_dict.Auth_Session_State, 0, &avp ) );
+		CHECK_FCT( fd_msg_avp_setvalue( avp, avphdr->avp_value ) );
+		CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
+	}
+
+	
+	
+	/* Check if method is REGISTER then User-Name must be present */
+	{
+		
+		CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.SIP_Method, &avp) );
+		CHECK_FCT( fd_msg_avp_hdr( avp, &avpheader ));
+		
+
+		char *method=NULL;
+		
+		CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.User_Name, &avp) );
+		if(avp!=NULL)
+		{
+			
+			int not_found=1;
+			MYSQL_RES *res=NULL;
+			MYSQL_ROW row;
+			
+			CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr )  );
+			
+			//We allocate the double size of username because at worst it can be all quotes
+			username=malloc(avphdr->avp_value->os.len*2+1);
+			//We purify username not to have forbidden characters
+			usernamelen=mysql_real_escape_string(conn, username, avphdr->avp_value->os.data, avphdr->avp_value->os.len);
+			
+			
+			if((strncmp(avpheader->avp_value->os.data,"REGISTER",avpheader->avp_value->os.len)==0))
+			{
+				not_found=1;
+				
+				//We copy username in query
+				querylen=SQL_GETPASSWORD_LEN + usernamelen;
+				query = malloc(querylen+2);
+				snprintf(query, querylen+1, SQL_GETPASSWORD, username);
+				
+				
+				
+				//We make the query	
+				request_mysql(query);
+				res=mysql_use_result(conn);
+				if(res==NULL)
+				{
+					//We couldn't make the request
+					result="DIAMETER_UNABLE_TO_COMPLY";
+					goto out;
+				}
+			
+			
+			
+				while ((row = mysql_fetch_row(res)) != NULL)
+      				{
+      					if(row[0]!="")
+      					{
+      						strcpy(password,row[0]);
+      						not_found=0;
+      						break;
+      					}
+      				}
+      				mysql_free_result(res);
+      				free(query);
+      				
+      				if(not_found)
+				{
+					TRACE_DEBUG(FULL,"The user %s doesn't exist!",username);
+					result="DIAMETER_ERROR_USER_UNKNOWN";
+					free(username);	
+					goto out;
+				}
+			
+			
+			
+				//Now that we know the user exist, we get the list of AOR owned by this user
+				querylen=SQL_GETSIPAOR_LEN + usernamelen;
+				query = malloc(querylen+2);
+				snprintf(query, querylen+1, SQL_GETSIPAOR, username);
+			
+				//We make the query
+				request_mysql(query);
+				res=mysql_use_result(conn);
+				if(res==NULL)
+				{
+					//We couldn't make the request
+					result="DIAMETER_UNABLE_TO_COMPLY";
+					goto out;
+				}
+			
+				//We retrieve the SIP-AOR from AVP to check if the user can us it
+				CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.SIP_AOR, &avp) );
+				CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr )  );
+			
+				not_found=1;
+				while ((row = mysql_fetch_row(res)) != NULL)
+      				{
+      					if(strncmp(avphdr->avp_value->os.data,row[0],avphdr->avp_value->os.len)==0)
+      					{
+      						not_found=0;
+      						break;
+      					}
+      				}
+      				mysql_free_result(res);
+      				free(query);
+      				
+      				if(not_found)
+				{
+					TRACE_DEBUG(FULL,"The user %s can't use this SIP-AOR!",username);
+					result="DIAMETER_ERROR_IDENTITIES_DONT_MATCH";
+					free(username);	
+					goto out;
+				}
+				
+			}
+
+			CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.SIP_Server_URI, &avp) );
+			CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr ));
+			
+			if(avphdr!=NULL)
+			{
+				char *sipuri=NULL;
+				int sipurilen=0;
+
+				//We allocate the double size of SIP-URI because at worst it can be all quotes
+				sipuri=malloc(avphdr->avp_value->os.len*2+1);
+				//We purify SIP-URI not to have forbidden characters
+				sipurilen=mysql_real_escape_string(conn, sipuri, avphdr->avp_value->os.data, avphdr->avp_value->os.len);
+				
+				
+				//We get the SIP-URI assignated to the user
+				querylen=SQL_GETSIPURI_LEN + usernamelen;
+				query = malloc(querylen+2);
+				snprintf(query, querylen+1, SQL_GETSIPURI, username);
+
+				//We make the query
+				request_mysql(query);
+				res=mysql_use_result(conn);
+				if(res==NULL)
+				{
+					//We couldn't make the request
+					result="DIAMETER_UNABLE_TO_COMPLY";
+					goto out;
+				}
+				not_found=1;
+				while ((row = mysql_fetch_row(res)) != NULL)
+      				{
+      					if(strncmp(avphdr->avp_value->os.data,row[0],avphdr->avp_value->os.len)==0)
+      					{
+      						not_found=0;
+      						break;
+      					}
+      				}
+      				mysql_free_result(res);
+      				free(query);
+
+      				if(not_found)
+				{
+					//We update the SIP_URI for the user and we flag "authentication in progress"
+					querylen=SQL_SETSIPURI_LEN + usernamelen + sipurilen;
+					query = malloc(querylen+2);
+					snprintf(query, querylen+1, SQL_SETSIPURI, sipuri, username);
+			
+					//We make the query
+					request_mysql(query);
+					
+	      				free(query);
+	      				authenticationpending=1;
+				}
+      				free(sipuri);
+				
+			}
+			else
+				sipurinotstored=1;
+
+		}
+		else
+		{
+			result="DIAMETER_USER_NAME_REQUIRED";
+			goto out;
+		}
+
+			
+		free(method);
+
+	}
+
+	
+	//TODO: remove loop for authdataitem because RFC say only one (wait for answer from Garcia)
+	// How many Auth Data Items?
+	CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.SIP_Number_Auth_Items, &avp) );
+	CHECK_FCT( fd_msg_avp_hdr( avp, &avpheader )  );
+	
+	
+	if(avp!=NULL)
+	{
+		CHECK_FCT(fd_msg_search_avp ( qry, sip_dict.SIP_Auth_Data_Item, &avp));
+		CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr )  );
+		
+		if(avp!=NULL)
+		{
+			//First is Authentication Scheme
+			CHECK_FCT(fd_msg_browse ( avp, MSG_BRW_FIRST_CHILD, &avp, NULL) );
+			CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr )  );
+			
+			//Digest-Authentication?
+			if(avphdr->avp_value->i32==0)
+			{
+				
+				for(idx=0;idx<avpheader->avp_value->i32;idx++)
+				{
+					//We look for SIP Auth items
+					CHECK_FCT(fd_msg_browse ( avp, MSG_BRW_WALK, &avp, NULL) );
+					
+					if(avp!=NULL)
+					{
+						
+						CHECK_FCT( fd_msg_avp_hdr( avp,&avphdr ));
+		
+						if(avphdr->avp_code==380) //We only create Auth-Data-Item to answer Auth-Data-Item
+						{
+							/* Add the Auth-Data-Item AVP */
+							CHECK_FCT( fd_msg_avp_new ( sip_dict.SIP_Auth_Data_Item, 0, &authdataitem ) );
+					
+							/* Add the Authentication Scheme AVP */
+							{
+								CHECK_FCT( fd_msg_avp_new ( sip_dict.SIP_Authentication_Scheme, 0, &a2 ) );
+								val.i32=0; //We only know Digest Authentication
+								CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) );
+								CHECK_FCT( fd_msg_avp_add( authdataitem, MSG_BRW_LAST_CHILD, a2 ) );
+							}
+							
+							//We need to know if there is a Cnonce attribute (only in the second MAR request)
+							
+							//CHECK_FCT(fd_msg_browse ( avp, MSG_BRW_WALK, &avp, NULL) );
+							
+							
+							CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_CNonce, &a2 ));
+							
+							if(a2!=NULL)
+								found_cnonce=1;
+							else
+								found_cnonce=0;
+								
+							if(!found_cnonce)
+							{
+								/*
+								We are in the case of first access request so we need to challenge the user.
+								*/
+								TRACE_DEBUG(FULL,"First Authorization in progress...");
+
+								/* Create a new session */ //this create a new session Id !!!
+								//CHECK_FCT_DO( fd_sess_new( &sess, fd_g_config->cnf_diamid, "diamsip", 7), goto out );
+								
+
+								/* Create the SIP-Authenticate AVP */
+								{
+									CHECK_FCT( fd_msg_avp_new ( sip_dict.SIP_Authenticate, 0, &sipAuthenticate ) );
+								}						
+
+								/* Add the Digest QOP AVP */
+								{
+									CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_QOP, 0, &a2 ) );
+									val.os.data="auth";
+									val.os.len=strlen(val.os.data);
+									CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) );
+									CHECK_FCT( fd_msg_avp_add( sipAuthenticate, MSG_BRW_LAST_CHILD, a2 ) );
+								}
+								/* Add the Digest Nonce AVP */
+								{
+									uint8_t buffer[NONCE_SIZE];
+									char nonce[NONCE_SIZE * 2 + 1];
+							
+								
+									gcry_create_nonce ((uint8_t *)buffer, sizeof(buffer));
+							
+									for(i=0;i<NONCE_SIZE;i++)
+										sprintf(&nonce[2 * i], "%2.2hhx", buffer[i]);
+								
+									CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_Nonce, 0, &a2 ) );
+									
+									
+									//We store the nonce (storednonce structure) inside the session
+									storednonce=malloc(sizeof(struct ds_nonce));
+									memset(storednonce,0,sizeof(struct ds_nonce));
+									CHECK_MALLOC(storednonce->nonce=malloc(NONCE_SIZE*2+1));
+									memcpy(storednonce->nonce,(char *)nonce,NONCE_SIZE*2+1);
+									CHECK_FCT( fd_sess_state_store ( ds_sess_hdl, sess, &storednonce ));  
+									
+									val.os.data=nonce;
+									val.os.len=NONCE_SIZE * 2;
+									
+									CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) );
+									CHECK_FCT( fd_msg_avp_add( sipAuthenticate, MSG_BRW_LAST_CHILD, a2 ) );
+								}
+								/* Add the Digest Algorithm AVP */
+								{
+									CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_Algorithm, 0, &a2 ) );
+									val.os.data="MD5";
+									val.os.len=strlen(val.os.data);
+									CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) );
+									CHECK_FCT( fd_msg_avp_add( sipAuthenticate, MSG_BRW_LAST_CHILD, a2 ) );
+							
+								}
+								/* Add the Digest Realm AVP */
+								{
+									tempavp=avp;
+								
+									avpheader_auth=walk_digest(tempavp, 104);
+									if(avpheader_auth!=NULL)
+									{
+										CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_Realm, 0, &a2 ) );
+										CHECK_FCT( fd_msg_avp_setvalue( a2, avpheader_auth->avp_value ) );
+										CHECK_FCT( fd_msg_avp_add( sipAuthenticate, MSG_BRW_LAST_CHILD, a2 ) );
+								
+									}
+								}
+						
+						
+								//We add SIP Authenticate to Auth Data Item
+								CHECK_FCT( fd_msg_avp_add( authdataitem, MSG_BRW_LAST_CHILD, sipAuthenticate ) );
+								//We add Auth Data Item to Answer
+								CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, authdataitem ) );
+						
+								number_of_auth_items++;
+								if(sipurinotstored)
+									result="DIAMETER_SUCCESS_AUTH_SENT_SERVER_NOT_STORED";
+								else
+									result="DIAMETER_MULTI_ROUND_AUTH";
+								found_cnonce=0;
+							}
+							else
+							{
+								/*
+								We are in the case of access request after challenge so we need to check credentials.
+								*/
+								TRACE_DEBUG(FULL,"Authentication after challenge");
+								
+								/* Search the session, retrieve its data */
+								{
+									//int new=0;
+									
+									//TRACE_DEBUG(FULL,"new: *%d*",new);
+									//ASSERT( new == 0 );
+									CHECK_FCT( fd_sess_state_retrieve( ds_sess_hdl, sess, &storednonce ));
+									if(storednonce ==NULL)
+									{
+										result="DIAMETER_UNABLE_TO_COMPLY";
+												
+										if(username!=NULL)
+											free(username);
+										goto out;
+									}
+								}
+								
+								/* Create the SIP-Authentication-Info AVP */
+								{
+									CHECK_FCT( fd_msg_avp_new ( sip_dict.SIP_Authentication_Info, 0, &sipAuthentication ) );
+								}
+								
+								
+							
+								/* Add the Digest response Auth AVP */
+								{
+									//uint8_t bufferresp[DIGEST_LEN];
+									//char response[DIGEST_LEN*2+1];
+									int i=0;
+									
+									//We extract all the data we need
+									tempavp=avp;
+									
+									char * digest_username=NULL, *digest_uri=NULL, *digest_response=NULL, *digest_realm=NULL, *digest_nonce=NULL, *digest_method=NULL, *digest_qop=NULL, *digest_algorithm=NULL, *digest_cnonce=NULL, *digest_noncecount=NULL;
+									
+									
+									CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_Nonce, &a2 ));
+									if(a2!=NULL)
+									{
+										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
+										if(digestheader!=NULL)
+										{
+											
+											digest_nonce = malloc(digestheader->avp_value->os.len + 1);
+											memcpy(digest_nonce, digestheader->avp_value->os.data,
+											digestheader->avp_value->os.len);
+											digest_nonce[digestheader->avp_value->os.len]='\0';
+											TRACE_DEBUG(FULL,"Element:*%s*",digest_nonce);	
+											TRACE_DEBUG(FULL,"Stored Nonce:*%s*",storednonce->nonce);
+											
+											if(strcmp(digest_nonce,storednonce->nonce)!=0)
+											{
+												free(digest_nonce);
+												free(storednonce->nonce);
+												free(storednonce);
+												result="DIAMETER_UNABLE_TO_COMPLY";
+												
+												if(username!=NULL)
+													free(username);
+												goto out;
+											}
+											
+										}
+											
+									}
+									CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_Response, &a2 ));
+									if(a2!=NULL)
+									{
+										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
+										if(digestheader!=NULL)
+										{
+											digest_response = malloc(digestheader->avp_value->os.len + 1);
+											memcpy(digest_response, digestheader->avp_value->os.data,
+											digestheader->avp_value->os.len);
+											digest_response[digestheader->avp_value->os.len]='\0';
+											TRACE_DEBUG(FULL,"Element:*%s*",digest_response);	
+										}
+										
+									}
+									CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_Realm, &a2 ));
+									if(a2!=NULL)
+									{
+										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
+										if(digestheader!=NULL)
+										{
+											digest_realm = malloc(digestheader->avp_value->os.len + 1);
+											memcpy(digest_realm, digestheader->avp_value->os.data,
+											digestheader->avp_value->os.len);
+											digest_realm[digestheader->avp_value->os.len]='\0';
+											TRACE_DEBUG(FULL,"Element:*%s*",digest_realm);	
+										}
+									}
+									
+									CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_Method, &a2 ));
+									if(a2!=NULL)
+									{
+										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
+										if(digestheader!=NULL)
+										{
+											digest_method = malloc(digestheader->avp_value->os.len + 1);
+											memcpy(digest_method, digestheader->avp_value->os.data,
+											digestheader->avp_value->os.len);
+											digest_method[digestheader->avp_value->os.len]='\0';
+											TRACE_DEBUG(FULL,"Element:*%s*",digest_method);	
+										}
+									}
+									else
+										digest_method="";
+									
+									CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_URI, &a2 ));
+									if(a2!=NULL)
+									{
+										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
+										if(digestheader!=NULL)
+										{
+											digest_uri = malloc(digestheader->avp_value->os.len + 1);
+											memcpy(digest_uri, digestheader->avp_value->os.data,
+											digestheader->avp_value->os.len);
+											digest_uri[digestheader->avp_value->os.len]='\0';
+											TRACE_DEBUG(FULL,"Element:*%s*",digest_uri);	
+										}
+									}
+									
+									CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_QOP, &a2 ));
+									if(a2!=NULL)
+									{
+										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
+										if(digestheader!=NULL)
+										{
+											digest_qop = malloc(digestheader->avp_value->os.len + 1);
+											memcpy(digest_qop, digestheader->avp_value->os.data,
+											digestheader->avp_value->os.len);
+											digest_qop[digestheader->avp_value->os.len]='\0';
+											TRACE_DEBUG(FULL,"Element:*%s*",digest_qop);	
+										}
+									}
+									else
+										digest_qop=NULL;
+									CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_Algorithm, &a2 ));
+									if(a2!=NULL)
+									{
+										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
+										if(digestheader!=NULL)
+										{
+											digest_algorithm = malloc(digestheader->avp_value->os.len + 1);
+											memcpy(digest_algorithm, digestheader->avp_value->os.data,
+											digestheader->avp_value->os.len);
+											digest_algorithm[digestheader->avp_value->os.len]='\0';
+											TRACE_DEBUG(FULL,"Element:*%s*",digest_algorithm);	
+										}
+									}
+									else
+										digest_algorithm=NULL;
+									CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_CNonce, &a2 ));
+									if(a2!=NULL)
+									{
+										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
+										if(digestheader!=NULL)
+										{
+											digest_cnonce = malloc(digestheader->avp_value->os.len + 1);
+											memcpy(digest_cnonce, digestheader->avp_value->os.data,
+											digestheader->avp_value->os.len);
+											digest_cnonce[digestheader->avp_value->os.len]='\0';
+											TRACE_DEBUG(FULL,"Element:*%s*",digest_cnonce);	
+										}
+									}
+									else
+										digest_cnonce="";
+									CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_Nonce_Count, &a2 ));
+									if(a2!=NULL)
+									{
+										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
+										if(digestheader!=NULL)
+										{
+											digest_noncecount = malloc(digestheader->avp_value->os.len + 1);
+											memcpy(digest_noncecount, digestheader->avp_value->os.data,
+											digestheader->avp_value->os.len);
+											digest_noncecount[digestheader->avp_value->os.len]='\0';
+											TRACE_DEBUG(FULL,"Element:*%s*",digest_noncecount);	
+										}
+									}
+									else
+										digest_noncecount="";
+									CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_Username, &a2 ));
+									if(a2!=NULL)
+									{
+										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
+										if(digestheader!=NULL)
+										{
+											digest_username = malloc(digestheader->avp_value->os.len + 1);
+											memcpy(digest_username, digestheader->avp_value->os.data,
+											digestheader->avp_value->os.len);
+											digest_username[digestheader->avp_value->os.len]='\0';
+											TRACE_DEBUG(FULL,"Element:*%s*",digest_username);	
+										}
+									}
+									//TODO: replace by authentication function
+									
+									HASHHEX HA1;
+									HASHHEX HA2 = "";
+									HASHHEX response, responseauth;
+
+									
+									DigestCalcHA1(digest_algorithm, digest_username, digest_realm, password, digest_nonce,digest_cnonce, HA1);
+									
+      							DigestCalcResponse(HA1, digest_nonce, digest_noncecount, digest_cnonce, digest_qop,digest_method, digest_uri, HA2, response);
+      									
+		
+									// We check that the Digest-Response is the same (UA, Diameter)
+									if(strcmp(response,digest_response)!=0)
+									{
+										TRACE_DEBUG(FULL,"Response calculated by Diameter server:%s",response);
+										TRACE_DEBUG(FULL,"Response calculated by UA:%s",digest_response);
+										TRACE_DEBUG(INFO,"Digest-Response does not match!");
+										result="DIAMETER_UNABLE_TO_COMPLY";
+										free(digest_algorithm);
+										free(digest_cnonce);
+										free(digest_nonce);
+										free(digest_noncecount);
+										free(digest_method);
+										free(digest_username);
+										free(digest_uri);
+										free(digest_qop);
+										free(digest_response);
+										free(digest_realm);
+										free(storednonce->nonce);
+										free(storednonce);
+										if(username!=NULL)
+											free(username);
+										goto out;
+										
+									}
+									//We calculate Digest_Response_Auth
+									DigestCalcResponseAuth(HA1, digest_nonce, digest_noncecount, digest_cnonce, digest_qop,digest_method, digest_uri, HA2, responseauth);
+									
+									
+									if(strcmp(digest_qop,"auth-int")==0)
+									{
+										//Digest-HA1 MUST be used instead of Digest-Response-Auth if Digest-Qop is 'auth-int'.
+										CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_HA1, 0, &a2 ) );
+										val.os.data=HA1;
+										val.os.len=HASHHEXLEN+1;
+										CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) );
+										CHECK_FCT( fd_msg_avp_add( sipAuthentication, MSG_BRW_LAST_CHILD, a2 ) );
+									}
+									else
+									{
+										//Digest-Response-Auth MUST be used instead of Digest-HA1 if Digest-Qop is 'auth'.
+										CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_Response_Auth, 0, &a2 ) );
+										val.os.data=responseauth;
+										val.os.len=DIGEST_LEN*2;
+										CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) );
+										CHECK_FCT( fd_msg_avp_add( sipAuthentication, MSG_BRW_LAST_CHILD, a2 ) );
+									}
+									free(digest_algorithm);
+									free(digest_cnonce);
+									free(digest_nonce);
+									free(digest_noncecount);
+									free(digest_method);
+									free(digest_username);
+									free(digest_uri);
+									free(digest_qop);
+									free(digest_response);
+									free(digest_realm);
+									free(storednonce->nonce);
+									free(storednonce);
+								
+									number_of_auth_items++;
+								}	
+								
+								
+								//We add SIP Authentication-Info to Auth Data Item
+								CHECK_FCT( fd_msg_avp_add( authdataitem, MSG_BRW_LAST_CHILD, sipAuthentication ) );
+								//We add Auth Data Item to Answer
+								CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, authdataitem ) );
+								
+								
+								if(username!=NULL && authenticationpending)
+								{
+									//We clear the flag "authentication pending"
+									querylen=SQL_CLEARFLAG_LEN + usernamelen;
+									query = malloc(querylen+2);
+									snprintf(query, querylen+1, SQL_CLEARFLAG, username);
+		
+									//We make the query
+									request_mysql(query);
+				
+					      				free(query);
+								}
+								
+								if(sipurinotstored)
+									result="DIAMETER_SUCCESS_SERVER_NAME_NOT_STORED";
+								else
+									result="DIAMETER_SUCCESS";
+								found_cnonce=0;
+							}
+						}
+					}
+					else
+						TRACE_DEBUG(INFO,"No auth data items!");
+				}
+				/*Add SIP_Number_Auth_Items  AVP */
+				{
+					CHECK_FCT( fd_msg_avp_new ( sip_dict.SIP_Number_Auth_Items, 0, &avp ) );
+					val.i32 = number_of_auth_items;
+					CHECK_FCT( fd_msg_avp_setvalue ( avp, &val ) );
+					CHECK_FCT( fd_msg_avp_add ( ans, MSG_BRW_LAST_CHILD, avp) );
+				}
+				
+				
+			}
+			else
+			{
+				TRACE_DEBUG(INFO,"We only support DIGEST for now, unable to comply");
+				result="DIAMETER_ERROR_AUTH_SCHEME_NOT_SUPPORTED";
+				if(username!=NULL)
+					free(username);
+				goto out;
+			}
+		}
+	}
+	else
+	{
+		//TODO: remove this because Number_Auth_Items is not compulsory
+		TRACE_DEBUG(FULL,"Number-Auth-Items is not included.");
+		result="DIAMETER_UNABLE_TO_COMPLY";
+		if(username!=NULL)
+			free(username);
+		goto out;
+	}
+	
+	if(username!=NULL)
+		free(username);
+	
+
+out:
+	/* Set the Origin-Host, Origin-Realm, Result-Code AVPs */
+	CHECK_FCT( fd_msg_rescode_set( ans, result, NULL, NULL, 1 ) );
+	
+	
+	/* Send the answer */
+	CHECK_FCT( fd_msg_send( msg, NULL, NULL ) );
+	
+	
+	return 0;
+}
"Welcome to our mercurial repository"