Navigation


source: freeDiameter/libfdcore/routing_dispatch.c @ 738:d666051658bd

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

Fix broken 'almostcasecmp' logic

File size: 39.4 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
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, c->diamidlen, 0, (void *)&peer ) );
211                if (peer && !peer->p_hdr.info.runtime.pir_relay) {
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               
267            #if 0 /* this is actually useless since the sending process will also ensure that the peer is still available */
268                struct fd_peer * peer;
269                /* Since the candidates list comes from the peers list, we do not have any issue with upper/lower case to find the peer object */
270                CHECK_FCT( fd_peer_getbyid( c->diamid, c->diamidlen, 0, (void *)&peer ) );
271                if (!peer)
272                        continue; /* it has been deleted since the candidate list was generated; avoid sending to this one in that case. */
273            #endif /* 0 */
274               
275                /* In the AVPs, the value comes from the network, so let's be case permissive */
276                if (dh && !fd_os_almostcasesrch(dh->os.data, dh->os.len, c->diamid, c->diamidlen, NULL) ) {
277                        /* The candidate is the Destination-Host */
278                        c->score += FD_SCORE_FINALDEST;
279                } else {
280                        if (dr && !fd_os_almostcasesrch(dr->os.data, dr->os.len, c->realm, c->realmlen, NULL) ) {
281                                /* The candidate's realm matchs the Destination-Realm */
282                                c->score += FD_SCORE_REALM;
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 && at_idx, return );
301       
302        *excl_idx = 0;
303        *at_idx = 0;
304       
305        /* Search if there is a '!' before any '@' -- do we need to check it contains a '.' ? */
306        for (i = 0; i < un->os.len; i++) {
307                /* The '!' marks the decorated NAI */
308                if ( un->os.data[i] == (unsigned char) '!' ) {
309                        if (!*excl_idx)
310                                *excl_idx = i;
311                        continue;
312                }
313                /* If we reach the realm part, we can stop */
314                if ( un->os.data[i] == (unsigned char) '@' ) {
315                        *at_idx = i;
316                        break;
317                }
318                /* Stop if we find a \0 in the middle */
319                if ( un->os.data[i] == 0 ) {
320                        return;
321                }
322                /* Skip escaped characters */
323                if ( un->os.data[i] == (unsigned char) '\\' ) {
324                        i++;
325                        continue;
326                }
327        }
328       
329        return;
330}       
331
332/* Test if a User-Name AVP contains a Decorated NAI -- RFC4282, RFC5729 */
333/* Create new User-Name and Destination-Realm values */
334static int process_decorated_NAI(int * was_nai, union avp_value * un, union avp_value * dr)
335{
336        int at_idx, sep_idx;
337        unsigned char * old_un;
338        TRACE_ENTRY("%p %p %p", was_nai, un, dr);
339        CHECK_PARAMS(was_nai && un && dr);
340       
341        /* Save the decorated User-Name, for example 'homerealm.example.net!user@otherrealm.example.net' */
342        old_un = un->os.data;
343       
344        /* Search the positions of the first '!' and the '@' in the string */
345        nai_get_indexes(un, &sep_idx, &at_idx);
346        if ((!sep_idx) || (sep_idx > at_idx) || !fd_os_is_valid_DiameterIdentity(old_un, sep_idx /* this is the new realm part */)) {
347                *was_nai = 0;
348                return 0;
349        }
350       
351        *was_nai = 1;
352       
353        /* Create the new User-Name value */
354        CHECK_MALLOC( un->os.data = malloc( at_idx ) );
355        memcpy( un->os.data, old_un + sep_idx + 1, at_idx - sep_idx ); /* user@ */
356        memcpy( un->os.data + at_idx - sep_idx, old_un, sep_idx ); /* homerealm.example.net */
357       
358        /* Create the new Destination-Realm value */
359        CHECK_MALLOC( dr->os.data = realloc(dr->os.data, sep_idx) );
360        memcpy( dr->os.data, old_un, sep_idx );
361        dr->os.len = sep_idx;
362       
363        TRACE_DEBUG(FULL, "Processed Decorated NAI : '%.*s' became '%.*s' (%.*s)",
364                                un->os.len, old_un,
365                                at_idx, un->os.data,
366                                dr->os.len, dr->os.data);
367       
368        un->os.len = at_idx;
369        free(old_un);
370       
371        return 0;
372}
373
374
375/* Function to return an error to an incoming request */
376static int return_error(struct msg ** pmsg, char * error_code, char * error_message, struct avp * failedavp)
377{
378        struct fd_peer * peer;
379        int is_loc = 0;
380
381        /* Get the source of the message */
382        {
383                DiamId_t id;
384                size_t   idlen;
385                CHECK_FCT( fd_msg_source_get( *pmsg, &id, &idlen ) );
386               
387                if (id == NULL) {
388                        is_loc = 1; /* The message was issued locally */
389                } else {
390               
391                        /* Search the peer with this id */
392                        CHECK_FCT( fd_peer_getbyid( id, idlen, 0, (void *)&peer ) );
393
394                        if (!peer) {
395                                fd_msg_log(FD_MSG_LOG_DROPPED, *pmsg, "Unable to send error '%s' to deleted peer '%s' in reply to this message.", error_code, id);
396                                fd_msg_free(*pmsg);
397                                *pmsg = NULL;
398                                return 0;
399                        }
400                }
401        }
402       
403        /* Create the error message */
404        CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, pmsg, MSGFL_ANSW_ERROR ) );
405
406        /* Set the error code */
407        CHECK_FCT( fd_msg_rescode_set(*pmsg, error_code, error_message, failedavp, 1 ) );
408
409        /* Send the answer */
410        if (is_loc) {
411                CHECK_FCT( fd_fifo_post(fd_g_incoming, pmsg) );
412        } else {
413                CHECK_FCT( fd_out_send(pmsg, NULL, peer, 0) );
414        }
415       
416        /* Done */
417        return 0;
418}
419
420
421/****************************************************************************/
422/*         Second part : threads moving messages in the daemon              */
423/****************************************************************************/
424
425/* The DISPATCH message processing */
426static int msg_dispatch(struct msg ** pmsg)
427{
428        struct msg_hdr * hdr;
429        int is_req = 0, ret;
430        struct session * sess;
431        enum disp_action action;
432        char * ec = NULL;
433        char * em = NULL;
434
435        /* Read the message header */
436        CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) );
437        is_req = hdr->msg_flags & CMD_FLAG_REQUEST;
438       
439        /* Note: if the message is for local delivery, we should test for duplicate
440          (draft-asveren-dime-dupcons-00). This may conflict with path validation decisions, no clear answer yet */
441
442        /* At this point, we need to understand the message content, so parse it */
443        CHECK_FCT_DO( ret = fd_msg_parse_or_error( pmsg ),
444                {
445                        /* in case of error */
446                        if ((ret == EBADMSG) && (*pmsg != NULL)) {
447                                /* msg now contains the answer message to send back */
448                                CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
449                        }
450                        if (*pmsg) {    /* another error happen'd */
451                                fd_msg_log( FD_MSG_LOG_DROPPED, *pmsg,  "An unexpected error occurred while parsing the message (%s)", strerror(ret));
452                                CHECK_FCT_DO( fd_msg_free(*pmsg), /* continue */);
453                                *pmsg = NULL;
454                        }
455                        /* We're done with this one */
456                        return 0;
457                } );
458
459        /* First, if the original request was registered with a callback and we receive the answer, call it. */
460        if ( ! is_req ) {
461                struct msg * qry;
462                void (*anscb)(void *, struct msg **) = NULL;
463                void * data = NULL;
464
465                /* Retrieve the corresponding query */
466                CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) );
467
468                /* Retrieve any registered handler */
469                CHECK_FCT( fd_msg_anscb_get( qry, &anscb, &data ) );
470
471                /* If a callback was registered, pass the message to it */
472                if (anscb != NULL) {
473
474                        TRACE_DEBUG(FULL, "Calling callback registered when query was sent (%p, %p)", anscb, data);
475                        (*anscb)(data, pmsg);
476                       
477                        /* If the message is processed, we're done */
478                        if (*pmsg == NULL) {
479                                return 0;
480                        }
481                }
482        }
483       
484        /* Retrieve the session of the message */
485        CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *pmsg, &sess, NULL) );
486
487        /* Now, call any callback registered for the message */
488        CHECK_FCT( fd_msg_dispatch ( pmsg, sess, &action, &ec) );
489
490        /* Now, act depending on msg and action and ec */
491        if (*pmsg)
492                switch ( action ) {
493                        case DISP_ACT_CONT:
494                                /* No callback has handled the message, let's reply with a generic error */
495                                em = "The message was not handled by any extension callback";
496                                ec = "DIAMETER_COMMAND_UNSUPPORTED";
497                                /* and continue as if an error occurred... */
498                        case DISP_ACT_ERROR:
499                                /* We have a problem with delivering the message */
500                                if (ec == NULL) {
501                                        ec = "DIAMETER_UNABLE_TO_COMPLY";
502                                }
503                               
504                                if (!is_req) {
505                                        fd_msg_log( FD_MSG_LOG_DROPPED, *pmsg,  "Internal error: Answer received to locally issued request, but not handled by any handler.");
506                                        fd_msg_free(*pmsg);
507                                        *pmsg = NULL;
508                                        break;
509                                }
510                               
511                                /* Create an answer with the error code and message */
512                                CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, pmsg, 0 ) );
513                                CHECK_FCT( fd_msg_rescode_set(*pmsg, ec, em, NULL, 1 ) );
514                               
515                        case DISP_ACT_SEND:
516                                /* Now, send the message */
517                                CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
518                }
519       
520        /* We're done with dispatching this message */
521        return 0;
522}
523
524/* The ROUTING-IN message processing */
525static int msg_rt_in(struct msg ** pmsg)
526{
527        struct msg_hdr * hdr;
528        int is_req = 0;
529        int is_err = 0;
530        DiamId_t qry_src = NULL;
531        size_t   qry_src_len = 0;
532
533        /* Read the message header */
534        CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) );
535        is_req = hdr->msg_flags & CMD_FLAG_REQUEST;
536        is_err = hdr->msg_flags & CMD_FLAG_ERROR;
537
538        /* Handle incorrect bits */
539        if (is_req && is_err) {
540                CHECK_FCT( return_error( pmsg, "DIAMETER_INVALID_HDR_BITS", "R & E bits were set", NULL) );
541                return 0;
542        }
543       
544        /* If it is a request, we must analyze its content to decide what we do with it */
545        if (is_req) {
546                struct avp * avp, *un = NULL;
547                union avp_value * un_val = NULL, *dr_val = NULL;
548                enum status { UNKNOWN, YES, NO };
549                /* Are we Destination-Host? */
550                enum status is_dest_host = UNKNOWN;
551                /* Are we Destination-Realm? */
552                enum status is_dest_realm = UNKNOWN;
553                /* Do we support the application of the message? */
554                enum status is_local_app = UNKNOWN;
555
556                /* Check if we have local support for the message application */
557                if ( (hdr->msg_appl == 0) || (hdr->msg_appl == AI_RELAY) ) {
558                        TRACE_DEBUG(INFO, "Received a routable message with application id 0 or " _stringize(AI_RELAY) " (relay),\n"
559                                          " returning DIAMETER_APPLICATION_UNSUPPORTED");
560                        CHECK_FCT( return_error( pmsg, "DIAMETER_APPLICATION_UNSUPPORTED", "Routable message with application id 0 or relay", NULL) );
561                        return 0;
562                } else {
563                        struct fd_app * app;
564                        CHECK_FCT( fd_app_check(&fd_g_config->cnf_apps, hdr->msg_appl, &app) );
565                        is_local_app = (app ? YES : NO);
566                }
567
568                /* Parse the message for Dest-Host and Dest-Realm */
569                CHECK_FCT(  fd_msg_browse(*pmsg, MSG_BRW_FIRST_CHILD, &avp, NULL)  );
570                while (avp) {
571                        struct avp_hdr * ahdr;
572                        struct fd_pei error_info;
573                        int ret;
574                       
575                        memset(&error_info, 0, sizeof(struct fd_pei)); 
576                       
577                        CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr )  );
578
579                        if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) {
580                                switch (ahdr->avp_code) {
581                                        case AC_DESTINATION_HOST:
582                                                /* Parse this AVP */
583                                                CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
584                                                        {
585                                                                if (error_info.pei_errcode) {
586                                                                        CHECK_FCT( return_error( pmsg, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
587                                                                        return 0;
588                                                                } else {
589                                                                        return ret;
590                                                                }
591                                                        } );
592                                                ASSERT( ahdr->avp_value );
593                                                /* Compare the Destination-Host AVP of the message with our identity */
594                                                if (!fd_os_almostcasesrch(ahdr->avp_value->os.data, ahdr->avp_value->os.len, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, NULL)) {
595                                                        is_dest_host = YES;
596                                                } else {
597                                                        is_dest_host = NO;
598                                                }
599                                                break;
600
601                                        case AC_DESTINATION_REALM:
602                                                /* Parse this AVP */
603                                                CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
604                                                        {
605                                                                if (error_info.pei_errcode) {
606                                                                        CHECK_FCT( return_error( pmsg, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
607                                                                        return 0;
608                                                                } else {
609                                                                        return ret;
610                                                                }
611                                                        } );
612                                                ASSERT( ahdr->avp_value );
613                                                dr_val = ahdr->avp_value;
614                                                /* Compare the Destination-Realm AVP of the message with our identity */
615                                                if (!fd_os_almostcasesrch(dr_val->os.data, dr_val->os.len, fd_g_config->cnf_diamrlm, fd_g_config->cnf_diamrlm_len, NULL)) {
616                                                        is_dest_realm = YES;
617                                                } else {
618                                                        is_dest_realm = NO;
619                                                }
620                                                break;
621
622                                        /* we also use User-Name for decorated NAI */
623                                        case AC_USER_NAME:
624                                                /* Parse this AVP */
625                                                CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
626                                                        {
627                                                                if (error_info.pei_errcode) {
628                                                                        CHECK_FCT( return_error( pmsg, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
629                                                                        return 0;
630                                                                } else {
631                                                                        return ret;
632                                                                }
633                                                        } );
634                                                ASSERT( ahdr->avp_value );
635                                                un = avp;
636                                                un_val = ahdr->avp_value;
637                                                break;
638                                }
639                        }
640
641                        /* Stop when we found all 3 AVPs -- they are supposed to be at the beginning of the message, so this should be fast */
642                        if ((is_dest_host != UNKNOWN) && (is_dest_realm != UNKNOWN) && un)
643                                break;
644
645                        /* Go to next AVP */
646                        CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL)  );
647                }
648
649                /* OK, now decide what we do with the request */
650
651                /* Handle the missing routing AVPs first */
652                if ( is_dest_realm == UNKNOWN ) {
653                        CHECK_FCT( return_error( pmsg, "DIAMETER_COMMAND_UNSUPPORTED", "Non-routable message not supported (invalid bit ? missing Destination-Realm ?)", NULL) );
654                        return 0;
655                }
656
657                /* If we are listed as Destination-Host */
658                if (is_dest_host == YES) {
659                        if (is_local_app == YES) {
660                                /* Ok, give the message to the dispatch thread */
661                                CHECK_FCT( fd_fifo_post(fd_g_local, pmsg) );
662                        } else {
663                                /* We don't support the application, reply an error */
664                                CHECK_FCT( return_error( pmsg, "DIAMETER_APPLICATION_UNSUPPORTED", NULL, NULL) );
665                        }
666                        return 0;
667                }
668
669                /* If the message is explicitely for someone else */
670                if ((is_dest_host == NO) || (is_dest_realm == NO)) {
671                        if (fd_g_config->cnf_flags.no_fwd) {
672                                CHECK_FCT( return_error( pmsg, "DIAMETER_UNABLE_TO_DELIVER", "I am not a Diameter agent", NULL) );
673                                return 0;
674                        }
675                } else {
676                /* Destination-Host was not set, and Destination-Realm is matching : we may handle or pass to a fellow peer */
677                        int is_nai = 0;
678
679                        /* test for decorated NAI  (RFC5729 section 4.4) */
680                        /* Handle the decorated NAI */
681                        if (un_val) {
682                                CHECK_FCT_DO( process_decorated_NAI(&is_nai, un_val, dr_val),
683                                        {
684                                                /* If the process failed, we assume it is because of the AVP format */
685                                                CHECK_FCT( return_error( pmsg, "DIAMETER_INVALID_AVP_VALUE", "Failed to process decorated NAI", un) );
686                                                return 0;
687                                        } );
688                        }
689                               
690                        if (is_nai) {
691                                /* We have transformed the AVP, now submit it again in the queue */
692                                CHECK_FCT(fd_fifo_post(fd_g_incoming, pmsg) );
693                                return 0;
694                        }
695
696                        if (is_local_app == YES) {
697                                /* Handle localy since we are able to */
698                                CHECK_FCT(fd_fifo_post(fd_g_local, pmsg) );
699                                return 0;
700                        }
701
702                        if (fd_g_config->cnf_flags.no_fwd) {
703                                /* We return an error */
704                                CHECK_FCT( return_error( pmsg, "DIAMETER_APPLICATION_UNSUPPORTED", NULL, NULL) );
705                                return 0;
706                        }
707                }
708
709                /* From that point, for requests, we will call the registered callbacks, then forward to another peer */
710
711        } else {
712                /* The message is an answer */
713                struct msg * qry;
714
715                /* Retrieve the corresponding query and its origin */
716                CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) );
717                CHECK_FCT( fd_msg_source_get( qry, &qry_src, &qry_src_len ) );
718
719                if ((!qry_src) && (!is_err)) {
720                        /* The message is a normal answer to a request issued localy, we do not call the callbacks chain on it. */
721                        CHECK_FCT(fd_fifo_post(fd_g_local, pmsg) );
722                        return 0;
723                }
724               
725                /* From that point, for answers, we will call the registered callbacks, then pass it to the dispatch module or forward it */
726        }
727
728        /* Call all registered callbacks for this message */
729        {
730                struct fd_list * li;
731
732                CHECK_FCT( pthread_rwlock_rdlock( &rt_fwd_lock ) );
733                pthread_cleanup_push( fd_cleanup_rwlock, &rt_fwd_lock );
734
735                /* requests: dir = 1 & 2 => in order; answers = 3 & 2 => in reverse order */
736                for (   li = (is_req ? rt_fwd_list.next : rt_fwd_list.prev) ; *pmsg && (li != &rt_fwd_list) ; li = (is_req ? li->next : li->prev) ) {
737                        struct rt_hdl * rh = (struct rt_hdl *)li;
738                        int ret;
739
740                        if (is_req && (rh->dir > RT_FWD_ALL))
741                                break;
742                        if ((!is_req) && (rh->dir < RT_FWD_ALL))
743                                break;
744
745                        /* Ok, call this cb */
746                        TRACE_DEBUG(ANNOYING, "Calling next FWD callback on %p : %p", *pmsg, rh->rt_fwd_cb);
747                        CHECK_FCT_DO( ret = (*rh->rt_fwd_cb)(rh->cbdata, pmsg),
748                                {
749                                        fd_msg_log( FD_MSG_LOG_DROPPED, *pmsg, "Internal error: a FWD routing callback returned an error (%s)", strerror(ret));
750                                        fd_msg_free(*pmsg);
751                                        *pmsg = NULL;
752                                } );
753                }
754
755                pthread_cleanup_pop(0);
756                CHECK_FCT( pthread_rwlock_unlock( &rt_fwd_lock ) );
757
758                /* If a callback has handled the message, we stop now */
759                if (!*pmsg)
760                        return 0;
761        }
762
763        /* Now pass the message to the next step: either forward to another peer, or dispatch to local extensions */
764        if (is_req || qry_src) {
765                CHECK_FCT(fd_fifo_post(fd_g_outgoing, pmsg) );
766        } else {
767                CHECK_FCT(fd_fifo_post(fd_g_local, pmsg) );
768        }
769
770        /* We're done with this message */
771        return 0;
772}
773               
774
775/* The ROUTING-OUT message processing */
776static int msg_rt_out(struct msg ** pmsg)
777{
778        struct rt_data * rtd = NULL;
779        struct msg_hdr * hdr;
780        int is_req = 0;
781        int ret;
782        struct fd_list * li, *candidates;
783        struct avp * avp;
784        struct rtd_candidate * c;
785       
786        /* Read the message header */
787        CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) );
788        is_req = hdr->msg_flags & CMD_FLAG_REQUEST;
789       
790        /* For answers, the routing is very easy */
791        if ( ! is_req ) {
792                struct msg * qry;
793                DiamId_t qry_src = NULL;
794                size_t qry_src_len = 0;
795                struct msg_hdr * qry_hdr;
796                struct fd_peer * peer = NULL;
797
798                /* Retrieve the corresponding query and its origin */
799                CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) );
800                CHECK_FCT( fd_msg_source_get( qry, &qry_src, &qry_src_len ) );
801
802                ASSERT( qry_src ); /* if it is NULL, the message should have been in the LOCAL queue! */
803
804                /* Find the peer corresponding to this name */
805                CHECK_FCT( fd_peer_getbyid( qry_src, qry_src_len, 0, (void *) &peer ) );
806                if (fd_peer_getstate(peer) != STATE_OPEN) {
807                        fd_msg_log( FD_MSG_LOG_DROPPED, *pmsg, "Unable to forward answer to deleted / closed peer '%s'.", qry_src);
808                        fd_msg_free(*pmsg);
809                        *pmsg = NULL;
810                        return 0;
811                }
812
813                /* We must restore the hop-by-hop id */
814                CHECK_FCT( fd_msg_hdr(qry, &qry_hdr) );
815                hdr->msg_hbhid = qry_hdr->msg_hbhid;
816
817                /* Push the message into this peer */
818                CHECK_FCT( fd_out_send(pmsg, NULL, peer, 0) );
819
820                /* We're done with this answer */
821                return 0;
822        }
823       
824        /* From that point, the message is a request */
825
826        /* Get the routing data out of the message if any (in case of re-transmit) */
827        CHECK_FCT( fd_msg_rt_get ( *pmsg, &rtd ) );
828
829        /* If there is no routing data already, let's create it */
830        if (rtd == NULL) {
831                CHECK_FCT( fd_rtd_init(&rtd) );
832
833                /* Add all peers currently in OPEN state */
834                CHECK_FCT( pthread_rwlock_rdlock(&fd_g_activ_peers_rw) );
835                for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) {
836                        struct fd_peer * p = (struct fd_peer *)li->o;
837                        CHECK_FCT_DO( ret = fd_rtd_candidate_add(rtd, 
838                                                        p->p_hdr.info.pi_diamid, 
839                                                        p->p_hdr.info.pi_diamidlen, 
840                                                        p->p_hdr.info.runtime.pir_realm,
841                                                        p->p_hdr.info.runtime.pir_realmlen), 
842                                { CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_activ_peers_rw), ); return ret; } );
843                }
844                CHECK_FCT( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
845
846                /* Now let's remove all peers from the Route-Records */
847                CHECK_FCT(  fd_msg_browse(*pmsg, MSG_BRW_FIRST_CHILD, &avp, NULL)  );
848                while (avp) {
849                        struct avp_hdr * ahdr;
850                        struct fd_pei error_info;
851                        CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr )  );
852
853                        if ((ahdr->avp_code == AC_ROUTE_RECORD) && (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) ) {
854                                /* Parse this AVP */
855                                CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
856                                        {
857                                                if (error_info.pei_errcode) {
858                                                        CHECK_FCT( return_error( pmsg, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
859                                                        return 0;
860                                                } else {
861                                                        return ret;
862                                                }
863                                        } );
864                                ASSERT( ahdr->avp_value );
865                                /* Remove this value from the list. We don't need to pay special attention to the contents here. */
866                                fd_rtd_candidate_del(rtd, ahdr->avp_value->os.data, ahdr->avp_value->os.len);
867                        }
868
869                        /* Go to next AVP */
870                        CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL)  );
871                }
872        }
873
874        /* 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 ? -- TODO */
875
876        /* Ok, we have our list in rtd now, let's (re)initialize the scores */
877        fd_rtd_candidate_extract(rtd, &candidates, FD_SCORE_INI);
878
879        /* Pass the list to registered callbacks (even if it is empty) */
880        {
881                CHECK_FCT( pthread_rwlock_rdlock( &rt_out_lock ) );
882                pthread_cleanup_push( fd_cleanup_rwlock, &rt_out_lock );
883
884                /* We call the cb by reverse priority order */
885                for (   li = rt_out_list.prev ; li != &rt_out_list ; li = li->prev ) {
886                        struct rt_hdl * rh = (struct rt_hdl *)li;
887
888                        TRACE_DEBUG(ANNOYING, "Calling next OUT callback on %p : %p (prio %d)", *pmsg, rh->rt_out_cb, rh->prio);
889                        CHECK_FCT_DO( ret = (*rh->rt_out_cb)(rh->cbdata, *pmsg, candidates),
890                                {
891                                        fd_msg_log( FD_MSG_LOG_DROPPED, *pmsg, "Internal error: an OUT routing callback returned an error (%s)", strerror(ret));
892                                        fd_msg_free(*pmsg);
893                                        *pmsg = NULL;
894                                        break;
895                                } );
896                }
897
898                pthread_cleanup_pop(0);
899                CHECK_FCT( pthread_rwlock_unlock( &rt_out_lock ) );
900
901                /* If an error occurred, skip to the next message */
902                if (! *pmsg) {
903                        if (rtd)
904                                fd_rtd_free(&rtd);
905                        return 0;
906                }
907        }
908       
909        /* Order the candidate peers by score attributed by the callbacks */
910        CHECK_FCT( fd_rtd_candidate_reorder(candidates) );
911
912        /* Save the routing information in the message */
913        CHECK_FCT( fd_msg_rt_associate ( *pmsg, &rtd ) );
914
915        /* Now try sending the message */
916        for (li = candidates->prev; li != candidates; li = li->prev) {
917                struct fd_peer * peer;
918
919                c = (struct rtd_candidate *) li;
920
921                /* Stop when we have reached the end of valid candidates */
922                if (c->score < 0)
923                        break;
924
925                /* Search for the peer */
926                CHECK_FCT( fd_peer_getbyid( c->diamid, c->diamidlen, 0, (void *)&peer ) );
927
928                if (fd_peer_getstate(peer) == STATE_OPEN) {
929                        /* Send to this one */
930                        CHECK_FCT_DO( fd_out_send(pmsg, NULL, peer, 0), continue );
931                       
932                        /* If the sending was successful */
933                        break;
934                }
935        }
936
937        /* If the message has not been sent, return an error */
938        if (*pmsg) {
939                fd_msg_log( FD_MSG_LOG_NODELIVER, *pmsg, "No suitable candidate to route the message to." );
940                return_error( pmsg, "DIAMETER_UNABLE_TO_DELIVER", "No suitable candidate to route the message to", NULL);
941        }
942
943        /* We're done with this message */
944       
945        return 0;
946}
947
948
949/********************************************************************************/
950/*                     Management of the threads                                */
951/********************************************************************************/
952
953/* Note: in the first version, we only create one thread of each kind.
954 We could improve the scalability by using the threshold feature of the queues
955 to create additional threads if a queue is filling up, or at least giving a configurable
956 number of threads of each kind.
957 */
958
959/* Control of the threads */
960static enum { RUN = 0, STOP = 1 } order_val = RUN;
961static pthread_mutex_t order_state_lock = PTHREAD_MUTEX_INITIALIZER;
962
963/* Threads report their status */
964enum thread_state { NOTRUNNING = 0, RUNNING = 1 };
965static void cleanup_state(void * state_loc)
966{
967        CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
968        *(enum thread_state *)state_loc = NOTRUNNING;
969        CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
970}
971
972/* This is the common thread code (same for routing and dispatching) */
973static void * process_thr(void * arg, int (*action_cb)(struct msg ** pmsg), struct fifo * queue, char * action_name)
974{
975        TRACE_ENTRY("%p %p %p %p", arg, action_cb, queue, action_name);
976       
977        /* Set the thread name */
978        {
979                char buf[48];
980                snprintf(buf, sizeof(buf), "%s (%p)", action_name, arg);
981                fd_log_threadname ( buf );
982        }
983       
984        /* The thread reports its status when canceled */
985        CHECK_PARAMS_DO(arg, return NULL);
986        pthread_cleanup_push( cleanup_state, arg );
987       
988        /* Mark the thread running */
989        CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
990        *(enum thread_state *)arg = RUNNING;
991        CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
992       
993        do {
994                struct msg * msg;
995       
996                /* Test the current order */
997                {
998                        int must_stop;
999                        CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), { ASSERT(0); } ); /* we lock to flush the caches */
1000                        must_stop = (order_val == STOP);
1001                        CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), { ASSERT(0); } );
1002                        if (must_stop)
1003                                goto end;
1004                       
1005                        pthread_testcancel();
1006                }
1007               
1008                /* Ok, we are allowed to run */
1009               
1010                /* Get the next message from the queue */
1011                {
1012                        int ret;
1013                        struct timespec ts;
1014                       
1015                        CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), goto fatal_error );
1016                        ts.tv_sec += 1;
1017                       
1018                        ret = fd_fifo_timedget ( queue, &msg, &ts );
1019                        if (ret == ETIMEDOUT)
1020                                /* loop, check if the thread must stop now */
1021                                continue;
1022                        if (ret == EPIPE)
1023                                /* The queue was destroyed, we are probably exiting */
1024                                goto end;
1025                       
1026                        /* check if another error occurred */
1027                        CHECK_FCT_DO( ret, goto fatal_error );
1028                }
1029               
1030                if (TRACE_BOOL(FULL)) {
1031                        TRACE_DEBUG(FULL, "Picked next message");
1032                        fd_msg_dump_one(ANNOYING, msg);
1033                }
1034               
1035                /* Now process the message */
1036                CHECK_FCT_DO( (*action_cb)(&msg), goto fatal_error);
1037
1038                /* We're done with this message */
1039       
1040        } while (1);
1041       
1042fatal_error:
1043        TRACE_DEBUG(INFO, "An unrecoverable error occurred, %s thread is terminating...", action_name);
1044        CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
1045       
1046end:   
1047        ; /* noop so that we get rid of "label at end of compund statement" warning */
1048        /* Mark the thread as terminated */
1049        pthread_cleanup_pop(1);
1050        return NULL;
1051}
1052
1053/* The dispatch thread */
1054static void * dispatch_thr(void * arg)
1055{
1056        return process_thr(arg, msg_dispatch, fd_g_local, "Dispatch");
1057}
1058
1059/* The (routing-in) thread -- see description in freeDiameter.h */
1060static void * routing_in_thr(void * arg)
1061{
1062        return process_thr(arg, msg_rt_in, fd_g_incoming, "Routing-IN");
1063}
1064
1065/* The (routing-out) thread -- see description in freeDiameter.h */
1066static void * routing_out_thr(void * arg)
1067{
1068        return process_thr(arg, msg_rt_out, fd_g_outgoing, "Routing-OUT");
1069}
1070
1071
1072/********************************************************************************/
1073/*                     The functions for the other files                        */
1074/********************************************************************************/
1075
1076static pthread_t * dispatch = NULL;
1077static enum thread_state * disp_state = NULL;
1078
1079/* Later: make this more dynamic */
1080static pthread_t rt_out = (pthread_t)NULL;
1081static enum thread_state out_state = NOTRUNNING;
1082
1083static pthread_t rt_in  = (pthread_t)NULL;
1084static enum thread_state in_state = NOTRUNNING;
1085
1086/* Initialize the routing and dispatch threads */
1087int fd_rtdisp_init(void)
1088{
1089        int i;
1090       
1091        /* Prepare the array for dispatch */
1092        CHECK_MALLOC( disp_state = calloc(fd_g_config->cnf_dispthr, sizeof(enum thread_state)) );
1093        CHECK_MALLOC( dispatch = calloc(fd_g_config->cnf_dispthr, sizeof(pthread_t)) );
1094       
1095        /* Create the threads */
1096        for (i=0; i < fd_g_config->cnf_dispthr; i++) {
1097                CHECK_POSIX( pthread_create( &dispatch[i], NULL, dispatch_thr, &disp_state[i] ) );
1098        }
1099        CHECK_POSIX( pthread_create( &rt_out, NULL, routing_out_thr, &out_state) );
1100        CHECK_POSIX( pthread_create( &rt_in,  NULL, routing_in_thr,  &in_state) );
1101       
1102        /* Later: TODO("Set the thresholds for the queues to create more threads as needed"); */
1103       
1104        /* Register the built-in callbacks */
1105        CHECK_FCT( fd_rt_out_register( dont_send_if_no_common_app, NULL, 10, NULL ) );
1106        CHECK_FCT( fd_rt_out_register( score_destination_avp, NULL, 10, NULL ) );
1107       
1108        return 0;
1109}
1110
1111/* Ask the thread to terminate after next iteration */
1112int fd_rtdisp_cleanstop(void)
1113{
1114        CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
1115        order_val = STOP;
1116        CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_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        int terminated;
1126       
1127        CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
1128        terminated = (*st == NOTRUNNING);
1129        CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
1130       
1131
1132        /* Wait for a second for the thread to complete, by monitoring my_state */
1133        if (!terminated) {
1134                TRACE_DEBUG(INFO, "Waiting for the %s thread to have a chance to terminate", th_name);
1135                do {
1136                        struct timespec  ts, ts_final;
1137
1138                        CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), break );
1139                       
1140                        ts_final.tv_sec = ts.tv_sec + 1;
1141                        ts_final.tv_nsec = ts.tv_nsec;
1142                       
1143                        while (TS_IS_INFERIOR( &ts, &ts_final )) {
1144                       
1145                                CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
1146                                terminated = (*st == NOTRUNNING);
1147                                CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
1148                                if (terminated)
1149                                        break;
1150                               
1151                                usleep(100000);
1152                                CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), break );
1153                        }
1154                } while (0);
1155        }
1156
1157        /* Now stop the thread and reclaim its resources */
1158        CHECK_FCT_DO( fd_thr_term(thr ), /* continue */);
1159       
1160}
1161
1162/* Stop the thread after up to one second of wait */
1163int fd_rtdisp_fini(void)
1164{
1165        int i;
1166       
1167        /* Destroy the incoming queue */
1168        CHECK_FCT_DO( fd_queues_fini(&fd_g_incoming), /* ignore */);
1169       
1170        /* Stop the routing IN thread */
1171        stop_thread_delayed(&in_state, &rt_in, "IN routing");
1172       
1173        /* Destroy the outgoing queue */
1174        CHECK_FCT_DO( fd_queues_fini(&fd_g_outgoing), /* ignore */);
1175       
1176        /* Stop the routing OUT thread */
1177        stop_thread_delayed(&out_state, &rt_out, "OUT routing");
1178       
1179        /* Destroy the local queue */
1180        CHECK_FCT_DO( fd_queues_fini(&fd_g_local), /* ignore */);
1181       
1182        /* Stop the Dispatch threads */
1183        if (dispatch != NULL) {
1184                for (i=0; i < fd_g_config->cnf_dispthr; i++) {
1185                        stop_thread_delayed(&disp_state[i], &dispatch[i], "Dispatching");
1186                }
1187                free(dispatch);
1188                dispatch = NULL;
1189        }
1190        if (disp_state != NULL) {
1191                free(disp_state);
1192                disp_state = NULL;
1193        }
1194       
1195        return 0;
1196}
1197
1198/* Cleanup handlers */
1199int fd_rtdisp_cleanup(void)
1200{
1201        /* Cleanup all remaining handlers */
1202        while (!FD_IS_LIST_EMPTY(&rt_fwd_list)) {
1203                CHECK_FCT_DO( fd_rt_fwd_unregister ( (void *)rt_fwd_list.next, NULL ), /* continue */ );
1204        }
1205        while (!FD_IS_LIST_EMPTY(&rt_out_list)) {
1206                CHECK_FCT_DO( fd_rt_out_unregister ( (void *)rt_out_list.next, NULL ), /* continue */ );
1207        }
1208       
1209        fd_disp_unregister_all(); /* destroy remaining handlers */
1210
1211        return 0;
1212}
1213
1214
1215/********************************************************************************/
1216/*                     For extensiosn to register a new appl                    */
1217/********************************************************************************/
1218
1219/* Add an application into the peer's supported apps */
1220int fd_disp_app_support ( struct dict_object * app, struct dict_object * vendor, int auth, int acct )
1221{
1222        application_id_t aid = 0;
1223        vendor_id_t      vid = 0;
1224       
1225        TRACE_ENTRY("%p %p %d %d", app, vendor, auth, acct);
1226        CHECK_PARAMS( app && (auth || acct) );
1227       
1228        {
1229                enum dict_object_type type = 0;
1230                struct dict_application_data data;
1231                CHECK_FCT( fd_dict_gettype(app, &type) );
1232                CHECK_PARAMS( type == DICT_APPLICATION );
1233                CHECK_FCT( fd_dict_getval(app, &data) );
1234                aid = data.application_id;
1235        }
1236
1237        if (vendor) {
1238                enum dict_object_type type = 0;
1239                struct dict_vendor_data data;
1240                CHECK_FCT( fd_dict_gettype(vendor, &type) );
1241                CHECK_PARAMS( type == DICT_VENDOR );
1242                CHECK_FCT( fd_dict_getval(vendor, &data) );
1243                vid = data.vendor_id;
1244        }
1245       
1246        return fd_app_merge(&fd_g_config->cnf_apps, aid, vid, auth, acct);
1247}
1248
1249
1250
Note: See TracBrowser for help on using the repository browser.