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