nwrap: Don't fail if we want to add an existing entry
[samba.git] / lib / nss_wrapper / nss_wrapper.c
index bca1012b8f461459cb536056b2bda5f24a02aa73..f48b9cc7e8d65d1f992a014d8dad80fc2ed833a8 100644 (file)
@@ -52,6 +52,8 @@
 #include <unistd.h>
 #include <ctype.h>
 
+#include <netinet/in.h>
+
 #include <search.h>
 #include <assert.h>
 
@@ -758,16 +760,18 @@ struct nwrap_entdata {
        struct nwrap_vector nwrap_addrdata;
 
        ssize_t aliases_count;
+};
 
-       struct nwrap_entdata *ed_next;
-       struct nwrap_entdata *ed_tail;
+struct nwrap_entlist {
+       struct nwrap_entlist *next;
+       struct nwrap_entdata *ed;
 };
 
 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;
@@ -1562,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");
@@ -1759,7 +1764,7 @@ static void nwrap_files_cache_unload(struct nwrap_cache *nwrap)
        nwrap_lines_unload(nwrap);
 }
 
-static void nwrap_files_cache_reload(struct nwrap_cache *nwrap)
+static bool nwrap_files_cache_reload(struct nwrap_cache *nwrap)
 {
        struct stat st;
        int ret;
@@ -1777,7 +1782,7 @@ reopen:
                                  "Unable to open '%s' readonly %d:%s",
                                  nwrap->path, nwrap->fd,
                                  strerror(errno));
-                       return;
+                       return false;
 
                }
                nwrap->fd = fileno(nwrap->fp);
@@ -1794,7 +1799,7 @@ reopen:
                fclose(nwrap->fp);
                nwrap->fp = NULL;
                nwrap->fd = -1;
-               return;
+               return false;
        }
 
        if (retried == false && st.st_nlink == 0) {
@@ -1814,7 +1819,7 @@ reopen:
                NWRAP_LOG(NWRAP_LOG_TRACE,
                          "st_mtime[%u] hasn't changed, skip reload",
                          (unsigned)st.st_mtime);
-               return;
+               return true;
        }
 
        NWRAP_LOG(NWRAP_LOG_TRACE,
@@ -1830,9 +1835,11 @@ reopen:
        if (!ok) {
                NWRAP_LOG(NWRAP_LOG_ERROR, "Failed to reload %s", nwrap->path);
                nwrap_files_cache_unload(nwrap);
+               return false;
        }
 
        NWRAP_LOG(NWRAP_LOG_TRACE, "Reloaded %s", nwrap->path);
+       return true;
 }
 
 /*
@@ -2541,112 +2548,134 @@ static int nwrap_gr_copy_r(const struct group *src, struct group *dst,
        return 0;
 }
 
-static bool nwrap_add_ai(char *const ip_addr, struct nwrap_entdata *const ed)
+static struct nwrap_entlist *nwrap_entlist_init(struct nwrap_entdata *ed)
 {
-       ENTRY e = {
-               .key = ip_addr,
-               .data = (void *)ed,
-       };
-       ENTRY *p;
+       struct nwrap_entlist *el;
 
-       p = hsearch(e, ENTER);
-       if (p == NULL) {
-               NWRAP_LOG(NWRAP_LOG_DEBUG, "Hash table is full");
-               return false;
+       if (ed == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "entry is NULL, can't create list item");
+               return NULL;
        }
 
-       return true;
-}
+       el = (struct nwrap_entlist *)malloc(sizeof(struct nwrap_entlist));
+       if (el == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "malloc failed");
+               return NULL;
+       }
 
+       el->next = NULL;
+       el->ed = ed;
 
-static bool nwrap_add_hname_add_new(char *const h_name,
-                                   struct nwrap_entdata *const ed)
+       return el;
+}
+
+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");
+               return false;
+       }
+
+       el = nwrap_entlist_init(ed);
+       if (el == NULL) {
+               return false;
+       }
 
        e.key = h_name;
-       e.data = (void *)ed;
-       ed->ed_tail = NULL;
-       ed->ed_next = NULL;
+       e.data = (void *)el;
 
        p = hsearch(e, ENTER);
        if (p == NULL) {
-               NWRAP_LOG(NWRAP_LOG_DEBUG, "Hash table is full!");
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Hash table is full!");
+               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 void nwrap_add_hname_add_to_existing(struct nwrap_entdata *const ed,
-                                           struct nwrap_entdata *const ed_dst)
+static bool nwrap_ed_inventarize_add_to_existing(struct nwrap_entdata *const ed,
+                                                struct nwrap_entlist *const el)
 {
-       if (ed_dst->ed_tail != NULL) {
-               ed_dst->ed_tail->ed_next = ed;
-               if (ed_dst->ed_tail != ed) {
-                       ed_dst->ed_tail = ed;
-                       ed->ed_next = NULL;
+       struct nwrap_entlist *cursor;
+       struct nwrap_entlist *el_new;
+
+       if (el == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "list is NULL, can not add");
+               return false;
+       }
+
+
+       for (cursor = el; cursor->next != NULL; cursor = cursor->next)
+       {
+               if (cursor->ed == ed) {
+                       /* The entry already exists in this list. */
+                       return true;
                }
-       } else {
-               ed_dst->ed_tail = ed;
        }
+
+       if (cursor->ed == ed) {
+               /* The entry already exists in this list. */
+               return true;
+       }
+
+       el_new = nwrap_entlist_init(ed);
+       if (el_new == NULL) {
+               return false;
+       }
+
+       cursor->next = el_new;
+       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;
 
-       assert(ed != NULL);
-       assert(h_name_a != NULL);
-
-       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);
-               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_entdata *ed_dst = (struct nwrap_entdata *)p->data;
+               struct nwrap_entlist *el = (struct nwrap_entlist *)p->data;
 
-               assert(p->data != NULL);
-               NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s found. Add record to list.", h_name_a);
-               nwrap_add_hname_add_to_existing(ed, ed_dst);
+               NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s found. Add record to list.", name);
+               ok = nwrap_ed_inventarize_add_to_existing(ed, el);
        }
 
-       return true;
+       return ok;
 }
 
 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;
 
-       assert(ed != NULL);
-       assert(h_name != NULL);
-
-       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);
-               nwrap_add_hname_add_new(h_name, ed);
-
-               if (ed->ed_tail == NULL) {
-                       ed->ed_tail = ed;
-               }
-       } else {
-               struct nwrap_entdata *ed_dst = (struct nwrap_entdata *)p->data;
-
-               assert(p->data != NULL);
-               NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s found. Add record to list.", h_name);
-               nwrap_add_hname_add_to_existing(ed, ed_dst);
+       ok = nwrap_ed_inventarize(h_name, ed);
+       if (!ok) {
+               return false;
        }
 
        if (ed->ht.h_aliases == NULL) {
@@ -2658,13 +2687,13 @@ static bool nwrap_add_hname(struct nwrap_entdata *const ed)
                char *h_name_alias;
 
                h_name_alias = ed->ht.h_aliases[i];
-               assert(h_name_alias != NULL);
 
                NWRAP_LOG(NWRAP_LOG_DEBUG, "Add alias: %s", h_name_alias);
 
-               if (!nwrap_add_hname_alias(h_name_alias, ed)) {
-                       NWRAP_LOG(NWRAP_LOG_DEBUG,
+               if (!nwrap_ed_inventarize(h_name_alias, ed)) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
                                  "Unable to add alias: %s", h_name_alias);
+                       return false;
                }
        }
 
@@ -2681,6 +2710,7 @@ static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line)
        char *n;
 
        char *ip;
+       bool ok;
 
        struct nwrap_entdata *ed = (struct nwrap_entdata *)
                                   malloc(sizeof(struct nwrap_entdata));
@@ -2738,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++;
@@ -2825,12 +2860,24 @@ 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 */
-       nwrap_add_hname(ed);
-       nwrap_add_ai(ip, ed);
+       ok = nwrap_add_hname(ed);
+       if (!ok) {
+               return false;
+       }
+
+       ok = nwrap_ed_inventarize(ip, ed);
+       if (!ok) {
+               return false;
+       }
 
        nwrap_he->num++;
        return true;
@@ -2841,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;
@@ -2862,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) {
@@ -2907,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) {
@@ -2961,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) {
@@ -3017,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) {
@@ -3043,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) {
@@ -3134,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) {
@@ -3177,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) {
@@ -3231,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) {
@@ -3277,8 +3380,7 @@ static int nwrap_files_gethostbyname(const char *name, int af,
                                     struct hostent *result,
                                     struct nwrap_vector *addr_list)
 {
-       struct nwrap_entdata *ed_head;
-       struct nwrap_entdata *ed_cur;
+       struct nwrap_entlist *el;
        struct hostent *he;
        char *h_name_lower;
        ENTRY e;
@@ -3286,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] == '.') {
@@ -3327,9 +3434,9 @@ static int nwrap_files_gethostbyname(const char *name, int af,
        }
 
        /* Iterate through results */
-       ed_head = (struct nwrap_entdata *)e_p->data;
-       for (ed_cur = ed_head; ed_cur != NULL; ed_cur = ed_cur->ed_next) {
-               he = &(ed_cur->ht);
+       for (el = (struct nwrap_entlist *)e_p->data; el != NULL; el = el->next)
+       {
+               he = &(el->ed->ht);
 
                /* Filter by address familiy if provided */
                if (af != AF_UNSPEC && he->h_addrtype != af) {
@@ -3351,7 +3458,7 @@ static int nwrap_files_gethostbyname(const char *name, int af,
                                  he->h_name);
                        he_found = true;
                }
-               nwrap_vector_merge(addr_list, &ed_cur->nwrap_addrdata);
+               nwrap_vector_merge(addr_list, &el->ed->nwrap_addrdata);
                result->h_addr_list = nwrap_vector_head(addr_list);
        }
 
@@ -3432,25 +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_entdata *ed_head;
-       struct nwrap_entdata *ed_cur;
+       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] == '.') {
@@ -3461,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);
@@ -3472,48 +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);
 
-       ed_head = (struct nwrap_entdata *)e_p->data;
-
-       for (ed_cur = ed_head; ed_cur != NULL; ed_cur = ed_cur->ed_next) {
-               int rc;
+       rc = EAI_NONAME;
+       for (el = (struct nwrap_entlist *)e_p->data; el != NULL; el = el->next)
+       {
+               int rc2;
+               struct addrinfo *ai_new = NULL;
 
-               he = &(ed_cur->ht);
+               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;
        }
 
-       *ai_tail = ai;
-       return ai_head;
+       if (ai_head != NULL) {
+               rc = 0;
+       }
+
+       *ai = ai_head;
+
+       return rc;
 }
 
 static struct hostent *nwrap_files_gethostbyaddr(const void *addr,
@@ -3524,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) {
@@ -3535,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) {
@@ -3599,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) {
@@ -3607,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);
 
@@ -4953,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);
 
@@ -5022,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;
@@ -5035,6 +5181,7 @@ static int nwrap_getaddrinfo(const char *node,
        } addr = {
                .family = AF_UNSPEC,
        };
+       int rc;
 
        if (node == NULL && service == NULL) {
                return EAI_NONAME;
@@ -5098,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;
 
@@ -5129,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;
@@ -5402,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;
 
@@ -5414,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;
 }