# HG changeset patch # User Sebastien Decugis # Date 1265791659 -32400 # Node ID 536b1dde8761ddd4057da88e4a630a3ed5cba0b4 # Parent b4380eb4174dfb8b204e559eb6f81fd41d82ba2d Some cleanups in the cnxctx module diff -r b4380eb4174d -r 536b1dde8761 extensions/test_app/ta_cli.c --- a/extensions/test_app/ta_cli.c Wed Feb 10 14:31:33 2010 +0900 +++ b/extensions/test_app/ta_cli.c Wed Feb 10 17:47:39 2010 +0900 @@ -242,9 +242,9 @@ void ta_cli_fini(void) { - (void) fd_sess_handler_destroy(&ta_cli_reg); + ta_sig_fini(); - ta_sig_fini(); + (void) fd_sess_handler_destroy(&ta_cli_reg); return; }; diff -r b4380eb4174d -r 536b1dde8761 freeDiameter/cnxctx.c --- a/freeDiameter/cnxctx.c Wed Feb 10 14:31:33 2010 +0900 +++ b/freeDiameter/cnxctx.c Wed Feb 10 17:47:39 2010 +0900 @@ -226,7 +226,7 @@ fd_log_debug("].\n"); } - CHECK_MALLOC_DO( cli = fd_cnx_init(1), { shutdown(cli_sock, SHUT_RDWR); return NULL; } ); + CHECK_MALLOC_DO( cli = fd_cnx_init(1), { shutdown(cli_sock, SHUT_RDWR); close(cli_sock); return NULL; } ); cli->cc_socket = cli_sock; cli->cc_proto = serv->cc_proto; @@ -324,10 +324,6 @@ } return cnx; - -error: - fd_cnx_destroy(cnx); - return NULL; } /* Same for SCTP, accepts a list of remote addresses to connect to (see sctp_connectx for how they are used) */ @@ -348,7 +344,7 @@ CHECK_FCT_DO( fd_sctp_client( &sock, no_ip6, port, list ), return NULL ); /* Once the socket is created successfuly, prepare the remaining of the cnx */ - CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); return NULL; } ); + CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); close(sock); return NULL; } ); cnx->cc_socket = sock; cnx->cc_proto = IPPROTO_SCTP; @@ -527,6 +523,10 @@ CHECK_SYS_DO(ret, /* continue */); } + /* Mark the error */ + if (ret <= 0) + conn->cc_goterror=1; + return ret; } @@ -548,6 +548,10 @@ CHECK_SYS_DO(ret, /* continue */); } + /* Mark the error */ + if (ret <= 0) + conn->cc_goterror=1; + return ret; } @@ -656,6 +660,7 @@ do { CHECK_FCT_DO( fd_sctp_recvmeta(conn->cc_socket, NULL, &buf, &bufsz, &event, &conn->cc_closing), goto error ); if (event == FDEVP_CNX_ERROR) { + conn->cc_goterror = 1; goto error; } @@ -707,9 +712,47 @@ } } ); end: + if (ret <= 0) + conn->cc_goterror = 1; return ret; } +/* Wrapper around gnutls_record_send to handle some error codes */ +static ssize_t fd_tls_send_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz) +{ + ssize_t ret; +again: + CHECK_GNUTLS_DO( ret = gnutls_record_send(session, data, sz), + { + switch (ret) { + case GNUTLS_E_REHANDSHAKE: + if (!conn->cc_closing) + CHECK_GNUTLS_DO( ret = gnutls_handshake(session), + { + if (TRACE_BOOL(INFO)) { + fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret)); + } + goto end; + } ); + + case GNUTLS_E_AGAIN: + case GNUTLS_E_INTERRUPTED: + if (!conn->cc_closing) + goto again; + TRACE_DEBUG(INFO, "Connection is closing, so abord gnutls_record_send now."); + break; + + default: + TRACE_DEBUG(INFO, "This TLS error is not handled, assume unrecoverable error"); + } + } ); +end: + if (ret <= 0) + conn->cc_goterror = 1; + return ret; +} + + /* The function that receives TLS data and re-builds a Diameter message -- it exits only on error or cancelation */ int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session) { @@ -798,12 +841,12 @@ CHECK_PARAMS( conn && Target_Queue(conn) && (!conn->cc_tls) && (!conn->cc_loop)); + /* Release resources in case of a previous call was already made */ + CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */); + /* Save the loop request */ conn->cc_loop = loop; - /* Release resources in case of a previous call was already made */ - CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */); - switch (conn->cc_proto) { case IPPROTO_TCP: /* Start the tcp_notls thread */ @@ -1104,29 +1147,32 @@ if (TRACE_BOOL(INFO)) { fd_log_debug("TLS Handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret)); } + conn->cc_goterror = 1; return EINVAL; } ); - /* Now verify the remote credentials are valid -- only simple test here */ - CHECK_FCT( fd_tls_verify_credentials(conn->cc_tls_para.session, conn, 1) ); + /* Now verify the remote credentials are valid -- only simple tests here */ + CHECK_FCT_DO( fd_tls_verify_credentials(conn->cc_tls_para.session, conn, 1), + { + CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_RDWR), /* Continue */ ); + gnutls_deinit(conn->cc_tls_para.session); + return EINVAL; + }); } + /* Mark the connection as protected from here */ + conn->cc_tls = 1; + /* Multi-stream TLS: handshake other streams as well */ if (conn->cc_sctp_para.pairs > 1) { #ifndef DISABLE_SCTP /* Resume all additional sessions from the master one. */ CHECK_FCT(fd_sctps_handshake_others(conn, priority, alt_creds)); - - /* Mark the connection as protected from here */ - conn->cc_tls = 1; /* Start decrypting the messages from all threads and queuing them in target queue */ CHECK_FCT(fd_sctps_startthreads(conn)); #endif /* DISABLE_SCTP */ } else { - /* Mark the connection as protected from here */ - conn->cc_tls = 1; - /* Start decrypting the data */ CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_tls_single, conn ) ); } @@ -1210,37 +1256,6 @@ return 0; } -/* Wrapper around gnutls_record_send to handle some error codes */ -static ssize_t fd_tls_send_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz) -{ - ssize_t ret; -again: - CHECK_GNUTLS_DO( ret = gnutls_record_send(session, data, sz), - { - switch (ret) { - case GNUTLS_E_REHANDSHAKE: - CHECK_GNUTLS_DO( ret = gnutls_handshake(session), - { - if (TRACE_BOOL(INFO)) { - fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret)); - } - goto end; - } ); - - case GNUTLS_E_AGAIN: - case GNUTLS_E_INTERRUPTED: - goto again; - - default: - TRACE_DEBUG(INFO, "This TLS error is not handled, assume unrecoverable error"); - } - } ); -end: - return ret; -} - - - /* Send function when no multi-stream is involved, or sending on stream #0 (send() always use stream 0)*/ static int send_simple(struct cnxctx * conn, unsigned char * buf, size_t len) { @@ -1263,7 +1278,7 @@ { TRACE_ENTRY("%p %p %zd", conn, buf, len); - CHECK_PARAMS(conn && (conn->cc_socket > 0) && buf && len); + CHECK_PARAMS(conn && (conn->cc_socket > 0) && (! conn->cc_goterror) && buf && len); TRACE_DEBUG(FULL, "Sending %zdb %sdata on connection %s", len, conn->cc_tls ? "TLS-protected ":"", conn->cc_id); @@ -1287,7 +1302,7 @@ CHECK_FCT( send_simple(conn, buf, len) ); } else { if (!conn->cc_tls) { - CHECK_FCT( fd_sctp_sendstr(conn->cc_socket, conn->cc_sctp_para.next, buf, len, &conn->cc_closing) ); + CHECK_FCT_DO( fd_sctp_sendstr(conn->cc_socket, conn->cc_sctp_para.next, buf, len, &conn->cc_closing), { conn->cc_goterror = 1; return ENOTCONN; } ); } else { /* push the record to the appropriate session */ ssize_t ret; @@ -1329,15 +1344,20 @@ if (conn->cc_tls) { #ifndef DISABLE_SCTP if (conn->cc_sctp_para.pairs > 1) { - /* Bye on master session */ - CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), /* Continue */ ); - - /* and other stream pairs */ - fd_sctps_bye(conn); - - /* Now wait for all decipher threads to terminate */ - fd_sctps_waitthreadsterm(conn); - + if (! conn->cc_goterror ) { + /* Bye on master session */ + CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), /* Continue */ ); + + /* and other stream pairs */ + fd_sctps_bye(conn); + + /* Now wait for all decipher threads to terminate */ + fd_sctps_waitthreadsterm(conn); + } else { + /* Abord the threads, the connection is dead already */ + fd_sctps_stopthreads(conn); + } + /* Deinit gnutls resources */ fd_sctps_gnutls_deinit_others(conn); gnutls_deinit(conn->cc_tls_para.session); @@ -1348,13 +1368,18 @@ } else { #endif /* DISABLE_SCTP */ /* We are not using the sctps wrapper layer */ - /* Master session */ - CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), /* Continue */ ); - - /* In this case, just wait for thread rcvthr_tls_single to terminate */ - if (conn->cc_rcvthr != (pthread_t)NULL) { - CHECK_POSIX_DO( pthread_join(conn->cc_rcvthr, NULL), /* continue */ ); - conn->cc_rcvthr = (pthread_t)NULL; + if (! conn->cc_goterror ) { + /* Master session */ + CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), /* Continue */ ); + + /* In this case, just wait for thread rcvthr_tls_single to terminate */ + if (conn->cc_rcvthr != (pthread_t)NULL) { + CHECK_POSIX_DO( pthread_join(conn->cc_rcvthr, NULL), /* continue */ ); + conn->cc_rcvthr = (pthread_t)NULL; + } + } else { + /* Cancel the receiver thread in case it did not already terminate */ + CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ ); } /* Free the resources of the TLS session */ @@ -1365,7 +1390,7 @@ #endif /* DISABLE_SCTP */ } - /* Terminate the thread in case it is not done yet */ + /* Terminate the thread in case it is not done yet -- is there any such case left ?*/ CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ ); /* Shut the connection down */ diff -r b4380eb4174d -r 536b1dde8761 freeDiameter/cnxctx.h --- a/freeDiameter/cnxctx.h Wed Feb 10 14:31:33 2010 +0900 +++ b/freeDiameter/cnxctx.h Wed Feb 10 17:47:39 2010 +0900 @@ -47,6 +47,7 @@ int cc_proto; /* IPPROTO_TCP or IPPROTO_SCTP */ int cc_tls; /* Is TLS already started ? */ + int cc_goterror; /* True when an error occurred on the socket */ int cc_closing; /* True if the object is being destroyed: we don't send events anymore */ pthread_t cc_rcvthr; /* thread for receiving messages on the connection */ diff -r b4380eb4174d -r 536b1dde8761 libfreeDiameter/sessions.c --- a/libfreeDiameter/sessions.c Wed Feb 10 14:31:33 2010 +0900 +++ b/libfreeDiameter/sessions.c Wed Feb 10 17:47:39 2010 +0900 @@ -163,19 +163,20 @@ fd_log_threadname ( "Session/expire" ); TRACE_ENTRY( "" ); - CHECK_POSIX_DO( pthread_mutex_lock(&exp_lock), goto error ); - pthread_cleanup_push( fd_cleanup_mutex, &exp_lock ); do { struct timespec now; struct session * first; + CHECK_POSIX_DO( pthread_mutex_lock(&exp_lock), break ); + pthread_cleanup_push( fd_cleanup_mutex, &exp_lock ); +again: /* Check if there are expiring sessions available */ if (FD_IS_LIST_EMPTY(&exp_sentinel)) { /* Just wait for a change or cancelation */ - CHECK_POSIX_DO( pthread_cond_wait( &exp_cond, &exp_lock ), goto error ); + CHECK_POSIX_DO( pthread_cond_wait( &exp_cond, &exp_lock ), break ); /* Restart the loop on wakeup */ - continue; + goto again; } /* Get the pointer to the session that expires first */ @@ -183,28 +184,27 @@ ASSERT( VALIDATE_SI(first) ); /* Get the current time */ - CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), goto error ); + CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), break ); /* If first session is not expired, we just wait until it happens */ if ( TS_IS_INFERIOR( &now, &first->timeout ) ) { CHECK_POSIX_DO2( pthread_cond_timedwait( &exp_cond, &exp_lock, &first->timeout ), ETIMEDOUT, /* ETIMEDOUT is a normal error, continue */, - /* on other error, */ goto error ); + /* on other error, */ break ); /* on wakeup, loop */ - continue; + goto again; } /* Now, the first session in the list is expired; destroy it */ - CHECK_POSIX_DO( pthread_mutex_unlock(&exp_lock), goto error ); - CHECK_FCT_DO( fd_sess_destroy( &first ), goto error ); - CHECK_POSIX_DO( pthread_mutex_lock(&exp_lock), goto error ); + pthread_cleanup_pop( 0 ); + CHECK_POSIX_DO( pthread_mutex_unlock(&exp_lock), break ); + + CHECK_FCT_DO( fd_sess_destroy( &first ), break ); } while (1); - pthread_cleanup_pop( 1 ); -error: TRACE_DEBUG(INFO, "An error occurred in session module! Expiry thread is terminating..."); ASSERT(0); return NULL; @@ -346,6 +346,8 @@ sidlen = strlen(sid); } } else { + uint32_t sid_h_cpy; + uint32_t sid_l_cpy; /* ";;[;opt]" */ sidlen = strlen(diamId); sidlen += 22; /* max size of ';;' */ @@ -353,20 +355,22 @@ sidlen += 1 + (optlen ?: strlen(opt)) ; sidlen++; /* space for the final \0 also */ CHECK_MALLOC( sid = malloc(sidlen) ); + CHECK_POSIX( pthread_mutex_lock(&sid_lock) ); if ( ++sid_l == 0 ) /* overflow */ ++sid_h; + sid_h_cpy = sid_h; + sid_l_cpy = sid_l; + CHECK_POSIX( pthread_mutex_unlock(&sid_lock) ); if (opt) { if (optlen) - sidlen = snprintf(sid, sidlen, "%s;%u;%u;%.*s", diamId, sid_h, sid_l, (int)optlen, opt); + sidlen = snprintf(sid, sidlen, "%s;%u;%u;%.*s", diamId, sid_h_cpy, sid_l_cpy, (int)optlen, opt); else - sidlen = snprintf(sid, sidlen, "%s;%u;%u;%s", diamId, sid_h, sid_l, opt); + sidlen = snprintf(sid, sidlen, "%s;%u;%u;%s", diamId, sid_h_cpy, sid_l_cpy, opt); } else { - sidlen = snprintf(sid, sidlen, "%s;%u;%u", diamId, sid_h, sid_l); + sidlen = snprintf(sid, sidlen, "%s;%u;%u", diamId, sid_h_cpy, sid_l_cpy); } - - CHECK_POSIX( pthread_mutex_unlock(&sid_lock) ); } /* Initialize the session object now, to spend less time inside locked section later. @@ -375,6 +379,8 @@ /* Now find the place to add this object in the hash table. */ CHECK_POSIX( pthread_mutex_lock( H_LOCK(sess->hash) ) ); + pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(sess->hash) ); + for (li = H_LIST(sess->hash)->next; li != H_LIST(sess->hash); li = li->next) { int cmp; struct session * s = (struct session *)(li->o); @@ -403,6 +409,7 @@ /* We must also insert in the expiry list */ CHECK_POSIX( pthread_mutex_lock( &exp_lock ) ); + pthread_cleanup_push( fd_cleanup_mutex, &exp_lock ); /* Find the position in that list. We take it in reverse order */ for (li = exp_sentinel.prev; li != &exp_sentinel; li = li->prev) { @@ -429,9 +436,11 @@ #endif /* We're done */ + pthread_cleanup_pop(0); CHECK_POSIX( pthread_mutex_unlock( &exp_lock ) ); } + pthread_cleanup_pop(0); CHECK_POSIX( pthread_mutex_unlock( H_LOCK(sess->hash) ) ); /* If a session already existed, we must destroy the new element */ @@ -491,6 +500,7 @@ /* Lock -- do we need to lock the hash table as well? I don't think so... */ CHECK_POSIX( pthread_mutex_lock( &exp_lock ) ); + pthread_cleanup_push( fd_cleanup_mutex, &exp_lock ); /* Update the timeout */ fd_list_unlink(&session->expire); @@ -524,6 +534,7 @@ #endif /* We're done */ + pthread_cleanup_pop(0); CHECK_POSIX( pthread_mutex_unlock( &exp_lock ) ); return 0; @@ -542,11 +553,13 @@ /* Unlink and invalidate */ CHECK_FCT( pthread_mutex_lock( H_LOCK(sess->hash) ) ); + pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(sess->hash) ); CHECK_FCT( pthread_mutex_lock( &exp_lock ) ); fd_list_unlink( &sess->chain_h ); fd_list_unlink( &sess->expire ); /* no need to signal the condition here */ sess->eyec = 0xdead; CHECK_FCT( pthread_mutex_unlock( &exp_lock ) ); + pthread_cleanup_pop(0); CHECK_FCT( pthread_mutex_unlock( H_LOCK(sess->hash) ) ); /* Now destroy all states associated -- we don't take the lock since nobody can access this session anymore (in theory) */ @@ -577,6 +590,7 @@ *session = NULL; CHECK_FCT( pthread_mutex_lock( H_LOCK(sess->hash) ) ); + pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(sess->hash) ); CHECK_FCT( pthread_mutex_lock( &exp_lock ) ); if (FD_IS_LIST_EMPTY(&sess->states)) { fd_list_unlink( &sess->chain_h ); @@ -586,6 +600,7 @@ free(sess); } CHECK_FCT( pthread_mutex_unlock( &exp_lock ) ); + pthread_cleanup_pop(0); CHECK_FCT( pthread_mutex_unlock( H_LOCK(sess->hash) ) ); return 0; @@ -603,6 +618,7 @@ /* Lock the session state list */ CHECK_POSIX( pthread_mutex_lock(&session->stlock) ); + pthread_cleanup_push( fd_cleanup_mutex, &session->stlock ); /* Create the new state object */ CHECK_MALLOC(new = malloc(sizeof(struct state)) ); @@ -635,6 +651,7 @@ free(new); } + pthread_cleanup_pop(0); CHECK_POSIX( pthread_mutex_unlock(&session->stlock) ); return already ? EALREADY : 0; @@ -653,6 +670,7 @@ /* Lock the session state list */ CHECK_POSIX( pthread_mutex_lock(&session->stlock) ); + pthread_cleanup_push( fd_cleanup_mutex, &session->stlock ); /* find the state in the list */ for (li = session->states.next; li != &session->states; li = li->next) { @@ -670,6 +688,7 @@ free(st); } + pthread_cleanup_pop(0); CHECK_POSIX( pthread_mutex_unlock(&session->stlock) ); return 0; @@ -737,10 +756,12 @@ fd_log_debug("\t %*s timeout %s.%09ld\n", level, "", buf, session->timeout.tv_nsec); CHECK_POSIX_DO( pthread_mutex_lock(&session->stlock), /* ignore */ ); + pthread_cleanup_push( fd_cleanup_mutex, &session->stlock ); for (li = session->states.next; li != &session->states; li = li->next) { struct state * st = (struct state *)(li->o); fd_log_debug("\t %*s handler %d registered data %p\n", level, "", st->hdl->id, st->state); } + pthread_cleanup_pop(0); CHECK_POSIX_DO( pthread_mutex_unlock(&session->stlock), /* ignore */ ); } fd_log_debug("\t %*s -- end of session @%p --\n", level, "", session);