Navigation


source: freeDiameter/freeDiameter/server.c @ 29:5ba91682f0bc

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

Added a test for cnxctx (tbc) and fixed some bugs

File size: 12.9 KB
Line 
1/*********************************************************************************************************
2* Software License Agreement (BSD License)                                                               *
3* Author: Sebastien Decugis <sdecugis@nict.go.jp>                                                        *
4*                                                                                                        *
5* Copyright (c) 2009, 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 "fD.h"
37
38/* Server (listening) part of the daemon */
39
40struct fd_list          FD_SERVERS = FD_LIST_INITIALIZER(FD_SERVERS);   /* The list of all server objects */
41/* We don't need to protect this list, it is only accessed from the main daemon thread. */
42
43/* Servers information */
44struct server {
45        struct fd_list  chain;          /* link in the FD_SERVERS list */
46
47        struct cnxctx * conn;           /* server connection context (listening socket) */
48        int             proto;          /* IPPROTO_TCP or IPPROTO_SCTP */
49        int             secur;          /* TLS is started immediatly after connection ? */
50       
51        pthread_t       thr;            /* The thread listening for new connections */
52        int             status;         /* 0 : not created; 1 : running; 2 : terminated */
53       
54        struct fd_list  clients;        /* List of clients connected to this server, not yet identified */
55        pthread_mutex_t clients_mtx;    /* Mutex to protect the list of clients */
56};
57
58/* Client information (connecting peer for which we don't have the CER yet) */
59struct client {
60        struct fd_list   chain; /* link in the server's list of clients */
61        struct cnxctx   *conn;  /* Parameters of the connection */
62        struct timespec  ts;    /* Deadline for receiving CER (after INCNX_TIMEOUT) */
63        pthread_t        thr;   /* connection state machine */
64};
65
66
67/* Dump all servers information */
68void fd_servers_dump()
69{
70        struct fd_list * li, *cli;
71       
72        fd_log_debug("Dumping servers list :\n");
73        for (li = FD_SERVERS.next; li != &FD_SERVERS; li = li->next) {
74                struct server * s = (struct server *)li;
75                fd_log_debug("  Serv %p '%s': %s, %s, %s\n", 
76                                s, fd_cnx_getid(s->conn), 
77                                IPPROTO_NAME( s->proto ),
78                                s->secur ? "Secur" : "NotSecur",
79                                (s->status == 0) ? "Thread not created" :
80                                ((s->status == 1) ? "Thread running" :
81                                ((s->status == 2) ? "Thread terminated" :
82                                                          "Thread status unknown")));
83                /* Dump the client list of this server */
84                (void) pthread_mutex_lock(&s->clients_mtx);
85                for (cli = s->clients.next; cli != &s->clients; cli = cli->next) {
86                        struct client * c = (struct client *)cli;
87                        char bufts[128];
88                        fd_log_debug("     Connected: '%s' (timeout: %s)\n",
89                                        fd_cnx_getid(c->conn),
90                                        fd_log_time(&c->ts, bufts, sizeof(bufts)));
91                }
92                (void) pthread_mutex_unlock(&s->clients_mtx);
93        }
94}
95
96
97/* The state machine to handle incoming connection before the remote peer is identified */
98static void * client_sm(void * arg)
99{
100        struct client * c = arg;
101        struct server * s = NULL;
102        uint8_t       * buf = NULL;
103        size_t          bufsz;
104        struct msg    * msg = NULL;
105        struct msg_hdr *hdr = NULL;
106       
107        TRACE_ENTRY("%p", c);
108       
109        CHECK_PARAMS_DO(c && c->conn && c->chain.head, goto fatal_error );
110       
111        s = c->chain.head->o;
112       
113        /* Name the current thread */
114        fd_log_threadname ( fd_cnx_getid(c->conn) );
115       
116        /* Handshake if we are a secure server port, or start clear otherwise */
117        if (s->secur) {
118                int ret = fd_cnx_handshake(c->conn, GNUTLS_SERVER, NULL, NULL);
119                if (ret != 0) {
120                        if (TRACE_BOOL(INFO)) {
121                                fd_log_debug("TLS handshake failed for client '%s', connection aborted.\n", fd_cnx_getid(c->conn));
122                        }
123                        goto cleanup;
124                }
125        } else {
126                CHECK_FCT_DO( fd_cnx_start_clear(c->conn, 0), goto cleanup );
127        }
128       
129        /* Set the timeout to receive the first message */
130        CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &c->ts), goto fatal_error );
131        c->ts.tv_sec += INCNX_TIMEOUT;
132       
133        /* Receive the first Diameter message on the connection -- cleanup in case of timeout */
134        CHECK_FCT_DO( fd_cnx_receive(c->conn, &c->ts, &buf, &bufsz), goto cleanup );
135       
136        TRACE_DEBUG(FULL, "Received %zdb from new client '%s'", bufsz, fd_cnx_getid(c->conn));
137       
138        /* Try parsing this message */
139        CHECK_FCT_DO( fd_msg_parse_buffer( &buf, bufsz, &msg ), /* Parsing failed */ goto cleanup );
140       
141        /* We expect a CER, it must parse with our dictionary and rules */
142        CHECK_FCT_DO( fd_msg_parse_rules( msg, fd_g_config->cnf_dict, NULL ), /* Parsing failed -- trace details ? */ goto cleanup );
143       
144        if (TRACE_BOOL(FULL)) {
145                fd_log_debug("Received Diameter message from new client '%s':\n", fd_cnx_getid(c->conn));
146                fd_msg_dump_walk(FULL, msg);
147        }
148       
149        /* Now check we received a CER */
150        CHECK_FCT_DO( fd_msg_hdr ( msg, &hdr ), goto fatal_error );
151        CHECK_PARAMS_DO( (hdr->msg_appl == 0) && (hdr->msg_flags & CMD_FLAG_REQUEST) && (hdr->msg_code == CC_CAPABILITIES_EXCHANGE),
152                { fd_log_debug("Connection '%s', expecting CER, received something else, closing...\n", fd_cnx_getid(c->conn)); goto cleanup; } );
153       
154        /* Finally, pass the information to the peers module which will handle it next */
155        pthread_cleanup_push((void *)fd_cnx_destroy, c->conn);
156        pthread_cleanup_push((void *)fd_msg_free, msg);
157        CHECK_FCT_DO( fd_peer_handle_newCER( &msg, &c->conn ), goto cleanup );
158        pthread_cleanup_pop(0);
159        pthread_cleanup_pop(0);
160       
161        /* The end, we cleanup the client structure */
162cleanup:
163        /* Unlink the client structure */
164        CHECK_POSIX_DO( pthread_mutex_lock(&s->clients_mtx), goto fatal_error );
165        fd_list_unlink( &c->chain );
166        CHECK_POSIX_DO( pthread_mutex_unlock(&s->clients_mtx), goto fatal_error );
167       
168        /* Destroy the connection object if present */
169        if (c->conn)
170                fd_cnx_destroy(c->conn);
171       
172        /* Cleanup the received buffer if any */
173        free(buf);
174       
175        /* Cleanup the parsed message if any */
176        if (msg) {
177                CHECK_FCT_DO( fd_msg_free(msg), /* continue */);
178        }
179       
180        /* Detach the thread, cleanup the client structure */
181        pthread_detach(pthread_self());
182        free(c);
183        return NULL;
184       
185fatal_error:    /* This has effect to terminate the daemon */
186        CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
187        return NULL;
188}
189
190/* The thread managing a server */
191static void * serv_th(void * arg)
192{
193        struct server *s = (struct server *)arg;
194       
195        CHECK_PARAMS_DO(s, goto error);
196        fd_log_threadname ( fd_cnx_getid(s->conn) );
197        s->status = 1;
198       
199        /* Accept incoming connections */
200        CHECK_FCT_DO( fd_cnx_serv_listen(s->conn), goto error );
201       
202        do {
203                struct client * c = NULL;
204                struct cnxctx * conn = NULL;
205               
206                /* Wait for a new client */
207                CHECK_MALLOC_DO( conn = fd_cnx_serv_accept(s->conn), goto error );
208               
209                TRACE_DEBUG(FULL, "New connection accepted");
210               
211                /* Create a client structure */
212                CHECK_MALLOC_DO( c = malloc(sizeof(struct client)), goto error );
213                memset(c, 0, sizeof(struct client));
214                fd_list_init(&c->chain, c);
215                c->conn = conn;
216               
217                /* Save the client in the list */
218                CHECK_POSIX_DO( pthread_mutex_lock( &s->clients_mtx ), goto error );
219                fd_list_insert_before(&s->clients, &c->chain);
220                CHECK_POSIX_DO( pthread_mutex_unlock( &s->clients_mtx ), goto error );
221
222                /* Start the client thread */
223                CHECK_POSIX_DO( pthread_create( &c->thr, NULL, client_sm, c ), goto error );
224               
225        } while (1);
226       
227error: 
228        if (s)
229                s->status = 2;
230        /* Send error signal to the daemon */
231        TRACE_DEBUG(INFO, "An error occurred in server module! Thread is terminating...");
232        CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
233
234        return NULL;
235}
236
237
238/* Create a new server structure */
239static struct server * new_serv( int proto, int secur )
240{
241        struct server * new;
242       
243        /* New server structure */
244        CHECK_MALLOC_DO( new = malloc(sizeof(struct server)), return NULL );
245       
246        memset(new, 0, sizeof(struct server));
247        fd_list_init(&new->chain, new);
248        new->proto = proto;
249        new->secur = secur;
250        CHECK_POSIX_DO( pthread_mutex_init(&new->clients_mtx, NULL), return NULL );
251        fd_list_init(&new->clients, new);
252       
253        return new;
254}
255
256/* Start all the servers */
257int fd_servers_start()
258{
259        struct server * s;
260       
261        int empty_conf_ep = FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints);
262       
263        /* SCTP */
264        if (!fd_g_config->cnf_flags.no_sctp) {
265#ifdef DISABLE_SCTP
266                ASSERT(0);
267#else /* DISABLE_SCTP */
268               
269                /* Create the server on default port */
270                CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 0) );
271                CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port, FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints) ? NULL : &fd_g_config->cnf_endpoints) );
272                fd_list_insert_before( &FD_SERVERS, &s->chain );
273                CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
274               
275                /* Retrieve the list of endpoints if it was empty */
276                if (empty_conf_ep) {
277                        (void) fd_cnx_getendpoints(s->conn, &fd_g_config->cnf_endpoints, NULL);
278                }
279               
280                /* Create the server on secure port */
281                CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 1) );
282                CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port_tls, FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints) ? NULL : &fd_g_config->cnf_endpoints) );
283                fd_list_insert_before( &FD_SERVERS, &s->chain );
284                CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
285               
286#endif /* DISABLE_SCTP */
287        }
288       
289        /* TCP */
290        if (!fd_g_config->cnf_flags.no_tcp) {
291               
292                if (empty_conf_ep) {
293                        /* Bind TCP servers on [0.0.0.0] */
294                        if (!fd_g_config->cnf_flags.no_ip4) {
295                               
296                                CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
297                                CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET, NULL) );
298                                fd_list_insert_before( &FD_SERVERS, &s->chain );
299                                CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
300
301                                CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
302                                CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET, NULL) );
303                                fd_list_insert_before( &FD_SERVERS, &s->chain );
304                                CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
305                        }
306                        /* Bind TCP servers on [::] */
307                        if (!fd_g_config->cnf_flags.no_ip6) {
308                               
309                                CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
310                                CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET6, NULL) );
311                                fd_list_insert_before( &FD_SERVERS, &s->chain );
312                                CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
313
314                                CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
315                                CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET6, NULL) );
316                                fd_list_insert_before( &FD_SERVERS, &s->chain );
317                                CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
318                        }
319                } else {
320                        /* Create all endpoints -- check flags */
321                        struct fd_list * li;
322                        for (li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
323                                struct fd_endpoint * ep = (struct fd_endpoint *)li;
324                                sSA * sa = (sSA *) &ep->ss;
325                                if (! (ep->flags & EP_FL_CONF))
326                                        continue;
327                                if (fd_g_config->cnf_flags.no_ip4 && (sa->sa_family == AF_INET))
328                                        continue;
329                                if (fd_g_config->cnf_flags.no_ip6 && (sa->sa_family == AF_INET6))
330                                        continue;
331                               
332                                CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
333                                CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, sa->sa_family, ep) );
334                                fd_list_insert_before( &FD_SERVERS, &s->chain );
335                                CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
336
337                                CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
338                                CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, sa->sa_family, ep) );
339                                fd_list_insert_before( &FD_SERVERS, &s->chain );
340                                CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
341                        }
342                }
343        }
344       
345        return 0;
346}
347
348/* Terminate all the servers */
349int fd_servers_stop()
350{
351        TODO("Not implemented");
352       
353        /* Loop on all servers */
354                /* cancel thread */
355                /* destroy server connection context */
356                /* cancel and destroy all clients */
357}
Note: See TracBrowser for help on using the repository browser.