Corrected typo
[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         /*
688          *      Fallback servers must be defined, and must be
689          *      virtual servers.
690          */
691         cp = cf_pair_find(cs, "fallback");
692         if (cp) {
693                 if (!pool_check_home_server(rc, cp, cf_pair_value(cp),
694                                             server_type, &pool->fallback)) {
695                         
696                         goto error;
697                 }
698
699                 if (!pool->fallback->server) {
700                         cf_log_err(cf_sectiontoitem(cs), "Fallback home_server %s does NOT contain a virtual_server directive.", pool->fallback->name);
701                         goto error;
702                 }
703         }
704
705         if (do_print) cf_log_info(cs, " home_server_pool %s {", name2);
706
707         cp = cf_pair_find(cs, "type");
708         if (cp) {
709                 static FR_NAME_NUMBER pool_types[] = {
710                         { "load-balance", HOME_POOL_LOAD_BALANCE },
711                         { "fail-over", HOME_POOL_FAIL_OVER },
712                         { "round_robin", HOME_POOL_LOAD_BALANCE },
713                         { "fail_over", HOME_POOL_FAIL_OVER },
714                         { "client-balance", HOME_POOL_CLIENT_BALANCE },
715                         { "client-port-balance", HOME_POOL_CLIENT_PORT_BALANCE },
716                         { "keyed-balance", HOME_POOL_KEYED_BALANCE },
717                         { NULL, 0 }
718                 };
719
720                 value = cf_pair_value(cp);
721                 if (!value) {
722                         cf_log_err(cf_pairtoitem(cp),
723                                    "No value given for type.");
724                         goto error;
725                 }
726
727                 pool->type = fr_str2int(pool_types, value, 0);
728                 if (!pool->type) {
729                         cf_log_err(cf_pairtoitem(cp),
730                                    "Unknown type \"%s\".",
731                                    value);
732                         goto error;
733                 }
734
735                 if (do_print) cf_log_info(cs, "\ttype = %s", value);
736         }
737
738         cp = cf_pair_find(cs, "virtual_server");
739         if (cp) {
740                 pool->virtual_server = cf_pair_value(cp);
741                 if (do_print && pool->virtual_server) {
742                         cf_log_info(cs, "\tvirtual_server = %s", pool->virtual_server);
743                 }
744
745                 if (!cf_section_sub_find_name2(rc->cs, "server",
746                                                pool->virtual_server)) {
747                         cf_log_err(cf_pairtoitem(cp), "No such server %s",
748                                    pool->virtual_server);
749                         goto error;
750                 }
751
752         }
753
754         num_home_servers = 0;
755         for (cp = cf_pair_find(cs, "home_server");
756              cp != NULL;
757              cp = cf_pair_find_next(cs, cp, "home_server")) {
758                 home_server myhome;
759
760                 value = cf_pair_value(cp);
761
762                 memset(&myhome, 0, sizeof(&myhome));
763                 myhome.name = value;
764                 myhome.type = server_type;
765
766                 home = rbtree_finddata(home_servers_byname, &myhome);
767                 if (!home) {
768                         DEBUG2("Internal sanity check failed");
769                         goto error;
770                 }
771
772                 if (0) {
773                         DEBUG2("Warning: Duplicate home server %s in server pool %s", home->name, pool->name);
774                         continue;
775                 }
776
777                 if (do_print) cf_log_info(cs, "\thome_server = %s", home->name);
778                 pool->servers[num_home_servers++] = home;
779         } /* loop over home_server's */
780
781         if (pool->fallback && do_print) {
782                 cf_log_info(cs, "\tfallback = %s", pool->fallback->name);
783         }
784
785         if (!rbtree_insert(home_pools_byname, pool)) {
786                 rad_assert("Internal sanity check failed");
787                 goto error;
788         }
789
790         if (do_print) cf_log_info(cs, " }");
791
792         rad_assert(pool->server_type != 0);
793
794         return 1;
795
796  error:
797         if (do_print) cf_log_info(cs, " }");
798         free(pool);
799         return 0;
800 }
801
802
803 static int old_server_add(realm_config_t *rc, CONF_SECTION *cs,
804                           const char *realm,
805                           const char *name, const char *secret,
806                           home_pool_type_t ldflag, home_pool_t **pool_p,
807                           int type, const char *server)
808 {
809         int i, insert_point, num_home_servers;
810         home_server myhome, *home;
811         home_pool_t mypool, *pool;
812         CONF_SECTION *subcs;
813
814         /*
815          *      LOCAL realms get sanity checked, and nothing else happens.
816          */
817         if (strcmp(name, "LOCAL") == 0) {
818                 if (*pool_p) {
819                         cf_log_err(cf_sectiontoitem(cs), "Realm \"%s\" cannot be both LOCAL and remote", name);
820                         return 0;
821                 }
822                 return 1;
823         }
824
825         mypool.name = realm;
826         mypool.server_type = type;
827         pool = rbtree_finddata(home_pools_byname, &mypool);
828         if (pool) {
829                 if (pool->type != ldflag) {
830                         cf_log_err(cf_sectiontoitem(cs), "Inconsistent ldflag for server pool \"%s\"", name);
831                         return 0;
832                 }
833
834                 if (pool->server_type != type) {
835                         cf_log_err(cf_sectiontoitem(cs), "Inconsistent home server type for server pool \"%s\"", name);
836                         return 0;
837                 }
838         }
839
840         myhome.name = name;
841         myhome.type = type;
842         home = rbtree_finddata(home_servers_byname, &myhome);
843         if (home) {
844                 if (strcmp(home->secret, secret) != 0) {
845                         cf_log_err(cf_sectiontoitem(cs), "Inconsistent shared secret for home server \"%s\"", name);
846                         return 0;
847                 }
848
849                 if (home->type != type) {
850                         cf_log_err(cf_sectiontoitem(cs), "Inconsistent type for home server \"%s\"", name);
851                         return 0;
852                 }
853
854                 /*
855                  *      See if the home server is already listed
856                  *      in the pool.  If so, do nothing else.
857                  */
858                 if (pool) for (i = 0; i < pool->num_home_servers; i++) {
859                         if (pool->servers[i] == home) {
860                                 return 1;
861                         }
862                 }
863         }
864
865         /*
866          *      If we do have a pool, check that there is room to
867          *      insert the home server we've found, or the one that we
868          *      create here.
869          *
870          *      Note that we insert it into the LAST available
871          *      position, in order to maintain the same order as in
872          *      the configuration files.
873          */
874         insert_point = -1;
875         if (pool) {
876                 for (i = pool->num_home_servers - 1; i >= 0; i--) {
877                         if (pool->servers[i]) break;
878
879                         if (!pool->servers[i]) {
880                                 insert_point = i;
881                         }
882                 }
883
884                 if (insert_point < 0) {
885                         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);
886                         return 0;
887                 }
888         }
889
890         /*
891          *      No home server, allocate one.
892          */
893         if (!home) {
894                 const char *p;
895                 char *q;
896
897                 home = rad_malloc(sizeof(*home));
898                 memset(home, 0, sizeof(*home));
899
900                 home->name = name;
901                 home->hostname = name;
902                 home->type = type;
903                 home->secret = secret;
904                 home->cs = cs;
905
906                 p = strchr(name, ':');
907                 if (!p) {
908                         if (type == HOME_TYPE_AUTH) {
909                                 home->port = PW_AUTH_UDP_PORT;
910                         } else {
911                                 home->port = PW_ACCT_UDP_PORT;
912                         }
913
914                         p = name;
915                         q = NULL;
916
917                 } else if (p == name) {
918                                 cf_log_err(cf_sectiontoitem(cs),
919                                            "Invalid hostname %s.",
920                                            name);
921                                 free(home);
922                                 return 0;
923
924                 } else {
925                         home->port = atoi(p + 1);
926                         if ((home->port == 0) || (home->port > 65535)) {
927                                 cf_log_err(cf_sectiontoitem(cs),
928                                            "Invalid port %s.",
929                                            p + 1);
930                                 free(home);
931                                 return 0;
932                         }
933
934                         q = rad_malloc((p - name) + 1);
935                         memcpy(q, name, (p - name));
936                         q[p - name] = '\0';
937                         p = q;
938                 }
939
940                 if (!server) {
941                         if (ip_hton(p, AF_UNSPEC, &home->ipaddr) < 0) {
942                                 cf_log_err(cf_sectiontoitem(cs),
943                                            "Failed looking up hostname %s.",
944                                            p);
945                                 free(home);
946                                 free(q);
947                                 return 0;
948                         }
949                 } else {
950                         home->ipaddr.af = AF_UNSPEC;
951                         home->server = server;
952                 }
953                 free(q);
954
955                 /*
956                  *      Use the old-style configuration.
957                  */
958                 home->max_outstanding = 65535*16;
959                 home->zombie_period = rc->retry_delay * rc->retry_count;
960                 if (home->zombie_period == 0) home->zombie_period =30;
961                 home->response_window = home->zombie_period - 1;
962
963                 home->ping_check = HOME_PING_CHECK_NONE;
964
965                 home->revive_interval = rc->dead_time;
966
967                 if (rbtree_finddata(home_servers_byaddr, home)) {
968                         cf_log_err(cf_sectiontoitem(cs), "Home server %s has the same IP address and/or port as another home server.", name);
969                         free(home);
970                         return 0;
971                 }
972
973                 if (!rbtree_insert(home_servers_byname, home)) {
974                         cf_log_err(cf_sectiontoitem(cs), "Internal error adding home server %s.", name);
975                         free(home);
976                         return 0;
977                 }
978
979                 if (!rbtree_insert(home_servers_byaddr, home)) {
980                         rbtree_deletebydata(home_servers_byname, home);
981                         cf_log_err(cf_sectiontoitem(cs), "Internal error adding home server %s.", name);
982                         free(home);
983                         return 0;
984                 }
985         }
986
987         /*
988          *      We now have a home server, see if we can insert it
989          *      into pre-existing pool.
990          */
991         if (insert_point >= 0) {
992                 rad_assert(pool != NULL);
993                 pool->servers[insert_point] = home;
994                 return 1;
995         }
996
997         rad_assert(pool == NULL);
998         rad_assert(home != NULL);
999
1000         /*
1001          *      Count the old-style realms of this name.
1002          */
1003         num_home_servers = 0;
1004         for (subcs = cf_section_find_next(cs, NULL, "realm");
1005              subcs != NULL;
1006              subcs = cf_section_find_next(cs, subcs, "realm")) {
1007                 const char *this = cf_section_name2(subcs);
1008
1009                 if (!this || (strcmp(this, realm) != 0)) continue;
1010                 num_home_servers++;
1011         }
1012
1013         if (num_home_servers == 0) {
1014                 cf_log_err(cf_sectiontoitem(cs), "Internal error counting pools for home server %s.", name);
1015                 free(home);
1016                 return 0;
1017         }
1018
1019         pool = server_pool_alloc(realm, ldflag, type, num_home_servers);
1020         pool->cs = cs;
1021
1022         pool->servers[0] = home;
1023
1024         if (!rbtree_insert(home_pools_byname, pool)) {
1025                 rad_assert("Internal sanity check failed");
1026                 return 0;
1027         }
1028
1029         *pool_p = pool;
1030
1031         return 1;
1032 }
1033
1034 static int old_realm_config(realm_config_t *rc, CONF_SECTION *cs, REALM *r)
1035 {
1036         const char *host;
1037         const char *secret = NULL;
1038         home_pool_type_t ldflag;
1039         CONF_PAIR *cp;
1040
1041         cp = cf_pair_find(cs, "ldflag");
1042         ldflag = HOME_POOL_FAIL_OVER;
1043         if (cp) {
1044                 host = cf_pair_value(cp);
1045                 if (!host) {
1046                         cf_log_err(cf_pairtoitem(cp), "No value specified for ldflag");
1047                         return 0;
1048                 }
1049
1050                 if (strcasecmp(host, "fail_over") == 0) {
1051                         cf_log_info(cs, "\tldflag = fail_over");
1052                         
1053                 } else if (strcasecmp(host, "round_robin") == 0) {
1054                         ldflag = HOME_POOL_LOAD_BALANCE;
1055                         cf_log_info(cs, "\tldflag = round_robin");
1056                         
1057                 } else {
1058                         cf_log_err(cf_sectiontoitem(cs), "Unknown value \"%s\" for ldflag", host);
1059                         return 0;
1060                 }
1061         } /* else don't print it. */
1062
1063         /*
1064          *      Allow old-style if it doesn't exist, or if it exists and
1065          *      it's LOCAL.
1066          */
1067         cp = cf_pair_find(cs, "authhost");
1068         if (cp) {
1069                 host = cf_pair_value(cp);
1070                 if (!host) {
1071                         cf_log_err(cf_pairtoitem(cp), "No value specified for authhost");
1072                         return 0;
1073                 }
1074
1075                 if (strcmp(host, "LOCAL") != 0) {
1076                         cp = cf_pair_find(cs, "secret");
1077                         if (!cp) {
1078                                 cf_log_err(cf_sectiontoitem(cs), "No shared secret supplied for realm: %s", r->name);
1079                                 return 0;
1080                         }
1081
1082                         secret = cf_pair_value(cp);
1083                         if (!secret) {
1084                                 cf_log_err(cf_pairtoitem(cp), "No value specified for secret");
1085                                 return 0;
1086                         }
1087                 }
1088                         
1089                 cf_log_info(cs, "\tauthhost = %s",  host);
1090
1091                 if (!old_server_add(rc, cs, r->name, host, secret, ldflag,
1092                                     &r->auth_pool, HOME_TYPE_AUTH, NULL)) {
1093                         return 0;
1094                 }
1095         }
1096
1097         cp = cf_pair_find(cs, "accthost");
1098         if (cp) {
1099                 host = cf_pair_value(cp);
1100                 if (!host) {
1101                         cf_log_err(cf_pairtoitem(cp), "No value specified for accthost");
1102                         return 0;
1103                 }
1104
1105                 /*
1106                  *      Don't look for a secret again if it was found
1107                  *      above.
1108                  */
1109                 if ((strcmp(host, "LOCAL") != 0) && !secret) {
1110                         cp = cf_pair_find(cs, "secret");
1111                         if (!cp) {
1112                                 cf_log_err(cf_sectiontoitem(cs), "No shared secret supplied for realm: %s", r->name);
1113                                 return 0;
1114                         }
1115                         
1116                         secret = cf_pair_value(cp);
1117                         if (!secret) {
1118                                 cf_log_err(cf_pairtoitem(cp), "No value specified for secret");
1119                                 return 0;
1120                         }
1121                 }
1122                 
1123                 cf_log_info(cs, "\taccthost = %s", host);
1124
1125                 if (!old_server_add(rc, cs, r->name, host, secret, ldflag,
1126                                     &r->acct_pool, HOME_TYPE_ACCT, NULL)) {
1127                         return 0;
1128                 }
1129         }
1130
1131         cp = cf_pair_find(cs, "virtual_server");
1132         if (cp) {
1133                 host = cf_pair_value(cp);
1134                 if (!host) {
1135                         cf_log_err(cf_pairtoitem(cp), "No value specified for virtual_server");
1136                         return 0;
1137                 }
1138
1139                 cf_log_info(cs, "\tvirtual_server = %s", host);
1140
1141                 if (!old_server_add(rc, cs, r->name, host, "", ldflag,
1142                                     &r->auth_pool, HOME_TYPE_AUTH, host)) {
1143                         return 0;
1144                 }
1145                 if (!old_server_add(rc, cs, r->name, host, "", ldflag,
1146                                     &r->acct_pool, HOME_TYPE_ACCT, host)) {
1147                         return 0;
1148                 }
1149         }
1150
1151         if (secret) cf_log_info(cs, "\tsecret = %s", secret);
1152
1153         return 1;
1154
1155 }
1156
1157
1158 static int add_pool_to_realm(realm_config_t *rc, CONF_SECTION *cs,
1159                              const char *name, home_pool_t **dest,
1160                              int server_type, int do_print)
1161 {
1162         home_pool_t mypool, *pool;
1163
1164         mypool.name = name;
1165         mypool.server_type = server_type;
1166
1167         pool = rbtree_finddata(home_pools_byname, &mypool);
1168         if (!pool) {
1169                 CONF_SECTION *pool_cs;
1170
1171                 pool_cs = cf_section_sub_find_name2(rc->cs,
1172                                                     "home_server_pool",
1173                                                     name);
1174                 if (!pool_cs) {
1175                         pool_cs = cf_section_sub_find_name2(rc->cs,
1176                                                             "server_pool",
1177                                                             name);
1178                 }
1179                 if (!pool_cs) {
1180                         cf_log_err(cf_sectiontoitem(cs), "Failed to find home_server_pool \"%s\"", name);
1181                         return 0;
1182                 }
1183
1184                 if (!server_pool_add(rc, pool_cs, server_type, do_print)) {
1185                         return 0;
1186                 }
1187
1188                 pool = rbtree_finddata(home_pools_byname, &mypool);
1189                 if (!pool) {
1190                         radlog(L_ERR, "Internal sanity check failed in add_pool_to_realm");
1191                         return 0;
1192                 }
1193         }
1194
1195         if (pool->server_type != server_type) {
1196                 cf_log_err(cf_sectiontoitem(cs), "Incompatible home_server_pool \"%s\" (mixed auth_pool / acct_pool)", name);
1197                 return 0;
1198         }
1199
1200         *dest = pool;
1201
1202         return 1;
1203 }
1204
1205
1206 static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
1207 {
1208         const char *name2;
1209         REALM *r = NULL;
1210         CONF_PAIR *cp;
1211         home_pool_t *auth_pool, *acct_pool;
1212         const char *auth_pool_name, *acct_pool_name;
1213
1214         name2 = cf_section_name1(cs);
1215         if (!name2 || (strcasecmp(name2, "realm") != 0)) {
1216                 cf_log_err(cf_sectiontoitem(cs), "Section is not a realm.");
1217                 return 0;
1218         }
1219
1220         name2 = cf_section_name2(cs);
1221         if (!name2) {
1222                 cf_log_err(cf_sectiontoitem(cs), "Realm section is missing the realm name.");
1223                 return 0;
1224         }
1225
1226         auth_pool = acct_pool = NULL;
1227         auth_pool_name = acct_pool_name = NULL;
1228
1229         /*
1230          *      Prefer new configuration to old one.
1231          */
1232         cp = cf_pair_find(cs, "pool");
1233         if (!cp) cp = cf_pair_find(cs, "home_server_pool");
1234         if (cp) auth_pool_name = cf_pair_value(cp);
1235         if (cp && auth_pool_name) {
1236                 acct_pool_name = auth_pool_name;
1237                 if (!add_pool_to_realm(rc, cs,
1238                                        auth_pool_name, &auth_pool,
1239                                        HOME_TYPE_AUTH, 1)) {
1240                         return 0;
1241                 }
1242                 if (!add_pool_to_realm(rc, cs,
1243                                        auth_pool_name, &acct_pool,
1244                                        HOME_TYPE_ACCT, 0)) {
1245                         return 0;
1246                 }
1247         }
1248
1249         cp = cf_pair_find(cs, "auth_pool");
1250         if (cp) auth_pool_name = cf_pair_value(cp);
1251         if (cp && auth_pool_name) {
1252                 if (auth_pool) {
1253                         cf_log_err(cf_sectiontoitem(cs), "Cannot use \"pool\" and \"auth_pool\" at the same time.");
1254                         return 0;
1255                 }
1256                 if (!add_pool_to_realm(rc, cs,
1257                                        auth_pool_name, &auth_pool,
1258                                        HOME_TYPE_AUTH, 1)) {
1259                         return 0;
1260                 }
1261         }
1262
1263         cp = cf_pair_find(cs, "acct_pool");
1264         if (cp) acct_pool_name = cf_pair_value(cp);
1265         if (cp && acct_pool_name) {
1266                 int do_print = TRUE;
1267
1268                 if (acct_pool) {
1269                         cf_log_err(cf_sectiontoitem(cs), "Cannot use \"pool\" and \"acct_pool\" at the same time.");
1270                         return 0;
1271                 }
1272
1273                 if (!auth_pool ||
1274                     (strcmp(auth_pool_name, acct_pool_name) != 0)) {
1275                         do_print = TRUE;
1276                 }
1277
1278                 if (!add_pool_to_realm(rc, cs,
1279                                        acct_pool_name, &acct_pool,
1280                                        HOME_TYPE_ACCT, do_print)) {
1281                         return 0;
1282                 }
1283         }
1284
1285         cf_log_info(cs, " realm %s {", name2);
1286
1287         /*
1288          *      The realm MAY already exist if it's an old-style realm.
1289          *      In that case, merge the old-style realm with this one.
1290          */
1291         r = realm_find2(name2);
1292         if (r && (strcmp(r->name, name2) == 0)) {
1293                 if (cf_pair_find(cs, "auth_pool") ||
1294                     cf_pair_find(cs, "acct_pool")) {
1295                         cf_log_err(cf_sectiontoitem(cs), "Duplicate realm \"%s\"", name2);
1296                         goto error;
1297                 }
1298
1299                 if (!old_realm_config(rc, cs, r)) {
1300                         goto error;
1301                 }
1302
1303                 cf_log_info(cs, " } # realm %s", name2);
1304                 return 1;
1305         }
1306
1307 #ifdef HAVE_REGEX_H
1308         if (name2[0] == '~') {
1309                 regex_t reg;
1310                 
1311                 /*
1312                  *      Include substring matches.
1313                  */
1314                 if (regcomp(&reg, name2 + 1,
1315                             REG_EXTENDED | REG_NOSUB | REG_ICASE) != 0) {
1316                         cf_log_err(cf_sectiontoitem(cs),
1317                                    "Invalid regex in realm \"%s\"", name2);
1318                         goto error;
1319                 }
1320                 regfree(&reg);
1321         }
1322 #endif
1323
1324         r = rad_malloc(sizeof(*r));
1325         memset(r, 0, sizeof(*r));
1326
1327         r->name = name2;
1328         r->auth_pool = auth_pool;
1329         r->acct_pool = acct_pool;
1330         r->striprealm = 1;
1331
1332         if (auth_pool_name &&
1333             (auth_pool_name == acct_pool_name)) { /* yes, ptr comparison */
1334                 cf_log_info(cs, "\tpool = %s", auth_pool_name);
1335         } else {
1336                 if (auth_pool_name) cf_log_info(cs, "\tauth_pool = %s", auth_pool_name);
1337                 if (acct_pool_name) cf_log_info(cs, "\tacct_pool = %s", acct_pool_name);
1338         }
1339
1340         cp = cf_pair_find(cs, "nostrip");
1341         if (cp && (cf_pair_value(cp) == NULL)) {
1342                 r->striprealm = 0;
1343                 cf_log_info(cs, "\tnostrip");
1344         }
1345
1346         /*
1347          *      We're a new-style realm.  Complain if we see the old
1348          *      directives.
1349          */
1350         if (r->auth_pool || r->acct_pool) {
1351                 if (((cp = cf_pair_find(cs, "authhost")) != NULL) ||
1352                     ((cp = cf_pair_find(cs, "accthost")) != NULL) ||
1353                     ((cp = cf_pair_find(cs, "secret")) != NULL) ||
1354                     ((cp = cf_pair_find(cs, "ldflag")) != NULL)) {
1355                         DEBUG2("WARNING: Ignoring old-style configuration entry \"%s\" in realm \"%s\"", cf_pair_attr(cp), r->name);
1356                 }
1357
1358
1359                 /*
1360                  *      The realm MAY be an old-style realm, as there
1361                  *      was no auth_pool or acct_pool.  Double-check
1362                  *      it, just to be safe.
1363                  */
1364         } else if (!old_realm_config(rc, cs, r)) {
1365                 goto error;
1366         }
1367
1368 #ifdef HAVE_REGEX_H
1369         /*
1370          *      It's a regex.  Add it to a separate list.
1371          */
1372         if (name2[0] == '~') {
1373                 realm_regex_t *rr, **last;
1374
1375                 rr = rad_malloc(sizeof(*rr));
1376                 
1377                 last = &realms_regex;
1378                 while (*last) last = &((*last)->next);  /* O(N^2)... sue me. */
1379
1380                 r->name = name2;
1381                 rr->realm = r;
1382                 rr->next = NULL;
1383
1384                 *last = rr;
1385
1386                 cf_log_info(cs, " }");
1387                 return 1;
1388         }
1389 #endif
1390
1391         if (!rbtree_insert(realms_byname, r)) {
1392                 rad_assert("Internal sanity check failed");
1393                 goto error;
1394         }
1395
1396         cf_log_info(cs, " }");
1397
1398         return 1;
1399
1400  error:
1401         cf_log_info(cs, " } # realm %s", name2);
1402         free(r);
1403         return 0;
1404 }
1405
1406
1407 int realms_init(CONF_SECTION *config)
1408 {
1409         CONF_SECTION *cs;
1410         realm_config_t *rc, *old_rc;
1411
1412         if (realms_byname) return 1;
1413
1414         realms_byname = rbtree_create(realm_name_cmp, free, 0);
1415         if (!realms_byname) {
1416                 realms_free();
1417                 return 0;
1418         }
1419
1420         home_servers_byaddr = rbtree_create(home_server_addr_cmp, free, 0);
1421         if (!home_servers_byaddr) {
1422                 realms_free();
1423                 return 0;
1424         }
1425
1426         home_servers_byname = rbtree_create(home_server_name_cmp, NULL, 0);
1427         if (!home_servers_byname) {
1428                 realms_free();
1429                 return 0;
1430         }
1431
1432         home_pools_byname = rbtree_create(home_pool_name_cmp, free, 0);
1433         if (!home_pools_byname) {
1434                 realms_free();
1435                 return 0;
1436         }
1437
1438         rc = rad_malloc(sizeof(*rc));
1439         memset(rc, 0, sizeof(*rc));
1440         rc->cs = config;
1441
1442         cs = cf_subsection_find_next(config, NULL, "proxy");
1443         if (cs) {
1444                 cf_section_parse(cs, rc, proxy_config);
1445         } else {
1446                 rc->dead_time = DEAD_TIME;
1447                 rc->retry_count = RETRY_COUNT;
1448                 rc->retry_delay = RETRY_DELAY;
1449                 rc->fallback = 0;
1450                 rc->wake_all_if_all_dead= 0;
1451         }
1452
1453         for (cs = cf_subsection_find_next(config, NULL, "realm");
1454              cs != NULL;
1455              cs = cf_subsection_find_next(config, cs, "realm")) {
1456                 if (!realm_add(rc, cs)) {
1457                         free(rc);
1458                         realms_free();
1459                         return 0;
1460                 }
1461         }
1462
1463         xlat_register("home_server", xlat_home_server, NULL);
1464         xlat_register("home_server_pool", xlat_server_pool, NULL);
1465
1466         /*
1467          *      Swap pointers atomically.
1468          */
1469         old_rc = realm_config;
1470         realm_config = rc;
1471         free(old_rc);
1472
1473         return 1;
1474 }
1475
1476 /*
1477  *      Find a realm where "name" might be the regex.
1478  */
1479 REALM *realm_find2(const char *name)
1480 {
1481         REALM myrealm;
1482         REALM *realm;
1483         
1484         if (!name) name = "NULL";
1485
1486         myrealm.name = name;
1487         realm = rbtree_finddata(realms_byname, &myrealm);
1488         if (realm) return realm;
1489
1490 #ifdef HAVE_REGEX_H
1491         if (realms_regex) {
1492                 realm_regex_t *this;
1493
1494                 for (this = realms_regex; this != NULL; this = this->next) {
1495                         if (strcmp(this->realm->name, name) == 0) {
1496                                 return this->realm;
1497                         }
1498                 }
1499         }
1500 #endif
1501
1502         /*
1503          *      Couldn't find a realm.  Look for DEFAULT.
1504          */
1505         myrealm.name = "DEFAULT";
1506         return rbtree_finddata(realms_byname, &myrealm);
1507 }
1508
1509
1510 /*
1511  *      Find a realm in the REALM list.
1512  */
1513 REALM *realm_find(const char *name)
1514 {
1515         REALM myrealm;
1516         REALM *realm;
1517         
1518         if (!name) name = "NULL";
1519
1520         myrealm.name = name;
1521         realm = rbtree_finddata(realms_byname, &myrealm);
1522         if (realm) return realm;
1523
1524 #ifdef HAVE_REGEX_H
1525         if (realms_regex) {
1526                 realm_regex_t *this;
1527
1528                 for (this = realms_regex; this != NULL; this = this->next) {
1529                         int compare;
1530                         regex_t reg;
1531
1532                         /*
1533                          *      Include substring matches.
1534                          */
1535                         if (regcomp(&reg, this->realm->name + 1,
1536                                     REG_EXTENDED | REG_NOSUB | REG_ICASE) != 0) {
1537                                 continue;
1538                         }
1539
1540                         compare = regexec(&reg, name, 0, NULL, 0);
1541                         regfree(&reg);
1542
1543                         if (compare == 0) return this->realm;
1544                 }
1545         }
1546 #endif
1547
1548         /*
1549          *      Couldn't find a realm.  Look for DEFAULT.
1550          */
1551         myrealm.name = "DEFAULT";
1552         return rbtree_finddata(realms_byname, &myrealm);
1553 }
1554
1555
1556 home_server *home_server_ldb(const char *realmname,
1557                              home_pool_t *pool, REQUEST *request)
1558 {
1559         int             start;
1560         int             count;
1561         home_server     *found = NULL;
1562         VALUE_PAIR      *vp;
1563
1564         start = 0;
1565
1566         /*
1567          *      Determine how to pick choose the home server.
1568          */
1569         switch (pool->type) {
1570                 uint32_t hash;
1571
1572                 /*
1573                  *      For load-balancing by client IP address, we
1574                  *      pick a home server by hashing the client IP.
1575                  *
1576                  *      This isn't as even a load distribution as
1577                  *      tracking the State attribute, but it's better
1578                  *      than nothing.
1579                  */
1580         case HOME_POOL_CLIENT_BALANCE:
1581                 switch (request->packet->src_ipaddr.af) {
1582                 case AF_INET:
1583                         hash = fr_hash(&request->packet->src_ipaddr.ipaddr.ip4addr,
1584                                          sizeof(request->packet->src_ipaddr.ipaddr.ip4addr));
1585                         break;
1586                 case AF_INET6:
1587                         hash = fr_hash(&request->packet->src_ipaddr.ipaddr.ip6addr,
1588                                          sizeof(request->packet->src_ipaddr.ipaddr.ip6addr));
1589                         break;
1590                 default:
1591                         hash = 0;
1592                         break;
1593                 }
1594                 start = hash % pool->num_home_servers;
1595                 break;
1596
1597         case HOME_POOL_CLIENT_PORT_BALANCE:
1598                 switch (request->packet->src_ipaddr.af) {
1599                 case AF_INET:
1600                         hash = fr_hash(&request->packet->src_ipaddr.ipaddr.ip4addr,
1601                                          sizeof(request->packet->src_ipaddr.ipaddr.ip4addr));
1602                         break;
1603                 case AF_INET6:
1604                         hash = fr_hash(&request->packet->src_ipaddr.ipaddr.ip6addr,
1605                                          sizeof(request->packet->src_ipaddr.ipaddr.ip6addr));
1606                         break;
1607                 default:
1608                         hash = 0;
1609                         break;
1610                 }
1611                 fr_hash_update(&request->packet->src_port,
1612                                  sizeof(request->packet->src_port), hash);
1613                 start = hash % pool->num_home_servers;
1614                 break;
1615
1616         case HOME_POOL_KEYED_BALANCE:
1617                 if ((vp = pairfind(request->config_items, PW_LOAD_BALANCE_KEY)) != NULL) {
1618                         hash = fr_hash(vp->vp_strvalue, vp->length);
1619                         start = hash % pool->num_home_servers;
1620                         break;
1621                 }
1622                 /* FALL-THROUGH */
1623                                 
1624         case HOME_POOL_LOAD_BALANCE:
1625                 found = pool->servers[0];
1626
1627         default:
1628                 start = 0;
1629                 break;
1630         }
1631
1632         /*
1633          *      Starting with the home server we chose, loop through
1634          *      all home servers.  If the current one is dead, skip
1635          *      it.  If it is too busy, skip it.
1636          *
1637          *      Otherwise, use it.
1638          */
1639         for (count = 0; count < pool->num_home_servers; count++) {
1640                 home_server *home = pool->servers[(start + count) % pool->num_home_servers];
1641
1642                 if (home->state == HOME_STATE_IS_DEAD) {
1643                         continue;
1644                 }
1645
1646                 /*
1647                  *      This home server is too busy.  Choose another one.
1648                  */
1649                 if (home->currently_outstanding >= home->max_outstanding) {
1650                         continue;
1651                 }
1652
1653                 if (pool->type != HOME_POOL_LOAD_BALANCE) {
1654                         return home;
1655                 }
1656
1657                 DEBUG3("PROXY %s %d\t%s %d",
1658                        found->name, found->currently_outstanding,
1659                        home->name, home->currently_outstanding);
1660
1661                 /*
1662                  *      Prefer this server if it's less busy than the
1663                  *      one we previously found.
1664                  */
1665                 if (home->currently_outstanding < found->currently_outstanding) {
1666                         DEBUG3("Choosing %s: It's less busy than %s",
1667                                home->name, found->name);
1668                         found = home;
1669                         continue;
1670                 }
1671
1672                 /*
1673                  *      Ignore servers which are busier than the one
1674                  *      we found.
1675                  */
1676                 if (home->currently_outstanding > found->currently_outstanding) {
1677                         DEBUG3("Skipping %s: It's busier than %s",
1678                                home->name, found->name);
1679                         continue;
1680                 }
1681
1682                 if (home->total_requests_sent < found->total_requests_sent) {
1683                         DEBUG3("Choosing %s: It's been less busy than %s",
1684                                home->name, found->name);
1685                         found = home;
1686                         continue;
1687                 }
1688
1689                 if (home->total_requests_sent > found->total_requests_sent) {
1690                         DEBUG3("Skipping %s: It's been busier than %s",
1691                                home->name, found->name);
1692                         continue;
1693                 }
1694
1695                 /*
1696                  *      From the list of servers which have the same
1697                  *      load, choose one at random.
1698                  */
1699                 if (((count + 1) * (fr_rand() & 0xffff)) < (uint32_t) 0x10000) {
1700                         found = home;
1701                 }
1702
1703         } /* loop over the home servers */
1704
1705         if (found) return found;
1706
1707         /*
1708          *      There's a fallback if they're all dead.
1709          */
1710         if (pool->fallback) {
1711                 return pool->fallback;
1712         }
1713
1714         /*
1715          *      No live match found, and no fallback to the "DEFAULT"
1716          *      realm.  We fix this by blindly marking all servers as
1717          *      "live".  But only do it for ones that don't support
1718          *      "pings", as they will be marked live when they
1719          *      actually are live.
1720          */
1721         if (!realm_config->fallback &&
1722             realm_config->wake_all_if_all_dead) {
1723                 home_server *lb = NULL;
1724
1725                 for (count = 0; count < pool->num_home_servers; count++) {
1726                         home_server *home = pool->servers[count];
1727
1728                         if ((home->state == HOME_STATE_IS_DEAD) &&
1729                             (home->ping_check == HOME_PING_CHECK_NONE)) {
1730                                 home->state = HOME_STATE_ALIVE;
1731                                 if (!lb) lb = home;
1732                         }
1733                 }
1734
1735                 if (lb) return lb;
1736         }
1737
1738         /*
1739          *      Still nothing.  Look up the DEFAULT realm, but only
1740          *      if we weren't looking up the NULL or DEFAULT realms.
1741          */
1742         if (realm_config->fallback &&
1743             realmname &&
1744             (strcmp(realmname, "NULL") != 0) &&
1745             (strcmp(realmname, "DEFAULT") != 0)) {
1746                 REALM *rd = realm_find("DEFAULT");
1747
1748                 if (!rd) return NULL;
1749
1750                 pool = NULL;
1751                 if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
1752                         pool = rd->auth_pool;
1753
1754                 } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
1755                         pool = rd->acct_pool;
1756                 }
1757                 if (!pool) return NULL;
1758
1759                 DEBUG2("  Realm %s has no live home servers.  Falling back to the DEFAULT realm.", realmname);
1760                 return home_server_ldb(rd->name, pool, request);
1761         }
1762
1763         /*
1764          *      Still haven't found anything.  Oh well.
1765          */
1766         return NULL;
1767 }
1768
1769
1770 home_server *home_server_find(fr_ipaddr_t *ipaddr, int port)
1771 {
1772         home_server myhome;
1773
1774         memset(&myhome, 0, sizeof(myhome));
1775         myhome.ipaddr = *ipaddr;
1776         myhome.port = port;
1777         myhome.server = NULL;   /* we're not called for internal proxying */
1778
1779         return rbtree_finddata(home_servers_byaddr, &myhome);
1780 }