Navigation


source: freeDiameter/freeDiameter/routing_dispatch.c @ 649:5e5d8152c229

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

Implemented fd_msg_send_timeout to close #10. Not tested yet.

File size: 38.4 KB
Line 
1/*********************************************************************************************************
2* Software License Agreement (BSD License)                                                               *
3* Author: Sebastien Decugis <sdecugis@nict.go.jp>                                                        *
4*                                                                                                        *
5* Copyright (c) 2010, 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/********************************************************************************/
39/*              First part : handling the extensions callbacks                  */
40/********************************************************************************/
41
42/* Lists of the callbacks, and locks to protect them */
43static pthread_rwlock_t rt_fwd_lock = PTHREAD_RWLOCK_INITIALIZER;
44static struct fd_list   rt_fwd_list = FD_LIST_INITIALIZER_O(rt_fwd_list, &rt_fwd_lock);
45
46static pthread_rwlock_t rt_out_lock = PTHREAD_RWLOCK_INITIALIZER;
47static struct fd_list   rt_out_list = FD_LIST_INITIALIZER_O(rt_out_list, &rt_out_lock);
48
49/* Items in the lists are the same */
50struct rt_hdl {
51        struct fd_list  chain;  /* link in the rt_fwd_list or rt_out_list */
52        void *          cbdata; /* the registered data */
53        union {
54                int     order;  /* This value is used to sort the list */
55                int     dir;    /* It is the direction for FWD handlers */
56                int     prio;   /* and the priority for OUT handlers */
57        };
58        union {
59                int (*rt_fwd_cb)(void * cbdata, struct msg ** msg);
60                int (*rt_out_cb)(void * cbdata, struct msg * msg, struct fd_list * candidates);
61        };
62};     
63
64/* Add a new entry in the list */
65static int add_ordered(struct rt_hdl * new, struct fd_list * list)
66{
67        /* The list is ordered by prio parameter */
68        struct fd_list * li;
69       
70        CHECK_POSIX( pthread_rwlock_wrlock(list->o) );
71       
72        for (li = list->next; li != list; li = li->next) {
73                struct rt_hdl * h = (struct rt_hdl *) li;
74                if (new->order <= h->order)
75                        break;
76        }
77       
78        fd_list_insert_before(li, &new->chain);
79       
80        CHECK_POSIX( pthread_rwlock_unlock(list->o) );
81       
82        return 0;
83}
84
85/* Register a new FWD callback */
86int fd_rt_fwd_register ( int (*rt_fwd_cb)(void * cbdata, struct msg ** msg), void * cbdata, enum fd_rt_fwd_dir dir, struct fd_rt_fwd_hdl ** handler )
87{
88        struct rt_hdl * new;
89       
90        TRACE_ENTRY("%p %p %d %p", rt_fwd_cb, cbdata, dir, handler);
91        CHECK_PARAMS( rt_fwd_cb );
92        CHECK_PARAMS( (dir >= RT_FWD_REQ) && ( dir <= RT_FWD_ANS) );
93       
94        /* Create a new container */
95        CHECK_MALLOC(new = malloc(sizeof(struct rt_hdl)));
96        memset(new, 0, sizeof(struct rt_hdl));
97       
98        /* Write the content */
99        fd_list_init(&new->chain, NULL);
100        new->cbdata     = cbdata;
101        new->dir        = dir;
102        new->rt_fwd_cb  = rt_fwd_cb;
103       
104        /* Save this in the list */
105        CHECK_FCT( add_ordered(new, &rt_fwd_list) );
106       
107        /* Give it back to the extension if needed */
108        if (handler)
109                *handler = (void *)new;
110       
111        return 0;
112}
113
114/* Remove it */
115int fd_rt_fwd_unregister ( struct fd_rt_fwd_hdl * handler, void ** cbdata )
116{
117        struct rt_hdl * del;
118        TRACE_ENTRY( "%p %p", handler, cbdata);
119        CHECK_PARAMS( handler );
120       
121        del = (struct rt_hdl *)handler;
122        CHECK_PARAMS( del->chain.head == &rt_fwd_list );
123       
124        /* Unlink */
125        CHECK_POSIX( pthread_rwlock_wrlock(&rt_fwd_lock) );
126        fd_list_unlink(&del->chain);
127        CHECK_POSIX( pthread_rwlock_unlock(&rt_fwd_lock) );
128       
129        if (cbdata)
130                *cbdata = del->cbdata;
131       
132        free(del);
133        return 0;
134}
135
136/* Register a new OUT callback */
137int fd_rt_out_register ( int (*rt_out_cb)(void * cbdata, struct msg * msg, struct fd_list * candidates), void * cbdata, int priority, struct fd_rt_out_hdl ** handler )
138{
139        struct rt_hdl * new;
140       
141        TRACE_ENTRY("%p %p %d %p", rt_out_cb, cbdata, priority, handler);
142        CHECK_PARAMS( rt_out_cb );
143       
144        /* Create a new container */
145        CHECK_MALLOC(new = malloc(sizeof(struct rt_hdl)));
146        memset(new, 0, sizeof(struct rt_hdl));
147       
148        /* Write the content */
149        fd_list_init(&new->chain, NULL);
150        new->cbdata     = cbdata;
151        new->prio       = priority;
152        new->rt_out_cb  = rt_out_cb;
153       
154        /* Save this in the list */
155        CHECK_FCT( add_ordered(new, &rt_out_list) );
156       
157        /* Give it back to the extension if needed */
158        if (handler)
159                *handler = (void *)new;
160       
161        return 0;
162}
163
164/* Remove it */
165int fd_rt_out_unregister ( struct fd_rt_out_hdl * handler, void ** cbdata )
166{
167        struct rt_hdl * del;
168        TRACE_ENTRY( "%p %p", handler, cbdata);
169        CHECK_PARAMS( handler );
170       
171        del = (struct rt_hdl *)handler;
172        CHECK_PARAMS( del->chain.head == &rt_out_list );
173       
174        /* Unlink */
175        CHECK_POSIX( pthread_rwlock_wrlock(&rt_out_lock) );
176        fd_list_unlink(&del->chain);
177        CHECK_POSIX( pthread_rwlock_unlock(&rt_out_lock) );
178       
179        if (cbdata)
180                *cbdata = del->cbdata;
181       
182        free(del);
183        return 0;
184}
185
186/********************************************************************************/
187/*                      Some default OUT routing callbacks                      */
188/********************************************************************************/
189
190/* Prevent sending to peers that do not support the message application */
191static int dont_send_if_no_common_app(void * cbdata, struct msg * msg, struct fd_list * candidates)
192{
193        struct fd_list * li;
194        struct msg_hdr * hdr;
195       
196        TRACE_ENTRY("%p %p %p", cbdata, msg, candidates);
197        CHECK_PARAMS(msg && candidates);
198       
199        CHECK_FCT( fd_msg_hdr(msg, &hdr) );
200       
201        /* For Base Diameter Protocol, every peer is supposed to support it, so skip */
202        if (hdr->msg_appl == 0)
203                return 0;
204       
205        /* Otherwise, check that the peers support the application */
206        for (li = candidates->next; li != candidates; li = li->next) {
207                struct rtd_candidate *c = (struct rtd_candidate *) li;
208                struct fd_peer * peer;
209                struct fd_app *found;
210                CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) );
211                if (peer && (peer->p_hdr.info.runtime.pir_relay == 0)) {
212                        /* Check if the remote peer advertised the message's appli */
213                        CHECK_FCT( fd_app_check(&peer->p_hdr.info.runtime.pir_apps, hdr->msg_appl, &found) );
214                        if (!found)
215                                c->score += FD_SCORE_NO_DELIVERY;
216                }
217        }
218
219        return 0;
220}
221
222/* Detect if the Destination-Host and Destination-Realm match the peer */
223static int score_destination_avp(void * cbdata, struct msg * msg, struct fd_list * candidates)
224{
225        struct fd_list * li;
226        struct avp * avp;
227        union avp_value *dh = NULL, *dr = NULL;
228       
229        TRACE_ENTRY("%p %p %p", cbdata, msg, candidates);
230        CHECK_PARAMS(msg && candidates);
231       
232        /* Search the Destination-Host and Destination-Realm AVPs -- we could also use fd_msg_search_avp here, but this one is slightly more efficient */
233        CHECK_FCT(  fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &avp, NULL) );
234        while (avp) {
235                struct avp_hdr * ahdr;
236                CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr ) );
237
238                if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) {
239                        switch (ahdr->avp_code) {
240                                case AC_DESTINATION_HOST:
241                                        /* Parse this AVP */
242                                        CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) );
243                                        ASSERT( ahdr->avp_value );
244                                        dh = ahdr->avp_value;
245                                        break;
246
247                                case AC_DESTINATION_REALM:
248                                        /* Parse this AVP */
249                                        CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) );
250                                        ASSERT( ahdr->avp_value );
251                                        dr = ahdr->avp_value;
252                                        break;
253                        }
254                }
255
256                if (dh && dr)
257                        break;
258
259                /* Go to next AVP */
260                CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) );
261        }
262       
263        /* Now, check each candidate against these AVP values */
264        for (li = candidates->next; li != candidates; li = li->next) {
265                struct rtd_candidate *c = (struct rtd_candidate *) li;
266                struct fd_peer * peer;
267                CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) );
268                if (peer) {
269                        if (dh
270                                && (dh->os.len == strlen(peer->p_hdr.info.pi_diamid)) 
271                                /* Here again we use strncasecmp on UTF8 data... This should probably be changed. */
272                                && (strncasecmp(peer->p_hdr.info.pi_diamid, (char *)dh->os.data, dh->os.len) == 0)) {
273                                /* The candidate is the Destination-Host */
274                                c->score += FD_SCORE_FINALDEST;
275                        } else {
276                                if (dr  && peer->p_hdr.info.runtime.pir_realm
277                                        && (dr->os.len == strlen(peer->p_hdr.info.runtime.pir_realm)) 
278                                        /* Yet another case where we use strncasecmp on UTF8 data... Hmmm :-( */
279                                        && (strncasecmp(peer->p_hdr.info.runtime.pir_realm, (char *)dr->os.data, dr->os.len) == 0)) {
280                                        /* The candidate's realm matchs the Destination-Realm */
281                                        c->score += FD_SCORE_REALM;
282                                }
283                        }
284                }
285        }
286
287        return 0;
288}
289
290/********************************************************************************/
291/*                        Helper functions                                      */
292/********************************************************************************/
293
294/* Find (first) '!' and '@' positions in a UTF-8 encoded string (User-Name AVP value) */
295static void nai_get_indexes(union avp_value * un, int * excl_idx, int * at_idx)
296{
297        int i;
298       
299        TRACE_ENTRY("%p %p %p", un, excl_idx, at_idx);
300        CHECK_PARAMS_DO( un && excl_idx, return );
301        *excl_idx = 0;
302       
303        /* Search if there is a '!' before any '@' -- do we need to check it contains a '.' ? */
304        for (i = 0; i < un->os.len; i++) {
305                /* The '!' marks the decorated NAI */
306                if ( un->os.data[i] == (unsigned char) '!' ) {
307                        if (!*excl_idx)
308                                *excl_idx = i;
309                        if (!at_idx)
310                                return;
311                }
312                /* If we reach the realm part, we can stop */
313                if ( un->os.data[i] == (unsigned char) '@' ) {
314                        if (at_idx)
315                                *at_idx = i;
316                        break;
317                }
318                /* Skip escaped characters */
319                if ( un->os.data[i] == (unsigned char) '\\' ) {
320                        i++;
321                        continue;
322                }
323                /* Skip UTF-8 characters spanning on several bytes */
324                if ( (un->os.data[i] & 0xF8) == 0xF0 ) { /* 11110zzz */
325                        i += 3;
326                        continue;
327                }
328                if ( (un->os.data[i] & 0xF0) == 0xE0 ) { /* 1110yyyy */
329                        i += 2;
330                        continue;
331                }
332                if ( (un->os.data[i] & 0xE0) == 0xC0 ) { /* 110yyyxx */
333                        i += 1;
334                        continue;
335                }
336        }
337       
338        return;
339}       
340
341/* Test if a User-Name AVP contains a Decorated NAI -- RFC4282, RFC5729 */
342static int is_decorated_NAI(union avp_value * un)
343{
344        int i;
345        TRACE_ENTRY("%p", un);
346       
347        /* If there was no User-Name, we return false */
348        if (un == NULL)
349                return 0;
350       
351        nai_get_indexes(un, &i, NULL);
352       
353        return i;
354}
355
356/* Create new User-Name and Destination-Realm values */
357static int process_decorated_NAI(union avp_value * un, union avp_value * dr)
358{
359        int at_idx = 0, sep_idx = 0;
360        unsigned char * old_un;
361        TRACE_ENTRY("%p %p", un, dr);
362        CHECK_PARAMS(un && dr);
363       
364        /* Save the decorated User-Name, for example 'homerealm.example.net!user@otherrealm.example.net' */
365        old_un = un->os.data;
366       
367        /* Search the positions of the first '!' and the '@' in the string */
368        nai_get_indexes(un, &sep_idx, &at_idx);
369        CHECK_PARAMS( (0 < sep_idx) && (sep_idx < at_idx) && (at_idx < un->os.len));
370       
371        /* Create the new User-Name value */
372        CHECK_MALLOC( un->os.data = malloc( at_idx ) );
373        memcpy( un->os.data, old_un + sep_idx + 1, at_idx - sep_idx ); /* user@ */
374        memcpy( un->os.data + at_idx - sep_idx, old_un, sep_idx ); /* homerealm.example.net */
375       
376        /* Create the new Destination-Realm value */
377        CHECK_MALLOC( dr->os.data = realloc(dr->os.data, sep_idx) );
378        memcpy( dr->os.data, old_un, sep_idx );
379        dr->os.len = sep_idx;
380       
381        TRACE_DEBUG(FULL, "Processed Decorated NAI : '%.*s' became '%.*s' (%.*s)",
382                                un->os.len, old_un,
383                                at_idx, un->os.data,
384                                dr->os.len, dr->os.data);
385       
386        un->os.len = at_idx;
387        free(old_un);
388       
389        return 0;
390}
391
392/* Function to return an error to an incoming request */
393static int return_error(struct msg ** pmsg, char * error_code, char * error_message, struct avp * failedavp)
394{
395        struct fd_peer * peer;
396        int is_loc = 0;
397
398        /* Get the source of the message */
399        {
400                char * id;
401                CHECK_FCT( fd_msg_source_get( *pmsg, &id ) );
402               
403                if (id == NULL) {
404                        is_loc = 1; /* The message was issued locally */
405                } else {
406               
407                        /* Search the peer with this id */
408                        CHECK_FCT( fd_peer_getbyid( id, (void *)&peer ) );
409
410                        if (!peer) {
411                                TRACE_DEBUG(INFO, "Unable to send error '%s' to deleted peer '%s' in reply to:", error_code, id);
412                                fd_msg_dump_walk(INFO, *pmsg);
413                                fd_msg_free(*pmsg);
414                                *pmsg = NULL;
415                                return 0;
416                        }
417                }
418        }
419       
420        /* Create the error message */
421        CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, pmsg, MSGFL_ANSW_ERROR ) );
422
423        /* Set the error code */
424        CHECK_FCT( fd_msg_rescode_set(*pmsg, error_code, error_message, failedavp, 1 ) );
425
426        /* Send the answer */
427        if (is_loc) {
428                CHECK_FCT( fd_fifo_post(fd_g_incoming, pmsg) );
429        } else {
430                CHECK_FCT( fd_out_send(pmsg, NULL, peer, 0) );
431        }
432       
433        /* Done */
434        return 0;
435}
436
437
438/****************************************************************************/
439/*         Second part : threads moving messages in the daemon              */
440/****************************************************************************/
441
442/* These are the functions of each threads: dispatch & routing */
443/* The DISPATCH message processing */
444static int msg_dispatch(struct msg ** pmsg)
445{
446        struct msg_hdr * hdr;
447        int is_req = 0, ret;
448        struct session * sess;
449        enum disp_action action;
450        const char * ec = NULL;
451        const char * em = NULL;
452
453        /* Read the message header */
454        CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) );
455        is_req = hdr->msg_flags & CMD_FLAG_REQUEST;
456       
457        /* Note: if the message is for local delivery, we should test for duplicate
458          (draft-asveren-dime-dupcons-00). This may conflict with path validation decisions, no clear answer yet */
459
460        /* At this point, we need to understand the message content, so parse it */
461        CHECK_FCT_DO( ret = fd_msg_parse_or_error( pmsg ),
462                {
463                        /* in case of error, the message is already dump'd */
464                        if ((ret == EBADMSG) && (*pmsg != NULL)) {
465                                /* msg now contains the answer message to send back */
466                                CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
467                        }
468                        if (*pmsg) {    /* another error happen'd */
469                                TRACE_DEBUG(INFO, "An unexpected error occurred (%s), discarding a message:", strerror(ret));
470                                fd_msg_dump_walk(INFO, *pmsg);
471                                CHECK_FCT_DO( fd_msg_free(*pmsg), /* continue */);
472                                *pmsg = NULL;
473                        }
474                        /* We're done with this one */
475                        return 0;
476                } );
477
478        /* First, if the original request was registered with a callback and we receive the answer, call it. */
479        if ( ! is_req ) {
480                struct msg * qry;
481                void (*anscb)(void *, struct msg **) = NULL;
482                void * data = NULL;
483
484                /* Retrieve the corresponding query */
485                CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) );
486
487                /* Retrieve any registered handler */
488                CHECK_FCT( fd_msg_anscb_get( qry, &anscb, &data ) );
489
490                /* If a callback was registered, pass the message to it */
491                if (anscb != NULL) {
492
493                        TRACE_DEBUG(FULL, "Calling callback registered when query was sent (%p, %p)", anscb, data);
494                        (*anscb)(data, pmsg);
495                       
496                        /* If the message is processed, we're done */
497                        if (*pmsg == NULL) {
498                                return 0;
499                        }
500                }
501        }
502       
503        /* Retrieve the session of the message */
504        CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *pmsg, &sess, NULL) );
505
506        /* Now, call any callback registered for the message */
507        CHECK_FCT( fd_msg_dispatch ( pmsg, sess, &action, &ec) );
508
509        /* Now, act depending on msg and action and ec */
510        if (*pmsg)
511                switch ( action ) {
512                        case DISP_ACT_CONT:
513                                /* No callback has handled the message, let's reply with a generic error */
514                                em = "The message was not handled by any extension callback";
515                                ec = "DIAMETER_COMMAND_UNSUPPORTED";
516                       
517                        case DISP_ACT_ERROR:
518                                /* We have a problem with delivering the message */
519                                if (ec == NULL) {
520                                        ec = "DIAMETER_UNABLE_TO_COMPLY";
521                                }
522                               
523                                if (!is_req) {
524                                        TRACE_DEBUG(INFO, "Received an answer to a localy issued query, but no handler processed this answer!");
525                                        fd_msg_dump_walk(INFO, *pmsg);
526                                        fd_msg_free(*pmsg);
527                                        *pmsg = NULL;
528                                        break;
529                                }
530                               
531                                /* Create an answer with the error code and message */
532                                CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, pmsg, 0 ) );
533                                CHECK_FCT( fd_msg_rescode_set(*pmsg, (char *)ec, (char *)em, NULL, 1 ) );
534                               
535                        case DISP_ACT_SEND:
536                                /* Now, send the message */
537                                CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
538                }
539       
540        /* We're done with dispatching this message */
541        return 0;
542}
543
544/* The ROUTING-IN message processing */
545static int msg_rt_in(struct msg ** pmsg)
546{
547        struct msg_hdr * hdr;
548        int is_req = 0;
549        int is_err = 0;
550        char * qry_src = NULL;
551
552        /* Read the message header */
553        CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) );
554        is_req = hdr->msg_flags & CMD_FLAG_REQUEST;
555        is_err = hdr->msg_flags & CMD_FLAG_ERROR;
556
557        /* Handle incorrect bits */
558        if (is_req && is_err) {
559                CHECK_FCT( return_error( pmsg, "DIAMETER_INVALID_HDR_BITS", "R & E bits were set", NULL) );
560                return 0;
561        }
562       
563        /* If it is a request, we must analyze its content to decide what we do with it */
564        if (is_req) {
565                struct avp * avp, *un = NULL;
566                union avp_value * un_val = NULL, *dr_val = NULL;
567                enum status { UNKNOWN, YES, NO };
568                /* Are we Destination-Host? */
569                enum status is_dest_host = UNKNOWN;
570                /* Are we Destination-Realm? */
571                enum status is_dest_realm = UNKNOWN;
572                /* Do we support the application of the message? */
573                enum status is_local_app = UNKNOWN;
574
575                /* Check if we have local support for the message application */
576                if ( (hdr->msg_appl == 0) || (hdr->msg_appl == AI_RELAY) ) {
577                        TRACE_DEBUG(INFO, "Received a routable message with application id 0, returning DIAMETER_APPLICATION_UNSUPPORTED");
578                        CHECK_FCT( return_error( pmsg, "DIAMETER_APPLICATION_UNSUPPORTED", "Routable message with application id 0 or relay", NULL) );
579                        return 0;
580                } else {
581                        struct fd_app * app;
582                        CHECK_FCT( fd_app_check(&fd_g_config->cnf_apps, hdr->msg_appl, &app) );
583                        is_local_app = (app ? YES : NO);
584                }
585
586                /* Parse the message for Dest-Host and Dest-Realm */
587                CHECK_FCT(  fd_msg_browse(*pmsg, MSG_BRW_FIRST_CHILD, &avp, NULL)  );
588                while (avp) {
589                        struct avp_hdr * ahdr;
590                        struct fd_pei error_info;
591                        int ret;
592                        CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr )  );
593
594                        if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) {
595                                switch (ahdr->avp_code) {
596                                        case AC_DESTINATION_HOST:
597                                                /* Parse this AVP */
598                                                CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
599                                                        {
600                                                                if (error_info.pei_errcode) {
601                                                                        CHECK_FCT( return_error( pmsg, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
602                                                                        return 0;
603                                                                } else {
604                                                                        return ret;
605                                                                }
606                                                        } );
607                                                ASSERT( ahdr->avp_value );
608                                                /* Compare the Destination-Host AVP of the message with our identity */
609                                                if (ahdr->avp_value->os.len != fd_g_config->cnf_diamid_len) {
610                                                        is_dest_host = NO;
611                                                } else {
612                                                        is_dest_host = (strncasecmp(fd_g_config->cnf_diamid, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamid_len) 
613                                                                                ? NO : YES);
614                                                }
615                                                break;
616
617                                        case AC_DESTINATION_REALM:
618                                                /* Parse this AVP */
619                                                CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
620                                                        {
621                                                                if (error_info.pei_errcode) {
622                                                                        CHECK_FCT( return_error( pmsg, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
623                                                                        return 0;
624                                                                } else {
625                                                                        return ret;
626                                                                }
627                                                        } );
628                                                ASSERT( ahdr->avp_value );
629                                                dr_val = ahdr->avp_value;
630                                                /* Compare the Destination-Realm AVP of the message with our identity */
631                                                if (ahdr->avp_value->os.len != fd_g_config->cnf_diamrlm_len) {
632                                                        is_dest_realm = NO;
633                                                } else {
634                                                        is_dest_realm = (strncasecmp(fd_g_config->cnf_diamrlm, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamrlm_len) 
635                                                                                ? NO : YES);
636                                                }
637                                                break;
638
639                                        case AC_USER_NAME:
640                                                /* Parse this AVP */
641                                                CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
642                                                        {
643                                                                if (error_info.pei_errcode) {
644                                                                        CHECK_FCT( return_error( pmsg, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
645                                                                        return 0;
646                                                                } else {
647                                                                        return ret;
648                                                                }
649                                                        } );
650                                                ASSERT( ahdr->avp_value );
651                                                un = avp;
652                                                un_val = ahdr->avp_value;
653                                                break;
654                                }
655                        }
656
657                        if ((is_dest_host != UNKNOWN) && (is_dest_realm != UNKNOWN) && un)
658                                break;
659
660                        /* Go to next AVP */
661                        CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL)  );
662                }
663
664                /* OK, now decide what we do with the request */
665
666                /* Handle the missing routing AVPs first */
667                if ( is_dest_realm == UNKNOWN ) {
668                        CHECK_FCT( return_error( pmsg, "DIAMETER_COMMAND_UNSUPPORTED", "Non-routable message not supported (invalid bit ? missing Destination-Realm ?)", NULL) );
669                        return 0;
670                }
671
672                /* If we are listed as Destination-Host */
673                if (is_dest_host == YES) {
674                        if (is_local_app == YES) {
675                                /* Ok, give the message to the dispatch thread */
676                                CHECK_FCT( fd_fifo_post(fd_g_local, pmsg) );
677                        } else {
678                                /* We don't support the application, reply an error */
679                                CHECK_FCT( return_error( pmsg, "DIAMETER_APPLICATION_UNSUPPORTED", NULL, NULL) );
680                        }
681                        return 0;
682                }
683
684                /* If the message is explicitely for someone else */
685                if ((is_dest_host == NO) || (is_dest_realm == NO)) {
686                        if (fd_g_config->cnf_flags.no_fwd) {
687                                CHECK_FCT( return_error( pmsg, "DIAMETER_UNABLE_TO_DELIVER", "This peer is not an agent", NULL) );
688                                return 0;
689                        }
690                } else {
691                /* Destination-Host was not set, and Destination-Realm is matching : we may handle or pass to a fellow peer */
692
693                        /* test for decorated NAI  (RFC5729 section 4.4) */
694                        if (is_decorated_NAI(un_val)) {
695                                /* Handle the decorated NAI */
696                                CHECK_FCT_DO( process_decorated_NAI(un_val, dr_val),
697                                        {
698                                                /* If the process failed, we assume it is because of the AVP format */
699                                                CHECK_FCT( return_error( pmsg, "DIAMETER_INVALID_AVP_VALUE", "Failed to process decorated NAI", un) );
700                                                return 0;
701                                        } );
702
703                                /* We have transformed the AVP, now submit it again in the queue */
704                                CHECK_FCT(fd_fifo_post(fd_g_incoming, pmsg) );
705                                return 0;
706                        }
707
708                        if (is_local_app == YES) {
709                                /* Handle localy since we are able to */
710                                CHECK_FCT(fd_fifo_post(fd_g_local, pmsg) );
711                                return 0;
712                        }
713
714                        if (fd_g_config->cnf_flags.no_fwd) {
715                                /* We return an error */
716                                CHECK_FCT( return_error( pmsg, "DIAMETER_APPLICATION_UNSUPPORTED", NULL, NULL) );
717                                return 0;
718                        }
719                }
720
721                /* From that point, for requests, we will call the registered callbacks, then forward to another peer */
722
723        } else {
724                /* The message is an answer */
725                struct msg * qry;
726
727                /* Retrieve the corresponding query and its origin */
728                CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) );
729                CHECK_FCT( fd_msg_source_get( qry, &qry_src ) );
730
731                if ((!qry_src) && (!is_err)) {
732                        /* The message is a normal answer to a request issued localy, we do not call the callbacks chain on it. */
733                        CHECK_FCT(fd_fifo_post(fd_g_local, pmsg) );
734                        return 0;
735                }
736
737                /* From that point, for answers, we will call the registered callbacks, then pass it to the dispatch module or forward it */
738        }
739
740        /* Call all registered callbacks for this message */
741        {
742                struct fd_list * li;
743
744                CHECK_FCT( pthread_rwlock_rdlock( &rt_fwd_lock ) );
745                pthread_cleanup_push( fd_cleanup_rwlock, &rt_fwd_lock );
746
747                /* requests: dir = 1 & 2 => in order; answers = 3 & 2 => in reverse order */
748                for (   li = (is_req ? rt_fwd_list.next : rt_fwd_list.prev) ; *pmsg && (li != &rt_fwd_list) ; li = (is_req ? li->next : li->prev) ) {
749                        struct rt_hdl * rh = (struct rt_hdl *)li;
750
751                        if (is_req && (rh->dir > RT_FWD_ALL))
752                                break;
753                        if ((!is_req) && (rh->dir < RT_FWD_ALL))
754                                break;
755
756                        /* Ok, call this cb */
757                        TRACE_DEBUG(ANNOYING, "Calling next FWD callback on %p : %p", *pmsg, rh->rt_fwd_cb);
758                        CHECK_FCT_DO( (*rh->rt_fwd_cb)(rh->cbdata, pmsg),
759                                {
760                                        TRACE_DEBUG(INFO, "A FWD routing callback returned an error, message discarded.");
761                                        fd_msg_dump_walk(INFO, *pmsg);
762                                        fd_msg_free(*pmsg);
763                                        *pmsg = NULL;
764                                } );
765                }
766
767                pthread_cleanup_pop(0);
768                CHECK_FCT( pthread_rwlock_unlock( &rt_fwd_lock ) );
769
770                /* If a callback has handled the message, we stop now */
771                if (!*pmsg)
772                        return 0;
773        }
774
775        /* Now pass the message to the next step: either forward to another peer, or dispatch to local extensions */
776        if (is_req || qry_src) {
777                CHECK_FCT(fd_fifo_post(fd_g_outgoing, pmsg) );
778        } else {
779                CHECK_FCT(fd_fifo_post(fd_g_local, pmsg) );
780        }
781
782        /* We're done with this message */
783        return 0;
784}
785               
786
787/* The ROUTING-OUT message processing */
788static int msg_rt_out(struct msg ** pmsg)
789{
790        struct rt_data * rtd = NULL;
791        struct msg_hdr * hdr;
792        int is_req = 0;
793        int ret;
794        struct fd_list * li, *candidates;
795        struct avp * avp;
796        struct rtd_candidate * c;
797       
798        /* Read the message header */
799        CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) );
800        is_req = hdr->msg_flags & CMD_FLAG_REQUEST;
801       
802        /* For answers, the routing is very easy */
803        if ( ! is_req ) {
804                struct msg * qry;
805                char * qry_src = NULL;
806                struct msg_hdr * qry_hdr;
807                struct fd_peer * peer = NULL;
808
809                /* Retrieve the corresponding query and its origin */
810                CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) );
811                CHECK_FCT( fd_msg_source_get( qry, &qry_src ) );
812
813                ASSERT( qry_src ); /* if it is NULL, the message should have been in the LOCAL queue! */
814
815                /* Find the peer corresponding to this name */
816                CHECK_FCT( fd_peer_getbyid( qry_src, (void *) &peer ) );
817                fd_cpu_flush_cache();
818                if ((!peer) || (peer->p_hdr.info.runtime.pir_state != STATE_OPEN)) {
819                        TRACE_DEBUG(INFO, "Unable to forward answer message to peer '%s', deleted or not in OPEN state.", qry_src);
820                        fd_msg_dump_walk(INFO, *pmsg);
821                        fd_msg_free(*pmsg);
822                        *pmsg = NULL;
823                        return 0;
824                }
825
826                /* We must restore the hop-by-hop id */
827                CHECK_FCT( fd_msg_hdr(qry, &qry_hdr) );
828                hdr->msg_hbhid = qry_hdr->msg_hbhid;
829
830                /* Push the message into this peer */
831                CHECK_FCT( fd_out_send(pmsg, NULL, peer, 0) );
832
833                /* We're done with this answer */
834                return 0;
835        }
836       
837        /* From that point, the message is a request */
838
839        /* Get the routing data out of the message if any (in case of re-transmit) */
840        CHECK_FCT( fd_msg_rt_get ( *pmsg, &rtd ) );
841
842        /* If there is no routing data already, let's create it */
843        if (rtd == NULL) {
844                CHECK_FCT( fd_rtd_init(&rtd) );
845
846                /* Add all peers currently in OPEN state */
847                CHECK_FCT( pthread_rwlock_rdlock(&fd_g_activ_peers_rw) );
848                for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) {
849                        struct fd_peer * p = (struct fd_peer *)li->o;
850                        CHECK_FCT_DO( ret = fd_rtd_candidate_add(rtd, p->p_hdr.info.pi_diamid, p->p_hdr.info.runtime.pir_realm), { CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_activ_peers_rw), ); return ret; } );
851                }
852                CHECK_FCT( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
853
854                /* Now let's remove all peers from the Route-Records */
855                CHECK_FCT(  fd_msg_browse(*pmsg, MSG_BRW_FIRST_CHILD, &avp, NULL)  );
856                while (avp) {
857                        struct avp_hdr * ahdr;
858                        struct fd_pei error_info;
859                        CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr )  );
860
861                        if ((ahdr->avp_code == AC_ROUTE_RECORD) && (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) ) {
862                                /* Parse this AVP */
863                                CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
864                                        {
865                                                if (error_info.pei_errcode) {
866                                                        CHECK_FCT( return_error( pmsg, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
867                                                        return 0;
868                                                } else {
869                                                        return ret;
870                                                }
871                                        } );
872                                ASSERT( ahdr->avp_value );
873                                /* Remove this value from the list */
874                                fd_rtd_candidate_del(rtd, (char *)ahdr->avp_value->os.data, ahdr->avp_value->os.len);
875                        }
876
877                        /* Go to next AVP */
878                        CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL)  );
879                }
880        }
881
882        /* Note: we reset the scores and pass the message to the callbacks, maybe we could re-use the saved scores when we have received an error ? */
883
884        /* Ok, we have our list in rtd now, let's (re)initialize the scores */
885        fd_rtd_candidate_extract(rtd, &candidates, FD_SCORE_INI);
886
887        /* Pass the list to registered callbacks (even if it is empty) */
888        {
889                CHECK_FCT( pthread_rwlock_rdlock( &rt_out_lock ) );
890                pthread_cleanup_push( fd_cleanup_rwlock, &rt_out_lock );
891
892                /* We call the cb by reverse priority order */
893                for (   li = rt_out_list.prev ; li != &rt_out_list ; li = li->prev ) {
894                        struct rt_hdl * rh = (struct rt_hdl *)li;
895
896                        TRACE_DEBUG(ANNOYING, "Calling next OUT callback on %p : %p (prio %d)", *pmsg, rh->rt_out_cb, rh->prio);
897                        CHECK_FCT_DO( ret = (*rh->rt_out_cb)(rh->cbdata, *pmsg, candidates),
898                                {
899                                        TRACE_DEBUG(INFO, "An OUT routing callback returned an error (%s) ! Message discarded.", strerror(ret));
900                                        fd_msg_dump_walk(INFO, *pmsg);
901                                        fd_msg_free(*pmsg);
902                                        *pmsg = NULL;
903                                        break;
904                                } );
905                }
906
907                pthread_cleanup_pop(0);
908                CHECK_FCT( pthread_rwlock_unlock( &rt_out_lock ) );
909
910                /* If an error occurred, skip to the next message */
911                if (! *pmsg) {
912                        if (rtd)
913                                fd_rtd_free(&rtd);
914                        return 0;
915                }
916        }
917       
918        /* Order the candidate peers by score attributed by the callbacks */
919        CHECK_FCT( fd_rtd_candidate_reorder(candidates) );
920
921        /* Save the routing information in the message */
922        CHECK_FCT( fd_msg_rt_associate ( *pmsg, &rtd ) );
923
924        /* Now try sending the message */
925        for (li = candidates->prev; li != candidates; li = li->prev) {
926                struct fd_peer * peer;
927
928                c = (struct rtd_candidate *) li;
929
930                /* Stop when we have reached the end of valid candidates */
931                if (c->score < 0)
932                        break;
933
934                /* Search for the peer */
935                CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) );
936
937                fd_cpu_flush_cache();
938                if (peer && (peer->p_hdr.info.runtime.pir_state == STATE_OPEN)) {
939                        /* Send to this one */
940                        CHECK_FCT_DO( fd_out_send(pmsg, NULL, peer, 0), continue );
941                       
942                        /* If the sending was successful */
943                        break;
944                }
945        }
946
947        /* If the message has not been sent, return an error */
948        if (*pmsg) {
949                TRACE_DEBUG(INFO, "Could not send the following message, replying with UNABLE_TO_DELIVER");
950                fd_msg_dump_walk(INFO, *pmsg);
951                return_error( pmsg, "DIAMETER_UNABLE_TO_DELIVER", "No suitable candidate to route the message to", NULL);
952        }
953
954        /* We're done with this message */
955       
956        return 0;
957}
958
959
960/********************************************************************************/
961/*                     Management of the threads                                */
962/********************************************************************************/
963
964/* Note: in the first version, we only create one thread of each kind.
965 We could improve the scalability by using the threshold feature of the queues
966 to create additional threads if a queue is filling up, or at least giving a configurable
967 number of threads of each kind.
968 */
969
970/* Control of the threads */
971static enum { RUN = 0, STOP = 1 } order_val = RUN;
972static pthread_mutex_t order_lock = PTHREAD_MUTEX_INITIALIZER;
973
974/* Threads report their status */
975enum thread_state { INITIAL = 0, RUNNING = 1, TERMINATED = 2 };
976static void cleanup_state(void * state_loc)
977{
978        if (state_loc)
979                *(enum thread_state *)state_loc = TERMINATED;
980}
981
982/* This is the common thread code (same for routing and dispatching) */
983static void * process_thr(void * arg, int (*action_cb)(struct msg ** pmsg), struct fifo * queue, char * action_name)
984{
985        TRACE_ENTRY("%p %p %p %p", arg, action_cb, queue, action_name);
986       
987        /* Set the thread name */
988        {
989                char buf[48];
990                snprintf(buf, sizeof(buf), "%s (%p)", action_name, arg);
991                fd_log_threadname ( buf );
992        }
993       
994        /* The thread reports its status when canceled */
995        CHECK_PARAMS_DO(arg, return NULL);
996        pthread_cleanup_push( cleanup_state, arg );
997       
998        /* Mark the thread running */
999        *(enum thread_state *)arg = RUNNING;
1000        fd_cpu_flush_cache();
1001       
1002        do {
1003                struct msg * msg;
1004       
1005                /* Test the current order */
1006                {
1007                        int must_stop;
1008                        CHECK_POSIX_DO( pthread_mutex_lock(&order_lock), goto end ); /* we lock to flush the caches */
1009                        must_stop = (order_val == STOP);
1010                        CHECK_POSIX_DO( pthread_mutex_unlock(&order_lock), goto end );
1011                        if (must_stop)
1012                                goto end;
1013                       
1014                        pthread_testcancel();
1015                }
1016               
1017                /* Ok, we are allowed to run */
1018               
1019                /* Get the next message from the queue */
1020                {
1021                        int ret;
1022                        ret = fd_fifo_get ( queue, &msg );
1023                        if (ret == EPIPE)
1024                                /* The queue was destroyed, we are probably exiting */
1025                                goto end;
1026                       
1027                        /* check if another error occurred */
1028                        CHECK_FCT_DO( ret, goto fatal_error );
1029                }
1030               
1031                if (TRACE_BOOL(FULL)) {
1032                        TRACE_DEBUG(FULL, "Picked next message");
1033                        fd_msg_dump_one(ANNOYING, msg);
1034                }
1035               
1036                /* Now process the message */
1037                CHECK_FCT_DO( (*action_cb)(&msg), goto fatal_error);
1038
1039                /* We're done with this message */
1040       
1041        } while (1);
1042       
1043fatal_error:
1044        TRACE_DEBUG(INFO, "An unrecoverable error occurred, %s thread is terminating...", action_name);
1045        CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
1046       
1047end:   
1048        ; /* noop so that we get rid of "label at end of compund statement" warning */
1049        /* Mark the thread as terminated */
1050        pthread_cleanup_pop(1);
1051        return NULL;
1052}
1053
1054/* The dispatch thread */
1055static void * dispatch_thr(void * arg)
1056{
1057        return process_thr(arg, msg_dispatch, fd_g_local, "Dispatch");
1058}
1059
1060/* The (routing-in) thread -- see description in freeDiameter.h */
1061static void * routing_in_thr(void * arg)
1062{
1063        return process_thr(arg, msg_rt_in, fd_g_incoming, "Routing-IN");
1064}
1065
1066/* The (routing-out) thread -- see description in freeDiameter.h */
1067static void * routing_out_thr(void * arg)
1068{
1069        return process_thr(arg, msg_rt_out, fd_g_outgoing, "Routing-OUT");
1070}
1071
1072
1073/********************************************************************************/
1074/*                     The functions for the other files                        */
1075/********************************************************************************/
1076
1077static pthread_t * dispatch = NULL;
1078static enum thread_state * disp_state = NULL;
1079
1080/* Later: make this more dynamic */
1081static pthread_t rt_out = (pthread_t)NULL;
1082static enum thread_state out_state = INITIAL;
1083
1084static pthread_t rt_in  = (pthread_t)NULL;
1085static enum thread_state in_state = INITIAL;
1086
1087/* Initialize the routing and dispatch threads */
1088int fd_rtdisp_init(void)
1089{
1090        int i;
1091       
1092        /* Prepare the array for dispatch */
1093        CHECK_MALLOC( dispatch = calloc(fd_g_config->cnf_dispthr, sizeof(pthread_t)) );
1094        CHECK_MALLOC( disp_state = calloc(fd_g_config->cnf_dispthr, sizeof(enum thread_state)) );
1095       
1096        /* Create the threads */
1097        for (i=0; i < fd_g_config->cnf_dispthr; i++) {
1098                CHECK_POSIX( pthread_create( &dispatch[i], NULL, dispatch_thr, &disp_state[i] ) );
1099        }
1100        CHECK_POSIX( pthread_create( &rt_out, NULL, routing_out_thr, &out_state) );
1101        CHECK_POSIX( pthread_create( &rt_in,  NULL, routing_in_thr,  &in_state) );
1102       
1103        /* Later: TODO("Set the thresholds for the queues to create more threads as needed"); */
1104       
1105        /* Register the built-in callbacks */
1106        CHECK_FCT( fd_rt_out_register( dont_send_if_no_common_app, NULL, 10, NULL ) );
1107        CHECK_FCT( fd_rt_out_register( score_destination_avp, NULL, 10, NULL ) );
1108        return 0;
1109}
1110
1111/* Ask the thread to terminate after next iteration */
1112int fd_rtdisp_cleanstop(void)
1113{
1114        CHECK_POSIX( pthread_mutex_lock(&order_lock) );
1115        order_val = STOP;
1116        CHECK_POSIX( pthread_mutex_unlock(&order_lock) );
1117
1118        return 0;
1119}
1120
1121static void stop_thread_delayed(enum thread_state *st, pthread_t * thr, char * th_name)
1122{
1123        TRACE_ENTRY("%p %p", st, thr);
1124        CHECK_PARAMS_DO(st && thr, return);
1125
1126        /* Wait for a second for the thread to complete, by monitoring my_state */
1127        fd_cpu_flush_cache();
1128        if (*st != TERMINATED) {
1129                TRACE_DEBUG(INFO, "Waiting for the %s thread to have a chance to terminate", th_name);
1130                do {
1131                        struct timespec  ts, ts_final;
1132
1133                        CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), break );
1134                       
1135                        ts_final.tv_sec = ts.tv_sec + 1;
1136                        ts_final.tv_nsec = ts.tv_nsec;
1137                       
1138                        while (TS_IS_INFERIOR( &ts, &ts_final )) {
1139                                if (*st == TERMINATED)
1140                                        break;
1141                               
1142                                usleep(100000);
1143                                CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), break );
1144                        }
1145                } while (0);
1146        }
1147
1148        /* Now stop the thread and reclaim its resources */
1149        CHECK_FCT_DO( fd_thr_term(thr ), /* continue */);
1150       
1151}
1152
1153/* Stop the thread after up to one second of wait */
1154int fd_rtdisp_fini(void)
1155{
1156        int i;
1157       
1158        /* Destroy the incoming queue */
1159        CHECK_FCT_DO( fd_queues_fini(&fd_g_incoming), /* ignore */);
1160       
1161        /* Stop the routing IN thread */
1162        stop_thread_delayed(&in_state, &rt_in, "IN routing");
1163       
1164        /* Destroy the outgoing queue */
1165        CHECK_FCT_DO( fd_queues_fini(&fd_g_outgoing), /* ignore */);
1166       
1167        /* Stop the routing OUT thread */
1168        stop_thread_delayed(&out_state, &rt_out, "OUT routing");
1169       
1170        /* Destroy the local queue */
1171        CHECK_FCT_DO( fd_queues_fini(&fd_g_local), /* ignore */);
1172       
1173        /* Stop the Dispatch thread */
1174        for (i=0; i < fd_g_config->cnf_dispthr; i++) {
1175                stop_thread_delayed(&disp_state[i], &dispatch[i], "Dispatching");
1176        }
1177       
1178        return 0;
1179}
1180
1181/* Cleanup handlers */
1182int fd_rtdisp_cleanup(void)
1183{
1184        /* Cleanup all remaining handlers */
1185        while (!FD_IS_LIST_EMPTY(&rt_fwd_list)) {
1186                CHECK_FCT_DO( fd_rt_fwd_unregister ( (void *)rt_fwd_list.next, NULL ), /* continue */ );
1187        }
1188        while (!FD_IS_LIST_EMPTY(&rt_out_list)) {
1189                CHECK_FCT_DO( fd_rt_out_unregister ( (void *)rt_out_list.next, NULL ), /* continue */ );
1190        }
1191       
1192        fd_disp_unregister_all(); /* destroy remaining handlers */
1193
1194        return 0;
1195}
1196
1197
1198/********************************************************************************/
1199/*                     For extensiosn to register a new appl                    */
1200/********************************************************************************/
1201
1202/* Add an application into the peer's supported apps */
1203int fd_disp_app_support ( struct dict_object * app, struct dict_object * vendor, int auth, int acct )
1204{
1205        application_id_t aid = 0;
1206        vendor_id_t      vid = 0;
1207       
1208        TRACE_ENTRY("%p %p %d %d", app, vendor, auth, acct);
1209        CHECK_PARAMS( app && (auth || acct) );
1210       
1211        {
1212                enum dict_object_type type = 0;
1213                struct dict_application_data data;
1214                CHECK_FCT( fd_dict_gettype(app, &type) );
1215                CHECK_PARAMS( type == DICT_APPLICATION );
1216                CHECK_FCT( fd_dict_getval(app, &data) );
1217                aid = data.application_id;
1218        }
1219
1220        if (vendor) {
1221                enum dict_object_type type = 0;
1222                struct dict_vendor_data data;
1223                CHECK_FCT( fd_dict_gettype(vendor, &type) );
1224                CHECK_PARAMS( type == DICT_VENDOR );
1225                CHECK_FCT( fd_dict_getval(vendor, &data) );
1226                vid = data.vendor_id;
1227        }
1228       
1229        return fd_app_merge(&fd_g_config->cnf_apps, aid, vid, auth, acct);
1230}
1231
1232
1233
Note: See TracBrowser for help on using the repository browser.