Better way of updating cf_data_add
[freeradius.git] / src / main / realms.c
index d868085..7abf7f0 100644 (file)
@@ -142,6 +142,7 @@ static int home_server_addr_cmp(const void *one, const void *two)
 
        if (a->server && !b->server) return -1;
        if (!a->server && b->server) return +1;
+
        if (a->server && b->server) {
                int rcode = a->type - b->type;
                if (rcode != 0) return rcode;
@@ -386,6 +387,10 @@ static CONF_PARSER home_server_config[] = {
 };
 
 
+static void null_free(UNUSED void *data)
+{
+}
+
 static int home_server_add(realm_config_t *rc, CONF_SECTION *cs, int pool_type)
 {
        const char *name2;
@@ -416,6 +421,24 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs, int pool_type)
        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);
+
+       /*
+        *      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.
+        */
+       home->no_response_fail = 2;
+
        memset(&hs_ip4addr, 0, sizeof(hs_ip4addr));
        memset(&hs_ip6addr, 0, sizeof(hs_ip6addr));
        if (cf_section_parse(cs, home, home_server_config) < 0) {
@@ -505,16 +528,18 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs, int pool_type)
 
        if (strcasecmp(hs_type, "auth") == 0) {
                home->type = HOME_TYPE_AUTH;
+               if (home->no_response_fail == 2) home->no_response_fail = 0;
                if (pool_type != home->type) {
                mismatch:
                        cf_log_err(cf_sectiontoitem(cs),
-                                  "Server pool cannot include home server %s of type \"%s\"",
+                                  "Home server %s of unexpected type \"%s\"",
                                   name2, hs_type);
                        goto error;
                }
 
        } else if (strcasecmp(hs_type, "acct") == 0) {
                home->type = HOME_TYPE_ACCT;
+               if (home->no_response_fail == 2) home->no_response_fail = 1;
                if (pool_type != home->type) goto mismatch;
 
        } else if (strcasecmp(hs_type, "auth+acct") == 0) {
@@ -725,6 +750,9 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs, int pool_type)
                home2->ping_user_password = NULL;
                home2->cs = cs;
 
+               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.",
@@ -759,6 +787,11 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs, int pool_type)
 #endif
        }
 
+       /*
+        *      Mark it as already processed
+        */
+       cf_data_add(cs, "home_server", null_free, null_free);
+
        return 1;
 }
 
@@ -1125,6 +1158,7 @@ static int old_server_add(realm_config_t *rc, CONF_SECTION *cs,
                home->type = type;
                home->secret = secret;
                home->cs = cs;
+               home->proto = IPPROTO_UDP;
 
                p = strchr(name, ':');
                if (!p) {
@@ -1554,15 +1588,22 @@ static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
 
 #ifdef HAVE_REGEX_H
        if (name2[0] == '~') {
+               int rcode;
                regex_t reg;
                
                /*
                 *      Include substring matches.
                 */
-               if (regcomp(&reg, name2 + 1,
-                           REG_EXTENDED | REG_NOSUB | REG_ICASE) != 0) {
+               rcode = regcomp(&reg, name2 + 1,
+                               REG_EXTENDED | REG_NOSUB | REG_ICASE);
+               if (rcode != 0) {
+                       char buffer[256];
+
+                       regerror(rcode, &reg, buffer, sizeof(buffer));
+
                        cf_log_err(cf_sectiontoitem(cs),
-                                  "Invalid regex in realm \"%s\"", name2);
+                                  "Invalid regex \"%s\": %s",
+                                  name2 + 1, buffer);
                        goto error;
                }
                regfree(&reg);
@@ -1798,6 +1839,22 @@ int realms_init(CONF_SECTION *config)
                        return 0;
                }
        }
+
+       /*
+        *      CoA home servers aren't tied to realms.
+        */
+       for (cs = cf_subsection_find_next(config, NULL, "home_server");
+            cs != NULL;
+            cs = cf_subsection_find_next(config, cs, "home_server")) {
+               /*
+                *      Server was already loaded.
+                */
+               if (cf_data_find(cs, "home_server")) continue;
+
+               if (!home_server_add(rc, cs, HOME_TYPE_COA)) {
+                       return 0;
+               }
+       }
 #endif
 
 
@@ -1957,7 +2014,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)) != NULL) {
+               if ((vp = pairfind(request->config_items, PW_LOAD_BALANCE_KEY, 0)) != NULL) {
                        hash = fr_hash(vp->vp_strvalue, vp->length);
                        start = hash % pool->num_home_servers;
                        break;
@@ -2027,7 +2084,7 @@ home_server *home_server_ldb(const char *realmname,
                /*
                 *      We've found the first "live" one.  Use that.
                 */
-               if (pool->type == HOME_POOL_FAIL_OVER) {
+               if (pool->type != HOME_POOL_LOAD_BALANCE) {
                        found = home;
                        break;
                }
@@ -2129,11 +2186,6 @@ home_server *home_server_ldb(const char *realmname,
                         *      the 'hints' file.
                         */
                        request->proxy->vps =  paircopy(request->packet->vps);
-
-                       /*
-                        *      Set the source IP address for proxying.
-                        */
-                       request->proxy->src_ipaddr = found->src_ipaddr;
                }
 
                /*
@@ -2151,7 +2203,7 @@ home_server *home_server_ldb(const char *realmname,
                 */
                if (found->message_authenticator &&
                    (request->packet->code == PW_AUTHENTICATION_REQUEST) &&
-                   !pairfind(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR)) {
+                   !pairfind(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR, 0)) {
                        radius_pairmake(request, &request->proxy->vps,
                                        "Message-Authenticator", "0x00",
                                        T_OP_SET);
@@ -2216,13 +2268,18 @@ home_server *home_server_ldb(const char *realmname,
 }
 
 
-home_server *home_server_find(fr_ipaddr_t *ipaddr, int port)
+home_server *home_server_find(fr_ipaddr_t *ipaddr, int port, int proto)
 {
        home_server myhome;
 
        memset(&myhome, 0, sizeof(myhome));
        myhome.ipaddr = *ipaddr;
        myhome.port = port;
+#ifdef WITH_TCP
+       myhome.proto = proto;
+#else
+       myhome.proto = IPPROTO_UDP;
+#endif
        myhome.server = NULL;   /* we're not called for internal proxying */
 
        return rbtree_finddata(home_servers_byaddr, &myhome);