Commit be5054b1 authored by Sebastien Decugis's avatar Sebastien Decugis
Browse files

Some cleanups in the cnxctx module

parent 6a6d3bb1
......@@ -242,9 +242,9 @@ int ta_cli_init(void)
void ta_cli_fini(void)
{
(void) fd_sess_handler_destroy(&ta_cli_reg);
ta_sig_fini();
(void) fd_sess_handler_destroy(&ta_cli_reg);
return;
};
......@@ -226,7 +226,7 @@ struct cnxctx * fd_cnx_serv_accept(struct cnxctx * serv)
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 @@ struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa /* contains the port already */,
}
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 @@ struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_lis
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 @@ again:
CHECK_SYS_DO(ret, /* continue */);
}
/* Mark the error */
if (ret <= 0)
conn->cc_goterror=1;
return ret;
}
......@@ -548,6 +548,10 @@ again:
CHECK_SYS_DO(ret, /* continue */);
}
/* Mark the error */
if (ret <= 0)
conn->cc_goterror=1;
return ret;
}
......@@ -656,6 +660,7 @@ static void * rcvthr_notls_sctp(void * arg)
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 @@ again:
}
} );
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 @@ int fd_cnx_start_clear(struct cnxctx * conn, int loop)
CHECK_PARAMS( conn && Target_Queue(conn) && (!conn->cc_tls) && (!conn->cc_loop));
/* 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 */);
/* Save the loop request */
conn->cc_loop = loop;
switch (conn->cc_proto) {
case IPPROTO_TCP:
/* Start the tcp_notls thread */
......@@ -1104,29 +1147,32 @@ int fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority, void * alt
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 @@ int fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo)
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 @@ int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len)
{
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 @@ int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len)
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 @@ void fd_cnx_destroy(struct cnxctx * conn)
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 @@ void fd_cnx_destroy(struct cnxctx * conn)
} 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 @@ void fd_cnx_destroy(struct cnxctx * conn)
#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 */
......
......@@ -47,6 +47,7 @@ struct cnxctx {
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 */
......
......@@ -163,19 +163,20 @@ static void * exp_fct(void * arg)
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 @@ static void * exp_fct(void * arg)
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 @@ int fd_sess_new ( struct session ** session, char * diamId, char * opt, size_t o
sidlen = strlen(sid);
}
} else {
uint32_t sid_h_cpy;
uint32_t sid_l_cpy;
/* "<diamId>;<high32>;<low32>[;opt]" */
sidlen = strlen(diamId);
sidlen += 22; /* max size of ';<high32>;<low32>' */
......@@ -353,20 +355,22 @@ int fd_sess_new ( struct session ** session, char * diamId, char * opt, size_t o
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 @@ int fd_sess_new ( struct session ** session, char * diamId, char * opt, size_t o
/* 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 @@ int fd_sess_new ( struct session ** session, char * diamId, char * opt, size_t o
/* 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 @@ int fd_sess_new ( struct session ** session, char * diamId, char * opt, size_t o
#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 @@ int fd_sess_settimeout( struct session * session, const struct timespec * timeou
/* 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 @@ int fd_sess_settimeout( struct session * session, const struct timespec * timeou
#endif
/* We're done */
pthread_cleanup_pop(0);
CHECK_POSIX( pthread_mutex_unlock( &exp_lock ) );
return 0;
......@@ -542,11 +553,13 @@ int fd_sess_destroy ( struct session ** session )
/* 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 @@ int fd_sess_reclaim ( struct session ** session )
*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 @@ int fd_sess_reclaim ( struct session ** session )
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 @@ int fd_sess_state_store_internal ( struct session_handler * handler, struct sess
/* 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 @@ int fd_sess_state_store_internal ( struct session_handler * handler, struct sess
free(new);
}
pthread_cleanup_pop(0);
CHECK_POSIX( pthread_mutex_unlock(&session->stlock) );
return already ? EALREADY : 0;
......@@ -653,6 +670,7 @@ int fd_sess_state_retrieve_internal ( struct session_handler * handler, struct s
/* 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 @@ int fd_sess_state_retrieve_internal ( struct session_handler * handler, struct s
free(st);
}
pthread_cleanup_pop(0);
CHECK_POSIX( pthread_mutex_unlock(&session->stlock) );
return 0;
......@@ -737,10 +756,12 @@ void fd_sess_dump(int level, struct session * session)
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);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment