summary refs log tree commit diff
path: root/sysdeps/posix/getaddrinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/posix/getaddrinfo.c')
-rw-r--r--sysdeps/posix/getaddrinfo.c189
1 files changed, 116 insertions, 73 deletions
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 9a27efdf83..3de83e3948 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -40,6 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <errno.h>
 #include <ifaddrs.h>
 #include <netdb.h>
+#include <nss.h>
 #include <resolv.h>
 #include <stdbool.h>
 #include <stdio.h>
@@ -91,14 +92,6 @@ struct gaih_servtuple
 
 static const struct gaih_servtuple nullserv;
 
-struct gaih_addrtuple
-  {
-    struct gaih_addrtuple *next;
-    char *name;
-    int family;
-    uint32_t addr[4];
-    uint32_t scopeid;
-  };
 
 struct gaih_typeproto
   {
@@ -202,6 +195,7 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
       if (herrno == NETDB_INTERNAL)					      \
 	{								      \
 	  __set_h_errno (herrno);					      \
+	  _res.options = old_res_options;				      \
 	  return -EAI_SYSTEM;						      \
 	}								      \
       if (herrno == TRY_AGAIN)						      \
@@ -246,6 +240,10 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
  }
 
 
+typedef enum nss_status (*nss_gethostbyname4_r)
+  (const char *name, struct gaih_addrtuple **pat,
+   char *buffer, size_t buflen, int *errnop,
+   int *h_errnop, int32_t *ttlp);
 typedef enum nss_status (*nss_gethostbyname3_r)
   (const char *name, int af, struct hostent *host,
    char *buffer, size_t buflen, int *errnop,
@@ -685,87 +683,132 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 	  while (!no_more)
 	    {
-	      nss_gethostbyname3_r fct = NULL;
-	      if (req->ai_flags & AI_CANONNAME)
-		/* No need to use this function if we do not look for
-		   the canonical name.  The function does not exist in
-		   all NSS modules and therefore the lookup would
-		   often fail.  */
-		fct = __nss_lookup_function (nip, "gethostbyname3_r");
-	      if (fct == NULL)
-		/* We are cheating here.  The gethostbyname2_r function does
-		   not have the same interface as gethostbyname3_r but the
-		   extra arguments the latter takes are added at the end.
-		   So the gethostbyname2_r code will just ignore them.  */
-		fct = __nss_lookup_function (nip, "gethostbyname2_r");
-
-	      if (fct != NULL)
+	      nss_gethostbyname4_r fct4
+		= __nss_lookup_function (nip, "gethostbyname4_r");
+	      if (fct4 != NULL)
 		{
-		  if (req->ai_family == AF_INET6
-		      || req->ai_family == AF_UNSPEC)
+		  int herrno;
+
+		  while (1)
 		    {
-		      gethosts (AF_INET6, struct in6_addr);
-		      no_inet6_data = no_data;
-		      inet6_status = status;
+		      rc = 0;
+		      status = DL_CALL_FCT (fct4, (name, pat, tmpbuf,
+						   tmpbuflen, &rc, &herrno,
+						   NULL));
+		      if (status != NSS_STATUS_TRYAGAIN
+			  || rc != ERANGE || herrno != NETDB_INTERNAL)
+			{
+			  if (herrno == NETDB_INTERNAL)
+			    {
+			      __set_h_errno (herrno);
+			      _res.options = old_res_options;
+			      return -EAI_SYSTEM;
+			    }
+			  if (herrno == TRY_AGAIN)
+			    no_data = EAI_AGAIN;
+			  else
+			    no_data = herrno == NO_DATA;
+			  break;
+			}
+		      tmpbuf = extend_alloca (tmpbuf,
+					      tmpbuflen, 2 * tmpbuflen);
 		    }
-		  if (req->ai_family == AF_INET
-		      || req->ai_family == AF_UNSPEC
-		      || (req->ai_family == AF_INET6
-			  && (req->ai_flags & AI_V4MAPPED)
-			  /* Avoid generating the mapped addresses if we
-			     know we are not going to need them.  */
-			  && ((req->ai_flags & AI_ALL) || !got_ipv6)))
+
+		  if (status == NSS_STATUS_SUCCESS)
 		    {
-		      gethosts (AF_INET, struct in_addr);
+		      canon = (*pat)->name;
 
-		      if (req->ai_family == AF_INET)
+		      while (*pat != NULL)
+			pat = &((*pat)->next);
+		    }
+		}
+	      else
+		{
+		  nss_gethostbyname3_r fct = NULL;
+		  if (req->ai_flags & AI_CANONNAME)
+		    /* No need to use this function if we do not look for
+		       the canonical name.  The function does not exist in
+		       all NSS modules and therefore the lookup would
+		       often fail.  */
+		    fct = __nss_lookup_function (nip, "gethostbyname3_r");
+		  if (fct == NULL)
+		    /* We are cheating here.  The gethostbyname2_r
+		       function does not have the same interface as
+		       gethostbyname3_r but the extra arguments the
+		       latter takes are added at the end.  So the
+		       gethostbyname2_r code will just ignore them.  */
+		    fct = __nss_lookup_function (nip, "gethostbyname2_r");
+
+		  if (fct != NULL)
+		    {
+		      if (req->ai_family == AF_INET6
+			  || req->ai_family == AF_UNSPEC)
 			{
+			  gethosts (AF_INET6, struct in6_addr);
 			  no_inet6_data = no_data;
 			  inet6_status = status;
 			}
-		    }
-
-		  /* If we found one address for AF_INET or AF_INET6,
-		     don't continue the search.  */
-		  if (inet6_status == NSS_STATUS_SUCCESS
-		      || status == NSS_STATUS_SUCCESS)
-		    {
-		      if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
+		      if (req->ai_family == AF_INET
+			  || req->ai_family == AF_UNSPEC
+			  || (req->ai_family == AF_INET6
+			      && (req->ai_flags & AI_V4MAPPED)
+			      /* Avoid generating the mapped addresses if we
+				 know we are not going to need them.  */
+			      && ((req->ai_flags & AI_ALL) || !got_ipv6)))
 			{
-			  /* If we need the canonical name, get it
-			     from the same service as the result.  */
-			  nss_getcanonname_r cfct;
-			  int herrno;
+			  gethosts (AF_INET, struct in_addr);
 
-			  cfct = __nss_lookup_function (nip, "getcanonname_r");
-			  if (cfct != NULL)
+			  if (req->ai_family == AF_INET)
 			    {
-			      const size_t max_fqdn_len = 256;
-			      char *buf = alloca (max_fqdn_len);
-			      char *s;
-
-			      if (DL_CALL_FCT (cfct, (at->name ?: name, buf,
-						      max_fqdn_len, &s, &rc,
-						      &herrno))
-				  == NSS_STATUS_SUCCESS)
-				canon = s;
-			      else
-				/* Set to name now to avoid using
-				   gethostbyaddr.  */
-				canon = name;
+			      no_inet6_data = no_data;
+			      inet6_status = status;
 			    }
 			}
 
-		      break;
-		    }
+		      /* If we found one address for AF_INET or AF_INET6,
+			 don't continue the search.  */
+		      if (inet6_status == NSS_STATUS_SUCCESS
+			  || status == NSS_STATUS_SUCCESS)
+			{
+			  if ((req->ai_flags & AI_CANONNAME) != 0
+			      && canon == NULL)
+			    {
+			      /* If we need the canonical name, get it
+				 from the same service as the result.  */
+			      nss_getcanonname_r cfct;
+			      int herrno;
+
+			      cfct = __nss_lookup_function (nip,
+							    "getcanonname_r");
+			      if (cfct != NULL)
+				{
+				  const size_t max_fqdn_len = 256;
+				  char *buf = alloca (max_fqdn_len);
+				  char *s;
+
+				  if (DL_CALL_FCT (cfct, (at->name ?: name,
+							  buf, max_fqdn_len,
+							  &s, &rc, &herrno))
+				      == NSS_STATUS_SUCCESS)
+				    canon = s;
+				  else
+				    /* Set to name now to avoid using
+				       gethostbyaddr.  */
+				    canon = name;
+				}
+			    }
 
-		  /* We can have different states for AF_INET and
-		     AF_INET6.  Try to find a useful one for both.  */
-		  if (inet6_status == NSS_STATUS_TRYAGAIN)
-		    status = NSS_STATUS_TRYAGAIN;
-		  else if (status == NSS_STATUS_UNAVAIL
-			   && inet6_status != NSS_STATUS_UNAVAIL)
-		    status = inet6_status;
+			  break;
+			}
+
+		      /* We can have different states for AF_INET and
+			 AF_INET6.  Try to find a useful one for both.  */
+		      if (inet6_status == NSS_STATUS_TRYAGAIN)
+			status = NSS_STATUS_TRYAGAIN;
+		      else if (status == NSS_STATUS_UNAVAIL
+			       && inet6_status != NSS_STATUS_UNAVAIL)
+			status = inet6_status;
+		    }
 		}
 
 	      if (nss_next_action (nip, status) == NSS_ACTION_RETURN)