nwrap: rename nwrap_he.entdata to nwrap_he.entries
[obnox/samba/samba-obnox.git] / lib / nss_wrapper / nss_wrapper.c
index 3496162aad69b2f78ce576d12def8fcf03b68a9c..4bde91ca3ac8926a80c8119422c4cd10112b1260 100644 (file)
@@ -52,6 +52,8 @@
 #include <unistd.h>
 #include <ctype.h>
 
+#include <netinet/in.h>
+
 #include <search.h>
 #include <assert.h>
 
@@ -604,6 +606,8 @@ struct nwrap_vector {
             item != NULL; \
             (item) = (vect).items[++iter])
 
+#define nwrap_vector_is_initialized(vector) ((vector)->items != NULL)
+
 static inline bool nwrap_vector_init(struct nwrap_vector *const vector)
 {
        if (vector == NULL) {
@@ -756,16 +760,17 @@ 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;
 
        int num;
        int idx;
@@ -1560,14 +1565,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");
@@ -1757,7 +1763,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;
@@ -1775,7 +1781,7 @@ reopen:
                                  "Unable to open '%s' readonly %d:%s",
                                  nwrap->path, nwrap->fd,
                                  strerror(errno));
-                       return;
+                       return false;
 
                }
                nwrap->fd = fileno(nwrap->fp);
@@ -1792,7 +1798,7 @@ reopen:
                fclose(nwrap->fp);
                nwrap->fp = NULL;
                nwrap->fd = -1;
-               return;
+               return false;
        }
 
        if (retried == false && st.st_nlink == 0) {
@@ -1812,7 +1818,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,
@@ -1828,9 +1834,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;
 }
 
 /*
@@ -2539,141 +2547,142 @@ 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;
 
+       return el;
+}
 
-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)
 {
-       /* No element found.. inventarize new item */
        ENTRY e;
        ENTRY *p;
+       struct nwrap_entlist *el;
+
+       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;
        }
 
        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) {
+                       return false;
                }
-       } else {
-               ed_dst->ed_tail = ed;
        }
+
+       if (cursor->ed == ed) {
+               return false;
+       }
+
+       el_new = nwrap_entlist_init(ed);
+       if (el_new == NULL) {
+               return false;
+       }
+
+       cursor->next = el_new;
+       return true;
 }
 
-static bool nwrap_add_hname_alias(const char *const h_name_a,
-                                 struct nwrap_entdata *const ed)
+static bool nwrap_ed_inventarize(char *const name,
+                                struct nwrap_entdata *const ed)
 {
-       /* One of argument 'h_hame_a' are "optional" */
-       char *const h_name = (char *const) ((h_name_a == NULL) ? ed->ht.h_name : h_name_a);
        ENTRY e;
        ENTRY *p;
+       bool ok;
 
-       /* Maybe it's little bit late ... */
-       assert(ed != NULL);
-       assert(h_name != NULL);
-
-       e.key = h_name;
+       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);
-               /* Just add alias and don't mess with metadata */
-               nwrap_add_hname_add_new(h_name, ed);
+               NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s not found. Adding...", name);
+               ok = nwrap_ed_inventarize_add_new(name, ed);
        } else {
-               /* Element found. Add them to end of list */
-               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);
-               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(const char *const h_name_a,
-                           struct nwrap_entdata *const ed)
+static bool nwrap_add_hname(struct nwrap_entdata *const ed)
 {
-       /* One of argument 'h_hame_a' are "optional" */
-       char *const h_name = (char *const) ((h_name_a == NULL) ? ed->ht.h_name : h_name_a);
-       ENTRY e;
-       ENTRY *p;
-       char *h_name_alias;
+       char *const h_name = (char *const)(ed->ht.h_name);
        unsigned i;
+       bool ok;
 
-       /* Maybe it's little bit late ... */
-       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);
-               /* Just add alias and don't mess with metadata */
-               nwrap_add_hname_add_new(h_name, ed);
-
-               if (ed->ed_tail == NULL) {
-                       ed->ed_tail = ed;
-               }
-       } else {
-               /* Element found. Add them to end of list */
-               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;
        }
 
-       /* Return true when list of aliases is empty */
        if (ed->ht.h_aliases == NULL) {
                return true;
        }
 
        /* Itemize aliases */
        for (i = 0; ed->ht.h_aliases[i] != NULL; ++i) {
+               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;
                }
        }
 
@@ -2690,6 +2699,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));
@@ -2834,12 +2844,19 @@ 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);
+       nwrap_vector_add_item(&(nwrap_he->entries), (void *const)ed);
 
        ed->aliases_count = aliases_count;
        /* Inventarize item */
-       nwrap_add_hname(NULL, 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;
@@ -2852,14 +2869,14 @@ static void nwrap_he_unload(struct nwrap_cache *nwrap)
        struct nwrap_entdata *ed;
        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_he->num = 0;
        nwrap_he->idx = 0;
@@ -2871,12 +2888,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) {
@@ -2916,10 +2938,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) {
@@ -2970,7 +2997,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) {
@@ -3026,7 +3058,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) {
@@ -3052,10 +3090,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) {
@@ -3143,10 +3186,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) {
@@ -3186,10 +3234,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) {
@@ -3240,7 +3293,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) {
@@ -3286,8 +3345,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;
@@ -3295,8 +3353,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] == '.') {
@@ -3323,16 +3386,22 @@ static int nwrap_files_gethostbyname(const char *name, int af,
        SAFE_FREE(h_name_lower);
 
        /* Always cleanup vector and results */
-       if (!nwrap_vector_init(addr_list)) {
-               NWRAP_LOG(NWRAP_LOG_DEBUG,
-                         "Unable to initialize memory for addr_list vector");
-               goto no_ent;
+       if (!nwrap_vector_is_initialized(addr_list)) {
+               if (!nwrap_vector_init(addr_list)) {
+                       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                                 "Unable to initialize memory for addr_list vector");
+                       goto no_ent;
+               }
+       } else {
+               /* When vector is initialized data are valid no more.
+                * Quick way how to free vector is: */
+               addr_list->count = 0;
        }
 
        /* 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) {
@@ -3354,7 +3423,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);
        }
 
@@ -3398,7 +3467,21 @@ static int nwrap_gethostbyname_r(const char *name,
                return -1;
        }
 
-       memset(buf, '\0', buflen);
+       if (buflen < (addr_list->count * sizeof(void *))) {
+               SAFE_FREE(addr_list->items);
+               SAFE_FREE(addr_list);
+               return ERANGE;
+       }
+
+       /* Copy all to user provided buffer and change
+        * pointers in returned structure.
+        * +1 is for ending NULL pointer. */
+       memcpy(buf, addr_list->items, (addr_list->count + 1) * sizeof(void *));
+
+       free(addr_list->items);
+       free(addr_list);
+
+       ret->h_addr_list = (char **)buf;
        *result = ret;
        return 0;
 }
@@ -3421,25 +3504,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;
-       ENTRY *e_p;
+       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] == '.') {
@@ -3450,7 +3539,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);
@@ -3461,48 +3550,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,
@@ -3513,10 +3614,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) {
@@ -3524,7 +3630,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) {
@@ -3588,7 +3694,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) {
@@ -3596,7 +3708,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);
 
@@ -4942,12 +5054,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);
 
@@ -5011,8 +5134,6 @@ static int nwrap_getaddrinfo(const char *node,
                             struct addrinfo **res)
 {
        struct addrinfo *ai = NULL;
-       struct addrinfo *p = NULL;
-       struct addrinfo *ai_tail;
        unsigned short port = 0;
        struct {
                int family;
@@ -5025,8 +5146,6 @@ static int nwrap_getaddrinfo(const char *node,
        } addr = {
                .family = AF_UNSPEC,
        };
-       int eai = EAI_SYSTEM;
-       int ret;
        int rc;
 
        if (node == NULL && service == NULL) {
@@ -5045,140 +5164,144 @@ static int nwrap_getaddrinfo(const char *node,
                return EAI_BADFLAGS;
        }
 
-       ret = libc_getaddrinfo(node, service, hints, &p);
-       if (ret == 0) {
-               *res = p;
-       }
        /* If no node has been specified, let glibc deal with it */
        if (node == NULL) {
+               int ret;
+               struct addrinfo *p = NULL;
+
+               ret = libc_getaddrinfo(node, service, hints, &p);
+
+               if (ret == 0) {
+                       *res = p;
+               }
                return ret;
        }
 
        if (service != NULL && service[0] != '\0') {
-               if (isdigit((int)service[0])) {
-                       port = (unsigned short)atoi(service);
-               } else {
-                       const char *proto = NULL;
-                       struct servent *s;
+               const char *proto = NULL;
+               struct servent *s;
+               char *end_ptr;
+               long sl;
 
-                       if (hints->ai_protocol != 0) {
-                               struct protoent *pent;
+               errno = 0;
+               sl = strtol(service, &end_ptr, 10);
 
-                               pent = getprotobynumber(hints->ai_protocol);
-                               if (pent != NULL) {
-                                       proto = pent->p_name;
-                               }
-                       }
+               if (*end_ptr == '\0') {
+                       port = sl;
+                       goto valid_port;
+               } else if (hints->ai_flags & AI_NUMERICSERV) {
+                       return EAI_NONAME;
+               }
 
-                       s = getservbyname(service, proto);
-                       if (s != NULL) {
-                               port = ntohs(s->s_port);
-                       } else {
-                               if (p != NULL) {
-                                       freeaddrinfo(p);
-                               }
-                               return EAI_SERVICE;
+               if (hints->ai_protocol != 0) {
+                       struct protoent *pent;
+
+                       pent = getprotobynumber(hints->ai_protocol);
+                       if (pent != NULL) {
+                               proto = pent->p_name;
                        }
                }
-       }
 
-       rc = 0;
-       if (hints->ai_family == AF_UNSPEC || hints->ai_family == AF_INET) {
-               rc = inet_pton(AF_INET, node, &addr.in.v4);
+               s = getservbyname(service, proto);
+               if (s == NULL) {
+                       return EAI_NONAME;
+               }
+               port = ntohs(s->s_port);
        }
+
+valid_port:
+
+       rc = inet_pton(AF_INET, node, &addr.in.v4);
        if (rc == 1) {
                addr.family = AF_INET;
+       }
 #ifdef HAVE_IPV6
-       } else {
+       if (addr.family == AF_UNSPEC) {
                rc = inet_pton(AF_INET6, node, &addr.in.v6);
                if (rc == 1) {
                        addr.family = AF_INET6;
                }
-#endif
        }
-
-       rc = -1;
-       if (addr.family == AF_INET) {
-               ai = nwrap_files_getaddrinfo(node, port, hints, &ai_tail);
-               if (ai != NULL) {
-                       rc = 1;
-               }
-#ifdef HAVE_IPV6
-       } else if (addr.family == AF_INET6) {
-               ai = nwrap_files_getaddrinfo(node, port, hints, &ai_tail);
-               if (ai != NULL) {
-                       rc = 1;
-               }
 #endif
-       } else {
-               ai = nwrap_files_getaddrinfo(node, port, hints, &ai_tail);
-               if (ai != NULL) {
-                       rc = 1;
+
+       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;
        }
 
-       if (rc < 0) {
-               return ret == 0 ? 0 : eai;
-       }
+       rc = nwrap_files_getaddrinfo(node, port, hints, &ai);
+       if (rc != 0) {
+               int ret;
+               struct addrinfo *p = NULL;
 
-       if (ret == 0) {
-               freeaddrinfo(p);
-       }
+               ret = libc_getaddrinfo(node, service, hints, &p);
 
-       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 */
+               if (ret == 0) {
+                       /*
+                        * nwrap_files_getaddrinfo failed, but libc was
+                        * successful -- use the result from libc.
+                        */
+                       *res = p;
+                       return 0;
+               }
+
+               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;