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