Look up home servers by type, too.
[freeradius.git] / src / main / realms.c
1 /*
2  * realms.c     Realm handling code
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2007  The FreeRADIUS server project
21  * Copyright 2007  Alan DeKok <aland@deployingradius.com>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/rad_assert.h>
29
30 #include <sys/stat.h>
31
32 #include <ctype.h>
33 #include <fcntl.h>
34
35 #ifdef HAVE_REGEX_H
36 #include <regex.h>
37
38 /*
39  *  For POSIX Regular expressions.
40  *  (0) Means no extended regular expressions.
41  *  REG_EXTENDED means use extended regular expressions.
42  */
43 #ifndef REG_EXTENDED
44 #define REG_EXTENDED (0)
45 #endif
46
47 #ifndef REG_NOSUB
48 #define REG_NOSUB (0)
49 #endif
50
51 #ifndef REG_ICASE
52 #define REG_ICASE (0)
53 #endif
54 #endif
55
56 static rbtree_t *realms_byname = NULL;
57
58 #ifdef HAVE_REGEX_H
59 typedef struct realm_regex_t {
60         REALM   *realm;
61         struct realm_regex_t *next;
62 } realm_regex_t;
63
64 static realm_regex_t *realms_regex = NULL;
65
66 #endif /* HAVE_REGEX_H */
67
68 typedef struct realm_config_t {
69         CONF_SECTION    *cs;
70         int             dead_time;
71         int             retry_count;
72         int             retry_delay;
73         int             fallback;
74         int             wake_all_if_all_dead;
75 } realm_config_t;
76
77 static realm_config_t *realm_config = NULL;
78
79 #ifdef WITH_PROXY
80 static rbtree_t *home_servers_byaddr = NULL;
81 static rbtree_t *home_servers_byname = NULL;
82 #ifdef WITH_STATS
83 static int home_server_max_number = 0;
84 static rbtree_t *home_servers_bynumber = NULL;
85 #endif
86
87 static rbtree_t *home_pools_byname = NULL;
88
89 /*
90  *  Map the proxy server configuration parameters to variables.
91  */
92 static const CONF_PARSER proxy_config[] = {
93         { "retry_delay",  PW_TYPE_INTEGER,
94           offsetof(realm_config_t, retry_delay),
95           NULL, Stringify(RETRY_DELAY) },
96
97         { "retry_count",  PW_TYPE_INTEGER,
98           offsetof(realm_config_t, retry_count),
99           NULL, Stringify(RETRY_COUNT) },
100
101         { "default_fallback", PW_TYPE_BOOLEAN,
102           offsetof(realm_config_t, fallback),
103           NULL, "no" },
104
105         { "dead_time",    PW_TYPE_INTEGER, 
106           offsetof(realm_config_t, dead_time),
107           NULL, Stringify(DEAD_TIME) },
108
109         { "wake_all_if_all_dead", PW_TYPE_BOOLEAN,
110           offsetof(realm_config_t, wake_all_if_all_dead),
111           NULL, "no" },
112
113         { NULL, -1, 0, NULL, NULL }
114 };
115 #endif
116
117 static int realm_name_cmp(const void *one, const void *two)
118 {
119         const REALM *a = one;
120         const REALM *b = two;
121
122         return strcasecmp(a->name, b->name);
123 }
124
125
126 #ifdef WITH_PROXY
127 static int home_server_name_cmp(const void *one, const void *two)
128 {
129         const home_server *a = one;
130         const home_server *b = two;
131
132         if (a->type < b->type) return -1;
133         if (a->type > b->type) return +1;
134
135         return strcasecmp(a->name, b->name);
136 }
137
138 static int home_server_addr_cmp(const void *one, const void *two)
139 {
140         const home_server *a = one;
141         const home_server *b = two;
142
143         if (a->server && !b->server) return -1;
144         if (!a->server && b->server) return +1;
145         if (a->server && b->server) {
146                 int rcode = a->type - b->type;
147                 if (rcode != 0) return rcode;
148                 return strcmp(a->server, b->server);
149         }
150
151         if (a->port < b->port) return -1;
152         if (a->port > b->port) return +1;
153
154         return fr_ipaddr_cmp(&a->ipaddr, &b->ipaddr);
155 }
156
157 #ifdef WITH_STATS
158 static int home_server_number_cmp(const void *one, const void *two)
159 {
160         const home_server *a = one;
161         const home_server *b = two;
162
163         return (a->number - b->number);
164 }
165 #endif
166
167 static int home_pool_name_cmp(const void *one, const void *two)
168 {
169         const home_pool_t *a = one;
170         const home_pool_t *b = two;
171
172         if (a->server_type < b->server_type) return -1;
173         if (a->server_type > b->server_type) return +1;
174
175         return strcasecmp(a->name, b->name);
176 }
177
178
179 /*
180  *      Xlat for %{home_server:foo}
181  */
182 static size_t xlat_home_server(UNUSED void *instance, REQUEST *request,
183                                char *fmt, char *out, size_t outlen,
184                                UNUSED RADIUS_ESCAPE_STRING func)
185 {
186         const char *value = NULL;
187         CONF_PAIR *cp;
188
189         if (!fmt || !out || (outlen < 1)) return 0;
190
191         if (!request || !request->home_server) {
192                 *out = '\0';
193                 return 0;
194         }
195
196         cp = cf_pair_find(request->home_server->cs, fmt);
197         if (!cp || !(value = cf_pair_value(cp))) {
198                 *out = '\0';
199                 return 0;
200         }
201         
202         strlcpy(out, value, outlen);
203
204         return strlen(out);
205 }
206
207
208 /*
209  *      Xlat for %{home_server_pool:foo}
210  */
211 static size_t xlat_server_pool(UNUSED void *instance, REQUEST *request,
212                                char *fmt, char *out, size_t outlen,
213                                UNUSED RADIUS_ESCAPE_STRING func)
214 {
215         const char *value = NULL;
216         CONF_PAIR *cp;
217
218         if (!fmt || !out || (outlen < 1)) return 0;
219
220         if (!request || !request->home_pool) {
221                 *out = '\0';
222                 return 0;
223         }
224
225         cp = cf_pair_find(request->home_pool->cs, fmt);
226         if (!cp || !(value = cf_pair_value(cp))) {
227                 *out = '\0';
228                 return 0;
229         }
230         
231         strlcpy(out, value, outlen);
232
233         return strlen(out);
234 }
235 #endif
236
237 void realms_free(void)
238 {
239 #ifdef WITH_PROXY
240 #ifdef WITH_STATS
241         rbtree_free(home_servers_bynumber);
242         home_servers_bynumber = NULL;
243 #endif
244
245         rbtree_free(home_servers_byname);
246         home_servers_byname = NULL;
247
248         rbtree_free(home_servers_byaddr);
249         home_servers_byaddr = NULL;
250
251         rbtree_free(home_pools_byname);
252         home_pools_byname = NULL;
253 #endif
254
255         rbtree_free(realms_byname);
256         realms_byname = NULL;
257
258 #ifdef HAVE_REGEX_H
259         if (realms_regex) {
260                 realm_regex_t *this, *next;
261
262                 for (this = realms_regex; this != NULL; this = next) {
263                         next = this->next;
264                         free(this->realm);
265                         free(this);
266                 }
267                 realms_regex = NULL;
268         }
269 #endif
270
271         free(realm_config);
272         realm_config = NULL;
273 }
274
275
276 #ifdef WITH_PROXY
277 static struct in_addr hs_ip4addr;
278 static struct in6_addr hs_ip6addr;
279 static char *hs_srcipaddr = NULL;
280 static char *hs_type = NULL;
281 static char *hs_check = NULL;
282 static char *hs_virtual_server = NULL;
283
284 static CONF_PARSER home_server_config[] = {
285         { "ipaddr",  PW_TYPE_IPADDR,
286           0, &hs_ip4addr,  NULL },
287         { "ipv6addr",  PW_TYPE_IPV6ADDR,
288           0, &hs_ip6addr, NULL },
289         { "virtual_server",  PW_TYPE_STRING_PTR,
290           0, &hs_virtual_server, NULL },
291
292         { "port", PW_TYPE_INTEGER,
293           offsetof(home_server,port), NULL,   "0" },
294
295         { "type",  PW_TYPE_STRING_PTR,
296           0, &hs_type, NULL },
297
298         { "secret",  PW_TYPE_STRING_PTR,
299           offsetof(home_server,secret), NULL,  NULL},
300
301         { "src_ipaddr",  PW_TYPE_STRING_PTR,
302           0, &hs_srcipaddr,  NULL },
303
304         { "response_window", PW_TYPE_INTEGER,
305           offsetof(home_server,response_window), NULL,   "30" },
306         { "no_response_fail", PW_TYPE_BOOLEAN,
307           offsetof(home_server,no_response_fail), NULL,   NULL },
308         { "max_outstanding", PW_TYPE_INTEGER,
309           offsetof(home_server,max_outstanding), NULL,   "65536" },
310         { "require_message_authenticator",  PW_TYPE_BOOLEAN,
311           offsetof(home_server, message_authenticator), 0, NULL },
312
313         { "zombie_period", PW_TYPE_INTEGER,
314           offsetof(home_server,zombie_period), NULL,   "40" },
315         { "status_check", PW_TYPE_STRING_PTR,
316           0, &hs_check,   "none" },
317         { "ping_check", PW_TYPE_STRING_PTR,
318           0, &hs_check,   NULL },
319
320         { "ping_interval", PW_TYPE_INTEGER,
321           offsetof(home_server,ping_interval), NULL,   "30" },
322         { "check_interval", PW_TYPE_INTEGER,
323           offsetof(home_server,ping_interval), NULL,   "30" },
324         { "num_answers_to_alive", PW_TYPE_INTEGER,
325           offsetof(home_server,num_pings_to_alive), NULL,   "3" },
326         { "num_pings_to_alive", PW_TYPE_INTEGER,
327           offsetof(home_server,num_pings_to_alive), NULL,   "3" },
328         { "revive_interval", PW_TYPE_INTEGER,
329           offsetof(home_server,revive_interval), NULL,   "300" },
330         { "status_check_timeout", PW_TYPE_INTEGER,
331           offsetof(home_server,ping_timeout), NULL,   "4" },
332
333         { "username",  PW_TYPE_STRING_PTR,
334           offsetof(home_server,ping_user_name), NULL,  NULL},
335         { "password",  PW_TYPE_STRING_PTR,
336           offsetof(home_server,ping_user_password), NULL,  NULL},
337
338 #ifdef WITH_STATS
339         { "historic_average_window", PW_TYPE_INTEGER,
340           offsetof(home_server,ema.window), NULL,  NULL },
341 #endif
342
343 #ifdef WITH_COA
344         { "irt",  PW_TYPE_INTEGER,
345           offsetof(home_server, coa_irt), 0, Stringify(2) },
346         { "mrt",  PW_TYPE_INTEGER,
347           offsetof(home_server, coa_mrt), 0, Stringify(16) },
348         { "mrc",  PW_TYPE_INTEGER,
349           offsetof(home_server, coa_mrc), 0, Stringify(5) },
350         { "mrd",  PW_TYPE_INTEGER,
351           offsetof(home_server, coa_mrd), 0, Stringify(30) },
352 #endif
353
354         { NULL, -1, 0, NULL, NULL }             /* end the list */
355 };
356
357
358 static int home_server_add(realm_config_t *rc, CONF_SECTION *cs, int pool_type)
359 {
360         const char *name2;
361         home_server *home;
362         int dual = FALSE;
363         CONF_PAIR *cp;
364
365         free(hs_virtual_server); /* used only for printing during parsing */
366         hs_virtual_server = NULL;
367
368         name2 = cf_section_name1(cs);
369         if (!name2 || (strcasecmp(name2, "home_server") != 0)) {
370                 cf_log_err(cf_sectiontoitem(cs),
371                            "Section is not a home_server.");
372                 return 0;
373         }
374
375         name2 = cf_section_name2(cs);
376         if (!name2) {
377                 cf_log_err(cf_sectiontoitem(cs),
378                            "Home server section is missing a name.");
379                 return 0;
380         }
381
382         home = rad_malloc(sizeof(*home));
383         memset(home, 0, sizeof(*home));
384
385         home->name = name2;
386         home->cs = cs;
387
388         memset(&hs_ip4addr, 0, sizeof(hs_ip4addr));
389         memset(&hs_ip6addr, 0, sizeof(hs_ip6addr));
390         if (cf_section_parse(cs, home, home_server_config) < 0) {
391                 free(home);
392                 return 0;
393         }
394
395         /*
396          *      Figure out which one to use.
397          */
398         if (cf_pair_find(cs, "ipaddr")) {
399                 home->ipaddr.af = AF_INET;
400                 home->ipaddr.ipaddr.ip4addr = hs_ip4addr;
401
402         } else if (cf_pair_find(cs, "ipv6addr")) {
403                 home->ipaddr.af = AF_INET6;
404                 home->ipaddr.ipaddr.ip6addr = hs_ip6addr;
405
406         } else if ((cp = cf_pair_find(cs, "virtual_server")) != NULL) {
407                 home->ipaddr.af = AF_UNSPEC;
408                 home->server = cf_pair_value(cp);
409                 if (!home->server) {
410                         cf_log_err(cf_sectiontoitem(cs),
411                                    "Invalid value for virtual_server");
412                         goto error;
413                 }
414
415                 if (!cf_section_sub_find_name2(rc->cs, "server", home->server)) {
416                   
417                         cf_log_err(cf_sectiontoitem(cs),
418                                    "No such server %s", home->server);
419                         goto error;
420                 }
421
422                 /*
423                  *      When CoA is used, the user has to specify the type
424                  *      of the home server, even when they point to
425                  *      virtual servers.
426                  */
427                 home->secret = strdup("");
428                 goto skip_port;
429
430         } else {
431                 cf_log_err(cf_sectiontoitem(cs),
432                            "No ipaddr, ipv6addr, or virtual_server defined for home server \"%s\".",
433                            name2);
434         error:
435                 free(home);
436                 free(hs_type);
437                 hs_type = NULL;
438                 free(hs_check);
439                 hs_check = NULL;
440                 free(hs_srcipaddr);
441                 hs_srcipaddr = NULL;
442                 return 0;
443         }
444
445         if (!home->port || (home->port > 65535)) {
446                 cf_log_err(cf_sectiontoitem(cs),
447                            "No port, or invalid port defined for home server %s.",
448                            name2);
449                 goto error;
450         }
451
452         if (0) {
453                 cf_log_err(cf_sectiontoitem(cs),
454                            "Fatal error!  Home server %s is ourselves!",
455                            name2);
456                 goto error;
457         }
458
459         if (!home->secret) {
460                 cf_log_err(cf_sectiontoitem(cs),
461                            "No shared secret defined for home server %s.",
462                            name2);
463                 goto error;
464         }
465
466         /*
467          *      Use a reasonable default.
468          */
469  skip_port:
470         if (!hs_type) hs_type = strdup("auth+acct");
471
472         if (strcasecmp(hs_type, "auth") == 0) {
473                 home->type = HOME_TYPE_AUTH;
474                 if (pool_type != home->type) {
475                 mismatch:
476                         cf_log_err(cf_sectiontoitem(cs),
477                                    "Server pool cannot include home server %s of type \"%s\"",
478                                    name2, hs_type);
479                         goto error;
480                 }
481
482         } else if (strcasecmp(hs_type, "acct") == 0) {
483                 home->type = HOME_TYPE_ACCT;
484                 if (pool_type != home->type) goto mismatch;
485
486         } else if (strcasecmp(hs_type, "auth+acct") == 0) {
487                 home->type = HOME_TYPE_AUTH;
488                 dual = TRUE;
489
490 #ifdef WITH_COA
491         } else if (strcasecmp(hs_type, "coa") == 0) {
492                 home->type = HOME_TYPE_COA;
493                 dual = FALSE;
494
495                 if (pool_type != home->type) goto mismatch;
496
497                 if (home->server != NULL) {
498                         cf_log_err(cf_sectiontoitem(cs),
499                                    "Home servers of type \"coa\" cannot point to a virtual server");
500                         goto error;
501                 }
502 #endif
503
504         } else {
505                 cf_log_err(cf_sectiontoitem(cs),
506                            "Invalid type \"%s\" for home server %s.",
507                            hs_type, name2);
508                 goto error;
509         }
510         free(hs_type);
511         hs_type = NULL;
512
513         if (!hs_check || (strcasecmp(hs_check, "none") == 0)) {
514                 home->ping_check = HOME_PING_CHECK_NONE;
515
516         } else if (strcasecmp(hs_check, "status-server") == 0) {
517                 home->ping_check = HOME_PING_CHECK_STATUS_SERVER;
518
519         } else if (strcasecmp(hs_check, "request") == 0) {
520                 home->ping_check = HOME_PING_CHECK_REQUEST;
521
522         } else {
523                 cf_log_err(cf_sectiontoitem(cs),
524                            "Invalid ping_check \"%s\" for home server %s.",
525                            hs_check, name2);
526                 goto error;
527         }
528         free(hs_check);
529         hs_check = NULL;
530
531         if ((home->ping_check != HOME_PING_CHECK_NONE) &&
532             (home->ping_check != HOME_PING_CHECK_STATUS_SERVER)) {
533                 if (!home->ping_user_name) {
534                         cf_log_err(cf_sectiontoitem(cs), "You must supply a user name to enable ping checks");
535                         goto error;
536                 }
537
538                 if ((home->type == HOME_TYPE_AUTH) &&
539                     !home->ping_user_password) {
540                         cf_log_err(cf_sectiontoitem(cs), "You must supply a password to enable ping checks");
541                         goto error;
542                 }
543         }
544
545         if ((home->ipaddr.af != AF_UNSPEC) && /* could be virtual server */
546             rbtree_finddata(home_servers_byaddr, home)) {
547                 cf_log_err(cf_sectiontoitem(cs), "Duplicate home server");
548                 goto error;
549         }
550
551         /*
552          *      Look up the name using the *same* address family as
553          *      for the home server.
554          */
555         if (hs_srcipaddr && (home->ipaddr.af != AF_UNSPEC)) {
556                 if (ip_hton(hs_srcipaddr, home->ipaddr.af, &home->src_ipaddr) < 0) {
557                         cf_log_err(cf_sectiontoitem(cs), "Failed parsing src_ipaddr");
558                         goto error;
559                 }
560         }
561         free(hs_srcipaddr);
562         hs_srcipaddr = NULL;
563
564         if (!rbtree_insert(home_servers_byname, home)) {
565                 cf_log_err(cf_sectiontoitem(cs),
566                            "Internal error %d adding home server %s.",
567                            __LINE__, name2);
568                 goto error;
569         }
570
571         if ((home->ipaddr.af != AF_UNSPEC) && /* could be virtual server */
572             !rbtree_insert(home_servers_byaddr, home)) {
573                 rbtree_deletebydata(home_servers_byname, home);
574                 cf_log_err(cf_sectiontoitem(cs),
575                            "Internal error %d adding home server %s.",
576                            __LINE__, name2);
577                 goto error;
578         }
579
580 #ifdef WITH_STATS
581         home->number = home_server_max_number++;
582         if (!rbtree_insert(home_servers_bynumber, home)) {
583                 rbtree_deletebydata(home_servers_byname, home);
584                 if (home->ipaddr.af != AF_UNSPEC) {
585                         rbtree_deletebydata(home_servers_byname, home);
586                 }
587                 cf_log_err(cf_sectiontoitem(cs),
588                            "Internal error %d adding home server %s.",
589                            __LINE__, name2);
590                 goto error;
591         }
592 #endif
593
594         if (home->max_outstanding < 8) home->max_outstanding = 8;
595         if (home->max_outstanding > 65536*16) home->max_outstanding = 65536*16;
596
597         if (home->ping_interval < 6) home->ping_interval = 6;
598         if (home->ping_interval > 120) home->ping_interval = 120;
599
600         if (home->response_window < 1) home->response_window = 1;
601         if (home->response_window > 60) home->response_window = 60;
602
603         if (home->zombie_period < 1) home->zombie_period = 1;
604         if (home->zombie_period > 120) home->zombie_period = 120;
605
606         if (home->zombie_period < home->response_window) {
607                 home->zombie_period = home->response_window;
608         }
609
610         if (home->num_pings_to_alive < 3) home->num_pings_to_alive = 3;
611         if (home->num_pings_to_alive > 10) home->num_pings_to_alive = 10;
612
613         if (home->ping_timeout < 3) home->ping_timeout = 3;
614         if (home->ping_timeout > 10) home->ping_timeout = 10;
615
616         if (home->revive_interval < 60) home->revive_interval = 60;
617         if (home->revive_interval > 3600) home->revive_interval = 3600;
618
619 #ifdef WITH_COA
620         if (home->coa_irt < 1) home->coa_irt = 1;
621         if (home->coa_irt > 5) home->coa_irt = 5;
622
623         if (home->coa_mrc < 0) home->coa_mrc = 0;
624         if (home->coa_mrc > 20 ) home->coa_mrc = 20;
625
626         if (home->coa_mrt < 0) home->coa_mrt = 0;
627         if (home->coa_mrt > 30 ) home->coa_mrt = 30;
628
629         if (home->coa_mrd < 5) home->coa_mrd = 5;
630         if (home->coa_mrd > 60 ) home->coa_mrd = 60;
631 #endif
632
633         if (dual) {
634                 home_server *home2 = rad_malloc(sizeof(*home2));
635
636                 memcpy(home2, home, sizeof(*home2));
637
638                 home2->type = HOME_TYPE_ACCT;
639                 home2->port++;
640                 home2->ping_user_password = NULL;
641                 home2->cs = cs;
642
643                 if (!rbtree_insert(home_servers_byname, home2)) {
644                         cf_log_err(cf_sectiontoitem(cs),
645                                    "Internal error %d adding home server %s.",
646                                    __LINE__, name2);
647                         free(home2);
648                         return 0;
649                 }
650                 
651                 if ((home->ipaddr.af != AF_UNSPEC) &&
652                     !rbtree_insert(home_servers_byaddr, home2)) {
653                         rbtree_deletebydata(home_servers_byname, home2);
654                         cf_log_err(cf_sectiontoitem(cs),
655                                    "Internal error %d adding home server %s.",
656                                    __LINE__, name2);
657                         free(home2);
658                         return 0;
659                 }
660
661 #ifdef WITH_STATS
662                 home2->number = home_server_max_number++;
663                 if (!rbtree_insert(home_servers_bynumber, home2)) {
664                         rbtree_deletebydata(home_servers_byname, home2);
665                         if (home2->ipaddr.af != AF_UNSPEC) {
666                                 rbtree_deletebydata(home_servers_byname, home2);
667                         }
668                         cf_log_err(cf_sectiontoitem(cs),
669                                    "Internal error %d adding home server %s.",
670                                    __LINE__, name2);
671                         free(home2);
672                         return 0;
673                 }
674 #endif
675         }
676
677         return 1;
678 }
679
680
681 static home_pool_t *server_pool_alloc(const char *name, home_pool_type_t type,
682                                       int server_type, int num_home_servers)
683 {
684         home_pool_t *pool;
685
686         pool = rad_malloc(sizeof(*pool) + (sizeof(pool->servers[0]) *
687                                            num_home_servers));
688         if (!pool) return NULL; /* just for pairanoia */
689         
690         memset(pool, 0, sizeof(*pool) + (sizeof(pool->servers[0]) *
691                                          num_home_servers));
692
693         pool->name = name;
694         pool->type = type;
695         pool->server_type = server_type;
696         pool->num_home_servers = num_home_servers;
697
698         return pool;
699 }
700
701 static int pool_check_home_server(realm_config_t *rc, CONF_PAIR *cp,
702                                   const char *name, int server_type,
703                                   home_server **phome)
704 {
705         home_server myhome, *home;
706         CONF_SECTION *server_cs;
707
708         if (!name) {
709                 cf_log_err(cf_pairtoitem(cp),
710                            "No value given for home_server.");
711                 return 0;
712         }
713
714         myhome.name = name;
715         myhome.type = server_type;
716         home = rbtree_finddata(home_servers_byname, &myhome);
717         if (home) {
718                 *phome = home;
719                 return 1;
720         }
721         
722         server_cs = cf_section_sub_find_name2(rc->cs, "home_server", name);
723         if (!server_cs) {
724                 cf_log_err(cf_pairtoitem(cp),
725                            "Unknown home_server \"%s\".", name);
726                 return 0;
727         }
728         
729         if (!home_server_add(rc, server_cs, server_type)) {
730                 return 0;
731         }
732         
733         home = rbtree_finddata(home_servers_byname, &myhome);
734         if (!home) {
735                 return 0;
736         }
737
738         *phome = home;
739         return 1;
740 }
741
742
743 static int server_pool_add(realm_config_t *rc,
744                            CONF_SECTION *cs, int server_type, int do_print)
745 {
746         const char *name2;
747         home_pool_t *pool = NULL;
748         const char *value;
749         CONF_PAIR *cp;
750         int num_home_servers;
751         home_server *home;
752
753         name2 = cf_section_name1(cs);
754         if (!name2 || ((strcasecmp(name2, "server_pool") != 0) &&
755                        (strcasecmp(name2, "home_server_pool") != 0))) {
756                 cf_log_err(cf_sectiontoitem(cs),
757                            "Section is not a home_server_pool.");
758                 return 0;
759         }
760
761         name2 = cf_section_name2(cs);
762         if (!name2) {
763                 cf_log_err(cf_sectiontoitem(cs),
764                            "Server pool section is missing a name.");
765                 return 0;
766         }
767
768         /*
769          *      Count the home servers and initalize them.
770          */
771         num_home_servers = 0;
772         for (cp = cf_pair_find(cs, "home_server");
773              cp != NULL;
774              cp = cf_pair_find_next(cs, cp, "home_server")) {
775                 num_home_servers++;
776
777                 if (!pool_check_home_server(rc, cp, cf_pair_value(cp),
778                                             server_type, &home)) {
779                                             
780                         return 0;
781                 }
782         }
783
784         if (num_home_servers == 0) {
785                 cf_log_err(cf_sectiontoitem(cs),
786                            "No home servers defined in pool %s",
787                            name2);
788                 goto error;
789         }
790
791         pool = server_pool_alloc(name2, HOME_POOL_FAIL_OVER, server_type,
792                                  num_home_servers);
793         pool->cs = cs;
794
795
796         /*
797          *      Fallback servers must be defined, and must be
798          *      virtual servers.
799          */
800         cp = cf_pair_find(cs, "fallback");
801         if (cp) {
802 #ifdef WITH_COA
803                 if (server_type == HOME_TYPE_COA) {
804                         cf_log_err(cf_sectiontoitem(cs), "Home server pools of type \"coa\" cannot have a fallback virtual server.");
805                         goto error;
806                 }
807 #endif
808
809                 if (!pool_check_home_server(rc, cp, cf_pair_value(cp),
810                                             server_type, &pool->fallback)) {
811                         
812                         goto error;
813                 }
814
815                 if (!pool->fallback->server) {
816                         cf_log_err(cf_sectiontoitem(cs), "Fallback home_server %s does NOT contain a virtual_server directive.", pool->fallback->name);
817                         goto error;
818                 }
819         }
820
821         if (do_print) cf_log_info(cs, " home_server_pool %s {", name2);
822
823         cp = cf_pair_find(cs, "type");
824         if (cp) {
825                 static FR_NAME_NUMBER pool_types[] = {
826                         { "load-balance", HOME_POOL_LOAD_BALANCE },
827                         { "fail-over", HOME_POOL_FAIL_OVER },
828                         { "round_robin", HOME_POOL_LOAD_BALANCE },
829                         { "fail_over", HOME_POOL_FAIL_OVER },
830                         { "client-balance", HOME_POOL_CLIENT_BALANCE },
831                         { "client-port-balance", HOME_POOL_CLIENT_PORT_BALANCE },
832                         { "keyed-balance", HOME_POOL_KEYED_BALANCE },
833                         { NULL, 0 }
834                 };
835
836                 value = cf_pair_value(cp);
837                 if (!value) {
838                         cf_log_err(cf_pairtoitem(cp),
839                                    "No value given for type.");
840                         goto error;
841                 }
842
843                 pool->type = fr_str2int(pool_types, value, 0);
844                 if (!pool->type) {
845                         cf_log_err(cf_pairtoitem(cp),
846                                    "Unknown type \"%s\".",
847                                    value);
848                         goto error;
849                 }
850
851                 if (do_print) cf_log_info(cs, "\ttype = %s", value);
852         }
853
854         cp = cf_pair_find(cs, "virtual_server");
855         if (cp) {
856                 pool->virtual_server = cf_pair_value(cp);               
857                 if (!pool->virtual_server) {
858                         cf_log_err(cf_pairtoitem(cp), "No value given for virtual_server");
859                         goto error;
860                 }
861
862                 if (do_print) {
863                         cf_log_info(cs, "\tvirtual_server = %s", pool->virtual_server);
864                 }
865
866                 if (!cf_section_sub_find_name2(rc->cs, "server",
867                                                pool->virtual_server)) {
868                         cf_log_err(cf_pairtoitem(cp), "No such server %s",
869                                    pool->virtual_server);
870                         goto error;
871                 }
872
873         }
874
875         num_home_servers = 0;
876         for (cp = cf_pair_find(cs, "home_server");
877              cp != NULL;
878              cp = cf_pair_find_next(cs, cp, "home_server")) {
879                 home_server myhome;
880
881                 value = cf_pair_value(cp);
882
883                 memset(&myhome, 0, sizeof(&myhome));
884                 myhome.name = value;
885                 myhome.type = server_type;
886
887                 home = rbtree_finddata(home_servers_byname, &myhome);
888                 if (!home) {
889                         DEBUG2("Internal sanity check failed");
890                         goto error;
891                 }
892
893                 if (0) {
894                         DEBUG2("Warning: Duplicate home server %s in server pool %s", home->name, pool->name);
895                         continue;
896                 }
897
898                 if (do_print) cf_log_info(cs, "\thome_server = %s", home->name);
899                 pool->servers[num_home_servers++] = home;
900         } /* loop over home_server's */
901
902         if (pool->fallback && do_print) {
903                 cf_log_info(cs, "\tfallback = %s", pool->fallback->name);
904         }
905
906         if (!rbtree_insert(home_pools_byname, pool)) {
907                 rad_assert("Internal sanity check failed");
908                 goto error;
909         }
910
911         if (do_print) cf_log_info(cs, " }");
912
913         cf_data_add(cs, "home_server_pool", pool, free);
914
915         rad_assert(pool->server_type != 0);
916
917         return 1;
918
919  error:
920         if (do_print) cf_log_info(cs, " }");
921         free(pool);
922         return 0;
923 }
924 #endif
925
926 static int old_server_add(realm_config_t *rc, CONF_SECTION *cs,
927                           const char *realm,
928                           const char *name, const char *secret,
929                           home_pool_type_t ldflag, home_pool_t **pool_p,
930                           int type, const char *server)
931 {
932 #ifdef WITH_PROXY
933         int i, insert_point, num_home_servers;
934         home_server myhome, *home;
935         home_pool_t mypool, *pool;
936         CONF_SECTION *subcs;
937 #else
938         rc = rc;                /* -Wunused */
939         realm = realm;
940         secret = secret;
941         ldflag = ldflag;
942         type = type;
943         server = server;
944 #endif
945
946         /*
947          *      LOCAL realms get sanity checked, and nothing else happens.
948          */
949         if (strcmp(name, "LOCAL") == 0) {
950                 if (*pool_p) {
951                         cf_log_err(cf_sectiontoitem(cs), "Realm \"%s\" cannot be both LOCAL and remote", name);
952                         return 0;
953                 }
954                 return 1;
955         }
956
957 #ifndef WITH_PROXY
958         return 0;               /* Not proxying.  Can't do non-LOCAL realms */
959
960 #else
961         mypool.name = realm;
962         mypool.server_type = type;
963         pool = rbtree_finddata(home_pools_byname, &mypool);
964         if (pool) {
965                 if (pool->type != ldflag) {
966                         cf_log_err(cf_sectiontoitem(cs), "Inconsistent ldflag for server pool \"%s\"", name);
967                         return 0;
968                 }
969
970                 if (pool->server_type != type) {
971                         cf_log_err(cf_sectiontoitem(cs), "Inconsistent home server type for server pool \"%s\"", name);
972                         return 0;
973                 }
974         }
975
976         myhome.name = name;
977         myhome.type = type;
978         home = rbtree_finddata(home_servers_byname, &myhome);
979         if (home) {
980                 if (secret && (strcmp(home->secret, secret) != 0)) {
981                         cf_log_err(cf_sectiontoitem(cs), "Inconsistent shared secret for home server \"%s\"", name);
982                         return 0;
983                 }
984
985                 if (home->type != type) {
986                         cf_log_err(cf_sectiontoitem(cs), "Inconsistent type for home server \"%s\"", name);
987                         return 0;
988                 }
989
990                 /*
991                  *      See if the home server is already listed
992                  *      in the pool.  If so, do nothing else.
993                  */
994                 if (pool) for (i = 0; i < pool->num_home_servers; i++) {
995                         if (pool->servers[i] == home) {
996                                 return 1;
997                         }
998                 }
999         }
1000
1001         /*
1002          *      If we do have a pool, check that there is room to
1003          *      insert the home server we've found, or the one that we
1004          *      create here.
1005          *
1006          *      Note that we insert it into the LAST available
1007          *      position, in order to maintain the same order as in
1008          *      the configuration files.
1009          */
1010         insert_point = -1;
1011         if (pool) {
1012                 for (i = pool->num_home_servers - 1; i >= 0; i--) {
1013                         if (pool->servers[i]) break;
1014
1015                         if (!pool->servers[i]) {
1016                                 insert_point = i;
1017                         }
1018                 }
1019
1020                 if (insert_point < 0) {
1021                         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);
1022                         return 0;
1023                 }
1024         }
1025
1026         /*
1027          *      No home server, allocate one.
1028          */
1029         if (!home) {
1030                 const char *p;
1031                 char *q;
1032
1033                 home = rad_malloc(sizeof(*home));
1034                 memset(home, 0, sizeof(*home));
1035
1036                 home->name = name;
1037                 home->hostname = name;
1038                 home->type = type;
1039                 home->secret = secret;
1040                 home->cs = cs;
1041
1042                 p = strchr(name, ':');
1043                 if (!p) {
1044                         if (type == HOME_TYPE_AUTH) {
1045                                 home->port = PW_AUTH_UDP_PORT;
1046                         } else {
1047                                 home->port = PW_ACCT_UDP_PORT;
1048                         }
1049
1050                         p = name;
1051                         q = NULL;
1052
1053                 } else if (p == name) {
1054                                 cf_log_err(cf_sectiontoitem(cs),
1055                                            "Invalid hostname %s.",
1056                                            name);
1057                                 free(home);
1058                                 return 0;
1059
1060                 } else {
1061                         home->port = atoi(p + 1);
1062                         if ((home->port == 0) || (home->port > 65535)) {
1063                                 cf_log_err(cf_sectiontoitem(cs),
1064                                            "Invalid port %s.",
1065                                            p + 1);
1066                                 free(home);
1067                                 return 0;
1068                         }
1069
1070                         q = rad_malloc((p - name) + 1);
1071                         memcpy(q, name, (p - name));
1072                         q[p - name] = '\0';
1073                         p = q;
1074                 }
1075
1076                 if (!server) {
1077                         if (ip_hton(p, AF_UNSPEC, &home->ipaddr) < 0) {
1078                                 cf_log_err(cf_sectiontoitem(cs),
1079                                            "Failed looking up hostname %s.",
1080                                            p);
1081                                 free(home);
1082                                 free(q);
1083                                 return 0;
1084                         }
1085                 } else {
1086                         home->ipaddr.af = AF_UNSPEC;
1087                         home->server = server;
1088                 }
1089                 free(q);
1090
1091                 /*
1092                  *      Use the old-style configuration.
1093                  */
1094                 home->max_outstanding = 65535*16;
1095                 home->zombie_period = rc->retry_delay * rc->retry_count;
1096                 if (home->zombie_period == 0) home->zombie_period =30;
1097                 home->response_window = home->zombie_period - 1;
1098
1099                 home->ping_check = HOME_PING_CHECK_NONE;
1100
1101                 home->revive_interval = rc->dead_time;
1102
1103                 if (rbtree_finddata(home_servers_byaddr, home)) {
1104                         cf_log_err(cf_sectiontoitem(cs), "Home server %s has the same IP address and/or port as another home server.", name);
1105                         free(home);
1106                         return 0;
1107                 }
1108
1109                 if (!rbtree_insert(home_servers_byname, home)) {
1110                         cf_log_err(cf_sectiontoitem(cs), "Internal error %d adding home server %s.", __LINE__, name);
1111                         free(home);
1112                         return 0;
1113                 }
1114
1115                 if (!rbtree_insert(home_servers_byaddr, home)) {
1116                         rbtree_deletebydata(home_servers_byname, home);
1117                         cf_log_err(cf_sectiontoitem(cs), "Internal error %d adding home server %s.", __LINE__, name);
1118                         free(home);
1119                         return 0;
1120                 }
1121
1122 #ifdef WITH_STATS
1123                 home->number = home_server_max_number++;
1124                 if (!rbtree_insert(home_servers_bynumber, home)) {
1125                         rbtree_deletebydata(home_servers_byname, home);
1126                         if (home->ipaddr.af != AF_UNSPEC) {
1127                                 rbtree_deletebydata(home_servers_byname, home);
1128                         }
1129                         cf_log_err(cf_sectiontoitem(cs),
1130                                    "Internal error %d adding home server %s.",
1131                                    __LINE__, name);
1132                         free(home);
1133                         return 0;
1134                 }
1135 #endif
1136         }
1137
1138         /*
1139          *      We now have a home server, see if we can insert it
1140          *      into pre-existing pool.
1141          */
1142         if (insert_point >= 0) {
1143                 rad_assert(pool != NULL);
1144                 pool->servers[insert_point] = home;
1145                 return 1;
1146         }
1147
1148         rad_assert(pool == NULL);
1149         rad_assert(home != NULL);
1150
1151         /*
1152          *      Count the old-style realms of this name.
1153          */
1154         num_home_servers = 0;
1155         for (subcs = cf_section_find_next(cs, NULL, "realm");
1156              subcs != NULL;
1157              subcs = cf_section_find_next(cs, subcs, "realm")) {
1158                 const char *this = cf_section_name2(subcs);
1159
1160                 if (!this || (strcmp(this, realm) != 0)) continue;
1161                 num_home_servers++;
1162         }
1163
1164         if (num_home_servers == 0) {
1165                 cf_log_err(cf_sectiontoitem(cs), "Internal error counting pools for home server %s.", name);
1166                 free(home);
1167                 return 0;
1168         }
1169
1170         pool = server_pool_alloc(realm, ldflag, type, num_home_servers);
1171         pool->cs = cs;
1172
1173         pool->servers[0] = home;
1174
1175         if (!rbtree_insert(home_pools_byname, pool)) {
1176                 rad_assert("Internal sanity check failed");
1177                 return 0;
1178         }
1179
1180         *pool_p = pool;
1181
1182         return 1;
1183 #endif
1184 }
1185
1186 static int old_realm_config(realm_config_t *rc, CONF_SECTION *cs, REALM *r)
1187 {
1188         const char *host;
1189         const char *secret = NULL;
1190         home_pool_type_t ldflag;
1191         CONF_PAIR *cp;
1192
1193         cp = cf_pair_find(cs, "ldflag");
1194         ldflag = HOME_POOL_FAIL_OVER;
1195         if (cp) {
1196                 host = cf_pair_value(cp);
1197                 if (!host) {
1198                         cf_log_err(cf_pairtoitem(cp), "No value specified for ldflag");
1199                         return 0;
1200                 }
1201
1202                 if (strcasecmp(host, "fail_over") == 0) {
1203                         cf_log_info(cs, "\tldflag = fail_over");
1204                         
1205                 } else if (strcasecmp(host, "round_robin") == 0) {
1206                         ldflag = HOME_POOL_LOAD_BALANCE;
1207                         cf_log_info(cs, "\tldflag = round_robin");
1208                         
1209                 } else {
1210                         cf_log_err(cf_sectiontoitem(cs), "Unknown value \"%s\" for ldflag", host);
1211                         return 0;
1212                 }
1213         } /* else don't print it. */
1214
1215         /*
1216          *      Allow old-style if it doesn't exist, or if it exists and
1217          *      it's LOCAL.
1218          */
1219         cp = cf_pair_find(cs, "authhost");
1220         if (cp) {
1221                 host = cf_pair_value(cp);
1222                 if (!host) {
1223                         cf_log_err(cf_pairtoitem(cp), "No value specified for authhost");
1224                         return 0;
1225                 }
1226
1227                 if (strcmp(host, "LOCAL") != 0) {
1228                         cp = cf_pair_find(cs, "secret");
1229                         if (!cp) {
1230                                 cf_log_err(cf_sectiontoitem(cs), "No shared secret supplied for realm: %s", r->name);
1231                                 return 0;
1232                         }
1233
1234                         secret = cf_pair_value(cp);
1235                         if (!secret) {
1236                                 cf_log_err(cf_pairtoitem(cp), "No value specified for secret");
1237                                 return 0;
1238                         }
1239                 }
1240                         
1241                 cf_log_info(cs, "\tauthhost = %s",  host);
1242
1243                 if (!old_server_add(rc, cs, r->name, host, secret, ldflag,
1244                                     &r->auth_pool, HOME_TYPE_AUTH, NULL)) {
1245                         return 0;
1246                 }
1247         }
1248
1249         cp = cf_pair_find(cs, "accthost");
1250         if (cp) {
1251                 host = cf_pair_value(cp);
1252                 if (!host) {
1253                         cf_log_err(cf_pairtoitem(cp), "No value specified for accthost");
1254                         return 0;
1255                 }
1256
1257                 /*
1258                  *      Don't look for a secret again if it was found
1259                  *      above.
1260                  */
1261                 if ((strcmp(host, "LOCAL") != 0) && !secret) {
1262                         cp = cf_pair_find(cs, "secret");
1263                         if (!cp) {
1264                                 cf_log_err(cf_sectiontoitem(cs), "No shared secret supplied for realm: %s", r->name);
1265                                 return 0;
1266                         }
1267                         
1268                         secret = cf_pair_value(cp);
1269                         if (!secret) {
1270                                 cf_log_err(cf_pairtoitem(cp), "No value specified for secret");
1271                                 return 0;
1272                         }
1273                 }
1274                 
1275                 cf_log_info(cs, "\taccthost = %s", host);
1276
1277                 if (!old_server_add(rc, cs, r->name, host, secret, ldflag,
1278                                     &r->acct_pool, HOME_TYPE_ACCT, NULL)) {
1279                         return 0;
1280                 }
1281         }
1282
1283         cp = cf_pair_find(cs, "virtual_server");
1284         if (cp) {
1285                 host = cf_pair_value(cp);
1286                 if (!host) {
1287                         cf_log_err(cf_pairtoitem(cp), "No value specified for virtual_server");
1288                         return 0;
1289                 }
1290
1291                 cf_log_info(cs, "\tvirtual_server = %s", host);
1292
1293                 if (!old_server_add(rc, cs, r->name, host, "", ldflag,
1294                                     &r->auth_pool, HOME_TYPE_AUTH, host)) {
1295                         return 0;
1296                 }
1297                 if (!old_server_add(rc, cs, r->name, host, "", ldflag,
1298                                     &r->acct_pool, HOME_TYPE_ACCT, host)) {
1299                         return 0;
1300                 }
1301         }
1302
1303         if (secret) cf_log_info(cs, "\tsecret = %s", secret);
1304
1305         return 1;
1306
1307 }
1308
1309
1310 #ifdef WITH_PROXY
1311 static int add_pool_to_realm(realm_config_t *rc, CONF_SECTION *cs,
1312                              const char *name, home_pool_t **dest,
1313                              int server_type, int do_print)
1314 {
1315         home_pool_t mypool, *pool;
1316
1317         mypool.name = name;
1318         mypool.server_type = server_type;
1319
1320         pool = rbtree_finddata(home_pools_byname, &mypool);
1321         if (!pool) {
1322                 CONF_SECTION *pool_cs;
1323
1324                 pool_cs = cf_section_sub_find_name2(rc->cs,
1325                                                     "home_server_pool",
1326                                                     name);
1327                 if (!pool_cs) {
1328                         pool_cs = cf_section_sub_find_name2(rc->cs,
1329                                                             "server_pool",
1330                                                             name);
1331                 }
1332                 if (!pool_cs) {
1333                         cf_log_err(cf_sectiontoitem(cs), "Failed to find home_server_pool \"%s\"", name);
1334                         return 0;
1335                 }
1336
1337                 if (!server_pool_add(rc, pool_cs, server_type, do_print)) {
1338                         return 0;
1339                 }
1340
1341                 pool = rbtree_finddata(home_pools_byname, &mypool);
1342                 if (!pool) {
1343                         radlog(L_ERR, "Internal sanity check failed in add_pool_to_realm");
1344                         return 0;
1345                 }
1346         }
1347
1348         if (pool->server_type != server_type) {
1349                 cf_log_err(cf_sectiontoitem(cs), "Incompatible home_server_pool \"%s\" (mixed auth_pool / acct_pool)", name);
1350                 return 0;
1351         }
1352
1353         *dest = pool;
1354
1355         return 1;
1356 }
1357 #endif
1358
1359
1360 static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
1361 {
1362         const char *name2;
1363         REALM *r = NULL;
1364         CONF_PAIR *cp;
1365 #ifdef WITH_PROXY
1366         home_pool_t *auth_pool, *acct_pool;
1367         const char *auth_pool_name, *acct_pool_name;
1368 #endif
1369
1370         name2 = cf_section_name1(cs);
1371         if (!name2 || (strcasecmp(name2, "realm") != 0)) {
1372                 cf_log_err(cf_sectiontoitem(cs), "Section is not a realm.");
1373                 return 0;
1374         }
1375
1376         name2 = cf_section_name2(cs);
1377         if (!name2) {
1378                 cf_log_err(cf_sectiontoitem(cs), "Realm section is missing the realm name.");
1379                 return 0;
1380         }
1381
1382 #ifdef WITH_PROXY
1383         auth_pool = acct_pool = NULL;
1384         auth_pool_name = acct_pool_name = NULL;
1385
1386         /*
1387          *      Prefer new configuration to old one.
1388          */
1389         cp = cf_pair_find(cs, "pool");
1390         if (!cp) cp = cf_pair_find(cs, "home_server_pool");
1391         if (cp) auth_pool_name = cf_pair_value(cp);
1392         if (cp && auth_pool_name) {
1393                 acct_pool_name = auth_pool_name;
1394                 if (!add_pool_to_realm(rc, cs,
1395                                        auth_pool_name, &auth_pool,
1396                                        HOME_TYPE_AUTH, 1)) {
1397                         return 0;
1398                 }
1399                 if (!add_pool_to_realm(rc, cs,
1400                                        auth_pool_name, &acct_pool,
1401                                        HOME_TYPE_ACCT, 0)) {
1402                         return 0;
1403                 }
1404         }
1405
1406         cp = cf_pair_find(cs, "auth_pool");
1407         if (cp) auth_pool_name = cf_pair_value(cp);
1408         if (cp && auth_pool_name) {
1409                 if (auth_pool) {
1410                         cf_log_err(cf_sectiontoitem(cs), "Cannot use \"pool\" and \"auth_pool\" at the same time.");
1411                         return 0;
1412                 }
1413                 if (!add_pool_to_realm(rc, cs,
1414                                        auth_pool_name, &auth_pool,
1415                                        HOME_TYPE_AUTH, 1)) {
1416                         return 0;
1417                 }
1418         }
1419
1420         cp = cf_pair_find(cs, "acct_pool");
1421         if (cp) acct_pool_name = cf_pair_value(cp);
1422         if (cp && acct_pool_name) {
1423                 int do_print = TRUE;
1424
1425                 if (acct_pool) {
1426                         cf_log_err(cf_sectiontoitem(cs), "Cannot use \"pool\" and \"acct_pool\" at the same time.");
1427                         return 0;
1428                 }
1429
1430                 if (!auth_pool ||
1431                     (strcmp(auth_pool_name, acct_pool_name) != 0)) {
1432                         do_print = TRUE;
1433                 }
1434
1435                 if (!add_pool_to_realm(rc, cs,
1436                                        acct_pool_name, &acct_pool,
1437                                        HOME_TYPE_ACCT, do_print)) {
1438                         return 0;
1439                 }
1440         }
1441 #endif
1442
1443         cf_log_info(cs, " realm %s {", name2);
1444
1445 #ifdef WITH_PROXY
1446         /*
1447          *      The realm MAY already exist if it's an old-style realm.
1448          *      In that case, merge the old-style realm with this one.
1449          */
1450         r = realm_find2(name2);
1451         if (r && (strcmp(r->name, name2) == 0)) {
1452                 if (cf_pair_find(cs, "auth_pool") ||
1453                     cf_pair_find(cs, "acct_pool")) {
1454                         cf_log_err(cf_sectiontoitem(cs), "Duplicate realm \"%s\"", name2);
1455                         goto error;
1456                 }
1457
1458                 if (!old_realm_config(rc, cs, r)) {
1459                         goto error;
1460                 }
1461
1462                 cf_log_info(cs, " } # realm %s", name2);
1463                 return 1;
1464         }
1465 #endif
1466
1467 #ifdef HAVE_REGEX_H
1468         if (name2[0] == '~') {
1469                 regex_t reg;
1470                 
1471                 /*
1472                  *      Include substring matches.
1473                  */
1474                 if (regcomp(&reg, name2 + 1,
1475                             REG_EXTENDED | REG_NOSUB | REG_ICASE) != 0) {
1476                         cf_log_err(cf_sectiontoitem(cs),
1477                                    "Invalid regex in realm \"%s\"", name2);
1478                         goto error;
1479                 }
1480                 regfree(&reg);
1481         }
1482 #endif
1483
1484         r = rad_malloc(sizeof(*r));
1485         memset(r, 0, sizeof(*r));
1486
1487         r->name = name2;
1488         r->striprealm = 1;
1489 #ifdef WITH_PROXY
1490         r->auth_pool = auth_pool;
1491         r->acct_pool = acct_pool;
1492         
1493         if (auth_pool_name &&
1494             (auth_pool_name == acct_pool_name)) { /* yes, ptr comparison */
1495                 cf_log_info(cs, "\tpool = %s", auth_pool_name);
1496         } else {
1497                 if (auth_pool_name) cf_log_info(cs, "\tauth_pool = %s", auth_pool_name);
1498                 if (acct_pool_name) cf_log_info(cs, "\tacct_pool = %s", acct_pool_name);
1499         }
1500 #endif
1501
1502         cp = cf_pair_find(cs, "nostrip");
1503         if (cp && (cf_pair_value(cp) == NULL)) {
1504                 r->striprealm = 0;
1505                 cf_log_info(cs, "\tnostrip");
1506         }
1507
1508         /*
1509          *      We're a new-style realm.  Complain if we see the old
1510          *      directives.
1511          */
1512         if (r->auth_pool || r->acct_pool) {
1513                 if (((cp = cf_pair_find(cs, "authhost")) != NULL) ||
1514                     ((cp = cf_pair_find(cs, "accthost")) != NULL) ||
1515                     ((cp = cf_pair_find(cs, "secret")) != NULL) ||
1516                     ((cp = cf_pair_find(cs, "ldflag")) != NULL)) {
1517                         DEBUG2("WARNING: Ignoring old-style configuration entry \"%s\" in realm \"%s\"", cf_pair_attr(cp), r->name);
1518                 }
1519
1520
1521                 /*
1522                  *      The realm MAY be an old-style realm, as there
1523                  *      was no auth_pool or acct_pool.  Double-check
1524                  *      it, just to be safe.
1525                  */
1526         } else if (!old_realm_config(rc, cs, r)) {
1527                 goto error;
1528         }
1529
1530 #ifdef HAVE_REGEX_H
1531         /*
1532          *      It's a regex.  Add it to a separate list.
1533          */
1534         if (name2[0] == '~') {
1535                 realm_regex_t *rr, **last;
1536
1537                 rr = rad_malloc(sizeof(*rr));
1538                 
1539                 last = &realms_regex;
1540                 while (*last) last = &((*last)->next);  /* O(N^2)... sue me. */
1541
1542                 r->name = name2;
1543                 rr->realm = r;
1544                 rr->next = NULL;
1545
1546                 *last = rr;
1547
1548                 cf_log_info(cs, " }");
1549                 return 1;
1550         }
1551 #endif
1552
1553         if (!rbtree_insert(realms_byname, r)) {
1554                 rad_assert("Internal sanity check failed");
1555                 goto error;
1556         }
1557
1558         cf_log_info(cs, " }");
1559
1560         return 1;
1561
1562  error:
1563         cf_log_info(cs, " } # realm %s", name2);
1564         free(r);
1565         return 0;
1566 }
1567
1568 #ifdef WITH_COA
1569 static const FR_NAME_NUMBER home_server_types[] = {
1570         { "auth", HOME_TYPE_AUTH },
1571         { "auth+acct", HOME_TYPE_AUTH },
1572         { "acct", HOME_TYPE_ACCT },
1573         { "coa", HOME_TYPE_COA },
1574         { NULL, 0 }
1575 };
1576
1577 static int pool_peek_type(CONF_SECTION *config, CONF_SECTION *cs)
1578 {
1579         int home;
1580         const char *name, *type;
1581         CONF_PAIR *cp;
1582         CONF_SECTION *server_cs;
1583
1584         cp = cf_pair_find(cs, "home_server");
1585         if (!cp) {
1586                 cf_log_err(cf_sectiontoitem(cs), "Pool does not contain a \"home_server\" entry");
1587                 return HOME_TYPE_INVALID;
1588         }
1589
1590         name = cf_pair_value(cp);
1591         if (!name) {
1592                 cf_log_err(cf_pairtoitem(cp), "home_server entry does not reference a home server");
1593                 return HOME_TYPE_INVALID;
1594         }
1595
1596         server_cs = cf_section_sub_find_name2(config, "home_server", name);
1597         if (!server_cs) {
1598                 cf_log_err(cf_pairtoitem(cp), "home_server \"%s\" does not exist", name);
1599                 return HOME_TYPE_INVALID;
1600         }
1601
1602         cp = cf_pair_find(server_cs, "type");
1603         if (!cp) {
1604                 cf_log_err(cf_sectiontoitem(server_cs), "home_server %s does not contain a \"type\" entry", name);
1605                 return HOME_TYPE_INVALID;
1606         }
1607
1608         type = cf_pair_value(cp);
1609         if (!type) {
1610                 cf_log_err(cf_sectiontoitem(server_cs), "home_server %s contains an empty \"type\" entry", name);
1611                 return HOME_TYPE_INVALID;
1612         }
1613
1614         home = fr_str2int(home_server_types, type, HOME_TYPE_INVALID);
1615         if (home == HOME_TYPE_INVALID) {
1616                 cf_log_err(cf_sectiontoitem(server_cs), "home_server %s contains an invalid \"type\" entry of value \"%s\"", name, type);
1617                 return HOME_TYPE_INVALID;
1618         }
1619
1620         return home;            /* 'cause we miss it so much */
1621 }
1622 #endif
1623
1624 int realms_init(CONF_SECTION *config)
1625 {
1626         CONF_SECTION *cs;
1627         realm_config_t *rc, *old_rc;
1628
1629         if (realms_byname) return 1;
1630
1631         realms_byname = rbtree_create(realm_name_cmp, free, 0);
1632         if (!realms_byname) {
1633                 realms_free();
1634                 return 0;
1635         }
1636
1637 #ifdef WITH_PROXY
1638         home_servers_byaddr = rbtree_create(home_server_addr_cmp, free, 0);
1639         if (!home_servers_byaddr) {
1640                 realms_free();
1641                 return 0;
1642         }
1643
1644         home_servers_byname = rbtree_create(home_server_name_cmp, NULL, 0);
1645         if (!home_servers_byname) {
1646                 realms_free();
1647                 return 0;
1648         }
1649
1650 #ifdef WITH_STATS
1651         home_servers_bynumber = rbtree_create(home_server_number_cmp, NULL, 0);
1652         if (!home_servers_bynumber) {
1653                 realms_free();
1654                 return 0;
1655         }
1656 #endif
1657
1658         home_pools_byname = rbtree_create(home_pool_name_cmp, NULL, 0);
1659         if (!home_pools_byname) {
1660                 realms_free();
1661                 return 0;
1662         }
1663 #endif
1664
1665         rc = rad_malloc(sizeof(*rc));
1666         memset(rc, 0, sizeof(*rc));
1667         rc->cs = config;
1668
1669 #ifdef WITH_PROXY
1670         cs = cf_subsection_find_next(config, NULL, "proxy");
1671         if (cs) {
1672                 cf_section_parse(cs, rc, proxy_config);
1673         } else {
1674                 rc->dead_time = DEAD_TIME;
1675                 rc->retry_count = RETRY_COUNT;
1676                 rc->retry_delay = RETRY_DELAY;
1677                 rc->fallback = 0;
1678                 rc->wake_all_if_all_dead= 0;
1679         }
1680 #endif
1681
1682         for (cs = cf_subsection_find_next(config, NULL, "realm");
1683              cs != NULL;
1684              cs = cf_subsection_find_next(config, cs, "realm")) {
1685                 if (!realm_add(rc, cs)) {
1686                         free(rc);
1687                         realms_free();
1688                         return 0;
1689                 }
1690         }
1691
1692 #ifdef WITH_COA
1693         /*
1694          *      CoA pools aren't tied to realms.
1695          */
1696         for (cs = cf_subsection_find_next(config, NULL, "home_server_pool");
1697              cs != NULL;
1698              cs = cf_subsection_find_next(config, cs, "home_server_pool")) {
1699                 int type;
1700
1701                 /*
1702                  *      Pool was already loaded.
1703                  */
1704                 if (cf_data_find(cs, "home_server_pool")) continue;
1705
1706                 type = pool_peek_type(config, cs);
1707                 if (type == HOME_TYPE_INVALID) return 0;
1708
1709                 if (!server_pool_add(rc, cs, type, TRUE)) {
1710                         return 0;
1711                 }
1712         }
1713 #endif
1714
1715
1716 #ifdef WITH_PROXY
1717         xlat_register("home_server", xlat_home_server, NULL);
1718         xlat_register("home_server_pool", xlat_server_pool, NULL);
1719 #endif
1720
1721         /*
1722          *      Swap pointers atomically.
1723          */
1724         old_rc = realm_config;
1725         realm_config = rc;
1726         free(old_rc);
1727
1728         return 1;
1729 }
1730
1731 /*
1732  *      Find a realm where "name" might be the regex.
1733  */
1734 REALM *realm_find2(const char *name)
1735 {
1736         REALM myrealm;
1737         REALM *realm;
1738         
1739         if (!name) name = "NULL";
1740
1741         myrealm.name = name;
1742         realm = rbtree_finddata(realms_byname, &myrealm);
1743         if (realm) return realm;
1744
1745 #ifdef HAVE_REGEX_H
1746         if (realms_regex) {
1747                 realm_regex_t *this;
1748
1749                 for (this = realms_regex; this != NULL; this = this->next) {
1750                         if (strcmp(this->realm->name, name) == 0) {
1751                                 return this->realm;
1752                         }
1753                 }
1754         }
1755 #endif
1756
1757         /*
1758          *      Couldn't find a realm.  Look for DEFAULT.
1759          */
1760         myrealm.name = "DEFAULT";
1761         return rbtree_finddata(realms_byname, &myrealm);
1762 }
1763
1764
1765 /*
1766  *      Find a realm in the REALM list.
1767  */
1768 REALM *realm_find(const char *name)
1769 {
1770         REALM myrealm;
1771         REALM *realm;
1772         
1773         if (!name) name = "NULL";
1774
1775         myrealm.name = name;
1776         realm = rbtree_finddata(realms_byname, &myrealm);
1777         if (realm) return realm;
1778
1779 #ifdef HAVE_REGEX_H
1780         if (realms_regex) {
1781                 realm_regex_t *this;
1782
1783                 for (this = realms_regex; this != NULL; this = this->next) {
1784                         int compare;
1785                         regex_t reg;
1786
1787                         /*
1788                          *      Include substring matches.
1789                          */
1790                         if (regcomp(&reg, this->realm->name + 1,
1791                                     REG_EXTENDED | REG_NOSUB | REG_ICASE) != 0) {
1792                                 continue;
1793                         }
1794
1795                         compare = regexec(&reg, name, 0, NULL, 0);
1796                         regfree(&reg);
1797
1798                         if (compare == 0) return this->realm;
1799                 }
1800         }
1801 #endif
1802
1803         /*
1804          *      Couldn't find a realm.  Look for DEFAULT.
1805          */
1806         myrealm.name = "DEFAULT";
1807         return rbtree_finddata(realms_byname, &myrealm);
1808 }
1809
1810
1811 #ifdef WITH_PROXY
1812 home_server *home_server_ldb(const char *realmname,
1813                              home_pool_t *pool, REQUEST *request)
1814 {
1815         int             start;
1816         int             count;
1817         home_server     *found = NULL;
1818         home_server     *zombie = NULL;
1819         VALUE_PAIR      *vp;
1820
1821         /*
1822          *      Determine how to pick choose the home server.
1823          */
1824         switch (pool->type) {
1825                 uint32_t hash;
1826
1827                 /*
1828                  *      For load-balancing by client IP address, we
1829                  *      pick a home server by hashing the client IP.
1830                  *
1831                  *      This isn't as even a load distribution as
1832                  *      tracking the State attribute, but it's better
1833                  *      than nothing.
1834                  */
1835         case HOME_POOL_CLIENT_BALANCE:
1836                 switch (request->packet->src_ipaddr.af) {
1837                 case AF_INET:
1838                         hash = fr_hash(&request->packet->src_ipaddr.ipaddr.ip4addr,
1839                                          sizeof(request->packet->src_ipaddr.ipaddr.ip4addr));
1840                         break;
1841                 case AF_INET6:
1842                         hash = fr_hash(&request->packet->src_ipaddr.ipaddr.ip6addr,
1843                                          sizeof(request->packet->src_ipaddr.ipaddr.ip6addr));
1844                         break;
1845                 default:
1846                         hash = 0;
1847                         break;
1848                 }
1849                 start = hash % pool->num_home_servers;
1850                 break;
1851
1852         case HOME_POOL_CLIENT_PORT_BALANCE:
1853                 switch (request->packet->src_ipaddr.af) {
1854                 case AF_INET:
1855                         hash = fr_hash(&request->packet->src_ipaddr.ipaddr.ip4addr,
1856                                          sizeof(request->packet->src_ipaddr.ipaddr.ip4addr));
1857                         break;
1858                 case AF_INET6:
1859                         hash = fr_hash(&request->packet->src_ipaddr.ipaddr.ip6addr,
1860                                          sizeof(request->packet->src_ipaddr.ipaddr.ip6addr));
1861                         break;
1862                 default:
1863                         hash = 0;
1864                         break;
1865                 }
1866                 fr_hash_update(&request->packet->src_port,
1867                                  sizeof(request->packet->src_port), hash);
1868                 start = hash % pool->num_home_servers;
1869                 break;
1870
1871         case HOME_POOL_KEYED_BALANCE:
1872                 if ((vp = pairfind(request->config_items, PW_LOAD_BALANCE_KEY)) != NULL) {
1873                         hash = fr_hash(vp->vp_strvalue, vp->length);
1874                         start = hash % pool->num_home_servers;
1875                         break;
1876                 }
1877                 /* FALL-THROUGH */
1878                                 
1879         case HOME_POOL_LOAD_BALANCE:
1880         case HOME_POOL_FAIL_OVER:
1881                 start = 0;
1882                 break;
1883
1884         default:                /* this shouldn't happen... */
1885                 start = 0;
1886                 break;
1887
1888         }
1889
1890         /*
1891          *      Starting with the home server we chose, loop through
1892          *      all home servers.  If the current one is dead, skip
1893          *      it.  If it is too busy, skip it.
1894          *
1895          *      Otherwise, use it.
1896          */
1897         for (count = 0; count < pool->num_home_servers; count++) {
1898                 home_server *home = pool->servers[(start + count) % pool->num_home_servers];
1899
1900                 if (!home) continue;
1901
1902                 /*
1903                  *      Skip dead home servers.
1904                  */
1905                 if (home->state == HOME_STATE_IS_DEAD) {
1906                         continue;
1907                 }
1908
1909                 /*
1910                  *      This home server is too busy.  Choose another one.
1911                  */
1912                 if (home->currently_outstanding >= home->max_outstanding) {
1913                         continue;
1914                 }
1915
1916 #ifdef WITH_DETAIL
1917                 /*
1918                  *      We read the packet from a detail file, AND it
1919                  *      came from this server.  Don't re-proxy it
1920                  *      there.
1921                  */
1922                 if ((request->listener->type == RAD_LISTEN_DETAIL) &&
1923                     (request->packet->code == PW_ACCOUNTING_REQUEST) &&
1924                     (fr_ipaddr_cmp(&home->ipaddr, &request->packet->src_ipaddr) == 0)) {
1925                         continue;
1926                 }
1927 #endif
1928
1929                 /*
1930                  *      It's zombie, so we remember the first zombie
1931                  *      we find, but we don't mark it as a "live"
1932                  *      server.
1933                  */
1934                 if (home->state == HOME_STATE_ZOMBIE) {
1935                         if (!zombie) zombie = home;
1936                         continue;
1937                 }
1938
1939                 /*
1940                  *      We've found the first "live" one.  Use that.
1941                  */
1942                 if (pool->type == HOME_POOL_FAIL_OVER) {
1943                         found = home;
1944                         break;
1945                 }
1946
1947                 /*
1948                  *      Otherwise we're doing some kind of load balancing.
1949                  *      If we haven't found one yet, pick this one.
1950                  */
1951                 if (!found) {
1952                         found = home;
1953                         continue;
1954                 }
1955
1956                 RDEBUG3("PROXY %s %d\t%s %d",
1957                        found->name, found->currently_outstanding,
1958                        home->name, home->currently_outstanding);
1959
1960                 /*
1961                  *      Prefer this server if it's less busy than the
1962                  *      one we had previously found.
1963                  */
1964                 if (home->currently_outstanding < found->currently_outstanding) {
1965                         RDEBUG3("PROXY Choosing %s: It's less busy than %s",
1966                                home->name, found->name);
1967                         found = home;
1968                         continue;
1969                 }
1970
1971                 /*
1972                  *      Ignore servers which are busier than the one
1973                  *      we found.
1974                  */
1975                 if (home->currently_outstanding > found->currently_outstanding) {
1976                         RDEBUG3("PROXY Skipping %s: It's busier than %s",
1977                                home->name, found->name);
1978                         continue;
1979                 }
1980
1981                 /*
1982                  *      From the list of servers which have the same
1983                  *      load, choose one at random.
1984                  */
1985                 if (((count + 1) * (fr_rand() & 0xffff)) < (uint32_t) 0x10000) {
1986                         found = home;
1987                 }
1988         } /* loop over the home servers */
1989
1990         /*
1991          *      We have no live servers, BUT we have a zombie.  Use
1992          *      the zombie as a last resort.
1993          */
1994         if (!found && zombie) {
1995                 found = zombie;
1996                 zombie = NULL;
1997         }
1998
1999         /*
2000          *      There's a fallback if they're all dead.
2001          */
2002         if (!found && pool->fallback) {
2003                 found = pool->fallback;
2004         }
2005
2006         if (found) {
2007         update_and_return:
2008                 /*
2009                  *      Allocate the proxy packet, only if it wasn't
2010                  *      already allocated by a module.  This check is
2011                  *      mainly to support the proxying of EAP-TTLS and
2012                  *      EAP-PEAP tunneled requests.
2013                  *
2014                  *      In those cases, the EAP module creates a
2015                  *      "fake" request, and recursively passes it
2016                  *      through the authentication stage of the
2017                  *      server.  The module then checks if the request
2018                  *      was supposed to be proxied, and if so, creates
2019                  *      a proxy packet from the TUNNELED request, and
2020                  *      not from the EAP request outside of the
2021                  *      tunnel.
2022                  *
2023                  *      The proxy then works like normal, except that
2024                  *      the response packet is "eaten" by the EAP
2025                  *      module, and encapsulated into an EAP packet.
2026                  */
2027                 if (!request->proxy) {
2028                         if ((request->proxy = rad_alloc(TRUE)) == NULL) {
2029                                 radlog(L_ERR|L_CONS, "no memory");
2030                                 exit(1);
2031                         }
2032                         
2033                         /*
2034                          *      Copy the request, then look up name
2035                          *      and plain-text password in the copy.
2036                          *
2037                          *      Note that the User-Name attribute is
2038                          *      the *original* as sent over by the
2039                          *      client.  The Stripped-User-Name
2040                          *      attribute is the one hacked through
2041                          *      the 'hints' file.
2042                          */
2043                         request->proxy->vps =  paircopy(request->packet->vps);
2044
2045                         /*
2046                          *      Set the source IP address for proxying.
2047                          */
2048                         request->proxy->src_ipaddr = found->src_ipaddr;
2049                 }
2050
2051                 /*
2052                  *      Update the various fields as appropriate.
2053                  */
2054                 request->proxy->dst_ipaddr = found->ipaddr;
2055                 request->proxy->dst_port = found->port;
2056                 request->home_server = found;
2057
2058                 /*
2059                  *      We're supposed to add a Message-Authenticator
2060                  *      if it doesn't exist, and it doesn't exist.
2061                  */
2062                 if (found->message_authenticator &&
2063                     (request->packet->code == PW_AUTHENTICATION_REQUEST) &&
2064                     !pairfind(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR)) {
2065                         radius_pairmake(request, &request->proxy->vps,
2066                                         "Message-Authenticator", "0x00",
2067                                         T_OP_SET);
2068                 }
2069
2070                 return found;
2071         }
2072
2073         /*
2074          *      No live match found, and no fallback to the "DEFAULT"
2075          *      realm.  We fix this by blindly marking all servers as
2076          *      "live".  But only do it for ones that don't support
2077          *      "pings", as they will be marked live when they
2078          *      actually are live.
2079          */
2080         if (!realm_config->fallback &&
2081             realm_config->wake_all_if_all_dead) {
2082                 for (count = 0; count < pool->num_home_servers; count++) {
2083                         home_server *home = pool->servers[count];
2084
2085                         if (!home) continue;
2086
2087                         if ((home->state == HOME_STATE_IS_DEAD) &&
2088                             (home->ping_check == HOME_PING_CHECK_NONE)) {
2089                                 home->state = HOME_STATE_ALIVE;
2090                                 if (!found) found = home;
2091                         }
2092                 }
2093
2094                 if (found) goto update_and_return;
2095         }
2096
2097         /*
2098          *      Still nothing.  Look up the DEFAULT realm, but only
2099          *      if we weren't looking up the NULL or DEFAULT realms.
2100          */
2101         if (realm_config->fallback &&
2102             realmname &&
2103             (strcmp(realmname, "NULL") != 0) &&
2104             (strcmp(realmname, "DEFAULT") != 0)) {
2105                 REALM *rd = realm_find("DEFAULT");
2106
2107                 if (!rd) return NULL;
2108
2109                 pool = NULL;
2110                 if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
2111                         pool = rd->auth_pool;
2112
2113                 } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
2114                         pool = rd->acct_pool;
2115                 }
2116                 if (!pool) return NULL;
2117
2118                 RDEBUG2("PROXY - realm %s has no live home servers.  Falling back to the DEFAULT realm.", realmname);
2119                 return home_server_ldb(rd->name, pool, request);
2120         }
2121
2122         /*
2123          *      Still haven't found anything.  Oh well.
2124          */
2125         return NULL;
2126 }
2127
2128
2129 home_server *home_server_find(fr_ipaddr_t *ipaddr, int port)
2130 {
2131         home_server myhome;
2132
2133         memset(&myhome, 0, sizeof(myhome));
2134         myhome.ipaddr = *ipaddr;
2135         myhome.port = port;
2136         myhome.server = NULL;   /* we're not called for internal proxying */
2137
2138         return rbtree_finddata(home_servers_byaddr, &myhome);
2139 }
2140
2141 #ifdef WITH_COA
2142 home_server *home_server_byname(const char *name, int type)
2143 {
2144         home_server myhome;
2145
2146         memset(&myhome, 0, sizeof(myhome));
2147         myhome.type = type;
2148         myhome.name = name;
2149
2150         return rbtree_finddata(home_servers_byname, &myhome);
2151 }
2152 #endif
2153
2154 #ifdef WITH_STATS
2155 home_server *home_server_bynumber(int number)
2156 {
2157         home_server myhome;
2158
2159         memset(&myhome, 0, sizeof(myhome));
2160         myhome.number = number;
2161         myhome.server = NULL;   /* we're not called for internal proxying */
2162
2163         return rbtree_finddata(home_servers_bynumber, &myhome);
2164 }
2165 #endif
2166
2167 home_pool_t *home_pool_byname(const char *name, int type)
2168 {
2169         home_pool_t mypool;
2170         
2171         memset(&mypool, 0, sizeof(mypool));
2172         mypool.name = name;
2173         mypool.server_type = type;
2174         return rbtree_finddata(home_pools_byname, &mypool);
2175 }
2176
2177 #endif
2178
2179 #ifdef WITH_PROXY
2180 static int home_server_create_callback(void *ctx, void *data)
2181 {
2182         rad_listen_t *head = ctx;
2183         home_server *home = data;
2184         rad_listen_t *this;
2185
2186         /*
2187          *      If there WAS a src address defined, ensure that a
2188          *      proxy listener has been defined.
2189          */
2190         if (home->src_ipaddr.af != AF_UNSPEC) {
2191                 this = proxy_new_listener(&home->src_ipaddr, TRUE);
2192
2193                 /*
2194                  *      Failed to create it: Die
2195                  */
2196                 if (!this) return 1;
2197
2198                 this->next = head->next;
2199                 head->next = this;
2200         }
2201
2202         return 0;
2203 }
2204
2205 /*
2206  *      Taking a void* here solves some header issues.
2207  */
2208 int home_server_create_listeners(void *ctx)
2209 {
2210         rad_listen_t *head = ctx;
2211
2212         if (!home_servers_byaddr) return 0;
2213
2214         rad_assert(head != NULL);
2215
2216         /*
2217          *      Add the listeners to the TAIL of the list.
2218          */
2219         while (head->next) head = head->next;
2220
2221         if (rbtree_walk(home_servers_byaddr, InOrder,
2222                         home_server_create_callback, head) != 0) {
2223                 return -1;
2224         }
2225
2226         return 0;
2227 }
2228 #endif