Mercurial > hg > freeDiameter
diff freeDiameter/p_cnx.c @ 38:68c1890f7049
Fixed a small bug in SCTP close
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Thu, 05 Nov 2009 17:29:12 +0900 |
parents | cc3c59fe98fe |
children | d7535cf7bab5 |
line wrap: on
line diff
--- a/freeDiameter/p_cnx.c Thu Nov 05 14:28:46 2009 +0900 +++ b/freeDiameter/p_cnx.c Thu Nov 05 17:29:12 2009 +0900 @@ -37,36 +37,140 @@ /* 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 */ + sSS ss; /* The address, only for TCP */ + uint16_t port; /* The port, for SCTP (included in ss for TCP) */ + int dotls; /* Handshake TLS after connection ? */ +}; + +static int prepare_connection_list(struct fd_peer * 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 this connection\n", peer->p_hdr.info.pi_diamid, gai_strerror(ret)); + fd_psm_terminate( peer ); + 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)); + } + + TODO("Prepare the list in peer->p_connparams obeying the flags"); + + TODO("Return an error if the list is empty in the end"); + + return ENOTSUP; +} + +static __inline__ void failed_connection_attempt(struct fd_peer * peer) +{ + /* Simply remove the first item in the list */ + 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); + } +} + /* 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; - /* Use the flags in the peer to select the protocol */ - - TODO("loop on fd_cnx_cli_connect_tcp or fd_cnx_cli_connect_sctp"); - + TRACE_ENTRY("%p", arg); + CHECK_PARAMS_DO( CHECK_PEER(peer), return NULL ); + + do { + /* Rebuild the list if needed, if it is empty */ + if (FD_IS_LIST_EMPTY(&peer->p_connparams)) { + CHECK_FCT_DO( prepare_connection_list(peer), goto fatal_error ); + if (FD_IS_LIST_EMPTY(&peer->p_connparams)) + /* Already logged and peer terminated */ + 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, sSSlen(&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); /* 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; + } ); + } /* 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 */ - fd_cnx_destroy(cnx); + 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), ); @@ -78,7 +182,25 @@ /* 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); + } +}