Navigation


source: freeDiameter/extensions/app_radgw/rgw_clients.c @ 718:2d5e76dd1117

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

Updated copyright information

File size: 39.3 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/* Manage the list of RADIUS clients, along with their shared secrets. */
37
38/* Probably some changes are needed to support RADIUS Proxies */
39
40#include "rgw.h"
41
42#define REVERSE_DNS_SIZE_MAX    512 /* length of our buffer for reverse DNS */
43#define DUPLICATE_CHECK_LIFETIME 60 /* number of seconds that the received RADIUS records are kept for duplicate checking . TODO: make it configurable if needed */
44
45/* Ordered lists of clients. The order relationship is a memcmp on the address zone.
46   For same addresses, the port is compared.
47   The same address cannot be added twice, once with a 0-port and once with another port value.
48 */
49static struct fd_list cli_ip = FD_LIST_INITIALIZER(cli_ip);
50static struct fd_list cli_ip6 = FD_LIST_INITIALIZER(cli_ip6);
51
52/* Lock to protect the previous lists. We use a rwlock because this list is mostly static, to allow parallel reading */
53static pthread_rwlock_t cli_rwl = PTHREAD_RWLOCK_INITIALIZER;
54
55/* Structure describing one received RADIUS message, for duplicate checks purpose. */
56struct req_info {
57        uint16_t        port;   /* UDP source port of the request */
58        uint8_t         id;     /* The identifier in the request header */
59        uint8_t         auth[16]; /* Request authenticator, since some RADIUS clients do not implement the id mechanism properly. */
60        struct radius_msg *ans; /* The replied answer if any, in case the previous answer got lost. */
61       
62        int             nbdup;  /* Number of times this request was received as a duplicate */
63        struct fd_list  by_id;    /* The list of requests ordered by their id, port, and auth */
64        time_t          received; /* When was the last duplicate received? */
65        struct fd_list  by_time;  /* The list of requests ordered by the 'received' value . */
66};
67
68static pthread_t dbt_expire = (pthread_t)NULL; /* The thread that will remove old requests information from all clients (one thread for all) */
69
70/* Structure describing one client */
71struct rgw_client {
72        /* Link information in global list (cli_ip or cli_ip6) */
73        struct fd_list          chain;
74       
75        /* Reference count */
76        int                     refcount;
77       
78        /* The address and optional port (alloc'd during configuration file parsing). */
79        union {
80                struct sockaddr         *sa; /* generic pointer */
81                struct sockaddr_in      *sin;
82                struct sockaddr_in6     *sin6;
83        };
84       
85        /* The FQDN, realm, and optional aliases */
86        int                      is_local; /* true if the RADIUS client runs on the same host -- we use Diameter Identity in that case */
87        enum rgw_cli_type        type; /* is it a proxy ? */
88        DiamId_t                 fqdn; /* malloc'd here */
89        size_t                   fqdn_len;
90        DiamId_t                 realm; /* references another string, do not free */
91        size_t                   realm_len;
92        struct {
93                os0_t            name;
94                size_t           len;
95        }                       *aliases; /* Received aliases */
96        size_t                   aliases_nb;
97       
98        /* The secret key data. */
99        struct {
100                unsigned char * data;
101                size_t          len;
102        }                       key;
103       
104        /* information of previous msg received, for duplicate checks. */
105        struct {
106                pthread_mutex_t dupl_lock;    /* The mutex protecting the following lists */
107                struct fd_list  dupl_by_id;   /* The list of req_info structures ordered by their id, port, and auth */
108                struct fd_list  dupl_by_time; /* The list of req_info structures ordered by their time (approximative) */
109        } dupl_info[2]; /*[0] for auth, [1] for acct. */
110};
111
112
113/* Create a new req_info structure and initialize its data from a RADIUS request message */
114static struct req_info * dupl_new_req_info(struct rgw_radius_msg_meta *msg) {
115        struct req_info * ret = NULL;
116        CHECK_MALLOC_DO( ret = malloc(sizeof(struct req_info)), return NULL );
117        memset(ret, 0, sizeof(struct req_info));
118        ret->port = msg->port;
119        ret->id   = msg->radius.hdr->identifier;
120        memcpy(&ret->auth[0], &msg->radius.hdr->authenticator[0], 16);
121        fd_list_init(&ret->by_id, ret);
122        fd_list_init(&ret->by_time, ret);
123        ret->received = time(NULL);
124        return ret;
125}
126
127/* Destroy a req_info structure, after it has been unlinked */
128static void dupl_free_req_info(struct req_info * r) {
129        CHECK_PARAMS_DO( r && FD_IS_LIST_EMPTY(&r->by_id) && FD_IS_LIST_EMPTY(&r->by_time), return );
130        if (r->ans) {
131                /* Free this RADIUS message */
132                radius_msg_free(r->ans);
133                free(r->ans);
134        }
135       
136        /* Use r->nbdup for some purpose? */
137       
138        free(r);
139}
140
141/* The core of the purge thread */
142static int dupl_purge_list(struct fd_list * clients) {
143
144        struct fd_list *li = NULL;
145       
146        for (li = clients->next; li != clients; li = li->next) {
147                struct rgw_client * client = (struct rgw_client *)li;
148                int p;
149               
150                for (p=0; p<=1; p++) {
151               
152                        /* Lock this list */
153                        time_t now;
154                        CHECK_POSIX( pthread_mutex_lock(&client->dupl_info[p].dupl_lock) );
155                       
156                        now = time(NULL);
157                       
158                        while (!FD_IS_LIST_EMPTY(&client->dupl_info[p].dupl_by_time)) {
159                       
160                                /* Check the first item in the list */
161                                struct req_info * r = (struct req_info *)(client->dupl_info[p].dupl_by_time.next->o);
162                               
163                                if (now - r->received > DUPLICATE_CHECK_LIFETIME) {
164                               
165                                        TRACE_DEBUG(ANNOYING + 1, "Purging RADIUS request (id: %02hhx, port: %hu, dup #%d, age %d secs)", r->id, ntohs(r->port), r->nbdup, now - r->received);
166                                       
167                                        /* Remove this record */
168                                        fd_list_unlink(&r->by_time);
169                                        fd_list_unlink(&r->by_id);
170                                        dupl_free_req_info(r);
171                                } else {
172                                        /* We are done for this list */
173                                        break;
174                                }
175                        }
176                       
177                        CHECK_POSIX( pthread_mutex_unlock(&client->dupl_info[p].dupl_lock) );
178                }
179        }
180        return 0;
181}
182
183/* Thread that purges old RADIUS requests */
184static void * dupl_th(void * arg) {
185        /* Set the thread name */
186        fd_log_threadname ( "app_radgw:duplicate_purge" );
187       
188        /* The thread will be canceled */
189        while (1) {
190               
191                /* We don't use a cond var, we simply wake up every 5 seconds. If the size of the duplicate cache is critical, it might be changed */
192                sleep(5);
193               
194                /* When we wake up, we will check all clients duplicate lists one by one */
195                CHECK_POSIX_DO( pthread_rwlock_rdlock(&cli_rwl), break );
196               
197                CHECK_FCT_DO( dupl_purge_list(&cli_ip), break );
198                CHECK_FCT_DO( dupl_purge_list(&cli_ip6), break );
199               
200                CHECK_POSIX_DO( pthread_rwlock_unlock(&cli_rwl), break );
201       
202                /* Loop */
203        }
204       
205        /* If we reach this part, some fatal error was encountered */
206        CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
207        TRACE_DEBUG(FULL, "Thread terminated"); 
208        return NULL;
209}
210
211
212/* create a new rgw_client. the arguments are MOVED into the structure (to limit malloc & free calls). */
213static int client_create(struct rgw_client ** res, struct sockaddr ** ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type )
214{
215        struct rgw_client *tmp = NULL;
216        DiamId_t fqdn;
217        size_t fqdn_len = 0;
218        int ret, i;
219        int loc = 0;
220       
221        /* Check if the IP address is local */
222        if (   ( ((*ip_port)->sa_family == AF_INET ) && (   IN_IS_ADDR_LOOPBACK( &((struct sockaddr_in  *)(*ip_port))->sin_addr ) ) )
223             ||( ((*ip_port)->sa_family == AF_INET6) && (  IN6_IS_ADDR_LOOPBACK( &((struct sockaddr_in6 *)(*ip_port))->sin6_addr) ) )) {
224                /* The client is local */
225                loc = 1;
226        } else {
227                char buf[255];
228       
229                /* Search FQDN for the client */
230                ret = getnameinfo( *ip_port, sizeof(struct sockaddr_storage), &buf[0], sizeof(buf), NULL, 0, 0 );
231                if (ret) {
232                        TRACE_DEBUG(INFO, "Unable to resolve peer name: %s", gai_strerror(ret));
233                        return EINVAL;
234                }
235                fqdn = &buf[0];
236                CHECK_FCT_DO( ret = fd_os_validate_DiameterIdentity(&fqdn, &fqdn_len, 1),
237                        {
238                                TRACE_DEBUG(INFO, "Unable to use resolved peer name '%s' as DiameterIdentity: %s", buf, strerror(ret));
239                                return ret;
240                        } );
241        }
242       
243        /* Create the new object */
244        CHECK_MALLOC( tmp = malloc(sizeof (struct rgw_client)) );
245        memset(tmp, 0, sizeof(struct rgw_client));
246        fd_list_init(&tmp->chain, NULL);
247       
248        /* Initialize the duplicate list info */
249        for (i=0; i<=1; i++) {
250                CHECK_POSIX( pthread_mutex_init(&tmp->dupl_info[i].dupl_lock, NULL) );
251                fd_list_init(&tmp->dupl_info[i].dupl_by_id, NULL);
252                fd_list_init(&tmp->dupl_info[i].dupl_by_time, NULL);
253        }
254        tmp->type = type;
255       
256        if (loc) {
257                tmp->is_local = 1;
258        } else {
259                /* Copy the fqdn */
260                tmp->fqdn = fqdn;
261                tmp->fqdn_len = fqdn_len;
262
263                /* Find an appropriate realm */
264                tmp->realm = strchr(fqdn, '.');
265                if (tmp->realm) {
266                        tmp->realm += 1;
267                        tmp->realm_len = tmp->fqdn_len - (tmp->realm - fqdn);
268                }
269                if ((!tmp->realm) || (*tmp->realm == '\0')) { /* in case the fqdn was "localhost." for example, if it is possible... */
270                        tmp->realm = fd_g_config->cnf_diamrlm;
271                        tmp->realm_len = fd_g_config->cnf_diamrlm_len;
272                }
273        }
274       
275        /* move the sa info reference */
276        tmp->sa = *ip_port;
277        *ip_port = NULL;
278       
279        /* move the key material */
280        tmp->key.data = *key;
281        tmp->key.len = keylen;
282        *key = NULL;
283       
284        /* Done! */
285        *res = tmp;
286        return 0;
287}
288
289/* Decrease refcount on a client; the lock must be held when this function is called. */
290static void client_unlink(struct rgw_client * client)
291{
292        client->refcount -= 1;
293       
294        if (client->refcount <= 0) {
295                int idx;
296                /* to be sure: the refcount should be 0 only when client_fini is called */
297                ASSERT( FD_IS_LIST_EMPTY(&client->chain) );
298               
299                /* Free the data */
300                for (idx = 0; idx < client->aliases_nb; idx++)
301                        free(client->aliases[idx].name);
302                free(client->aliases);
303                free(client->fqdn);
304                free(client->sa);
305                free(client->key.data);
306               
307                /* Free the duplicate info */
308                for (idx=0; idx <= 1; idx++){
309                        CHECK_POSIX_DO( pthread_mutex_lock( &client->dupl_info[idx].dupl_lock ), /* continue */ );
310                       
311                        while (!FD_IS_LIST_EMPTY(&client->dupl_info[idx].dupl_by_id)) {
312                                struct req_info * r = (struct req_info *)(client->dupl_info[idx].dupl_by_id.next->o);
313                                fd_list_unlink( &r->by_id );
314                                fd_list_unlink( &r->by_time );
315                                dupl_free_req_info(r);
316                        }
317                       
318                        CHECK_POSIX_DO( pthread_mutex_unlock( &client->dupl_info[idx].dupl_lock ), /* continue */ );
319
320                }
321               
322                free(client);
323        }
324}
325
326
327/* Macro to avoid duplicating the code in the next function */
328#define client_search_family( _family_ )                                                                                                \
329                case AF_INET##_family_: {                                                                                               \
330                        struct sockaddr_in##_family_ * sin##_family_ = (struct sockaddr_in##_family_ *)ip_port;                         \
331                        for (ref = cli_ip##_family_.next; ref != &cli_ip##_family_; ref = ref->next) {                                  \
332                                cmp = memcmp(&sin##_family_->sin##_family_##_addr,                                                      \
333                                             &((struct rgw_client *)ref)->sin##_family_->sin##_family_##_addr,                          \
334                                             sizeof(struct in##_family_##_addr));                                                       \
335                                if (cmp > 0) continue; /* search further in the list */                                                 \
336                                if (cmp < 0) break; /* this IP is not in the list */                                                    \
337                                /* Now compare the ports as follow: */                                                                  \
338                                     /* If the ip_port we are searching does not contain a port, just return the first match result */  \
339                                if ( (sin##_family_->sin##_family_##_port == 0)                                                         \
340                                     /* If the entry in the list does not contain a port, return it as a match */                       \
341                                  || (((struct rgw_client *)ref)->sin##_family_->sin##_family_##_port == 0)                             \
342                                     /* If both ports are equal, it is a match */                                                       \
343                                  || (sin##_family_->sin##_family_##_port ==                                                            \
344                                                ((struct rgw_client *)ref)->sin##_family_->sin##_family_##_port)) {                     \
345                                        *res = (struct rgw_client *)ref;                                                                \
346                                        return EEXIST;                                                                                  \
347                                }                                                                                                       \
348                                /* Otherwise, the list is ordered by port value (byte order does not matter */                          \
349                                if (sin##_family_->sin##_family_##_port                                                                 \
350                                        > ((struct rgw_client *)ref)->sin##_family_->sin##_family_##_port) continue;                    \
351                                else break;                                                                                             \
352                        }                                                                                                               \
353                        *res = (struct rgw_client *)(ref->prev);                                                                        \
354                        return ENOENT;                                                                                                  \
355                }
356/* Function to look for an existing rgw_client, or the previous element.
357   The cli_rwl must be held for reading (at least) when calling this function.
358   Returns ENOENT if the matching client does not exist, and res points to the previous element in the list.
359   Returns EEXIST if the matching client is found, and res points to this element.
360   Returns other error code on other error. */
361static int client_search(struct rgw_client ** res, struct sockaddr * ip_port )
362{
363        int cmp;
364        struct fd_list *ref = NULL;
365       
366        CHECK_PARAMS(res && ip_port);
367       
368        switch (ip_port->sa_family) {
369                client_search_family()
370                                break;
371               
372                client_search_family( 6 )
373                                break;
374        }
375       
376        /* We're never supposed to reach this point */
377        ASSERT(0);
378        return EINVAL;
379}
380
381int rgw_clients_getkey(struct rgw_client * cli, unsigned char **key, size_t *key_len)
382{
383        CHECK_PARAMS( cli && key && key_len );
384        *key = cli->key.data;
385        *key_len = cli->key.len;
386        return 0;
387}
388
389int rgw_clients_gettype(struct rgw_client * cli, enum rgw_cli_type *type)
390{
391        CHECK_PARAMS( cli && type );
392        *type = cli->type;
393        return 0;
394}
395
396
397int rgw_clients_search(struct sockaddr * ip_port, struct rgw_client ** ref)
398{
399        int ret = 0;
400       
401        TRACE_ENTRY("%p %p", ip_port, ref);
402       
403        CHECK_PARAMS(ip_port && ref);
404       
405        CHECK_POSIX( pthread_rwlock_rdlock(&cli_rwl) );
406
407        ret = client_search(ref, ip_port);
408        if (ret == EEXIST) {
409                (*ref)->refcount ++;
410                ret = 0;
411        } else {
412                *ref = NULL;
413        }
414       
415        CHECK_POSIX( pthread_rwlock_unlock(&cli_rwl) );
416       
417        return ret;
418}
419
420int rgw_clients_check_dup(struct rgw_radius_msg_meta **msg, struct rgw_client *cli)
421{
422        int p, dup = 0;
423        struct fd_list * li;
424        struct req_info * r;
425       
426        TRACE_ENTRY("%p %p", msg, cli);
427       
428        CHECK_PARAMS( msg && cli );
429       
430        if ((*msg)->serv_type == RGW_PLG_TYPE_AUTH)
431                p = 0;
432        else
433                p = 1;
434       
435        CHECK_POSIX( pthread_mutex_lock( &cli->dupl_info[p].dupl_lock ) );
436       
437        /* Search if we have this message in our list */
438        for (li = cli->dupl_info[p].dupl_by_id.next; li != &cli->dupl_info[p].dupl_by_id; li = li->next) {
439                int cmp = 0;
440                r = (struct req_info *)(li->o);
441                if (r->id < (*msg)->radius.hdr->identifier)
442                        continue;
443                if (r->id > (*msg)->radius.hdr->identifier)
444                        break;
445                if (r->port < (*msg)->port)
446                        continue;
447                if (r->port > (*msg)->port)
448                        break;
449                cmp = memcmp(&r->auth[0], &(*msg)->radius.hdr->authenticator[0], 16);
450                if (cmp < 0)
451                        continue;
452                if (cmp > 0)
453                        break;
454                dup = 1;
455                break;
456        }
457       
458        if (dup) {
459                time_t now = time(NULL);
460                r->nbdup += 1;
461                TRACE_DEBUG(INFO, "Received duplicated RADIUS message (id: %02hhx, port: %hu, dup #%d, previously seen %d secs ago).", 
462                                r->id, ntohs(r->port), r->nbdup, now - r->received);
463               
464                if (r->ans) {
465                        /* Resend the answer */
466                        CHECK_FCT_DO( rgw_servers_send((*msg)->serv_type, r->ans->buf, r->ans->buf_used, cli->sa, r->port),  );
467                       
468                        /* Should we delete 'r' so that a further duplicate will again be converted to Diameter? */
469                }
470               
471                /* Update the timestamp */
472                r->received = now;
473                fd_list_unlink(&r->by_time);
474                fd_list_insert_before(&cli->dupl_info[p].dupl_by_time, &r->by_time); /* Move as last entry, since it is the most recent */
475               
476                /* Delete the request message */
477                rgw_msg_free(msg);
478               
479        } else {
480                /* The message was not a duplicate, we save it */
481                /* li currently points the the next entry in list_by_id */
482                CHECK_MALLOC_DO( r= dupl_new_req_info(*msg), { CHECK_POSIX_DO(pthread_mutex_unlock( &cli->dupl_info[p].dupl_lock ), ); return ENOMEM; } );
483                fd_list_insert_before(li, &r->by_id);
484                fd_list_insert_before(&cli->dupl_info[p].dupl_by_time, &r->by_time); /* it is the most recent */
485        }
486               
487        CHECK_POSIX( pthread_mutex_unlock( &cli->dupl_info[p].dupl_lock ) );
488       
489        return 0;
490}
491
492/* Check if the message has a valid authenticator, and update the meta-data accordingly */
493int rgw_clients_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth)
494{
495        unsigned char * key;
496        size_t keylen;
497        int count;
498       
499        TRACE_ENTRY("%p %p %p", msg, cli, req_auth);
500       
501        CHECK_PARAMS(msg && cli);
502       
503        CHECK_FCT(rgw_clients_getkey(cli, &key, &keylen));
504       
505        count = radius_msg_count_attr(&msg->radius, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 0);
506        if (count > 1) {
507                TRACE_DEBUG(INFO, "Too many Message-Authenticator attributes (%d), discarding message.", count);
508                return EINVAL;
509        }
510        if (count == 0) {
511                TRACE_DEBUG(FULL, "Message does not contain a Message-Authenticator attributes.");
512                msg->valid_mac = 0;
513        } else {
514                if (radius_msg_verify_msg_auth( &msg->radius, key, keylen, req_auth )) {
515                        TRACE_DEBUG(INFO, "Invalid Message-Authenticator received, discarding message.");
516                        return EINVAL;
517                }
518                msg->valid_mac = 1;
519        }
520       
521        return 0;
522}
523
524static struct dict_object * cache_orig_host = NULL;
525static struct dict_object * cache_orig_realm = NULL;
526static struct dict_object * cache_route_record = NULL;
527
528int rgw_clients_init(void)
529{
530        TRACE_ENTRY();
531        CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &cache_orig_host, ENOENT) );
532        CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &cache_orig_realm, ENOENT) );
533        CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &cache_route_record, ENOENT) );
534       
535        /* Create the thread that will purge old RADIUS duplicates */
536        CHECK_POSIX( pthread_create( &dbt_expire, NULL, dupl_th, NULL) );
537       
538        return 0;
539}
540
541
542/* The following function checks if a RADIUS message contains a valid NAS identifier, and initializes an empty Diameter
543 message with the appropriate routing information */
544/* Check that the NAS-IP-Adress or NAS-Identifier is coherent with the IP the packet was received from */
545/* Also update the client list of aliases if needed */
546int rgw_clients_create_origin(struct rgw_radius_msg_meta *msg, struct rgw_client * cli, struct msg ** diam)
547{
548        int idx;
549        int valid_nas_info = 0;
550        struct radius_attr_hdr *nas_ip = NULL, *nas_ip6 = NULL, *nas_id = NULL;
551        size_t nas_id_len;
552        char * oh_str = NULL; size_t oh_strlen = 0; int oh_free = 0;
553        char * or_str = NULL; size_t or_strlen = 0;
554        char * rr_str = NULL; size_t rr_strlen = 0;
555        char buf[REVERSE_DNS_SIZE_MAX]; /* to store DNS lookups results */
556       
557        struct avp *avp = NULL;
558        union avp_value avp_val;
559       
560        TRACE_ENTRY("%p %p %p", msg, cli, diam);
561        CHECK_PARAMS(msg && cli && diam && (*diam == NULL));
562       
563        /* Find the relevant attributes, if any */
564        for (idx = 0; idx < msg->radius.attr_used; idx++) {
565                struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[idx]);
566                size_t attr_len = attr->length - sizeof(struct radius_attr_hdr);
567               
568                if ((attr->type == RADIUS_ATTR_NAS_IP_ADDRESS) && (attr_len = 4)) {
569                        nas_ip = attr;
570                        continue;
571                }
572                       
573                if ((attr->type == RADIUS_ATTR_NAS_IDENTIFIER) && (attr_len > 0)) {
574                        nas_id = attr;
575                        nas_id_len = attr_len;
576                        continue;
577                }
578                       
579                if ((attr->type == RADIUS_ATTR_NAS_IPV6_ADDRESS) && (attr_len = 16)) {
580                        nas_ip6 = attr;
581                        continue;
582                }
583        }
584               
585        if (!nas_ip && !nas_ip6 && !nas_id) {
586                TRACE_DEBUG(FULL, "The message does not contain any NAS identification attribute.");
587               
588                /* Get information on this peer */
589                CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
590               
591                goto diameter;
592        }
593       
594        /* Check if the message was received from the IP in NAS-IP-Address attribute */
595        if (nas_ip && (cli->sa->sa_family == AF_INET) && !memcmp(nas_ip+1, &cli->sin->sin_addr, sizeof(struct in_addr))) {
596                TRACE_DEBUG(FULL, "NAS-IP-Address contains the same address as the message was received from.");
597                valid_nas_info |= 1;
598        }
599        if (nas_ip6 && (cli->sa->sa_family == AF_INET6) && !memcmp(nas_ip6+1, &cli->sin6->sin6_addr, sizeof(struct in6_addr))) {
600                TRACE_DEBUG(FULL, "NAS-IPv6-Address contains the same address as the message was received from.");
601                valid_nas_info |= 2;
602        }
603       
604       
605        /*
606                        In RADIUS it would be possible for a rogue NAS to forge the NAS-IP-
607                        Address attribute value.  Diameter/RADIUS translation agents MUST
608                        check a received NAS-IP-Address or NAS-IPv6-Address attribute against
609                        the source address of the RADIUS packet.  If they do not match and
610                        the Diameter/RADIUS translation agent does not know whether the
611                        packet was sent by a RADIUS proxy or NAS (e.g., no Proxy-State
612                        attribute), then by default it is assumed that the source address
613                        corresponds to a RADIUS proxy, and that the NAS Address is behind
614                        that proxy, potentially with some additional RADIUS proxies in
615                        between.  The Diameter/RADIUS translation agent MUST insert entries
616                        in the Route-Record AVP corresponding to the apparent route.  This
617                        implies doing a reverse lookup on the source address and NAS-IP-
618                        Address or NAS-IPv6-Address attributes to determine the corresponding
619                        FQDNs.
620
621                        If the source address and the NAS-IP-Address or NAS-IPv6-Address do
622                        not match, and the Diameter/RADIUS translation agent knows that it is
623                        talking directly to the NAS (e.g., there are no RADIUS proxies
624                        between it and the NAS), then the error should be logged, and the
625                        packet MUST be discarded.
626
627                        Diameter agents and servers MUST check whether the NAS-IP-Address AVP
628                        corresponds to an entry in the Route-Record AVP.  This is done by
629                        doing a reverse lookup (PTR RR) for the NAS-IP-Address to retrieve
630                        the corresponding FQDN, and by checking for a match with the Route-
631                        Record AVP.  If no match is found, then an error is logged, but no
632                        other action is taken.
633        */
634        if (nas_ip || nas_ip6) {
635                if (!valid_nas_info) {
636                        if ((!cli->is_local) && (cli->type == RGW_CLI_NAS)) {
637                                TRACE_DEBUG(INFO, "Message received with a NAS-IP-Address or NAS-IPv6-Address different \nfrom the sender's. Please configure as Proxy if this is expected.\n Message discarded.");
638                                return EINVAL;
639                        } else {
640                                int ret;
641                                sSS ss;
642                                /* the peer is configured as a proxy, or running on localhost, so accept the message */
643                               
644                                /* In that case, the cli will be stored as Route-Record and the NAS-IP-Address as origin */
645                                if (!cli->is_local) {
646                                        rr_str = cli->fqdn;
647                                        rr_strlen = cli->fqdn_len;
648                                }
649                               
650                                /* We must DNS-reverse the NAS-IP*-Address */
651                                memset(&ss, 0 , sizeof(sSS));
652                                if (nas_ip) {
653                                        sSA4 * sin = (sSA4 *)&ss;
654                                        sin->sin_family = AF_INET;
655                                        memcpy(&sin->sin_addr, nas_ip + 1, sizeof(struct in_addr));
656                                } else {
657                                        sSA6 * sin6 = (sSA6 *)&ss;
658                                        sin6->sin6_family = AF_INET6;
659                                        memcpy(&sin6->sin6_addr, nas_ip6 + 1, sizeof(struct in6_addr));
660                                }
661                                CHECK_SYS_DO( getnameinfo( (sSA *)&ss, sSAlen(&ss), &buf[0], sizeof(buf), NULL, 0, NI_NAMEREQD),
662                                        {
663                                                if (cli->is_local) {
664                                                        CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
665                                                        goto diameter;
666                                                }
667                                               
668                                                TRACE_DEBUG(INFO, "The NAS-IP*-Address cannot be DNS reversed in order to create the Origin-Host AVP; rejecting the message (translation is impossible).");
669                                                return EINVAL;
670                                        } );
671                                       
672                                oh_str = &buf[0];
673                                CHECK_FCT_DO( ret = fd_os_validate_DiameterIdentity(&oh_str, &oh_strlen, 1),
674                                        {
675                                                if (cli->is_local) {
676                                                        CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
677                                                        goto diameter;
678                                                }
679                                               
680                                                TRACE_DEBUG(INFO, "Unable to use resolved client name '%s' as DiameterIdentity: %s", buf, strerror(ret));
681                                                return ret;
682                                        } );
683                                oh_free = 1;
684                               
685                                or_str = strchr(oh_str, '.');
686                                if (or_str) {
687                                        or_str ++; /* move after the first dot */
688                                        if (*or_str == '\0')
689                                                or_str = NULL; /* Discard this realm, we will use the local realm later */
690                                        else
691                                                or_strlen = oh_strlen - (or_str - oh_str);
692                                }
693                        }
694                } else {
695                        /* The attribute matches the source address, just use this in origin-host */
696                        CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
697                }
698               
699                goto diameter; /* we ignore the nas_id in that case */
700        }
701       
702        /* We don't have a NAS-IP*-Address attribute if we are here */
703        if (cli->is_local) {
704                /* Simple: we use our own configuration */
705                CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
706                goto diameter;
707        }
708       
709        /* At this point, we only have nas_id, and the client is not local */
710        ASSERT(nas_id);
711       
712        {
713                int found, ret;
714                struct addrinfo hint, *res, *ptr;
715               
716                /*
717                        In RADIUS it would be possible for a rogue NAS to forge the NAS-
718                        Identifier attribute.  Diameter/RADIUS translation agents SHOULD
719                        attempt to check a received NAS-Identifier attribute against the
720                        source address of the RADIUS packet, by doing an A/AAAA RR query.  If
721                        the NAS-Identifier attribute contains an FQDN, then such a query
722                        would resolve to an IP address matching the source address.  However,
723                        the NAS-Identifier attribute is not required to contain an FQDN, so
724                        such a query could fail.  If it fails, an error should be logged, but
725                        no action should be taken, other than a reverse lookup on the source
726                        address and insert the resulting FQDN into the Route-Record AVP.
727
728                        Diameter agents and servers SHOULD check whether a NAS-Identifier AVP
729                        corresponds to an entry in the Route-Record AVP.  If no match is
730                        found, then an error is logged, but no other action is taken.
731                */
732               
733                /* first, check if the nas_id is the fqdn of the peer or a known alias */
734                if (!fd_os_almostcasecmp(nas_id + 1, nas_id_len, 
735                                                cli->fqdn, cli->fqdn_len)) {
736                        TRACE_DEBUG(FULL, "NAS-Identifier contains the fqdn of the client");
737                        found = 1;
738                } else {
739                        for (idx = 0; idx < cli->aliases_nb; idx++) {
740                                if (!fd_os_cmp(nas_id + 1, nas_id_len, 
741                                                cli->aliases[idx].name, cli->aliases[idx].len)) {
742                                        TRACE_DEBUG(FULL, "NAS-Identifier valid value found in the cache");
743                                        found = 1;
744                                        break;
745                                }
746                        }
747                }
748               
749                if (found) {
750                        /* The NAS-Identifier matches the source IP */
751                        CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
752
753                        goto diameter;
754                }
755               
756                /* Attempt DNS resolution of the identifier */
757                ASSERT( nas_id_len < sizeof(buf) );
758                memcpy(buf, nas_id + 1, nas_id_len);
759                buf[nas_id->length - sizeof(struct radius_attr_hdr)] = '\0';
760               
761                /* Now check if this alias is valid for this peer */
762                memset(&hint, 0, sizeof(hint));
763                hint.ai_flags  = AI_CANONNAME;
764                ret = getaddrinfo(buf, NULL, &hint, &res);
765                if (ret == 0) {
766                        strncpy(buf, res->ai_canonname, sizeof(buf));
767                        /* The name was resolved correctly, does it match the IP of the client? */
768                        for (ptr = res; ptr != NULL; ptr = ptr->ai_next) {
769                                if (cli->sa->sa_family != ptr->ai_family)
770                                        continue;
771                                if (memcmp(cli->sa, ptr->ai_addr, sSAlen(cli->sa)))
772                                        continue;
773                               
774                                found = 1;
775                                break;
776                        }
777                        freeaddrinfo(res);
778                       
779                        if (!found) {
780                                if (cli->type == RGW_CLI_NAS) {
781                                        TRACE_DEBUG(INFO, "The NAS-Identifier value '%.*s' resolves to a different IP than the client's, discarding the message. \nConfigure this client as a Proxy if this message should be valid.", 
782                                                nas_id_len, nas_id + 1);
783                                        return EINVAL;
784                                } else {
785                                        /* This identifier matches a different IP, assume it is a proxied message */
786                                        if (!cli->is_local) {
787                                                rr_str = cli->fqdn;
788                                                rr_strlen = cli->fqdn_len;
789                                        }
790                                        oh_str = &buf[0]; /* The canonname resolved */
791                                        oh_strlen = 0;
792                                        CHECK_FCT_DO( ret = fd_os_validate_DiameterIdentity(&oh_str, &oh_strlen, 1),
793                                                {
794                                                        TRACE_DEBUG(INFO, "Unable to use resolved client name '%s' as DiameterIdentity: %s", buf, strerror(ret));
795                                                        return ret;
796                                                } );
797                                        oh_free = 1;
798                                        or_str = strchr(oh_str, '.');
799                                        if (or_str) {
800                                                or_str ++; /* move after the first dot */
801                                                if (*or_str == '\0')
802                                                        or_str = NULL; /* Discard this realm, we will use the local realm later */
803                                                else
804                                                        or_strlen = oh_strlen - (or_str - oh_str);
805                                        }
806                                }
807                        } else {
808                                /* It is a valid alias, save it */
809                                CHECK_MALLOC( cli->aliases = realloc(cli->aliases, (cli->aliases_nb + 1) * sizeof(cli->aliases[0])) );
810                               
811                                CHECK_MALLOC( cli->aliases[cli->aliases_nb + 1].name = os0dup(nas_id + 1, nas_id_len ) );
812                                cli->aliases[cli->aliases_nb + 1].len = nas_id_len;
813
814                                cli->aliases_nb ++;
815                                TRACE_DEBUG(FULL, "Saved valid alias for client: '%.*s' -> '%s'", nas_id_len, nas_id + 1, cli->fqdn);
816                                CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
817                        }
818                } else {
819                        /* Error resolving the name */
820                        TRACE_DEBUG(INFO, "NAS-Identifier '%s' cannot be resolved: %s. Ignoring...", buf, gai_strerror(ret));
821                        /* Assume this is a valid identifier for the client */
822                        CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
823                }
824        }
825       
826        /* Now, let's create the empty Diameter message with Origin-Host, -Realm, and Route-Record if needed. */
827diameter:
828        ASSERT(oh_str); /* If it is not defined here, there is a bug... */
829        if (!or_str) {
830                or_str = fd_g_config->cnf_diamrlm; /* Use local realm in that case */
831                or_strlen = fd_g_config->cnf_diamrlm_len;
832        }
833       
834        /* Create an empty Diameter message so that extensions can store their AVPs */
835        CHECK_FCT(  fd_msg_new ( NULL, MSGFL_ALLOC_ETEID, diam )  );
836       
837        /* Add the Origin-Host as next AVP */
838        CHECK_FCT( fd_msg_avp_new ( cache_orig_host, 0, &avp ) );
839        memset(&avp_val, 0, sizeof(avp_val));
840        avp_val.os.data = (unsigned char *)oh_str;
841        avp_val.os.len = oh_strlen;
842        CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
843        CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
844       
845        /* Add the Origin-Realm as next AVP */
846        CHECK_FCT( fd_msg_avp_new ( cache_orig_realm, 0, &avp ) );
847        memset(&avp_val, 0, sizeof(avp_val));
848        avp_val.os.data = (unsigned char *)or_str;
849        avp_val.os.len = or_strlen;
850        CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
851        CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
852       
853        if (rr_str) {
854                CHECK_FCT( fd_msg_avp_new ( cache_route_record, 0, &avp ) );
855                memset(&avp_val, 0, sizeof(avp_val));
856                avp_val.os.data = (unsigned char *)rr_str;
857                avp_val.os.len = rr_strlen;
858                CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
859                CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
860        }
861       
862        if (oh_free)
863                free(oh_str);
864       
865        /* Done! */
866        return 0;
867}
868
869int rgw_clients_get_origin(struct rgw_client *cli, DiamId_t *fqdn, size_t *fqdnlen, DiamId_t *realm, size_t *realmlen)
870{
871        TRACE_ENTRY("%p %p %p %p %p", cli, fqdn, fqdnlen, realm, realmlen);
872        CHECK_PARAMS(cli && fqdn && fqdnlen);
873       
874        if (cli->is_local) {
875                *fqdn = fd_g_config->cnf_diamid;
876                *fqdnlen = fd_g_config->cnf_diamid_len;
877                if (realm)
878                        *realm= fd_g_config->cnf_diamrlm;
879                if (realmlen)
880                        *realmlen= fd_g_config->cnf_diamrlm_len;
881        } else {
882                *fqdn = cli->fqdn;
883                *fqdnlen = cli->fqdn_len;
884                if (realm)
885                        *realm= cli->realm;
886                if (realmlen)
887                        *realmlen= cli->realm_len;
888        }
889               
890        return 0;
891}
892
893char * rgw_clients_id(struct rgw_client *cli)
894{
895        return cli->is_local ? "(local)" : cli->fqdn;
896}
897
898
899void rgw_clients_dispose(struct rgw_client ** ref)
900{
901        TRACE_ENTRY("%p", ref);
902        CHECK_PARAMS_DO(ref, return);
903       
904        CHECK_POSIX_DO( pthread_rwlock_wrlock(&cli_rwl),  );
905        client_unlink(*ref);
906        *ref = NULL;
907        CHECK_POSIX_DO( pthread_rwlock_unlock(&cli_rwl), );
908}
909
910int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type )
911{
912        struct rgw_client * prev = NULL, *new = NULL;
913        int ret;
914       
915        TRACE_ENTRY("%p %p %lu", ip_port, key, keylen);
916       
917        CHECK_PARAMS( ip_port && key && *key && keylen );
918        CHECK_PARAMS( (ip_port->sa_family == AF_INET) || (ip_port->sa_family == AF_INET6) );
919        CHECK_PARAMS( (type == RGW_CLI_NAS) || (type == RGW_CLI_PXY) );
920       
921        /* Dump the entry in debug mode */
922        if (TRACE_BOOL(FULL + 1 )) {
923                TRACE_DEBUG(FULL, "Adding %s:", (type == RGW_CLI_NAS) ? "NAS" : "PROXY"  );
924                TRACE_DEBUG_sSA(FULL,    "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "" );
925                TRACE_DEBUG_BUFFER(FULL, "\tKey: [", *key, keylen, "]" );
926        }
927       
928        /* Lock the lists */
929        CHECK_POSIX( pthread_rwlock_wrlock(&cli_rwl) );
930       
931        /* Check if the same entry does not already exist */
932        ret = client_search(&prev, ip_port );
933        if (ret == ENOENT) {
934                /* No duplicate found, Ok to add */
935                CHECK_FCT_DO( ret = client_create( &new, &ip_port, key, keylen, type ), goto end );
936                fd_list_insert_after(&prev->chain, &new->chain);
937                new->refcount++;
938                ret = 0;
939                goto end;
940        }
941       
942        if (ret == EEXIST) {
943                /* Check if the key is the same, then skip or return an error */
944                if ((keylen == prev->key.len ) && ( ! memcmp(*key, prev->key.data, keylen) ) && (type == prev->type)) {
945                        TRACE_DEBUG(INFO, "Skipping duplicate client description");
946                        ret = 0;
947                        goto end;
948                }
949               
950                fd_log_debug("ERROR: Conflicting RADIUS clients descriptions!\n");
951                TRACE_DEBUG(NONE, "Previous entry: %s", (prev->type == RGW_CLI_NAS) ? "NAS" : "PROXY");
952                TRACE_DEBUG_sSA(NONE,    "\tIP : ", prev->sa, NI_NUMERICHOST | NI_NUMERICSERV, "" );
953                TRACE_DEBUG_BUFFER(NONE, "\tKey: [", prev->key.data, prev->key.len, "]" );
954                TRACE_DEBUG(NONE, "Conflicting entry: %s", (type == RGW_CLI_NAS) ? "NAS" : "PROXY");
955                TRACE_DEBUG_sSA(NONE,    "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "" );
956                TRACE_DEBUG_BUFFER(NONE, "\tKey: [", *key, keylen, "]" );
957        }
958end:
959        /* release the lists */
960        CHECK_POSIX( pthread_rwlock_unlock(&cli_rwl) );
961       
962        return ret;
963}
964
965static void dump_cli_list(struct fd_list *senti)
966{
967        struct rgw_client * client = NULL;
968        struct fd_list *ref = NULL;
969       
970        for (ref = senti->next; ref != senti; ref = ref->next) {
971                client = (struct rgw_client *)ref;
972                TRACE_DEBUG_sSA(NONE,    "  - ", client->sa, NI_NUMERICHOST | NI_NUMERICSERV, (client->type == RGW_CLI_NAS) ? "" : " [PROXY]" );
973        }
974}
975
976void rgw_clients_dump(void)
977{
978        if ( ! TRACE_BOOL(FULL) )
979                return;
980       
981        CHECK_POSIX_DO( pthread_rwlock_rdlock(&cli_rwl), /* ignore error */ );
982       
983        if (!FD_IS_LIST_EMPTY(&cli_ip))
984                fd_log_debug(" RADIUS IP clients list:\n");
985        dump_cli_list(&cli_ip);
986               
987        if (!FD_IS_LIST_EMPTY(&cli_ip6))
988                fd_log_debug(" RADIUS IPv6 clients list:\n");
989        dump_cli_list(&cli_ip6);
990               
991        CHECK_POSIX_DO( pthread_rwlock_unlock(&cli_rwl), /* ignore error */ );
992}
993
994void rgw_clients_fini(void)
995{
996        struct fd_list * client;
997       
998        TRACE_ENTRY();
999       
1000        CHECK_POSIX_DO( pthread_rwlock_wrlock(&cli_rwl), /* ignore error */ );
1001       
1002        CHECK_FCT_DO( fd_thr_term(&dbt_expire), /* continue */ );
1003
1004        /* empty the lists */
1005        while ( ! FD_IS_LIST_EMPTY(&cli_ip) ) {
1006                client = cli_ip.next;
1007                fd_list_unlink(client);
1008                client_unlink((struct rgw_client *)client);
1009        }
1010        while (! FD_IS_LIST_EMPTY(&cli_ip6)) {
1011                client = cli_ip6.next;
1012                fd_list_unlink(client);
1013                client_unlink((struct rgw_client *)client);
1014        }
1015       
1016        CHECK_POSIX_DO( pthread_rwlock_unlock(&cli_rwl), /* ignore error */ );
1017       
1018}
1019
1020int rgw_client_finish_send(struct radius_msg ** msg, struct rgw_radius_msg_meta * req, struct rgw_client * cli)
1021{
1022        int p;
1023        struct fd_list * li;
1024       
1025        TRACE_ENTRY("%p %p %p", msg, req, cli);
1026        CHECK_PARAMS( msg && *msg && cli );
1027       
1028        if (!req) {
1029                /* We don't support this case yet */
1030                ASSERT(0);
1031                return ENOTSUP;
1032        }
1033       
1034        /* Add all the Proxy-States back in the message */
1035        for (p = 0; p < req->ps_nb; p++) {
1036                struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(req->radius.buf + req->radius.attr_pos[req->ps_first + p]);
1037               
1038                if (radius_msg_add_attr_to_array(*msg, attr)) {
1039                        TRACE_DEBUG(INFO, "Error in radius_msg_add_attr_to_array, ENOMEM");
1040                        radius_msg_free(*msg);
1041                        free(*msg);
1042                        *msg = NULL;
1043                        return ENOMEM;
1044                }
1045        }
1046       
1047        /* Add the Message-Authenticator if needed, and other final tasks */
1048        if (radius_msg_finish_srv(*msg, cli->key.data, cli->key.len, req->radius.hdr->authenticator)) {
1049                TRACE_DEBUG(INFO, "An error occurred while preparing the RADIUS answer");
1050                radius_msg_free(*msg);
1051                free(*msg);
1052                *msg = NULL;
1053                return EINVAL;
1054        }
1055       
1056        /* Debug */
1057        TRACE_DEBUG(FULL, "RADIUS message ready for sending:");
1058        rgw_msg_dump((struct rgw_radius_msg_meta *)*msg, 0);
1059
1060        /* Send the message */
1061        CHECK_FCT( rgw_servers_send(req->serv_type, (*msg)->buf, (*msg)->buf_used, cli->sa, req->port) );
1062
1063        /* update the duplicate cache */
1064        if (req->serv_type == RGW_PLG_TYPE_AUTH)
1065                p = 0;
1066        else
1067                p = 1;
1068       
1069        CHECK_POSIX( pthread_mutex_lock( &cli->dupl_info[p].dupl_lock ) );
1070       
1071        /* Search this message in our list */
1072        for (li = cli->dupl_info[p].dupl_by_id.next; li != &cli->dupl_info[p].dupl_by_id; li = li->next) {
1073                int cmp = 0;
1074                struct req_info * r = (struct req_info *)(li->o);
1075                if (r->id < req->radius.hdr->identifier)
1076                        continue;
1077                if (r->id > req->radius.hdr->identifier)
1078                        break;
1079                if (r->port < req->port)
1080                        continue;
1081                if (r->port > req->port)
1082                        break;
1083                cmp = memcmp(&r->auth[0], &req->radius.hdr->authenticator[0], 16);
1084                if (cmp < 0)
1085                        continue;
1086                if (cmp > 0)
1087                        break;
1088               
1089                /* We have the request in our duplicate cache */
1090                /* This should not happen, but just in case... */
1091                if (r->ans) {
1092                        radius_msg_free(r->ans);
1093                        free(r->ans);
1094                }
1095               
1096                /* Now save the message */
1097                r->ans = *msg;
1098                *msg = NULL;
1099               
1100                /* Update the timestamp */
1101                {
1102                        time_t now = time(NULL);
1103                        r->received = now;
1104                        fd_list_unlink(&r->by_time); /* Move as last entry, since it is the most recent */
1105                        fd_list_insert_before(&cli->dupl_info[p].dupl_by_time, &r->by_time);
1106                }
1107                break;
1108        }
1109               
1110        CHECK_POSIX( pthread_mutex_unlock( &cli->dupl_info[p].dupl_lock ) );
1111       
1112        /* If we have not found the request in our list, the purge time is probably too small */
1113        if (*msg) {
1114                TODO("Augment the purge time...");
1115                /* If we receive the duplicate request again, it will be converted to Diameter... */
1116                radius_msg_free(*msg);
1117                free(*msg);
1118                *msg = NULL;
1119        }
1120       
1121        /* Finished */
1122        return 0;
1123}
1124
1125/* Call this function when a RADIUS request has explicitely no answer (mainly accounting) so
1126that we purge the duplicate cache and allow further message to be translated again.
1127This is useful for example when a temporary error occurred in Diameter (like UNABLE_TO_DELIVER) */
1128int rgw_client_finish_nosend(struct rgw_radius_msg_meta * req, struct rgw_client * cli)
1129{
1130        int p;
1131        struct fd_list * li;
1132       
1133        TRACE_ENTRY("%p %p", req, cli);
1134        CHECK_PARAMS( req && cli );
1135       
1136        /* update the duplicate cache */
1137        if (req->serv_type == RGW_PLG_TYPE_AUTH)
1138                p = 0;
1139        else
1140                p = 1;
1141       
1142        CHECK_POSIX( pthread_mutex_lock( &cli->dupl_info[p].dupl_lock ) );
1143       
1144        /* Search this message in our list */
1145        for (li = cli->dupl_info[p].dupl_by_id.next; li != &cli->dupl_info[p].dupl_by_id; li = li->next) {
1146                int cmp = 0;
1147                struct req_info * r = (struct req_info *)(li->o);
1148                if (r->id < req->radius.hdr->identifier)
1149                        continue;
1150                if (r->id > req->radius.hdr->identifier)
1151                        break;
1152                if (r->port < req->port)
1153                        continue;
1154                if (r->port > req->port)
1155                        break;
1156                cmp = memcmp(&r->auth[0], &req->radius.hdr->authenticator[0], 16);
1157                if (cmp < 0)
1158                        continue;
1159                if (cmp > 0)
1160                        break;
1161               
1162                /* We have the request in our duplicate cache, remove it */
1163                fd_list_unlink(&r->by_id);
1164                fd_list_unlink(&r->by_time);
1165                dupl_free_req_info(r);
1166                break;
1167        }
1168               
1169        CHECK_POSIX( pthread_mutex_unlock( &cli->dupl_info[p].dupl_lock ) );
1170       
1171        /* Finished */
1172        return 0;
1173}
1174
Note: See TracBrowser for help on using the repository browser.