Index: libfdcore/p_ce.c
===================================================================
--- libfdcore/p_ce.c	(revision 688)
+++ libfdcore/p_ce.c	(revision 706)
@@ -38,11 +38,4 @@
 /* This file contains code to handle Capabilities Exchange messages (CER and CEA) and election process */
 
-/* Compilation option:
- USE_CEA_BROADCAST
- 	Define this to enable sending multiple copies of the CEA in case of SCTP connection.
-	This avoids a race condition when sending an application message over a different stream
-	than the CEA, it might be delivered first and thus ignored.
-*/
-
 /* Save a connection as peer's principal */
 static int set_peer_cnx(struct fd_peer * peer, struct cnxctx **cnx)
@@ -88,5 +81,5 @@
 }
 
-/* Election: compare the Diameter Ids, return true if the election is won */
+/* Election: compare the Diameter Ids by lexical order, return true if the election is won */
 static __inline__ int election_result(struct fd_peer * peer)
 {
@@ -245,13 +238,7 @@
 static void cleanup_remote_CE_info(struct fd_peer * peer)
 {
+	/* free linked information */
 	free(peer->p_hdr.info.runtime.pir_realm);
-	peer->p_hdr.info.runtime.pir_realm = NULL;
-	peer->p_hdr.info.runtime.pir_vendorid = 0;
-	peer->p_hdr.info.runtime.pir_orstate = 0;
 	free(peer->p_hdr.info.runtime.pir_prodname);
-	peer->p_hdr.info.runtime.pir_prodname = NULL;
-	peer->p_hdr.info.runtime.pir_firmrev = 0;
-	peer->p_hdr.info.runtime.pir_relay = 0;
-	peer->p_hdr.info.runtime.pir_isi = 0;
 	while (!FD_IS_LIST_EMPTY(&peer->p_hdr.info.runtime.pir_apps)) {
 		struct fd_list * li = peer->p_hdr.info.runtime.pir_apps.next;
@@ -259,10 +246,18 @@
 		free(li);
 	}
-	
-	fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_ADV /* Remove previously advertised endpoints */ );
+	/* note: pir_cert_list needs not be freed (belongs to gnutls) */
+	
+	/* cleanup the area */
+	memset(&peer->p_hdr.info.runtime, 0, sizeof(peer->p_hdr.info.runtime));
+	
+	/* reinit the list */
+	fd_list_init(&peer->p_hdr.info.runtime.pir_apps, peer);
+
+	/* Remove previously advertised endpoints */
+	fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_ADV );
 }
 
 /* Extract information sent by the remote peer and save it in our peer structure */
-static int save_remote_CE_info(struct msg * msg, struct fd_peer * peer, char ** error_code, uint32_t *rc)
+static int save_remote_CE_info(struct msg * msg, struct fd_peer * peer, struct fd_pei * error, uint32_t *rc)
 {
 	struct avp * avp = NULL;
@@ -309,9 +304,11 @@
 				
 				/* We check that the value matches what we know, otherwise disconnect the peer */
-				/* here also, using strcasecmp on (supposed) UTF8 data might be bad idea... to be improved */
-				if (strncasecmp((char *)hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid, hdr->avp_value->os.len)) {
+				if (fd_os_almostcasecmp(hdr->avp_value->os.data, hdr->avp_value->os.len, 
+							peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen)) {
 					TRACE_DEBUG(INFO, "Received a message with Origin-Host set to '%.*s' while expecting '%s'\n", 
 							hdr->avp_value->os.len, hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid);
-					*error_code = "DIAMETER_UNKNOWN_PEER";
+					error->pei_errcode = "ER_DIAMETER_AVP_NOT_ALLOWED";
+					error->pei_message = "Your Origin-Host value does not match my configuration.";
+					error->pei_avp = avp;
 					return EINVAL;
 				}
@@ -330,11 +327,22 @@
 				/* In case of multiple AVPs */
 				if (peer->p_hdr.info.runtime.pir_realm) {
-					TRACE_DEBUG(INFO, "Ignored multiple instances of the Origin-Realm AVP");
-					goto next;
-				}
-				
-				/* Save the value -- we don't change the case to avoid risking breaking UTF-8 with poor tolower() impls. */
-				CHECK_MALLOC(  peer->p_hdr.info.runtime.pir_realm = calloc( hdr->avp_value->os.len + 1, 1 )  );
-				memcpy(peer->p_hdr.info.runtime.pir_realm, hdr->avp_value->os.data, hdr->avp_value->os.len);
+					TRACE_DEBUG(INFO, "Multiple instances of the Origin-Realm AVP");
+					error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
+					error->pei_message = "I found several Origin-Realm AVPs";
+					error->pei_avp = avp;
+					return EINVAL;
+				}
+				
+				/* If the octet string contains a \0 */
+				if (!fd_os_is_valid_DiameterIdentity(hdr->avp_value->os.data, hdr->avp_value->os.len)) {
+					error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE";
+					error->pei_message = "Your Origin-Realm contains invalid characters.";
+					error->pei_avp = avp;
+					return EINVAL;
+				}
+				
+				/* Save the value */
+				CHECK_MALLOC(  peer->p_hdr.info.runtime.pir_realm = os0dup( hdr->avp_value->os.data, hdr->avp_value->os.len )  );
+				peer->p_hdr.info.runtime.pir_realmlen = hdr->avp_value->os.len;
 				break;
 
@@ -352,5 +360,11 @@
 					/* Get the sockaddr value */
 					memset(&ss, 0, sizeof(ss));
-					CHECK_FCT( fd_msg_avp_value_interpret( avp, &ss) );
+					CHECK_FCT_DO( fd_msg_avp_value_interpret( avp, &ss),
+						{
+							/* in case of error, assume the AVP value was wrong */
+							error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE";
+							error->pei_avp = avp;
+							return EINVAL;
+						} );
 
 					/* Save this endpoint in the list as advertized */
@@ -370,6 +384,9 @@
 				/* In case of multiple AVPs */
 				if (peer->p_hdr.info.runtime.pir_vendorid) {
-					TRACE_DEBUG(INFO, "Ignored multiple instances of the Vendor-Id AVP");
-					goto next;
+					TRACE_DEBUG(INFO, "Multiple instances of the Vendor-Id AVP");
+					error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
+					error->pei_message = "I found several Vendor-Id AVPs";
+					error->pei_avp = avp;
+					return EINVAL;
 				}
 				
@@ -388,6 +405,9 @@
 				/* In case of multiple AVPs */
 				if (peer->p_hdr.info.runtime.pir_prodname) {
-					TRACE_DEBUG(INFO, "Ignored multiple instances of the Product-Name AVP");
-					goto next;
+					TRACE_DEBUG(INFO, "Multiple instances of the Product-Name AVP");
+					error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
+					error->pei_message = "I found several Product-Name AVPs";
+					error->pei_avp = avp;
+					return EINVAL;
 				}
 
@@ -407,6 +427,9 @@
 				/* In case of multiple AVPs */
 				if (peer->p_hdr.info.runtime.pir_orstate) {
-					TRACE_DEBUG(INFO, "Ignored multiple instances of the Origin-State-Id AVP");
-					goto next;
+					TRACE_DEBUG(INFO, "Multiple instances of the Origin-State-Id AVP");
+					error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
+					error->pei_message = "I found several Origin-State-Id AVPs";
+					error->pei_avp = avp;
+					return EINVAL;
 				}
 				
@@ -423,5 +446,8 @@
 				}
 				
-				TRACE_DEBUG(FULL, "'%s' supports a subset of vendor %d features.", peer->p_hdr.info.pi_diamid, hdr->avp_value->u32);
+				TRACE_DEBUG(FULL, "'%s' claims support for a subset of vendor %d features.", peer->p_hdr.info.pi_diamid, hdr->avp_value->u32);
+				/* not that it makes a difference for us... 
+				 -- if an application actually needs this info, we could save it somewhere.
+				*/
 				break;
 
@@ -477,4 +503,7 @@
 						TRACE_DEBUG(FULL, "Invalid Vendor-Specific-Application-Id AVP received, ignored");
 						fd_msg_dump_one(FULL, avp);
+						error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE";
+						error->pei_avp = avp;
+						return EINVAL;
 					} else {
 						/* Add an entry in the list */
@@ -510,7 +539,7 @@
 				
 				if (hdr->avp_value->u32 == AI_RELAY) {
+					/* Not clear if the relay application can be inside this AVP... */
 					peer->p_hdr.info.runtime.pir_relay = 1;
 				} else {
-					/* Not clear if the relay application can be inside this AVP... */
 					CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, hdr->avp_value->u32, 0, 0, 1) );
 				}
@@ -537,5 +566,10 @@
 					goto next;
 				}
-				ASSERT( hdr->avp_value->u32 < 32 ); /* if false, we have to change the code bellow */
+				if (hdr->avp_value->u32 >= 32 ) {
+					error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE";
+					error->pei_message = "I don't support this Inband-Security-Id value (yet).";
+					error->pei_avp = avp;
+					return EINVAL;
+				}
 				peer->p_hdr.info.runtime.pir_isi |= (1 << hdr->avp_value->u32);
 				break;
@@ -559,7 +593,7 @@
 	CHECK_FCT( fd_msg_new ( fd_dict_cmd_CER, MSGFL_ALLOC_ETEID, cer ) );
 	
-	/* Do we need Inband-Security-Id AVPs ? */
+	/* Do we need Inband-Security-Id AVPs ? If we're already using TLS, we don't... */
 	if (!fd_cnx_getTLS(cnx)) {
-		isi_none = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE; /* we add it event if the peer does not use the old mechanism */
+		isi_none = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE; /* we add it even if the peer does not use the old mechanism, it is impossible to distinguish */
 		isi_tls  = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD;
 	}
@@ -587,14 +621,15 @@
 
 /* Reject an incoming connection attempt */
-static void receiver_reject(struct cnxctx * recv_cnx, struct msg ** cer, char * rescode, char * errormsg)
+static void receiver_reject(struct cnxctx ** recv_cnx, struct msg ** cer, struct fd_pei * error)
 {
 	/* Create and send the CEA with appropriate error code */
 	CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ), goto destroy );
-	CHECK_FCT_DO( fd_msg_rescode_set(*cer, rescode, errormsg, NULL, 1 ), goto destroy );
-	CHECK_FCT_DO( fd_out_send(cer, recv_cnx, NULL, FD_CNX_ORDERED), goto destroy );
+	CHECK_FCT_DO( fd_msg_rescode_set(*cer, error->pei_errcode, error->pei_message, error->pei_avp, 1 ), goto destroy );
+	CHECK_FCT_DO( fd_out_send(cer, *recv_cnx, NULL, FD_CNX_ORDERED), goto destroy );
 	
 	/* And now destroy this connection */
 destroy:
-	fd_cnx_destroy(recv_cnx);
+	fd_cnx_destroy(*recv_cnx);
+	*recv_cnx = NULL;
 	if (*cer) {
 		fd_msg_log(FD_MSG_LOG_DROPPED, *cer, "An error occurred while rejecting a CER.");
@@ -614,6 +649,5 @@
 	
 	/* Are we doing an election ? */
-	fd_cpu_flush_cache();
-	if (peer->p_hdr.info.runtime.pir_state == STATE_WAITCNXACK_ELEC) {
+	if (fd_peer_getstate(peer) == STATE_WAITCNXACK_ELEC) {
 		if (election_result(peer)) {
 			/* Close initiator connection */
@@ -624,8 +658,10 @@
 
 		} else {
+			struct fd_pei pei;
+			memset(&pei, 0, sizeof(pei));
+			pei.pei_errcode = "ELECTION_LOST";
 
 			/* Answer an ELECTION LOST to the receiver side */
-			receiver_reject(peer->p_receiver, &peer->p_cer, "ELECTION_LOST", NULL);
-			peer->p_receiver = NULL;
+			receiver_reject(&peer->p_receiver, &peer->p_cer, &pei);
 			CHECK_FCT( to_waitcea(peer, initiator) );
 		}
@@ -641,6 +677,8 @@
 int fd_p_ce_msgrcv(struct msg ** msg, int req, struct fd_peer * peer)
 {
-	char * ec;
 	uint32_t rc = 0;
+	int st;
+	struct fd_pei pei;
+	
 	TRACE_ENTRY("%p %p", msg, peer);
 	CHECK_PARAMS( msg && *msg && CHECK_PEER(peer) );
@@ -656,5 +694,5 @@
 		
 		/* Set the error code */
-		CHECK_FCT( fd_msg_rescode_set(*msg, "DIAMETER_COMMAND_UNSUPPORTED", "No CER allowed in current state", NULL, 1 ) );
+		CHECK_FCT( fd_msg_rescode_set(*msg, "ER_DIAMETER_UNABLE_TO_COMPLY", "No CER allowed in current state", NULL, 1 ) );
 
 		/* msg now contains an answer message to send back */
@@ -663,8 +701,7 @@
 	
 	/* If the state is not WAITCEA, just discard the message */
-	fd_cpu_flush_cache();
-	if (req || (peer->p_hdr.info.runtime.pir_state != STATE_WAITCEA)) {
+	if (req || ((st = fd_peer_getstate(peer)) != STATE_WAITCEA)) {
 		if (*msg) {
-			fd_msg_log( FD_MSG_LOG_DROPPED, *msg, "Received CER/CEA while in '%s' state.\n", STATE_STR(peer->p_hdr.info.runtime.pir_state));
+			fd_msg_log( FD_MSG_LOG_DROPPED, *msg, "Received CER/CEA while in '%s' state.\n", STATE_STR(st));
 			CHECK_FCT_DO( fd_msg_free(*msg), /* continue */);
 			*msg = NULL;
@@ -674,6 +711,8 @@
 	}
 	
+	memset(&pei, 0, sizeof(pei));
+	
 	/* Save info from the CEA into the peer */
-	CHECK_FCT_DO( save_remote_CE_info(*msg, peer, &ec, &rc), goto cleanup );
+	CHECK_FCT_DO( save_remote_CE_info(*msg, peer, &pei, &rc), goto cleanup );
 	
 	/* Dispose of the message, we don't need it anymore */
@@ -701,5 +740,5 @@
 		default:
 			/* In any other case, we abort all attempts to connect to this peer */
-			TRACE_DEBUG(INFO, "Peer %s replied a CEA with Result-Code AVP %d, aborting connection attempts.", peer->p_hdr.info.pi_diamid, rc);
+			TRACE_DEBUG(INFO, "Peer %s replied a CEA with Result-Code %d, aborting connection attempts.", peer->p_hdr.info.pi_diamid, rc);
 			return EINVAL;
 	}
@@ -751,11 +790,12 @@
 }
 
-/* Handle the receiver side to go to OPEN state (any election is resolved) */
+/* Handle the receiver side to go to OPEN or OPEN_NEW state (any election is resolved) */
 int fd_p_ce_process_receiver(struct fd_peer * peer)
 {
-	char * ec = NULL;
+	struct fd_pei pei;
 	struct msg * msg = NULL;
 	int isi = 0;
 	int fatal = 0;
+	int tls_sync=0;
 	
 	TRACE_ENTRY("%p", peer);
@@ -765,6 +805,18 @@
 	peer->p_cer = NULL;
 	
+	memset(&pei, 0, sizeof(pei));
+	
 	/* Parse the content of the received CER */
-	CHECK_FCT_DO( save_remote_CE_info(msg, peer, &ec, NULL), goto error_abort );
+	CHECK_FCT_DO( save_remote_CE_info(msg, peer, &pei, NULL), goto error_abort );
+	
+	/* Validate the realm if needed */
+	if (peer->p_hdr.info.config.pic_realm) {
+		size_t len = strlen(peer->p_hdr.info.config.pic_realm);
+		if (fd_os_almostcasecmp(peer->p_hdr.info.config.pic_realm, len, peer->p_hdr.info.runtime.pir_realm, peer->p_hdr.info.runtime.pir_realmlen)) {
+			TRACE_DEBUG(INFO, "Rejected CER from peer '%s', realm mismatch with configured value (returning DIAMETER_UNKNOWN_PEER).\n", peer->p_hdr.info.pi_diamid);
+			pei.pei_errcode = "DIAMETER_UNKNOWN_PEER"; /* maybe AVP_NOT_ALLOWED would be better fit? */
+			goto error_abort;
+		}
+	}
 	
 	/* Validate the peer if needed */
@@ -773,5 +825,5 @@
 		if (res < 0) {
 			TRACE_DEBUG(INFO, "Rejected CER from peer '%s', validation failed (returning DIAMETER_UNKNOWN_PEER).\n", peer->p_hdr.info.pi_diamid);
-			ec = "DIAMETER_UNKNOWN_PEER";
+			pei.pei_errcode = "DIAMETER_UNKNOWN_PEER";
 			goto error_abort;
 		}
@@ -785,5 +837,5 @@
 		if (!got_common) {
 			TRACE_DEBUG(INFO, "No common application with peer '%s', sending DIAMETER_NO_COMMON_APPLICATION", peer->p_hdr.info.pi_diamid);
-			ec = "DIAMETER_NO_COMMON_APPLICATION";
+			pei.pei_errcode = "DIAMETER_NO_COMMON_APPLICATION";
 			fatal = 1;
 			goto error_abort;
@@ -835,5 +887,5 @@
 		if (!isi) {
 			TRACE_DEBUG(INFO, "No common security mechanism with '%s', sending DIAMETER_NO_COMMON_SECURITY", peer->p_hdr.info.pi_diamid);
-			ec = "DIAMETER_NO_COMMON_SECURITY";
+			pei.pei_errcode = "DIAMETER_NO_COMMON_SECURITY";
 			fatal = 1;
 			goto error_abort;
@@ -849,9 +901,5 @@
 	CHECK_FCT( fd_msg_rescode_set(msg, "DIAMETER_SUCCESS", NULL, NULL, 0 ) );
 	CHECK_FCT( add_CE_info(msg, peer->p_cnxctx, isi & PI_SEC_TLS_OLD, isi & PI_SEC_NONE) );
-#ifdef USE_CEA_BROADCAST
-	CHECK_FCT( fd_out_send(&msg, peer->p_cnxctx, peer, (isi & PI_SEC_TLS_OLD) ? FD_CNX_ORDERED : FD_CNX_BROADCAST) ); /* Broadcast in order to avoid further messages sent over a different stream be delivered first... */
-#else /* USE_CEA_BROADCAST */
 	CHECK_FCT( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED ) );
-#endif /* USE_CEA_BROADCAST */
 	
 	/* Handshake if needed */
@@ -878,5 +926,5 @@
 				}  );
 		}
-		
+		tls_sync = 1;
 	} else {
 		if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) {
@@ -891,6 +939,13 @@
 		CHECK_FCT( fd_p_dw_reopen(peer) );
 	} else {
-		fd_psm_change_state(peer, STATE_OPEN );
-		fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
+		if ((!tls_sync) && (fd_cnx_isMultichan(peer->p_cnxctx))) {
+			fd_psm_change_state(peer, STATE_OPEN_NEW );
+			/* send DWR */
+			CHECK_FCT( fd_p_dw_timeout(peer) );
+		} else {
+
+			fd_psm_change_state(peer, STATE_OPEN );
+			fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
+		}
 	}
 	
@@ -898,13 +953,7 @@
 
 error_abort:
-	if (ec) {
-		/* Create the error message */
-		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, MSGFL_ANSW_ERROR ) );
-
-		/* Set the error code */
-		CHECK_FCT( fd_msg_rescode_set(msg, ec, NULL, NULL, 1 ) );
-
-		/* msg now contains an answer message to send back */
-		CHECK_FCT_DO( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED), /* In case of error the message has already been dumped */ );
+	if (pei.pei_errcode) {
+		/* Send the error */
+		receiver_reject(&peer->p_cnxctx, &msg, &pei);
 	}
 	
@@ -925,6 +974,9 @@
 int fd_p_ce_handle_newCER(struct msg ** msg, struct fd_peer * peer, struct cnxctx ** cnx, int valid)
 {
-	fd_cpu_flush_cache();
-	switch (peer->p_hdr.info.runtime.pir_state) {
+	struct fd_pei pei;
+	int cur_state = fd_peer_getstate(peer);
+	memset(&pei, 0, sizeof(pei));
+	
+	switch (cur_state) {
 		case STATE_CLOSED:
 			peer->p_receiver = *cnx;
@@ -960,12 +1012,14 @@
 
 				/* Answer an ELECTION LOST to the receiver side and continue */
-				receiver_reject(*cnx, msg, "ELECTION_LOST", "Please answer my CER instead, you won the election.");
-				*cnx = NULL;
+				pei.pei_errcode = "ELECTION_LOST";
+				pei.pei_message = "Please answer my CER instead, you won the election.";
+				receiver_reject(cnx, msg, &pei);
 			}
 			break;
 
 		default:
-			receiver_reject(*cnx, msg, "DIAMETER_UNABLE_TO_COMPLY", "Invalid state to receive a new connection attempt");
-			*cnx = NULL;
+			pei.pei_errcode = "DIAMETER_UNABLE_TO_COMPLY"; /* INVALID COMMAND? in case of Capabilities-Updates? */
+			pei.pei_message = "Invalid state to receive a new connection attempt.";
+			receiver_reject(cnx, msg, &pei);
 	}
 				
