Navigation


source: freeDiameter/extensions/rt_redirect/redir_fwd.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: 9.5 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 "rt_redir.h"
37
38/* This structure contains the data to keep when a new peer's connection is attempted (for later) */
39struct redir_task {
40        struct msg * answer; /* the message that was being processed */
41        uint32_t     rhu;    /* Redirect-Host-Usage value */
42        uint32_t     rmct;   /* Redirect-Max-Cache-Time value */
43        struct fd_list rh;   /* the list of Redirect-Hosts */
44};
45
46/* Received answers FWD callback */
47int redir_fwd_cb(void * cbdata, struct msg ** msg)
48{
49        struct msg * m, * q;
50        struct rt_data *rtd;
51        struct msg_hdr * hdr;
52        union avp_value *a_rc = NULL, *a_rhu = NULL, *a_rmct = NULL, *a_oh = NULL;
53        int known = 0, actives = 0;
54        struct fd_list * li;
55        struct avp * avp;
56        struct redir_task task = { .answer = NULL, .rhu = 0, .rmct = 0, .rh = FD_LIST_INITIALIZER(task.rh) };
57        DiamId_t nh;
58        size_t   nhlen;
59        int nbrh = 0;
60        struct redir_entry * entry;
61       
62        TRACE_ENTRY("%p %p", cbdata, msg);
63       
64        CHECK_PARAMS(msg && *msg);
65       
66        m = *msg;
67       
68        /* First get the header */
69        CHECK_FCT( fd_msg_hdr(m, &hdr) );
70       
71        /* Check if we have an error */
72        ASSERT(!(hdr->msg_flags & CMD_FLAG_REQUEST));
73        if (!(hdr->msg_flags & CMD_FLAG_ERROR)) {
74                /* This answer does not have the E flag, no need to process further */
75                return 0;
76        }
77       
78        /* Now get the AVPs we are interested in */
79        CHECK_FCT(  fd_msg_browse(m, MSG_BRW_FIRST_CHILD, &avp, NULL)  );
80        while (avp) {
81                struct avp_hdr * ahdr;
82                       
83                CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr )  );
84                if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) {
85                        switch (ahdr->avp_code) {
86                                case AC_ORIGIN_HOST:
87                                        /* Parse this AVP */
88                                        CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) );
89                                        ASSERT( ahdr->avp_value );
90                                        a_oh = ahdr->avp_value;
91                                        break;
92                                       
93                                case AC_RESULT_CODE:
94                                        /* Parse this AVP */
95                                        CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) );
96                                        ASSERT( ahdr->avp_value );
97                                        a_rc = ahdr->avp_value;
98                                       
99                                        if (a_rc->u32 != ER_DIAMETER_REDIRECT_INDICATION) {
100                                                /* It is not a REDIRECT error, we don't do anything */
101                                                goto out;
102                                        }
103                                        break;
104                                       
105                                case AC_REDIRECT_HOST:
106                                        {
107                                                struct redir_host * h = NULL;
108                                                DiamId_t id = NULL;
109                                                size_t   len = 0;
110                                                int      secure = 0;
111                                                uint16_t port = 0;
112                                                int      l4 = 0;
113                                                char     proto = 0;
114                                               
115                                                /* Parse this AVP */
116                                                CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) );
117                                                ASSERT( ahdr->avp_value );
118                                               
119                                                nbrh++;
120                                               
121                                                CHECK_FCT_DO( fd_os_parse_DiameterURI(ahdr->avp_value->os.data, ahdr->avp_value->os.len, 
122                                                                        &id, &len, &secure, &port, &l4, &proto),
123                                                        {
124                                                                TRACE_DEBUG(INFO, "Received an invalid Redirect-Host AVP value ('%.*s'), ignored", ahdr->avp_value->os.len, ahdr->avp_value->os.data);
125                                                                break;
126                                                        } );
127                                               
128                                                /* Now check if the transport & protocol are supported */
129                                                if (proto && (proto != 'd')) {
130                                                        TRACE_DEBUG(FULL, "Ignored unsupported non-Diameter Redirect-Host AVP (%.*s)", ahdr->avp_value->os.len, ahdr->avp_value->os.data);
131                                                        free(id);
132                                                        break;
133                                                }
134                                                if (l4 && (l4 == IPPROTO_UDP)) {
135                                                        TRACE_DEBUG(FULL, "Ignored unsupported UDP Redirect-Host AVP (%.*s)", ahdr->avp_value->os.len, ahdr->avp_value->os.data);
136                                                        free(id);
137                                                        break;
138                                                }
139                                               
140                                                /* It looks OK, save this entry. */
141                                               
142                                                CHECK_MALLOC( h = malloc(sizeof(struct redir_host)) );
143                                                memset(h, 0, sizeof(struct redir_host));
144                                                fd_list_init(&h->chain, h);
145                                                h->id = id;
146                                                h->len = len;
147                                                /* later: secure, port */
148                                               
149                                                /* The list is kept ordered by id so that it is faster to compare to candidates later */
150                                                for (li = task.rh.next; li != &task.rh; li = li->next) {
151                                                        struct redir_host * nhost = li->o;
152                                                        if ( fd_os_cmp(id, len, nhost->id, nhost->len) <= 0 )
153                                                                break;
154                                                }
155                                                fd_list_insert_before(li, &h->chain);
156                                        }
157                                        break;
158
159                                case AC_REDIRECT_HOST_USAGE:
160                                        /* Parse this AVP */
161                                        CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) );
162                                        ASSERT( ahdr->avp_value );
163                                        a_rhu = ahdr->avp_value;
164                                        if (a_rhu->u32 > H_U_MAX) {
165                                                TRACE_DEBUG(INFO, "Received unsupported Redirect-Host-Usage value (%d), defaulting to DONT_CACHE", a_rhu->u32);
166                                        } else {
167                                                task.rhu = a_rhu->u32;
168                                        }
169                                        break;
170
171                                case AC_REDIRECT_MAX_CACHE_TIME:
172                                        /* Parse this AVP */
173                                        CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) );
174                                        ASSERT( ahdr->avp_value );
175                                        a_rmct = ahdr->avp_value;
176                                        task.rmct = a_rmct->u32;
177                                        break;
178
179                        }
180                }
181
182                /* Go to next AVP */
183                CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL)  );
184        }
185       
186        /* Check we have received the necessary information */
187        if (!a_rc) {
188                TRACE_DEBUG(FULL, "Invalid Diameter answer without a Result-Code AVP, Redirect module gave up");
189                goto out;
190        }
191       
192        if (!a_oh) {
193                TRACE_DEBUG(FULL, "Invalid Diameter answer without an Origin-Host AVP, Redirect module gave up");
194                goto out;
195        }
196       
197        if (FD_IS_LIST_EMPTY(&task.rh)) {
198                TRACE_DEBUG(FULL, "Diameter answer with a DIAMETER_REDIRECT_INDICATION Result-Code AVP but no valid/supported Redirect-Host AVP, Redirect module gave up");
199                goto out;
200        }
201
202        if (a_rhu && (task.rhu != DONT_CACHE) && !a_rmct) {
203                TRACE_DEBUG(FULL, "Invalid Diameter Redirect answer with a Redirect-Host-Usage AVP but no Redirect-Max-Cache-Time, Redirect module gave up");
204                goto out;
205        }
206       
207        /* It looks like we can process the Redirect indication */
208       
209        /* Search for the peers we already know */
210        for (li = task.rh.next; li != &task.rh; li = li->next) {
211                struct redir_host * h = li->o;
212                struct peer_hdr * peer;
213               
214                CHECK_FCT( fd_peer_getbyid( h->id, h->len, 1, &peer ) );
215                if (peer) {
216                        known ++;
217                        memcpy(h->id, peer->info.pi_diamid, h->len); /* Overwrite the case so we can search case-sensitive from here on */
218                        if (fd_peer_get_state(peer) == STATE_OPEN) {
219                                actives ++;
220                        }
221                }
222        }
223       
224        TRACE_DEBUG(FULL, "Redirect module: received %d Redirect-Hosts, %d are known peers, %d have an OPEN connection", nbrh, known, actives);
225       
226        /* in this version, we only redirect when there are known active peers. TODO: add new peers via fd_peer_add when no active peer is available */
227       
228        if (!actives) {
229                TRACE_DEBUG(INFO, "Unable to comply to Redirect indication: none of the peers included is in OPEN state");
230                goto out;
231        }
232       
233        /* From this point, we will re-send the query to a different peer, so stop forwarding the answer here */
234        *msg = NULL;
235       
236        /* Get the query's routing data & add the new error */
237        CHECK_FCT( fd_msg_answ_getq(m, &q) );
238        CHECK_FCT( fd_msg_rt_get(q, &rtd) );
239        CHECK_FCT( fd_msg_source_get( m, &nh, &nhlen ) );
240        CHECK_FCT( fd_rtd_error_add(rtd, nh, nhlen, a_oh->os.data, a_oh->os.len, a_rc->u32) );
241       
242        /* Create a redir_rule  */
243        CHECK_FCT( redir_entry_new(&entry, &task.rh, task.rhu, q, nh, nhlen, a_oh->os.data, a_oh->os.len) );
244               
245        CHECK_POSIX(  pthread_mutex_lock(&redir_exp_peer_lock)  );
246        /* Insert in the split list */
247        CHECK_FCT( redir_entry_insert(entry) );
248        /* Set the expiry */
249        CHECK_FCT( redir_exp_set(entry, task.rmct ?: DEFAULT_EXPIRE_TIME) );
250        CHECK_POSIX(  pthread_mutex_unlock(&redir_exp_peer_lock)  );
251
252        /* Now we can get rid of the received answer and send again the query. */
253        CHECK_FCT( fd_msg_answ_detach(m) );
254        CHECK_FCT( fd_msg_free(m) );
255       
256        /* Send it */
257        CHECK_FCT( fd_msg_send(&q, NULL, NULL) );
258       
259        /* Done! */
260       
261out:
262        while (!FD_IS_LIST_EMPTY(&task.rh)) {
263                struct redir_host * h = task.rh.next->o;
264                fd_list_unlink(&h->chain);
265                free(h->id);
266                free(h);
267        }
268               
269        return 0;
270
271}
272
Note: See TracBrowser for help on using the repository browser.