Define realm_find2() where the argument is the realm name
[freeradius.git] / src / main / realms.c
1 /*
2  * realms.c     Realm handling code
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2007  The FreeRADIUS server project
21  * Copyright 2007  Alan DeKok <aland@deployingradius.com>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/rad_assert.h>
29
30 #include <sys/stat.h>
31
32 #include <ctype.h>
33 #include <fcntl.h>
34
35 #ifdef HAVE_REGEX_H
36 #include <regex.h>
37
38 /*
39  *  For POSIX Regular expressions.
40  *  (0) Means no extended regular expressions.
41  *  REG_EXTENDED means use extended regular expressions.
42  */
43 #ifndef REG_EXTENDED
44 #define REG_EXTENDED (0)
45 #endif
46
47 #ifndef REG_NOSUB
48 #define REG_NOSUB (0)
49 #endif
50
51 #ifndef REG_ICASE
52 #define REG_ICASE (0)
53 #endif
54 #endif
55
56 static rbtree_t *realms_byname = NULL;
57
58 #ifdef HAVE_REGEX_H
59 typedef struct realm_regex_t {
60         REALM   *realm;
61         struct realm_regex_t *next;
62 } realm_regex_t;
63
64 static realm_regex_t *realms_regex = NULL;
65
66 #endif /* HAVE_REGEX_H */
67
68 static rbtree_t *home_servers_byaddr = NULL;
69 static rbtree_t *home_servers_byname = NULL;
70
71 static rbtree_t *home_pools_byname = NULL;
72
73 typedef struct realm_config_t {
74         CONF_SECTION    *cs;
75         int             dead_time;
76         int             retry_count;
77         int             retry_delay;
78         int             fallback;
79         int             wake_all_if_all_dead;
80 } realm_config_t;
81
82 static realm_config_t *realm_config = NULL;
83
84 /*
85  *  Map the proxy server configuration parameters to variables.
86  */
87 static const CONF_PARSER proxy_config[] = {
88         { "retry_delay",  PW_TYPE_INTEGER,
89           offsetof(realm_config_t, retry_delay),
90           NULL, Stringify(RETRY_DELAY) },
91
92         { "retry_count",  PW_TYPE_INTEGER,
93           offsetof(realm_config_t, retry_count),
94           NULL, Stringify(RETRY_COUNT) },
95
96         { "default_fallback", PW_TYPE_BOOLEAN,
97           offsetof(realm_config_t, fallback),
98           NULL, "no" },
99
100         { "dead_time",    PW_TYPE_INTEGER, 
101           offsetof(realm_config_t, dead_time),
102           NULL, Stringify(DEAD_TIME) },
103
104         { "wake_all_if_all_dead", PW_TYPE_BOOLEAN,
105           offsetof(realm_config_t, wake_all_if_all_dead),
106           NULL, "no" },
107
108         { NULL, -1, 0, NULL, NULL }
109 };
110
111 static int realm_name_cmp(const void *one, const void *two)
112 {
113         const REALM *a = one;
114         const REALM *b = two;
115
116         return strcasecmp(a->name, b->name);
117 }
118
119
120 static int home_server_name_cmp(const void *one, const void *two)
121 {
122         const home_server *a = one;
123         const home_server *b = two;
124
125         if (a->type < b->type) return -1;
126         if (a->type > b->type) return +1;
127
128         return strcasecmp(a->name, b->name);
129 }
130
131 static int home_server_addr_cmp(const void *one, const void *two)
132 {
133         const home_server *a = one;
134         const home_server *b = two;
135
136         if (a->server && !b->server) return -1;
137         if (!a->server && b->server) return +1;
138         if (a->server && b->server) {
139                 int rcode = a->type - b->type;
140                 if (rcode != 0) return rcode;
141                 return strcmp(a->server, b->server);
142         }
143
144         if (a->port < b->port) return -1;
145         if (a->port > b->port) return +1;
146
147         return fr_ipaddr_cmp(&a->ipaddr, &b->ipaddr);
148 }
149
150
151 static int home_pool_name_cmp(const void *one, const void *two)
152 {
153         const home_pool_t *a = one;
154         const home_pool_t *b = two;
155
156         if (a->server_type < b->server_type) return -1;
157         if (a->server_type > b->server_type) return +1;
158
159         return strcasecmp(a->name, b->name);
160 }
161
162
163 /*
164  *      Xlat for %{home_server:foo}
165  */
166 static size_t xlat_home_server(UNUSED void *instance, REQUEST *request,
167                                char *fmt, char *out, size_t outlen,
168                                UNUSED RADIUS_ESCAPE_STRING func)
169 {
170         const char *value = NULL;
171         CONF_PAIR *cp;
172
173         if (!fmt || !out || (outlen < 1)) return 0;
174
175         if (!request || !request->home_server) {
176                 *out = '\0';
177                 return 0;
178         }
179
180         cp = cf_pair_find(request->home_server->cs, fmt);
181         if (!cp || !(value = cf_pair_value(cp))) {
182                 *out = '\0';
183                 return 0;
184         }
185         
186         strlcpy(out, value, outlen);
187
188         return strlen(out);
189 }
190
191
192 /*
193  *      Xlat for %{home_server_pool:foo}
194  */
195 static size_t xlat_server_pool(UNUSED void *instance, REQUEST *request,
196                                char *fmt, char *out, size_t outlen,
197                                UNUSED RADIUS_ESCAPE_STRING func)
198 {
199         const char *value = NULL;
200         CONF_PAIR *cp;
201
202         if (!fmt || !out || (outlen < 1)) return 0;
203
204         if (!request || !request->home_pool) {
205                 *out = '\0';
206                 return 0;
207         }
208
209         cp = cf_pair_find(request->home_pool->cs, fmt);
210         if (!cp || !(value = cf_pair_value(cp))) {
211                 *out = '\0';
212                 return 0;
213         }
214         
215         strlcpy(out, value, outlen);
216
217         return strlen(out);
218 }
219
220
221 void realms_free(void)
222 {
223         rbtree_free(home_servers_byname);
224         home_servers_byname = NULL;
225
226         rbtree_free(home_servers_byaddr);
227         home_servers_byaddr = NULL;
228
229         rbtree_free(home_pools_byname);
230         home_pools_byname = NULL;
231
232         rbtree_free(realms_byname);
233         realms_byname = NULL;
234
235 #ifdef HAVE_REGEX_H
236         if (realms_regex) {
237                 realm_regex_t *this, *next;
238
239                 for (this = realms_regex; this != NULL; this = next) {
240                         next = this->next;
241                         free(this->realm);
242                         free(this);
243                 }
244         }
245 #endif
246
247         free(realm_config);
248 }
249
250
251 static struct in_addr hs_ip4addr;
252 static struct in6_addr hs_ip6addr;
253 static char *hs_type = NULL;
254 static char *hs_check = NULL;
255 static char *hs_virtual_server = NULL;
256
257 static CONF_PARSER home_server_config[] = {
258         { "ipaddr",  PW_TYPE_IPADDR,
259           0, &hs_ip4addr,  NULL },
260         { "ipv6addr",  PW_TYPE_IPV6ADDR,
261           0, &hs_ip6addr, NULL },
262         { "virtual_server",  PW_TYPE_STRING_PTR,
263           0, &hs_virtual_server, NULL },
264
265         { "port", PW_TYPE_INTEGER,
266           offsetof(home_server,port), NULL,   "0" },
267
268         { "type",  PW_TYPE_STRING_PTR,
269           0, &hs_type, NULL },
270
271         { "secret",  PW_TYPE_STRING_PTR,
272           offsetof(home_server,secret), NULL,  NULL},
273
274         { "response_window", PW_TYPE_INTEGER,
275           offsetof(home_server,response_window), NULL,   "30" },
276         { "max_outstanding", PW_TYPE_INTEGER,
277           offsetof(home_server,max_outstanding), NULL,   "65536" },
278
279         { "zombie_period", PW_TYPE_INTEGER,
280           offsetof(home_server,zombie_period), NULL,   "40" },
281         { "status_check", PW_TYPE_STRING_PTR,
282           0, &hs_check,   "none" },
283         { "ping_check", PW_TYPE_STRING_PTR,
284           0, &hs_check,   "none" },
285
286         { "ping_interval", PW_TYPE_INTEGER,
287           offsetof(home_server,ping_interval), NULL,   "30" },
288         { "check_interval", PW_TYPE_INTEGER,
289           offsetof(home_server,ping_interval), NULL,   "30" },
290         { "num_answers_to_alive", PW_TYPE_INTEGER,
291           offsetof(home_server,num_pings_to_alive), NULL,   "3" },
292         { "num_pings_to_alive", PW_TYPE_INTEGER,
293           offsetof(home_server,num_pings_to_alive), NULL,   "3" },
294         { "revive_interval", PW_TYPE_INTEGER,
295           offsetof(home_server,revive_interval), NULL,   "300" },
296         { "status_check_timeout", PW_TYPE_INTEGER,
297           offsetof(home_server,ping_timeout), NULL,   "4" },
298
299         { "username",  PW_TYPE_STRING_PTR,
300           offsetof(home_server,ping_user_name), NULL,  NULL},
301         { "password",  PW_TYPE_STRING_PTR,
302           offsetof(home_server,ping_user_password), NULL,  NULL},
303
304         { NULL, -1, 0, NULL, NULL }             /* end the list */
305
306 };
307
308
309 static int home_server_add(realm_config_t *rc, CONF_SECTION *cs, int type)
310 {
311         const char *name2;
312         home_server *home;
313         int dual = FALSE;
314         CONF_PAIR *cp;
315
316         free(hs_virtual_server); /* used only for printing during parsing */
317         hs_virtual_server = NULL;
318
319         name2 = cf_section_name1(cs);
320         if (!name2 || (strcasecmp(name2, "home_server") != 0)) {
321                 cf_log_err(cf_sectiontoitem(cs),
322                            "Section is not a home_server.");
323                 return 0;
324         }
325
326         name2 = cf_section_name2(cs);
327         if (!name2) {
328                 cf_log_err(cf_sectiontoitem(cs),
329                            "Home server section is missing a name.");
330                 return 0;
331         }
332
333         home = rad_malloc(sizeof(*home));
334         memset(home, 0, sizeof(*home));
335
336         home->name = name2;
337         home->cs = cs;
338
339         memset(&hs_ip4addr, 0, sizeof(hs_ip4addr));
340         memset(&hs_ip6addr, 0, sizeof(hs_ip6addr));
341         cf_section_parse(cs, home, home_server_config);
342
343         /*
344          *      Figure out which one to use.
345          */
346         if (cf_pair_find(cs, "ipaddr")) {
347                 home->ipaddr.af = AF_INET;
348                 home->ipaddr.ipaddr.ip4addr = hs_ip4addr;
349
350         } else if (cf_pair_find(cs, "ipv6addr")) {
351                 home->ipaddr.af = AF_INET6;
352                 home->ipaddr.ipaddr.ip6addr = hs_ip6addr;
353
354         } else if ((cp = cf_pair_find(cs, "virtual_server")) != NULL) {
355                 home->ipaddr.af = AF_UNSPEC;
356                 home->server = cf_pair_value(cp);
357                 if (!home->server) {
358                         cf_log_err(cf_sectiontoitem(cs),
359                                    "Invalid value for virtual_server");
360                         goto error;
361                 }
362
363                 if (!cf_section_sub_find_name2(rc->cs, "server", home->server)) {
364                   
365                         cf_log_err(cf_sectiontoitem(cs),
366                                    "No such server %s", home->server);
367                         goto error;
368                 }
369
370                 free(hs_type);
371                 hs_type = NULL;
372                 home->secret = strdup("");
373                 goto skip_port;
374
375         } else {
376                 cf_log_err(cf_sectiontoitem(cs),
377                            "No ipaddr, ipv6addr, or virtual_server defined for home server \"%s\".",
378                            name2);
379         error:
380                 free(home);
381                 free(hs_type);
382                 hs_type = NULL;
383                 free(hs_check);
384                 hs_check = NULL;
385                 return 0;
386         }
387
388         if (!home->port || (home->port > 65535)) {
389                 cf_log_err(cf_sectiontoitem(cs),
390                            "No port, or invalid port defined for home server %s.",
391                            name2);
392                 goto error;
393         }
394
395         if (0) {
396                 cf_log_err(cf_sectiontoitem(cs),
397                            "Fatal error!  Home server %s is ourselves!",
398                            name2);
399                 goto error;
400         }
401
402         if (!home->secret) {
403                 cf_log_err(cf_sectiontoitem(cs),
404                            "No shared secret defined for home server %s.",
405                            name2);
406                 free(home);
407                 return 0;
408         }
409
410         /*
411          *      Use a reasonable default.
412          */
413  skip_port:
414         if (!hs_type) hs_type = strdup("auth+acct");
415
416         if (strcasecmp(hs_type, "auth") == 0) {
417                 home->type = HOME_TYPE_AUTH;
418                 if (type != home->type) {
419                         cf_log_err(cf_sectiontoitem(cs),
420                                    "Server pool of \"acct\" servers cannot include home server %s of type \"auth\"",
421                                    name2);
422                         free(home);
423                         return 0;
424                 }
425
426         } else if (strcasecmp(hs_type, "acct") == 0) {
427                 home->type = HOME_TYPE_ACCT;
428                 if (type != home->type) {
429                         cf_log_err(cf_sectiontoitem(cs),
430                                    "Server pool of \"auth\" servers cannot include home server %s of type \"acct\"",
431                                    name2);
432                         free(home);
433                         return 0;
434                 }
435
436         } else if (strcasecmp(hs_type, "auth+acct") == 0) {
437                 home->type = HOME_TYPE_AUTH;
438                 dual = TRUE;
439
440         } else {
441                 cf_log_err(cf_sectiontoitem(cs),
442                            "Invalid type \"%s\" for home server %s.",
443                            hs_type, name2);
444                 free(home);
445                 free(hs_type);
446                 hs_type = NULL;
447                 free(hs_check);
448                 hs_check = NULL;
449                 return 0;
450         }
451         free(hs_type);
452         hs_type = NULL;
453
454         if (!hs_check || (strcasecmp(hs_check, "none") == 0)) {
455                 home->ping_check = HOME_PING_CHECK_NONE;
456
457         } else if (strcasecmp(hs_check, "status-server") == 0) {
458                 home->ping_check = HOME_PING_CHECK_STATUS_SERVER;
459
460         } else if (strcasecmp(hs_check, "request") == 0) {
461                 home->ping_check = HOME_PING_CHECK_REQUEST;
462
463         } else {
464                 cf_log_err(cf_sectiontoitem(cs),
465                            "Invalid ping_check \"%s\" for home server %s.",
466                            hs_check, name2);
467                 free(home);
468                 free(hs_check);
469                 hs_check = NULL;
470                 return 0;
471         }
472         free(hs_check);
473         hs_check = NULL;
474
475         if ((home->ping_check != HOME_PING_CHECK_NONE) &&
476             (home->ping_check != HOME_PING_CHECK_STATUS_SERVER)) {
477                 if (!home->ping_user_name) {
478                         cf_log_err(cf_sectiontoitem(cs), "You must supply a user name to enable ping checks");
479                         free(home);
480                         return 0;
481                 }
482
483                 if ((home->type == HOME_TYPE_AUTH) &&
484                     !home->ping_user_password) {
485                         cf_log_err(cf_sectiontoitem(cs), "You must supply a password to enable ping checks");
486                         free(home);
487                         return 0;
488                 }
489         }
490
491         if (rbtree_finddata(home_servers_byaddr, home)) {
492                 DEBUG2("Ignoring duplicate home server %s.", name2);
493                 return 1;
494         }
495
496         if (!rbtree_insert(home_servers_byname, home)) {
497                 cf_log_err(cf_sectiontoitem(cs),
498                            "Internal error adding home server %s.",
499                            name2);
500                 free(home);
501                 return 0;
502         }
503
504         if (!rbtree_insert(home_servers_byaddr, home)) {
505                 rbtree_deletebydata(home_servers_byname, home);
506                 cf_log_err(cf_sectiontoitem(cs),
507                            "Internal error adding home server %s.",
508                            name2);
509                 free(home);
510                 return 0;
511         }
512
513         if (home->response_window < 5) home->response_window = 5;
514         if (home->response_window > 60) home->response_window = 60;
515
516         if (home->max_outstanding < 8) home->max_outstanding = 8;
517         if (home->max_outstanding > 65536*16) home->max_outstanding = 65536*16;
518
519         if (home->ping_interval < 6) home->ping_interval = 6;
520         if (home->ping_interval > 120) home->ping_interval = 120;
521
522         if (home->zombie_period < 20) home->zombie_period = 20;
523         if (home->zombie_period > 120) home->zombie_period = 120;
524
525         if (home->zombie_period < home->response_window) {
526                 home->zombie_period = home->response_window;
527         }
528
529         if (home->num_pings_to_alive < 3) home->num_pings_to_alive = 3;
530         if (home->num_pings_to_alive > 10) home->num_pings_to_alive = 10;
531
532         if (home->ping_timeout < 3) home->ping_timeout = 3;
533         if (home->ping_timeout > 10) home->ping_timeout = 10;
534
535         if (home->revive_interval < 60) home->revive_interval = 60;
536         if (home->revive_interval > 3600) home->revive_interval = 3600;
537
538         if (dual) {
539                 home_server *home2 = rad_malloc(sizeof(*home2));
540
541                 memcpy(home2, home, sizeof(*home2));
542
543                 home2->type = HOME_TYPE_ACCT;
544                 home2->port++;
545                 home2->ping_user_password = NULL;
546                 home2->cs = cs;
547
548                 if (!rbtree_insert(home_servers_byname, home2)) {
549                         cf_log_err(cf_sectiontoitem(cs),
550                                    "Internal error adding home server %s.",
551                                    name2);
552                         free(home2);
553                         return 0;
554                 }
555                 
556                 if (!rbtree_insert(home_servers_byaddr, home2)) {
557                         rbtree_deletebydata(home_servers_byname, home2);
558                         cf_log_err(cf_sectiontoitem(cs),
559                                    "Internal error adding home server %s.",
560                                    name2);
561                         free(home2);
562                         return 0;
563                 }
564         }
565
566         return 1;
567 }
568
569
570 static home_pool_t *server_pool_alloc(const char *name, home_pool_type_t type,
571                                       int server_type, int num_home_servers)
572 {
573         home_pool_t *pool;
574
575         pool = rad_malloc(sizeof(*pool) + (sizeof(pool->servers[0]) *
576                                            num_home_servers));
577         if (!pool) return NULL; /* just for pairanoia */
578         
579         memset(pool, 0, sizeof(*pool) + (sizeof(pool->servers[0]) *
580                                          num_home_servers));
581
582         pool->name = name;
583         pool->type = type;
584         pool->server_type = server_type;
585         pool->num_home_servers = num_home_servers;
586
587         return pool;
588 }
589
590 static int pool_check_home_server(realm_config_t *rc, CONF_PAIR *cp,
591                                   const char *name, int server_type,
592                                   home_server **phome)
593 {
594         home_server myhome, *home;
595         CONF_SECTION *server_cs;
596
597         if (!name) {
598                 cf_log_err(cf_pairtoitem(cp),
599                            "No value given for home_server.");
600                 return 0;
601         }
602
603         myhome.name = name;
604         myhome.type = server_type;
605         home = rbtree_finddata(home_servers_byname, &myhome);
606         if (home) {
607                 *phome = home;
608                 return 1;
609         }
610         
611         server_cs = cf_section_sub_find_name2(rc->cs, "home_server", name);
612         if (!server_cs) {
613                 cf_log_err(cf_pairtoitem(cp),
614                            "Unknown home_server \"%s\".", name);
615                 return 0;
616         }
617         
618         if (!home_server_add(rc, server_cs, server_type)) {
619                 return 0;
620         }
621         
622         home = rbtree_finddata(home_servers_byname, &myhome);
623         if (!home) {
624                 radlog(L_ERR, "Internal sanity check failed %d",
625                        __LINE__);
626                 return 0;
627         }
628
629         *phome = home;
630         return 1;
631 }
632
633
634 static int server_pool_add(realm_config_t *rc,
635                            CONF_SECTION *cs, int server_type, int do_print)
636 {
637         const char *name2;
638         home_pool_t *pool = NULL;
639         const char *value;
640         CONF_PAIR *cp;
641         int num_home_servers;
642         home_server *home;
643
644         name2 = cf_section_name1(cs);
645         if (!name2 || ((strcasecmp(name2, "server_pool") != 0) &&
646                        (strcasecmp(name2, "home_server_pool") != 0))) {
647                 cf_log_err(cf_sectiontoitem(cs),
648                            "Section is not a home_server_pool.");
649                 return 0;
650         }
651
652         name2 = cf_section_name2(cs);
653         if (!name2) {
654                 cf_log_err(cf_sectiontoitem(cs),
655                            "Server pool section is missing a name.");
656                 return 0;
657         }
658
659         /*
660          *      Count the home servers and initalize them.
661          */
662         num_home_servers = 0;
663         for (cp = cf_pair_find(cs, "home_server");
664              cp != NULL;
665              cp = cf_pair_find_next(cs, cp, "home_server")) {
666                 num_home_servers++;
667
668                 if (!pool_check_home_server(rc, cp, cf_pair_value(cp),
669                                             server_type, &home)) {
670                                             
671                         return 0;
672                 }
673         }
674
675         if (num_home_servers == 0) {
676                 cf_log_err(cf_sectiontoitem(cs),
677                            "No home servers defined in pool %s",
678                            name2);
679                 goto error;
680         }
681
682         pool = server_pool_alloc(name2, HOME_POOL_FAIL_OVER, server_type,
683                                  num_home_servers);
684         pool->cs = cs;
685
686
687         cp = cf_pair_find(cs, "fallback");
688         if (cp) {
689                 if (!pool_check_home_server(rc, cp, cf_pair_value(cp),
690                                             server_type, &pool->fallback)) {
691                         
692                         goto error;
693                 }
694         }
695
696         if (do_print) cf_log_info(cs, " home_server_pool %s {", name2);
697
698         cp = cf_pair_find(cs, "type");
699         if (cp) {
700                 static FR_NAME_NUMBER pool_types[] = {
701                         { "load-balance", HOME_POOL_LOAD_BALANCE },
702                         { "fail-over", HOME_POOL_FAIL_OVER },
703                         { "round_robin", HOME_POOL_LOAD_BALANCE },
704                         { "fail_over", HOME_POOL_FAIL_OVER },
705                         { "client-balance", HOME_POOL_CLIENT_BALANCE },
706                         { "client-port-balance", HOME_POOL_CLIENT_PORT_BALANCE },
707                         { "keyed-balance", HOME_POOL_KEYED_BALANCE },
708                         { NULL, 0 }
709                 };
710
711                 value = cf_pair_value(cp);
712                 if (!value) {
713                         cf_log_err(cf_pairtoitem(cp),
714                                    "No value given for type.");
715                         goto error;
716                 }
717
718                 pool->type = fr_str2int(pool_types, value, 0);
719                 if (!pool->type) {
720                         cf_log_err(cf_pairtoitem(cp),
721                                    "Unknown type \"%s\".",
722                                    value);
723                         goto error;
724                 }
725
726                 if (do_print) cf_log_info(cs, "\ttype = %s", value);
727         }
728
729         cp = cf_pair_find(cs, "virtual_server");
730         if (cp) {
731                 pool->virtual_server = cf_pair_value(cp);
732                 if (do_print && pool->virtual_server) {
733                         cf_log_info(cs, "\tvirtual_server = %s", pool->virtual_server);
734                 }
735
736                 if (!cf_section_sub_find_name2(rc->cs, "server",
737                                                pool->virtual_server)) {
738                         cf_log_err(cf_pairtoitem(cp), "No such server %s",
739                                    pool->virtual_server);
740                         goto error;
741                 }
742
743         }
744
745         num_home_servers = 0;
746         for (cp = cf_pair_find(cs, "home_server");
747              cp != NULL;
748              cp = cf_pair_find_next(cs, cp, "home_server")) {
749                 home_server myhome;
750
751                 value = cf_pair_value(cp);
752
753                 memset(&myhome, 0, sizeof(&myhome));
754                 myhome.name = value;
755                 myhome.type = server_type;
756
757                 home = rbtree_finddata(home_servers_byname, &myhome);
758                 if (!home) {
759                         DEBUG2("Internal sanity check failed");
760                         goto error;
761                 }
762
763                 if (0) {
764                         DEBUG2("Warning: Duplicate home server %s in server pool %s", home->name, pool->name);
765                         continue;
766                 }
767
768                 if (do_print) cf_log_info(cs, "\thome_server = %s", home->name);
769                 pool->servers[num_home_servers++] = home;
770         } /* loop over home_server's */
771
772         if (pool->fallback && do_print) {
773                 cf_log_info(cs, "\tfallback = %s", pool->fallback->name);
774         }
775
776         if (!rbtree_insert(home_pools_byname, pool)) {
777                 rad_assert("Internal sanity check failed");
778                 goto error;
779         }
780
781         if (do_print) cf_log_info(cs, " }");
782
783         rad_assert(pool->server_type != 0);
784
785         return 1;
786
787  error:
788         if (do_print) cf_log_info(cs, " }");
789         free(pool);
790         return 0;
791 }
792
793
794 static int old_server_add(realm_config_t *rc, CONF_SECTION *cs,
795                           const char *realm,
796                           const char *name, const char *secret,
797                           home_pool_type_t ldflag, home_pool_t **pool_p,
798                           int type, const char *server)
799 {
800         int i, insert_point, num_home_servers;
801         home_server myhome, *home;
802         home_pool_t mypool, *pool;
803         CONF_SECTION *subcs;
804
805         /*
806          *      LOCAL realms get sanity checked, and nothing else happens.
807          */
808         if (strcmp(name, "LOCAL") == 0) {
809                 if (*pool_p) {
810                         cf_log_err(cf_sectiontoitem(cs), "Realm \"%s\" cannot be both LOCAL and remote", name);
811                         return 0;
812                 }
813                 return 1;
814         }
815
816         mypool.name = realm;
817         mypool.server_type = type;
818         pool = rbtree_finddata(home_pools_byname, &mypool);
819         if (pool) {
820                 if (pool->type != ldflag) {
821                         cf_log_err(cf_sectiontoitem(cs), "Inconsistent ldflag for server pool \"%s\"", name);
822                         return 0;
823                 }
824
825                 if (pool->server_type != type) {
826                         cf_log_err(cf_sectiontoitem(cs), "Inconsistent home server type for server pool \"%s\"", name);
827                         return 0;
828                 }
829         }
830
831         myhome.name = name;
832         myhome.type = type;
833         home = rbtree_finddata(home_servers_byname, &myhome);
834         if (home) {
835                 if (strcmp(home->secret, secret) != 0) {
836                         cf_log_err(cf_sectiontoitem(cs), "Inconsistent shared secret for home server \"%s\"", name);
837                         return 0;
838                 }
839
840                 if (home->type != type) {
841                         cf_log_err(cf_sectiontoitem(cs), "Inconsistent type for home server \"%s\"", name);
842                         return 0;
843                 }
844
845                 /*
846                  *      See if the home server is already listed
847                  *      in the pool.  If so, do nothing else.
848                  */
849                 if (pool) for (i = 0; i < pool->num_home_servers; i++) {
850                         if (pool->servers[i] == home) {
851                                 return 1;
852                         }
853                 }
854         }
855
856         /*
857          *      If we do have a pool, check that there is room to
858          *      insert the home server we've found, or the one that we
859          *      create here.
860          *
861          *      Note that we insert it into the LAST available
862          *      position, in order to maintain the same order as in
863          *      the configuration files.
864          */
865         insert_point = -1;
866         if (pool) {
867                 for (i = pool->num_home_servers - 1; i >= 0; i--) {
868                         if (pool->servers[i]) break;
869
870                         if (!pool->servers[i]) {
871                                 insert_point = i;
872                         }
873                 }
874
875                 if (insert_point < 0) {
876                         cf_log_err(cf_sectiontoitem(cs), "No room in pool to add home server \"%s\".  Please update the realm configuration to use the new-style home servers and server pools.", name);
877                         return 0;
878                 }
879         }
880
881         /*
882          *      No home server, allocate one.
883          */
884         if (!home) {
885                 const char *p;
886                 char *q;
887
888                 home = rad_malloc(sizeof(*home));
889                 memset(home, 0, sizeof(*home));
890
891                 home->name = name;
892                 home->hostname = name;
893                 home->type = type;
894                 home->secret = secret;
895                 home->cs = cs;
896
897                 p = strchr(name, ':');
898                 if (!p) {
899                         if (type == HOME_TYPE_AUTH) {
900                                 home->port = PW_AUTH_UDP_PORT;
901                         } else {
902                                 home->port = PW_ACCT_UDP_PORT;
903                         }
904
905                         p = name;
906                         q = NULL;
907
908                 } else if (p == name) {
909                                 cf_log_err(cf_sectiontoitem(cs),
910                                            "Invalid hostname %s.",
911                                            name);
912                                 free(home);
913                                 return 0;
914
915                 } else {
916                         home->port = atoi(p + 1);
917                         if ((home->port == 0) || (home->port > 65535)) {
918                                 cf_log_err(cf_sectiontoitem(cs),
919                                            "Invalid port %s.",
920                                            p + 1);
921                                 free(home);
922                                 return 0;
923                         }
924
925                         q = rad_malloc((p - name) + 1);
926                         memcpy(q, name, (p - name));
927                         q[p - name] = '\0';
928                         p = q;
929                 }
930
931                 if (!server) {
932                         if (ip_hton(p, AF_UNSPEC, &home->ipaddr) < 0) {
933                                 cf_log_err(cf_sectiontoitem(cs),
934                                            "Failed looking up hostname %s.",
935                                            p);
936                                 free(home);
937                                 free(q);
938                                 return 0;
939                         }
940                 } else {
941                         home->ipaddr.af = AF_UNSPEC;
942                         home->server = server;
943                 }
944                 free(q);
945
946                 /*
947                  *      Use the old-style configuration.
948                  */
949                 home->max_outstanding = 65535*16;
950                 home->zombie_period = rc->retry_delay * rc->retry_count;
951                 if (home->zombie_period == 0) home->zombie_period =30;
952                 home->response_window = home->zombie_period - 1;
953
954                 home->ping_check = HOME_PING_CHECK_NONE;
955
956                 home->revive_interval = rc->dead_time;
957
958                 if (rbtree_finddata(home_servers_byaddr, home)) {
959                         cf_log_err(cf_sectiontoitem(cs), "Home server %s has the same IP address and/or port as another home server.", name);
960                         free(home);
961                         return 0;
962                 }
963
964                 if (!rbtree_insert(home_servers_byname, home)) {
965                         cf_log_err(cf_sectiontoitem(cs), "Internal error adding home server %s.", name);
966                         free(home);
967                         return 0;
968                 }
969
970                 if (!rbtree_insert(home_servers_byaddr, home)) {
971                         rbtree_deletebydata(home_servers_byname, home);
972                         cf_log_err(cf_sectiontoitem(cs), "Internal error adding home server %s.", name);
973                         free(home);
974                         return 0;
975                 }
976         }
977
978         /*
979          *      We now have a home server, see if we can insert it
980          *      into pre-existing pool.
981          */
982         if (insert_point >= 0) {
983                 rad_assert(pool != NULL);
984                 pool->servers[insert_point] = home;
985                 return 1;
986         }
987
988         rad_assert(pool == NULL);
989         rad_assert(home != NULL);
990
991         /*
992          *      Count the old-style realms of this name.
993          */
994         num_home_servers = 0;
995         for (subcs = cf_section_find_next(cs, NULL, "realm");
996              subcs != NULL;
997              subcs = cf_section_find_next(cs, subcs, "realm")) {
998                 const char *this = cf_section_name2(subcs);
999
1000                 if (!this || (strcmp(this, realm) != 0)) continue;
1001                 num_home_servers++;
1002         }
1003
1004         if (num_home_servers == 0) {
1005                 cf_log_err(cf_sectiontoitem(cs), "Internal error counting pools for home server %s.", name);
1006                 free(home);
1007                 return 0;
1008         }
1009
1010         pool = server_pool_alloc(realm, ldflag, type, num_home_servers);
1011         pool->cs = cs;
1012
1013         pool->servers[0] = home;
1014
1015         if (!rbtree_insert(home_pools_byname, pool)) {
1016                 rad_assert("Internal sanity check failed");
1017                 return 0;
1018         }
1019
1020         *pool_p = pool;
1021
1022         return 1;
1023 }
1024
1025 static int old_realm_config(realm_config_t *rc, CONF_SECTION *cs, REALM *r)
1026 {
1027         const char *host;
1028         const char *secret = NULL;
1029         home_pool_type_t ldflag;
1030         CONF_PAIR *cp;
1031
1032         cp = cf_pair_find(cs, "ldflag");
1033         ldflag = HOME_POOL_FAIL_OVER;
1034         if (cp) {
1035                 host = cf_pair_value(cp);
1036                 if (!host) {
1037                         cf_log_err(cf_pairtoitem(cp), "No value specified for ldflag");
1038                         return 0;
1039                 }
1040
1041                 if (strcasecmp(host, "fail_over") == 0) {
1042                         cf_log_info(cs, "\tldflag = fail_over");
1043                         
1044                 } else if (strcasecmp(host, "round_robin") == 0) {
1045                         ldflag = HOME_POOL_LOAD_BALANCE;
1046                         cf_log_info(cs, "\tldflag = round_robin");
1047                         
1048                 } else {
1049                         cf_log_err(cf_sectiontoitem(cs), "Unknown value \"%s\" for ldflag", host);
1050                         return 0;
1051                 }
1052         } /* else don't print it. */
1053
1054         /*
1055          *      Allow old-style if it doesn't exist, or if it exists and
1056          *      it's LOCAL.
1057          */
1058         cp = cf_pair_find(cs, "authhost");
1059         if (cp) {
1060                 host = cf_pair_value(cp);
1061                 if (!host) {
1062                         cf_log_err(cf_pairtoitem(cp), "No value specified for authhost");
1063                         return 0;
1064                 }
1065
1066                 if (strcmp(host, "LOCAL") != 0) {
1067                         cp = cf_pair_find(cs, "secret");
1068                         if (!cp) {
1069                                 cf_log_err(cf_sectiontoitem(cs), "No shared secret supplied for realm: %s", r->name);
1070                                 return 0;
1071                         }
1072
1073                         secret = cf_pair_value(cp);
1074                         if (!secret) {
1075                                 cf_log_err(cf_pairtoitem(cp), "No value specified for secret");
1076                                 return 0;
1077                         }
1078                 }
1079                         
1080                 cf_log_info(cs, "\tauthhost = %s",  host);
1081
1082                 if (!old_server_add(rc, cs, r->name, host, secret, ldflag,
1083                                     &r->auth_pool, HOME_TYPE_AUTH, NULL)) {
1084                         return 0;
1085                 }
1086         }
1087
1088         cp = cf_pair_find(cs, "accthost");
1089         if (cp) {
1090                 host = cf_pair_value(cp);
1091                 if (!host) {
1092                         cf_log_err(cf_pairtoitem(cp), "No value specified for accthost");
1093                         return 0;
1094                 }
1095
1096                 /*
1097                  *      Don't look for a secret again if it was found
1098                  *      above.
1099                  */
1100                 if ((strcmp(host, "LOCAL") != 0) && !secret) {
1101                         cp = cf_pair_find(cs, "secret");
1102                         if (!cp) {
1103                                 cf_log_err(cf_sectiontoitem(cs), "No shared secret supplied for realm: %s", r->name);
1104                                 return 0;
1105                         }
1106                         
1107                         secret = cf_pair_value(cp);
1108                         if (!secret) {
1109                                 cf_log_err(cf_pairtoitem(cp), "No value specified for secret");
1110                                 return 0;
1111                         }
1112                 }
1113                 
1114                 cf_log_info(cs, "\taccthost = %s", host);
1115
1116                 if (!old_server_add(rc, cs, r->name, host, secret, ldflag,
1117                                     &r->acct_pool, HOME_TYPE_ACCT, NULL)) {
1118                         return 0;
1119                 }
1120         }
1121
1122         cp = cf_pair_find(cs, "virtual_server");
1123         if (cp) {
1124                 host = cf_pair_value(cp);
1125                 if (!host) {
1126                         cf_log_err(cf_pairtoitem(cp), "No value specified for virtual_server");
1127                         return 0;
1128                 }
1129
1130                 cf_log_info(cs, "\tvirtual_server = %s", host);
1131
1132                 if (!old_server_add(rc, cs, r->name, host, "", ldflag,
1133                                     &r->auth_pool, HOME_TYPE_AUTH, host)) {
1134                         return 0;
1135                 }
1136                 if (!old_server_add(rc, cs, r->name, host, "", ldflag,
1137                                     &r->acct_pool, HOME_TYPE_ACCT, host)) {
1138                         return 0;
1139                 }
1140         }
1141
1142         if (secret) cf_log_info(cs, "\tsecret = %s", secret);
1143
1144         return 1;
1145
1146 }
1147
1148
1149 static int add_pool_to_realm(realm_config_t *rc, CONF_SECTION *cs,
1150                              const char *name, home_pool_t **dest,
1151                              int server_type, int do_print)
1152 {
1153         home_pool_t mypool, *pool;
1154
1155         mypool.name = name;
1156         mypool.server_type = server_type;
1157
1158         pool = rbtree_finddata(home_pools_byname, &mypool);
1159         if (!pool) {
1160                 CONF_SECTION *pool_cs;
1161
1162                 pool_cs = cf_section_sub_find_name2(rc->cs,
1163                                                     "home_server_pool",
1164                                                     name);
1165                 if (!pool_cs) {
1166                         pool_cs = cf_section_sub_find_name2(rc->cs,
1167                                                             "server_pool",
1168                                                             name);
1169                 }
1170                 if (!pool_cs) {
1171                         cf_log_err(cf_sectiontoitem(cs), "Failed to find home_server_pool \"%s\"", name);
1172                         return 0;
1173                 }
1174
1175                 if (!server_pool_add(rc, pool_cs, server_type, do_print)) {
1176                         return 0;
1177                 }
1178
1179                 pool = rbtree_finddata(home_pools_byname, &mypool);
1180                 if (!pool) {
1181                         radlog(L_ERR, "Internal sanity check failed in add_pool_to_realm");
1182                         return 0;
1183                 }
1184         }
1185
1186         if (pool->server_type != server_type) {
1187                 cf_log_err(cf_sectiontoitem(cs), "Incompatible home_server_pool \"%s\" (mixed auth_pool / acct_pool)", name);
1188                 return 0;
1189         }
1190
1191         *dest = pool;
1192
1193         return 1;
1194 }
1195
1196
1197 static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
1198 {
1199         const char *name2;
1200         REALM *r = NULL;
1201         CONF_PAIR *cp;
1202         home_pool_t *auth_pool, *acct_pool;
1203         const char *auth_pool_name, *acct_pool_name;
1204
1205         name2 = cf_section_name1(cs);
1206         if (!name2 || (strcasecmp(name2, "realm") != 0)) {
1207                 cf_log_err(cf_sectiontoitem(cs), "Section is not a realm.");
1208                 return 0;
1209         }
1210
1211         name2 = cf_section_name2(cs);
1212         if (!name2) {
1213                 cf_log_err(cf_sectiontoitem(cs), "Realm section is missing the realm name.");
1214                 return 0;
1215         }
1216
1217         auth_pool = acct_pool = NULL;
1218         auth_pool_name = acct_pool_name = NULL;
1219
1220         /*
1221          *      Prefer new configuration to old one.
1222          */
1223         cp = cf_pair_find(cs, "pool");
1224         if (!cp) cp = cf_pair_find(cs, "home_server_pool");
1225         if (cp) auth_pool_name = cf_pair_value(cp);
1226         if (cp && auth_pool_name) {
1227                 acct_pool_name = auth_pool_name;
1228                 if (!add_pool_to_realm(rc, cs,
1229                                        auth_pool_name, &auth_pool,
1230                                        HOME_TYPE_AUTH, 1)) {
1231                         return 0;
1232                 }
1233                 if (!add_pool_to_realm(rc, cs,
1234                                        auth_pool_name, &acct_pool,
1235                                        HOME_TYPE_ACCT, 0)) {
1236                         return 0;
1237                 }
1238         }
1239
1240         cp = cf_pair_find(cs, "auth_pool");
1241         if (cp) auth_pool_name = cf_pair_value(cp);
1242         if (cp && auth_pool_name) {
1243                 if (auth_pool) {
1244                         cf_log_err(cf_sectiontoitem(cs), "Cannot use \"pool\" and \"auth_pool\" at the same time.");
1245                         return 0;
1246                 }
1247                 if (!add_pool_to_realm(rc, cs,
1248                                        auth_pool_name, &auth_pool,
1249                                        HOME_TYPE_AUTH, 1)) {
1250                         return 0;
1251                 }
1252         }
1253
1254         cp = cf_pair_find(cs, "acct_pool");
1255         if (cp) acct_pool_name = cf_pair_value(cp);
1256         if (cp && acct_pool_name) {
1257                 int do_print = TRUE;
1258
1259                 if (acct_pool) {
1260                         cf_log_err(cf_sectiontoitem(cs), "Cannot use \"pool\" and \"acct_pool\" at the same time.");
1261                         return 0;
1262                 }
1263
1264                 if (!auth_pool ||
1265                     (strcmp(auth_pool_name, acct_pool_name) != 0)) {
1266                         do_print = TRUE;
1267                 }
1268
1269                 if (!add_pool_to_realm(rc, cs,
1270                                        acct_pool_name, &acct_pool,
1271                                        HOME_TYPE_ACCT, do_print)) {
1272                         return 0;
1273                 }
1274         }
1275
1276         cf_log_info(cs, " realm %s {", name2);
1277
1278         /*
1279          *      The realm MAY already exist if it's an old-style realm.
1280          *      In that case, merge the old-style realm with this one.
1281          */
1282         r = realm_find2(name2);
1283         if (r && (strcmp(r->name, name2) == 0)) {
1284                 if (cf_pair_find(cs, "auth_pool") ||
1285                     cf_pair_find(cs, "acct_pool")) {
1286                         cf_log_err(cf_sectiontoitem(cs), "Duplicate realm \"%s\"", name2);
1287                         goto error;
1288                 }
1289
1290                 if (!old_realm_config(rc, cs, r)) {
1291                         goto error;
1292                 }
1293
1294                 cf_log_info(cs, " } # realm %s", name2);
1295                 return 1;
1296         }
1297
1298 #ifdef HAVE_REGEX_H
1299         if (name2[0] == '~') {
1300                 regex_t reg;
1301                 
1302                 /*
1303                  *      Include substring matches.
1304                  */
1305                 if (regcomp(&reg, name2 + 1,
1306                             REG_EXTENDED | REG_NOSUB | REG_ICASE) != 0) {
1307                         cf_log_err(cf_sectiontoitem(cs),
1308                                    "Invalid regex in realm \"%s\"", name2);
1309                         goto error;
1310                 }
1311                 regfree(&reg);
1312         }
1313 #endif
1314
1315         r = rad_malloc(sizeof(*r));
1316         memset(r, 0, sizeof(*r));
1317
1318         r->name = name2;
1319         r->auth_pool = auth_pool;
1320         r->acct_pool = acct_pool;
1321         r->striprealm = 1;
1322
1323         if (auth_pool_name &&
1324             (auth_pool_name == acct_pool_name)) { /* yes, ptr comparison */
1325                 cf_log_info(cs, "\tpool = %s", auth_pool_name);
1326         } else {
1327                 if (auth_pool_name) cf_log_info(cs, "\tauth_pool = %s", auth_pool_name);
1328                 if (acct_pool_name) cf_log_info(cs, "\tacct_pool = %s", acct_pool_name);
1329         }
1330
1331         cp = cf_pair_find(cs, "nostrip");
1332         if (cp && (cf_pair_value(cp) == NULL)) {
1333                 r->striprealm = 0;
1334                 cf_log_info(cs, "\tnostrip");
1335         }
1336
1337         /*
1338          *      We're a new-style realm.  Complain if we see the old
1339          *      directives.
1340          */
1341         if (r->auth_pool || r->acct_pool) {
1342                 if (((cp = cf_pair_find(cs, "authhost")) != NULL) ||
1343                     ((cp = cf_pair_find(cs, "accthost")) != NULL) ||
1344                     ((cp = cf_pair_find(cs, "secret")) != NULL) ||
1345                     ((cp = cf_pair_find(cs, "ldflag")) != NULL)) {
1346                         DEBUG2("WARNING: Ignoring old-style configuration entry \"%s\" in realm \"%s\"", cf_pair_attr(cp), r->name);
1347                 }
1348
1349
1350                 /*
1351                  *      The realm MAY be an old-style realm, as there
1352                  *      was no auth_pool or acct_pool.  Double-check
1353                  *      it, just to be safe.
1354                  */
1355         } else if (!old_realm_config(rc, cs, r)) {
1356                 goto error;
1357         }
1358
1359 #ifdef HAVE_REGEX_H
1360         /*
1361          *      It's a regex.  Add it to a separate list.
1362          */
1363         if (name2[0] == '~') {
1364                 realm_regex_t *rr, **last;
1365
1366                 rr = rad_malloc(sizeof(*rr));
1367                 
1368                 last = &realms_regex;
1369                 while (*last) last = &((*last)->next);  /* O(N^2)... sue me. */
1370
1371                 r->name = name2;
1372                 rr->realm = r;
1373                 rr->next = NULL;
1374
1375                 *last = rr;
1376
1377                 cf_log_info(cs, " }");
1378                 return 1;
1379         }
1380 #endif
1381
1382         if (!rbtree_insert(realms_byname, r)) {
1383                 rad_assert("Internal sanity check failed");
1384                 goto error;
1385         }
1386
1387         cf_log_info(cs, " }");
1388
1389         return 1;
1390
1391  error:
1392         cf_log_info(cs, " } # realm %s", name2);
1393         free(r);
1394         return 0;
1395 }
1396
1397
1398 int realms_init(CONF_SECTION *config)
1399 {
1400         CONF_SECTION *cs;
1401         realm_config_t *rc, *old_rc;
1402
1403         if (realms_byname) return 1;
1404
1405         realms_byname = rbtree_create(realm_name_cmp, free, 0);
1406         if (!realms_byname) {
1407                 realms_free();
1408                 return 0;
1409         }
1410
1411         home_servers_byaddr = rbtree_create(home_server_addr_cmp, free, 0);
1412         if (!home_servers_byaddr) {
1413                 realms_free();
1414                 return 0;
1415         }
1416
1417         home_servers_byname = rbtree_create(home_server_name_cmp, NULL, 0);
1418         if (!home_servers_byname) {
1419                 realms_free();
1420                 return 0;
1421         }
1422
1423         home_pools_byname = rbtree_create(home_pool_name_cmp, free, 0);
1424         if (!home_pools_byname) {
1425                 realms_free();
1426                 return 0;
1427         }
1428
1429         rc = rad_malloc(sizeof(*rc));
1430         memset(rc, 0, sizeof(*rc));
1431         rc->cs = config;
1432
1433         cs = cf_subsection_find_next(config, NULL, "proxy");
1434         if (cs) {
1435                 cf_section_parse(cs, rc, proxy_config);
1436         } else {
1437                 rc->dead_time = DEAD_TIME;
1438                 rc->retry_count = RETRY_COUNT;
1439                 rc->retry_delay = RETRY_DELAY;
1440                 rc->fallback = 0;
1441                 rc->wake_all_if_all_dead= 0;
1442         }
1443
1444         for (cs = cf_subsection_find_next(config, NULL, "realm");
1445              cs != NULL;
1446              cs = cf_subsection_find_next(config, cs, "realm")) {
1447                 if (!realm_add(rc, cs)) {
1448                         free(rc);
1449                         realms_free();
1450                         return 0;
1451                 }
1452         }
1453
1454         xlat_register("home_server", xlat_home_server, NULL);
1455         xlat_register("home_server_pool", xlat_server_pool, NULL);
1456
1457         /*
1458          *      Swap pointers atomically.
1459          */
1460         old_rc = realm_config;
1461         realm_config = rc;
1462         free(old_rc);
1463
1464         return 1;
1465 }
1466
1467 /*
1468  *      Find a realm where "name" might be the regex.
1469  */
1470 REALM *realm_find2(const char *name)
1471 {
1472         REALM myrealm;
1473         REALM *realm;
1474         
1475         if (!name) name = "NULL";
1476
1477         myrealm.name = name;
1478         realm = rbtree_finddata(realms_byname, &myrealm);
1479         if (realm) return realm;
1480
1481 #ifdef HAVE_REGEX_H
1482         if (realms_regex) {
1483                 realm_regex_t *this;
1484
1485                 for (this = realms_regex; this != NULL; this = this->next) {
1486                         if (strcmp(this->realm->name, name) == 0) {
1487                                 return this->realm;
1488                         }
1489                 }
1490         }
1491 #endif
1492
1493         /*
1494          *      Couldn't find a realm.  Look for DEFAULT.
1495          */
1496         myrealm.name = "DEFAULT";
1497         return rbtree_finddata(realms_byname, &myrealm);
1498 }
1499
1500
1501 /*
1502  *      Find a realm in the REALM list.
1503  */
1504 REALM *realm_find(const char *name)
1505 {
1506         REALM myrealm;
1507         REALM *realm;
1508         
1509         if (!name) name = "NULL";
1510
1511         myrealm.name = name;
1512         realm = rbtree_finddata(realms_byname, &myrealm);
1513         if (realm) return realm;
1514
1515 #ifdef HAVE_REGEX_H
1516         if (realms_regex) {
1517                 realm_regex_t *this;
1518
1519                 for (this = realms_regex; this != NULL; this = this->next) {
1520                         int compare;
1521                         regex_t reg;
1522
1523                         /*
1524                          *      Include substring matches.
1525                          */
1526                         if (regcomp(&reg, this->realm->name + 1,
1527                                     REG_EXTENDED | REG_NOSUB | REG_ICASE) != 0) {
1528                                 continue;
1529                         }
1530
1531                         compare = regexec(&reg, name, 0, NULL, 0);
1532                         regfree(&reg);
1533
1534                         if (compare == 0) return this->realm;
1535                 }
1536         }
1537 #endif
1538
1539         /*
1540          *      Couldn't find a realm.  Look for DEFAULT.
1541          */
1542         myrealm.name = "DEFAULT";
1543         return rbtree_finddata(realms_byname, &myrealm);
1544 }
1545
1546
1547 home_server *home_server_ldb(const char *realmname,
1548                              home_pool_t *pool, REQUEST *request)
1549 {
1550         int             start;
1551         int             count;
1552         home_server     *found = NULL;
1553         VALUE_PAIR      *vp;
1554
1555         start = 0;
1556
1557         /*
1558          *      Determine how to pick choose the home server.
1559          */
1560         switch (pool->type) {
1561                 uint32_t hash;
1562
1563                 /*
1564                  *      For load-balancing by client IP address, we
1565                  *      pick a home server by hashing the client IP.
1566                  *
1567                  *      This isn't as even a load distribution as
1568                  *      tracking the State attribute, but it's better
1569                  *      than nothing.
1570                  */
1571         case HOME_POOL_CLIENT_BALANCE:
1572                 switch (request->packet->src_ipaddr.af) {
1573                 case AF_INET:
1574                         hash = fr_hash(&request->packet->src_ipaddr.ipaddr.ip4addr,
1575                                          sizeof(request->packet->src_ipaddr.ipaddr.ip4addr));
1576                         break;
1577                 case AF_INET6:
1578                         hash = fr_hash(&request->packet->src_ipaddr.ipaddr.ip6addr,
1579                                          sizeof(request->packet->src_ipaddr.ipaddr.ip6addr));
1580                         break;
1581                 default:
1582                         hash = 0;
1583                         break;
1584                 }
1585                 start = hash % pool->num_home_servers;
1586                 break;
1587
1588         case HOME_POOL_CLIENT_PORT_BALANCE:
1589                 switch (request->packet->src_ipaddr.af) {
1590                 case AF_INET:
1591                         hash = fr_hash(&request->packet->src_ipaddr.ipaddr.ip4addr,
1592                                          sizeof(request->packet->src_ipaddr.ipaddr.ip4addr));
1593                         break;
1594                 case AF_INET6:
1595                         hash = fr_hash(&request->packet->src_ipaddr.ipaddr.ip6addr,
1596                                          sizeof(request->packet->src_ipaddr.ipaddr.ip6addr));
1597                         break;
1598                 default:
1599                         hash = 0;
1600                         break;
1601                 }
1602                 fr_hash_update(&request->packet->src_port,
1603                                  sizeof(request->packet->src_port), hash);
1604                 start = hash % pool->num_home_servers;
1605                 break;
1606
1607         case HOME_POOL_KEYED_BALANCE:
1608                 if ((vp = pairfind(request->config_items, PW_LOAD_BALANCE_KEY)) != NULL) {
1609                         hash = fr_hash(vp->vp_strvalue, vp->length);
1610                         start = hash % pool->num_home_servers;
1611                         break;
1612                 }
1613                 /* FALL-THROUGH */
1614                                 
1615         case HOME_POOL_LOAD_BALANCE:
1616                 found = pool->servers[0];
1617
1618         default:
1619                 start = 0;
1620                 break;
1621         }
1622
1623         /*
1624          *      Starting with the home server we chose, loop through
1625          *      all home servers.  If the current one is dead, skip
1626          *      it.  If it is too busy, skip it.
1627          *
1628          *      Otherwise, use it.
1629          */
1630         for (count = 0; count < pool->num_home_servers; count++) {
1631                 home_server *home = pool->servers[(start + count) % pool->num_home_servers];
1632
1633                 if (home->state == HOME_STATE_IS_DEAD) {
1634                         continue;
1635                 }
1636
1637                 /*
1638                  *      This home server is too busy.  Choose another one.
1639                  */
1640                 if (home->currently_outstanding >= home->max_outstanding) {
1641                         continue;
1642                 }
1643
1644                 if (pool->type != HOME_POOL_LOAD_BALANCE) {
1645                         return home;
1646                 }
1647
1648                 DEBUG3("PROXY %s %d\t%s %d",
1649                        found->name, found->currently_outstanding,
1650                        home->name, home->currently_outstanding);
1651
1652                 /*
1653                  *      Prefer this server if it's less busy than the
1654                  *      one we previously found.
1655                  */
1656                 if (home->currently_outstanding < found->currently_outstanding) {
1657                         DEBUG3("Choosing %s: It's less busy than %s",
1658                                home->name, found->name);
1659                         found = home;
1660                         continue;
1661                 }
1662
1663                 /*
1664                  *      Ignore servers which are busier than the one
1665                  *      we found.
1666                  */
1667                 if (home->currently_outstanding > found->currently_outstanding) {
1668                         DEBUG3("Skipping %s: It's busier than %s",
1669                                home->name, found->name);
1670                         continue;
1671                 }
1672
1673                 if (home->total_requests_sent < found->total_requests_sent) {
1674                         DEBUG3("Choosing %s: It's been less busy than %s",
1675                                home->name, found->name);
1676                         found = home;
1677                         continue;
1678                 }
1679
1680                 if (home->total_requests_sent > found->total_requests_sent) {
1681                         DEBUG3("Skipping %s: It's been busier than %s",
1682                                home->name, found->name);
1683                         continue;
1684                 }
1685
1686                 /*
1687                  *      From the list of servers which have the same
1688                  *      load, choose one at random.
1689                  */
1690                 if (((count + 1) * (fr_rand() & 0xffff)) < (uint32_t) 0x10000) {
1691                         found = home;
1692                 }
1693
1694         } /* loop over the home servers */
1695
1696         if (found) return found;
1697
1698         /*
1699          *      There's a fallback if they're all dead.
1700          */
1701         if (pool->fallback &&
1702             (pool->fallback->state == HOME_STATE_ALIVE)) {
1703                 return pool->fallback;
1704         }
1705
1706         /*
1707          *      No live match found, and no fallback to the "DEFAULT"
1708          *      realm.  We fix this by blindly marking all servers as
1709          *      "live".  But only do it for ones that don't support
1710          *      "pings", as they will be marked live when they
1711          *      actually are live.
1712          */
1713         if (!realm_config->fallback &&
1714             realm_config->wake_all_if_all_dead) {
1715                 home_server *lb = NULL;
1716
1717                 for (count = 0; count < pool->num_home_servers; count++) {
1718                         home_server *home = pool->servers[count];
1719
1720                         if ((home->state == HOME_STATE_IS_DEAD) &&
1721                             (home->ping_check == HOME_PING_CHECK_NONE)) {
1722                                 home->state = HOME_STATE_ALIVE;
1723                                 if (!lb) lb = home;
1724                         }
1725                 }
1726
1727                 if (lb) return lb;
1728         }
1729
1730         /*
1731          *      Still nothing.  Look up the DEFAULT realm, but only
1732          *      if we weren't looking up the NULL or DEFAULT realms.
1733          */
1734         if (realm_config->fallback &&
1735             realmname &&
1736             (strcmp(realmname, "NULL") != 0) &&
1737             (strcmp(realmname, "DEFAULT") != 0)) {
1738                 REALM *rd = realm_find("DEFAULT");
1739
1740                 if (!rd) return NULL;
1741
1742                 pool = NULL;
1743                 if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
1744                         pool = rd->auth_pool;
1745
1746                 } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
1747                         pool = rd->acct_pool;
1748                 }
1749                 if (!pool) return NULL;
1750
1751                 DEBUG2("  Realm %s has no live home servers.  Falling back to the DEFAULT realm.", realmname);
1752                 return home_server_ldb(rd->name, pool, request);
1753         }
1754
1755         /*
1756          *      Still haven't found anything.  Oh well.
1757          */
1758         return NULL;
1759 }
1760
1761
1762 home_server *home_server_find(fr_ipaddr_t *ipaddr, int port)
1763 {
1764         home_server myhome;
1765
1766         memset(&myhome, 0, sizeof(myhome));
1767         myhome.ipaddr = *ipaddr;
1768         myhome.port = port;
1769         myhome.server = NULL;   /* we're not called for internal proxying */
1770
1771         return rbtree_finddata(home_servers_byaddr, &myhome);
1772 }