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