2d8e5deb5f7f40c64b7b1fbaa8284daa7d0e8ace
[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 static rbtree_t *realms_byname = NULL;
36
37 static rbtree_t *home_servers_byaddr = NULL;
38 static rbtree_t *home_servers_byname = NULL;
39
40 static rbtree_t *home_pools_byname = NULL;
41
42 static int realm_name_cmp(const void *one, const void *two)
43 {
44         const REALM *a = one;
45         const REALM *b = two;
46
47         return strcasecmp(a->name, b->name);
48 }
49
50
51 static int home_server_name_cmp(const void *one, const void *two)
52 {
53         const home_server *a = one;
54         const home_server *b = two;
55
56         if (a->type < b->type) return -1;
57         if (a->type > b->type) return +1;
58
59         return strcasecmp(a->name, b->name);
60 }
61
62 static int home_server_addr_cmp(const void *one, const void *two)
63 {
64         const home_server *a = one;
65         const home_server *b = two;
66
67         if (a->port < b->port) return -1;
68         if (a->port > b->port) return +1;
69
70         return lrad_ipaddr_cmp(&a->ipaddr, &b->ipaddr);
71 }
72
73
74 static int home_pool_name_cmp(const void *one, const void *two)
75 {
76         const home_pool_t *a = one;
77         const home_pool_t *b = two;
78
79         if (a->server_type < b->server_type) return -1;
80         if (a->server_type > b->server_type) return +1;
81
82         return strcasecmp(a->name, b->name);
83 }
84
85
86 void realms_free(void)
87 {
88         rbtree_free(home_servers_byname);
89         home_servers_byname = NULL;
90
91         rbtree_free(home_servers_byaddr);
92         home_servers_byaddr = NULL;
93
94         rbtree_free(home_pools_byname);
95         home_pools_byname = NULL;
96
97         rbtree_free(realms_byname);
98         realms_byname = NULL;
99 }
100
101
102 int realms_init(CONF_SECTION *config)
103 {
104         CONF_SECTION *cs;
105
106         if (realms_byname) return 1;
107
108         realms_byname = rbtree_create(realm_name_cmp, free, 0);
109         if (!realms_byname) {
110                 realms_free();
111                 return 0;
112         }
113
114         home_servers_byaddr = rbtree_create(home_server_addr_cmp, free, 0);
115         if (!home_servers_byaddr) {
116                 realms_free();
117                 return 0;
118         }
119
120         home_servers_byname = rbtree_create(home_server_name_cmp, NULL, 0);
121         if (!home_servers_byname) {
122                 realms_free();
123                 return 0;
124         }
125
126         home_pools_byname = rbtree_create(home_pool_name_cmp, free, 0);
127         if (!home_pools_byname) {
128                 realms_free();
129                 return 0;
130         }
131
132         for (cs = cf_subsection_find_next(config, NULL, "realm");
133              cs != NULL;
134              cs = cf_subsection_find_next(config, cs, "realm")) {
135                 if (!realm_add(cs)) {
136                         realms_free();
137                         return 0;
138                 }
139         }
140
141         return 1;
142 }
143
144 static struct in_addr hs_ip4addr;
145 static struct in6_addr hs_ip6addr;
146 static char *hs_type = NULL;
147 static char *hs_check = NULL;
148
149 static CONF_PARSER home_server_config[] = {
150         { "ipaddr",  PW_TYPE_IPADDR,
151           0, &hs_ip4addr,  NULL },
152         { "ipv6addr",  PW_TYPE_IPV6ADDR,
153           0, &hs_ip6addr, NULL },
154
155         { "port", PW_TYPE_INTEGER,
156           offsetof(home_server,port), NULL,   "0" },
157
158         { "type",  PW_TYPE_STRING_PTR,
159           0, &hs_type, NULL },
160
161         { "secret",  PW_TYPE_STRING_PTR,
162           offsetof(home_server,secret), NULL,  NULL},
163
164         { "response_window", PW_TYPE_INTEGER,
165           offsetof(home_server,response_window), NULL,   "30" },
166         { "max_outstanding", PW_TYPE_INTEGER,
167           offsetof(home_server,max_outstanding), NULL,   "65536" },
168
169         { "zombie_period", PW_TYPE_INTEGER,
170           offsetof(home_server,zombie_period), NULL,   "40" },
171         { "status_check", PW_TYPE_STRING_PTR,
172           0, &hs_check,   "none" },
173         { "ping_check", PW_TYPE_STRING_PTR,
174           0, &hs_check,   "none" },
175
176         { "ping_interval", PW_TYPE_INTEGER,
177           offsetof(home_server,ping_interval), NULL,   "30" },
178         { "check_interval", PW_TYPE_INTEGER,
179           offsetof(home_server,ping_interval), NULL,   "30" },
180         { "num_answers_to_alive", PW_TYPE_INTEGER,
181           offsetof(home_server,num_pings_to_alive), NULL,   "3" },
182         { "num_pings_to_alive", PW_TYPE_INTEGER,
183           offsetof(home_server,num_pings_to_alive), NULL,   "3" },
184         { "revive_interval", PW_TYPE_INTEGER,
185           offsetof(home_server,revive_interval), NULL,   "300" },
186         { "status_check_timeout", PW_TYPE_INTEGER,
187           offsetof(home_server,ping_timeout), NULL,   "4" },
188
189         { "username",  PW_TYPE_STRING_PTR,
190           offsetof(home_server,ping_user_name), NULL,  NULL},
191         { "password",  PW_TYPE_STRING_PTR,
192           offsetof(home_server,ping_user_password), NULL,  NULL},
193
194         { NULL, -1, 0, NULL, NULL }             /* end the list */
195
196 };
197
198
199 static int home_server_add(CONF_SECTION *cs, int type)
200 {
201         const char *name2;
202         home_server *home;
203         int dual = FALSE;
204
205         name2 = cf_section_name1(cs);
206         if (!name2 || (strcasecmp(name2, "home_server") != 0)) {
207                 cf_log_err(cf_sectiontoitem(cs),
208                            "Section is not a home_server.");
209                 return 0;
210         }
211
212         name2 = cf_section_name2(cs);
213         if (!name2) {
214                 cf_log_err(cf_sectiontoitem(cs),
215                            "Home server section is missing a name.");
216                 return 0;
217         }
218
219         home = rad_malloc(sizeof(*home));
220         memset(home, 0, sizeof(*home));
221
222         home->name = name2;
223
224         memset(&hs_ip4addr, 0, sizeof(hs_ip4addr));
225         memset(&hs_ip6addr, 0, sizeof(hs_ip6addr));
226         cf_section_parse(cs, home, home_server_config);
227
228         if ((htonl(hs_ip4addr.s_addr) == INADDR_NONE) &&
229             IN6_IS_ADDR_UNSPECIFIED(&hs_ip6addr)) {
230                 cf_log_err(cf_sectiontoitem(cs),
231                            "No IPv4 or IPv6 address defined for home server %s.",
232                            name2);
233                 free(home);
234                 free(hs_type);
235                 hs_type = NULL;
236                 free(hs_check);
237                 hs_check = NULL;
238                 return 0;
239         }
240
241         /*
242          *      Figure out which one to use.
243          *
244          *      FIXME: Set hostname to something?
245          */
246         if (htonl(hs_ip4addr.s_addr) != INADDR_NONE) {
247                 home->ipaddr.af = AF_INET;
248                 home->ipaddr.ipaddr.ip4addr = hs_ip4addr;
249
250         } else if (!IN6_IS_ADDR_UNSPECIFIED(&hs_ip6addr)) {
251                 home->ipaddr.af = AF_INET6;
252                 home->ipaddr.ipaddr.ip6addr = hs_ip6addr;
253
254         } else {
255                 cf_log_err(cf_sectiontoitem(cs),
256                            "Internal sanity check failed for home server %s.",
257                            name2);
258                 free(home);
259                 free(hs_type);
260                 hs_type = NULL;
261                 free(hs_check);
262                 hs_check = NULL;
263                 return 0;
264         }
265
266         if (!home->port || (home->port > 65535)) {
267                 cf_log_err(cf_sectiontoitem(cs),
268                            "No port, or invalid port defined for home server %s.",
269                            name2);
270                 free(home);
271                 free(hs_type);
272                 hs_type = NULL;
273                 free(hs_check);
274                 hs_check = NULL;
275                 return 0;
276         }
277
278         if (0) {
279                 cf_log_err(cf_sectiontoitem(cs),
280                            "Fatal error!  Home server %s is ourselves!",
281                            name2);
282                 free(home);
283                 free(hs_type);
284                 hs_type = NULL;
285                 free(hs_check);
286                 hs_check = NULL;
287                 return 0;
288         }
289
290         if (strcasecmp(hs_type, "auth") == 0) {
291                 home->type = HOME_TYPE_AUTH;
292                 if (type != home->type) {
293                         cf_log_err(cf_sectiontoitem(cs),
294                                    "Server pool of \"acct\" servers cannot include home server %s of type \"auth\"",
295                                    name2);
296                         free(home);
297                         return 0;
298                 }
299
300         } else if (strcasecmp(hs_type, "acct") == 0) {
301                 home->type = HOME_TYPE_ACCT;
302                 if (type != home->type) {
303                         cf_log_err(cf_sectiontoitem(cs),
304                                    "Server pool of \"auth\" servers cannot include home server %s of type \"acct\"",
305                                    name2);
306                         free(home);
307                         return 0;
308                 }
309
310         } else if (strcasecmp(hs_type, "auth+acct") == 0) {
311                 home->type = HOME_TYPE_AUTH;
312                 dual = TRUE;
313
314         } else {
315                 cf_log_err(cf_sectiontoitem(cs),
316                            "Invalid type \"%s\" for home server %s.",
317                            hs_type, name2);
318                 free(home);
319                 free(hs_type);
320                 hs_type = NULL;
321                 free(hs_check);
322                 hs_check = NULL;
323                 return 0;
324         }
325         free(hs_type);
326         hs_type = NULL;
327
328         if (!home->secret) {
329                 cf_log_err(cf_sectiontoitem(cs),
330                            "No shared secret defined for home server %s.",
331                            name2);
332                 free(home);
333                 return 0;
334         }
335
336         if (strcasecmp(hs_check, "none") == 0) {
337                 home->ping_check = HOME_PING_CHECK_NONE;
338
339         } else if (strcasecmp(hs_check, "status-server") == 0) {
340                 home->ping_check = HOME_PING_CHECK_STATUS_SERVER;
341
342         } else if (strcasecmp(hs_check, "request") == 0) {
343                 home->ping_check = HOME_PING_CHECK_REQUEST;
344
345         } else {
346                 cf_log_err(cf_sectiontoitem(cs),
347                            "Invalid ping_check \"%s\" for home server %s.",
348                            hs_check, name2);
349                 free(home);
350                 free(hs_check);
351                 hs_check = NULL;
352                 return 0;
353         }
354         free(hs_check);
355         hs_check = NULL;
356
357         if ((home->ping_check != HOME_PING_CHECK_NONE) &&
358             (home->ping_check != HOME_PING_CHECK_STATUS_SERVER)) {
359                 if (!home->ping_user_name) {
360                         cf_log_err(cf_sectiontoitem(cs), "You must supply a user name to enable ping checks");
361                         free(home);
362                         return 0;
363                 }
364
365                 if ((home->type == HOME_TYPE_AUTH) &&
366                     !home->ping_user_password) {
367                         cf_log_err(cf_sectiontoitem(cs), "You must supply a password to enable ping checks");
368                         free(home);
369                         return 0;
370                 }
371         }
372
373         if (rbtree_finddata(home_servers_byaddr, home)) {
374                 DEBUG2("Ignoring duplicate home server %s.", name2);
375                 return 1;
376         }
377
378         if (!rbtree_insert(home_servers_byname, home)) {
379                 cf_log_err(cf_sectiontoitem(cs),
380                            "Internal error adding home server %s.",
381                            name2);
382                 free(home);
383                 return 0;
384         }
385
386         if (!rbtree_insert(home_servers_byaddr, home)) {
387                 rbtree_deletebydata(home_servers_byname, home);
388                 cf_log_err(cf_sectiontoitem(cs),
389                            "Internal error adding home server %s.",
390                            name2);
391                 free(home);
392                 return 0;
393         }
394
395         if (home->response_window < 5) home->response_window = 5;
396         if (home->response_window > 60) home->response_window = 60;
397
398         if (home->max_outstanding < 8) home->max_outstanding = 8;
399         if (home->max_outstanding > 65536*16) home->max_outstanding = 65536*16;
400
401         if (home->ping_interval < 6) home->ping_interval = 6;
402         if (home->ping_interval > 120) home->ping_interval = 120;
403
404         if (home->zombie_period < 20) home->zombie_period = 20;
405         if (home->zombie_period > 120) home->zombie_period = 120;
406
407         if (home->zombie_period < home->response_window) {
408                 home->zombie_period = home->response_window;
409         }
410
411         if (home->num_pings_to_alive < 3) home->num_pings_to_alive = 3;
412         if (home->num_pings_to_alive > 10) home->num_pings_to_alive = 10;
413
414         if (home->ping_timeout < 3) home->ping_timeout = 3;
415         if (home->ping_timeout > 10) home->ping_timeout = 10;
416
417         if (home->revive_interval < 60) home->revive_interval = 60;
418         if (home->revive_interval > 3600) home->revive_interval = 3600;
419
420         if (dual) {
421                 home_server *home2 = rad_malloc(sizeof(*home2));
422
423                 memcpy(home2, home, sizeof(*home2));
424
425                 home2->type = HOME_TYPE_ACCT;
426                 home2->port++;
427                 home2->ping_user_password = NULL;
428
429                 if (!rbtree_insert(home_servers_byname, home2)) {
430                         cf_log_err(cf_sectiontoitem(cs),
431                                    "Internal error adding home server %s.",
432                                    name2);
433                         free(home2);
434                         return 0;
435                 }
436                 
437                 if (!rbtree_insert(home_servers_byaddr, home2)) {
438                         rbtree_deletebydata(home_servers_byname, home2);
439                         cf_log_err(cf_sectiontoitem(cs),
440                                    "Internal error adding home server %s.",
441                                    name2);
442                         free(home2);
443                         return 0;
444                 }
445         }
446
447         return 1;
448 }
449
450
451 static int server_pool_add(CONF_SECTION *cs, int server_type, int do_print)
452 {
453         const char *name2;
454         home_pool_t *pool = NULL;
455         const char *value;
456         CONF_PAIR *cp;
457         int num_home_servers;
458
459         name2 = cf_section_name1(cs);
460         if (!name2 || (strcasecmp(name2, "server_pool") != 0)) {
461                 cf_log_err(cf_sectiontoitem(cs),
462                            "Section is not a server_pool.");
463                 return 0;
464         }
465
466         name2 = cf_section_name2(cs);
467         if (!name2) {
468                 cf_log_err(cf_sectiontoitem(cs),
469                            "Server pool section is missing a name.");
470                 return 0;
471         }
472
473         /*
474          *      Count the home servers and initalize them.
475          */
476         num_home_servers = 0;
477         for (cp = cf_pair_find(cs, "home_server");
478              cp != NULL;
479              cp = cf_pair_find_next(cs, cp, "home_server")) {
480                 home_server myhome, *home;
481                 CONF_SECTION *server_cs;
482
483                 num_home_servers++;
484
485                 value = cf_pair_value(cp);
486                 if (!value) {
487                         cf_log_err(cf_pairtoitem(cp),
488                                    "No value given for home_server.");
489                         return 0;;
490                 }
491
492                 myhome.name = value;
493                 myhome.type = server_type;
494                 home = rbtree_finddata(home_servers_byname, &myhome);
495                 if (home) continue;
496
497                 server_cs = cf_section_sub_find_name2(mainconfig.config,
498                                                       "home_server",
499                                                       value);
500                 if (!server_cs) {
501                         cf_log_err(cf_pairtoitem(cp),
502                                    "Unknown home_server \"%s\".",
503                                    value);
504                         return 0;
505                 }
506
507                 if (!home_server_add(server_cs, server_type)) {
508                         return 0;
509                 }
510
511                 home = rbtree_finddata(home_servers_byname, &myhome);
512                 if (!home) {
513                         radlog(L_ERR, "Internal sanity check failed %d",
514                                __LINE__);
515                         return 0;
516                 }
517         }
518
519         if (num_home_servers == 0) {
520                 cf_log_err(cf_sectiontoitem(cs),
521                            "No home servers defined in pool %s",
522                            name2);
523                 goto error;
524         }
525
526         pool = rad_malloc(sizeof(*pool) + num_home_servers * sizeof(pool->servers[0]));
527         memset(pool, 0, sizeof(*pool) + num_home_servers * sizeof(pool->servers[0]));
528
529         pool->type = HOME_POOL_FAIL_OVER;
530         pool->name = name2;
531         pool->server_type = server_type;
532
533         if (do_print) DEBUG2(" server_pool %s {", name2);
534
535         cp = cf_pair_find(cs, "type");
536         if (cp) {
537                 static LRAD_NAME_NUMBER pool_types[] = {
538                         { "load-balance", HOME_POOL_LOAD_BALANCE },
539                         { "fail-over", HOME_POOL_FAIL_OVER },
540                         { "round_robin", HOME_POOL_LOAD_BALANCE },
541                         { "fail_over", HOME_POOL_FAIL_OVER },
542                         { "client-balance", HOME_POOL_CLIENT_BALANCE },
543                         { "client-port-balance", HOME_POOL_CLIENT_PORT_BALANCE },
544                         { "keyed-balance", HOME_POOL_KEYED_BALANCE },
545                         { NULL, 0 }
546                 };
547
548                 value = cf_pair_value(cp);
549                 if (!value) {
550                         cf_log_err(cf_pairtoitem(cp),
551                                    "No value given for type.");
552                         goto error;
553                 }
554
555                 pool->type = lrad_str2int(pool_types, value, 0);
556                 if (!pool->type) {
557                         cf_log_err(cf_pairtoitem(cp),
558                                    "Unknown type \"%s\".",
559                                    value);
560                         goto error;
561                 }
562
563                 if (do_print) DEBUG2("\ttype = %s", value);
564         }
565
566         for (cp = cf_pair_find(cs, "home_server");
567              cp != NULL;
568              cp = cf_pair_find_next(cs, cp, "home_server")) {
569                 home_server myhome, *home;
570
571                 value = cf_pair_value(cp);
572                 if (!value) {
573                         cf_log_err(cf_pairtoitem(cp),
574                                    "No value given for home_server.");
575                         goto error;
576                 }
577
578                 myhome.name = value;
579                 myhome.type = server_type;
580
581                 home = rbtree_finddata(home_servers_byname, &myhome);
582                 if (!home) {
583                         DEBUG2("Internal sanity check failed");
584                         goto error;
585                 }
586
587                 if (0) {
588                         DEBUG2("Warning: Duplicate home server %s in server pool %s", home->name, pool->name);
589                         continue;
590                 }
591
592                 if (do_print) DEBUG2("\thome_server = %s", home->name);
593                 pool->servers[pool->num_home_servers] = home;
594                 pool->num_home_servers++;
595         } /* loop over home_server's */
596
597         if (!rbtree_insert(home_pools_byname, pool)) {
598                 rad_assert("Internal sanity check failed");
599                 goto error;
600         }
601
602         if (do_print) DEBUG2(" }");
603
604         rad_assert(pool->server_type != 0);
605
606         return 1;
607
608  error:
609         if (do_print) DEBUG2(" }");
610         free(pool);
611         return 0;
612 }
613
614
615 static int old_server_add(CONF_SECTION *cs, const char *realm,
616                           const char *name, const char *secret,
617                           home_pool_type_t ldflag, home_pool_t **pool_p,
618                           int type)
619 {
620         int i, insert_point, num_home_servers;
621         home_server myhome, *home;
622         home_pool_t mypool, *pool;
623         CONF_SECTION *subcs;
624
625         /*
626          *      LOCAL realms get sanity checked, and nothing else happens.
627          */
628         if (strcmp(name, "LOCAL") == 0) {
629                 if (*pool_p) {
630                         cf_log_err(cf_sectiontoitem(cs), "Realm \"%s\" cannot be both LOCAL and remote", name);
631                         return 0;
632                 }
633                 return 1;
634         }
635
636         mypool.name = realm;
637         mypool.server_type = type;
638         pool = rbtree_finddata(home_pools_byname, &mypool);
639         if (pool) {
640                 if (pool->type != ldflag) {
641                         cf_log_err(cf_sectiontoitem(cs), "Inconsistent ldflag for server pool \"%s\"", name);
642                         return 0;
643                 }
644
645                 if (pool->server_type != type) {
646                         cf_log_err(cf_sectiontoitem(cs), "Inconsistent home server type for server pool \"%s\"", name);
647                         return 0;
648                 }
649         }
650
651         myhome.name = name;
652         myhome.type = type;
653         home = rbtree_finddata(home_servers_byname, &myhome);
654         if (home) {
655                 if (strcmp(home->secret, secret) != 0) {
656                         cf_log_err(cf_sectiontoitem(cs), "Inconsistent shared secret for home server \"%s\"", name);
657                         return 0;
658                 }
659
660                 if (home->type != type) {
661                         cf_log_err(cf_sectiontoitem(cs), "Inconsistent type for home server \"%s\"", name);
662                         return 0;
663                 }
664
665                 /*
666                  *      See if the home server is already listed
667                  *      in the pool.  If so, do nothing else.
668                  */
669                 if (pool) for (i = 0; i < pool->num_home_servers; i++) {
670                         if (pool->servers[i] == home) {
671                                 return 1;
672                         }
673                 }
674         }
675
676         /*
677          *      If we do have a pool, check that there is room to
678          *      insert the home server we've found, or the one that we
679          *      create here.
680          *
681          *      Note that we insert it into the LAST available
682          *      position, in order to maintain the same order as in
683          *      the configuration files.
684          */
685         insert_point = -1;
686         if (pool) {
687                 for (i = pool->num_home_servers - 1; i >= 0; i--) {
688                         if (pool->servers[i]) break;
689
690                         if (!pool->servers[i]) {
691                                 insert_point = i;
692                         }
693                 }
694
695                 if (insert_point < 0) {
696                         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);
697                         return 0;
698                 }
699         }
700
701         /*
702          *      No home server, allocate one.
703          */
704         if (!home) {
705                 const char *p;
706                 char *q;
707
708                 home = rad_malloc(sizeof(*home));
709                 memset(home, 0, sizeof(*home));
710
711                 home->name = name;
712                 home->hostname = name;
713                 home->type = type;
714                 home->secret = secret;
715
716                 p = strchr(name, ':');
717                 if (!p) {
718                         if (type == HOME_TYPE_AUTH) {
719                                 home->port = PW_AUTH_UDP_PORT;
720                         } else {
721                                 home->port = PW_ACCT_UDP_PORT;
722                         }
723
724                         p = name;
725                         q = NULL;
726
727                 } else if (p == name) {
728                                 cf_log_err(cf_sectiontoitem(cs),
729                                            "Invalid hostname %s.",
730                                            name);
731                                 free(home);
732                                 return 0;
733
734                 } else {
735                         home->port = atoi(p + 1);
736                         if ((home->port == 0) || (home->port > 65535)) {
737                                 cf_log_err(cf_sectiontoitem(cs),
738                                            "Invalid port %s.",
739                                            p + 1);
740                                 free(home);
741                                 return 0;
742                         }
743
744                         q = rad_malloc((p - name) + 1);
745                         memcpy(q, name, (p - name));
746                         q[p - name] = '\0';
747                         p = q;
748                 }
749
750                 if (ip_hton(p, AF_UNSPEC, &home->ipaddr) < 0) {
751                         cf_log_err(cf_sectiontoitem(cs),
752                                    "Failed looking up hostname %s.",
753                                    p);
754                         free(home);
755                         free(q);
756                         return 0;
757                 }
758                 free(q);
759
760                 /*
761                  *      Use the old-style configuration.
762                  */
763                 home->max_outstanding = 65535*16;
764                 home->zombie_period = mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count;
765                 if (home->zombie_period == 0) home->zombie_period =30;
766                 home->response_window = home->zombie_period - 1;
767
768                 home->ping_check = HOME_PING_CHECK_NONE;
769
770                 home->revive_interval = mainconfig.proxy_dead_time;
771
772                 if (rbtree_finddata(home_servers_byaddr, home)) {
773                         cf_log_err(cf_sectiontoitem(cs), "Home server %s has the same IP address and/or port as another home server.", name);
774                         free(home);
775                         return 0;
776                 }
777
778                 if (!rbtree_insert(home_servers_byname, home)) {
779                         cf_log_err(cf_sectiontoitem(cs), "Internal error adding home server %s.", name);
780                         free(home);
781                         return 0;
782                 }
783
784                 if (!rbtree_insert(home_servers_byaddr, home)) {
785                         rbtree_deletebydata(home_servers_byname, home);
786                         cf_log_err(cf_sectiontoitem(cs), "Internal error adding home server %s.", name);
787                         free(home);
788                         return 0;
789                 }
790         }
791
792         /*
793          *      We now have a home server, see if we can insert it
794          *      into pre-existing pool.
795          */
796         if (insert_point >= 0) {
797                 rad_assert(pool != NULL);
798                 pool->servers[insert_point] = home;
799                 return 1;
800         }
801
802         rad_assert(pool == NULL);
803         rad_assert(home != NULL);
804
805         /*
806          *      Count the old-style realms of this name.
807          */
808         num_home_servers = 0;
809         for (subcs = cf_subsection_find_next(cs, NULL, "realm");
810              subcs != NULL;
811              subcs = cf_subsection_find_next(cs, subcs, "realm")) {
812                 const char *this = cf_section_name2(subcs);
813
814                 if (!this || (strcmp(this, realm) != 0)) continue;
815                 num_home_servers++;
816         }
817
818         pool = rad_malloc(sizeof(*pool) + num_home_servers * sizeof(pool->servers[0]));
819         memset(pool, 0, sizeof(*pool) + num_home_servers * sizeof(pool->servers[0]));
820
821         pool->name = realm;
822         pool->type = ldflag;
823         pool->server_type = type;
824         pool->num_home_servers = num_home_servers;
825         pool->servers[0] = home;
826
827         if (!rbtree_insert(home_pools_byname, pool)) {
828                 rad_assert("Internal sanity check failed");
829                 return 0;
830         }
831
832         *pool_p = pool;
833
834         return 1;
835 }
836
837 static int old_realm_config(CONF_SECTION *cs, REALM *r)
838 {
839         char *host;
840         const char *secret = NULL;
841         home_pool_type_t ldflag;
842         CONF_PAIR *cp;
843
844         cp = cf_pair_find(cs, "ldflag");
845         ldflag = HOME_POOL_FAIL_OVER;
846         if (cp) {
847                 host = cf_pair_value(cp);
848                 if (!host) {
849                         cf_log_err(cf_pairtoitem(cp), "No value specified for ldflag");
850                         return 0;
851                 }
852
853                 if (strcasecmp(host, "fail_over") == 0) {
854                         DEBUG2("\tldflag = fail_over");
855                         
856                 } else if (strcasecmp(host, "round_robin") == 0) {
857                         ldflag = HOME_POOL_LOAD_BALANCE;
858                         DEBUG2("\tldflag = round_robin");
859                         
860                 } else {
861                         cf_log_err(cf_sectiontoitem(cs), "Unknown value \"%s\" for ldflag", host);
862                         return 0;
863                 }
864         } /* else don't print it. */
865
866         /*
867          *      Allow old-style if it doesn't exist, or if it exists and
868          *      it's LOCAL.
869          */
870         cp = cf_pair_find(cs, "authhost");
871         if (cp) {
872                 host = cf_pair_value(cp);
873                 if (!host) {
874                         cf_log_err(cf_pairtoitem(cp), "No value specified for authhost");
875                         return 0;
876                 }
877
878                 if (strcmp(host, "LOCAL") != 0) {
879                         cp = cf_pair_find(cs, "secret");
880                         if (!cp) {
881                                 cf_log_err(cf_sectiontoitem(cs), "No shared secret supplied for realm: %s", r->name);
882                                 return 0;
883                         }
884
885                         secret = cf_pair_value(cp);
886                         if (!secret) {
887                                 cf_log_err(cf_pairtoitem(cp), "No value specified for secret");
888                                 return 0;
889                         }
890                 }
891                         
892                 DEBUG2("\tauthhost = %s",  host);
893
894                 if (!old_server_add(cs, r->name, host, secret, ldflag,
895                                     &r->auth_pool, HOME_TYPE_AUTH)) {
896                         return 0;
897                 }
898         }
899
900         cp = cf_pair_find(cs, "accthost");
901         if (cp) {
902                 host = cf_pair_value(cp);
903                 if (!host) {
904                         cf_log_err(cf_pairtoitem(cp), "No value specified for accthost");
905                         return 0;
906                 }
907
908                 /*
909                  *      Don't look for a secret again if it was found
910                  *      above.
911                  */
912                 if ((strcmp(host, "LOCAL") != 0) && !secret) {
913                         cp = cf_pair_find(cs, "secret");
914                         if (!cp) {
915                                 cf_log_err(cf_sectiontoitem(cs), "No shared secret supplied for realm: %s", r->name);
916                                 return 0;
917                         }
918                         
919                         secret = cf_pair_value(cp);
920                         if (!secret) {
921                                 cf_log_err(cf_pairtoitem(cp), "No value specified for secret");
922                                 return 0;
923                         }
924                 }
925                 
926                 DEBUG2("\taccthost = %s", host);
927
928                 if (!old_server_add(cs, r->name, host, secret, ldflag,
929                                     &r->acct_pool, HOME_TYPE_ACCT)) {
930                         return 0;
931                 }
932         }
933
934         if (secret) DEBUG2("\tsecret = %s", secret);
935
936         return 1;
937
938 }
939
940
941 static int add_pool_to_realm(CONF_SECTION *cs,
942                              const char *name, home_pool_t **dest,
943                              int server_type, int do_print)
944 {
945         home_pool_t mypool, *pool;
946
947         mypool.name = name;
948         mypool.server_type = server_type;
949
950         pool = rbtree_finddata(home_pools_byname, &mypool);
951         if (!pool) {
952                 CONF_SECTION *pool_cs;
953
954                 pool_cs = cf_section_sub_find_name2(mainconfig.config,
955                                                     "server_pool",
956                                                     name);
957                 if (!pool_cs) {
958                         cf_log_err(cf_sectiontoitem(cs), "Failed to find server_pool \"%s\"", name);
959                         return 0;
960                 }
961
962                 if (!server_pool_add(pool_cs, server_type, do_print)) {
963                         return 0;
964                 }
965
966                 pool = rbtree_finddata(home_pools_byname, &mypool);
967                 if (!pool) {
968                         radlog(L_ERR, "Internal sanity check failed in add_pool_to_realm");
969                         return 0;
970                 }
971         }
972
973         if (pool->server_type != server_type) {
974                 cf_log_err(cf_sectiontoitem(cs), "Incompatible server_pool \"%s\" (mixed auth_pool / acct_pool)", name);
975                 return 0;
976         }
977
978         *dest = pool;
979
980         return 1;
981 }
982
983 int realm_add(CONF_SECTION *cs)
984 {
985         const char *name2;
986         REALM *r = NULL;
987         CONF_PAIR *cp;
988         home_pool_t *auth_pool, *acct_pool;
989         const char *auth_pool_name, *acct_pool_name;
990
991         name2 = cf_section_name1(cs);
992         if (!name2 || (strcasecmp(name2, "realm") != 0)) {
993                 cf_log_err(cf_sectiontoitem(cs), "Section is not a realm.");
994                 return 0;
995         }
996
997         name2 = cf_section_name2(cs);
998         if (!name2) {
999                 cf_log_err(cf_sectiontoitem(cs), "Realm section is missing the realm name.");
1000                 return 0;
1001         }
1002
1003         auth_pool = acct_pool = NULL;
1004         auth_pool_name = acct_pool_name = NULL;
1005
1006         /*
1007          *      Prefer new configuration to old one.
1008          */
1009         cp = cf_pair_find(cs, "pool");
1010         if (cp) auth_pool_name = cf_pair_value(cp);
1011         if (cp && auth_pool_name) {
1012                 acct_pool_name = auth_pool_name;
1013                 if (!add_pool_to_realm(cs,
1014                                        auth_pool_name, &auth_pool,
1015                                        HOME_TYPE_AUTH, 1)) {
1016                         return 0;
1017                 }
1018                 if (!add_pool_to_realm(cs,
1019                                        auth_pool_name, &acct_pool,
1020                                        HOME_TYPE_ACCT, 0)) {
1021                         return 0;
1022                 }
1023         }
1024
1025         cp = cf_pair_find(cs, "auth_pool");
1026         if (cp) auth_pool_name = cf_pair_value(cp);
1027         if (cp && auth_pool_name) {
1028                 if (auth_pool) {
1029                         cf_log_err(cf_sectiontoitem(cs), "Cannot use \"pool\" and \"auth_pool\" at the same time.");
1030                         return 0;
1031                 }
1032                 if (!add_pool_to_realm(cs,
1033                                        auth_pool_name, &auth_pool,
1034                                        HOME_TYPE_AUTH, 1)) {
1035                         return 0;
1036                 }
1037         }
1038
1039         cp = cf_pair_find(cs, "acct_pool");
1040         if (cp) acct_pool_name = cf_pair_value(cp);
1041         if (cp && acct_pool_name) {
1042                 int do_print = TRUE;
1043
1044                 if (acct_pool) {
1045                         cf_log_err(cf_sectiontoitem(cs), "Cannot use \"pool\" and \"acct_pool\" at the same time.");
1046                         return 0;
1047                 }
1048
1049                 if (!auth_pool ||
1050                     (strcmp(auth_pool_name, acct_pool_name) != 0)) {
1051                         do_print = TRUE;
1052                 }
1053
1054                 if (!add_pool_to_realm(cs,
1055                                        acct_pool_name, &acct_pool,
1056                                        HOME_TYPE_ACCT, do_print)) {
1057                         return 0;
1058                 }
1059         }
1060
1061         DEBUG2(" realm %s {", name2);
1062
1063         /*
1064          *      The realm MAY already exist if it's an old-style realm.
1065          *      In that case, merge the old-style realm with this one.
1066          */
1067         r = realm_find(name2);
1068         if (r) {
1069                 if (cf_pair_find(cs, "auth_pool") ||
1070                     cf_pair_find(cs, "acct_pool")) {
1071                         cf_log_err(cf_sectiontoitem(cs), "Duplicate realm \"%s\"", name2);
1072                         goto error;
1073                 }
1074
1075                 if (!old_realm_config(cs, r)) {
1076                         goto error;
1077                 }
1078
1079                 DEBUG2(" } # realm %s", name2);
1080                 return 1;
1081         }
1082
1083         r = rad_malloc(sizeof(*r));
1084         memset(r, 0, sizeof(*r));
1085
1086         r->name = name2;
1087         r->auth_pool = auth_pool;
1088         r->acct_pool = acct_pool;
1089         r->striprealm = 1;
1090
1091         if (auth_pool_name &&
1092             (auth_pool_name == acct_pool_name)) { /* yes, ptr comparison */
1093                 DEBUG2("\tpool = %s", auth_pool_name);
1094         } else {
1095                 if (auth_pool_name) DEBUG2("\tauth_pool = %s", auth_pool_name);
1096                 if (acct_pool_name) DEBUG2("\tacct_pool = %s", acct_pool_name);
1097         }
1098
1099         cp = cf_pair_find(cs, "nostrip");
1100         if (cp && (cf_pair_value(cp) == NULL)) {
1101                 r->striprealm = 0;
1102                 DEBUG2("\tnostrip");
1103         }
1104
1105         /*
1106          *      We're a new-style realm.  Complain if we see the old
1107          *      directives.
1108          */
1109         if (r->auth_pool || r->acct_pool) {
1110                 if (((cp = cf_pair_find(cs, "authhost")) != NULL) ||
1111                     ((cp = cf_pair_find(cs, "accthost")) != NULL) ||
1112                     ((cp = cf_pair_find(cs, "secret")) != NULL) ||
1113                     ((cp = cf_pair_find(cs, "ldflag")) != NULL)) {
1114                         DEBUG2("WARNING: Ignoring old-style configuration entry \"%s\" in realm \"%s\"", cf_pair_attr(cp), r->name);
1115                 }
1116
1117
1118                 /*
1119                  *      The realm MAY be an old-style realm, as there
1120                  *      was no auth_pool or acct_pool.  Double-check
1121                  *      it, just to be safe.
1122                  */
1123         } else if (!old_realm_config(cs, r)) {
1124                 goto error;
1125         }
1126
1127         if (!rbtree_insert(realms_byname, r)) {
1128                 rad_assert("Internal sanity check failed");
1129                 goto error;
1130         }
1131
1132         DEBUG2(" }");
1133
1134         return 1;
1135
1136  error:
1137         DEBUG2(" } # realm %s", name2);
1138         free(r);
1139         return 0;
1140 }
1141
1142
1143 /*
1144  *      Find a realm in the REALM list.
1145  */
1146 REALM *realm_find(const char *name)
1147 {
1148         REALM myrealm;
1149
1150         if (!name) name = "NULL";
1151
1152         myrealm.name = name;
1153         return rbtree_finddata(realms_byname, &myrealm);
1154 }
1155
1156
1157 home_server *home_server_ldb(const char *realmname,
1158                              home_pool_t *pool, REQUEST *request)
1159 {
1160         int             start;
1161         int             count;
1162         home_server     *found = NULL;
1163         VALUE_PAIR      *vp;
1164
1165         start = 0;
1166
1167         /*
1168          *      Determine how to pick choose the home server.
1169          */
1170         switch (pool->type) {
1171                 uint32_t hash;
1172
1173                 /*
1174                  *      For load-balancing by client IP address, we
1175                  *      pick a home server by hashing the client IP.
1176                  *
1177                  *      This isn't as even a load distribution as
1178                  *      tracking the State attribute, but it's better
1179                  *      than nothing.
1180                  */
1181         case HOME_POOL_CLIENT_BALANCE:
1182                 switch (request->packet->src_ipaddr.af) {
1183                 case AF_INET:
1184                         hash = lrad_hash(&request->packet->src_ipaddr.ipaddr.ip4addr,
1185                                          sizeof(request->packet->src_ipaddr.ipaddr.ip4addr));
1186                         break;
1187                 case AF_INET6:
1188                         hash = lrad_hash(&request->packet->src_ipaddr.ipaddr.ip6addr,
1189                                          sizeof(request->packet->src_ipaddr.ipaddr.ip6addr));
1190                         break;
1191                 default:
1192                         hash = 0;
1193                         break;
1194                 }
1195                 start = hash % pool->num_home_servers;
1196                 break;
1197
1198         case HOME_POOL_CLIENT_PORT_BALANCE:
1199                 switch (request->packet->src_ipaddr.af) {
1200                 case AF_INET:
1201                         hash = lrad_hash(&request->packet->src_ipaddr.ipaddr.ip4addr,
1202                                          sizeof(request->packet->src_ipaddr.ipaddr.ip4addr));
1203                         break;
1204                 case AF_INET6:
1205                         hash = lrad_hash(&request->packet->src_ipaddr.ipaddr.ip6addr,
1206                                          sizeof(request->packet->src_ipaddr.ipaddr.ip6addr));
1207                         break;
1208                 default:
1209                         hash = 0;
1210                         break;
1211                 }
1212                 lrad_hash_update(&request->packet->src_port,
1213                                  sizeof(request->packet->src_port), hash);
1214                 start = hash % pool->num_home_servers;
1215                 break;
1216
1217         case HOME_POOL_KEYED_BALANCE:
1218                 if ((vp = pairfind(request->config_items, PW_LOAD_BALANCE_KEY)) != NULL) {
1219                         hash = lrad_hash(vp->vp_strvalue, vp->length);
1220                         start = hash % pool->num_home_servers;
1221                         break;
1222                 }
1223                 /* FALL-THROUGH */
1224                                 
1225         case HOME_POOL_LOAD_BALANCE:
1226                 found = pool->servers[0];
1227
1228         default:
1229                 start = 0;
1230                 break;
1231         }
1232
1233         /*
1234          *      Starting with the home server we chose, loop through
1235          *      all home servers.  If the current one is dead, skip
1236          *      it.  If it is too busy, skip it.
1237          *
1238          *      Otherwise, use it.
1239          */
1240         for (count = 0; count < pool->num_home_servers; count++) {
1241                 home_server *home = pool->servers[(start + count) % pool->num_home_servers];
1242
1243                 if (home->state == HOME_STATE_IS_DEAD) {
1244                         continue;
1245                 }
1246
1247                 /*
1248                  *      This home server is too busy.  Choose another one.
1249                  */
1250                 if (home->currently_outstanding >= home->max_outstanding) {
1251                         continue;
1252                 }
1253
1254                 if (pool->type != HOME_POOL_LOAD_BALANCE) {
1255                         return home;
1256                 }
1257
1258                 DEBUG3("PROXY %s %d\t%s %d",
1259                        found->name, found->currently_outstanding,
1260                        home->name, home->currently_outstanding);
1261
1262                 /*
1263                  *      Prefer this server if it's less busy than the
1264                  *      one we previously found.
1265                  */
1266                 if (home->currently_outstanding < found->currently_outstanding) {
1267                         DEBUG3("Choosing %s: It's less busy than %s",
1268                                home->name, found->name);
1269                         found = home;
1270                         continue;
1271                 }
1272
1273                 /*
1274                  *      Ignore servers which are busier than the one
1275                  *      we found.
1276                  */
1277                 if (home->currently_outstanding > found->currently_outstanding) {
1278                         DEBUG3("Skipping %s: It's busier than %s",
1279                                home->name, found->name);
1280                         continue;
1281                 }
1282
1283                 /*
1284                  *      From the list of servers which have the same
1285                  *      load, choose one at random.
1286                  */
1287                 if (((count + 1) * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) {
1288                         found = home;
1289                 }
1290
1291         } /* loop over the home servers */
1292
1293         if (found) return found;
1294
1295         /*
1296          *      No live match found, and no fallback to the "DEFAULT"
1297          *      realm.  We fix this by blindly marking all servers as
1298          *      "live".  But only do it for ones that don't support
1299          *      "pings", as they will be marked live when they
1300          *      actually are live.
1301          */
1302         if (!mainconfig.proxy_fallback &&
1303             mainconfig.wake_all_if_all_dead) {
1304                 home_server *lb = NULL;
1305
1306                 for (count = 0; count < pool->num_home_servers; count++) {
1307                         home_server *home = pool->servers[count];
1308
1309                         if ((home->state == HOME_STATE_IS_DEAD) &&
1310                             (home->ping_check == HOME_PING_CHECK_NONE)) {
1311                                 home->state = HOME_STATE_ALIVE;
1312                                 if (!lb) lb = home;
1313                         }
1314                 }
1315
1316                 if (lb) return lb;
1317         }
1318
1319         /*
1320          *      Still nothing.  Look up the DEFAULT realm, but only
1321          *      if we weren't looking up the NULL or DEFAULT realms.
1322          */
1323         if (mainconfig.proxy_fallback &&
1324             realmname &&
1325             (strcmp(realmname, "NULL") != 0) &&
1326             (strcmp(realmname, "DEFAULT") != 0)) {
1327                 REALM *rd = realm_find("DEFAULT");
1328
1329                 if (!rd) return NULL;
1330
1331                 pool = NULL;
1332                 if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
1333                         pool = rd->auth_pool;
1334
1335                 } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
1336                         pool = rd->acct_pool;
1337                 }
1338                 if (!pool) return NULL;
1339
1340                 DEBUG2("  Realm %s has no live home servers.  Falling back to the DEFAULT realm.", realmname);
1341                 return home_server_ldb(rd->name, pool, request);
1342         }
1343
1344         /*
1345          *      Still haven't found anything.  Oh well.
1346          */
1347         return NULL;
1348 }
1349
1350
1351 home_server *home_server_find(lrad_ipaddr_t *ipaddr, int port)
1352 {
1353         home_server myhome;
1354
1355         myhome.ipaddr = *ipaddr;
1356         myhome.port = port;
1357
1358         return rbtree_finddata(home_servers_byaddr, &myhome);
1359 }