radsecproxy-1.6.5.
[radsecproxy.git] / hostport.c
index 5f248f2..4214c55 100644 (file)
@@ -6,15 +6,22 @@
  * copyright notice and this permission notice appear in all copies.
  */
 
+/* Code contributions from:
+ *
+ * Simon Leinen <simon.leinen@switch.ch>
+ */
+
 #include <stdlib.h>
+#include <unistd.h>
 #include <string.h>
 #include <netdb.h>
+#include <netinet/in.h>
 #include "debug.h"
 #include "util.h"
 #include "list.h"
 #include "hostport.h"
 
-static void freehostport(struct hostportres *hp) {
+void freehostport(struct hostportres *hp) {
     if (hp) {
        free(hp->host);
        free(hp->port);
@@ -28,6 +35,10 @@ static int parsehostport(struct hostportres *hp, char *hostport, char *default_p
     char *p, *field;
     int ipv6 = 0;
 
+    if (!hostport) {
+       hp->port = default_port ? stringcopy(default_port, 0) : NULL;
+       return 1;
+    }
     p = hostport;
     /* allow literal addresses and port, e.g. [2001:db8::1]:1812 */
     if (*p == '[') {
@@ -41,7 +52,7 @@ static int parsehostport(struct hostportres *hp, char *hostport, char *default_p
        ipv6 = 1;
     } else {
        field = p;
-       for (; *p && *p != ':' && *p != ' ' && *p != '\t' && *p != '\n'; p++);
+       for (; *p && *p != ':' && *p != '/' && *p != ' ' && *p != '\t' && *p != '\n'; p++);
     }
     if (field == p) {
        debug(DBG_ERR, "missing host/address");
@@ -51,30 +62,30 @@ static int parsehostport(struct hostportres *hp, char *hostport, char *default_p
     hp->host = stringcopy(field, p - field);
     if (ipv6) {
        p++;
-       if (*p && *p != ':' && *p != ' ' && *p != '\t' && *p != '\n') {
+       if (*p && *p != ':' && *p != '/' && *p != ' ' && *p != '\t' && *p != '\n') {
            debug(DBG_ERR, "unexpected character after ]");
            return 0;
        }
     }
     if (*p == ':') {
-           /* port number or service name is specified */;
-           field = ++p;
-           for (; *p && *p != ' ' && *p != '\t' && *p != '\n'; p++);
-           if (field == p) {
-               debug(DBG_ERR, "syntax error, : but no following port");
-               return 0;
-           }
-           hp->port = stringcopy(field, p - field);
+       /* port number or service name is specified */;
+       field = ++p;
+       for (; *p && *p != ' ' && *p != '\t' && *p != '\n'; p++);
+       if (field == p) {
+           debug(DBG_ERR, "syntax error, : but no following port");
+           return 0;
+       }
+       hp->port = stringcopy(field, p - field);
     } else
        hp->port = default_port ? stringcopy(default_port, 0) : NULL;
     return 1;
 }
-    
-static struct hostportres *newhostport(char *hostport, char *default_port, uint8_t prefixok) {
+
+struct hostportres *newhostport(char *hostport, char *default_port, uint8_t prefixok) {
     struct hostportres *hp;
     char *slash, *s;
     int plen;
-    
+
     hp = malloc(sizeof(struct hostportres));
     if (!hp) {
        debug(DBG_ERR, "resolve_newhostport: malloc failed");
@@ -85,12 +96,12 @@ static struct hostportres *newhostport(char *hostport, char *default_port, uint8
     if (!parsehostport(hp, hostport, default_port))
        goto errexit;
 
-    if (!strcmp(hp->host, "*")) {
+    if (hp->host && !strcmp(hp->host, "*")) {
        free(hp->host);
        hp->host = NULL;
     }
 
-    slash = hp->host ? strchr(hp->host, '/') : NULL;
+    slash = hostport ? strchr(hostport, '/') : NULL;
     if (slash) {
        if (!prefixok) {
            debug(DBG_WARN, "newhostport: prefix not allowed here", hp->host);
@@ -112,25 +123,24 @@ static struct hostportres *newhostport(char *hostport, char *default_port, uint8
            goto errexit;
        }
        hp->prefixlen = plen;
-       *slash = '\0';
     } else
        hp->prefixlen = 255;
     return hp;
 
- errexit:
+errexit:
     freehostport(hp);
     return NULL;
 }
 
-static int resolvehostport(struct hostportres *hp, int socktype, uint8_t passive) {
+int resolvehostport(struct hostportres *hp, int af, int socktype, uint8_t passive) {
     struct addrinfo hints, *res;
 
     memset(&hints, 0, sizeof(hints));
     hints.ai_socktype = socktype;
-    hints.ai_family = AF_UNSPEC;
+    hints.ai_family = af;
     if (passive)
        hints.ai_flags = AI_PASSIVE;
-    
+
     if (!hp->host && !hp->port) {
        /* getaddrinfo() doesn't like host and port to be NULL */
        if (getaddrinfo(hp->host, "1812" /* can be anything */, &hints, &hp->addrinfo)) {
@@ -162,26 +172,36 @@ static int resolvehostport(struct hostportres *hp, int socktype, uint8_t passive
            }
        }
     }
+    debug(DBG_DBG, "%s: %s -> %s", __func__, hp->host, addr2string(hp->addrinfo->ai_addr));
     return 1;
 
- errexit:
+errexit:
     if (hp->addrinfo)
        freeaddrinfo(hp->addrinfo);
     return 0;
-}        
+}
 
-int addhostport(struct list **hostports, char *hostport, char *portdefault, uint8_t prefixok) {
+int addhostport(struct list **hostports, char **hostport, char *portdefault, uint8_t prefixok) {
     struct hostportres *hp;
+    int i;
 
-    hp = newhostport(hostport, portdefault, prefixok);
-    if (!hp)
-       return 0;
-    if (!*hostports)
+    if (!*hostports) {
        *hostports = list_create();
-    if (!*hostports || !list_push(*hostports, hp)) {
-       freehostport(hp);
-       debug(DBG_ERR, "addhostport: malloc failed");
-       return 0;
+        if (!*hostports) {
+           debug(DBG_ERR, "addhostport: malloc failed");
+           return 0;
+       }
+    }
+
+    for (i = 0; hostport[i]; i++) {
+       hp = newhostport(hostport[i], portdefault, prefixok);
+       if (!hp)
+           return 0;
+       if (!list_push(*hostports, hp)) {
+           freehostport(hp);
+           debug(DBG_ERR, "addhostport: malloc failed");
+           return 0;
+       }
     }
     return 1;
 }
@@ -194,22 +214,22 @@ void freehostports(struct list *hostports) {
     list_destroy(hostports);
 }
 
-int resolvehostports(struct list *hostports, int socktype) {
+int resolvehostports(struct list *hostports, int af, int socktype) {
     struct list_node *entry;
     struct hostportres *hp;
-    
+
     for (entry = list_first(hostports); entry; entry = list_next(entry)) {
        hp = (struct hostportres *)entry->data;
-       if (!hp->addrinfo && !resolvehostport(hp, socktype, 0))
+       if (!hp->addrinfo && !resolvehostport(hp, af, socktype, 0))
            return 0;
     }
     return 1;
 }
 
-struct addrinfo *resolvepassiveaddrinfo(char *hostport, char *default_port, int socktype) {
+struct addrinfo *resolvepassiveaddrinfo(char *hostport, int af, char *default_port, int socktype) {
     struct addrinfo *ai = NULL;
     struct hostportres *hp = newhostport(hostport, default_port, 0);
-    if (hp && resolvehostport(hp, socktype, 1)) {
+    if (hp && resolvehostport(hp, af, socktype, 1)) {
        ai = hp->addrinfo;
        hp->addrinfo = NULL;
     }
@@ -229,13 +249,13 @@ static int prefixmatch(void *a1, void *a2, uint8_t len) {
     return (((uint8_t *)a1)[l] & mask[r]) == (((uint8_t *)a2)[l] & mask[r]);
 }
 
-int addressmatches(struct list *hostports, struct sockaddr *addr) {
+int addressmatches(struct list *hostports, struct sockaddr *addr, uint8_t checkport) {
     struct sockaddr_in6 *sa6 = NULL;
     struct in_addr *a4 = NULL;
     struct addrinfo *res;
     struct list_node *entry;
     struct hostportres *hp = NULL;
-    
+
     if (addr->sa_family == AF_INET6) {
         sa6 = (struct sockaddr_in6 *)addr;
         if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
@@ -250,9 +270,14 @@ int addressmatches(struct list *hostports, struct sockaddr *addr) {
        for (res = hp->addrinfo; res; res = res->ai_next)
            if (hp->prefixlen == 255) {
                if ((a4 && res->ai_family == AF_INET &&
-                    !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) ||
+                    !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4) &&
+                    (!checkport || ((struct sockaddr_in *)res->ai_addr)->sin_port ==
+                     ((struct sockaddr_in *)addr)->sin_port)) ||
                    (sa6 && res->ai_family == AF_INET6 &&
-                    !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16)))
+                    !memcmp(&sa6->sin6_addr,
+                            &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16) &&
+                    (!checkport || ((struct sockaddr_in6 *)res->ai_addr)->sin6_port ==
+                     ((struct sockaddr_in6 *)addr)->sin6_port)))
                    return 1;
            } else {
                if ((a4 && res->ai_family == AF_INET &&
@@ -264,3 +289,24 @@ int addressmatches(struct list *hostports, struct sockaddr *addr) {
     }
     return 0;
 }
+
+int connecttcphostlist(struct list *hostports,  struct addrinfo *src) {
+    int s;
+    struct list_node *entry;
+    struct hostportres *hp = NULL;
+
+    for (entry = list_first(hostports); entry; entry = list_next(entry)) {
+       hp = (struct hostportres *)entry->data;
+       debug(DBG_WARN, "connecttcphostlist: trying to open TCP connection to %s port %s", hp->host, hp->port);
+       if ((s = connecttcp(hp->addrinfo, src, list_count(hostports) > 1 ? 5 : 30)) >= 0) {
+           debug(DBG_WARN, "connecttcphostlist: TCP connection to %s port %s up", hp->host, hp->port);
+           return s;
+       }
+    }
+    debug(DBG_ERR, "connecttcphostlist: failed");
+    return -1;
+}
+
+/* Local Variables: */
+/* c-file-style: "stroustrup" */
+/* End: */