radsecproxy-1.6.3-rc0
[radsecproxy.git] / radsecproxy.c
index fa72fb2..a3a4999 100644 (file)
@@ -52,6 +52,9 @@
 #include <string.h>
 #include <unistd.h>
 #include <limits.h>
+#if defined(HAVE_MALLOPT)
+#include <malloc.h>
+#endif
 #ifdef SYS_SOLARIS9
 #include <fcntl.h>
 #endif
@@ -327,8 +330,13 @@ void freeserver(struct server *server, uint8_t destroymutex) {
     if (server->rbios)
        freebios(server->rbios);
     free(server->dynamiclookuparg);
-    if (server->ssl)
-       SSL_free(server->ssl);
+    if (server->ssl) {
+#if defined ENABLE_EXPERIMENTAL_DYNDISC
+        if (server->sock >= 0)
+            close(server->sock);
+#endif
+        SSL_free(server->ssl);
+    }
     if (destroymutex) {
        pthread_mutex_destroy(&server->lock);
        pthread_cond_destroy(&server->newrq_cond);
@@ -768,7 +776,12 @@ int hasdynamicserver(struct list *srvconfs) {
     struct list_node *entry;
 
     for (entry = list_first(srvconfs); entry; entry = list_next(entry))
-       if (((struct clsrvconf *)entry->data)->dynamiclookupcommand)
+#if defined ENABLE_EXPERIMENTAL_DYNDISC
+        if (((struct clsrvconf *)entry->data)->dynamiclookupcommand
+            || ((struct clsrvconf *)entry->data)->servers->in_use)
+#else
+        if (((struct clsrvconf *)entry->data)->dynamiclookupcommand)
+#endif
            return 1;
     return 0;
 }
@@ -1377,6 +1390,22 @@ struct request *newrequest() {
     return rq;
 }
 
+static void
+purgedupcache(struct client *client) {
+    struct request *r;
+    struct timeval now;
+    int i;
+
+    gettimeofday(&now, NULL);
+    for (i = 0; i < MAX_REQUESTS; i++) {
+       r = client->rqs[i];
+       if (r && now.tv_sec - r->created.tv_sec > r->from->conf->dupinterval) {
+           freerq(r);
+           client->rqs[i] = NULL;
+       }
+    }
+}
+
 int addclientrq(struct request *rq) {
     struct request *r;
     struct timeval now;
@@ -1440,6 +1469,7 @@ int radsrv(struct request *rq) {
        goto exit;
     }
 
+    purgedupcache(from);
     if (!addclientrq(rq))
        goto exit;
 
@@ -1764,12 +1794,17 @@ void *clientwr(void *arg) {
        dynconffail = 1;
        server->dynstartup = 0;
        server->dynfailing = 1;
-       debug(DBG_WARN, "%s: dynamicconfig(%s) failed, sleeping %ds",
-              __func__, server->conf->name, ZZZ);
+#if defined ENABLE_EXPERIMENTAL_DYNDISC
+       pthread_mutex_unlock(&server->lock);
+#endif
+       debug(DBG_WARN, "%s: dynamicconfig(%s: %s) failed, sleeping %ds",
+              __func__, server->conf->name, server->dynamiclookuparg, ZZZ);
        sleep(ZZZ);
        goto errexit;
     }
-
+#if defined ENABLE_EXPERIMENTAL_DYNDISC
+    pthread_mutex_unlock(&server->lock);
+#endif
     /* FIXME: Is resolving not always done by compileserverconfig(),
      * either as part of static configuration setup or by
      * dynamicconfig() above?  */
@@ -1798,7 +1833,10 @@ void *clientwr(void *arg) {
            goto errexit;
        }
        server->connectionok = 1;
-       if (pthread_create(&clientrdth, NULL, conf->pdef->clientconnreader, (void *)server)) {
+#if defined ENABLE_EXPERIMENTAL_DYNDISC
+       server->in_use = 1;
+#endif
+       if (pthread_create(&clientrdth, &pthread_attr, conf->pdef->clientconnreader, (void *)server)) {
            debugerrno(errno, DBG_ERR, "clientwr: pthread_create failed");
            goto errexit;
        }
@@ -1907,6 +1945,9 @@ void *clientwr(void *arg) {
        }
     }
 errexit:
+#if defined ENABLE_EXPERIMENTAL_DYNDISC
+    server->in_use = 0;
+#endif
     conf->servers = NULL;
     if (server->dynamiclookuparg) {
        removeserversubrealms(realms, conf);
@@ -1954,7 +1995,7 @@ void createlistener(uint8_t type, char *arg) {
         if (!sp)
             debugx(1, DBG_ERR, "malloc failed");
        *sp = s;
-       if (pthread_create(&th, NULL, protodefs[type]->listener, (void *)sp))
+       if (pthread_create(&th, &pthread_attr, protodefs[type]->listener, (void *)sp))
             debugerrnox(errno, DBG_ERR, "pthread_create failed");
        pthread_detach(th);
     }
@@ -2201,12 +2242,29 @@ struct list *createsubrealmservers(struct realm *realm, struct list *srvconfs) {
                srvconf->servers->dynstartup = 1;
                 debug(DBG_DBG, "%s: new client writer for %s",
                       __func__, srvconf->servers->conf->name);
-               if (pthread_create(&clientth, NULL, clientwr, (void *)(srvconf->servers))) {
+#if defined ENABLE_EXPERIMENTAL_DYNDISC
+               pthread_mutex_lock(&srvconf->servers->lock);
+#endif
+               if (pthread_create(&clientth, &pthread_attr, clientwr, (void *)(srvconf->servers))) {
+#if defined ENABLE_EXPERIMENTAL_DYNDISC
+                    pthread_mutex_unlock(&srvconf->servers->lock);
+#endif
                    debugerrno(errno, DBG_ERR, "pthread_create failed");
                    freeserver(srvconf->servers, 1);
                    srvconf->servers = NULL;
+#if defined ENABLE_EXPERIMENTAL_DYNDISC
+                   conf = srvconf;
+                   continue;
+#endif
                } else
                    pthread_detach(clientth);
+
+#if defined ENABLE_EXPERIMENTAL_DYNDISC
+                /* If clientwr() could not find a NAPTR we have to
+                 * wait for dynfailing=1 what is set in clientwr().  */
+                pthread_mutex_lock(&srvconf->servers->lock);
+                pthread_mutex_unlock(&srvconf->servers->lock);
+#endif
            }
            conf = srvconf;
        }
@@ -2407,7 +2465,9 @@ struct modattr *extractmodattr(char *nameval) {
     if (s[strlen(s) - 1] == '/')
        s[strlen(s) - 1] = '\0';
 
-    t = strchr(s, '/');
+    for (t = strchr(s, '/'); t; t = strchr(t+1, '/'))
+        if (t == s || t[-1] != '\\')
+            break;
     if (!t)
        return NULL;
     *t = '\0';
@@ -2586,8 +2646,8 @@ void freeclsrvconf(struct clsrvconf *conf) {
        free(conf->rewriteusername);
     }
     free(conf->dynamiclookupcommand);
-    free(conf->rewritein);
-    free(conf->rewriteout);
+    conf->rewritein=NULL;
+    conf->rewriteout=NULL;
     if (conf->hostports)
        freehostports(conf->hostports);
     if (conf->lock) {
@@ -2684,13 +2744,19 @@ int mergesrvconf(struct clsrvconf *dst, struct clsrvconf *src) {
     return 1;
 }
 
-int config_hostaf(const char *block, int ipv4only, int ipv6only, int *af) {
+/** Set *AF according to IPV4ONLY and IPV6ONLY:
+    - If both are set, the function fails.
+    - If exactly one is set, *AF is set accordingly.
+    - If none is set, *AF is not affected.
+    Return 0 on success and !0 on failure.
+    In the case of an error, *AF is not affected.  */
+int config_hostaf(const char *desc, int ipv4only, int ipv6only, int *af) {
+    assert(af != NULL);
     if (ipv4only && ipv6only) {
        debug(DBG_ERR, "error in block %s, at most one of IPv4Only and "
-              "IPv6Only can be enabled", block);
+              "IPv6Only can be enabled", desc);
         return -1;
     }
-    *af = AF_UNSPEC;
     if (ipv4only)
         *af = AF_INET;
     if (ipv6only)
@@ -2702,7 +2768,7 @@ int confclient_cb(struct gconffile **cf, void *arg, char *block, char *opt, char
     struct clsrvconf *conf;
     char *conftype = NULL, *rewriteinalias = NULL;
     long int dupinterval = LONG_MIN, addttl = LONG_MIN;
-    uint8_t ipv4only, ipv6only;
+    uint8_t ipv4only = 0, ipv6only = 0;
 
     debug(DBG_DBG, "confclient_cb called for %s", block);
 
@@ -2769,6 +2835,9 @@ int confclient_cb(struct gconffile **cf, void *arg, char *block, char *opt, char
     }
 #endif
 
+    conf->hostaf = AF_UNSPEC;
+    if (config_hostaf("top level", options.ipv4only, options.ipv6only, &conf->hostaf))
+        debugx(1, DBG_ERR, "config error: ^");
     if (config_hostaf(block, ipv4only, ipv6only, &conf->hostaf))
         debugx(1, DBG_ERR, "error in block %s: ^", block);
 
@@ -2877,7 +2946,7 @@ int confserver_cb(struct gconffile **cf, void *arg, char *block, char *opt, char
     struct clsrvconf *conf, *resconf;
     char *conftype = NULL, *rewriteinalias = NULL;
     long int retryinterval = LONG_MIN, retrycount = LONG_MIN, addttl = LONG_MIN;
-    uint8_t ipv4only, ipv6only;
+    uint8_t ipv4only = 0, ipv6only = 0;
 
     debug(DBG_DBG, "confserver_cb called for %s", block);
 
@@ -2947,6 +3016,9 @@ int confserver_cb(struct gconffile **cf, void *arg, char *block, char *opt, char
     free(conftype);
     conftype = NULL;
 
+    conf->hostaf = AF_UNSPEC;
+    if (config_hostaf("top level", options.ipv4only, options.ipv6only, &conf->hostaf))
+        debugx(1, DBG_ERR, "config error: ^");
     if (config_hostaf(block, ipv4only, ipv6only, &conf->hostaf))
         goto errexit;
 
@@ -3151,6 +3223,8 @@ void getmainconfig(const char *configfile) {
            "FTicksKey", CONF_STR, &fticks_key_str,
            "FTicksSyslogFacility", CONF_STR, &options.ftickssyslogfacility,
 #endif
+            "IPv4Only", CONF_BLN, &options.ipv4only,
+            "IPv6Only", CONF_BLN, &options.ipv6only,
            NULL
            ))
        debugx(1, DBG_ERR, "configuration error");
@@ -3290,6 +3364,15 @@ int radsecproxy_main(int argc, char **argv) {
     debug_init("radsecproxy");
     debug_set_level(DEBUG_LEVEL);
 
+    if (pthread_attr_init(&pthread_attr))
+       debugx(1, DBG_ERR, "pthread_attr_init failed");
+    if (pthread_attr_setstacksize(&pthread_attr, PTHREAD_STACK_SIZE))
+       debugx(1, DBG_ERR, "pthread_attr_setstacksize failed");
+#if defined(HAVE_MALLOPT)
+    if (mallopt(M_TRIM_THRESHOLD, 4 * 1024) != 1)
+       debugx(1, DBG_ERR, "mallopt failed");
+#endif
+
     for (i = 0; i < RAD_PROTOCOUNT; i++)
        protodefs[i] = protoinits[i](i);
 
@@ -3341,7 +3424,7 @@ int radsecproxy_main(int argc, char **argv) {
     sigaddset(&sigset, SIGHUP);
     sigaddset(&sigset, SIGPIPE);
     pthread_sigmask(SIG_BLOCK, &sigset, NULL);
-    pthread_create(&sigth, NULL, sighandler, NULL);
+    pthread_create(&sigth, &pthread_attr, sighandler, NULL);
 
     for (entry = list_first(srvconfs); entry; entry = list_next(entry)) {
        srvconf = (struct clsrvconf *)entry->data;
@@ -3349,7 +3432,7 @@ int radsecproxy_main(int argc, char **argv) {
            continue;
        if (!addserver(srvconf))
            debugx(1, DBG_ERR, "failed to add server");
-       if (pthread_create(&srvconf->servers->clientth, NULL, clientwr,
+       if (pthread_create(&srvconf->servers->clientth, &pthread_attr, clientwr,
                           (void *)(srvconf->servers)))
            debugx(1, DBG_ERR, "pthread_create failed");
     }