now changed to allow prefix/prefixlen for host
authorvenaas <venaas>
Tue, 18 Sep 2007 15:06:15 +0000 (15:06 +0000)
committervenaas <venaas@e88ac4ed-0b26-0410-9574-a7f39faa03bf>
Tue, 18 Sep 2007 15:06:15 +0000 (15:06 +0000)
git-svn-id: https://svn.testnett.uninett.no/radsecproxy/trunk@159 e88ac4ed-0b26-0410-9574-a7f39faa03bf

list.c
list.h
radsecproxy.c
radsecproxy.h

diff --git a/list.c b/list.c
index e9dcebb..4e80300 100644 (file)
--- a/list.c
+++ b/list.c
@@ -52,12 +52,40 @@ void *list_shift(struct list *list) {
     
     node = list->first;
     list->first = node->next;
+    if (!list->first)
+       list->last = NULL;
     data = node->data;
     free(node);
     
     return data;
 }
 
+/* removes first entry with matching data pointer */
+void list_removedata(struct list *list, void *data) {
+    struct list_node *node, *t;
+    
+    if (!list->first)
+       return;
+
+    node = list->first;
+    if (node->data == data) {
+       list->first = node->next;
+       if (!list->first)
+           list->last = NULL;
+       free(node);
+       return;
+    }
+    for (; node->next; node = node->next)
+       if (node->next->data == data) {
+           t = node->next;
+           node->next = node->next->next;
+           if (!node->next) /* we removed the last one */
+               list->last = node;
+           free(t);
+           return;
+       }
+}
+
 /* returns first node */
 struct list_node *list_first(struct list *list) {
     return list->first;
diff --git a/list.h b/list.h
index c89f902..938c879 100644 (file)
--- a/list.h
+++ b/list.h
@@ -19,6 +19,9 @@ int list_push(struct list *list, void *data);
 /* removes first entry from list and returns data */
 void *list_shift(struct list *list);
 
+/* removes first entry with matching data pointer */
+void list_removedata(struct list *list, void *data);
+    
 /* returns first node */
 struct list_node *list_first(struct list *list);
 
index 9df3786..7c27d82 100644 (file)
@@ -132,16 +132,60 @@ static int verify_cb(int ok, X509_STORE_CTX *ctx) {
 
 int resolvepeer(struct clsrvconf *conf, int ai_flags) {
     struct addrinfo hints, *addrinfo;
-    
+    char *slash, *s;
+    int plen;
+
+    slash = conf->host ? strchr(conf->host, '/') : NULL;
+    if (slash) {
+       s = slash + 1;
+       if (!*s) {
+           debug(DBG_WARN, "resolvepeer: prefix length must be specified after the / in %s", conf->host);
+           return 0;
+       }
+       for (; *s; s++)
+           if (*s < '0' || *s > '9') {
+               debug(DBG_WARN, "resolvepeer: %s in %s is not a valid prefix length", slash + 1, conf->host);
+               return 0;
+           }
+       plen = atoi(slash + 1);
+       if (plen < 0 || plen > 128) {
+           debug(DBG_WARN, "resolvepeer: %s in %s is not a valid prefix length", slash + 1, conf->host);
+           return 0;
+       }
+       *slash = '\0';
+    }
     memset(&hints, 0, sizeof(hints));
     hints.ai_socktype = (conf->type == 'T' ? SOCK_STREAM : SOCK_DGRAM);
     hints.ai_family = AF_UNSPEC;
     hints.ai_flags = ai_flags;
+    if (slash)
+       hints.ai_flags |= AI_NUMERICHOST;
     if (getaddrinfo(conf->host, conf->port, &hints, &addrinfo)) {
        debug(DBG_WARN, "resolvepeer: can't resolve %s port %s", conf->host, conf->port);
        return 0;
     }
 
+    if (slash) {
+       *slash = '/';
+       switch (addrinfo->ai_family) {
+       case AF_INET:
+           if (plen > 32) {
+               debug(DBG_WARN, "resolvepeer: prefix length must be <= 32 in %s", conf->host);
+               freeaddrinfo(addrinfo);
+               return 0;
+           }
+           break;
+       case AF_INET6:
+           break;
+       default:
+           debug(DBG_WARN, "resolvepeer: prefix must be IPv4 or IPv6 in %s", conf->host);
+           freeaddrinfo(addrinfo);
+           return 0;
+       }
+       conf->prefixlen = plen;
+    } else
+       conf->prefixlen = 255;
+
     if (conf->addrinfo)
        freeaddrinfo(conf->addrinfo);
     conf->addrinfo = addrinfo;
@@ -187,7 +231,19 @@ int bindtoaddr(struct addrinfo *addrinfo) {
     return -1;
 }        
 
-/* returns the peer with matching address, or NULL */
+/* returns 1 if the len first bits are equal, else 0 */
+int prefixmatch(void *a1, void *a2, uint8_t len) {
+    static uint8_t mask[] = { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
+    int r, l = len / 8;
+    if (l && memcmp(a1, a2, l))
+       return 0;
+    r = len % 8;
+    if (!r)
+       return 1;
+    return (((uint8_t *)a1)[l] & mask[r]) == (((uint8_t *)a2)[l] & mask[r]);
+}
+
+/* returns the config with matching address, or NULL */
 /* if conf argument is not NULL, we only check that one */
 struct clsrvconf *find_conf(char type, struct sockaddr *addr, struct list *confs, struct clsrvconf *conf) {
     struct sockaddr_in6 *sa6 = NULL;
@@ -197,35 +253,55 @@ struct clsrvconf *find_conf(char type, struct sockaddr *addr, struct list *confs
     
     if (addr->sa_family == AF_INET6) {
         sa6 = (struct sockaddr_in6 *)addr;
-        if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr))
+        if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
             a4 = (struct in_addr *)&sa6->sin6_addr.s6_addr[12];
+           sa6 = NULL;
+       }
     } else
        a4 = &((struct sockaddr_in *)addr)->sin_addr;
 
     if (conf) {
-       if (conf->type == type)
-           if (!conf->host) /* for now this means match everything */
-               return conf;
-           for (res = conf->addrinfo; res; res = res->ai_next)
-               if ((a4 && res->ai_family == AF_INET &&
-                    !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) ||
-                   (sa6 && res->ai_family == AF_INET6 &&
-                    !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16)))
+       if (conf->type == type) {
+           if (conf->prefixlen == 255) {
+               for (res = conf->addrinfo; res; res = res->ai_next)
+                   if ((a4 && res->ai_family == AF_INET &&
+                        !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) ||
+                       (sa6 && res->ai_family == AF_INET6 &&
+                        !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16)))
+                       return conf;
+           } else {
+               res = conf->addrinfo;
+               if (res &&
+                   ((a4 && res->ai_family == AF_INET &&
+                     prefixmatch(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, conf->prefixlen)) ||
+                    (sa6 && res->ai_family == AF_INET6 &&
+                     prefixmatch(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, conf->prefixlen))))
                    return conf;
+           }
+       }
        return NULL;
     }
 
     for (entry = list_first(confs); entry; entry = list_next(entry)) { 
        conf = (struct clsrvconf *)entry->data;
-       if (conf->type == type)
-           if (!conf->host) /* for now this means match everything */
-               return conf;
-           for (res = conf->addrinfo; res; res = res->ai_next)
-               if ((a4 && res->ai_family == AF_INET &&
-                    !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) ||
-                   (sa6 && res->ai_family == AF_INET6 &&
-                    !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16)))
+       if (conf->type == type) {
+           if (conf->prefixlen == 255) {
+               for (res = conf->addrinfo; res; res = res->ai_next)
+                   if ((a4 && res->ai_family == AF_INET &&
+                        !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) ||
+                       (sa6 && res->ai_family == AF_INET6 &&
+                        !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16)))
+                       return conf;
+           } else {
+               res = conf->addrinfo;
+               if (res &&
+                   ((a4 && res->ai_family == AF_INET &&
+                     prefixmatch(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, conf->prefixlen)) ||
+                    (sa6 && res->ai_family == AF_INET6 &&
+                     prefixmatch(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, conf->prefixlen))))
                    return conf;
+           }
+       }
     }    
     return NULL;
 }
@@ -244,19 +320,35 @@ struct replyq *newreplyq() {
     return replyq;
 }
 
-void addclient(struct clsrvconf *conf) {
-    if (conf->clients) {
-       debug(DBG_ERR, "currently works with just one client per conf");
-       return;
+struct client *addclient(struct clsrvconf *conf) {
+    struct client *new = malloc(sizeof(struct client));
+    
+    if (!new) {
+       debug(DBG_ERR, "malloc failed");
+       return NULL;
     }
-    conf->clients = malloc(sizeof(struct client));
     if (!conf->clients) {
-       debug(DBG_ERR, "malloc failed");
-       return;
+       conf->clients = list_create();
+       if (!conf->clients) {
+           debug(DBG_ERR, "malloc failed");
+           return NULL;
+       }
     }
-    memset(conf->clients, 0, sizeof(struct client));
-    conf->clients->conf = conf;
-    conf->clients->replyq = conf->type == 'T' ? newreplyq() : udp_server_replyq;
+    
+    memset(new, 0, sizeof(struct client));
+    new->conf = conf;
+    new->replyq = conf->type == 'T' ? newreplyq() : udp_server_replyq;
+
+    list_push(conf->clients, new);
+    return new;
+}
+
+void removeclient(struct client *client) {
+    if (!client || !client->conf->clients)
+       return;
+
+    list_removedata(client->conf->clients, client);
+    free(client);
 }
 
 void addserver(struct clsrvconf *conf) {
@@ -340,12 +432,11 @@ unsigned char *radudpget(int s, struct client **client, struct server **server,
        
        if (client && !*client) {
            if (!p->clients)
-               addclient(p);
-           if (!p->clients) {
+               *client = addclient(p);
+           if (!*client) {
                free(rad);
                continue;
            }
-           *client = p->clients;
        } else if (server && !*server)
            *server = p->servers;
        
@@ -1725,7 +1816,7 @@ void *tlsserverrd(void *arg) {
     shutdown(s, SHUT_RDWR);
     close(s);
     debug(DBG_DBG, "tlsserverrd thread for %s exiting", client->conf->host);
-    client->ssl = NULL;
+    removeclient(client);
     pthread_exit(NULL);
 }
 
@@ -1760,13 +1851,10 @@ int tlslistener() {
            continue;
        }
 
-       if (!conf->clients)
-           addclient(conf);
-       client = conf->clients;
+       client = addclient(conf);
 
-       if (!client || client->ssl) {
-           if (client)
-               debug(DBG_WARN, "Ignoring incoming TLS connection, already have one from this client");
+       if (!client) {
+           debug(DBG_WARN, "Failed to create new client instance");
            shutdown(snew, SHUT_RDWR);
            close(snew);
            continue;
@@ -1776,9 +1864,9 @@ int tlslistener() {
        if (pthread_create(&tlsserverth, NULL, tlsserverrd, (void *)client)) {
            debug(DBG_ERR, "tlslistener: pthread_create failed");
            SSL_free(client->ssl);
+           removeclient(client);
            shutdown(snew, SHUT_RDWR);
            close(snew);
-           client->ssl = NULL;
            continue;
        }
        pthread_detach(tlsserverth);
@@ -2235,11 +2323,6 @@ void confclient_cb(FILE *f, char *block, char *opt, char *val) {
     if (!conf->host)
        conf->host = stringcopy(val, 0);
     
-    if (!strcmp(conf->host, "*")) {
-       free(conf->host);
-       conf->host = NULL;
-    }
-    
     if (type && !strcasecmp(type, "udp")) {
        conf->type = 'U';
        client_udp_count++;
@@ -2259,10 +2342,8 @@ void confclient_cb(FILE *f, char *block, char *opt, char *val) {
     if (matchcertattr)
        free(matchcertattr);
 
-    if (conf->host) {
-       if (!resolvepeer(conf, 0))
-           debugx(1, DBG_ERR, "failed to resolve host %s port %s, exiting", conf->host, conf->port);
-    }
+    if (!resolvepeer(conf, 0))
+       debugx(1, DBG_ERR, "failed to resolve host %s port %s, exiting", conf->host, conf->port);
     
     if (!conf->secret) {
        if (conf->type == 'U')
index 2516074..5342363 100644 (file)
@@ -82,7 +82,8 @@ struct clsrvconf {
     uint8_t statusserver;
     SSL_CTX *ssl_ctx;
     struct addrinfo *addrinfo;
-    struct client *clients;
+    uint8_t prefixlen;
+    struct list *clients;
     struct server *servers;
 };
 
@@ -90,6 +91,7 @@ struct client {
     struct clsrvconf *conf;
     SSL *ssl;
     struct replyq *replyq;
+    struct client *next;
 };
 
 struct server {