ocs init
This commit is contained in:
1 parent
5f1710dc22
commit
dae6fc93f7
1057 files changed
+519829
No files matched your search
@@ -0,0 +1,152 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2011, 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 "fdcore-internal.h"
|
||||
|
||||
/* Merge information into a list of apps */
|
||||
int fd_app_merge(struct fd_list * list, application_id_t aid, vendor_id_t vid, int auth, int acct)
|
||||
{
|
||||
struct fd_list * li;
|
||||
int skip = 0;
|
||||
|
||||
/* List is ordered by appid. Avoid duplicates */
|
||||
for (li = list; li->next != list; li = li->next) {
|
||||
struct fd_app * na = (struct fd_app *)(li->next);
|
||||
if (na->appid < aid)
|
||||
continue;
|
||||
|
||||
if (na->appid > aid)
|
||||
break;
|
||||
|
||||
/* Otherwise, we merge with existing entry -- ignore vendor id in this case */
|
||||
skip = 1;
|
||||
|
||||
if (auth)
|
||||
na->flags.auth = 1;
|
||||
if (acct)
|
||||
na->flags.acct = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!skip) {
|
||||
struct fd_app * new = NULL;
|
||||
|
||||
CHECK_MALLOC( new = malloc(sizeof(struct fd_app)) );
|
||||
memset(new, 0, sizeof(struct fd_app));
|
||||
fd_list_init(&new->chain, NULL);
|
||||
new->flags.auth = (auth ? 1 : 0);
|
||||
new->flags.acct = (acct ? 1 : 0);
|
||||
new->vndid = vid;
|
||||
new->appid = aid;
|
||||
fd_list_insert_after(li, &new->chain);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if a given application id is in a list */
|
||||
int fd_app_check(struct fd_list * list, application_id_t aid, struct fd_app **detail)
|
||||
{
|
||||
struct fd_list * li;
|
||||
|
||||
TRACE_ENTRY("%p %d %p", list, aid, detail);
|
||||
CHECK_PARAMS(list && detail);
|
||||
|
||||
*detail = NULL;
|
||||
|
||||
/* Search in the list */
|
||||
for (li = list->next; li != list; li = li->next) {
|
||||
struct fd_app * a = (struct fd_app *)li;
|
||||
if (a->appid < aid)
|
||||
continue;
|
||||
|
||||
if (a->appid == aid)
|
||||
*detail = a;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if two lists have at least one common application */
|
||||
int fd_app_check_common(struct fd_list * list1, struct fd_list * list2, int * common_found)
|
||||
{
|
||||
struct fd_list * li1, *li2;
|
||||
|
||||
TRACE_ENTRY("%p %p %p", list1, list2, common_found);
|
||||
CHECK_PARAMS( list1 && list2 && common_found );
|
||||
|
||||
/* Both lists are ordered, so advance both pointers at the same time */
|
||||
for (li1 = list1->next, li2 = list2->next; (li1 != list1) && (li2 != list2); ) {
|
||||
struct fd_app * a1 = (struct fd_app *)li1, *a2 = (struct fd_app *)li2;
|
||||
if (a1->appid < a2->appid) {
|
||||
li1 = li1->next;
|
||||
continue;
|
||||
}
|
||||
if (a1->appid > a2->appid) {
|
||||
li2 = li2->next;
|
||||
continue;
|
||||
}
|
||||
/* They are equal, compare the applications */
|
||||
if ((a1->flags.auth && a2->flags.auth) || (a1->flags.acct && a2->flags.acct)) {
|
||||
/* found! */
|
||||
*common_found = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This application is not common, advance both lists */
|
||||
li1 = li1->next;
|
||||
li2 = li2->next;
|
||||
}
|
||||
|
||||
/* We did not find a common app */
|
||||
*common_found = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove the apps from a list */
|
||||
int fd_app_empty(struct fd_list * list)
|
||||
{
|
||||
TRACE_ENTRY("%p", list);
|
||||
CHECK_PARAMS( list );
|
||||
|
||||
while (!FD_IS_LIST_EMPTY(list)) {
|
||||
struct fd_list * li = list->next;
|
||||
fd_list_unlink(li);
|
||||
free(li);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,153 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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. *
|
||||
*********************************************************************************************************/
|
||||
|
||||
/* This file contains the definitions for internal use in the connection context files */
|
||||
|
||||
#ifndef _CNXCTX_H
|
||||
#define _CNXCTX_H
|
||||
|
||||
/* Maximum time we allow a connection to be blocked because of head-of-the-line buffers. After this delay, connection is considered in error. */
|
||||
#define MAX_HOTL_BLOCKING_TIME 1000 /* ms */
|
||||
|
||||
/* The connection context structure */
|
||||
struct cnxctx {
|
||||
char cc_id[60]; /* The name of this connection. the first 5 chars are reserved for flags display (cc_state). */
|
||||
char cc_remid[60]; /* Id of remote peer */
|
||||
|
||||
int cc_socket; /* The socket object of the connection -- <=0 if no socket is created */
|
||||
|
||||
int cc_family; /* AF_INET or AF_INET6 (mixed) */
|
||||
int cc_proto; /* IPPROTO_TCP or IPPROTO_SCTP */
|
||||
|
||||
uint32_t cc_state; /* True if the object is being destroyed: we don't send events anymore. access with fd_cnx_getstate() */
|
||||
#define CC_STATUS_CLOSING 1
|
||||
#define CC_STATUS_ERROR 2
|
||||
#define CC_STATUS_SIGNALED 4
|
||||
#define CC_STATUS_TLS 8
|
||||
|
||||
pthread_t cc_rcvthr; /* thread for receiving messages on the connection */
|
||||
int cc_loop; /* tell the thread if it loops or stops after the first message is received */
|
||||
|
||||
struct fifo * cc_incoming; /* FIFO queue of events received on the connection, FDEVP_CNX_* */
|
||||
struct fifo * cc_alt; /* alternate fifo to send FDEVP_CNX_* events to. */
|
||||
|
||||
/* If cc_tls == true */
|
||||
struct {
|
||||
DiamId_t cn; /* If not NULL, remote certif will be checked to match this Common Name */
|
||||
int mode; /* GNUTLS_CLIENT / GNUTLS_SERVER */
|
||||
int algo; /* ALGO_HANDSHAKE_DEFAULT / ALGO_HANDSHAKE_3436 */
|
||||
gnutls_session_t session; /* Session object (stream #0 in case of SCTP) */
|
||||
} cc_tls_para;
|
||||
|
||||
/* If cc_proto == SCTP */
|
||||
struct {
|
||||
uint16_t str_out; /* Out streams */
|
||||
uint16_t str_in; /* In streams */
|
||||
uint16_t pairs; /* max number of pairs ( = min(in, out)) */
|
||||
uint16_t next; /* # of stream the next message will be sent to */
|
||||
int unordered; /* boolean telling if use of streams > 0 is permitted */
|
||||
} cc_sctp_para;
|
||||
|
||||
/* If both conditions */
|
||||
struct {
|
||||
struct sctp3436_ctx *array; /* an array of cc_sctp_para.pairs elements -- the #0 is special (session is outside)*/
|
||||
struct sr_store *sess_store; /* Session data of the master session, to resume the children sessions */
|
||||
} cc_sctp3436_data;
|
||||
};
|
||||
|
||||
void fd_cnx_markerror(struct cnxctx * conn);
|
||||
uint32_t fd_cnx_getstate(struct cnxctx * conn);
|
||||
int fd_cnx_teststate(struct cnxctx * conn, uint32_t flag);
|
||||
void fd_cnx_addstate(struct cnxctx * conn, uint32_t orstate);
|
||||
void fd_cnx_setstate(struct cnxctx * conn, uint32_t abstate);
|
||||
struct fifo * fd_cnx_target_queue(struct cnxctx * conn);
|
||||
|
||||
|
||||
/* Socket */
|
||||
ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length);
|
||||
void fd_cnx_s_setto(int sock);
|
||||
|
||||
/* TLS */
|
||||
int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session);
|
||||
int fd_tls_prepare(gnutls_session_t * session, int mode, int dtls, char * priority, void * alt_creds);
|
||||
#ifndef GNUTLS_VERSION_300
|
||||
int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, int verbose);
|
||||
#endif /* GNUTLS_VERSION_300 */
|
||||
|
||||
/* TCP */
|
||||
int fd_tcp_create_bind_server( int * sock, sSA * sa, socklen_t salen );
|
||||
int fd_tcp_listen( int sock );
|
||||
int fd_tcp_client( int *sock, sSA * sa, socklen_t salen );
|
||||
int fd_tcp_get_local_ep(int sock, sSS * ss, socklen_t *sl);
|
||||
int fd_tcp_get_remote_ep(int sock, sSS * ss, socklen_t *sl);
|
||||
|
||||
#ifndef DISABLE_SCTP
|
||||
/* SCTP */
|
||||
int fd_sctp_create_bind_server( int * sock, int family, struct fd_list * list, uint16_t port );
|
||||
int fd_sctp_listen( int sock );
|
||||
int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list );
|
||||
int fd_sctp_get_local_ep(int sock, struct fd_list * list);
|
||||
int fd_sctp_get_remote_ep(int sock, struct fd_list * list);
|
||||
int fd_sctp_get_str_info( int sock, uint16_t *in, uint16_t *out, sSS *primary );
|
||||
ssize_t fd_sctp_sendstrv(struct cnxctx * conn, uint16_t strid, const struct iovec *iov, int iovcnt);
|
||||
int fd_sctp_recvmeta(struct cnxctx * conn, uint16_t * strid, uint8_t ** buf, size_t * len, int *event);
|
||||
|
||||
/* TLS over SCTP (multi-stream) */
|
||||
struct sctp3436_ctx {
|
||||
struct cnxctx *parent; /* for info such as socket, conn name, event list */
|
||||
uint16_t strid; /* Stream # of this session */
|
||||
struct fifo *raw_recv; /* Raw data received on this stream, for demux */
|
||||
struct {
|
||||
uint8_t *buf;
|
||||
size_t bufsz;
|
||||
size_t offset;
|
||||
} partial; /* If the pull function did not read the full content of first message in raw, it stores it here for next read call. */
|
||||
pthread_t thr; /* Thread to decrypt raw data in this pair of streams */
|
||||
gnutls_session_t session; /* TLS context using this pair of streams -- except if strid == 0, in that case session is outside the array */
|
||||
};
|
||||
|
||||
int fd_sctp3436_init(struct cnxctx * conn);
|
||||
int fd_sctp3436_handshake_others(struct cnxctx * conn, char * priority, void * alt_creds);
|
||||
int fd_sctp3436_startthreads(struct cnxctx * conn, int others);
|
||||
void fd_sctp3436_bye(struct cnxctx * conn);
|
||||
void fd_sctp3436_waitthreadsterm(struct cnxctx * conn);
|
||||
void fd_sctp3436_gnutls_deinit_others(struct cnxctx * conn);
|
||||
void fd_sctp3436_stopthreads(struct cnxctx * conn);
|
||||
void fd_sctp3436_destroy(struct cnxctx * conn);
|
||||
|
||||
#endif /* DISABLE_SCTP */
|
||||
|
||||
#endif /* _CNXCTX_H */
|
||||
|
||||
@@ -0,0 +1,643 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2015, 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 "fdcore-internal.h"
|
||||
#include <sys/stat.h>
|
||||
/* Configuration management */
|
||||
|
||||
#ifndef GNUTLS_DEFAULT_PRIORITY
|
||||
# define GNUTLS_DEFAULT_PRIORITY "NORMAL"
|
||||
#endif /* GNUTLS_DEFAULT_PRIORITY */
|
||||
#ifndef GNUTLS_DEFAULT_DHBITS
|
||||
# define GNUTLS_DEFAULT_DHBITS 1024
|
||||
#endif /* GNUTLS_DEFAULT_DHBITS */
|
||||
|
||||
/* Initialize the fd_g_config structure to default values -- it should already have been initialized to all-0 */
|
||||
int fd_conf_init()
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
fd_g_config->cnf_eyec = EYEC_CONFIG;
|
||||
|
||||
fd_g_config->cnf_timer_tc = 30;
|
||||
fd_g_config->cnf_timer_tw = 30;
|
||||
|
||||
fd_g_config->cnf_port = DIAMETER_PORT;
|
||||
fd_g_config->cnf_port_tls = DIAMETER_SECURE_PORT;
|
||||
fd_g_config->cnf_sctp_str = 30;
|
||||
fd_g_config->cnf_thr_srv = 10;
|
||||
fd_g_config->cnf_dispthr = 1;// = 10;
|
||||
fd_list_init(&fd_g_config->cnf_endpoints, NULL);
|
||||
fd_list_init(&fd_g_config->cnf_apps, NULL);
|
||||
#ifdef DISABLE_SCTP
|
||||
fd_g_config->cnf_flags.no_sctp = 1;
|
||||
#endif /* DISABLE_SCTP */
|
||||
|
||||
fd_g_config->cnf_orstateid = (uint32_t) time(NULL);
|
||||
|
||||
CHECK_FCT( fd_dict_init(&fd_g_config->cnf_dict) );
|
||||
CHECK_FCT( fd_fifo_new(&fd_g_config->cnf_main_ev, 0) );
|
||||
|
||||
/* TLS parameters */
|
||||
CHECK_GNUTLS_DO( gnutls_certificate_allocate_credentials (&fd_g_config->cnf_sec_data.credentials), return ENOMEM );
|
||||
CHECK_GNUTLS_DO( gnutls_dh_params_init (&fd_g_config->cnf_sec_data.dh_cache), return ENOMEM );
|
||||
#ifdef GNUTLS_VERSION_300
|
||||
CHECK_GNUTLS_DO( gnutls_x509_trust_list_init(&fd_g_config->cnf_sec_data.trustlist, 0), return ENOMEM );
|
||||
#endif /* GNUTLS_VERSION_300 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
DECLARE_FD_DUMP_PROTOTYPE(fd_conf_dump)
|
||||
{
|
||||
FD_DUMP_HANDLE_OFFSET();
|
||||
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "freeDiameter configuration:\n"), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Default trace level .... : %+d\n", fd_g_debug_lvl), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Configuration file ..... : %s\n", fd_g_config->cnf_file), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Diameter Identity ...... : %s (l:%zi)\n", fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Diameter Realm ......... : %s (l:%zi)\n", fd_g_config->cnf_diamrlm, fd_g_config->cnf_diamrlm_len), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Tc Timer ............... : %u\n", fd_g_config->cnf_timer_tc), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Tw Timer ............... : %u\n", fd_g_config->cnf_timer_tw), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local port ............. : %hu\n", fd_g_config->cnf_port), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local secure port ...... : %hu\n", fd_g_config->cnf_port_tls), return NULL);
|
||||
if (fd_g_config->cnf_port_3436) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local SCTP TLS port .... : %hu\n", fd_g_config->cnf_port_3436), return NULL);
|
||||
}
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Number of SCTP streams . : %hu\n", fd_g_config->cnf_sctp_str), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Number of clients thr .. : %d\n", fd_g_config->cnf_thr_srv), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Number of app threads .. : %hu\n", fd_g_config->cnf_dispthr), return NULL);
|
||||
if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local endpoints ........ : Default (use all available)\n"), return NULL);
|
||||
} else {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local endpoints ........ : "), return NULL);
|
||||
CHECK_MALLOC_DO( fd_ep_dump( FD_DUMP_STD_PARAMS, 0, 0, &fd_g_config->cnf_endpoints ), return NULL);
|
||||
}
|
||||
if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_apps)) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local applications ..... : (none)"), return NULL);
|
||||
} else {
|
||||
struct fd_list * li = fd_g_config->cnf_apps.next;
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local applications ..... : "), return NULL);
|
||||
while (li != &fd_g_config->cnf_apps) {
|
||||
struct fd_app * app = (struct fd_app *)li;
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "App: %u,%s%s,Vnd:%u\t",
|
||||
app->appid,
|
||||
app->flags.auth ? "Au" : "--",
|
||||
app->flags.acct ? "Ac" : "--",
|
||||
app->vndid), return NULL);
|
||||
li = li->next;
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n Flags : - IP ........... : %s\n", fd_g_config->cnf_flags.no_ip4 ? "DISABLED" : "Enabled"), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - IPv6 ......... : %s\n", fd_g_config->cnf_flags.no_ip6 ? "DISABLED" : "Enabled"), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - Relay app .... : %s\n", fd_g_config->cnf_flags.no_fwd ? "DISABLED" : "Enabled"), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - TCP .......... : %s\n", fd_g_config->cnf_flags.no_tcp ? "DISABLED" : "Enabled"), return NULL);
|
||||
#ifdef DISABLE_SCTP
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - SCTP ......... : DISABLED (at compilation)\n"), return NULL);
|
||||
#else /* DISABLE_SCTP */
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - SCTP ......... : %s\n", fd_g_config->cnf_flags.no_sctp ? "DISABLED" : "Enabled"), return NULL);
|
||||
#endif /* DISABLE_SCTP */
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - Pref. proto .. : %s\n", fd_g_config->cnf_flags.pr_tcp ? "TCP" : "SCTP"), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - TLS method ... : %s\n", fd_g_config->cnf_flags.tls_alg ? "INBAND" : "Separate port"), return NULL);
|
||||
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " TLS : - Certificate .. : %s\n", fd_g_config->cnf_sec_data.cert_file ?: "(NONE)"), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - Private key .. : %s\n", fd_g_config->cnf_sec_data.key_file ?: "(NONE)"), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - CA (trust) ... : %s (%d certs)\n", fd_g_config->cnf_sec_data.ca_file ?: "(none)", fd_g_config->cnf_sec_data.ca_file_nr), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - CRL .......... : %s\n", fd_g_config->cnf_sec_data.crl_file ?: "(none)"), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - Priority ..... : %s\n", fd_g_config->cnf_sec_data.prio_string ?: "(default: '" GNUTLS_DEFAULT_PRIORITY "')"), return NULL);
|
||||
if (fd_g_config->cnf_sec_data.dh_file) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - DH file ...... : %s\n", fd_g_config->cnf_sec_data.dh_file), return NULL);
|
||||
} else {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - DH bits ...... : %d\n", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS), return NULL);
|
||||
}
|
||||
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Origin-State-Id ........ : %u", fd_g_config->cnf_orstateid), return NULL);
|
||||
|
||||
return *buf;
|
||||
}
|
||||
|
||||
/* read contents of a file opened in "rb" mode and alloc this data into a gnutls_datum_t (must be freed afterwards) */
|
||||
int fd_conf_stream_to_gnutls_datum(FILE * pemfile, gnutls_datum_t *out)
|
||||
{
|
||||
size_t alloc = 0;
|
||||
|
||||
CHECK_PARAMS( pemfile && out );
|
||||
memset(out, 0, sizeof(gnutls_datum_t));
|
||||
|
||||
do {
|
||||
uint8_t * realloced = NULL;
|
||||
size_t read = 0;
|
||||
|
||||
if (alloc < out->size + BUFSIZ + 1) {
|
||||
alloc += alloc / 2 + BUFSIZ + 1;
|
||||
CHECK_MALLOC_DO( realloced = realloc(out->data, alloc),
|
||||
{
|
||||
free(out->data);
|
||||
return ENOMEM;
|
||||
} )
|
||||
out->data = realloced;
|
||||
}
|
||||
|
||||
read = fread( out->data + out->size, 1, alloc - out->size - 1, pemfile );
|
||||
out->size += read;
|
||||
|
||||
if (ferror(pemfile)) {
|
||||
int err = errno;
|
||||
TRACE_DEBUG(INFO, "An error occurred while reading file: %s", strerror(err));
|
||||
return err;
|
||||
}
|
||||
} while (!feof(pemfile));
|
||||
|
||||
out->data[out->size] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef GNUTLS_VERSION_300
|
||||
/* inspired from GnuTLS manual */
|
||||
static int fd_conf_print_details_func (gnutls_x509_crt_t cert,
|
||||
gnutls_x509_crt_t issuer, gnutls_x509_crl_t crl,
|
||||
unsigned int verification_output)
|
||||
{
|
||||
char name[512];
|
||||
char issuer_name[512];
|
||||
size_t name_size;
|
||||
size_t issuer_name_size;
|
||||
|
||||
if (!TRACE_BOOL(GNUTLS_DBG_LEVEL))
|
||||
return 0;
|
||||
|
||||
issuer_name_size = sizeof (issuer_name);
|
||||
gnutls_x509_crt_get_issuer_dn (cert, issuer_name, &issuer_name_size);
|
||||
|
||||
name_size = sizeof (name);
|
||||
gnutls_x509_crt_get_dn (cert, name, &name_size);
|
||||
|
||||
fd_log_debug("\tSubject: %s", name);
|
||||
fd_log_debug("\tIssuer: %s", issuer_name);
|
||||
|
||||
if (issuer != NULL)
|
||||
{
|
||||
issuer_name_size = sizeof (issuer_name);
|
||||
gnutls_x509_crt_get_dn (issuer, issuer_name, &issuer_name_size);
|
||||
|
||||
fd_log_debug("\tVerified against: %s", issuer_name);
|
||||
}
|
||||
|
||||
if (crl != NULL)
|
||||
{
|
||||
issuer_name_size = sizeof (issuer_name);
|
||||
gnutls_x509_crl_get_issuer_dn (crl, issuer_name, &issuer_name_size);
|
||||
|
||||
fd_log_debug("\tVerified against CRL of: %s", issuer_name);
|
||||
}
|
||||
|
||||
fd_log_debug("\tVerification output: %x", verification_output);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* GNUTLS_VERSION_300 */
|
||||
|
||||
#ifndef GNUTLS_VERSION_300
|
||||
GCC_DIAG_OFF("-Wdeprecated-declarations")
|
||||
#endif /* !GNUTLS_VERSION_300 */
|
||||
/* Parse the configuration file (using the yacc parser) */
|
||||
int fd_conf_parse()
|
||||
{
|
||||
FILE * fddin;
|
||||
const char * orig = NULL;
|
||||
|
||||
/* Attempt to find the configuration file */
|
||||
if (!fd_g_config->cnf_file)
|
||||
fd_g_config->cnf_file = FD_DEFAULT_CONF_FILENAME;
|
||||
|
||||
fddin = fopen(fd_g_config->cnf_file, "r");
|
||||
if ((fddin == NULL) && (*fd_g_config->cnf_file != '/')) {
|
||||
char * new_cnf = NULL;
|
||||
/* We got a relative path, attempt to add the default directory prefix */
|
||||
orig = fd_g_config->cnf_file;
|
||||
CHECK_MALLOC( new_cnf = malloc(strlen(orig) + strlen(DEFAULT_CONF_PATH) + 2) ); /* we will not free it, but not important */
|
||||
sprintf( new_cnf, DEFAULT_CONF_PATH "/%s", orig );
|
||||
fd_g_config->cnf_file = new_cnf;
|
||||
fddin = fopen(fd_g_config->cnf_file, "r");
|
||||
}
|
||||
if (fddin == NULL) {
|
||||
int ret = errno;
|
||||
LOG_F("Unable to open configuration file for reading; tried the following locations: %s%s%s; Error: %s",
|
||||
orig ?: "", orig? " and " : "", fd_g_config->cnf_file, strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* call yacc parser */
|
||||
TRACE_DEBUG (FULL, "Parsing configuration file: %s", fd_g_config->cnf_file);
|
||||
CHECK_FCT( fddparse(fd_g_config) );
|
||||
|
||||
/* close the file */
|
||||
fclose(fddin);
|
||||
|
||||
/* Check that TLS private key was given */
|
||||
if (! fd_g_config->cnf_sec_data.key_file) {
|
||||
/* If TLS is not enabled, we allow empty TLS configuration */
|
||||
if ((fd_g_config->cnf_port_tls == 0) && (fd_g_config->cnf_flags.tls_alg == 0)) {
|
||||
LOG_N("TLS is disabled, this is *NOT* a recommended practice! Diameter protocol conveys highly sensitive information on your users.");
|
||||
fd_g_config->cnf_sec_data.tls_disabled = 1;
|
||||
} else {
|
||||
LOG_F( "Missing private key configuration for TLS. Please provide the TLS_cred configuration directive.");
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Resolve hostname if not provided */
|
||||
if (fd_g_config->cnf_diamid == NULL) {
|
||||
char buf[HOST_NAME_MAX + 1];
|
||||
struct addrinfo hints, *info;
|
||||
int ret;
|
||||
|
||||
/* local host name */
|
||||
CHECK_SYS(gethostname(buf, sizeof(buf)));
|
||||
|
||||
/* get FQDN */
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_flags = AI_CANONNAME;
|
||||
|
||||
ret = getaddrinfo(buf, NULL, &hints, &info);
|
||||
if (ret != 0) {
|
||||
TRACE_ERROR( "Error resolving local FQDN : '%s' : %s"
|
||||
". Please provide Identity in configuration file.",
|
||||
buf, gai_strerror(ret));
|
||||
return EINVAL;
|
||||
}
|
||||
fd_g_config->cnf_diamid = info->ai_canonname;
|
||||
CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamid, &fd_g_config->cnf_diamid_len, 1) );
|
||||
freeaddrinfo(info);
|
||||
} else {
|
||||
CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamid, &fd_g_config->cnf_diamid_len, 0) );
|
||||
}
|
||||
|
||||
/* Handle the realm part */
|
||||
if (fd_g_config->cnf_diamrlm == NULL) {
|
||||
char * start = NULL;
|
||||
|
||||
/* Check the diameter identity is a fqdn */
|
||||
start = strchr(fd_g_config->cnf_diamid, '.');
|
||||
if ((start == NULL) || (start[1] == '\0')) {
|
||||
TRACE_ERROR( "Unable to extract realm from the Identity '%s'."
|
||||
" Please fix your Identity setting or provide Realm.",
|
||||
fd_g_config->cnf_diamid);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
fd_g_config->cnf_diamrlm = start + 1;
|
||||
CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamrlm, &fd_g_config->cnf_diamrlm_len, 1) );
|
||||
} else {
|
||||
CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamrlm, &fd_g_config->cnf_diamrlm_len, 0) );
|
||||
}
|
||||
|
||||
/* Validate some flags */
|
||||
if (fd_g_config->cnf_flags.no_ip4 && fd_g_config->cnf_flags.no_ip6) {
|
||||
TRACE_ERROR( "IP and IPv6 cannot be disabled at the same time.");
|
||||
return EINVAL;
|
||||
}
|
||||
if (fd_g_config->cnf_flags.no_tcp && fd_g_config->cnf_flags.no_sctp) {
|
||||
TRACE_ERROR( "TCP and SCTP cannot be disabled at the same time.");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Validate local endpoints */
|
||||
if ((!FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) && (fd_g_config->cnf_flags.no_ip4 || fd_g_config->cnf_flags.no_ip6)) {
|
||||
struct fd_list * li;
|
||||
for ( li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
|
||||
struct fd_endpoint * ep = (struct fd_endpoint *)li;
|
||||
if ( (fd_g_config->cnf_flags.no_ip4 && (ep->sa.sa_family == AF_INET))
|
||||
||(fd_g_config->cnf_flags.no_ip6 && (ep->sa.sa_family == AF_INET6)) ) {
|
||||
char sa_buf[sSA_DUMP_STRLEN];;
|
||||
li = li->prev;
|
||||
fd_list_unlink(&ep->chain);
|
||||
fd_sa_sdump_numeric(sa_buf, &ep->sa);
|
||||
LOG_N("Info: Removing local address conflicting with the flags no_IP / no_IP6 : %s", sa_buf);
|
||||
free(ep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Configure TLS default parameters */
|
||||
if ((!fd_g_config->cnf_sec_data.tls_disabled) && (!fd_g_config->cnf_sec_data.prio_string)) {
|
||||
const char * err_pos = NULL;
|
||||
CHECK_GNUTLS_DO( gnutls_priority_init(
|
||||
&fd_g_config->cnf_sec_data.prio_cache,
|
||||
GNUTLS_DEFAULT_PRIORITY,
|
||||
&err_pos),
|
||||
{ TRACE_ERROR("Error in priority string at position : %s", err_pos); return EINVAL; } );
|
||||
}
|
||||
|
||||
/* Verify that our certificate is valid -- otherwise remote peers will reject it */
|
||||
if (!fd_g_config->cnf_sec_data.tls_disabled) {
|
||||
int ret = 0, i;
|
||||
|
||||
gnutls_datum_t certfile;
|
||||
|
||||
gnutls_x509_crt_t * certs = NULL;
|
||||
unsigned int cert_max = 0;
|
||||
|
||||
|
||||
/* Read the certificate file */
|
||||
FILE *stream = fopen (fd_g_config->cnf_sec_data.cert_file, "rb");
|
||||
if (!stream) {
|
||||
int err = errno;
|
||||
TRACE_DEBUG(INFO, "An error occurred while opening '%s': %s", fd_g_config->cnf_sec_data.cert_file, strerror(err));
|
||||
return err;
|
||||
}
|
||||
CHECK_FCT( fd_conf_stream_to_gnutls_datum(stream, &certfile) );
|
||||
fclose(stream);
|
||||
|
||||
/* Import the certificate(s) */
|
||||
GNUTLS_TRACE( ret = gnutls_x509_crt_list_import(NULL, &cert_max, &certfile, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED) );
|
||||
if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) {
|
||||
CHECK_GNUTLS_DO(ret, return EINVAL);
|
||||
}
|
||||
|
||||
CHECK_MALLOC( certs = calloc(cert_max, sizeof(gnutls_x509_crt_t)) );
|
||||
CHECK_GNUTLS_DO( gnutls_x509_crt_list_import(certs, &cert_max, &certfile, GNUTLS_X509_FMT_PEM,
|
||||
#ifdef GNUTLS_VERSION_300
|
||||
GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED
|
||||
#else /* GNUTLS_VERSION_300 */
|
||||
0
|
||||
#endif /* GNUTLS_VERSION_300 */
|
||||
),
|
||||
{
|
||||
TRACE_ERROR("Failed to import the data from file '%s'", fd_g_config->cnf_sec_data.cert_file);
|
||||
free(certfile.data);
|
||||
return EINVAL;
|
||||
} );
|
||||
free(certfile.data);
|
||||
|
||||
ASSERT(cert_max >= 1);
|
||||
|
||||
/* Now, verify the list against the local CA and CRL */
|
||||
|
||||
#ifdef GNUTLS_VERSION_300
|
||||
|
||||
/* We use the trust list for this purpose */
|
||||
{
|
||||
unsigned int output;
|
||||
|
||||
gnutls_x509_trust_list_verify_named_crt (
|
||||
fd_g_config->cnf_sec_data.trustlist,
|
||||
certs[0],
|
||||
fd_g_config->cnf_diamid,
|
||||
fd_g_config->cnf_diamid_len,
|
||||
0,
|
||||
&output,
|
||||
fd_conf_print_details_func);
|
||||
|
||||
/* if this certificate is not explicitly trusted verify against CAs
|
||||
*/
|
||||
if (output != 0)
|
||||
{
|
||||
gnutls_x509_trust_list_verify_crt (
|
||||
fd_g_config->cnf_sec_data.trustlist,
|
||||
certs,
|
||||
cert_max,
|
||||
0,
|
||||
&output,
|
||||
fd_conf_print_details_func);
|
||||
}
|
||||
|
||||
if (output & GNUTLS_CERT_INVALID)
|
||||
{
|
||||
fd_log_debug("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
|
||||
if (output & GNUTLS_CERT_SIGNER_NOT_FOUND)
|
||||
TRACE_ERROR(" - The certificate hasn't got a known issuer. Did you forget to specify TLS_CA ?");
|
||||
if (output & GNUTLS_CERT_SIGNER_NOT_CA)
|
||||
TRACE_ERROR(" - The certificate signer is not a CA, or uses version 1, or 3 without basic constraints.");
|
||||
if (output & GNUTLS_CERT_NOT_ACTIVATED)
|
||||
TRACE_ERROR(" - The certificate is not yet activated.");
|
||||
if (output & GNUTLS_CERT_EXPIRED)
|
||||
TRACE_ERROR(" - The certificate is expired.");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Now check the subject matches our hostname */
|
||||
if (!gnutls_x509_crt_check_hostname (certs[0], fd_g_config->cnf_diamid))
|
||||
{
|
||||
TRACE_ERROR("TLS: The certificate owner does not match the hostname '%s'", fd_g_config->cnf_diamid);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#else /* GNUTLS_VERSION_300 */
|
||||
|
||||
/* GnuTLS 2.x way of checking certificates */
|
||||
{
|
||||
gnutls_x509_crt_t * CA_list;
|
||||
int CA_list_length;
|
||||
|
||||
gnutls_x509_crl_t * CRL_list;
|
||||
int CRL_list_length;
|
||||
|
||||
unsigned int verify;
|
||||
time_t now;
|
||||
/*
|
||||
GNUTLS_TRACE( gnutls_certificate_get_x509_cas (fd_g_config->cnf_sec_data.credentials, &CA_list, (unsigned int *) &CA_list_length) );
|
||||
GNUTLS_TRACE( gnutls_certificate_get_x509_crls (fd_g_config->cnf_sec_data.credentials, &CRL_list, (unsigned int *) &CRL_list_length) );
|
||||
CHECK_GNUTLS_DO( gnutls_x509_crt_list_verify(certs, cert_max, CA_list, CA_list_length, CRL_list, CRL_list_length, 0, &verify),
|
||||
{
|
||||
TRACE_ERROR("Failed to verify the local certificate '%s' against local credentials. Please check your certificate is valid.", fd_g_config->cnf_sec_data.cert_file);
|
||||
return EINVAL;
|
||||
} );
|
||||
*/
|
||||
if (verify) {
|
||||
fd_log_debug("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
|
||||
if (verify & GNUTLS_CERT_INVALID)
|
||||
TRACE_ERROR(" - The certificate is not trusted (unknown CA? expired?)");
|
||||
if (verify & GNUTLS_CERT_REVOKED)
|
||||
TRACE_ERROR(" - The certificate has been revoked.");
|
||||
if (verify & GNUTLS_CERT_SIGNER_NOT_FOUND)
|
||||
TRACE_ERROR(" - The certificate hasn't got a known issuer.");
|
||||
if (verify & GNUTLS_CERT_SIGNER_NOT_CA)
|
||||
TRACE_ERROR(" - The certificate signer is not a CA, or uses version 1, or 3 without basic constraints.");
|
||||
if (verify & GNUTLS_CERT_INSECURE_ALGORITHM)
|
||||
TRACE_ERROR(" - The certificate signature uses a weak algorithm.");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Check the local Identity is valid with the certificate */
|
||||
if (!gnutls_x509_crt_check_hostname (certs[0], fd_g_config->cnf_diamid)) {
|
||||
TRACE_ERROR("TLS: Local certificate '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
|
||||
TRACE_ERROR(" - The certificate hostname does not match '%s'", fd_g_config->cnf_diamid);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Check validity of all the certificates in the chain */
|
||||
now = time(NULL);
|
||||
for (i = 0; i < cert_max; i++)
|
||||
{
|
||||
time_t deadline;
|
||||
|
||||
GNUTLS_TRACE( deadline = gnutls_x509_crt_get_expiration_time(certs[i]) );
|
||||
if ((deadline != (time_t)-1) && (deadline < now)) {
|
||||
TRACE_ERROR("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
|
||||
TRACE_ERROR(" - The certificate %d in the chain is expired", i);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
GNUTLS_TRACE( deadline = gnutls_x509_crt_get_activation_time(certs[i]) );
|
||||
if ((deadline != (time_t)-1) && (deadline > now)) {
|
||||
TRACE_ERROR("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
|
||||
TRACE_ERROR(" - The certificate %d in the chain is not yet activated", i);
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* GNUTLS_VERSION_300 */
|
||||
|
||||
/* Everything checked OK, free the certificate list */
|
||||
for (i = 0; i < cert_max; i++)
|
||||
{
|
||||
GNUTLS_TRACE( gnutls_x509_crt_deinit (certs[i]) );
|
||||
}
|
||||
free(certs);
|
||||
|
||||
#ifdef GNUTLS_VERSION_300
|
||||
/* Use certificate verification during the handshake */
|
||||
gnutls_certificate_set_verify_function (fd_g_config->cnf_sec_data.credentials, fd_tls_verify_credentials_2);
|
||||
#endif /* GNUTLS_VERSION_300 */
|
||||
|
||||
}
|
||||
|
||||
/* gnutls_certificate_set_verify_limits -- so far the default values are fine... */
|
||||
|
||||
/* DH */
|
||||
if (!fd_g_config->cnf_sec_data.tls_disabled) {
|
||||
if (fd_g_config->cnf_sec_data.dh_file) {
|
||||
gnutls_datum_t dhparams = { NULL, 0 };
|
||||
size_t alloc = 0;
|
||||
FILE *stream = fopen (fd_g_config->cnf_sec_data.dh_file, "rb");
|
||||
if (!stream) {
|
||||
int err = errno;
|
||||
TRACE_DEBUG(INFO, "An error occurred while opening '%s': %s", fd_g_config->cnf_sec_data.dh_file, strerror(err));
|
||||
return err;
|
||||
}
|
||||
do {
|
||||
uint8_t * realloced = NULL;
|
||||
size_t read = 0;
|
||||
|
||||
if (alloc < dhparams.size + BUFSIZ + 1) {
|
||||
alloc += alloc / 2 + BUFSIZ + 1;
|
||||
CHECK_MALLOC_DO( realloced = realloc(dhparams.data, alloc),
|
||||
{
|
||||
free(dhparams.data);
|
||||
return ENOMEM;
|
||||
} )
|
||||
dhparams.data = realloced;
|
||||
}
|
||||
|
||||
read = fread( dhparams.data + dhparams.size, 1, alloc - dhparams.size - 1, stream );
|
||||
dhparams.size += read;
|
||||
|
||||
if (ferror(stream)) {
|
||||
int err = errno;
|
||||
TRACE_DEBUG(INFO, "An error occurred while reading '%s': %s", fd_g_config->cnf_sec_data.dh_file, strerror(err));
|
||||
return err;
|
||||
}
|
||||
} while (!feof(stream));
|
||||
dhparams.data[dhparams.size] = '\0';
|
||||
fclose(stream);
|
||||
CHECK_GNUTLS_DO( gnutls_dh_params_import_pkcs3(
|
||||
fd_g_config->cnf_sec_data.dh_cache,
|
||||
&dhparams,
|
||||
GNUTLS_X509_FMT_PEM),
|
||||
{ TRACE_ERROR("Error in DH bits value : %d", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS); return EINVAL; } );
|
||||
free(dhparams.data);
|
||||
|
||||
} else {
|
||||
LOG_D( "Generating fresh Diffie-Hellman parameters of size %d (this takes some time)... ", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS);
|
||||
CHECK_GNUTLS_DO( gnutls_dh_params_generate2(
|
||||
fd_g_config->cnf_sec_data.dh_cache,
|
||||
fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS),
|
||||
{ TRACE_ERROR("Error in DH bits value : %d", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS); return EINVAL; } );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#ifndef GNUTLS_VERSION_300
|
||||
GCC_DIAG_ON("-Wdeprecated-declarations")
|
||||
#endif /* !GNUTLS_VERSION_300 */
|
||||
|
||||
|
||||
/* Destroy contents of fd_g_config structure */
|
||||
int fd_conf_deinit()
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (!fd_g_config)
|
||||
return 0;
|
||||
|
||||
/* Free the TLS parameters */
|
||||
#ifdef GNUTLS_VERSION_300
|
||||
gnutls_x509_trust_list_deinit(fd_g_config->cnf_sec_data.trustlist, 1);
|
||||
#endif /* GNUTLS_VERSION_300 */
|
||||
gnutls_priority_deinit(fd_g_config->cnf_sec_data.prio_cache);
|
||||
gnutls_dh_params_deinit(fd_g_config->cnf_sec_data.dh_cache);
|
||||
gnutls_certificate_free_credentials(fd_g_config->cnf_sec_data.credentials);
|
||||
|
||||
free(fd_g_config->cnf_sec_data.cert_file); fd_g_config->cnf_sec_data.cert_file = NULL;
|
||||
free(fd_g_config->cnf_sec_data.key_file); fd_g_config->cnf_sec_data.key_file = NULL;
|
||||
free(fd_g_config->cnf_sec_data.ca_file); fd_g_config->cnf_sec_data.ca_file = NULL;
|
||||
free(fd_g_config->cnf_sec_data.crl_file); fd_g_config->cnf_sec_data.crl_file = NULL;
|
||||
free(fd_g_config->cnf_sec_data.prio_string); fd_g_config->cnf_sec_data.prio_string = NULL;
|
||||
free(fd_g_config->cnf_sec_data.dh_file); fd_g_config->cnf_sec_data.dh_file = NULL;
|
||||
|
||||
/* Destroy dictionary */
|
||||
CHECK_FCT_DO( fd_dict_fini(&fd_g_config->cnf_dict), );
|
||||
|
||||
/* Destroy the main event queue */
|
||||
CHECK_FCT_DO( fd_fifo_del(&fd_g_config->cnf_main_ev), );
|
||||
|
||||
/* Destroy the local endpoints and applications */
|
||||
CHECK_FCT_DO(fd_ep_filter(&fd_g_config->cnf_endpoints, 0 ), );
|
||||
CHECK_FCT_DO(fd_app_empty(&fd_g_config->cnf_apps ), );
|
||||
|
||||
/* Destroy the local identity */
|
||||
free(fd_g_config->cnf_diamid); fd_g_config->cnf_diamid = NULL;
|
||||
free(fd_g_config->cnf_diamrlm); fd_g_config->cnf_diamrlm = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,403 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.h"
|
||||
|
||||
GCC_DIAG_OFF("-Wdeprecated-declarations")
|
||||
#include <gcrypt.h>
|
||||
GCC_DIAG_ON("-Wdeprecated-declarations")
|
||||
|
||||
/* hash: session-id===>port id */
|
||||
GHashTable *g_sess_hash = NULL;
|
||||
|
||||
/* The static configuration structure */
|
||||
static struct fd_config g_conf;
|
||||
struct fd_config * fd_g_config = NULL;
|
||||
|
||||
/* gcrypt functions to support posix threads */
|
||||
#ifndef GNUTLS_VERSION_210
|
||||
GCRY_THREAD_OPTION_PTHREAD_IMPL;
|
||||
#endif /* GNUTLS_VERSION_210 */
|
||||
|
||||
/* Thread that process incoming events on the main queue -- and terminates the framework when requested */
|
||||
static pthread_t core_runner = (pthread_t)NULL;
|
||||
|
||||
/* Signal extensions when the framework is completely initialized (they are waiting in fd_core_waitstartcomplete()) */
|
||||
static enum core_state {
|
||||
CORE_NOT_INIT, /* initial state */
|
||||
CORE_LIBS_INIT, /* fd_core_initialize was called */
|
||||
CORE_CONF_READY,/* Configuration was parsed, extensions are loaded */
|
||||
CORE_RUNNING, /* Servers and clients are started, core_runner thread is running */
|
||||
CORE_SHUTDOWN, /* The framework is terminating all objects */
|
||||
CORE_TERM /* Shutdown complete. */
|
||||
} core_state = CORE_NOT_INIT;
|
||||
static pthread_mutex_t core_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t core_cnd = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
static enum core_state core_state_get(void)
|
||||
{
|
||||
enum core_state cur_state;
|
||||
CHECK_POSIX_DO( pthread_mutex_lock( &core_mtx ), );
|
||||
cur_state = core_state;
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock( &core_mtx ), );
|
||||
return cur_state;
|
||||
}
|
||||
|
||||
static void core_state_set(enum core_state newstate)
|
||||
{
|
||||
CHECK_POSIX_DO( pthread_mutex_lock( &core_mtx ), );
|
||||
LOG_D("Core state: %d -> %d", core_state, newstate);
|
||||
core_state = newstate;
|
||||
CHECK_POSIX_DO( pthread_cond_broadcast( &core_cnd ), );
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock( &core_mtx ), );
|
||||
}
|
||||
|
||||
static int core_state_wait(enum core_state waitstate)
|
||||
{
|
||||
int ret = 0;
|
||||
CHECK_POSIX( pthread_mutex_lock( &core_mtx ));
|
||||
pthread_cleanup_push( fd_cleanup_mutex, &core_mtx );
|
||||
while (waitstate > core_state) {
|
||||
CHECK_POSIX_DO(ret = pthread_cond_wait(&core_cnd, &core_mtx), break);
|
||||
}
|
||||
pthread_cleanup_pop( 0 );
|
||||
CHECK_POSIX( pthread_mutex_unlock( &core_mtx ));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void core_shutdown(void)
|
||||
{
|
||||
LOG_N( FD_PROJECT_BINARY " framework is stopping...");
|
||||
fd_log_threadname("fD Core Shutdown");
|
||||
|
||||
/* cleanups */
|
||||
CHECK_FCT_DO( fd_servers_stop(), /* Stop accepting new connections */ );
|
||||
CHECK_FCT_DO( fd_rtdisp_cleanstop(), /* Stop dispatch thread(s) after a clean loop if possible */ );
|
||||
CHECK_FCT_DO( fd_peer_fini(), /* Stop all connections */ );
|
||||
CHECK_FCT_DO( fd_rtdisp_fini(), /* Stop routing threads and destroy routing queues */ );
|
||||
|
||||
CHECK_FCT_DO( fd_ext_term(), /* Cleanup all extensions */ );
|
||||
CHECK_FCT_DO( fd_rtdisp_cleanup(), /* destroy remaining handlers */ );
|
||||
|
||||
GNUTLS_TRACE( gnutls_global_deinit() );
|
||||
|
||||
CHECK_FCT_DO( fd_conf_deinit(), );
|
||||
|
||||
CHECK_FCT_DO( fd_event_trig_fini(), );
|
||||
|
||||
fd_log_debug(FD_PROJECT_BINARY " framework is terminated.");
|
||||
|
||||
fd_libproto_fini();
|
||||
|
||||
g_hash_table_destroy(g_sess_hash);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void * core_runner_thread(void * arg)
|
||||
{
|
||||
char cause[128]={"no cb"};
|
||||
|
||||
fd_log_threadname("fD Core Runner");
|
||||
|
||||
core_state_wait(CORE_RUNNING);
|
||||
|
||||
/* Handle events incoming on the main event queue */
|
||||
while (1) {
|
||||
int code; size_t sz; void * data;
|
||||
CHECK_FCT_DO( fd_event_get(fd_g_config->cnf_main_ev, &code, &sz, &data), break );
|
||||
switch (code) {
|
||||
case FDEV_TRIGGER:
|
||||
{
|
||||
int tv, *p;
|
||||
CHECK_PARAMS_DO( sz == sizeof(int),
|
||||
{
|
||||
TRACE_DEBUG(NONE, "Internal error: got FDEV_TRIGGER without trigger value!");
|
||||
ASSERT(0);
|
||||
sprintf(cause,"Internal error: got FDEV_TRIGGER without trigger value!");
|
||||
goto end;
|
||||
} );
|
||||
p = data;
|
||||
tv = *p;
|
||||
free(p);
|
||||
CHECK_FCT_DO( fd_event_trig_call_cb(tv), goto end );
|
||||
}
|
||||
break;
|
||||
|
||||
case FDEV_TERMINATE_INT:
|
||||
sprintf(cause,"Internal error: got FDEV_TERMINATE_INT!");
|
||||
|
||||
goto end;
|
||||
|
||||
default:
|
||||
TRACE_DEBUG(INFO, "Unexpected event in the main event queue (%d), ignored.", code);
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
core_shutdown();
|
||||
if(1)
|
||||
{
|
||||
FILE *fp=NULL;
|
||||
|
||||
fp = fopen("fd_cause.txt","w");
|
||||
if(fp)
|
||||
{
|
||||
fprintf(fp,"%s\r\n",cause);
|
||||
fclose(fp);
|
||||
}
|
||||
exit(12);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*********************************/
|
||||
/* Public interfaces */
|
||||
|
||||
/* Initialize the libfdcore internals. This also initializes libfdproto */
|
||||
int fd_core_initialize(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (core_state_get() != CORE_NOT_INIT) {
|
||||
fprintf(stderr, "fd_core_initialize() called more than once!\n");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Initialize the library -- must come first since it initializes the debug facility */
|
||||
ret = fd_libproto_init();
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Unable to initialize libfdproto: %s\n", strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Name this thread */
|
||||
fd_log_threadname("Main");
|
||||
|
||||
LOG_N("libfdproto '%s' initialized.", fd_libproto_version);
|
||||
|
||||
/* Initialize gcrypt and gnutls */
|
||||
#ifndef GNUTLS_VERSION_210
|
||||
GNUTLS_TRACE( (void) gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread) );
|
||||
GNUTLS_TRACE( (void) gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0) );
|
||||
#endif /* GNUTLS_VERSION_210 */
|
||||
CHECK_GNUTLS_DO( gnutls_global_init(), return EINVAL );
|
||||
if ( ! gnutls_check_version(GNUTLS_VERSION) ) {
|
||||
TRACE_ERROR( "The GNUTLS library is too old; found '%s', need '" GNUTLS_VERSION "'", gnutls_check_version(NULL));
|
||||
return EINVAL;
|
||||
} else {
|
||||
#ifdef GNUTLS_VERSION_210
|
||||
TRACE_DEBUG(INFO, "libgnutls '%s' initialized.", gnutls_check_version(NULL) );
|
||||
#else /* GNUTLS_VERSION_210 */
|
||||
TRACE_DEBUG(INFO, "libgnutls '%s', libgcrypt '%s', initialized.", gnutls_check_version(NULL), gcry_check_version(NULL) );
|
||||
#endif /* GNUTLS_VERSION_210 */
|
||||
}
|
||||
|
||||
/* Initialize the config with default values */
|
||||
memset(&g_conf, 0, sizeof(struct fd_config));
|
||||
fd_g_config = &g_conf;
|
||||
CHECK_FCT( fd_conf_init() );
|
||||
|
||||
/* Add definitions of the base protocol */
|
||||
CHECK_FCT( fd_dict_base_protocol(fd_g_config->cnf_dict) );
|
||||
|
||||
/* Initialize some modules */
|
||||
CHECK_FCT( fd_hooks_init() );
|
||||
CHECK_FCT( fd_queues_init() );
|
||||
CHECK_FCT( fd_sess_start() );
|
||||
CHECK_FCT( fd_p_expi_init() );
|
||||
|
||||
core_state_set(CORE_LIBS_INIT);
|
||||
|
||||
LOG_N("libfdcore '%s' initialized.", fd_core_version);
|
||||
|
||||
|
||||
/* Next thing is to parse the config, leave this for a different function */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static pthread_mutex_t core_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/* Parse the freeDiameter.conf configuration file, load the extensions */
|
||||
static int fd_core_parseconf_int(const char * conffile)
|
||||
{
|
||||
char * buf = NULL, *b;
|
||||
size_t len = 0, offset=0;
|
||||
|
||||
|
||||
TRACE_ENTRY("%p", conffile);
|
||||
|
||||
/* Conf file */
|
||||
if (conffile)
|
||||
fd_g_config->cnf_file = conffile; /* otherwise, we use the default name */
|
||||
|
||||
CHECK_FCT( fd_conf_parse() );
|
||||
|
||||
/* The following module use data from the configuration */
|
||||
CHECK_FCT( fd_rtdisp_init() );
|
||||
|
||||
/* Now, load all dynamic extensions */
|
||||
CHECK_FCT( fd_ext_load() );
|
||||
|
||||
/* Display configuration */
|
||||
b = fd_conf_dump(&buf, &len, NULL);
|
||||
LOG_SPLIT(FD_LOG_NOTICE, NULL, b ?: "<Error during configuration dump...>", NULL);
|
||||
|
||||
/* Display extensions status */
|
||||
b = fd_ext_dump(&buf, &len, NULL);
|
||||
LOG_SPLIT(FD_LOG_NOTICE, "Loaded extensions: ", b?:"<Error during extensions dump...>", NULL);
|
||||
|
||||
/* Display registered triggers for FDEV_TRIGGER */
|
||||
b = fd_event_trig_dump(&buf, &len, &offset);
|
||||
if (!b || offset) {
|
||||
LOG_N("%s", b ?: "Error during triggers dump...");
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
/* Since some extensions might have modified the definitions from the dict_base_protocol, we only load the objects now */
|
||||
CHECK_FCT( fd_msg_init() );
|
||||
|
||||
/* Ok, ready for next step */
|
||||
core_state_set(CORE_CONF_READY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fd_core_parseconf(const char * conffile)
|
||||
{
|
||||
int ret;
|
||||
CHECK_POSIX( pthread_mutex_lock(&core_lock) );
|
||||
ret = fd_core_parseconf_int(conffile);
|
||||
CHECK_POSIX( pthread_mutex_unlock(&core_lock) );
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* For threads that would need to wait complete start of the framework (ex: in extensions) */
|
||||
int fd_core_waitstartcomplete(void)
|
||||
{
|
||||
TRACE_ENTRY("");
|
||||
|
||||
return core_state_wait(CORE_RUNNING);
|
||||
}
|
||||
|
||||
/*static void free_key(gpointer data)
|
||||
{
|
||||
//printf("We free key: %s /n", data);
|
||||
free(data);
|
||||
}*/
|
||||
|
||||
/* Start the server & client threads */
|
||||
static int fd_core_start_int(void)
|
||||
{
|
||||
/* create hash: session id===>port id */
|
||||
g_sess_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
|
||||
|
||||
/* Start server threads */
|
||||
CHECK_FCT( fd_servers_start() );
|
||||
|
||||
/* Start the peer state machines */
|
||||
CHECK_FCT( fd_psm_start() );
|
||||
|
||||
/* Start the core runner thread that handles main events (until shutdown) */
|
||||
CHECK_POSIX( pthread_create(&core_runner, NULL, core_runner_thread, NULL) );
|
||||
|
||||
/* Unlock threads waiting into fd_core_waitstartcomplete */
|
||||
core_state_set(CORE_RUNNING);
|
||||
|
||||
/* Ok, everything is running now... */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fd_core_start(void)
|
||||
{
|
||||
int ret;
|
||||
CHECK_POSIX( pthread_mutex_lock(&core_lock) );
|
||||
ret = fd_core_start_int();
|
||||
CHECK_POSIX( pthread_mutex_unlock(&core_lock) );
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initialize shutdown of the framework. This is not blocking. */
|
||||
int fd_core_shutdown(void)
|
||||
{
|
||||
enum core_state cur_state = core_state_get();
|
||||
|
||||
LOG_F("Initiating freeDiameter shutdown sequence (%d)", cur_state);
|
||||
|
||||
if (cur_state < CORE_RUNNING) {
|
||||
/* Calling application must make sure the initialization is not ongoing in a separate thread... */
|
||||
if (pthread_mutex_lock(&core_lock) != 0) {
|
||||
/* This function must not be called asynchronously from fd_core_parseconf / fd_core_start ! Please review your main app design */
|
||||
ASSERT(0);
|
||||
return EINVAL;
|
||||
}
|
||||
core_shutdown();
|
||||
core_state_set(CORE_TERM);
|
||||
pthread_mutex_unlock(&core_lock);
|
||||
} else if (cur_state == CORE_RUNNING) {
|
||||
core_state_set(CORE_SHUTDOWN);
|
||||
CHECK_FCT( fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE_INT, 0, NULL) );
|
||||
}
|
||||
|
||||
/* Other case, the framework is already shutting down */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Wait for the shutdown to be complete -- this must be called after fd_core_shutdown to reclaim some resources. */
|
||||
int fd_core_wait_shutdown_complete(void)
|
||||
{
|
||||
enum core_state cur_state = core_state_get();
|
||||
void * th_ret = NULL;
|
||||
|
||||
CHECK_FCT(core_state_wait(CORE_SHUTDOWN));
|
||||
|
||||
if (cur_state == CORE_TERM)
|
||||
return 0;
|
||||
|
||||
/* Just wait for core_runner_thread to complete and return gracefully */
|
||||
CHECK_POSIX(pthread_join(core_runner, &th_ret));
|
||||
|
||||
core_state_set(CORE_TERM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,271 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.h"
|
||||
|
||||
|
||||
/* Add an endpoint information in a list */
|
||||
int fd_ep_add_merge( struct fd_list * list, sSA * sa, socklen_t sl, uint32_t flags )
|
||||
{
|
||||
struct fd_endpoint * ep;
|
||||
struct fd_list * li;
|
||||
union {
|
||||
sSA * sa;
|
||||
sSA4 *sin;
|
||||
sSA6 *sin6;
|
||||
} ptr;
|
||||
in_port_t * port;
|
||||
int cmp = -1;
|
||||
|
||||
TRACE_ENTRY("%p %p %u %x", list, sa, sl, flags);
|
||||
CHECK_PARAMS( list && sa && (sl <= sizeof(sSS)) );
|
||||
|
||||
if (list->next == NULL) {
|
||||
/* the list is not initialized yet, do it */
|
||||
fd_list_init(list, NULL);
|
||||
}
|
||||
|
||||
ptr.sa = sa;
|
||||
|
||||
/* Filter out a bunch of invalid addresses */
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET:
|
||||
if (! (flags & EP_ACCEPTALL)) {
|
||||
if (IN_IS_ADDR_UNSPECIFIED(&ptr.sin->sin_addr)
|
||||
|| IN_IS_ADDR_LOOPBACK(&ptr.sin->sin_addr)
|
||||
/* the next one filters both EXPERIMENTAL, BADCLASS and MULTICAST. */
|
||||
|| ((ntohl(ptr.sin->sin_addr.s_addr) & 0xe0000000) == 0xe0000000)
|
||||
|| (ptr.sin->sin_addr.s_addr == INADDR_BROADCAST)) {
|
||||
LOG_A(" DEBUG:fd_ep_add_merge Address was ignored, not added.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
port = &ptr.sin->sin_port;
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
if (! (flags & EP_ACCEPTALL)) {
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(&ptr.sin6->sin6_addr)
|
||||
|| IN6_IS_ADDR_LOOPBACK(&ptr.sin6->sin6_addr)
|
||||
|| IN6_IS_ADDR_MULTICAST(&ptr.sin6->sin6_addr)
|
||||
|| IN6_IS_ADDR_LINKLOCAL(&ptr.sin6->sin6_addr)
|
||||
|| IN6_IS_ADDR_SITELOCAL(&ptr.sin6->sin6_addr)) {
|
||||
LOG_A(" DEBUG:fd_ep_add_merge Address was ignored, not added.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
port = &ptr.sin6->sin6_port;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_A(" DEBUG:fd_ep_add_merge Address family was unknown, not added.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* remove the ACCEPTALL flag */
|
||||
flags &= ~EP_ACCEPTALL;
|
||||
|
||||
/* Search place in the list */
|
||||
for (li = list->next; li != list; li = li->next) {
|
||||
ep = (struct fd_endpoint *)li;
|
||||
in_port_t * ep_port;
|
||||
|
||||
/* First, compare the address family */
|
||||
if (ep->sa.sa_family < sa->sa_family)
|
||||
continue;
|
||||
if (ep->sa.sa_family > sa->sa_family)
|
||||
break;
|
||||
|
||||
/* Then compare the address field */
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET:
|
||||
cmp = memcmp(&ep->sin.sin_addr, &ptr.sin->sin_addr, sizeof(struct in_addr));
|
||||
ep_port = &ep->sin.sin_port;
|
||||
break;
|
||||
case AF_INET6:
|
||||
cmp = memcmp(&ep->sin6.sin6_addr, &ptr.sin6->sin6_addr, sizeof(struct in6_addr));
|
||||
ep_port = &ep->sin6.sin6_port;
|
||||
break;
|
||||
default:
|
||||
ASSERT( 0 ); /* we got a different value previously in this same function */
|
||||
}
|
||||
if (cmp < 0)
|
||||
continue;
|
||||
if (cmp > 0)
|
||||
break;
|
||||
|
||||
/* Finally compare the port, only if not 0 */
|
||||
if (*port == 0)
|
||||
break;
|
||||
if (*ep_port == 0) {
|
||||
/* save the port information in the list, and break */
|
||||
*ep_port = *port;
|
||||
break;
|
||||
}
|
||||
if (*ep_port < *port) {
|
||||
cmp = -1;
|
||||
continue;
|
||||
}
|
||||
if (*ep_port > *port)
|
||||
cmp = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmp) {
|
||||
/* new item to be added */
|
||||
CHECK_MALLOC( ep = malloc(sizeof(struct fd_endpoint)) );
|
||||
memset(ep, 0, sizeof(struct fd_endpoint));
|
||||
fd_list_init(&ep->chain, NULL);
|
||||
memcpy(&ep->ss, sa, sl);
|
||||
|
||||
/* Insert in the list */
|
||||
fd_list_insert_before(li, &ep->chain);
|
||||
}
|
||||
|
||||
/* Merge the flags */
|
||||
ep->flags |= flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Delete endpoints that do not have a matching flag from a list (0: delete all endpoints) */
|
||||
int fd_ep_filter( struct fd_list * list, uint32_t flags )
|
||||
{
|
||||
struct fd_list * li;
|
||||
|
||||
TRACE_ENTRY("%p %x", list, flags);
|
||||
CHECK_PARAMS(list);
|
||||
|
||||
for (li = list->next; li != list; li = li->next) {
|
||||
struct fd_endpoint * ep = (struct fd_endpoint *)li;
|
||||
|
||||
if (! (ep->flags & flags)) {
|
||||
li = li->prev;
|
||||
fd_list_unlink(&ep->chain);
|
||||
free(ep);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Keep only endpoints of the same family as af */
|
||||
int fd_ep_filter_family( struct fd_list * list, int af )
|
||||
{
|
||||
struct fd_list * li;
|
||||
|
||||
TRACE_ENTRY("%p %d", list, af);
|
||||
CHECK_PARAMS(list);
|
||||
|
||||
for (li = list->next; li != list; li = li->next) {
|
||||
struct fd_endpoint * ep = (struct fd_endpoint *)li;
|
||||
|
||||
if (ep->sa.sa_family != af) {
|
||||
li = li->prev;
|
||||
fd_list_unlink(&ep->chain);
|
||||
free(ep);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reset the given flag(s) from all items in the list */
|
||||
int fd_ep_clearflags( struct fd_list * list, uint32_t flags )
|
||||
{
|
||||
struct fd_list * li;
|
||||
|
||||
TRACE_ENTRY("%p %x", list, flags);
|
||||
CHECK_PARAMS(list);
|
||||
|
||||
for (li = list->next; li != list; li = li->next) {
|
||||
struct fd_endpoint * ep = (struct fd_endpoint *)li;
|
||||
ep->flags &= ~flags;
|
||||
if (ep->flags == 0) {
|
||||
li = li->prev;
|
||||
fd_list_unlink(&ep->chain);
|
||||
free(ep);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DECLARE_FD_DUMP_PROTOTYPE(fd_ep_dump_one, int preamble, struct fd_endpoint * ep )
|
||||
{
|
||||
FD_DUMP_HANDLE_OFFSET();
|
||||
|
||||
if (preamble) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{ep}(@%p): ", ep), return NULL);
|
||||
}
|
||||
|
||||
if (!ep) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID/NULL"), return NULL);
|
||||
return *buf;
|
||||
}
|
||||
|
||||
CHECK_MALLOC_DO( fd_sa_dump( FD_DUMP_STD_PARAMS, &ep->sa, NI_NUMERICHOST | NI_NUMERICSERV ), return NULL);
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{%s%s%s%s%s}",
|
||||
(ep->flags & EP_FL_CONF) ? "C" : "-",
|
||||
(ep->flags & EP_FL_DISC) ? "D" : "-",
|
||||
(ep->flags & EP_FL_ADV) ? "A" : "-",
|
||||
(ep->flags & EP_FL_LL) ? "L" : "-",
|
||||
(ep->flags & EP_FL_PRIMARY) ? "P" : "-"), return NULL);
|
||||
return *buf;
|
||||
}
|
||||
|
||||
DECLARE_FD_DUMP_PROTOTYPE(fd_ep_dump, int preamble, int indent, struct fd_list * eps )
|
||||
{
|
||||
struct fd_list * li;
|
||||
|
||||
FD_DUMP_HANDLE_OFFSET();
|
||||
|
||||
if (preamble) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%*s{eps}(@%p):", indent, "", eps), return NULL);
|
||||
}
|
||||
if (eps) {
|
||||
for (li = eps->next; li != eps; li = li->next) {
|
||||
struct fd_endpoint * ep = (struct fd_endpoint *)li;
|
||||
if (preamble) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n%*s", indent+1, ""), return NULL);
|
||||
} else if (li->prev != eps) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\t"), return NULL);
|
||||
}
|
||||
CHECK_MALLOC_DO( fd_ep_dump_one( FD_DUMP_STD_PARAMS, preamble, ep ), return NULL);
|
||||
}
|
||||
}
|
||||
return *buf;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.h"
|
||||
|
||||
/* Events are a subset of fifo queues, with a known type */
|
||||
|
||||
int fd_event_send(struct fifo *queue, int code, size_t datasz, void * data)
|
||||
{
|
||||
struct fd_event * ev;
|
||||
CHECK_MALLOC( ev = malloc(sizeof(struct fd_event)) );
|
||||
ev->code = code;
|
||||
ev->size = datasz;
|
||||
ev->data = data;
|
||||
CHECK_FCT( fd_fifo_post(queue, &ev) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fd_event_get(struct fifo *queue, int *code, size_t *datasz, void ** data)
|
||||
{
|
||||
struct fd_event * ev;
|
||||
CHECK_FCT( fd_fifo_get(queue, &ev) );
|
||||
if (code)
|
||||
*code = ev->code;
|
||||
if (datasz)
|
||||
*datasz = ev->size;
|
||||
if (data)
|
||||
*data = ev->data;
|
||||
free(ev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fd_event_timedget(struct fifo *queue, struct timespec * timeout, int timeoutcode, int *code, size_t *datasz, void ** data)
|
||||
{
|
||||
struct fd_event * ev;
|
||||
int ret = 0;
|
||||
ret = fd_fifo_timedget(queue, &ev, timeout);
|
||||
if (ret == ETIMEDOUT) {
|
||||
if (code)
|
||||
*code = timeoutcode;
|
||||
if (datasz)
|
||||
*datasz = 0;
|
||||
if (data)
|
||||
*data = NULL;
|
||||
} else {
|
||||
CHECK_FCT( ret );
|
||||
if (code)
|
||||
*code = ev->code;
|
||||
if (datasz)
|
||||
*datasz = ev->size;
|
||||
if (data)
|
||||
*data = ev->data;
|
||||
free(ev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fd_event_destroy(struct fifo **queue, void (*free_cb)(void * data))
|
||||
{
|
||||
struct fd_event * ev;
|
||||
/* Purge all events, and free the associated data if any */
|
||||
while (fd_fifo_tryget( *queue, &ev ) == 0) {
|
||||
(*free_cb)(ev->data);
|
||||
free(ev);
|
||||
}
|
||||
CHECK_FCT_DO( fd_fifo_del(queue), /* continue */ );
|
||||
return ;
|
||||
}
|
||||
|
||||
const char * fd_ev_str(int event)
|
||||
{
|
||||
switch (event) {
|
||||
#define case_str( _val )\
|
||||
case _val : return #_val
|
||||
case_str(FDEV_TERMINATE_INT);
|
||||
case_str(FDEV_TRIGGER);
|
||||
|
||||
default:
|
||||
TRACE_DEBUG(FULL, "Unknown event : %d", event);
|
||||
return "Unknown event";
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
/* Trigged events */
|
||||
/* This allows extensions to register for / send triggers to other extensions */
|
||||
/* It is used for example for users interactions (through signals or ...) */
|
||||
|
||||
/* Because the number of triggers is not expected to grow, we use a simple ordered chained list */
|
||||
static pthread_rwlock_t trig_rwl = PTHREAD_RWLOCK_INITIALIZER;
|
||||
static struct fd_list trig_list = FD_LIST_INITIALIZER(trig_list); /* The list of triggers ordered by trigger value */
|
||||
struct trig_item {
|
||||
struct fd_list chain;
|
||||
int trig_value;
|
||||
const char * trig_module;
|
||||
void (*cb)(void);
|
||||
};
|
||||
|
||||
/* Add a new entry in the trigger list */
|
||||
int fd_event_trig_regcb(int trigger_val, const char * module, void (*cb)(void))
|
||||
{
|
||||
struct trig_item * ti;
|
||||
struct fd_list * li;
|
||||
|
||||
TRACE_ENTRY("%d %p %p", trigger_val, module, cb);
|
||||
CHECK_PARAMS( trigger_val && cb );
|
||||
|
||||
/* Create a new trig_item */
|
||||
CHECK_MALLOC( ti = malloc(sizeof(struct trig_item)) );
|
||||
memset(ti, 0, sizeof(struct trig_item));
|
||||
fd_list_init(&ti->chain, ti);
|
||||
ti->trig_value = trigger_val;
|
||||
ti->trig_module = module;
|
||||
ti->cb = cb;
|
||||
|
||||
/* Now insert in the list */
|
||||
CHECK_POSIX( pthread_rwlock_wrlock(&trig_rwl) );
|
||||
|
||||
for (li = trig_list.next; li != &trig_list; li = li->next) {
|
||||
struct trig_item *t = li->o;
|
||||
if (t->trig_value >= trigger_val)
|
||||
break;
|
||||
}
|
||||
|
||||
fd_list_insert_before(li, &ti->chain);
|
||||
|
||||
CHECK_POSIX( pthread_rwlock_unlock(&trig_rwl) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DECLARE_FD_DUMP_PROTOTYPE(fd_event_trig_dump)
|
||||
{
|
||||
struct fd_list * li;
|
||||
FD_DUMP_HANDLE_OFFSET();
|
||||
|
||||
CHECK_POSIX_DO( pthread_rwlock_rdlock(&trig_rwl), );
|
||||
|
||||
for (li = trig_list.next; li != &trig_list; li = li->next) {
|
||||
struct trig_item *t = li->o;
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{signal:%d}'%s'->%p ", t->trig_value, t->trig_module, t->cb), break);
|
||||
}
|
||||
|
||||
CHECK_POSIX_DO( pthread_rwlock_unlock(&trig_rwl), );
|
||||
return *buf;
|
||||
}
|
||||
|
||||
static void *call_cb_detached(void * arg)
|
||||
{
|
||||
void (*cb)(void) = arg;
|
||||
fd_log_threadname("Trig'd callback thread");
|
||||
TRACE_ENTRY("%p", arg);
|
||||
(*cb)();
|
||||
TRACE_DEBUG(ANNOYING, "Callback %p completed", cb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fd_event_trig_call_cb(int trigger_val)
|
||||
{
|
||||
struct fd_list * li;
|
||||
pthread_attr_t detached;
|
||||
pthread_t th;
|
||||
|
||||
CHECK_POSIX( pthread_attr_init(&detached) );
|
||||
CHECK_POSIX( pthread_attr_setdetachstate(&detached, PTHREAD_CREATE_DETACHED) );
|
||||
|
||||
CHECK_POSIX( pthread_rwlock_rdlock(&trig_rwl) );
|
||||
|
||||
for (li = trig_list.next; li != &trig_list; li = li->next) {
|
||||
struct trig_item *t = li->o;
|
||||
if (t->trig_value == trigger_val) {
|
||||
TRACE_DEBUG(FULL, "Trigger %d: Calling %p in %s", t->trig_value, t->cb, t->trig_module);
|
||||
CHECK_POSIX_DO( pthread_create( &th, &detached, call_cb_detached, t->cb ), break );
|
||||
}
|
||||
if (t->trig_value > trigger_val)
|
||||
break;
|
||||
}
|
||||
|
||||
CHECK_POSIX( pthread_rwlock_unlock(&trig_rwl) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fd_event_trig_fini(void) {
|
||||
|
||||
TRACE_ENTRY("");
|
||||
|
||||
CHECK_POSIX( pthread_rwlock_wrlock(&trig_rwl) );
|
||||
|
||||
while (!FD_IS_LIST_EMPTY(&trig_list)) {
|
||||
struct fd_list * li = trig_list.next;
|
||||
fd_list_unlink(li);
|
||||
free(li);
|
||||
}
|
||||
|
||||
CHECK_POSIX( pthread_rwlock_unlock(&trig_rwl) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.h"
|
||||
|
||||
#include <dlfcn.h> /* We may use libtool's <ltdl.h> later for better portability.... */
|
||||
#include <libgen.h> /* for "basename" */
|
||||
|
||||
/* plugins management */
|
||||
|
||||
/* List of extensions to load, from the configuration parsing */
|
||||
struct fd_ext_info {
|
||||
struct fd_list chain; /* link in the list */
|
||||
char *filename; /* extension filename. must be a dynamic library with fd_ext_init symbol. */
|
||||
char *conffile; /* optional configuration file name for the extension */
|
||||
void *handler; /* object returned by dlopen() */
|
||||
const char **depends; /* names of the other extensions this one depends on (if provided) */
|
||||
char *ext_name; /* points to the extension name, either inside depends, or basename(filename) */
|
||||
int free_ext_name; /* must be freed if it was malloc'd */
|
||||
void (*fini)(void); /* optional address of the fd_ext_fini callback */
|
||||
};
|
||||
|
||||
/* list of extensions */
|
||||
static struct fd_list ext_list = FD_LIST_INITIALIZER(ext_list);
|
||||
|
||||
/* Add new extension */
|
||||
int fd_ext_add( char * filename, char * conffile )
|
||||
{
|
||||
struct fd_ext_info * new;
|
||||
|
||||
TRACE_ENTRY("%p %p", filename, conffile);
|
||||
|
||||
/* Check the filename is valid */
|
||||
CHECK_PARAMS( filename );
|
||||
|
||||
/* Create a new object in the list */
|
||||
CHECK_MALLOC( new = malloc( sizeof(struct fd_ext_info) ) );
|
||||
memset(new, 0, sizeof(struct fd_ext_info));
|
||||
fd_list_init(&new->chain, NULL);
|
||||
new->filename = filename;
|
||||
new->conffile = conffile;
|
||||
fd_list_insert_before( &ext_list, &new->chain );
|
||||
TRACE_DEBUG (FULL, "Extension %s added to the list.", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Dump the list */
|
||||
DECLARE_FD_DUMP_PROTOTYPE(fd_ext_dump)
|
||||
{
|
||||
struct fd_list * li;
|
||||
FD_DUMP_HANDLE_OFFSET();
|
||||
|
||||
if (FD_IS_LIST_EMPTY(&ext_list)) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "-none-"), return NULL);
|
||||
} else {
|
||||
for (li = ext_list.next; li != &ext_list; li = li->next)
|
||||
{
|
||||
struct fd_ext_info * ext = (struct fd_ext_info *)li;
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s'[%s], %sloaded%s",
|
||||
ext->filename,
|
||||
ext->conffile?:"(no config file)",
|
||||
ext->handler ? "" : "not ", (li->next == &ext_list) ? "":"\n"), return NULL);
|
||||
}
|
||||
}
|
||||
return *buf;
|
||||
}
|
||||
|
||||
/* Check the dependencies. The object must have been dlopened already. */
|
||||
#if 0
|
||||
static int check_dependencies(struct fd_ext_info * ext)
|
||||
{
|
||||
int i = 1;
|
||||
TRACE_ENTRY( "%p", ext );
|
||||
|
||||
/* Attempt to resolve the dependency array */
|
||||
ext->depends = dlsym( ext->handler, "fd_ext_depends" );
|
||||
if (!ext->depends) {
|
||||
/* Duplicate the filename */
|
||||
char * tmp = strdup(ext->filename);
|
||||
ext->ext_name = strdup(basename(tmp));
|
||||
free(tmp);
|
||||
ext->free_ext_name = 1;
|
||||
TRACE_DEBUG(FULL, "Old extension's [%s] API: missing dependencies (ignored)", ext->ext_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ext->ext_name = (char *)ext->depends[0];
|
||||
|
||||
TRACE_DEBUG(FULL, "Checking dependencies for '%s'...", ext->ext_name);
|
||||
|
||||
while (ext->depends[i]) {
|
||||
struct fd_list * li;
|
||||
for (li = ext_list.next; li != &ext->chain; li = li->next)
|
||||
{
|
||||
struct fd_ext_info * e = (struct fd_ext_info *)li;
|
||||
if (!strcasecmp(e->ext_name, ext->depends[i])) {
|
||||
/* the dependency was already loaded */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (li == &ext->chain) {
|
||||
/* the dependency was not found */
|
||||
LOG_F("Error: extension [%s] depends on [%s] which was not loaded first. Please fix your configuration file.",
|
||||
ext->ext_name, ext->depends[i]);
|
||||
return ESRCH;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
/* All dependencies resolved successfully */
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Load all extensions in the list */
|
||||
/* fj added 2016/06/22 */
|
||||
#if 1
|
||||
extern int fd_ext_init(int major, int minor, char * conffile);
|
||||
int fd_ext_load()
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
fd_ext_init(FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int fd_ext_load()
|
||||
{
|
||||
int ret;
|
||||
int (*fd_ext_init)(int, int, char *) = NULL;
|
||||
struct fd_list * li;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
/* Loop on all extensions */
|
||||
for (li = ext_list.next; li != &ext_list; li = li->next)
|
||||
{
|
||||
struct fd_ext_info * ext = (struct fd_ext_info *)li;
|
||||
LOG_D( "Loading : %s", ext->filename);
|
||||
|
||||
/* Load the extension */
|
||||
#ifndef DEBUG
|
||||
ext->handler = dlopen(ext->filename, RTLD_LAZY | RTLD_GLOBAL);
|
||||
#else /* DEBUG */
|
||||
/* We resolve symbols immediatly so it's easier to find problems in ABI */
|
||||
ext->handler = dlopen(ext->filename, RTLD_NOW | RTLD_GLOBAL);
|
||||
#endif /* DEBUG */
|
||||
if (ext->handler == NULL) {
|
||||
/* An error occured */
|
||||
LOG_F("Loading of extension %s failed: %s", ext->filename, dlerror());
|
||||
ext->handler = dlopen(ext->filename, RTLD_LAZY | RTLD_GLOBAL);
|
||||
if (ext->handler) {
|
||||
if (!check_dependencies(ext)) {
|
||||
LOG_F("In addition, not all declared dependencies are satisfied (Internal Error!)");
|
||||
}
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Check if declared dependencies are satisfied. */
|
||||
CHECK_FCT( check_dependencies(ext) );
|
||||
|
||||
/* Resolve the entry point of the extension */
|
||||
fd_ext_init = ( int (*) (int, int, char *) )dlsym( ext->handler, "fd_ext_init" );
|
||||
|
||||
if (fd_ext_init == NULL) {
|
||||
/* An error occured */
|
||||
TRACE_ERROR("Unable to resolve symbol 'fd_ext_init' for extension %s: %s", ext->filename, dlerror());
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Resolve the exit point of the extension, which is optional for extensions */
|
||||
ext->fini = ( void (*) (void) )dlsym( ext->handler, "fd_ext_fini" );
|
||||
|
||||
if (ext->fini == NULL) {
|
||||
/* Not provided */
|
||||
TRACE_DEBUG (FULL, "Extension [%s] has no fd_ext_fini function.", ext->filename);
|
||||
} else {
|
||||
/* Provided */
|
||||
TRACE_DEBUG (FULL, "Extension [%s] fd_ext_fini has been resolved successfully.", ext->filename);
|
||||
}
|
||||
|
||||
/* Now call the entry point to initialize the extension */
|
||||
ret = (*fd_ext_init)( FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, ext->conffile );
|
||||
if (ret != 0) {
|
||||
/* The extension was unable to load cleanly */
|
||||
TRACE_ERROR("Extension %s returned an error during initialization: %s", ext->filename, strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Proceed to the next extension */
|
||||
}
|
||||
|
||||
LOG_N("All extensions loaded.");
|
||||
|
||||
/* We have finished. */
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Now unload the extensions and free the memory */
|
||||
/* fj added 2016/06/22 */
|
||||
#if 1
|
||||
extern void fd_ext_fini(void);
|
||||
int fd_ext_term( void )
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
fd_ext_fini();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
int fd_ext_term( void )
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
/* Loop on all extensions, in FIFO order */
|
||||
while (!FD_IS_LIST_EMPTY(&ext_list))
|
||||
{
|
||||
struct fd_list * li = ext_list.next;
|
||||
struct fd_ext_info * ext = (struct fd_ext_info *)li;
|
||||
|
||||
/* Unlink this element from the list */
|
||||
fd_list_unlink(li);
|
||||
|
||||
/* Call the exit point of the extension, if it was resolved */
|
||||
if (ext->fini != NULL) {
|
||||
TRACE_DEBUG (FULL, "Calling [%s]->fd_ext_fini function.", ext->ext_name ?: ext->filename);
|
||||
(*ext->fini)();
|
||||
}
|
||||
|
||||
#ifndef SKIP_DLCLOSE
|
||||
/* Now unload the extension */
|
||||
if (ext->handler) {
|
||||
TRACE_DEBUG (FULL, "Unloading %s", ext->ext_name ?: ext->filename);
|
||||
if ( dlclose(ext->handler) != 0 ) {
|
||||
TRACE_DEBUG (INFO, "Unloading [%s] failed : %s", ext->ext_name ?: ext->filename, dlerror());
|
||||
}
|
||||
}
|
||||
#endif /* SKIP_DLCLOSE */
|
||||
|
||||
/* Free the object and continue */
|
||||
if (ext->free_ext_name)
|
||||
free(ext->ext_name);
|
||||
free(ext->filename);
|
||||
free(ext->conffile);
|
||||
free(ext);
|
||||
}
|
||||
|
||||
/* We always return 0 since we would not handle an error anyway... */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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. *
|
||||
*********************************************************************************************************/
|
||||
|
||||
/* This file contains the definitions for internal use in the freeDiameter core library */
|
||||
|
||||
#ifndef _FDCORE_INTERNAL_H
|
||||
#define _FDCORE_INTERNAL_H
|
||||
|
||||
#include "freeDiameter-host.h"
|
||||
#include "libfdcore.h"
|
||||
|
||||
#ifdef DISABLE_SCTP
|
||||
#undef IPPROTO_SCTP
|
||||
#define IPPROTO_SCTP (2 = 4) /* some compilation error to spot the references */
|
||||
#endif /* DISABLE_SCTP */
|
||||
|
||||
#ifndef HAVE_AI_ADDRCONFIG
|
||||
#define AI_ADDRCONFIG 0 /* ignore this flag at the moment */
|
||||
#endif /* HAVE_AI_ADDRCONFIG */
|
||||
|
||||
/* Timeout for establishing a connection */
|
||||
#ifndef CNX_TIMEOUT
|
||||
#define CNX_TIMEOUT 10 /* in seconds */
|
||||
#endif /* CNX_TIMEOUT */
|
||||
|
||||
/* Timeout for receiving a CER after incoming connection is established */
|
||||
#ifndef INCNX_TIMEOUT
|
||||
#define INCNX_TIMEOUT 20 /* in seconds */
|
||||
#endif /* INCNX_TIMEOUT */
|
||||
|
||||
/* Timeout for receiving a CEA after CER is sent */
|
||||
#ifndef CEA_TIMEOUT
|
||||
#define CEA_TIMEOUT 20 /* in seconds */
|
||||
#endif /* CEA_TIMEOUT */
|
||||
|
||||
/* The timeout value to wait for answer to a DPR */
|
||||
#ifndef DPR_TIMEOUT
|
||||
#define DPR_TIMEOUT 15 /* in seconds */
|
||||
#endif /* DPR_TIMEOUT */
|
||||
|
||||
/* Delay where the connection is maintained opened to allow exchanging remaining pending answers after DPR/DPA */
|
||||
#ifndef GRACE_TIMEOUT
|
||||
#define GRACE_TIMEOUT 1 /* in seconds */
|
||||
#endif /* GRACE_TIMEOUT */
|
||||
|
||||
/* The Vendor-Id to advertise in CER/CEA */
|
||||
#ifndef MY_VENDOR_ID
|
||||
#define MY_VENDOR_ID 0 /* Reserved value to tell it must be ignored */
|
||||
#endif /* MY_VENDOR_ID */
|
||||
|
||||
|
||||
|
||||
/* Configuration */
|
||||
int fd_conf_init();
|
||||
int fd_conf_deinit();
|
||||
int fd_conf_parse();
|
||||
int fddparse(struct fd_config * conf); /* yacc generated */
|
||||
int fd_conf_stream_to_gnutls_datum(FILE * pemfile, gnutls_datum_t *out);
|
||||
|
||||
|
||||
/* Extensions */
|
||||
int fd_ext_add( char * filename, char * conffile );
|
||||
int fd_ext_load();
|
||||
int fd_ext_term(void);
|
||||
|
||||
/* Messages */
|
||||
int fd_msg_init(void);
|
||||
extern struct dict_object * fd_dict_avp_OSI; /* Origin-State-Id */
|
||||
extern struct dict_object * fd_dict_cmd_CER; /* Capabilities-Exchange-Request */
|
||||
extern struct dict_object * fd_dict_cmd_DWR; /* Device-Watchdog-Request */
|
||||
extern struct dict_object * fd_dict_avp_DC; /* Disconnect-Cause */
|
||||
extern struct dict_object * fd_dict_cmd_DPR; /* Disconnect-Peer-Request */
|
||||
|
||||
/* Global message queues */
|
||||
extern struct fifo * fd_g_incoming; /* all messages received from other peers, except local messages (CER, ...) */
|
||||
extern struct fifo * fd_g_outgoing; /* messages to be sent to other peers on the network following routing procedure */
|
||||
extern struct fifo * fd_g_local; /* messages to be handled to local extensions */
|
||||
/* Message queues */
|
||||
int fd_queues_init(void);
|
||||
int fd_queues_fini(struct fifo ** queue);
|
||||
|
||||
/* Trigged events */
|
||||
int fd_event_trig_call_cb(int trigger_val);
|
||||
int fd_event_trig_fini(void);
|
||||
|
||||
/* Create all the dictionary objects defined in the Diameter base RFC. */
|
||||
int fd_dict_base_protocol(struct dictionary * dict);
|
||||
|
||||
/* Routing */
|
||||
int fd_rtdisp_init(void);
|
||||
int fd_rtdisp_cleanstop(void);
|
||||
int fd_rtdisp_fini(void);
|
||||
int fd_rtdisp_cleanup(void);
|
||||
|
||||
/* Sentinel for the sent requests list */
|
||||
struct sr_list {
|
||||
struct fd_list srs; /* requests ordered by hop-by-hop id */
|
||||
struct fd_list exp; /* requests that have a timeout set, ordered by timeout */
|
||||
long cnt; /* number of requests in the srs list */
|
||||
long cnt_lost; /* number of requests that have not been answered in time.
|
||||
It is decremented when an unexpected answer is received, so this may not be accurate. */
|
||||
pthread_mutex_t mtx; /* mutex to protect these lists */
|
||||
pthread_cond_t cnd; /* cond var used by the thread that handles timeouts */
|
||||
pthread_t thr; /* the thread that handles timeouts (expirecb called in separate forked threads) */
|
||||
};
|
||||
|
||||
/* Peers */
|
||||
struct fd_peer { /* The "real" definition of the peer structure */
|
||||
|
||||
/* The public data */
|
||||
struct peer_hdr p_hdr;
|
||||
|
||||
/* Eye catcher, EYEC_PEER */
|
||||
int p_eyec;
|
||||
#define EYEC_PEER 0x373C9336
|
||||
|
||||
/* Origin of this peer object, for debug */
|
||||
char *p_dbgorig;
|
||||
|
||||
/* State of the peer, and its lock */
|
||||
enum peer_state p_state;
|
||||
pthread_mutex_t p_state_mtx;
|
||||
|
||||
/* Chaining in peers sublists */
|
||||
struct fd_list p_actives; /* list of peers in the STATE_OPEN state -- used by routing */
|
||||
struct fd_list p_expiry; /* list of expiring peers, ordered by their timeout value */
|
||||
struct timespec p_exp_timer; /* Timestamp where the peer will expire; updated each time activity is seen on the peer (except DW) */
|
||||
|
||||
/* Some flags influencing the peer state machine */
|
||||
struct {
|
||||
unsigned pf_responder : 1; /* The peer has been created to handle incoming connection */
|
||||
unsigned pf_delete : 1; /* Destroy the peer when the connection is terminated */
|
||||
unsigned pf_localterm : 1; /* If the latest DPR/DPA was initiated from this side */
|
||||
|
||||
unsigned pf_dw_pending : 1; /* A DWR message was sent and not answered yet */
|
||||
|
||||
unsigned pf_cnx_pb : 1; /* The peer was disconnected because of watchdogs; must exchange 3 watchdogs before putting back to normal */
|
||||
unsigned pf_reopen_cnt : 2; /* remaining DW to be exchanged after re-established connection */
|
||||
|
||||
} p_flags;
|
||||
|
||||
/* The events queue, peer state machine thread, timer for states timeouts */
|
||||
struct fifo *p_events; /* The mutex of this FIFO list protects also the state and timer information */
|
||||
pthread_t p_psm;
|
||||
struct timespec p_psm_timer;
|
||||
|
||||
/* Outgoing message queue, and thread managing sending the messages */
|
||||
struct fifo *p_tosend;
|
||||
pthread_t p_outthr;
|
||||
|
||||
/* The next hop-by-hop id value for the link, only read & modified by p_outthr */
|
||||
uint32_t p_hbh;
|
||||
|
||||
/* Sent requests (for fallback), list of struct sentreq ordered by hbh */
|
||||
struct sr_list p_sr;
|
||||
struct fifo *p_tofailover;
|
||||
|
||||
/* Pending received requests not yet answered (count only) */
|
||||
long p_reqin_count; /* We use p_state_mtx to protect this value */
|
||||
|
||||
/* Data for transitional states before the peer is in OPEN state */
|
||||
struct {
|
||||
struct cnxctx * p_receiver; /* Only used in case of election */
|
||||
struct msg * p_cer; /* Only used in case of election */
|
||||
|
||||
pthread_t p_ini_thr; /* Initiator thread for establishing a connection */
|
||||
struct fd_list p_connparams; /* The list of connection attempts, see p_cnx.c */
|
||||
};
|
||||
|
||||
/* connection context: socket and related information */
|
||||
struct cnxctx *p_cnxctx;
|
||||
|
||||
/* Callback for peer validation after the handshake */
|
||||
int (*p_cb2)(struct peer_info *);
|
||||
|
||||
/* Callback on initial connection success / failure after the peer was added */
|
||||
void (*p_cb)(struct peer_info *, void *);
|
||||
void *p_cb_data;
|
||||
|
||||
};
|
||||
#define CHECK_PEER( _p ) \
|
||||
(((_p) != NULL) && (((struct fd_peer *)(_p))->p_eyec == EYEC_PEER))
|
||||
|
||||
#define fd_peer_getstate(peer) fd_peer_get_state((struct peer_hdr *)(peer))
|
||||
|
||||
|
||||
/* Events codespace for struct fd_peer->p_events */
|
||||
enum {
|
||||
/* request to terminate this peer : disconnect, requeue all messages */
|
||||
FDEVP_TERMINATE = 1500
|
||||
|
||||
/* A connection object has received a message. (data contains the buffer + padding + struct fd_msg_pmdl) */
|
||||
,FDEVP_CNX_MSG_RECV
|
||||
|
||||
/* A connection object has encountered an error (disconnected). */
|
||||
,FDEVP_CNX_ERROR
|
||||
|
||||
/* Endpoints of a connection have been changed (multihomed SCTP). */
|
||||
,FDEVP_CNX_EP_CHANGE
|
||||
|
||||
/* The connection is being shutdown (SCTP notification). */
|
||||
,FDEVP_CNX_SHUTDOWN
|
||||
|
||||
/* A new connection (with a CER) has been received */
|
||||
,FDEVP_CNX_INCOMING
|
||||
|
||||
/* A new connection has been established to the remote peer (event data is the cnxctx object) */
|
||||
,FDEVP_CNX_ESTABLISHED
|
||||
|
||||
/* A connection attempt (initiator side) has failed */
|
||||
,FDEVP_CNX_FAILED
|
||||
|
||||
/* The PSM state is expired */
|
||||
,FDEVP_PSM_TIMEOUT
|
||||
|
||||
};
|
||||
#define CHECK_PEVENT( _e ) \
|
||||
(((int)(_e) >= FDEVP_TERMINATE) && ((int)(_e) <= FDEVP_PSM_TIMEOUT))
|
||||
/* The following macro is actually called in p_psm.c -- another solution would be to declare it static inline */
|
||||
#define DECLARE_PEV_STR() \
|
||||
const char * fd_pev_str(int event) \
|
||||
{ \
|
||||
switch (event) { \
|
||||
case_str(FDEVP_TERMINATE); \
|
||||
case_str(FDEVP_CNX_MSG_RECV); \
|
||||
case_str(FDEVP_CNX_ERROR); \
|
||||
case_str(FDEVP_CNX_EP_CHANGE); \
|
||||
case_str(FDEVP_CNX_INCOMING); \
|
||||
case_str(FDEVP_CNX_ESTABLISHED); \
|
||||
case_str(FDEVP_CNX_FAILED); \
|
||||
case_str(FDEVP_PSM_TIMEOUT); \
|
||||
} \
|
||||
TRACE_DEBUG(FULL, "Unknown event : %d", event); \
|
||||
return "Unknown event"; \
|
||||
}
|
||||
const char * fd_pev_str(int event);
|
||||
|
||||
/* The data structure for FDEVP_CNX_INCOMING event */
|
||||
struct cnx_incoming {
|
||||
struct msg * cer; /* the CER message received on this connection */
|
||||
struct cnxctx * cnx; /* The connection context */
|
||||
int validate; /* The peer is new, it must be validated (by an extension) or error CEA to be sent */
|
||||
};
|
||||
|
||||
/* Functions */
|
||||
int fd_peer_fini();
|
||||
int fd_peer_alloc(struct fd_peer ** ptr);
|
||||
int fd_peer_free(struct fd_peer ** ptr);
|
||||
int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx );
|
||||
/* fd_peer_add declared in freeDiameter.h */
|
||||
int fd_peer_validate( struct fd_peer * peer );
|
||||
void fd_peer_failover_msg(struct fd_peer * peer);
|
||||
|
||||
/* Peer expiry */
|
||||
int fd_p_expi_init(void);
|
||||
int fd_p_expi_fini(void);
|
||||
int fd_p_expi_update(struct fd_peer * peer );
|
||||
|
||||
/* Peer state machine */
|
||||
int fd_psm_start();
|
||||
int fd_psm_begin(struct fd_peer * peer );
|
||||
int fd_psm_terminate(struct fd_peer * peer, char * reason );
|
||||
void fd_psm_abord(struct fd_peer * peer );
|
||||
void fd_psm_next_timeout(struct fd_peer * peer, int add_random, int delay);
|
||||
int fd_psm_change_state(struct fd_peer * peer, int new_state);
|
||||
void fd_psm_cleanup(struct fd_peer * peer, int terminate);
|
||||
|
||||
/* Peer out */
|
||||
int fd_out_send(struct msg ** msg, struct cnxctx * cnx, struct fd_peer * peer, int update_reqin_cnt);
|
||||
int fd_out_start(struct fd_peer * peer);
|
||||
int fd_out_stop(struct fd_peer * peer);
|
||||
|
||||
/* Initiating connections */
|
||||
int fd_p_cnx_init(struct fd_peer * peer);
|
||||
void fd_p_cnx_abort(struct fd_peer * peer, int cleanup_all);
|
||||
|
||||
/* Peer sent requests cache */
|
||||
int fd_p_sr_store(struct sr_list * srlist, struct msg **req, uint32_t *hbhloc, uint32_t hbh_restore);
|
||||
int fd_p_sr_fetch(struct sr_list * srlist, uint32_t hbh, struct msg **req);
|
||||
int fd_p_sr_start(struct sr_list * srlist);
|
||||
int fd_p_sr_stop(struct sr_list * srlist);
|
||||
void fd_p_sr_failover(struct sr_list * srlist);
|
||||
|
||||
/* Local Link messages (CER/CEA, DWR/DWA, DPR/DPA) */
|
||||
int fd_p_ce_msgrcv(struct msg ** msg, int req, struct fd_peer * peer);
|
||||
int fd_p_ce_handle_newCER(struct msg ** msg, struct fd_peer * peer, struct cnxctx ** cnx, int valid);
|
||||
int fd_p_ce_handle_newcnx(struct fd_peer * peer, struct cnxctx * initiator);
|
||||
int fd_p_ce_process_receiver(struct fd_peer * peer);
|
||||
void fd_p_ce_clear_cnx(struct fd_peer * peer, struct cnxctx ** cnx_kept);
|
||||
int fd_p_dw_handle(struct msg ** msg, int req, struct fd_peer * peer);
|
||||
int fd_p_dw_timeout(struct fd_peer * peer);
|
||||
int fd_p_dw_reopen(struct fd_peer * peer);
|
||||
int fd_p_dp_handle(struct msg ** msg, int req, struct fd_peer * peer);
|
||||
int fd_p_dp_initiate(struct fd_peer * peer, char * reason);
|
||||
int fd_p_dp_newdelay(struct fd_peer * peer);
|
||||
|
||||
/* Active peers -- routing process should only ever take the read lock, the write lock is managed by PSMs */
|
||||
extern struct fd_list fd_g_activ_peers;
|
||||
extern pthread_rwlock_t fd_g_activ_peers_rw; /* protect the list */
|
||||
|
||||
|
||||
/* Server sockets */
|
||||
int fd_servers_start();
|
||||
int fd_servers_stop();
|
||||
|
||||
/* Connection contexts -- there are also definitions in cnxctx.h for the relevant files */
|
||||
struct cnxctx * fd_cnx_serv_tcp(uint16_t port, int family, struct fd_endpoint * ep);
|
||||
struct cnxctx * fd_cnx_serv_sctp(uint16_t port, struct fd_list * ep_list);
|
||||
int fd_cnx_serv_listen(struct cnxctx * conn);
|
||||
struct cnxctx * fd_cnx_serv_accept(struct cnxctx * serv);
|
||||
struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa, socklen_t addrlen);
|
||||
struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list);
|
||||
int fd_cnx_start_clear(struct cnxctx * conn, int loop);
|
||||
void fd_cnx_sethostname(struct cnxctx * conn, DiamId_t hn);
|
||||
int fd_cnx_proto_info(struct cnxctx * conn, char * buf, size_t len);
|
||||
#define ALGO_HANDSHAKE_DEFAULT 0 /* TLS for TCP, DTLS for SCTP */
|
||||
#define ALGO_HANDSHAKE_3436 1 /* For TLS for SCTP also */
|
||||
int fd_cnx_handshake(struct cnxctx * conn, int mode, int algo, char * priority, void * alt_creds);
|
||||
char * fd_cnx_getid(struct cnxctx * conn);
|
||||
int fd_cnx_getproto(struct cnxctx * conn);
|
||||
int fd_cnx_getTLS(struct cnxctx * conn);
|
||||
int fd_cnx_is_unordered_delivery_supported(struct cnxctx * conn);
|
||||
int fd_cnx_unordered_delivery(struct cnxctx * conn, int is_allowed);
|
||||
int fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size);
|
||||
int fd_cnx_get_local_eps(struct fd_list * list);
|
||||
int fd_cnx_getremoteeps(struct cnxctx * conn, struct fd_list * eps);
|
||||
char * fd_cnx_getremoteid(struct cnxctx * conn);
|
||||
int fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len);
|
||||
int fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo); /* send FDEVP_CNX_MSG_RECV event to the fifo list */
|
||||
int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len);
|
||||
void fd_cnx_destroy(struct cnxctx * conn);
|
||||
#ifdef GNUTLS_VERSION_300
|
||||
int fd_tls_verify_credentials_2(gnutls_session_t session);
|
||||
#endif /* GNUTLS_VERSION_300 */
|
||||
|
||||
/* Internal calls of the hook mechanism */
|
||||
void fd_hook_call(enum fd_hook_type type, struct msg * msg, struct fd_peer * peer, void * other, struct fd_msg_pmdl * pmdl);
|
||||
void fd_hook_associate(struct msg * msg, struct fd_msg_pmdl * pmdl);
|
||||
int fd_hooks_init(void);
|
||||
size_t fd_msg_pmdl_sizewithoverhead(size_t datalen);
|
||||
struct fd_msg_pmdl * fd_msg_pmdl_get_inbuf(uint8_t * buf, size_t datalen);
|
||||
|
||||
#endif /* _FDCORE_INTERNAL_H */
|
||||
@@ -0,0 +1,285 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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. *
|
||||
*********************************************************************************************************/
|
||||
|
||||
/* Lex configuration parser.
|
||||
*
|
||||
* This file defines the token for parsing the daemon's configuration file
|
||||
* Note that each extension has a separate independant configuration file.
|
||||
*
|
||||
* Note : This module is NOT thread-safe. All processing must be done from one thread only.
|
||||
*/
|
||||
%{
|
||||
/* Include the daemon's header files */
|
||||
#include "fdcore-internal.h"
|
||||
/* Include yacc tokens definitions */
|
||||
#include "fdd.tab.h"
|
||||
|
||||
/* Update the column information */
|
||||
#ifdef DEBUG_LEX
|
||||
#define YY_USER_ACTION { \
|
||||
yylloc->first_column = yylloc->last_column + 1; \
|
||||
yylloc->last_column = yylloc->first_column + yyleng - 1; \
|
||||
fd_log_debug( \
|
||||
"(%d:%d-%d:%d) matched rule %d, length=%d, txt='%s'", \
|
||||
yylloc->first_line, yylloc->first_column, \
|
||||
yylloc->last_line, yylloc->last_column, \
|
||||
yy_act, yyleng, yytext); \
|
||||
}
|
||||
#else /* DEBUG_LEX */
|
||||
#define YY_USER_ACTION { \
|
||||
yylloc->first_column = yylloc->last_column + 1; \
|
||||
yylloc->last_column = yylloc->first_column + yyleng - 1; \
|
||||
}
|
||||
#endif
|
||||
|
||||
/* %option noinput ? */
|
||||
#define YY_NO_INPUT
|
||||
|
||||
/* Additional for files inclusion */
|
||||
#include <glob.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_NESTED_CONF_FILES 5
|
||||
|
||||
struct nested_conffiles_t {
|
||||
YY_BUFFER_STATE parent_level_state;
|
||||
glob_t filelist;
|
||||
int current_file;
|
||||
} nested_conffiles[MAX_NESTED_CONF_FILES];
|
||||
|
||||
int current_nested_level = 0;
|
||||
|
||||
int globerrfct(const char *epath, int eerrno)
|
||||
{
|
||||
TRACE_ERROR("Failed to scan %s: %s", epath, strerror(eerrno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%option bison-bridge bison-locations
|
||||
%option noyywrap
|
||||
%option nounput
|
||||
|
||||
%x in_include
|
||||
|
||||
/* Quoted string. Multilines do not match. */
|
||||
qstring \"[^\"\n]*\"
|
||||
|
||||
%%
|
||||
<*>\n {
|
||||
/* Update the line count */
|
||||
yylloc->first_line++;
|
||||
yylloc->last_line++;
|
||||
yylloc->last_column=0;
|
||||
}
|
||||
|
||||
<*>([[:space:]]{-}[\n])+ ; /* Eat all spaces, not new lines */
|
||||
<*>#.*$ ; /* Eat all comments */
|
||||
|
||||
|
||||
include BEGIN(in_include);
|
||||
/* Following an "include" keyword */
|
||||
<in_include>{
|
||||
{qstring} { /* Name of the file to include. This is directly sent to glob. */
|
||||
int globerror=0;
|
||||
char * buf = strdup(yytext+1);
|
||||
if (buf[yyleng-2] != '"')
|
||||
{
|
||||
TRACE_ERROR("Unterminated string: %s", yytext);
|
||||
return LEX_ERROR;
|
||||
}
|
||||
buf[yyleng-2] = '\0';
|
||||
|
||||
if (current_nested_level >= MAX_NESTED_CONF_FILES)
|
||||
{
|
||||
TRACE_ERROR("Too many recursion levels in configuration files includes");
|
||||
return LEX_ERROR;
|
||||
}
|
||||
|
||||
/* glob the include */
|
||||
globerror = glob(buf, GLOB_ERR, globerrfct, &nested_conffiles[current_nested_level].filelist);
|
||||
|
||||
if (globerror == GLOB_NOSPACE)
|
||||
{
|
||||
TRACE_ERROR("Not enough memory to parse include directive.");
|
||||
return LEX_ERROR;
|
||||
}
|
||||
if (globerror == GLOB_ABORTED)
|
||||
{
|
||||
TRACE_ERROR("An error was encountered in include directive.");
|
||||
return LEX_ERROR;
|
||||
}
|
||||
if (globerror == GLOB_NOMATCH)
|
||||
{
|
||||
globfree(&nested_conffiles[current_nested_level].filelist);
|
||||
goto nomatch;
|
||||
}
|
||||
if (globerror)
|
||||
{
|
||||
TRACE_ERROR("Unexpected error in glob (%d).", globerror);
|
||||
return LEX_ERROR;
|
||||
}
|
||||
|
||||
/* We have a list of files to include. */
|
||||
|
||||
/* save the current buffer for returning when this include has been parsed */
|
||||
nested_conffiles[current_nested_level].parent_level_state = YY_CURRENT_BUFFER;
|
||||
|
||||
/* Start with the first match */
|
||||
nested_conffiles[current_nested_level].current_file = 0;
|
||||
|
||||
yyin = fopen( nested_conffiles[current_nested_level].filelist.gl_pathv[0], "r" );
|
||||
|
||||
if ( ! yyin )
|
||||
{
|
||||
TRACE_ERROR("Error in %s: %s", nested_conffiles[current_nested_level].filelist.gl_pathv[0], strerror(errno));
|
||||
return LEX_ERROR;
|
||||
}
|
||||
|
||||
yy_switch_to_buffer(yy_create_buffer( yyin, YY_BUF_SIZE ));
|
||||
|
||||
/* In case of recursive includes */
|
||||
current_nested_level++;
|
||||
|
||||
nomatch:
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
}
|
||||
|
||||
<<EOF>> {
|
||||
if (current_nested_level == 0)
|
||||
{
|
||||
/* We are at the end of parsing */
|
||||
yyterminate();
|
||||
}
|
||||
|
||||
/* Otherwise we are doing an include statement */
|
||||
--current_nested_level;
|
||||
yy_delete_buffer(YY_CURRENT_BUFFER);
|
||||
|
||||
/* Go to next file, if any */
|
||||
nested_conffiles[current_nested_level].current_file++;
|
||||
if ( nested_conffiles[current_nested_level].filelist.gl_pathv[nested_conffiles[current_nested_level].current_file] == NULL )
|
||||
{
|
||||
/* We have finished with this list of includes */
|
||||
globfree(&nested_conffiles[current_nested_level].filelist);
|
||||
yy_switch_to_buffer(nested_conffiles[current_nested_level].parent_level_state);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Proceed to next included file */
|
||||
yyin = fopen( nested_conffiles[current_nested_level].filelist.gl_pathv[nested_conffiles[current_nested_level].current_file], "r" );
|
||||
|
||||
if ( ! yyin )
|
||||
{
|
||||
TRACE_ERROR("Error in %s: %s", nested_conffiles[current_nested_level].filelist.gl_pathv[nested_conffiles[current_nested_level].current_file], strerror(errno));
|
||||
return LEX_ERROR;
|
||||
}
|
||||
|
||||
yy_switch_to_buffer(yy_create_buffer( yyin, YY_BUF_SIZE ));
|
||||
|
||||
/* In case of recursive includes */
|
||||
current_nested_level++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{qstring} {
|
||||
/* First copy the string without the quotes for use in the yacc parser */
|
||||
CHECK_MALLOC_DO( yylval->string = strdup(yytext+1), /* This allocates one useless tail char but... it's easier :D */
|
||||
return LEX_ERROR );/* on error, trig an error in yacc parser */
|
||||
|
||||
yylval->string[yyleng-2] = '\0';
|
||||
|
||||
/* the yacc parser will check the string is valid */
|
||||
return QSTRING;
|
||||
}
|
||||
|
||||
[[:digit:]]+ {
|
||||
/* Convert this to an integer value */
|
||||
int ret = sscanf(yytext, "%i", &yylval->integer);
|
||||
if (ret != 1) {
|
||||
/* No matching: an error occurred */
|
||||
TRACE_ERROR("Unable to convert the value '%s' to a valid number: %s", yytext, strerror(errno));
|
||||
return LEX_ERROR; /* trig an error in yacc parser */
|
||||
/* Maybe we could REJECT instead of failing here? */
|
||||
}
|
||||
return INTEGER;
|
||||
}
|
||||
|
||||
/* Full words tokens (keywords) */
|
||||
(?i:"Identity") { return IDENTITY; }
|
||||
(?i:"Realm") { return REALM; }
|
||||
(?i:"Port") { return PORT; }
|
||||
(?i:"SecPort") { return SECPORT; }
|
||||
/* (?i:"SctpSec3436") { return SEC3436; } */
|
||||
(?i:"No_IPv6") { return NOIP6; }
|
||||
(?i:"No_IP") { return NOIP; }
|
||||
(?i:"No_TCP") { return NOTCP; }
|
||||
(?i:"No_SCTP") { return NOSCTP; }
|
||||
(?i:"Prefer_TCP") { return PREFERTCP; }
|
||||
(?i:"TLS_old_method") { return OLDTLS; }
|
||||
(?i:"SCTP_streams") { return SCTPSTREAMS; }
|
||||
(?i:"AppServThreads") { return APPSERVTHREADS;}
|
||||
(?i:"ListenOn") { return LISTENON; }
|
||||
(?i:"ThreadsPerServer") { return THRPERSRV; }
|
||||
(?i:"TcTimer") { return TCTIMER; }
|
||||
(?i:"TwTimer") { return TWTIMER; }
|
||||
(?i:"NoRelay") { return NORELAY; }
|
||||
(?i:"LoadExtension") { return LOADEXT; }
|
||||
(?i:"ConnectPeer") { return CONNPEER; }
|
||||
(?i:"ConnectTo") { return CONNTO; }
|
||||
(?i:"No_TLS") { return NOTLS; }
|
||||
(?i:"TLS_Cred") { return TLS_CRED; }
|
||||
(?i:"TLS_CA") { return TLS_CA; }
|
||||
(?i:"TLS_CRL") { return TLS_CRL; }
|
||||
(?i:"TLS_Prio") { return TLS_PRIO; }
|
||||
(?i:"TLS_DH_bits") { return TLS_DH_BITS; }
|
||||
(?i:"TLS_DH_file") { return TLS_DH_FILE; }
|
||||
|
||||
|
||||
/* Valid single characters for yyparse */
|
||||
<*>[=,:;{}] { return yytext[0]; }
|
||||
|
||||
/* Unrecognized token */
|
||||
<*>[[:alnum:]]+ | /* This rule is only useful to print a complete token in error messages */
|
||||
/* Unrecognized character */
|
||||
<*>. {
|
||||
TRACE_ERROR("Unrecognized text on line %d col %d: '%s'.", yylloc->first_line, yylloc->first_column, yytext);
|
||||
return LEX_ERROR;
|
||||
}
|
||||
|
||||
%%
|
||||
@@ -0,0 +1,667 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2015, 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. *
|
||||
*********************************************************************************************************/
|
||||
|
||||
/* Yacc configuration parser.
|
||||
*
|
||||
* This file defines the grammar of the configuration file.
|
||||
* Note that each extension has a separate independant configuration file.
|
||||
*
|
||||
* Note : This module is NOT thread-safe. All processing must be done from one thread only.
|
||||
*/
|
||||
|
||||
/* For development only : */
|
||||
%debug
|
||||
%error-verbose
|
||||
|
||||
%parse-param {struct fd_config * conf}
|
||||
|
||||
/* Keep track of location */
|
||||
%locations
|
||||
%pure-parser
|
||||
|
||||
%{
|
||||
#include "fdcore-internal.h"
|
||||
#include "fdd.tab.h" /* bug : bison does not define the YYLTYPE before including this bloc, so... */
|
||||
|
||||
/* The Lex parser prototype */
|
||||
int fddlex(YYSTYPE *lvalp, YYLTYPE *llocp);
|
||||
|
||||
/* Function to report error */
|
||||
void yyerror (YYLTYPE *ploc, struct fd_config * conf, char const *s)
|
||||
{
|
||||
if (ploc->first_line != ploc->last_line) {
|
||||
TRACE_ERROR("%s:%d.%d-%d.%d : %s", conf->cnf_file, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s);
|
||||
} else if (ploc->first_column != ploc->last_column) {
|
||||
TRACE_ERROR("%s:%d.%d-%d : %s", conf->cnf_file, ploc->first_line, ploc->first_column, ploc->last_column, s);
|
||||
} else {
|
||||
TRACE_ERROR("%s:%d.%d : %s", conf->cnf_file, ploc->first_line, ploc->first_column, s);
|
||||
}
|
||||
}
|
||||
|
||||
int got_peer_noip = 0;
|
||||
int got_peer_noipv6 = 0;
|
||||
int got_peer_notcp = 0;
|
||||
int got_peer_nosctp = 0;
|
||||
|
||||
struct peer_info fddpi;
|
||||
|
||||
%}
|
||||
|
||||
/* Values returned by lex for token */
|
||||
%union {
|
||||
char *string; /* The string is allocated by strdup in lex.*/
|
||||
int integer; /* Store integer values */
|
||||
}
|
||||
|
||||
/* In case of error in the lexical analysis */
|
||||
%token LEX_ERROR
|
||||
|
||||
%token <string> QSTRING
|
||||
%token <integer> INTEGER
|
||||
|
||||
%type <string> extconf
|
||||
|
||||
%token IDENTITY
|
||||
%token REALM
|
||||
%token PORT
|
||||
%token SECPORT
|
||||
%token SEC3436
|
||||
%token NOIP
|
||||
%token NOIP6
|
||||
%token NOTCP
|
||||
%token NOSCTP
|
||||
%token PREFERTCP
|
||||
%token OLDTLS
|
||||
%token NOTLS
|
||||
%token SCTPSTREAMS
|
||||
%token APPSERVTHREADS
|
||||
%token LISTENON
|
||||
%token THRPERSRV
|
||||
%token TCTIMER
|
||||
%token TWTIMER
|
||||
%token NORELAY
|
||||
%token LOADEXT
|
||||
%token CONNPEER
|
||||
%token CONNTO
|
||||
%token TLS_CRED
|
||||
%token TLS_CA
|
||||
%token TLS_CRL
|
||||
%token TLS_PRIO
|
||||
%token TLS_DH_BITS
|
||||
%token TLS_DH_FILE
|
||||
|
||||
|
||||
/* -------------------------------------- */
|
||||
%%
|
||||
|
||||
/* The grammar definition - Sections blocs. */
|
||||
conffile: /* Empty is OK -- for simplicity here, we reject in daemon later */
|
||||
| conffile identity
|
||||
| conffile realm
|
||||
| conffile tctimer
|
||||
| conffile twtimer
|
||||
| conffile port
|
||||
| conffile secport
|
||||
| conffile sec3436
|
||||
| conffile sctpstreams
|
||||
| conffile listenon
|
||||
| conffile thrpersrv
|
||||
| conffile norelay
|
||||
| conffile appservthreads
|
||||
| conffile noip
|
||||
| conffile noip6
|
||||
| conffile notcp
|
||||
| conffile nosctp
|
||||
| conffile prefertcp
|
||||
| conffile oldtls
|
||||
| conffile loadext
|
||||
| conffile connpeer
|
||||
| conffile tls_cred
|
||||
| conffile tls_ca
|
||||
| conffile tls_crl
|
||||
| conffile tls_prio
|
||||
| conffile tls_dh
|
||||
| conffile errors
|
||||
{
|
||||
yyerror(&yylloc, conf, "An error occurred while parsing the configuration file");
|
||||
return EINVAL;
|
||||
}
|
||||
;
|
||||
|
||||
/* Lexical or syntax error */
|
||||
errors: LEX_ERROR
|
||||
| error
|
||||
;
|
||||
|
||||
identity: IDENTITY '=' QSTRING ';'
|
||||
{
|
||||
conf->cnf_diamid = $3;
|
||||
}
|
||||
;
|
||||
|
||||
realm: REALM '=' QSTRING ';'
|
||||
{
|
||||
conf->cnf_diamrlm = $3;
|
||||
}
|
||||
;
|
||||
|
||||
tctimer: TCTIMER '=' INTEGER ';'
|
||||
{
|
||||
CHECK_PARAMS_DO( ($3 > 0),
|
||||
{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
|
||||
conf->cnf_timer_tc = (unsigned int)$3;
|
||||
}
|
||||
;
|
||||
|
||||
twtimer: TWTIMER '=' INTEGER ';'
|
||||
{
|
||||
CHECK_PARAMS_DO( ($3 > 5),
|
||||
{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
|
||||
conf->cnf_timer_tw = (unsigned int)$3;
|
||||
}
|
||||
;
|
||||
|
||||
port: PORT '=' INTEGER ';'
|
||||
{
|
||||
CHECK_PARAMS_DO( ($3 >= 0) && ($3 < 1<<16),
|
||||
{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
|
||||
conf->cnf_port = (uint16_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
secport: SECPORT '=' INTEGER ';'
|
||||
{
|
||||
CHECK_PARAMS_DO( ($3 >= 0) && ($3 < 1<<16),
|
||||
{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
|
||||
conf->cnf_port_tls = (uint16_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
sec3436: SEC3436 '=' INTEGER ';'
|
||||
{
|
||||
CHECK_PARAMS_DO( ($3 >= 0) && ($3 < 1<<16),
|
||||
{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
|
||||
conf->cnf_port_3436 = (uint16_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
sctpstreams: SCTPSTREAMS '=' INTEGER ';'
|
||||
{
|
||||
CHECK_PARAMS_DO( ($3 > 0) && ($3 < 1<<16),
|
||||
{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
|
||||
conf->cnf_sctp_str = (uint16_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
listenon: LISTENON '=' QSTRING ';'
|
||||
{
|
||||
struct addrinfo hints, *ai;
|
||||
int ret;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
|
||||
ret = getaddrinfo($3, NULL, &hints, &ai);
|
||||
if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); YYERROR; }
|
||||
CHECK_FCT_DO( fd_ep_add_merge( &conf->cnf_endpoints, ai->ai_addr, ai->ai_addrlen, EP_FL_CONF ), YYERROR );
|
||||
freeaddrinfo(ai);
|
||||
free($3);
|
||||
}
|
||||
;
|
||||
|
||||
thrpersrv: THRPERSRV '=' INTEGER ';'
|
||||
{
|
||||
CHECK_PARAMS_DO( ($3 > 0),
|
||||
{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
|
||||
conf->cnf_thr_srv = $3;
|
||||
}
|
||||
;
|
||||
|
||||
norelay: NORELAY ';'
|
||||
{
|
||||
conf->cnf_flags.no_fwd = 1;
|
||||
}
|
||||
;
|
||||
|
||||
appservthreads: APPSERVTHREADS '=' INTEGER ';'
|
||||
{
|
||||
CHECK_PARAMS_DO( ($3 > 0) && ($3 < 256),
|
||||
{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
|
||||
conf->cnf_dispthr = (uint16_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
noip: NOIP ';'
|
||||
{
|
||||
if (got_peer_noipv6) {
|
||||
yyerror (&yylloc, conf, "No_IP conflicts with a ConnectPeer directive No_IPv6.");
|
||||
YYERROR;
|
||||
}
|
||||
conf->cnf_flags.no_ip4 = 1;
|
||||
}
|
||||
;
|
||||
|
||||
noip6: NOIP6 ';'
|
||||
{
|
||||
if (got_peer_noip) {
|
||||
yyerror (&yylloc, conf, "No_IP conflicts with a ConnectPeer directive No_IP.");
|
||||
YYERROR;
|
||||
}
|
||||
conf->cnf_flags.no_ip6 = 1;
|
||||
}
|
||||
;
|
||||
|
||||
notcp: NOTCP ';'
|
||||
{
|
||||
#ifdef DISABLE_SCTP
|
||||
yyerror (&yylloc, conf, "No_TCP cannot be specified for daemon compiled with DISABLE_SCTP option.");
|
||||
YYERROR;
|
||||
#endif
|
||||
if (conf->cnf_flags.no_sctp)
|
||||
{
|
||||
yyerror (&yylloc, conf, "No_TCP conflicts with No_SCTP directive." );
|
||||
YYERROR;
|
||||
}
|
||||
if (got_peer_nosctp) {
|
||||
yyerror (&yylloc, conf, "No_TCP conflicts with a ConnectPeer directive No_SCTP.");
|
||||
YYERROR;
|
||||
}
|
||||
conf->cnf_flags.no_tcp = 1;
|
||||
}
|
||||
;
|
||||
|
||||
nosctp: NOSCTP ';'
|
||||
{
|
||||
if (conf->cnf_flags.no_tcp)
|
||||
{
|
||||
yyerror (&yylloc, conf, "No_SCTP conflicts with No_TCP directive." );
|
||||
YYERROR;
|
||||
}
|
||||
if (got_peer_notcp) {
|
||||
yyerror (&yylloc, conf, "No_SCTP conflicts with a ConnectPeer directive No_TCP.");
|
||||
YYERROR;
|
||||
}
|
||||
conf->cnf_flags.no_sctp = 1;
|
||||
}
|
||||
;
|
||||
|
||||
prefertcp: PREFERTCP ';'
|
||||
{
|
||||
conf->cnf_flags.pr_tcp = 1;
|
||||
}
|
||||
;
|
||||
|
||||
oldtls: OLDTLS ';'
|
||||
{
|
||||
conf->cnf_flags.tls_alg = 1;
|
||||
}
|
||||
;
|
||||
|
||||
loadext: LOADEXT '=' QSTRING extconf ';'
|
||||
{
|
||||
char * fname;
|
||||
char * cfname;
|
||||
FILE * fd;
|
||||
|
||||
/* Try and open the extension file */
|
||||
fname = $3;
|
||||
fd = fopen(fname, "r");
|
||||
if ((fd == NULL) && (*fname != '/')) {
|
||||
char * bkp = fname;
|
||||
CHECK_MALLOC_DO( fname = malloc( strlen(bkp) + strlen(DEFAULT_EXTENSIONS_PATH) + 2 ),
|
||||
{ yyerror (&yylloc, conf, "Not enough memory"); YYERROR; } );
|
||||
sprintf(fname, DEFAULT_EXTENSIONS_PATH "/%s", bkp);
|
||||
fd = fopen(fname, "r");
|
||||
if (fd == NULL) {
|
||||
free(fname);
|
||||
fname = bkp;
|
||||
} else {
|
||||
free(bkp);
|
||||
}
|
||||
}
|
||||
if (fd != NULL) {
|
||||
fclose(fd);
|
||||
} /* otherwise, LD_LIBRARY_PATH will be tested by dl_open.
|
||||
This should not give any security issue, otherwise we can add an "else fail" here. */
|
||||
|
||||
/* Try and open the configuration file (optional) */
|
||||
cfname = $4;
|
||||
if (cfname) {
|
||||
fd = fopen(cfname, "r");
|
||||
if ((fd == NULL) && (*cfname != '/')) {
|
||||
char * test;
|
||||
CHECK_MALLOC_DO( test = malloc( strlen(cfname) + strlen(DEFAULT_CONF_PATH) + 2 ),
|
||||
{ yyerror (&yylloc, conf, "Not enough memory"); YYERROR; } );
|
||||
sprintf(test, DEFAULT_CONF_PATH "/%s", cfname);
|
||||
fd = fopen(test, "r");
|
||||
if (fd) {
|
||||
free(cfname);
|
||||
cfname=test;
|
||||
} else {
|
||||
/* This is not an error, we allow an extension to wait for something else than a real conf file. */
|
||||
free(test);
|
||||
}
|
||||
}
|
||||
if (fd)
|
||||
fclose(fd);
|
||||
}
|
||||
|
||||
CHECK_FCT_DO( fd_ext_add( fname, cfname ),
|
||||
{ yyerror (&yylloc, conf, "Error adding extension"); YYERROR; } );
|
||||
}
|
||||
;
|
||||
|
||||
extconf: /* empty */
|
||||
{
|
||||
$$ = NULL;
|
||||
}
|
||||
| ':' QSTRING
|
||||
{
|
||||
$$ = $2;
|
||||
}
|
||||
;
|
||||
|
||||
connpeer: {
|
||||
memset(&fddpi, 0, sizeof(fddpi));
|
||||
fddpi.config.pic_flags.persist = PI_PRST_ALWAYS;
|
||||
fd_list_init( &fddpi.pi_endpoints, NULL );
|
||||
}
|
||||
CONNPEER '=' QSTRING peerinfo ';'
|
||||
{
|
||||
fddpi.pi_diamid = $4;
|
||||
CHECK_FCT_DO( fd_peer_add ( &fddpi, conf->cnf_file, NULL, NULL ),
|
||||
{ yyerror (&yylloc, conf, "Error adding ConnectPeer information"); YYERROR; } );
|
||||
|
||||
/* Now destroy any content in the structure */
|
||||
free(fddpi.pi_diamid);
|
||||
free(fddpi.config.pic_realm);
|
||||
free(fddpi.config.pic_priority);
|
||||
while (!FD_IS_LIST_EMPTY(&fddpi.pi_endpoints)) {
|
||||
struct fd_list * li = fddpi.pi_endpoints.next;
|
||||
fd_list_unlink(li);
|
||||
free(li);
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
peerinfo: /* empty */
|
||||
| '{' peerparams '}'
|
||||
;
|
||||
|
||||
peerparams: /* empty */
|
||||
| peerparams NOIP ';'
|
||||
{
|
||||
if ((conf->cnf_flags.no_ip6) || (fddpi.config.pic_flags.pro3 == PI_P3_IP)) {
|
||||
yyerror (&yylloc, conf, "No_IP conflicts with a No_IPv6 directive.");
|
||||
YYERROR;
|
||||
}
|
||||
got_peer_noip++;
|
||||
fddpi.config.pic_flags.pro3 = PI_P3_IPv6;
|
||||
}
|
||||
| peerparams NOIP6 ';'
|
||||
{
|
||||
if ((conf->cnf_flags.no_ip4) || (fddpi.config.pic_flags.pro3 == PI_P3_IPv6)) {
|
||||
yyerror (&yylloc, conf, "No_IPv6 conflicts with a No_IP directive.");
|
||||
YYERROR;
|
||||
}
|
||||
got_peer_noipv6++;
|
||||
fddpi.config.pic_flags.pro3 = PI_P3_IP;
|
||||
}
|
||||
| peerparams NOTCP ';'
|
||||
{
|
||||
#ifdef DISABLE_SCTP
|
||||
yyerror (&yylloc, conf, "No_TCP cannot be specified in daemon compiled with DISABLE_SCTP option.");
|
||||
YYERROR;
|
||||
#endif
|
||||
if ((conf->cnf_flags.no_sctp) || (fddpi.config.pic_flags.pro4 == PI_P4_TCP)) {
|
||||
yyerror (&yylloc, conf, "No_TCP conflicts with a No_SCTP directive.");
|
||||
YYERROR;
|
||||
}
|
||||
got_peer_notcp++;
|
||||
fddpi.config.pic_flags.pro4 = PI_P4_SCTP;
|
||||
}
|
||||
| peerparams NOSCTP ';'
|
||||
{
|
||||
if ((conf->cnf_flags.no_tcp) || (fddpi.config.pic_flags.pro4 == PI_P4_SCTP)) {
|
||||
yyerror (&yylloc, conf, "No_SCTP conflicts with a No_TCP directive.");
|
||||
YYERROR;
|
||||
}
|
||||
got_peer_nosctp++;
|
||||
fddpi.config.pic_flags.pro4 = PI_P4_TCP;
|
||||
}
|
||||
| peerparams PREFERTCP ';'
|
||||
{
|
||||
fddpi.config.pic_flags.alg = PI_ALGPREF_TCP;
|
||||
}
|
||||
| peerparams OLDTLS ';'
|
||||
{
|
||||
fddpi.config.pic_flags.sec |= PI_SEC_TLS_OLD;
|
||||
}
|
||||
| peerparams NOTLS ';'
|
||||
{
|
||||
fddpi.config.pic_flags.sec |= PI_SEC_NONE;
|
||||
}
|
||||
| peerparams SEC3436 ';'
|
||||
{
|
||||
fddpi.config.pic_flags.sctpsec |= PI_SCTPSEC_3436;
|
||||
}
|
||||
| peerparams REALM '=' QSTRING ';'
|
||||
{
|
||||
fddpi.config.pic_realm = $4;
|
||||
}
|
||||
| peerparams PORT '=' INTEGER ';'
|
||||
{
|
||||
CHECK_PARAMS_DO( ($4 > 0) && ($4 < 1<<16),
|
||||
{ yyerror (&yylloc, conf, "Invalid port value"); YYERROR; } );
|
||||
fddpi.config.pic_port = (uint16_t)$4;
|
||||
}
|
||||
| peerparams TCTIMER '=' INTEGER ';'
|
||||
{
|
||||
fddpi.config.pic_tctimer = $4;
|
||||
}
|
||||
| peerparams TWTIMER '=' INTEGER ';'
|
||||
{
|
||||
fddpi.config.pic_twtimer = $4;
|
||||
}
|
||||
| peerparams TLS_PRIO '=' QSTRING ';'
|
||||
{
|
||||
fddpi.config.pic_priority = $4;
|
||||
}
|
||||
| peerparams CONNTO '=' QSTRING ';'
|
||||
{
|
||||
struct addrinfo hints, *ai;
|
||||
int ret;
|
||||
int disc = 0;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST;
|
||||
ret = getaddrinfo($4, NULL, &hints, &ai);
|
||||
if (ret == EAI_NONAME) {
|
||||
/* The name was maybe not numeric, try again */
|
||||
disc = EP_FL_DISC;
|
||||
hints.ai_flags &= ~ AI_NUMERICHOST;
|
||||
ret = getaddrinfo($4, NULL, &hints, &ai);
|
||||
}
|
||||
if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); YYERROR; }
|
||||
|
||||
CHECK_FCT_DO( fd_ep_add_merge( &fddpi.pi_endpoints, ai->ai_addr, ai->ai_addrlen, EP_FL_CONF | (disc ?: EP_ACCEPTALL) ), YYERROR );
|
||||
free($4);
|
||||
freeaddrinfo(ai);
|
||||
}
|
||||
;
|
||||
|
||||
tls_cred: TLS_CRED '=' QSTRING ',' QSTRING ';'
|
||||
{
|
||||
FILE * fd;
|
||||
fd = fopen($3, "r");
|
||||
if (fd == NULL) {
|
||||
int ret = errno;
|
||||
TRACE_ERROR("Unable to open certificate file %s for reading: %s", $3, strerror(ret));
|
||||
yyerror (&yylloc, conf, "Error on file name");
|
||||
YYERROR;
|
||||
}
|
||||
fclose(fd);
|
||||
fd = fopen($5, "r");
|
||||
if (fd == NULL) {
|
||||
int ret = errno;
|
||||
TRACE_ERROR("Unable to open private key file %s for reading: %s", $5, strerror(ret));
|
||||
yyerror (&yylloc, conf, "Error on file name");
|
||||
YYERROR;
|
||||
}
|
||||
fclose(fd);
|
||||
conf->cnf_sec_data.cert_file = $3;
|
||||
conf->cnf_sec_data.key_file = $5;
|
||||
|
||||
CHECK_GNUTLS_DO( gnutls_certificate_set_x509_key_file(
|
||||
conf->cnf_sec_data.credentials,
|
||||
conf->cnf_sec_data.cert_file,
|
||||
conf->cnf_sec_data.key_file,
|
||||
GNUTLS_X509_FMT_PEM),
|
||||
{ yyerror (&yylloc, conf, "Error opening certificate or private key file."); YYERROR; } );
|
||||
}
|
||||
;
|
||||
|
||||
tls_ca: TLS_CA '=' QSTRING ';'
|
||||
{
|
||||
FILE * fd;
|
||||
fd = fopen($3, "rb");
|
||||
if (fd == NULL) {
|
||||
int ret = errno;
|
||||
TRACE_ERROR("Unable to open CA file %s for reading: %s", $3, strerror(ret));
|
||||
yyerror (&yylloc, conf, "Error on file name");
|
||||
YYERROR;
|
||||
}
|
||||
#ifdef GNUTLS_VERSION_300
|
||||
{
|
||||
/* We import these CA in the trust list */
|
||||
gnutls_x509_crt_t * calist;
|
||||
unsigned int cacount;
|
||||
gnutls_datum_t cafile;
|
||||
|
||||
CHECK_FCT_DO( fd_conf_stream_to_gnutls_datum(fd, &cafile),
|
||||
{ yyerror (&yylloc, conf, "Error reading CA file."); YYERROR; } );
|
||||
|
||||
CHECK_GNUTLS_DO( gnutls_x509_crt_list_import2(&calist, &cacount, &cafile, GNUTLS_X509_FMT_PEM,
|
||||
GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED),
|
||||
{ yyerror (&yylloc, conf, "Error importing CA file."); YYERROR; } );
|
||||
free(cafile.data);
|
||||
|
||||
CHECK_GNUTLS_DO( gnutls_x509_trust_list_add_cas (fd_g_config->cnf_sec_data.trustlist, calist, cacount, 0),
|
||||
{ yyerror (&yylloc, conf, "Error saving CA in trust list."); YYERROR; } );
|
||||
}
|
||||
#endif /* GNUTLS_VERSION_300 */
|
||||
fclose(fd);
|
||||
conf->cnf_sec_data.ca_file = $3;
|
||||
CHECK_GNUTLS_DO( conf->cnf_sec_data.ca_file_nr += gnutls_certificate_set_x509_trust_file(
|
||||
conf->cnf_sec_data.credentials,
|
||||
conf->cnf_sec_data.ca_file,
|
||||
GNUTLS_X509_FMT_PEM),
|
||||
{ yyerror (&yylloc, conf, "Error setting CA parameters."); YYERROR; } );
|
||||
|
||||
}
|
||||
;
|
||||
|
||||
tls_crl: TLS_CRL '=' QSTRING ';'
|
||||
{
|
||||
FILE * fd;
|
||||
fd = fopen($3, "rb");
|
||||
if (fd == NULL) {
|
||||
int ret = errno;
|
||||
TRACE_ERROR("Unable to open CRL file %s for reading: %s", $3, strerror(ret));
|
||||
yyerror (&yylloc, conf, "Error on file name");
|
||||
YYERROR;
|
||||
}
|
||||
#ifdef GNUTLS_VERSION_300
|
||||
{
|
||||
/* We import these CRL in the trust list */
|
||||
gnutls_x509_crl_t * crllist;
|
||||
unsigned int crlcount;
|
||||
gnutls_datum_t crlfile;
|
||||
|
||||
CHECK_FCT_DO( fd_conf_stream_to_gnutls_datum(fd, &crlfile),
|
||||
{ yyerror (&yylloc, conf, "Error reading CRL file."); YYERROR; } );
|
||||
|
||||
CHECK_GNUTLS_DO( gnutls_x509_crl_list_import2(&crllist, &crlcount, &crlfile, GNUTLS_X509_FMT_PEM, 0),
|
||||
{ yyerror (&yylloc, conf, "Error importing CRL file."); YYERROR; } );
|
||||
free(crlfile.data);
|
||||
|
||||
CHECK_GNUTLS_DO( gnutls_x509_trust_list_add_crls (fd_g_config->cnf_sec_data.trustlist, crllist, crlcount,
|
||||
GNUTLS_TL_VERIFY_CRL,
|
||||
0),
|
||||
{ yyerror (&yylloc, conf, "Error importing CRL in trust list."); YYERROR; } );
|
||||
}
|
||||
#endif /* GNUTLS_VERSION_300 */
|
||||
fclose(fd);
|
||||
conf->cnf_sec_data.crl_file = $3;
|
||||
CHECK_GNUTLS_DO( gnutls_certificate_set_x509_crl_file(
|
||||
conf->cnf_sec_data.credentials,
|
||||
conf->cnf_sec_data.crl_file,
|
||||
GNUTLS_X509_FMT_PEM),
|
||||
{ yyerror (&yylloc, conf, "Error setting CRL parameters."); YYERROR; } );
|
||||
}
|
||||
;
|
||||
|
||||
tls_prio: TLS_PRIO '=' QSTRING ';'
|
||||
{
|
||||
const char * err_pos = NULL;
|
||||
conf->cnf_sec_data.prio_string = $3;
|
||||
CHECK_GNUTLS_DO( gnutls_priority_init(
|
||||
&conf->cnf_sec_data.prio_cache,
|
||||
conf->cnf_sec_data.prio_string,
|
||||
&err_pos),
|
||||
{ yyerror (&yylloc, conf, "Error setting Priority parameter.");
|
||||
TRACE_ERROR("Error at position : %s", err_pos);
|
||||
YYERROR; } );
|
||||
}
|
||||
;
|
||||
|
||||
tls_dh: TLS_DH_BITS '=' INTEGER ';'
|
||||
{
|
||||
conf->cnf_sec_data.dh_bits = $3;
|
||||
}
|
||||
| TLS_DH_FILE '=' QSTRING ';'
|
||||
{
|
||||
FILE * fd;
|
||||
free(conf->cnf_sec_data.dh_file);
|
||||
conf->cnf_sec_data.dh_file = $3;
|
||||
fd = fopen($3, "r");
|
||||
if (fd == NULL) {
|
||||
int ret = errno;
|
||||
TRACE_ERROR("Unable to open DH file %s for reading: %s", $3, strerror(ret));
|
||||
yyerror (&yylloc, conf, "Error on file name");
|
||||
YYERROR;
|
||||
}
|
||||
fclose(fd);
|
||||
}
|
||||
;
|
||||
@@ -0,0 +1,79 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.h"
|
||||
|
||||
/* See include/freeDiameter/libfdcore.h for more information */
|
||||
int fd_stat_getstats(enum fd_stat_type stat, struct peer_hdr * peer,
|
||||
int * current_count, int * limit_count, int * highest_count, long long * total_count,
|
||||
struct timespec * total, struct timespec * blocking, struct timespec * last)
|
||||
{
|
||||
struct fd_peer * p = (struct fd_peer *)peer;
|
||||
TRACE_ENTRY( "%d %p %p %p %p %p %p %p %p", stat, peer, current_count, limit_count, highest_count, total_count, total, blocking, last);
|
||||
|
||||
switch (stat) {
|
||||
case STAT_G_LOCAL: {
|
||||
CHECK_FCT( fd_fifo_getstats(fd_g_local, current_count, limit_count, highest_count, total_count, total, blocking, last) );
|
||||
}
|
||||
break;
|
||||
|
||||
case STAT_G_INCOMING: {
|
||||
CHECK_FCT( fd_fifo_getstats(fd_g_incoming, current_count, limit_count, highest_count, total_count, total, blocking, last) );
|
||||
}
|
||||
break;
|
||||
|
||||
case STAT_G_OUTGOING: {
|
||||
CHECK_FCT( fd_fifo_getstats(fd_g_outgoing, current_count, limit_count, highest_count, total_count, total, blocking, last) );
|
||||
}
|
||||
break;
|
||||
|
||||
case STAT_P_PSM: {
|
||||
CHECK_PARAMS( CHECK_PEER( peer ) );
|
||||
CHECK_FCT( fd_fifo_getstats(p->p_events, current_count, limit_count, highest_count, total_count, total, blocking, last) );
|
||||
}
|
||||
break;
|
||||
|
||||
case STAT_P_TOSEND: {
|
||||
CHECK_PARAMS( CHECK_PEER( peer ) );
|
||||
CHECK_FCT( fd_fifo_getstats(p->p_tosend, current_count, limit_count, highest_count, total_count, total, blocking, last) );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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. *
|
||||
*********************************************************************************************************/
|
||||
|
||||
/* Configuration from compile-time */
|
||||
#ifndef FD_IS_CONFIG
|
||||
#define FD_IS_CONFIG
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* #undef HAVE_NTOHLL */
|
||||
#define HAVE_MALLOC_H
|
||||
/* #undef HAVE_SIGNALENT_H */
|
||||
#define HAVE_AI_ADDRCONFIG
|
||||
#define HAVE_CLOCK_GETTIME
|
||||
#define HAVE_STRNDUP
|
||||
#define HAVE_PTHREAD_BAR
|
||||
|
||||
/* #undef HOST_BIG_ENDIAN */
|
||||
|
||||
/* #undef DISABLE_SCTP */
|
||||
/* #undef DEBUG_SCTP */
|
||||
/* #undef DEBUG_WITH_META */
|
||||
/* #undef SCTP_USE_MAPPED_ADDRESSES */
|
||||
#define SCTP_CONNECTX_4_ARGS
|
||||
/* #undef SKIP_DLCLOSE */
|
||||
/* #undef DIAMID_IDNA_IGNORE */
|
||||
/* #undef DIAMID_IDNA_REJECT */
|
||||
/* #undef DISABLE_PEER_EXPIRY */
|
||||
/* #undef WORKAROUND_ACCEPT_INVALID_VSAI */
|
||||
#define GNUTLS_VERSION_210
|
||||
#define GNUTLS_VERSION_212
|
||||
/* #undef GNUTLS_VERSION_300 */
|
||||
/* #undef GNUTLS_VERSION_310 */
|
||||
|
||||
/* #undef ERRORS_ON_TODO */
|
||||
/* #undef DEBUG */
|
||||
|
||||
#define FD_PROJECT_BINARY "freeDiameterd"
|
||||
#define FD_PROJECT_NAME "freeDiameter"
|
||||
#define FD_PROJECT_VERSION_MAJOR 1
|
||||
#ifndef FD_PROJECT_VERSION_MAJOR
|
||||
# define FD_PROJECT_VERSION_MAJOR 0
|
||||
#endif /*FD_PROJECT_VERSION_MAJOR*/
|
||||
#define FD_PROJECT_VERSION_MINOR 2
|
||||
#ifndef FD_PROJECT_VERSION_MINOR
|
||||
# define FD_PROJECT_VERSION_MINOR 0
|
||||
#endif /*FD_PROJECT_VERSION_MINOR*/
|
||||
#define FD_PROJECT_VERSION_REV 1
|
||||
#ifndef FD_PROJECT_VERSION_REV
|
||||
# define FD_PROJECT_VERSION_REV 0
|
||||
#endif /*FD_PROJECT_VERSION_REV*/
|
||||
#define FD_PROJECT_VERSION_API 6
|
||||
#ifndef FD_PROJECT_VERSION_API
|
||||
# define FD_PROJECT_VERSION_API 0
|
||||
#endif /*FD_PROJECT_VERSION_API*/
|
||||
#define FD_PROJECT_COPYRIGHT "Copyright (c) 2008-2015, WIDE Project (www.wide.ad.jp) and NICT (www.nict.go.jp)"
|
||||
|
||||
#define DEFAULT_CONF_PATH "DEFAULT_CONF_PATH"
|
||||
#define DEFAULT_EXTENSIONS_PATH "CMAKE_INSTALL_PREFIX/INSTALL_EXTENSIONS_SUFFIX"
|
||||
|
||||
#ifndef FD_DEFAULT_CONF_FILENAME
|
||||
#define FD_DEFAULT_CONF_FILENAME "freeDiameter.conf"
|
||||
#endif /* FD_DEFAULT_CONF_FILENAME */
|
||||
|
||||
/* Maximum number of hooks handlers that can be registered. Make this compilation option if needed */
|
||||
#define FD_HOOK_HANDLE_LIMIT 5
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FD_IS_CONFIG */
|
||||
@@ -0,0 +1,454 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.h"
|
||||
|
||||
/* Structures for the fd_hook_data_hdl management */
|
||||
static struct fd_hook_data_hdl {
|
||||
size_t pmd_size;
|
||||
void (*pmd_init_cb)(struct fd_hook_permsgdata *);
|
||||
void (*pmd_fini_cb)(struct fd_hook_permsgdata *);
|
||||
} HDH_array[FD_HOOK_HANDLE_LIMIT];
|
||||
static int max_index = 0;
|
||||
static pthread_mutex_t HDH_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/* The structure linked from the msg structure list */
|
||||
struct pmd_list_item {
|
||||
struct fd_list chain; /* this list is ordered by hdl */
|
||||
struct fd_hook_data_hdl * hdl;
|
||||
struct fd_hook_permsgdata { } pmd; /* this data belongs to the extension; we only know the size of it */
|
||||
};
|
||||
|
||||
#define sizeof_pmd(hdl) (((size_t)&((struct pmd_list_item *)0)->pmd) + hdl->pmd_size)
|
||||
|
||||
/* Now a hook registered by an extension */
|
||||
struct fd_hook_hdl {
|
||||
struct fd_list chain[HOOK_LAST+1];
|
||||
void (*fd_hook_cb)(enum fd_hook_type type, struct msg * msg, struct peer_hdr * peer, void * other, struct fd_hook_permsgdata *pmd, void * regdata);
|
||||
void *regdata;
|
||||
struct fd_hook_data_hdl *data_hdl;
|
||||
};
|
||||
|
||||
/* Array of those hooks */
|
||||
struct {
|
||||
struct fd_list sentinel;
|
||||
pthread_rwlock_t rwlock;
|
||||
} HS_array[HOOK_LAST+1];
|
||||
|
||||
/* Initialize the array of sentinels for the hooks */
|
||||
int fd_hooks_init(void)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i <= HOOK_LAST; i++) {
|
||||
fd_list_init(&HS_array[i].sentinel, NULL);
|
||||
CHECK_POSIX( pthread_rwlock_init(&HS_array[i].rwlock, NULL) );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get a slot in the array */
|
||||
int fd_hook_data_register(
|
||||
size_t permsgdata_size,
|
||||
void (*permsgdata_init_cb) (struct fd_hook_permsgdata *),
|
||||
void (*permsgdata_fini_cb) (struct fd_hook_permsgdata *),
|
||||
struct fd_hook_data_hdl **new_handle)
|
||||
{
|
||||
int ret = ENOSPC, idx;
|
||||
TRACE_ENTRY("%zd %p %p %p", permsgdata_size, permsgdata_init_cb, permsgdata_fini_cb, new_handle);
|
||||
|
||||
CHECK_PARAMS( permsgdata_size && new_handle );
|
||||
|
||||
CHECK_POSIX( pthread_mutex_lock(&HDH_lock) );
|
||||
if (max_index < FD_HOOK_HANDLE_LIMIT) {
|
||||
idx = max_index++;
|
||||
ret = 0;
|
||||
}
|
||||
CHECK_POSIX( pthread_mutex_unlock(&HDH_lock) );
|
||||
|
||||
if (ret == 0) {
|
||||
HDH_array[idx].pmd_size = permsgdata_size;
|
||||
HDH_array[idx].pmd_init_cb = permsgdata_init_cb;
|
||||
HDH_array[idx].pmd_fini_cb = permsgdata_fini_cb;
|
||||
*new_handle = &HDH_array[idx];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register a new hook callback */
|
||||
int fd_hook_register ( uint32_t type_mask,
|
||||
void (*fd_hook_cb)(enum fd_hook_type type, struct msg * msg, struct peer_hdr * peer, void * other, struct fd_hook_permsgdata *pmd, void * regdata),
|
||||
void *regdata,
|
||||
struct fd_hook_data_hdl *data_hdl,
|
||||
struct fd_hook_hdl ** handler )
|
||||
{
|
||||
struct fd_hook_hdl * newhdl = NULL;
|
||||
int i;
|
||||
|
||||
TRACE_ENTRY("%x %p %p %p %p", type_mask, fd_hook_cb, regdata, data_hdl, handler);
|
||||
|
||||
CHECK_PARAMS( fd_hook_cb && handler );
|
||||
|
||||
CHECK_MALLOC( newhdl = malloc(sizeof(struct fd_hook_hdl)) );
|
||||
memset(newhdl, 0, sizeof(struct fd_hook_hdl));
|
||||
|
||||
newhdl->fd_hook_cb = fd_hook_cb;
|
||||
newhdl->regdata = regdata;
|
||||
newhdl->data_hdl = data_hdl;
|
||||
|
||||
for (i=0; i <= HOOK_LAST; i++) {
|
||||
fd_list_init(&newhdl->chain[i], newhdl);
|
||||
if (type_mask & (1<<i)) {
|
||||
CHECK_POSIX( pthread_rwlock_wrlock(&HS_array[i].rwlock) );
|
||||
fd_list_insert_before( &HS_array[i].sentinel, &newhdl->chain[i]);
|
||||
CHECK_POSIX( pthread_rwlock_unlock(&HS_array[i].rwlock) );
|
||||
}
|
||||
}
|
||||
|
||||
*handler = newhdl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* free this hook callback */
|
||||
int fd_hook_unregister( struct fd_hook_hdl * handler )
|
||||
{
|
||||
int i;
|
||||
TRACE_ENTRY("%p", handler);
|
||||
CHECK_PARAMS( handler );
|
||||
|
||||
for (i=0; i <= HOOK_LAST; i++) {
|
||||
if ( ! FD_IS_LIST_EMPTY(&handler->chain[i])) {
|
||||
CHECK_POSIX( pthread_rwlock_wrlock(&HS_array[i].rwlock) );
|
||||
fd_list_unlink(&handler->chain[i]);
|
||||
CHECK_POSIX( pthread_rwlock_unlock(&HS_array[i].rwlock) );
|
||||
}
|
||||
}
|
||||
|
||||
free(handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* callback for the libfdproto to free the data associated with a message */
|
||||
static void pmdl_free(struct fd_msg_pmdl *pmdl)
|
||||
{
|
||||
/* destroy all the items in the list */
|
||||
while (!FD_IS_LIST_EMPTY(&pmdl->sentinel)) {
|
||||
struct pmd_list_item * li = (struct pmd_list_item *)(pmdl->sentinel.next);
|
||||
if (li->hdl->pmd_fini_cb) {
|
||||
(*li->hdl->pmd_fini_cb)(&li->pmd);
|
||||
}
|
||||
fd_list_unlink(&li->chain);
|
||||
free(li);
|
||||
}
|
||||
CHECK_POSIX_DO( pthread_mutex_destroy(&pmdl->lock), );
|
||||
pmdl->sentinel.o = NULL;
|
||||
}
|
||||
|
||||
/* Save the list of pmd into the message structure, as well as the callback to free this list */
|
||||
void fd_hook_associate(struct msg * msg, struct fd_msg_pmdl * pmdl)
|
||||
{
|
||||
struct fd_msg_pmdl * in_msg;
|
||||
|
||||
CHECK_PARAMS_DO( msg && pmdl, return );
|
||||
in_msg = fd_msg_pmdl_get(msg);
|
||||
ASSERT(in_msg && (in_msg->sentinel.o == NULL)); /* error / already initialized ??? */
|
||||
in_msg->sentinel.o = pmdl_free;
|
||||
/* Now move all items from the pmdl pointer into the initialized list */
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
|
||||
fd_list_move_end(&in_msg->sentinel, &pmdl->sentinel);
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
|
||||
pmdl_free(pmdl);
|
||||
/* We're done */
|
||||
}
|
||||
|
||||
/* Return the location of the permsgdata area corresponding to this handle, after eventually having created it. Return NULL in case of failure */
|
||||
static struct fd_hook_permsgdata * get_or_create_pmd(struct fd_msg_pmdl *pmdl, struct fd_hook_hdl * h)
|
||||
{
|
||||
struct fd_hook_permsgdata * ret = NULL;
|
||||
struct fd_list * li;
|
||||
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
|
||||
|
||||
if (pmdl->sentinel.o == NULL) {
|
||||
pmdl->sentinel.o = pmdl_free;
|
||||
}
|
||||
|
||||
/* Search in the list for an item with the same handle. The list is ordered by this handle */
|
||||
for (li=pmdl->sentinel.next; li != &pmdl->sentinel; li = li->next) {
|
||||
struct pmd_list_item * pli = (struct pmd_list_item *) li;
|
||||
if (pli->hdl == h->data_hdl)
|
||||
ret = &pli->pmd;
|
||||
if (pli->hdl >= h->data_hdl)
|
||||
break;
|
||||
}
|
||||
if (!ret) {
|
||||
/* we need to create a new one and insert before li */
|
||||
struct pmd_list_item * pli;
|
||||
CHECK_MALLOC_DO( pli = malloc(sizeof_pmd(h->data_hdl)), );
|
||||
if (pli) {
|
||||
memset(pli, 0, sizeof_pmd(h->data_hdl));
|
||||
fd_list_init(&pli->chain, pli);
|
||||
pli->hdl = h->data_hdl;
|
||||
ret = &pli->pmd;
|
||||
if (h->data_hdl->pmd_init_cb) {
|
||||
(*h->data_hdl->pmd_init_cb)(ret);
|
||||
}
|
||||
fd_list_insert_before(li, &pli->chain);
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct fd_hook_permsgdata * fd_hook_get_request_pmd(struct fd_hook_data_hdl *data_hdl, struct msg * answer)
|
||||
{
|
||||
struct msg * qry;
|
||||
struct fd_msg_pmdl *pmdl;
|
||||
struct fd_hook_permsgdata * ret = NULL;
|
||||
struct fd_list * li;
|
||||
|
||||
CHECK_FCT_DO( fd_msg_answ_getq(answer, &qry), return NULL );
|
||||
if (!qry)
|
||||
return NULL;
|
||||
|
||||
pmdl = fd_msg_pmdl_get(qry);
|
||||
if (!pmdl)
|
||||
return NULL;
|
||||
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
|
||||
/* Search in the list for an item with the same handle. The list is ordered by this handle */
|
||||
for (li=pmdl->sentinel.next; li != &pmdl->sentinel; li = li->next) {
|
||||
struct pmd_list_item * pli = (struct pmd_list_item *) li;
|
||||
if (pli->hdl == data_hdl)
|
||||
ret = &pli->pmd;
|
||||
if (pli->hdl >= data_hdl)
|
||||
break;
|
||||
}
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Create a mask */
|
||||
uint32_t fd_hook_mask_helper(int dummy, ...)
|
||||
{
|
||||
va_list ap;
|
||||
uint32_t ret = 0;
|
||||
int next;
|
||||
|
||||
va_start(ap, dummy);
|
||||
while ((next = va_arg(ap, int)) >= 0) {
|
||||
if (next > HOOK_LAST)
|
||||
break; /* invalid parameter */
|
||||
ret |= (1<<next);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static pthread_mutex_t hook_default_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
static char * hook_default_buf = NULL;
|
||||
static size_t hook_default_len = 0;
|
||||
|
||||
/* The function that does the work of calling the extension's callbacks and also managing the permessagedata structures */
|
||||
void fd_hook_call(enum fd_hook_type type, struct msg * msg, struct fd_peer * peer, void * other, struct fd_msg_pmdl * pmdl)
|
||||
{
|
||||
struct fd_list * li;
|
||||
ASSERT(type <= HOOK_LAST);
|
||||
int call_default = 0;
|
||||
|
||||
/* lock the list of hooks for this type */
|
||||
CHECK_POSIX_DO( pthread_rwlock_rdlock(&HS_array[type].rwlock), );
|
||||
|
||||
pthread_cleanup_push( fd_cleanup_rwlock, &HS_array[type].rwlock );
|
||||
|
||||
if (FD_IS_LIST_EMPTY(&HS_array[type].sentinel)) {
|
||||
call_default = 1;
|
||||
} else {
|
||||
/* for each registered hook */
|
||||
for (li = HS_array[type].sentinel.next; li != &HS_array[type].sentinel; li = li->next) {
|
||||
struct fd_hook_hdl * h = (struct fd_hook_hdl *)li->o;
|
||||
struct fd_hook_permsgdata * pmd = NULL;
|
||||
|
||||
/* do we need to handle pmd ? */
|
||||
if (h->data_hdl && pmdl) {
|
||||
pmd = get_or_create_pmd(pmdl, h);
|
||||
}
|
||||
|
||||
/* Now, call this callback */
|
||||
(*h->fd_hook_cb)(type, msg, &peer->p_hdr, other, pmd, h->regdata);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_cleanup_pop(0);
|
||||
|
||||
/* done */
|
||||
CHECK_POSIX_DO( pthread_rwlock_unlock(&HS_array[type].rwlock), );
|
||||
|
||||
if (call_default) {
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&hook_default_mtx), );
|
||||
|
||||
pthread_cleanup_push( fd_cleanup_mutex, &hook_default_mtx );
|
||||
|
||||
/* There was no registered handler, default behavior for this hook */
|
||||
switch (type) {
|
||||
case HOOK_DATA_RECEIVED: {
|
||||
struct fd_cnx_rcvdata *rcv_data = other;
|
||||
LOG_A("RCV: %zd bytes", rcv_data->length);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_RECEIVED: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_D("RCV from '%s': %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_LOCAL: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_full(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_A("Handled to framework for sending: %s", hook_default_buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_SENDING: {
|
||||
LOG_A("SENDING message to '%s'", peer ? peer->p_hdr.info.pi_diamid : "<unknown>");
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_SENT: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_D("SENT to '%s': %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_FAILOVER: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_D("Failing over message sent to '%s': %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_PARSING_ERROR: {
|
||||
if (msg) {
|
||||
DiamId_t id = NULL;
|
||||
if (!fd_msg_source_get( msg, &id, NULL ))
|
||||
id = (DiamId_t)"<error getting source>";
|
||||
|
||||
if (!id)
|
||||
id = (DiamId_t)"<local>";
|
||||
|
||||
CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
|
||||
LOG_E("Parsing error: '%s' for the following message received from '%s':", (char *)other, (char *)id);
|
||||
LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL);
|
||||
} else {
|
||||
struct fd_cnx_rcvdata *rcv_data = other;
|
||||
CHECK_MALLOC_DO(fd_dump_extend_hexdump(&hook_default_buf, &hook_default_len, NULL, rcv_data->buffer, rcv_data->length, 0, 0), break);
|
||||
LOG_E("Parsing error: cannot parse %zdB buffer from '%s': %s", rcv_data->length, peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_PARSING_ERROR2: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
|
||||
LOG_E("Returning following message after parsing error:");
|
||||
LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_ROUTING_ERROR: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_E("Routing error: '%s' for the following message:", (char *)other);
|
||||
LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_ROUTING_FORWARD: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_D("FORWARDING: %s", hook_default_buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_ROUTING_LOCAL: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_D("DISPATCHING: %s", hook_default_buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_DROPPED: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_E("Message discarded ('%s'):", (char *)other);
|
||||
LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_PEER_CONNECT_FAILED: {
|
||||
if (msg) {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_full(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_N("Connection to '%s' failed: '%s'; CER/CEA dump:", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", (char *)other);
|
||||
LOG_SPLIT(FD_LOG_NOTICE, " ", hook_default_buf, NULL);
|
||||
} else {
|
||||
LOG_D("Connection to '%s' failed: %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", (char *)other);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_PEER_CONNECT_SUCCESS: {
|
||||
DiamId_t id = NULL;
|
||||
if ((!fd_msg_source_get( msg, &id, NULL )) && (id == NULL)) { /* The CEA is locally issued */
|
||||
fd_msg_answ_getq(msg, &msg); /* We dump the CER in that case */
|
||||
}
|
||||
CHECK_MALLOC_DO(fd_msg_dump_full(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
char protobuf[40];
|
||||
if (peer) {
|
||||
CHECK_FCT_DO(fd_peer_cnx_proto_info(&peer->p_hdr, protobuf, sizeof(protobuf)), break );
|
||||
} else {
|
||||
protobuf[0] = '-';
|
||||
protobuf[1] = '\0';
|
||||
}
|
||||
LOG_N("Connected to '%s' (%s), remote capabilities: ", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", protobuf);
|
||||
LOG_SPLIT(FD_LOG_NOTICE, " ", hook_default_buf, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pthread_cleanup_pop(0);
|
||||
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&hook_default_mtx), );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2015, 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 "fdcore-internal.h"
|
||||
|
||||
static struct dict_object * dict_avp_SI = NULL; /* Session-Id */
|
||||
static struct dict_object * dict_avp_OH = NULL; /* Origin-Host */
|
||||
static struct dict_object * dict_avp_OR = NULL; /* Origin-Realm */
|
||||
static struct dict_object * dict_avp_EM = NULL; /* Error-Message */
|
||||
static struct dict_object * dict_avp_ERH = NULL; /* Error-Reporting-Host */
|
||||
static struct dict_object * dict_avp_FAVP= NULL; /* Failed-AVP */
|
||||
static struct dict_object * dict_avp_RC = NULL; /* Result-Code */
|
||||
struct dict_object * fd_dict_avp_OSI = NULL; /* Origin-State-Id */
|
||||
struct dict_object * fd_dict_cmd_CER = NULL; /* Capabilities-Exchange-Request */
|
||||
struct dict_object * fd_dict_cmd_DWR = NULL; /* Device-Watchdog-Request */
|
||||
struct dict_object * fd_dict_avp_DC = NULL; /* Disconnect-Cause */
|
||||
struct dict_object * fd_dict_cmd_DPR = NULL; /* Disconnect-Peer-Request */
|
||||
|
||||
/* Resolve the dictionary objects */
|
||||
int fd_msg_init(void)
|
||||
{
|
||||
TRACE_ENTRY("");
|
||||
|
||||
/* Initialize the dictionary objects that we may use frequently */
|
||||
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &dict_avp_SI , ENOENT) );
|
||||
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &dict_avp_OH , ENOENT) );
|
||||
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &dict_avp_OR , ENOENT) );
|
||||
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-State-Id", &fd_dict_avp_OSI , ENOENT) );
|
||||
|
||||
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code", &dict_avp_RC , ENOENT) );
|
||||
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Message", &dict_avp_EM , ENOENT) );
|
||||
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &dict_avp_ERH , ENOENT) );
|
||||
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Failed-AVP", &dict_avp_FAVP, ENOENT) );
|
||||
|
||||
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Disconnect-Cause", &fd_dict_avp_DC , ENOENT) );
|
||||
|
||||
CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &fd_dict_cmd_CER, ENOENT ) );
|
||||
CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request", &fd_dict_cmd_DWR, ENOENT ) );
|
||||
CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Disconnect-Peer-Request", &fd_dict_cmd_DPR, ENOENT ) );
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add Origin-Host, Origin-Realm, Origin-State-Id AVPS at the end of the message */
|
||||
int fd_msg_add_origin ( struct msg * msg, int osi )
|
||||
{
|
||||
union avp_value val;
|
||||
struct avp * avp_OH = NULL;
|
||||
struct avp * avp_OR = NULL;
|
||||
struct avp * avp_OSI = NULL;
|
||||
|
||||
TRACE_ENTRY("%p", msg);
|
||||
CHECK_PARAMS( msg );
|
||||
|
||||
/* Create the Origin-Host AVP */
|
||||
CHECK_FCT( fd_msg_avp_new( dict_avp_OH, 0, &avp_OH ) );
|
||||
|
||||
/* Set its value */
|
||||
memset(&val, 0, sizeof(val));
|
||||
val.os.data = (os0_t)fd_g_config->cnf_diamid;
|
||||
val.os.len = fd_g_config->cnf_diamid_len;
|
||||
CHECK_FCT( fd_msg_avp_setvalue( avp_OH, &val ) );
|
||||
|
||||
/* Add it to the message */
|
||||
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OH ) );
|
||||
|
||||
|
||||
/* Create the Origin-Realm AVP */
|
||||
CHECK_FCT( fd_msg_avp_new( dict_avp_OR, 0, &avp_OR ) );
|
||||
|
||||
/* Set its value */
|
||||
memset(&val, 0, sizeof(val));
|
||||
val.os.data = (os0_t)fd_g_config->cnf_diamrlm;
|
||||
val.os.len = fd_g_config->cnf_diamrlm_len;
|
||||
CHECK_FCT( fd_msg_avp_setvalue( avp_OR, &val ) );
|
||||
|
||||
/* Add it to the message */
|
||||
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OR ) );
|
||||
|
||||
if (osi) {
|
||||
/* Create the Origin-State-Id AVP */
|
||||
CHECK_FCT( fd_msg_avp_new( fd_dict_avp_OSI, 0, &avp_OSI ) );
|
||||
|
||||
/* Set its value */
|
||||
memset(&val, 0, sizeof(val));
|
||||
val.u32 = fd_g_config->cnf_orstateid;
|
||||
CHECK_FCT( fd_msg_avp_setvalue( avp_OSI, &val ) );
|
||||
|
||||
/* Add it to the message */
|
||||
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OSI ) );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create a new Session-Id and add at the beginning of the message. */
|
||||
int fd_msg_new_session( struct msg * msg, os0_t opt, size_t optlen )
|
||||
{
|
||||
union avp_value val;
|
||||
struct avp * avp = NULL;
|
||||
struct session * sess = NULL;
|
||||
os0_t sid;
|
||||
size_t sidlen;
|
||||
|
||||
TRACE_ENTRY("%p %p %zd", msg, opt, optlen);
|
||||
CHECK_PARAMS( msg );
|
||||
|
||||
/* Check there is not already a session in the message */
|
||||
CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, msg, &sess, NULL) );
|
||||
CHECK_PARAMS( sess == NULL );
|
||||
|
||||
/* Ok, now create the session */
|
||||
CHECK_FCT( fd_sess_new ( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, opt, optlen ) );
|
||||
CHECK_FCT( fd_sess_getsid( sess, &sid, &sidlen) );
|
||||
|
||||
/* Create an AVP to hold it */
|
||||
CHECK_FCT( fd_msg_avp_new( dict_avp_SI, 0, &avp ) );
|
||||
|
||||
/* Set its value */
|
||||
memset(&val, 0, sizeof(val));
|
||||
val.os.data = sid;
|
||||
val.os.len = sidlen;
|
||||
CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
|
||||
|
||||
/* Add it to the message */
|
||||
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_FIRST_CHILD, avp ) );
|
||||
|
||||
/* Save the session associated with the message */
|
||||
CHECK_FCT( fd_msg_sess_set( msg, sess) );
|
||||
|
||||
/* Done! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Add Result-Code and eventually Failed-AVP, Error-Message and Error-Reporting-Host AVPs */
|
||||
int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id )
|
||||
{
|
||||
union avp_value val;
|
||||
struct avp * avp_RC = NULL;
|
||||
struct avp * avp_EM = NULL;
|
||||
struct avp * avp_ERH = NULL;
|
||||
struct avp * avp_FAVP= NULL;
|
||||
uint32_t rc_val = 0;
|
||||
int set_e_bit=0;
|
||||
int std_err_msg=0;
|
||||
|
||||
TRACE_ENTRY("%p %s %p %p %d", msg, rescode, errormsg, optavp, type_id);
|
||||
|
||||
CHECK_PARAMS( msg && rescode );
|
||||
|
||||
/* Find the enum value corresponding to the rescode string, this will give the class of error */
|
||||
{
|
||||
struct dict_object * enum_obj = NULL;
|
||||
struct dict_enumval_request req;
|
||||
memset(&req, 0, sizeof(struct dict_enumval_request));
|
||||
|
||||
/* First, get the enumerated type of the Result-Code AVP (this is fast, no need to cache the object) */
|
||||
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, dict_avp_RC, &(req.type_obj), ENOENT ) );
|
||||
|
||||
/* Now search for the value given as parameter */
|
||||
req.search.enum_name = rescode;
|
||||
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &req, &enum_obj, ENOTSUP) );
|
||||
|
||||
/* finally retrieve its data */
|
||||
CHECK_FCT_DO( fd_dict_getval( enum_obj, &(req.search) ), return EINVAL );
|
||||
|
||||
/* copy the found value, we're done */
|
||||
rc_val = req.search.enum_value.u32;
|
||||
}
|
||||
|
||||
if (type_id == 1) {
|
||||
/* Add the Origin-Host and Origin-Realm AVP */
|
||||
CHECK_FCT( fd_msg_add_origin ( msg, 0 ) );
|
||||
}
|
||||
|
||||
/* Create the Result-Code AVP */
|
||||
CHECK_FCT( fd_msg_avp_new( dict_avp_RC, 0, &avp_RC ) );
|
||||
|
||||
/* Set its value */
|
||||
memset(&val, 0, sizeof(val));
|
||||
val.u32 = rc_val;
|
||||
CHECK_FCT( fd_msg_avp_setvalue( avp_RC, &val ) );
|
||||
|
||||
/* Add it to the message */
|
||||
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_RC ) );
|
||||
|
||||
if (type_id == 2) {
|
||||
/* Add the Error-Reporting-Host AVP */
|
||||
|
||||
CHECK_FCT( fd_msg_avp_new( dict_avp_ERH, 0, &avp_ERH ) );
|
||||
|
||||
/* Set its value */
|
||||
memset(&val, 0, sizeof(val));
|
||||
val.os.data = (uint8_t *)fd_g_config->cnf_diamid;
|
||||
val.os.len = fd_g_config->cnf_diamid_len;
|
||||
CHECK_FCT( fd_msg_avp_setvalue( avp_ERH, &val ) );
|
||||
|
||||
/* Add it to the message */
|
||||
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_ERH ) );
|
||||
|
||||
}
|
||||
|
||||
/* Now add the optavp in a FailedAVP if provided */
|
||||
if (optavp) {
|
||||
struct avp * optavp_cpy = NULL;
|
||||
struct avp_hdr *opt_hdr, *optcpy_hdr;
|
||||
struct dict_object * opt_model = NULL;
|
||||
int is_grouped = 0;
|
||||
|
||||
/* Create the Failed-AVP AVP */
|
||||
CHECK_FCT( fd_msg_avp_new( dict_avp_FAVP, 0, &avp_FAVP ) );
|
||||
|
||||
/* Was this AVP a grouped one? Best effort only here */
|
||||
if (!fd_msg_model ( optavp, &opt_model ) && (opt_model != NULL)) {
|
||||
struct dict_avp_data dictdata;
|
||||
CHECK_FCT( fd_dict_getval(opt_model, &dictdata) );
|
||||
if (dictdata.avp_basetype == AVP_TYPE_GROUPED)
|
||||
is_grouped = 1;
|
||||
}
|
||||
|
||||
/* Create a new AVP with a copy of the data of the invalid or missing AVP */
|
||||
optavp_cpy = optavp;
|
||||
|
||||
if (is_grouped) {
|
||||
CHECK_FCT( fd_msg_avp_new( opt_model, 0, &optavp_cpy) );
|
||||
} else {
|
||||
CHECK_FCT( fd_msg_avp_new( NULL, AVPFL_SET_BLANK_VALUE | AVPFL_SET_RAWDATA_FROM_AVP, &optavp_cpy) );
|
||||
|
||||
CHECK_FCT( fd_msg_avp_hdr(optavp, &opt_hdr) );
|
||||
CHECK_FCT( fd_msg_avp_hdr(optavp_cpy, &optcpy_hdr) );
|
||||
memcpy(optcpy_hdr, opt_hdr, sizeof(struct avp_hdr));
|
||||
}
|
||||
|
||||
/* Add the passed AVP inside it */
|
||||
CHECK_FCT( fd_msg_avp_add( avp_FAVP, MSG_BRW_LAST_CHILD, optavp_cpy ) );
|
||||
|
||||
/* And add to the message */
|
||||
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_FAVP ) );
|
||||
}
|
||||
|
||||
|
||||
/* Deal with the 'E' bit and the error message */
|
||||
switch (rc_val / 1000) {
|
||||
case 1: /* Informational */
|
||||
case 2: /* Success */
|
||||
/* Nothing special here: no E bit, no error message unless one is specified */
|
||||
break;
|
||||
|
||||
case 3: /* Protocol Errors */
|
||||
set_e_bit = 1;
|
||||
std_err_msg = 1;
|
||||
break;
|
||||
|
||||
case 4: /* Transcient Failure */
|
||||
case 5: /* Permanent Failure */
|
||||
default:
|
||||
std_err_msg = 1;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
struct msg_hdr * hdr = NULL;
|
||||
|
||||
CHECK_FCT( fd_msg_hdr( msg, &hdr ) );
|
||||
|
||||
if (set_e_bit)
|
||||
hdr->msg_flags |= CMD_FLAG_ERROR;
|
||||
else
|
||||
hdr->msg_flags &= ~ CMD_FLAG_ERROR;
|
||||
}
|
||||
|
||||
if (std_err_msg || errormsg) {
|
||||
/* Add the Error-Message AVP */
|
||||
|
||||
CHECK_FCT( fd_msg_avp_new( dict_avp_EM, 0, &avp_EM ) );
|
||||
|
||||
/* Set its value */
|
||||
memset(&val, 0, sizeof(val));
|
||||
|
||||
if (errormsg) {
|
||||
val.os.data = (uint8_t *)errormsg;
|
||||
val.os.len = strlen(errormsg);
|
||||
} else {
|
||||
val.os.data = (uint8_t *)rescode;
|
||||
val.os.len = strlen(rescode);
|
||||
}
|
||||
CHECK_FCT( fd_msg_avp_setvalue( avp_EM, &val ) );
|
||||
|
||||
/* Add it to the message */
|
||||
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_EM ) );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fd_msg_send_int( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, void (*expirecb)(void *, DiamId_t, size_t, struct msg **), const struct timespec *timeout )
|
||||
{
|
||||
struct msg_hdr *hdr;
|
||||
DiamId_t diamid;
|
||||
|
||||
/* Save the callback in the message, with the timeout */
|
||||
CHECK_FCT( fd_msg_anscb_associate( *pmsg, anscb, data, expirecb, timeout ) );
|
||||
|
||||
/* If this is a new request, call the HOOK_MESSAGE_LOCAL hook */
|
||||
if ( (fd_msg_hdr(*pmsg, &hdr) == 0)
|
||||
&& (hdr->msg_flags & CMD_FLAG_REQUEST)
|
||||
&& (fd_msg_source_get(*pmsg, &diamid, NULL) == 0)
|
||||
&& (diamid == NULL)) {
|
||||
fd_hook_call(HOOK_MESSAGE_LOCAL, *pmsg, NULL, NULL, fd_msg_pmdl_get(*pmsg));
|
||||
}
|
||||
|
||||
/* Post the message in the outgoing queue */
|
||||
CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Send a message and optionally register a callback for an answer */
|
||||
int fd_msg_send ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data )
|
||||
{
|
||||
TRACE_ENTRY("%p %p %p", pmsg, anscb, data);
|
||||
CHECK_PARAMS( pmsg );
|
||||
|
||||
return fd_msg_send_int(pmsg, anscb, data, NULL, NULL);
|
||||
}
|
||||
|
||||
/* The variation of the same function with a timeout callback */
|
||||
int fd_msg_send_timeout ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, void (*expirecb)(void *, DiamId_t, size_t, struct msg **), const struct timespec *timeout )
|
||||
{
|
||||
TRACE_ENTRY("%p %p %p %p %p", pmsg, anscb, data, expirecb, timeout);
|
||||
CHECK_PARAMS( pmsg && expirecb && timeout );
|
||||
|
||||
return fd_msg_send_int(pmsg, anscb, data, expirecb, timeout);
|
||||
}
|
||||
|
||||
|
||||
/* Parse a message against our dictionary, and in case of error log and eventually build the error reply -- returns the parsing status */
|
||||
int fd_msg_parse_or_error( struct msg ** msg, struct msg **error)
|
||||
{
|
||||
int ret = 0;
|
||||
struct msg * m;
|
||||
struct msg_hdr * hdr = NULL;
|
||||
struct fd_pei pei;
|
||||
|
||||
TRACE_ENTRY("%p", msg);
|
||||
|
||||
CHECK_PARAMS(msg && *msg && error);
|
||||
m = *msg;
|
||||
*error = NULL;
|
||||
|
||||
/* Parse the message against our dictionary */
|
||||
ret = fd_msg_parse_rules ( m, fd_g_config->cnf_dict, &pei);
|
||||
if ((ret != EBADMSG) /* Parsing grouped AVP failed / Conflicting rule found */
|
||||
&& (ret != ENOTSUP)) /* Command is not supported / Mandatory AVP is not supported */
|
||||
return ret; /* 0 or another error */
|
||||
|
||||
/* Log */
|
||||
fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, m, NULL, pei.pei_message ?: pei.pei_errcode, fd_msg_pmdl_get(m));
|
||||
|
||||
CHECK_FCT( fd_msg_hdr(m, &hdr) );
|
||||
|
||||
/* Now create an answer error if the message is a query */
|
||||
if (hdr->msg_flags & CMD_FLAG_REQUEST) {
|
||||
|
||||
/* Create the error message */
|
||||
CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &m, pei.pei_protoerr ? MSGFL_ANSW_ERROR : 0 ) );
|
||||
|
||||
/* Set the error code */
|
||||
CHECK_FCT( fd_msg_rescode_set(m, pei.pei_errcode, pei.pei_message, pei.pei_avp, 1 ) );
|
||||
|
||||
/* free the pei AVP to avoid memory leak */
|
||||
if (pei.pei_avp_free) {
|
||||
fd_msg_free(pei.pei_avp);
|
||||
}
|
||||
|
||||
*msg = NULL;
|
||||
*error = m;
|
||||
|
||||
} else {
|
||||
do { /* Rescue error messages */
|
||||
struct avp * avp;
|
||||
union avp_value * rc = NULL;
|
||||
|
||||
/* Search the Result-Code AVP */
|
||||
CHECK_FCT_DO( fd_msg_browse(*msg, MSG_BRW_FIRST_CHILD, &avp, NULL), break );
|
||||
while (avp) {
|
||||
struct avp_hdr * ahdr;
|
||||
CHECK_FCT_DO( fd_msg_avp_hdr( avp, &ahdr ), break );
|
||||
|
||||
if ((ahdr->avp_code == AC_RESULT_CODE) && (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) ) {
|
||||
/* Parse this AVP */
|
||||
ASSERT( ahdr->avp_value );
|
||||
rc = ahdr->avp_value;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Go to next AVP */
|
||||
CHECK_FCT_DO( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), break );
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
switch (rc->u32 / 1000) {
|
||||
case 1: /* 1xxx : Informational */
|
||||
case 2: /* 2xxx : Sucess */
|
||||
/* In these cases, we want the message to validate the ABNF, so we will discard the bad message */
|
||||
break;
|
||||
|
||||
default: /* Other errors */
|
||||
/* We let the application decide what to do with the message, we rescue it */
|
||||
*error = m;
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
}
|
||||
|
||||
return EBADMSG; /* We convert ENOTSUP to EBADMSG as well */
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,357 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.h"
|
||||
|
||||
|
||||
/* TODO: change the behavior to handle properly forced ordering at beginning & end of OPEN state */
|
||||
|
||||
/* 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;
|
||||
int count = 0;
|
||||
|
||||
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) {
|
||||
TRACE_DEBUG(INFO, "Unable to resolve address for peer '%s' (%s), aborting", peer->p_hdr.info.pi_diamid, gai_strerror(ret));
|
||||
if (ret != EAI_AGAIN)
|
||||
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));
|
||||
}
|
||||
if (fd_g_config->cnf_flags.no_ip4) {
|
||||
CHECK_FCT( fd_ep_filter_family(
|
||||
&peer->p_hdr.info.pi_endpoints,
|
||||
AF_INET6));
|
||||
}
|
||||
if (fd_g_config->cnf_flags.no_ip6) {
|
||||
CHECK_FCT( fd_ep_filter_family(
|
||||
&peer->p_hdr.info.pi_endpoints,
|
||||
AF_INET));
|
||||
}
|
||||
|
||||
/* We don't use the alternate addresses that were sent by the remote peer */
|
||||
CHECK_FCT( fd_ep_clearflags(&peer->p_hdr.info.pi_endpoints, EP_FL_ADV) );
|
||||
|
||||
|
||||
/* Now check we have at least one address to attempt */
|
||||
if (FD_IS_LIST_EMPTY(&peer->p_hdr.info.pi_endpoints)) {
|
||||
TRACE_DEBUG(INFO, "No address %savailable to connect to peer '%s', aborting",
|
||||
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;
|
||||
}
|
||||
|
||||
/* Check if we are able to communicate with this peer */
|
||||
if (fd_g_config->cnf_sec_data.tls_disabled && ( peer->p_hdr.info.config.pic_flags.sec != PI_SEC_NONE)) {
|
||||
LOG_E("Peer '%s' not configured for No_TLS and TLS is locally disabled; giving up connection attempts",
|
||||
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) || (fd_g_config->cnf_flags.tls_alg)) {
|
||||
dotls_immediate = 0;
|
||||
port_no = htons(peer->p_hdr.info.config.pic_port ?: DIAMETER_PORT);
|
||||
} else {
|
||||
dotls_immediate = 1;
|
||||
port_no = htons(peer->p_hdr.info.config.pic_port ?: DIAMETER_SECURE_PORT);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 ((fd_g_config->cnf_flags.pr_tcp) || (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 */
|
||||
}
|
||||
count++;
|
||||
}
|
||||
#endif /* DISABLE_SCTP */
|
||||
|
||||
LOG_D("Prepared %d sets of connection parameters to peer %s", count, peer->p_hdr.info.pi_diamid);
|
||||
|
||||
return 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;
|
||||
int fatal_error=0;
|
||||
|
||||
TRACE_ENTRY("%p", arg);
|
||||
CHECK_PARAMS_DO( CHECK_PEER(peer), return NULL );
|
||||
|
||||
/* Set the thread name */
|
||||
{
|
||||
char buf[48];
|
||||
snprintf(buf, sizeof(buf), "ConnTo:%s", 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( fatal_error = prepare_connection_list(peer), goto out );
|
||||
rebuilt ++;
|
||||
}
|
||||
if (FD_IS_LIST_EMPTY(&peer->p_connparams)) {
|
||||
/* We encountered an error or we have looped over all the addresses of the peer. */
|
||||
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "All connection attempts failed, will retry later", NULL);
|
||||
|
||||
CHECK_FCT_DO( fatal_error = fd_event_send(peer->p_events, FDEVP_CNX_FAILED, 0, NULL), goto out );
|
||||
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) ? 1 : 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 or all addresses attempted without success */
|
||||
|
||||
/* 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,
|
||||
ALGO_HANDSHAKE_3436,
|
||||
peer->p_hdr.info.config.pic_priority, NULL),
|
||||
{
|
||||
/* Handshake failed ... */
|
||||
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "TLS Handshake failed", NULL);
|
||||
fd_cnx_destroy(cnx);
|
||||
empty_connection_list(peer);
|
||||
fd_ep_filter(&peer->p_hdr.info.pi_endpoints, EP_FL_CONF);
|
||||
goto out_pop;
|
||||
} );
|
||||
LOG_A("%s: TLS handshake successful.", peer->p_hdr.info.pi_diamid);
|
||||
} else {
|
||||
/* Prepare to receive the next message */
|
||||
CHECK_FCT_DO( fatal_error = fd_cnx_start_clear(cnx, 0), goto out_pop );
|
||||
}
|
||||
|
||||
/* Upon success, generate FDEVP_CNX_ESTABLISHED */
|
||||
CHECK_FCT_DO( fatal_error = fd_event_send(peer->p_events, FDEVP_CNX_ESTABLISHED, 0, cnx), );
|
||||
out_pop:
|
||||
;
|
||||
pthread_cleanup_pop(0);
|
||||
|
||||
out:
|
||||
|
||||
if (fatal_error) {
|
||||
|
||||
/* Cleanup the connection */
|
||||
if (cnx)
|
||||
fd_cnx_destroy(cnx);
|
||||
|
||||
/* Generate a termination event */
|
||||
CHECK_FCT_DO(fd_core_shutdown(), );
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.h"
|
||||
|
||||
/* This file contains code to handle Disconnect Peer messages (DPR and DPA) */
|
||||
|
||||
/* Delay to use before next reconnect attempt */
|
||||
int fd_p_dp_newdelay(struct fd_peer * peer)
|
||||
{
|
||||
int delay = peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc;
|
||||
|
||||
switch (peer->p_hdr.info.runtime.pir_lastDC) {
|
||||
case ACV_DC_REBOOTING:
|
||||
default:
|
||||
/* We use TcTimer to attempt reconnection */
|
||||
break;
|
||||
case ACV_DC_BUSY:
|
||||
/* No need to hammer the overloaded peer */
|
||||
delay *= 10;
|
||||
break;
|
||||
case ACV_DC_NOT_FRIEND:
|
||||
/* He does not want to speak to us... let's retry a *lot* later maybe */
|
||||
delay *= 200;
|
||||
break;
|
||||
}
|
||||
return delay;
|
||||
}
|
||||
|
||||
/* Handle a received message */
|
||||
int fd_p_dp_handle(struct msg ** msg, int req, struct fd_peer * peer)
|
||||
{
|
||||
long to_receive, to_send;
|
||||
TRACE_ENTRY("%p %d %p", msg, req, peer);
|
||||
|
||||
if (req) {
|
||||
/* We received a DPR, save the Disconnect-Cause and go to CLOSING_GRACE or terminate the connection */
|
||||
struct avp * dc;
|
||||
|
||||
CHECK_FCT( fd_msg_search_avp ( *msg, fd_dict_avp_DC, &dc ));
|
||||
if (dc) {
|
||||
struct avp_hdr * hdr;
|
||||
CHECK_FCT( fd_msg_avp_hdr( dc, &hdr ) );
|
||||
if (hdr->avp_value == NULL) {
|
||||
/* This is a sanity check */
|
||||
LOG_F("BUG: Unset value in Disconnect-Cause in DPR");
|
||||
ASSERT(0); /* To check if this really happens, and understand why... */
|
||||
}
|
||||
|
||||
/* save the cause */
|
||||
peer->p_hdr.info.runtime.pir_lastDC = hdr->avp_value->u32;
|
||||
}
|
||||
if (TRACE_BOOL(INFO)) {
|
||||
if (dc) {
|
||||
struct dict_object * dictobj;
|
||||
struct dict_enumval_request er;
|
||||
memset(&er, 0, sizeof(er));
|
||||
|
||||
/* prepare the request */
|
||||
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, fd_dict_avp_DC, &er.type_obj, ENOENT ) );
|
||||
er.search.enum_value.u32 = peer->p_hdr.info.runtime.pir_lastDC;
|
||||
|
||||
/* Search the enum value */
|
||||
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &er, &dictobj, 0 ) );
|
||||
if (dictobj) {
|
||||
CHECK_FCT( fd_dict_getval( dictobj, &er.search ) );
|
||||
TRACE_DEBUG(INFO, "Peer '%s' sent a DPR with cause: %s", peer->p_hdr.info.pi_diamid, er.search.enum_name);
|
||||
} else {
|
||||
TRACE_DEBUG(INFO, "Peer '%s' sent a DPR with unknown cause: %u", peer->p_hdr.info.pi_diamid, peer->p_hdr.info.runtime.pir_lastDC);
|
||||
}
|
||||
} else {
|
||||
TRACE_DEBUG(INFO, "Peer '%s' sent a DPR without Disconnect-Cause AVP", peer->p_hdr.info.pi_diamid);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now reply with a DPA */
|
||||
CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) );
|
||||
CHECK_FCT( fd_msg_rescode_set( *msg, "DIAMETER_SUCCESS", NULL, NULL, 1 ) );
|
||||
|
||||
/* Do we have pending exchanges with this peer? */
|
||||
CHECK_FCT( fd_peer_get_load_pending(&peer->p_hdr, &to_receive, &to_send) );
|
||||
|
||||
if ((to_receive == 0) && (to_send == 0)) {
|
||||
/* No pending exchange, move to CLOSING directly */
|
||||
CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING) );
|
||||
|
||||
/* Now send the DPA */
|
||||
CHECK_FCT( fd_out_send( msg, NULL, peer, 0) );
|
||||
|
||||
/* and move to CLOSED */
|
||||
fd_psm_cleanup(peer, 0);
|
||||
|
||||
/* Reset the timer for next connection attempt -- we'll retry sooner or later depending on the disconnection cause */
|
||||
fd_psm_next_timeout(peer, 1, fd_p_dp_newdelay(peer));
|
||||
} else {
|
||||
/* We have pending exchanges, we move to CLOSING_GRACE which allows exchanges of answers but
|
||||
not new requests */
|
||||
CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING_GRACE) );
|
||||
fd_psm_next_timeout(peer, 0, GRACE_TIMEOUT);
|
||||
|
||||
/* Now send the DPA */
|
||||
CHECK_FCT( fd_out_send( msg, NULL, peer, 0) );
|
||||
}
|
||||
} else {
|
||||
/* We received a DPA */
|
||||
int curstate = fd_peer_getstate(peer);
|
||||
if (curstate != STATE_CLOSING_GRACE) {
|
||||
TRACE_DEBUG(INFO, "Ignoring DPA received in state %s", STATE_STR(curstate));
|
||||
}
|
||||
|
||||
/* In theory, we should control the Result-Code AVP. But since we will not go back to OPEN state here anyway, let's skip it */
|
||||
|
||||
/* TODO("Control Result-Code in the DPA") */
|
||||
CHECK_FCT_DO( fd_msg_free( *msg ), /* continue */ );
|
||||
*msg = NULL;
|
||||
|
||||
/* Do we still have pending exchanges with this peer? */
|
||||
CHECK_FCT( fd_peer_get_load_pending(&peer->p_hdr, &to_receive, &to_send) );
|
||||
if ((to_receive != 0) || (to_send != 0)) {
|
||||
TRACE_DEBUG(INFO, "Received DPA but pending load: [%ld, %ld], giving grace delay before closing", to_receive, to_send);
|
||||
fd_psm_next_timeout(peer, 0, GRACE_TIMEOUT);
|
||||
peer->p_flags.pf_localterm = 1;
|
||||
} else {
|
||||
/* otherwise, go to CLOSING state, the psm will handle terminating the connection */
|
||||
CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING) );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Start disconnection of a peer: send DPR */
|
||||
int fd_p_dp_initiate(struct fd_peer * peer, char * reason)
|
||||
{
|
||||
struct msg * msg = NULL;
|
||||
struct dict_object * dictobj = NULL;
|
||||
struct avp * avp = NULL;
|
||||
struct dict_enumval_request er;
|
||||
union avp_value val;
|
||||
|
||||
TRACE_ENTRY("%p %p", peer, reason);
|
||||
|
||||
/* Create a new DWR instance */
|
||||
CHECK_FCT( fd_msg_new ( fd_dict_cmd_DPR, MSGFL_ALLOC_ETEID, &msg ) );
|
||||
|
||||
/* Add the Origin information */
|
||||
CHECK_FCT( fd_msg_add_origin ( msg, 0 ) );
|
||||
|
||||
/* Add the Disconnect-Cause */
|
||||
CHECK_FCT( fd_msg_avp_new ( fd_dict_avp_DC, 0, &avp ) );
|
||||
|
||||
/* Search the value in the dictionary */
|
||||
memset(&er, 0, sizeof(er));
|
||||
CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, fd_dict_avp_DC, &er.type_obj, ENOENT ) );
|
||||
er.search.enum_name = reason ?: "REBOOTING";
|
||||
CHECK_FCT_DO( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &er, &dictobj, ENOENT ), { ASSERT(0); /* internal error: unknown reason */ } );
|
||||
CHECK_FCT( fd_dict_getval( dictobj, &er.search ) );
|
||||
|
||||
/* Set the value in the AVP */
|
||||
val.u32 = er.search.enum_value.u32;
|
||||
CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
|
||||
CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
|
||||
|
||||
/* Save the value also in the peer */
|
||||
peer->p_hdr.info.runtime.pir_lastDC = val.u32;
|
||||
|
||||
/* Update the peer state and timer */
|
||||
CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING_GRACE) );
|
||||
fd_psm_next_timeout(peer, 0, DPR_TIMEOUT);
|
||||
|
||||
/* Now send the DPR message */
|
||||
CHECK_FCT_DO( fd_out_send(&msg, NULL, peer, 0), /* ignore since we are on timeout anyway */ );
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.h"
|
||||
|
||||
/* This file contains code to handle Device Watchdog messages (DWR and DWA) */
|
||||
|
||||
/* Check the value of Origin-State-Id is consistent in a DWR or DWA -- we return an error otherwise */
|
||||
static int check_state_id(struct msg * msg, struct fd_peer * peer)
|
||||
{
|
||||
struct avp * osi;
|
||||
|
||||
/* Check if the request contains the Origin-State-Id */
|
||||
CHECK_FCT( fd_msg_search_avp ( msg, fd_dict_avp_OSI, &osi ) );
|
||||
if (osi) {
|
||||
/* Check the value is consistent with the saved one */
|
||||
struct avp_hdr * hdr;
|
||||
CHECK_FCT( fd_msg_avp_hdr( osi, &hdr ) );
|
||||
if (hdr->avp_value == NULL) {
|
||||
/* This is a sanity check */
|
||||
LOG_F("Ignored an Origin-State-Id AVP with unset value in DWR/DWA");
|
||||
ASSERT(0); /* To check if this really happens, and understand why... */
|
||||
}
|
||||
|
||||
if (! peer->p_hdr.info.runtime.pir_orstate) {
|
||||
/* It was not already received in CER/CEA, save it now */
|
||||
peer->p_hdr.info.runtime.pir_orstate = hdr->avp_value->u32;
|
||||
}
|
||||
|
||||
if (peer->p_hdr.info.runtime.pir_orstate != hdr->avp_value->u32) {
|
||||
TRACE_DEBUG(INFO, "Received a new Origin-State-Id from peer '%s'! (%x -> %x); resetting the connection.",
|
||||
peer->p_hdr.info.pi_diamid,
|
||||
peer->p_hdr.info.runtime.pir_orstate,
|
||||
hdr->avp_value->u32 );
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create and send a DWR */
|
||||
static int send_DWR(struct fd_peer * peer)
|
||||
{
|
||||
struct msg * msg = NULL;
|
||||
|
||||
/* Create a new DWR instance */
|
||||
CHECK_FCT( fd_msg_new ( fd_dict_cmd_DWR, MSGFL_ALLOC_ETEID, &msg ) );
|
||||
|
||||
/* Add the content of the message (only the origin) */
|
||||
CHECK_FCT( fd_msg_add_origin ( msg, 1 ) );
|
||||
|
||||
/* Now send this message */
|
||||
CHECK_FCT( fd_out_send(&msg, NULL, peer, 0) );
|
||||
|
||||
/* And mark the pending DW */
|
||||
peer->p_flags.pf_dw_pending = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handle an incoming message */
|
||||
int fd_p_dw_handle(struct msg ** msg, int req, struct fd_peer * peer)
|
||||
{
|
||||
int reset_tmr = 0;
|
||||
|
||||
TRACE_ENTRY("%p %d %p", msg, req, peer);
|
||||
|
||||
/* Check the value of OSI for information */
|
||||
CHECK_FCT( check_state_id(*msg, peer) );
|
||||
|
||||
if (req) {
|
||||
/* If we receive a DWR, send back a DWA */
|
||||
CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) );
|
||||
CHECK_FCT( fd_msg_rescode_set( *msg, "DIAMETER_SUCCESS", NULL, NULL, 0 ) );
|
||||
CHECK_FCT( fd_msg_add_origin ( *msg, 1 ) );
|
||||
CHECK_FCT( fd_out_send( msg, peer->p_cnxctx, peer, 0) );
|
||||
|
||||
} else {
|
||||
/* Discard the DWA */
|
||||
CHECK_FCT_DO( fd_msg_free(*msg), /* continue */ );
|
||||
*msg = NULL;
|
||||
|
||||
/* And clear the pending DW flag */
|
||||
peer->p_flags.pf_dw_pending = 0;
|
||||
}
|
||||
|
||||
/* Now update timeout */
|
||||
if (req) {
|
||||
/* Update timeout only if we did not already send a DWR ourselves */
|
||||
reset_tmr = !peer->p_flags.pf_dw_pending;
|
||||
} else {
|
||||
/* Reset the timer */
|
||||
reset_tmr = 1;
|
||||
}
|
||||
if (reset_tmr) {
|
||||
fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
|
||||
}
|
||||
|
||||
/* If we are in REOPEN state, increment the counter */
|
||||
if (fd_peer_getstate(peer) == STATE_REOPEN) {
|
||||
peer->p_flags.pf_reopen_cnt += 1;
|
||||
|
||||
if (peer->p_flags.pf_reopen_cnt) {
|
||||
/* Send a new DWR */
|
||||
CHECK_FCT( send_DWR(peer) );
|
||||
} else {
|
||||
/* Move to OPEN state */
|
||||
CHECK_FCT( fd_psm_change_state(peer, STATE_OPEN) );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handle a timeout in the PSM (OPEN or REOPEN state only) */
|
||||
int fd_p_dw_timeout(struct fd_peer * peer)
|
||||
{
|
||||
TRACE_ENTRY("%p", peer);
|
||||
|
||||
if (peer->p_flags.pf_dw_pending) {
|
||||
/* We have sent a DWR and received no answer during TwTimer */
|
||||
CHECK_FCT( fd_psm_change_state(peer, STATE_SUSPECT) );
|
||||
fd_psm_next_timeout(peer, 0, 2 * (peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw) );
|
||||
} else {
|
||||
/* The timeout has expired, send a DWR */
|
||||
CHECK_FCT( send_DWR(peer) );
|
||||
fd_psm_next_timeout(peer, 0, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handle DW exchanges after the peer has come alive again */
|
||||
int fd_p_dw_reopen(struct fd_peer * peer)
|
||||
{
|
||||
TRACE_ENTRY("%p", peer);
|
||||
|
||||
peer->p_flags.pf_reopen_cnt = 1;
|
||||
peer->p_flags.pf_cnx_pb = 0;
|
||||
CHECK_FCT( send_DWR(peer) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.h"
|
||||
|
||||
/* Delay for garbage collection of expired peers, in seconds */
|
||||
#define GC_TIME 120
|
||||
|
||||
static pthread_t exp_thr = (pthread_t)NULL;
|
||||
static pthread_t gc_thr = (pthread_t)NULL;
|
||||
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/garb. col." );
|
||||
TRACE_ENTRY( "%p", arg );
|
||||
|
||||
do {
|
||||
struct fd_list * li, purge = FD_LIST_INITIALIZER(purge);
|
||||
|
||||
sleep(GC_TIME); /* sleep is a cancellation point */
|
||||
|
||||
/* 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->o;
|
||||
|
||||
if (fd_peer_getstate(peer) != STATE_ZOMBIE)
|
||||
continue;
|
||||
|
||||
if (peer->p_hdr.info.config.pic_flags.persist == PI_PRST_ALWAYS)
|
||||
continue; /* This peer was not supposed to terminate, keep it in the list for debug */
|
||||
|
||||
/* 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->o);
|
||||
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_core_shutdown(), );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void * exp_th_fct(void * arg)
|
||||
{
|
||||
fd_log_threadname ( "Peers/expire" );
|
||||
TRACE_ENTRY( "%p", arg );
|
||||
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&exp_mtx), { ASSERT(0); } );
|
||||
pthread_cleanup_push( fd_cleanup_mutex, &exp_mtx );
|
||||
|
||||
do {
|
||||
struct timespec now;
|
||||
struct fd_peer * first;
|
||||
|
||||
/* Check if there are expiring peers available */
|
||||
if (FD_IS_LIST_EMPTY(&exp_list)) {
|
||||
/* Just wait for a change or cancelation */
|
||||
CHECK_POSIX_DO( pthread_cond_wait( &exp_cnd, &exp_mtx ), { ASSERT(0); } );
|
||||
/* Restart the loop on wakeup */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get the pointer to the peer that expires first */
|
||||
first = (struct fd_peer *)(exp_list.next->o);
|
||||
ASSERT( CHECK_PEER(first) );
|
||||
|
||||
/* Get the current time */
|
||||
CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), { ASSERT(0); } );
|
||||
|
||||
/* If first peer is not expired, we just wait until it happens */
|
||||
if ( TS_IS_INFERIOR( &now, &first->p_exp_timer ) ) {
|
||||
|
||||
CHECK_POSIX_DO2( pthread_cond_timedwait( &exp_cnd, &exp_mtx, &first->p_exp_timer ),
|
||||
ETIMEDOUT, /* ETIMEDOUT is a normal return value, continue */,
|
||||
/* on other error, */ { ASSERT(0); } );
|
||||
|
||||
/* on wakeup, loop */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Now, the first peer in the list is expired; signal it */
|
||||
fd_list_unlink( &first->p_expiry );
|
||||
CHECK_FCT_DO( fd_event_send(first->p_events, FDEVP_TERMINATE, 0, "DO_NOT_WANT_TO_TALK_TO_YOU"), break );
|
||||
|
||||
} while (1);
|
||||
|
||||
pthread_cleanup_pop( 1 );
|
||||
|
||||
TRACE_DEBUG(INFO, "An error occurred in peers module! Expiry thread is terminating...");
|
||||
CHECK_FCT_DO(fd_core_shutdown(), );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize peers expiry mechanism */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Finish peers expiry mechanism */
|
||||
int fd_p_expi_fini(void)
|
||||
{
|
||||
CHECK_FCT_DO( fd_thr_term(&exp_thr), );
|
||||
CHECK_POSIX( pthread_mutex_lock(&exp_mtx) );
|
||||
while (!FD_IS_LIST_EMPTY(&exp_list)) {
|
||||
struct fd_peer * peer = (struct fd_peer *)(exp_list.next->o);
|
||||
fd_list_unlink(&peer->p_expiry );
|
||||
}
|
||||
CHECK_POSIX( pthread_mutex_unlock(&exp_mtx) );
|
||||
|
||||
CHECK_FCT_DO( fd_thr_term(&gc_thr), );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add / requeue a peer in the expiry list */
|
||||
int fd_p_expi_update(struct fd_peer * peer )
|
||||
{
|
||||
TRACE_ENTRY("%p", peer);
|
||||
CHECK_PARAMS( CHECK_PEER(peer) );
|
||||
|
||||
CHECK_POSIX( pthread_mutex_lock(&exp_mtx) );
|
||||
|
||||
fd_list_unlink(&peer->p_expiry );
|
||||
|
||||
/* if peer expires */
|
||||
if (peer->p_hdr.info.config.pic_flags.exp) {
|
||||
struct fd_list * li;
|
||||
|
||||
/* update the p_exp_timer value */
|
||||
CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &peer->p_exp_timer), { ASSERT(0); } );
|
||||
peer->p_exp_timer.tv_sec += peer->p_hdr.info.config.pic_lft;
|
||||
|
||||
/* add to the expiry list in appropriate position (probably around the end) */
|
||||
for (li = exp_list.prev; li != &exp_list; li = li->prev) {
|
||||
struct fd_peer * p = (struct fd_peer *)(li->o);
|
||||
if (TS_IS_INFERIOR( &p->p_exp_timer, &peer->p_exp_timer ) )
|
||||
break;
|
||||
}
|
||||
|
||||
fd_list_insert_after(li, &peer->p_expiry);
|
||||
|
||||
/* signal the expiry thread if we added in first position */
|
||||
if (li == &exp_list) {
|
||||
CHECK_POSIX( pthread_cond_signal(&exp_cnd) );
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_POSIX( pthread_mutex_unlock(&exp_mtx) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.h"
|
||||
|
||||
/* Alloc a new hbh for requests, bufferize the message and send on the connection, save in sentreq if provided */
|
||||
static int do_send(struct msg ** msg, struct cnxctx * cnx, uint32_t * hbh, struct fd_peer * peer)
|
||||
{
|
||||
struct msg_hdr * hdr;
|
||||
int msg_is_a_req;
|
||||
uint8_t * buf;
|
||||
size_t sz;
|
||||
int ret;
|
||||
uint32_t bkp_hbh = 0;
|
||||
struct msg *cpy_for_logs_only;
|
||||
|
||||
TRACE_ENTRY("%p %p %p %p", msg, cnx, hbh, peer);
|
||||
|
||||
/* Retrieve the message header */
|
||||
CHECK_FCT( fd_msg_hdr(*msg, &hdr) );
|
||||
|
||||
msg_is_a_req = (hdr->msg_flags & CMD_FLAG_REQUEST);
|
||||
if (msg_is_a_req) {
|
||||
CHECK_PARAMS(hbh && peer);
|
||||
/* Alloc the hop-by-hop id and increment the value for next message */
|
||||
bkp_hbh = hdr->msg_hbhid;
|
||||
hdr->msg_hbhid = *hbh;
|
||||
*hbh = hdr->msg_hbhid + 1;
|
||||
}
|
||||
|
||||
/* Create the message buffer */
|
||||
CHECK_FCT(fd_msg_bufferize( *msg, &buf, &sz ));
|
||||
pthread_cleanup_push( free, buf );
|
||||
|
||||
cpy_for_logs_only = *msg;
|
||||
|
||||
/* Save a request before sending so that there is no race condition with the answer */
|
||||
if (msg_is_a_req) {
|
||||
CHECK_FCT_DO( ret = fd_p_sr_store(&peer->p_sr, msg, &hdr->msg_hbhid, bkp_hbh), goto out );
|
||||
}
|
||||
|
||||
/* Log the message */
|
||||
fd_hook_call(HOOK_MESSAGE_SENT, cpy_for_logs_only, peer, NULL, fd_msg_pmdl_get(cpy_for_logs_only));
|
||||
|
||||
pthread_cleanup_push((void *)fd_msg_free, *msg /* might be NULL, no problem */);
|
||||
|
||||
/* Send the message */
|
||||
CHECK_FCT_DO( ret = fd_cnx_send(cnx, buf, sz), );
|
||||
|
||||
pthread_cleanup_pop(0);
|
||||
|
||||
out:
|
||||
;
|
||||
pthread_cleanup_pop(1);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Free remaining messages (i.e. answers) */
|
||||
if (*msg) {
|
||||
CHECK_FCT( fd_msg_free(*msg) );
|
||||
*msg = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The code of the "out" thread */
|
||||
static void * out_thr(void * arg)
|
||||
{
|
||||
struct fd_peer * peer = arg;
|
||||
int stop = 0;
|
||||
struct msg * msg;
|
||||
ASSERT( CHECK_PEER(peer) );
|
||||
|
||||
/* Set the thread name */
|
||||
{
|
||||
char buf[48];
|
||||
snprintf(buf, sizeof(buf), "OUT/%s", peer->p_hdr.info.pi_diamid);
|
||||
fd_log_threadname ( buf );
|
||||
}
|
||||
|
||||
/* Loop until cancelation */
|
||||
while (!stop) {
|
||||
int ret;
|
||||
|
||||
/* Retrieve next message to send */
|
||||
CHECK_FCT_DO( fd_fifo_get(peer->p_tosend, &msg), goto error );
|
||||
|
||||
/* Send the message, log any error */
|
||||
CHECK_FCT_DO( ret = do_send(&msg, peer->p_cnxctx, &peer->p_hbh, peer),
|
||||
{
|
||||
if (msg) {
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "Error while sending this message: %s", strerror(ret));
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, buf, fd_msg_pmdl_get(msg));
|
||||
fd_msg_free(msg);
|
||||
}
|
||||
stop = 1;
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
/* If we're here it means there was an error on the socket. We need to continue to purge the fifo & until we are canceled */
|
||||
CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), /* What do we do if it fails? */ );
|
||||
|
||||
/* Requeue all routable messages in the global "out" queue, until we are canceled once the PSM deals with the CNX_ERROR sent above */
|
||||
while ( fd_fifo_get(peer->p_tosend, &msg) == 0 ) {
|
||||
if (fd_msg_is_routable(msg)) {
|
||||
CHECK_FCT_DO(fd_fifo_post_noblock(peer->p_tofailover, (void *)&msg),
|
||||
{
|
||||
/* fallback: destroy the message */
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, "Internal error: unable to requeue this message during failover process", fd_msg_pmdl_get(msg));
|
||||
CHECK_FCT_DO(fd_msg_free(msg), /* What can we do more? */)
|
||||
} );
|
||||
} else {
|
||||
/* Just free it */
|
||||
/* fd_hook_call(HOOK_MESSAGE_DROPPED, m, NULL, "Non-routable message freed during handover", fd_msg_pmdl_get(m)); */
|
||||
CHECK_FCT_DO(fd_msg_free(msg), /* What can we do more? */)
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
/* It is not really a connection error, but the effect is the same, we are not able to send anymore message */
|
||||
CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), /* What do we do if it fails? */ );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Wrapper to sending a message either by out thread (peer in OPEN state) or directly; cnx or peer must be provided. Flags are valid only for direct sending, not through thread (unused) */
|
||||
int fd_out_send(struct msg ** msg, struct cnxctx * cnx, struct fd_peer * peer, int update_reqin_cnt)
|
||||
{
|
||||
struct msg_hdr * hdr;
|
||||
|
||||
TRACE_ENTRY("%p %p %p", msg, cnx, peer);
|
||||
CHECK_PARAMS( msg && *msg && (cnx || (peer && peer->p_cnxctx)));
|
||||
|
||||
fd_hook_call(HOOK_MESSAGE_SENDING, *msg, peer, NULL, fd_msg_pmdl_get(*msg));
|
||||
|
||||
if (update_reqin_cnt && peer) {
|
||||
CHECK_FCT( fd_msg_hdr(*msg, &hdr) );
|
||||
if (!(hdr->msg_flags & CMD_FLAG_REQUEST)) {
|
||||
/* Update the count of pending answers to send */
|
||||
CHECK_POSIX( pthread_mutex_lock(&peer->p_state_mtx) );
|
||||
peer->p_reqin_count--;
|
||||
CHECK_POSIX( pthread_mutex_unlock(&peer->p_state_mtx) );
|
||||
}
|
||||
}
|
||||
|
||||
if (fd_peer_getstate(peer) == STATE_OPEN) {
|
||||
/* Normal case: just queue for the out thread to pick it up */
|
||||
CHECK_FCT( fd_fifo_post(peer->p_tosend, msg) );
|
||||
|
||||
} else {
|
||||
int ret;
|
||||
uint32_t *hbh = NULL;
|
||||
|
||||
/* In other cases, the thread is not running, so we handle the sending directly */
|
||||
if (peer)
|
||||
hbh = &peer->p_hbh;
|
||||
|
||||
if (!cnx)
|
||||
cnx = peer->p_cnxctx;
|
||||
|
||||
/* Do send the message */
|
||||
CHECK_FCT_DO( ret = do_send(msg, cnx, hbh, peer),
|
||||
{
|
||||
if (msg) {
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "Error while sending this message: %s", strerror(ret));
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, *msg, NULL, buf, fd_msg_pmdl_get(*msg));
|
||||
fd_msg_free(*msg);
|
||||
*msg = NULL;
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Start the "out" thread that picks messages in p_tosend and send them on p_cnxctx */
|
||||
int fd_out_start(struct fd_peer * peer)
|
||||
{
|
||||
TRACE_ENTRY("%p", peer);
|
||||
CHECK_PARAMS( CHECK_PEER(peer) && (peer->p_outthr == (pthread_t)NULL) );
|
||||
|
||||
CHECK_POSIX( pthread_create(&peer->p_outthr, NULL, out_thr, peer) );
|
||||
|
||||
CHECK_FCT( fd_cnx_unordered_delivery(peer->p_cnxctx, 1) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Stop that thread */
|
||||
int fd_out_stop(struct fd_peer * peer)
|
||||
{
|
||||
TRACE_ENTRY("%p", peer);
|
||||
CHECK_PARAMS( CHECK_PEER(peer) );
|
||||
|
||||
CHECK_FCT( fd_cnx_unordered_delivery(peer->p_cnxctx, 0) );
|
||||
|
||||
CHECK_FCT( fd_thr_term(&peer->p_outthr) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,958 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.h"
|
||||
|
||||
/*
|
||||
This file implement a Peer State Machine which is a mix of:
|
||||
- the state machine described in rfc3588bis
|
||||
- the state machine described in rfc3539#section-3.4
|
||||
- the following observations.
|
||||
|
||||
The delivery of Diameter messages must not always be unordered: order is important at
|
||||
begining and end of a connection lifetime. It means we need agility to
|
||||
switch between "ordering enforced" and "ordering not enforced to counter
|
||||
Head of the Line Blocking" modes of operation.
|
||||
|
||||
The connection state machine represented in RFC3588 (and RFC6733) is
|
||||
incomplete, because it lacks the SUSPECT state and the 3 DWR/DWA
|
||||
exchanges (section 5.1) when the peer recovers from this state.
|
||||
Personnally I don't see the rationale for exchanging 3 messages (why 3?)
|
||||
but, if we require at least 1 DWR/DWA exchange to be always performed
|
||||
after the CER/CEA exchange (and initiated by the peer that sent the
|
||||
CEA), we have a simple way to deal with our ordering problem, as resumed
|
||||
below. Peers are: [i]nitiator, [r]esponder.
|
||||
(1) [i] SCTP connection attempt.
|
||||
(2) [r] accept the connection.
|
||||
(3) [i,r] (if secure port) DTLS handshake, close on failure.
|
||||
(4) [i] Send CER
|
||||
(5) [r] Receive CER, send CEA using stream 0, flag "unordered" cleared.
|
||||
[r] Immediately send a DWR after the CEA, also using stream 0,
|
||||
flag "unordered" cleared.
|
||||
[r] Move to STATE_OPEN_NEW state -- equivalent to OPEN except
|
||||
that all messages are sent ordered at the moment.
|
||||
(6) [i] receive CEA, move to OPEN state. All messages can be sent
|
||||
unordered in OPEN state.
|
||||
[i] As per normal operation, reply with DWA to the DWR.
|
||||
(7) [r] Upon reception of the DWA, move to OPEN state, messages can be
|
||||
sent unordered from this point.
|
||||
|
||||
Note about (5) and (6): if the Diameter Identity received in CER or CEA
|
||||
does not match the credentials from the certificate presented during
|
||||
TLS handshake, we may need to specify a path of clean disconnection
|
||||
(not blocking the remote peer waiting for something).
|
||||
|
||||
This proposed mechanism removes the problem of application messages
|
||||
received before the CEA by the initiator. Note that if the "old" inband
|
||||
TLS handshake is used, this handshake plays the same synchronization
|
||||
role than the new DWR/DWA, which becomes useless.
|
||||
|
||||
|
||||
The other time where ordering is important is by the end of connection
|
||||
lifetime, when one peer is shutting down the link for some reason
|
||||
(reboot, overload, no activity, etc...). In case of unordered delivery,
|
||||
we may have:
|
||||
- peer A sends an application message followed by a DPR. Peer B receives
|
||||
the DPR first and tears down the connection. Application message is lost.
|
||||
- Peer B sends an application message, then receives a DPR and answers a
|
||||
DPA. Peer A receives the DPA before the application message. The
|
||||
application message is lost.
|
||||
|
||||
This situation is actually happening easily because DPR/DPA messages are
|
||||
very short, while application messages can be quite large. Therefore,
|
||||
they require much more time to deliver.
|
||||
|
||||
I really cannot see a way to counter this effect by using the ordering
|
||||
of the messages, except by applying a timer (state STATE_CLOSING_GRACE).
|
||||
This timer can be also useful when we detect that some messages has not
|
||||
yet received an answer on this link, to give time to the application to
|
||||
complete the exchange ongoing.
|
||||
|
||||
However, this problem must be balanced with the fact that the message
|
||||
that is lost will be in many cases sent again as the failover mechanism
|
||||
specifies.
|
||||
*/
|
||||
|
||||
/* The actual declaration of peer_state_str */
|
||||
DECLARE_STATE_STR();
|
||||
|
||||
/* Helper for next macro */
|
||||
#define case_str( _val ) \
|
||||
case _val : return #_val
|
||||
|
||||
DECLARE_PEV_STR();
|
||||
|
||||
/************************************************************************/
|
||||
/* Delayed startup */
|
||||
/************************************************************************/
|
||||
static int started = 0;
|
||||
static pthread_mutex_t started_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t started_cnd = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
/* Wait for start signal */
|
||||
static int fd_psm_waitstart()
|
||||
{
|
||||
int ret = 0;
|
||||
TRACE_ENTRY("");
|
||||
CHECK_POSIX( pthread_mutex_lock(&started_mtx) );
|
||||
awake:
|
||||
if (!ret && !started) {
|
||||
pthread_cleanup_push( fd_cleanup_mutex, &started_mtx );
|
||||
CHECK_POSIX_DO( ret = pthread_cond_wait(&started_cnd, &started_mtx), );
|
||||
pthread_cleanup_pop( 0 );
|
||||
goto awake;
|
||||
}
|
||||
CHECK_POSIX( pthread_mutex_unlock(&started_mtx) );
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Allow the state machines to start */
|
||||
int fd_psm_start()
|
||||
{
|
||||
TRACE_ENTRY("");
|
||||
CHECK_POSIX( pthread_mutex_lock(&started_mtx) );
|
||||
started = 1;
|
||||
CHECK_POSIX( pthread_cond_broadcast(&started_cnd) );
|
||||
CHECK_POSIX( pthread_mutex_unlock(&started_mtx) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Manage the list of active peers */
|
||||
/************************************************************************/
|
||||
|
||||
/* Enter/leave OPEN state */
|
||||
static int enter_open_state(struct fd_peer * peer)
|
||||
{
|
||||
struct fd_list * li;
|
||||
CHECK_PARAMS( FD_IS_LIST_EMPTY(&peer->p_actives) );
|
||||
|
||||
/* Callback registered by the credential validator (fd_peer_validate_register) */
|
||||
if (peer->p_cb2) {
|
||||
CHECK_FCT_DO( (*peer->p_cb2)(&peer->p_hdr.info),
|
||||
{
|
||||
TRACE_DEBUG(FULL, "Validation failed, terminating the connection");
|
||||
fd_psm_terminate(peer, "DO_NOT_WANT_TO_TALK_TO_YOU" );
|
||||
} );
|
||||
peer->p_cb2 = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Insert in the active peers list */
|
||||
CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_activ_peers_rw) );
|
||||
for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) {
|
||||
struct fd_peer * next_p = (struct fd_peer *)li->o;
|
||||
int cmp = fd_os_cmp(peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen,
|
||||
next_p->p_hdr.info.pi_diamid, next_p->p_hdr.info.pi_diamidlen);
|
||||
if (cmp < 0)
|
||||
break;
|
||||
}
|
||||
fd_list_insert_before(li, &peer->p_actives);
|
||||
CHECK_POSIX( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
|
||||
|
||||
/* Callback registered when the peer was added, by fd_peer_add */
|
||||
if (peer->p_cb) {
|
||||
LOG_N("Calling add callback for peer %s", peer->p_hdr.info.pi_diamid);
|
||||
(*peer->p_cb)(&peer->p_hdr.info, peer->p_cb_data); /* TODO: do this in a separate detached thread? */
|
||||
// peer->p_cb = NULL;
|
||||
// peer->p_cb_data = NULL;
|
||||
}
|
||||
|
||||
/* Start the thread to handle outgoing messages */
|
||||
CHECK_FCT( fd_out_start(peer) );
|
||||
|
||||
/* Update the expiry timer now */
|
||||
CHECK_FCT( fd_p_expi_update(peer) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int leave_open_state(struct fd_peer * peer, int skip_failover)
|
||||
{
|
||||
/* Remove from active peers list */
|
||||
CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_activ_peers_rw) );
|
||||
fd_list_unlink( &peer->p_actives );
|
||||
CHECK_POSIX( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
|
||||
|
||||
/* Stop the "out" thread */
|
||||
CHECK_FCT( fd_out_stop(peer) );
|
||||
|
||||
/* Failover the messages */
|
||||
if (!skip_failover) {
|
||||
fd_peer_failover_msg(peer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Helpers for state changes */
|
||||
/************************************************************************/
|
||||
|
||||
/* Cleanup pending events in the peer */
|
||||
void fd_psm_events_free(struct fd_peer * peer)
|
||||
{
|
||||
struct fd_event * ev;
|
||||
/* Purge all events, and free the associated data if any */
|
||||
while (fd_fifo_tryget( peer->p_events, &ev ) == 0) {
|
||||
switch (ev->code) {
|
||||
case FDEVP_CNX_ESTABLISHED: {
|
||||
fd_cnx_destroy(ev->data);
|
||||
}
|
||||
break;
|
||||
|
||||
case FDEVP_TERMINATE:
|
||||
/* Do not free the string since it is a constant */
|
||||
break;
|
||||
|
||||
case FDEVP_CNX_INCOMING: {
|
||||
struct cnx_incoming * evd = ev->data;
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, evd->cer, NULL, "Message discarded while cleaning peer state machine queue.", fd_msg_pmdl_get(evd->cer));
|
||||
CHECK_FCT_DO( fd_msg_free(evd->cer), /* continue */);
|
||||
fd_cnx_destroy(evd->cnx);
|
||||
}
|
||||
default:
|
||||
free(ev->data);
|
||||
}
|
||||
free(ev);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read state */
|
||||
int fd_peer_get_state(struct peer_hdr *peer)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct fd_peer * p = (struct fd_peer *)peer;
|
||||
|
||||
if (!CHECK_PEER(p))
|
||||
return -1;
|
||||
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&p->p_state_mtx), return -1 );
|
||||
ret = p->p_state;
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&p->p_state_mtx), return -1 );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Change state */
|
||||
int fd_psm_change_state(struct fd_peer * peer, int new_state)
|
||||
{
|
||||
int old;
|
||||
|
||||
TRACE_ENTRY("%p %d(%s)", peer, new_state, STATE_STR(new_state));
|
||||
CHECK_PARAMS( CHECK_PEER(peer) );
|
||||
|
||||
old = fd_peer_getstate(peer);
|
||||
if (old == new_state)
|
||||
return 0;
|
||||
|
||||
LOG(((old == STATE_OPEN) || (new_state == STATE_OPEN)) ? FD_LOG_NOTICE : FD_LOG_DEBUG, "'%s'\t-> '%s'\t'%s'",
|
||||
STATE_STR(old),
|
||||
STATE_STR(new_state),
|
||||
peer->p_hdr.info.pi_diamid);
|
||||
|
||||
|
||||
CHECK_POSIX( pthread_mutex_lock(&peer->p_state_mtx) );
|
||||
peer->p_state = new_state;
|
||||
CHECK_POSIX( pthread_mutex_unlock(&peer->p_state_mtx) );
|
||||
|
||||
if (old == STATE_OPEN) {
|
||||
CHECK_FCT( leave_open_state(peer, new_state == STATE_CLOSING_GRACE) );
|
||||
}
|
||||
if (old == STATE_CLOSING_GRACE) {
|
||||
fd_peer_failover_msg(peer);
|
||||
}
|
||||
|
||||
if (new_state == STATE_OPEN) {
|
||||
CHECK_FCT( enter_open_state(peer) );
|
||||
}
|
||||
|
||||
if (new_state == STATE_CLOSED) {
|
||||
/* Purge event list */
|
||||
fd_psm_events_free(peer);
|
||||
|
||||
/* Reset the counter of pending anwers to send */
|
||||
peer->p_reqin_count = 0;
|
||||
|
||||
/* If the peer is not persistant, we destroy it */
|
||||
if (peer->p_hdr.info.config.pic_flags.persist == PI_PRST_NONE) {
|
||||
CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, NULL) );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set timeout timer of next event */
|
||||
void fd_psm_next_timeout(struct fd_peer * peer, int add_random, int delay)
|
||||
{
|
||||
TRACE_DEBUG(FULL, "Peer timeout reset to %d seconds%s", delay, add_random ? " (+/- 2)" : "" );
|
||||
|
||||
/* Initialize the timer */
|
||||
CHECK_POSIX_DO( clock_gettime( CLOCK_REALTIME, &peer->p_psm_timer ), ASSERT(0) );
|
||||
|
||||
if (add_random) {
|
||||
if (delay > 2)
|
||||
delay -= 2;
|
||||
else
|
||||
delay = 0;
|
||||
|
||||
/* Add a random value between 0 and 4sec */
|
||||
peer->p_psm_timer.tv_sec += random() % 4;
|
||||
peer->p_psm_timer.tv_nsec+= random() % 1000000000L;
|
||||
if (peer->p_psm_timer.tv_nsec >= 1000000000L) {
|
||||
peer->p_psm_timer.tv_nsec -= 1000000000L;
|
||||
peer->p_psm_timer.tv_sec ++;
|
||||
}
|
||||
}
|
||||
|
||||
peer->p_psm_timer.tv_sec += delay;
|
||||
|
||||
#ifdef SLOW_PSM
|
||||
/* temporary for debug */
|
||||
peer->p_psm_timer.tv_sec += 10;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Cleanup the peer */
|
||||
void fd_psm_cleanup(struct fd_peer * peer, int terminate)
|
||||
{
|
||||
/* Move to CLOSED state: failover messages, stop OUT thread, unlink peer from active list */
|
||||
if (fd_peer_getstate(peer) != STATE_ZOMBIE) {
|
||||
CHECK_FCT_DO( fd_psm_change_state(peer, STATE_CLOSED), /* continue */ );
|
||||
}
|
||||
|
||||
fd_p_cnx_abort(peer, terminate);
|
||||
|
||||
fd_p_ce_clear_cnx(peer, NULL);
|
||||
|
||||
if (peer->p_receiver) {
|
||||
fd_cnx_destroy(peer->p_receiver);
|
||||
peer->p_receiver = NULL;
|
||||
}
|
||||
|
||||
if (terminate) {
|
||||
fd_psm_events_free(peer);
|
||||
CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* The PSM thread */
|
||||
/************************************************************************/
|
||||
/* Cancelation cleanup : set ZOMBIE state in the peer */
|
||||
void cleanup_setstate(void * arg)
|
||||
{
|
||||
struct fd_peer * peer = (struct fd_peer *)arg;
|
||||
CHECK_PARAMS_DO( CHECK_PEER(peer), return );
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), );
|
||||
peer->p_state = STATE_ZOMBIE;
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), );
|
||||
return;
|
||||
}
|
||||
|
||||
/* The state machine thread (controler) */
|
||||
static void * p_psm_th( void * arg )
|
||||
{
|
||||
struct fd_peer * peer = (struct fd_peer *)arg;
|
||||
int created_started = started ? 1 : 0;
|
||||
int event;
|
||||
size_t ev_sz;
|
||||
void * ev_data;
|
||||
int cur_state;
|
||||
|
||||
CHECK_PARAMS_DO( CHECK_PEER(peer), ASSERT(0) );
|
||||
|
||||
pthread_cleanup_push( cleanup_setstate, arg );
|
||||
|
||||
/* Set the thread name */
|
||||
{
|
||||
char buf[48];
|
||||
snprintf(buf, sizeof(buf), "PSM/%s", peer->p_hdr.info.pi_diamid);
|
||||
fd_log_threadname ( buf );
|
||||
}
|
||||
|
||||
/* The state machine starts in CLOSED state */
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), goto psm_end );
|
||||
peer->p_state = STATE_CLOSED;
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), goto psm_end );
|
||||
|
||||
/* 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) {
|
||||
fd_psm_next_timeout(peer, 0, INCNX_TIMEOUT);
|
||||
} else {
|
||||
fd_psm_next_timeout(peer, created_started, 0);
|
||||
}
|
||||
|
||||
psm_loop:
|
||||
/* Get next event */
|
||||
TRACE_DEBUG(FULL, "'%s' in state '%s' waiting for next event.",
|
||||
peer->p_hdr.info.pi_diamid, STATE_STR(fd_peer_getstate(peer)));
|
||||
CHECK_FCT_DO( fd_event_timedget(peer->p_events, &peer->p_psm_timer, FDEVP_PSM_TIMEOUT, &event, &ev_sz, &ev_data), goto psm_end );
|
||||
|
||||
cur_state = fd_peer_getstate(peer);
|
||||
if (cur_state == -1)
|
||||
goto psm_end;
|
||||
|
||||
TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p,%zd)\t'%s'",
|
||||
STATE_STR(cur_state),
|
||||
fd_pev_str(event), ev_data, ev_sz,
|
||||
peer->p_hdr.info.pi_diamid);
|
||||
|
||||
/* Now, the action depends on the current state and the incoming event */
|
||||
|
||||
/* The following states are impossible */
|
||||
ASSERT( cur_state != STATE_NEW );
|
||||
ASSERT( cur_state != STATE_ZOMBIE );
|
||||
ASSERT( cur_state != STATE_OPEN_HANDSHAKE ); /* because it should exist only between two loops */
|
||||
|
||||
/* Purge invalid events */
|
||||
if (!CHECK_PEVENT(event)) {
|
||||
TRACE_DEBUG(INFO, "Invalid event received in PSM '%s' : %d", peer->p_hdr.info.pi_diamid, event);
|
||||
ASSERT(0); /* we should investigate this situation */
|
||||
goto psm_loop;
|
||||
}
|
||||
|
||||
/* Requests to terminate the peer object */
|
||||
if (event == FDEVP_TERMINATE) {
|
||||
switch (cur_state) {
|
||||
case STATE_OPEN:
|
||||
case STATE_OPEN_NEW:
|
||||
case STATE_REOPEN:
|
||||
/* We cannot just close the connection, we have to send a DPR first */
|
||||
CHECK_FCT_DO( fd_p_dp_initiate(peer, ev_data), goto psm_end );
|
||||
goto psm_loop;
|
||||
|
||||
/*
|
||||
case STATE_CLOSING:
|
||||
case STATE_CLOSING_GRACE:
|
||||
case STATE_WAITCNXACK:
|
||||
case STATE_WAITCNXACK_ELEC:
|
||||
case STATE_WAITCEA:
|
||||
case STATE_SUSPECT:
|
||||
case STATE_CLOSED:
|
||||
*/
|
||||
default:
|
||||
/* In these cases, we just cleanup the peer object (if needed) and terminate */
|
||||
goto psm_end;
|
||||
}
|
||||
}
|
||||
|
||||
/* A message was received */
|
||||
if (event == FDEVP_CNX_MSG_RECV) {
|
||||
struct msg * msg = NULL;
|
||||
struct msg_hdr * hdr;
|
||||
struct fd_cnx_rcvdata rcv_data;
|
||||
struct fd_msg_pmdl * pmdl = NULL;
|
||||
|
||||
rcv_data.buffer = ev_data;
|
||||
rcv_data.length = ev_sz;
|
||||
pmdl = fd_msg_pmdl_get_inbuf(rcv_data.buffer, rcv_data.length);
|
||||
|
||||
/* Parse the received buffer */
|
||||
CHECK_FCT_DO( fd_msg_parse_buffer( (void *)&ev_data, ev_sz, &msg),
|
||||
{
|
||||
fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, NULL, peer, &rcv_data, pmdl );
|
||||
free(ev_data);
|
||||
CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), goto psm_reset );
|
||||
goto psm_loop;
|
||||
} );
|
||||
|
||||
fd_hook_associate(msg, pmdl);
|
||||
CHECK_FCT_DO( fd_msg_source_set( msg, peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen), goto psm_end);
|
||||
|
||||
/* If the current state does not allow receiving messages, just drop it */
|
||||
if (cur_state == STATE_CLOSED) {
|
||||
/* In such case, just discard the message */
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, "Message purged from queue, peer in CLOSED state", fd_msg_pmdl_get(msg));
|
||||
fd_msg_free(msg);
|
||||
goto psm_loop;
|
||||
}
|
||||
|
||||
/* Extract the header */
|
||||
CHECK_FCT_DO( fd_msg_hdr(msg, &hdr), goto psm_end );
|
||||
|
||||
/* If it is an answer, associate with the request or drop */
|
||||
if (!(hdr->msg_flags & CMD_FLAG_REQUEST)) {
|
||||
struct msg * req;
|
||||
/* Search matching request (same hbhid) */
|
||||
CHECK_FCT_DO( fd_p_sr_fetch(&peer->p_sr, hdr->msg_hbhid, &req), goto psm_end );
|
||||
if (req == NULL) {
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, "Answer received with no corresponding sent request.", fd_msg_pmdl_get(msg));
|
||||
fd_msg_free(msg);
|
||||
goto psm_loop;
|
||||
}
|
||||
|
||||
/* Associate */
|
||||
CHECK_FCT_DO( fd_msg_answ_associate( msg, req ), goto psm_end );
|
||||
|
||||
}
|
||||
|
||||
/* Log incoming message */
|
||||
fd_hook_call(HOOK_MESSAGE_RECEIVED, msg, peer, NULL, fd_msg_pmdl_get(msg));
|
||||
|
||||
if (cur_state == STATE_OPEN_NEW) {
|
||||
/* OK, we have received something, so the connection is supposedly now in OPEN state at the remote site */
|
||||
fd_psm_change_state(peer, STATE_OPEN );
|
||||
}
|
||||
|
||||
/* Now handle non-link-local messages */
|
||||
if (fd_msg_is_routable(msg)) {
|
||||
switch (cur_state) {
|
||||
/* To maximize compatibility -- should not be a security issue here */
|
||||
case STATE_REOPEN:
|
||||
case STATE_SUSPECT:
|
||||
case STATE_CLOSING:
|
||||
case STATE_CLOSING_GRACE:
|
||||
TRACE_DEBUG(FULL, "Accepted a message while not in OPEN state... ");
|
||||
/* The standard situation : */
|
||||
case STATE_OPEN_NEW:
|
||||
case STATE_OPEN:
|
||||
/* We received a valid routable message, update the expiry timer */
|
||||
CHECK_FCT_DO( fd_p_expi_update(peer), goto psm_end );
|
||||
|
||||
/* Set the message source and add the Route-Record */
|
||||
CHECK_FCT_DO( fd_msg_source_setrr( msg, peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen, fd_g_config->cnf_dict ), goto psm_end);
|
||||
|
||||
if ((hdr->msg_flags & CMD_FLAG_REQUEST)) {
|
||||
/* Mark the incoming request so that we know we have pending answers for this peer */
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), goto psm_end );
|
||||
peer->p_reqin_count++;
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), goto psm_end );
|
||||
}
|
||||
|
||||
/* Requeue to the global incoming queue */
|
||||
CHECK_FCT_DO(fd_fifo_post(fd_g_incoming, &msg), goto psm_end );
|
||||
|
||||
/* Update the peer timer (only in OPEN state) */
|
||||
if ((cur_state == STATE_OPEN) && (!peer->p_flags.pf_dw_pending)) {
|
||||
fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
|
||||
}
|
||||
break;
|
||||
|
||||
/* In other states, we discard the message, it is either old or invalid to send it for the remote peer */
|
||||
case STATE_WAITCNXACK:
|
||||
case STATE_WAITCNXACK_ELEC:
|
||||
case STATE_WAITCEA:
|
||||
case STATE_CLOSED:
|
||||
default: {
|
||||
/* In such case, just discard the message */
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "Received while peer state machine was in state %s.", STATE_STR(cur_state));
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, buf, fd_msg_pmdl_get(msg));
|
||||
fd_msg_free(msg);
|
||||
}
|
||||
}
|
||||
goto psm_loop;
|
||||
}
|
||||
|
||||
/* Link-local message: They must be understood by our dictionary, otherwise we return an error */
|
||||
{
|
||||
struct msg * error = NULL;
|
||||
int ret = fd_msg_parse_or_error( &msg, &error );
|
||||
if (ret != EBADMSG) {
|
||||
CHECK_FCT_DO( ret,
|
||||
{
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "%s: An unexpected error occurred while parsing a link-local message", peer->p_hdr.info.pi_diamid);
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, buf, fd_msg_pmdl_get(msg));
|
||||
fd_msg_free(msg);
|
||||
goto psm_end;
|
||||
} );
|
||||
} else {
|
||||
if (msg == NULL) {
|
||||
/* Send the error back to the peer */
|
||||
CHECK_FCT_DO( ret = fd_out_send(&error, NULL, peer, 0), );
|
||||
if (error) {
|
||||
char buf[256];
|
||||
/* Only if an error occurred & the message was not saved / dumped */
|
||||
snprintf(buf, sizeof(buf), "%s: error sending a message", peer->p_hdr.info.pi_diamid);
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, error, peer, buf, fd_msg_pmdl_get(error));
|
||||
CHECK_FCT_DO( fd_msg_free(error), goto psm_end);
|
||||
}
|
||||
} else {
|
||||
char buf[256];
|
||||
/* We received an invalid answer, let's disconnect */
|
||||
snprintf(buf, sizeof(buf), "%s: Received invalid answer to Base protocol message, disconnecting...", peer->p_hdr.info.pi_diamid);
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, buf, fd_msg_pmdl_get(msg));
|
||||
CHECK_FCT_DO( fd_msg_free(msg), goto psm_end);
|
||||
CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), goto psm_reset );
|
||||
}
|
||||
goto psm_loop;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle the LL message and update the expiry timer appropriately */
|
||||
switch (hdr->msg_code) {
|
||||
case CC_CAPABILITIES_EXCHANGE:
|
||||
CHECK_FCT_DO( fd_p_ce_msgrcv(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer),
|
||||
{
|
||||
if (msg)
|
||||
CHECK_FCT_DO( fd_msg_free(msg), );
|
||||
goto psm_reset;
|
||||
} );
|
||||
break;
|
||||
|
||||
case CC_DISCONNECT_PEER:
|
||||
CHECK_FCT_DO( fd_p_dp_handle(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_reset );
|
||||
if (fd_peer_getstate(peer) == STATE_CLOSING)
|
||||
goto psm_end;
|
||||
|
||||
break;
|
||||
|
||||
case CC_DEVICE_WATCHDOG:
|
||||
/* We received a WDA message, update the expiry timer */
|
||||
if (!(hdr->msg_flags & CMD_FLAG_REQUEST))
|
||||
{
|
||||
fd_p_expi_update(peer);
|
||||
}
|
||||
|
||||
CHECK_FCT_DO( fd_p_dw_handle(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_reset );
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Unknown / unexpected / invalid message -- but validated by our dictionary */
|
||||
TRACE_DEBUG(INFO, "Invalid non-routable command received: %u.", hdr->msg_code);
|
||||
if (hdr->msg_flags & CMD_FLAG_REQUEST) {
|
||||
do {
|
||||
/* Reply with an error code */
|
||||
CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, MSGFL_ANSW_ERROR ), break );
|
||||
|
||||
/* Set the error code */
|
||||
CHECK_FCT_DO( fd_msg_rescode_set(msg, "DIAMETER_COMMAND_UNSUPPORTED", "Or maybe the P-bit or application Id are erroneous.", NULL, 1 ), break );
|
||||
|
||||
/* Send the answer */
|
||||
CHECK_FCT_DO( fd_out_send(&msg, peer->p_cnxctx, peer, 0), break );
|
||||
} while (0);
|
||||
} else {
|
||||
/* We did ASK for it ??? */
|
||||
TRACE_DEBUG(INFO, "Received answer with erroneous 'is_routable' result...");
|
||||
}
|
||||
|
||||
/* Cleanup the message if not done */
|
||||
if (msg) {
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "Received un-handled non-routable command from peer '%s'.", peer->p_hdr.info.pi_diamid);
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, buf, fd_msg_pmdl_get(msg));
|
||||
CHECK_FCT_DO( fd_msg_free(msg), /* continue */);
|
||||
msg = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
/* At this point the message must have been fully handled already */
|
||||
if (msg) {
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "Internal error ('%s'): unhandled message.", peer->p_hdr.info.pi_diamid);
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, buf, fd_msg_pmdl_get(msg));
|
||||
fd_msg_free(msg);
|
||||
}
|
||||
|
||||
goto psm_loop;
|
||||
}
|
||||
|
||||
/* The connection object is broken */
|
||||
if (event == FDEVP_CNX_ERROR) {
|
||||
switch (cur_state) {
|
||||
case STATE_WAITCNXACK_ELEC:
|
||||
/* Abort the initiating side */
|
||||
fd_p_cnx_abort(peer, 0);
|
||||
/* Process the receiver side */
|
||||
CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end );
|
||||
break;
|
||||
|
||||
case STATE_WAITCEA:
|
||||
case STATE_OPEN:
|
||||
case STATE_OPEN_NEW:
|
||||
case STATE_REOPEN:
|
||||
case STATE_WAITCNXACK:
|
||||
case STATE_SUSPECT:
|
||||
default:
|
||||
/* Mark the connection problem */
|
||||
peer->p_flags.pf_cnx_pb = 1;
|
||||
|
||||
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "The connection was broken", NULL);
|
||||
|
||||
/* Destroy the connection, restart the timer to a new connection attempt */
|
||||
fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
|
||||
|
||||
case STATE_CLOSED:
|
||||
goto psm_reset;
|
||||
|
||||
case STATE_CLOSING:
|
||||
/* We sent a DPR so we are terminating, do not wait for DPA */
|
||||
goto psm_end;
|
||||
|
||||
case STATE_CLOSING_GRACE:
|
||||
if (peer->p_flags.pf_localterm) /* initiated here */
|
||||
goto psm_end;
|
||||
|
||||
fd_psm_cleanup(peer, 0);
|
||||
|
||||
/* Reset the timer for next connection attempt */
|
||||
fd_psm_next_timeout(peer, 1, fd_p_dp_newdelay(peer));
|
||||
goto psm_loop;
|
||||
}
|
||||
goto psm_loop;
|
||||
}
|
||||
|
||||
/* The connection notified a change in endpoints */
|
||||
if (event == FDEVP_CNX_EP_CHANGE) {
|
||||
/* We actually don't care if we are in OPEN state here... */
|
||||
|
||||
/* Cleanup the remote LL and primary addresses */
|
||||
CHECK_FCT_DO( fd_ep_filter( &peer->p_hdr.info.pi_endpoints, EP_FL_CONF | EP_FL_DISC | EP_FL_ADV ), /* ignore the error */);
|
||||
CHECK_FCT_DO( fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_PRIMARY ), /* ignore the error */);
|
||||
|
||||
/* Get the new ones */
|
||||
CHECK_FCT_DO( fd_cnx_getremoteeps(peer->p_cnxctx, &peer->p_hdr.info.pi_endpoints), /* ignore the error */);
|
||||
|
||||
/* We do not support local endpoints change currently, but it could be added here if needed (refresh fd_g_config->cnf_endpoints) */
|
||||
{
|
||||
char * buf = NULL;
|
||||
size_t len = 0;
|
||||
LOG_D("Got low layer notification (IGNORED): remote endpoint(s) changed: %s", fd_ep_dump(&buf, &len, NULL, 0, 0, &peer->p_hdr.info.pi_endpoints) ?: "error");
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/* Done */
|
||||
goto psm_loop;
|
||||
}
|
||||
|
||||
/* A new connection was established and CER containing this peer id was received */
|
||||
if (event == FDEVP_CNX_INCOMING) {
|
||||
struct cnx_incoming * params = ev_data;
|
||||
ASSERT(params);
|
||||
|
||||
/* Handle the message */
|
||||
CHECK_FCT_DO( fd_p_ce_handle_newCER(¶ms->cer, peer, ¶ms->cnx, params->validate), goto psm_end );
|
||||
|
||||
/* Cleanup if needed */
|
||||
if (params->cnx) {
|
||||
fd_cnx_destroy(params->cnx);
|
||||
params->cnx = NULL;
|
||||
}
|
||||
if (params->cer) {
|
||||
CHECK_FCT_DO( fd_msg_free(params->cer), );
|
||||
params->cer = NULL;
|
||||
}
|
||||
|
||||
/* Loop */
|
||||
free(ev_data);
|
||||
goto psm_loop;
|
||||
}
|
||||
|
||||
/* A new connection has been established with the remote peer */
|
||||
if (event == FDEVP_CNX_ESTABLISHED) {
|
||||
struct cnxctx * cnx = ev_data;
|
||||
|
||||
/* Release the resources of the connecting thread */
|
||||
CHECK_POSIX_DO( pthread_join( peer->p_ini_thr, NULL), /* ignore, it is not a big deal */);
|
||||
peer->p_ini_thr = (pthread_t)NULL;
|
||||
|
||||
switch (cur_state) {
|
||||
case STATE_WAITCNXACK_ELEC:
|
||||
case STATE_WAITCNXACK:
|
||||
LOG_D("%s: Connection established, %s", peer->p_hdr.info.pi_diamid, fd_cnx_getid(cnx));
|
||||
fd_p_ce_handle_newcnx(peer, cnx);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Just abort the attempt and continue */
|
||||
TRACE_DEBUG(FULL, "Connection attempt successful but current state is %s, closing... (too slow?)", STATE_STR(cur_state));
|
||||
fd_cnx_destroy(cnx);
|
||||
}
|
||||
|
||||
goto psm_loop;
|
||||
}
|
||||
|
||||
/* A new connection has not been established with the remote peer */
|
||||
if (event == FDEVP_CNX_FAILED) {
|
||||
|
||||
/* Release the resources of the connecting thread */
|
||||
CHECK_POSIX_DO( pthread_join( peer->p_ini_thr, NULL), /* ignore, it is not a big deal */);
|
||||
peer->p_ini_thr = (pthread_t)NULL;
|
||||
|
||||
switch (cur_state) {
|
||||
case STATE_WAITCNXACK_ELEC:
|
||||
/* Abort the initiating side */
|
||||
fd_p_cnx_abort(peer, 0);
|
||||
/* Process the receiver side */
|
||||
CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end );
|
||||
break;
|
||||
|
||||
case STATE_WAITCNXACK:
|
||||
/* Go back to CLOSE */
|
||||
fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
|
||||
goto psm_reset;
|
||||
|
||||
default:
|
||||
/* Just ignore */
|
||||
TRACE_DEBUG(FULL, "Connection attempt failed but current state is %s, ignoring...", STATE_STR(cur_state));
|
||||
}
|
||||
|
||||
goto psm_loop;
|
||||
}
|
||||
|
||||
/* The timeout for the current state has been reached */
|
||||
if (event == FDEVP_PSM_TIMEOUT) {
|
||||
switch (cur_state) {
|
||||
case STATE_OPEN:
|
||||
case STATE_REOPEN:
|
||||
case STATE_OPEN_NEW:
|
||||
CHECK_FCT_DO( fd_p_dw_timeout(peer), goto psm_end );
|
||||
goto psm_loop;
|
||||
|
||||
case STATE_CLOSED:
|
||||
LOG_D("%s: Connecting...", peer->p_hdr.info.pi_diamid);
|
||||
CHECK_FCT_DO( fd_psm_change_state(peer, STATE_WAITCNXACK), goto psm_end );
|
||||
fd_psm_next_timeout(peer, 0, CNX_TIMEOUT);
|
||||
CHECK_FCT_DO( fd_p_cnx_init(peer), goto psm_end );
|
||||
goto psm_loop;
|
||||
|
||||
case STATE_SUSPECT:
|
||||
/* Mark the connection problem */
|
||||
peer->p_flags.pf_cnx_pb = 1;
|
||||
case STATE_WAITCNXACK:
|
||||
case STATE_WAITCEA:
|
||||
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "Timeout while waiting for remote peer", NULL);
|
||||
case STATE_CLOSING:
|
||||
/* Destroy the connection, restart the timer to a new connection attempt */
|
||||
fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
|
||||
goto psm_reset;
|
||||
|
||||
case STATE_CLOSING_GRACE:
|
||||
/* The grace period is completed, now close */
|
||||
if (peer->p_flags.pf_localterm)
|
||||
goto psm_end;
|
||||
|
||||
fd_psm_cleanup(peer, 0);
|
||||
/* Reset the timer for next connection attempt */
|
||||
fd_psm_next_timeout(peer, 1, fd_p_dp_newdelay(peer));
|
||||
goto psm_loop;
|
||||
|
||||
case STATE_WAITCNXACK_ELEC:
|
||||
/* Abort the initiating side */
|
||||
fd_p_cnx_abort(peer, 0);
|
||||
/* Process the receiver side */
|
||||
CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end );
|
||||
goto psm_loop;
|
||||
|
||||
default:
|
||||
ASSERT(0); /* implementation problem, we did not foresee this case? */
|
||||
}
|
||||
}
|
||||
|
||||
/* Default action : the handling has not yet been implemented. [for debug only] */
|
||||
TRACE_DEBUG(INFO, "Missing handler in PSM for '%s'\t<-- '%s'", STATE_STR(cur_state), fd_pev_str(event));
|
||||
psm_reset:
|
||||
if (peer->p_flags.pf_delete)
|
||||
goto psm_end;
|
||||
fd_psm_cleanup(peer, 0);
|
||||
goto psm_loop;
|
||||
|
||||
psm_end:
|
||||
LOG_N("%s: Going to ZOMBIE state (no more activity)", peer->p_hdr.info.pi_diamid);
|
||||
fd_psm_cleanup(peer, 1);
|
||||
TRACE_DEBUG(INFO, "'%s'\t-> STATE_ZOMBIE (terminated)\t'%s'",
|
||||
STATE_STR(fd_peer_getstate(peer)),
|
||||
peer->p_hdr.info.pi_diamid);
|
||||
pthread_cleanup_pop(1); /* set STATE_ZOMBIE */
|
||||
peer->p_psm = (pthread_t)NULL;
|
||||
pthread_detach(pthread_self());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Functions to control the PSM */
|
||||
/************************************************************************/
|
||||
/* Create the PSM thread of one peer structure */
|
||||
int fd_psm_begin(struct fd_peer * peer )
|
||||
{
|
||||
TRACE_ENTRY("%p", peer);
|
||||
|
||||
/* Check the peer and state are OK */
|
||||
CHECK_PARAMS( fd_peer_getstate(peer) == STATE_NEW );
|
||||
|
||||
/* Create the FIFO for events */
|
||||
CHECK_FCT( fd_fifo_new(&peer->p_events, 0) );
|
||||
|
||||
/* 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) */
|
||||
int fd_psm_terminate(struct fd_peer * peer, char * reason )
|
||||
{
|
||||
TRACE_ENTRY("%p", peer);
|
||||
CHECK_PARAMS( CHECK_PEER(peer) );
|
||||
|
||||
if (fd_peer_getstate(peer) != STATE_ZOMBIE) {
|
||||
CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, reason) );
|
||||
} else {
|
||||
TRACE_DEBUG(FULL, "Peer '%s' was already terminated", peer->p_hdr.info.pi_diamid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* End the PSM & cleanup the peer structure */
|
||||
void fd_psm_abord(struct fd_peer * peer )
|
||||
{
|
||||
TRACE_ENTRY("%p", peer);
|
||||
|
||||
/* Cancel PSM thread */
|
||||
CHECK_FCT_DO( fd_thr_term(&peer->p_psm), /* continue */ );
|
||||
|
||||
/* Cleanup the data */
|
||||
fd_psm_cleanup(peer, 1);
|
||||
|
||||
/* Destroy the event list */
|
||||
CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ );
|
||||
|
||||
/* Remaining cleanups are performed in fd_peer_free */
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.h"
|
||||
|
||||
/* Structure to store a sent request */
|
||||
struct sentreq {
|
||||
struct fd_list chain; /* the "o" field points directly to the (new) hop-by-hop of the request (uint32_t *) */
|
||||
struct msg *req; /* A request that was sent and not yet answered. */
|
||||
uint32_t prevhbh;/* The value to set back in the hbh header when the message is retrieved */
|
||||
struct fd_list expire; /* the list of expiring requests */
|
||||
struct timespec timeout; /* Cache the expire date of the request so that the timeout thread does not need to get it each time. */
|
||||
struct timespec added_on; /* the time the request was added */
|
||||
};
|
||||
|
||||
/* Find an element in the hbh list, or the following one */
|
||||
static struct fd_list * find_or_next(struct fd_list * srlist, uint32_t hbh, int * match)
|
||||
{
|
||||
struct fd_list * li;
|
||||
*match = 0;
|
||||
for (li = srlist->next; li != srlist; li = li->next) {
|
||||
uint32_t * nexthbh = li->o;
|
||||
if (*nexthbh < hbh)
|
||||
continue;
|
||||
if (*nexthbh == hbh)
|
||||
*match = 1;
|
||||
break;
|
||||
}
|
||||
return li;
|
||||
}
|
||||
|
||||
/* Similar but start from the end, since we add requests in growing hbh order usually */
|
||||
static struct fd_list * find_or_prev(struct fd_list * srlist, uint32_t hbh, int * match)
|
||||
{
|
||||
struct fd_list * li;
|
||||
*match = 0;
|
||||
for (li = srlist->prev; li != srlist; li = li->prev) {
|
||||
uint32_t * prevhbh = li->o;
|
||||
if (*prevhbh > hbh)
|
||||
continue;
|
||||
if (*prevhbh == hbh)
|
||||
*match = 1;
|
||||
break;
|
||||
}
|
||||
return li;
|
||||
}
|
||||
|
||||
static void srl_dump(const char * text, struct fd_list * srlist)
|
||||
{
|
||||
struct fd_list * li;
|
||||
struct timespec now;
|
||||
|
||||
LOG_D("%sSentReq list @%p:", text, srlist);
|
||||
|
||||
CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), );
|
||||
|
||||
for (li = srlist->next; li != srlist; li = li->next) {
|
||||
struct sentreq * sr = (struct sentreq *)li;
|
||||
uint32_t * nexthbh = li->o;
|
||||
|
||||
LOG_D(" - Next req (hbh:0x%x, prev:0x%x): [since %ld.%06ld sec]", *nexthbh, sr->prevhbh,
|
||||
(long)((now.tv_nsec >= sr->added_on.tv_nsec) ? (now.tv_sec - sr->added_on.tv_sec) : (now.tv_sec - sr->added_on.tv_sec - 1)),
|
||||
(long)((now.tv_nsec >= sr->added_on.tv_nsec) ? ((now.tv_nsec - sr->added_on.tv_nsec) / 1000) : ((now.tv_nsec - sr->added_on.tv_nsec + 1000000000) / 1000)));
|
||||
}
|
||||
}
|
||||
|
||||
/* thread that handles messages expiring. The thread is started only when needed */
|
||||
static void * sr_expiry_th(void * arg) {
|
||||
struct sr_list * srlist = arg;
|
||||
|
||||
TRACE_ENTRY("%p", arg);
|
||||
CHECK_PARAMS_DO( arg, return NULL );
|
||||
|
||||
/* Set the thread name */
|
||||
{
|
||||
char buf[48];
|
||||
snprintf(buf, sizeof(buf), "ReqExp/%s", ((struct fd_peer *)(srlist->exp.o))->p_hdr.info.pi_diamid);
|
||||
fd_log_threadname ( buf );
|
||||
}
|
||||
|
||||
do {
|
||||
struct timespec now;
|
||||
struct sentreq * first;
|
||||
struct msg * request;
|
||||
struct fd_peer * sentto;
|
||||
void (*expirecb)(void *, DiamId_t, size_t, struct msg **);
|
||||
void * data;
|
||||
int no_error;
|
||||
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&srlist->mtx), return NULL );
|
||||
pthread_cleanup_push( fd_cleanup_mutex, &srlist->mtx );
|
||||
|
||||
loop:
|
||||
no_error = 0;
|
||||
|
||||
/* Check if there are expiring requests available */
|
||||
if (FD_IS_LIST_EMPTY(&srlist->exp)) {
|
||||
/* Just wait for a change or cancelation */
|
||||
CHECK_POSIX_DO( pthread_cond_wait( &srlist->cnd, &srlist->mtx ), goto unlock );
|
||||
/* Restart the loop on wakeup */
|
||||
goto loop;
|
||||
}
|
||||
|
||||
/* Get the pointer to the request that expires first */
|
||||
first = (struct sentreq *)(srlist->exp.next->o);
|
||||
|
||||
/* Get the current time */
|
||||
CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), goto unlock );
|
||||
|
||||
/* If first request is not expired, we just wait until it happens */
|
||||
if ( TS_IS_INFERIOR( &now, &first->timeout ) ) {
|
||||
|
||||
CHECK_POSIX_DO2( pthread_cond_timedwait( &srlist->cnd, &srlist->mtx, &first->timeout ),
|
||||
ETIMEDOUT, /* ETIMEDOUT is a normal return value, continue */,
|
||||
/* on other error, */ goto unlock );
|
||||
|
||||
/* on wakeup, loop */
|
||||
goto loop;
|
||||
}
|
||||
|
||||
/* Now, the first request in the list is expired; remove it and call the expirecb for it */
|
||||
request = first->req;
|
||||
sentto = first->chain.head->o;
|
||||
|
||||
TRACE_DEBUG(FULL, "Request %x was not answered by %s within the timer delay", *((uint32_t *)first->chain.o), sentto->p_hdr.info.pi_diamid);
|
||||
|
||||
/* Restore the hbhid */
|
||||
*((uint32_t *)first->chain.o) = first->prevhbh;
|
||||
|
||||
/* Free the sentreq information */
|
||||
fd_list_unlink(&first->chain);
|
||||
srlist->cnt--;
|
||||
srlist->cnt_lost++; /* We are not waiting for this answer anymore, but the remote peer may still be processing it. */
|
||||
fd_list_unlink(&first->expire);
|
||||
free(first);
|
||||
|
||||
no_error = 1;
|
||||
unlock:
|
||||
; /* pthread_cleanup_pop sometimes expands as "} ..." and the label before this cause some compilers to complain... */
|
||||
pthread_cleanup_pop( 1 ); /* unlock the mutex */
|
||||
if (!no_error)
|
||||
break;
|
||||
|
||||
|
||||
/* Retrieve callback in the message */
|
||||
CHECK_FCT_DO( fd_msg_anscb_get( request, NULL, &expirecb, &data ), break);
|
||||
ASSERT(expirecb);
|
||||
|
||||
/* Clean up this expirecb from the message */
|
||||
CHECK_FCT_DO( fd_msg_anscb_reset( request, 0, 1 ), break);
|
||||
|
||||
/* Call it */
|
||||
(*expirecb)(data, sentto->p_hdr.info.pi_diamid, sentto->p_hdr.info.pi_diamidlen, &request);
|
||||
|
||||
/* If the callback did not dispose of the message, do it now */
|
||||
if (request) {
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, request, NULL, "Expiration period completed without an answer, and the expiry callback did not dispose of the message.", fd_msg_pmdl_get(request));
|
||||
CHECK_FCT_DO( fd_msg_free(request), /* ignore */ );
|
||||
}
|
||||
|
||||
} while (1);
|
||||
|
||||
ASSERT(0); /* we have encountered a problem, maybe time to signal the framework to terminate? */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Store a new sent request */
|
||||
int fd_p_sr_store(struct sr_list * srlist, struct msg **req, uint32_t *hbhloc, uint32_t hbh_restore)
|
||||
{
|
||||
struct sentreq * sr;
|
||||
struct fd_list * prev;
|
||||
int match;
|
||||
struct timespec * ts;
|
||||
|
||||
TRACE_ENTRY("%p %p %p %x", srlist, req, hbhloc, hbh_restore);
|
||||
CHECK_PARAMS(srlist && req && *req && hbhloc);
|
||||
|
||||
CHECK_MALLOC( sr = malloc(sizeof(struct sentreq)) );
|
||||
memset(sr, 0, sizeof(struct sentreq));
|
||||
fd_list_init(&sr->chain, hbhloc);
|
||||
sr->req = *req;
|
||||
sr->prevhbh = hbh_restore;
|
||||
fd_list_init(&sr->expire, sr);
|
||||
CHECK_SYS( clock_gettime(CLOCK_REALTIME, &sr->added_on) );
|
||||
|
||||
/* Search the place in the list */
|
||||
CHECK_POSIX( pthread_mutex_lock(&srlist->mtx) );
|
||||
prev = find_or_prev(&srlist->srs, *hbhloc, &match);
|
||||
if (match) {
|
||||
TRACE_DEBUG(INFO, "A request with the same hop-by-hop Id (0x%x) was already sent: error", *hbhloc);
|
||||
free(sr);
|
||||
srl_dump("Current list of SR: ", &srlist->srs);
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&srlist->mtx), /* ignore */ );
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Save in the list */
|
||||
*req = NULL;
|
||||
fd_list_insert_after(prev, &sr->chain);
|
||||
srlist->cnt++;
|
||||
|
||||
/* In case of request with a timeout, also store in the timeout list */
|
||||
ts = fd_msg_anscb_gettimeout( sr->req );
|
||||
if (ts) {
|
||||
struct fd_list * li;
|
||||
|
||||
memcpy(&sr->timeout, ts, sizeof(struct timespec));
|
||||
|
||||
/* browse srlist->exp from the end */
|
||||
for (li = srlist->exp.prev; li != &srlist->exp; li = li->prev) {
|
||||
struct sentreq * s = (struct sentreq *)(li->o);
|
||||
if (TS_IS_INFERIOR(&s->timeout, ts))
|
||||
break;
|
||||
}
|
||||
|
||||
fd_list_insert_after(li, &sr->expire);
|
||||
|
||||
/* if the thread does not exist yet, create it */
|
||||
if (srlist->thr == (pthread_t)NULL) {
|
||||
CHECK_POSIX_DO( pthread_create(&srlist->thr, NULL, sr_expiry_th, srlist), /* continue anyway */);
|
||||
} else {
|
||||
/* or, if added in first position, signal the condvar to update the sleep time of the thread */
|
||||
if (li == &srlist->exp) {
|
||||
CHECK_POSIX_DO( pthread_cond_signal(&srlist->cnd), /* continue anyway */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_POSIX( pthread_mutex_unlock(&srlist->mtx) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fetch a request by hbh */
|
||||
int fd_p_sr_fetch(struct sr_list * srlist, uint32_t hbh, struct msg **req)
|
||||
{
|
||||
struct sentreq * sr;
|
||||
int match;
|
||||
|
||||
TRACE_ENTRY("%p %x %p", srlist, hbh, req);
|
||||
CHECK_PARAMS(srlist && req);
|
||||
|
||||
/* Search the request in the list */
|
||||
CHECK_POSIX( pthread_mutex_lock(&srlist->mtx) );
|
||||
sr = (struct sentreq *)find_or_next(&srlist->srs, hbh, &match);
|
||||
if (!match) {
|
||||
TRACE_DEBUG(INFO, "There is no saved request with this hop-by-hop id (%x)", hbh);
|
||||
srl_dump("Current list of SR: ", &srlist->srs);
|
||||
*req = NULL;
|
||||
if (srlist->cnt_lost > 0) {
|
||||
srlist->cnt_lost--; /* This is probably an answer for a request we already timedout. */
|
||||
} /* else, probably a bug in the remote peer */
|
||||
} else {
|
||||
/* Restore hop-by-hop id */
|
||||
*((uint32_t *)sr->chain.o) = sr->prevhbh;
|
||||
/* Unlink */
|
||||
fd_list_unlink(&sr->chain);
|
||||
srlist->cnt--;
|
||||
fd_list_unlink(&sr->expire);
|
||||
*req = sr->req;
|
||||
free(sr);
|
||||
}
|
||||
CHECK_POSIX( pthread_mutex_unlock(&srlist->mtx) );
|
||||
|
||||
/* do not stop the expire thread here, it might cause creating/destroying it very often otherwise */
|
||||
|
||||
/* Done */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Failover requests (free or requeue routables) */
|
||||
void fd_p_sr_failover(struct sr_list * srlist)
|
||||
{
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&srlist->mtx), /* continue anyway */ );
|
||||
while (!FD_IS_LIST_EMPTY(&srlist->srs)) {
|
||||
struct sentreq * sr = (struct sentreq *)(srlist->srs.next);
|
||||
fd_list_unlink(&sr->chain);
|
||||
srlist->cnt--;
|
||||
fd_list_unlink(&sr->expire);
|
||||
if (fd_msg_is_routable(sr->req)) {
|
||||
struct msg_hdr * hdr = NULL;
|
||||
int ret;
|
||||
|
||||
/* Set the 'T' flag */
|
||||
CHECK_FCT_DO(fd_msg_hdr(sr->req, &hdr), /* continue */);
|
||||
if (hdr)
|
||||
hdr->msg_flags |= CMD_FLAG_RETRANSMIT;
|
||||
|
||||
/* Restore the original hop-by-hop id of the request */
|
||||
*((uint32_t *)sr->chain.o) = sr->prevhbh;
|
||||
|
||||
fd_hook_call(HOOK_MESSAGE_FAILOVER, sr->req, (struct fd_peer *)srlist->srs.o, NULL, fd_msg_pmdl_get(sr->req));
|
||||
|
||||
/* Requeue for sending to another peer */
|
||||
CHECK_FCT_DO( ret = fd_fifo_post_noblock(fd_g_outgoing, (void *)&sr->req),
|
||||
{
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "Internal error: error while requeuing during failover: %s", strerror(ret));
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, sr->req, NULL, buf, fd_msg_pmdl_get(sr->req));
|
||||
CHECK_FCT_DO(fd_msg_free(sr->req), /* What can we do more? */)
|
||||
});
|
||||
} else {
|
||||
/* Just free the request. */
|
||||
/* fd_hook_call(HOOK_MESSAGE_DROPPED, sr->req, NULL, "Sent & unanswered local message discarded during failover.", fd_msg_pmdl_get(sr->req)); */
|
||||
CHECK_FCT_DO(fd_msg_free(sr->req), /* Ignore */);
|
||||
}
|
||||
free(sr);
|
||||
}
|
||||
/* The list of expiring requests must be empty now */
|
||||
ASSERT( FD_IS_LIST_EMPTY(&srlist->exp) );
|
||||
ASSERT( srlist->cnt == 0 ); /* debug the counter management if needed */
|
||||
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&srlist->mtx), /* continue anyway */ );
|
||||
|
||||
/* Terminate the expiry thread (must be done when the lock can be taken) */
|
||||
CHECK_FCT_DO( fd_thr_term(&srlist->thr), /* ignore error */ );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,700 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.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;
|
||||
|
||||
/* List of active peers */
|
||||
struct fd_list fd_g_activ_peers = FD_LIST_INITIALIZER(fd_g_activ_peers); /* peers linked by their p_actives oredered by p_diamid */
|
||||
pthread_rwlock_t fd_g_activ_peers_rw = PTHREAD_RWLOCK_INITIALIZER;
|
||||
|
||||
/* List of validation callbacks (registered with fd_peer_validate_register) */
|
||||
static struct fd_list validators = FD_LIST_INITIALIZER(validators); /* list items are simple fd_list with "o" pointing to the callback */
|
||||
static pthread_rwlock_t validators_rw = PTHREAD_RWLOCK_INITIALIZER;
|
||||
|
||||
|
||||
/* Alloc / reinit a peer structure. if *ptr is not NULL, it must already point to a valid struct fd_peer. */
|
||||
int fd_peer_alloc(struct fd_peer ** ptr)
|
||||
{
|
||||
struct fd_peer *p;
|
||||
|
||||
TRACE_ENTRY("%p", ptr);
|
||||
CHECK_PARAMS(ptr);
|
||||
|
||||
if (*ptr) {
|
||||
p = *ptr;
|
||||
} else {
|
||||
CHECK_MALLOC( p = malloc(sizeof(struct fd_peer)) );
|
||||
*ptr = p;
|
||||
}
|
||||
|
||||
/* Now initialize the content */
|
||||
memset(p, 0, sizeof(struct fd_peer));
|
||||
|
||||
fd_list_init(&p->p_hdr.chain, p);
|
||||
|
||||
fd_list_init(&p->p_hdr.info.pi_endpoints, p);
|
||||
fd_list_init(&p->p_hdr.info.runtime.pir_apps, p);
|
||||
|
||||
p->p_eyec = EYEC_PEER;
|
||||
CHECK_POSIX( pthread_mutex_init(&p->p_state_mtx, NULL) );
|
||||
|
||||
fd_list_init(&p->p_actives, p);
|
||||
fd_list_init(&p->p_expiry, p);
|
||||
CHECK_FCT( fd_fifo_new(&p->p_tosend, 1024) );// initial is 5
|
||||
CHECK_FCT( fd_fifo_new(&p->p_tofailover, 0) );
|
||||
p->p_hbh = lrand48();
|
||||
|
||||
fd_list_init(&p->p_sr.srs, p);
|
||||
fd_list_init(&p->p_sr.exp, p);
|
||||
CHECK_POSIX( pthread_mutex_init(&p->p_sr.mtx, NULL) );
|
||||
CHECK_POSIX( pthread_cond_init(&p->p_sr.cnd, NULL) );
|
||||
|
||||
fd_list_init(&p->p_connparams, p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add a new peer entry */
|
||||
int fd_peer_add ( struct peer_info * info, const char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data )
|
||||
{
|
||||
struct fd_peer *p = NULL;
|
||||
struct fd_list * li, *li_inf;
|
||||
int ret = 0;
|
||||
|
||||
TRACE_ENTRY("%p %p %p %p", info, orig_dbg, cb, cb_data);
|
||||
CHECK_PARAMS(info && info->pi_diamid);
|
||||
|
||||
if (info->config.pic_realm) {
|
||||
if (!fd_os_is_valid_DiameterIdentity((os0_t)info->config.pic_realm, strlen(info->config.pic_realm))) {
|
||||
TRACE_DEBUG(INFO, "'%s' is not a valid DiameterIdentity.", info->config.pic_realm);
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a structure to contain the new peer information */
|
||||
CHECK_FCT( fd_peer_alloc(&p) );
|
||||
|
||||
/* Copy the informations from the parameters received */
|
||||
p->p_hdr.info.pi_diamid = info->pi_diamid;
|
||||
CHECK_FCT( fd_os_validate_DiameterIdentity(&p->p_hdr.info.pi_diamid, &p->p_hdr.info.pi_diamidlen, 1) );
|
||||
|
||||
memcpy( &p->p_hdr.info.config, &info->config, sizeof(p->p_hdr.info.config) );
|
||||
|
||||
/* Duplicate the strings if provided */
|
||||
if (info->config.pic_realm) {
|
||||
CHECK_MALLOC( p->p_hdr.info.config.pic_realm = strdup(info->config.pic_realm) );
|
||||
}
|
||||
if (info->config.pic_priority) {
|
||||
CHECK_MALLOC( p->p_hdr.info.config.pic_priority = strdup(info->config.pic_priority) );
|
||||
}
|
||||
|
||||
/* Move the list of endpoints into the peer */
|
||||
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("unspecified") );
|
||||
}
|
||||
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) );
|
||||
li_inf = &fd_g_peers;
|
||||
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
|
||||
struct fd_peer * next = (struct fd_peer *)li;
|
||||
int cont;
|
||||
int cmp = fd_os_almostcasesrch( p->p_hdr.info.pi_diamid, p->p_hdr.info.pi_diamidlen,
|
||||
next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen,
|
||||
&cont );
|
||||
if (cmp > 0)
|
||||
li_inf = li; /* it will come after this element, for sure */
|
||||
|
||||
if (cmp == 0) {
|
||||
ret = EEXIST; /* we have a duplicate */
|
||||
break;
|
||||
}
|
||||
if (!cont)
|
||||
break;
|
||||
}
|
||||
|
||||
/* We can insert the new peer object */
|
||||
if (! ret)
|
||||
do {
|
||||
/* Update expiry list */
|
||||
CHECK_FCT_DO( ret = fd_p_expi_update( p ), break );
|
||||
|
||||
/* Insert the new element in the list */
|
||||
fd_list_insert_after( li_inf, &p->p_hdr.chain );
|
||||
} while (0);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* Search for a peer */
|
||||
int fd_peer_getbyid( DiamId_t diamid, size_t diamidlen, int igncase, struct peer_hdr ** peer )
|
||||
{
|
||||
struct fd_list * li;
|
||||
TRACE_ENTRY("%p %zd %d %p", diamid, diamidlen, igncase, peer);
|
||||
CHECK_PARAMS( diamid && diamidlen && peer );
|
||||
|
||||
*peer = NULL;
|
||||
|
||||
/* Search in the list */
|
||||
CHECK_POSIX( pthread_rwlock_rdlock(&fd_g_peers_rw) );
|
||||
if (igncase) {
|
||||
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
|
||||
struct fd_peer * next = (struct fd_peer *)li;
|
||||
int cmp, cont;
|
||||
cmp = fd_os_almostcasesrch( diamid, diamidlen, next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen, &cont );
|
||||
if (cmp == 0) {
|
||||
*peer = &next->p_hdr;
|
||||
break;
|
||||
}
|
||||
if (!cont)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOG_D("fd_peer_getbyid enter");
|
||||
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
|
||||
struct fd_peer * next = (struct fd_peer *)li;
|
||||
LOG_D("fd_peer_getbyid src[%u,%s] peer[%u,%s]",
|
||||
(unsigned int)diamidlen,
|
||||
diamid,
|
||||
(unsigned int)( next->p_hdr.info.pi_diamidlen ),
|
||||
next->p_hdr.info.pi_diamid);
|
||||
int cmp = fd_os_cmp( diamid, diamidlen, next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen );
|
||||
if (cmp > 0)
|
||||
continue;
|
||||
if (cmp == 0)
|
||||
*peer = &next->p_hdr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define free_null( _v ) \
|
||||
if (_v) { \
|
||||
free(_v); \
|
||||
(_v) = NULL; \
|
||||
}
|
||||
|
||||
#define free_list( _l ) \
|
||||
while (!FD_IS_LIST_EMPTY(_l)) { \
|
||||
struct fd_list * __li = ((struct fd_list *)(_l))->next; \
|
||||
fd_list_unlink(__li); \
|
||||
free(__li); \
|
||||
}
|
||||
|
||||
/* Empty the lists of p_tosend, p_failover, and p_sentreq messages */
|
||||
void fd_peer_failover_msg(struct fd_peer * peer)
|
||||
{
|
||||
struct msg *m;
|
||||
TRACE_ENTRY("%p", peer);
|
||||
CHECK_PARAMS_DO(CHECK_PEER(peer), return);
|
||||
|
||||
/* Requeue all messages in the "out" queue */
|
||||
while ( fd_fifo_tryget(peer->p_tosend, &m) == 0 ) {
|
||||
/* but only if they are routable */
|
||||
if (fd_msg_is_routable(m)) {
|
||||
fd_hook_call(HOOK_MESSAGE_FAILOVER, m, peer, NULL, fd_msg_pmdl_get(m));
|
||||
CHECK_FCT_DO(fd_fifo_post_noblock(fd_g_outgoing, (void *)&m),
|
||||
{
|
||||
/* fallback: destroy the message */
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, m, NULL, "Internal error: unable to requeue this message during failover process", fd_msg_pmdl_get(m));
|
||||
CHECK_FCT_DO(fd_msg_free(m), /* What can we do more? */)
|
||||
} );
|
||||
} else {
|
||||
/* Just free it */
|
||||
/* fd_hook_call(HOOK_MESSAGE_DROPPED, m, NULL, "Non-routable message freed during handover", fd_msg_pmdl_get(m)); */
|
||||
CHECK_FCT_DO(fd_msg_free(m), /* What can we do more? */)
|
||||
}
|
||||
}
|
||||
|
||||
/* Requeue all messages in the "failover" queue */
|
||||
while ( fd_fifo_tryget(peer->p_tofailover, &m) == 0 ) {
|
||||
fd_hook_call(HOOK_MESSAGE_FAILOVER, m, peer, NULL, fd_msg_pmdl_get(m));
|
||||
CHECK_FCT_DO(fd_fifo_post_noblock(fd_g_outgoing, (void *)&m),
|
||||
{
|
||||
/* fallback: destroy the message */
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, m, NULL, "Internal error: unable to requeue this message during failover process", fd_msg_pmdl_get(m));
|
||||
CHECK_FCT_DO(fd_msg_free(m), /* What can we do more? */)
|
||||
} );
|
||||
}
|
||||
|
||||
/* Requeue all routable sent requests */
|
||||
fd_p_sr_failover(&peer->p_sr);
|
||||
|
||||
/* Done */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Describe the current connection */
|
||||
int fd_peer_cnx_proto_info(struct peer_hdr *peer, char * buf, size_t len)
|
||||
{
|
||||
struct fd_peer * p = (struct fd_peer *)peer;
|
||||
TRACE_ENTRY("%p %p %zd", peer, buf, len);
|
||||
CHECK_PARAMS(CHECK_PEER(peer) && buf && len);
|
||||
|
||||
if (p->p_cnxctx) {
|
||||
CHECK_FCT(fd_cnx_proto_info(p->p_cnxctx, buf, len));
|
||||
} else if (p->p_receiver) {
|
||||
CHECK_FCT(fd_cnx_proto_info(p->p_receiver, buf, len));
|
||||
} else {
|
||||
snprintf(buf, len, "Not Connected");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return the value of srlist->cnt */
|
||||
int fd_peer_get_load_pending(struct peer_hdr *peer, long * to_receive, long * to_send)
|
||||
{
|
||||
struct fd_peer * p = (struct fd_peer *)peer;
|
||||
TRACE_ENTRY("%p %p %p", peer, to_receive, to_send);
|
||||
CHECK_PARAMS(CHECK_PEER(peer));
|
||||
|
||||
if (to_receive) {
|
||||
CHECK_POSIX( pthread_mutex_lock(&p->p_sr.mtx) );
|
||||
*to_receive = p->p_sr.cnt;
|
||||
CHECK_POSIX( pthread_mutex_unlock(&p->p_sr.mtx) );
|
||||
}
|
||||
if (to_send) {
|
||||
CHECK_POSIX( pthread_mutex_lock(&p->p_state_mtx) );
|
||||
*to_send = p->p_reqin_count;
|
||||
CHECK_POSIX( pthread_mutex_unlock(&p->p_state_mtx) );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Destroy a structure once cleanups have been performed (fd_psm_abord, ...) */
|
||||
int fd_peer_free(struct fd_peer ** ptr)
|
||||
{
|
||||
struct fd_peer *p;
|
||||
|
||||
TRACE_ENTRY("%p", ptr);
|
||||
CHECK_PARAMS(ptr);
|
||||
p = *ptr;
|
||||
*ptr = NULL;
|
||||
CHECK_PARAMS(p);
|
||||
|
||||
CHECK_PARAMS( FD_IS_LIST_EMPTY(&p->p_hdr.chain) );
|
||||
|
||||
free_null(p->p_hdr.info.pi_diamid);
|
||||
|
||||
free_null(p->p_hdr.info.config.pic_realm);
|
||||
free_null(p->p_hdr.info.config.pic_priority);
|
||||
|
||||
free_null(p->p_hdr.info.runtime.pir_realm);
|
||||
free_null(p->p_hdr.info.runtime.pir_prodname);
|
||||
free_list( &p->p_hdr.info.runtime.pir_apps );
|
||||
|
||||
free_list( &p->p_hdr.info.pi_endpoints );
|
||||
|
||||
free_null(p->p_dbgorig);
|
||||
|
||||
fd_list_unlink(&p->p_expiry);
|
||||
fd_list_unlink(&p->p_actives);
|
||||
|
||||
CHECK_FCT_DO( fd_fifo_del(&p->p_tosend), /* continue */ );
|
||||
CHECK_FCT_DO( fd_fifo_del(&p->p_tofailover), /* continue */ );
|
||||
CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_state_mtx), /* continue */);
|
||||
CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_sr.mtx), /* continue */);
|
||||
CHECK_POSIX_DO( pthread_cond_destroy(&p->p_sr.cnd), /* continue */);
|
||||
|
||||
/* If the callback is still around... */
|
||||
if (p->p_cb)
|
||||
(*p->p_cb)(NULL, p->p_cb_data);
|
||||
|
||||
/* Free the structure */
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Terminate peer module (destroy all peers, first gently, then violently) */
|
||||
int fd_peer_fini()
|
||||
{
|
||||
struct fd_list * li;
|
||||
struct fd_list purge = FD_LIST_INITIALIZER(purge); /* Store zombie peers here */
|
||||
int list_empty;
|
||||
struct timespec wait_until, now;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
CHECK_FCT_DO(fd_p_expi_fini(), /* continue */);
|
||||
|
||||
TRACE_DEBUG(INFO, "Sending terminate signal to all peer connections");
|
||||
|
||||
CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
|
||||
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
|
||||
struct fd_peer * peer = (struct fd_peer *)li->o;
|
||||
|
||||
if (fd_peer_getstate(peer) != STATE_ZOMBIE) {
|
||||
CHECK_FCT_DO( fd_psm_terminate(peer, "REBOOTING"), /* continue */ );
|
||||
} else {
|
||||
li = li->prev; /* to avoid breaking the loop */
|
||||
fd_list_unlink(&peer->p_hdr.chain);
|
||||
fd_list_insert_before(&purge, &peer->p_hdr.chain);
|
||||
}
|
||||
}
|
||||
list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
|
||||
CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
|
||||
|
||||
if (!list_empty) {
|
||||
CHECK_SYS( clock_gettime(CLOCK_REALTIME, &now) );
|
||||
fd_psm_start(); /* just in case */
|
||||
TRACE_DEBUG(INFO, "Waiting for connections shutdown... (%d sec max)", DPR_TIMEOUT + 1);
|
||||
wait_until.tv_sec = now.tv_sec + DPR_TIMEOUT + 1;
|
||||
wait_until.tv_nsec = now.tv_nsec;
|
||||
}
|
||||
|
||||
while ((!list_empty) && (TS_IS_INFERIOR(&now, &wait_until))) {
|
||||
|
||||
/* Allow the PSM(s) to execute */
|
||||
usleep(100000);
|
||||
|
||||
/* Remove zombie peers */
|
||||
CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
|
||||
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
|
||||
struct fd_peer * peer = (struct fd_peer *)li->o;
|
||||
if (fd_peer_getstate(peer) == STATE_ZOMBIE) {
|
||||
li = li->prev; /* to avoid breaking the loop */
|
||||
fd_list_unlink(&peer->p_hdr.chain);
|
||||
fd_list_insert_before(&purge, &peer->p_hdr.chain);
|
||||
}
|
||||
}
|
||||
list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
|
||||
CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
|
||||
CHECK_SYS( clock_gettime(CLOCK_REALTIME, &now) );
|
||||
}
|
||||
|
||||
if (!list_empty) {
|
||||
TRACE_DEBUG(INFO, "Forcing connections shutdown");
|
||||
CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
|
||||
while (!FD_IS_LIST_EMPTY(&fd_g_peers)) {
|
||||
struct fd_peer * peer = (struct fd_peer *)(fd_g_peers.next->o);
|
||||
fd_psm_abord(peer);
|
||||
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), /* continue */ );
|
||||
}
|
||||
|
||||
/* Free memory objects of all peers */
|
||||
while (!FD_IS_LIST_EMPTY(&purge)) {
|
||||
struct fd_peer * peer = (struct fd_peer *)(purge.next->o);
|
||||
fd_list_unlink(&peer->p_hdr.chain);
|
||||
fd_peer_free(&peer);
|
||||
}
|
||||
|
||||
/* Now empty the validators list */
|
||||
CHECK_FCT_DO( pthread_rwlock_wrlock(&validators_rw), /* continue */ );
|
||||
while (!FD_IS_LIST_EMPTY( &validators )) {
|
||||
struct fd_list * v = validators.next;
|
||||
fd_list_unlink(v);
|
||||
free(v);
|
||||
}
|
||||
CHECK_FCT_DO( pthread_rwlock_unlock(&validators_rw), /* continue */ );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Dump info of one peer */
|
||||
DECLARE_FD_DUMP_PROTOTYPE(fd_peer_dump, struct peer_hdr * p, int details)
|
||||
{
|
||||
FD_DUMP_HANDLE_OFFSET();
|
||||
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{peer}(@%p): ", p), return NULL);
|
||||
|
||||
if (!CHECK_PEER(p)) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID/NULL"), return NULL);
|
||||
} else {
|
||||
struct fd_peer * peer = (struct fd_peer *)p;
|
||||
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%s [%s, cnt:%ldsr,%ldpa]", peer->p_hdr.info.pi_diamid, STATE_STR(fd_peer_getstate(peer)), peer->p_sr.cnt, peer->p_reqin_count), return NULL);
|
||||
if (details > 0) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " rlm:%s", peer->p_hdr.info.runtime.pir_realm ?: "<unknown>"), return NULL);
|
||||
if (peer->p_hdr.info.runtime.pir_prodname) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " ['%s' %u]", peer->p_hdr.info.runtime.pir_prodname, peer->p_hdr.info.runtime.pir_firmrev), return NULL);
|
||||
}
|
||||
}
|
||||
if (details > 1) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " [from:%s] flags:%s%s%s%s%s%s%s%s lft:%ds",
|
||||
peer->p_dbgorig ?: "unset",
|
||||
peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_DEFAULT ? "-" :
|
||||
(peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP ? "4" : "6"),
|
||||
peer->p_hdr.info.config.pic_flags.pro4 == PI_P4_DEFAULT ? "-" :
|
||||
(peer->p_hdr.info.config.pic_flags.pro4 == PI_P4_TCP ? "T" : "S"),
|
||||
peer->p_hdr.info.config.pic_flags.alg ? "P" : "-",
|
||||
peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE ? "N" :"-",
|
||||
peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD ? "O" :"-",
|
||||
peer->p_hdr.info.config.pic_flags.sctpsec & PI_SCTPSEC_3436 ? "3" :"-",
|
||||
peer->p_hdr.info.config.pic_flags.exp ? "E" : "-",
|
||||
peer->p_hdr.info.config.pic_flags.persist ? "P" : "-",
|
||||
peer->p_hdr.info.config.pic_lft), return NULL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return *buf;
|
||||
}
|
||||
|
||||
/* Dump the list of peers */
|
||||
DECLARE_FD_DUMP_PROTOTYPE(fd_peer_dump_list, int details)
|
||||
{
|
||||
struct fd_list * li;
|
||||
FD_DUMP_HANDLE_OFFSET();
|
||||
|
||||
CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ );
|
||||
|
||||
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
|
||||
CHECK_MALLOC_DO( fd_peer_dump(FD_DUMP_STD_PARAMS, (struct peer_hdr *)li->o, details), break);
|
||||
if (li->next != &fd_g_peers) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), break);
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
|
||||
return *buf;
|
||||
}
|
||||
|
||||
static struct dict_object *avp_oh_model = NULL;
|
||||
static pthread_mutex_t cache_avp_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/* Handle an incoming CER request on a new connection */
|
||||
int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx )
|
||||
{
|
||||
struct msg * msg;
|
||||
struct avp *avp_oh;
|
||||
struct avp_hdr * avp_hdr;
|
||||
struct fd_list * li, *li_inf;
|
||||
int found = 0;
|
||||
int ret = 0;
|
||||
struct fd_peer * peer;
|
||||
struct cnx_incoming * ev_data;
|
||||
|
||||
TRACE_ENTRY("%p %p", cer, cnx);
|
||||
CHECK_PARAMS(cer && *cer && cnx && *cnx);
|
||||
|
||||
msg = *cer;
|
||||
|
||||
/* If needed, resolve the dictionary model for Origin-Host */
|
||||
CHECK_POSIX( pthread_mutex_lock(&cache_avp_lock) );
|
||||
if (!avp_oh_model) {
|
||||
avp_code_t code = AC_ORIGIN_HOST;
|
||||
CHECK_FCT_DO( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE, &code, &avp_oh_model, ENOENT),
|
||||
{ LOG_E("Cannot find Origin-Host AVP definition in the dictionary!"); (void) pthread_mutex_unlock(&cache_avp_lock); return __ret__; } );
|
||||
}
|
||||
CHECK_POSIX( pthread_mutex_unlock(&cache_avp_lock) );
|
||||
|
||||
/* Find the Diameter Identity of the remote peer in the message */
|
||||
CHECK_FCT( fd_msg_search_avp ( msg, avp_oh_model, &avp_oh ) );
|
||||
ASSERT(avp_oh); /* otherwise it should not have passed rules validation, right? */
|
||||
CHECK_FCT( fd_msg_avp_hdr ( avp_oh, &avp_hdr ) );
|
||||
|
||||
/* First, check if the Origin-Host value is valid */
|
||||
if (!fd_os_is_valid_DiameterIdentity(avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len)) {
|
||||
CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ) );
|
||||
CHECK_FCT( fd_msg_rescode_set(*cer, "DIAMETER_INVALID_AVP_VALUE",
|
||||
"Your Origin-Host contains invalid characters.", avp_oh, 1 ) );
|
||||
|
||||
fd_hook_call(HOOK_PEER_CONNECT_FAILED, *cer, NULL, "Received CER with invalid Origin-Host AVP", NULL);
|
||||
|
||||
CHECK_FCT( fd_out_send(cer, *cnx, NULL, 0) );
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Search if we already have this peer id in our list. We take directly the write lock so that we don't need to upgrade if it is a new peer.
|
||||
* There is space for a small optimization here if needed.
|
||||
*/
|
||||
CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) );
|
||||
|
||||
li_inf = &fd_g_peers;
|
||||
for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
|
||||
int cmp, cont;
|
||||
peer = (struct fd_peer *)li;
|
||||
cmp = fd_os_almostcasesrch( avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len, peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen, &cont );
|
||||
if (cmp > 0) {
|
||||
li_inf = li;
|
||||
}
|
||||
if (cmp == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
if (!cont)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
/* Create a new peer entry for this new remote peer */
|
||||
peer = NULL;
|
||||
CHECK_FCT_DO( ret = fd_peer_alloc(&peer), goto out );
|
||||
|
||||
/* Set the peer Diameter Id and the responder flag parameters */
|
||||
CHECK_MALLOC_DO( peer->p_hdr.info.pi_diamid = os0dup(avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len),
|
||||
{ ret = ENOMEM; goto out; } );
|
||||
peer->p_hdr.info.pi_diamidlen = avp_hdr->avp_value->os.len;
|
||||
CHECK_MALLOC_DO( peer->p_dbgorig = strdup(fd_cnx_getid(*cnx)), { ret = ENOMEM; goto out; } );
|
||||
peer->p_flags.pf_responder = 1;
|
||||
peer->p_flags.pf_delete = 1;
|
||||
|
||||
LOG_D("Created new peer object for incoming CER: %s", peer->p_hdr.info.pi_diamid);
|
||||
|
||||
#ifndef DISABLE_PEER_EXPIRY
|
||||
/* Set this peer to expire on inactivity */
|
||||
peer->p_hdr.info.config.pic_flags.exp = PI_EXP_INACTIVE;
|
||||
peer->p_hdr.info.config.pic_lft = 3600; /* 1 hour without any message
|
||||
-- RFC3539 states that this must not be inferior to BRINGDOWN_INTERVAL = 5 minutes */
|
||||
|
||||
CHECK_FCT_DO( ret = fd_p_expi_update( peer ), goto out );
|
||||
#endif /* DISABLE_PEER_EXPIRY */
|
||||
|
||||
/* Insert the new peer in the list (the PSM will take care of setting the expiry after validation) */
|
||||
fd_list_insert_after( li_inf, &peer->p_hdr.chain );
|
||||
|
||||
/* Start the PSM, which will receive the event below */
|
||||
CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out );
|
||||
} else {
|
||||
/* Check if the peer is in zombie state */
|
||||
if (fd_peer_getstate(peer) == STATE_ZOMBIE) {
|
||||
/* Re-activate the peer */
|
||||
if (peer->p_hdr.info.config.pic_flags.exp)
|
||||
peer->p_flags.pf_responder = 1;
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), );
|
||||
peer->p_state = STATE_NEW;
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), );
|
||||
peer->p_flags.pf_localterm = 0;
|
||||
CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out );
|
||||
}
|
||||
}
|
||||
|
||||
/* Send the new connection event to the PSM */
|
||||
CHECK_MALLOC_DO( ev_data = malloc(sizeof(struct cnx_incoming)), { ret = ENOMEM; goto out; } );
|
||||
memset(ev_data, 0, sizeof(*ev_data));
|
||||
|
||||
ev_data->cer = msg;
|
||||
ev_data->cnx = *cnx;
|
||||
ev_data->validate = !found;
|
||||
|
||||
CHECK_FCT_DO( ret = fd_event_send(peer->p_events, FDEVP_CNX_INCOMING, sizeof(*ev_data), ev_data), goto out );
|
||||
|
||||
out:
|
||||
CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
|
||||
|
||||
if (ret == 0) {
|
||||
/* Reset the "out" parameters, so that they are not cleanup on function return. */
|
||||
*cer = NULL;
|
||||
*cnx = NULL;
|
||||
} else {
|
||||
char buf[1024];
|
||||
snprintf(buf, sizeof(buf), "An error occurred while processing new incoming CER: %s", strerror(ret));
|
||||
fd_hook_call(HOOK_PEER_CONNECT_FAILED, *cer, NULL, buf, NULL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Save a callback to accept / reject incoming unknown peers */
|
||||
int fd_peer_validate_register ( int (*peer_validate)(struct peer_info * /* info */, int * /* auth */, int (**cb2)(struct peer_info *)) )
|
||||
{
|
||||
struct fd_list * v;
|
||||
|
||||
TRACE_ENTRY("%p", peer_validate);
|
||||
CHECK_PARAMS(peer_validate);
|
||||
|
||||
/* Alloc a new entry */
|
||||
CHECK_MALLOC( v = malloc(sizeof(struct fd_list)) );
|
||||
fd_list_init( v, peer_validate );
|
||||
|
||||
/* Add at the beginning of the list */
|
||||
CHECK_FCT( pthread_rwlock_wrlock(&validators_rw) );
|
||||
fd_list_insert_after(&validators, v);
|
||||
CHECK_FCT( pthread_rwlock_unlock(&validators_rw));
|
||||
|
||||
/* Done! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Validate a peer by calling the callbacks in turn -- return 0 if the peer is validated, ! 0 in case of error (>0) or if the peer is rejected (-1) */
|
||||
int fd_peer_validate( struct fd_peer * peer )
|
||||
{
|
||||
int ret = 0;
|
||||
struct fd_list * v;
|
||||
|
||||
CHECK_FCT( pthread_rwlock_rdlock(&validators_rw) );
|
||||
for (v = validators.next; v != &validators; v = v->next) {
|
||||
int auth = 0;
|
||||
pthread_cleanup_push(fd_cleanup_rwlock, &validators_rw);
|
||||
CHECK_FCT_DO( ret = ((int(*)(struct peer_info *, int *, int (**)(struct peer_info *)))(v->o)) (&peer->p_hdr.info, &auth, &peer->p_cb2), );
|
||||
pthread_cleanup_pop(0);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (auth) {
|
||||
ret = (auth > 0) ? 0 : -1;
|
||||
goto out;
|
||||
}
|
||||
peer->p_cb2 = NULL;
|
||||
}
|
||||
|
||||
/* No callback has given a firm result, the default is to reject */
|
||||
ret = -1;
|
||||
ret = 0;
|
||||
out:
|
||||
CHECK_FCT( pthread_rwlock_unlock(&validators_rw));
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.h"
|
||||
|
||||
/* The global message queues */
|
||||
struct fifo * fd_g_incoming = NULL;
|
||||
struct fifo * fd_g_outgoing = NULL;
|
||||
struct fifo * fd_g_local = NULL;
|
||||
|
||||
/* Initialize the message queues. */
|
||||
#define FD_MSG_QUEUE_SIZE 2048
|
||||
|
||||
int fd_queues_init(void)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
CHECK_FCT( fd_fifo_new ( &fd_g_incoming, FD_MSG_QUEUE_SIZE ) );
|
||||
CHECK_FCT( fd_fifo_new ( &fd_g_outgoing, FD_MSG_QUEUE_SIZE ) );
|
||||
CHECK_FCT( fd_fifo_new ( &fd_g_local, FD_MSG_QUEUE_SIZE ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Destroy a queue after emptying it (and dumping the content) */
|
||||
int fd_queues_fini(struct fifo ** queue)
|
||||
{
|
||||
struct msg * msg;
|
||||
int ret = 0;
|
||||
|
||||
TRACE_ENTRY("%p", queue);
|
||||
|
||||
/* Note : the threads that post into this queue should already been stopped before this !!! */
|
||||
|
||||
CHECK_PARAMS(queue);
|
||||
if (*queue == NULL)
|
||||
return 0; /* the queue was not already initialized */
|
||||
|
||||
/* Empty all contents */
|
||||
while (1) {
|
||||
/* Check if there is a message in the queue */
|
||||
ret = fd_fifo_tryget(*queue, &msg);
|
||||
if (ret == EWOULDBLOCK)
|
||||
break;
|
||||
CHECK_FCT(ret);
|
||||
|
||||
/* We got one! */
|
||||
fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, "Message lost because framework is terminating.", fd_msg_pmdl_get(msg));
|
||||
fd_msg_free(msg);
|
||||
}
|
||||
|
||||
/* Now, delete the empty queue */
|
||||
CHECK_FCT( fd_fifo_del ( queue ) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,793 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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. *
|
||||
*********************************************************************************************************/
|
||||
|
||||
/* This file contains code for TLS over multi-stream SCTP wrapper implementation (GnuTLS does not support this) */
|
||||
/* See http://aaa.koganei.wide.ad.jp/blogs/index.php/waaad/2008/08/18/tls-over-sctp for history */
|
||||
|
||||
#include "fdcore-internal.h"
|
||||
#include "cnxctx.h"
|
||||
|
||||
#include <netinet/sctp.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
/*
|
||||
|
||||
Architecture of this wrapper:
|
||||
- we have several fifo queues (1 per stream pairs).
|
||||
GnuTLS is configured to use custom push / pull functions:
|
||||
- the pull function retrieves the data from the fifo queue corresponding to a stream #.
|
||||
- the push function sends the data on a certain stream.
|
||||
We also have a demux thread that reads the socket and store received data in the appropriate fifo
|
||||
|
||||
We have one gnutls_session per stream pair, and as many threads that read the gnutls records and save incoming data to the target queue.
|
||||
|
||||
This complexity is required because we cannot read a socket for a given stream only; we can only get the next message and find its stream.
|
||||
*/
|
||||
|
||||
/* Note that this mechanism is replaced by DTLS in RFC6733 */
|
||||
|
||||
/*************************************************************/
|
||||
/* threads */
|
||||
/*************************************************************/
|
||||
|
||||
/* Demux received data and store in the appropriate fifo */
|
||||
static void * demuxer(void * arg)
|
||||
{
|
||||
struct cnxctx * conn = arg;
|
||||
uint8_t * buf;
|
||||
size_t bufsz;
|
||||
int event;
|
||||
uint16_t strid;
|
||||
|
||||
TRACE_ENTRY("%p", arg);
|
||||
CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto out);
|
||||
|
||||
/* Set the thread name */
|
||||
{
|
||||
char buf[48];
|
||||
snprintf(buf, sizeof(buf), "Demuxer (%d:%s)", conn->cc_socket, conn->cc_remid);
|
||||
fd_log_threadname ( buf );
|
||||
}
|
||||
|
||||
ASSERT( conn->cc_proto == IPPROTO_SCTP );
|
||||
ASSERT( fd_cnx_target_queue(conn) );
|
||||
ASSERT( conn->cc_sctp3436_data.array );
|
||||
|
||||
do {
|
||||
CHECK_FCT_DO( fd_sctp_recvmeta(conn, &strid, &buf, &bufsz, &event), goto fatal );
|
||||
switch (event) {
|
||||
case FDEVP_CNX_MSG_RECV:
|
||||
/* Demux this message to the appropriate fifo, another thread will pull, gnutls process, and send to target queue */
|
||||
if (strid < conn->cc_sctp_para.pairs) {
|
||||
CHECK_FCT_DO(fd_event_send(conn->cc_sctp3436_data.array[strid].raw_recv, event, bufsz, buf), goto fatal );
|
||||
} else {
|
||||
TRACE_DEBUG(INFO, "Received packet (%zd bytes) on out-of-range stream #%d from %s, discarded.", bufsz, strid, conn->cc_remid);
|
||||
free(buf);
|
||||
}
|
||||
break;
|
||||
|
||||
case FDEVP_CNX_EP_CHANGE:
|
||||
/* Send this event to the target queue */
|
||||
CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), event, bufsz, buf), goto fatal );
|
||||
break;
|
||||
|
||||
case FDEVP_CNX_ERROR:
|
||||
goto out;
|
||||
|
||||
case FDEVP_CNX_SHUTDOWN:
|
||||
/* Just ignore the notification for now, we will get another error later anyway */
|
||||
continue;
|
||||
|
||||
default:
|
||||
goto fatal;
|
||||
}
|
||||
|
||||
} while (conn->cc_loop);
|
||||
|
||||
out:
|
||||
/* Signal termination of the connection to all decipher threads */
|
||||
for (strid = 0; strid < conn->cc_sctp_para.pairs; strid++) {
|
||||
if (conn->cc_sctp3436_data.array[strid].raw_recv) {
|
||||
CHECK_FCT_DO(fd_event_send(conn->cc_sctp3436_data.array[strid].raw_recv, FDEVP_CNX_ERROR, 0, NULL), goto fatal );
|
||||
}
|
||||
}
|
||||
fd_cnx_markerror(conn);
|
||||
TRACE_DEBUG(FULL, "Thread terminated");
|
||||
return NULL;
|
||||
|
||||
fatal:
|
||||
/* An unrecoverable error occurred, stop the daemon */
|
||||
CHECK_FCT_DO(fd_core_shutdown(), );
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Decrypt the data received in this stream pair and store it in the target queue */
|
||||
static void * decipher(void * arg)
|
||||
{
|
||||
struct sctp3436_ctx * ctx = arg;
|
||||
struct cnxctx *cnx;
|
||||
|
||||
TRACE_ENTRY("%p", arg);
|
||||
CHECK_PARAMS_DO(ctx && ctx->raw_recv && ctx->parent, goto error);
|
||||
cnx = ctx->parent;
|
||||
ASSERT( fd_cnx_target_queue(cnx) );
|
||||
|
||||
/* Set the thread name */
|
||||
{
|
||||
char buf[48];
|
||||
snprintf(buf, sizeof(buf), "Decipher (%hu@%d:%s)", ctx->strid, cnx->cc_socket, cnx->cc_remid);
|
||||
fd_log_threadname ( buf );
|
||||
}
|
||||
|
||||
/* The next function loops while there is no error */
|
||||
CHECK_FCT_DO(fd_tls_rcvthr_core(cnx, ctx->strid ? ctx->session : cnx->cc_tls_para.session), /* continue */);
|
||||
error:
|
||||
fd_cnx_markerror(cnx);
|
||||
TRACE_DEBUG(FULL, "Thread terminated");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/* push / pull */
|
||||
/*************************************************************/
|
||||
|
||||
#ifdef GNUTLS_VERSION_300
|
||||
/* Check if data is available for gnutls on a given context */
|
||||
static int sctp3436_pull_timeout(gnutls_transport_ptr_t tr, unsigned int ms)
|
||||
{
|
||||
struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
|
||||
struct timespec tsstore, *ts = NULL;
|
||||
int ret;
|
||||
|
||||
TRACE_ENTRY("%p %d", tr, ms);
|
||||
|
||||
if (ctx->partial.buf)
|
||||
return 1; /* data is already available for pull */
|
||||
|
||||
if (ms) {
|
||||
CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &tsstore), return -1 );
|
||||
tsstore.tv_nsec += (long)ms * 1000000;
|
||||
tsstore.tv_sec += tsstore.tv_nsec / 1000000000L;
|
||||
tsstore.tv_nsec %= 1000000000L;
|
||||
ts = &tsstore;
|
||||
}
|
||||
|
||||
ret = fd_fifo_select ( ctx->raw_recv, ts );
|
||||
if (ret < 0) {
|
||||
errno = -ret;
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* GNUTLS_VERSION_300 */
|
||||
|
||||
/* Send data over the connection, called by gnutls */
|
||||
#ifndef GNUTLS_VERSION_212
|
||||
static ssize_t sctp3436_push(gnutls_transport_ptr_t tr, const void * data, size_t len)
|
||||
{
|
||||
struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
|
||||
struct iovec iov;
|
||||
|
||||
TRACE_ENTRY("%p %p %zd", tr, data, len);
|
||||
CHECK_PARAMS_DO( tr && data, { errno = EINVAL; return -1; } );
|
||||
|
||||
iov.iov_base = (void *)data;
|
||||
iov.iov_len = len;
|
||||
|
||||
return fd_sctp_sendstrv(ctx->parent, ctx->strid, &iov, 1);
|
||||
}
|
||||
#else /* GNUTLS_VERSION_212 */
|
||||
static ssize_t sctp3436_pushv(gnutls_transport_ptr_t tr, const giovec_t * iov, int iovcnt)
|
||||
{
|
||||
struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
|
||||
|
||||
TRACE_ENTRY("%p %p %d", tr, iov, iovcnt);
|
||||
CHECK_PARAMS_DO( tr && iov, { errno = EINVAL; return -1; } );
|
||||
|
||||
return fd_sctp_sendstrv(ctx->parent, ctx->strid, (const struct iovec *)iov, iovcnt);
|
||||
}
|
||||
#endif /* GNUTLS_VERSION_212 */
|
||||
|
||||
/* Retrieve data received on a stream and already demultiplexed */
|
||||
static ssize_t sctp3436_pull(gnutls_transport_ptr_t tr, void * buf, size_t len)
|
||||
{
|
||||
struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
|
||||
size_t pulled = 0;
|
||||
int emptied;
|
||||
|
||||
TRACE_ENTRY("%p %p %zd", tr, buf, len);
|
||||
CHECK_PARAMS_DO( tr && buf, { errno = EINVAL; goto error; } );
|
||||
|
||||
/* If we don't have data available now, pull new message from the fifo -- this is blocking (until the queue is destroyed) */
|
||||
if (!ctx->partial.buf) {
|
||||
int ev;
|
||||
CHECK_FCT_DO( errno = fd_event_get(ctx->raw_recv, &ev, &ctx->partial.bufsz, (void *)&ctx->partial.buf), goto error );
|
||||
if (ev == FDEVP_CNX_ERROR) {
|
||||
/* Documentations says to return 0 on connection closed, but it does hang within gnutls_handshake */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
pulled = ctx->partial.bufsz - ctx->partial.offset;
|
||||
if (pulled <= len) {
|
||||
emptied = 1;
|
||||
} else {
|
||||
/* limit to the capacity of destination buffer */
|
||||
emptied = 0;
|
||||
pulled = len;
|
||||
}
|
||||
|
||||
/* Store the data in the destination buffer */
|
||||
memcpy(buf, ctx->partial.buf + ctx->partial.offset, pulled);
|
||||
|
||||
/* Free the buffer if we read all its content, and reset the partial structure */
|
||||
if (emptied) {
|
||||
free(ctx->partial.buf);
|
||||
memset(&ctx->partial, 0, sizeof(ctx->partial));
|
||||
} else {
|
||||
ctx->partial.offset += pulled;
|
||||
}
|
||||
|
||||
/* We are done */
|
||||
return pulled;
|
||||
|
||||
error:
|
||||
gnutls_transport_set_errno (ctx->session, errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set the parameters of a session to use the appropriate fifo and stream information */
|
||||
#ifndef GNUTLS_VERSION_300
|
||||
GCC_DIAG_OFF("-Wdeprecated-declarations")
|
||||
#endif /* !GNUTLS_VERSION_300 */
|
||||
static void set_sess_transport(gnutls_session_t session, struct sctp3436_ctx *ctx)
|
||||
{
|
||||
/* Set the transport pointer passed to push & pull callbacks */
|
||||
GNUTLS_TRACE( gnutls_transport_set_ptr( session, (gnutls_transport_ptr_t) ctx ) );
|
||||
|
||||
/* Reset the low water value, since we don't use sockets */
|
||||
#ifndef GNUTLS_VERSION_300
|
||||
/* starting version 2.12, this call is not needed */
|
||||
//GNUTLS_TRACE( gnutls_transport_set_lowat( session, 0 ) );
|
||||
#else /* GNUTLS_VERSION_300 */
|
||||
/* but in 3.0 we have to provide the pull_timeout callback */
|
||||
GNUTLS_TRACE( gnutls_transport_set_pull_timeout_function( session, sctp3436_pull_timeout ) );
|
||||
#endif /* GNUTLS_VERSION_300 */
|
||||
|
||||
/* Set the push and pull callbacks */
|
||||
GNUTLS_TRACE( gnutls_transport_set_pull_function(session, sctp3436_pull) );
|
||||
#ifndef GNUTLS_VERSION_212
|
||||
GNUTLS_TRACE( gnutls_transport_set_push_function(session, sctp3436_push) );
|
||||
#else /* GNUTLS_VERSION_212 */
|
||||
GNUTLS_TRACE( gnutls_transport_set_vec_push_function(session, sctp3436_pushv) );
|
||||
#endif /* GNUTLS_VERSION_212 */
|
||||
|
||||
return;
|
||||
}
|
||||
#ifndef GNUTLS_VERSION_300
|
||||
GCC_DIAG_ON("-Wdeprecated-declarations")
|
||||
#endif /* !GNUTLS_VERSION_300 */
|
||||
|
||||
/*************************************************************/
|
||||
/* Session resuming support */
|
||||
/*************************************************************/
|
||||
|
||||
struct sr_store {
|
||||
struct fd_list list; /* list of sr_data, ordered by key.size then key.data */
|
||||
pthread_rwlock_t lock;
|
||||
struct cnxctx *parent;
|
||||
/* Add another list to chain in a global list to implement a garbage collector on sessions -- TODO if needed */
|
||||
};
|
||||
|
||||
/* Saved master session data for resuming sessions */
|
||||
struct sr_data {
|
||||
struct fd_list chain;
|
||||
gnutls_datum_t key;
|
||||
gnutls_datum_t data;
|
||||
};
|
||||
|
||||
/* Initialize the store area for a connection */
|
||||
static int store_init(struct cnxctx * conn)
|
||||
{
|
||||
TRACE_ENTRY("%p", conn);
|
||||
CHECK_PARAMS( conn && !conn->cc_sctp3436_data.sess_store );
|
||||
|
||||
CHECK_MALLOC( conn->cc_sctp3436_data.sess_store = malloc(sizeof(struct sr_store)) );
|
||||
memset(conn->cc_sctp3436_data.sess_store, 0, sizeof(struct sr_store));
|
||||
|
||||
fd_list_init(&conn->cc_sctp3436_data.sess_store->list, NULL);
|
||||
CHECK_POSIX( pthread_rwlock_init(&conn->cc_sctp3436_data.sess_store->lock, NULL) );
|
||||
conn->cc_sctp3436_data.sess_store->parent = conn;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Destroy the store area for a connection, and all its content */
|
||||
static void store_destroy(struct cnxctx * conn)
|
||||
{
|
||||
/* Del all list entries */
|
||||
TRACE_ENTRY("%p", conn);
|
||||
CHECK_PARAMS_DO( conn, return );
|
||||
|
||||
if (!conn->cc_sctp3436_data.sess_store)
|
||||
return;
|
||||
|
||||
CHECK_POSIX_DO( pthread_rwlock_destroy(&conn->cc_sctp3436_data.sess_store->lock), /* continue */ );
|
||||
|
||||
while (!FD_IS_LIST_EMPTY(&conn->cc_sctp3436_data.sess_store->list)) {
|
||||
struct sr_data * sr = (struct sr_data *) conn->cc_sctp3436_data.sess_store->list.next;
|
||||
fd_list_unlink( &sr->chain );
|
||||
free(sr->key.data);
|
||||
free(sr->data.data);
|
||||
free(sr);
|
||||
}
|
||||
|
||||
free(conn->cc_sctp3436_data.sess_store);
|
||||
conn->cc_sctp3436_data.sess_store = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Search the position (or next if not found) of a key in a store */
|
||||
static struct fd_list * find_or_next(struct sr_store * sto, gnutls_datum_t key, int * match)
|
||||
{
|
||||
struct fd_list * ret;
|
||||
*match = 0;
|
||||
|
||||
for (ret = sto->list.next; ret != &sto->list; ret = ret->next) {
|
||||
int cmp = 0;
|
||||
struct sr_data * sr = (struct sr_data *)ret;
|
||||
|
||||
cmp = fd_os_cmp(key.data, key.size, sr->key.data, sr->key.size);
|
||||
if (cmp > 0)
|
||||
continue;
|
||||
|
||||
if (cmp == 0)
|
||||
*match = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Callbacks for the TLS server side of the connection, called during gnutls_handshake */
|
||||
static int sr_store (void *dbf, gnutls_datum_t key, gnutls_datum_t data)
|
||||
{
|
||||
struct sr_store * sto = (struct sr_store *)dbf;
|
||||
struct fd_list * li;
|
||||
struct sr_data * sr;
|
||||
int match = 0;
|
||||
int ret = 0;
|
||||
|
||||
TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ );
|
||||
CHECK_PARAMS_DO( sto && key.data && data.data, return -1 );
|
||||
|
||||
CHECK_POSIX_DO( pthread_rwlock_wrlock(&sto->lock), return -1 );
|
||||
TRACE_BUFFER(FD_LOG_DEBUG, GNUTLS_DBG_LEVEL, "Session store [key ", key.data, key.size, "]");
|
||||
|
||||
li = find_or_next(sto, key, &match);
|
||||
if (match) {
|
||||
sr = (struct sr_data *)li;
|
||||
|
||||
/* Check the data is the same */
|
||||
if ((data.size != sr->data.size) || memcmp(data.data, sr->data.data, data.size)) {
|
||||
TRACE_DEBUG(INFO, "GnuTLS tried to store a session with same key and different data!");
|
||||
TRACE_BUFFER(FD_LOG_DEBUG, INFO, "Session store [key ", key.data, key.size, "]");
|
||||
TRACE_BUFFER(FD_LOG_DEBUG, INFO, " -- old data [", sr->data.data, sr->data.size, "]");
|
||||
TRACE_BUFFER(FD_LOG_DEBUG, INFO, " -- new data [", data.data, data.size, "]");
|
||||
|
||||
ret = -1;
|
||||
} else {
|
||||
TRACE_DEBUG(GNUTLS_DBG_LEVEL, "GnuTLS tried to store a session with same key and same data, skipped.");
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Create a new entry */
|
||||
CHECK_MALLOC_DO( sr = malloc(sizeof(struct sr_data)), { ret = -1; goto out; } );
|
||||
memset(sr, 0, sizeof(struct sr_data));
|
||||
|
||||
fd_list_init(&sr->chain, sr);
|
||||
|
||||
CHECK_MALLOC_DO( sr->key.data = malloc(key.size), { ret = -1; goto out; } );
|
||||
sr->key.size = key.size;
|
||||
memcpy(sr->key.data, key.data, key.size);
|
||||
|
||||
CHECK_MALLOC_DO( sr->data.data = malloc(data.size), { ret = -1; goto out; } );
|
||||
sr->data.size = data.size;
|
||||
memcpy(sr->data.data, data.data, data.size);
|
||||
|
||||
/* Save this new entry in the list, we are done */
|
||||
fd_list_insert_before(li, &sr->chain);
|
||||
|
||||
out:
|
||||
CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return -1 );
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sr_remove (void *dbf, gnutls_datum_t key)
|
||||
{
|
||||
struct sr_store * sto = (struct sr_store *)dbf;
|
||||
struct fd_list * li;
|
||||
struct sr_data * sr;
|
||||
int match = 0;
|
||||
int ret = 0;
|
||||
|
||||
TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ );
|
||||
CHECK_PARAMS_DO( sto && key.data, return -1 );
|
||||
|
||||
CHECK_POSIX_DO( pthread_rwlock_wrlock(&sto->lock), return -1 );
|
||||
TRACE_BUFFER(FD_LOG_DEBUG, GNUTLS_DBG_LEVEL, "Session delete [key ", key.data, key.size, "]");
|
||||
|
||||
li = find_or_next(sto, key, &match);
|
||||
if (match) {
|
||||
sr = (struct sr_data *)li;
|
||||
|
||||
/* Destroy this data */
|
||||
fd_list_unlink(li);
|
||||
free(sr->key.data);
|
||||
free(sr->data.data);
|
||||
free(sr);
|
||||
} else {
|
||||
/* It was not found */
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return -1 );
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gnutls_datum_t sr_fetch (void *dbf, gnutls_datum_t key)
|
||||
{
|
||||
struct sr_store * sto = (struct sr_store *)dbf;
|
||||
struct fd_list * li;
|
||||
struct sr_data * sr;
|
||||
int match = 0;
|
||||
gnutls_datum_t res = { NULL, 0 };
|
||||
gnutls_datum_t error = { NULL, 0 };
|
||||
|
||||
TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ );
|
||||
CHECK_PARAMS_DO( sto && key.data, return error );
|
||||
|
||||
CHECK_POSIX_DO( pthread_rwlock_rdlock(&sto->lock), return error );
|
||||
TRACE_BUFFER(FD_LOG_DEBUG, GNUTLS_DBG_LEVEL, "Session fetch [key ", key.data, key.size, "]");
|
||||
|
||||
li = find_or_next(sto, key, &match);
|
||||
if (match) {
|
||||
sr = (struct sr_data *)li;
|
||||
GNUTLS_TRACE( CHECK_MALLOC_DO(res.data = gnutls_malloc(sr->data.size), goto out ) );
|
||||
res.size = sr->data.size;
|
||||
memcpy(res.data, sr->data.data, res.size);
|
||||
}
|
||||
out:
|
||||
TRACE_DEBUG(GNUTLS_DBG_LEVEL, "Fetched (%p, %d) from store %p", res.data, res.size, sto);
|
||||
CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return error);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Set the session pointer in a session object */
|
||||
static void set_resume_callbacks(gnutls_session_t session, struct cnxctx * conn)
|
||||
{
|
||||
TRACE_ENTRY("%p", conn);
|
||||
|
||||
GNUTLS_TRACE( gnutls_db_set_retrieve_function(session, sr_fetch));
|
||||
GNUTLS_TRACE( gnutls_db_set_remove_function (session, sr_remove));
|
||||
GNUTLS_TRACE( gnutls_db_set_store_function (session, sr_store));
|
||||
GNUTLS_TRACE( gnutls_db_set_ptr (session, conn->cc_sctp3436_data.sess_store));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* The handshake is made in parallel in several threads to speed up */
|
||||
static void * handshake_resume_th(void * arg)
|
||||
{
|
||||
struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) arg;
|
||||
int resumed;
|
||||
|
||||
TRACE_ENTRY("%p", arg);
|
||||
|
||||
/* Set the thread name */
|
||||
{
|
||||
char buf[48];
|
||||
snprintf(buf, sizeof(buf), "Handshake resume (%hu@%d)", ctx->strid, ctx->parent->cc_socket);
|
||||
fd_log_threadname ( buf );
|
||||
}
|
||||
|
||||
TRACE_DEBUG(FULL, "Starting TLS resumed handshake on stream %hu", ctx->strid);
|
||||
|
||||
CHECK_GNUTLS_DO( gnutls_handshake( ctx->session ), return NULL);
|
||||
|
||||
GNUTLS_TRACE( resumed = gnutls_session_is_resumed(ctx->session) );
|
||||
#ifndef GNUTLS_VERSION_300
|
||||
if (!resumed) {
|
||||
/* Check the credentials here also */
|
||||
CHECK_FCT_DO( fd_tls_verify_credentials(ctx->session, ctx->parent, 0), return NULL );
|
||||
}
|
||||
#endif /* GNUTLS_VERSION_300 */
|
||||
if (TRACE_BOOL(FULL)) {
|
||||
if (resumed) {
|
||||
fd_log_debug("Session was resumed successfully on stream %hu (conn: '%s')", ctx->strid, fd_cnx_getid(ctx->parent));
|
||||
} else {
|
||||
fd_log_debug("Session was NOT resumed on stream %hu (full handshake) (conn: '%s')", ctx->strid, fd_cnx_getid(ctx->parent));
|
||||
}
|
||||
}
|
||||
|
||||
/* Finished, OK */
|
||||
return arg;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************/
|
||||
/* Exported functions */
|
||||
/*************************************************************/
|
||||
|
||||
/* Initialize the wrapper for the connection */
|
||||
int fd_sctp3436_init(struct cnxctx * conn)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
TRACE_ENTRY("%p", conn);
|
||||
CHECK_PARAMS( conn && (conn->cc_sctp_para.pairs > 1) && (!conn->cc_sctp3436_data.array) );
|
||||
|
||||
/* First, alloc the array and initialize the non-TLS data */
|
||||
CHECK_MALLOC( conn->cc_sctp3436_data.array = calloc(conn->cc_sctp_para.pairs, sizeof(struct sctp3436_ctx)) );
|
||||
for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
|
||||
conn->cc_sctp3436_data.array[i].parent = conn;
|
||||
conn->cc_sctp3436_data.array[i].strid = i;
|
||||
CHECK_FCT( fd_fifo_new(&conn->cc_sctp3436_data.array[i].raw_recv, 10) );
|
||||
}
|
||||
|
||||
/* Set push/pull functions in the master session, using fifo in array[0] */
|
||||
set_sess_transport(conn->cc_tls_para.session, &conn->cc_sctp3436_data.array[0]);
|
||||
|
||||
/* For server side, we also initialize the resuming capabilities */
|
||||
if (conn->cc_tls_para.mode == GNUTLS_SERVER) {
|
||||
|
||||
/* Prepare the store for sessions data */
|
||||
CHECK_FCT( store_init(conn) );
|
||||
|
||||
/* Set the callbacks for resuming in the master session */
|
||||
set_resume_callbacks(conn->cc_tls_para.session, conn);
|
||||
}
|
||||
|
||||
/* Start the demux thread */
|
||||
CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, demuxer, conn ) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handshake other streams, after full handshake on the master session */
|
||||
int fd_sctp3436_handshake_others(struct cnxctx * conn, char * priority, void * alt_creds)
|
||||
{
|
||||
uint16_t i;
|
||||
int errors = 0;
|
||||
gnutls_datum_t master_data;
|
||||
|
||||
TRACE_ENTRY("%p %p", conn, priority);
|
||||
CHECK_PARAMS( conn && (conn->cc_sctp_para.pairs > 1) && conn->cc_sctp3436_data.array );
|
||||
|
||||
/* Server side: we set all the parameters, the resume callback will take care of resuming the session */
|
||||
/* Client side: we duplicate the parameters of the master session, then set the transport pointer */
|
||||
|
||||
/* For client side, retrieve the master session parameters */
|
||||
if (conn->cc_tls_para.mode == GNUTLS_CLIENT) {
|
||||
CHECK_GNUTLS_DO( gnutls_session_get_data2(conn->cc_tls_para.session, &master_data), return ENOMEM );
|
||||
/* For debug: */
|
||||
if (TRACE_BOOL(GNUTLS_DBG_LEVEL)) {
|
||||
uint8_t id[256];
|
||||
size_t ids = sizeof(id);
|
||||
CHECK_GNUTLS_DO( gnutls_session_get_id(conn->cc_tls_para.session, id, &ids), /* continue */ );
|
||||
TRACE_BUFFER(FD_LOG_DEBUG, GNUTLS_DBG_LEVEL, "Master session id: [", id, ids, "]");
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize the session objects and start the handshake in a separate thread */
|
||||
for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
|
||||
/* Set credentials and priority */
|
||||
CHECK_FCT( fd_tls_prepare(&conn->cc_sctp3436_data.array[i].session, conn->cc_tls_para.mode, 0, priority, alt_creds) );
|
||||
|
||||
/* additional initialization for gnutls 3.x */
|
||||
#ifdef GNUTLS_VERSION_300
|
||||
/* the verify function has already been set in the global initialization in config.c */
|
||||
|
||||
/* fd_tls_verify_credentials_2 uses the connection */
|
||||
gnutls_session_set_ptr (conn->cc_sctp3436_data.array[i].session, (void *) conn);
|
||||
|
||||
if ((conn->cc_tls_para.cn != NULL) && (conn->cc_tls_para.mode == GNUTLS_CLIENT)) {
|
||||
/* this might allow virtual hosting on the remote peer */
|
||||
CHECK_GNUTLS_DO( gnutls_server_name_set (conn->cc_sctp3436_data.array[i].session, GNUTLS_NAME_DNS, conn->cc_tls_para.cn, strlen(conn->cc_tls_para.cn)), /* ignore failure */);
|
||||
}
|
||||
|
||||
#endif /* GNUTLS_VERSION_300 */
|
||||
|
||||
#ifdef GNUTLS_VERSION_310
|
||||
GNUTLS_TRACE( gnutls_handshake_set_timeout( conn->cc_sctp3436_data.array[i].session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT));
|
||||
#endif /* GNUTLS_VERSION_310 */
|
||||
|
||||
/* For the client, copy data from master session; for the server, set session resuming pointers */
|
||||
if (conn->cc_tls_para.mode == GNUTLS_CLIENT) {
|
||||
CHECK_GNUTLS_DO( gnutls_session_set_data(conn->cc_sctp3436_data.array[i].session, master_data.data, master_data.size), return ENOMEM );
|
||||
} else {
|
||||
set_resume_callbacks(conn->cc_sctp3436_data.array[i].session, conn);
|
||||
}
|
||||
|
||||
/* Set transport parameters */
|
||||
set_sess_transport(conn->cc_sctp3436_data.array[i].session, &conn->cc_sctp3436_data.array[i]);
|
||||
|
||||
/* Start the handshake thread */
|
||||
CHECK_POSIX( pthread_create( &conn->cc_sctp3436_data.array[i].thr, NULL, handshake_resume_th, &conn->cc_sctp3436_data.array[i] ) );
|
||||
}
|
||||
|
||||
/* We can now release the memory of master session data if any */
|
||||
if (conn->cc_tls_para.mode == GNUTLS_CLIENT) {
|
||||
GNUTLS_TRACE( gnutls_free(master_data.data) );
|
||||
}
|
||||
|
||||
/* Now wait for all handshakes to finish */
|
||||
for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
|
||||
void * ret;
|
||||
CHECK_POSIX( pthread_join(conn->cc_sctp3436_data.array[i].thr, &ret) );
|
||||
conn->cc_sctp3436_data.array[i].thr = (pthread_t) NULL;
|
||||
if (ret == NULL) {
|
||||
errors++; /* Handshake failed on this stream */
|
||||
}
|
||||
}
|
||||
|
||||
if (errors) {
|
||||
TRACE_DEBUG(INFO, "Handshake failed on %d/%hd stream pairs", errors, conn->cc_sctp_para.pairs);
|
||||
fd_cnx_markerror(conn);
|
||||
return ENOTCONN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Receive messages from others ? all other stream pairs : the master pair */
|
||||
int fd_sctp3436_startthreads(struct cnxctx * conn, int others)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
TRACE_ENTRY("%p", conn);
|
||||
CHECK_PARAMS( conn && conn->cc_sctp3436_data.array );
|
||||
|
||||
if (others) {
|
||||
for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
|
||||
|
||||
/* Start the decipher thread */
|
||||
CHECK_POSIX( pthread_create( &conn->cc_sctp3436_data.array[i].thr, NULL, decipher, &conn->cc_sctp3436_data.array[i] ) );
|
||||
}
|
||||
} else {
|
||||
CHECK_POSIX( pthread_create( &conn->cc_sctp3436_data.array[0].thr, NULL, decipher, &conn->cc_sctp3436_data.array[0] ) );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initiate a "bye" on all stream pairs */
|
||||
void fd_sctp3436_bye(struct cnxctx * conn)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return );
|
||||
|
||||
/* End all TLS sessions, in series (not as efficient as paralel, but simpler) */
|
||||
for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
|
||||
if ( ! fd_cnx_teststate(conn, CC_STATUS_ERROR)) {
|
||||
CHECK_GNUTLS_DO( gnutls_bye(conn->cc_sctp3436_data.array[i].session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* After "bye" was sent on all streams, read from sessions until an error is received */
|
||||
void fd_sctp3436_waitthreadsterm(struct cnxctx * conn)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
TRACE_ENTRY("%p", conn);
|
||||
CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return );
|
||||
|
||||
for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
|
||||
if (conn->cc_sctp3436_data.array[i].thr != (pthread_t)NULL) {
|
||||
CHECK_POSIX_DO( pthread_join(conn->cc_sctp3436_data.array[i].thr, NULL), /* continue */ );
|
||||
conn->cc_sctp3436_data.array[i].thr = (pthread_t)NULL;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Free gnutls resources of all sessions */
|
||||
void fd_sctp3436_gnutls_deinit_others(struct cnxctx * conn)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
TRACE_ENTRY("%p", conn);
|
||||
CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return );
|
||||
|
||||
for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
|
||||
if (conn->cc_sctp3436_data.array[i].session) {
|
||||
GNUTLS_TRACE( gnutls_deinit(conn->cc_sctp3436_data.array[i].session) );
|
||||
conn->cc_sctp3436_data.array[i].session = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Stop all receiver threads */
|
||||
void fd_sctp3436_stopthreads(struct cnxctx * conn)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
TRACE_ENTRY("%p", conn);
|
||||
CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return );
|
||||
|
||||
for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
|
||||
CHECK_FCT_DO( fd_thr_term(&conn->cc_sctp3436_data.array[i].thr), /* continue */ );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Destroy a wrapper context */
|
||||
void fd_sctp3436_destroy(struct cnxctx * conn)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return );
|
||||
|
||||
/* Terminate all receiving threads in case we did not do it yet */
|
||||
fd_sctp3436_stopthreads(conn);
|
||||
|
||||
/* Now, stop the demux thread */
|
||||
CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ );
|
||||
|
||||
/* Free remaining data in the array */
|
||||
for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
|
||||
if (conn->cc_sctp3436_data.array[i].raw_recv)
|
||||
fd_event_destroy( &conn->cc_sctp3436_data.array[i].raw_recv, free );
|
||||
free(conn->cc_sctp3436_data.array[i].partial.buf);
|
||||
if (conn->cc_sctp3436_data.array[i].session) {
|
||||
GNUTLS_TRACE( gnutls_deinit(conn->cc_sctp3436_data.array[i].session) );
|
||||
conn->cc_sctp3436_data.array[i].session = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free the array itself now */
|
||||
free(conn->cc_sctp3436_data.array);
|
||||
conn->cc_sctp3436_data.array = NULL;
|
||||
|
||||
/* Delete the store of sessions */
|
||||
store_destroy(conn);
|
||||
|
||||
return ;
|
||||
}
|
||||
@@ -0,0 +1,509 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.h"
|
||||
|
||||
/* Server (listening) part of the framework */
|
||||
|
||||
static struct fd_list FD_SERVERS = FD_LIST_INITIALIZER(FD_SERVERS); /* The list of all server objects */
|
||||
/* We don't need to protect this list, it is only accessed from the main framework thread. */
|
||||
|
||||
enum s_state {
|
||||
NOT_CREATED=0,
|
||||
RUNNING,
|
||||
TERMINATED,
|
||||
ERROR /* an error occurred, this is not a valid status */
|
||||
};
|
||||
|
||||
/* Servers information */
|
||||
struct server {
|
||||
struct fd_list chain; /* link in the FD_SERVERS list */
|
||||
|
||||
struct cnxctx * conn; /* server connection context (listening socket) */
|
||||
int proto; /* IPPROTO_TCP or IPPROTO_SCTP */
|
||||
int secur; /* TLS is started immediatly after connection ? 0: no; 1: RFU; 2: yes (TLS/TCP or TLS/SCTP) */
|
||||
|
||||
pthread_t thr; /* The thread waiting for new connections (will store the data in the clients fifo) */
|
||||
enum s_state state; /* state of the thread */
|
||||
|
||||
struct fifo *pending; /* FIFO of struct cnxctx */
|
||||
struct pool_workers {
|
||||
struct server * s; /* pointer to the parent server structure */
|
||||
int id; /* The worker id for logs */
|
||||
pthread_t worker; /* The thread */
|
||||
} *workers; /* array of cnf_thr_srv items */
|
||||
};
|
||||
|
||||
|
||||
/* Micro functions to read/change the status thread-safely */
|
||||
static pthread_mutex_t s_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static enum s_state get_status(struct server * s)
|
||||
{
|
||||
enum s_state r;
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&s_lock), return ERROR );
|
||||
r = s->state;
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&s_lock), return ERROR );
|
||||
return r;
|
||||
}
|
||||
static void set_status(struct server * s, enum s_state st)
|
||||
{
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&s_lock), return );
|
||||
s->state = st;
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&s_lock), return );
|
||||
}
|
||||
|
||||
|
||||
/* dump one item of the server->pending fifo */
|
||||
static DECLARE_FD_DUMP_PROTOTYPE(dump_cnx, void * item) {
|
||||
struct cnxctx * c = item;
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " '%s'", fd_cnx_getid(c)), return NULL);
|
||||
return *buf;
|
||||
}
|
||||
|
||||
/* Dump all servers information */
|
||||
DECLARE_FD_DUMP_PROTOTYPE(fd_servers_dump, int details)
|
||||
{
|
||||
struct fd_list * li;
|
||||
|
||||
FD_DUMP_HANDLE_OFFSET();
|
||||
|
||||
for (li = FD_SERVERS.next; li != &FD_SERVERS; li = li->next) {
|
||||
struct server * s = (struct server *)li;
|
||||
enum s_state st = get_status(s);
|
||||
|
||||
if (details) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{server}(@%p)'%s': %s, %s(%d), %s", s, fd_cnx_getid(s->conn),
|
||||
IPPROTO_NAME( s->proto ),
|
||||
s->secur ? "Secur" : "NotSecur", s->secur,
|
||||
(st == NOT_CREATED) ? "Thread not created" :
|
||||
((st == RUNNING) ? "Thread running" :
|
||||
((st == TERMINATED) ? "Thread terminated" :
|
||||
"Thread status unknown"))), return NULL);
|
||||
/* Dump the client list of this server */
|
||||
CHECK_MALLOC_DO( fd_fifo_dump(FD_DUMP_STD_PARAMS, "pending connections", s->pending, dump_cnx), return NULL );
|
||||
|
||||
if (li->next != &FD_SERVERS) {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL);
|
||||
}
|
||||
} else {
|
||||
CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s'(%s,%s) ", fd_cnx_getid(s->conn),
|
||||
IPPROTO_NAME( s->proto ), s->secur ? "Secur" : "NotSecur"), return NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return *buf;
|
||||
}
|
||||
|
||||
|
||||
/* The thread in the pool for handling new clients connecting to a server */
|
||||
static void * client_worker(void * arg)
|
||||
{
|
||||
struct pool_workers * pw = arg;
|
||||
struct server * s = pw->s;
|
||||
struct cnxctx * c = NULL;
|
||||
int fatal = 0;
|
||||
struct timespec ts;
|
||||
struct fd_cnx_rcvdata rcv_data;
|
||||
struct fd_msg_pmdl * pmdl = NULL;
|
||||
struct msg * msg = NULL;
|
||||
struct msg_hdr *hdr = NULL;
|
||||
struct fd_pei pei;
|
||||
|
||||
TRACE_ENTRY("%p", arg);
|
||||
|
||||
/* Set the thread name */
|
||||
{
|
||||
char buf[48];
|
||||
snprintf(buf, sizeof(buf), "Worker#%d[%s%s]", pw->id, IPPROTO_NAME(s->proto), s->secur?", Sec" : "");
|
||||
fd_log_threadname ( buf );
|
||||
}
|
||||
|
||||
/* Loop until canceled / error */
|
||||
next_client:
|
||||
LOG_A("Ready to process next incoming connection");
|
||||
|
||||
memset(&rcv_data, 0, sizeof(rcv_data));
|
||||
|
||||
/* Get the next connection */
|
||||
CHECK_FCT_DO( fd_fifo_get( s->pending, &c ), { fatal = 1; goto cleanup; } );
|
||||
|
||||
/* Handshake if we are a secure server port, or start clear otherwise */
|
||||
if (s->secur) {
|
||||
LOG_D("Starting handshake with %s", fd_cnx_getid(c));
|
||||
|
||||
int ret = fd_cnx_handshake(c, GNUTLS_SERVER, (s->secur == 1) ? ALGO_HANDSHAKE_DEFAULT : ALGO_HANDSHAKE_3436, NULL, NULL);
|
||||
if (ret != 0) {
|
||||
char buf[1024];
|
||||
snprintf(buf, sizeof(buf), "TLS handshake failed for connection '%s', connection closed.", fd_cnx_getid(c));
|
||||
|
||||
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
} else {
|
||||
CHECK_FCT_DO( fd_cnx_start_clear(c, 0), goto cleanup );
|
||||
}
|
||||
|
||||
/* Set the timeout to receive the first message */
|
||||
CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), { fatal = 1; goto cleanup; } );
|
||||
ts.tv_sec += INCNX_TIMEOUT;
|
||||
|
||||
/* Receive the first Diameter message on the connection -- cleanup in case of timeout */
|
||||
CHECK_FCT_DO( fd_cnx_receive(c, &ts, &rcv_data.buffer, &rcv_data.length),
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
switch (__ret__) {
|
||||
case ETIMEDOUT:
|
||||
snprintf(buf, sizeof(buf), "Client '%s' did not send CER within %ds, connection aborted.", fd_cnx_getid(c), INCNX_TIMEOUT);
|
||||
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
|
||||
break;
|
||||
|
||||
case ENOTCONN:
|
||||
snprintf(buf, sizeof(buf), "Connection from '%s' in error before CER was received.", fd_cnx_getid(c));
|
||||
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
snprintf(buf, sizeof(buf), "Connection from '%s': unspecified error, connection aborted.", fd_cnx_getid(c));
|
||||
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
|
||||
}
|
||||
goto cleanup;
|
||||
} );
|
||||
|
||||
TRACE_DEBUG(FULL, "Received %zdb from new client '%s'", rcv_data.length, fd_cnx_getid(c));
|
||||
|
||||
pmdl = fd_msg_pmdl_get_inbuf(rcv_data.buffer, rcv_data.length);
|
||||
|
||||
/* Try parsing this message */
|
||||
CHECK_FCT_DO( fd_msg_parse_buffer( &rcv_data.buffer, rcv_data.length, &msg ),
|
||||
{ /* Parsing failed */
|
||||
fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, NULL, NULL, &rcv_data, pmdl );
|
||||
goto cleanup;
|
||||
} );
|
||||
|
||||
/* Log incoming message */
|
||||
fd_hook_associate(msg, pmdl);
|
||||
fd_hook_call(HOOK_MESSAGE_RECEIVED, msg, NULL, fd_cnx_getid(c), fd_msg_pmdl_get(msg));
|
||||
|
||||
/* We expect a CER, it must parse with our dictionary and rules */
|
||||
CHECK_FCT_DO( fd_msg_parse_rules( msg, fd_g_config->cnf_dict, &pei ),
|
||||
{ /* Parsing failed -- trace details */
|
||||
char buf[1024];
|
||||
|
||||
fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msg, NULL, pei.pei_message ?: pei.pei_errcode, fd_msg_pmdl_get(msg));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Error parsing CER from '%s', connection aborted.", fd_cnx_getid(c));
|
||||
fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
|
||||
|
||||
goto cleanup;
|
||||
} );
|
||||
|
||||
/* Now check we received a CER */
|
||||
CHECK_FCT_DO( fd_msg_hdr ( msg, &hdr ), { fatal = 1; goto cleanup; } );
|
||||
CHECK_PARAMS_DO( (hdr->msg_appl == 0) && (hdr->msg_flags & CMD_FLAG_REQUEST) && (hdr->msg_code == CC_CAPABILITIES_EXCHANGE),
|
||||
{ /* Parsing failed -- trace details */
|
||||
char buf[1024];
|
||||
snprintf(buf, sizeof(buf), "Expected CER from '%s', received a different message, connection aborted.", fd_cnx_getid(c));
|
||||
fd_hook_call(HOOK_PEER_CONNECT_FAILED, msg, NULL, buf, NULL);
|
||||
goto cleanup;
|
||||
} );
|
||||
|
||||
/* Finally, pass the information to the peers module which will handle it in a separate thread */
|
||||
pthread_cleanup_push((void *)fd_cnx_destroy, c);
|
||||
pthread_cleanup_push((void *)fd_msg_free, msg);
|
||||
CHECK_FCT_DO( fd_peer_handle_newCER( &msg, &c ), );
|
||||
pthread_cleanup_pop(0);
|
||||
pthread_cleanup_pop(0);
|
||||
|
||||
cleanup:
|
||||
/* Cleanup the parsed message if any */
|
||||
if (msg) {
|
||||
CHECK_FCT_DO( fd_msg_free(msg), /* continue */);
|
||||
msg = NULL;
|
||||
}
|
||||
|
||||
/* Close the connection if needed */
|
||||
if (c != NULL) {
|
||||
fd_cnx_destroy(c);
|
||||
c = NULL;
|
||||
}
|
||||
|
||||
/* Cleanup the received buffer if any */
|
||||
free(rcv_data.buffer);
|
||||
|
||||
|
||||
if (!fatal)
|
||||
goto next_client;
|
||||
|
||||
LOG_E("Worker thread exiting.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The thread managing a server */
|
||||
static void * serv_th(void * arg)
|
||||
{
|
||||
struct server *s = (struct server *)arg;
|
||||
|
||||
CHECK_PARAMS_DO(s, goto error);
|
||||
fd_log_threadname ( fd_cnx_getid(s->conn) );
|
||||
|
||||
set_status(s, RUNNING);
|
||||
|
||||
/* Accept incoming connections */
|
||||
CHECK_FCT_DO( fd_cnx_serv_listen(s->conn), goto error );
|
||||
|
||||
do {
|
||||
struct cnxctx * conn = NULL;
|
||||
|
||||
/* Wait for a new client or cancel */
|
||||
CHECK_MALLOC_DO( conn = fd_cnx_serv_accept(s->conn), break );
|
||||
|
||||
/* Store this connection in the fifo for processing by the worker pool. Will block when the fifo is full */
|
||||
pthread_cleanup_push((void *)fd_cnx_destroy, conn);
|
||||
CHECK_FCT_DO( fd_fifo_post( s->pending, &conn ), break );
|
||||
pthread_cleanup_pop(0);
|
||||
|
||||
} while (1);
|
||||
error:
|
||||
if (s)
|
||||
set_status(s, TERMINATED);
|
||||
|
||||
/* Send error signal to the core */
|
||||
LOG_F( "An error occurred in server module! Thread is terminating...");
|
||||
CHECK_FCT_DO(fd_core_shutdown(), );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Create a new server structure */
|
||||
static struct server * new_serv( int proto, int secur )
|
||||
{
|
||||
struct server * new;
|
||||
int i;
|
||||
|
||||
/* New server structure */
|
||||
CHECK_MALLOC_DO( new = malloc(sizeof(struct server)), return NULL );
|
||||
|
||||
memset(new, 0, sizeof(struct server));
|
||||
fd_list_init(&new->chain, new);
|
||||
new->proto = proto;
|
||||
new->secur = secur;
|
||||
|
||||
CHECK_FCT_DO( fd_fifo_new(&new->pending, 5), return NULL);
|
||||
CHECK_MALLOC_DO( new->workers = calloc( fd_g_config->cnf_thr_srv, sizeof(struct pool_workers) ), return NULL );
|
||||
|
||||
for (i = 0; i < fd_g_config->cnf_thr_srv; i++) {
|
||||
/* Create the pool */
|
||||
new->workers[i].s = new;
|
||||
new->workers[i].id = i;
|
||||
CHECK_POSIX_DO( pthread_create( &new->workers[i].worker, NULL, client_worker, &new->workers[i]), return NULL );
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
/* Start all the servers */
|
||||
int fd_servers_start()
|
||||
{
|
||||
struct server * s;
|
||||
|
||||
int empty_conf_ep = FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints);
|
||||
|
||||
/* SCTP */
|
||||
if (!fd_g_config->cnf_flags.no_sctp) {
|
||||
#ifdef DISABLE_SCTP
|
||||
ASSERT(0);
|
||||
#else /* DISABLE_SCTP */
|
||||
|
||||
/* Create the server on unsecure port */
|
||||
if (fd_g_config->cnf_port) {
|
||||
CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 0) );
|
||||
CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port, empty_conf_ep ? NULL : &fd_g_config->cnf_endpoints) );
|
||||
fd_list_insert_before( &FD_SERVERS, &s->chain );
|
||||
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
|
||||
}
|
||||
|
||||
/* Create the server on secure port */
|
||||
if (fd_g_config->cnf_port_tls) {
|
||||
CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 2 /* Change when DTLS is introduced */) );
|
||||
CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port_tls, empty_conf_ep ? NULL : &fd_g_config->cnf_endpoints) );
|
||||
fd_list_insert_before( &FD_SERVERS, &s->chain );
|
||||
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
|
||||
}
|
||||
|
||||
/* Create the other server on 3436 secure port */
|
||||
/*if (fd_g_config->cnf_port_3436) {
|
||||
CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 2) );
|
||||
CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port_3436, empty_conf_ep ? NULL : &fd_g_config->cnf_endpoints) );
|
||||
fd_list_insert_before( &FD_SERVERS, &s->chain );
|
||||
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
|
||||
}*/
|
||||
|
||||
#endif /* DISABLE_SCTP */
|
||||
}
|
||||
|
||||
/* TCP */
|
||||
if (!fd_g_config->cnf_flags.no_tcp) {
|
||||
|
||||
if (empty_conf_ep) {
|
||||
/* Bind TCP servers on [0.0.0.0] */
|
||||
if (!fd_g_config->cnf_flags.no_ip4) {
|
||||
|
||||
if (fd_g_config->cnf_port) {
|
||||
CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
|
||||
CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET, NULL) );
|
||||
fd_list_insert_before( &FD_SERVERS, &s->chain );
|
||||
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
|
||||
}
|
||||
|
||||
if (fd_g_config->cnf_port_tls) {
|
||||
CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
|
||||
CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET, NULL) );
|
||||
fd_list_insert_before( &FD_SERVERS, &s->chain );
|
||||
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
|
||||
}
|
||||
}
|
||||
|
||||
/* Bind TCP servers on [::] */
|
||||
if (!fd_g_config->cnf_flags.no_ip6) {
|
||||
|
||||
if (fd_g_config->cnf_port) {
|
||||
CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
|
||||
CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET6, NULL) );
|
||||
fd_list_insert_before( &FD_SERVERS, &s->chain );
|
||||
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
|
||||
}
|
||||
|
||||
if (fd_g_config->cnf_port_tls) {
|
||||
CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
|
||||
CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET6, NULL) );
|
||||
fd_list_insert_before( &FD_SERVERS, &s->chain );
|
||||
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Create all endpoints -- check flags */
|
||||
struct fd_list * li;
|
||||
for (li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
|
||||
struct fd_endpoint * ep = (struct fd_endpoint *)li;
|
||||
sSA * sa = (sSA *) &ep->ss;
|
||||
if (! (ep->flags & EP_FL_CONF))
|
||||
continue;
|
||||
if (fd_g_config->cnf_flags.no_ip4 && (sa->sa_family == AF_INET))
|
||||
continue;
|
||||
if (fd_g_config->cnf_flags.no_ip6 && (sa->sa_family == AF_INET6))
|
||||
continue;
|
||||
|
||||
if (fd_g_config->cnf_port) {
|
||||
CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
|
||||
CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, sa->sa_family, ep) );
|
||||
fd_list_insert_before( &FD_SERVERS, &s->chain );
|
||||
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
|
||||
}
|
||||
|
||||
if (fd_g_config->cnf_port_tls) {
|
||||
CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
|
||||
CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, sa->sa_family, ep) );
|
||||
fd_list_insert_before( &FD_SERVERS, &s->chain );
|
||||
CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now, if we had an empty list of local adresses (no address configured), try to read the real addresses from the kernel */
|
||||
if (empty_conf_ep) {
|
||||
CHECK_FCT(fd_cnx_get_local_eps(&fd_g_config->cnf_endpoints));
|
||||
if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) {
|
||||
TRACE_DEBUG(INFO, "Unable to find the address(es) of the local system. "
|
||||
"Please use \"ListenOn\" parameter in the configuration. "
|
||||
"This information is required to generate the CER/CEA messages.");
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
char * buf = NULL;
|
||||
size_t len = 0, offset = 0;
|
||||
CHECK_MALLOC_DO( fd_dump_extend( &buf, &len, &offset , "Local server address(es): "), );
|
||||
CHECK_MALLOC_DO( fd_ep_dump( &buf, &len, &offset, 0, 0, &fd_g_config->cnf_endpoints ), );
|
||||
LOG_N("%s", buf ?: "Error dumping addresses");
|
||||
free(buf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Terminate all the servers */
|
||||
int fd_servers_stop()
|
||||
{
|
||||
TRACE_ENTRY("");
|
||||
|
||||
TRACE_DEBUG(INFO, "Shutting down server sockets...");
|
||||
|
||||
/* Loop on all servers */
|
||||
while (!FD_IS_LIST_EMPTY(&FD_SERVERS)) {
|
||||
struct server * s = (struct server *)(FD_SERVERS.next);
|
||||
int i;
|
||||
struct cnxctx * c;
|
||||
|
||||
/* cancel thread */
|
||||
CHECK_FCT_DO( fd_thr_term(&s->thr), /* continue */);
|
||||
|
||||
/* destroy server connection context */
|
||||
fd_cnx_destroy(s->conn);
|
||||
|
||||
/* cancel and destroy all worker threads */
|
||||
for (i = 0; i < fd_g_config->cnf_thr_srv; i++) {
|
||||
/* Destroy worker thread */
|
||||
CHECK_FCT_DO( fd_thr_term(&s->workers[i].worker), /* continue */);
|
||||
}
|
||||
free(s->workers);
|
||||
|
||||
/* Close any pending connection */
|
||||
while ( fd_fifo_tryget( s->pending, &c ) == 0 ) {
|
||||
fd_cnx_destroy(c);
|
||||
}
|
||||
CHECK_FCT_DO( fd_fifo_del(&s->pending), );
|
||||
|
||||
/* Now destroy the server object */
|
||||
fd_list_unlink(&s->chain);
|
||||
free(s);
|
||||
}
|
||||
|
||||
/* We're done! */
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 "fdcore-internal.h"
|
||||
#include "cnxctx.h"
|
||||
|
||||
#include <netinet/tcp.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
/* Set the socket options for TCP sockets, before bind is called */
|
||||
static int fd_tcp_setsockopt(int family, int sk)
|
||||
{
|
||||
int ret = 0;
|
||||
int opt;
|
||||
|
||||
/* Clear the NODELAY option in case it was set, as requested by rfc3539#section-3.2 */
|
||||
/* Note that this is supposed to be the default, so we could probably remove this call ... */
|
||||
opt = 0;
|
||||
ret = setsockopt(sk, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
|
||||
if (ret != 0) {
|
||||
ret = errno;
|
||||
TRACE_DEBUG(INFO, "Unable to set the socket TCP_NODELAY option: %s", strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Under Linux, we may also set the TCP_CONGESTION option to one of the following strings:
|
||||
- reno (default)
|
||||
- bic
|
||||
- cubic
|
||||
- highspeed
|
||||
- htcp
|
||||
- hybla
|
||||
- illinois
|
||||
- lp
|
||||
- scalable
|
||||
- vegas
|
||||
- veno
|
||||
- westwood
|
||||
- yeah
|
||||
*/
|
||||
|
||||
/* In case of v6 address, force the v6only option, we use a different socket for v4 */
|
||||
#ifdef IPV6_V6ONLY
|
||||
if (family == AF_INET6) {
|
||||
opt = 1;
|
||||
CHECK_SYS(setsockopt(sk, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)));
|
||||
}
|
||||
#endif /* IPV6_V6ONLY */
|
||||
|
||||
{
|
||||
opt = 1;
|
||||
CHECK_SYS( setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) );
|
||||
}
|
||||
|
||||
/* There are also others sockopt that can be set, but nothing useful for us AFAICT */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create a socket server and bind it */
|
||||
int fd_tcp_create_bind_server( int * sock, sSA * sa, socklen_t salen )
|
||||
{
|
||||
TRACE_ENTRY("%p %p %d", sock, sa, salen);
|
||||
|
||||
CHECK_PARAMS( sock && sa );
|
||||
|
||||
/* Create the socket */
|
||||
CHECK_SYS( *sock = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP) );
|
||||
|
||||
/* Set the socket options */
|
||||
CHECK_FCT( fd_tcp_setsockopt(sa->sa_family, *sock) );
|
||||
|
||||
/* Bind the socket */
|
||||
CHECK_SYS( bind( *sock, sa, salen ) );
|
||||
|
||||
/* We're done */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allow clients connections on server sockets */
|
||||
int fd_tcp_listen( int sock )
|
||||
{
|
||||
TRACE_ENTRY("%d", sock);
|
||||
CHECK_SYS( listen(sock, 5) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create a client socket and connect to remote server */
|
||||
int fd_tcp_client( int *sock, sSA * sa, socklen_t salen )
|
||||
{
|
||||
int ret = 0;
|
||||
int s;
|
||||
|
||||
TRACE_ENTRY("%p %p %d", sock, sa, salen);
|
||||
CHECK_PARAMS( sock && (*sock <= 0) && sa && salen );
|
||||
|
||||
/* Create the socket */
|
||||
CHECK_SYS( s = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP) );
|
||||
|
||||
/* Set the socket options */
|
||||
CHECK_FCT( fd_tcp_setsockopt(sa->sa_family, s) );
|
||||
|
||||
/* Cleanup if we are cancelled */
|
||||
pthread_cleanup_push(fd_cleanup_socket, &s);
|
||||
|
||||
/* Try connecting to the remote address */
|
||||
ret = connect(s, sa, salen);
|
||||
|
||||
pthread_cleanup_pop(0);
|
||||
|
||||
if (ret < 0) {
|
||||
ret = errno;
|
||||
LOG_A( "connect returned an error: %s", strerror(ret));
|
||||
CHECK_SYS_DO( close(s), /* continue */ );
|
||||
*sock = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Done! */
|
||||
*sock = s;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get the remote name of a TCP socket */
|
||||
int fd_tcp_get_remote_ep(int sock, sSS * ss, socklen_t *sl)
|
||||
{
|
||||
TRACE_ENTRY("%d %p %p", sock, ss, sl);
|
||||
CHECK_PARAMS( ss && sl );
|
||||
|
||||
*sl = sizeof(sSS);
|
||||
CHECK_SYS(getpeername(sock, (sSA *)ss, sl));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, 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 <fdcore-internal.h>
|
||||
#include "version.h"
|
||||
|
||||
#ifdef FD_PROJECT_VERSION_HG
|
||||
# define FD_LIBFDCORE_VERSION \
|
||||
_stringize(FD_PROJECT_VERSION_MAJOR) "." _stringize(FD_PROJECT_VERSION_MINOR) "." _stringize(FD_PROJECT_VERSION_REV) "-" FD_PROJECT_VERSION_HG_VAL
|
||||
#else
|
||||
# define FD_LIBFDCORE_VERSION \
|
||||
_stringize(FD_PROJECT_VERSION_MAJOR) "." _stringize(FD_PROJECT_VERSION_MINOR) "." _stringize(FD_PROJECT_VERSION_REV)
|
||||
#endif
|
||||
|
||||
const char fd_core_version[] = FD_LIBFDCORE_VERSION;
|
||||
@@ -0,0 +1,3 @@
|
||||
/* Following variable is expended at build time based on the result of "hg id" command */
|
||||
#define FD_PROJECT_VERSION_HG
|
||||
#define FD_PROJECT_VERSION_HG_VAL "1304(5a505af0bba0)"
|
||||
Reference in new issue
Block a user