Navigation


Changes in / [20:277ec00d793e:30:bca243c65b56] in freeDiameter


Ignore:
Files:
5 added
23 edited

Legend:

Unmodified
Added
Removed
  • contrib/ca_script/Makefile

    r19 r29  
    3232O = WIDE
    3333OU = "AAA WG"
     34
     35#Default lifetime
     36DAYS = 365
    3437
    3538#Values for the CA
     
    130133        @openssl ca $(CONFIG) -in $(DIR)/clients/csr/$(name).csr \
    131134                -out $(DIR)/clients/certs/$(name).cert \
     135                -days $(DAYS) \
    132136                -batch
    133137        @ln -s $(DIR)/clients/certs/$(name).cert $(DIR)/certs/`openssl x509 -noout -hash < $(DIR)/clients/certs/$(name).cert`.0
  • doc/freediameter.conf.sample

    r20 r24  
    6868#ListenOn = "202.249.37.5";
    6969#ListenOn = "2001:200:903:2::202:1";
     70#ListenOn = "fe80::21c:5ff:fe98:7d62%eth0";
    7071
    7172##############################################################
     
    184185#  No_TLS;       # assume transparent security instead of TLS
    185186#  Port = 3868;  # The port to connect to
    186 #  SCTP_streams = 30;
    187187#  TcTimer = 30;
    188188#  TwTimer = 30;
    189189#  ConnectTo = "202.249.37.5";
    190190#  ConnectTo = "2001:200:903:2::202:1";
     191#  TLS_Prio = "NORMAL";
    191192# Examples:
    192193#ConnectPeer = "aaa.wide.ad.jp";
     
    201202SecPort = 3867;
    202203TLS_old_method;
    203 No_IP;
    204 Prefer_TCP;
    205204SCTP_streams = 50;
    206 ListenOn = "202.249.37.5";
    207 ListenOn = "2001:200:903:2::202:1";
    208205TcTimer = 60;
    209206TwTimer = 6;
     207#ListenOn = "133.243.146.201";
     208#ListenOn = "fe80::21d:9ff:fe89:7d68%eth0";
    210209NoRelay;
    211210LoadExtension = "extensions/dbg_monitor.fdx";
     
    213212LoadExtension = "extensions/dict_eap.fdx";
    214213ConnectPeer = "jules.nautilus6.org" ;
    215 ConnectPeer = "aaa.nautilus6.org" { No_TLS; No_IP; No_TCP; SCTP_streams = 60; } ;
    216 TLS_Cred = "/etc/openssl-ca/clients/certs/fdtest.cert" , "/etc/openssl-ca/clients/privkeys/fdtest.key.pem";
     214ConnectPeer = "aaa.nautilus6.org" { No_TLS; No_IP; } ;
     215TLS_Cred = "/etc/openssl-ca/clients/certs/test.cert" , "/etc/openssl-ca/clients/privkeys/test.key.pem";
    217216TLS_CA = "/etc/openssl-ca/public-www/cacert.pem";
    218217# TLS_CRL = "/etc/openssl-ca/public-www/crl.pem";
  • extensions/dbg_monitor/monitor.c

    r11 r25  
    4949static void got_sig(int signal)
    5050{
    51         CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_DUMP_DICT, NULL), /* continue */);
    52         CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_DUMP_CONFIG, NULL), /* continue */);
    53         CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_DUMP_EXT, NULL), /* continue */);
     51        CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_DUMP_DICT, 0, NULL), /* continue */);
     52        CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_DUMP_CONFIG, 0, NULL), /* continue */);
     53        CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_DUMP_EXT, 0, NULL), /* continue */);
    5454}
    5555/* Thread to display periodical debug information */
     
    7777                #endif /* DEBUG */
    7878                TRACE_DEBUG(NONE, "Monitor information");
    79                 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_DUMP_QUEUES, NULL), /* continue */);
    80                 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_DUMP_PEERS, NULL), /* continue */);
     79                CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_DUMP_QUEUES, 0, NULL), /* continue */);
     80                CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_DUMP_SERV, 0, NULL), /* continue */);
     81                CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_DUMP_PEERS, 0, NULL), /* continue */);
    8182                pthread_testcancel();
    8283        }
  • freeDiameter/CMakeLists.txt

    r20 r25  
    1010SET(FD_COMMON_SRC
    1111        fD.h
     12        cnxctx.h
    1213        config.c
    1314        cnxctx.c
    1415        dispatch.c
     16        endpoints.c
    1517        extensions.c
    1618        dict_base_proto.c
     
    2123        p_psm.c
    2224        server.c
     25        tcp.c
    2326        )
    2427
    2528IF(NOT DISABLE_SCTP)
    26         SET(FD_COMMON_SRC ${FD_COMMON_SRC} sctp.c)
     29        SET(FD_COMMON_SRC ${FD_COMMON_SRC} sctp.c sctps.c)
    2730ENDIF(NOT DISABLE_SCTP)
    2831
  • freeDiameter/cnxctx.c

    r20 r30  
    3535
    3636#include "fD.h"
    37 
    38 /* Initialize a connection context */
    39 struct cnxctx * fd_cnx_init(int sock, int proto)
     37#include "cnxctx.h"
     38
     39/* The maximum size of Diameter message we accept to receive (<= 2^24) to avoid too big mallocs in case of trashed headers */
     40#ifndef DIAMETER_MSG_SIZE_MAX
     41#define DIAMETER_MSG_SIZE_MAX   65535   /* in bytes */
     42#endif /* DIAMETER_MSG_SIZE_MAX */
     43
     44/* Connections contexts (cnxctx) in freeDiameter are wrappers around the sockets and TLS operations .
     45 * They are used to hide the details of the processing to the higher layers of the daemon.
     46 * They are always oriented on connections (TCP or SCTP), connectionless modes (UDP or SCTP) are not supported.
     47 */
     48
     49/* Note: this file could be moved to libfreeDiameter instead, but since it uses gnuTLS we prefer to keep it in the daemon */
     50
     51/* Lifetime of a cnxctx object:
     52 * 1) Creation
     53 *    a) a server socket:
     54 *       - create the object with fd_cnx_serv_tcp or fd_cnx_serv_sctp
     55 *       - start listening incoming connections: fd_cnx_serv_listen
     56 *       - accept new clients with fd_cnx_serv_accept.
     57 *    b) a client socket:
     58 *       - connect to a remote server with fd_cnx_cli_connect
     59 *
     60 * 2) Initialization
     61 *    - if TLS is started first, call fd_cnx_handshake
     62 *    - otherwise to receive clear messages, call fd_cnx_start_clear. fd_cnx_handshake can be called later.
     63 *
     64 * 3) Usage
     65 *    - fd_cnx_receive, fd_cnx_send : exchange messages on this connection (send is synchronous, receive is not, but blocking).
     66 *    - fd_cnx_recv_setaltfifo : when a message is received, the event is sent to an external fifo list. fd_cnx_receive does not work when the alt_fifo is set.
     67 *    - fd_cnx_getid : retrieve a descriptive string for the connection (for debug)
     68 *    - fd_cnx_getremoteid : identification of the remote peer (IP address or fqdn)
     69 *    - fd_cnx_getcred : get the remote peer TLS credentials, after handshake
     70 *    - fd_cnx_getendpoints : get the endpoints (IP) of the connection
     71 *
     72 * 4) End
     73 *    - fd_cnx_destroy
     74 */
     75
     76
     77/*******************************************/
     78/*     Creation of a connection object     */
     79/*******************************************/
     80
     81/* Initialize a context structure */
     82static struct cnxctx * fd_cnx_init(int full)
    4083{
    4184        struct cnxctx * conn = NULL;
    42        
    43         TRACE_ENTRY("%d %d", sock, proto);
    44         CHECK_PARAMS_DO( (proto == IPPROTO_TCP) || (proto == IPPROTO_SCTP), return NULL);
    45        
     85
     86        TRACE_ENTRY("%d", full);
     87
    4688        CHECK_MALLOC_DO( conn = malloc(sizeof(struct cnxctx)), return NULL );
    4789        memset(conn, 0, sizeof(struct cnxctx));
    48        
    49         conn->cc_socket = sock;
    50         conn->cc_proto  = proto;
    51        
    52         fd_list_init(&conn->cc_ep_remote, conn);
    53         fd_list_init(&conn->cc_ep_local, conn);
    54        
    55         if (proto == IPPROTO_SCTP) {
     90
     91        if (full) {
     92                CHECK_FCT_DO( fd_fifo_new ( &conn->cc_incoming ), return NULL );
     93        }
     94
     95        return conn;
     96}
     97
     98/* Create and bind a server socket to the given endpoint and port */
     99struct cnxctx * fd_cnx_serv_tcp(uint16_t port, int family, struct fd_endpoint * ep)
     100{
     101        struct cnxctx * cnx = NULL;
     102        sSS dummy;
     103        sSA * sa = (sSA *) &dummy;
     104
     105        TRACE_ENTRY("%hu %d %p", port, family, ep);
     106
     107        CHECK_PARAMS_DO( port, return NULL );
     108        CHECK_PARAMS_DO( ep || family, return NULL );
     109        CHECK_PARAMS_DO( (! family) || (family == AF_INET) || (family == AF_INET6), return NULL );
     110        CHECK_PARAMS_DO( (! ep) || (!family) || (ep->ss.ss_family == family), return NULL );
     111
     112        /* The connection object */
     113        CHECK_MALLOC_DO( cnx = fd_cnx_init(0), return NULL );
     114
     115        /* Prepare the socket address information */
     116        if (ep) {
     117                memcpy(sa, &ep->ss, sizeof(sSS));
     118        } else {
     119                memset(&dummy, 0, sizeof(dummy));
     120                sa->sa_family = family;
     121        }
     122        if (sa->sa_family == AF_INET) {
     123                ((sSA4 *)sa)->sin_port = htons(port);
     124        } else {
     125                ((sSA6 *)sa)->sin6_port = htons(port);
     126        }
     127
     128        /* Create the socket */
     129        CHECK_FCT_DO( fd_tcp_create_bind_server( &cnx->cc_socket, sa, sizeof(sSS) ), goto error );
     130
     131        /* Generate the name for the connection object */
     132        {
     133                char addrbuf[INET6_ADDRSTRLEN];
     134                int  rc;
     135                rc = getnameinfo(sa, sizeof(sSS), addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
     136                if (rc)
     137                        snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
     138                snprintf(cnx->cc_id, sizeof(cnx->cc_id), "Srv TCP [%s]:%hu (%d)", addrbuf, port, cnx->cc_socket);
     139        }
     140
     141        cnx->cc_proto = IPPROTO_TCP;
     142
     143        return cnx;
     144
     145error:
     146        fd_cnx_destroy(cnx);
     147        return NULL;
     148}
     149
     150/* Same function for SCTP, with a list of local endpoints to bind to */
     151struct cnxctx * fd_cnx_serv_sctp(uint16_t port, struct fd_list * ep_list)
     152{
     153#ifdef DISABLE_SCTP
     154        TRACE_DEBUG(INFO, "This function should never been called when SCTP is disabled...");
     155        ASSERT(0);
     156        CHECK_FCT_DO( ENOTSUP, return NULL);
     157#else /* DISABLE_SCTP */
     158        struct cnxctx * cnx = NULL;
     159        sSS dummy;
     160        sSA * sa = (sSA *) &dummy;
     161
     162        TRACE_ENTRY("%hu %p", port, ep_list);
     163
     164        CHECK_PARAMS_DO( port, return NULL );
     165
     166        /* The connection object */
     167        CHECK_MALLOC_DO( cnx = fd_cnx_init(0), return NULL );
     168
     169        /* Create the socket */
     170        CHECK_FCT_DO( fd_sctp_create_bind_server( &cnx->cc_socket, ep_list, port ), goto error );
     171
     172        /* Generate the name for the connection object */
     173        snprintf(cnx->cc_id, sizeof(cnx->cc_id), "Srv SCTP :%hu (%d)", port, cnx->cc_socket);
     174
     175        cnx->cc_proto = IPPROTO_SCTP;
     176
     177        return cnx;
     178
     179error:
     180        fd_cnx_destroy(cnx);
     181        return NULL;
     182#endif /* DISABLE_SCTP */
     183}
     184
     185/* Allow clients to connect on the server socket */
     186int fd_cnx_serv_listen(struct cnxctx * conn)
     187{
     188        CHECK_PARAMS( conn );
     189
     190        switch (conn->cc_proto) {
     191                case IPPROTO_TCP:
     192                        CHECK_FCT(fd_tcp_listen(conn->cc_socket));
     193                        break;
     194
    56195#ifndef DISABLE_SCTP
    57                 CHECK_FCT_DO( fd_sctp_get_str_info( sock, &conn->cc_sctp_para.str_in, &conn->cc_sctp_para.str_out ),
    58                                 { free(conn); return NULL; } );
    59                 conn->cc_sctp_para.pairs = (conn->cc_sctp_para.str_out < conn->cc_sctp_para.str_in) ? conn->cc_sctp_para.str_out : conn->cc_sctp_para.str_in;
     196                case IPPROTO_SCTP:
     197                        CHECK_FCT(fd_sctp_listen(conn->cc_socket));
     198                        break;
     199#endif /* DISABLE_SCTP */
     200
     201                default:
     202                        CHECK_PARAMS(0);
     203        }
     204
     205        return 0;
     206}
     207
     208/* Accept a client (blocking until a new client connects) -- cancelable */
     209struct cnxctx * fd_cnx_serv_accept(struct cnxctx * serv)
     210{
     211        struct cnxctx * cli = NULL;
     212        sSS ss;
     213        socklen_t ss_len = sizeof(ss);
     214        int cli_sock = 0;
     215        struct fd_endpoint * ep;
     216
     217        TRACE_ENTRY("%p", serv);
     218        CHECK_PARAMS_DO(serv, return NULL);
     219       
     220        /* Accept the new connection -- this is blocking until new client enters or cancellation */
     221        CHECK_SYS_DO( cli_sock = accept(serv->cc_socket, (sSA *)&ss, &ss_len), return NULL );
     222       
     223        if (TRACE_BOOL(INFO)) {
     224                fd_log_debug("%s : accepted new client [", fd_cnx_getid(serv));
     225                sSA_DUMP_NODE( &ss, NI_NUMERICHOST );
     226                fd_log_debug("].\n");
     227        }
     228       
     229        CHECK_MALLOC_DO( cli = fd_cnx_init(1), { shutdown(cli_sock, SHUT_RDWR); return NULL; } );
     230        cli->cc_socket = cli_sock;
     231        cli->cc_proto = serv->cc_proto;
     232       
     233        /* Generate the name for the connection object */
     234        {
     235                char addrbuf[INET6_ADDRSTRLEN];
     236                char portbuf[10];
     237                int  rc;
     238               
     239                /* Numeric values for debug */
     240                rc = getnameinfo((sSA *)&ss, sizeof(sSS), addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
     241                if (rc) {
     242                        snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
     243                        portbuf[0] = '\0';
     244                }
     245               
     246                snprintf(cli->cc_id, sizeof(cli->cc_id), "Incoming %s [%s]:%s (%d) @ serv (%d)",
     247                                IPPROTO_NAME(cli->cc_proto),
     248                                addrbuf, portbuf,
     249                                cli->cc_socket, serv->cc_socket);
     250               
     251                /* Name for log messages */
     252                rc = getnameinfo((sSA *)&ss, sizeof(sSS), cli->cc_remid, sizeof(cli->cc_remid), NULL, 0, 0);
     253                if (rc)
     254                        snprintf(cli->cc_remid, sizeof(cli->cc_remid), "[err:%s]", gai_strerror(rc));
     255        }
     256
     257#ifndef DISABLE_SCTP
     258        /* SCTP-specific handlings */
     259        if (cli->cc_proto == IPPROTO_SCTP) {
     260                /* Retrieve the number of streams */
     261                CHECK_FCT_DO( fd_sctp_get_str_info( cli->cc_socket, &cli->cc_sctp_para.str_in, &cli->cc_sctp_para.str_out, NULL ), goto error );
     262                if (cli->cc_sctp_para.str_out > cli->cc_sctp_para.str_in)
     263                        cli->cc_sctp_para.pairs = cli->cc_sctp_para.str_out;
     264                else
     265                        cli->cc_sctp_para.pairs = cli->cc_sctp_para.str_in;
     266        }
     267#endif /* DISABLE_SCTP */
     268
     269        return cli;
     270error:
     271        fd_cnx_destroy(cli);
     272        return NULL;
     273}
     274
     275/* Client side: connect to a remote server -- cancelable */
     276struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa /* contains the port already */, socklen_t addrlen)
     277{
     278        int sock;
     279        struct cnxctx * cnx = NULL;
     280       
     281        TRACE_ENTRY("%p %d", sa, addrlen);
     282        CHECK_PARAMS_DO( sa && addrlen, return NULL );
     283       
     284        /* Create the socket and connect, which can take some time and/or fail */
     285        CHECK_FCT_DO( fd_tcp_client( &sock, sa, addrlen ), return NULL );
     286       
     287        if (TRACE_BOOL(INFO)) {
     288                fd_log_debug("Connection established to server '");
     289                sSA_DUMP_NODE_SERV( sa, NI_NUMERICSERV);
     290                fd_log_debug("' (TCP:%d).\n", sock);
     291        }
     292       
     293        /* Once the socket is created successfuly, prepare the remaining of the cnx */
     294        CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); return NULL; } );
     295       
     296        cnx->cc_socket = sock;
     297        cnx->cc_proto  = IPPROTO_TCP;
     298       
     299        /* Generate the names for the object */
     300        {
     301                char addrbuf[INET6_ADDRSTRLEN];
     302                char portbuf[10];
     303                int  rc;
     304               
     305                /* Numeric values for debug */
     306                rc = getnameinfo(sa, addrlen, addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
     307                if (rc) {
     308                        snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
     309                        portbuf[0] = '\0';
     310                }
     311               
     312                snprintf(cnx->cc_id, sizeof(cnx->cc_id), "Client of TCP server [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket);
     313               
     314                /* Name for log messages */
     315                rc = getnameinfo(sa, addrlen, cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0);
     316                if (rc)
     317                        snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc));
     318        }
     319       
     320        return cnx;
     321
     322error:
     323        fd_cnx_destroy(cnx);
     324        return NULL;
     325}
     326
     327/* Same for SCTP, accepts a list of remote addresses to connect to (see sctp_connectx for how they are used) */
     328struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list)
     329{
     330#ifdef DISABLE_SCTP
     331        TRACE_DEBUG(INFO, "This function should never been called when SCTP is disabled...");
     332        ASSERT(0);
     333        CHECK_FCT_DO( ENOTSUP, return NULL);
    60334#else /* DISABLE_SCTP */
    61                 ASSERT(0);
     335        int sock;
     336        struct cnxctx * cnx = NULL;
     337        sSS primary;
     338       
     339        TRACE_ENTRY("%p", list);
     340        CHECK_PARAMS_DO( list && !FD_IS_LIST_EMPTY(list), return NULL );
     341       
     342        CHECK_FCT_DO( fd_sctp_client( &sock, no_ip6, port, list ), return NULL );
     343       
     344        /* Once the socket is created successfuly, prepare the remaining of the cnx */
     345        CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); return NULL; } );
     346       
     347        cnx->cc_socket = sock;
     348        cnx->cc_proto  = IPPROTO_SCTP;
     349       
     350        /* Retrieve the number of streams and primary address */
     351        CHECK_FCT_DO( fd_sctp_get_str_info( sock, &cnx->cc_sctp_para.str_in, &cnx->cc_sctp_para.str_out, &primary ), goto error );
     352        if (cnx->cc_sctp_para.str_out > cnx->cc_sctp_para.str_in)
     353                cnx->cc_sctp_para.pairs = cnx->cc_sctp_para.str_out;
     354        else
     355                cnx->cc_sctp_para.pairs = cnx->cc_sctp_para.str_in;
     356       
     357        if (TRACE_BOOL(INFO)) {
     358                fd_log_debug("Connection established to server '");
     359                sSA_DUMP_NODE_SERV( &primary, NI_NUMERICSERV);
     360                fd_log_debug("' (SCTP:%d, %d/%d streams).\n", sock, cnx->cc_sctp_para.str_in, cnx->cc_sctp_para.str_out);
     361        }
     362       
     363        /* Generate the names for the object */
     364        {
     365                char addrbuf[INET6_ADDRSTRLEN];
     366                char portbuf[10];
     367                int  rc;
     368               
     369                /* Numeric values for debug */
     370                rc = getnameinfo((sSA *)&primary, sizeof(sSS), addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
     371                if (rc) {
     372                        snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
     373                        portbuf[0] = '\0';
     374                }
     375               
     376                snprintf(cnx->cc_id, sizeof(cnx->cc_id), "Client of SCTP server [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket);
     377               
     378                /* Name for log messages */
     379                rc = getnameinfo((sSA *)&primary, sizeof(sSS), cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0);
     380                if (rc)
     381                        snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc));
     382        }
     383       
     384        return cnx;
     385
     386error:
     387        fd_cnx_destroy(cnx);
     388        return NULL;
    62389#endif /* DISABLE_SCTP */
    63         }
    64        
    65         return conn;
    66 }
    67 
    68 /* TLS handshake the connection */
    69 int fd_cnx_handshake(struct cnxctx * conn, int mode)
     390}
     391
     392/* Return a string describing the connection, for debug */
     393char * fd_cnx_getid(struct cnxctx * conn)
     394{
     395        CHECK_PARAMS_DO( conn, return "" );
     396        return conn->cc_id;
     397}
     398
     399/* Return the protocol of a connection */
     400int fd_cnx_getproto(struct cnxctx * conn)
     401{
     402        CHECK_PARAMS_DO( conn, return 0 );
     403        return conn->cc_proto;
     404}
     405
     406/* Return the TLS state of a connection */
     407int fd_cnx_getTLS(struct cnxctx * conn)
     408{
     409        CHECK_PARAMS_DO( conn, return 0 );
     410        return conn->cc_tls;
     411}
     412
     413/* Get the list of endpoints (IP addresses) of the local and remote peers on this connection */
     414int fd_cnx_getendpoints(struct cnxctx * conn, struct fd_list * local, struct fd_list * remote)
     415{
     416        TRACE_ENTRY("%p %p %p", conn, local, remote);
     417        CHECK_PARAMS(conn);
     418       
     419        if (local) {
     420                /* Retrieve the local endpoint(s) of the connection */
     421                switch (conn->cc_proto) {
     422                        case IPPROTO_TCP: {
     423                                sSS ss;
     424                                socklen_t sl;
     425                                CHECK_FCT(fd_tcp_get_local_ep(conn->cc_socket, &ss, &sl));
     426                                CHECK_FCT(fd_ep_add_merge( local, (sSA *)&ss, sl, EP_FL_LL | EP_FL_PRIMARY));
     427                        }
     428                        break;
     429
     430                        #ifndef DISABLE_SCTP
     431                        case IPPROTO_SCTP: {
     432                                CHECK_FCT(fd_sctp_get_local_ep(conn->cc_socket, local));
     433                        }
     434                        break;
     435                        #endif /* DISABLE_SCTP */
     436
     437                        default:
     438                                CHECK_PARAMS(0);
     439                }
     440        }
     441       
     442        if (remote) {
     443                /* Check we have a full connection object, not a listening socket (with no remote) */
     444                CHECK_PARAMS( conn->cc_incoming );
     445               
     446                /* Retrieve the peer endpoint(s) of the connection */
     447                switch (conn->cc_proto) {
     448                        case IPPROTO_TCP: {
     449                                sSS ss;
     450                                socklen_t sl;
     451                                CHECK_FCT(fd_tcp_get_remote_ep(conn->cc_socket, &ss, &sl));
     452                                CHECK_FCT(fd_ep_add_merge( remote, (sSA *)&ss, sl, EP_FL_LL | EP_FL_PRIMARY ));
     453                        }
     454                        break;
     455
     456                        #ifndef DISABLE_SCTP
     457                        case IPPROTO_SCTP: {
     458                                CHECK_FCT(fd_sctp_get_remote_ep(conn->cc_socket, remote));
     459                        }
     460                        break;
     461                        #endif /* DISABLE_SCTP */
     462
     463                        default:
     464                                CHECK_PARAMS(0);
     465                }
     466        }
     467
     468        return 0;
     469}
     470
     471
     472/* Get a string describing the remote peer address (ip address or fqdn) */
     473char * fd_cnx_getremoteid(struct cnxctx * conn)
     474{
     475        CHECK_PARAMS_DO( conn, return "" );
     476        return conn->cc_remid;
     477}
     478
     479
     480/**************************************/
     481/*     Use of a connection object     */
     482/**************************************/
     483
     484/* Receiver thread (TCP & noTLS) : incoming message is directly saved into the target queue */
     485static void * rcvthr_notls_tcp(void * arg)
     486{
     487        struct cnxctx * conn = arg;
     488       
     489        TRACE_ENTRY("%p", arg);
     490        CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto out);
     491       
     492        /* Set the thread name */
     493        {
     494                char buf[48];
     495                snprintf(buf, sizeof(buf), "Receiver (%d) TCP/noTLS)", conn->cc_socket);
     496                fd_log_threadname ( buf );
     497        }
     498       
     499        ASSERT( conn->cc_proto == IPPROTO_TCP );
     500        ASSERT( conn->cc_tls == 0 );
     501        ASSERT( Target_Queue(conn) );
     502       
     503        /* Receive from a TCP connection: we have to rebuild the message boundaries */
     504        do {
     505                uint8_t header[4];
     506                uint8_t * newmsg;
     507                size_t  length;
     508                ssize_t ret = 0;
     509                size_t  received = 0;
     510
     511                do {
     512                        ret = recv(conn->cc_socket, &header[received], sizeof(header) - received, 0);
     513                        if (ret <= 0) {
     514                                CHECK_SYS_DO(ret, /* continue */);
     515                                goto error; /* Stop the thread, the recipient of the event will cleanup */
     516                        }
     517
     518                        received += ret;
     519                } while (received < sizeof(header));
     520
     521                length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3];
     522
     523                /* Check the received word is a valid begining of a Diameter message */
     524                if ((header[0] != DIAMETER_VERSION)     /* defined in <libfreeDiameter.h> */
     525                   || (length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */
     526                        /* The message is suspect */
     527                        TRACE_DEBUG(INFO, "Received suspect header [ver: %d, size: %zd], assume disconnection", (int)header[0], length);
     528                        goto error; /* Stop the thread, the recipient of the event will cleanup */
     529                }
     530
     531                /* Ok, now we can really receive the data */
     532                CHECK_MALLOC_DO(  newmsg = malloc( length ), goto error );
     533                memcpy(newmsg, header, sizeof(header));
     534
     535                while (received < length) {
     536                        pthread_cleanup_push(free, newmsg); /* In case we are canceled, clean the partialy built buffer */
     537                        ret = recv(conn->cc_socket, newmsg + received, length - received, 0);
     538                        pthread_cleanup_pop(0);
     539
     540                        if (ret <= 0) {
     541                                CHECK_SYS_DO(ret, /* continue */);
     542                                free(newmsg);
     543                                goto error; /* Stop the thread, the recipient of the event will cleanup */
     544                        }
     545                        received += ret;
     546                }
     547               
     548                /* We have received a complete message, send it */
     549                CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), /* continue or destroy everything? */);
     550               
     551        } while (conn->cc_loop);
     552       
     553out:
     554        TRACE_DEBUG(FULL, "Thread terminated");
     555        return NULL;
     556error:
     557        CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_ERROR, 0, NULL), /* continue or destroy everything? */);
     558        goto out;
     559}
     560
     561#ifndef DISABLE_SCTP
     562/* Receiver thread (SCTP & noTLS) : incoming message is directly saved into cc_incoming, no need to care for the stream ID */
     563static void * rcvthr_notls_sctp(void * arg)
     564{
     565        struct cnxctx * conn = arg;
     566        uint8_t * buf;
     567        size_t    bufsz;
     568        int       event;
     569       
     570        TRACE_ENTRY("%p", arg);
     571        CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto out);
     572       
     573        /* Set the thread name */
     574        {
     575                char buf[48];
     576                snprintf(buf, sizeof(buf), "Receiver (%d) SCTP/noTLS)", conn->cc_socket);
     577                fd_log_threadname ( buf );
     578        }
     579       
     580        ASSERT( conn->cc_proto == IPPROTO_SCTP );
     581        ASSERT( conn->cc_tls == 0 );
     582        ASSERT( Target_Queue(conn) );
     583       
     584        do {
     585                CHECK_FCT_DO( fd_sctp_recvmeta(conn->cc_socket, NULL, &buf, &bufsz, &event), goto error );
     586                if (event == FDEVP_CNX_ERROR) {
     587                        goto error;
     588                }
     589               
     590                CHECK_FCT_DO( fd_event_send( Target_Queue(conn), event, bufsz, buf), goto error );
     591               
     592        } while (conn->cc_loop);
     593       
     594out:
     595        TRACE_DEBUG(FULL, "Thread terminated");
     596        return NULL;
     597error:
     598        CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_ERROR, 0, NULL), /* continue or destroy everything? */);
     599        goto out;
     600}
     601#endif /* DISABLE_SCTP */
     602
     603/* Returns 0 on error, received data size otherwise (always >= 0) */
     604static ssize_t fd_tls_recv_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz)
     605{
     606        ssize_t ret;
     607again: 
     608        CHECK_GNUTLS_DO( ret = gnutls_record_recv(session, data, sz),
     609                {
     610                        switch (ret) {
     611                                case GNUTLS_E_REHANDSHAKE:
     612                                        CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
     613                                                {
     614                                                        if (TRACE_BOOL(INFO)) {
     615                                                                fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
     616                                                        }
     617                                                        ret = 0;
     618                                                        goto end;
     619                                                } );
     620
     621                                case GNUTLS_E_AGAIN:
     622                                case GNUTLS_E_INTERRUPTED:
     623                                        goto again;
     624
     625                                default:
     626                                        TRACE_DEBUG(INFO, "This TLS error is not handled, assume unrecoverable error");
     627                                        ret = 0;
     628                        }
     629                } );
     630end:   
     631        return ret;
     632}
     633
     634/* The function that receives TLS data and re-builds a Diameter message -- it exits only on error or cancelation */
     635int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session)
     636{
     637        /* No guarantee that GnuTLS preserves the message boundaries, so we re-build it as in TCP */
     638        do {
     639                uint8_t header[4];
     640                uint8_t * newmsg;
     641                size_t  length;
     642                ssize_t ret = 0;
     643                size_t  received = 0;
     644
     645                do {
     646                        ret = fd_tls_recv_handle_error(conn, session, &header[received], sizeof(header) - received);
     647                        if (ret == 0) {
     648                                /* The connection is closed */
     649                                goto out;
     650                        }
     651                        received += ret;
     652                } while (received < sizeof(header));
     653
     654                length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3];
     655
     656                /* Check the received word is a valid beginning of a Diameter message */
     657                if ((header[0] != DIAMETER_VERSION)     /* defined in <libfreeDiameter.h> */
     658                   || (length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */
     659                        /* The message is suspect */
     660                        TRACE_DEBUG(INFO, "Received suspect header [ver: %d, size: %zd], assume disconnection", (int)header[0], length);
     661                        goto out;
     662                }
     663
     664                /* Ok, now we can really receive the data */
     665                CHECK_MALLOC(  newmsg = malloc( length ) );
     666                memcpy(newmsg, header, sizeof(header));
     667
     668                while (received < length) {
     669                        pthread_cleanup_push(free, newmsg); /* In case we are canceled, clean the partialy built buffer */
     670                        ret = fd_tls_recv_handle_error(conn, session, newmsg + received, length - received);
     671                        pthread_cleanup_pop(0);
     672
     673                        if (ret == 0) {
     674                                free(newmsg);
     675                                goto out; /* Stop the thread, the recipient of the event will cleanup */
     676                        }
     677                        received += ret;
     678                }
     679               
     680                /* We have received a complete message, send it */
     681                CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), /* continue or destroy everything? */);
     682               
     683        } while (1);
     684out:
     685        return ENOTCONN;
     686}
     687
     688/* Receiver thread (TLS & 1 stream SCTP or TCP) : gnutls directly handles the socket, save records into the target queue */
     689static void * rcvthr_tls_single(void * arg)
     690{
     691        struct cnxctx * conn = arg;
     692       
     693        TRACE_ENTRY("%p", arg);
     694        CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto error);
     695       
     696        /* Set the thread name */
     697        {
     698                char buf[48];
     699                snprintf(buf, sizeof(buf), "Receiver (%d) TLS/ single stream)", conn->cc_socket);
     700                fd_log_threadname ( buf );
     701        }
     702       
     703        ASSERT( conn->cc_tls == 1 );
     704        ASSERT( Target_Queue(conn) );
     705       
     706        CHECK_FCT_DO(fd_tls_rcvthr_core(conn, conn->cc_tls_para.session), /* continue */);
     707error:
     708        CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_ERROR, 0, NULL), /* continue or destroy everything? */);
     709        TRACE_DEBUG(FULL, "Thread terminated");
     710        return NULL;
     711}
     712
     713/* Start receving messages in clear (no TLS) on the connection */
     714int fd_cnx_start_clear(struct cnxctx * conn, int loop)
     715{
     716        TRACE_ENTRY("%p %i", conn, loop);
     717       
     718        CHECK_PARAMS( conn && Target_Queue(conn) && (!conn->cc_tls) && (!conn->cc_loop));
     719       
     720        /* Save the loop request */
     721        conn->cc_loop = loop;
     722       
     723        /* Release resources in case of a previous call was already made */
     724        CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */);
     725       
     726        switch (conn->cc_proto) {
     727                case IPPROTO_TCP:
     728                        /* Start the tcp_notls thread */
     729                        CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_notls_tcp, conn ) );
     730                        break;
     731#ifndef DISABLE_SCTP
     732                case IPPROTO_SCTP:
     733                        /* Start the tcp_notls thread */
     734                        CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_notls_sctp, conn ) );
     735                        break;
     736#endif /* DISABLE_SCTP */
     737                default:
     738                        TRACE_DEBUG(INFO, "Unknown protocol: %d", conn->cc_proto);
     739                        return ENOTSUP;
     740        }
     741                       
     742        return 0;
     743}
     744
     745/* Prepare a gnutls session object for handshake */
     746int fd_tls_prepare(gnutls_session_t * session, int mode, char * priority, void * alt_creds)
     747{
     748        /* Create the master session context */
     749        CHECK_GNUTLS_DO( gnutls_init (session, mode), return ENOMEM );
     750
     751        /* Set the algorithm suite */
     752        if (priority) {
     753                const char * errorpos;
     754                CHECK_GNUTLS_DO( gnutls_priority_set_direct( *session, priority, &errorpos ),
     755                        { TRACE_DEBUG(INFO, "Error in priority string '%s' at position: '%s'\n", priority, errorpos); return EINVAL; } );
     756        } else {
     757                CHECK_GNUTLS_DO( gnutls_priority_set( *session, fd_g_config->cnf_sec_data.prio_cache ), return EINVAL );
     758        }
     759
     760        /* Set the credentials of this side of the connection */
     761        CHECK_GNUTLS_DO( gnutls_credentials_set (*session, GNUTLS_CRD_CERTIFICATE, alt_creds ?: fd_g_config->cnf_sec_data.credentials), return EINVAL );
     762
     763        /* Request the remote credentials as well */
     764        if (mode == GNUTLS_SERVER) {
     765                gnutls_certificate_server_set_request (*session, GNUTLS_CERT_REQUIRE);
     766        }
     767       
     768        return 0;
     769}
     770
     771/* TLS handshake a connection; no need to have called start_clear before. Reception is active if handhsake is successful */
     772int fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority, void * alt_creds)
    70773{
    71774        TRACE_ENTRY( "%p %d", conn, mode);
    72         CHECK_PARAMS( conn && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) );
    73        
     775        CHECK_PARAMS( conn && (!conn->cc_tls) && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) && (!conn->cc_loop) );
     776
    74777        /* Save the mode */
    75778        conn->cc_tls_para.mode = mode;
    76779       
    77         /* Create the master session context */
    78         CHECK_GNUTLS_DO( gnutls_init (&conn->cc_tls_para.session, mode), return ENOMEM );
    79        
    80         /* Set the algorithm suite */
    81         CHECK_GNUTLS_DO( gnutls_priority_set( conn->cc_tls_para.session, fd_g_config->cnf_sec_data.prio_cache ), return EINVAL );
    82        
    83         /* Set the credentials of this side of the connection */
    84         CHECK_GNUTLS_DO( gnutls_credentials_set (conn->cc_tls_para.session, GNUTLS_CRD_CERTIFICATE, fd_g_config->cnf_sec_data.credentials), return EINVAL );
    85        
    86         /* Request the remote credentials as well */
    87         if (mode == GNUTLS_SERVER) {
    88                 gnutls_certificate_server_set_request (conn->cc_tls_para.session, GNUTLS_CERT_REQUIRE);
    89         }
    90 
    91         /* Set the socket info in the session */
    92         gnutls_transport_set_ptr (conn->cc_tls_para.session, (gnutls_transport_ptr_t) conn->cc_socket);
     780        /* Cancel receiving thread if any -- it should already be terminated anyway, we just release the resources */
     781        CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */);
     782       
     783        /* Once TLS handshake is done, we don't stop after the first message */
     784        conn->cc_loop = 1;
     785       
     786        /* Prepare the master session credentials and priority */
     787        CHECK_FCT( fd_tls_prepare(&conn->cc_tls_para.session, mode, priority, alt_creds) );
    93788
    94789        /* Special case: multi-stream TLS is not natively managed in GNU TLS, we use a wrapper library */
    95         if ((conn->cc_proto == IPPROTO_SCTP) && (conn->cc_sctp_para.pairs > 0)) {
    96 #ifndef DISABLE_SCTP
    97                 TODO("Initialize the SCTP TLS wrapper");
    98                 TODO("Set the lowat, push and pull functions");
     790        if (conn->cc_sctp_para.pairs > 1) {
     791#ifdef DISABLE_SCTP
     792                ASSERT(0);
     793                CHECK_FCT( ENOTSUP );
    99794#else /* DISABLE_SCTP */
    100                 ASSERT(0);
     795                /* Initialize the wrapper, start the demux thread */
     796                CHECK_FCT( fd_sctps_init(conn) );
    101797#endif /* DISABLE_SCTP */
    102         }
    103        
     798        } else {
     799                /* Set the socket info in the session */
     800                gnutls_transport_set_ptr (conn->cc_tls_para.session, (gnutls_transport_ptr_t) conn->cc_socket);
     801        }
     802
    104803        /* Handshake master session */
    105804        {
     
    108807                        {
    109808                                if (TRACE_BOOL(INFO)) {
    110                                         fd_log_debug("TLS Handshake failed on socket %d : %s\n", conn->cc_socket, gnutls_strerror(ret));
     809                                        fd_log_debug("TLS Handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
    111810                                }
    112811                                return EINVAL;
    113812                        } );
    114                
     813
    115814                /* Now verify the remote credentials are valid -- only simple test here */
    116815                CHECK_GNUTLS_DO( gnutls_certificate_verify_peers2 (conn->cc_tls_para.session, &ret), return EINVAL );
    117816                if (ret) {
    118817                        if (TRACE_BOOL(INFO)) {
    119                                 fd_log_debug("TLS: Remote certificate invalid on socket %d :\n", conn->cc_socket);
     818                                fd_log_debug("TLS: Remote certificate invalid on socket %d (%s) :\n", conn->cc_socket, conn->cc_id);
    120819                                if (ret & GNUTLS_CERT_INVALID)
    121820                                        fd_log_debug(" - The certificate is not trusted (unknown CA?)\n");
     
    132831                }
    133832        }
    134        
    135         /* Other sessions in case of multi-stream SCTP are resumed from the master */
    136         if ((conn->cc_proto == IPPROTO_SCTP) && (conn->cc_sctp_para.pairs > 0)) {
     833
     834        /* Multi-stream TLS: handshake other streams as well */
     835        if (conn->cc_sctp_para.pairs > 1) {
    137836#ifndef DISABLE_SCTP
    138                 TODO("Init and resume all additional sessions from the master one.");
     837                /* Resume all additional sessions from the master one. */
     838                CHECK_FCT(fd_sctps_handshake_others(conn, priority, alt_creds));
     839               
     840                /* Mark the connection as protected from here */
     841                conn->cc_tls = 1;
     842
     843                /* Start decrypting the messages from all threads and queuing them in target queue */
     844                CHECK_FCT(fd_sctps_startthreads(conn));
    139845#endif /* DISABLE_SCTP */
     846        } else {
     847                /* Mark the connection as protected from here */
     848                conn->cc_tls = 1;
     849
     850                /* Start decrypting the data */
     851                CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_tls_single, conn ) );
    140852        }
    141853       
    142854        return 0;
    143855}
     856
     857/* Retrieve TLS credentials of the remote peer, after handshake */
     858int fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size)
     859{
     860        TRACE_ENTRY("%p %p %p", conn, cert_list, cert_list_size);
     861        CHECK_PARAMS( conn && (conn->cc_tls) && cert_list && cert_list_size );
     862       
     863        /* This function only works for X.509 certificates. */
     864        CHECK_PARAMS( gnutls_certificate_type_get (conn->cc_tls_para.session) == GNUTLS_CRT_X509 );
     865       
     866        *cert_list = gnutls_certificate_get_peers (conn->cc_tls_para.session, cert_list_size);
     867        if (*cert_list == NULL) {
     868                TRACE_DEBUG(INFO, "No certificate was provided by remote peer / an error occurred.");
     869                return EINVAL;
     870        }
     871
     872        TRACE_DEBUG( FULL, "Remote peer provided %d certificates.\n", *cert_list_size);
     873       
     874        return 0;
     875}
     876
     877/* Receive next message. if timeout is not NULL, wait only until timeout. This function only pulls from a queue, mgr thread is filling that queue aynchrounously. */
     878int fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len)
     879{
     880        int    ev;
     881        size_t ev_sz;
     882        void * ev_data;
     883       
     884        TRACE_ENTRY("%p %p %p %p", conn, timeout, buf, len);
     885        CHECK_PARAMS(conn && (conn->cc_socket > 0) && buf && len);
     886        CHECK_PARAMS(conn->cc_rcvthr != (pthread_t)NULL);
     887        CHECK_PARAMS(conn->cc_alt == NULL);
     888
     889        /* Now, pull the first event */
     890get_next:
     891        if (timeout) {
     892                CHECK_FCT( fd_event_timedget(conn->cc_incoming, timeout, FDEVP_PSM_TIMEOUT, &ev, &ev_sz, &ev_data) );
     893        } else {
     894                CHECK_FCT( fd_event_get(conn->cc_incoming, &ev, &ev_sz, &ev_data) );
     895        }
     896       
     897        switch (ev) {
     898                case FDEVP_CNX_MSG_RECV:
     899                        /* We got one */
     900                        *len = ev_sz;
     901                        *buf = ev_data;
     902                        return 0;
     903                       
     904                case FDEVP_PSM_TIMEOUT:
     905                        TRACE_DEBUG(FULL, "Timeout event received");
     906                        return ETIMEDOUT;
     907                       
     908                case FDEVP_CNX_EP_CHANGE:
     909                        /* We ignore this event */
     910                        goto get_next;
     911                       
     912                case FDEVP_CNX_ERROR:
     913                        TRACE_DEBUG(FULL, "Received ERROR event on the connection");
     914                        return ENOTCONN;
     915        }
     916       
     917        TRACE_DEBUG(INFO, "Received unexpected event %d (%s)", ev, fd_pev_str(ev));
     918        return EINVAL;
     919}
     920
     921/* Set an alternate FIFO list to send FDEVP_CNX_* events to */
     922int fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo)
     923{
     924        TRACE_ENTRY( "%p %p", conn, alt_fifo );
     925        CHECK_PARAMS( conn && alt_fifo && conn->cc_incoming );
     926       
     927        /* The magic function does it all */
     928        CHECK_FCT( fd_fifo_move( &conn->cc_incoming, alt_fifo, &conn->cc_alt ) );
     929       
     930        return 0;
     931}
     932
     933/* Wrapper around gnutls_record_recv to handle some error codes */
     934static ssize_t fd_tls_send_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz)
     935{
     936        ssize_t ret;
     937again: 
     938        CHECK_GNUTLS_DO( ret = gnutls_record_send(session, data, sz),
     939                {
     940                        switch (ret) {
     941                                case GNUTLS_E_REHANDSHAKE:
     942                                        CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
     943                                                {
     944                                                        if (TRACE_BOOL(INFO)) {
     945                                                                fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
     946                                                        }
     947                                                        goto end;
     948                                                } );
     949
     950                                case GNUTLS_E_AGAIN:
     951                                case GNUTLS_E_INTERRUPTED:
     952                                        goto again;
     953
     954                                default:
     955                                        TRACE_DEBUG(INFO, "This TLS error is not handled, assume unrecoverable error");
     956                        }
     957                } );
     958end:   
     959        return ret;
     960}
     961
     962
     963
     964/* Send function when no multi-stream is involved, or sending on stream #0 (send() always use stream 0)*/
     965static int send_simple(struct cnxctx * conn, unsigned char * buf, size_t len)
     966{
     967        ssize_t ret;
     968        size_t sent = 0;
     969        TRACE_ENTRY("%p %p %zd", conn, buf, len);
     970        do {
     971                if (conn->cc_tls) {
     972                        CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_tls_para.session, buf + sent, len - sent), return ENOTCONN );
     973                } else {
     974                        CHECK_SYS( ret = send(conn->cc_socket, buf + sent, len - sent, 0) ); /* better to replace with sendmsg for atomic sending? */
     975                }
     976                sent += ret;
     977        } while ( sent < len );
     978        return 0;
     979}
     980
     981/* Send a message -- this is synchronous -- and we assume it's never called by several threads at the same time, so we don't protect. */
     982int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len)
     983{
     984        TRACE_ENTRY("%p %p %zd", conn, buf, len);
     985       
     986        CHECK_PARAMS(conn && (conn->cc_socket > 0) && buf && len);
     987
     988        TRACE_DEBUG(FULL, "Sending %zdb %sdata on connection %s", len, conn->cc_tls ? "TLS-protected ":"", conn->cc_id);
     989       
     990        switch (conn->cc_proto) {
     991                case IPPROTO_TCP:
     992                        CHECK_FCT( send_simple(conn, buf, len) );
     993                        break;
     994               
     995#ifndef DISABLE_SCTP
     996                case IPPROTO_SCTP: {
     997                        int multistr = 0;
     998                       
     999                        if ((conn->cc_sctp_para.str_out > 1) && ((! conn->cc_tls) || (conn->cc_sctp_para.pairs > 1)))  {
     1000                                /* Update the id of the stream we will send this message on */
     1001                                conn->cc_sctp_para.next += 1;
     1002                                conn->cc_sctp_para.next %= (conn->cc_tls ? conn->cc_sctp_para.pairs : conn->cc_sctp_para.str_out);
     1003                                multistr = 1;
     1004                        }
     1005                       
     1006                        if ((!multistr) || (conn->cc_sctp_para.next == 0)) {
     1007                                CHECK_FCT( send_simple(conn, buf, len) );
     1008                        } else {
     1009                                if (!conn->cc_tls) {
     1010                                        CHECK_FCT( fd_sctp_sendstr(conn->cc_socket, conn->cc_sctp_para.next, buf, len) );
     1011                                } else {
     1012                                        /* push the record to the appropriate session */
     1013                                        ssize_t ret;
     1014                                        size_t sent = 0;
     1015                                        ASSERT(conn->cc_sctps_data.array != NULL);
     1016                                        do {
     1017                                                CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_sctps_data.array[conn->cc_sctp_para.next].session, buf + sent, len - sent), return ENOTCONN );
     1018                                                sent += ret;
     1019                                        } while ( sent < len );
     1020                                }
     1021                        }
     1022                }
     1023                break;
     1024#endif /* DISABLE_SCTP */
     1025       
     1026                default:
     1027                        TRACE_DEBUG(INFO, "Unknwon protocol: %d", conn->cc_proto);
     1028                        return ENOTSUP; /* or EINVAL... */
     1029        }
     1030       
     1031        return 0;
     1032}
     1033
     1034
     1035/**************************************/
     1036/*     Destruction of connection      */
     1037/**************************************/
     1038
     1039/* Destroy a conn structure, and shutdown the socket */
     1040void fd_cnx_destroy(struct cnxctx * conn)
     1041{
     1042        TRACE_ENTRY("%p", conn);
     1043       
     1044        CHECK_PARAMS_DO(conn, return);
     1045
     1046        /* In case of TLS, stop receiver thread, then close properly the gnutls session */
     1047        if ((conn->cc_tls) && (conn->cc_sctp_para.pairs > 1)) {
     1048#ifndef DISABLE_SCTP
     1049                /* Multi-stream TLS: Stop all decipher threads, but not the demux thread */
     1050                fd_sctps_stopthreads(conn);
     1051#endif /* DISABLE_SCTP */
     1052        } else {
     1053                /* Stop the decoding thread */
     1054                CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ );
     1055        }
     1056       
     1057        /* Terminate properly the TLS session(s) */
     1058        if (conn->cc_tls) {
     1059                /* Master session */
     1060                CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_RDWR), /* Continue */ );
     1061                gnutls_deinit(conn->cc_tls_para.session);
     1062               
     1063#ifndef DISABLE_SCTP
     1064                if (conn->cc_sctp_para.pairs > 1) {
     1065                        /* Multi-stream TLS: destroy the wrapper and stop the demux thread */
     1066                        fd_sctps_destroy(conn);
     1067                }
     1068#endif /* DISABLE_SCTP */
     1069               
     1070        }
     1071       
     1072        /* Shut the connection down */
     1073        if (conn->cc_socket > 0) {
     1074                shutdown(conn->cc_socket, SHUT_RDWR);
     1075        }
     1076       
     1077        /* Empty and destroy FIFO list */
     1078        if (conn->cc_incoming) {
     1079                fd_event_destroy( &conn->cc_incoming, free );
     1080        }
     1081       
     1082        /* Free the object */
     1083        free(conn);
     1084       
     1085        /* Done! */
     1086        return;
     1087}
  • freeDiameter/config.c

    r20 r24  
    100100                        struct fd_endpoint * ep = (struct fd_endpoint *)li;
    101101                        if (li != fd_g_config->cnf_endpoints.next) fd_log_debug("                             ");
    102                         sSA_DUMP_NODE( &ep->ss, NI_NUMERICHOST );
     102                        sSA_DUMP_NODE( &ep->sa, NI_NUMERICHOST );
    103103                        fd_log_debug("\n");
    104104                        li = li->next;
     
    229229        }
    230230       
     231        /* Validate local endpoints */
     232        if ((!FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) && (fd_g_config->cnf_flags.no_ip4 || fd_g_config->cnf_flags.no_ip6)) {
     233                struct fd_list * li;
     234                for ( li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
     235                        struct fd_endpoint * ep = (struct fd_endpoint *)li;
     236                        if ( (fd_g_config->cnf_flags.no_ip4 && (ep->sa.sa_family == AF_INET))
     237                           ||(fd_g_config->cnf_flags.no_ip6 && (ep->sa.sa_family == AF_INET6)) ) {
     238                                li = li->prev;
     239                                fd_list_unlink(&ep->chain);
     240                                if (TRACE_BOOL(INFO)) {
     241                                        fd_log_debug("Info: Removing local address conflicting with the flags no_IP / no_IP6 : ");
     242                                        sSA_DUMP_NODE( &ep->sa, AI_NUMERICHOST );
     243                                        fd_log_debug("\n");
     244                                }
     245                                free(ep);
     246                        }
     247                }
     248        }
     249       
    231250        /* Configure TLS default parameters */
    232251        if (! fd_g_config->cnf_sec_data.prio_string) {
     
    239258        }
    240259        if (! fd_g_config->cnf_sec_data.dh_bits) {
    241                 TRACE_DEBUG(FULL, "Generating DH parameters...");
     260                if (TRACE_BOOL(INFO)) {
     261                        fd_log_debug("Generating Diffie-Hellman parameters of size %d (this takes a few seconds)... ", GNUTLS_DEFAULT_DHBITS);
     262                }
    242263                CHECK_GNUTLS_DO( gnutls_dh_params_generate2(
    243264                                        fd_g_config->cnf_sec_data.dh_cache,
    244265                                        GNUTLS_DEFAULT_DHBITS),
    245266                                 { TRACE_DEBUG(INFO, "Error in DH bits value : %d", GNUTLS_DEFAULT_DHBITS); return EINVAL; } );
    246                 TRACE_DEBUG(FULL, "DH parameters generated.");
    247         }
    248        
     267                if (TRACE_BOOL(INFO)) {
     268                        fd_log_debug("Done!\n");
     269                }
     270        }
    249271       
    250272        return 0;
  • freeDiameter/fD.h

    r20 r29  
    152152enum {
    153153        /* Dump all info about this peer in the debug log */
    154          FDEVP_DUMP_ALL = 2000
     154         FDEVP_DUMP_ALL = 1500
    155155       
    156156        /* request to terminate this peer : disconnect, requeue all messages */
    157157        ,FDEVP_TERMINATE
    158158       
    159         /* A message was received in the peer */
    160         ,FDEVP_MSG_INCOMING
     159        /* A connection object has received a message. (data contains the buffer) */
     160        ,FDEVP_CNX_MSG_RECV
     161                         
     162        /* A connection object has encountered an error (disconnected). */
     163        ,FDEVP_CNX_ERROR
     164       
     165        /* Endpoints of a connection have been changed (multihomed SCTP). */
     166        ,FDEVP_CNX_EP_CHANGE
     167       
     168        /* A new connection has been established (data contains the appropriate info) */
     169        ,FDEVP_CNX_INCOMING
    161170       
    162171        /* The PSM state is expired */
    163172        ,FDEVP_PSM_TIMEOUT
     173       
    164174};
    165175const char * fd_pev_str(int event);
    166176#define CHECK_EVENT( _e ) \
    167177        (((int)(_e) >= FDEVP_DUMP_ALL) && ((int)(_e) <= FDEVP_PSM_TIMEOUT))
     178
     179/* The data structure for FDEVP_CNX_INCOMING events */
     180struct cnx_incoming {
     181        struct msg      * cer;          /* the CER message received on this connection */
     182        struct cnxctx   * cnx;          /* The connection context */
     183        int               validate;     /* The peer is new, it must be validated (by an extension) or error CEA to be sent */
     184};
    168185
    169186/* Structure to store a sent request */
     
    173190};
    174191
    175 /* The connection context structure */
    176 struct cnxctx {
    177         int             cc_socket;      /* The socket object of the connection -- <=0 if no socket is created */
    178        
    179         struct fifo   **cc_events;      /* Location of the events list to send connection events */
    180        
    181         int             cc_proto;       /* IPPROTO_TCP or IPPROTO_SCTP */
    182         int             cc_tls;         /* Is TLS already started ? */
    183        
    184         uint16_t        cc_port;        /* Remote port of the connection, when we are client */
    185         struct fd_list  cc_ep_remote;   /* The remote address(es) of the connection */
    186         struct fd_list  cc_ep_local;    /* The local address(es) of the connection */
    187        
    188         /* If cc_proto == SCTP */
    189         struct  {
    190                 int             str_out;/* Out streams */
    191                 int             str_in; /* In streams */
    192                 int             pairs;  /* max number of pairs ( = min(in, out)) */
    193                 int             next;   /* # of stream the next message will be sent to */
    194         }               cc_sctp_para;
    195        
    196         /* If cc_tls == true */
    197         struct {
    198                 int                              mode;          /* GNUTLS_CLIENT / GNUTLS_SERVER */
    199                 gnutls_session_t                 session;       /* Session object (stream #0 in case of SCTP) */
    200         }               cc_tls_para;
    201        
    202         /* If both conditions */
    203         struct {
    204                 gnutls_session_t                *res_sessions;  /* Sessions of other pairs of streams, resumed from the first */
    205                 /* Buffers, threads, ... */
    206         }               cc_sctp_tls_para;
    207 };
    208192
    209193/* Functions */
    210 int fd_peer_fini();
     194int  fd_peer_fini();
    211195void fd_peer_dump_list(int details);
    212196void fd_peer_dump(struct fd_peer * peer, int details);
    213 int fd_peer_alloc(struct fd_peer ** ptr);
    214 int fd_peer_free(struct fd_peer ** ptr);
     197int  fd_peer_alloc(struct fd_peer ** ptr);
     198int  fd_peer_free(struct fd_peer ** ptr);
     199int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx );
    215200/* fd_peer_add declared in freeDiameter.h */
     201int fd_peer_validate( struct fd_peer * peer );
    216202
    217203/* Peer expiry */
     
    221207
    222208/* Peer state machine */
    223 int fd_psm_start();
    224 int fd_psm_begin(struct fd_peer * peer );
    225 int fd_psm_terminate(struct fd_peer * peer );
     209int  fd_psm_start();
     210int  fd_psm_begin(struct fd_peer * peer );
     211int  fd_psm_terminate(struct fd_peer * peer );
    226212void fd_psm_abord(struct fd_peer * peer );
    227213
    228214/* Server sockets */
    229215void fd_servers_dump();
    230 int fd_servers_start();
    231 void fd_servers_stop();
    232 
    233 /* Connection contexts */
    234 struct cnxctx * fd_cnx_init(int sock, int proto);
    235 int fd_cnx_handshake(struct cnxctx * conn, int mode);
    236 
    237 /* SCTP */
    238 #ifndef DISABLE_SCTP
    239 int fd_sctp_create_bind_server( int * socket, uint16_t port );
    240 int fd_sctp_get_str_info( int socket, int *in, int *out );
    241 
    242 #endif /* DISABLE_SCTP */
    243 
     216int  fd_servers_start();
     217int  fd_servers_stop();
     218
     219/* Connection contexts -- there are also definitions in cnxctx.h for the relevant files */
     220struct cnxctx * fd_cnx_serv_tcp(uint16_t port, int family, struct fd_endpoint * ep);
     221struct cnxctx * fd_cnx_serv_sctp(uint16_t port, struct fd_list * ep_list);
     222int             fd_cnx_serv_listen(struct cnxctx * conn);
     223struct cnxctx * fd_cnx_serv_accept(struct cnxctx * serv);
     224struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa, socklen_t addrlen);
     225struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list);
     226int             fd_cnx_start_clear(struct cnxctx * conn, int loop);
     227int             fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority, void * alt_creds);
     228char *          fd_cnx_getid(struct cnxctx * conn);
     229int             fd_cnx_getproto(struct cnxctx * conn);
     230int             fd_cnx_getTLS(struct cnxctx * conn);
     231int             fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size);
     232int             fd_cnx_getendpoints(struct cnxctx * conn, struct fd_list * local, struct fd_list * remote);
     233char *          fd_cnx_getremoteid(struct cnxctx * conn);
     234int             fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len);
     235int             fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo); /* send FDEVP_CNX_MSG_RECV event to the fifo list */
     236int             fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len);
     237void            fd_cnx_destroy(struct cnxctx * conn);
    244238
    245239
  • freeDiameter/fdd.y

    r20 r24  
    212212listenon:               LISTENON '=' QSTRING ';'
    213213                        {
    214                                 struct fd_endpoint * ep;
    215214                                struct addrinfo hints, *ai;
    216215                                int ret;
    217                                
    218                                 CHECK_MALLOC_DO( ep = malloc(sizeof(struct fd_endpoint)),
    219                                         { yyerror (&yylloc, conf, "Out of memory"); YYERROR; } );
    220                                 memset(ep, 0, sizeof(struct fd_endpoint));
    221                                 fd_list_init(&ep->chain, NULL);
    222                                 ep->meta.conf = 1;
    223216                               
    224217                                memset(&hints, 0, sizeof(hints));
    225218                                hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
    226219                                ret = getaddrinfo($3, NULL, &hints, &ai);
    227                                 if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); free(ep); YYERROR; }
    228                                 ASSERT( ai->ai_addrlen <= sizeof(sSS) );
    229                                 memcpy(&ep->ss, ai->ai_addr, ai->ai_addrlen);
     220                                if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); YYERROR; }
     221                                CHECK_FCT_DO( fd_ep_add_merge( &conf->cnf_endpoints, ai->ai_addr, ai->ai_addrlen, EP_FL_CONF ), YYERROR );
     222                                freeaddrinfo(ai);
    230223                                free($3);
    231                                 freeaddrinfo(ai);
    232                                 fd_list_insert_before(&conf->cnf_endpoints, &ep->chain);
    233224                        }
    234225                        ;
     
    336327                                /* Now destroy any content in the structure */
    337328                                free(fddpi.pi_diamid);
     329                                free(fddpi.pi_sec_data.priority);
    338330                                while (!FD_IS_LIST_EMPTY(&fddpi.pi_endpoints)) {
    339331                                        struct fd_list * li = fddpi.pi_endpoints.next;
     
    415407                                fddpi.pi_port = (uint16_t)$4;
    416408                        }
    417                         | peerparams SCTPSTREAMS '=' INTEGER ';'
    418                         {
    419                                 CHECK_PARAMS_DO( ($4 > 0) && ($4 < 1<<16),
    420                                         { yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
    421                                 fddpi.pi_streams = (uint16_t)$4;
    422                         }
    423409                        | peerparams TCTIMER '=' INTEGER ';'
    424410                        {
    425411                                fddpi.pi_tctimer = $4;
    426412                        }
     413                        | peerparams TLS_PRIO '=' QSTRING ';'
     414                        {
     415                                fddpi.pi_sec_data.priority = $4;
     416                        }
    427417                        | peerparams TWTIMER '=' INTEGER ';'
    428418                        {
     
    431421                        | peerparams CONNTO '=' QSTRING ';'
    432422                        {
    433                                 struct fd_endpoint * ep;
    434423                                struct addrinfo hints, *ai;
    435424                                int ret;
     425                                int disc = 0;
    436426                               
    437                                 CHECK_MALLOC_DO( ep = malloc(sizeof(struct fd_endpoint)),
    438                                         { yyerror (&yylloc, conf, "Out of memory"); YYERROR; } );
    439                                 memset(ep, 0, sizeof(struct fd_endpoint));
    440                                 fd_list_init(&ep->chain, NULL);
    441                                 ep->meta.conf = 1;
    442427                                memset(&hints, 0, sizeof(hints));
    443428                                hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST;
     
    445430                                if (ret == EAI_NONAME) {
    446431                                        /* The name was maybe not numeric, try again */
    447                                         ep->meta.disc = 1;
     432                                        disc = EP_FL_DISC;
    448433                                        hints.ai_flags &= ~ AI_NUMERICHOST;
    449434                                        ret = getaddrinfo($4, NULL, &hints, &ai);
    450435                                }
    451                                 if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); free(ep); YYERROR; }
     436                                if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); YYERROR; }
    452437                               
    453                                 memcpy(&ep->ss, ai->ai_addr, ai->ai_addrlen);
     438                                CHECK_FCT_DO( fd_ep_add_merge( &fddpi.pi_endpoints, ai->ai_addr, ai->ai_addrlen, EP_FL_CONF | disc ), YYERROR );
    454439                                free($4);
    455440                                freeaddrinfo(ai);
    456                                 fd_list_insert_before(&fddpi.pi_endpoints, &ep->chain);
    457441                        }
    458442                        ;
  • freeDiameter/main.c

    r20 r25  
    5151struct fd_config * fd_g_config = &conf;
    5252
     53/* gcrypt functions to support posix threads */
    5354GCRY_THREAD_OPTION_PTHREAD_IMPL;
    5455
     
    105106        CHECK_FCT(  fd_ext_load()  );
    106107       
     108        fd_conf_dump();
     109       
     110        /* Start the servers */
     111        CHECK_FCT( fd_servers_start() );
     112       
    107113        /* Start the peer state machines */
    108114        CHECK_FCT( fd_psm_start() );
     
    110116        /* Now, just wait for events */
    111117        TRACE_DEBUG(INFO, FD_PROJECT_BINARY " daemon initialized.");
    112         fd_conf_dump();
    113118        while (1) {
    114                 int code;
    115                 CHECK_FCT_DO(  fd_event_get(fd_g_config->cnf_main_ev, &code, NULL),  break  );
     119                int code; size_t sz; void * data;
     120                CHECK_FCT_DO(  fd_event_get(fd_g_config->cnf_main_ev, &code, &sz, &data),  break  );
    116121                switch (code) {
    117122                        case FDEV_DUMP_DICT:
     
    154159       
    155160        /* cleanups */
     161        CHECK_FCT_DO( fd_servers_stop(), /* Stop accepting new connections */ );
    156162        TODO("Stop dispatch thread(s) properly (no cancel yet)");
    157163        CHECK_FCT_DO( fd_peer_fini(), /* Stop all connections */ );
     
    333339       
    334340        TRACE_DEBUG(INFO, "Received signal %s (%d), exiting", SIGNALSTR(sig), sig);
    335         CHECK_FCT_DO( fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, NULL), exit(2) );
     341        CHECK_FCT_DO( fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), exit(2) );
    336342        return NULL;
    337343}
  • freeDiameter/p_expiry.c

    r16 r25  
    4747static void * gc_th_fct(void * arg)
    4848{
    49         fd_log_threadname ( "Peers/garbage" );
    50         TRACE_ENTRY( "" );
     49        fd_log_threadname ( "Peers/garb. col." );
     50        TRACE_ENTRY( "%p", arg );
    5151       
    5252        do {
    5353                struct fd_list * li, purge = FD_LIST_INITIALIZER(purge);
    5454               
    55                 pthread_testcancel();
    56                 sleep(GC_TIME);
     55                sleep(GC_TIME); /* sleep is a cancellation point */
    5756               
    5857                /* Now check in the peers list if any peer can be deleted */
     
    8887        TRACE_DEBUG(INFO, "An error occurred in peers module! GC thread is terminating...");
    8988        ASSERT(0);
    90         CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, NULL), );
     89        CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
    9190        return NULL;
    9291}
     
    9695{
    9796        fd_log_threadname ( "Peers/expire" );
    98         TRACE_ENTRY( "" );
     97        TRACE_ENTRY( "%p", arg );
    9998       
    10099        CHECK_POSIX_DO( pthread_mutex_lock(&exp_mtx),  goto error );
     
    124123                       
    125124                        CHECK_POSIX_DO2(  pthread_cond_timedwait( &exp_cnd, &exp_mtx, &first->p_exp_timer ), 
    126                                         ETIMEDOUT, /* ETIMEDOUT is a normal error, continue */,
     125                                        ETIMEDOUT, /* ETIMEDOUT is a normal return value, continue */,
    127126                                        /* on other error, */ goto error );
    128127       
     
    133132                /* Now, the first peer in the list is expired; signal it */
    134133                fd_list_unlink( &first->p_expiry );
    135                 CHECK_FCT_DO( fd_event_send(first->p_events, FDEVP_TERMINATE, NULL), goto error );
     134                CHECK_FCT_DO( fd_event_send(first->p_events, FDEVP_TERMINATE, 0, NULL), goto error );
    136135               
    137136        } while (1);
     
    141140        TRACE_DEBUG(INFO, "An error occurred in peers module! Expiry thread is terminating...");
    142141        ASSERT(0);
    143         CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, NULL), );
     142        CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
    144143        return NULL;
    145144}
  • freeDiameter/p_psm.c

    r20 r29  
    5555        #define case_str( _val )\
    5656                case _val : return #_val
     57                case_str(FDEVP_DUMP_ALL);
    5758                case_str(FDEVP_TERMINATE);
    58                 case_str(FDEVP_DUMP_ALL);
    59                 case_str(FDEVP_MSG_INCOMING);
     59                case_str(FDEVP_CNX_MSG_RECV);
     60                case_str(FDEVP_CNX_ERROR);
     61                case_str(FDEVP_CNX_EP_CHANGE);
     62                case_str(FDEVP_CNX_INCOMING);
    6063                case_str(FDEVP_PSM_TIMEOUT);
    6164               
     
    125128}
    126129
    127 /* Wait for the next event in the PSM, or timeout */
    128 static int psm_ev_timedget(struct fd_peer * peer, int *code, void ** data)
    129 {
    130         struct fd_event * ev;
    131         int ret = 0;
    132        
    133         TRACE_ENTRY("%p %p %p", peer, code, data);
    134        
    135         ret = fd_fifo_timedget(peer->p_events, &ev, &peer->p_psm_timer);
    136         if (ret == ETIMEDOUT) {
    137                 *code = FDEVP_PSM_TIMEOUT;
    138                 *data = NULL;
    139         } else {
    140                 CHECK_FCT( ret );
    141                 *code = ev->code;
    142                 *data = ev->data;
    143                 free(ev);
    144         }
    145        
    146         return 0;
    147 }
    148 
    149130/* The state machine thread (controler) */
    150131static void * p_psm_th( void * arg )
     
    153134        int created_started = started;
    154135        int event;
     136        size_t ev_sz;
    155137        void * ev_data;
    156138       
     
    181163psm_loop:
    182164        /* Get next event */
    183         CHECK_FCT_DO( psm_ev_timedget(peer, &event, &ev_data), goto psm_end );
    184         TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p)\t'%s'",
     165        CHECK_FCT_DO( fd_event_timedget(peer->p_events, &peer->p_psm_timer, FDEVP_PSM_TIMEOUT, &event, &ev_sz, &ev_data), goto psm_end );
     166        TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p,%zd)\t'%s'",
    185167                        STATE_STR(peer->p_hdr.info.pi_state),
    186                         fd_pev_str(event), ev_data,
     168                        fd_pev_str(event), ev_data, ev_sz,
    187169                        peer->p_hdr.info.pi_diamid);
    188170
     
    227209        }
    228210       
     211        /* A new connection was established and CER containing this peer id was received */
     212        if (event == FDEVP_CNX_INCOMING) {
     213                struct cnx_incoming * params = ev_data;
     214                ASSERT(params);
     215               
     216                switch (peer->p_hdr.info.pi_state) {
     217                        case STATE_CLOSED:
     218                                TODO("Handle the CER, validate the peer if needed (and set expiry), set the alt_fifo in the connection, reply a CEA, eventually handshake, move to OPEN or REOPEN state");
     219                                break;
     220                               
     221                        case STATE_WAITCNXACK:
     222                        case STATE_WAITCEA:
     223                                TODO("Election");
     224                                break;
     225                               
     226                        default:
     227                                TODO("Reply with error CEA");
     228                                TODO("Close the connection");
     229                                /* reject_incoming_connection */
     230                       
     231                }
     232               
     233                free(ev_data);
     234                goto psm_loop;
     235        }
     236       
    229237        /* MSG_RECEIVED: fd_p_expi_update(struct fd_peer * peer ) */
    230238        /* If timeout or OPEN : call cb if defined */
     
    241249psm_end:
    242250        pthread_cleanup_pop(1); /* set STATE_ZOMBIE */
     251        peer->p_psm = (pthread_t)NULL;
     252        pthread_detach(pthread_self());
    243253        return NULL;
    244254}       
    245        
    246        
    247 
    248255
    249256/* Create the PSM thread of one peer structure */
     
    267274        TRACE_ENTRY("%p", peer);
    268275        CHECK_PARAMS( CHECK_PEER(peer) );
     276       
    269277        if (peer->p_hdr.info.pi_state != STATE_ZOMBIE) {
    270                 CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, NULL) );
     278                CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, NULL) );
    271279        } else {
    272280                TRACE_DEBUG(FULL, "Peer '%s' was already terminated", peer->p_hdr.info.pi_diamid);
  • freeDiameter/peers.c

    r20 r29  
    101101       
    102102        p->p_hdr.info.pi_lft     = info->pi_lft;
    103         p->p_hdr.info.pi_streams = info->pi_streams;
    104103        p->p_hdr.info.pi_port    = info->pi_port;
    105104        p->p_hdr.info.pi_tctimer = info->pi_tctimer;
    106105        p->p_hdr.info.pi_twtimer = info->pi_twtimer;
     106       
     107        if (info->pi_sec_data.priority) {
     108                CHECK_MALLOC( p->p_hdr.info.pi_sec_data.priority = strdup(info->pi_sec_data.priority) );
     109        }
    107110       
    108111        /* Move the items from one list to the other */
     
    114117                }
    115118       
     119       
    116120        /* The internal data */
    117121        if (orig_dbg) {
     
    127131       
    128132        for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
    129                 struct fd_peer * prev = (struct fd_peer *)li;
    130                 int cmp = strcasecmp( p->p_hdr.info.pi_diamid, prev->p_hdr.info.pi_diamid );
    131                 if (cmp < 0)
     133                struct fd_peer * next = (struct fd_peer *)li;
     134                int cmp = strcasecmp( p->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamid );
     135                if (cmp > 0)
    132136                        continue;
    133137                if (cmp == 0)
     
    229233       
    230234        if (p->p_cnxctx) {
    231                 TODO("destroy p->p_cnxctx");
     235                fd_cnx_destroy(p->p_cnxctx);
    232236        }
    233237       
     
    293297                list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
    294298                CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
     299                CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &now)  );
    295300        }
    296301       
     
    370375}
    371376
     377/* Handle an incoming CER request on a new connection */
     378int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx )
     379{
     380        struct msg * msg;
     381        struct dict_object *avp_oh_model;
     382        avp_code_t code = AC_ORIGIN_HOST;
     383        struct avp *avp_oh;
     384        struct avp_hdr * avp_hdr;
     385        struct fd_list * li;
     386        int found = 0;
     387        int ret = 0;
     388        struct fd_peer * peer;
     389        struct cnx_incoming * ev_data;
     390       
     391        TRACE_ENTRY("%p %p", cer, cnx);
     392        CHECK_PARAMS(cer && *cer && cnx && *cnx);
     393       
     394        msg = *cer;
     395       
     396        /* Find the Diameter Identity of the remote peer in the message */
     397        CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE, &code, &avp_oh_model, ENOENT) );
     398        CHECK_FCT( fd_msg_search_avp ( msg, avp_oh_model, &avp_oh ) );
     399        CHECK_FCT( fd_msg_avp_hdr ( avp_oh, &avp_hdr ) );
     400       
     401        /* Search if we already have this peer id in our list */
     402        CHECK_POSIX( pthread_rwlock_rdlock(&fd_g_peers_rw) );
     403       
     404        for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
     405                peer = (struct fd_peer *)li;
     406                int cmp = strncasecmp( avp_hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid, avp_hdr->avp_value->os.len );
     407                if (cmp > 0)
     408                        continue;
     409                if (cmp == 0)
     410                        found = 1;
     411                break;
     412        }
     413       
     414        if (!found) {
     415                /* Create a new peer entry for this new remote peer */
     416                peer = NULL;
     417                CHECK_FCT_DO( ret = fd_peer_alloc(&peer), goto out );
     418               
     419                /* Set the peer Diameter Id and the responder flag parameters */
     420                CHECK_MALLOC_DO( peer->p_hdr.info.pi_diamid = malloc(avp_hdr->avp_value->os.len + 1), { ret = ENOMEM; goto out; } );
     421                CHECK_MALLOC_DO( peer->p_dbgorig = strdup(fd_cnx_getid(*cnx)), { ret = ENOMEM; goto out; } );
     422                peer->p_flags.pf_responder = 1;
     423               
     424                /* Upgrade the lock to write lock */
     425                CHECK_POSIX_DO( ret = pthread_rwlock_wrlock(&fd_g_peers_rw), goto out );
     426               
     427                /* Insert the new peer in the list (the PSM will take care of setting the expiry after validation) */
     428                fd_list_insert_before( li, &peer->p_hdr.chain );
     429               
     430                /* Release the write lock */
     431                CHECK_POSIX_DO( ret = pthread_rwlock_unlock(&fd_g_peers_rw), goto out );
     432               
     433                /* Start the PSM, which will receive the event bellow */
     434                CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out );
     435        }
     436               
     437        /* Send the new connection event to the PSM */
     438        CHECK_MALLOC_DO( ev_data = malloc(sizeof(struct cnx_incoming)), { ret = ENOMEM; goto out; } );
     439        memset(ev_data, 0, sizeof(ev_data));
     440       
     441        ev_data->cer = msg;
     442        ev_data->cnx = *cnx;
     443        ev_data->validate = !found;
     444       
     445        CHECK_FCT_DO( ret = fd_event_send(peer->p_events, FDEVP_CNX_INCOMING, sizeof(ev_data), ev_data), goto out );
     446       
     447out:   
     448        CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
     449
     450        if (ret == 0) {
     451                /* Reset the "out" parameters, so that they are not cleanup on function return. */
     452                *cer = NULL;
     453                *cnx = NULL;
     454        }
     455       
     456        return ret;
     457}
     458
     459/* Save a callback to accept / reject incoming unknown peers */
     460int fd_peer_validate_register ( int (*peer_validate)(struct peer_info * /* info */, int * /* auth */, int (**cb2)(struct peer_info *)) )
     461{
     462       
     463        TODO("...");
     464        return ENOTSUP;
     465}
     466
     467/* Validate a peer by calling the callbacks in turn -- return 0 if the peer is validated, ! 0 in case of error or if the peer is rejected */
     468int fd_peer_validate( struct fd_peer * peer )
     469{
     470        TODO("Default to reject");
     471        TODO("Call all callbacks in turn");
     472        TODO("Save cb2 in the peer if needed");
     473        return ENOTSUP;
     474}
  • freeDiameter/sctp.c

    r20 r29  
    3535
    3636#include "fD.h"
    37 
    38 int fd_sctp_create_bind_server( int * socket, uint16_t port )
     37#include "cnxctx.h"
     38
     39#include <netinet/sctp.h>
     40#include <sys/uio.h>
     41
     42/* Size of buffer to receive ancilliary data. May need to be enlarged if more sockopt are set... */
     43#ifndef CMSG_BUF_LEN
     44#define CMSG_BUF_LEN    1024
     45#endif /* CMSG_BUF_LEN */
     46
     47/* Level of SCTP-specific traces */
     48#ifdef DEBUG_SCTP
     49#define SCTP_LEVEL      FULL
     50#else /* DEBUG_SCTP */
     51#define SCTP_LEVEL      ANNOYING
     52#endif /* DEBUG_SCTP */
     53
     54/* Pre-binding socket options -- # streams read in config */
     55static int fd_setsockopt_prebind(int sk)
    3956{
    40         TODO("Create sctp server, using fd_g_config: cnf_endpoints, no_ip4, no_ip6, cnf_sctp_str");
    41        
    42         return ENOTSUP;
     57        socklen_t sz;
     58       
     59        TRACE_ENTRY( "%d", sk);
     60       
     61        CHECK_PARAMS( sk > 0 );
     62       
     63        /* Subscribe to some notifications */
     64        {
     65                struct sctp_event_subscribe event;
     66
     67                memset(&event, 0, sizeof(event));
     68                event.sctp_data_io_event        = 1;    /* to receive the stream ID in SCTP_SNDRCV ancilliary data on message reception */
     69                event.sctp_association_event    = 0;    /* new or closed associations (mostly for one-to-many style sockets) */
     70                event.sctp_address_event        = 1;    /* address changes */
     71                event.sctp_send_failure_event   = 1;    /* delivery failures */
     72                event.sctp_peer_error_event     = 1;    /* remote peer sends an error */
     73                event.sctp_shutdown_event       = 1;    /* peer has sent a SHUTDOWN */
     74                event.sctp_partial_delivery_event = 1;  /* a partial delivery is aborted, probably indicating the connection is being shutdown */
     75                // event.sctp_adaptation_layer_event = 0;       /* adaptation layer notifications */
     76                // event.sctp_authentication_event = 0; /* when new key is made active */
     77
     78                /* Set the option to the socket */
     79                CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) );
     80               
     81                if (TRACE_BOOL(SCTP_LEVEL)) {
     82                        sz = sizeof(event);
     83                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, &sz) );
     84                        if (sz != sizeof(event))
     85                        {
     86                                TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(event));
     87                                return ENOTSUP;
     88                        }
     89
     90                        fd_log_debug( "SCTP_EVENTS : sctp_data_io_event          : %hhu\n", event.sctp_data_io_event);
     91                        fd_log_debug( "              sctp_association_event      : %hhu\n", event.sctp_association_event);
     92                        fd_log_debug( "              sctp_address_event          : %hhu\n", event.sctp_address_event);
     93                        fd_log_debug( "              sctp_send_failure_event     : %hhu\n", event.sctp_send_failure_event);
     94                        fd_log_debug( "              sctp_peer_error_event       : %hhu\n", event.sctp_peer_error_event);
     95                        fd_log_debug( "              sctp_shutdown_event         : %hhu\n", event.sctp_shutdown_event);
     96                        fd_log_debug( "              sctp_partial_delivery_event : %hhu\n", event.sctp_partial_delivery_event);
     97                        fd_log_debug( "              sctp_adaptation_layer_event : %hhu\n", event.sctp_adaptation_layer_event);
     98                        // fd_log_debug( "             sctp_authentication_event    : %hhu\n", event.sctp_authentication_event);
     99                }
     100               
     101        }
     102       
     103        /* Set the INIT parameters, such as number of streams */
     104        {
     105                struct sctp_initmsg init;
     106                memset(&init, 0, sizeof(init));
     107               
     108                if (TRACE_BOOL(SCTP_LEVEL)) {
     109                        sz = sizeof(init);
     110
     111                        /* Read socket defaults */
     112                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz)  );
     113                        if (sz != sizeof(init))
     114                        {
     115                                TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(init));
     116                                return ENOTSUP;
     117                        }
     118                        fd_log_debug( "Def SCTP_INITMSG : sinit_num_ostreams   : %hu\n", init.sinit_num_ostreams);
     119                        fd_log_debug( "                   sinit_max_instreams  : %hu\n", init.sinit_max_instreams);
     120                        fd_log_debug( "                   sinit_max_attempts   : %hu\n", init.sinit_max_attempts);
     121                        fd_log_debug( "                   sinit_max_init_timeo : %hu\n", init.sinit_max_init_timeo);
     122                }
     123
     124                /* Set the init options -- need to receive SCTP_COMM_UP to confirm the requested parameters */
     125                init.sinit_num_ostreams   = fd_g_config->cnf_sctp_str;  /* desired number of outgoing streams */
     126                init.sinit_max_init_timeo = CNX_TIMEOUT * 1000;
     127
     128                /* Set the option to the socket */
     129                CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, sizeof(init))  );
     130               
     131                if (TRACE_BOOL(SCTP_LEVEL)) {
     132                        /* Check new values */
     133                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz)  );
     134                        fd_log_debug( "New SCTP_INITMSG : sinit_num_ostreams   : %hu\n", init.sinit_num_ostreams);
     135                        fd_log_debug( "                   sinit_max_instreams  : %hu\n", init.sinit_max_instreams);
     136                        fd_log_debug( "                   sinit_max_attempts   : %hu\n", init.sinit_max_attempts);
     137                        fd_log_debug( "                   sinit_max_init_timeo : %hu\n", init.sinit_max_init_timeo);
     138                }
     139        }
     140       
     141        /* Set the SCTP_DISABLE_FRAGMENTS option, required for TLS */
     142        #ifdef SCTP_DISABLE_FRAGMENTS
     143        {
     144                int nofrag;
     145               
     146                if (TRACE_BOOL(SCTP_LEVEL)) {
     147                        sz = sizeof(nofrag);
     148                        /* Read socket defaults */
     149                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz)  );
     150                        if (sz != sizeof(nofrag))
     151                        {
     152                                TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nofrag));
     153                                return ENOTSUP;
     154                        }
     155                        fd_log_debug( "Def SCTP_DISABLE_FRAGMENTS value : %s\n", nofrag ? "true" : "false");
     156                }
     157
     158                nofrag = 0;     /* We turn ON the fragmentation */
     159               
     160                /* Set the option to the socket */
     161                CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, sizeof(nofrag))  );
     162               
     163                if (TRACE_BOOL(SCTP_LEVEL)) {
     164                        /* Check new values */
     165                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz)  );
     166                        fd_log_debug( "New SCTP_DISABLE_FRAGMENTS value : %s\n", nofrag ? "true" : "false");
     167                }
     168        }
     169        #else /* SCTP_DISABLE_FRAGMENTS */
     170        # error "TLS requires support of SCTP_DISABLE_FRAGMENTS"
     171        #endif /* SCTP_DISABLE_FRAGMENTS */
     172       
     173       
     174        /* Set the RETRANSMIT parameters */
     175        #ifdef SCTP_RTOINFO
     176        {
     177                struct sctp_rtoinfo rtoinfo;
     178                memset(&rtoinfo, 0, sizeof(rtoinfo));
     179
     180                if (TRACE_BOOL(SCTP_LEVEL)) {
     181                        sz = sizeof(rtoinfo);
     182                        /* Read socket defaults */
     183                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz)  );
     184                        if (sz != sizeof(rtoinfo))
     185                        {
     186                                TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(rtoinfo));
     187                                return ENOTSUP;
     188                        }
     189                        fd_log_debug( "Def SCTP_RTOINFO : srto_initial : %u\n", rtoinfo.srto_initial);
     190                        fd_log_debug( "                   srto_max     : %u\n", rtoinfo.srto_max);
     191                        fd_log_debug( "                   srto_min     : %u\n", rtoinfo.srto_min);
     192                }
     193
     194                rtoinfo.srto_max     = fd_g_config->cnf_timer_tw * 500 - 1000;  /* Maximum retransmit timer (in ms) (set to Tw / 2 - 1) */
     195
     196                /* Set the option to the socket */
     197                CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, sizeof(rtoinfo))  );
     198               
     199                if (TRACE_BOOL(SCTP_LEVEL)) {
     200                        /* Check new values */
     201                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz)  );
     202                        fd_log_debug( "New SCTP_RTOINFO : srto_initial : %u\n", rtoinfo.srto_initial);
     203                        fd_log_debug( "                   srto_max     : %u\n", rtoinfo.srto_max);
     204                        fd_log_debug( "                   srto_min     : %u\n", rtoinfo.srto_min);
     205                }
     206        }
     207        #else /* SCTP_RTOINFO */
     208        TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_RTOINFO");
     209        #endif /* SCTP_RTOINFO */
     210       
     211        /* Set the ASSOCIATION parameters */
     212        #ifdef SCTP_ASSOCINFO
     213        {
     214                struct sctp_assocparams assoc;
     215                memset(&assoc, 0, sizeof(assoc));
     216
     217                if (TRACE_BOOL(SCTP_LEVEL)) {
     218                        sz = sizeof(assoc);
     219                        /* Read socket defaults */
     220                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz)  );
     221                        if (sz != sizeof(assoc))
     222                        {
     223                                TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(assoc));
     224                                return ENOTSUP;
     225                        }
     226                        fd_log_debug( "Def SCTP_ASSOCINFO : sasoc_asocmaxrxt               : %hu\n", assoc.sasoc_asocmaxrxt);
     227                        fd_log_debug( "                     sasoc_number_peer_destinations : %hu\n", assoc.sasoc_number_peer_destinations);
     228                        fd_log_debug( "                     sasoc_peer_rwnd                : %u\n" , assoc.sasoc_peer_rwnd);
     229                        fd_log_debug( "                     sasoc_local_rwnd               : %u\n" , assoc.sasoc_local_rwnd);
     230                        fd_log_debug( "                     sasoc_cookie_life              : %u\n" , assoc.sasoc_cookie_life);
     231                }
     232
     233                assoc.sasoc_asocmaxrxt = 5;     /* Maximum retransmission attempts: we want fast detection of errors */
     234               
     235                /* Set the option to the socket */
     236                CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, sizeof(assoc))  );
     237               
     238                if (TRACE_BOOL(SCTP_LEVEL)) {
     239                        /* Check new values */
     240                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz)  );
     241                        fd_log_debug( "New SCTP_ASSOCINFO : sasoc_asocmaxrxt               : %hu\n", assoc.sasoc_asocmaxrxt);
     242                        fd_log_debug( "                     sasoc_number_peer_destinations : %hu\n", assoc.sasoc_number_peer_destinations);
     243                        fd_log_debug( "                     sasoc_peer_rwnd                : %u\n" , assoc.sasoc_peer_rwnd);
     244                        fd_log_debug( "                     sasoc_local_rwnd               : %u\n" , assoc.sasoc_local_rwnd);
     245                        fd_log_debug( "                     sasoc_cookie_life              : %u\n" , assoc.sasoc_cookie_life);
     246                }
     247        }
     248        #else /* SCTP_ASSOCINFO */
     249        TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_ASSOCINFO");
     250        #endif /* SCTP_ASSOCINFO */
     251       
     252       
     253        /* The SO_LINGER option will be re-set if we want to perform SCTP ABORT */
     254        #ifdef SO_LINGER
     255        {
     256                struct linger linger;
     257                memset(&linger, 0, sizeof(linger));
     258               
     259                if (TRACE_BOOL(SCTP_LEVEL)) {
     260                        sz = sizeof(linger);
     261                        /* Read socket defaults */
     262                        CHECK_SYS(  getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz)  );
     263                        if (sz != sizeof(linger))
     264                        {
     265                                TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(linger));
     266                                return ENOTSUP;
     267                        }
     268                        fd_log_debug( "Def SO_LINGER : l_onoff  : %d\n", linger.l_onoff);
     269                        fd_log_debug( "                l_linger : %d\n", linger.l_linger);
     270                }
     271               
     272                linger.l_onoff  = 0;    /* Do not activate the linger */
     273                linger.l_linger = 0;    /* Return immediately when closing (=> abort) */
     274               
     275                /* Set the option */
     276                CHECK_SYS(  setsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger))  );
     277               
     278                if (TRACE_BOOL(SCTP_LEVEL)) {
     279                        /* Check new values */
     280                        CHECK_SYS(  getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz)  );
     281                        fd_log_debug( "New SO_LINGER : l_onoff  : %d\n", linger.l_onoff);
     282                        fd_log_debug( "           l_linger : %d\n", linger.l_linger);
     283                }
     284        }
     285        #else /* SO_LINGER */
     286        TRACE_DEBUG(SCTP_LEVEL, "Skipping SO_LINGER");
     287        #endif /* SO_LINGER */
     288       
     289        /* Set the NODELAY option (Nagle-like algorithm) */
     290        #ifdef SCTP_NODELAY
     291        {
     292                int nodelay;
     293               
     294                if (TRACE_BOOL(SCTP_LEVEL)) {
     295                        sz = sizeof(nodelay);
     296                        /* Read socket defaults */
     297                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz)  );
     298                        if (sz != sizeof(nodelay))
     299                        {
     300                                TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nodelay));
     301                                return ENOTSUP;
     302                        }
     303                        fd_log_debug( "Def SCTP_NODELAY value : %s\n", nodelay ? "true" : "false");
     304                }
     305
     306                nodelay = 0;    /* We turn ON the Nagle algorithm (probably the default already) */
     307               
     308                /* Set the option to the socket */
     309                CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, sizeof(nodelay))  );
     310               
     311                if (TRACE_BOOL(SCTP_LEVEL)) {
     312                        /* Check new values */
     313                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz)  );
     314                        fd_log_debug( "New SCTP_NODELAY value : %s\n", nodelay ? "true" : "false");
     315                }
     316        }
     317        #else /* SCTP_NODELAY */
     318        TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_NODELAY");
     319        #endif /* SCTP_NODELAY */
     320       
     321        /* Set the interleaving option */
     322        #ifdef SCTP_FRAGMENT_INTERLEAVE
     323        {
     324                int interleave;
     325               
     326                if (TRACE_BOOL(SCTP_LEVEL)) {
     327                        sz = sizeof(interleave);
     328                        /* Read socket defaults */
     329                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz)  );
     330                        if (sz != sizeof(interleave))
     331                        {
     332                                TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(interleave));
     333                                return ENOTSUP;
     334                        }
     335                        fd_log_debug( "Def SCTP_FRAGMENT_INTERLEAVE value : %d\n", interleave);
     336                }
     337
     338                #if 0
     339                interleave = 2; /* Allow partial delivery on several streams at the same time, since we are stream-aware in our security modules */
     340                #else /* 0 */
     341                interleave = 1; /* hmmm actually, we are not yet capable of handling this, and we don t need it. */
     342                #endif /* 0 */
     343               
     344                /* Set the option to the socket */
     345                CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, sizeof(interleave))  );
     346               
     347                if (TRACE_BOOL(SCTP_LEVEL)) {
     348                        /* Check new values */
     349                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz)  );
     350                        fd_log_debug( "New SCTP_FRAGMENT_INTERLEAVE value : %d\n", interleave);
     351                }
     352        }
     353        #else /* SCTP_FRAGMENT_INTERLEAVE */
     354        TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_FRAGMENT_INTERLEAVE");
     355        #endif /* SCTP_FRAGMENT_INTERLEAVE */
     356       
     357        /* Set the v4 mapped addresses option */
     358        #ifdef SCTP_I_WANT_MAPPED_V4_ADDR
     359        {
     360                int v4mapped;
     361               
     362                if (TRACE_BOOL(SCTP_LEVEL)) {
     363                        sz = sizeof(v4mapped);
     364                        /* Read socket defaults */
     365                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz)  );
     366                        if (sz != sizeof(v4mapped))
     367                        {
     368                                TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(v4mapped));
     369                                return ENOTSUP;
     370                        }
     371                        fd_log_debug( "Def SCTP_I_WANT_MAPPED_V4_ADDR value : %s\n", v4mapped ? "true" : "false");
     372                }
     373
     374#ifndef SCTP_USE_MAPPED_ADDRESSES
     375                v4mapped = 0;   /* We don't want v4 mapped addresses */
     376#else /* SCTP_USE_MAPPED_ADDRESSES */
     377                v4mapped = 1;   /* but we may have to, otherwise the bind fails in some environments */
     378#endif /* SCTP_USE_MAPPED_ADDRESSES */
     379               
     380                /* Set the option to the socket */
     381                CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, sizeof(v4mapped))  );
     382               
     383                if (TRACE_BOOL(SCTP_LEVEL)) {
     384                        /* Check new values */
     385                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz)  );
     386                        fd_log_debug( "New SCTP_I_WANT_MAPPED_V4_ADDR value : %s\n", v4mapped ? "true" : "false");
     387                }
     388        }
     389        #else /* SCTP_I_WANT_MAPPED_V4_ADDR */
     390        TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_I_WANT_MAPPED_V4_ADDR");
     391        #endif /* SCTP_I_WANT_MAPPED_V4_ADDR */
     392                           
     393                           
     394        /* Other settable options (draft-ietf-tsvwg-sctpsocket-17):
     395           SO_RCVBUF                    size of receiver window
     396           SO_SNDBUF                    size of pending data to send
     397           SCTP_AUTOCLOSE               for one-to-many only
     398           SCTP_SET_PEER_PRIMARY_ADDR   ask remote peer to use this local address as primary
     399           SCTP_PRIMARY_ADDR            use this address as primary locally
     400           SCTP_ADAPTATION_LAYER        set adaptation layer indication
     401           SCTP_PEER_ADDR_PARAMS        control heartbeat per peer address
     402           SCTP_DEFAULT_SEND_PARAM      parameters for the sendto() call
     403           SCTP_MAXSEG                  max size of fragmented segments -- bound to PMTU
     404           SCTP_AUTH_CHUNK              request authentication of some type of chunk
     405            SCTP_HMAC_IDENT             authentication algorithms
     406            SCTP_AUTH_KEY               set a shared key
     407            SCTP_AUTH_ACTIVE_KEY        set the active key
     408            SCTP_AUTH_DELETE_KEY        remove a key
     409            SCTP_AUTH_DEACTIVATE_KEY    will not use that key anymore
     410           SCTP_DELAYED_SACK            control delayed acks
     411           SCTP_PARTIAL_DELIVERY_POINT  control partial delivery size
     412           SCTP_USE_EXT_RCVINFO         use extended receive info structure (information about the next message if available)
     413           SCTP_MAX_BURST               number of packets that can be burst emitted
     414           SCTP_CONTEXT                 save a context information along with the association.
     415           SCTP_EXPLICIT_EOR            enable sending one message across several send calls
     416           SCTP_REUSE_PORT              share one listening port with several sockets
     417           
     418           read-only options:
     419           SCTP_STATUS                  retrieve info such as number of streams, pending packets, state, ...
     420           SCTP_GET_PEER_ADDR_INFO      get information about a specific peer address of the association.
     421           SCTP_PEER_AUTH_CHUNKS        list of chunks the remote peer wants authenticated
     422           SCTP_LOCAL_AUTH_CHUNKS       list of chunks the local peer wants authenticated
     423           SCTP_GET_ASSOC_NUMBER        number of associations in a one-to-many socket
     424           SCTP_GET_ASSOC_ID_LIST       list of these associations
     425        */
     426       
     427        /* In case of no_ip4, force the v6only option -- is it a valid option for SCTP ? */
     428        #ifdef IPV6_V6ONLY
     429        if (fd_g_config->cnf_flags.no_ip4) {
     430                int opt = 1;
     431                CHECK_SYS(setsockopt(sk, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)));
     432        }
     433        #endif /* IPV6_V6ONLY */
     434       
     435        return 0;
    43436}
    44437
    45 int fd_sctp_get_str_info( int socket, int *in, int *out )
     438
     439/* Post-binding socket options */
     440static int fd_setsockopt_postbind(int sk, int bound_to_default)
    46441{
    47         TODO("Retrieve streams info from the socket");
    48        
    49         return ENOTSUP;
     442        TRACE_ENTRY( "%d %d", sk, bound_to_default);
     443       
     444        CHECK_PARAMS( (sk > 0) );
     445       
     446        /* Set the ASCONF option */
     447        #ifdef SCTP_AUTO_ASCONF
     448        {
     449                int asconf;
     450               
     451                if (TRACE_BOOL(SCTP_LEVEL)) {
     452                        socklen_t sz;
     453
     454                        sz = sizeof(asconf);
     455                        /* Read socket defaults */
     456                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz)  );
     457                        if (sz != sizeof(asconf))
     458                        {
     459                                TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(asconf));
     460                                return ENOTSUP;
     461                        }
     462                        fd_log_debug( "Def SCTP_AUTO_ASCONF value : %s\n", asconf ? "true" : "false");
     463                }
     464
     465                asconf = bound_to_default ? 1 : 0;      /* allow automatic use of added or removed addresses in the association (for bound-all sockets) */
     466               
     467                /* Set the option to the socket */
     468                CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, sizeof(asconf))  );
     469               
     470                if (TRACE_BOOL(SCTP_LEVEL)) {
     471                        socklen_t sz = sizeof(asconf);
     472                        /* Check new values */
     473                        CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz)  );
     474                        fd_log_debug( "New SCTP_AUTO_ASCONF value : %s\n", asconf ? "true" : "false");
     475                }
     476        }
     477        #else /* SCTP_AUTO_ASCONF */
     478        TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_AUTO_ASCONF");
     479        #endif /* SCTP_AUTO_ASCONF */
     480       
     481        return 0;
    50482}
     483
     484/* Create a socket server and bind it according to daemon s configuration */
     485int fd_sctp_create_bind_server( int * sock, struct fd_list * list, uint16_t port )
     486{
     487        int family;
     488        int bind_default;
     489       
     490        TRACE_ENTRY("%p %p %hu", sock, list, port);
     491        CHECK_PARAMS(sock);
     492       
     493        if (fd_g_config->cnf_flags.no_ip6) {
     494                family = AF_INET;
     495        } else {
     496                family = AF_INET6; /* can create socket for both IP and IPv6 */
     497        }
     498       
     499        /* Create the socket */
     500        CHECK_SYS( *sock = socket(family, SOCK_STREAM, IPPROTO_SCTP) );
     501       
     502        /* Set pre-binding socket options, including number of streams etc... */
     503        CHECK_FCT( fd_setsockopt_prebind(*sock) );
     504       
     505        bind_default = (! list) || (FD_IS_LIST_EMPTY(list)) ;
     506redo:
     507        if ( bind_default ) {
     508                /* Implicit endpoints : bind to default addresses */
     509                union {
     510                        sSS  ss;
     511                        sSA  sa;
     512                        sSA4 sin;
     513                        sSA6 sin6;
     514                } s;
     515               
     516                /* 0.0.0.0 and [::] are all zeros */
     517                memset(&s, 0, sizeof(s));
     518               
     519                s.sa.sa_family = family;
     520               
     521                if (family == AF_INET)
     522                        s.sin.sin_port = htons(port);
     523                else
     524                        s.sin6.sin6_port = htons(port);
     525               
     526                CHECK_SYS( bind(*sock, &s.sa, sizeof(s)) );
     527               
     528        } else {
     529                /* Explicit endpoints to bind to from config */
     530               
     531                union {
     532                        sSA     * sa;
     533                        sSA4    *sin;
     534                        sSA6    *sin6;
     535                        uint8_t *buf;
     536                } ptr;
     537                union {
     538                        sSA     * sa;
     539                        uint8_t * buf;
     540                } sar;
     541                int count = 0; /* number of sock addr in sar array */
     542                size_t offset = 0;
     543                struct fd_list * li;
     544               
     545                sar.buf = NULL;
     546               
     547                /* Create a flat array from the list of configured addresses */
     548                for (li = list->next; li != list; li = li->next) {
     549                        struct fd_endpoint * ep = (struct fd_endpoint *)li;
     550                        size_t sz = 0;
     551                       
     552                        if (! (ep->flags & EP_FL_CONF))
     553                                continue;
     554                       
     555                        count++;
     556                       
     557                        /* Size of the new SA we are adding (sar may contain a mix of sockaddr_in and sockaddr_in6) */
     558#ifndef SCTP_USE_MAPPED_ADDRESSES
     559                        if (ep->sa.sa_family == AF_INET6)
     560#else /* SCTP_USE_MAPPED_ADDRESSES */
     561                        if (family == AF_INET6)
     562#endif /* SCTP_USE_MAPPED_ADDRESSES */
     563                                sz = sizeof(sSA6);
     564                        else
     565                                sz = sizeof(sSA4);
     566                       
     567                        /* augment sar to contain the additional info */
     568                        CHECK_MALLOC( sar.buf = realloc(sar.buf, offset + sz)  );
     569                       
     570                        ptr.buf = sar.buf + offset; /* place of the new SA */
     571                        offset += sz; /* update to end of sar */
     572                       
     573                        if (sz == sizeof(sSA4)) {
     574                                memcpy(ptr.buf, &ep->sin, sz);
     575                                ptr.sin->sin_port = htons(port);
     576                        } else {
     577                                if (ep->sa.sa_family == AF_INET) { /* We must map the address */
     578                                        memset(ptr.buf, 0, sz);
     579                                        ptr.sin6->sin6_family = AF_INET6;
     580                                        IN6_ADDR_V4MAP( &ptr.sin6->sin6_addr.s6_addr, ep->sin.sin_addr.s_addr );
     581                                } else {
     582                                        memcpy(ptr.sin6, &ep->sin6, sz);
     583                                }
     584                                ptr.sin6->sin6_port = htons(port);
     585                        }
     586                }
     587               
     588                if (!count) {
     589                        /* None of the addresses in the list came from configuration, we bind to default */
     590                        bind_default = 1;
     591                        goto redo;
     592                }
     593               
     594                if (TRACE_BOOL(SCTP_LEVEL)) {
     595                        int i;
     596                        ptr.buf = sar.buf;
     597                        fd_log_debug("Calling sctp_bindx with the following address array:\n");
     598                        for (i = 0; i < count; i++) {
     599                                TRACE_DEBUG_sSA(FULL, "    - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" );
     600                                ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6) ;
     601                        }
     602                }
     603               
     604                /* Bind to this array */
     605                CHECK_SYS(  sctp_bindx(*sock, sar.sa, count, SCTP_BINDX_ADD_ADDR)  );
     606               
     607                /* We don't need sar anymore */
     608                free(sar.buf);
     609        }
     610       
     611        /* Now, the server is bound, set remaining sockopt */
     612        CHECK_FCT( fd_setsockopt_postbind(*sock, bind_default) );
     613       
     614        /* Debug: show all local listening addresses */
     615        if (TRACE_BOOL(SCTP_LEVEL)) {
     616                sSA *sar;
     617                union {
     618                        sSA     *sa;
     619                        uint8_t *buf;
     620                } ptr;
     621                int sz;
     622               
     623                CHECK_SYS(  sz = sctp_getladdrs(*sock, 0, &sar)  );
     624               
     625                fd_log_debug("SCTP server bound on :\n");
     626                for (ptr.sa = sar; sz-- > 0; ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6)) {
     627                        TRACE_DEBUG_sSA(FULL, "    - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" );
     628                }
     629                sctp_freeladdrs(sar);
     630        }
     631
     632        return 0;
     633}
     634
     635/* Allow clients connections on server sockets */
     636int fd_sctp_listen( int sock )
     637{
     638        TRACE_ENTRY("%d", sock);
     639        CHECK_SYS( listen(sock, 5) );
     640        return 0;
     641}
     642
     643/* Create a client socket and connect to remote server */
     644int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list )
     645{
     646        int family;
     647        int count = 0;
     648        size_t offset = 0, sz;
     649        union {
     650                uint8_t *buf;
     651                sSA     *sa;
     652        } sar;
     653        union {
     654                uint8_t *buf;
     655                sSA     *sa;
     656                sSA4    *sin;
     657                sSA6    *sin6;
     658        } ptr;
     659        struct fd_list * li;
     660        int ret;
     661       
     662        sar.buf = NULL;
     663       
     664        TRACE_ENTRY("%p %i %hu %p", sock, no_ip6, port, list);
     665        CHECK_PARAMS( sock && list && (!FD_IS_LIST_EMPTY(list)) );
     666       
     667        if (no_ip6) {
     668                family = AF_INET;
     669        } else {
     670                family = AF_INET6;
     671        }
     672       
     673        /* Create the socket */
     674        CHECK_SYS( *sock = socket(family, SOCK_STREAM, IPPROTO_SCTP) );
     675       
     676        /* Cleanup if we are cancelled */
     677        pthread_cleanup_push(fd_cleanup_socket, sock);
     678       
     679        /* Set the socket options */
     680        CHECK_FCT_DO( ret = fd_setsockopt_prebind(*sock), goto fail );
     681       
     682        /* Create the array of addresses for sctp_connectx */
     683        for (li = list->next; li != list; li = li->next) {
     684                struct fd_endpoint * ep = (struct fd_endpoint *) li;
     685               
     686                count++;
     687               
     688                /* Size of the new SA we are adding (sar may contain a mix of sockaddr_in and sockaddr_in6) */
     689#ifndef SCTP_USE_MAPPED_ADDRESSES
     690                if (ep->sa.sa_family == AF_INET6)
     691#else /* SCTP_USE_MAPPED_ADDRESSES */
     692                if (family == AF_INET6)
     693#endif /* SCTP_USE_MAPPED_ADDRESSES */
     694                        sz = sizeof(sSA6);
     695                else
     696                        sz = sizeof(sSA4);
     697               
     698                /* augment sar to contain the additional info */
     699                CHECK_MALLOC_DO( sar.buf = realloc(sar.buf, offset + sz), { ret = ENOMEM; goto fail; }  );
     700
     701                ptr.buf = sar.buf + offset; /* place of the new SA */
     702                offset += sz; /* update to end of sar */
     703                       
     704                if (sz == sizeof(sSA4)) {
     705                        memcpy(ptr.buf, &ep->sin, sz);
     706                        ptr.sin->sin_port = htons(port);
     707                } else {
     708                        if (ep->sa.sa_family == AF_INET) { /* We must map the address */
     709                                memset(ptr.buf, 0, sz);
     710                                ptr.sin6->sin6_family = AF_INET6;
     711                                IN6_ADDR_V4MAP( &ptr.sin6->sin6_addr.s6_addr, ep->sin.sin_addr.s_addr );
     712                        } else {
     713                                memcpy(ptr.sin6, &ep->sin6, sz);
     714                        }
     715                        ptr.sin6->sin6_port = htons(port);
     716                }
     717        }
     718       
     719        /* Try connecting */
     720        TRACE_DEBUG(FULL, "Attempting SCTP connection (%d addresses attempted)...", count);
     721        CHECK_SYS_DO( sctp_connectx(*sock, sar.sa, count), { ret = errno; goto fail; } );
     722        free(sar.buf); sar.buf = NULL;
     723       
     724        /* Set the remaining sockopts */
     725        CHECK_FCT_DO( ret = fd_setsockopt_postbind(*sock, 1), goto fail );
     726       
     727        /* Done! */
     728        pthread_cleanup_pop(0);
     729        return 0;
     730       
     731fail:
     732        if (*sock > 0) {
     733                shutdown(*sock, SHUT_RDWR);
     734                *sock = -1;
     735        }
     736        free(sar.buf);
     737        return ret;
     738}
     739
     740/* Retrieve streams information from a connected association -- optionaly provide the primary address */
     741int fd_sctp_get_str_info( int sock, uint16_t *in, uint16_t *out, sSS *primary )
     742{
     743        struct sctp_status status;
     744        socklen_t sz = sizeof(status);
     745       
     746        TRACE_ENTRY("%d %p %p %p", sock, in, out, primary);
     747        CHECK_PARAMS( (sock > 0) && in && out );
     748       
     749        /* Read the association parameters */
     750        memset(&status, 0, sizeof(status));
     751        CHECK_SYS(  getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &status, &sz) );
     752        if (sz != sizeof(status))
     753        {
     754                TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %zd", sz, sizeof(status));
     755                return ENOTSUP;
     756        }
     757        if (TRACE_BOOL(SCTP_LEVEL)) {
     758                fd_log_debug( "SCTP_STATUS : sstat_state                  : %i\n" , status.sstat_state);
     759                fd_log_debug( "              sstat_rwnd                   : %u\n" , status.sstat_rwnd);
     760                fd_log_debug( "              sstat_unackdata              : %hu\n", status.sstat_unackdata);
     761                fd_log_debug( "              sstat_penddata               : %hu\n", status.sstat_penddata);
     762                fd_log_debug( "              sstat_instrms                : %hu\n", status.sstat_instrms);
     763                fd_log_debug( "              sstat_outstrms               : %hu\n", status.sstat_outstrms);
     764                fd_log_debug( "              sstat_fragmentation_point    : %u\n" , status.sstat_fragmentation_point);
     765                fd_log_debug( "              sstat_primary.spinfo_address : ");
     766                sSA_DUMP_NODE_SERV(&status.sstat_primary.spinfo_address, NI_NUMERICHOST | NI_NUMERICSERV );
     767                fd_log_debug( "\n" );
     768                fd_log_debug( "              sstat_primary.spinfo_state   : %d\n" , status.sstat_primary.spinfo_state);
     769                fd_log_debug( "              sstat_primary.spinfo_cwnd    : %u\n" , status.sstat_primary.spinfo_cwnd);
     770                fd_log_debug( "              sstat_primary.spinfo_srtt    : %u\n" , status.sstat_primary.spinfo_srtt);
     771                fd_log_debug( "              sstat_primary.spinfo_rto     : %u\n" , status.sstat_primary.spinfo_rto);
     772                fd_log_debug( "              sstat_primary.spinfo_mtu     : %u\n" , status.sstat_primary.spinfo_mtu);
     773        }
     774       
     775        *in = status.sstat_instrms;
     776        *out = status.sstat_outstrms;
     777       
     778        if (primary)
     779                memcpy(primary, &status.sstat_primary.spinfo_address, sizeof(sSS));
     780       
     781        return 0;
     782}
     783
     784/* Get the list of local endpoints of the socket */
     785int fd_sctp_get_local_ep(int sock, struct fd_list * list)
     786{
     787        union {
     788                sSA     *sa;
     789                uint8_t *buf;
     790        } ptr;
     791       
     792        sSA * data;
     793        int count;
     794       
     795        TRACE_ENTRY("%d %p", sock, list);
     796        CHECK_PARAMS(list);
     797       
     798        /* Read the list on the socket */
     799        CHECK_SYS( count = sctp_getladdrs(sock, 0, &data)  );
     800        ptr.sa = data;
     801       
     802        while (count) {
     803                socklen_t sl;
     804                switch (ptr.sa->sa_family) {
     805                        case AF_INET:   sl = sizeof(sSA4); break;
     806                        case AF_INET6:  sl = sizeof(sSA6); break;
     807                        default:
     808                                TRACE_DEBUG(INFO, "Unkown address family returned in sctp_getladdrs: %d", ptr.sa->sa_family);
     809                }
     810                               
     811                CHECK_FCT( fd_ep_add_merge( list, ptr.sa, sl, EP_FL_LL ) );
     812                ptr.buf += sl;
     813                count --;
     814        }
     815       
     816        /* Free the list */
     817        sctp_freeladdrs(data);
     818       
     819        /* Now get the primary address, the add function will take care of merging with existing entry */
     820        {
     821                 
     822                struct sctp_status status;
     823                socklen_t sz = sizeof(status);
     824                int ret;
     825               
     826                memset(&status, 0, sizeof(status));
     827                /* Attempt to use SCTP_STATUS message to retrieve the primary address */
     828                ret = getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &status, &sz);
     829                if (sz != sizeof(status))
     830                        ret = -1;
     831                sz = sizeof(sSS);
     832                if (ret < 0)
     833                {
     834                        /* Fallback to getsockname -- not recommended by draft-ietf-tsvwg-sctpsocket-19#section-7.4 */
     835                        CHECK_SYS(getsockname(sock, (sSA *)&status.sstat_primary.spinfo_address, &sz));
     836                }
     837                       
     838                CHECK_FCT( fd_ep_add_merge( list, (sSA *)&status.sstat_primary.spinfo_address, sz, EP_FL_PRIMARY ) );
     839        }
     840       
     841        return 0;
     842}
     843
     844/* Get the list of remote endpoints of the socket */
     845int fd_sctp_get_remote_ep(int sock, struct fd_list * list)
     846{
     847        union {
     848                sSA     *sa;
     849                uint8_t *buf;
     850        } ptr;
     851       
     852        sSA * data;
     853        int count;
     854       
     855        TRACE_ENTRY("%d %p", sock, list);
     856        CHECK_PARAMS(list);
     857       
     858        /* Read the list on the socket */
     859        CHECK_SYS( count = sctp_getpaddrs(sock, 0, &data)  );
     860        ptr.sa = data;
     861       
     862        while (count) {
     863                socklen_t sl;
     864                switch (ptr.sa->sa_family) {
     865                        case AF_INET:   sl = sizeof(sSA4); break;
     866                        case AF_INET6:  sl = sizeof(sSA6); break;
     867                        default:
     868                                TRACE_DEBUG(INFO, "Unkown address family returned in sctp_getpaddrs: %d", ptr.sa->sa_family);
     869                }
     870                               
     871                CHECK_FCT( fd_ep_add_merge( list, ptr.sa, sl, EP_FL_LL ) );
     872                ptr.buf += sl;
     873                count --;
     874        }
     875       
     876        /* Free the list */
     877        sctp_freepaddrs(data);
     878       
     879        /* Now get the primary address, the add function will take care of merging with existing entry */
     880        {
     881                sSS ss;
     882                socklen_t sl = sizeof(sSS);
     883       
     884                CHECK_SYS(getpeername(sock, (sSA *)&ss, &sl));
     885                CHECK_FCT( fd_ep_add_merge( list, (sSA *)&ss, sl, EP_FL_PRIMARY ) );
     886        }
     887       
     888        /* Done! */
     889        return 0;
     890}
     891
     892/* Send a buffer over a specified stream */
     893int fd_sctp_sendstr(int sock, uint16_t strid, uint8_t * buf, size_t len)
     894{
     895        struct msghdr mhdr;
     896        struct iovec  iov;
     897        struct {
     898                struct cmsghdr          hdr;
     899                struct sctp_sndrcvinfo  sndrcv;
     900        } anci;
     901        ssize_t ret;
     902       
     903        TRACE_ENTRY("%d %hu %p %zd", sock, strid, buf, len);
     904       
     905        memset(&mhdr, 0, sizeof(mhdr));
     906        memset(&iov,  0, sizeof(iov));
     907        memset(&anci, 0, sizeof(anci));
     908       
     909        /* IO Vector: message data */
     910        iov.iov_base = buf;
     911        iov.iov_len  = len;
     912       
     913        /* Anciliary data: specify SCTP stream */
     914        anci.hdr.cmsg_len   = sizeof(anci);
     915        anci.hdr.cmsg_level = IPPROTO_SCTP;
     916        anci.hdr.cmsg_type  = SCTP_SNDRCV;
     917        anci.sndrcv.sinfo_stream = strid;
     918        /* note : we could store other data also, for example in .sinfo_ppid for remote peer or in .sinfo_context for errors. */
     919       
     920        /* We don't use mhdr.msg_name here; it could be used to specify an address different from the primary */
     921       
     922        mhdr.msg_iov    = &iov;
     923        mhdr.msg_iovlen = 1;
     924       
     925        mhdr.msg_control    = &anci;
     926        mhdr.msg_controllen = sizeof(anci);
     927       
     928        TRACE_DEBUG(FULL, "Sending %db data on stream %hu of socket %d", len, strid, sock);
     929       
     930        CHECK_SYS( ret = sendmsg(sock, &mhdr, 0) );
     931        ASSERT( ret == len ); /* There should not be partial delivery with sendmsg... */
     932       
     933        return 0;
     934}
     935
     936/* Receive the next data from the socket, or next notification */
     937int fd_sctp_recvmeta(int sock, uint16_t * strid, uint8_t ** buf, size_t * len, int *event)
     938{
     939        ssize_t                  ret = 0;
     940        struct msghdr            mhdr;
     941        char                     ancidata[ CMSG_BUF_LEN ];
     942        struct iovec             iov;
     943        uint8_t                 *data = NULL;
     944        size_t                   bufsz = 0, datasize = 0;
     945        size_t                   mempagesz = sysconf(_SC_PAGESIZE); /* We alloc buffer by memory pages for efficiency */
     946       
     947        TRACE_ENTRY("%d %p %p %p %p", sock, strid, buf, len, event);
     948        CHECK_PARAMS( (sock > 0) && buf && len && event );
     949       
     950        /* Cleanup out parameters */
     951        *buf = NULL;
     952        *len = 0;
     953        *event = 0;
     954       
     955        /* Prepare header for receiving message */
     956        memset(&mhdr, 0, sizeof(mhdr));
     957        mhdr.msg_iov    = &iov;
     958        mhdr.msg_iovlen = 1;
     959        mhdr.msg_control    = &ancidata;
     960        mhdr.msg_controllen = sizeof(ancidata);
     961       
     962        /* We will loop while all data is not received. */
     963incomplete:
     964        if (datasize == bufsz) {
     965                /* The buffer is full, enlarge it */
     966                bufsz += mempagesz;
     967                CHECK_MALLOC( data = realloc(data, bufsz) );
     968        }
     969        /* the new data will be received following the preceding */
     970        memset(&iov,  0, sizeof(iov));
     971        iov.iov_base = data + datasize ;
     972        iov.iov_len  = bufsz - datasize;
     973
     974        /* Receive data from the socket */
     975        pthread_cleanup_push(free, data);
     976        ret = recvmsg(sock, &mhdr, 0);
     977        pthread_cleanup_pop(0);
     978       
     979        /* Handle errors */
     980        if (ret <= 0) { /* Socket is closed, or an error occurred */
     981                CHECK_SYS_DO(ret, /* to log in case of error */);
     982                free(data);
     983                *event = FDEVP_CNX_ERROR;
     984                return 0;
     985        }
     986       
     987        /* Update the size of data we received */
     988        datasize += ret;
     989
     990        /* SCTP provides an indication when we received a full record; loop if it is not the case */
     991        if ( ! (mhdr.msg_flags & MSG_EOR) ) {
     992                goto incomplete;
     993        }
     994       
     995        TRACE_DEBUG(FULL, "Received %db data on socket %d", datasize, sock);
     996       
     997        /* Handle the case where the data received is a notification */
     998        if (mhdr.msg_flags & MSG_NOTIFICATION) {
     999                union sctp_notification * notif = (union sctp_notification *) data;
     1000               
     1001                switch (notif->sn_header.sn_type) {
     1002                       
     1003                        case SCTP_ASSOC_CHANGE:
     1004                                TRACE_DEBUG(FULL, "Received SCTP_ASSOC_CHANGE notification");
     1005                                TRACE_DEBUG(SCTP_LEVEL, "    state : %hu", notif->sn_assoc_change.sac_state);
     1006                                TRACE_DEBUG(SCTP_LEVEL, "    error : %hu", notif->sn_assoc_change.sac_error);
     1007                                TRACE_DEBUG(SCTP_LEVEL, "    instr : %hu", notif->sn_assoc_change.sac_inbound_streams);
     1008                                TRACE_DEBUG(SCTP_LEVEL, "   outstr : %hu", notif->sn_assoc_change.sac_outbound_streams);
     1009                               
     1010                                *event = FDEVP_CNX_EP_CHANGE;
     1011                                break;
     1012       
     1013                        case SCTP_PEER_ADDR_CHANGE:
     1014                                TRACE_DEBUG(FULL, "Received SCTP_PEER_ADDR_CHANGE notification");
     1015                                TRACE_DEBUG_sSA(SCTP_LEVEL, "    intf_change : ", &(notif->sn_paddr_change.spc_aaddr), NI_NUMERICHOST | NI_NUMERICSERV, "" );
     1016                                TRACE_DEBUG(SCTP_LEVEL, "          state : %d", notif->sn_paddr_change.spc_state);
     1017                                TRACE_DEBUG(SCTP_LEVEL, "          error : %d", notif->sn_paddr_change.spc_error);
     1018                               
     1019                                *event = FDEVP_CNX_EP_CHANGE;
     1020                                break;
     1021       
     1022                        case SCTP_SEND_FAILED:
     1023                                TRACE_DEBUG(FULL, "Received SCTP_SEND_FAILED notification");
     1024                                TRACE_DEBUG(SCTP_LEVEL, "    len : %hu", notif->sn_send_failed.ssf_length);
     1025                                TRACE_DEBUG(SCTP_LEVEL, "    err : %d",  notif->sn_send_failed.ssf_error);
     1026                               
     1027                                *event = FDEVP_CNX_ERROR;
     1028                                break;
     1029                       
     1030                        case SCTP_REMOTE_ERROR:
     1031                                TRACE_DEBUG(FULL, "Received SCTP_REMOTE_ERROR notification");
     1032                                TRACE_DEBUG(SCTP_LEVEL, "    err : %hu", ntohs(notif->sn_remote_error.sre_error));
     1033                                TRACE_DEBUG(SCTP_LEVEL, "    len : %hu", ntohs(notif->sn_remote_error.sre_length));
     1034                               
     1035                                *event = FDEVP_CNX_ERROR;
     1036                                break;
     1037       
     1038                        case SCTP_SHUTDOWN_EVENT:
     1039                                TRACE_DEBUG(FULL, "Received SCTP_SHUTDOWN_EVENT notification");
     1040                               
     1041                                *event = FDEVP_CNX_ERROR;
     1042                                break;
     1043                       
     1044                        default:       
     1045                                TRACE_DEBUG(FULL, "Received unknown notification %d, assume error", notif->sn_header.sn_type);
     1046                                *event = FDEVP_CNX_ERROR;
     1047                }
     1048               
     1049                free(data);
     1050                return 0;
     1051        }
     1052       
     1053        /* From this point, we have received a message */
     1054        *event = FDEVP_CNX_MSG_RECV;
     1055        *buf = data;
     1056        *len = datasize;
     1057       
     1058        if (strid) {
     1059                struct cmsghdr          *hdr;
     1060                struct sctp_sndrcvinfo  *sndrcv;
     1061               
     1062                /* Handle the anciliary data */
     1063                for (hdr = CMSG_FIRSTHDR(&mhdr); hdr; hdr = CMSG_NXTHDR(&mhdr, hdr)) {
     1064
     1065                        /* We deal only with anciliary data at SCTP level */
     1066                        if (hdr->cmsg_level != IPPROTO_SCTP) {
     1067                                TRACE_DEBUG(FULL, "Received some anciliary data at level %d, skipped", hdr->cmsg_level);
     1068                                continue;
     1069                        }
     1070                       
     1071                        /* Also only interested in SCTP_SNDRCV message for the moment */
     1072                        if (hdr->cmsg_type != SCTP_SNDRCV) {
     1073                                TRACE_DEBUG(FULL, "Anciliary block IPPROTO_SCTP / %d, skipped", hdr->cmsg_type);
     1074                                continue;
     1075                        }
     1076                       
     1077                        sndrcv = (struct sctp_sndrcvinfo *) CMSG_DATA(hdr);
     1078                        if (TRACE_BOOL(SCTP_LEVEL)) {
     1079                                fd_log_debug( "Anciliary block IPPROTO_SCTP / SCTP_SNDRCV\n");
     1080                                fd_log_debug( "    sinfo_stream    : %hu\n", sndrcv->sinfo_stream);
     1081                                fd_log_debug( "    sinfo_ssn       : %hu\n", sndrcv->sinfo_ssn);
     1082                                fd_log_debug( "    sinfo_flags     : %hu\n", sndrcv->sinfo_flags);
     1083                                /* fd_log_debug( "    sinfo_pr_policy : %hu\n", sndrcv->sinfo_pr_policy); */
     1084                                fd_log_debug( "    sinfo_ppid      : %u\n" , sndrcv->sinfo_ppid);
     1085                                fd_log_debug( "    sinfo_context   : %u\n" , sndrcv->sinfo_context);
     1086                                /* fd_log_debug( "    sinfo_pr_value  : %u\n" , sndrcv->sinfo_pr_value); */
     1087                                fd_log_debug( "    sinfo_tsn       : %u\n" , sndrcv->sinfo_tsn);
     1088                                fd_log_debug( "    sinfo_cumtsn    : %u\n" , sndrcv->sinfo_cumtsn);
     1089                        }
     1090
     1091                        *strid = sndrcv->sinfo_stream;
     1092                }
     1093        }
     1094       
     1095        return 0;
     1096}
  • freeDiameter/server.c

    r20 r29  
    3636#include "fD.h"
    3737
    38 /* This file contains the server (listening) part of the daemon */
    39 
    40 struct fd_list          FD_SERVERS = FD_LIST_INITIALIZER(FD_SERVERS);   /* The list of all server sockets */
    41 /* We don't need to protect this list, it is only accessed from the main thread. */
    42 
    43 /* Server (listening socket) information */
     38/* Server (listening) part of the daemon */
     39
     40struct fd_list          FD_SERVERS = FD_LIST_INITIALIZER(FD_SERVERS);   /* The list of all server objects */
     41/* We don't need to protect this list, it is only accessed from the main daemon thread. */
     42
     43/* Servers information */
    4444struct server {
    4545        struct fd_list  chain;          /* link in the FD_SERVERS list */
    4646
    47         int             socket;         /* server socket, or <= 0 */
    48        
     47        struct cnxctx * conn;           /* server connection context (listening socket) */
    4948        int             proto;          /* IPPROTO_TCP or IPPROTO_SCTP */
    5049        int             secur;          /* TLS is started immediatly after connection ? */
    5150       
    52         pthread_t       serv_thr;       /* The thread listening for new connections */
    53         int             serv_status;    /* 0 : not created; 1 : running; 2 : terminated */
    54        
    55         pthread_mutex_t clients_mtx;    /* Mutex to protect the list of clients connected to the thread */
    56         struct fd_list  clients;        /* The list of clients connecting to this server, which information is not yet known */
    57        
    58         char *          serv_name;      /* A string to identify this server */
     51        pthread_t       thr;            /* The thread listening for new connections */
     52        int             status;         /* 0 : not created; 1 : running; 2 : terminated */
     53       
     54        struct fd_list  clients;        /* List of clients connected to this server, not yet identified */
     55        pthread_mutex_t clients_mtx;    /* Mutex to protect the list of clients */
    5956};
    6057
    61 /* Client (connected remote endpoint, not received CER yet) information */
     58/* Client information (connecting peer for which we don't have the CER yet) */
    6259struct client {
    6360        struct fd_list   chain; /* link in the server's list of clients */
    64        
    65         struct cnxctx   *conn;  /* Parameters of the connection; sends its events to the ev fifo bellow */
    66        
    67         struct timespec  ts;    /* Delay for receiving CER: INCNX_TIMEOUT */
    68         struct fifo     *ev;    /* Events of the connection -- allowed: TIMEOUT, ERROR (cnx, tls), MSG_RCV (CER, other=>error) */
    69        
    70         pthread_t        cli_thr; /* connection state machine (simplified PSM) */
     61        struct cnxctx   *conn;  /* Parameters of the connection */
     62        struct timespec  ts;    /* Deadline for receiving CER (after INCNX_TIMEOUT) */
     63        pthread_t        thr;   /* connection state machine */
    7164};
    7265
    73 /* Parameter for the thread handling the new connected client, to avoid bloking the server thread */
    74 struct cli_fast {
    75         struct server * serv;
    76         int             sock;
    77         sSS             ss;
    78         socklen_t       sslen;
    79 };
    80 
    81 
    82 /* This thread is called when a new client had just connected */
    83 static void * handle_client_fast(void * arg)
    84 {
    85         struct cli_fast * cf = arg;
    86         struct client * c = NULL;
     66
     67/* Dump all servers information */
     68void fd_servers_dump()
     69{
     70        struct fd_list * li, *cli;
     71       
     72        fd_log_debug("Dumping servers list :\n");
     73        for (li = FD_SERVERS.next; li != &FD_SERVERS; li = li->next) {
     74                struct server * s = (struct server *)li;
     75                fd_log_debug("  Serv %p '%s': %s, %s, %s\n",
     76                                s, fd_cnx_getid(s->conn),
     77                                IPPROTO_NAME( s->proto ),
     78                                s->secur ? "Secur" : "NotSecur",
     79                                (s->status == 0) ? "Thread not created" :
     80                                ((s->status == 1) ? "Thread running" :
     81                                ((s->status == 2) ? "Thread terminated" :
     82                                                          "Thread status unknown")));
     83                /* Dump the client list of this server */
     84                (void) pthread_mutex_lock(&s->clients_mtx);
     85                for (cli = s->clients.next; cli != &s->clients; cli = cli->next) {
     86                        struct client * c = (struct client *)cli;
     87                        char bufts[128];
     88                        fd_log_debug("     Connected: '%s' (timeout: %s)\n",
     89                                        fd_cnx_getid(c->conn),
     90                                        fd_log_time(&c->ts, bufts, sizeof(bufts)));
     91                }
     92                (void) pthread_mutex_unlock(&s->clients_mtx);
     93        }
     94}
     95
     96
     97/* The state machine to handle incoming connection before the remote peer is identified */
     98static void * client_sm(void * arg)
     99{
     100        struct client * c = arg;
     101        struct server * s = NULL;
     102        uint8_t       * buf = NULL;
     103        size_t          bufsz;
     104        struct msg    * msg = NULL;
     105        struct msg_hdr *hdr = NULL;
     106       
     107        TRACE_ENTRY("%p", c);
     108       
     109        CHECK_PARAMS_DO(c && c->conn && c->chain.head, goto fatal_error );
     110       
     111        s = c->chain.head->o;
    87112       
    88113        /* Name the current thread */
    89         ASSERT(arg);
    90         {
    91                 char addr[128];
    92                 int offset = snprintf(addr, sizeof(addr), "Srv %d/Cli %d : ", cf->serv->socket, cf->sock);
    93                 int rc = getnameinfo((sSA *)&cf->ss, sizeof(sSS), addr + offset, sizeof(addr) - offset, NULL, 0, 0);
    94                 if (rc)
    95                         memcpy(addr + offset, gai_strerror(rc), sizeof(addr) - offset);
    96                
    97                 if (TRACE_BOOL(INFO)) {
    98                         fd_log_debug( "New connection %s, sock %d, from '%s'\n", cf->serv->serv_name, cf->sock, addr + offset);
     114        fd_log_threadname ( fd_cnx_getid(c->conn) );
     115       
     116        /* Handshake if we are a secure server port, or start clear otherwise */
     117        if (s->secur) {
     118                int ret = fd_cnx_handshake(c->conn, GNUTLS_SERVER, NULL, NULL);
     119                if (ret != 0) {
     120                        if (TRACE_BOOL(INFO)) {
     121                                fd_log_debug("TLS handshake failed for client '%s', connection aborted.\n", fd_cnx_getid(c->conn));
     122                        }
     123                        goto cleanup;
    99124                }
    100        
    101                 fd_log_threadname ( addr );
    102         }
    103        
    104         /* Create a client structure */
    105         CHECK_MALLOC_DO( c = malloc(sizeof(struct client)), goto early_error );
    106         memset(c, 0, sizeof(struct client));
    107         fd_list_init(&c->chain, c);
    108         c->cli_thr = pthread_self();
    109        
    110         /* Create the connection context */
    111         CHECK_MALLOC_DO( c->conn = fd_cnx_init(cf->sock, cf->serv->proto), goto early_error );
    112        
    113         /* In case we are a secure server, handshake now */
    114         if (cf->serv->secur) {
    115                
    116                 TODO("Continue");
    117         }
    118        
    119         /* Save the client in the list */
    120         CHECK_POSIX_DO( pthread_mutex_lock( &cf->serv->clients_mtx ), goto early_error );
    121         fd_list_insert_before(&cf->serv->clients, &c->chain);
    122         CHECK_POSIX_DO( pthread_mutex_unlock( &cf->serv->clients_mtx ), goto error );
    123        
    124        
    125        
    126        
    127 early_error:
    128         TRACE_DEBUG(INFO, "Thread is detaching to die");
     125        } else {
     126                CHECK_FCT_DO( fd_cnx_start_clear(c->conn, 0), goto cleanup );
     127        }
     128       
     129        /* Set the timeout to receive the first message */
     130        CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &c->ts), goto fatal_error );
     131        c->ts.tv_sec += INCNX_TIMEOUT;
     132       
     133        /* Receive the first Diameter message on the connection -- cleanup in case of timeout */
     134        CHECK_FCT_DO( fd_cnx_receive(c->conn, &c->ts, &buf, &bufsz), goto cleanup );
     135       
     136        TRACE_DEBUG(FULL, "Received %zdb from new client '%s'", bufsz, fd_cnx_getid(c->conn));
     137       
     138        /* Try parsing this message */
     139        CHECK_FCT_DO( fd_msg_parse_buffer( &buf, bufsz, &msg ), /* Parsing failed */ goto cleanup );
     140       
     141        /* We expect a CER, it must parse with our dictionary and rules */
     142        CHECK_FCT_DO( fd_msg_parse_rules( msg, fd_g_config->cnf_dict, NULL ), /* Parsing failed -- trace details ? */ goto cleanup );
     143       
     144        if (TRACE_BOOL(FULL)) {
     145                fd_log_debug("Received Diameter message from new client '%s':\n", fd_cnx_getid(c->conn));
     146                fd_msg_dump_walk(FULL, msg);
     147        }
     148       
     149        /* Now check we received a CER */
     150        CHECK_FCT_DO( fd_msg_hdr ( msg, &hdr ), goto fatal_error );
     151        CHECK_PARAMS_DO( (hdr->msg_appl == 0) && (hdr->msg_flags & CMD_FLAG_REQUEST) && (hdr->msg_code == CC_CAPABILITIES_EXCHANGE),
     152                { fd_log_debug("Connection '%s', expecting CER, received something else, closing...\n", fd_cnx_getid(c->conn)); goto cleanup; } );
     153       
     154        /* Finally, pass the information to the peers module which will handle it next */
     155        pthread_cleanup_push((void *)fd_cnx_destroy, c->conn);
     156        pthread_cleanup_push((void *)fd_msg_free, msg);
     157        CHECK_FCT_DO( fd_peer_handle_newCER( &msg, &c->conn ), goto cleanup );
     158        pthread_cleanup_pop(0);
     159        pthread_cleanup_pop(0);
     160       
     161        /* The end, we cleanup the client structure */
     162cleanup:
     163        /* Unlink the client structure */
     164        CHECK_POSIX_DO( pthread_mutex_lock(&s->clients_mtx), goto fatal_error );
     165        fd_list_unlink( &c->chain );
     166        CHECK_POSIX_DO( pthread_mutex_unlock(&s->clients_mtx), goto fatal_error );
     167       
     168        /* Destroy the connection object if present */
     169        if (c->conn)
     170                fd_cnx_destroy(c->conn);
     171       
     172        /* Cleanup the received buffer if any */
     173        free(buf);
     174       
     175        /* Cleanup the parsed message if any */
     176        if (msg) {
     177                CHECK_FCT_DO( fd_msg_free(msg), /* continue */);
     178        }
     179       
     180        /* Detach the thread, cleanup the client structure */
    129181        pthread_detach(pthread_self());
    130         shutdown(cf->sock, SHUT_RDWR);
    131         free(cf);
    132182        free(c);
     183        return NULL;
     184       
     185fatal_error:    /* This has effect to terminate the daemon */
     186        CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
     187        return NULL;
     188}
     189
     190/* The thread managing a server */
     191static void * serv_th(void * arg)
     192{
     193        struct server *s = (struct server *)arg;
     194       
     195        CHECK_PARAMS_DO(s, goto error);
     196        fd_log_threadname ( fd_cnx_getid(s->conn) );
     197        s->status = 1;
     198       
     199        /* Accept incoming connections */
     200        CHECK_FCT_DO( fd_cnx_serv_listen(s->conn), goto error );
     201       
     202        do {
     203                struct client * c = NULL;
     204                struct cnxctx * conn = NULL;
     205               
     206                /* Wait for a new client */
     207                CHECK_MALLOC_DO( conn = fd_cnx_serv_accept(s->conn), goto error );
     208               
     209                TRACE_DEBUG(FULL, "New connection accepted");
     210               
     211                /* Create a client structure */
     212                CHECK_MALLOC_DO( c = malloc(sizeof(struct client)), goto error );
     213                memset(c, 0, sizeof(struct client));
     214                fd_list_init(&c->chain, c);
     215                c->conn = conn;
     216               
     217                /* Save the client in the list */
     218                CHECK_POSIX_DO( pthread_mutex_lock( &s->clients_mtx ), goto error );
     219                fd_list_insert_before(&s->clients, &c->chain);
     220                CHECK_POSIX_DO( pthread_mutex_unlock( &s->clients_mtx ), goto error );
     221
     222                /* Start the client thread */
     223                CHECK_POSIX_DO( pthread_create( &c->thr, NULL, client_sm, c ), goto error );
     224               
     225        } while (1);
     226       
    133227error: 
    134         TRACE_DEBUG(INFO, "Thread is terminating");
    135         CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, NULL), );
    136         return NULL;
    137 }
    138 
    139 /* The thread for the server */
    140 static void * serv_th(void * arg)
    141 {
    142         struct server *sv = (struct server *)arg;
    143         struct cli_fast cf;
    144        
    145         CHECK_PARAMS_DO(sv, goto error);
    146         fd_log_threadname ( sv->serv_name );
    147         sv->serv_status = 1;
    148        
    149         memset(&cf, 0, sizeof(struct cli_fast));
    150         cf.serv = sv;
    151        
    152        
    153         /* Accept incoming connections */
    154         CHECK_SYS_DO(  listen(sv->socket, 5), goto error );
    155        
    156         do {
    157                 struct cli_fast * ncf;
    158                 pthread_t         thr;
    159                
    160                 /* Re-init socket size */
    161                 cf.sslen = sizeof(sSS);
    162                
    163                 /* Wait for a new client */
    164                 CHECK_SYS_DO( cf.sock = accept(sv->socket, (sSA *)&cf.ss, &cf.sslen), goto error );
    165                
    166                 TRACE_DEBUG(FULL, "New connection accepted");
    167                
    168                 /* Create the copy for the client thread */
    169                 CHECK_MALLOC_DO( ncf = malloc(sizeof(struct cli_fast)), goto error );
    170                 memcpy(ncf, &cf, sizeof(struct cli_fast));
    171                
    172                 /* Create the thread to handle the new incoming connection */
    173                 CHECK_POSIX_DO( pthread_create( &thr /* we don't use it, but NULL is not standard */, NULL, handle_client_fast, ncf), goto error );
    174                
    175         } while (1);
    176        
    177 error: 
    178         if (sv)
    179                 sv->serv_status = 2;
     228        if (s)
     229                s->status = 2;
    180230        /* Send error signal to the daemon */
    181231        TRACE_DEBUG(INFO, "An error occurred in server module! Thread is terminating...");
    182         CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, NULL), );
     232        CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
    183233
    184234        return NULL;
     
    187237
    188238/* Create a new server structure */
    189 static struct server * new_serv( int proto, int secur, int socket )
    190 {
    191         char buf[32];
    192         char * sn = NULL;
     239static struct server * new_serv( int proto, int secur )
     240{
    193241        struct server * new;
    194        
    195         /* Create the server debug name */
    196         buf[sizeof(buf) - 1] = '\0';
    197         snprintf(buf, sizeof(buf) - 1, "Serv %d (%s%s)", socket, IPPROTO_NAME( proto ), secur ? "s" : "");
    198         CHECK_MALLOC_DO( sn = strdup(buf), return NULL );
    199242       
    200243        /* New server structure */
     
    203246        memset(new, 0, sizeof(struct server));
    204247        fd_list_init(&new->chain, new);
    205         new->socket = socket;
    206248        new->proto = proto;
    207249        new->secur = secur;
     
    209251        fd_list_init(&new->clients, new);
    210252       
    211         new->serv_name = sn;
    212        
    213253        return new;
    214 }
    215 
    216 /* Dump all servers information */
    217 void fd_servers_dump()
    218 {
    219         struct fd_list * li;
    220        
    221         fd_log_debug("Dumping servers list :\n");
    222         for (li = FD_SERVERS.next; li != &FD_SERVERS; li = li->next) {
    223                 struct server * sv = (struct server *)li;
    224                 fd_log_debug("  Serv '%s': %s(%d), %s, %s, %s\n",
    225                                 sv->serv_name,
    226                                 (sv->socket > 0) ? "Open" : "Closed", sv->socket,
    227                                 IPPROTO_NAME( sv->proto ),
    228                                 sv->secur ? "Secur" : "NotSecur",
    229                                 (sv->serv_status == 0) ? "Thread not created" :
    230                                 ((sv->serv_status == 1) ? "Thread running" :
    231                                 ((sv->serv_status == 2) ? "Thread terminated" :
    232                                                           "Thread status unknown")));
    233                 /* Dump the endpoints ? */
    234                 /* Dump the client list ? */
    235         }
    236254}
    237255
     
    239257int fd_servers_start()
    240258{
    241         int  socket;
    242         struct server * sv;
     259        struct server * s;
     260       
     261        int empty_conf_ep = FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints);
    243262       
    244263        /* SCTP */
     
    249268               
    250269                /* Create the server on default port */
    251                 CHECK_FCT( fd_sctp_create_bind_server( &socket, fd_g_config->cnf_port ) );
    252                 CHECK_MALLOC( sv = new_serv(IPPROTO_SCTP, 0, socket) );
    253                
    254                
    255                
     270                CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 0) );
     271                CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port, FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints) ? NULL : &fd_g_config->cnf_endpoints) );
     272                fd_list_insert_before( &FD_SERVERS, &s->chain );
     273                CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
     274               
     275                /* Retrieve the list of endpoints if it was empty */
     276                if (empty_conf_ep) {
     277                        (void) fd_cnx_getendpoints(s->conn, &fd_g_config->cnf_endpoints, NULL);
     278                }
     279               
     280                /* Create the server on secure port */
     281                CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 1) );
     282                CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port_tls, FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints) ? NULL : &fd_g_config->cnf_endpoints) );
     283                fd_list_insert_before( &FD_SERVERS, &s->chain );
     284                CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
    256285               
    257286#endif /* DISABLE_SCTP */
     
    261290        if (!fd_g_config->cnf_flags.no_tcp) {
    262291               
    263                
     292                if (empty_conf_ep) {
     293                        /* Bind TCP servers on [0.0.0.0] */
     294                        if (!fd_g_config->cnf_flags.no_ip4) {
     295                               
     296                                CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
     297                                CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET, NULL) );
     298                                fd_list_insert_before( &FD_SERVERS, &s->chain );
     299                                CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
     300
     301                                CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
     302                                CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET, NULL) );
     303                                fd_list_insert_before( &FD_SERVERS, &s->chain );
     304                                CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
     305                        }
     306                        /* Bind TCP servers on [::] */
     307                        if (!fd_g_config->cnf_flags.no_ip6) {
     308                               
     309                                CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
     310                                CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET6, NULL) );
     311                                fd_list_insert_before( &FD_SERVERS, &s->chain );
     312                                CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
     313
     314                                CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
     315                                CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET6, NULL) );
     316                                fd_list_insert_before( &FD_SERVERS, &s->chain );
     317                                CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
     318                        }
     319                } else {
     320                        /* Create all endpoints -- check flags */
     321                        struct fd_list * li;
     322                        for (li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
     323                                struct fd_endpoint * ep = (struct fd_endpoint *)li;
     324                                sSA * sa = (sSA *) &ep->ss;
     325                                if (! (ep->flags & EP_FL_CONF))
     326                                        continue;
     327                                if (fd_g_config->cnf_flags.no_ip4 && (sa->sa_family == AF_INET))
     328                                        continue;
     329                                if (fd_g_config->cnf_flags.no_ip6 && (sa->sa_family == AF_INET6))
     330                                        continue;
     331                               
     332                                CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
     333                                CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, sa->sa_family, ep) );
     334                                fd_list_insert_before( &FD_SERVERS, &s->chain );
     335                                CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
     336
     337                                CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
     338                                CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, sa->sa_family, ep) );
     339                                fd_list_insert_before( &FD_SERVERS, &s->chain );
     340                                CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
     341                        }
     342                }
    264343        }
    265344       
     
    268347
    269348/* Terminate all the servers */
    270 void fd_servers_stop()
    271 {
    272        
    273 }
     349int fd_servers_stop()
     350{
     351        TODO("Not implemented");
     352       
     353        /* Loop on all servers */
     354                /* cancel thread */
     355                /* destroy server connection context */
     356                /* cancel and destroy all clients */
     357}
  • freeDiameter/tests/CMakeLists.txt

    r10 r29  
    1818        testsess
    1919        testdisp
     20        testcnx
    2021)
    2122
  • freeDiameter/tests/tests.h

    r10 r29  
    4646#include <pthread.h>
    4747#include <errno.h>
     48#include <gcrypt.h>
    4849
    4950/* Test timeout duration, unless -n is passed on the command line */
    5051#ifndef TEST_TIMEOUT
    51 #define TEST_TIMEOUT    5       /* 5 seconds */
     52#define TEST_TIMEOUT    30      /* in seconds */
    5253#endif /* TEST_TIMEOUT */
    5354
     
    7879struct fd_config * fd_g_config = &conf;
    7980
     81/* gcrypt functions to support posix threads */
     82GCRY_THREAD_OPTION_PTHREAD_IMPL;
     83
    8084/* Define the standard check routines */
    8185#define CHECK( _val, _assert ){                         \
     
    99103
    100104/* Minimum inits */
    101 #define INIT_FD() {                                             \
    102         memset(fd_g_config, 0, sizeof(struct fd_config));       \
    103         CHECK( 0, fd_lib_init() );                              \
    104         fd_log_threadname(basename(__FILE__));                  \
    105         CHECK( 0, fd_conf_init() );                             \
    106         CHECK( 0, fd_dict_base_protocol(fd_g_config->cnf_dict) );       \
    107         parse_cmdline(argc, argv);                              \
     105#define INIT_FD() {                                                             \
     106        memset(fd_g_config, 0, sizeof(struct fd_config));                       \
     107        CHECK( 0, fd_lib_init() );                                              \
     108        fd_log_threadname(basename(__FILE__));                                  \
     109        (void) gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);    \
     110        (void) gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);                   \
     111        CHECK( 0, gnutls_global_init());                                        \
     112        CHECK( 0, fd_conf_init() );                                             \
     113        CHECK( 0, fd_dict_base_protocol(fd_g_config->cnf_dict) );               \
     114        parse_cmdline(argc, argv);                                              \
    108115}
    109116
  • include/freeDiameter/CMakeLists.txt

    r17 r24  
    1010
    1111# Disable SCTP support completly ?
    12 OPTION(DISABLE_SCTP "Disable SCTP support?")
     12OPTION(DISABLE_SCTP "Disable SCTP support?" OFF)
     13IF (NOT DISABLE_SCTP)
     14        OPTION(DEBUG_SCTP "Verbose SCTP (for debug)?" OFF)
     15        OPTION(SCTP_USE_MAPPED_ADDRESSES "Use v6-mapped v4 addresses in SCTP (workaround some SCTP limitations)?" OFF)
     16ENDIF (NOT DISABLE_SCTP)
    1317
    1418# Find TODO items in the code easily ?
  • include/freeDiameter/freeDiameter-host.h.in

    r13 r24  
    4545
    4646#cmakedefine DISABLE_SCTP
     47#cmakedefine DEBUG_SCTP
     48#cmakedefine SCTP_USE_MAPPED_ADDRESSES
    4749#cmakedefine ERRORS_ON_TODO
    4850#cmakedefine DEBUG
  • include/freeDiameter/freeDiameter.h

    r20 r25  
    118118struct fd_endpoint {
    119119        struct fd_list  chain;  /* link in cnf_endpoints list */
    120         sSS             ss;     /* the socket information. List is always ordered by ss value (memcmp) */
    121         struct {
    122                 unsigned conf : 1; /* This endpoint is statically configured in a configuration file */
    123                 unsigned disc : 1; /* This endpoint was resolved from the Diameter Identity or other DNS query */
    124                 unsigned adv  : 1; /* This endpoint was advertized in Diameter CER/CEA exchange */
    125                 unsigned ll   : 1; /* Lower layer mechanism provided this endpoint */
    126                
    127                 /* To add: a validity timestamp for DNS records ? How do we retrieve this lifetime from DNS ? */
    128 
    129         }               meta;   /* Additional information about the endpoint */
     120       
     121        union {
     122                sSS             ss;     /* the socket information. List is always ordered by ss value (memcmp) -- see fd_ep_add_merge */
     123                sSA4            sin;
     124                sSA6            sin6;
     125                sSA             sa;
     126        };
     127       
     128#define EP_FL_CONF      (1 << 0)        /* This endpoint is statically configured in a configuration file */
     129#define EP_FL_DISC      (1 << 1)        /* This endpoint was resolved from the Diameter Identity or other DNS query */
     130#define EP_FL_ADV       (1 << 2)        /* This endpoint was advertized in Diameter CER/CEA exchange */
     131#define EP_FL_LL        (1 << 3)        /* Lower layer mechanism provided this endpoint */
     132#define EP_FL_PRIMARY   (1 << 4)        /* This endpoint is primary in a multihomed SCTP association */
     133        uint32_t        flags;          /* Additional information about the endpoint */
     134               
     135        /* To add: a validity timestamp for DNS records ? How do we retrieve this lifetime from DNS ? */
    130136};
    131137
     
    146152struct fd_event {
    147153        int      code; /* codespace depends on the queue */
     154        size_t   size;
    148155        void    *data;
    149156};
    150157
    151 static __inline__ int fd_event_send(struct fifo *queue, int code, void * data)
    152 {
    153         struct fd_event * ev;
    154         CHECK_MALLOC( ev = malloc(sizeof(struct fd_event)) );
    155         ev->code = code;
    156         ev->data = data;
    157         CHECK_FCT( fd_fifo_post(queue, &ev) );
    158         return 0;
    159 }
    160 static __inline__ int fd_event_get(struct fifo *queue, int *code, void ** data)
    161 {
    162         struct fd_event * ev;
    163         CHECK_FCT( fd_fifo_get(queue, &ev) );
    164         if (code)
    165                 *code = ev->code;
    166         if (data)
    167                 *data = ev->data;
    168         free(ev);
    169         return 0;
    170 }
    171 
    172 /* Events codespace for fd_g_config->cnf_main_ev */
     158/* Daemon's codespace: 1000->1999 */
    173159enum {
    174          FDEV_TERMINATE = 1000  /* request to terminate */
     160         FDEV_TERMINATE = 1000  /* request to terminate */
    175161        ,FDEV_DUMP_DICT         /* Dump the content of the dictionary */
    176162        ,FDEV_DUMP_EXT          /* Dump state of extensions */
     
    180166        ,FDEV_DUMP_PEERS        /* Dump the list of peers */
    181167};
     168
     169static __inline__ int fd_event_send(struct fifo *queue, int code, size_t datasz, void * data)
     170{
     171        struct fd_event * ev;
     172        CHECK_MALLOC( ev = malloc(sizeof(struct fd_event)) );
     173        ev->code = code;
     174        ev->size = datasz;
     175        ev->data = data;
     176        CHECK_FCT( fd_fifo_post(queue, &ev) );
     177        return 0;
     178}
     179static __inline__ int fd_event_get(struct fifo *queue, int *code, size_t *datasz, void ** data)
     180{
     181        struct fd_event * ev;
     182        CHECK_FCT( fd_fifo_get(queue, &ev) );
     183        if (code)
     184                *code = ev->code;
     185        if (datasz)
     186                *datasz = ev->size;
     187        if (data)
     188                *data = ev->data;
     189        free(ev);
     190        return 0;
     191}
     192static __inline__ int fd_event_timedget(struct fifo *queue, struct timespec * timeout, int timeoutcode, int *code, size_t *datasz, void ** data)
     193{
     194        struct fd_event * ev;
     195        int ret = 0;
     196        ret = fd_fifo_timedget(queue, &ev, timeout);
     197        if (ret == ETIMEDOUT) {
     198                if (code)
     199                        *code = timeoutcode;
     200                if (datasz)
     201                        *datasz = 0;
     202                if (data)
     203                        *data = NULL;
     204        } else {
     205                CHECK_FCT( ret );
     206                if (code)
     207                        *code = ev->code;
     208                if (datasz)
     209                        *datasz = ev->size;
     210                if (data)
     211                        *data = ev->data;
     212                free(ev);
     213        }
     214        return 0;
     215}
     216static __inline__ void fd_event_destroy(struct fifo **queue, void (*free_cb)(void * data))
     217{
     218        struct fd_event * ev;
     219        /* Purge all events, and free the associated data if any */
     220        while (fd_fifo_tryget( *queue, &ev ) == 0) {
     221                (*free_cb)(ev->data);
     222                free(ev);
     223        }
     224        CHECK_FCT_DO( fd_fifo_del(queue), /* continue */ );
     225        return ;
     226
    182227const char * fd_ev_str(int event); /* defined in freeDiameter/main.c */
    183228
     
    256301        /* Additional parameters */
    257302        uint32_t        pi_lft;         /* lifetime of this peer when inactive (see pi_flags.exp definition) */
    258         uint16_t        pi_streams;     /* number of streams for SCTP. 0 = default */
    259303        uint16_t        pi_port;        /* port to connect to. 0: default. */
    260304        int             pi_tctimer;     /* use this value for TcTimer instead of global, if != 0 */
     
    271315        struct fd_list  pi_apps;        /* applications advertised by the remote peer, except relay (pi_flags.relay) */
    272316        struct {
     317                char                    *priority;      /* In case the default priority is not appropriate */
    273318                /* This is inspired from http://www.gnu.org/software/gnutls/manual/gnutls.html#ex_003ax509_002dinfo */
    274319                const gnutls_datum_t    *cert_list;     /* The (valid) credentials that the peer has presented */
     
    460505
    461506
     507/***************************************/
     508/*   Endpoints lists helpers           */
     509/***************************************/
     510
     511int fd_ep_add_merge( struct fd_list * list, sSA * sa, socklen_t sl, uint32_t flags );
     512int fd_ep_filter( struct fd_list * list, uint32_t flags );
     513int fd_ep_clearflags( struct fd_list * list, uint32_t flags );
     514
     515
     516
    462517#endif /* _FREEDIAMETER_H */
  • include/freeDiameter/libfreeDiameter.h

    r20 r29  
    120120 *
    121121 * PARAMETERS:
     122 *  ts          : The timestamp to log, or NULL for "now"
    122123 *  buf         : An array where the time must be stored
    123124 *  len         : size of the buffer
    124125 *
    125126 * DESCRIPTION:
    126  *  Writes the current timestamp (in human readable format) in a buffer.
     127 *  Writes the timestamp (in human readable format) in a buffer.
    127128 *
    128129 * RETURN VALUE:
    129130 *  pointer to buf.
    130131 */
    131 char * fd_log_time ( char * buf, size_t len );
     132char * fd_log_time ( struct timespec * ts, char * buf, size_t len );
    132133
    133134
     
    173174                fd_log_debug("\t | tid:%-20s\t%s\tin %s@%s:%d\n"                                                                \
    174175                          "\t%s|%*s" format "\n",                                                                               \
    175                                         __thn, fd_log_time(__buf, sizeof(__buf)), __PRETTY_FUNCTION__, __FILE__, __LINE__,      \
     176                                        __thn, fd_log_time(NULL, __buf, sizeof(__buf)), __PRETTY_FUNCTION__, __FILE__, __LINE__,        \
    176177                                        (level < FULL)?"@":" ",level, "", ## args);                                             \
    177178        }                                                                                                                       \
     
    180181/* Helper for function entry -- for very detailed trace of the execution */
    181182#define TRACE_ENTRY(_format,_args... ) \
    182         TRACE_DEBUG(FCTS, "->%s (" #_args ") = (" _format ") >", __PRETTY_FUNCTION__, ##_args );
     183        TRACE_DEBUG(FCTS, "[enter] %s(" _format ") {" #_args "}", __PRETTY_FUNCTION__, ##_args );
    183184
    184185/* Helper for debugging by adding traces -- for debuging a specific location of the code */
     
    328329                        flag);                                  \
    329330          if (__rc)                                             \
    330                 fd_log_debug((char *)gai_strerror(__rc));       \
     331                fd_log_debug("%s", (char *)gai_strerror(__rc)); \
    331332          else                                                  \
    332                 fd_log_debug(&__addrbuf[0]);                    \
     333                fd_log_debug("%s", &__addrbuf[0]);              \
    333334        } else {                                                \
    334335                fd_log_debug("(NULL / ANY)");                   \
    335336        }                                                       \
    336337}
    337 /* if needed, add sSA_DUMP_SERVICE */
     338/* Same but with the port (service) also */
     339#define sSA_DUMP_NODE_SERV( sa, flag ) {                                \
     340        sSA * __sa = (sSA *)(sa);                                       \
     341        char __addrbuf[INET6_ADDRSTRLEN];                               \
     342        char __servbuf[32];                                             \
     343        if (__sa) {                                                     \
     344          int __rc = getnameinfo(__sa,                                  \
     345                        sizeof(sSS),                                    \
     346                        __addrbuf,                                      \
     347                        sizeof(__addrbuf),                              \
     348                        __servbuf,                                      \
     349                        sizeof(__servbuf),                              \
     350                        flag);                                          \
     351          if (__rc)                                                     \
     352                fd_log_debug("%s", (char *)gai_strerror(__rc));         \
     353          else                                                          \
     354                fd_log_debug("[%s]:%s", &__addrbuf[0],&__servbuf[0]);   \
     355        } else {                                                        \
     356                fd_log_debug("(NULL / ANY)");                           \
     357        }                                                               \
     358}
     359/* Inside a debug trace */
     360#define TRACE_DEBUG_sSA(level, prefix, sa, flags, suffix ) {                                                                            \
     361        if ( TRACE_BOOL(level) ) {                                                                                              \
     362                char __buf[25];                                                                                                 \
     363                char * __thn = ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed");                                       \
     364                fd_log_debug("\t | tid:%-20s\t%s\tin %s@%s:%d\n"                                                                \
     365                          "\t%s|%*s" prefix ,                                                                                   \
     366                                        __thn, fd_log_time(NULL, __buf, sizeof(__buf)), __PRETTY_FUNCTION__, __FILE__, __LINE__,\
     367                                        (level < FULL)?"@":" ",level, "");                                                      \
     368                sSA_DUMP_NODE_SERV( sa, flags );                                                                                \
     369                fd_log_debug(suffix "\n");                                                                                      \
     370        }                                                                                                                       \
     371}
     372
    338373
    339374/* A l4 protocol name (TCP / SCTP) */
     
    390425        (    ((ts1)->tv_sec  < (ts2)->tv_sec )  \
    391426          || (((ts1)->tv_sec  == (ts2)->tv_sec ) && ((ts1)->tv_nsec < (ts2)->tv_nsec) ))
     427
     428
     429/* Trace a binary buffer content */
     430#define TRACE_DEBUG_BUFFER(level, prefix, buf, bufsz, suffix ) {                                                                \
     431        if ( TRACE_BOOL(level) ) {                                                                                              \
     432                int __i;                                                                                                        \
     433                size_t __sz = (size_t)(bufsz);                                                                                  \
     434                uint8_t * __buf = (uint8_t *)(buf);                                                                             \
     435                char * __thn = ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed");                                       \
     436                fd_log_debug("\t | tid:%-20s\t%s\tin %s@%s:%d\n"                                                                \
     437                          "\t%s|%*s" prefix ,                                                                                   \
     438                                        __thn, fd_log_time(NULL, __buf, sizeof(__buf)), __PRETTY_FUNCTION__, __FILE__, __LINE__,\
     439                                        (level < FULL)?"@":" ",level, "");                                                      \
     440                for (__i = 0; __i < __sz; __i++) {                                                                              \
     441                        fd_log_debug("%02.2hhx", __buf[__i]);                                                                   \
     442                }                                                                                                               \
     443                fd_log_debug(suffix "\n");                                                                                      \
     444        }                                                                                                                       \
     445}
     446
    392447
    393448
     
    415470       
    416471        if (th_ret != NULL) {
    417                 TRACE_DEBUG(FULL, "The thread returned the following value: %p (ignored)", th_ret);
     472                TRACE_DEBUG(ANNOYING, "The thread returned the following value: %p (ignored)", th_ret);
    418473        }
    419474       
     
    439494        free(buffer);
    440495}
     496static __inline__ void fd_cleanup_socket(void * sockptr)
     497{
     498        if (sockptr) {
     499                shutdown(*(int *)sockptr, SHUT_RDWR);
     500                *(int *)sockptr = 0;
     501        }
     502}
     503
    441504
    442505/*============================================================*/
     
    465528void fd_list_insert_after  ( struct fd_list * ref, struct fd_list * item );
    466529void fd_list_insert_before ( struct fd_list * ref, struct fd_list * item );
     530
     531/* Move a list at the end of another */
     532void fd_list_move_end(struct fd_list * ref, struct fd_list * senti);
    467533
    468534/* Insert an item in an ordered list -- ordering function provided. If duplicate object found, EEXIST and it is returned in ref_duplicate */
     
    23422408
    23432409/*
     2410 * FUNCTION:    fd_fifo_move
     2411 *
     2412 * PARAMETERS:
     2413 *  old         : Location of a FIFO that is to be emptied and deleted.
     2414 *  new         : A FIFO that will receive the old data.
     2415 *  loc_update  : if non NULL, a place to store the pointer to new FIFO atomically with the move.
     2416 *
     2417 * DESCRIPTION:
     2418 *  Delete a queue and move its content to another one atomically.
     2419 *
     2420 * RETURN VALUE:
     2421 *  0           : The queue has been destroyed successfully.
     2422 *  EINVAL      : A parameter is invalid.
     2423 */
     2424int fd_fifo_move ( struct fifo ** old, struct fifo * new, struct fifo ** loc_update );
     2425
     2426/*
    23442427 * FUNCTION:    fd_fifo_length
    23452428 *
  • libfreeDiameter/fifo.c

    r14 r25  
    136136}
    137137
    138 /* Delete a queue. It must be unused. */
     138/* Delete a queue. It must be empty. */
    139139int fd_fifo_del ( struct fifo  ** queue )
    140140{
    141141        struct fifo * q;
     142        int loops = 0;
    142143       
    143144        TRACE_ENTRY( "%p", queue );
     
    149150        CHECK_POSIX(  pthread_mutex_lock( &q->mtx )  );
    150151       
    151         if ((q->count != 0) || (q->thrs != 0) || (q->data != NULL)) {
    152                 TRACE_DEBUG(INFO, "The queue cannot be destroyed (%d, %d, %p)", q->count, q->thrs, q->data);
     152        if ((q->count != 0) || (q->data != NULL)) {
     153                TRACE_DEBUG(INFO, "The queue cannot be destroyed (%d, %p)", q->count, q->data);
    153154                CHECK_POSIX_DO(  pthread_mutex_unlock( &q->mtx ), /* no fallback */  );
    154155                return EINVAL;
    155156        }
    156157       
     158        /* Ok, now invalidate the queue */
     159        q->eyec = 0xdead;
     160       
     161        while (q->thrs) {
     162                CHECK_POSIX(  pthread_cond_signal(&q->cond)  );
     163                CHECK_POSIX(  pthread_mutex_unlock( &q->mtx ));
     164                pthread_yield();
     165                CHECK_POSIX(  pthread_mutex_lock( &q->mtx )  );
     166                ASSERT( ++loops < 10 ); /* detect infinite loops */
     167        }
     168       
    157169        /* sanity check */
    158170        ASSERT(FD_IS_LIST_EMPTY(&q->list));
    159171       
    160         /* Ok, now invalidate the queue */
    161         q->eyec = 0xdead;
    162        
    163172        /* And destroy it */
    164173        CHECK_POSIX(  pthread_mutex_unlock( &q->mtx )  );
     
    170179        free(q);
    171180        *queue = NULL;
     181       
     182        return 0;
     183}
     184
     185/* Move the content of old into new, and update loc_update atomically */
     186int fd_fifo_move ( struct fifo ** old, struct fifo * new, struct fifo ** loc_update )
     187{
     188        struct fifo * q;
     189        int loops = 0;
     190       
     191        TRACE_ENTRY("%p %p %p", old, new, loc_update);
     192        CHECK_PARAMS( old && CHECK_FIFO( *old ) && CHECK_FIFO( new ));
     193       
     194        q = *old;
     195        CHECK_PARAMS( ! q->data );
     196        if (new->high) {
     197                TODO("Implement support for thresholds in fd_fifo_move...");
     198        }
     199       
     200        /* Update loc_update */
     201        *old = NULL;
     202        if (loc_update)
     203                *loc_update = new;
     204       
     205        /* Lock the queues */
     206        CHECK_POSIX(  pthread_mutex_lock( &q->mtx )  );
     207        CHECK_POSIX(  pthread_mutex_lock( &new->mtx )  );
     208       
     209        /* Any waiting thread on the old queue returns an error */
     210        q->eyec = 0xdead;
     211        while (q->thrs) {
     212                CHECK_POSIX(  pthread_cond_signal(&q->cond)  );
     213                CHECK_POSIX(  pthread_mutex_unlock( &q->mtx ));
     214                pthread_yield();
     215                CHECK_POSIX(  pthread_mutex_lock( &q->mtx )  );
     216                ASSERT( ++loops < 10 ); /* detect infinite loops */
     217        }
     218       
     219        /* Move all data from old to new */
     220        fd_list_move_end( &new->list, &q->list );
     221        if (q->count && (!new->count)) {
     222                CHECK_POSIX(  pthread_cond_signal(&new->cond)  );
     223        }
     224        new->count += q->count;
     225       
     226        /* Destroy old */
     227        CHECK_POSIX(  pthread_mutex_unlock( &q->mtx )  );
     228        CHECK_POSIX(  pthread_cond_destroy( &q->cond )  );
     229        CHECK_POSIX(  pthread_mutex_destroy( &q->mtx )  );
     230        free(q);
     231       
     232        /* Unlock new, we're done */
     233        CHECK_POSIX(  pthread_mutex_unlock( &new->mtx )  );
    172234       
    173235        return 0;
     
    379441awaken:
    380442        /* Check queue status */
     443        if (!CHECK_FIFO( queue )) {
     444                /* The queue is being destroyed */
     445                CHECK_POSIX(  pthread_mutex_unlock( &queue->mtx )  );
     446                TRACE_DEBUG(FULL, "The queue is being destroyed -> EPIPE");
     447                return EPIPE;
     448        }
     449               
    381450        if (queue->count > 0) {
    382451                /* There are items in the queue, so pick the first one */
  • libfreeDiameter/lists.c

    r14 r25  
    7070        ASSERT(ref->head != item);
    7171        list_insert_after(ref, item);
     72}
     73
     74/* Move all elements of list senti at the end of list ref */
     75void fd_list_move_end(struct fd_list * ref, struct fd_list * senti)
     76{
     77        ASSERT(ref->head == ref);
     78        ASSERT(senti->head == senti);
     79       
     80        if (senti->next == senti)
     81                return;
     82       
     83        senti->next->prev = ref->prev;
     84        ref->prev->next   = senti->next;
     85        senti->prev->next = ref;
     86        ref->prev         = senti->prev;
     87        senti->prev = senti;
     88        senti->next = senti;
     89       
    7290}
    7391
  • libfreeDiameter/log.c

    r2 r22  
    8787}
    8888
    89 /* Write current time into a buffer */
    90 char * fd_log_time ( char * buf, size_t len )
     89/* Write time into a buffer */
     90char * fd_log_time ( struct timespec * ts, char * buf, size_t len )
    9191{
    9292        int ret;
     
    9696       
    9797        /* Get current time */
    98         ret = clock_gettime(CLOCK_REALTIME, &tp);
    99         if (ret != 0) {
    100                 snprintf(buf, len, "%s", strerror(ret));
    101                 return buf;
     98        if (!ts) {
     99                ret = clock_gettime(CLOCK_REALTIME, &tp);
     100                if (ret != 0) {
     101                        snprintf(buf, len, "%s", strerror(ret));
     102                        return buf;
     103                }
     104                ts = &tp;
    102105        }
    103106       
    104         offset += strftime(buf + offset, len - offset, "%D,%T", localtime_r( &tp.tv_sec , &tm ));
    105         offset += snprintf(buf + offset, len - offset, ".%6.6ld", tp.tv_nsec / 1000);
     107        offset += strftime(buf + offset, len - offset, "%D,%T", localtime_r( &ts->tv_sec , &tm ));
     108        offset += snprintf(buf + offset, len - offset, ".%6.6ld", ts->tv_nsec / 1000);
    106109
    107110        return buf;
Note: See TracChangeset for help on using the changeset viewer.