nwrap: Don't fail if we want to add an existing entry
[samba.git] / lib / nss_wrapper / nss_wrapper.c
index bcf92a4b1404ed45c2e14b55a23e21c9be6986c0..f48b9cc7e8d65d1f992a014d8dad80fc2ed833a8 100644 (file)
@@ -52,6 +52,8 @@
 #include <unistd.h>
 #include <ctype.h>
 
+#include <netinet/in.h>
+
 #include <search.h>
 #include <assert.h>
 
@@ -768,8 +770,8 @@ struct nwrap_entlist {
 struct nwrap_he {
        struct nwrap_cache *cache;
 
-       struct nwrap_entdata *list;
-       struct nwrap_vector entdata;
+       struct nwrap_vector entries;
+       struct nwrap_vector lists;
 
        int num;
        int idx;
@@ -1564,14 +1566,15 @@ static void nwrap_init(void)
                                  "Error parsing NSS_WRAPPER_MAX_HOSTENTS "
                                  "value or value is too small. "
                                  "Using default value: %lu.",
-                                 max_hostents);
+                                 (unsigned long)max_hostents);
                } else {
                        max_hostents = max_hostents_tmp;
                }
        }
        /* Initialize hash table */
        NWRAP_LOG(NWRAP_LOG_DEBUG,
-                 "Initializing hash table of size %lu items.", max_hostents);
+                 "Initializing hash table of size %lu items.",
+                 (unsigned long)max_hostents);
        if (hcreate(max_hostents) == 0) {
                NWRAP_LOG(NWRAP_LOG_ERROR,
                          "Failed to initialize hash table");
@@ -2567,41 +2570,13 @@ static struct nwrap_entlist *nwrap_entlist_init(struct nwrap_entdata *ed)
        return el;
 }
 
-static bool nwrap_add_ai(char *const ip_addr, struct nwrap_entdata *const ed)
-{
-       ENTRY e;
-       ENTRY *p;
-       struct nwrap_entlist *el;
-
-       if (ip_addr == NULL) {
-               NWRAP_LOG(NWRAP_LOG_ERROR, "ip_addr NULL - can't add");
-               return false;
-       }
-
-       el = nwrap_entlist_init(ed);
-       if (el == NULL) {
-               return false;
-       }
-
-       e.key = ip_addr;
-       e.data = el;
-
-       p = hsearch(e, ENTER);
-       if (p == NULL) {
-               NWRAP_LOG(NWRAP_LOG_ERROR, "Hash table is full");
-               return false;
-       }
-
-       return true;
-}
-
-
-static bool nwrap_add_hname_add_new(char *const h_name,
-                                   struct nwrap_entdata *const ed)
+static bool nwrap_ed_inventarize_add_new(char *const h_name,
+                                        struct nwrap_entdata *const ed)
 {
        ENTRY e;
        ENTRY *p;
        struct nwrap_entlist *el;
+       bool ok;
 
        if (h_name == NULL) {
                NWRAP_LOG(NWRAP_LOG_ERROR, "h_name NULL - can't add");
@@ -2622,11 +2597,18 @@ static bool nwrap_add_hname_add_new(char *const h_name,
                return false;
        }
 
+       ok = nwrap_vector_add_item(&(nwrap_he_global.lists), (void *)el);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Failed to add list entry to vector.");
+               return false;
+       }
+
        return true;
 }
 
-static bool nwrap_add_hname_add_to_existing(struct nwrap_entdata *const ed,
-                                           struct nwrap_entlist *const el)
+static bool nwrap_ed_inventarize_add_to_existing(struct nwrap_entdata *const ed,
+                                                struct nwrap_entlist *const el)
 {
        struct nwrap_entlist *cursor;
        struct nwrap_entlist *el_new;
@@ -2636,21 +2618,22 @@ static bool nwrap_add_hname_add_to_existing(struct nwrap_entdata *const ed,
                return false;
        }
 
-       el_new = nwrap_entlist_init(ed);
-       if (el_new == NULL) {
-               return false;
-       }
 
        for (cursor = el; cursor->next != NULL; cursor = cursor->next)
        {
                if (cursor->ed == ed) {
-                       free(el_new);
-                       return false;
+                       /* The entry already exists in this list. */
+                       return true;
                }
        }
 
        if (cursor->ed == ed) {
-               free(el_new);
+               /* The entry already exists in this list. */
+               return true;
+       }
+
+       el_new = nwrap_entlist_init(ed);
+       if (el_new == NULL) {
                return false;
        }
 
@@ -2658,25 +2641,27 @@ static bool nwrap_add_hname_add_to_existing(struct nwrap_entdata *const ed,
        return true;
 }
 
-static bool nwrap_add_hname_alias(char *const h_name_a,
-                                 struct nwrap_entdata *const ed)
+static bool nwrap_ed_inventarize(char *const name,
+                                struct nwrap_entdata *const ed)
 {
        ENTRY e;
        ENTRY *p;
        bool ok;
 
-       e.key = h_name_a;
+       e.key = name;
        e.data = NULL;
+
        NWRAP_LOG(NWRAP_LOG_DEBUG, "Searching name: %s", e.key);
+
        p = hsearch(e, FIND);
        if (p == NULL) {
-               NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s not found. Adding...", h_name_a);
-               ok = nwrap_add_hname_add_new(h_name_a, ed);
+               NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s not found. Adding...", name);
+               ok = nwrap_ed_inventarize_add_new(name, ed);
        } else {
                struct nwrap_entlist *el = (struct nwrap_entlist *)p->data;
 
-               NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s found. Add record to list.", h_name_a);
-               ok = nwrap_add_hname_add_to_existing(ed, el);
+               NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s found. Add record to list.", name);
+               ok = nwrap_ed_inventarize_add_to_existing(ed, el);
        }
 
        return ok;
@@ -2685,25 +2670,10 @@ static bool nwrap_add_hname_alias(char *const h_name_a,
 static bool nwrap_add_hname(struct nwrap_entdata *const ed)
 {
        char *const h_name = (char *const)(ed->ht.h_name);
-       ENTRY e;
-       ENTRY *p;
        unsigned i;
        bool ok;
 
-       e.key = h_name;
-       e.data = NULL;
-       NWRAP_LOG(NWRAP_LOG_DEBUG, "Searching name: %s", e.key);
-       p = hsearch(e, FIND);
-       if (p == NULL) {
-               NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s not found. Adding...", h_name);
-               ok = nwrap_add_hname_add_new(h_name, ed);
-       } else {
-               struct nwrap_entlist *el = (struct nwrap_entlist *)p->data;
-
-               NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s found. Add record to list.", h_name);
-               ok = nwrap_add_hname_add_to_existing(ed, el);
-       }
-
+       ok = nwrap_ed_inventarize(h_name, ed);
        if (!ok) {
                return false;
        }
@@ -2720,7 +2690,7 @@ static bool nwrap_add_hname(struct nwrap_entdata *const ed)
 
                NWRAP_LOG(NWRAP_LOG_DEBUG, "Add alias: %s", h_name_alias);
 
-               if (!nwrap_add_hname_alias(h_name_alias, ed)) {
+               if (!nwrap_ed_inventarize(h_name_alias, ed)) {
                        NWRAP_LOG(NWRAP_LOG_ERROR,
                                  "Unable to add alias: %s", h_name_alias);
                        return false;
@@ -2798,8 +2768,13 @@ static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line)
        }
        ip = i;
 
-       nwrap_vector_add_item(&(ed->nwrap_addrdata),
-                             (void *const)ed->addr.host_addr);
+       ok = nwrap_vector_add_item(&(ed->nwrap_addrdata),
+                                  (void *const)ed->addr.host_addr);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Unable to add addrdata to vector");
+               free(ed);
+               return false;
+       }
        ed->ht.h_addr_list = nwrap_vector_head(&ed->nwrap_addrdata);
 
        p++;
@@ -2885,7 +2860,12 @@ static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line)
                aliases_count += 1;
        }
 
-       nwrap_vector_add_item(&(nwrap_he->entdata), (void *const)ed);
+       ok = nwrap_vector_add_item(&(nwrap_he->entries), (void *const)ed);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Unable to add entry to vector");
+               free(ed);
+               return false;
+       }
 
        ed->aliases_count = aliases_count;
        /* Inventarize item */
@@ -2894,7 +2874,7 @@ static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line)
                return false;
        }
 
-       ok = nwrap_add_ai(ip, ed);
+       ok = nwrap_ed_inventarize(ip, ed);
        if (!ok) {
                return false;
        }
@@ -2908,16 +2888,30 @@ static void nwrap_he_unload(struct nwrap_cache *nwrap)
        struct nwrap_he *nwrap_he =
                (struct nwrap_he *)nwrap->private_data;
        struct nwrap_entdata *ed;
+       struct nwrap_entlist *el;
        size_t i;
 
-       nwrap_vector_foreach (ed, nwrap_he->entdata, i)
+       nwrap_vector_foreach (ed, nwrap_he->entries, i)
        {
                SAFE_FREE(ed->nwrap_addrdata.items);
                SAFE_FREE(ed->ht.h_aliases);
                SAFE_FREE(ed);
        }
-       SAFE_FREE(nwrap_he->entdata.items);
-       nwrap_he->entdata.count = nwrap_he->entdata.capacity = 0;
+       SAFE_FREE(nwrap_he->entries.items);
+       nwrap_he->entries.count = nwrap_he->entries.capacity = 0;
+
+       nwrap_vector_foreach(el, nwrap_he->lists, i)
+       {
+               while (el != NULL) {
+                       struct nwrap_entlist *el_next;
+
+                       el_next = el->next;
+                       SAFE_FREE(el);
+                       el = el_next;
+               }
+       }
+       SAFE_FREE(nwrap_he->lists.items);
+       nwrap_he->lists.count = nwrap_he->lists.capacity = 0;
 
        nwrap_he->num = 0;
        nwrap_he->idx = 0;
@@ -2929,12 +2923,17 @@ static struct passwd *nwrap_files_getpwnam(struct nwrap_backend *b,
                                           const char *name)
 {
        int i;
+       bool ok;
 
        (void) b; /* unused */
 
        NWRAP_LOG(NWRAP_LOG_DEBUG, "Lookup user %s in files", name);
 
-       nwrap_files_cache_reload(nwrap_pw_global.cache);
+       ok = nwrap_files_cache_reload(nwrap_pw_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading passwd file");
+               return NULL;
+       }
 
        for (i=0; i<nwrap_pw_global.num; i++) {
                if (strcmp(nwrap_pw_global.list[i].pw_name, name) == 0) {
@@ -2974,10 +2973,15 @@ static struct passwd *nwrap_files_getpwuid(struct nwrap_backend *b,
                                           uid_t uid)
 {
        int i;
+       bool ok;
 
        (void) b; /* unused */
 
-       nwrap_files_cache_reload(nwrap_pw_global.cache);
+       ok = nwrap_files_cache_reload(nwrap_pw_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading passwd file");
+               return NULL;
+       }
 
        for (i=0; i<nwrap_pw_global.num; i++) {
                if (nwrap_pw_global.list[i].pw_uid == uid) {
@@ -3028,7 +3032,12 @@ static struct passwd *nwrap_files_getpwent(struct nwrap_backend *b)
        (void) b; /* unused */
 
        if (nwrap_pw_global.idx == 0) {
-               nwrap_files_cache_reload(nwrap_pw_global.cache);
+               bool ok;
+               ok = nwrap_files_cache_reload(nwrap_pw_global.cache);
+               if (!ok) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading passwd file");
+                       return NULL;
+               }
        }
 
        if (nwrap_pw_global.idx >= nwrap_pw_global.num) {
@@ -3084,7 +3093,13 @@ static struct spwd *nwrap_files_getspent(void)
        struct spwd *sp;
 
        if (nwrap_sp_global.idx == 0) {
-               nwrap_files_cache_reload(nwrap_sp_global.cache);
+               bool ok;
+
+               ok = nwrap_files_cache_reload(nwrap_sp_global.cache);
+               if (!ok) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading shadow file");
+                       return NULL;
+               }
        }
 
        if (nwrap_sp_global.idx >= nwrap_sp_global.num) {
@@ -3110,10 +3125,15 @@ static void nwrap_files_endspent(void)
 static struct spwd *nwrap_files_getspnam(const char *name)
 {
        int i;
+       bool ok;
 
        NWRAP_LOG(NWRAP_LOG_DEBUG, "Lookup user %s in files", name);
 
-       nwrap_files_cache_reload(nwrap_sp_global.cache);
+       ok = nwrap_files_cache_reload(nwrap_sp_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading shadow file");
+               return NULL;
+       }
 
        for (i=0; i<nwrap_sp_global.num; i++) {
                if (strcmp(nwrap_sp_global.list[i].sp_namp, name) == 0) {
@@ -3201,10 +3221,15 @@ static struct group *nwrap_files_getgrnam(struct nwrap_backend *b,
                                          const char *name)
 {
        int i;
+       bool ok;
 
        (void) b; /* unused */
 
-       nwrap_files_cache_reload(nwrap_gr_global.cache);
+       ok = nwrap_files_cache_reload(nwrap_gr_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading group file");
+               return NULL;
+       }
 
        for (i=0; i<nwrap_gr_global.num; i++) {
                if (strcmp(nwrap_gr_global.list[i].gr_name, name) == 0) {
@@ -3244,10 +3269,15 @@ static struct group *nwrap_files_getgrgid(struct nwrap_backend *b,
                                          gid_t gid)
 {
        int i;
+       bool ok;
 
        (void) b; /* unused */
 
-       nwrap_files_cache_reload(nwrap_gr_global.cache);
+       ok = nwrap_files_cache_reload(nwrap_gr_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading group file");
+               return NULL;
+       }
 
        for (i=0; i<nwrap_gr_global.num; i++) {
                if (nwrap_gr_global.list[i].gr_gid == gid) {
@@ -3298,7 +3328,13 @@ static struct group *nwrap_files_getgrent(struct nwrap_backend *b)
        (void) b; /* unused */
 
        if (nwrap_gr_global.idx == 0) {
-               nwrap_files_cache_reload(nwrap_gr_global.cache);
+               bool ok;
+
+               ok = nwrap_files_cache_reload(nwrap_gr_global.cache);
+               if (!ok) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading group file");
+                       return NULL;
+               }
        }
 
        if (nwrap_gr_global.idx >= nwrap_gr_global.num) {
@@ -3352,8 +3388,13 @@ static int nwrap_files_gethostbyname(const char *name, int af,
        char canon_name[DNS_NAME_MAX] = { 0 };
        size_t name_len;
        bool he_found = false;
+       bool ok;
 
-       nwrap_files_cache_reload(nwrap_he_global.cache);
+       ok = nwrap_files_cache_reload(nwrap_he_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "error loading hosts file");
+               goto no_ent;
+       }
 
        name_len = strlen(name);
        if (name_len < sizeof(canon_name) && name[name_len - 1] == '.') {
@@ -3498,24 +3539,31 @@ int gethostbyname_r(const char *name,
 }
 #endif
 
-static struct addrinfo *nwrap_files_getaddrinfo(const char *name,
-                                               unsigned short port,
-                                               const struct addrinfo *hints,
-                                               struct addrinfo **ai_tail)
+static int nwrap_files_getaddrinfo(const char *name,
+                                  unsigned short port,
+                                  const struct addrinfo *hints,
+                                  struct addrinfo **ai)
 {
        struct nwrap_entlist *el;
        struct hostent *he;
-       struct addrinfo *ai = NULL;
        struct addrinfo *ai_head = NULL;
-       struct addrinfo *ai_prev = NULL;
+       struct addrinfo *ai_cur = NULL;
        char *h_name_lower;
        size_t name_len;
        char canon_name[DNS_NAME_MAX] = { 0 };
        bool skip_canonname = false;
-       ENTRY e = { 0 };
+       ENTRY e = {
+               .key = NULL,
+       };
        ENTRY *e_p = NULL;
+       int rc;
+       bool ok;
 
-       nwrap_files_cache_reload(nwrap_he_global.cache);
+       ok = nwrap_files_cache_reload(nwrap_he_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "error loading hosts file");
+               return EAI_SYSTEM;
+       }
 
        name_len = strlen(name);
        if (name_len < DNS_NAME_MAX && name[name_len - 1] == '.') {
@@ -3526,7 +3574,7 @@ static struct addrinfo *nwrap_files_getaddrinfo(const char *name,
        if (!str_tolower_copy(&h_name_lower, name)) {
                NWRAP_LOG(NWRAP_LOG_DEBUG,
                          "Out of memory while converting to lower case");
-               return NULL;
+               return EAI_MEMORY;
        }
 
        NWRAP_LOG(NWRAP_LOG_DEBUG, "Searching for name: %s", h_name_lower);
@@ -3537,47 +3585,60 @@ static struct addrinfo *nwrap_files_getaddrinfo(const char *name,
                NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s not found.", h_name_lower);
                SAFE_FREE(h_name_lower);
                errno = ENOENT;
-               return NULL;
+               return EAI_NONAME;
        }
        NWRAP_LOG(NWRAP_LOG_DEBUG, "Name: %s found.", h_name_lower);
        SAFE_FREE(h_name_lower);
 
+       rc = EAI_NONAME;
        for (el = (struct nwrap_entlist *)e_p->data; el != NULL; el = el->next)
        {
-               int rc;
+               int rc2;
+               struct addrinfo *ai_new = NULL;
 
                he = &(el->ed->ht);
 
                if (hints->ai_family != AF_UNSPEC &&
-                   he->h_addrtype != hints->ai_family) {
+                   he->h_addrtype != hints->ai_family)
+               {
+                       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                                 "Entry found but with wrong AF - "
+                                 "remembering EAI_ADDRINFO.");
+                       rc = EAI_ADDRFAMILY;
                        continue;
                }
 
                /* Function allocates memory and returns it in ai. */
-               rc = nwrap_convert_he_ai(he,
+               rc2 = nwrap_convert_he_ai(he,
                                         port,
                                         hints,
-                                        &ai,
+                                        &ai_new,
                                         skip_canonname);
-               if (rc != 0) {
-                       /* FIXME: Investigate if this is nice to do... */
-                       NWRAP_LOG(NWRAP_LOG_ERROR,
-                                 "Error in converting he to ai! Skipping.");
-                       continue;
+               if (rc2 != 0) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR, "Error converting he to ai");
+                       if (ai_head != NULL) {
+                               freeaddrinfo(ai_head);
+                       }
+                       return rc2;
                }
                skip_canonname = true;
 
                if (ai_head == NULL) {
-                       ai_head = ai;
+                       ai_head = ai_new;
                }
-               if (ai_prev != NULL) {
-                       ai_prev->ai_next = ai;
+               if (ai_cur != NULL) {
+                       ai_cur->ai_next = ai_new;
                }
-               ai_prev = ai;
+               ai_cur = ai_new;
+       }
+
+       if (ai_head != NULL) {
+               rc = 0;
        }
 
-       *ai_tail = ai;
-       return ai_head;
+       *ai = ai_head;
+
+       return rc;
 }
 
 static struct hostent *nwrap_files_gethostbyaddr(const void *addr,
@@ -3588,10 +3649,15 @@ static struct hostent *nwrap_files_gethostbyaddr(const void *addr,
        struct nwrap_entdata *ed;
        const char *a;
        size_t i;
+       bool ok;
 
        (void) len; /* unused */
 
-       nwrap_files_cache_reload(nwrap_he_global.cache);
+       ok = nwrap_files_cache_reload(nwrap_he_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "error loading hosts file");
+               return NULL;
+       }
 
        a = inet_ntop(type, addr, ip, sizeof(ip));
        if (a == NULL) {
@@ -3599,7 +3665,7 @@ static struct hostent *nwrap_files_gethostbyaddr(const void *addr,
                return NULL;
        }
 
-       nwrap_vector_foreach(ed, nwrap_he_global.entdata, i)
+       nwrap_vector_foreach(ed, nwrap_he_global.entries, i)
        {
                he = &(ed->ht);
                if (he->h_addrtype != type) {
@@ -3663,7 +3729,13 @@ static struct hostent *nwrap_files_gethostent(void)
        struct hostent *he;
 
        if (nwrap_he_global.idx == 0) {
-               nwrap_files_cache_reload(nwrap_he_global.cache);
+               bool ok;
+
+               ok = nwrap_files_cache_reload(nwrap_he_global.cache);
+               if (!ok) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading hosts file");
+                       return NULL;
+               }
        }
 
        if (nwrap_he_global.idx >= nwrap_he_global.num) {
@@ -3671,7 +3743,7 @@ static struct hostent *nwrap_files_gethostent(void)
                return NULL;
        }
 
-       he = &((struct nwrap_entdata *)nwrap_he_global.entdata.items[nwrap_he_global.idx++])->ht;
+       he = &((struct nwrap_entdata *)nwrap_he_global.entries.items[nwrap_he_global.idx++])->ht;
 
        NWRAP_LOG(NWRAP_LOG_DEBUG, "return hosts[%s]", he->h_name);
 
@@ -5017,12 +5089,23 @@ static int nwrap_convert_he_ai(const struct hostent *he,
                return EAI_MEMORY;
        }
 
-       ai->ai_flags = 0;
+       ai->ai_flags = hints->ai_flags;
        ai->ai_family = he->h_addrtype;
        ai->ai_socktype = hints->ai_socktype;
        ai->ai_protocol = hints->ai_protocol;
        ai->ai_canonname = NULL;
 
+       if (ai->ai_socktype == 0) {
+               ai->ai_socktype = SOCK_DGRAM;
+       }
+       if (ai->ai_protocol == 0) {
+               if (ai->ai_socktype == SOCK_DGRAM) {
+                       ai->ai_protocol = IPPROTO_UDP;
+               } else if (ai->ai_socktype == SOCK_STREAM) {
+                       ai->ai_protocol = IPPROTO_TCP;
+               }
+       }
+
        ai->ai_addrlen = socklen;
        ai->ai_addr = (void *)(ai + 1);
 
@@ -5086,7 +5169,6 @@ static int nwrap_getaddrinfo(const char *node,
                             struct addrinfo **res)
 {
        struct addrinfo *ai = NULL;
-       struct addrinfo *ai_tail;
        unsigned short port = 0;
        struct {
                int family;
@@ -5099,6 +5181,7 @@ static int nwrap_getaddrinfo(const char *node,
        } addr = {
                .family = AF_UNSPEC,
        };
+       int rc;
 
        if (node == NULL && service == NULL) {
                return EAI_NONAME;
@@ -5162,23 +5245,32 @@ static int nwrap_getaddrinfo(const char *node,
        }
 
 valid_port:
-       if (hints->ai_family == AF_UNSPEC || hints->ai_family == AF_INET) {
-               int rc = inet_pton(AF_INET, node, &addr.in.v4);
-               if (rc == 1) {
-                       addr.family = AF_INET;
-               }
+
+       rc = inet_pton(AF_INET, node, &addr.in.v4);
+       if (rc == 1) {
+               addr.family = AF_INET;
        }
 #ifdef HAVE_IPV6
        if (addr.family == AF_UNSPEC) {
-               int rc = inet_pton(AF_INET6, node, &addr.in.v6);
+               rc = inet_pton(AF_INET6, node, &addr.in.v6);
                if (rc == 1) {
                        addr.family = AF_INET6;
                }
        }
 #endif
 
-       ai = nwrap_files_getaddrinfo(node, port, hints, &ai_tail);
-       if (ai == NULL) {
+       if (addr.family == AF_UNSPEC) {
+              if (hints->ai_flags & AI_NUMERICHOST) {
+                       return EAI_NONAME;
+               }
+       } else if ((hints->ai_family != AF_UNSPEC) &&
+                  (hints->ai_family != addr.family))
+       {
+               return EAI_ADDRFAMILY;
+       }
+
+       rc = nwrap_files_getaddrinfo(node, port, hints, &ai);
+       if (rc != 0) {
                int ret;
                struct addrinfo *p = NULL;
 
@@ -5193,63 +5285,58 @@ valid_port:
                        return 0;
                }
 
-               return EAI_SYSTEM;
-       }
-
-       if (ai->ai_flags == 0) {
-               ai->ai_flags = hints->ai_flags;
-       }
-       if (ai->ai_socktype == 0) {
-               ai->ai_socktype = SOCK_DGRAM;
-       }
-       if (ai->ai_protocol == 0 && ai->ai_socktype == SOCK_DGRAM) {
-               ai->ai_protocol = 17; /* UDP */
-       } else if (ai->ai_protocol == 0 && ai->ai_socktype == SOCK_STREAM) {
-               ai->ai_protocol = 6; /* TCP */
+               return rc;
        }
 
+       /*
+        * If the socktype was not specified, duplicate
+        * each ai returned, so that we have variants for
+        * both UDP and TCP.
+        */
        if (hints->ai_socktype == 0) {
-               /* Add second ai */
-               struct addrinfo *ai_head = ai;
-               struct addrinfo *ai_tmp;
-               struct addrinfo *ai_new_tail = ai_tail;
-
-               /* Add at least one more struct */
-               do {
-                       /* CHECKS! */
-                       ai_tmp = malloc(sizeof(struct addrinfo));
-                       memcpy(ai_tmp, ai_head, sizeof(struct addrinfo));
-                       ai_tmp->ai_next = NULL;
+               struct addrinfo *ai_cur;
 
-                       /* We need a deep copy or freeaddrinfo() will blow up */
-                       if (ai_head->ai_canonname != NULL) {
-                               ai_tmp->ai_canonname =
-                                       strdup(ai_head->ai_canonname);
+               /* freeaddrinfo() frees ai_canonname and ai so allocate them */
+               for (ai_cur = ai; ai_cur != NULL; ai_cur = ai_cur->ai_next) {
+                       struct addrinfo *ai_new;
+
+                       /* duplicate the current entry */
+
+                       ai_new = malloc(sizeof(struct addrinfo));
+                       if (ai_new == NULL) {
+                               freeaddrinfo(ai);
+                               return EAI_MEMORY;
                        }
-                       /* ai_head should point inside hints. */
-                       ai_tmp->ai_addr = ai_head->ai_addr;
 
-                       if (ai_head->ai_flags == 0) {
-                               ai_tmp->ai_flags = hints->ai_flags;
+                       memcpy(ai_new, ai_cur, sizeof(struct addrinfo));
+                       ai_new->ai_next = NULL;
+
+                       /* We need a deep copy or freeaddrinfo() will blow up */
+                       if (ai_cur->ai_canonname != NULL) {
+                               ai_new->ai_canonname =
+                                       strdup(ai_cur->ai_canonname);
                        }
-                       if (ai_head->ai_socktype == SOCK_DGRAM) {
-                               ai_tmp->ai_socktype = SOCK_STREAM;
-                       } else if (ai_head->ai_socktype == SOCK_STREAM) {
-                               ai_tmp->ai_socktype = SOCK_DGRAM;
+
+                       if (ai_cur->ai_socktype == SOCK_DGRAM) {
+                               ai_new->ai_socktype = SOCK_STREAM;
+                       } else if (ai_cur->ai_socktype == SOCK_STREAM) {
+                               ai_new->ai_socktype = SOCK_DGRAM;
                        }
-                       if (ai_head->ai_socktype == SOCK_DGRAM) {
-                               ai_tmp->ai_protocol = 17; /* UDP */
-                       } else if (ai_head->ai_socktype == SOCK_STREAM) {
-                               ai_tmp->ai_protocol = 6; /* TCP */
+                       if (ai_cur->ai_protocol == IPPROTO_TCP) {
+                               ai_new->ai_protocol = IPPROTO_UDP;
+                       } else if (ai_cur->ai_protocol == IPPROTO_UDP) {
+                               ai_new->ai_protocol = IPPROTO_TCP;
                        }
-                       ai_new_tail->ai_next = ai_tmp;
-                       ai_new_tail = ai_tmp;
 
-                       if (ai_head == ai_tail) {
-                               break;
-                       }
-                       ai_head = ai_head->ai_next;
-               } while (1);
+                       /* now insert the new entry */
+
+                       ai_new->ai_next = ai_cur->ai_next;
+                       ai_cur->ai_next = ai_new;
+
+                       /* and move on (don't duplicate the new entry) */
+
+                       ai_cur = ai_new;
+               }
        }
 
        *res = ai;
@@ -5466,6 +5553,20 @@ void nwrap_destructor(void)
                nwrap_pw_global.num = 0;
        }
 
+#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM)
+       if (nwrap_sp_global.cache != NULL) {
+               struct nwrap_cache *c = nwrap_sp_global.cache;
+
+               nwrap_files_cache_unload(c);
+               if (c->fd >= 0) {
+                       fclose(c->fp);
+                       c->fd = -1;
+               }
+
+               nwrap_sp_global.num = 0;
+       }
+#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */
+
        if (nwrap_he_global.cache != NULL) {
                struct nwrap_cache *c = nwrap_he_global.cache;
 
@@ -5478,6 +5579,11 @@ void nwrap_destructor(void)
                nwrap_he_global.num = 0;
        }
 
+       free(user_addrlist.items);
+#ifdef HAVE_GETHOSTBYNAME2
+       free(user_addrlist2.items);
+#endif
+
        hdestroy();
        NWRAP_UNLOCK_ALL;
 }