Navigation


source: freeDiameter/extensions/rt_redirect/redir_out.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.9 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
39/* Find the data pertinent to a type in the input data */
40static int get_data_to_match(enum redir_h_u type, struct msg * msg, union matchdata * data, int * nodata)
41{
42        TRACE_ENTRY("%d %p %p %p", type, msg, data, nodata);
43       
44        /* Initialize the data area */
45        memset(data, 0, sizeof(union matchdata));
46        *nodata = 0;
47       
48        /* Now, find the appropriate information, depending on type */
49        switch (type) {
50                case DONT_CACHE:
51                        data->message.msg = msg;
52                        break;
53                       
54                case ALL_SESSION:
55                        {
56                                /* Get the sid from the message */
57                                struct session * sess;
58                                CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, msg, &sess, NULL) );
59                                if (!sess) {
60                                        TRACE_DEBUG(ANNOYING, "Message %p cannot match any ALL_SESSION rule since it does not have a Session-Id", msg);
61                                        *nodata = 1;
62                                } else {
63                                        CHECK_FCT( fd_sess_getsid(sess, &data->session.s, &data->session.l) );
64                                }
65                        }
66                        break;
67               
68                case ALL_REALM:
69                        {
70                                /* Search the Destination-Realm in the message */
71                                struct avp * dr;
72                                CHECK_FCT( fd_msg_search_avp(msg, redir_dict_dr, &dr) );
73                                if (!dr) {
74                                        TRACE_DEBUG(ANNOYING, "Message %p cannot match any ALL_REALM rule since it does not have a Destination-Realm", msg);
75                                        *nodata = 1;
76                                } else {
77                                        struct avp_hdr * ahdr;
78                                        CHECK_FCT(  fd_msg_avp_hdr( dr, &ahdr )  );
79                                        data->realm.s = ahdr->avp_value->os.data;
80                                        data->realm.l = ahdr->avp_value->os.len;
81                                }
82                        }
83                        break;
84                               
85                case REALM_AND_APPLICATION:
86                        {
87                                /* Search the Destination-Realm of the message */
88                                struct avp * dr;
89                                CHECK_FCT( fd_msg_search_avp(msg, redir_dict_dr, &dr) );
90                                if (!dr) {
91                                        TRACE_DEBUG(ANNOYING, "Message %p cannot match any REALM_AND_APPLICATION rule since it does not have a Destination-Realm", msg);
92                                        *nodata = 1;
93                                } else {
94                                        struct avp_hdr * ahdr;
95                                        CHECK_FCT(  fd_msg_avp_hdr( dr, &ahdr )  );
96                                        data->realm_app.s = ahdr->avp_value->os.data;
97                                        data->realm_app.l = ahdr->avp_value->os.len;
98                                       
99                                        /* and then the application */
100                                        {
101                                                struct msg_hdr * hdr;
102                                                CHECK_FCT( fd_msg_hdr(msg, &hdr) );
103                                                data->realm_app.a = hdr->msg_appl;
104                                                /* Should we forbid application 0? */
105                                        }
106                                }
107                        }
108                        break;
109                               
110                case ALL_APPLICATION:
111                        {
112                                /* Retrieve the application from the message */
113                                struct msg_hdr * hdr;
114                                CHECK_FCT( fd_msg_hdr(msg, &hdr) );
115                                data->app.a = hdr->msg_appl;
116                        }
117                        break;
118                       
119                case ALL_HOST:
120                                /* This is more complex, we need to match with all candidates in each rule, it'll be done later */
121                        break;
122                       
123                case ALL_USER:
124                        {
125                                /* Search the User-Name of the message */
126                                struct avp * un;
127                                CHECK_FCT( fd_msg_search_avp(msg, redir_dict_un, &un) );
128                                if (!un) {
129                                        TRACE_DEBUG(ANNOYING, "Message %p cannot match any ALL_USER rule since it does not have a User-Name", msg);
130                                        *nodata = 1;
131                                } else {
132                                        struct avp_hdr * ahdr;
133                                        CHECK_FCT(  fd_msg_avp_hdr( un, &ahdr )  );
134                                        data->user.s = ahdr->avp_value->os.data;
135                                        data->user.l = ahdr->avp_value->os.len;
136                                }
137                        }
138                        break;
139               
140                default:
141                        ASSERT(0);
142                        return EINVAL;
143        }
144
145        return 0;
146}
147
148
149/* Apply the score from a rule if the candidate list is appropriate */
150static int apply_rule(struct redir_entry * e, struct msg * msg, struct fd_list * candidates)
151{
152        struct fd_list * lic, *lirh;
153        struct rtd_candidate * c_oh = NULL;
154        int cmp;
155       
156        TRACE_ENTRY("%p %p %p", e, msg, candidates);
157        ASSERT( e && msg && candidates );
158       
159        if (FD_IS_LIST_EMPTY(candidates)) {
160                TRACE_DEBUG(ANNOYING, "Skip Redirect rule since candidates list is empty");
161                return 0;
162        }
163       
164        /* Now search common peers between e->target_peers_list and candidates */
165        TRACE_DEBUG(ANNOYING, "Message %p matches a Redirect rule (t:%d, @%p), processing candidates list", msg, e->type, e);
166       
167        /* First, decrease the score of the host that we received the previous Redirect from, in case it is in the list */
168        for (lic = candidates->next; lic != candidates; lic = lic->next) {
169                struct rtd_candidate * cand = (struct rtd_candidate *) lic;
170               
171                /* Special case: ALL_HOST rules: we decrease the score of the Origin-Host if present */
172                if (e->type == ALL_HOST) {
173                        cmp = fd_os_almostcasesrch(cand->diamid, cand->diamidlen, e->data.host.s, e->data.host.l, NULL);
174                        if (!cmp) {
175                                TRACE_DEBUG(FULL, "Redirect msg %p: peer '%.*s' += %d (previous ALL_HOST Redirect originated from this peer)", msg, cand->diamidlen, cand->diamid, FD_SCORE_SENT_REDIRECT);
176                                cand->score += FD_SCORE_SENT_REDIRECT;
177                                c_oh = cand;
178                                continue;
179                        }
180                }
181               
182                cmp = fd_os_cmp(cand->diamid, cand->diamidlen, e->from.s, e->from.l);
183                if (!cmp) {
184                        TRACE_DEBUG(FULL, "Redirect msg %p: peer '%.*s' += %d (previous Redirect received from this peer)", msg, cand->diamidlen, cand->diamid, FD_SCORE_SENT_REDIRECT);
185                        cand->score += FD_SCORE_SENT_REDIRECT;
186                }
187               
188        }
189       
190        if ((e->type == ALL_HOST) && (c_oh == NULL)) {
191                /* The rule does not apply, we're done */
192                return 0;
193        }
194       
195        /* for each candidate, if it is found in the target_peers list, we add the rule's score to this candidate */
196        for (lic = candidates->next; lic != candidates; lic = lic->next) {
197                /* the candidates list is not guaranteed to be ordered at this time, so we cannot avoid the two imbricated loops */
198                struct rtd_candidate * cand = (struct rtd_candidate *) lic;
199               
200                /* Is this candidate in the "Redirect-Host" list ? We must search caseinsentive here. */
201                for (lirh = e->target_peers_list.next; lirh != &e->target_peers_list; lirh = lirh->next) {
202                        struct redir_host * host = lirh->o;
203                        int cont;
204                       
205                        cmp = fd_os_almostcasesrch( cand->diamid, cand->diamidlen, host->id, host->len, &cont );
206                       
207                        if (cmp == 0) {
208                                TRACE_DEBUG(FULL, "Redirect msg %p: peer '%.*s' += %d (rule t:%d @%p)", msg, cand->diamidlen, cand->diamid, redirects_usages[e->type].score, e->type, e);
209                                cand->score += redirects_usages[e->type].score;
210                                break;
211                        }
212                        if (!cont)
213                                break;
214                }
215        }
216       
217        return 0;
218}
219
220
221/* OUT callback */
222int redir_out_cb(void * cbdata, struct msg * msg, struct fd_list * candidates)
223{
224        int i, ret = 0;
225       
226        TRACE_ENTRY("%p %p %p", cbdata, msg, candidates);
227       
228        for (i = 0; i <= H_U_MAX; i++) {
229                /* Lock the line. We write lock in case of DONT_CACHE so we can directly unlink the entry. read in other cases is sufficient */
230                if (i == DONT_CACHE) {
231                        CHECK_POSIX( pthread_rwlock_wrlock( &redirects_usages[i].lock ) );
232                } else {
233                        CHECK_POSIX( pthread_rwlock_rdlock( &redirects_usages[i].lock ) );
234                }
235               
236                if (!FD_IS_LIST_EMPTY(&redirects_usages[i].sentinel)) {
237                        union matchdata data;
238                        int nodata; /* The message does not allow to apply this rule, skip */
239                       
240                        /* Retrieve the data that may match in the message */
241                        CHECK_FCT_DO( ret = get_data_to_match(i, msg, &data, &nodata), goto out );
242                       
243                        /* If this message may match some of our rules */
244                        if (!nodata) {
245                                struct fd_list * li;
246                                /* Attempt each rule we have stored */
247                                for (li = redirects_usages[i].sentinel.next; li != &redirects_usages[i].sentinel; li = li->next) {
248                                        struct redir_entry * e = li->o;
249                                       
250                                        /* Does it match ? */
251                                        if (i != ALL_HOST) { /* this one is an exception, we handle it separately */
252                                                int cmp = redir_entry_cmp_key[i](&data, &e->data);
253                                                if (cmp > 0)
254                                                        continue;
255                                                if (cmp < 0)
256                                                        break;
257                                        }
258                                       
259                                        /* This rule matches (or we are in ALL_HOST), apply */
260                                        CHECK_FCT_DO( ret = apply_rule(e, msg, candidates), goto out );
261                                       
262                                        /* If this was a DONT_CACHE rule, we unlink it, so that it will not be used again */
263                                        if (i == DONT_CACHE) {
264                                                li=li->prev;
265                                                fd_list_unlink( li->next );
266                                                /* We cannot delete here without taking the mutex, which would mean we have first to release the lock...
267                                                        just let expiry garbage collet the rule */
268                                        }
269                                }
270                        }
271               
272                }
273out:           
274                CHECK_POSIX( pthread_rwlock_unlock( &redirects_usages[i].lock ) );
275                if (ret)
276                        return ret;
277        }
278       
279        return 0;
280}
281
Note: See TracBrowser for help on using the repository browser.