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