Navigation


source: freeDiameter/extensions/app_radgw/rgw_clients.c @ 554:25440e53a48e

1.0.1-rc1
Last change on this file since 554:25440e53a48e was 554:25440e53a48e, checked in by Sebastien Decugis <sdecugis@nict.go.jp>, 12 years ago

Remove erroneous comment, the value was actually quite meaningless

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