Navigation


source: freeDiameter/libfdcore/cnxctx.c @ 662:2e94ef0515d7

1.1.0-rc1
Last change on this file since 662:2e94ef0515d7 was 662:2e94ef0515d7, checked in by Sebastien Decugis <sdecugis@nict.go.jp>, 11 years ago

Updated copyright information

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