nwrap: catch NULL list in nwrap_add_hname_add_to_existing
[kai/samba-autobuild/.git] / lib / nss_wrapper / nss_wrapper.c
index a012cbd84b9431a6e3e74f42b48c3a065fbc0529..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,9 @@
 #include <unistd.h>
 #include <ctype.h>
 
+#include <search.h>
+#include <assert.h>
+
 /*
  * Defining _POSIX_PTHREAD_SEMANTICS before including pwd.h and grp.h  gives us
  * the posix getpwnam_r(), getpwuid_r(), getgrnam_r and getgrgid_r calls on
@@ -61,6 +66,9 @@
 
 #include <pwd.h>
 #include <grp.h>
+#ifdef HAVE_SHADOW_H
+#include <shadow.h>
+#endif /* HAVE_SHADOW_H */
 
 #include <netdb.h>
 #include <arpa/inet.h>
@@ -121,6 +129,10 @@ typedef nss_status_t NSS_STATUS;
 #define __location__ __FILE__ ":" __LINESTR__
 #endif
 
+#ifndef DNS_NAME_MAX
+#define DNS_NAME_MAX 255
+#endif
+
 /* GCC have printf type attribute check. */
 #ifdef HAVE_ATTRIBUTE_PRINTF_FORMAT
 #define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
@@ -136,6 +148,72 @@ typedef nss_status_t NSS_STATUS;
 
 #define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } while(0)
 
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0)
+#endif
+
+#ifdef HAVE_IPV6
+#define NWRAP_INET_ADDRSTRLEN INET6_ADDRSTRLEN
+#else
+#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,
@@ -326,6 +404,7 @@ struct nwrap_ops {
 /* Public prototypes */
 
 bool nss_wrapper_enabled(void);
+bool nss_wrapper_shadow_enabled(void);
 bool nss_wrapper_hosts_enabled(void);
 
 /* prototypes for files backend */
@@ -451,25 +530,174 @@ struct nwrap_libc {
 };
 
 struct nwrap_main {
-       const char *nwrap_switch;
        int num_backends;
        struct nwrap_backend *backends;
        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
+ */
+
+#define DEFAULT_VECTOR_CAPACITY 16
+
+struct nwrap_vector {
+       void **items;
+       size_t count;
+       size_t capacity;
+};
+
+/* Macro returns pointer to first element of vector->items array.
+ *
+ * nwrap_vector is used as a memory backend which take care of
+ * memory allocations and other stuff like memory growing.
+ * nwrap_vectors should not be considered as some abstract structures.
+ * On this level, vectors are more handy than direct realloc/malloc
+ * calls.
+ *
+ * nwrap_vector->items is array inside nwrap_vector which can be
+ * directly pointed by libc structure assembled by cwrap itself.
+ *
+ * EXAMPLE:
+ *
+ * 1) struct hostent contains char **h_addr_list element.
+ * 2) nwrap_vector holds array of pointers to addresses.
+ *    It's easier to use vector to store results of
+ *    file parsing etc.
+ *
+ * Now, pretend that cwrap assembled struct hostent and
+ * we need to set h_addr_list to point to nwrap_vector.
+ * Idea behind is to shield users from internal nwrap_vector
+ * implementation.
+ * (Yes, not fully - array terminated by NULL is needed because
+ * it's result expected by libc function caller.)
+ *
+ *
+ * CODE EXAMPLE:
+ *
+ * struct hostent he;
+ * struct nwrap_vector *vector = malloc(sizeof(struct nwrap_vector));
+ * ... don't care about failed allocation now ...
+ *
+ * ... fill nwrap vector ...
+ *
+ * struct hostent he;
+ * he.h_addr_list = nwrap_vector_head(vector);
+ *
+ */
+#define nwrap_vector_head(vect) ((void *)((vect)->items))
+
+#define nwrap_vector_foreach(item, vect, iter) \
+       for (iter = 0, (item) = (vect).items == NULL ? NULL : (vect).items[0]; \
+            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) {
+               return false;
+       }
+
+       /* count is initialized by ZERO_STRUCTP */
+       ZERO_STRUCTP(vector);
+       vector->items = malloc(sizeof(void *) * (DEFAULT_VECTOR_CAPACITY + 1));
+       if (vector->items == NULL) {
+               return false;
+       }
+       vector->capacity = DEFAULT_VECTOR_CAPACITY;
+       memset(vector->items, '\0', sizeof(void *) * (DEFAULT_VECTOR_CAPACITY + 1));
+
+       return true;
+}
+
+static bool nwrap_vector_add_item(struct nwrap_vector *vector, void *const item)
+{
+       assert (vector != NULL);
+
+       if (vector->items == NULL) {
+               nwrap_vector_init(vector);
+       }
+
+       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(vector->items, sizeof(void *) * ((vector->capacity * 2) + 1));
+               if (items == NULL) {
+                       return false;
+               }
+               vector->items = items;
+
+               /* Don't count ending NULL to capacity */
+               vector->capacity *= 2;
+       }
+
+       vector->items[vector->count] = item;
+
+       vector->count += 1;
+       vector->items[vector->count] = NULL;
+
+       return true;
+}
+
+static bool nwrap_vector_merge(struct nwrap_vector *dst,
+                              struct nwrap_vector *src)
+{
+       void **dst_items = NULL;
+       size_t count;
+
+       if (src->count == 0) {
+               return true;
+       }
+
+       count = dst->count + src->count;
+
+       /* We don't need reallocation if we have enough capacity. */
+       if (src->count > (dst->capacity - dst->count)) {
+               dst_items = (void **)realloc(dst->items, (count + 1) * sizeof(void *));
+               if (dst_items == NULL) {
+                       return false;
+               }
+               dst->items = dst_items;
+               dst->capacity = count;
+       }
+
+       memcpy((void *)(((long *)dst->items) + dst->count),
+              src->items,
+              src->count * sizeof(void *));
+       dst->count = count;
+
+       return true;
+}
 
 struct nwrap_cache {
        const char *path;
        int fd;
+       FILE *fp;
        struct stat st;
-       uint8_t *buf;
        void *private_data;
+
+       struct nwrap_vector lines;
+
        bool (*parse_line)(struct nwrap_cache *, char *line);
        void (*unload)(struct nwrap_cache *);
 };
 
+/* passwd */
 struct nwrap_pw {
        struct nwrap_cache *cache;
 
@@ -484,6 +712,24 @@ struct nwrap_pw nwrap_pw_global;
 static bool nwrap_pw_parse_line(struct nwrap_cache *nwrap, char *line);
 static void nwrap_pw_unload(struct nwrap_cache *nwrap);
 
+/* shadow */
+#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM)
+struct nwrap_sp {
+       struct nwrap_cache *cache;
+
+       struct spwd *list;
+       int num;
+       int idx;
+};
+
+struct nwrap_cache __nwrap_cache_sp;
+struct nwrap_sp nwrap_sp_global;
+
+static bool nwrap_sp_parse_line(struct nwrap_cache *nwrap, char *line);
+static void nwrap_sp_unload(struct nwrap_cache *nwrap);
+#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */
+
+/* group */
 struct nwrap_gr {
        struct nwrap_cache *cache;
 
@@ -495,29 +741,42 @@ struct nwrap_gr {
 struct nwrap_cache __nwrap_cache_gr;
 struct nwrap_gr nwrap_gr_global;
 
+/* hosts */
 static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line);
 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;
 
 
 /*********************************************************
@@ -562,10 +821,6 @@ static void *nwrap_load_lib_handle(enum nwrap_lib lib)
        void *handle = NULL;
        int i;
 
-#ifdef HAVE_APPLE
-       return RTLD_NEXT;
-#endif
-
 #ifdef RTLD_DEEPBIND
        flags |= RTLD_DEEPBIND;
 #endif
@@ -575,11 +830,14 @@ static void *nwrap_load_lib_handle(enum nwrap_lib lib)
 #ifdef HAVE_LIBNSL
                handle = nwrap_main_global->libc->nsl_handle;
                if (handle == NULL) {
-                       for (handle = NULL, i = 10; handle == NULL && i >= 0; i--) {
+                       for (i = 10; i >= 0; i--) {
                                char soname[256] = {0};
 
                                snprintf(soname, sizeof(soname), "libnsl.so.%d", i);
                                handle = dlopen(soname, flags);
+                               if (handle != NULL) {
+                                       break;
+                               }
                        }
 
                        nwrap_main_global->libc->nsl_handle = handle;
@@ -591,11 +849,14 @@ static void *nwrap_load_lib_handle(enum nwrap_lib lib)
 #ifdef HAVE_LIBSOCKET
                handle = nwrap_main_global->libc->sock_handle;
                if (handle == NULL) {
-                       for (handle = NULL, i = 10; handle == NULL && i >= 0; i--) {
+                       for (i = 10; i >= 0; i--) {
                                char soname[256] = {0};
 
                                snprintf(soname, sizeof(soname), "libsocket.so.%d", i);
                                handle = dlopen(soname, flags);
+                               if (handle != NULL) {
+                                       break;
+                               }
                        }
 
                        nwrap_main_global->libc->sock_handle = handle;
@@ -606,11 +867,14 @@ static void *nwrap_load_lib_handle(enum nwrap_lib lib)
        case NWRAP_LIBC:
                handle = nwrap_main_global->libc->handle;
                if (handle == NULL) {
-                       for (handle = NULL, i = 10; handle == NULL && i >= 0; i--) {
+                       for (i = 10; i >= 0; i--) {
                                char soname[256] = {0};
 
                                snprintf(soname, sizeof(soname), "libc.so.%d", i);
                                handle = dlopen(soname, flags);
+                               if (handle != NULL) {
+                                       break;
+                               }
                        }
 
                        nwrap_main_global->libc->handle = handle;
@@ -619,10 +883,17 @@ static void *nwrap_load_lib_handle(enum nwrap_lib lib)
        }
 
        if (handle == NULL) {
+#ifdef RTLD_NEXT
+               handle = nwrap_main_global->libc->handle
+                      = nwrap_main_global->libc->sock_handle
+                      = nwrap_main_global->libc->nsl_handle
+                      = RTLD_NEXT;
+#else
                NWRAP_LOG(NWRAP_LOG_ERROR,
                          "Failed to dlopen library: %s\n",
                          dlerror());
                exit(-1);
+#endif
        }
 
        return handle;
@@ -657,6 +928,19 @@ static void *_nwrap_load_lib_function(enum nwrap_lib lib, const char *fn_name)
                        _nwrap_load_lib_function(lib, #fn_name); \
        }
 
+/* INTERNAL HELPER FUNCTIONS */
+static void nwrap_lines_unload(struct nwrap_cache *const nwrap)
+{
+       size_t p;
+       void *item;
+       nwrap_vector_foreach(item, nwrap->lines, p) {
+               /* Maybe some vectors were merged ... */
+               SAFE_FREE(item);
+       }
+       SAFE_FREE(nwrap->lines.items);
+       ZERO_STRUCTP(&nwrap->lines);
+}
+
 /*
  * IMPORTANT
  *
@@ -727,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);
@@ -1062,8 +1377,7 @@ static void *nwrap_load_module_fn(struct nwrap_backend *b,
                          "Cannot find function %s in %s",
                          s, b->so_path);
        }
-       free(s);
-       s = NULL;
+       SAFE_FREE(s);
        return res;
 }
 
@@ -1214,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;
 
@@ -1225,29 +1584,51 @@ static void nwrap_init(void)
 
        nwrap_backend_init(nwrap_main_global);
 
+       /* passwd */
        nwrap_pw_global.cache = &__nwrap_cache_pw;
 
        nwrap_pw_global.cache->path = getenv("NSS_WRAPPER_PASSWD");
+       nwrap_pw_global.cache->fp = NULL;
        nwrap_pw_global.cache->fd = -1;
        nwrap_pw_global.cache->private_data = &nwrap_pw_global;
        nwrap_pw_global.cache->parse_line = nwrap_pw_parse_line;
        nwrap_pw_global.cache->unload = nwrap_pw_unload;
 
+       /* shadow */
+#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM)
+       nwrap_sp_global.cache = &__nwrap_cache_sp;
+
+       nwrap_sp_global.cache->path = getenv("NSS_WRAPPER_SHADOW");
+       nwrap_sp_global.cache->fp = NULL;
+       nwrap_sp_global.cache->fd = -1;
+       nwrap_sp_global.cache->private_data = &nwrap_sp_global;
+       nwrap_sp_global.cache->parse_line = nwrap_sp_parse_line;
+       nwrap_sp_global.cache->unload = nwrap_sp_unload;
+#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */
+
+       /* group */
        nwrap_gr_global.cache = &__nwrap_cache_gr;
 
        nwrap_gr_global.cache->path = getenv("NSS_WRAPPER_GROUP");
+       nwrap_gr_global.cache->fp = NULL;
        nwrap_gr_global.cache->fd = -1;
        nwrap_gr_global.cache->private_data = &nwrap_gr_global;
        nwrap_gr_global.cache->parse_line = nwrap_gr_parse_line;
        nwrap_gr_global.cache->unload = nwrap_gr_unload;
 
+       /* hosts */
        nwrap_he_global.cache = &__nwrap_cache_he;
 
        nwrap_he_global.cache->path = getenv("NSS_WRAPPER_HOSTS");
+       nwrap_he_global.cache->fp = NULL;
        nwrap_he_global.cache->fd = -1;
        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)
@@ -1266,6 +1647,20 @@ bool nss_wrapper_enabled(void)
        return true;
 }
 
+#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM)
+bool nss_wrapper_shadow_enabled(void)
+{
+       nwrap_init();
+
+       if (nwrap_sp_global.cache->path == NULL ||
+           nwrap_sp_global.cache->path[0] == '\0') {
+               return false;
+       }
+
+       return true;
+}
+#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */
+
 bool nss_wrapper_hosts_enabled(void)
 {
        nwrap_init();
@@ -1291,90 +1686,79 @@ static bool nwrap_hostname_enabled(void)
 
 static bool nwrap_parse_file(struct nwrap_cache *nwrap)
 {
-       int ret;
-       uint8_t *buf = NULL;
-       char *nline;
+       char *line = NULL;
+       ssize_t n;
+       /* Unused but getline needs it */
+       size_t len;
+       bool ok;
 
        if (nwrap->st.st_size == 0) {
                NWRAP_LOG(NWRAP_LOG_DEBUG, "size == 0");
-               goto done;
+               return true;
        }
 
+       /* Support for 32-bit system I guess */
        if (nwrap->st.st_size > INT32_MAX) {
                NWRAP_LOG(NWRAP_LOG_ERROR,
                          "Size[%u] larger than INT32_MAX",
                          (unsigned)nwrap->st.st_size);
-               goto failed;
-       }
-
-       ret = lseek(nwrap->fd, 0, SEEK_SET);
-       if (ret != 0) {
-               NWRAP_LOG(NWRAP_LOG_ERROR, "lseek - rc=%d\n", ret);
-               goto failed;
-       }
-
-       buf = (uint8_t *)malloc(nwrap->st.st_size + 1);
-       if (!buf) {
-               NWRAP_LOG(NWRAP_LOG_ERROR, "Out of memory");
-               goto failed;
-       }
-
-       ret = read(nwrap->fd, buf, nwrap->st.st_size);
-       if (ret != nwrap->st.st_size) {
-               NWRAP_LOG(NWRAP_LOG_ERROR,
-                         "read(%u) rc=%d\n",
-                         (unsigned)nwrap->st.st_size, ret);
-               goto failed;
+               return false;
        }
 
-       buf[nwrap->st.st_size] = '\0';
+       rewind(nwrap->fp);
 
-       nline = (char *)buf;
-       while (nline != NULL && nline[0] != '\0') {
-               char *line;
-               char *e;
-               bool ok;
+       do {
+               n = getline(&line, &len, nwrap->fp);
+               if (n < 0) {
+                       SAFE_FREE(line);
+                       if (feof(nwrap->fp)) {
+                               break;
+                       }
 
-               line = nline;
-               nline = NULL;
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "Unable to read line from file: %s",
+                                 nwrap->path);
+                       return false;
+               }
 
-               e = strchr(line, '\n');
-               if (e) {
-                       e[0] = '\0';
-                       e++;
-                       if (e[0] == '\r') {
-                               e[0] = '\0';
-                               e++;
-                       }
-                       nline = e;
+               if (line[n - 1] == '\n') {
+                       line[n - 1] = '\0';
                }
 
-               if (strlen(line) == 0) {
+               if (line[0] == '\0') {
+                       SAFE_FREE(line);
                        continue;
                }
 
                ok = nwrap->parse_line(nwrap, line);
                if (!ok) {
-                       goto failed;
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "Unable to parse line file: %s",
+                                 line);
+                       SAFE_FREE(line);
+                       return false;
+               }
+
+               /* Line is parsed without issues so add it to list */
+               ok = nwrap_vector_add_item(&(nwrap->lines), (void *const) line);
+               if (!ok) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "Unable to add line to vector");
+                       return false;
                }
-       }
 
-done:
-       nwrap->buf = buf;
-       return true;
+               /* This forces getline to allocate new memory for line. */
+               line = NULL;
+       } while (!feof(nwrap->fp));
 
-failed:
-       if (buf) free(buf);
-       return false;
+       return true;
 }
 
 static void nwrap_files_cache_unload(struct nwrap_cache *nwrap)
 {
        nwrap->unload(nwrap);
 
-       if (nwrap->buf) free(nwrap->buf);
-
-       nwrap->buf = NULL;
+       nwrap_lines_unload(nwrap);
 }
 
 static void nwrap_files_cache_reload(struct nwrap_cache *nwrap)
@@ -1384,16 +1768,21 @@ static void nwrap_files_cache_reload(struct nwrap_cache *nwrap)
        bool ok;
        bool retried = false;
 
+       assert(nwrap != NULL);
+
 reopen:
        if (nwrap->fd < 0) {
-               nwrap->fd = open(nwrap->path, O_RDONLY);
-               if (nwrap->fd < 0) {
+               nwrap->fp = fopen(nwrap->path, "re");
+               if (nwrap->fp == NULL) {
+                       nwrap->fd = -1;
                        NWRAP_LOG(NWRAP_LOG_ERROR,
                                  "Unable to open '%s' readonly %d:%s",
                                  nwrap->path, nwrap->fd,
                                  strerror(errno));
                        return;
+
                }
+               nwrap->fd = fileno(nwrap->fp);
                NWRAP_LOG(NWRAP_LOG_DEBUG, "Open '%s'", nwrap->path);
        }
 
@@ -1404,6 +1793,9 @@ reopen:
                          nwrap->path,
                          ret,
                          strerror(errno));
+               fclose(nwrap->fp);
+               nwrap->fp = NULL;
+               nwrap->fd = -1;
                return;
        }
 
@@ -1414,7 +1806,8 @@ reopen:
                          nwrap->path);
                retried = true;
                memset(&nwrap->st, 0, sizeof(nwrap->st));
-               close(nwrap->fd);
+               fclose(nwrap->fp);
+               nwrap->fp = NULL;
                nwrap->fd = -1;
                goto reopen;
        }
@@ -1610,9 +2003,7 @@ static void nwrap_pw_unload(struct nwrap_cache *nwrap)
        struct nwrap_pw *nwrap_pw;
        nwrap_pw = (struct nwrap_pw *)nwrap->private_data;
 
-       if (nwrap_pw->list) free(nwrap_pw->list);
-
-       nwrap_pw->list = NULL;
+       SAFE_FREE(nwrap_pw->list);
        nwrap_pw->num = 0;
        nwrap_pw->idx = 0;
 }
@@ -1657,64 +2048,357 @@ static int nwrap_pw_copy_r(const struct passwd *src, struct passwd *dst,
        return 0;
 }
 
-/*
- * the caller has to call nwrap_unload() on failure
- */
-static bool nwrap_gr_parse_line(struct nwrap_cache *nwrap, char *line)
+#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM)
+static bool nwrap_sp_parse_line(struct nwrap_cache *nwrap, char *line)
 {
-       struct nwrap_gr *nwrap_gr;
+       struct nwrap_sp *nwrap_sp;
+       struct spwd *sp;
+       size_t list_size;
        char *c;
-       char *p;
        char *e;
-       struct group *gr;
-       size_t list_size;
-       unsigned nummem;
+       char *p;
 
-       nwrap_gr = (struct nwrap_gr *)nwrap->private_data;
+       nwrap_sp = (struct nwrap_sp *)nwrap->private_data;
 
-       list_size = sizeof(*nwrap_gr->list) * (nwrap_gr->num+1);
-       gr = (struct group *)realloc(nwrap_gr->list, list_size);
-       if (!gr) {
-               NWRAP_LOG(NWRAP_LOG_ERROR, "realloc failed");
+       list_size = sizeof(*nwrap_sp->list) * (nwrap_sp->num+1);
+       sp = (struct spwd *)realloc(nwrap_sp->list, list_size);
+       if (sp == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "realloc(%u) failed",
+                         (unsigned)list_size);
                return false;
        }
-       nwrap_gr->list = gr;
+       nwrap_sp->list = sp;
 
-       gr = &nwrap_gr->list[nwrap_gr->num];
+       sp = &nwrap_sp->list[nwrap_sp->num];
 
        c = line;
 
        /* name */
        p = strchr(c, ':');
-       if (!p) {
-               NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c);
+       if (p == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "name -- Invalid line[%s]: '%s'",
+                         line,
+                         c);
                return false;
        }
        *p = '\0';
        p++;
-       gr->gr_name = c;
+       sp->sp_namp = c;
        c = p;
 
-       NWRAP_LOG(NWRAP_LOG_TRACE, "name[%s]", gr->gr_name);
+       NWRAP_LOG(NWRAP_LOG_TRACE, "name[%s]\n", sp->sp_namp);
 
-       /* password */
+       /* pwd */
        p = strchr(c, ':');
-       if (!p) {
-               NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c);
+       if (p == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "pwd -- Invalid line[%s]: '%s'",
+                         line,
+                         c);
                return false;
        }
        *p = '\0';
        p++;
-       gr->gr_passwd = c;
+       sp->sp_pwdp = c;
        c = p;
 
-       NWRAP_LOG(NWRAP_LOG_TRACE, "password[%s]", gr->gr_passwd);
-
-       /* gid */
-       p = strchr(c, ':');
-       if (!p) {
-               NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c);
-               return false;
+       /* lstchg (long) */
+       if (c[0] == ':') {
+               sp->sp_lstchg = -1;
+               p++;
+       } else {
+               p = strchr(c, ':');
+               if (p == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "lstchg -- Invalid line[%s]: '%s'",
+                                 line,
+                                 c);
+                       return false;
+               }
+               *p = '\0';
+               p++;
+               sp->sp_lstchg = strtol(c, &e, 10);
+               if (c == e) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "lstchg -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "lstchg -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e[0] != '\0') {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "lstchg -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+       }
+       c = p;
+
+       /* min (long) */
+       if (c[0] == ':') {
+               sp->sp_min = -1;
+               p++;
+       } else {
+               p = strchr(c, ':');
+               if (p == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "min -- Invalid line[%s]: '%s'",
+                                 line,
+                                 c);
+                       return false;
+               }
+               *p = '\0';
+               p++;
+               sp->sp_min = strtol(c, &e, 10);
+               if (c == e) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "min -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "min -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e[0] != '\0') {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "min -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+       }
+       c = p;
+
+       /* max (long) */
+       if (c[0] == ':') {
+               sp->sp_max = -1;
+               p++;
+       } else {
+               p = strchr(c, ':');
+               if (p == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "max -- Invalid line[%s]: '%s'",
+                                 line,
+                                 c);
+                       return false;
+               }
+               *p = '\0';
+               p++;
+               sp->sp_max = strtol(c, &e, 10);
+               if (c == e) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "max -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "max -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e[0] != '\0') {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "max -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+       }
+       c = p;
+
+       /* warn (long) */
+       if (c[0] == ':') {
+               sp->sp_warn = -1;
+               p++;
+       } else {
+               p = strchr(c, ':');
+               if (p == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "warn -- Invalid line[%s]: '%s'",
+                                 line,
+                                 c);
+                       return false;
+               }
+               *p = '\0';
+               p++;
+               sp->sp_warn = strtol(c, &e, 10);
+               if (c == e) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "warn -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "warn -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e[0] != '\0') {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "warn -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+       }
+       c = p;
+
+       /* inact (long) */
+       if (c[0] == ':') {
+               sp->sp_inact = -1;
+               p++;
+       } else {
+               p = strchr(c, ':');
+               if (p == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "inact -- Invalid line[%s]: '%s'",
+                                 line,
+                                 c);
+                       return false;
+               }
+               *p = '\0';
+               p++;
+               sp->sp_inact = strtol(c, &e, 10);
+               if (c == e) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "inact -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "inact -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e[0] != '\0') {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "inact -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+       }
+       c = p;
+
+       /* expire (long) */
+       if (c[0] == ':') {
+               sp->sp_expire = -1;
+               p++;
+       } else {
+               p = strchr(c, ':');
+               if (p == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "expire -- Invalid line[%s]: '%s'",
+                                 line,
+                                 c);
+                       return false;
+               }
+               *p = '\0';
+               p++;
+               sp->sp_expire = strtol(c, &e, 10);
+               if (c == e) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "expire -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "expire -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e[0] != '\0') {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "expire -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+       }
+       c = p;
+
+       nwrap_sp->num++;
+       return true;
+}
+
+static void nwrap_sp_unload(struct nwrap_cache *nwrap)
+{
+       struct nwrap_sp *nwrap_sp;
+       nwrap_sp = (struct nwrap_sp *)nwrap->private_data;
+
+       SAFE_FREE(nwrap_sp->list);
+       nwrap_sp->num = 0;
+       nwrap_sp->idx = 0;
+}
+#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */
+
+/*
+ * the caller has to call nwrap_unload() on failure
+ */
+static bool nwrap_gr_parse_line(struct nwrap_cache *nwrap, char *line)
+{
+       struct nwrap_gr *nwrap_gr;
+       char *c;
+       char *p;
+       char *e;
+       struct group *gr;
+       size_t list_size;
+       unsigned nummem;
+
+       nwrap_gr = (struct nwrap_gr *)nwrap->private_data;
+
+       list_size = sizeof(*nwrap_gr->list) * (nwrap_gr->num+1);
+       gr = (struct group *)realloc(nwrap_gr->list, list_size);
+       if (!gr) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "realloc failed");
+               return false;
+       }
+       nwrap_gr->list = gr;
+
+       gr = &nwrap_gr->list[nwrap_gr->num];
+
+       c = line;
+
+       /* name */
+       p = strchr(c, ':');
+       if (!p) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c);
+               return false;
+       }
+       *p = '\0';
+       p++;
+       gr->gr_name = c;
+       c = p;
+
+       NWRAP_LOG(NWRAP_LOG_TRACE, "name[%s]", gr->gr_name);
+
+       /* password */
+       p = strchr(c, ':');
+       if (!p) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c);
+               return false;
+       }
+       *p = '\0';
+       p++;
+       gr->gr_passwd = c;
+       c = p;
+
+       NWRAP_LOG(NWRAP_LOG_TRACE, "password[%s]", gr->gr_passwd);
+
+       /* gid */
+       p = strchr(c, ':');
+       if (!p) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c);
+               return false;
        }
        *p = '\0';
        p++;
@@ -1797,14 +2481,11 @@ static void nwrap_gr_unload(struct nwrap_cache *nwrap)
 
        if (nwrap_gr->list) {
                for (i=0; i < nwrap_gr->num; i++) {
-                       if (nwrap_gr->list[i].gr_mem) {
-                               free(nwrap_gr->list[i].gr_mem);
-                       }
+                       SAFE_FREE(nwrap_gr->list[i].gr_mem);
                }
-               free(nwrap_gr->list);
+               SAFE_FREE(nwrap_gr->list);
        }
 
-       nwrap_gr->list = NULL;
        nwrap_gr->num = 0;
        nwrap_gr->idx = 0;
 }
@@ -1862,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;
-       int aliases_count = 0;
+       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;
 
@@ -1903,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;
                }
        }
@@ -1912,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
@@ -1931,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++;
 
@@ -1952,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;
                }
        }
@@ -1965,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;
@@ -2006,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;
 }
@@ -2024,21 +2899,18 @@ 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++) {
-                       if (nwrap_he->list[i].ht.h_aliases != NULL) {
-                               free(nwrap_he->list[i].ht.h_aliases);
-                       }
-                       if (nwrap_he->list[i].addr != NULL) {
-                               free(nwrap_he->list[i].addr);
-                       }
-               }
-               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->list = NULL;
        nwrap_he->num = 0;
        nwrap_he->idx = 0;
 }
@@ -2141,64 +3013,179 @@ static void nwrap_files_setpwent(struct nwrap_backend *b)
        nwrap_pw_global.idx = 0;
 }
 
-static struct passwd *nwrap_files_getpwent(struct nwrap_backend *b)
+static struct passwd *nwrap_files_getpwent(struct nwrap_backend *b)
+{
+       struct passwd *pw;
+
+       (void) b; /* unused */
+
+       if (nwrap_pw_global.idx == 0) {
+               nwrap_files_cache_reload(nwrap_pw_global.cache);
+       }
+
+       if (nwrap_pw_global.idx >= nwrap_pw_global.num) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       pw = &nwrap_pw_global.list[nwrap_pw_global.idx++];
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                 "return user[%s] uid[%u]",
+                 pw->pw_name, pw->pw_uid);
+
+       return pw;
+}
+
+static int nwrap_files_getpwent_r(struct nwrap_backend *b,
+                                 struct passwd *pwdst, char *buf,
+                                 size_t buflen, struct passwd **pwdstp)
+{
+       struct passwd *pw;
+
+       pw = nwrap_files_getpwent(b);
+       if (!pw) {
+               if (errno == 0) {
+                       return ENOENT;
+               }
+               return errno;
+       }
+
+       return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
+}
+
+static void nwrap_files_endpwent(struct nwrap_backend *b)
+{
+       (void) b; /* unused */
+
+       nwrap_pw_global.idx = 0;
+}
+
+/* shadow */
+
+#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM)
+
+#ifdef HAVE_SETSPENT
+static void nwrap_files_setspent(void)
+{
+       nwrap_sp_global.idx = 0;
+}
+
+static struct spwd *nwrap_files_getspent(void)
+{
+       struct spwd *sp;
+
+       if (nwrap_sp_global.idx == 0) {
+               nwrap_files_cache_reload(nwrap_sp_global.cache);
+       }
+
+       if (nwrap_sp_global.idx >= nwrap_sp_global.num) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       sp = &nwrap_sp_global.list[nwrap_sp_global.idx++];
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                 "return user[%s]",
+                 sp->sp_namp);
+
+       return sp;
+}
+
+static void nwrap_files_endspent(void)
+{
+       nwrap_sp_global.idx = 0;
+}
+#endif /* HAVE_SETSPENT */
+
+static struct spwd *nwrap_files_getspnam(const char *name)
+{
+       int i;
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "Lookup user %s in files", name);
+
+       nwrap_files_cache_reload(nwrap_sp_global.cache);
+
+       for (i=0; i<nwrap_sp_global.num; i++) {
+               if (strcmp(nwrap_sp_global.list[i].sp_namp, name) == 0) {
+                       NWRAP_LOG(NWRAP_LOG_DEBUG, "user[%s] found", name);
+                       return &nwrap_sp_global.list[i];
+               }
+               NWRAP_LOG(NWRAP_LOG_DEBUG,
+                         "user[%s] does not match [%s]",
+                         name,
+                         nwrap_sp_global.list[i].sp_namp);
+       }
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "user[%s] not found\n", name);
+
+       errno = ENOENT;
+       return NULL;
+}
+#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */
+
+/* misc functions */
+static int nwrap_files_initgroups(struct nwrap_backend *b,
+                                 const char *user,
+                                 gid_t group)
 {
-       struct passwd *pw;
-
-       (void) b; /* unused */
-
-       if (nwrap_pw_global.idx == 0) {
-               nwrap_files_cache_reload(nwrap_pw_global.cache);
-       }
+       struct group *grp;
+       gid_t *groups;
+       int size = 1;
+       int rc;
 
-       if (nwrap_pw_global.idx >= nwrap_pw_global.num) {
-               errno = ENOENT;
-               return NULL;
+       groups = (gid_t *)malloc(size * sizeof(gid_t));
+       if (groups == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Out of memory");
+               errno = ENOMEM;
+               return -1;
        }
+       groups[0] = group;
 
-       pw = &nwrap_pw_global.list[nwrap_pw_global.idx++];
+       nwrap_files_setgrent(b);
+       while ((grp = nwrap_files_getgrent(b)) != NULL) {
+               int i = 0;
 
-       NWRAP_LOG(NWRAP_LOG_DEBUG,
-                 "return user[%s] uid[%u]",
-                 pw->pw_name, pw->pw_uid);
+               NWRAP_LOG(NWRAP_LOG_DEBUG,
+                         "Inspecting %s for group membership",
+                         grp->gr_name);
 
-       return pw;
-}
+               for (i=0; grp->gr_mem && grp->gr_mem[i] != NULL; i++) {
+                       if (group != grp->gr_gid &&
+                           (strcmp(user, grp->gr_mem[i]) == 0)) {
+                               NWRAP_LOG(NWRAP_LOG_DEBUG,
+                                         "%s is member of %s",
+                                         user,
+                                         grp->gr_name);
 
-static int nwrap_files_getpwent_r(struct nwrap_backend *b,
-                                 struct passwd *pwdst, char *buf,
-                                 size_t buflen, struct passwd **pwdstp)
-{
-       struct passwd *pw;
+                               groups = (gid_t *)realloc(groups,
+                                                         (size + 1) * sizeof(gid_t));
+                               if (groups == NULL) {
+                                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                                 "Out of memory");
+                                       errno = ENOMEM;
+                                       return -1;
+                               }
 
-       pw = nwrap_files_getpwent(b);
-       if (!pw) {
-               if (errno == 0) {
-                       return ENOENT;
+                               groups[size] = grp->gr_gid;
+                               size++;
+                       }
                }
-               return errno;
        }
 
-       return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
-}
+       nwrap_files_endgrent(b);
 
-static void nwrap_files_endpwent(struct nwrap_backend *b)
-{
-       (void) b; /* unused */
+       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                 "%s is member of %d groups",
+                 user, size);
 
-       nwrap_pw_global.idx = 0;
-}
+       /* This really only works if uid_wrapper is loaded */
+       rc = setgroups(size, groups);
 
-/* misc functions */
-static int nwrap_files_initgroups(struct nwrap_backend *b,
-                                 const char *user, gid_t group)
-{
-       (void) b; /* unused */
-       (void) user; /* unused */
-       (void) group; /* used */
+       free(groups);
 
-       /* TODO: maybe we should also fake this... */
-       return EPERM;
+       return rc;
 }
 
 /* group functions */
@@ -2345,44 +3332,96 @@ 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;
-       int i;
+       char *h_name_lower;
+       ENTRY e;
+       ENTRY *e_p;
+       char canon_name[DNS_NAME_MAX] = { 0 };
+       size_t name_len;
+       bool he_found = false;
 
        nwrap_files_cache_reload(nwrap_he_global.cache);
 
-       for (i = 0; i < nwrap_he_global.num; i++) {
-               int j;
+       name_len = strlen(name);
+       if (name_len < sizeof(canon_name) && 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");
+               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
@@ -2391,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,
@@ -2420,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[INET6_ADDRSTRLEN] = {0};
+       char ip[NWRAP_INET_ADDRSTRLEN] = {0};
+       struct nwrap_entdata *ed;
        const char *a;
-       int i;
+       size_t i;
 
        (void) len; /* unused */
 
@@ -2438,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;
                }
@@ -2510,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);
 
@@ -2526,9 +3679,6 @@ static void nwrap_files_endhostent(void)
  * module backend
  */
 
-#ifndef SAFE_FREE
-#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0)
-#endif
 
 static struct passwd *nwrap_module_getpwnam(struct nwrap_backend *b,
                                            const char *name)
@@ -3534,7 +4684,6 @@ static int nwrap_getgrouplist(const char *user, gid_t group,
        struct group *grp;
        gid_t *groups_tmp;
        int count = 1;
-       const char *name_of_group = "";
 
        NWRAP_LOG(NWRAP_LOG_DEBUG, "getgrouplist called for %s", user);
 
@@ -3544,13 +4693,7 @@ static int nwrap_getgrouplist(const char *user, gid_t group,
                errno = ENOMEM;
                return -1;
        }
-
-       memcpy(groups_tmp, &group, sizeof(gid_t));
-
-       grp = nwrap_getgrgid(group);
-       if (grp) {
-               name_of_group = grp->gr_name;
-       }
+       groups_tmp[0] = group;
 
        nwrap_setgrent();
        while ((grp = nwrap_getgrent()) != NULL) {
@@ -3562,8 +4705,8 @@ static int nwrap_getgrouplist(const char *user, gid_t group,
 
                for (i=0; grp->gr_mem && grp->gr_mem[i] != NULL; i++) {
 
-                       if ((strcmp(user, grp->gr_mem[i]) == 0) &&
-                           (strcmp(name_of_group, grp->gr_name) != 0)) {
+                       if (group != grp->gr_gid &&
+                           (strcmp(user, grp->gr_mem[i]) == 0)) {
 
                                NWRAP_LOG(NWRAP_LOG_DEBUG,
                                          "%s is member of %s",
@@ -3577,8 +4720,8 @@ static int nwrap_getgrouplist(const char *user, gid_t group,
                                        errno = ENOMEM;
                                        return -1;
                                }
+                               groups_tmp[count] = grp->gr_gid;
 
-                               memcpy(&groups_tmp[count], &grp->gr_gid, sizeof(gid_t));
                                count++;
                        }
                }
@@ -3613,6 +4756,72 @@ int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups)
 }
 #endif
 
+/**********************************************************
+ * SHADOW
+ **********************************************************/
+
+#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM)
+
+#ifdef HAVE_SETSPENT
+static void nwrap_setspent(void)
+{
+       nwrap_files_setspent();
+}
+
+void setspent(void)
+{
+       if (!nss_wrapper_shadow_enabled()) {
+               return;
+       }
+
+       nwrap_setspent();
+}
+
+static struct spwd *nwrap_getspent(void)
+{
+       return nwrap_files_getspent();
+}
+
+struct spwd *getspent(void)
+{
+       if (!nss_wrapper_shadow_enabled()) {
+               return NULL;
+       }
+
+       return nwrap_getspent();
+}
+
+static void nwrap_endspent(void)
+{
+       nwrap_files_endspent();
+}
+
+void endspent(void)
+{
+       if (!nss_wrapper_shadow_enabled()) {
+               return;
+       }
+
+       nwrap_endspent();
+}
+#endif /* HAVE_SETSPENT */
+
+static struct spwd *nwrap_getspnam(const char *name)
+{
+       return nwrap_files_getspnam(name);
+}
+
+struct spwd *getspnam(const char *name)
+{
+       if (!nss_wrapper_shadow_enabled()) {
+               return NULL;
+       }
+
+       return nwrap_getspnam(name);
+}
+
+#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */
+
 /**********************************************************
  * NETDB
  **********************************************************/
@@ -3688,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)
@@ -3702,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)
@@ -3750,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);
@@ -3777,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);
@@ -3813,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
@@ -3821,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);
@@ -3839,137 +5078,114 @@ 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 in_addr in;
-       bool is_addr_ipv4 = false;
-       bool is_addr_ipv6 = false;
-       int eai = EAI_SYSTEM;
-       int ret;
-       int rc;
-       int af;
+       struct {
+               int family;
+               union {
+                       struct in_addr v4;
+#ifdef HAVE_IPV6
+                       struct in6_addr v6;
+               } in;
+#endif
+       } addr = {
+               .family = AF_UNSPEC,
+       };
 
        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;
-
-                       if (hints->ai_protocol != 0) {
-                               struct protoent *pent;
+               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;
+               }
 
-                               pent = getprotobynumber(hints->ai_protocol);
-                               if (pent != NULL) {
-                                       proto = pent->p_name;
-                               }
-                       }
+               if (hints->ai_protocol != 0) {
+                       struct protoent *pent;
 
-                       s = getservbyname(service, proto);
-                       if (s != NULL) {
-                               port = ntohs(s->s_port);
-                       } else {
-                               if (p != NULL) {
-                                       freeaddrinfo(p);
-                               }
-                               return EAI_SERVICE;
+                       pent = getprotobynumber(hints->ai_protocol);
+                       if (pent != NULL) {
+                               proto = pent->p_name;
                        }
                }
-       }
 
-       af = hints->ai_family;
-       if (af == AF_UNSPEC) {
-               af = AF_INET;
+               s = getservbyname(service, proto);
+               if (s == NULL) {
+                       return EAI_NONAME;
+               }
+               port = ntohs(s->s_port);
        }
 
-       rc = inet_pton(af, node, &in);
-       if (rc == 1) {
-               is_addr_ipv4 = true;
-               if (af == AF_UNSPEC) {
-                       af = AF_INET;
+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;
                }
+       }
 #ifdef HAVE_IPV6
-       } else {
-               struct in6_addr in6;
-
-               af = AF_INET6;
-
-               rc = inet_pton(af, node, &in6);
+       if (addr.family == AF_UNSPEC) {
+               int rc = inet_pton(AF_INET6, node, &addr.in.v6);
                if (rc == 1) {
-                       is_addr_ipv6 = true;
+                       addr.family = AF_INET6;
                }
-#endif
        }
+#endif
 
-       if (is_addr_ipv4) {
-               he = nwrap_files_gethostbyaddr(&in, sizeof(struct in_addr), af);
-               if (he != NULL) {
-                       rc = nwrap_convert_he_ai(he, port, hints, &ai);
-               } else {
-                       eai = EAI_NODATA;
-                       rc = -1;
-               }
-#ifdef HAVE_IPV6
-       } else if (is_addr_ipv6) {
-               struct in6_addr in6;
+       ai = nwrap_files_getaddrinfo(node, port, hints, &ai_tail);
+       if (ai == NULL) {
+               int ret;
+               struct addrinfo *p = NULL;
 
-               rc =  inet_pton(af, node, &in6);
-               if (rc <= 0) {
-                       eai = EAI_ADDRFAMILY;
-                       return ret == 0 ? 0 : eai;
-               }
+               ret = libc_getaddrinfo(node, service, hints, &p);
 
-               he = nwrap_files_gethostbyaddr(&in6,
-                                              sizeof(struct in6_addr),
-                                              af);
-               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 (ret == 0) {
+                       /*
+                        * nwrap_files_getaddrinfo failed, but libc was
+                        * successful -- use the result from libc.
+                        */
+                       *res = p;
+                       return 0;
                }
-       }
 
-       if (rc < 0) {
-               return ret == 0 ? 0 : eai;
-       }
-
-       if (ret == 0) {
-               freeaddrinfo(p);
+               return EAI_SYSTEM;
        }
 
        if (ai->ai_flags == 0) {
@@ -3986,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;
@@ -4166,6 +5403,7 @@ void nwrap_destructor(void)
 {
        int i;
 
+       NWRAP_LOCK_ALL;
        if (nwrap_main_global != NULL) {
                struct nwrap_main *m = nwrap_main_global;
 
@@ -4199,7 +5437,8 @@ void nwrap_destructor(void)
 
                nwrap_files_cache_unload(c);
                if (c->fd >= 0) {
-                       close(c->fd);
+                       fclose(c->fp);
+                       c->fd = -1;
                }
 
                SAFE_FREE(nwrap_pw_global.list);
@@ -4211,7 +5450,8 @@ void nwrap_destructor(void)
 
                nwrap_files_cache_unload(c);
                if (c->fd >= 0) {
-                       close(c->fd);
+                       fclose(c->fp);
+                       c->fd = -1;
                }
 
                SAFE_FREE(nwrap_gr_global.list);
@@ -4223,10 +5463,13 @@ void nwrap_destructor(void)
 
                nwrap_files_cache_unload(c);
                if (c->fd >= 0) {
-                       close(c->fd);
+                       fclose(c->fp);
+                       c->fd = -1;
                }
 
-               SAFE_FREE(nwrap_he_global.list);
                nwrap_he_global.num = 0;
        }
+
+       hdestroy();
+       NWRAP_UNLOCK_ALL;
 }