Merge branch 'tr-integ' into kevin-chbind
[freeradius.git] / src / main / realms.c
index d5286ff..0dd0164 100644 (file)
@@ -32,6 +32,9 @@ RCSID("$Id$")
 #include <ctype.h>
 #include <fcntl.h>
 
+#ifdef HAVE_PCREPOSIX_H
+#include <pcreposix.h>
+#else
 #ifdef HAVE_REGEX_H
 #include <regex.h>
 
@@ -52,6 +55,7 @@ RCSID("$Id$")
 #define REG_ICASE (0)
 #endif
 #endif
+#endif
 
 static rbtree_t *realms_byname = NULL;
 
@@ -128,11 +132,6 @@ static void home_server_free(void *data)
 {
        home_server *home = data;
 
-#ifdef WITH_TLS
-       tls_server_conf_free(home->tls);
-       home->tls = NULL;
-#endif
-
        free(home);
 }
 
@@ -197,7 +196,7 @@ static int home_pool_name_cmp(const void *one, const void *two)
 }
 
 
-static size_t xlat_cs(CONF_SECTION *cs, char *fmt, char *out, size_t outlen)
+static size_t xlat_cs(CONF_SECTION *cs, const char *fmt, char *out, size_t outlen)
 
 {
        const char *value = NULL;
@@ -231,8 +230,7 @@ static size_t xlat_cs(CONF_SECTION *cs, char *fmt, char *out, size_t outlen)
  *     Xlat for %{home_server:foo}
  */
 static size_t xlat_home_server(UNUSED void *instance, REQUEST *request,
-                              char *fmt, char *out, size_t outlen,
-                              UNUSED RADIUS_ESCAPE_STRING func)
+                              const char *fmt, char *out, size_t outlen)
 {
        if (!fmt || !out || (outlen < 1)) return 0;
 
@@ -249,8 +247,7 @@ static size_t xlat_home_server(UNUSED void *instance, REQUEST *request,
  *     Xlat for %{home_server_pool:foo}
  */
 static size_t xlat_server_pool(UNUSED void *instance, REQUEST *request,
-                              char *fmt, char *out, size_t outlen,
-                              UNUSED RADIUS_ESCAPE_STRING func)
+                              const char *fmt, char *out, size_t outlen)
 {
        if (!fmt || !out || (outlen < 1)) return 0;
 
@@ -305,16 +302,16 @@ void realms_free(void)
 #ifdef WITH_PROXY
 static CONF_PARSER limit_config[] = {
        { "max_connections", PW_TYPE_INTEGER,
-         offsetof(home_server, max_connections), NULL,   "16" },
+         offsetof(home_server, limit.max_connections), NULL,   "16" },
 
        { "max_requests", PW_TYPE_INTEGER,
-         offsetof(home_server,max_requests), NULL,   "0" },
+         offsetof(home_server, limit.max_requests), NULL,   "0" },
 
        { "lifetime", PW_TYPE_INTEGER,
-         offsetof(home_server,lifetime), NULL,   "0" },
+         offsetof(home_server, limit.lifetime), NULL,   "0" },
 
        { "idle_timeout", PW_TYPE_INTEGER,
-         offsetof(home_server,idle_timeout), NULL,   "0" },
+         offsetof(home_server, limit.idle_timeout), NULL,   "0" },
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
@@ -371,12 +368,8 @@ static CONF_PARSER home_server_config[] = {
 
        { "response_window", PW_TYPE_INTEGER,
          offsetof(home_server,response_window), NULL,   "30" },
-       { "no_response_fail", PW_TYPE_BOOLEAN,
-         offsetof(home_server,no_response_fail), NULL,   NULL },
        { "max_outstanding", PW_TYPE_INTEGER,
          offsetof(home_server,max_outstanding), NULL,   "65536" },
-       { "require_message_authenticator",  PW_TYPE_BOOLEAN,
-         offsetof(home_server, message_authenticator), 0, NULL },
 
        { "zombie_period", PW_TYPE_INTEGER,
          offsetof(home_server,zombie_period), NULL,   "40" },
@@ -391,8 +384,6 @@ static CONF_PARSER home_server_config[] = {
          offsetof(home_server,ping_interval), NULL,   "30" },
        { "num_answers_to_alive", PW_TYPE_INTEGER,
          offsetof(home_server,num_pings_to_alive), NULL,   "3" },
-       { "num_pings_to_alive", PW_TYPE_INTEGER,
-         offsetof(home_server,num_pings_to_alive), NULL,   "3" },
        { "revive_interval", PW_TYPE_INTEGER,
          offsetof(home_server,revive_interval), NULL,   "300" },
        { "status_check_timeout", PW_TYPE_INTEGER,
@@ -422,6 +413,179 @@ static void null_free(UNUSED void *data)
 {
 }
 
+
+int realms_home_server_add(home_server *home, CONF_SECTION *cs, int dual)
+{
+        CONF_SECTION *parent = NULL;
+       const char * name2 = home->name;
+       
+       /*
+        *      Make sure that this is set.
+        */
+       if (home->src_ipaddr.af == AF_UNSPEC) {
+               home->src_ipaddr.af = home->ipaddr.af;
+       }
+
+       hs_srcipaddr = NULL;
+
+       if (rbtree_finddata(home_servers_byname, home) != NULL) {
+               cf_log_err(cf_sectiontoitem(cs),
+                          "Duplicate home server name %s.", name2);
+               goto error;
+       }
+
+       if (!home->server &&
+           (rbtree_finddata(home_servers_byaddr, home) != NULL)) {
+               cf_log_err(cf_sectiontoitem(cs),
+                          "Duplicate home server IP %s.", name2);
+               goto error;
+       }
+
+       if (!rbtree_insert(home_servers_byname, home)) {
+               cf_log_err(cf_sectiontoitem(cs),
+                          "Internal error %d adding home server %s.",
+                          __LINE__, name2);
+               goto error;
+       }
+
+       if (!home->server &&
+           !rbtree_insert(home_servers_byaddr, home)) {
+               rbtree_deletebydata(home_servers_byname, home);
+               cf_log_err(cf_sectiontoitem(cs),
+                          "Internal error %d adding home server %s.",
+                          __LINE__, name2);
+               goto error;
+       }
+
+#ifdef WITH_STATS
+       home->number = home_server_max_number++;
+       if (!rbtree_insert(home_servers_bynumber, home)) {
+               rbtree_deletebydata(home_servers_byname, home);
+               if (home->ipaddr.af != AF_UNSPEC) {
+                       rbtree_deletebydata(home_servers_byname, home);
+               }
+               cf_log_err(cf_sectiontoitem(cs),
+                          "Internal error %d adding home server %s.",
+                          __LINE__, name2);
+               goto error;
+       }
+#endif
+
+       if (home->max_outstanding < 8) home->max_outstanding = 8;
+       if (home->max_outstanding > 65536*16) home->max_outstanding = 65536*16;
+
+       if (home->ping_interval < 6) home->ping_interval = 6;
+       if (home->ping_interval > 120) home->ping_interval = 120;
+
+       if (home->response_window < 1) home->response_window = 1;
+       if (home->response_window > 60) home->response_window = 60;
+       if (home->response_window > mainconfig.max_request_time) home->response_window = mainconfig.max_request_time;
+
+       if (home->zombie_period < 1) home->zombie_period = 1;
+       if (home->zombie_period > 120) home->zombie_period = 120;
+
+       if (home->zombie_period < home->response_window) {
+               home->zombie_period = home->response_window;
+       }
+
+       if (home->num_pings_to_alive < 3) home->num_pings_to_alive = 3;
+       if (home->num_pings_to_alive > 10) home->num_pings_to_alive = 10;
+
+       if (home->ping_timeout < 3) home->ping_timeout = 3;
+       if (home->ping_timeout > 10) home->ping_timeout = 10;
+
+       if (home->revive_interval < 60) home->revive_interval = 60;
+       if (home->revive_interval > 3600) home->revive_interval = 3600;
+
+#ifdef WITH_COA
+       if (home->coa_irt < 1) home->coa_irt = 1;
+       if (home->coa_irt > 5) home->coa_irt = 5;
+
+       if (home->coa_mrc < 0) home->coa_mrc = 0;
+       if (home->coa_mrc > 20 ) home->coa_mrc = 20;
+
+       if (home->coa_mrt < 0) home->coa_mrt = 0;
+       if (home->coa_mrt > 30 ) home->coa_mrt = 30;
+
+       if (home->coa_mrd < 5) home->coa_mrd = 5;
+       if (home->coa_mrd > 60 ) home->coa_mrd = 60;
+#endif
+
+       if (home->limit.max_connections > 1024) home->limit.max_connections = 1024;
+
+#ifdef WITH_TCP
+       /*
+        *      UDP sockets can't be connection limited.
+        */
+       if (home->proto != IPPROTO_TCP) home->limit.max_connections = 0;
+#endif
+
+       if ((home->limit.idle_timeout > 0) && (home->limit.idle_timeout < 5))
+               home->limit.idle_timeout = 5;
+       if ((home->limit.lifetime > 0) && (home->limit.lifetime < 5))
+               home->limit.lifetime = 5;
+       if ((home->limit.lifetime > 0) && (home->limit.idle_timeout > home->limit.lifetime))
+               home->limit.idle_timeout = 0;
+
+       
+       if (cs) {
+               parent = cf_item_parent(cf_sectiontoitem(cs));
+               if (strcmp(cf_section_name1(parent), "server") == 0) {
+                       home->parent_server = cf_section_name2(parent);
+               }
+       }
+       
+       if (dual) {
+               home_server *home2 = rad_malloc(sizeof(*home2));
+
+               memcpy(home2, home, sizeof(*home2));
+
+               home2->type = HOME_TYPE_ACCT;
+               home2->port++;
+               home2->ping_user_password = NULL;
+               home2->cs = cs;
+               home2->parent_server = home->parent_server;
+
+               if (!rbtree_insert(home_servers_byname, home2)) {
+                       cf_log_err(cf_sectiontoitem(cs),
+                                  "Internal error %d adding home server %s.",
+                                  __LINE__, name2);
+                       free(home2);
+                       return 0;
+               }
+               
+               if (!home->server &&
+                   !rbtree_insert(home_servers_byaddr, home2)) {
+                       rbtree_deletebydata(home_servers_byname, home2);
+                       cf_log_err(cf_sectiontoitem(cs),
+                                  "Internal error %d adding home server %s.",
+                                  __LINE__, name2);
+                       free(home2);
+                       return 0;
+               }
+
+#ifdef WITH_STATS
+               home2->number = home_server_max_number++;
+               if (!rbtree_insert(home_servers_bynumber, home2)) {
+                       rbtree_deletebydata(home_servers_byname, home2);
+                       if (!home2->server) {
+                               rbtree_deletebydata(home_servers_byname, home2);
+                       }
+                       cf_log_err(cf_sectiontoitem(cs),
+                                  "Internal error %d adding home server %s.",
+                                  __LINE__, name2);
+                       free(home2);
+                       return 0;
+               }
+#endif
+       }
+
+       return 1;
+ error:
+       return 0;
+}
+
+
 static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
 {
        const char *name2;
@@ -430,7 +594,6 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
        CONF_PAIR *cp;
        CONF_SECTION *tls;
 
-       free(hs_virtual_server); /* used only for printing during parsing */
        hs_virtual_server = NULL;
 
        name2 = cf_section_name2(cs);
@@ -445,24 +608,11 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
 
        home->name = name2;
        home->cs = cs;
-
-        /*
-        *      For zombie period calculations.  We want to count
-        *      zombies from the time when the server starts, instead
-        *      of from 1970.
-        */
-       home->last_packet = time(NULL);
+       home->state = HOME_STATE_UNKNOWN;
 
        /*
-        *      Authentication servers have a default "no_response_fail = 0".
-        *      Accounting servers have a default "no_response_fail = 1".
-        *
-        *      This is because authentication packets are retried, so
-        *      they can fail over to another home server.  Accounting
-        *      packets are not retried, so they cannot fail over, and
-        *      instead should be rejected immediately.
+        *      Last packet sent / received are zero.
         */
-       home->no_response_fail = 2;
 
        memset(&hs_ip4addr, 0, sizeof(hs_ip4addr));
        memset(&hs_ip6addr, 0, sizeof(hs_ip6addr));
@@ -510,15 +660,11 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
                           "No ipaddr, ipv6addr, or virtual_server defined for home server \"%s\".",
                           name2);
        error:
-               free(home);
-               free(hs_type);
+               talloc_free(hs_type);
                hs_type = NULL;
-               free(hs_check);
                hs_check = NULL;
-               free(hs_srcipaddr);
                hs_srcipaddr = NULL;
 #ifdef WITH_TCP
-               free(hs_proto);
                hs_proto = NULL;
 #endif
                return 0;
@@ -549,15 +695,13 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
         *      Use a reasonable default.
         */
  skip_port:
-       if (!hs_type) hs_type = strdup("auth+acct");
+       if (!hs_type) hs_type = talloc_strdup(cs, "auth+acct");
 
        if (strcasecmp(hs_type, "auth") == 0) {
                home->type = HOME_TYPE_AUTH;
-               if (home->no_response_fail == 2) home->no_response_fail = 0;
 
        } else if (strcasecmp(hs_type, "acct") == 0) {
                home->type = HOME_TYPE_ACCT;
-               if (home->no_response_fail == 2) home->no_response_fail = 1;
 
        } else if (strcasecmp(hs_type, "auth+acct") == 0) {
                home->type = HOME_TYPE_AUTH;
@@ -581,7 +725,7 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
                           hs_type, name2);
                goto error;
        }
-       free(hs_type);
+       if (hs_type) talloc_free(hs_type);
        hs_type = NULL;
 
        if (!hs_check || (strcasecmp(hs_check, "none") == 0)) {
@@ -593,25 +737,37 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
        } else if (strcasecmp(hs_check, "request") == 0) {
                home->ping_check = HOME_PING_CHECK_REQUEST;
 
+               if (!home->ping_user_name ||
+                   !*home->ping_user_name) {
+                       cf_log_err(cf_sectiontoitem(cs), "You must supply a 'username' to enable status_check=request");
+                       goto error;
+               }
+
+               if ((home->type == HOME_TYPE_AUTH) &&
+                   (!home->ping_user_password ||
+                    !*home->ping_user_password)) {
+                       cf_log_err(cf_sectiontoitem(cs), "You must supply a password to enable status_check=request");
+                       goto error;
+               }
+
        } else {
                cf_log_err(cf_sectiontoitem(cs),
-                          "Invalid ping_check \"%s\" for home server %s.",
+                          "Invalid status__check \"%s\" for home server %s.",
                           hs_check, name2);
                goto error;
        }
-       free(hs_check);
        hs_check = NULL;
 
        if ((home->ping_check != HOME_PING_CHECK_NONE) &&
            (home->ping_check != HOME_PING_CHECK_STATUS_SERVER)) {
                if (!home->ping_user_name) {
-                       cf_log_err(cf_sectiontoitem(cs), "You must supply a user name to enable ping checks");
+                       cf_log_err(cf_sectiontoitem(cs), "You must supply a user name to enable status_check=request");
                        goto error;
                }
 
                if ((home->type == HOME_TYPE_AUTH) &&
                    !home->ping_user_password) {
-                       cf_log_err(cf_sectiontoitem(cs), "You must supply a password to enable ping checks");
+                       cf_log_err(cf_sectiontoitem(cs), "You must supply a password to enable status_check=request");
                        goto error;
                }
        }
@@ -620,11 +776,9 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
 #ifdef WITH_TCP
        if (hs_proto) {
                if (strcmp(hs_proto, "udp") == 0) {
-                       free(hs_proto);
                        hs_proto = NULL;
                        
                } else if (strcmp(hs_proto, "tcp") == 0) {
-                       free(hs_proto);
                        hs_proto = NULL;
                        home->proto = IPPROTO_TCP;
                        
@@ -706,167 +860,8 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
                goto error;
        }
 
-       /*
-        *      Make sure that this is set.
-        */
-       if (home->src_ipaddr.af == AF_UNSPEC) {
-               home->src_ipaddr.af = home->ipaddr.af;
-       }
-
-       free(hs_srcipaddr);
-       hs_srcipaddr = NULL;
-
-       if (rbtree_finddata(home_servers_byname, home) != NULL) {
-               cf_log_err(cf_sectiontoitem(cs),
-                          "Duplicate home server name %s.", name2);
-               goto error;
-       }
-
-       if (!home->server &&
-           (rbtree_finddata(home_servers_byaddr, home) != NULL)) {
-               cf_log_err(cf_sectiontoitem(cs),
-                          "Duplicate home server IP %s.", name2);
-               goto error;
-       }
-
-       if (!rbtree_insert(home_servers_byname, home)) {
-               cf_log_err(cf_sectiontoitem(cs),
-                          "Internal error %d adding home server %s.",
-                          __LINE__, name2);
-               goto error;
-       }
-
-       if (!home->server &&
-           !rbtree_insert(home_servers_byaddr, home)) {
-               rbtree_deletebydata(home_servers_byname, home);
-               cf_log_err(cf_sectiontoitem(cs),
-                          "Internal error %d adding home server %s.",
-                          __LINE__, name2);
+       if ( !realms_home_server_add( home, cs, dual))
                goto error;
-       }
-
-#ifdef WITH_STATS
-       home->number = home_server_max_number++;
-       if (!rbtree_insert(home_servers_bynumber, home)) {
-               rbtree_deletebydata(home_servers_byname, home);
-               if (home->ipaddr.af != AF_UNSPEC) {
-                       rbtree_deletebydata(home_servers_byname, home);
-               }
-               cf_log_err(cf_sectiontoitem(cs),
-                          "Internal error %d adding home server %s.",
-                          __LINE__, name2);
-               goto error;
-       }
-#endif
-
-       if (home->max_outstanding < 8) home->max_outstanding = 8;
-       if (home->max_outstanding > 65536*16) home->max_outstanding = 65536*16;
-
-       if (home->ping_interval < 6) home->ping_interval = 6;
-       if (home->ping_interval > 120) home->ping_interval = 120;
-
-       if (home->response_window < 1) home->response_window = 1;
-       if (home->response_window > 60) home->response_window = 60;
-       if (home->response_window > mainconfig.max_request_time) home->response_window = mainconfig.max_request_time;
-
-       if (home->zombie_period < 1) home->zombie_period = 1;
-       if (home->zombie_period > 120) home->zombie_period = 120;
-
-       if (home->zombie_period < home->response_window) {
-               home->zombie_period = home->response_window;
-       }
-
-       if (home->num_pings_to_alive < 3) home->num_pings_to_alive = 3;
-       if (home->num_pings_to_alive > 10) home->num_pings_to_alive = 10;
-
-       if (home->ping_timeout < 3) home->ping_timeout = 3;
-       if (home->ping_timeout > 10) home->ping_timeout = 10;
-
-       if (home->revive_interval < 60) home->revive_interval = 60;
-       if (home->revive_interval > 3600) home->revive_interval = 3600;
-
-#ifdef WITH_COA
-       if (home->coa_irt < 1) home->coa_irt = 1;
-       if (home->coa_irt > 5) home->coa_irt = 5;
-
-       if (home->coa_mrc < 0) home->coa_mrc = 0;
-       if (home->coa_mrc > 20 ) home->coa_mrc = 20;
-
-       if (home->coa_mrt < 0) home->coa_mrt = 0;
-       if (home->coa_mrt > 30 ) home->coa_mrt = 30;
-
-       if (home->coa_mrd < 5) home->coa_mrd = 5;
-       if (home->coa_mrd > 60 ) home->coa_mrd = 60;
-#endif
-
-       if (home->max_connections > 1024) home->max_connections = 1024;
-
-#ifdef WITH_TCP
-       /*
-        *      UDP sockets can't be connection limited.
-        */
-       if (home->proto != IPPROTO_TCP) home->max_connections = 0;
-#endif
-
-       if ((home->idle_timeout > 0) && (home->idle_timeout < 5))
-               home->idle_timeout = 5;
-       if ((home->lifetime > 0) && (home->lifetime < 5))
-               home->lifetime = 5;
-       if ((home->lifetime > 0) && (home->idle_timeout > home->lifetime))
-               home->idle_timeout = 0;
-
-       tls = cf_item_parent(cf_sectiontoitem(cs));
-       if (strcmp(cf_section_name1(tls), "server") == 0) {
-               home->parent_server = cf_section_name2(tls);
-       }
-
-       if (dual) {
-               home_server *home2 = rad_malloc(sizeof(*home2));
-
-               memcpy(home2, home, sizeof(*home2));
-
-               home2->type = HOME_TYPE_ACCT;
-               home2->port++;
-               home2->ping_user_password = NULL;
-               home2->cs = cs;
-               home2->parent_server = home->parent_server;
-
-               if (home->no_response_fail == 2) home->no_response_fail = 0;
-               if (home2->no_response_fail == 2) home2->no_response_fail = 1;
-
-               if (!rbtree_insert(home_servers_byname, home2)) {
-                       cf_log_err(cf_sectiontoitem(cs),
-                                  "Internal error %d adding home server %s.",
-                                  __LINE__, name2);
-                       free(home2);
-                       return 0;
-               }
-               
-               if (!home->server &&
-                   !rbtree_insert(home_servers_byaddr, home2)) {
-                       rbtree_deletebydata(home_servers_byname, home2);
-                       cf_log_err(cf_sectiontoitem(cs),
-                                  "Internal error %d adding home server %s.",
-                                  __LINE__, name2);
-                       free(home2);
-                       return 0;
-               }
-
-#ifdef WITH_STATS
-               home2->number = home_server_max_number++;
-               if (!rbtree_insert(home_servers_bynumber, home2)) {
-                       rbtree_deletebydata(home_servers_byname, home2);
-                       if (!home2->server) {
-                               rbtree_deletebydata(home_servers_byname, home2);
-                       }
-                       cf_log_err(cf_sectiontoitem(cs),
-                                  "Internal error %d adding home server %s.",
-                                  __LINE__, name2);
-                       free(home2);
-                       return 0;
-               }
-#endif
-       }
 
        /*
         *      Mark it as already processed
@@ -938,6 +933,16 @@ static int pool_check_home_server(realm_config_t *rc, CONF_PAIR *cp,
 }
 
 
+int realms_pool_add( home_pool_t *pool, UNUSED CONF_SECTION *cs)
+{
+               if (!rbtree_insert(home_pools_byname, pool)) {
+               rad_assert("Internal sanity check failed");
+               return 0;
+       }
+               return 1;
+}
+
+
 static int server_pool_add(realm_config_t *rc,
                           CONF_SECTION *cs, int server_type, int do_print)
 {
@@ -1077,7 +1082,7 @@ static int server_pool_add(realm_config_t *rc,
 
                value = cf_pair_value(cp);
 
-               memset(&myhome, 0, sizeof(&myhome));
+               memset(&myhome, 0, sizeof(myhome));
                myhome.name = value;
                myhome.type = server_type;
 
@@ -1100,8 +1105,7 @@ static int server_pool_add(realm_config_t *rc,
                cf_log_info(cs, "\tfallback = %s", pool->fallback->name);
        }
 
-       if (!rbtree_insert(home_pools_byname, pool)) {
-               rad_assert("Internal sanity check failed");
+       if (! realms_pool_add(pool, cs)) {
                goto error;
        }
 
@@ -1556,6 +1560,38 @@ static int add_pool_to_realm(realm_config_t *rc, CONF_SECTION *cs,
 #endif
 
 
+int realms_realm_add (REALM *r, CONF_SECTION *cs)
+{
+       #ifdef HAVE_REGEX_H
+       /*
+        *      It's a regex.  Add it to a separate list.
+        */
+       if (r->name[0] == '~') {
+               realm_regex_t *rr, **last;
+
+               rr = rad_malloc(sizeof(*rr));
+               
+               last = &realms_regex;
+               while (*last) last = &((*last)->next);  /* O(N^2)... sue me. */
+
+               rr->realm = r;
+               rr->next = NULL;
+
+               *last = rr;
+
+               return 1;
+       }
+#endif
+
+       if (!rbtree_insert(realms_byname, r)) {
+               rad_assert("Internal sanity check failed");
+               return 0;
+       }
+
+       return 1;
+}
+
+
 static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
 {
        const char *name2;
@@ -1564,6 +1600,10 @@ static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
 #ifdef WITH_PROXY
        home_pool_t *auth_pool, *acct_pool;
        const char *auth_pool_name, *acct_pool_name;
+#ifdef WITH_COA
+       const char *coa_pool_name;
+       home_pool_t *coa_pool;
+#endif
 #endif
 
        name2 = cf_section_name1(cs);
@@ -1581,6 +1621,10 @@ static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
 #ifdef WITH_PROXY
        auth_pool = acct_pool = NULL;
        auth_pool_name = acct_pool_name = NULL;
+#ifdef WITH_COA
+       coa_pool = NULL;
+       coa_pool_name = NULL;
+#endif
 
        /*
         *      Prefer new configuration to old one.
@@ -1627,7 +1671,8 @@ static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
                }
 
                if (!auth_pool ||
-                   (strcmp(auth_pool_name, acct_pool_name) != 0)) {
+                   (auth_pool_name &&
+                    (strcmp(auth_pool_name, acct_pool_name) != 0))) {
                        do_print = TRUE;
                }
 
@@ -1637,6 +1682,20 @@ static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
                        return 0;
                }
        }
+
+#ifdef WITH_COA
+       cp = cf_pair_find(cs, "coa_pool");
+       if (cp) coa_pool_name = cf_pair_value(cp);
+       if (cp && coa_pool_name) {
+               int do_print = TRUE;
+
+               if (!add_pool_to_realm(rc, cs,
+                                      coa_pool_name, &coa_pool,
+                                      HOME_TYPE_COA, do_print)) {
+                       return 0;
+               }
+       }
+#endif
 #endif
 
        cf_log_info(cs, " realm %s {", name2);
@@ -1695,13 +1754,19 @@ static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
 #ifdef WITH_PROXY
        r->auth_pool = auth_pool;
        r->acct_pool = acct_pool;
-       
+#ifdef WITH_COA
+       r->coa_pool = coa_pool;
+#endif
+
        if (auth_pool_name &&
            (auth_pool_name == acct_pool_name)) { /* yes, ptr comparison */
                cf_log_info(cs, "\tpool = %s", auth_pool_name);
        } else {
                if (auth_pool_name) cf_log_info(cs, "\tauth_pool = %s", auth_pool_name);
                if (acct_pool_name) cf_log_info(cs, "\tacct_pool = %s", acct_pool_name);
+#ifdef WITH_COA
+               if (coa_pool_name) cf_log_info(cs, "\tcoa_pool = %s", coa_pool_name);
+#endif
        }
 #endif
 
@@ -1720,7 +1785,7 @@ static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
                    ((cp = cf_pair_find(cs, "accthost")) != NULL) ||
                    ((cp = cf_pair_find(cs, "secret")) != NULL) ||
                    ((cp = cf_pair_find(cs, "ldflag")) != NULL)) {
-                       DEBUG2("WARNING: Ignoring old-style configuration entry \"%s\" in realm \"%s\"", cf_pair_attr(cp), r->name);
+                       DEBUG2W("Ignoring old-style configuration entry \"%s\" in realm \"%s\"", cf_pair_attr(cp), r->name);
                }
 
 
@@ -1733,38 +1798,12 @@ static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
                goto error;
        }
 
-#ifdef HAVE_REGEX_H
-       /*
-        *      It's a regex.  Add it to a separate list.
-        */
-       if (name2[0] == '~') {
-               realm_regex_t *rr, **last;
-
-               rr = rad_malloc(sizeof(*rr));
-               
-               last = &realms_regex;
-               while (*last) last = &((*last)->next);  /* O(N^2)... sue me. */
-
-               r->name = name2;
-               rr->realm = r;
-               rr->next = NULL;
-
-               *last = rr;
+       if ( !realms_realm_add(r, cs))
+               goto error;
 
                cf_log_info(cs, " }");
                return 1;
-       }
-#endif
-
-       if (!rbtree_insert(realms_byname, r)) {
-               rad_assert("Internal sanity check failed");
-               goto error;
-       }
-
-       cf_log_info(cs, " }");
-
-       return 1;
-
+               
  error:
        cf_log_info(cs, " } # realm %s", name2);
        free(r);
@@ -1878,7 +1917,13 @@ int realms_init(CONF_SECTION *config)
 #ifdef WITH_PROXY
        cs = cf_subsection_find_next(config, NULL, "proxy");
        if (cs) {
-               cf_section_parse(cs, rc, proxy_config);
+               if (cf_section_parse(cs, rc, proxy_config) < 0) {
+                       radlog(L_ERR, "Failed parsing proxy section");
+                       
+                       free(rc);
+                       realms_free();
+                       return 0;
+               }
        } else {
                rc->dead_time = DEAD_TIME;
                rc->retry_count = RETRY_COUNT;
@@ -1928,7 +1973,7 @@ int realms_init(CONF_SECTION *config)
 
 #ifdef WITH_COA
        /*
-        *      CoA pools aren't tied to realms.
+        *      CoA pools aren't necessarily tied to realms.
         */
        for (cs = cf_subsection_find_next(config, NULL, "home_server_pool");
             cs != NULL;
@@ -1941,9 +1986,15 @@ int realms_init(CONF_SECTION *config)
                if (cf_data_find(cs, "home_server_pool")) continue;
 
                type = pool_peek_type(config, cs);
-               if (type == HOME_TYPE_INVALID) return 0;
+               if (type == HOME_TYPE_INVALID) {
+                       free(rc);
+                       realms_free();
+                       return 0;
+               }
 
                if (!server_pool_add(rc, cs, type, TRUE)) {
+                       free(rc);
+                       realms_free();
                        return 0;
                }
        }
@@ -2046,6 +2097,80 @@ REALM *realm_find(const char *name)
 
 
 #ifdef WITH_PROXY
+
+/*
+ *     Allocate the proxy list if it doesn't already exist, and copy request
+ *     VPs into it. Setup src/dst IP addresses based on home server, and
+ *     calculate and add the message-authenticator.
+ *
+ *     This is a distinct function from home_server_ldb, as not all home_server
+ *     lookups result in the *CURRENT* request being proxied,
+ *     as in rlm_replicate, and this may trigger asserts elsewhere in the
+ *     server.
+ */
+void home_server_update_request(home_server *home, REQUEST *request)
+{
+
+       /*
+        *      Allocate the proxy packet, only if it wasn't
+        *      already allocated by a module.  This check is
+        *      mainly to support the proxying of EAP-TTLS and
+        *      EAP-PEAP tunneled requests.
+        *
+        *      In those cases, the EAP module creates a
+        *      "fake" request, and recursively passes it
+        *      through the authentication stage of the
+        *      server.  The module then checks if the request
+        *      was supposed to be proxied, and if so, creates
+        *      a proxy packet from the TUNNELED request, and
+        *      not from the EAP request outside of the
+        *      tunnel.
+        *
+        *      The proxy then works like normal, except that
+        *      the response packet is "eaten" by the EAP
+        *      module, and encapsulated into an EAP packet.
+        */
+       if (!request->proxy) {
+               request->proxy = rad_alloc(request, TRUE);
+               if (!request->proxy) {
+                       radlog(L_ERR, "no memory");
+                       exit(1);
+               }
+               
+               /*
+                *      Copy the request, then look up name
+                *      and plain-text password in the copy.
+                *
+                *      Note that the User-Name attribute is
+                *      the *original* as sent over by the
+                *      client.  The Stripped-User-Name
+                *      attribute is the one hacked through
+                *      the 'hints' file.
+                */
+               request->proxy->vps =  paircopy(request->packet->vps);
+       }
+
+       /*
+        *      Update the various fields as appropriate.
+        */
+       request->proxy->src_ipaddr = home->src_ipaddr;
+       request->proxy->src_port = 0;
+       request->proxy->dst_ipaddr = home->ipaddr;
+       request->proxy->dst_port = home->port;
+       request->home_server = home;
+
+       /*
+        *      Access-Requests have a Message-Authenticator added,
+        *      unless one already exists.
+        */
+       if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
+           !pairfind(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY)) {
+               radius_pairmake(request, &request->proxy->vps,
+                               "Message-Authenticator", "0x00",
+                               T_OP_SET);
+       }
+}
+
 home_server *home_server_ldb(const char *realmname,
                             home_pool_t *pool, REQUEST *request)
 {
@@ -2106,7 +2231,7 @@ home_server *home_server_ldb(const char *realmname,
                break;
 
        case HOME_POOL_KEYED_BALANCE:
-               if ((vp = pairfind(request->config_items, PW_LOAD_BALANCE_KEY, 0)) != NULL) {
+               if ((vp = pairfind(request->config_items, PW_LOAD_BALANCE_KEY, 0, TAG_ANY)) != NULL) {
                        hash = fr_hash(vp->vp_strvalue, vp->length);
                        start = hash % pool->num_home_servers;
                        break;
@@ -2138,6 +2263,9 @@ home_server *home_server_ldb(const char *realmname,
 
                /*
                 *      Skip dead home servers.
+                *
+                *      Home servers that are unknown, alive, or zombie
+                *      are used for proxying.
                 */
                if (home->state == HOME_STATE_IS_DEAD) {
                        continue;
@@ -2263,6 +2391,8 @@ home_server *home_server_ldb(const char *realmname,
        if (!found && pool->fallback) {
                found = pool->fallback;
 
+               DEBUGW("Home server pool %s failing over to fallback %s",
+                     pool->name, found->server);
                if (pool->in_fallback) goto update_and_return;
 
                pool->in_fallback = TRUE;
@@ -2273,7 +2403,7 @@ home_server *home_server_ldb(const char *realmname,
                 */
                if ((pool->time_all_dead + 3600) < request->timestamp) {
                        pool->time_all_dead = request->timestamp;
-                       exec_trigger(request, pool->cs, "home_server_pool.fallback");
+                       exec_trigger(request, pool->cs, "home_server_pool.fallback", FALSE);
                }
        }
 
@@ -2281,66 +2411,7 @@ home_server *home_server_ldb(const char *realmname,
        update_and_return:
                if ((found != pool->fallback) && pool->in_fallback) {
                        pool->in_fallback = FALSE;
-                       exec_trigger(request, pool->cs, "home_server_pool.normal");
-               }
-
-               /*
-                *      Allocate the proxy packet, only if it wasn't
-                *      already allocated by a module.  This check is
-                *      mainly to support the proxying of EAP-TTLS and
-                *      EAP-PEAP tunneled requests.
-                *
-                *      In those cases, the EAP module creates a
-                *      "fake" request, and recursively passes it
-                *      through the authentication stage of the
-                *      server.  The module then checks if the request
-                *      was supposed to be proxied, and if so, creates
-                *      a proxy packet from the TUNNELED request, and
-                *      not from the EAP request outside of the
-                *      tunnel.
-                *
-                *      The proxy then works like normal, except that
-                *      the response packet is "eaten" by the EAP
-                *      module, and encapsulated into an EAP packet.
-                */
-               if (!request->proxy) {
-                       if ((request->proxy = rad_alloc(TRUE)) == NULL) {
-                               radlog(L_ERR|L_CONS, "no memory");
-                               exit(1);
-                       }
-                       
-                       /*
-                        *      Copy the request, then look up name
-                        *      and plain-text password in the copy.
-                        *
-                        *      Note that the User-Name attribute is
-                        *      the *original* as sent over by the
-                        *      client.  The Stripped-User-Name
-                        *      attribute is the one hacked through
-                        *      the 'hints' file.
-                        */
-                       request->proxy->vps =  paircopy(request->packet->vps);
-               }
-
-               /*
-                *      Update the various fields as appropriate.
-                */
-               request->proxy->src_ipaddr = found->src_ipaddr;
-               request->proxy->src_port = 0;
-               request->proxy->dst_ipaddr = found->ipaddr;
-               request->proxy->dst_port = found->port;
-               request->home_server = found;
-
-               /*
-                *      We're supposed to add a Message-Authenticator
-                *      if it doesn't exist, and it doesn't exist.
-                */
-               if (found->message_authenticator &&
-                   (request->packet->code == PW_AUTHENTICATION_REQUEST) &&
-                   !pairfind(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR, 0)) {
-                       radius_pairmake(request, &request->proxy->vps,
-                                       "Message-Authenticator", "0x00",
-                                       T_OP_SET);
+                       exec_trigger(request, pool->cs, "home_server_pool.normal", FALSE);
                }
 
                return found;
@@ -2408,6 +2479,7 @@ home_server *home_server_find(fr_ipaddr_t *ipaddr, int port, int proto)
 
        memset(&myhome, 0, sizeof(myhome));
        myhome.ipaddr = *ipaddr;
+       myhome.src_ipaddr.af = ipaddr->af;
        myhome.port = port;
 #ifdef WITH_TCP
        myhome.proto = proto;