Mercurial > hg > freeDiameter
view freeDiameter/p_cnx.c @ 246:6418a188c1b5
getnameinfo is also more sensitive on freeBSD
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Fri, 02 Apr 2010 15:27:46 +0900 |
parents | e5bcc8c9322e |
children | 5df55136361b |
line wrap: on
line source
/********************************************************************************************************* * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@nict.go.jp> * * * * Copyright (c) 2009, 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 "fD.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)); 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)); } /* 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) { 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 (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 FD_IS_LIST_EMPTY(&peer->p_connparams) ? EINVAL : 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); } }