nwrap: catch NULL list in nwrap_add_hname_add_to_existing
[kai/samba-autobuild/.git] / lib / nss_wrapper / nss_wrapper.c
index 94cc4cf7e4f9381d1c2f71ba79915f99978a6d5b..064d3d7872ad8dcc4e52cd8c8fb279e744362f62 100644 (file)
@@ -35,6 +35,8 @@
 
 #include "config.h"
 
+#include <pthread.h>
+
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
@@ -50,6 +52,7 @@
 #include <unistd.h>
 #include <ctype.h>
 
+#include <search.h>
 #include <assert.h>
 
 /*
@@ -155,6 +158,62 @@ typedef nss_status_t NSS_STATUS;
 #define NWRAP_INET_ADDRSTRLEN INET_ADDRSTRLEN
 #endif
 
+#define NWRAP_LOCK(m) do { \
+       pthread_mutex_lock(&( m ## _mutex)); \
+} while(0)
+
+#define NWRAP_UNLOCK(m) do { \
+       pthread_mutex_unlock(&( m ## _mutex)); \
+} while(0)
+
+
+static bool nwrap_initialized = false;
+static pthread_mutex_t nwrap_initialized_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* The mutex or accessing the id */
+static pthread_mutex_t nwrap_global_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t nwrap_gr_global_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t nwrap_he_global_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t nwrap_pw_global_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t nwrap_sp_global_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Add new global locks here please */
+/* Also don't forget to add locks to
+ * nwrap_init() function.
+ */
+# define NWRAP_LOCK_ALL do { \
+       NWRAP_LOCK(nwrap_initialized); \
+       NWRAP_LOCK(nwrap_global); \
+       NWRAP_LOCK(nwrap_gr_global); \
+       NWRAP_LOCK(nwrap_he_global); \
+       NWRAP_LOCK(nwrap_pw_global); \
+       NWRAP_LOCK(nwrap_sp_global); \
+} while (0);
+
+# define NWRAP_UNLOCK_ALL do {\
+       NWRAP_UNLOCK(nwrap_sp_global); \
+       NWRAP_UNLOCK(nwrap_pw_global); \
+       NWRAP_UNLOCK(nwrap_he_global); \
+       NWRAP_UNLOCK(nwrap_gr_global); \
+       NWRAP_UNLOCK(nwrap_global); \
+       NWRAP_UNLOCK(nwrap_initialized); \
+} while (0);
+
+static void nwrap_thread_prepare(void)
+{
+       NWRAP_LOCK_ALL;
+}
+
+static void nwrap_thread_parent(void)
+{
+       NWRAP_UNLOCK_ALL;
+}
+
+static void nwrap_thread_child(void)
+{
+       NWRAP_UNLOCK_ALL;
+}
+
 enum nwrap_dbglvl_e {
        NWRAP_LOG_ERROR = 0,
        NWRAP_LOG_WARN,
@@ -476,8 +535,17 @@ struct nwrap_main {
        struct nwrap_libc *libc;
 };
 
-struct nwrap_main *nwrap_main_global;
-struct nwrap_main __nwrap_main_global;
+static struct nwrap_main *nwrap_main_global;
+static struct nwrap_main __nwrap_main_global;
+
+/*
+ * PROTOTYPES
+ */
+static int nwrap_convert_he_ai(const struct hostent *he,
+                              unsigned short port,
+                              const struct addrinfo *hints,
+                              struct addrinfo **pai,
+                              bool skip_canonname);
 
 /*
  * VECTORS
@@ -536,6 +604,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) {
@@ -554,32 +624,32 @@ static inline bool nwrap_vector_init(struct nwrap_vector *const vector)
        return true;
 }
 
-static bool nwrap_vector_add_item(struct nwrap_vector *cont, void *const item)
+static bool nwrap_vector_add_item(struct nwrap_vector *vector, void *const item)
 {
-       assert (cont != NULL);
+       assert (vector != NULL);
 
-       if (cont->items == NULL) {
-               nwrap_vector_init(cont);
+       if (vector->items == NULL) {
+               nwrap_vector_init(vector);
        }
 
-       if (cont->count == cont->capacity) {
+       if (vector->count == vector->capacity) {
                /* Items array _MUST_ be NULL terminated because it's passed
                 * as result to caller which expect NULL terminated array from libc.
                 */
-               void **items = realloc(cont->items, sizeof(void *) * ((cont->capacity * 2) + 1));
+               void **items = realloc(vector->items, sizeof(void *) * ((vector->capacity * 2) + 1));
                if (items == NULL) {
                        return false;
                }
-               cont->items = items;
+               vector->items = items;
 
                /* Don't count ending NULL to capacity */
-               cont->capacity *= 2;
+               vector->capacity *= 2;
        }
 
-       cont->items[cont->count] = item;
+       vector->items[vector->count] = item;
 
-       cont->count += 1;
-       cont->items[cont->count] = NULL;
+       vector->count += 1;
+       vector->items[vector->count] = NULL;
 
        return true;
 }
@@ -677,24 +747,36 @@ static void nwrap_he_unload(struct nwrap_cache *nwrap);
 
 struct nwrap_addrdata {
        unsigned char host_addr[16]; /* IPv4 or IPv6 address */
-       char *h_addr_ptrs[2]; /* host_addr pointer + NULL */
 };
 
+static size_t max_hostents = 100;
+
 struct nwrap_entdata {
-       struct nwrap_addrdata *addr;
+       struct nwrap_addrdata addr;
        struct hostent ht;
+
+       struct nwrap_vector nwrap_addrdata;
+
+       ssize_t aliases_count;
+};
+
+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;
+
        int num;
        int idx;
 };
 
-struct nwrap_cache __nwrap_cache_he;
-struct nwrap_he nwrap_he_global;
+static struct nwrap_cache __nwrap_cache_he;
+static struct nwrap_he nwrap_he_global;
 
 
 /*********************************************************
@@ -850,12 +932,13 @@ static void *_nwrap_load_lib_function(enum nwrap_lib lib, const char *fn_name)
 static void nwrap_lines_unload(struct nwrap_cache *const nwrap)
 {
        size_t p;
-       for (p = 0; p < nwrap->lines.count; p++) {
+       void *item;
+       nwrap_vector_foreach(item, nwrap->lines, p) {
                /* Maybe some vectors were merged ... */
-               SAFE_FREE(nwrap->lines.items[p]);
+               SAFE_FREE(item);
        }
        SAFE_FREE(nwrap->lines.items);
-       nwrap->lines.count = 0;
+       ZERO_STRUCTP(&nwrap->lines);
 }
 
 /*
@@ -928,6 +1011,37 @@ static int libc_getpwuid_r(uid_t uid,
 }
 #endif
 
+static inline void str_tolower(char *dst, char *src)
+{
+       register char *src_tmp = src;
+       register char *dst_tmp = dst;
+
+       while (*src_tmp != '\0') {
+               *dst_tmp = tolower(*src_tmp);
+               ++src_tmp;
+               ++dst_tmp;
+       }
+}
+
+static bool str_tolower_copy(char **dst_name, const char *const src_name)
+{
+       char *h_name_lower;
+
+       if ((dst_name == NULL) || (src_name == NULL)) {
+               return false;
+       }
+
+       h_name_lower = strdup(src_name);
+       if (h_name_lower == NULL) {
+               NWRAP_LOG(NWRAP_LOG_DEBUG, "Out of memory while strdup");
+               return false;
+       }
+
+       str_tolower(h_name_lower, h_name_lower);
+       *dst_name = h_name_lower;
+       return true;
+}
+
 static void libc_setpwent(void)
 {
        nwrap_load_lib_function(NWRAP_LIBC, setpwent);
@@ -1414,10 +1528,55 @@ static void nwrap_backend_init(struct nwrap_main *r)
 
 static void nwrap_init(void)
 {
-       static bool initialized;
+       const char *env;
+       char *endptr;
+       size_t max_hostents_tmp;
+
+       NWRAP_LOCK(nwrap_initialized);
+       if (nwrap_initialized) {
+               NWRAP_UNLOCK(nwrap_initialized);
+               return;
+       }
 
-       if (initialized) return;
-       initialized = true;
+       /*
+        * Still holding nwrap_initialized lock here.
+        * We don't use NWRAP_(UN)LOCK_ALL macros here because we
+        * want to avoid overhead when other threads do their job.
+        */
+       NWRAP_LOCK(nwrap_global);
+       NWRAP_LOCK(nwrap_gr_global);
+       NWRAP_LOCK(nwrap_he_global);
+       NWRAP_LOCK(nwrap_pw_global);
+       NWRAP_LOCK(nwrap_sp_global);
+
+       nwrap_initialized = true;
+
+       /* Initialize pthread_atfork handlers */
+       pthread_atfork(&nwrap_thread_prepare, &nwrap_thread_parent,
+                      &nwrap_thread_child);
+
+       env = getenv("NSS_WRAPPER_MAX_HOSTENTS");
+       if (env != NULL) {
+               max_hostents_tmp = (size_t)strtol(env, &endptr, 10);
+               if (((env != '\0') && (endptr == '\0')) ||
+                   (max_hostents_tmp == 0)) {
+                       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                                 "Error parsing NSS_WRAPPER_MAX_HOSTENTS "
+                                 "value or value is too small. "
+                                 "Using default value: %lu.",
+                                 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);
+       if (hcreate(max_hostents) == 0) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Failed to initialize hash table");
+               goto done;
+       }
 
        nwrap_main_global = &__nwrap_main_global;
 
@@ -1466,6 +1625,10 @@ static void nwrap_init(void)
        nwrap_he_global.cache->private_data = &nwrap_he_global;
        nwrap_he_global.cache->parse_line = nwrap_he_parse_line;
        nwrap_he_global.cache->unload = nwrap_he_unload;
+
+done:
+       /* We hold all locks here so we can use NWRAP_UNLOCK_ALL. */
+       NWRAP_UNLOCK_ALL;
 }
 
 bool nss_wrapper_enabled(void)
@@ -1547,6 +1710,7 @@ static bool nwrap_parse_file(struct nwrap_cache *nwrap)
        do {
                n = getline(&line, &len, nwrap->fp);
                if (n < 0) {
+                       SAFE_FREE(line);
                        if (feof(nwrap->fp)) {
                                break;
                        }
@@ -2379,34 +2543,212 @@ static int nwrap_gr_copy_r(const struct group *src, struct group *dst,
        return 0;
 }
 
+static struct nwrap_entlist *nwrap_entlist_init(struct nwrap_entdata *ed)
+{
+       struct nwrap_entlist *el;
+
+       if (ed == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "entry is NULL, can't create list item");
+               return NULL;
+       }
+
+       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_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)
+{
+       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 *)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_to_existing(struct nwrap_entdata *const ed,
+                                           struct nwrap_entlist *const el)
+{
+       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;
+       }
+
+       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;
+               }
+       }
+
+       if (cursor->ed == ed) {
+               free(el_new);
+               return false;
+       }
+
+       cursor->next = el_new;
+       return true;
+}
+
+static bool nwrap_add_hname_alias(char *const h_name_a,
+                                 struct nwrap_entdata *const ed)
+{
+       ENTRY e;
+       ENTRY *p;
+
+       assert(ed != NULL);
+       assert(h_name_a != NULL);
+
+       e.key = h_name_a;
+       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);
+       } else {
+               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, el);
+       }
+
+       return true;
+}
+
+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;
+
+       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);
+       } else {
+               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, el);
+       }
+
+       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,
+                                 "Unable to add alias: %s", h_name_alias);
+               }
+       }
+
+       return true;
+}
+
 static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line)
 {
        struct nwrap_he *nwrap_he = (struct nwrap_he *)nwrap->private_data;
-       struct nwrap_entdata *ed;
-       size_t list_size;
        bool do_aliases = true;
        ssize_t aliases_count = 0;
        char *p;
        char *i;
        char *n;
 
-       list_size = sizeof(struct nwrap_entdata) * (nwrap_he->num + 1);
+       char *ip;
 
-       ed = (struct nwrap_entdata *)realloc(nwrap_he->list, list_size);
+       struct nwrap_entdata *ed = (struct nwrap_entdata *)
+                                  malloc(sizeof(struct nwrap_entdata));
        if (ed == NULL) {
-               NWRAP_LOG(NWRAP_LOG_ERROR, "realloc[%zd] failed", list_size);
-               return false;
-       }
-       nwrap_he->list = ed;
-
-       /* set it to the last element */
-       ed = &(nwrap_he->list[nwrap_he->num]);
-
-       ed->addr = malloc(sizeof(struct nwrap_addrdata));
-       if (ed->addr == NULL) {
-               NWRAP_LOG(NWRAP_LOG_ERROR, "realloc[%zd] failed", list_size);
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Unable to allocate memory for nwrap_entdata");
                return false;
        }
+       ZERO_STRUCTP(ed);
 
        i = line;
 
@@ -2420,6 +2762,7 @@ static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line)
                        NWRAP_LOG(NWRAP_LOG_ERROR,
                                  "Invalid line[%s]: '%s'",
                                  line, i);
+                       free(ed);
                        return false;
                }
        }
@@ -2429,17 +2772,18 @@ static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line)
                        NWRAP_LOG(NWRAP_LOG_ERROR,
                                  "Invalid line[%s]: '%s'",
                                  line, i);
+                       free(ed);
                        return false;
                }
        }
 
        *p = '\0';
 
-       if (inet_pton(AF_INET, i, ed->addr->host_addr)) {
+       if (inet_pton(AF_INET, i, ed->addr.host_addr)) {
                ed->ht.h_addrtype = AF_INET;
                ed->ht.h_length = 4;
 #ifdef HAVE_IPV6
-       } else if (inet_pton(AF_INET6, i, ed->addr->host_addr)) {
+       } else if (inet_pton(AF_INET6, i, ed->addr.host_addr)) {
                ed->ht.h_addrtype = AF_INET6;
                ed->ht.h_length = 16;
 #endif
@@ -2448,13 +2792,14 @@ static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line)
                          "Invalid line[%s]: '%s'",
                          line, i);
 
+               free(ed);
                return false;
        }
+       ip = i;
 
-       ed->addr->h_addr_ptrs[0] = (char *)ed->addr->host_addr;
-       ed->addr->h_addr_ptrs[1] = NULL;
-
-       ed->ht.h_addr_list = ed->addr->h_addr_ptrs;
+       nwrap_vector_add_item(&(ed->nwrap_addrdata),
+                             (void *const)ed->addr.host_addr);
+       ed->ht.h_addr_list = nwrap_vector_head(&ed->nwrap_addrdata);
 
        p++;
 
@@ -2469,6 +2814,7 @@ static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line)
                                  "Invalid line[%s]: '%s'",
                                  line, n);
 
+                       free(ed);
                        return false;
                }
        }
@@ -2482,11 +2828,14 @@ static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line)
 
        *p = '\0';
 
+       /* Convert to lowercase. This operate on same memory region */
+       str_tolower(n, n);
        ed->ht.h_name = n;
 
        /* glib's getent always dereferences he->h_aliases */
        ed->ht.h_aliases = malloc(sizeof(char *));
        if (ed->ht.h_aliases == NULL) {
+               free(ed);
                return false;
        }
        ed->ht.h_aliases[0] = NULL;
@@ -2523,16 +2872,25 @@ static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line)
 
                aliases = realloc(ed->ht.h_aliases, sizeof(char *) * (aliases_count + 2));
                if (aliases == NULL) {
+                       free(ed);
                        return false;
                }
                ed->ht.h_aliases = aliases;
 
+               str_tolower(a, a);
                aliases[aliases_count] = a;
                aliases[aliases_count + 1] = NULL;
 
-               aliases_count++;
+               aliases_count += 1;
        }
 
+       nwrap_vector_add_item(&(nwrap_he->entdata), (void *const)ed);
+
+       ed->aliases_count = aliases_count;
+       /* Inventarize item */
+       nwrap_add_hname(ed);
+       nwrap_add_ai(ip, ed);
+
        nwrap_he->num++;
        return true;
 }
@@ -2541,15 +2899,17 @@ static void nwrap_he_unload(struct nwrap_cache *nwrap)
 {
        struct nwrap_he *nwrap_he =
                (struct nwrap_he *)nwrap->private_data;
-       int i;
+       struct nwrap_entdata *ed;
+       size_t i;
 
-       if (nwrap_he->list != NULL) {
-               for (i = 0; i < nwrap_he->num; i++) {
-                       SAFE_FREE(nwrap_he->list[i].ht.h_aliases);
-                       SAFE_FREE(nwrap_he->list[i].addr);
-               }
-               SAFE_FREE(nwrap_he->list);
+       nwrap_vector_foreach (ed, nwrap_he->entdata, 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;
 
        nwrap_he->num = 0;
        nwrap_he->idx = 0;
@@ -2972,12 +3332,18 @@ static void nwrap_files_endgrent(struct nwrap_backend *b)
 }
 
 /* hosts functions */
-static struct hostent *nwrap_files_gethostbyname(const char *name, int af)
+static int nwrap_files_gethostbyname(const char *name, int af,
+                                    struct hostent *result,
+                                    struct nwrap_vector *addr_list)
 {
+       struct nwrap_entlist *el;
        struct hostent *he;
+       char *h_name_lower;
+       ENTRY e;
+       ENTRY *e_p;
        char canon_name[DNS_NAME_MAX] = { 0 };
        size_t name_len;
-       int i;
+       bool he_found = false;
 
        nwrap_files_cache_reload(nwrap_he_global.cache);
 
@@ -2987,37 +3353,75 @@ static struct hostent *nwrap_files_gethostbyname(const char *name, int af)
                name = canon_name;
        }
 
-       for (i = 0; i < nwrap_he_global.num; i++) {
-               int j;
+       if (!str_tolower_copy(&h_name_lower, name)) {
+               NWRAP_LOG(NWRAP_LOG_DEBUG,
+                         "Out of memory while converting to lower case");
+               goto no_ent;
+       }
+
+       /* Look at hash table for element */
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "Searching for name: %s", h_name_lower);
+       e.key = h_name_lower;
+       e.data = NULL;
+       e_p = hsearch(e, FIND);
+       if (e_p == NULL) {
+               NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s not found.", h_name_lower);
+               SAFE_FREE(h_name_lower);
+               goto no_ent;
+       }
+       SAFE_FREE(h_name_lower);
+
+       /* Always cleanup vector and results */
+       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;
+       }
 
-               he = &nwrap_he_global.list[i].ht;
+       /* Iterate through results */
+       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) {
                        continue;
                }
 
-               if (strcasecmp(he->h_name, name) == 0) {
-                       NWRAP_LOG(NWRAP_LOG_DEBUG, "name[%s] found", name);
-                       return he;
-               }
-
-               if (he->h_aliases == NULL) {
+               /*
+                * GLIBC HACK?
+                * glibc doesn't return ipv6 addresses when AF_UNSPEC is used
+                */
+               if (af == AF_UNSPEC && he->h_addrtype != AF_INET) {
                        continue;
                }
 
-               for (j = 0; he->h_aliases[j] != NULL; j++) {
-                       if (strcasecmp(he->h_aliases[j], name) == 0) {
-                               NWRAP_LOG(NWRAP_LOG_DEBUG,
-                                         "name[%s] found",
-                                         name);
-                               return he;
-                       }
+               if (!he_found) {
+                       memcpy(result, he, sizeof(struct hostent));
+                       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                                 "Name found. Returning record for %s",
+                                 he->h_name);
+                       he_found = true;
                }
+               nwrap_vector_merge(addr_list, &el->ed->nwrap_addrdata);
+               result->h_addr_list = nwrap_vector_head(addr_list);
        }
 
+       if (he_found) {
+               return 0;
+       }
+       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                 "Name found in database. No records matches type.");
+
+no_ent:
        errno = ENOENT;
-       return NULL;
+       return -1;
 }
 
 #ifdef HAVE_GETHOSTBYNAME_R
@@ -3026,15 +3430,46 @@ static int nwrap_gethostbyname_r(const char *name,
                                 char *buf, size_t buflen,
                                 struct hostent **result, int *h_errnop)
 {
-       *result = nwrap_files_gethostbyname(name, AF_UNSPEC);
-       if (*result != NULL) {
-               memset(buf, '\0', buflen);
-               *ret = **result;
-               return 0;
-       } else {
+       struct nwrap_vector *addr_list = malloc(sizeof(struct nwrap_vector));
+       int rc;
+
+       if (addr_list == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Unable to allocate memory for address list");
+               errno = ENOENT;
+               return -1;
+       }
+
+       ZERO_STRUCTP(addr_list);
+
+       rc = nwrap_files_gethostbyname(name, AF_UNSPEC, ret, addr_list);
+       if (rc == -1) {
                *h_errnop = h_errno;
+               if (addr_list->items != NULL) {
+                       free(addr_list->items);
+               }
+               SAFE_FREE(addr_list);
+               errno = ENOENT;
                return -1;
        }
+
+       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;
 }
 
 int gethostbyname_r(const char *name,
@@ -3055,13 +3490,96 @@ 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)
+{
+       struct nwrap_entlist *el;
+       struct hostent *he;
+       struct addrinfo *ai = NULL;
+       struct addrinfo *ai_head = NULL;
+       struct addrinfo *ai_prev = 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_p = NULL;
+
+       nwrap_files_cache_reload(nwrap_he_global.cache);
+
+       name_len = strlen(name);
+       if (name_len < DNS_NAME_MAX && name[name_len - 1] == '.') {
+               strncpy(canon_name, name, name_len - 1);
+               name = canon_name;
+       }
+
+       if (!str_tolower_copy(&h_name_lower, name)) {
+               NWRAP_LOG(NWRAP_LOG_DEBUG,
+                         "Out of memory while converting to lower case");
+               return NULL;
+       }
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "Searching for name: %s", h_name_lower);
+       e.key = h_name_lower;
+       e.data = NULL;
+       e_p = hsearch(e, FIND);
+       if (e_p == NULL) {
+               NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s not found.", h_name_lower);
+               SAFE_FREE(h_name_lower);
+               errno = ENOENT;
+               return NULL;
+       }
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "Name: %s found.", h_name_lower);
+       SAFE_FREE(h_name_lower);
+
+       for (el = (struct nwrap_entlist *)e_p->data; el != NULL; el = el->next)
+       {
+               int rc;
+
+               he = &(el->ed->ht);
+
+               if (hints->ai_family != AF_UNSPEC &&
+                   he->h_addrtype != hints->ai_family) {
+                       continue;
+               }
+
+               /* Function allocates memory and returns it in ai. */
+               rc = nwrap_convert_he_ai(he,
+                                        port,
+                                        hints,
+                                        &ai,
+                                        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;
+               }
+               skip_canonname = true;
+
+               if (ai_head == NULL) {
+                       ai_head = ai;
+               }
+               if (ai_prev != NULL) {
+                       ai_prev->ai_next = ai;
+               }
+               ai_prev = ai;
+       }
+
+       *ai_tail = ai;
+       return ai_head;
+}
+
 static struct hostent *nwrap_files_gethostbyaddr(const void *addr,
                                                 socklen_t len, int type)
 {
        struct hostent *he;
        char ip[NWRAP_INET_ADDRSTRLEN] = {0};
+       struct nwrap_entdata *ed;
        const char *a;
-       int i;
+       size_t i;
 
        (void) len; /* unused */
 
@@ -3073,9 +3591,9 @@ static struct hostent *nwrap_files_gethostbyaddr(const void *addr,
                return NULL;
        }
 
-       for (i = 0; i < nwrap_he_global.num; i++) {
-               he = &nwrap_he_global.list[i].ht;
-
+       nwrap_vector_foreach(ed, nwrap_he_global.entdata, i)
+       {
+               he = &(ed->ht);
                if (he->h_addrtype != type) {
                        continue;
                }
@@ -3145,7 +3663,7 @@ static struct hostent *nwrap_files_gethostent(void)
                return NULL;
        }
 
-       he = &nwrap_he_global.list[nwrap_he_global.idx++].ht;
+       he = &((struct nwrap_entdata *)nwrap_he_global.entdata.items[nwrap_he_global.idx++])->ht;
 
        NWRAP_LOG(NWRAP_LOG_DEBUG, "return hosts[%s]", he->h_name);
 
@@ -4379,9 +4897,20 @@ void endhostent(void)
 }
 #endif /* HAVE_SOLARIS_ENDHOSTENT */
 
+#ifdef BSD
+/* BSD implementation stores data in thread local storage but GLIBC does not */
+static __thread struct hostent user_he;
+static __thread struct nwrap_vector user_addrlist;
+#else
+static struct hostent user_he;
+static struct nwrap_vector user_addrlist;
+#endif /* BSD */
 static struct hostent *nwrap_gethostbyname(const char *name)
 {
-       return nwrap_files_gethostbyname(name, AF_UNSPEC);
+       if (nwrap_files_gethostbyname(name, AF_UNSPEC, &user_he, &user_addrlist) == -1) {
+               return NULL;
+       }
+       return &user_he;
 }
 
 struct hostent *gethostbyname(const char *name)
@@ -4393,11 +4922,22 @@ struct hostent *gethostbyname(const char *name)
        return nwrap_gethostbyname(name);
 }
 
-/* This is a GNU extension */
+/* This is a GNU extension - Also can be found on BSD systems */
 #ifdef HAVE_GETHOSTBYNAME2
+#ifdef BSD
+/* BSD implementation stores data in  thread local storage but GLIBC not */
+static __thread struct hostent user_he2;
+static __thread struct nwrap_vector user_addrlist2;
+#else
+static struct hostent user_he2;
+static struct nwrap_vector user_addrlist2;
+#endif /* BSD */
 static struct hostent *nwrap_gethostbyname2(const char *name, int af)
 {
-       return nwrap_files_gethostbyname(name, af);
+       if (nwrap_files_gethostbyname(name, af, &user_he2, &user_addrlist2) == -1) {
+               return NULL;
+       }
+       return &user_he2;
 }
 
 struct hostent *gethostbyname2(const char *name, int af)
@@ -4441,11 +4981,16 @@ static const struct addrinfo default_hints =
 static int nwrap_convert_he_ai(const struct hostent *he,
                               unsigned short port,
                               const struct addrinfo *hints,
-                              struct addrinfo **pai)
+                              struct addrinfo **pai,
+                              bool skip_canonname)
 {
        struct addrinfo *ai;
        socklen_t socklen;
 
+       if (he == NULL) {
+               return EAI_MEMORY;
+       }
+
        switch (he->h_addrtype) {
                case AF_INET:
                        socklen = sizeof(struct sockaddr_in);
@@ -4468,6 +5013,7 @@ static int nwrap_convert_he_ai(const struct hostent *he,
        ai->ai_family = he->h_addrtype;
        ai->ai_socktype = hints->ai_socktype;
        ai->ai_protocol = hints->ai_protocol;
+       ai->ai_canonname = NULL;
 
        ai->ai_addrlen = socklen;
        ai->ai_addr = (void *)(ai + 1);
@@ -4504,7 +5050,9 @@ static int nwrap_convert_he_ai(const struct hostent *he,
                        sin6p->sin6_port = htons(port);
                        sin6p->sin6_family = AF_INET6;
 
-                       memcpy(&sin6p->sin6_addr, he->h_addr_list[0], he->h_length);
+                       memcpy(&sin6p->sin6_addr,
+                              he->h_addr_list[0],
+                              he->h_length);
                }
                break;
 #endif
@@ -4512,7 +5060,7 @@ static int nwrap_convert_he_ai(const struct hostent *he,
 
        ai->ai_next = NULL;
 
-       if (he->h_name) {
+       if (he->h_name && !skip_canonname) {
                ai->ai_canonname = strdup(he->h_name);
                if (ai->ai_canonname == NULL) {
                        freeaddrinfo(ai);
@@ -4530,9 +5078,8 @@ 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 hostent *he;
        struct {
                int family;
                union {
@@ -4544,115 +5091,101 @@ static int nwrap_getaddrinfo(const char *node,
        } addr = {
                .family = AF_UNSPEC,
        };
-       int eai = EAI_SYSTEM;
-       int ret;
-       int rc;
 
        if (node == NULL && service == NULL) {
                return EAI_NONAME;
        }
 
-       ret = libc_getaddrinfo(node, service, hints, &p);
-       if (ret == 0) {
-               *res = p;
+       if (hints == NULL) {
+               hints = &default_hints;
+       }
+
+        /* EAI_BADFLAGS
+              hints.ai_flags   contains   invalid  flags;  or,  hints.ai_flags
+              included AI_CANONNAME and name was NULL.
+       */
+       if ((hints->ai_flags & AI_CANONNAME) && (node == NULL)) {
+               return EAI_BADFLAGS;
        }
 
        /* If no node has been specified, let glibc deal with it */
        if (node == NULL) {
-               return ret;
-       }
+               int ret;
+               struct addrinfo *p = NULL;
 
-       if (hints == NULL) {
-               hints = &default_hints;
-       }
+               ret = libc_getaddrinfo(node, service, hints, &p);
 
-       if ((hints->ai_flags & AI_CANONNAME) && node == NULL) {
-               return EAI_BADFLAGS;
+               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;
+
+               errno = 0;
+               sl = strtol(service, &end_ptr, 10);
+
+               if (*end_ptr == '\0') {
+                       port = sl;
+                       goto valid_port;
+               } else if (hints->ai_flags & AI_NUMERICSERV) {
+                       return EAI_NONAME;
+               }
 
-                       if (hints->ai_protocol != 0) {
-                               struct protoent *pent;
+               if (hints->ai_protocol != 0) {
+                       struct protoent *pent;
 
-                               pent = getprotobynumber(hints->ai_protocol);
-                               if (pent != NULL) {
-                                       proto = pent->p_name;
-                               }
+                       pent = getprotobynumber(hints->ai_protocol);
+                       if (pent != NULL) {
+                               proto = pent->p_name;
                        }
+               }
 
-                       s = getservbyname(service, proto);
-                       if (s != NULL) {
-                               port = ntohs(s->s_port);
-                       } else {
-                               if (p != NULL) {
-                                       freeaddrinfo(p);
-                               }
-                               return EAI_SERVICE;
-                       }
+               s = getservbyname(service, proto);
+               if (s == NULL) {
+                       return EAI_NONAME;
                }
+               port = ntohs(s->s_port);
        }
 
-       rc = 0;
+valid_port:
        if (hints->ai_family == AF_UNSPEC || hints->ai_family == AF_INET) {
-               rc = inet_pton(AF_INET, node, &addr.in.v4);
+               int rc = inet_pton(AF_INET, node, &addr.in.v4);
+               if (rc == 1) {
+                       addr.family = AF_INET;
+               }
        }
-       if (rc == 1) {
-               addr.family = AF_INET;
 #ifdef HAVE_IPV6
-       } else {
-               rc = inet_pton(AF_INET6, node, &addr.in.v6);
+       if (addr.family == AF_UNSPEC) {
+               int rc = inet_pton(AF_INET6, node, &addr.in.v6);
                if (rc == 1) {
                        addr.family = AF_INET6;
                }
-#endif
        }
-
-       if (addr.family == AF_INET) {
-               he = nwrap_files_gethostbyaddr(&addr.in.v4,
-                                              sizeof(struct in_addr),
-                                              addr.family);
-               if (he != NULL) {
-                       rc = nwrap_convert_he_ai(he, port, hints, &ai);
-               } else {
-                       eai = EAI_NODATA;
-                       rc = -1;
-               }
-#ifdef HAVE_IPV6
-       } else if (addr.family == AF_INET6) {
-               he = nwrap_files_gethostbyaddr(&addr.in.v6,
-                                              sizeof(struct in6_addr),
-                                              addr.family);
-               if (he != NULL) {
-                       rc = nwrap_convert_he_ai(he, port, hints, &ai);
-                       eai = rc;
-               } else {
-                       eai = EAI_NODATA;
-                       rc = -1;
-               }
 #endif
-       } else {
-               he = nwrap_files_gethostbyname(node, hints->ai_family);
-               if (he != NULL) {
-                       rc = nwrap_convert_he_ai(he, port, hints, &ai);
-                       eai = rc;
-               } else {
-                       eai = EAI_NODATA;
-                       rc = -1;
-               }
-       }
 
-       if (rc < 0) {
-               return ret == 0 ? 0 : eai;
-       }
+       ai = nwrap_files_getaddrinfo(node, port, hints, &ai_tail);
+       if (ai == NULL) {
+               int ret;
+               struct addrinfo *p = NULL;
+
+               ret = libc_getaddrinfo(node, service, hints, &p);
+
+               if (ret == 0) {
+                       /*
+                        * nwrap_files_getaddrinfo failed, but libc was
+                        * successful -- use the result from libc.
+                        */
+                       *res = p;
+                       return 0;
+               }
 
-       if (ret == 0) {
-               freeaddrinfo(p);
+               return EAI_SYSTEM;
        }
 
        if (ai->ai_flags == 0) {
@@ -4669,25 +5202,46 @@ static int nwrap_getaddrinfo(const char *node,
 
        if (hints->ai_socktype == 0) {
                /* Add second ai */
-               rc = nwrap_convert_he_ai(he, port, hints, &ai->ai_next);
-               if (rc < 0) {
-                       freeaddrinfo(ai);
-                       return rc;
-               }
+               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;
+
+                       /* 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);
+                       }
+                       /* ai_head should point inside hints. */
+                       ai_tmp->ai_addr = ai_head->ai_addr;
 
-               if (ai->ai_next->ai_flags == 0) {
-                       ai->ai_next->ai_flags = hints->ai_flags;
-               }
-               if (ai->ai_socktype == SOCK_DGRAM) {
-                       ai->ai_next->ai_socktype = SOCK_STREAM;
-               } else if (ai->ai_socktype == SOCK_STREAM) {
-                       ai->ai_next->ai_socktype = SOCK_DGRAM;
-               }
-               if (ai->ai_next->ai_socktype == SOCK_DGRAM) {
-                       ai->ai_next->ai_protocol = 17; /* UDP */
-               } else if (ai->ai_next->ai_socktype == SOCK_STREAM) {
-                       ai->ai_next->ai_protocol = 6; /* TCP */
-               }
+                       if (ai_head->ai_flags == 0) {
+                               ai_tmp->ai_flags = hints->ai_flags;
+                       }
+                       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_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 */
+                       }
+                       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);
        }
 
        *res = ai;
@@ -4849,6 +5403,7 @@ void nwrap_destructor(void)
 {
        int i;
 
+       NWRAP_LOCK_ALL;
        if (nwrap_main_global != NULL) {
                struct nwrap_main *m = nwrap_main_global;
 
@@ -4912,7 +5467,9 @@ void nwrap_destructor(void)
                        c->fd = -1;
                }
 
-               SAFE_FREE(nwrap_he_global.list);
                nwrap_he_global.num = 0;
        }
+
+       hdestroy();
+       NWRAP_UNLOCK_ALL;
 }