nwrap: fix treatment of EAI_ADDRINFO in nwrap_files_getaddrinfo
[obnox/samba/samba-obnox.git] / lib / nss_wrapper / nss_wrapper.c
index 0837c7cd44f6515bd76e5b5585203c6a92448a60..770058cf2a39b976fab915c9ce7ba6f1ca1d155a 100644 (file)
@@ -758,9 +758,11 @@ struct nwrap_entdata {
        struct nwrap_vector nwrap_addrdata;
 
        ssize_t aliases_count;
+};
 
-       struct nwrap_entdata *ed_next;
-       struct nwrap_entdata *ed_tail;
+struct nwrap_entlist {
+       struct nwrap_entlist *next;
+       struct nwrap_entdata *ed;
 };
 
 struct nwrap_he {
@@ -1759,7 +1761,7 @@ static void nwrap_files_cache_unload(struct nwrap_cache *nwrap)
        nwrap_lines_unload(nwrap);
 }
 
-static void nwrap_files_cache_reload(struct nwrap_cache *nwrap)
+static bool nwrap_files_cache_reload(struct nwrap_cache *nwrap)
 {
        struct stat st;
        int ret;
@@ -1777,7 +1779,7 @@ reopen:
                                  "Unable to open '%s' readonly %d:%s",
                                  nwrap->path, nwrap->fd,
                                  strerror(errno));
-                       return;
+                       return false;
 
                }
                nwrap->fd = fileno(nwrap->fp);
@@ -1794,7 +1796,7 @@ reopen:
                fclose(nwrap->fp);
                nwrap->fp = NULL;
                nwrap->fd = -1;
-               return;
+               return false;
        }
 
        if (retried == false && st.st_nlink == 0) {
@@ -1814,7 +1816,7 @@ reopen:
                NWRAP_LOG(NWRAP_LOG_TRACE,
                          "st_mtime[%u] hasn't changed, skip reload",
                          (unsigned)st.st_mtime);
-               return;
+               return true;
        }
 
        NWRAP_LOG(NWRAP_LOG_TRACE,
@@ -1830,9 +1832,11 @@ reopen:
        if (!ok) {
                NWRAP_LOG(NWRAP_LOG_ERROR, "Failed to reload %s", nwrap->path);
                nwrap_files_cache_unload(nwrap);
+               return false;
        }
 
        NWRAP_LOG(NWRAP_LOG_TRACE, "Reloaded %s", nwrap->path);
+       return true;
 }
 
 /*
@@ -2541,17 +2545,50 @@ 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 = {
-               .key = ip_addr,
-               .data = (void *)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_DEBUG, "Hash table is full");
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Hash table is full");
                return false;
        }
 
@@ -2562,36 +2599,63 @@ static bool nwrap_add_ai(char *const ip_addr, struct nwrap_entdata *const ed)
 static bool nwrap_add_hname_add_new(char *const h_name,
                                    struct nwrap_entdata *const ed)
 {
-       /* No element found.. inventarize new item */
        ENTRY e;
        ENTRY *p;
+       struct nwrap_entlist *el;
+
+       if (h_name == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "h_name NULL - can't add");
+               return false;
+       }
+
+       el = nwrap_entlist_init(ed);
+       if (el == NULL) {
+               return false;
+       }
 
        e.key = h_name;
-       e.data = (void *)ed;
-       ed->ed_tail = NULL;
-       ed->ed_next = NULL;
+       e.data = (void *)el;
 
        p = hsearch(e, ENTER);
        if (p == NULL) {
-               NWRAP_LOG(NWRAP_LOG_DEBUG, "Hash table is full!");
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Hash table is full!");
                return false;
        }
 
        return true;
 }
 
-static void nwrap_add_hname_add_to_existing(struct nwrap_entdata *const ed,
-                                           struct nwrap_entdata *const ed_dst)
+static bool nwrap_add_hname_add_to_existing(struct nwrap_entdata *const ed,
+                                           struct nwrap_entlist *const el)
 {
-       if (ed_dst->ed_tail != NULL) {
-               ed_dst->ed_tail->ed_next = ed;
-               if (ed_dst->ed_tail != ed) {
-                       ed_dst->ed_tail = ed;
-                       ed->ed_next = NULL;
+       struct nwrap_entlist *cursor;
+       struct nwrap_entlist *el_new;
+
+       if (el == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "list is NULL, can not add");
+               return false;
+       }
+
+       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;
                }
-       } else {
-               ed_dst->ed_tail = ed;
        }
+
+       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,
@@ -2599,10 +2663,7 @@ static bool nwrap_add_hname_alias(char *const h_name_a,
 {
        ENTRY e;
        ENTRY *p;
-
-       /* Maybe it's little bit late ... */
-       assert(ed != NULL);
-       assert(h_name_a != NULL);
+       bool ok;
 
        e.key = h_name_a;
        e.data = NULL;
@@ -2610,18 +2671,15 @@ static bool nwrap_add_hname_alias(char *const h_name_a,
        p = hsearch(e, FIND);
        if (p == NULL) {
                NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s not found. Adding...", h_name_a);
-               /* Just add alias and don't mess with metadata */
-               nwrap_add_hname_add_new(h_name_a, ed);
+               ok = nwrap_add_hname_add_new(h_name_a, ed);
        } else {
-               /* Element found. Add them to end of list */
-               struct nwrap_entdata *ed_dst = (struct nwrap_entdata *)p->data;
+               struct nwrap_entlist *el = (struct nwrap_entlist *)p->data;
 
-               assert(p->data != NULL);
                NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s found. Add record to list.", h_name_a);
-               nwrap_add_hname_add_to_existing(ed, ed_dst);
+               ok = nwrap_add_hname_add_to_existing(ed, el);
        }
 
-       return true;
+       return ok;
 }
 
 static bool nwrap_add_hname(struct nwrap_entdata *const ed)
@@ -2629,12 +2687,8 @@ static bool nwrap_add_hname(struct nwrap_entdata *const ed)
        char *const h_name = (char *const)(ed->ht.h_name);
        ENTRY e;
        ENTRY *p;
-       char *h_name_alias;
        unsigned i;
-
-       /* Maybe it's little bit late ... */
-       assert(ed != NULL);
-       assert(h_name != NULL);
+       bool ok;
 
        e.key = h_name;
        e.data = NULL;
@@ -2642,36 +2696,34 @@ static bool nwrap_add_hname(struct nwrap_entdata *const ed)
        p = hsearch(e, FIND);
        if (p == NULL) {
                NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s not found. Adding...", h_name);
-               /* Just add alias and don't mess with metadata */
-               nwrap_add_hname_add_new(h_name, ed);
-
-               if (ed->ed_tail == NULL) {
-                       ed->ed_tail = ed;
-               }
+               ok = nwrap_add_hname_add_new(h_name, ed);
        } else {
-               /* Element found. Add them to end of list */
-               struct nwrap_entdata *ed_dst = (struct nwrap_entdata *)p->data;
+               struct nwrap_entlist *el = (struct nwrap_entlist *)p->data;
 
-               assert(p->data != NULL);
                NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s found. Add record to list.", h_name);
-               nwrap_add_hname_add_to_existing(ed, ed_dst);
+               ok = nwrap_add_hname_add_to_existing(ed, el);
+       }
+
+       if (!ok) {
+               return false;
        }
 
-       /* Return true when list of aliases is empty */
        if (ed->ht.h_aliases == NULL) {
                return true;
        }
 
        /* Itemize aliases */
        for (i = 0; ed->ht.h_aliases[i] != NULL; ++i) {
+               char *h_name_alias;
+
                h_name_alias = ed->ht.h_aliases[i];
-               assert(h_name_alias != NULL);
 
                NWRAP_LOG(NWRAP_LOG_DEBUG, "Add alias: %s", h_name_alias);
 
                if (!nwrap_add_hname_alias(h_name_alias, ed)) {
-                       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
                                  "Unable to add alias: %s", h_name_alias);
+                       return false;
                }
        }
 
@@ -2688,6 +2740,7 @@ static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line)
        char *n;
 
        char *ip;
+       bool ok;
 
        struct nwrap_entdata *ed = (struct nwrap_entdata *)
                                   malloc(sizeof(struct nwrap_entdata));
@@ -2836,8 +2889,15 @@ static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line)
 
        ed->aliases_count = aliases_count;
        /* Inventarize item */
-       nwrap_add_hname(ed);
-       nwrap_add_ai(ip, ed);
+       ok = nwrap_add_hname(ed);
+       if (!ok) {
+               return false;
+       }
+
+       ok = nwrap_add_ai(ip, ed);
+       if (!ok) {
+               return false;
+       }
 
        nwrap_he->num++;
        return true;
@@ -2869,12 +2929,17 @@ static struct passwd *nwrap_files_getpwnam(struct nwrap_backend *b,
                                           const char *name)
 {
        int i;
+       bool ok;
 
        (void) b; /* unused */
 
        NWRAP_LOG(NWRAP_LOG_DEBUG, "Lookup user %s in files", name);
 
-       nwrap_files_cache_reload(nwrap_pw_global.cache);
+       ok = nwrap_files_cache_reload(nwrap_pw_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading passwd file");
+               return NULL;
+       }
 
        for (i=0; i<nwrap_pw_global.num; i++) {
                if (strcmp(nwrap_pw_global.list[i].pw_name, name) == 0) {
@@ -2914,10 +2979,15 @@ static struct passwd *nwrap_files_getpwuid(struct nwrap_backend *b,
                                           uid_t uid)
 {
        int i;
+       bool ok;
 
        (void) b; /* unused */
 
-       nwrap_files_cache_reload(nwrap_pw_global.cache);
+       ok = nwrap_files_cache_reload(nwrap_pw_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading passwd file");
+               return NULL;
+       }
 
        for (i=0; i<nwrap_pw_global.num; i++) {
                if (nwrap_pw_global.list[i].pw_uid == uid) {
@@ -2968,7 +3038,12 @@ static struct passwd *nwrap_files_getpwent(struct nwrap_backend *b)
        (void) b; /* unused */
 
        if (nwrap_pw_global.idx == 0) {
-               nwrap_files_cache_reload(nwrap_pw_global.cache);
+               bool ok;
+               ok = nwrap_files_cache_reload(nwrap_pw_global.cache);
+               if (!ok) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading passwd file");
+                       return NULL;
+               }
        }
 
        if (nwrap_pw_global.idx >= nwrap_pw_global.num) {
@@ -3024,7 +3099,13 @@ static struct spwd *nwrap_files_getspent(void)
        struct spwd *sp;
 
        if (nwrap_sp_global.idx == 0) {
-               nwrap_files_cache_reload(nwrap_sp_global.cache);
+               bool ok;
+
+               ok = nwrap_files_cache_reload(nwrap_sp_global.cache);
+               if (!ok) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading shadow file");
+                       return NULL;
+               }
        }
 
        if (nwrap_sp_global.idx >= nwrap_sp_global.num) {
@@ -3050,10 +3131,15 @@ static void nwrap_files_endspent(void)
 static struct spwd *nwrap_files_getspnam(const char *name)
 {
        int i;
+       bool ok;
 
        NWRAP_LOG(NWRAP_LOG_DEBUG, "Lookup user %s in files", name);
 
-       nwrap_files_cache_reload(nwrap_sp_global.cache);
+       ok = nwrap_files_cache_reload(nwrap_sp_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading shadow file");
+               return NULL;
+       }
 
        for (i=0; i<nwrap_sp_global.num; i++) {
                if (strcmp(nwrap_sp_global.list[i].sp_namp, name) == 0) {
@@ -3141,10 +3227,15 @@ static struct group *nwrap_files_getgrnam(struct nwrap_backend *b,
                                          const char *name)
 {
        int i;
+       bool ok;
 
        (void) b; /* unused */
 
-       nwrap_files_cache_reload(nwrap_gr_global.cache);
+       ok = nwrap_files_cache_reload(nwrap_gr_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading group file");
+               return NULL;
+       }
 
        for (i=0; i<nwrap_gr_global.num; i++) {
                if (strcmp(nwrap_gr_global.list[i].gr_name, name) == 0) {
@@ -3184,10 +3275,15 @@ static struct group *nwrap_files_getgrgid(struct nwrap_backend *b,
                                          gid_t gid)
 {
        int i;
+       bool ok;
 
        (void) b; /* unused */
 
-       nwrap_files_cache_reload(nwrap_gr_global.cache);
+       ok = nwrap_files_cache_reload(nwrap_gr_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading group file");
+               return NULL;
+       }
 
        for (i=0; i<nwrap_gr_global.num; i++) {
                if (nwrap_gr_global.list[i].gr_gid == gid) {
@@ -3238,7 +3334,13 @@ static struct group *nwrap_files_getgrent(struct nwrap_backend *b)
        (void) b; /* unused */
 
        if (nwrap_gr_global.idx == 0) {
-               nwrap_files_cache_reload(nwrap_gr_global.cache);
+               bool ok;
+
+               ok = nwrap_files_cache_reload(nwrap_gr_global.cache);
+               if (!ok) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading group file");
+                       return NULL;
+               }
        }
 
        if (nwrap_gr_global.idx >= nwrap_gr_global.num) {
@@ -3284,8 +3386,7 @@ static int nwrap_files_gethostbyname(const char *name, int af,
                                     struct hostent *result,
                                     struct nwrap_vector *addr_list)
 {
-       struct nwrap_entdata *ed_head;
-       struct nwrap_entdata *ed_cur;
+       struct nwrap_entlist *el;
        struct hostent *he;
        char *h_name_lower;
        ENTRY e;
@@ -3293,8 +3394,13 @@ static int nwrap_files_gethostbyname(const char *name, int af,
        char canon_name[DNS_NAME_MAX] = { 0 };
        size_t name_len;
        bool he_found = false;
+       bool ok;
 
-       nwrap_files_cache_reload(nwrap_he_global.cache);
+       ok = nwrap_files_cache_reload(nwrap_he_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "error loading hosts file");
+               goto no_ent;
+       }
 
        name_len = strlen(name);
        if (name_len < sizeof(canon_name) && name[name_len - 1] == '.') {
@@ -3334,9 +3440,9 @@ static int nwrap_files_gethostbyname(const char *name, int af,
        }
 
        /* Iterate through results */
-       ed_head = (struct nwrap_entdata *)e_p->data;
-       for (ed_cur = ed_head; ed_cur != NULL; ed_cur = ed_cur->ed_next) {
-               he = &(ed_cur->ht);
+       for (el = (struct nwrap_entlist *)e_p->data; el != NULL; el = el->next)
+       {
+               he = &(el->ed->ht);
 
                /* Filter by address familiy if provided */
                if (af != AF_UNSPEC && he->h_addrtype != af) {
@@ -3358,7 +3464,7 @@ static int nwrap_files_gethostbyname(const char *name, int af,
                                  he->h_name);
                        he_found = true;
                }
-               nwrap_vector_merge(addr_list, &ed_cur->nwrap_addrdata);
+               nwrap_vector_merge(addr_list, &el->ed->nwrap_addrdata);
                result->h_addr_list = nwrap_vector_head(addr_list);
        }
 
@@ -3439,15 +3545,15 @@ int gethostbyname_r(const char *name,
 }
 #endif
 
-static struct addrinfo *nwrap_files_getaddrinfo(const char *name,
-                                               unsigned short port,
-                                               const struct addrinfo *hints,
-                                               struct addrinfo **ai_tail)
+static int nwrap_files_getaddrinfo(const char *name,
+                                  unsigned short port,
+                                  const struct addrinfo *hints,
+                                  struct addrinfo **ai,
+                                  struct addrinfo **ai_tail)
 {
-       struct nwrap_entdata *ed_head;
-       struct nwrap_entdata *ed_cur;
+       struct nwrap_entlist *el;
        struct hostent *he;
-       struct addrinfo *ai = NULL;
+       struct addrinfo *_ai = NULL;
        struct addrinfo *ai_head = NULL;
        struct addrinfo *ai_prev = NULL;
        char *h_name_lower;
@@ -3456,8 +3562,14 @@ static struct addrinfo *nwrap_files_getaddrinfo(const char *name,
        bool skip_canonname = false;
        ENTRY e = { 0 };
        ENTRY *e_p = NULL;
+       int rc;
+       bool ok;
 
-       nwrap_files_cache_reload(nwrap_he_global.cache);
+       ok = nwrap_files_cache_reload(nwrap_he_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "error loading hosts file");
+               return EAI_SYSTEM;
+       }
 
        name_len = strlen(name);
        if (name_len < DNS_NAME_MAX && name[name_len - 1] == '.') {
@@ -3468,7 +3580,7 @@ static struct addrinfo *nwrap_files_getaddrinfo(const char *name,
        if (!str_tolower_copy(&h_name_lower, name)) {
                NWRAP_LOG(NWRAP_LOG_DEBUG,
                          "Out of memory while converting to lower case");
-               return NULL;
+               return EAI_MEMORY;
        }
 
        NWRAP_LOG(NWRAP_LOG_DEBUG, "Searching for name: %s", h_name_lower);
@@ -3479,48 +3591,57 @@ static struct addrinfo *nwrap_files_getaddrinfo(const char *name,
                NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s not found.", h_name_lower);
                SAFE_FREE(h_name_lower);
                errno = ENOENT;
-               return NULL;
+               return EAI_NONAME;
        }
        NWRAP_LOG(NWRAP_LOG_DEBUG, "Name: %s found.", h_name_lower);
        SAFE_FREE(h_name_lower);
 
-       ed_head = (struct nwrap_entdata *)e_p->data;
-
-       for (ed_cur = ed_head; ed_cur != NULL; ed_cur = ed_cur->ed_next) {
-               int rc;
+       rc = EAI_NONAME;
+       for (el = (struct nwrap_entlist *)e_p->data; el != NULL; el = el->next)
+       {
+               int rc2;
 
-               he = &(ed_cur->ht);
+               he = &(el->ed->ht);
 
                if (hints->ai_family != AF_UNSPEC &&
-                   he->h_addrtype != hints->ai_family) {
+                   he->h_addrtype != hints->ai_family)
+               {
+                       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                                 "Entry found but with wrong AF - "
+                                 "remembering EAI_ADDRINFO.");
+                       rc = EAI_ADDRFAMILY;
                        continue;
                }
 
                /* Function allocates memory and returns it in ai. */
-               rc = nwrap_convert_he_ai(he,
+               rc2 = nwrap_convert_he_ai(he,
                                         port,
                                         hints,
-                                        &ai,
+                                        &_ai,
                                         skip_canonname);
-               if (rc != 0) {
-                       /* FIXME: Investigate if this is nice to do... */
-                       NWRAP_LOG(NWRAP_LOG_ERROR,
-                                 "Error in converting he to ai! Skipping.");
-                       continue;
+               if (rc2 != 0) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR, "Error converting he to ai");
+                       return rc2;
                }
                skip_canonname = true;
 
                if (ai_head == NULL) {
-                       ai_head = ai;
+                       ai_head = _ai;
                }
                if (ai_prev != NULL) {
-                       ai_prev->ai_next = ai;
+                       ai_prev->ai_next = _ai;
                }
-               ai_prev = ai;
+               ai_prev = _ai;
+       }
+
+       if (ai_head != NULL) {
+               rc = 0;
        }
 
-       *ai_tail = ai;
-       return ai_head;
+       *ai = ai_head;
+       *ai_tail = _ai;
+
+       return rc;
 }
 
 static struct hostent *nwrap_files_gethostbyaddr(const void *addr,
@@ -3531,10 +3652,15 @@ static struct hostent *nwrap_files_gethostbyaddr(const void *addr,
        struct nwrap_entdata *ed;
        const char *a;
        size_t i;
+       bool ok;
 
        (void) len; /* unused */
 
-       nwrap_files_cache_reload(nwrap_he_global.cache);
+       ok = nwrap_files_cache_reload(nwrap_he_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "error loading hosts file");
+               return NULL;
+       }
 
        a = inet_ntop(type, addr, ip, sizeof(ip));
        if (a == NULL) {
@@ -3606,7 +3732,13 @@ static struct hostent *nwrap_files_gethostent(void)
        struct hostent *he;
 
        if (nwrap_he_global.idx == 0) {
-               nwrap_files_cache_reload(nwrap_he_global.cache);
+               bool ok;
+
+               ok = nwrap_files_cache_reload(nwrap_he_global.cache);
+               if (!ok) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading hosts file");
+                       return NULL;
+               }
        }
 
        if (nwrap_he_global.idx >= nwrap_he_global.num) {
@@ -5042,6 +5174,7 @@ static int nwrap_getaddrinfo(const char *node,
        } addr = {
                .family = AF_UNSPEC,
        };
+       int rc;
 
        if (node == NULL && service == NULL) {
                return EAI_NONAME;
@@ -5105,23 +5238,29 @@ static int nwrap_getaddrinfo(const char *node,
        }
 
 valid_port:
-       if (hints->ai_family == AF_UNSPEC || hints->ai_family == AF_INET) {
-               int rc = inet_pton(AF_INET, node, &addr.in.v4);
-               if (rc == 1) {
-                       addr.family = AF_INET;
-               }
+
+       rc = inet_pton(AF_INET, node, &addr.in.v4);
+       if (rc == 1) {
+               addr.family = AF_INET;
        }
 #ifdef HAVE_IPV6
        if (addr.family == AF_UNSPEC) {
-               int rc = inet_pton(AF_INET6, node, &addr.in.v6);
+               rc = inet_pton(AF_INET6, node, &addr.in.v6);
                if (rc == 1) {
                        addr.family = AF_INET6;
                }
        }
 #endif
 
-       ai = nwrap_files_getaddrinfo(node, port, hints, &ai_tail);
-       if (ai == NULL) {
+       if ((addr.family != AF_UNSPEC) &&
+           (hints->ai_family != AF_UNSPEC) &&
+           (hints->ai_family != addr.family))
+       {
+               return EAI_ADDRFAMILY;
+       }
+
+       rc = nwrap_files_getaddrinfo(node, port, hints, &ai, &ai_tail);
+       if (rc != 0) {
                int ret;
                struct addrinfo *p = NULL;
 
@@ -5136,7 +5275,7 @@ valid_port:
                        return 0;
                }
 
-               return EAI_SYSTEM;
+               return rc;
        }
 
        if (ai->ai_flags == 0) {