diff libfdcore/p_cnx.c @ 658:f198d16fa7f4

Initial commit for 1.1.0: * Restructuring: * libfreeDiameter: - renamed folder & binary into libfdproto - renamed libfD.h into fdproto-internal.h - removed signals management (replaced by triggers in libfdcore) * freeDiameter split into: - libfdcore (most contents) - renamed fD.h into fdcore-internal.h - added core.c for framework init/shutdown. - new triggers mechanism in events.c. - freeDiameterd (main, command line parsing, signals management) * tests: - now in top-level directory tests. * other changes: - fd_dict_new now returns 0 on duplicate identical entries. - fixes in dict_legacy_xml - fixes in some dictionaries - moved FD_DEFAULT_CONF_FILENAME definition to freeDiameter-host.h
author Sebastien Decugis <sdecugis@nict.go.jp>
date Fri, 14 Jan 2011 15:15:23 +0900
parents freeDiameter/p_cnx.c@7337305ee51e
children 2e94ef0515d7
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfdcore/p_cnx.c	Fri Jan 14 15:15:23 2011 +0900
@@ -0,0 +1,329 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "fdcore-internal.h"
+
+/* This file contains code used by a peer state machine to initiate a connection to remote peer */
+
+struct next_conn {
+	struct fd_list	chain;
+	int		proto;	/* Protocol of the next attempt */
+	union {
+		sSS	ss;	/* The address, only for TCP */
+		sSA4	sin;
+		sSA6	sin6;
+	};
+	uint16_t	port;	/* The port, for SCTP (included in ss for TCP) */
+	int		dotls;	/* Handshake TLS after connection ? */
+};
+
+static __inline__ void failed_connection_attempt(struct fd_peer * peer)
+{
+	/* Simply remove the first item in the list if not empty */
+	if (! FD_IS_LIST_EMPTY(&peer->p_connparams) ) {
+		struct fd_list * li = peer->p_connparams.next;
+		fd_list_unlink(li);
+		free(li);
+	}
+}
+
+static void empty_connection_list(struct fd_peer * peer)
+{
+	/* Remove all items */
+	while (!FD_IS_LIST_EMPTY(&peer->p_connparams)) {
+		failed_connection_attempt(peer);
+	}
+}
+
+static int prepare_connection_list(struct fd_peer * peer)
+{
+	struct fd_list * li, *last_prio;
+	struct next_conn   * new; 
+	
+	uint16_t	port_no; /* network order */
+	int		dotls_immediate;
+	
+	TRACE_ENTRY("%p", peer);
+	 
+	/* Resolve peer address(es) if needed */
+	if (FD_IS_LIST_EMPTY(&peer->p_hdr.info.pi_endpoints)) {
+		struct addrinfo hints, *ai, *aip;
+		int ret;
+
+		memset(&hints, 0, sizeof(hints));
+		hints.ai_flags = AI_ADDRCONFIG;
+		ret = getaddrinfo(peer->p_hdr.info.pi_diamid, NULL, &hints, &ai);
+		if (ret) {
+			fd_log_debug("Unable to resolve address for peer '%s' (%s), aborting\n", peer->p_hdr.info.pi_diamid, gai_strerror(ret));
+			if (ret != EAI_AGAIN)
+				fd_psm_terminate( peer, NULL );
+			return 0;
+		}
+		
+		for (aip = ai; aip != NULL; aip = aip->ai_next) {
+			CHECK_FCT( fd_ep_add_merge( &peer->p_hdr.info.pi_endpoints, aip->ai_addr, aip->ai_addrlen, EP_FL_DISC ) );
+		}
+		freeaddrinfo(ai);
+	}
+	
+	/* Remove addresses from unwanted family */
+	if (peer->p_hdr.info.config.pic_flags.pro3) {
+		CHECK_FCT( fd_ep_filter_family(
+					&peer->p_hdr.info.pi_endpoints, 
+					(peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP) ? 
+						AF_INET 
+						: AF_INET6));
+	}
+	if (fd_g_config->cnf_flags.no_ip4) {
+		CHECK_FCT( fd_ep_filter_family(
+					&peer->p_hdr.info.pi_endpoints, 
+					AF_INET6));
+	}
+	if (fd_g_config->cnf_flags.no_ip6) {
+		CHECK_FCT( fd_ep_filter_family(
+					&peer->p_hdr.info.pi_endpoints, 
+					AF_INET));
+	}
+	
+	/* Remove any local address that would be here, it should not happen but it does sometimes... */
+	CHECK_FCT( fd_ep_filter_list(&peer->p_hdr.info.pi_endpoints, &fd_g_config->cnf_endpoints) );
+	
+	/* Now check we have at least one address to attempt */
+	if (FD_IS_LIST_EMPTY(&peer->p_hdr.info.pi_endpoints)) {
+		fd_log_debug("No address %savailable to connect to peer '%s', aborting\n", peer->p_hdr.info.config.pic_flags.pro3 ? "in the configured family " : "", peer->p_hdr.info.pi_diamid);
+		fd_psm_terminate( peer, NULL );
+		return 0;
+	}
+	
+	/* Cleanup any previous list */
+	empty_connection_list(peer);
+	
+	/* Prepare the parameters */
+	if ((peer->p_hdr.info.config.pic_flags.sec != PI_SEC_DEFAULT) || (fd_g_config->cnf_flags.tls_alg)) {
+		dotls_immediate = 0;
+		port_no = htons(peer->p_hdr.info.config.pic_port ?: fd_g_config->cnf_port);
+	} else {
+		dotls_immediate = 1;
+		port_no = htons(peer->p_hdr.info.config.pic_port ?: fd_g_config->cnf_port_tls);
+	}
+	
+	last_prio = &peer->p_connparams;
+	
+	/* Create TCP parameters unless specified otherwise */
+	if ((!fd_g_config->cnf_flags.no_tcp) && (peer->p_hdr.info.config.pic_flags.pro4 != PI_P4_SCTP)) {
+		for (li = peer->p_hdr.info.pi_endpoints.next; li != &peer->p_hdr.info.pi_endpoints; li = li->next) {
+			struct fd_endpoint * ep = (struct fd_endpoint *)li;
+			
+			CHECK_MALLOC( new = malloc(sizeof(struct next_conn)) );
+			memset(new, 0, sizeof(struct next_conn));
+			fd_list_init(&new->chain, new);
+			
+			new->proto = IPPROTO_TCP;
+			
+			memcpy( &new->ss, &ep->ss, sizeof(sSS) );
+			switch (new->ss.ss_family) {
+				case AF_INET:
+					new->sin.sin_port = port_no;
+					break;
+				case AF_INET6:
+					new->sin6.sin6_port = port_no;
+					break;
+				default:
+					free(new);
+					continue; /* Move to the next endpoint */
+			}
+			
+			new->dotls = dotls_immediate;
+			
+			/* Add the new entry to the appropriate position (conf and disc go first) */
+			if (ep->flags & (EP_FL_CONF | EP_FL_DISC)) {
+				fd_list_insert_after(last_prio, &new->chain);
+				last_prio = &new->chain;
+			} else {
+				fd_list_insert_before(&peer->p_connparams, &new->chain);
+			}
+		}
+	}
+	
+	/* Now, add the SCTP entry, if not disabled */
+#ifndef DISABLE_SCTP
+	if ((!fd_g_config->cnf_flags.no_sctp) && (peer->p_hdr.info.config.pic_flags.pro4 != PI_P4_TCP)) {
+		struct next_conn   * new;
+		
+		CHECK_MALLOC( new = malloc(sizeof(struct next_conn)) );
+		memset(new, 0, sizeof(struct next_conn));
+		fd_list_init(&new->chain, new);
+
+		new->proto = IPPROTO_SCTP;
+		new->port  = ntohs(port_no); /* back to host byte order... */
+		new->dotls = dotls_immediate;
+
+		/* Add the new entry to the appropriate position (depending on preferences) */
+		if ((fd_g_config->cnf_flags.pr_tcp) || (peer->p_hdr.info.config.pic_flags.alg == PI_ALGPREF_TCP)) {
+			fd_list_insert_after(last_prio, &new->chain);
+		} else {
+			fd_list_insert_after(&peer->p_connparams, &new->chain); /* very first position */
+		}
+	}
+#endif /* DISABLE_SCTP */
+	
+	return 0;
+}
+
+
+/* The thread that attempts the connection */
+static void * connect_thr(void * arg)
+{
+	struct fd_peer * peer = arg;
+	struct cnxctx * cnx = NULL;
+	struct next_conn * nc = NULL;
+	int rebuilt = 0;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO( CHECK_PEER(peer), return NULL );
+
+	/* Set the thread name */
+	{
+		char buf[48];
+		sprintf(buf, "ConnTo:%.*s", (int)(sizeof(buf)) - 8, peer->p_hdr.info.pi_diamid);
+		fd_log_threadname ( buf );
+	}
+	
+	do {
+		/* Rebuild the list if needed, if it is empty -- but at most once */
+		if (FD_IS_LIST_EMPTY(&peer->p_connparams)) {
+			if (! rebuilt) {
+				CHECK_FCT_DO( prepare_connection_list(peer), goto fatal_error );
+				rebuilt ++;
+			}
+			if (FD_IS_LIST_EMPTY(&peer->p_connparams)) {
+				/* We encountered an error or we have looped over all the addresses of the peer. */
+				TRACE_DEBUG(INFO, "Unable to connect to the peer %s, aborting attempts for now.", peer->p_hdr.info.pi_diamid);
+				CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_FAILED, 0, NULL), goto fatal_error );
+				return NULL;
+			}
+		}
+		
+		/* Attempt connection to the first entry */
+		nc = (struct next_conn *)(peer->p_connparams.next);
+		
+		switch (nc->proto) {
+			case IPPROTO_TCP:
+				cnx = fd_cnx_cli_connect_tcp((sSA *)&nc->ss, sSAlen(&nc->ss));
+				break;
+#ifndef DISABLE_SCTP			
+			case IPPROTO_SCTP:
+				cnx = fd_cnx_cli_connect_sctp((peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP) ?: fd_g_config->cnf_flags.no_ip6, nc->port, &peer->p_hdr.info.pi_endpoints);
+				break;
+#endif /* DISABLE_SCTP */
+		}
+		
+		if (cnx)
+			break;
+		
+		/* Pop these parameters and continue */
+		failed_connection_attempt(peer);
+		
+		pthread_testcancel();
+		
+	} while (!cnx); /* and until cancellation */
+	
+	/* Now, we have an established connection in cnx */
+	
+	pthread_cleanup_push((void *)fd_cnx_destroy, cnx);
+	
+	/* Set the hostname in the connection, so that handshake verifies the remote identity */
+	fd_cnx_sethostname(cnx,peer->p_hdr.info.pi_diamid);
+	
+	/* Handshake if needed (secure port) */
+	if (nc->dotls) {
+		CHECK_FCT_DO( fd_cnx_handshake(cnx, GNUTLS_CLIENT, peer->p_hdr.info.config.pic_priority, NULL),
+			{
+				/* Handshake failed ...  */
+				fd_log_debug("TLS Handshake failed with peer '%s', resetting the connection\n", peer->p_hdr.info.pi_diamid);
+				fd_cnx_destroy(cnx);
+				empty_connection_list(peer);
+				fd_ep_filter(&peer->p_hdr.info.pi_endpoints, EP_FL_CONF);
+				return NULL;
+			} );
+	} else {
+		/* Prepare to receive the next message */
+		CHECK_FCT_DO( fd_cnx_start_clear(cnx, 0), goto fatal_error );
+	}
+	
+	/* Upon success, generate FDEVP_CNX_ESTABLISHED */
+	CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ESTABLISHED, 0, cnx), goto fatal_error );
+	
+	pthread_cleanup_pop(0);
+	
+	return NULL;
+	
+fatal_error:
+	/* Cleanup the connection */
+	if (cnx)
+		fd_cnx_destroy(cnx);
+
+	/* Generate a termination event */
+	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
+	
+	return NULL;
+}
+
+
+/* Initiate a connection attempt to a remote peer */
+int fd_p_cnx_init(struct fd_peer * peer)
+{
+	TRACE_ENTRY("%p", peer);
+	
+	/* Start the connect thread */
+	CHECK_FCT( pthread_create(&peer->p_ini_thr, NULL, connect_thr, peer) );
+	return 0;
+}
+
+/* Cancel a connection attempt */
+void fd_p_cnx_abort(struct fd_peer * peer, int cleanup_all)
+{
+	TRACE_ENTRY("%p %d", peer, cleanup_all);
+	CHECK_PARAMS_DO( CHECK_PEER(peer), return );
+	
+	if (peer->p_ini_thr != (pthread_t)NULL) {
+		CHECK_FCT_DO( fd_thr_term(&peer->p_ini_thr), /* continue */);
+		failed_connection_attempt(peer);
+	}
+	
+	if (cleanup_all) {
+		empty_connection_list(peer);
+	}
+}
"Welcome to our mercurial repository"