Massive change to the server core to remove horrid code in
[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 2000,2006  The FreeRADIUS server project
21  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2000  Alan DeKok <aland@ox.org>
23  */
24
25 #include <freeradius-devel/ident.h>
26 RCSID("$Id$")
27
28 #include <freeradius-devel/autoconf.h>
29
30 #include <sys/stat.h>
31
32 #include <stdlib.h>
33 #include <string.h>
34 #include <netdb.h>
35 #include <ctype.h>
36 #include <fcntl.h>
37
38 #include <freeradius-devel/radiusd.h>
39 #include <freeradius-devel/rad_assert.h>
40
41 static rbtree_t *realms_byname = NULL;
42
43 static rbtree_t *home_servers_byaddr = NULL;
44 static rbtree_t *home_servers_byname = NULL;
45
46 static rbtree_t *home_pools_byname = NULL;
47
48 static int realm_name_cmp(const void *one, const void *two)
49 {
50         const REALM *a = one;
51         const REALM *b = two;
52
53         return strcasecmp(a->name, b->name);
54 }
55
56
57 static int home_server_name_cmp(const void *one, const void *two)
58 {
59         const home_server *a = one;
60         const home_server *b = two;
61
62         return strcasecmp(a->name, b->name);
63 }
64
65 static int home_server_addr_cmp(const void *one, const void *two)
66 {
67         const home_server *a = one;
68         const home_server *b = two;
69
70         if (a->ipaddr.af < b->ipaddr.af) return -1;
71         if (a->ipaddr.af > b->ipaddr.af) return +1;
72
73         if (a->port < b->port) return -1;
74         if (a->port > b->port) return +1;
75
76         switch (a->ipaddr.af) {
77         case AF_INET:
78                 return memcmp(&a->ipaddr.ipaddr.ip4addr,
79                               &b->ipaddr.ipaddr.ip4addr,
80                               sizeof(a->ipaddr.ipaddr.ip4addr));
81                 break;
82         case AF_INET6:
83                 return memcmp(&a->ipaddr.ipaddr.ip6addr,
84                               &b->ipaddr.ipaddr.ip6addr,
85                               sizeof(a->ipaddr.ipaddr.ip6addr));
86                 break;
87         default:
88                 break;
89         }
90         
91         return -1;
92 }
93
94
95 static int home_pool_name_cmp(const void *one, const void *two)
96 {
97         const home_pool_t *a = one;
98         const home_pool_t *b = two;
99
100         return strcasecmp(a->name, b->name);
101 }
102
103
104 void realms_free(void)
105 {
106         rbtree_free(home_servers_byname);
107         home_servers_byname = NULL;
108
109         rbtree_free(home_servers_byaddr);
110         home_servers_byaddr = NULL;
111
112         rbtree_free(home_pools_byname);
113         home_pools_byname = NULL;
114
115         rbtree_free(realms_byname);
116         realms_byname = NULL;
117 }
118
119
120 int realms_init(const char *filename)
121 {
122         CONF_SECTION *cs;
123
124         if (realms_byname) return 1;
125
126         realms_byname = rbtree_create(realm_name_cmp, free, 0);
127         if (!realms_byname) {
128                 realms_free();
129                 return 0;
130         }
131
132         home_servers_byaddr = rbtree_create(home_server_addr_cmp, free, 0);
133         if (!home_servers_byaddr) {
134                 realms_free();
135                 return 0;
136         }
137
138         home_servers_byname = rbtree_create(home_server_name_cmp, NULL, 0);
139         if (!home_servers_byname) {
140                 realms_free();
141                 return 0;
142         }
143
144         home_pools_byname = rbtree_create(home_pool_name_cmp, free, 0);
145         if (!home_pools_byname) {
146                 realms_free();
147                 return 0;
148         }
149
150         for (cs = cf_subsection_find_next(mainconfig.config, NULL, "realm");
151              cs != NULL;
152              cs = cf_subsection_find_next(mainconfig.config, cs, "realm")) {
153                 if (!realm_add(filename, cs)) {
154                         realms_free();
155                         return 0;
156                 }
157         }
158
159         return 1;
160 }
161
162 static struct in_addr hs_ip4addr;
163 static struct in6_addr hs_ip6addr;
164 static char *hs_type = NULL;
165 static char *hs_check = NULL;
166
167 static CONF_PARSER home_server_config[] = {
168         { "ipaddr",  PW_TYPE_IPADDR,
169           0, &hs_ip4addr,  NULL },
170         { "ipv6addr",  PW_TYPE_IPV6ADDR,
171           0, &hs_ip6addr, NULL },
172
173         { "hostname",  PW_TYPE_STRING_PTR,
174           offsetof(home_server,hostname), NULL,  NULL},
175         { "port", PW_TYPE_INTEGER,
176           offsetof(home_server,port), NULL,   "0" },
177
178         { "type",  PW_TYPE_STRING_PTR,
179           0, &hs_type, NULL },
180
181         { "secret",  PW_TYPE_STRING_PTR,
182           offsetof(home_server,secret), NULL,  NULL},
183
184         { "response_window", PW_TYPE_INTEGER,
185           offsetof(home_server,response_window), NULL,   "30" },
186         { "max_outstanding", PW_TYPE_INTEGER,
187           offsetof(home_server,max_outstanding), NULL,   "65536" },
188
189         { "zombie_period", PW_TYPE_INTEGER,
190           offsetof(home_server,zombie_period), NULL,   "40" },
191         { "ping_check", PW_TYPE_STRING_PTR,
192           0, &hs_check,   "none" },
193
194         { "ping_interval", PW_TYPE_INTEGER,
195           offsetof(home_server,ping_interval), NULL,   "30" },
196         { "num_pings_to_alive", PW_TYPE_INTEGER,
197           offsetof(home_server,num_pings_to_alive), NULL,   "3" },
198         { "revive_interval", PW_TYPE_INTEGER,
199           offsetof(home_server,revive_interval), NULL,   "300" },
200
201         { "username",  PW_TYPE_STRING_PTR,
202           offsetof(home_server,ping_user_name), NULL,  NULL},
203         { "password",  PW_TYPE_STRING_PTR,
204           offsetof(home_server,ping_user_password), NULL,  NULL},
205         
206         { NULL, -1, 0, NULL, NULL }             /* end the list */
207
208 };
209
210
211 static int home_server_add(const char *filename, CONF_SECTION *cs)
212 {
213         const char *name2;
214         home_server *home;
215
216         name2 = cf_section_name1(cs);
217         if (!name2 || (strcasecmp(name2, "home_server") != 0)) {
218                 radlog(L_ERR, "%s[%d]: Section is not a home_server.",
219                        filename, cf_section_lineno(cs));
220                 return 0;
221         }
222
223         name2 = cf_section_name2(cs);
224         if (!name2) {
225                 radlog(L_ERR, "%s[%d]: Home server section is missing a name.",
226                        filename, cf_section_lineno(cs));
227                 return 0;
228         }
229
230         home = rad_malloc(sizeof(*home));
231         memset(home, 0, sizeof(*home));
232
233         home->name = name2;
234         
235         memset(&hs_ip4addr, 0, sizeof(hs_ip4addr));
236         memset(&hs_ip6addr, 0, sizeof(hs_ip6addr));
237         cf_section_parse(cs, home, home_server_config);
238
239         if (!home->hostname && (htonl(hs_ip4addr.s_addr) == INADDR_NONE) &&
240             IN6_IS_ADDR_UNSPECIFIED(&hs_ip6addr)) {
241                 radlog(L_ERR, "%s[%d]: No hostname, IPv4 address, or IPv6 address defined for home server %s.",
242                        filename, cf_section_lineno(cs), name2);
243                 free(home);
244                 free(hs_type);
245                 hs_type = NULL;
246                 free(hs_check);
247                 hs_check = NULL;
248                 return 0;
249         }
250
251         /*
252          *      FIXME: Parse home->hostname!
253          *
254          *      Right now, only ipaddr && ip6addr are used.
255          *      The old-style parsing still allows hostnames.
256          */
257         if (htonl(hs_ip4addr.s_addr) != INADDR_NONE) {
258                 home->ipaddr.af = AF_INET;
259                 home->ipaddr.ipaddr.ip4addr = hs_ip4addr;
260
261         } else if (!IN6_IS_ADDR_UNSPECIFIED(&hs_ip6addr)) {
262                 home->ipaddr.af = AF_INET6;
263                 home->ipaddr.ipaddr.ip6addr = hs_ip6addr;
264
265         } else {
266                 radlog(L_ERR, "%s[%d]: FIXME: parse hostname for home server %s.",
267                        filename, cf_section_lineno(cs), name2);
268                 free(home);
269                 free(hs_type);
270                 hs_type = NULL;
271                 free(hs_check);
272                 hs_check = NULL;
273                 return 0;
274         }
275
276         if (!home->port || (home->port > 65535)) {
277                 radlog(L_ERR, "%s[%d]: No port, or invalid port defined for home server %s.",
278                        filename, cf_section_lineno(cs), name2);
279                 free(home);
280                 free(hs_type);
281                 hs_type = NULL;
282                 free(hs_check);
283                 hs_check = NULL;
284                 return 0;
285         }
286
287         if (0) {
288                 radlog(L_ERR, "%s[%d]: Fatal error!  Home server %s is ourselves!",
289                        filename, cf_section_lineno(cs), name2);
290                 free(home);
291                 free(hs_type);
292                 hs_type = NULL;
293                 free(hs_check);
294                 hs_check = NULL;
295                 return 0;
296         }
297
298         if (strcasecmp(hs_type, "auth") == 0) {
299                 home->type = HOME_TYPE_AUTH;
300
301         } else if (strcasecmp(hs_type, "acct") == 0) {
302                 home->type = HOME_TYPE_ACCT;
303
304         } else {
305                 radlog(L_ERR, "%s[%d]: Invalid type \"%s\" for home server %s.",
306                        filename, cf_section_lineno(cs), hs_type, name2);
307                 free(home);
308                 free(hs_type);
309                 hs_type = NULL;
310                 free(hs_check);
311                 hs_check = NULL;
312                 return 0;
313         }
314         free(hs_type);
315         hs_type = NULL;
316
317         if (!home->secret) {
318                 radlog(L_ERR, "%s[%d]: No shared secret defined for home server %s.",
319                        filename, cf_section_lineno(cs), name2);
320                 free(home);
321                 return 0;
322         }
323
324         if (strcasecmp(hs_check, "none") == 0) {
325                 home->ping_check = HOME_PING_CHECK_NONE;
326
327         } else if (strcasecmp(hs_check, "status-server") == 0) {
328                 home->ping_check = HOME_PING_CHECK_STATUS_SERVER;
329
330         } else if (strcasecmp(hs_check, "request") == 0) {
331                 home->ping_check = HOME_PING_CHECK_REQUEST;
332
333         } else {
334                 radlog(L_ERR, "%s[%d]: Invalid ping_check \"%s\" for home server %s.",
335                        filename, cf_section_lineno(cs), hs_check, name2);
336                 free(home);
337                 free(hs_check);
338                 hs_check = NULL;
339                 return 0;
340         }
341         free(hs_check);
342         hs_check = NULL;
343
344         if ((home->ping_check != HOME_PING_CHECK_NONE) &&
345             (home->ping_check != HOME_PING_CHECK_STATUS_SERVER)) {
346                 if (!home->ping_user_name) {
347                         radlog(L_INFO, "%s[%d]: You must supply a user name to enable ping checks",
348                                filename, cf_section_lineno(cs));
349                         free(home);
350                         return 0;
351                 }
352
353                 if ((home->type == HOME_TYPE_AUTH) && 
354                     !home->ping_user_password) {        
355                         radlog(L_INFO, "%s[%d]: You must supply a password to enable ping checks",
356                                filename, cf_section_lineno(cs));
357                         free(home);
358                         return 0;
359                 }
360         }
361
362         if (rbtree_finddata(home_servers_byaddr, home)) {
363                 radlog(L_INFO, "%s[%d]: Ignoring duplicate home server %s.",
364                        filename, cf_section_lineno(cs), name2);
365                 return 1;
366         }
367
368         if (!rbtree_insert(home_servers_byname, home)) {
369                 radlog(L_ERR, "%s[%d]: Internal error adding home server %s.",
370                        filename, cf_section_lineno(cs), name2);
371                 free(home);
372                 return 0;
373         }
374
375         if (!rbtree_insert(home_servers_byaddr, home)) {
376                 rbtree_deletebydata(home_servers_byname, home);
377                 radlog(L_ERR, "%s[%d]: Internal error adding home server %s.",
378                        filename, cf_section_lineno(cs), name2);
379                 free(home);
380                 return 0;
381         }
382
383         if (home->response_window < 5) home->response_window = 5;
384         if (home->response_window > 60) home->response_window = 60;
385
386         if (home->max_outstanding < 8) home->max_outstanding = 8;
387         if (home->max_outstanding > 65536*16) home->max_outstanding = 65536*16;
388
389         if (home->ping_interval < 6) home->ping_interval = 6;
390         if (home->ping_interval > 120) home->ping_interval = 120;
391
392         if (home->zombie_period < 20) home->zombie_period = 20;
393         if (home->zombie_period > 120) home->zombie_period = 120;
394
395         if (home->zombie_period < home->response_window) {
396                 home->zombie_period = home->response_window;
397         }
398
399         if (home->num_pings_to_alive < 3) home->num_pings_to_alive = 3;
400         if (home->num_pings_to_alive > 10) home->num_pings_to_alive = 10;
401
402         if (home->revive_interval < 60) home->revive_interval = 60;
403         if (home->revive_interval > 3600) home->revive_interval = 3600;
404
405         return 1;
406 }
407
408
409 static int server_pool_add(const char *filename, CONF_SECTION *cs)
410 {
411         const char *name2;
412         home_pool_t *pool;
413         const char *value;
414         CONF_PAIR *cp;
415         int num_home_servers;
416
417         name2 = cf_section_name1(cs);
418         if (!name2 || (strcasecmp(name2, "server_pool") != 0)) {
419                 radlog(L_ERR, "%s[%d]: Section is not a server_pool.",
420                        filename, cf_section_lineno(cs));
421                 return 0;
422         }
423
424         name2 = cf_section_name2(cs);
425         if (!name2) {
426                 radlog(L_ERR, "%s[%d]: Server pool section is missing a name.",
427                        filename, cf_section_lineno(cs));
428                 return 0;
429         }
430
431         num_home_servers = 0;
432         for (cp = cf_pair_find(cs, "home_server");
433              cp != NULL;
434              cp = cf_pair_find_next(cs, cp, "home_server")) {
435                 num_home_servers++;
436         }
437
438         if (num_home_servers == 0) {
439                 radlog(L_ERR, "%s[%d]: No home servers defined in pool %s",
440                        filename, cf_section_lineno(cs), name2);
441                 return 0;
442         }
443
444         pool = rad_malloc(sizeof(*pool) + num_home_servers * sizeof(pool->servers[0]));
445         memset(pool, 0, sizeof(*pool) + num_home_servers * sizeof(pool->servers[0]));
446
447         pool->type = HOME_POOL_FAIL_OVER;
448         pool->name = name2;
449
450         cp = cf_pair_find(cs, "type");
451         if (cp) {
452                 value = cf_pair_value(cp);
453                 if (!value) {
454                         radlog(L_ERR, "%s[%d]: No value given for type.",
455                                filename, cf_pair_lineno(cp));
456                         free(pool);
457                         return 0;
458                 }
459
460                 if (strcmp(value, "load-balance") == 0) {
461                         pool->type = HOME_POOL_LOAD_BALANCE;
462
463                 } else if (strcmp(value, "fail-over") == 0) {
464                         pool->type = HOME_POOL_FAIL_OVER;
465
466                 } else {
467                         radlog(L_ERR, "%s[%d]: Unknown type \"%s\".",
468                                filename, cf_pair_lineno(cp), value);
469                         free(pool);
470                         return 0;
471                 }
472
473                 DEBUG2(" server_pool %s: type = %s", name2, value);
474         }
475
476         for (cp = cf_pair_find(cs, "home_server");
477              cp != NULL;
478              cp = cf_pair_find_next(cs, cp, "home_server")) {
479                 home_server myhome, *home;
480
481                 value = cf_pair_value(cp);
482                 if (!value) {
483                         radlog(L_ERR, "%s[%d]: No value given for home_server.",
484                                filename, cf_pair_lineno(cp));
485                         free(pool);
486                         return 0;
487                 }
488
489                 myhome.name = value;
490
491                 home = rbtree_finddata(home_servers_byname, &myhome);
492                 if (!home) {
493                         CONF_SECTION *server_cs;
494
495                         server_cs = cf_section_sub_find_name2(NULL,
496                                                               "home_server",
497                                                               value);
498                         if (!server_cs) {
499                                 radlog(L_ERR, "%s[%d]: Unknown home_server \"%s\".",
500                                        filename, cf_pair_lineno(cp), value);
501                                 free(pool);
502                                 return 0;
503                         }
504                         
505                         if (!home_server_add(filename, server_cs)) {
506                                 free(pool);
507                                 return 0;
508                         }
509
510                         home = rbtree_finddata(home_servers_byname, &myhome);
511                         if (!home) {
512                                 rad_assert("Internal sanity check failed");
513                                 return 0;
514                         }
515                 }
516
517                 if (!pool->server_type) {
518                         rad_assert(home->type != 0);
519                         pool->server_type = home->type;
520
521                 } else if (pool->server_type != home->type) {
522                         radlog(L_ERR, "%s[%d]: Home server \"%s\" is not of the same type as previous servers in server pool %s",
523                                filename, cf_pair_lineno(cp), value, pool->name);
524                         free(pool);
525                         return 0;
526                 }
527
528                 if (0) {
529                         DEBUG2("Warning: Duplicate home server %s in server pool %s", home->name, pool->name);
530                         continue;
531                 }
532
533                 DEBUG2(" server_pool %s: home_server = %s", name2, home->name);
534                 pool->servers[pool->num_home_servers] = home;
535                 pool->num_home_servers++;
536
537         } /* loop over home_server's */
538
539         if (!rbtree_insert(home_pools_byname, pool)) {
540                 rad_assert("Internal sanity check failed");
541                 return 0;
542         }
543
544         rad_assert(pool->server_type != 0);
545         
546         return 1;
547 }
548
549
550 static int old_server_add(const char *filename, int lineno,
551                           const char *name, const char *secret,
552                           home_pool_type_t ldflag, home_pool_t **pool_p,
553                           int type)
554 {
555         int i, insert_point, num_home_servers;
556         home_server myhome, *home;
557         home_pool_t mypool, *pool;
558         CONF_SECTION *cs;
559
560         /*
561          *      LOCAL realms get sanity checked, and nothing else happens.
562          */
563         if (strcmp(name, "LOCAL") == 0) {
564                 if (*pool_p) {
565                         radlog(L_ERR, "%s[%d]: Realm \"%s\" cannot be both LOCAL and remote", filename, lineno, name);
566                         return 0;
567                 }
568                 return 1;
569         }
570
571         mypool.name = name;
572         pool = rbtree_finddata(home_pools_byname, &mypool);
573         if (pool) {
574                 if (pool->type != ldflag) {
575                         radlog(L_ERR, "%s[%d]: Inconsistent ldflag for server pool \"%s\"", filename, lineno, name);
576                         return 0;
577                 }
578
579                 if (pool->server_type != type) {
580                         radlog(L_ERR, "%s[%d]: Inconsistent home server type for server pool \"%s\"", filename, lineno, name);
581                         return 0;
582                 }
583         }
584
585         myhome.name = name;
586         home = rbtree_finddata(home_servers_byname, &myhome);
587         if (home) {
588                 if (strcmp(home->secret, secret) != 0) {
589                         radlog(L_ERR, "%s[%d]: Inconsistent shared secret for home server \"%s\"", filename, lineno, name);
590                         return 0;
591                 }
592
593                 if (home->type != type) {
594                         radlog(L_ERR, "%s[%d]: Inconsistent type for home server \"%s\"", filename, lineno, name);
595                         return 0;
596                 }
597                 
598                 /*
599                  *      See if the home server is already listed
600                  *      in the pool.  If so, do nothing else.
601                  */
602                 if (pool) for (i = 0; i < pool->num_home_servers; i++) {
603                         if (pool->servers[i] == home) {
604                                 return 1;
605                         }
606                 }
607         }
608
609         /*
610          *      If we do have a pool, check that there is room to
611          *      insert the home server we've found, or the one that we
612          *      create here.
613          *
614          *      Note that we insert it into the LAST available
615          *      position, in order to maintain the same order as in
616          *      the configuration files.
617          */
618         insert_point = -1;
619         if (pool) {
620                 for (i = pool->num_home_servers - 1; i >= 0; i--) {
621                         if (pool->servers[i]) break;
622
623                         if (!pool->servers[i]) {
624                                 insert_point = i;
625                         }
626                 }
627
628                 if (insert_point < 0) {
629                         radlog(L_ERR, "%s[%d]: No room in pool to add home server \"%s\".  Please update the realm configuration to use the new-style home servers and server pools.", filename, lineno, name);
630                         return 0;
631                 }
632         }
633
634         /*
635          *      No home server, allocate one.
636          */
637         if (!home) {         
638                 const char *p;
639                 char *q;
640
641                 home = rad_malloc(sizeof(*home));
642                 memset(home, 0, sizeof(*home));
643
644                 home->name = name;
645                 home->hostname = name;
646                 home->type = type;
647                 home->secret = secret;
648
649                 p = strchr(name, ':');
650                 if (!p) {
651                         if (type == HOME_TYPE_AUTH) {
652                                 home->port = PW_AUTH_UDP_PORT;
653                         } else {
654                                 home->port = PW_ACCT_UDP_PORT;
655                         }
656
657                         p = name;
658                         q = NULL;
659
660                 } else if (p == name) {
661                                 radlog(L_ERR, "%s[%d]: Invalid hostname %s.",
662                                        filename, lineno, name);
663                                 free(home);
664                                 return 0;
665
666                 } else {
667                         home->port = atoi(p + 1);
668                         if ((home->port == 0) || (home->port > 65535)) {
669                                 radlog(L_ERR, "%s[%d]: Invalid port %s.",
670                                        filename, lineno, p + 1);
671                                 free(home);
672                                 return 0;
673                         }
674
675                         q = rad_malloc((p - name) + 1);
676                         memcpy(q, name, (p - name));
677                         q[p - name] = '\0';
678                         p = q;
679                 }
680
681                 if (ip_hton(p, AF_UNSPEC, &home->ipaddr) < 0) {
682                         radlog(L_ERR, "%s[%d]: Failed looking up hostname %s.",
683                                filename, lineno, p);
684                         free(home);
685                         free(q);
686                         return 0;
687                 }
688                 free(q);
689
690                 /*
691                  *      Use the old-style configuration.
692                  */
693                 home->max_outstanding = 65535*16;
694                 home->zombie_period = mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count;
695                 if (home->zombie_period == 0) home->zombie_period =30;
696                 home->response_window = home->zombie_period - 1;
697
698                 home->ping_check = HOME_PING_CHECK_NONE;
699
700                 home->revive_interval = mainconfig.proxy_dead_time;
701
702                 if (rbtree_finddata(home_servers_byaddr, home)) {
703                         radlog(L_ERR, "%s[%d]: Home server %s has the same IP address as another home server.",
704                                filename, lineno, name);
705                         free(home);
706                         return 0;
707                 }
708
709                 if (!rbtree_insert(home_servers_byname, home)) {
710                         radlog(L_ERR, "%s[%d]: Internal error adding home server %s.",
711                                filename, lineno, name);
712                         free(home);
713                         return 0;
714                 }
715                 
716                 if (!rbtree_insert(home_servers_byaddr, home)) {
717                         rbtree_deletebydata(home_servers_byname, home);
718                         radlog(L_ERR, "%s[%d]: Internal error adding home server %s.",
719                                filename, lineno, name);
720                         free(home);
721                         return 0;
722                 }
723         }
724
725         /*
726          *      We now have a home server, see if we can insert it
727          *      into pre-existing pool.
728          */
729         if (insert_point >= 0) {
730                 rad_assert(pool != NULL);
731                 pool->servers[insert_point] = home;
732                 return 1;
733         }
734
735         rad_assert(pool == NULL);
736         rad_assert(home != NULL);
737
738         /*
739          *      Count the old-style realms of this name.
740          */
741         num_home_servers = 0;
742         for (cs = cf_section_sub_find_name2(mainconfig.config, "realm", name);
743              cs != NULL;
744              cs = cf_section_sub_find_name2(cs, "realm", name)) {
745                 num_home_servers++;
746         }
747
748
749         pool = rad_malloc(sizeof(*pool) + num_home_servers * sizeof(pool->servers[0]));
750         memset(pool, 0, sizeof(*pool) + num_home_servers * sizeof(pool->servers[0]));
751
752         pool->name = name;
753         pool->type = ldflag;
754         pool->server_type = type;
755         pool->num_home_servers = num_home_servers;
756         pool->servers[0] = home;
757
758         if (!rbtree_insert(home_pools_byname, pool)) {
759                 rad_assert("Internal sanity check failed");
760                 return 0;
761         }
762
763         *pool_p = pool;
764
765         return 1;
766 }
767
768 static int old_realm_config(const char *filename, CONF_SECTION *cs, REALM *r)
769 {
770         char *host;
771         const char *secret;
772         home_pool_type_t ldflag;
773
774         secret = cf_section_value_find(cs, "secret");
775
776         host = cf_section_value_find(cs, "ldflag");
777         if (!host ||
778             (strcasecmp(host, "fail_over") == 0)) {
779                 ldflag = HOME_POOL_FAIL_OVER;
780                 DEBUG2("  realm %s: ldflag = fail_over", r->name);
781
782         } else if (strcasecmp(host, "round_robin") == 0) {
783                 ldflag = HOME_POOL_LOAD_BALANCE;
784                 DEBUG2("  realm %s: ldflag = round_robin", r->name);
785
786         } else {
787                 radlog(L_ERR, "%s[%d]: Unknown value \"%s\" for ldflag",
788                        filename, cf_section_lineno(cs), host);
789                 return 0;
790         }
791
792         /*
793          *      Allow old-style if it doesn't exist, or if it exists and
794          *      it's LOCAL.
795          */
796         if (((host = cf_section_value_find(cs, "authhost")) != NULL) &&
797             (strcmp(host, "LOCAL") != 0)) {
798                 if (!secret) {
799                         radlog(L_ERR, "%s[%d]: No shared secret supplied for realm: %s",
800                                filename, cf_section_lineno(cs), r->name);
801                         return 0;
802                 }
803
804                 DEBUG2("  realm %s: authhost = %s",  r->name, host);
805
806                 if (!old_server_add(filename, cf_section_lineno(cs),
807                                     host, secret, ldflag,
808                                     &r->auth_pool, HOME_TYPE_AUTH)) {
809                         return 0;
810                 }
811         }
812
813         if (((host = cf_section_value_find(cs, "accthost")) != NULL) &&
814             (strcmp(host, "LOCAL") != 0)) {
815                 if (!secret) {
816                         radlog(L_ERR, "%s[%d]: No shared secret supplied for realm: %s",
817                                filename, cf_section_lineno(cs), r->name);
818                         return 0;
819                 }
820
821                 DEBUG2("  realm %s: accthost = %s", r->name, host);
822
823                 if (!old_server_add(filename, cf_section_lineno(cs),
824                                     host, secret, ldflag,
825                                     &r->auth_pool, HOME_TYPE_ACCT)) {
826                         return 0;
827                 }
828         }
829
830         if (secret) DEBUG2("  realm %s: secret = %s", r->name, secret);
831
832         return 1;
833         
834 }
835
836
837 static int add_pool_to_realm(const char *filename, int lineno,
838                              const char *name, home_pool_t **dest,
839                              int server_type)
840 {
841         home_pool_t mypool, *pool;
842
843         mypool.name = name;
844         pool = rbtree_finddata(home_pools_byname, &mypool);
845         if (!pool) {
846                 CONF_SECTION *pool_cs;
847
848                 pool_cs = cf_section_sub_find_name2(NULL, "server_pool",
849                                                     name);
850                 if (!pool_cs) {
851                         radlog(L_ERR, "%s[%d]: Failed to find server_pool \"%s\"",
852                                filename, lineno, name);
853                         return 0;
854                 }
855
856                 if (!server_pool_add(filename, pool_cs)) {
857                         return 0;
858                 }
859                 
860                 pool = rbtree_finddata(home_pools_byname, &mypool);
861                 if (!pool) {
862                         rad_assert("Internal sanity check failed");
863                         return 0;
864                 }
865         }
866
867         if (pool->server_type != server_type) {
868                 radlog(L_ERR, "%s[%d]: Incompatible server_pool \"%s\" (mixed auth_pool / acct_pool)",
869                        filename, lineno, name);
870                 return 0;
871         }
872
873         *dest = pool;
874
875         return 1;
876 }
877
878 int realm_add(const char *filename, CONF_SECTION *cs)
879 {
880         const char *name2;
881         char *pool = NULL;
882         REALM *r;
883         CONF_PAIR *cp;
884
885         name2 = cf_section_name1(cs);
886         if (!name2 || (strcasecmp(name2, "realm") != 0)) {
887                 radlog(L_ERR, "%s[%d]: Section is not a realm.",
888                        filename, cf_section_lineno(cs));
889                 return 0;
890         }
891
892         name2 = cf_section_name2(cs);
893         if (!name2) {
894                 radlog(L_ERR, "%s[%d]: Realm section is missing the realm name.",
895                        filename, cf_section_lineno(cs));
896                 return 0;
897         }
898
899         /*
900          *      The realm MAY already exist if it's an old-style realm.
901          *      In that case, merge the old-style realm with this one.
902          */
903         r = realm_find(name2);
904         if (r) {
905                 if (cf_pair_find(cs, "auth_pool") ||
906                     cf_pair_find(cs, "acct_pool")) {
907                         radlog(L_ERR, "%s[%d]: Duplicate realm \"%s\"",
908                                filename, cf_section_lineno(cs), name2);
909                         return 0;
910                 }
911
912                 if (!old_realm_config(filename, cs, r)) {
913                         return 0;
914                 }
915
916                 return 1;
917         }
918
919         r = rad_malloc(sizeof(*r));
920         memset(r, 0, sizeof(*r));
921
922         r->name = name2;
923
924         /*
925          *      Prefer new configuration to old one.
926          */
927         cp = cf_pair_find(cs, "auth_pool");
928         if (cp) pool = cf_pair_value(cp);
929         if (cp && pool) {
930                 if (!add_pool_to_realm(filename, cf_pair_lineno(cp),
931                                        pool, &r->auth_pool, HOME_TYPE_AUTH)) {
932                         free(r);
933                         return 0;
934                 }
935                 DEBUG2(" realm %s: auth_pool = %s", name2, pool);
936         }
937
938         cp = cf_pair_find(cs, "acct_pool");
939         if (cp) pool = cf_pair_value(cp);
940         if (cp && pool) {
941                 if (!add_pool_to_realm(filename, cf_pair_lineno(cp),
942                                        pool, &r->acct_pool, HOME_TYPE_ACCT)) {
943                         free(r);
944                         return 0;
945                 }
946                 DEBUG2(" realm %s: acct_pool = %s", name2, pool);
947         }
948
949         r->striprealm = 1;
950         
951         if ((cf_section_value_find(cs, "nostrip")) != NULL) {
952                 r->striprealm = 0;
953                 DEBUG2(" realm %s: nostrip", name2);
954         }
955
956         /*
957          *      We're a new-style realm.  Complain if we see the old
958          *      directives.
959          */
960         if (r->auth_pool || r->acct_pool) {
961                 if (((cp = cf_pair_find(cs, "authhost")) != NULL) ||
962                     ((cp = cf_pair_find(cs, "accthost")) != NULL) ||
963                     ((cp = cf_pair_find(cs, "secret")) != NULL) ||
964                     ((cp = cf_pair_find(cs, "ldflag")) != NULL)) {
965                         DEBUG2("WARNING: Ignoring old-style configuration entry \"%s\" in realm \"%s\"", cf_pair_attr(cp), r->name);
966                 }
967
968
969                 /*
970                  *      The realm MAY be an old-style realm, as there
971                  *      was no auth_pool or acct_pool.  Double-check
972                  *      it, just to be safe.
973                  */
974         } else if (!old_realm_config(filename, cs, r)) {
975                 free(r);
976                 return 0;
977         }
978
979         if (!rbtree_insert(realms_byname, r)) {
980                 rad_assert("Internal sanity check failed");
981                 free(r);
982                 return 0;
983         }
984
985         return 1;
986 }
987
988
989 /*
990  *      Find a realm in the REALM list.
991  */
992 REALM *realm_find(const char *name)
993 {
994         REALM myrealm;
995
996         if (!name) name = "NULL";
997
998         myrealm.name = name;
999         return rbtree_finddata(realms_byname, &myrealm);
1000 }
1001
1002
1003 home_server *home_server_ldb(REALM *r, int code)
1004 {
1005         uint32_t        count;
1006         home_server     *lb;
1007         home_pool_t     *pool;
1008
1009         if (code == PW_AUTHENTICATION_REQUEST) {
1010                 pool = r->auth_pool;
1011
1012         } else if (code == PW_ACCOUNTING_REQUEST) {
1013                 pool = r->acct_pool;
1014
1015         } else {
1016                 rad_assert("Internal sanity check failed");
1017                 return NULL;
1018         }
1019
1020         if (!pool) {
1021                 rad_assert("Internal sanity check failed");
1022                 return NULL;
1023         }
1024
1025         lb = NULL;
1026
1027         for (count = 0; count < pool->num_home_servers; count++) {
1028                 home_server *home = pool->servers[count];
1029
1030                 if (home->state == HOME_STATE_IS_DEAD) {
1031                         continue;
1032                 }
1033
1034                 /*
1035                  *      This home server is too busy.  Choose another one.
1036                  */
1037                 if (home->currently_outstanding >= home->max_outstanding) {
1038                         continue;
1039                 }
1040                 
1041                 /*
1042                  *      Fail-over, pick the first one that matches.
1043                  */
1044                 if (pool->type == HOME_POOL_FAIL_OVER) {
1045                         return home;
1046                 }
1047
1048                 /*
1049                  *      FUTURE: If we're well into "zombie_period",
1050                  *      then we probably want to think about
1051                  *      load-balancing the packet somewhere else, as
1052                  *      the home server is probably dead.
1053                  */
1054
1055                 /*
1056                  *      We're doing load-balancing.  Pick a random
1057                  *      number, which will be used to determine which
1058                  *      home server is chosen.
1059                  */
1060                 if (!lb) {
1061                         lb = home;
1062                         continue;
1063                 }
1064
1065                 /*
1066                  *      See the "camel book" for why this works.
1067                  *
1068                  *      If (rand(0..n) < 1), pick the current server.
1069                  *      We add a scale factor of 65536, to avoid
1070                  *      floating point.
1071                  */
1072                 if (((count + 1) * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) {
1073                         lb = home;
1074                 }
1075         } /* loop over the realms */
1076
1077         /*
1078          *      No live match found, and no fallback to the "DEFAULT"
1079          *      realm.  We fix this by blindly marking all servers as
1080          *      "live".  But only do it for ones that don't support
1081          *      "pings", as they will be marked live when they
1082          *      actually are live.
1083          */
1084         if (!lb &&
1085             !mainconfig.proxy_fallback &&
1086             mainconfig.wake_all_if_all_dead) {
1087                 for (count = 0; count < pool->num_home_servers; count++) {
1088                         home_server *home = pool->servers[count];
1089
1090                         if ((home->state == HOME_STATE_IS_DEAD) &&
1091                             (home->ping_check == HOME_PING_CHECK_NONE)) {
1092                                 home->state = HOME_STATE_ALIVE;
1093                                 if (!lb) lb = home;
1094                         }
1095                 }
1096         }
1097
1098         /*
1099          *      Still nothing.  Look up the DEFAULT realm, but only
1100          *      if we weren't looking up the NULL or DEFAULT realms.
1101          */
1102         if (!lb &&
1103             mainconfig.proxy_fallback &&
1104             (strcmp(r->name, "NULL") != 0) &&
1105             (strcmp(r->name, "DEFAULT") != 0)) {
1106                 REALM *rd = realm_find("DEFAULT");
1107
1108                 if (rd) {
1109                         DEBUG2("  Realm %s has no live home servers.  Falling back to the DEFAULT realm.", r->name);
1110                         return home_server_ldb(rd, code);
1111                 }
1112         }
1113
1114         /*
1115          *      Return the appropriate home server.
1116          */
1117         return lb;
1118 }
1119
1120
1121 home_server *home_server_find(lrad_ipaddr_t *ipaddr, int port)
1122 {
1123         home_server myhome;
1124
1125         myhome.ipaddr = *ipaddr;
1126         myhome.port = port;
1127
1128         return rbtree_finddata(home_servers_byaddr, &myhome);
1129 }