changeset 23:db6c40b8b307

Added some code in cnxctx.c mainly
author Sebastien Decugis <sdecugis@nict.go.jp>
date Tue, 20 Oct 2009 17:30:20 +0900
parents 0b3b46da2c12
children bd83ce9328ed
files doc/freediameter.conf.sample freeDiameter/cnxctx.c freeDiameter/config.c freeDiameter/fD.h freeDiameter/fdd.y freeDiameter/main.c freeDiameter/sctp.c freeDiameter/tcp.c include/freeDiameter/CMakeLists.txt include/freeDiameter/freeDiameter-host.h.in include/freeDiameter/freeDiameter.h include/freeDiameter/libfreeDiameter.h
diffstat 12 files changed, 797 insertions(+), 46 deletions(-) [+]
line wrap: on
line diff
--- a/doc/freediameter.conf.sample	Mon Oct 19 18:43:09 2009 +0900
+++ b/doc/freediameter.conf.sample	Tue Oct 20 17:30:20 2009 +0900
@@ -67,6 +67,7 @@
 # Default : listen on all addresses available.
 #ListenOn = "202.249.37.5";
 #ListenOn = "2001:200:903:2::202:1";
+#ListenOn = "fe80::21c:5ff:fe98:7d62%eth0";
 
 ##############################################################
 ##  TLS Configuration
@@ -200,13 +201,11 @@
 Port = 3866;
 SecPort = 3867;
 TLS_old_method;
-No_SCTP;
-Prefer_TCP;
 SCTP_streams = 50;
-#ListenOn = "202.249.37.5";
-#ListenOn = "2001:200:903:2::202:1";
 TcTimer = 60;
 TwTimer = 6;
+ListenOn = "133.243.146.201";
+ListenOn = "fe80::21d:9ff:fe89:7d68%eth0";
 NoRelay;
 LoadExtension = "extensions/dbg_monitor.fdx";
 LoadExtension = "extensions/dict_nasreq.fdx";
--- a/freeDiameter/cnxctx.c	Mon Oct 19 18:43:09 2009 +0900
+++ b/freeDiameter/cnxctx.c	Tue Oct 20 17:30:20 2009 +0900
@@ -213,9 +213,11 @@
 			CHECK_FCT(fd_tcp_listen(conn->cc_socket));
 			break;
 
+#ifndef DISABLE_SCTP
 		case IPPROTO_SCTP:
 			CHECK_FCT(fd_sctp_listen(conn->cc_socket));
 			break;
+#endif /* DISABLE_SCTP */
 
 		default:
 			CHECK_PARAMS(0);
@@ -399,7 +401,7 @@
 	return ENOTSUP;
 }
 
-/* Get the list of endpoints (IP addresses) of the local and remote peers on this conenction */
+/* Get the list of endpoints (IP addresses) of the local and remote peers on this connection */
 int fd_cnx_getendpoints(struct cnxctx * conn, struct fd_list * local, struct fd_list * remote)
 {
 	TRACE_ENTRY("%p %p %p", conn, local, remote);
@@ -407,18 +409,54 @@
 	
 	if (local) {
 		/* Retrieve the local endpoint(s) of the connection */
-		TODO("TCP : getsockname");
-		TODO("SCTP: sctp_getladdrs / _sctp_getboundaddrs (waaad)");
+		switch (conn->cc_proto) {
+			case IPPROTO_TCP: {
+				sSS ss;
+				socklen_t sl;
+				CHECK_FCT(fd_tcp_get_local_ep(conn->cc_socket, &ss, &sl));
+				CHECK_FCT(fd_ep_add_merge( local, (sSA *)&ss, sl, 0, 0, 0, 1 ));
+			}
+			break;
+
+			#ifndef DISABLE_SCTP
+			case IPPROTO_SCTP: {
+				CHECK_FCT(fd_sctp_get_local_ep(conn->cc_socket, local));
+			}
+			break;
+			#endif /* DISABLE_SCTP */
+
+			default:
+				CHECK_PARAMS(0);
+		}
 	}
 	
 	if (remote) {
+		/* Check we have a full connection object, not a listening socket (with no remote) */
+		CHECK_PARAMS( conn->cc_events );
+		
 		/* Retrieve the peer endpoint(s) of the connection */
-		TODO("TCP : getpeername");
-		TODO("SCTP: sctp_getpaddrs");
-		
+		switch (conn->cc_proto) {
+			case IPPROTO_TCP: {
+				sSS ss;
+				socklen_t sl;
+				CHECK_FCT(fd_tcp_get_remote_ep(conn->cc_socket, &ss, &sl));
+				CHECK_FCT(fd_ep_add_merge( remote, (sSA *)&ss, sl, 0, 0, 0, 1 ));
+			}
+			break;
+
+			#ifndef DISABLE_SCTP
+			case IPPROTO_SCTP: {
+				CHECK_FCT(fd_sctp_get_remote_ep(conn->cc_socket, remote));
+			}
+			break;
+			#endif /* DISABLE_SCTP */
+
+			default:
+				CHECK_PARAMS(0);
+		}
 	}
 
-	return ENOTSUP;
+	return 0;
 }
 
 
--- a/freeDiameter/config.c	Mon Oct 19 18:43:09 2009 +0900
+++ b/freeDiameter/config.c	Tue Oct 20 17:30:20 2009 +0900
@@ -99,7 +99,7 @@
 		while (li != &fd_g_config->cnf_endpoints) {
 			struct fd_endpoint * ep = (struct fd_endpoint *)li;
 			if (li != fd_g_config->cnf_endpoints.next) fd_log_debug("                             ");
-			sSA_DUMP_NODE( &ep->ss, NI_NUMERICHOST );
+			sSA_DUMP_NODE( &ep->sa, NI_NUMERICHOST );
 			fd_log_debug("\n");
 			li = li->next;
 		}
@@ -233,13 +233,13 @@
 		struct fd_list * li;
 		for ( li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
 			struct fd_endpoint * ep = (struct fd_endpoint *)li;
-			if ( (fd_g_config->cnf_flags.no_ip4 && (ep->ss.ss_family == AF_INET))
-			   ||(fd_g_config->cnf_flags.no_ip6 && (ep->ss.ss_family == AF_INET6)) ) {
+			if ( (fd_g_config->cnf_flags.no_ip4 && (ep->sa.sa_family == AF_INET))
+			   ||(fd_g_config->cnf_flags.no_ip6 && (ep->sa.sa_family == AF_INET6)) ) {
 				li = li->prev;
 				fd_list_unlink(&ep->chain);
 				if (TRACE_BOOL(INFO)) {
 					fd_log_debug("Info: Removing local address conflicting with the flags no_IP / no_IP6 : ");
-					sSA_DUMP_NODE( &ep->ss, AI_NUMERICHOST );
+					sSA_DUMP_NODE( &ep->sa, AI_NUMERICHOST );
 					fd_log_debug("\n");
 				}
 				free(ep);
@@ -271,3 +271,43 @@
 	
 	return 0;
 }
+
+/* Add an endpoint information in a list */
+int fd_ep_add_merge( struct fd_list * list, sSA * sa, socklen_t sl, int conf, int disc, int adv, int ll )
+{
+	struct fd_endpoint * ep;
+	struct fd_list * li;
+	int cmp = -1;
+	
+	TRACE_ENTRY("%p %p %u %i %i %i %i", list, sa, sl, conf, disc, adv, ll);
+	CHECK_PARAMS( list && sa && (sl <= sizeof(sSS)) && (conf || disc || adv || ll) );
+	
+	/* Search place in the list */
+	for (li = list->next; li != list; li = li->next) {
+		ep = (struct fd_endpoint *)li;
+		
+		cmp = memcmp(&ep->ss, sa, sl);
+		if (cmp >= 0)
+			break;
+	}
+	
+	if (cmp) {
+		/* new item to be added */
+		CHECK_MALLOC( ep = malloc(sizeof(struct fd_endpoint)) );
+		memset(ep, 0, sizeof(struct fd_endpoint));
+		fd_list_init(&ep->chain, NULL);
+		memcpy(&ep->ss, sa, sl);
+		
+		/* Insert in the list */
+		fd_list_insert_before(li, &ep->chain);
+	}
+	
+	/* Merge the flags */
+	ep->meta.conf = conf || ep->meta.conf;
+	ep->meta.disc = disc || ep->meta.disc;
+	ep->meta.adv  =  adv || ep->meta.adv;
+	ep->meta.ll   =   ll || ep->meta.ll;
+	
+	return 0;
+}
+
--- a/freeDiameter/fD.h	Mon Oct 19 18:43:09 2009 +0900
+++ b/freeDiameter/fD.h	Tue Oct 20 17:30:20 2009 +0900
@@ -221,12 +221,15 @@
 /* TCP */
 int fd_tcp_create_bind_server( int * sock, sSA * sa, socklen_t salen );
 int fd_tcp_listen( int sock );
+int fd_tcp_get_local_ep(int sock, sSS * ss, socklen_t *sl);
+int fd_tcp_get_remote_ep(int sock, sSS * ss, socklen_t *sl);
 
 /* SCTP */
 #ifndef DISABLE_SCTP
 int fd_sctp_create_bind_server( int * sock, struct fd_list * list, uint16_t port );
 int fd_sctp_listen( int sock );
-
+int fd_sctp_get_local_ep(int sock, struct fd_list * list);
+int fd_sctp_get_remote_ep(int sock, struct fd_list * list);
 int fd_sctp_get_str_info( int socket, int *in, int *out );
 
 #endif /* DISABLE_SCTP */
--- a/freeDiameter/fdd.y	Mon Oct 19 18:43:09 2009 +0900
+++ b/freeDiameter/fdd.y	Tue Oct 20 17:30:20 2009 +0900
@@ -211,25 +211,16 @@
 
 listenon:		LISTENON '=' QSTRING ';'
 			{
-				struct fd_endpoint * ep;
 				struct addrinfo hints, *ai;
 				int ret;
 				
-				CHECK_MALLOC_DO( ep = malloc(sizeof(struct fd_endpoint)),
-					{ yyerror (&yylloc, conf, "Out of memory"); YYERROR; } );
-				memset(ep, 0, sizeof(struct fd_endpoint));
-				fd_list_init(&ep->chain, NULL);
-				ep->meta.conf = 1;
-				
 				memset(&hints, 0, sizeof(hints));
 				hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
 				ret = getaddrinfo($3, NULL, &hints, &ai);
-				if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); free(ep); YYERROR; }
-				ASSERT( ai->ai_addrlen <= sizeof(sSS) );
-				memcpy(&ep->ss, ai->ai_addr, ai->ai_addrlen);
+				if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); YYERROR; }
+				CHECK_FCT_DO( fd_ep_add_merge( &conf->cnf_endpoints, ai->ai_addr, ai->ai_addrlen, 1, 0, 0, 0 ), YYERROR );
+				freeaddrinfo(ai);
 				free($3);
-				freeaddrinfo(ai);
-				fd_list_insert_before(&conf->cnf_endpoints, &ep->chain);
 			}
 			;
 
@@ -429,30 +420,24 @@
 			}
 			| peerparams CONNTO '=' QSTRING ';'
 			{
-				struct fd_endpoint * ep;
 				struct addrinfo hints, *ai;
 				int ret;
+				int disc = 0;
 				
-				CHECK_MALLOC_DO( ep = malloc(sizeof(struct fd_endpoint)),
-					{ yyerror (&yylloc, conf, "Out of memory"); YYERROR; } );
-				memset(ep, 0, sizeof(struct fd_endpoint));
-				fd_list_init(&ep->chain, NULL);
-				ep->meta.conf = 1;
 				memset(&hints, 0, sizeof(hints));
 				hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST;
 				ret = getaddrinfo($4, NULL, &hints, &ai);
 				if (ret == EAI_NONAME) {
 					/* The name was maybe not numeric, try again */
-					ep->meta.disc = 1;
+					disc = 1;
 					hints.ai_flags &= ~ AI_NUMERICHOST;
 					ret = getaddrinfo($4, NULL, &hints, &ai);
 				}
-				if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); free(ep); YYERROR; }
+				if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); YYERROR; }
 				
-				memcpy(&ep->ss, ai->ai_addr, ai->ai_addrlen);
+				CHECK_FCT_DO( fd_ep_add_merge( &fddpi.pi_endpoints, ai->ai_addr, ai->ai_addrlen, 1, disc, 0, 0 ), YYERROR );
 				free($4);
 				freeaddrinfo(ai);
-				fd_list_insert_before(&fddpi.pi_endpoints, &ep->chain);
 			}
 			;
 
--- a/freeDiameter/main.c	Mon Oct 19 18:43:09 2009 +0900
+++ b/freeDiameter/main.c	Tue Oct 20 17:30:20 2009 +0900
@@ -105,6 +105,8 @@
 	/* Load the dynamic extensions */
 	CHECK_FCT(  fd_ext_load()  );
 	
+	fd_conf_dump();
+	
 	/* Start the servers */
 	CHECK_FCT( fd_servers_start() );
 	
@@ -113,7 +115,6 @@
 	
 	/* Now, just wait for events */
 	TRACE_DEBUG(INFO, FD_PROJECT_BINARY " daemon initialized.");
-	fd_conf_dump();
 	while (1) {
 		int code;
 		CHECK_FCT_DO(  fd_event_get(fd_g_config->cnf_main_ev, &code, NULL),  break  );
--- a/freeDiameter/sctp.c	Mon Oct 19 18:43:09 2009 +0900
+++ b/freeDiameter/sctp.c	Tue Oct 20 17:30:20 2009 +0900
@@ -34,13 +34,578 @@
 *********************************************************************************************************/
 
 #include "fD.h"
+#include <netinet/sctp.h>
+#include <sys/uio.h>
+
+/* Pre-binding socket options -- # streams read in config */
+static int fd_setsockopt_prebind(int sk)
+{
+	#ifdef DEBUG_SCTP
+	socklen_t sz;
+	#endif /* DEBUG_SCTP */
+	
+	TRACE_ENTRY( "%d", sk);
+	
+	CHECK_PARAMS( sk > 0 );
+	
+	/* Subscribe to some notifications */
+	{
+		struct sctp_event_subscribe event;
+
+		memset(&event, 0, sizeof(event));
+		event.sctp_data_io_event	= 1;	/* to receive the stream ID in SCTP_SNDRCV ancilliary data on message reception */
+		event.sctp_association_event	= 0;	/* new or closed associations (mostly for one-to-many style sockets) */
+		event.sctp_address_event	= 1;	/* address changes */
+		event.sctp_send_failure_event	= 1;	/* delivery failures */
+		event.sctp_peer_error_event	= 1;	/* remote peer sends an error */
+		event.sctp_shutdown_event	= 1;	/* peer has sent a SHUTDOWN */
+		event.sctp_partial_delivery_event = 1;	/* a partial delivery is aborted, probably indicating the connection is being shutdown */
+		// event.sctp_adaptation_layer_event = 0;	/* adaptation layer notifications */
+		// event.sctp_authentication_event = 0;	/* when new key is made active */
+
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) );
+		
+		#ifdef DEBUG_SCTP
+		sz = sizeof(event);
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, &sz) );
+		if (sz != sizeof(event))
+		{
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(event));
+			return ENOTSUP;
+		}
+		
+		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_data_io_event          : %hhu", event.sctp_data_io_event);
+		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_association_event      : %hhu", event.sctp_association_event);
+		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_address_event          : %hhu", event.sctp_address_event);
+		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_send_failure_event     : %hhu", event.sctp_send_failure_event);
+		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_peer_error_event       : %hhu", event.sctp_peer_error_event);
+		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_shutdown_event         : %hhu", event.sctp_shutdown_event);
+		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_partial_delivery_event : %hhu", event.sctp_partial_delivery_event);
+		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_adaptation_layer_event : %hhu", event.sctp_adaptation_layer_event);
+		// TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_authentication_event   : %hhu", event.sctp_authentication_event);
+		#endif /* DEBUG_SCTP */
+		
+	}
+	
+	/* Set the INIT parameters, such as number of streams */
+	{
+		struct sctp_initmsg init;
+		memset(&init, 0, sizeof(init));
+		
+		#ifdef DEBUG_SCTP
+		sz = sizeof(init);
+		
+		/* Read socket defaults */
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz)  );
+		if (sz != sizeof(init))
+		{
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(init));
+			return ENOTSUP;
+		}
+		TRACE_DEBUG(FULL, "Def SCTP_INITMSG sinit_num_ostreams   : %hu", init.sinit_num_ostreams);
+		TRACE_DEBUG(FULL, "Def SCTP_INITMSG sinit_max_instreams  : %hu", init.sinit_max_instreams);
+		TRACE_DEBUG(FULL, "Def SCTP_INITMSG sinit_max_attempts   : %hu", init.sinit_max_attempts);
+		TRACE_DEBUG(FULL, "Def SCTP_INITMSG sinit_max_init_timeo : %hu", init.sinit_max_init_timeo);
+		#endif /* DEBUG_SCTP */
+
+		/* Set the init options -- need to receive SCTP_COMM_UP to confirm the requested parameters */
+		init.sinit_num_ostreams	  = fd_g_config->cnf_sctp_str;	/* desired number of outgoing streams */
+		init.sinit_max_init_timeo = CNX_TIMEOUT * 1000;
+
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, sizeof(init))  );
+		
+		#ifdef DEBUG_SCTP
+		/* Check new values */
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz)  );
+		TRACE_DEBUG(FULL, "New SCTP_INITMSG sinit_num_ostreams   : %hu", init.sinit_num_ostreams);
+		TRACE_DEBUG(FULL, "New SCTP_INITMSG sinit_max_instreams  : %hu", init.sinit_max_instreams);
+		TRACE_DEBUG(FULL, "New SCTP_INITMSG sinit_max_attempts   : %hu", init.sinit_max_attempts);
+		TRACE_DEBUG(FULL, "New SCTP_INITMSG sinit_max_init_timeo : %hu", init.sinit_max_init_timeo);
+		#endif /* DEBUG_SCTP */
+	}
+	
+	/* Set the SCTP_DISABLE_FRAGMENTS option, required for TLS */
+	#ifdef SCTP_DISABLE_FRAGMENTS
+	{
+		int nofrag;
+		
+		#ifdef DEBUG_SCTP
+		sz = sizeof(nofrag);
+		/* Read socket defaults */
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz)  );
+		if (sz != sizeof(nofrag))
+		{
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nofrag));
+			return ENOTSUP;
+		}
+		TRACE_DEBUG(FULL, "Def SCTP_DISABLE_FRAGMENTS value : %s", nofrag ? "true" : "false");
+		#endif /* DEBUG_SCTP */
+
+		nofrag = 0;	/* We turn ON the fragmentation */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, sizeof(nofrag))  );
+		
+		#ifdef DEBUG_SCTP
+		/* Check new values */
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz)  );
+		TRACE_DEBUG(FULL, "New SCTP_DISABLE_FRAGMENTS value : %s", nofrag ? "true" : "false");
+		#endif /* DEBUG_SCTP */
+	}
+	#else /* SCTP_DISABLE_FRAGMENTS */
+	# error "TLS requires support of SCTP_DISABLE_FRAGMENTS"
+	#endif /* SCTP_DISABLE_FRAGMENTS */
+	
+	
+	/* Set the RETRANSMIT parameters */
+	#ifdef SCTP_RTOINFO
+	{
+		struct sctp_rtoinfo rtoinfo;
+		memset(&rtoinfo, 0, sizeof(rtoinfo));
+
+		#ifdef DEBUG_SCTP
+		sz = sizeof(rtoinfo);
+		/* Read socket defaults */
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz)  );
+		if (sz != sizeof(rtoinfo))
+		{
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(rtoinfo));
+			return ENOTSUP;
+		}
+		TRACE_DEBUG(FULL, "Def SCTP_RTOINFO srto_initial : %u", rtoinfo.srto_initial);
+		TRACE_DEBUG(FULL, "Def SCTP_RTOINFO srto_max     : %u", rtoinfo.srto_max);
+		TRACE_DEBUG(FULL, "Def SCTP_RTOINFO srto_min     : %u", rtoinfo.srto_min);
+		#endif /* DEBUG_SCTP */
+
+		rtoinfo.srto_max     = fd_g_config->cnf_timer_tw * 500 - 1000;	/* Maximum retransmit timer (in ms) (set to Tw / 2 - 1) */
+
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, sizeof(rtoinfo))  );
+		
+		#ifdef DEBUG_SCTP
+		/* Check new values */
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz)  );
+		TRACE_DEBUG(FULL, "New SCTP_RTOINFO srto_initial : %u", rtoinfo.srto_initial);
+		TRACE_DEBUG(FULL, "New SCTP_RTOINFO srto_max     : %u", rtoinfo.srto_max);
+		TRACE_DEBUG(FULL, "New SCTP_RTOINFO srto_min     : %u", rtoinfo.srto_min);
+		#endif /* DEBUG_SCTP */
+	}
+	#else /* SCTP_RTOINFO */
+	# ifdef DEBUG_SCTP
+	TRACE_DEBUG(FULL, "Skipping SCTP_RTOINFO");
+	# endif /* DEBUG_SCTP */
+	#endif /* SCTP_RTOINFO */
+	
+	/* Set the ASSOCIATION parameters */
+	#ifdef SCTP_ASSOCINFO
+	{
+		struct sctp_assocparams assoc;
+		memset(&assoc, 0, sizeof(assoc));
+
+		#ifdef DEBUG_SCTP
+		sz = sizeof(assoc);
+		/* Read socket defaults */
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz)  );
+		if (sz != sizeof(assoc))
+		{
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(assoc));
+			return ENOTSUP;
+		}
+		TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_asocmaxrxt               : %hu", assoc.sasoc_asocmaxrxt);
+		TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_number_peer_destinations : %hu", assoc.sasoc_number_peer_destinations);
+		TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_peer_rwnd                : %u" , assoc.sasoc_peer_rwnd);
+		TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_local_rwnd               : %u" , assoc.sasoc_local_rwnd);
+		TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_cookie_life              : %u" , assoc.sasoc_cookie_life);
+		#endif /* DEBUG_SCTP */
+
+		assoc.sasoc_asocmaxrxt = 5;	/* Maximum retransmission attempts: we want fast detection of errors */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, sizeof(assoc))  );
+		
+		#ifdef DEBUG_SCTP
+		/* Check new values */
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz)  );
+		TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_asocmaxrxt               : %hu", assoc.sasoc_asocmaxrxt);
+		TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_number_peer_destinations : %hu", assoc.sasoc_number_peer_destinations);
+		TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_peer_rwnd                : %u" , assoc.sasoc_peer_rwnd);
+		TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_local_rwnd               : %u" , assoc.sasoc_local_rwnd);
+		TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_cookie_life              : %u" , assoc.sasoc_cookie_life);
+		#endif /* DEBUG_SCTP */
+	}
+	#else /* SCTP_ASSOCINFO */
+	# ifdef DEBUG_SCTP
+	TRACE_DEBUG(FULL, "Skipping SCTP_ASSOCINFO");
+	# endif /* DEBUG_SCTP */
+	#endif /* SCTP_ASSOCINFO */
+	
+	
+	/* The SO_LINGER option will be re-set if we want to perform SCTP ABORT */
+	#ifdef SO_LINGER
+	{
+		struct linger linger;
+		memset(&linger, 0, sizeof(linger));
+		
+		#ifdef DEBUG_SCTP
+		sz = sizeof(linger);
+		/* Read socket defaults */
+		CHECK_SYS(  getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz)  );
+		if (sz != sizeof(linger))
+		{
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(linger));
+			return ENOTSUP;
+		}
+		TRACE_DEBUG(FULL, "Def SO_LINGER l_onoff  : %d", linger.l_onoff);
+		TRACE_DEBUG(FULL, "Def SO_LINGER l_linger : %d", linger.l_linger);
+		#endif /* DEBUG_SCTP */
+		
+		linger.l_onoff	= 0;	/* Do not activate the linger */
+		linger.l_linger = 0;	/* Return immediately when closing (=> abort) */
+		
+		/* Set the option */
+		CHECK_SYS(  setsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger))  );
+		
+		#ifdef DEBUG_SCTP
+		/* Check new values */
+		CHECK_SYS(  getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz)  );
+		TRACE_DEBUG(FULL, "New SO_LINGER l_onoff  : %d", linger.l_onoff);
+		TRACE_DEBUG(FULL, "New SO_LINGER l_linger : %d", linger.l_linger);
+		#endif /* DEBUG_SCTP */
+	}
+	#else /* SO_LINGER */
+	# ifdef DEBUG_SCTP
+	TRACE_DEBUG(FULL, "Skipping SO_LINGER");
+	# endif /* DEBUG_SCTP */
+	#endif /* SO_LINGER */
+	
+	/* Set the NODELAY option (Nagle-like algorithm) */
+	#ifdef SCTP_NODELAY
+	{
+		int nodelay;
+		
+		#ifdef DEBUG_SCTP
+		sz = sizeof(nodelay);
+		/* Read socket defaults */
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz)  );
+		if (sz != sizeof(nodelay))
+		{
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nodelay));
+			return ENOTSUP;
+		}
+		TRACE_DEBUG(FULL, "Def SCTP_NODELAY value : %s", nodelay ? "true" : "false");
+		#endif /* DEBUG_SCTP */
+
+		nodelay = 0;	/* We turn ON the Nagle algorithm (probably the default already) */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, sizeof(nodelay))  );
+		
+		#ifdef DEBUG_SCTP
+		/* Check new values */
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz)  );
+		TRACE_DEBUG(FULL, "New SCTP_NODELAY value : %s", nodelay ? "true" : "false");
+		#endif /* DEBUG_SCTP */
+	}
+	#else /* SCTP_NODELAY */
+	# ifdef DEBUG_SCTP
+	TRACE_DEBUG(FULL, "Skipping SCTP_NODELAY");
+	# endif /* DEBUG_SCTP */
+	#endif /* SCTP_NODELAY */
+	
+	/* Set the interleaving option */
+	#ifdef SCTP_FRAGMENT_INTERLEAVE
+	{
+		int interleave;
+		
+		#ifdef DEBUG_SCTP
+		sz = sizeof(interleave);
+		/* Read socket defaults */
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz)  );
+		if (sz != sizeof(interleave))
+		{
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(interleave));
+			return ENOTSUP;
+		}
+		TRACE_DEBUG(FULL, "Def SCTP_FRAGMENT_INTERLEAVE value : %d", interleave);
+		#endif /* DEBUG_SCTP */
+
+		#if 0
+		interleave = 2;	/* Allow partial delivery on several streams at the same time, since we are stream-aware in our security modules */
+		#else /* 0 */
+		interleave = 1;	/* hmmm actually, we are not yet capable of handling this, and we don t need it. */
+		#endif /* 0 */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, sizeof(interleave))  );
+		
+		#ifdef DEBUG_SCTP
+		/* Check new values */
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz)  );
+		TRACE_DEBUG(FULL, "New SCTP_FRAGMENT_INTERLEAVE value : %d", interleave);
+		#endif /* DEBUG_SCTP */
+	}
+	#else /* SCTP_FRAGMENT_INTERLEAVE */
+	# ifdef DEBUG_SCTP
+	TRACE_DEBUG(FULL, "Skipping SCTP_FRAGMENT_INTERLEAVE");
+	# endif /* DEBUG_SCTP */
+	#endif /* SCTP_FRAGMENT_INTERLEAVE */
+	
+	/* Set the v4 mapped addresses option */
+	#ifdef SCTP_I_WANT_MAPPED_V4_ADDR
+	{
+		int v4mapped;
+		
+		#ifdef DEBUG_SCTP
+		sz = sizeof(v4mapped);
+		/* Read socket defaults */
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz)  );
+		if (sz != sizeof(v4mapped))
+		{
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(v4mapped));
+			return ENOTSUP;
+		}
+		TRACE_DEBUG(FULL, "Def SCTP_I_WANT_MAPPED_V4_ADDR value : %s", v4mapped ? "true" : "false");
+		#endif /* DEBUG_SCTP */
+
+		v4mapped = 0;	/* We don't want v4 mapped addresses */
+		v4mapped = 1;	/* but we have to, otherwise the bind fails in linux currently ... (Ok, It'd be better with a cmake test, any volunteer?) */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, sizeof(v4mapped))  );
+		
+		#ifdef DEBUG_SCTP
+		/* Check new values */
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz)  );
+		TRACE_DEBUG(FULL, "New SCTP_I_WANT_MAPPED_V4_ADDR value : %s", v4mapped ? "true" : "false");
+		#endif /* DEBUG_SCTP */
+	}
+	#else /* SCTP_I_WANT_MAPPED_V4_ADDR */
+	# ifdef DEBUG_SCTP
+	TRACE_DEBUG(FULL, "Skipping SCTP_I_WANT_MAPPED_V4_ADDR");
+	# endif /* DEBUG_SCTP */
+	#endif /* SCTP_I_WANT_MAPPED_V4_ADDR */
+			   
+			   
+	/* Other settable options (draft-ietf-tsvwg-sctpsocket-17):
+	   SO_RCVBUF			size of receiver window
+	   SO_SNDBUF			size of pending data to send
+	   SCTP_AUTOCLOSE		for one-to-many only
+	   SCTP_SET_PEER_PRIMARY_ADDR	ask remote peer to use this local address as primary
+	   SCTP_PRIMARY_ADDR		use this address as primary locally
+	   SCTP_ADAPTATION_LAYER	set adaptation layer indication 
+	   SCTP_PEER_ADDR_PARAMS	control heartbeat per peer address
+	   SCTP_DEFAULT_SEND_PARAM	parameters for the sendto() call
+	   SCTP_MAXSEG			max size of fragmented segments -- bound to PMTU
+	   SCTP_AUTH_CHUNK		request authentication of some type of chunk
+	    SCTP_HMAC_IDENT		authentication algorithms
+	    SCTP_AUTH_KEY		set a shared key
+	    SCTP_AUTH_ACTIVE_KEY	set the active key
+	    SCTP_AUTH_DELETE_KEY	remove a key
+	    SCTP_AUTH_DEACTIVATE_KEY	will not use that key anymore
+	   SCTP_DELAYED_SACK		control delayed acks
+	   SCTP_PARTIAL_DELIVERY_POINT	control partial delivery size
+	   SCTP_USE_EXT_RCVINFO		use extended receive info structure (information about the next message if available)
+	   SCTP_MAX_BURST		number of packets that can be burst emitted
+	   SCTP_CONTEXT			save a context information along with the association.
+	   SCTP_EXPLICIT_EOR		enable sending one message across several send calls
+	   SCTP_REUSE_PORT		share one listening port with several sockets
+	   
+	   read-only options:
+	   SCTP_STATUS			retrieve info such as number of streams, pending packets, state, ...
+	   SCTP_GET_PEER_ADDR_INFO	get information about a specific peer address of the association.
+	   SCTP_PEER_AUTH_CHUNKS	list of chunks the remote peer wants authenticated
+	   SCTP_LOCAL_AUTH_CHUNKS	list of chunks the local peer wants authenticated
+	   SCTP_GET_ASSOC_NUMBER	number of associations in a one-to-many socket
+	   SCTP_GET_ASSOC_ID_LIST	list of these associations
+	*/
+	
+	/* In case of no_ip4, force the v6only option */
+	#ifdef IPV6_V6ONLY
+	if (fd_g_config->cnf_flags.no_ip4) {
+		int opt = 1;
+		CHECK_SYS(setsockopt(sk, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)));
+	}
+	#endif /* IPV6_V6ONLY */
+	
+	return 0;
+}
+
+
+/* Post-binding socket options */
+static int fd_setsockopt_postbind(int sk, int bound_to_default)
+{
+	TRACE_ENTRY( "%d %d", sk, bound_to_default);
+	
+	CHECK_PARAMS( (sk > 0) );
+	
+	/* Set the ASCONF option */
+	#ifdef SCTP_AUTO_ASCONF
+	{
+		int asconf;
+		
+		#ifdef DEBUG_SCTP
+		socklen_t sz;
+		
+		sz = sizeof(asconf);
+		/* Read socket defaults */
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz)  );
+		if (sz != sizeof(asconf))
+		{
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(asconf));
+			return ENOTSUP;
+		}
+		TRACE_DEBUG(FULL, "Def SCTP_AUTO_ASCONF value : %s", asconf ? "true" : "false");
+		#endif /* DEBUG_SCTP */
+
+		asconf = bound_to_default ? 1 : 0;	/* allow automatic use of added or removed addresses in the association (for bound-all sockets) */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, sizeof(asconf))  );
+		
+		#ifdef DEBUG_SCTP
+		/* Check new values */
+		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz)  );
+		TRACE_DEBUG(FULL, "New SCTP_AUTO_ASCONF value : %s", asconf ? "true" : "false");
+		#endif /* DEBUG_SCTP */
+	}
+	#else /* SCTP_AUTO_ASCONF */
+	# ifdef DEBUG_SCTP
+	TRACE_DEBUG(FULL, "Skipping SCTP_AUTO_ASCONF");
+	# endif /* DEBUG_SCTP */
+	#endif /* SCTP_AUTO_ASCONF */
+	
+	return 0;
+}
 
 /* Create a socket server and bind it according to daemon s configuration */
 int fd_sctp_create_bind_server( int * sock, struct fd_list * list, uint16_t port )
 {
-	TODO("Create sctp server, using fd_g_config: cnf_endpoints, no_ip4, no_ip6, cnf_sctp_str");
+	int family;
+	int bind_default;
+	
+	TRACE_ENTRY("%p %p %hu", sock, list, port);
+	CHECK_PARAMS(sock);
+	
+	if (fd_g_config->cnf_flags.no_ip6) {
+		family = AF_INET;
+	} else {
+		family = AF_INET6; /* can create socket for both IP and IPv6 */
+	}
+	
+	/* Create the socket */
+	CHECK_SYS( *sock = socket(family, SOCK_STREAM, IPPROTO_SCTP) );
+	
+	/* Set pre-binding socket options, including number of streams etc... */
+	CHECK_FCT( fd_setsockopt_prebind(*sock) );
 	
-	return ENOTSUP;
+	bind_default = (! list) || (FD_IS_LIST_EMPTY(list)) ;
+redo:
+	if ( bind_default ) {
+		/* Implicit endpoints : bind to default addresses */
+		union {
+			sSS  ss;
+			sSA  sa;
+			sSA4 sin;
+			sSA6 sin6;
+		} s;
+		
+		/* 0.0.0.0 and [::] are all zeros */
+		memset(&s, 0, sizeof(s));
+		
+		s.sa.sa_family = family;
+		
+		if (family == AF_INET)
+			s.sin.sin_port = htons(port);
+		else
+			s.sin6.sin6_port = htons(port);
+		
+		CHECK_SYS( bind(*sock, &s.sa, sizeof(s)) );
+		
+	} else {
+		/* Explicit endpoints to bind to from config */
+		union {
+			sSA  	*sa;
+			sSA4 	*sin;
+			sSA6 	*sin6;
+		} sar; /* build the list of endpoints to bind to */
+		int count = 0; /* number of sock addr in sar array */
+		struct fd_list * li;
+		
+		sar.sa = NULL;
+		
+		/* Create a flat array from the list of configured addresses */
+		for (li = list->next; li != list; li = li->next) {
+			struct fd_endpoint * ep = (struct fd_endpoint *)li;
+			
+			if ( ! ep->meta.conf )
+				continue;
+			
+			count++;
+			if (fd_g_config->cnf_flags.no_ip6) {
+				ASSERT(ep->sa.sa_family == AF_INET);
+				CHECK_MALLOC( sar.sa = realloc(sar.sa, count * sizeof(sSA4))  );
+				memcpy(&sar.sin[count - 1], &ep->sin, sizeof(sSA4));
+				sar.sin[count - 1].sin_port = htons(port);
+			} else {
+				/* Pass all addresses as IPv6, eventually mapped -- we already filtered out IPv4 addresses if no_ip4 flag is set */
+				CHECK_MALLOC( sar.sa = realloc(sar.sa, count * sizeof(sSA6))  );
+				if (ep->sa.sa_family == AF_INET) {
+					memset(&sar.sin6[count - 1], 0, sizeof(sSA6));
+					sar.sin6[count - 1].sin6_family = AF_INET6;
+					IN6_ADDR_V4MAP( &sar.sin6[count - 1].sin6_addr.s6_addr, ep->sin.sin_addr.s_addr );
+				} else {
+					memcpy(&sar.sin6[count - 1], &ep->sin6, sizeof(sSA6));
+				}
+				sar.sin6[count - 1].sin6_port = htons(port);
+			}
+		}
+		
+		if (!count) {
+			/* None of the addresses in the list came from configuration, we bind to default */
+			bind_default = 1;
+			goto redo;
+		}
+		
+		# ifdef DEBUG_SCTP
+		if (TRACE_BOOL(FULL)) {
+			int i;
+			fd_log_debug("Calling sctp_bindx with the following array:\n");
+			for (i = 0; i < count; i++) {
+				fd_log_debug("    - ");
+				sSA_DUMP_NODE_SERV( (sar.sa[0].sa_family == AF_INET) ? (sSA *)(&sar.sin[i]) : (sSA *)(&sar.sin6[i]), NI_NUMERICHOST | NI_NUMERICSERV );
+				fd_log_debug("\n");
+			}
+		}
+		#endif /* DEBUG_SCTP */
+		
+		CHECK_SYS(  sctp_bindx(*sock, sar.sa, count, SCTP_BINDX_ADD_ADDR)  );
+		
+	}
+	
+	/* Now, the server is bound, set remaining sockopt */
+	CHECK_FCT( fd_setsockopt_postbind(*sock, bind_default) );
+	
+	#ifdef DEBUG_SCTP
+	/* Debug: show all local listening addresses */
+	if (TRACE_BOOL(FULL)) {
+		sSA *sa, *sar;
+		int sz;
+		
+		CHECK_SYS(  sz = sctp_getladdrs(*sock, 0, &sar)  );
+		
+		fd_log_debug("SCTP server bound on :\n");
+		for (sa = sar; sz-- > 0; sa = (sSA *)(((uint8_t *)sa) + ((sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6)))) {
+			fd_log_debug("    - ");
+			sSA_DUMP_NODE_SERV( sa, NI_NUMERICHOST | NI_NUMERICSERV );
+			fd_log_debug("\n");
+		}
+		sctp_freeladdrs(sar);
+	}
+	#endif /* DEBUG_SCTP */
+
+	return 0;
 }
 
 /* Allow clients connections on server sockets */
@@ -59,3 +624,45 @@
 	return ENOTSUP;
 }
 
+/* Get the list of local endpoints of the socket */
+int fd_sctp_get_local_ep(int sock, struct fd_list * list)
+{
+	union {
+		sSA	*sa;
+		sSA4	*sin;
+		sSA6	*sin6;
+		uint8_t	*buf;
+	} ptr;
+	
+	sSA * data;
+	int count;
+	
+	TRACE_ENTRY("%d %p", sock, list);
+	CHECK_PARAMS(list);
+	
+	/* Read the list on the socket */
+	CHECK_SYS( count = sctp_getladdrs(sock, 0, &data)  );
+	ptr.sa = data;
+	
+	while (count) {
+		TODO("get the data from ptr");
+		TODO("Increment ptr to the next sa in data");
+		
+		count --;
+	}
+	
+	/* And now, free the list and return */
+	sctp_freeladdrs(data);
+	
+	return ENOTSUP;
+}
+
+/* Get the list of remote endpoints of the socket */
+int fd_sctp_get_remote_ep(int sock, struct fd_list * list)
+{
+	TODO("SCTP: sctp_getpaddrs");
+		
+	
+	return ENOTSUP;
+}
+
--- a/freeDiameter/tcp.c	Mon Oct 19 18:43:09 2009 +0900
+++ b/freeDiameter/tcp.c	Tue Oct 20 17:30:20 2009 +0900
@@ -36,6 +36,7 @@
 #include "fD.h"
 #include <netinet/tcp.h>
 #include <netinet/ip6.h>
+#include <sys/socket.h>
 
 /* Set the socket options for TCP sockets, before bind is called */
 static int fd_tcp_setsockopt(int family, int sk)
@@ -110,3 +111,27 @@
 	CHECK_SYS( listen(sock, 5) );
 	return 0;
 }
+
+/* Get the local name of a TCP socket -- would be nice if it did not return "0.0.0.0"... */
+int fd_tcp_get_local_ep(int sock, sSS * ss, socklen_t *sl)
+{
+	TRACE_ENTRY("%d %p %p", sock, ss, sl);
+	CHECK_PARAMS( ss && sl );
+	
+	*sl = sizeof(sSS);
+	CHECK_SYS(getsockname(sock, (sSA *)ss, sl));
+	
+	return 0;
+}
+
+/* Get the remote name of a TCP socket */
+int fd_tcp_get_remote_ep(int sock, sSS * ss, socklen_t *sl)
+{
+	TRACE_ENTRY("%d %p %p", sock, ss, sl);
+	CHECK_PARAMS( ss && sl );
+	
+	*sl = sizeof(sSS);
+	CHECK_SYS(getpeername(sock, (sSA *)ss, sl));
+	
+	return 0;
+}
--- a/include/freeDiameter/CMakeLists.txt	Mon Oct 19 18:43:09 2009 +0900
+++ b/include/freeDiameter/CMakeLists.txt	Tue Oct 20 17:30:20 2009 +0900
@@ -9,7 +9,10 @@
 OPTION(DEFAULT_CONF_FILE "Default path to configuration file?" OFF)
 
 # Disable SCTP support completly ?
-OPTION(DISABLE_SCTP "Disable SCTP support?")
+OPTION(DISABLE_SCTP "Disable SCTP support?" OFF)
+IF (NOT DISABLE_SCTP)
+	OPTION(DEBUG_SCTP "Verbose SCTP (for debug)?" OFF)
+ENDIF (NOT DISABLE_SCTP)
 
 # Find TODO items in the code easily ?
 OPTION(ERRORS_ON_TODO "(development) Generate compilation errors on TODO items ?" OFF)
--- a/include/freeDiameter/freeDiameter-host.h.in	Mon Oct 19 18:43:09 2009 +0900
+++ b/include/freeDiameter/freeDiameter-host.h.in	Tue Oct 20 17:30:20 2009 +0900
@@ -44,6 +44,7 @@
 #cmakedefine HOST_BIG_ENDIAN @HOST_BIG_ENDIAN@
 
 #cmakedefine DISABLE_SCTP
+#cmakedefine DEBUG_SCTP
 #cmakedefine ERRORS_ON_TODO
 #cmakedefine DEBUG
 
--- a/include/freeDiameter/freeDiameter.h	Mon Oct 19 18:43:09 2009 +0900
+++ b/include/freeDiameter/freeDiameter.h	Tue Oct 20 17:30:20 2009 +0900
@@ -117,7 +117,12 @@
 /* Endpoints */
 struct fd_endpoint {
 	struct fd_list  chain;	/* link in cnf_endpoints list */
-	sSS		ss;	/* the socket information. List is always ordered by ss value (memcmp) */
+	union {
+		sSS		ss;	/* the socket information. List is always ordered by ss value (memcmp) */
+		sSA4		sin;
+		sSA6		sin6;
+		sSA		sa;
+	};
 	struct {
 		unsigned conf : 1; /* This endpoint is statically configured in a configuration file */
 		unsigned disc : 1; /* This endpoint was resolved from the Diameter Identity or other DNS query */
@@ -129,6 +134,9 @@
 	}		meta;	/* Additional information about the endpoint */
 };
 
+/* Add a new entry in a list of endpoints -- merge if the sockaddr was already there */
+int fd_ep_add_merge( struct fd_list * list, sSA * sa, socklen_t sl, int conf, int disc, int adv, int ll );
+
 /* Applications */
 struct fd_app {
 	struct fd_list	 chain;	/* link in cnf_apps list. List ordered by appid. */
--- a/include/freeDiameter/libfreeDiameter.h	Mon Oct 19 18:43:09 2009 +0900
+++ b/include/freeDiameter/libfreeDiameter.h	Tue Oct 20 17:30:20 2009 +0900
@@ -328,14 +328,55 @@
 			0,					\
 			flag);					\
 	  if (__rc)						\
-	  	fd_log_debug((char *)gai_strerror(__rc));	\
+	  	fd_log_debug("%s", (char *)gai_strerror(__rc));	\
 	  else							\
-	  	fd_log_debug(&__addrbuf[0]);			\
+	  	fd_log_debug("%s", &__addrbuf[0]);		\
 	} else {						\
 		fd_log_debug("(NULL / ANY)");			\
 	}							\
 }
-/* if needed, add sSA_DUMP_SERVICE */
+/* Same, for a service */
+#define sSA_DUMP_SERV( sa, flag ) {				\
+	sSA * __sa = (sSA *)(sa);				\
+	char __servbuf[32];					\
+	if (__sa) {						\
+	  int __rc = getnameinfo(__sa, 				\
+	  		sizeof(sSS),				\
+			NULL,					\
+			0,					\
+			__servbuf,				\
+			sizeof(__servbuf),			\
+			flag);					\
+	  if (__rc)						\
+	  	fd_log_debug("%s", (char *)gai_strerror(__rc));	\
+	  else							\
+	  	fd_log_debug("%s", &__servbuf[0]);		\
+	} else {						\
+		fd_log_debug("(unknown)");			\
+	}							\
+}
+/* Combine both */
+#define sSA_DUMP_NODE_SERV( sa, flag ) {				\
+	sSA * __sa = (sSA *)(sa);					\
+	char __addrbuf[INET6_ADDRSTRLEN];				\
+	char __servbuf[32];						\
+	if (__sa) {							\
+	  int __rc = getnameinfo(__sa, 					\
+	  		sizeof(sSS),					\
+			__addrbuf,					\
+			sizeof(__addrbuf),				\
+			__servbuf,					\
+			sizeof(__servbuf),				\
+			flag);						\
+	  if (__rc)							\
+	  	fd_log_debug("%s", (char *)gai_strerror(__rc));		\
+	  else								\
+	  	fd_log_debug("[%s]:%s", &__addrbuf[0],&__servbuf[0]);	\
+	} else {							\
+		fd_log_debug("(NULL / ANY)");				\
+	}								\
+}
+
 
 /* A l4 protocol name (TCP / SCTP) */
 #define IPPROTO_NAME( _proto )					\
"Welcome to our mercurial repository"