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

Started including TLS code

parent 8d95d265
......@@ -26,6 +26,10 @@ SET(FD_COMMON_GEN_SRC
fdd.tab.h
)
# Require GNU TLS for building the daemon
FIND_PACKAGE(GNUTLS REQUIRED)
INCLUDE_DIRECTORIES(${GNUTLS_INCLUDE_DIRS})
SET(FD_LIBS ${FD_LIBS} ${GNUTLS_LIBRARIES})
# Building the executable
ADD_EXECUTABLE(freeDiameterd ${FD_COMMON_SRC} ${FD_COMMON_GEN_SRC} main.c)
......
......@@ -150,11 +150,11 @@ struct fd_peer { /* The "real" definition of the peer structure */
/* Events codespace for struct fd_peer->p_events */
enum {
/* request to terminate this peer : disconnect, requeue all messages */
FDEVP_TERMINATE = 2000
/* Dump all info about this peer in the debug log */
,FDEVP_DUMP_ALL
FDEVP_DUMP_ALL = 2000
/* request to terminate this peer : disconnect, requeue all messages */
,FDEVP_TERMINATE
/* A message was received in the peer */
,FDEVP_MSG_INCOMING
......@@ -163,6 +163,8 @@ enum {
,FDEVP_PSM_TIMEOUT
};
const char * fd_pev_str(int event);
#define CHECK_EVENT( _e ) \
(((int)(_e) >= FDEVP_DUMP_ALL) && ((int)(_e) <= FDEVP_PSM_TIMEOUT))
/* Structure to store a sent request */
struct sentreq {
......@@ -173,6 +175,9 @@ struct sentreq {
/* Functions */
int fd_peer_fini();
void fd_peer_dump_list(int details);
void fd_peer_dump(struct fd_peer * peer, int details);
int fd_peer_alloc(struct fd_peer ** ptr);
int fd_peer_free(struct fd_peer ** ptr);
/* fd_peer_add declared in freeDiameter.h */
/* Peer expiry */
......
......@@ -37,6 +37,12 @@
#include <signal.h>
#include <getopt.h>
#include <locale.h>
#ifdef GCRY_THREAD_OPTION_PTHREAD_IMPL
GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif /* GCRY_THREAD_OPTION_PTHREAD_IMPL */
/* forward declarations */
static void * sig_hdl(void * arg);
......@@ -61,6 +67,7 @@ int main(int argc, char * argv[])
/* Initialize the library */
CHECK_FCT( fd_lib_init() );
TRACE_DEBUG(INFO, "libfreeDiameter initialized.");
/* Name this thread */
fd_log_threadname("Main");
......@@ -71,6 +78,15 @@ int main(int argc, char * argv[])
/* Parse the command-line */
CHECK_FCT( main_cmdline(argc, argv) );
/* Initialize gnutls */
CHECK_GNUTLS_DO( gnutls_global_init(), return EINVAL );
if ( ! gnutls_check_version(GNUTLS_VERSION) ) {
fprintf(stderr, "The GNUTLS library is too old; found '%s', need '" GNUTLS_VERSION "'\n", gnutls_check_version(NULL));
return EINVAL;
} else {
TRACE_DEBUG(INFO, "GNUTLS library '%s' initialized.", gnutls_check_version(NULL));
}
/* Allow SIGINT and SIGTERM from this point */
CHECK_POSIX( pthread_create(&sig_th, NULL, sig_hdl, NULL) );
......@@ -141,6 +157,8 @@ end:
CHECK_FCT_DO( fd_thr_term(&sig_th), /* continue */ );
gnutls_global_deinit();
return ret;
}
......@@ -167,21 +185,23 @@ static int main_cmdline(int argc, char *argv[])
{
int c;
int option_index = 0;
char * locale;
struct option long_options[] = {
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'V' },
{ "config", 1, NULL, 'c' },
{ "debug", 0, NULL, 'd' },
{ "quiet", 0, NULL, 'q' },
{ NULL, 0, NULL, 0 }
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "config", required_argument, NULL, 'c' },
{ "debug", no_argument, NULL, 'd' },
{ "quiet", no_argument, NULL, 'q' },
{ "dbglocale", optional_argument, NULL, 'l' },
{ NULL, 0, NULL, 0 }
};
TRACE_ENTRY("%d %p", argc, argv);
/* Loop on arguments */
while (1) {
c = getopt_long (argc, argv, "hVc:dq", long_options, &option_index);
c = getopt_long (argc, argv, "hVc:dql:", long_options, &option_index);
if (c == -1)
break; /* Exit from the loop. */
......@@ -199,6 +219,16 @@ static int main_cmdline(int argc, char *argv[])
fd_g_config->cnf_file = optarg;
break;
case 'l': /* Change the locale. */
locale = setlocale(LC_ALL, optarg?:"");
if (locale) {
TRACE_DEBUG(INFO, "Locale set to: %s", optarg ?: locale);
} else {
TRACE_DEBUG(INFO, "Unable to set locale (%s)", optarg);
return EINVAL;
}
break;
case 'd': /* Increase verbosity of debug messages. */
fd_g_debug_lvl++;
break;
......@@ -209,11 +239,11 @@ static int main_cmdline(int argc, char *argv[])
case '?': /* Invalid option. */
/* `getopt_long' already printed an error message. */
TRACE_DEBUG(INFO, "getopt_long found an invalid character\n");
TRACE_DEBUG(INFO, "getopt_long found an invalid character");
return EINVAL;
default: /* bug: option not considered. */
TRACE_DEBUG(INFO, "A command-line option is missing in parser: %c\n", c);
TRACE_DEBUG(INFO, "A command-line option is missing in parser: %c", c);
ASSERT(0);
return EINVAL;
}
......@@ -266,6 +296,7 @@ static void main_help( void )
" default location (%s).\n", DEFAULT_CONF_FILE);
printf( "\nDebug:\n"
" These options are mostly useful for developers\n"
" -l, --dbglocale Set the locale for error messages\n"
" -d, --debug Increase verbosity of debug messages\n"
" -q, --quiet Decrease verbosity then remove debug messages\n");
}
......
......@@ -35,11 +35,63 @@
#include "fD.h"
/* Delay for garbage collection of expired threads, in seconds */
#define GC_TIME 60
static pthread_t exp_thr;
static pthread_t gc_thr;
static struct fd_list exp_list = FD_LIST_INITIALIZER( exp_list );
static pthread_cond_t exp_cnd = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t exp_mtx = PTHREAD_MUTEX_INITIALIZER;
static void * gc_th_fct(void * arg)
{
fd_log_threadname ( "Peers/garbage" );
TRACE_ENTRY( "" );
do {
struct fd_list * li, purge = FD_LIST_INITIALIZER(purge);
pthread_testcancel();
sleep(GC_TIME);
/* Now check in the peers list if any peer can be deleted */
CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), goto error );
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
struct fd_peer * peer = (struct fd_peer *)li;
if (peer->p_hdr.info.pi_state != STATE_ZOMBIE)
continue;
if (peer->p_hdr.info.pi_flags.exp == PI_EXP_NONE)
continue; /* This peer was not supposed to expire, keep it in the list */
/* Ok, the peer was expired, let's remove it */
li = li->prev; /* to avoid breaking the loop */
fd_list_unlink(&peer->p_hdr.chain);
fd_list_insert_before(&purge, &peer->p_hdr.chain);
}
CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), goto error );
/* Now delete peers that are in the purge list */
while (!FD_IS_LIST_EMPTY(&purge)) {
struct fd_peer * peer = (struct fd_peer *)(purge.next);
fd_list_unlink(&peer->p_hdr.chain);
TRACE_DEBUG(INFO, "Garbage Collect: delete zombie peer '%s'", peer->p_hdr.info.pi_diamid);
CHECK_FCT_DO( fd_peer_free(&peer), /* Continue... what else to do ? */ );
}
} while (1);
error:
TRACE_DEBUG(INFO, "An error occurred in peers module! GC thread is terminating...");
ASSERT(0);
CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, NULL), );
return NULL;
}
static void * exp_th_fct(void * arg)
{
fd_log_threadname ( "Peers/expire" );
......@@ -97,6 +149,7 @@ int fd_p_expi_init(void)
{
TRACE_ENTRY();
CHECK_FCT( pthread_create( &exp_thr, NULL, exp_th_fct, NULL ) );
CHECK_FCT( pthread_create( &gc_thr, NULL, gc_th_fct, NULL ) );
return 0;
}
......@@ -112,6 +165,7 @@ int fd_p_expi_fini(void)
}
CHECK_POSIX( pthread_mutex_unlock(&exp_mtx) );
CHECK_FCT_DO( fd_thr_term(&gc_thr), );
return 0;
}
......
......@@ -36,7 +36,7 @@
#include "fD.h"
const char *peer_state_str[] = {
"STATE_ZOMBIE"
"STATE_NEW"
, "STATE_OPEN"
, "STATE_CLOSED"
, "STATE_CLOSING"
......@@ -45,6 +45,7 @@ const char *peer_state_str[] = {
, "STATE_WAITCEA"
, "STATE_SUSPECT"
, "STATE_REOPEN"
, "STATE_ZOMBIE"
};
const char * fd_pev_str(int event)
......@@ -122,6 +123,7 @@ static void psm_next_timeout(struct fd_peer * peer, int add_random, int delay)
#endif
}
/* Wait for the next event in the PSM, or timeout */
static int psm_ev_timedget(struct fd_peer * peer, int *code, void ** data)
{
struct fd_event * ev;
......@@ -141,13 +143,15 @@ static int psm_ev_timedget(struct fd_peer * peer, int *code, void ** data)
}
return 0;
}
}
/* The state machine thread */
/* The state machine thread (controler) */
static void * p_psm_th( void * arg )
{
struct fd_peer * peer = (struct fd_peer *)arg;
int created_started = started;
int event;
void * ev_data;
CHECK_PARAMS_DO( CHECK_PEER(peer), ASSERT(0) );
......@@ -160,12 +164,12 @@ static void * p_psm_th( void * arg )
fd_log_threadname ( buf );
}
/* Wait that the PSM are authorized to start in the daemon */
CHECK_FCT_DO( fd_psm_waitstart(), goto end );
/* The state machine starts in CLOSED state */
peer->p_hdr.info.pi_state = STATE_CLOSED;
/* Wait that the PSM are authorized to start in the daemon */
CHECK_FCT_DO( fd_psm_waitstart(), goto psm_end );
/* Initialize the timer */
if (peer->p_flags.pf_responder) {
psm_next_timeout(peer, 0, INCNX_TIMEOUT);
......@@ -173,27 +177,68 @@ static void * p_psm_th( void * arg )
psm_next_timeout(peer, created_started ? 0 : 1, 0);
}
psm:
do {
int event;
void * ev_data;
/* Get next event */
CHECK_FCT_DO( psm_ev_timedget(peer, &event, &ev_data), goto end );
TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p)\t'%s'",
STATE_STR(peer->p_hdr.info.pi_state),
fd_pev_str(event), ev_data,
peer->p_hdr.info.pi_diamid);
/* Now, the action depends on the current state and the incoming event */
psm_loop:
/* Get next event */
CHECK_FCT_DO( psm_ev_timedget(peer, &event, &ev_data), goto psm_end );
TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p)\t'%s'",
STATE_STR(peer->p_hdr.info.pi_state),
fd_pev_str(event), ev_data,
peer->p_hdr.info.pi_diamid);
/* Now, the action depends on the current state and the incoming event */
/* The following two states are impossible */
ASSERT( peer->p_hdr.info.pi_state != STATE_NEW );
ASSERT( peer->p_hdr.info.pi_state != STATE_ZOMBIE );
/* Purge invalid events */
if (!CHECK_EVENT(event)) {
TRACE_DEBUG(INFO, "Invalid event received in PSM '%s' : %d", peer->p_hdr.info.pi_diamid, event);
goto psm_loop;
}
/* Handle the (easy) debug event now */
if (event == FDEVP_DUMP_ALL) {
fd_peer_dump(peer, ANNOYING);
goto psm_loop;
}
/* Requests to terminate the peer object */
if (event == FDEVP_TERMINATE) {
switch (peer->p_hdr.info.pi_state) {
case STATE_CLOSING:
case STATE_WAITCNXACK:
case STATE_WAITCNXACK_ELEC:
case STATE_WAITCEA:
case STATE_SUSPECT:
/* In these cases, we just cleanup the peer object and terminate now */
TODO("Cleanup the PSM: terminate connection object, ...");
case STATE_CLOSED:
/* Then we just terminate the PSM */
goto psm_end;
case STATE_OPEN:
case STATE_REOPEN:
/* We cannot just close the conenction, we have to send a DPR first */
TODO("Send DPR, mark the peer as CLOSING");
goto psm_loop;
}
}
} while (1);
/* MSG_RECEIVED: fd_p_expi_update(struct fd_peer * peer ) */
/* If timeout or OPEN : call cb if defined */
/* Default action : the handling has not yet been implemented. */
TODO("Missing handler in PSM : '%s'\t<-- '%s'", STATE_STR(peer->p_hdr.info.pi_state), fd_pev_str(event));
if (event == FDEVP_PSM_TIMEOUT) {
/* We have not handled timeout in this state, let's postpone next alert */
psm_next_timeout(peer, 0, 60);
}
goto psm_loop;
end:
/* set STATE_ZOMBIE */
pthread_cleanup_pop(1);
psm_end:
pthread_cleanup_pop(1); /* set STATE_ZOMBIE */
return NULL;
}
......@@ -204,8 +249,15 @@ end:
int fd_psm_begin(struct fd_peer * peer )
{
TRACE_ENTRY("%p", peer);
TODO("");
return ENOTSUP;
/* Check the peer and state are OK */
CHECK_PARAMS( CHECK_PEER(peer) && (peer->p_hdr.info.pi_state == STATE_NEW) );
/* Create the PSM controler thread */
CHECK_POSIX( pthread_create( &peer->p_psm, NULL, p_psm_th, peer ) );
/* We're done */
return 0;
}
/* End the PSM (clean ending) */
......@@ -213,7 +265,11 @@ int fd_psm_terminate(struct fd_peer * peer )
{
TRACE_ENTRY("%p", peer);
CHECK_PARAMS( CHECK_PEER(peer) );
CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, NULL) );
if (peer->p_hdr.info.pi_state != STATE_ZOMBIE) {
CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, NULL) );
} else {
TRACE_DEBUG(FULL, "Peer '%s' was already terminated", peer->p_hdr.info.pi_diamid);
}
return 0;
}
......@@ -224,7 +280,8 @@ void fd_psm_abord(struct fd_peer * peer )
TODO("Cancel PSM thread");
TODO("Cancel IN thread");
TODO("Cancel OUT thread");
TODO("Cleanup the connection");
TODO("Cleanup the peer connection object");
TODO("Cleanup the message queues (requeue)");
return;
}
......
......@@ -35,63 +35,13 @@
#include "fD.h"
/* Global list of peers */
struct fd_list fd_g_peers = FD_LIST_INITIALIZER(fd_g_peers);
pthread_rwlock_t fd_g_peers_rw = PTHREAD_RWLOCK_INITIALIZER;
/* Terminate peer module (destroy all peers) */
int fd_peer_fini()
{
struct fd_list * li;
TRACE_ENTRY();
CHECK_FCT_DO(fd_p_expi_fini(), /* continue */);
TRACE_DEBUG(INFO, "Sending signal to terminate to all peer connections");
CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ );
/* For each peer in the list, ... */
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
struct fd_peer * np = (struct fd_peer *)li;
CHECK_FCT_DO( fd_psm_terminate(np), /* continue */ );
}
CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
TODO("Give some time to all PSM, then destroy remaining threads");
/* fd_psm_abord(struct fd_peer * peer ) */
return 0;
}
/* Dump the list of peers */
void fd_peer_dump_list(int details)
{
struct fd_list * li;
fd_log_debug("Dumping list of peers :\n");
CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ );
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
struct fd_peer * np = (struct fd_peer *)li;
if (np->p_eyec != EYEC_PEER) {
fd_log_debug(" Invalid entry @%p !\n", li);
continue;
}
fd_log_debug(" %s\t%s", STATE_STR(np->p_hdr.info.pi_state), np->p_hdr.info.pi_diamid);
if (details > INFO) {
fd_log_debug("\t(rlm:%s)", np->p_hdr.info.pi_realm);
if (np->p_hdr.info.pi_prodname)
fd_log_debug("\t['%s' %u]", np->p_hdr.info.pi_prodname, np->p_hdr.info.pi_firmrev);
fd_log_debug("\t(from %s)", np->p_dbgorig);
}
fd_log_debug("\n");
}
CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
}
/* Alloc / reinit a peer structure. if *ptr is not NULL, it must already point to a valid struct fd_peer. */
static int fd_sp_reinit(struct fd_peer ** ptr)
int fd_peer_alloc(struct fd_peer ** ptr)
{
struct fd_peer *p;
......@@ -125,6 +75,86 @@ static int fd_sp_reinit(struct fd_peer ** ptr)
return 0;
}
/* Add a new peer entry */
int fd_peer_add ( struct peer_info * info, char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data )
{
struct fd_peer *p = NULL;
struct fd_list * li;
int ret = 0;
TRACE_ENTRY("%p %p %p %p", info, orig_dbg, cb, cb_data);
CHECK_PARAMS(info && info->pi_diamid);
/* Create a structure to contain the new peer information */
CHECK_FCT( fd_peer_alloc(&p) );
/* Copy the informations from the parameters received */
CHECK_MALLOC( p->p_hdr.info.pi_diamid = strdup(info->pi_diamid) );
if (info->pi_realm) {
CHECK_MALLOC( p->p_hdr.info.pi_realm = strdup(info->pi_realm) );
}
p->p_hdr.info.pi_flags.pro3 = info->pi_flags.pro3;
p->p_hdr.info.pi_flags.pro4 = info->pi_flags.pro4;
p->p_hdr.info.pi_flags.alg = info->pi_flags.alg;
p->p_hdr.info.pi_flags.sec = info->pi_flags.sec;
p->p_hdr.info.pi_flags.exp = info->pi_flags.exp;
p->p_hdr.info.pi_lft = info->pi_lft;
p->p_hdr.info.pi_streams = info->pi_streams;
p->p_hdr.info.pi_port = info->pi_port;
p->p_hdr.info.pi_tctimer = info->pi_tctimer;
p->p_hdr.info.pi_twtimer = info->pi_twtimer;
/* Move the items from one list to the other */
if (info->pi_endpoints.next)
while (!FD_IS_LIST_EMPTY( &info->pi_endpoints ) ) {
li = info->pi_endpoints.next;
fd_list_unlink(li);
fd_list_insert_before(&p->p_hdr.info.pi_endpoints, li);
}
/* The internal data */
if (orig_dbg) {
CHECK_MALLOC( p->p_dbgorig = strdup(orig_dbg) );
} else {
CHECK_MALLOC( p->p_dbgorig = strdup("unknown") );
}
p->p_cb = cb;
p->p_cb_data = cb_data;
/* Ok, now check if we don't already have an entry with the same Diameter Id, and insert this one */
CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) );
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
struct fd_peer * prev = (struct fd_peer *)li;
int cmp = strcasecmp( p->p_hdr.info.pi_diamid, prev->p_hdr.info.pi_diamid );
if (cmp < 0)
continue;
if (cmp == 0)
ret = EEXIST;
break;
}
/* We can insert the new peer object */
if (! ret) {
/* Update expiry list */
CHECK_FCT_DO( ret = fd_p_expi_update( p ), goto out );
/* Insert the new element in the list */
fd_list_insert_before( li, &p->p_hdr.chain );
}
out:
CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
if (ret) {
CHECK_FCT( fd_peer_free(&p) );
} else {
CHECK_FCT( fd_psm_begin(p) );
}
return ret;
}
#define free_null( _v ) \
if (_v) { \
free(_v); \
......@@ -139,7 +169,7 @@ static int fd_sp_reinit(struct fd_peer ** ptr)
}
/* Destroy a structure once all cleanups have been performed */
static int fd_sp_destroy(struct fd_peer ** ptr)
int fd_peer_free(struct fd_peer ** ptr)
{
struct fd_peer *p;
void * t;
......@@ -209,85 +239,133 @@ static int fd_sp_destroy(struct fd_peer ** ptr)
return 0;
}
/* Add a new peer entry */
int fd_peer_add ( struct peer_info * info, char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data )
/* Terminate peer module (destroy all peers) */
int fd_peer_fini()
{
struct fd_peer *p = NULL;
struct fd_list * li;
int ret = 0;
TRACE_ENTRY("%p %p %p %p", info, orig_dbg, cb, cb_data);
CHECK_PARAMS(info && info->pi_diamid);
struct fd_list purge = FD_LIST_INITIALIZER(purge); /* Store zombie peers here */
int list_empty;
struct timespec wait_until, now;
/* Create a structure to contain the new peer information */
CHECK_FCT( fd_sp_reinit(&p) );
/* Copy the informations from the parameters received */
CHECK_MALLOC( p->p_hdr.info.pi_diamid = strdup(info->pi_diamid) );
if (info->pi_realm) {
CHECK_MALLOC( p->p_hdr.info.pi_realm = strdup(info->pi_realm) );
}
TRACE_ENTRY();
p->p_hdr.info.pi_flags.pro3 = info->pi_flags.pro3;
p->p_hdr.info.pi_flags.pro4 = info->pi_flags.pro4;
p->p_hdr.info.pi_flags.alg = info->pi_flags.alg;
p->p_hdr.info.pi_flags.sec = info->pi_flags.sec;
p->p_hdr.info.pi_flags.exp = info->pi_flags.exp;
CHECK_FCT_DO(fd_p_expi_fini(), /* continue */);
p->p_hdr.info.pi_lft = info->pi_lft;
p->p_hdr.info.pi_streams = info->pi_streams;
p->p_hdr.info.pi_port = info->pi_port;
p->p_hdr.info.pi_tctimer = info->pi_tctimer;
p->p_hdr.info.pi_twtimer = info->pi_twtimer;
TRACE_DEBUG(INFO, "Sending terminate signal to all peer connections");