Blame SOURCES/0001-krb5-locator-add-support-for-multiple-addresses.patch

ca1eb8
From 4b1137562c3446e85a6383010702850f9532a4f2 Mon Sep 17 00:00:00 2001
ca1eb8
From: Sumit Bose <sbose@redhat.com>
ca1eb8
Date: Fri, 24 Feb 2017 13:55:47 +0100
ca1eb8
Subject: [PATCH] krb5 locator: add support for multiple addresses
ca1eb8
ca1eb8
Read multiple addresses from the kdcinfo files add call the provided
ca1eb8
callback with each of them.
ca1eb8
ca1eb8
Related to https://pagure.io/SSSD/sssd/issue/941
ca1eb8
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
ca1eb8
ca1eb8
(cherry picked from commit efae9509cb05648357e9b4c10a93c0d38558bed4)
ca1eb8
ca1eb8
DOWNSTREAM:
ca1eb8
Resolves: rhbz#1494690 - kdcinfo files are not created for subdomains of a directly joined AD client
ca1eb8
---
ca1eb8
 src/krb5_plugin/sssd_krb5_locator_plugin.c | 344 +++++++++++++++------
ca1eb8
 1 file changed, 246 insertions(+), 98 deletions(-)
ca1eb8
ca1eb8
diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c
ca1eb8
index 7c17fcb33373293fbbbe2be967dca57b31ef13de..82fb5c7b2ffa319ed250e54cdf9a0b6798d4ff51 100644
ca1eb8
--- a/src/krb5_plugin/sssd_krb5_locator_plugin.c
ca1eb8
+++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c
ca1eb8
@@ -42,7 +42,7 @@
ca1eb8
 #define DEFAULT_KADMIN_PORT 749
ca1eb8
 #define DEFAULT_KPASSWD_PORT 464
ca1eb8
 
ca1eb8
-#define BUFSIZE 512
ca1eb8
+#define BUFSIZE 4096
ca1eb8
 #define PORT_STR_SIZE 7
ca1eb8
 #define SSSD_KRB5_LOCATOR_DEBUG "SSSD_KRB5_LOCATOR_DEBUG"
ca1eb8
 #define SSSD_KRB5_LOCATOR_DISABLE "SSSD_KRB5_LOCATOR_DISABLE"
ca1eb8
@@ -53,12 +53,15 @@
ca1eb8
     } \
ca1eb8
 } while(0)
ca1eb8
 
ca1eb8
+struct addr_port {
ca1eb8
+    char *addr;
ca1eb8
+    uint16_t port;
ca1eb8
+};
ca1eb8
+
ca1eb8
 struct sssd_ctx {
ca1eb8
     char *sssd_realm;
ca1eb8
-    char *kdc_addr;
ca1eb8
-    uint16_t kdc_port;
ca1eb8
-    char *kpasswd_addr;
ca1eb8
-    uint16_t kpasswd_port;
ca1eb8
+    struct addr_port *kdc_addr;
ca1eb8
+    struct addr_port *kpasswd_addr;
ca1eb8
     bool debug;
ca1eb8
     bool disabled;
ca1eb8
 };
ca1eb8
@@ -82,6 +85,186 @@ void plugin_debug_fn(const char *format, ...)
ca1eb8
     free(s);
ca1eb8
 }
ca1eb8
 
ca1eb8
+
ca1eb8
+static void free_addr_port_list(struct addr_port **list)
ca1eb8
+{
ca1eb8
+    size_t c;
ca1eb8
+
ca1eb8
+    if (list == NULL || *list == NULL) {
ca1eb8
+        return;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    for (c = 0; (*list)[c].addr != NULL; c++) {
ca1eb8
+        free((*list)[c].addr);
ca1eb8
+    }
ca1eb8
+    free(*list);
ca1eb8
+    *list = NULL;
ca1eb8
+}
ca1eb8
+
ca1eb8
+static int copy_addr_port_list(struct addr_port *src, bool clear_port,
ca1eb8
+                               struct addr_port **dst)
ca1eb8
+{
ca1eb8
+    size_t c;
ca1eb8
+    struct addr_port *d = NULL;
ca1eb8
+    int ret;
ca1eb8
+
ca1eb8
+    /* only copy if dst is initialized to NULL */
ca1eb8
+    if (dst == NULL || *dst != NULL) {
ca1eb8
+        return EINVAL;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    if (src == NULL) {
ca1eb8
+        return 0;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    for (c = 0; src[c].addr != NULL; c++);
ca1eb8
+
ca1eb8
+    d = calloc((c + 1), sizeof(struct addr_port));
ca1eb8
+    if (d == NULL) {
ca1eb8
+        return ENOMEM;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    for (c = 0; src[c].addr != NULL; c++) {
ca1eb8
+        d[c].addr = strdup(src[c].addr);
ca1eb8
+        if (d[c].addr == NULL) {
ca1eb8
+            ret = ENOMEM;
ca1eb8
+            goto done;
ca1eb8
+        }
ca1eb8
+        if (clear_port) {
ca1eb8
+            d[c].port = 0;
ca1eb8
+        } else {
ca1eb8
+            d[c].port = src[c].port;
ca1eb8
+        }
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    ret = EOK;
ca1eb8
+
ca1eb8
+done:
ca1eb8
+    if (ret != EOK) {
ca1eb8
+        free_addr_port_list(&d);
ca1eb8
+    } else {
ca1eb8
+        *dst = d;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    return ret;
ca1eb8
+}
ca1eb8
+
ca1eb8
+static int buf_to_addr_port_list(struct sssd_ctx *ctx,
ca1eb8
+                                 uint8_t *buf, size_t buf_size,
ca1eb8
+                                 struct addr_port **list)
ca1eb8
+{
ca1eb8
+    struct addr_port *l = NULL;
ca1eb8
+    int ret;
ca1eb8
+    uint8_t *p;
ca1eb8
+    uint8_t *pn;
ca1eb8
+    size_t c;
ca1eb8
+    size_t len;
ca1eb8
+    char *tmp = NULL;
ca1eb8
+    char *port_str;
ca1eb8
+    long port;
ca1eb8
+    char *endptr;
ca1eb8
+
ca1eb8
+    /* only create if list is initialized to NULL */
ca1eb8
+    if (buf == NULL || buf_size == 0 || list == NULL || *list != NULL) {
ca1eb8
+        return EINVAL;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    c = 1; /* to account for a missing \n at the very end */
ca1eb8
+    p = buf;
ca1eb8
+    while ((p - buf) < buf_size
ca1eb8
+                && (p = memchr(p, '\n', buf_size - (p - buf))) != NULL) {
ca1eb8
+        p++;
ca1eb8
+        c++;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    l = calloc((c + 1), sizeof(struct addr_port));
ca1eb8
+    if (l == NULL) {
ca1eb8
+        return ENOMEM;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    c = 0;
ca1eb8
+    p = buf;
ca1eb8
+    do {
ca1eb8
+        pn = memchr(p, '\n', buf_size - (p - buf));
ca1eb8
+        if (pn != NULL) {
ca1eb8
+            len = pn - p;
ca1eb8
+        } else {
ca1eb8
+            len = buf_size - (p - buf);
ca1eb8
+        }
ca1eb8
+        if (len == 0) {
ca1eb8
+            /* empty line no more processing */
ca1eb8
+            break;
ca1eb8
+        }
ca1eb8
+
ca1eb8
+        free(tmp);
ca1eb8
+        tmp = strndup((char *) p, len);
ca1eb8
+        if (tmp == NULL) {
ca1eb8
+            ret = ENOMEM;
ca1eb8
+            goto done;
ca1eb8
+        }
ca1eb8
+
ca1eb8
+        port_str = strrchr(tmp, ':');
ca1eb8
+        if (port_str == NULL) {
ca1eb8
+            port = 0;
ca1eb8
+        } else {
ca1eb8
+            *port_str = '\0';
ca1eb8
+            ++port_str;
ca1eb8
+
ca1eb8
+            if (isdigit(*port_str)) {
ca1eb8
+                errno = 0;
ca1eb8
+                port = strtol(port_str, &endptr, 10);
ca1eb8
+                if (errno != 0) {
ca1eb8
+                    ret = errno;
ca1eb8
+                    PLUGIN_DEBUG(("strtol failed on [%s]: [%d][%s], "
ca1eb8
+                                "assuming default.\n", port_str, ret,
ca1eb8
+                                                       strerror(ret)));
ca1eb8
+                    port = 0;
ca1eb8
+                }
ca1eb8
+                if (*endptr != '\0') {
ca1eb8
+                    PLUGIN_DEBUG(("Found additional characters [%s] in port "
ca1eb8
+                                "number [%s], assuming default.\n", endptr,
ca1eb8
+                                                                    port_str));
ca1eb8
+                    port = 0;
ca1eb8
+                }
ca1eb8
+
ca1eb8
+                if (port < 0 || port > 65535) {
ca1eb8
+                    PLUGIN_DEBUG(("Illegal port number [%ld], assuming "
ca1eb8
+                                  "default.\n", port));
ca1eb8
+                    port = 0;
ca1eb8
+                }
ca1eb8
+            } else {
ca1eb8
+                PLUGIN_DEBUG(("Illegal port number [%s], assuming default.\n",
ca1eb8
+                            port_str));
ca1eb8
+                port = 0;
ca1eb8
+            }
ca1eb8
+        }
ca1eb8
+
ca1eb8
+        PLUGIN_DEBUG(("Found [%s][%d].\n", tmp, port));
ca1eb8
+
ca1eb8
+        l[c].addr = strdup(tmp);
ca1eb8
+        if (l[c].addr == NULL) {
ca1eb8
+            ret = ENOMEM;
ca1eb8
+            goto done;
ca1eb8
+        }
ca1eb8
+        l[c].port = port;
ca1eb8
+
ca1eb8
+        c++;
ca1eb8
+        p = pn == NULL ? NULL : (pn + 1);
ca1eb8
+    } while (p != NULL);
ca1eb8
+
ca1eb8
+    ret = EOK;
ca1eb8
+
ca1eb8
+done:
ca1eb8
+    free(tmp);
ca1eb8
+    if (ret != EOK) {
ca1eb8
+        free_addr_port_list(&l);
ca1eb8
+    } else {
ca1eb8
+        *list = l;
ca1eb8
+    }
ca1eb8
+
ca1eb8
+    return ret;
ca1eb8
+}
ca1eb8
+
ca1eb8
 static int get_krb5info(const char *realm, struct sssd_ctx *ctx,
ca1eb8
                         enum locate_service_type svc)
ca1eb8
 {
ca1eb8
@@ -91,9 +274,6 @@ static int get_krb5info(const char *realm, struct sssd_ctx *ctx,
ca1eb8
     uint8_t buf[BUFSIZE + 1];
ca1eb8
     int fd = -1;
ca1eb8
     const char *name_tmpl = NULL;
ca1eb8
-    char *port_str;
ca1eb8
-    long port;
ca1eb8
-    char *endptr;
ca1eb8
 
ca1eb8
     switch (svc) {
ca1eb8
         case locate_service_kdc:
ca1eb8
@@ -148,62 +328,21 @@ static int get_krb5info(const char *realm, struct sssd_ctx *ctx,
ca1eb8
         PLUGIN_DEBUG(("Content of krb5info file [%s] is [%d] or larger.\n",
ca1eb8
                       krb5info_name, BUFSIZE));
ca1eb8
     }
ca1eb8
-    PLUGIN_DEBUG(("Found [%s] in [%s].\n", buf, krb5info_name));
ca1eb8
-
ca1eb8
-    port_str = strrchr((char *) buf, ':');
ca1eb8
-    if (port_str == NULL) {
ca1eb8
-        port = 0;
ca1eb8
-    } else {
ca1eb8
-        *port_str = '\0';
ca1eb8
-        ++port_str;
ca1eb8
-
ca1eb8
-        if (isdigit(*port_str)) {
ca1eb8
-            errno = 0;
ca1eb8
-            port = strtol(port_str, &endptr, 10);
ca1eb8
-            if (errno != 0) {
ca1eb8
-                ret = errno;
ca1eb8
-                PLUGIN_DEBUG(("strtol failed on [%s]: [%d][%s], "
ca1eb8
-                            "assuming default.\n", port_str, ret, strerror(ret)));
ca1eb8
-                port = 0;
ca1eb8
-            }
ca1eb8
-            if (*endptr != '\0') {
ca1eb8
-                PLUGIN_DEBUG(("Found additional characters [%s] in port number "
ca1eb8
-                            "[%s], assuming default.\n", endptr, port_str));
ca1eb8
-                port = 0;
ca1eb8
-            }
ca1eb8
-
ca1eb8
-            if (port < 0 || port > 65535) {
ca1eb8
-                PLUGIN_DEBUG(("Illegal port number [%ld], assuming default.\n",
ca1eb8
-                            port));
ca1eb8
-                port = 0;
ca1eb8
-            }
ca1eb8
-        } else {
ca1eb8
-            PLUGIN_DEBUG(("Illegal port number [%s], assuming default.\n",
ca1eb8
-                        port_str));
ca1eb8
-            port = 0;
ca1eb8
-        }
ca1eb8
-    }
ca1eb8
 
ca1eb8
     switch (svc) {
ca1eb8
         case locate_service_kdc:
ca1eb8
-            free(ctx->kdc_addr);
ca1eb8
-            ctx->kdc_addr = strdup((char *) buf);
ca1eb8
-            if (ctx->kdc_addr == NULL) {
ca1eb8
-                PLUGIN_DEBUG(("strdup failed.\n"));
ca1eb8
-                ret = ENOMEM;
ca1eb8
+            free_addr_port_list(&(ctx->kdc_addr));
ca1eb8
+            ret = buf_to_addr_port_list(ctx, buf, len, &(ctx->kdc_addr));
ca1eb8
+            if (ret != EOK) {
ca1eb8
                 goto done;
ca1eb8
             }
ca1eb8
-            ctx->kdc_port = (uint16_t) port;
ca1eb8
             break;
ca1eb8
         case locate_service_kpasswd:
ca1eb8
-            free(ctx->kpasswd_addr);
ca1eb8
-            ctx->kpasswd_addr = strdup((char *) buf);
ca1eb8
-            if (ctx->kpasswd_addr == NULL) {
ca1eb8
-                PLUGIN_DEBUG(("strdup failed.\n"));
ca1eb8
-                ret = ENOMEM;
ca1eb8
+            free_addr_port_list(&(ctx->kpasswd_addr));
ca1eb8
+            ret = buf_to_addr_port_list(ctx, buf, len, &(ctx->kpasswd_addr));
ca1eb8
+            if (ret != EOK) {
ca1eb8
                 goto done;
ca1eb8
             }
ca1eb8
-            ctx->kpasswd_port = (uint16_t) port;
ca1eb8
             break;
ca1eb8
         default:
ca1eb8
             PLUGIN_DEBUG(("Unsupported service [%d].\n", svc));
ca1eb8
@@ -256,8 +395,8 @@ void sssd_krb5_locator_close(void *private_data)
ca1eb8
     ctx = (struct sssd_ctx *) private_data;
ca1eb8
     PLUGIN_DEBUG(("sssd_krb5_locator_close called\n"));
ca1eb8
 
ca1eb8
-    free(ctx->kdc_addr);
ca1eb8
-    free(ctx->kpasswd_addr);
ca1eb8
+    free_addr_port_list(&(ctx->kdc_addr));
ca1eb8
+    free_addr_port_list(&(ctx->kpasswd_addr));
ca1eb8
     free(ctx->sssd_realm);
ca1eb8
     free(ctx);
ca1eb8
 
ca1eb8
@@ -277,8 +416,10 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data,
ca1eb8
     struct sssd_ctx *ctx;
ca1eb8
     struct addrinfo ai_hints;
ca1eb8
     uint16_t port = 0;
ca1eb8
-    const char *addr = NULL;
ca1eb8
+    uint16_t default_port = 0;
ca1eb8
+    struct addr_port *addr = NULL;
ca1eb8
     char port_str[PORT_STR_SIZE];
ca1eb8
+    size_t c;
ca1eb8
 
ca1eb8
     if (private_data == NULL) return KRB5_PLUGIN_NO_HANDLE;
ca1eb8
     ctx = (struct sssd_ctx *) private_data;
ca1eb8
@@ -308,9 +449,13 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data,
ca1eb8
             if (ret != EOK) {
ca1eb8
                 PLUGIN_DEBUG(("reading kpasswd address failed, "
ca1eb8
                               "using kdc address.\n"));
ca1eb8
-                free(ctx->kpasswd_addr);
ca1eb8
-                ctx->kpasswd_addr = strdup(ctx->kdc_addr);
ca1eb8
-                ctx->kpasswd_port = 0;
ca1eb8
+                free_addr_port_list(&(ctx->kpasswd_addr));
ca1eb8
+                ret = copy_addr_port_list(ctx->kdc_addr, true,
ca1eb8
+                                          &(ctx->kpasswd_addr));
ca1eb8
+                if (ret != EOK) {
ca1eb8
+                    PLUGIN_DEBUG(("copying address list failed.\n"));
ca1eb8
+                    return KRB5_PLUGIN_NO_HANDLE;
ca1eb8
+                }
ca1eb8
             }
ca1eb8
         }
ca1eb8
     }
ca1eb8
@@ -322,19 +467,19 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data,
ca1eb8
     switch (svc) {
ca1eb8
         case locate_service_kdc:
ca1eb8
             addr = ctx->kdc_addr;
ca1eb8
-            port = ctx->kdc_port ? ctx->kdc_port : DEFAULT_KERBEROS_PORT;
ca1eb8
+            default_port = DEFAULT_KERBEROS_PORT;
ca1eb8
             break;
ca1eb8
         case locate_service_master_kdc:
ca1eb8
             addr = ctx->kpasswd_addr;
ca1eb8
-            port = DEFAULT_KERBEROS_PORT;
ca1eb8
+            default_port = DEFAULT_KERBEROS_PORT;
ca1eb8
             break;
ca1eb8
         case locate_service_kadmin:
ca1eb8
             addr = ctx->kpasswd_addr;
ca1eb8
-            port = DEFAULT_KADMIN_PORT;
ca1eb8
+            default_port = DEFAULT_KADMIN_PORT;
ca1eb8
             break;
ca1eb8
         case locate_service_kpasswd:
ca1eb8
             addr = ctx->kpasswd_addr;
ca1eb8
-            port = ctx->kpasswd_port ? ctx->kpasswd_port : DEFAULT_KPASSWD_PORT;
ca1eb8
+            default_port = DEFAULT_KPASSWD_PORT;
ca1eb8
             break;
ca1eb8
         case locate_service_krb524:
ca1eb8
             return KRB5_PLUGIN_NO_HANDLE;
ca1eb8
@@ -362,46 +507,49 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data,
ca1eb8
     if (strcmp(realm, ctx->sssd_realm) != 0)
ca1eb8
         return KRB5_PLUGIN_NO_HANDLE;
ca1eb8
 
ca1eb8
-    memset(port_str, 0, PORT_STR_SIZE);
ca1eb8
-    ret = snprintf(port_str, PORT_STR_SIZE-1, "%u", port);
ca1eb8
-    if (ret < 0 || ret >= (PORT_STR_SIZE-1)) {
ca1eb8
-        PLUGIN_DEBUG(("snprintf failed.\n"));
ca1eb8
-        return KRB5_PLUGIN_NO_HANDLE;
ca1eb8
-    }
ca1eb8
-
ca1eb8
-    memset(&ai_hints, 0, sizeof(struct addrinfo));
ca1eb8
-    ai_hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV;
ca1eb8
-    ai_hints.ai_socktype = socktype;
ca1eb8
-
ca1eb8
-    ret = getaddrinfo(addr, port_str, &ai_hints, &ai;;
ca1eb8
-    if (ret != 0) {
ca1eb8
-        PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", ret,
ca1eb8
-                                                        gai_strerror(ret)));
ca1eb8
-        if (ret == EAI_SYSTEM) {
ca1eb8
-            PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", errno,
ca1eb8
-                                                            strerror(errno)));
ca1eb8
+    for (c = 0; addr[c].addr != NULL; c++) {
ca1eb8
+        port = (addr[c].port == 0 ? default_port : addr[c].port);
ca1eb8
+        memset(port_str, 0, PORT_STR_SIZE);
ca1eb8
+        ret = snprintf(port_str, PORT_STR_SIZE-1, "%u", port);
ca1eb8
+        if (ret < 0 || ret >= (PORT_STR_SIZE-1)) {
ca1eb8
+            PLUGIN_DEBUG(("snprintf failed.\n"));
ca1eb8
+            return KRB5_PLUGIN_NO_HANDLE;
ca1eb8
         }
ca1eb8
-        return KRB5_PLUGIN_NO_HANDLE;
ca1eb8
-    }
ca1eb8
 
ca1eb8
-    PLUGIN_DEBUG(("addr[%s:%s] family[%d] socktype[%d]\n", addr, port_str,
ca1eb8
-                 ai->ai_family, ai->ai_socktype));
ca1eb8
+        memset(&ai_hints, 0, sizeof(struct addrinfo));
ca1eb8
+        ai_hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV;
ca1eb8
+        ai_hints.ai_socktype = socktype;
ca1eb8
 
ca1eb8
-    if ((family == AF_UNSPEC || ai->ai_family == family) &&
ca1eb8
-        ai->ai_socktype == socktype) {
ca1eb8
-
ca1eb8
-        ret = cbfunc(cbdata, socktype, ai->ai_addr);
ca1eb8
+        ret = getaddrinfo(addr[c].addr, port_str, &ai_hints, &ai;;
ca1eb8
         if (ret != 0) {
ca1eb8
-            PLUGIN_DEBUG(("cbfunc failed\n"));
ca1eb8
-            freeaddrinfo(ai);
ca1eb8
-            return ret;
ca1eb8
+            PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", ret,
ca1eb8
+                                                            gai_strerror(ret)));
ca1eb8
+            if (ret == EAI_SYSTEM) {
ca1eb8
+                PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n",
ca1eb8
+                              errno, strerror(errno)));
ca1eb8
+            }
ca1eb8
+            return KRB5_PLUGIN_NO_HANDLE;
ca1eb8
+        }
ca1eb8
+
ca1eb8
+        PLUGIN_DEBUG(("addr[%s:%s] family[%d] socktype[%d]\n", addr[c].addr,
ca1eb8
+                     port_str, ai->ai_family, ai->ai_socktype));
ca1eb8
+
ca1eb8
+        if ((family == AF_UNSPEC || ai->ai_family == family) &&
ca1eb8
+            ai->ai_socktype == socktype) {
ca1eb8
+
ca1eb8
+            ret = cbfunc(cbdata, socktype, ai->ai_addr);
ca1eb8
+            if (ret != 0) {
ca1eb8
+                PLUGIN_DEBUG(("cbfunc failed\n"));
ca1eb8
+                freeaddrinfo(ai);
ca1eb8
+                return ret;
ca1eb8
+            } else {
ca1eb8
+                PLUGIN_DEBUG(("[%s] used\n", addr[c].addr));
ca1eb8
+            }
ca1eb8
         } else {
ca1eb8
-            PLUGIN_DEBUG(("[%s] used\n", addr));
ca1eb8
+            PLUGIN_DEBUG(("[%s] NOT used\n", addr[c].addr));
ca1eb8
         }
ca1eb8
-    } else {
ca1eb8
-        PLUGIN_DEBUG(("[%s] NOT used\n", addr));
ca1eb8
+        freeaddrinfo(ai);
ca1eb8
     }
ca1eb8
-    freeaddrinfo(ai);
ca1eb8
 
ca1eb8
     return 0;
ca1eb8
 }
ca1eb8
-- 
ca1eb8
2.17.1
ca1eb8