Navigation


source: freeDiameter/libfdcore/cnxctx.c @ 706:4ffbc9f1e922

Last change on this file since 706:4ffbc9f1e922 was 706:4ffbc9f1e922, checked in by Sebastien Decugis <sdecugis@nict.go.jp>, 11 years ago

Large UNTESTED commit with the following changes:

  • Improved DiameterIdentity? handling (esp. interationalization issues), and improve efficiency of some string operations in peers, sessions, and dictionary modules (closes #7)
  • Cleanup in the session module to free only unreferenced sessions (#16)
  • Removed fd_cpu_flush_cache(), replaced by more robust alternatives.
  • Improved peer state machine algorithm to counter SCTP multistream race condition.
File size: 50.9 KB
Line 
1/*********************************************************************************************************
2* Software License Agreement (BSD License)                                                               *
3* Author: Sebastien Decugis <sdecugis@nict.go.jp>                                                        *
4*                                                                                                        *
5* Copyright (c) 2011, WIDE Project and NICT                                                              *
6* All rights reserved.                                                                                   *
7*                                                                                                        *
8* Redistribution and use of this software in source and binary forms, with or without modification, are  *
9* permitted provided that the following conditions are met:                                              *
10*                                                                                                        *
11* * Redistributions of source code must retain the above                                                 *
12*   copyright notice, this list of conditions and the                                                    *
13*   following disclaimer.                                                                                *
14*                                                                                                        *
15* * Redistributions in binary form must reproduce the above                                              *
16*   copyright notice, this list of conditions and the                                                    *
17*   following disclaimer in the documentation and/or other                                               *
18*   materials provided with the distribution.                                                            *
19*                                                                                                        *
20* * Neither the name of the WIDE Project or NICT nor the                                                 *
21*   names of its contributors may be used to endorse or                                                  *
22*   promote products derived from this software without                                                  *
23*   specific prior written permission of WIDE Project and                                                *
24*   NICT.                                                                                                *
25*                                                                                                        *
26* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
27* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
28* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
29* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT     *
30* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS    *
31* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
32* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
33* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                                             *
34*********************************************************************************************************/
35
36#include "fdcore-internal.h"
37#include "cnxctx.h"
38
39#include <net/if.h>
40#include <ifaddrs.h> /* for getifaddrs */
41
42/* The maximum size of Diameter message we accept to receive (<= 2^24) to avoid too big mallocs in case of trashed headers */
43#ifndef DIAMETER_MSG_SIZE_MAX
44#define DIAMETER_MSG_SIZE_MAX   65535   /* in bytes */
45#endif /* DIAMETER_MSG_SIZE_MAX */
46
47
48/* Connections contexts (cnxctx) in freeDiameter are wrappers around the sockets and TLS operations .
49 * They are used to hide the details of the processing to the higher layers of the daemon.
50 * They are always oriented on connections (TCP or SCTP), connectionless modes (UDP or SCTP) are not supported.
51 */
52
53/* Lifetime of a cnxctx object:
54 * 1) Creation
55 *    a) a server socket:
56 *       - create the object with fd_cnx_serv_tcp or fd_cnx_serv_sctp
57 *       - start listening incoming connections: fd_cnx_serv_listen
58 *       - accept new clients with fd_cnx_serv_accept.
59 *    b) a client socket:
60 *       - connect to a remote server with fd_cnx_cli_connect
61 *
62 * 2) Initialization
63 *    - if TLS is started first, call fd_cnx_handshake
64 *    - otherwise to receive clear messages, call fd_cnx_start_clear. fd_cnx_handshake can be called later.
65 *
66 * 3) Usage
67 *    - fd_cnx_receive, fd_cnx_send : exchange messages on this connection (send is synchronous, receive is not, but blocking).
68 *    - 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.
69 *    - fd_cnx_getid : retrieve a descriptive string for the connection (for debug)
70 *    - fd_cnx_getremoteid : identification of the remote peer (IP address or fqdn)
71 *    - fd_cnx_getcred : get the remote peer TLS credentials, after handshake
72 *
73 * 4) End
74 *    - fd_cnx_destroy
75 */
76
77/*******************************************/
78/*     Creation of a connection object     */
79/*******************************************/
80
81/* Initialize a context structure */
82static struct cnxctx * fd_cnx_init(int full)
83{
84        struct cnxctx * conn = NULL;
85
86        TRACE_ENTRY("%d", full);
87
88        CHECK_MALLOC_DO( conn = malloc(sizeof(struct cnxctx)), return NULL );
89        memset(conn, 0, sizeof(struct cnxctx));
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                cnx->cc_family = AF_INET;
125        } else {
126                ((sSA6 *)sa)->sin6_port = htons(port);
127                cnx->cc_family = AF_INET6;
128        }
129
130        /* Create the socket */
131        CHECK_FCT_DO( fd_tcp_create_bind_server( &cnx->cc_socket, sa, sSAlen(sa) ), goto error );
132
133        /* Generate the name for the connection object */
134        {
135                char addrbuf[INET6_ADDRSTRLEN];
136                int  rc;
137                rc = getnameinfo(sa, sSAlen(sa), addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
138                if (rc)
139                        snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
140                snprintf(cnx->cc_id, sizeof(cnx->cc_id), "TCP srv [%s]:%hu (%d)", addrbuf, port, cnx->cc_socket);
141        }
142
143        cnx->cc_proto = IPPROTO_TCP;
144
145        return cnx;
146
147error:
148        fd_cnx_destroy(cnx);
149        return NULL;
150}
151
152/* Same function for SCTP, with a list of local endpoints to bind to */
153struct cnxctx * fd_cnx_serv_sctp(uint16_t port, struct fd_list * ep_list)
154{
155#ifdef DISABLE_SCTP
156        TRACE_DEBUG(INFO, "This function should never been called when SCTP is disabled...");
157        ASSERT(0);
158        CHECK_FCT_DO( ENOTSUP, return NULL);
159#else /* DISABLE_SCTP */
160        struct cnxctx * cnx = NULL;
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        if (fd_g_config->cnf_flags.no_ip6) {
170                cnx->cc_family = AF_INET;
171        } else {
172                cnx->cc_family = AF_INET6; /* can create socket for both IP and IPv6 */
173        }
174       
175        /* Create the socket */
176        CHECK_FCT_DO( fd_sctp_create_bind_server( &cnx->cc_socket, cnx->cc_family, ep_list, port ), goto error );
177
178        /* Generate the name for the connection object */
179        snprintf(cnx->cc_id, sizeof(cnx->cc_id), "SCTP srv :%hu (%d)", port, cnx->cc_socket);
180
181        cnx->cc_proto = IPPROTO_SCTP;
182
183        return cnx;
184
185error:
186        fd_cnx_destroy(cnx);
187        return NULL;
188#endif /* DISABLE_SCTP */
189}
190
191/* Allow clients to connect on the server socket */
192int fd_cnx_serv_listen(struct cnxctx * conn)
193{
194        CHECK_PARAMS( conn );
195
196        switch (conn->cc_proto) {
197                case IPPROTO_TCP:
198                        CHECK_FCT(fd_tcp_listen(conn->cc_socket));
199                        break;
200
201#ifndef DISABLE_SCTP
202                case IPPROTO_SCTP:
203                        CHECK_FCT(fd_sctp_listen(conn->cc_socket));
204                        break;
205#endif /* DISABLE_SCTP */
206
207                default:
208                        CHECK_PARAMS(0);
209        }
210
211        return 0;
212}
213
214/* Accept a client (blocking until a new client connects) -- cancelable */
215struct cnxctx * fd_cnx_serv_accept(struct cnxctx * serv)
216{
217        struct cnxctx * cli = NULL;
218        sSS ss;
219        socklen_t ss_len = sizeof(ss);
220        int cli_sock = 0;
221
222        TRACE_ENTRY("%p", serv);
223        CHECK_PARAMS_DO(serv, return NULL);
224       
225        /* Accept the new connection -- this is blocking until new client enters or until cancellation */
226        CHECK_SYS_DO( cli_sock = accept(serv->cc_socket, (sSA *)&ss, &ss_len), return NULL );
227       
228        if (TRACE_BOOL(INFO)) {
229                fd_log_debug("%s : accepted new client [", fd_cnx_getid(serv));
230                sSA_DUMP_NODE( &ss, NI_NUMERICHOST );
231                fd_log_debug("].\n");
232        }
233       
234        CHECK_MALLOC_DO( cli = fd_cnx_init(1), { shutdown(cli_sock, SHUT_RDWR); close(cli_sock); return NULL; } );
235        cli->cc_socket = cli_sock;
236        cli->cc_family = serv->cc_family;
237        cli->cc_proto = serv->cc_proto;
238       
239        /* Set the timeout */
240        fd_cnx_s_setto(cli->cc_socket);
241       
242        /* Generate the name for the connection object */
243        {
244                char addrbuf[INET6_ADDRSTRLEN];
245                char portbuf[10];
246                int  rc;
247               
248                rc = getnameinfo((sSA *)&ss, sSAlen(&ss), addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
249                if (rc) {
250                        snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
251                        portbuf[0] = '\0';
252                }
253               
254                /* Numeric values for debug... */
255                snprintf(cli->cc_id, sizeof(cli->cc_id), "%s from [%s]:%s (%d<-%d)", 
256                                IPPROTO_NAME(cli->cc_proto), addrbuf, portbuf, serv->cc_socket, cli->cc_socket);
257               
258               
259                /* ...Name for log messages */
260                rc = getnameinfo((sSA *)&ss, sSAlen(&ss), cli->cc_remid, sizeof(cli->cc_remid), NULL, 0, 0);
261                if (rc)
262                        snprintf(cli->cc_remid, sizeof(cli->cc_remid), "[err:%s]", gai_strerror(rc));
263        }
264
265#ifndef DISABLE_SCTP
266        /* SCTP-specific handlings */
267        if (cli->cc_proto == IPPROTO_SCTP) {
268                /* Retrieve the number of streams */
269                CHECK_FCT_DO( fd_sctp_get_str_info( cli->cc_socket, &cli->cc_sctp_para.str_in, &cli->cc_sctp_para.str_out, NULL ), {fd_cnx_destroy(cli); return NULL;} );
270                if (cli->cc_sctp_para.str_out < cli->cc_sctp_para.str_in)
271                        cli->cc_sctp_para.pairs = cli->cc_sctp_para.str_out;
272                else
273                        cli->cc_sctp_para.pairs = cli->cc_sctp_para.str_in;
274               
275                TRACE_DEBUG(FULL,"%s : client '%s' (SCTP:%d, %d/%d streams)", fd_cnx_getid(serv), fd_cnx_getid(cli), cli->cc_socket, cli->cc_sctp_para.str_in, cli->cc_sctp_para.str_out);
276        }
277#endif /* DISABLE_SCTP */
278
279        return cli;
280}
281
282/* Client side: connect to a remote server -- cancelable */
283struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa /* contains the port already */, socklen_t addrlen)
284{
285        int sock = 0;
286        struct cnxctx * cnx = NULL;
287       
288        TRACE_ENTRY("%p %d", sa, addrlen);
289        CHECK_PARAMS_DO( sa && addrlen, return NULL );
290       
291        /* Create the socket and connect, which can take some time and/or fail */
292        {
293                int ret = fd_tcp_client( &sock, sa, addrlen );
294                if (ret != 0) {
295                        int lvl;
296                        switch (ret) {
297                                case ECONNREFUSED:
298
299                                        /* "Normal" errors */
300                                        lvl = FULL;
301                                        break;
302                                default:
303                                        lvl = INFO;
304                        }
305                        /* Some errors are expected, we log at different level */
306                        TRACE_DEBUG( lvl, "fd_tcp_client returned an error: %s", strerror(ret));
307                        return NULL;
308                }
309        }
310       
311        if (TRACE_BOOL(INFO)) {
312                fd_log_debug("Connection established to server '");
313                sSA_DUMP_NODE_SERV( sa, NI_NUMERICSERV);
314                fd_log_debug("' (TCP:%d).\n", sock);
315        }
316       
317        /* Once the socket is created successfuly, prepare the remaining of the cnx */
318        CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); close(sock); return NULL; } );
319       
320        cnx->cc_socket = sock;
321        cnx->cc_family = sa->sa_family;
322        cnx->cc_proto  = IPPROTO_TCP;
323       
324        /* Set the timeout */
325        fd_cnx_s_setto(cnx->cc_socket);
326       
327        /* Generate the names for the object */
328        {
329                char addrbuf[INET6_ADDRSTRLEN];
330                char portbuf[10];
331                int  rc;
332               
333                /* Numeric values for debug... */
334                rc = getnameinfo(sa, addrlen, addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
335                if (rc) {
336                        snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
337                        portbuf[0] = '\0';
338                }
339               
340                snprintf(cnx->cc_id, sizeof(cnx->cc_id), "TCP to [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket);
341               
342                /* ...Name for log messages */
343                rc = getnameinfo(sa, addrlen, cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0);
344                if (rc)
345                        snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc));
346        }
347       
348        return cnx;
349}
350
351/* Same for SCTP, accepts a list of remote addresses to connect to (see sctp_connectx for how they are used) */
352struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list)
353{
354#ifdef DISABLE_SCTP
355        TRACE_DEBUG(INFO, "This function should never be called when SCTP is disabled...");
356        ASSERT(0);
357        CHECK_FCT_DO( ENOTSUP, return NULL);
358#else /* DISABLE_SCTP */
359        int sock = 0;
360        struct cnxctx * cnx = NULL;
361        sSS primary;
362       
363        TRACE_ENTRY("%p", list);
364        CHECK_PARAMS_DO( list && !FD_IS_LIST_EMPTY(list), return NULL );
365       
366        {
367                int ret = fd_sctp_client( &sock, no_ip6, port, list );
368                if (ret != 0) {
369                        int lvl;
370                        switch (ret) {
371                                case ECONNREFUSED:
372
373                                        /* "Normal" errors */
374                                        lvl = FULL;
375                                        break;
376                                default:
377                                        lvl = INFO;
378                        }
379                        /* Some errors are expected, we log at different level */
380                        TRACE_DEBUG( lvl, "fd_sctp_client returned an error: %s", strerror(ret));
381                        return NULL;
382                }
383        }
384       
385        /* Once the socket is created successfuly, prepare the remaining of the cnx */
386        CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); close(sock); return NULL; } );
387       
388        cnx->cc_socket = sock;
389        cnx->cc_family = no_ip6 ? AF_INET : AF_INET6;
390        cnx->cc_proto  = IPPROTO_SCTP;
391       
392        /* Set the timeout */
393        fd_cnx_s_setto(cnx->cc_socket);
394       
395        /* Retrieve the number of streams and primary address */
396        CHECK_FCT_DO( fd_sctp_get_str_info( sock, &cnx->cc_sctp_para.str_in, &cnx->cc_sctp_para.str_out, &primary ), goto error );
397        if (cnx->cc_sctp_para.str_out < cnx->cc_sctp_para.str_in)
398                cnx->cc_sctp_para.pairs = cnx->cc_sctp_para.str_out;
399        else
400                cnx->cc_sctp_para.pairs = cnx->cc_sctp_para.str_in;
401       
402        if (TRACE_BOOL(INFO)) {
403                fd_log_debug("Connection established to server '");
404                sSA_DUMP_NODE_SERV( &primary, NI_NUMERICSERV);
405                fd_log_debug("' (SCTP:%d, %d/%d streams).\n", sock, cnx->cc_sctp_para.str_in, cnx->cc_sctp_para.str_out);
406        }
407       
408        /* Generate the names for the object */
409        {
410                char addrbuf[INET6_ADDRSTRLEN];
411                char portbuf[10];
412                int  rc;
413               
414                /* Numeric values for debug... */
415                rc = getnameinfo((sSA *)&primary, sSAlen(&primary), addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
416                if (rc) {
417                        snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
418                        portbuf[0] = '\0';
419                }
420               
421                snprintf(cnx->cc_id, sizeof(cnx->cc_id), "SCTP to [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket);
422               
423                /* ...Name for log messages */
424                rc = getnameinfo((sSA *)&primary, sSAlen(&primary), cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0);
425                if (rc)
426                        snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc));
427        }
428       
429        return cnx;
430
431error:
432        fd_cnx_destroy(cnx);
433        return NULL;
434#endif /* DISABLE_SCTP */
435}
436
437/* Return a string describing the connection, for debug */
438char * fd_cnx_getid(struct cnxctx * conn)
439{
440        CHECK_PARAMS_DO( conn, return "" );
441        return conn->cc_id;
442}
443
444/* Return the protocol of a connection */
445int fd_cnx_getproto(struct cnxctx * conn)
446{
447        CHECK_PARAMS_DO( conn, return 0 );
448        return conn->cc_proto;
449}
450
451/* Set the hostname to check during handshake */
452void fd_cnx_sethostname(struct cnxctx * conn, DiamId_t hn)
453{
454        CHECK_PARAMS_DO( conn, return );
455        conn->cc_tls_para.cn = hn;
456}
457
458/* We share a lock with many threads but we hold it only very short time so it is OK */
459static pthread_mutex_t state_lock = PTHREAD_MUTEX_INITIALIZER;
460uint32_t fd_cnx_getstate(struct cnxctx * conn)
461{
462        uint32_t st;
463        CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
464        st = conn->cc_state;
465        CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
466        return st;
467}
468int  fd_cnx_teststate(struct cnxctx * conn, uint32_t flag)
469{
470        uint32_t st;
471        CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
472        st = conn->cc_state;
473        CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
474        return st & flag;
475}
476void fd_cnx_addstate(struct cnxctx * conn, uint32_t orstate)
477{
478        CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
479        conn->cc_state |= orstate;
480        CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
481}
482void fd_cnx_setstate(struct cnxctx * conn, uint32_t abstate)
483{
484        CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
485        conn->cc_state = abstate;
486        CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
487}
488
489
490/* Return the TLS state of a connection */
491int fd_cnx_getTLS(struct cnxctx * conn)
492{
493        CHECK_PARAMS_DO( conn, return 0 );
494        return fd_cnx_teststate(conn, CC_STATUS_TLS);
495}
496
497/* Return true if the connection supports unordered delivery of messages */
498int fd_cnx_isMultichan(struct cnxctx * conn)
499{
500        CHECK_PARAMS_DO( conn, return 0 );
501        #ifdef DISABLE_SCTP
502        if (conn->cc_proto == IPPROTO_SCTP)
503                return (conn->cc_sctp_para.str_in > 1) || (conn->cc_sctp_para.str_out > 1);
504        #endif /* DISABLE_SCTP */
505        return 0;
506}
507
508
509/* Get the list of endpoints (IP addresses) of the local and remote peers on this connection */
510int fd_cnx_getremoteeps(struct cnxctx * conn, struct fd_list * eps)
511{
512        TRACE_ENTRY("%p %p %p", conn, eps);
513        CHECK_PARAMS(conn && eps);
514       
515        /* Check we have a full connection object, not a listening socket (with no remote) */
516        CHECK_PARAMS( conn->cc_incoming );
517
518        /* Retrieve the peer endpoint(s) of the connection */
519        switch (conn->cc_proto) {
520                case IPPROTO_TCP: {
521                        sSS ss;
522                        socklen_t sl;
523                        CHECK_FCT(fd_tcp_get_remote_ep(conn->cc_socket, &ss, &sl));
524                        CHECK_FCT(fd_ep_add_merge( eps, (sSA *)&ss, sl, EP_FL_LL | EP_FL_PRIMARY ));
525                }
526                break;
527
528                #ifndef DISABLE_SCTP
529                case IPPROTO_SCTP: {
530                        CHECK_FCT(fd_sctp_get_remote_ep(conn->cc_socket, eps));
531                }
532                break;
533                #endif /* DISABLE_SCTP */
534
535                default:
536                        CHECK_PARAMS(0);
537        }
538
539        return 0;
540}
541
542/* Get a string describing the remote peer address (ip address or fqdn) */
543char * fd_cnx_getremoteid(struct cnxctx * conn)
544{
545        CHECK_PARAMS_DO( conn, return "" );
546        return conn->cc_remid;
547}
548
549/* Retrieve a list of all IP addresses of the local system from the kernel, using getifaddrs */
550int fd_cnx_get_local_eps(struct fd_list * list)
551{
552        struct ifaddrs *iflist, *cur;
553       
554        CHECK_SYS(getifaddrs(&iflist));
555       
556        for (cur = iflist; cur != NULL; cur = cur->ifa_next) {
557                if (cur->ifa_flags & IFF_LOOPBACK)
558                        continue;
559               
560                if (fd_g_config->cnf_flags.no_ip4 && (cur->ifa_addr->sa_family == AF_INET))
561                        continue;
562               
563                if (fd_g_config->cnf_flags.no_ip6 && (cur->ifa_addr->sa_family == AF_INET6))
564                        continue;
565               
566                CHECK_FCT(fd_ep_add_merge( list, cur->ifa_addr, sSAlen(cur->ifa_addr), EP_FL_LL ));
567        }
568       
569        freeifaddrs(iflist);
570       
571        return 0;
572}
573
574
575/**************************************/
576/*     Use of a connection object     */
577/**************************************/
578
579/* An error occurred on the socket */
580void fd_cnx_markerror(struct cnxctx * conn)
581{
582        TRACE_ENTRY("%p", conn);
583        CHECK_PARAMS_DO( conn, goto fatal );
584       
585        TRACE_DEBUG(FULL, "Error flag set for socket %d (%s, %s)", conn->cc_socket, conn->cc_id, conn->cc_remid);
586       
587        /* Mark the error */
588        fd_cnx_addstate(conn, CC_STATUS_ERROR);
589       
590        /* Report the error if not reported yet, and not closing */
591        if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING | CC_STATUS_SIGNALED ))  {
592                TRACE_DEBUG(FULL, "Sending FDEVP_CNX_ERROR event");
593                CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_ERROR, 0, NULL), goto fatal);
594                fd_cnx_addstate(conn, CC_STATUS_SIGNALED);
595        }
596        return;
597fatal:
598        /* An unrecoverable error occurred, stop the daemon */
599        ASSERT(0);
600        CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );       
601}
602
603/* Set the timeout option on the socket */
604void fd_cnx_s_setto(int sock) 
605{
606        struct timeval tv;
607       
608        /* Set a timeout on the socket so that in any case we are not stuck waiting for something */
609        memset(&tv, 0, sizeof(tv));
610        tv.tv_sec = 3;  /* allow 3 seconds timeout for TLS session cleanup */
611        CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), /* best effort only */ );
612        CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), /* Also timeout for sending, to avoid waiting forever */ );
613}
614
615/* A recv-like function, taking a cnxctx object instead of socket as entry. We use it to quickly react to timeouts without traversing GNUTLS wrapper each time */
616ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length)
617{
618        ssize_t ret = 0;
619        int timedout = 0;
620again:
621        ret = recv(conn->cc_socket, buffer, length, 0);
622        /* Handle special case of timeout */
623        if ((ret < 0) && (errno == EAGAIN)) {
624                if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING ))
625                        goto again; /* don't care, just ignore */
626                if (!timedout) {
627                        timedout ++; /* allow for one timeout while closing */
628                        goto again;
629                }
630        }
631       
632        /* Mark the error */
633        if (ret <= 0) {
634                CHECK_SYS_DO(ret, /* continue, this is only used to log the error here */);
635                fd_cnx_markerror(conn);
636        }
637       
638        return ret;
639}
640
641/* Send */
642static ssize_t fd_cnx_s_send(struct cnxctx * conn, void *buffer, size_t length)
643{
644        ssize_t ret = 0;
645        int timedout = 0;
646again:
647        ret = send(conn->cc_socket, buffer, length, 0);
648        /* Handle special case of timeout */
649        if ((ret < 0) && (errno == EAGAIN)) {
650                if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING ))
651                        goto again; /* don't care, just ignore */
652                if (!timedout) {
653                        timedout ++; /* allow for one timeout while closing */
654                        goto again;
655                }
656                CHECK_SYS_DO(ret, /* continue */);
657        }
658       
659        /* Mark the error */
660        if (ret <= 0)
661                fd_cnx_markerror(conn);
662       
663        return ret;
664}
665
666/* Receiver thread (TCP & noTLS) : incoming message is directly saved into the target queue */
667static void * rcvthr_notls_tcp(void * arg)
668{
669        struct cnxctx * conn = arg;
670       
671        TRACE_ENTRY("%p", arg);
672        CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto out);
673       
674        /* Set the thread name */
675        {
676                char buf[48];
677                snprintf(buf, sizeof(buf), "Receiver (%d) TCP/noTLS)", conn->cc_socket);
678                fd_log_threadname ( buf );
679        }
680       
681        ASSERT( conn->cc_proto == IPPROTO_TCP );
682        ASSERT( ! fd_cnx_teststate(conn, CC_STATUS_TLS ) );
683        ASSERT( fd_cnx_target_queue(conn) );
684       
685        /* Receive from a TCP connection: we have to rebuild the message boundaries */
686        do {
687                uint8_t header[4];
688                uint8_t * newmsg;
689                size_t  length;
690                ssize_t ret = 0;
691                size_t  received = 0;
692
693                do {
694                        ret = fd_cnx_s_recv(conn, &header[received], sizeof(header) - received);
695                        if (ret <= 0) {
696                                goto out; /* Stop the thread, the event was already sent */
697                        }
698
699                        received += ret;
700                } while (received < sizeof(header));
701
702                length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3];
703
704                /* Check the received word is a valid begining of a Diameter message */
705                if ((header[0] != DIAMETER_VERSION)     /* defined in <libfdproto.h> */
706                   || (length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */
707                        /* The message is suspect */
708                        TRACE_DEBUG(INFO, "Received suspect header [ver: %d, size: %zd], assume disconnection", (int)header[0], length);
709                        fd_cnx_markerror(conn);
710                        goto out; /* Stop the thread, the recipient of the event will cleanup */
711                }
712
713                /* Ok, now we can really receive the data */
714                CHECK_MALLOC_DO(  newmsg = malloc( length ), goto fatal );
715                memcpy(newmsg, header, sizeof(header));
716
717                while (received < length) {
718                        pthread_cleanup_push(free, newmsg); /* In case we are canceled, clean the partialy built buffer */
719                        ret = fd_cnx_s_recv(conn, newmsg + received, length - received);
720                        pthread_cleanup_pop(0);
721
722                        if (ret <= 0) {
723                                free(newmsg);
724                                goto out;
725                        }
726                        received += ret;
727                }
728               
729                /* We have received a complete message, pass it to the daemon */
730                CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), /* continue or destroy everything? */);
731               
732        } while (conn->cc_loop);
733       
734out:
735        TRACE_DEBUG(FULL, "Thread terminated"); 
736        return NULL;
737       
738fatal:
739        /* An unrecoverable error occurred, stop the daemon */
740        CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
741        goto out;
742}
743
744#ifndef DISABLE_SCTP
745/* Receiver thread (SCTP & noTLS) : incoming message is directly saved into cc_incoming, no need to care for the stream ID */
746static void * rcvthr_notls_sctp(void * arg)
747{
748        struct cnxctx * conn = arg;
749        uint8_t * buf;
750        size_t    bufsz;
751        int       event;
752       
753        TRACE_ENTRY("%p", arg);
754        CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto fatal);
755       
756        /* Set the thread name */
757        {
758                char buf[48];
759                snprintf(buf, sizeof(buf), "Receiver (%d) SCTP/noTLS)", conn->cc_socket);
760                fd_log_threadname ( buf );
761        }
762       
763        ASSERT( conn->cc_proto == IPPROTO_SCTP );
764        ASSERT( ! fd_cnx_teststate(conn, CC_STATUS_TLS ) );
765        ASSERT( fd_cnx_target_queue(conn) );
766       
767        do {
768                CHECK_FCT_DO( fd_sctp_recvmeta(conn, NULL, &buf, &bufsz, &event), goto fatal );
769                if (event == FDEVP_CNX_ERROR) {
770                        fd_cnx_markerror(conn);
771                        goto out;
772                }
773               
774                if (event == FDEVP_CNX_SHUTDOWN) {
775                        /* Just ignore the notification for now, we will get another error later anyway */
776                        continue;
777                }
778               
779                CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), event, bufsz, buf), goto fatal );
780               
781        } while (conn->cc_loop || (event != FDEVP_CNX_MSG_RECV));
782       
783out:
784        TRACE_DEBUG(FULL, "Thread terminated"); 
785        return NULL;
786
787fatal:
788        /* An unrecoverable error occurred, stop the daemon */
789        CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
790        goto out;
791}
792#endif /* DISABLE_SCTP */
793
794/* Start receving messages in clear (no TLS) on the connection */
795int fd_cnx_start_clear(struct cnxctx * conn, int loop)
796{
797        TRACE_ENTRY("%p %i", conn, loop);
798       
799        CHECK_PARAMS( conn && fd_cnx_target_queue(conn) && (!fd_cnx_teststate(conn, CC_STATUS_TLS)) && (!conn->cc_loop));
800       
801        /* Release resources in case of a previous call was already made */
802        CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */);
803       
804        /* Save the loop request */
805        conn->cc_loop = loop;
806       
807        switch (conn->cc_proto) {
808                case IPPROTO_TCP:
809                        /* Start the tcp_notls thread */
810                        CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_notls_tcp, conn ) );
811                        break;
812#ifndef DISABLE_SCTP
813                case IPPROTO_SCTP:
814                        /* Start the tcp_notls thread */
815                        CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_notls_sctp, conn ) );
816                        break;
817#endif /* DISABLE_SCTP */
818                default:
819                        TRACE_DEBUG(INFO, "Unknown protocol: %d", conn->cc_proto);
820                        ASSERT(0);
821                        return ENOTSUP;
822        }
823                       
824        return 0;
825}
826
827
828
829
830/* Returns 0 on error, received data size otherwise (always >= 0) */
831static ssize_t fd_tls_recv_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz)
832{
833        ssize_t ret;
834again: 
835        CHECK_GNUTLS_DO( ret = gnutls_record_recv(session, data, sz),
836                {
837                        switch (ret) {
838                                case GNUTLS_E_REHANDSHAKE: 
839                                        if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING))
840                                                CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
841                                                        {
842                                                                if (TRACE_BOOL(INFO)) {
843                                                                        fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
844                                                                }
845                                                                goto end;
846                                                        } );
847
848                                case GNUTLS_E_AGAIN:
849                                case GNUTLS_E_INTERRUPTED:
850                                        if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING))
851                                                goto again;
852                                        TRACE_DEBUG(FULL, "Connection is closing, so abord gnutls_record_recv now.");
853                                        break;
854
855                                case GNUTLS_E_UNEXPECTED_PACKET_LENGTH:
856                                        /* The connection is closed */
857                                        TRACE_DEBUG(FULL, "Got 0 size while reading the socket, probably connection closed...");
858                                        break;
859                               
860                                default:
861                                        TRACE_DEBUG(INFO, "This GNU TLS error is not handled, assume unrecoverable error");
862                        }
863                } );
864               
865        if (ret == 0)
866                CHECK_GNUTLS_DO( gnutls_bye(session, GNUTLS_SHUT_RDWR),  );
867       
868end:   
869        if (ret <= 0)
870                fd_cnx_markerror(conn);
871        return ret;
872}
873
874/* Wrapper around gnutls_record_send to handle some error codes */
875static ssize_t fd_tls_send_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz)
876{
877        ssize_t ret;
878again: 
879        CHECK_GNUTLS_DO( ret = gnutls_record_send(session, data, sz),
880                {
881                        switch (ret) {
882                                case GNUTLS_E_REHANDSHAKE: 
883                                        if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING))
884                                                CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
885                                                        {
886                                                                if (TRACE_BOOL(INFO)) {
887                                                                        fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
888                                                                }
889                                                                goto end;
890                                                        } );
891
892                                case GNUTLS_E_AGAIN:
893                                case GNUTLS_E_INTERRUPTED:
894                                        if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING))
895                                                goto again;
896                                        TRACE_DEBUG(INFO, "Connection is closing, so abord gnutls_record_send now.");
897                                        break;
898
899                                default:
900                                        TRACE_DEBUG(INFO, "This TLS error is not handled, assume unrecoverable error");
901                        }
902                } );
903end:   
904        if (ret <= 0)
905                fd_cnx_markerror(conn);
906               
907        return ret;
908}
909
910
911/* The function that receives TLS data and re-builds a Diameter message -- it exits only on error or cancelation */
912int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session)
913{
914        /* No guarantee that GnuTLS preserves the message boundaries, so we re-build it as in TCP */
915        do {
916                uint8_t header[4];
917                uint8_t * newmsg;
918                size_t  length;
919                ssize_t ret = 0;
920                size_t  received = 0;
921
922                do {
923                        ret = fd_tls_recv_handle_error(conn, session, &header[received], sizeof(header) - received);
924                        if (ret <= 0) {
925                                /* The connection is closed */
926                                goto out;
927                        }
928                        received += ret;
929                } while (received < sizeof(header));
930
931                length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3];
932
933                /* Check the received word is a valid beginning of a Diameter message */
934                if ((header[0] != DIAMETER_VERSION)     /* defined in <libfreeDiameter.h> */
935                   || (length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */
936                        /* The message is suspect */
937                        TRACE_DEBUG(INFO, "Received suspect header [ver: %d, size: %zd], assume disconnection", (int)header[0], length);
938                        fd_cnx_markerror(conn);
939                        goto out;
940                }
941
942                /* Ok, now we can really receive the data */
943                CHECK_MALLOC(  newmsg = malloc( length ) );
944                memcpy(newmsg, header, sizeof(header));
945
946                while (received < length) {
947                        pthread_cleanup_push(free, newmsg); /* In case we are canceled, clean the partialy built buffer */
948                        ret = fd_tls_recv_handle_error(conn, session, newmsg + received, length - received);
949                        pthread_cleanup_pop(0);
950
951                        if (ret <= 0) {
952                                free(newmsg);
953                                goto out;
954                        }
955                        received += ret;
956                }
957               
958                /* We have received a complete message, pass it to the daemon */
959                CHECK_FCT_DO( ret = fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), 
960                        { 
961                                free(newmsg); 
962                                CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
963                                return ret; 
964                        } );
965               
966        } while (1);
967       
968out:
969        return ENOTCONN;
970}
971
972/* Receiver thread (TLS & 1 stream SCTP or TCP)  */
973static void * rcvthr_tls_single(void * arg)
974{
975        struct cnxctx * conn = arg;
976       
977        TRACE_ENTRY("%p", arg);
978        CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), return NULL );
979       
980        /* Set the thread name */
981        {
982                char buf[48];
983                snprintf(buf, sizeof(buf), "Receiver (%d) TLS/single stream", conn->cc_socket);
984                fd_log_threadname ( buf );
985        }
986       
987        ASSERT( fd_cnx_teststate(conn, CC_STATUS_TLS) );
988        ASSERT( fd_cnx_target_queue(conn) );
989
990        /* The next function only returns when there is an error on the socket */       
991        CHECK_FCT_DO(fd_tls_rcvthr_core(conn, conn->cc_tls_para.session), /* continue */);
992
993        TRACE_DEBUG(FULL, "Thread terminated"); 
994        return NULL;
995}
996
997/* Prepare a gnutls session object for handshake */
998int fd_tls_prepare(gnutls_session_t * session, int mode, char * priority, void * alt_creds)
999{
1000        /* Create the session context */
1001        CHECK_GNUTLS_DO( gnutls_init (session, mode), return ENOMEM );
1002
1003        /* Set the algorithm suite */
1004        if (priority) {
1005                const char * errorpos;
1006                CHECK_GNUTLS_DO( gnutls_priority_set_direct( *session, priority, &errorpos ), 
1007                        { TRACE_DEBUG(INFO, "Error in priority string '%s' at position: '%s'\n", priority, errorpos); return EINVAL; } );
1008        } else {
1009                CHECK_GNUTLS_DO( gnutls_priority_set( *session, fd_g_config->cnf_sec_data.prio_cache ), return EINVAL );
1010        }
1011
1012        /* Set the credentials of this side of the connection */
1013        CHECK_GNUTLS_DO( gnutls_credentials_set (*session, GNUTLS_CRD_CERTIFICATE, alt_creds ?: fd_g_config->cnf_sec_data.credentials), return EINVAL );
1014
1015        /* Request the remote credentials as well */
1016        if (mode == GNUTLS_SERVER) {
1017                gnutls_certificate_server_set_request (*session, GNUTLS_CERT_REQUIRE);
1018        }
1019       
1020        return 0;
1021}
1022
1023/* Verify remote credentials after successful handshake (return 0 if OK, EINVAL otherwise) */
1024int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, int verbose)
1025{
1026        int i, ret = 0;
1027        unsigned int gtret;
1028        const gnutls_datum_t *cert_list;
1029        unsigned int cert_list_size;
1030        gnutls_x509_crt_t cert;
1031        time_t now;
1032       
1033        TRACE_ENTRY("%p %d", conn, verbose);
1034        CHECK_PARAMS(conn);
1035       
1036        /* Trace the session information -- http://www.gnu.org/software/gnutls/manual/gnutls.html#Obtaining-session-information */
1037        if (verbose && TRACE_BOOL(FULL)) {
1038                const char *tmp;
1039                gnutls_kx_algorithm_t kx;
1040                gnutls_credentials_type_t cred;
1041               
1042                fd_log_debug("TLS Session information for connection '%s':\n", conn->cc_id);
1043
1044                /* print the key exchange's algorithm name */
1045                GNUTLS_TRACE( kx = gnutls_kx_get (session) );
1046                GNUTLS_TRACE( tmp = gnutls_kx_get_name (kx) );
1047                fd_log_debug("\t - Key Exchange: %s\n", tmp);
1048
1049                /* Check the authentication type used and switch
1050                * to the appropriate. */
1051                GNUTLS_TRACE( cred = gnutls_auth_get_type (session) );
1052                switch (cred)
1053                {
1054                        case GNUTLS_CRD_IA:
1055                                fd_log_debug("\t - TLS/IA session\n");
1056                                break;
1057
1058                        case GNUTLS_CRD_PSK:
1059                                /* This returns NULL in server side. */
1060                                if (gnutls_psk_client_get_hint (session) != NULL)
1061                                        fd_log_debug("\t - PSK authentication. PSK hint '%s'\n",
1062                                                gnutls_psk_client_get_hint (session));
1063                                /* This returns NULL in client side. */
1064                                if (gnutls_psk_server_get_username (session) != NULL)
1065                                        fd_log_debug("\t - PSK authentication. Connected as '%s'\n",
1066                                                gnutls_psk_server_get_username (session));
1067                                break;
1068
1069                        case GNUTLS_CRD_ANON:   /* anonymous authentication */
1070                                fd_log_debug("\t - Anonymous DH using prime of %d bits\n",
1071                                        gnutls_dh_get_prime_bits (session));
1072                                break;
1073
1074                        case GNUTLS_CRD_CERTIFICATE:    /* certificate authentication */
1075                                /* Check if we have been using ephemeral Diffie-Hellman. */
1076                                if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS) {
1077                                        fd_log_debug("\t - Ephemeral DH using prime of %d bits\n",
1078                                                gnutls_dh_get_prime_bits (session));
1079                                }
1080                                break;
1081#ifdef ENABLE_SRP                               
1082                        case GNUTLS_CRD_SRP:
1083                                fd_log_debug("\t - SRP session with username %s\n",
1084                                        gnutls_srp_server_get_username (session));
1085                                break;
1086#endif /* ENABLE_SRP */
1087
1088                        default:
1089                                fd_log_debug("\t - Different type of credentials for the session (%d).\n", cred);
1090                                break;
1091
1092                }
1093
1094                /* print the protocol's name (ie TLS 1.0) */
1095                tmp = gnutls_protocol_get_name (gnutls_protocol_get_version (session));
1096                fd_log_debug("\t - Protocol: %s\n", tmp);
1097
1098                /* print the certificate type of the peer. ie X.509 */
1099                tmp = gnutls_certificate_type_get_name (gnutls_certificate_type_get (session));
1100                fd_log_debug("\t - Certificate Type: %s\n", tmp);
1101
1102                /* print the compression algorithm (if any) */
1103                tmp = gnutls_compression_get_name (gnutls_compression_get (session));
1104                fd_log_debug("\t - Compression: %s\n", tmp);
1105
1106                /* print the name of the cipher used. ie 3DES. */
1107                tmp = gnutls_cipher_get_name (gnutls_cipher_get (session));
1108                fd_log_debug("\t - Cipher: %s\n", tmp);
1109
1110                /* Print the MAC algorithms name. ie SHA1 */
1111                tmp = gnutls_mac_get_name (gnutls_mac_get (session));
1112                fd_log_debug("\t - MAC: %s\n", tmp);
1113        }
1114       
1115        /* First, use built-in verification */
1116        CHECK_GNUTLS_DO( gnutls_certificate_verify_peers2 (session, &gtret), return EINVAL );
1117        if (gtret) {
1118                if (TRACE_BOOL(INFO)) {
1119                        fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :\n", conn->cc_socket, conn->cc_remid, conn->cc_id);
1120                        if (gtret & GNUTLS_CERT_INVALID)
1121                                fd_log_debug(" - The certificate is not trusted (unknown CA? expired?)\n");
1122                        if (gtret & GNUTLS_CERT_REVOKED)
1123                                fd_log_debug(" - The certificate has been revoked.\n");
1124                        if (gtret & GNUTLS_CERT_SIGNER_NOT_FOUND)
1125                                fd_log_debug(" - The certificate hasn't got a known issuer.\n");
1126                        if (gtret & GNUTLS_CERT_SIGNER_NOT_CA)
1127                                fd_log_debug(" - The certificate signer is not a CA, or uses version 1, or 3 without basic constraints.\n");
1128                        if (gtret & GNUTLS_CERT_INSECURE_ALGORITHM)
1129                                fd_log_debug(" - The certificate signature uses a weak algorithm.\n");
1130                }
1131                return EINVAL;
1132        }
1133       
1134        /* Code from http://www.gnu.org/software/gnutls/manual/gnutls.html#Verifying-peer_0027s-certificate */
1135        if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509)
1136                return EINVAL;
1137       
1138        GNUTLS_TRACE( cert_list = gnutls_certificate_get_peers (session, &cert_list_size) );
1139        if (cert_list == NULL)
1140                return EINVAL;
1141       
1142        now = time(NULL);
1143       
1144        if (verbose && TRACE_BOOL(FULL)) {
1145                char serial[40];
1146                char dn[128];
1147                size_t size;
1148                unsigned int algo, bits;
1149                time_t expiration_time, activation_time;
1150               
1151                fd_log_debug("TLS Certificate information for connection '%s' (%d certs provided):\n", conn->cc_id, cert_list_size);
1152                for (i = 0; i < cert_list_size; i++)
1153                {
1154
1155                        CHECK_GNUTLS_DO( gnutls_x509_crt_init (&cert), return EINVAL);
1156                        CHECK_GNUTLS_DO( gnutls_x509_crt_import (cert, &cert_list[i], GNUTLS_X509_FMT_DER), return EINVAL);
1157               
1158                        fd_log_debug(" Certificate %d info:\n", i);
1159
1160                        GNUTLS_TRACE( expiration_time = gnutls_x509_crt_get_expiration_time (cert) );
1161                        GNUTLS_TRACE( activation_time = gnutls_x509_crt_get_activation_time (cert) );
1162
1163                        fd_log_debug("\t - Certificate is valid since: %s", ctime (&activation_time));
1164                        fd_log_debug("\t - Certificate expires: %s", ctime (&expiration_time));
1165
1166                        /* Print the serial number of the certificate. */
1167                        size = sizeof (serial);
1168                        gnutls_x509_crt_get_serial (cert, serial, &size);
1169                       
1170                        fd_log_debug("\t - Certificate serial number: ");
1171                        {
1172                                int j;
1173                                for (j = 0; j < size; j++) {
1174                                        fd_log_debug("%02.2hhx", serial[j]);
1175                                }
1176                        }
1177                        fd_log_debug("\n");
1178
1179                        /* Extract some of the public key algorithm's parameters */
1180                        GNUTLS_TRACE( algo = gnutls_x509_crt_get_pk_algorithm (cert, &bits) );
1181                        fd_log_debug("\t - Certificate public key: %s\n",
1182                              gnutls_pk_algorithm_get_name (algo));
1183
1184                        /* Print the version of the X.509 certificate. */
1185                        fd_log_debug("\t - Certificate version: #%d\n",
1186                              gnutls_x509_crt_get_version (cert));
1187
1188                        size = sizeof (dn);
1189                        GNUTLS_TRACE( gnutls_x509_crt_get_dn (cert, dn, &size) );
1190                        fd_log_debug("\t - DN: %s\n", dn);
1191
1192                        size = sizeof (dn);
1193                        GNUTLS_TRACE( gnutls_x509_crt_get_issuer_dn (cert, dn, &size) );
1194                        fd_log_debug("\t - Issuer's DN: %s\n", dn);
1195
1196                        GNUTLS_TRACE( gnutls_x509_crt_deinit (cert) );
1197                }
1198        }
1199
1200        /* Check validity of all the certificates */
1201        for (i = 0; i < cert_list_size; i++)
1202        {
1203                time_t deadline;
1204               
1205                CHECK_GNUTLS_DO( gnutls_x509_crt_init (&cert), return EINVAL);
1206                CHECK_GNUTLS_DO( gnutls_x509_crt_import (cert, &cert_list[i], GNUTLS_X509_FMT_DER), return EINVAL);
1207               
1208                GNUTLS_TRACE( deadline = gnutls_x509_crt_get_expiration_time(cert) );
1209                if ((deadline != (time_t)-1) && (deadline < now)) {
1210                        if (TRACE_BOOL(INFO)) {
1211                                fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :\n", conn->cc_socket, conn->cc_remid, conn->cc_id);
1212                                fd_log_debug(" - The certificate %d in the chain is expired\n", i);
1213                        }
1214                        ret = EINVAL;
1215                }
1216               
1217                GNUTLS_TRACE( deadline = gnutls_x509_crt_get_activation_time(cert) );
1218                if ((deadline != (time_t)-1) && (deadline > now)) {
1219                        if (TRACE_BOOL(INFO)) {
1220                                fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :\n", conn->cc_socket, conn->cc_remid, conn->cc_id);
1221                                fd_log_debug(" - The certificate %d in the chain is not yet activated\n", i);
1222                        }
1223                        ret = EINVAL;
1224                }
1225               
1226                if ((i == 0) && (conn->cc_tls_para.cn)) {
1227                        if (!gnutls_x509_crt_check_hostname (cert, conn->cc_tls_para.cn)) {
1228                                if (TRACE_BOOL(INFO)) {
1229                                        fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :\n", conn->cc_socket, conn->cc_remid, conn->cc_id);
1230                                        fd_log_debug(" - The certificate hostname does not match '%s'\n", conn->cc_tls_para.cn);
1231                                }
1232                                ret = EINVAL;
1233                        }
1234                }
1235               
1236                GNUTLS_TRACE( gnutls_x509_crt_deinit (cert) );
1237        }
1238
1239        return ret;
1240}
1241
1242/* TLS handshake a connection; no need to have called start_clear before. Reception is active if handhsake is successful */
1243int fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority, void * alt_creds)
1244{
1245        TRACE_ENTRY( "%p %d %p %p", conn, mode, priority, alt_creds);
1246        CHECK_PARAMS( conn && (!fd_cnx_teststate(conn, CC_STATUS_TLS)) && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) && (!conn->cc_loop) );
1247
1248        /* Save the mode */
1249        conn->cc_tls_para.mode = mode;
1250       
1251        /* Cancel receiving thread if any -- it should already be terminated anyway, we just release the resources */
1252        CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */);
1253       
1254        /* Once TLS handshake is done, we don't stop after the first message */
1255        conn->cc_loop = 1;
1256       
1257        /* Prepare the master session credentials and priority */
1258        CHECK_FCT( fd_tls_prepare(&conn->cc_tls_para.session, mode, priority, alt_creds) );
1259
1260        /* Special case: multi-stream TLS is not natively managed in GNU TLS, we use a wrapper library */
1261        if (conn->cc_sctp_para.pairs > 1) {
1262#ifdef DISABLE_SCTP
1263                ASSERT(0);
1264                CHECK_FCT( ENOTSUP );
1265#else /* DISABLE_SCTP */
1266                /* Initialize the wrapper, start the demux thread */
1267                CHECK_FCT( fd_sctps_init(conn) );
1268#endif /* DISABLE_SCTP */
1269        } else {
1270                /* Set the transport pointer passed to push & pull callbacks */
1271                GNUTLS_TRACE( gnutls_transport_set_ptr( conn->cc_tls_para.session, (gnutls_transport_ptr_t) conn ) );
1272
1273                /* Set the push and pull callbacks */
1274                GNUTLS_TRACE( gnutls_transport_set_pull_function(conn->cc_tls_para.session, (void *)fd_cnx_s_recv) );
1275                GNUTLS_TRACE( gnutls_transport_set_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_send) );
1276        }
1277
1278        /* Mark the connection as protected from here, so that the gnutls credentials will be freed */
1279        fd_cnx_addstate(conn, CC_STATUS_TLS);
1280       
1281        /* Handshake master session */
1282        {
1283                int ret;
1284               
1285                /* When gnutls 2.10.1 is around, we should use gnutls_certificate_set_verify_function and fd_tls_verify_credentials, so that handshake fails directly. */
1286               
1287                CHECK_GNUTLS_DO( ret = gnutls_handshake(conn->cc_tls_para.session),
1288                        {
1289                                if (TRACE_BOOL(INFO)) {
1290                                        fd_log_debug("TLS Handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
1291                                }
1292                                fd_cnx_markerror(conn);
1293                                return EINVAL;
1294                        } );
1295
1296                /* Now verify the remote credentials are valid -- only simple tests here */
1297                CHECK_FCT_DO( fd_tls_verify_credentials(conn->cc_tls_para.session, conn, 1), 
1298                        { 
1299                                CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_RDWR),  );
1300                                fd_cnx_markerror(conn);
1301                                return EINVAL;
1302                        });
1303        }
1304
1305        /* Multi-stream TLS: handshake other streams as well */
1306        if (conn->cc_sctp_para.pairs > 1) {
1307#ifndef DISABLE_SCTP
1308                /* Start reading the messages from the master session. That way, if the remote peer closed, we are not stuck inside handshake */
1309                CHECK_FCT(fd_sctps_startthreads(conn, 0));
1310               
1311                /* Resume all additional sessions from the master one. */
1312                CHECK_FCT(fd_sctps_handshake_others(conn, priority, alt_creds));
1313
1314                /* Start decrypting the messages from all threads and queuing them in target queue */
1315                CHECK_FCT(fd_sctps_startthreads(conn, 1));
1316#endif /* DISABLE_SCTP */
1317        } else {
1318                /* Start decrypting the data */
1319                CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_tls_single, conn ) );
1320        }
1321       
1322        return 0;
1323}
1324
1325/* Retrieve TLS credentials of the remote peer, after handshake */
1326int fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size)
1327{
1328        TRACE_ENTRY("%p %p %p", conn, cert_list, cert_list_size);
1329        CHECK_PARAMS( conn && fd_cnx_teststate(conn, CC_STATUS_TLS) && cert_list && cert_list_size );
1330       
1331        /* This function only works for X.509 certificates. */
1332        CHECK_PARAMS( gnutls_certificate_type_get (conn->cc_tls_para.session) == GNUTLS_CRT_X509 );
1333       
1334        GNUTLS_TRACE( *cert_list = gnutls_certificate_get_peers (conn->cc_tls_para.session, cert_list_size) );
1335        if (*cert_list == NULL) {
1336                TRACE_DEBUG(INFO, "No certificate was provided by remote peer / an error occurred.");
1337                return EINVAL;
1338        }
1339
1340        TRACE_DEBUG( FULL, "Saved certificate chain (%d certificates) in peer structure.", *cert_list_size);
1341       
1342        return 0;
1343}
1344
1345/* 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. */
1346/* if the altfifo has been set on this conn object, this function must not be called */
1347int fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len)
1348{
1349        int    ev;
1350        size_t ev_sz;
1351        void * ev_data;
1352       
1353        TRACE_ENTRY("%p %p %p %p", conn, timeout, buf, len);
1354        CHECK_PARAMS(conn && (conn->cc_socket > 0) && buf && len);
1355        CHECK_PARAMS(conn->cc_rcvthr != (pthread_t)NULL);
1356        CHECK_PARAMS(conn->cc_alt == NULL);
1357
1358        /* Now, pull the first event */
1359get_next:
1360        if (timeout) {
1361                CHECK_FCT( fd_event_timedget(conn->cc_incoming, timeout, FDEVP_PSM_TIMEOUT, &ev, &ev_sz, &ev_data) );
1362        } else {
1363                CHECK_FCT( fd_event_get(conn->cc_incoming, &ev, &ev_sz, &ev_data) );
1364        }
1365       
1366        switch (ev) {
1367                case FDEVP_CNX_MSG_RECV:
1368                        /* We got one */
1369                        *len = ev_sz;
1370                        *buf = ev_data;
1371                        return 0;
1372                       
1373                case FDEVP_PSM_TIMEOUT:
1374                        TRACE_DEBUG(FULL, "Timeout event received");
1375                        return ETIMEDOUT;
1376                       
1377                case FDEVP_CNX_EP_CHANGE:
1378                        /* We ignore this event */
1379                        goto get_next;
1380                       
1381                case FDEVP_CNX_ERROR:
1382                        TRACE_DEBUG(FULL, "Received ERROR event on the connection");
1383                        return ENOTCONN;
1384        }
1385       
1386        TRACE_DEBUG(INFO, "Received unexpected event %d (%s)", ev, fd_pev_str(ev));
1387        return EINVAL;
1388}
1389
1390/* Where the events are sent */
1391struct fifo * fd_cnx_target_queue(struct cnxctx * conn)
1392{
1393        struct fifo *q;
1394        CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
1395        q = conn->cc_alt ?: conn->cc_incoming;
1396        CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
1397        return q;
1398}
1399
1400/* Set an alternate FIFO list to send FDEVP_CNX_* events to */
1401int fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo)
1402{
1403        int ret;
1404        TRACE_ENTRY( "%p %p", conn, alt_fifo );
1405        CHECK_PARAMS( conn && alt_fifo && conn->cc_incoming );
1406       
1407        /* The magic function does it all */
1408        CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
1409        CHECK_FCT_DO( ret = fd_fifo_move( conn->cc_incoming, alt_fifo, &conn->cc_alt ), );
1410        CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
1411       
1412        return ret;
1413}
1414
1415/* Send function when no multi-stream is involved, or sending on stream #0 (send() always use stream 0)*/
1416static int send_simple(struct cnxctx * conn, unsigned char * buf, size_t len)
1417{
1418        ssize_t ret;
1419        size_t sent = 0;
1420        TRACE_ENTRY("%p %p %zd", conn, buf, len);
1421        do {
1422                if (fd_cnx_teststate(conn, CC_STATUS_TLS)) {
1423                        CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_tls_para.session, buf + sent, len - sent),  );
1424                } else {
1425                        /* Maybe better to replace this call with sendmsg for atomic sending? */
1426                        CHECK_SYS_DO( ret = fd_cnx_s_send(conn, buf + sent, len - sent), );
1427                }
1428                if (ret <= 0)
1429                        return ENOTCONN;
1430               
1431                sent += ret;
1432        } while ( sent < len );
1433        return 0;
1434}
1435
1436/* Send a message -- this is synchronous -- and we assume it's never called by several threads at the same time (on the same conn), so we don't protect. */
1437int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len, uint32_t flags)
1438{
1439        TRACE_ENTRY("%p %p %zd %x", conn, buf, len, flags);
1440       
1441        CHECK_PARAMS(conn && (conn->cc_socket > 0) && (! fd_cnx_teststate(conn, CC_STATUS_ERROR)) && buf && len);
1442
1443        TRACE_DEBUG(FULL, "Sending %zdb %sdata on connection %s", len, fd_cnx_teststate(conn, CC_STATUS_TLS) ? "TLS-protected ":"", conn->cc_id);
1444       
1445        switch (conn->cc_proto) {
1446                case IPPROTO_TCP:
1447                        CHECK_FCT( send_simple(conn, buf, len) );
1448                        break;
1449               
1450#ifndef DISABLE_SCTP
1451                case IPPROTO_SCTP: {
1452                        if (flags & FD_CNX_ORDERED) {
1453                                /* We send over stream #0 */
1454                                CHECK_FCT( send_simple(conn, buf, len) );
1455                        } else {
1456                                /* Default case : no flag specified */
1457                       
1458                                int another_str = 0; /* do we send over stream #0 ? */
1459                               
1460                                if ((conn->cc_sctp_para.str_out > 1) && ((!fd_cnx_teststate(conn, CC_STATUS_TLS)) || (conn->cc_sctp_para.pairs > 1)))  {
1461                                        /* Update the id of the stream we will send this message over */
1462                                        conn->cc_sctp_para.next += 1;
1463                                        conn->cc_sctp_para.next %= (fd_cnx_teststate(conn, CC_STATUS_TLS) ? conn->cc_sctp_para.pairs : conn->cc_sctp_para.str_out);
1464                                        another_str = (conn->cc_sctp_para.next ? 1 : 0);
1465                                }
1466
1467                                if ( ! another_str ) {
1468                                        CHECK_FCT( send_simple(conn, buf, len) );
1469                                } else {
1470                                        if (!fd_cnx_teststate(conn, CC_STATUS_TLS)) {
1471                                                CHECK_FCT_DO( fd_sctp_sendstr(conn, conn->cc_sctp_para.next, buf, len), { fd_cnx_markerror(conn); return ENOTCONN; } );
1472                                        } else {
1473                                                /* push the record to the appropriate session */
1474                                                ssize_t ret;
1475                                                size_t sent = 0;
1476                                                ASSERT(conn->cc_sctps_data.array != NULL);
1477                                                do {
1478                                                        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), );
1479                                                        if (ret <= 0)
1480                                                                return ENOTCONN;
1481
1482                                                        sent += ret;
1483                                                } while ( sent < len );
1484                                        }
1485                                }
1486                        }
1487                }
1488                break;
1489#endif /* DISABLE_SCTP */
1490       
1491                default:
1492                        TRACE_DEBUG(INFO, "Unknwon protocol: %d", conn->cc_proto);
1493                        ASSERT(0);
1494                        return ENOTSUP; /* or EINVAL... */
1495        }
1496       
1497        return 0;
1498}
1499
1500
1501/**************************************/
1502/*     Destruction of connection      */
1503/**************************************/
1504
1505/* Destroy a conn structure, and shutdown the socket */
1506void fd_cnx_destroy(struct cnxctx * conn)
1507{
1508        TRACE_ENTRY("%p", conn);
1509       
1510        CHECK_PARAMS_DO(conn, return);
1511       
1512        fd_cnx_addstate(conn, CC_STATUS_CLOSING);
1513       
1514        /* Initiate shutdown of the TLS session(s): call gnutls_bye(WR), then read until error */
1515        if (fd_cnx_teststate(conn, CC_STATUS_TLS)) {
1516#ifndef DISABLE_SCTP
1517                if (conn->cc_sctp_para.pairs > 1) {
1518                        if (! fd_cnx_teststate(conn, CC_STATUS_ERROR )) {
1519                                /* Bye on master session */
1520                                CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) );
1521                        }
1522
1523                        if (! fd_cnx_teststate(conn, CC_STATUS_ERROR ) ) {
1524                                /* and other stream pairs */
1525                                fd_sctps_bye(conn);
1526                        }
1527
1528                        if (! fd_cnx_teststate(conn, CC_STATUS_ERROR ) ) {
1529                                /* Now wait for all decipher threads to terminate */
1530                                fd_sctps_waitthreadsterm(conn);
1531                        } else {
1532                                /* Abord the threads, the connection is dead already */
1533                                fd_sctps_stopthreads(conn);
1534                        }
1535
1536                        /* Deinit gnutls resources */
1537                        fd_sctps_gnutls_deinit_others(conn);
1538                        if (conn->cc_tls_para.session) {
1539                                GNUTLS_TRACE( gnutls_deinit(conn->cc_tls_para.session) );
1540                                conn->cc_tls_para.session = NULL;
1541                        }
1542                       
1543                        /* Destroy the wrapper (also stops the demux thread) */
1544                        fd_sctps_destroy(conn);
1545
1546                } else {
1547#endif /* DISABLE_SCTP */
1548                /* We are TLS, but not using the sctps wrapper layer */
1549                        if (! fd_cnx_teststate(conn, CC_STATUS_ERROR ) ) {
1550                                /* Master session */
1551                                CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) );
1552                        }
1553
1554                        if (! fd_cnx_teststate(conn, CC_STATUS_ERROR ) ) {
1555                                /* In this case, just wait for thread rcvthr_tls_single to terminate */
1556                                if (conn->cc_rcvthr != (pthread_t)NULL) {
1557                                        CHECK_POSIX_DO(  pthread_join(conn->cc_rcvthr, NULL), /* continue */  );
1558                                        conn->cc_rcvthr = (pthread_t)NULL;
1559                                }
1560                        } else {
1561                                /* Cancel the receiver thread in case it did not already terminate */
1562                                CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ );
1563                        }
1564                       
1565                        /* Free the resources of the TLS session */
1566                        if (conn->cc_tls_para.session) {
1567                                GNUTLS_TRACE( gnutls_deinit(conn->cc_tls_para.session) );
1568                                conn->cc_tls_para.session = NULL;
1569                        }
1570#ifndef DISABLE_SCTP
1571                }
1572#endif /* DISABLE_SCTP */
1573        }
1574       
1575        /* Terminate the thread in case it is not done yet -- is there any such case left ?*/
1576        CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ );
1577               
1578        /* Shut the connection down */
1579        if (conn->cc_socket > 0) {
1580                shutdown(conn->cc_socket, SHUT_RDWR);
1581                close(conn->cc_socket);
1582                conn->cc_socket = -1;
1583        }
1584       
1585        /* Empty and destroy FIFO list */
1586        if (conn->cc_incoming) {
1587                fd_event_destroy( &conn->cc_incoming, free );
1588        }
1589       
1590        /* Free the object */
1591        free(conn);
1592       
1593        /* Done! */
1594        return;
1595}
Note: See TracBrowser for help on using the repository browser.