about 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.c184
1 files changed, 143 insertions, 41 deletions
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 46c66a8f7e..843e60bba3 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -68,7 +68,7 @@ extern int __idna_to_unicode_lzlz (const char *input, char **output,
 #define GAIH_EAI        ~(GAIH_OKIFUNSPEC)
 
 #ifndef UNIX_PATH_MAX
-#define UNIX_PATH_MAX  108
+# define UNIX_PATH_MAX  108
 #endif
 
 struct gaih_service
@@ -177,9 +177,9 @@ gaih_local (const char *name, const struct gaih_service *service,
       if (! tp->name[0])
 	{
 	  if (req->ai_socktype)
-	    return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
+	    return GAIH_OKIFUNSPEC | -EAI_SOCKTYPE;
 	  else
-	    return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
+	    return GAIH_OKIFUNSPEC | -EAI_SERVICE;
 	}
     }
 
@@ -249,9 +249,10 @@ gaih_local (const char *name, const struct gaih_service *service,
 }
 #endif	/* 0 */
 
+
 static int
 gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
-	       const struct addrinfo *req, struct gaih_servtuple *st)
+		const struct addrinfo *req, struct gaih_servtuple *st)
 {
   struct servent *s;
   size_t tmpbuflen = 1024;
@@ -362,6 +363,7 @@ typedef enum nss_status (*nss_getcanonname_r)
    int *errnop, int *h_errnop);
 extern service_user *__nss_hosts_database attribute_hidden;
 
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai,
@@ -389,9 +391,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
       if (! tp->name[0])
 	{
 	  if (req->ai_socktype)
-	    return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
+	    return GAIH_OKIFUNSPEC | -EAI_SOCKTYPE;
 	  else
-	    return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
+	    return GAIH_OKIFUNSPEC | -EAI_SERVICE;
 	}
     }
 
@@ -399,7 +401,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   if (service != NULL)
     {
       if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
-	return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
+	return GAIH_OKIFUNSPEC | -EAI_SERVICE;
 
       if (service->num < 0)
 	{
@@ -443,7 +445,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		  pst = &(newp->next);
 		}
 	      if (st == (struct gaih_servtuple *) &nullserv)
-		return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
+		return GAIH_OKIFUNSPEC | -EAI_SERVICE;
 	    }
 	}
       else
@@ -538,16 +540,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	  else
 	    return -EAI_ADDRFAMILY;
 
-	dupname:
 	  if (req->ai_flags & AI_CANONNAME)
-	    {
-	      canon = strdup (name);
-	      if (canon == NULL)
-		return -EAI_MEMORY;
-	    }
+	    canon = name;
 	}
-
-      if (at->family == AF_UNSPEC)
+      else if (at->family == AF_UNSPEC)
 	{
 	  char *namebuf = (char *) name;
 	  char *scope_delim = strchr (name, SCOPE_DELIMITER);
@@ -595,7 +591,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		    }
 		}
 
-	      goto dupname;
+	      if (req->ai_flags & AI_CANONNAME)
+		canon = name;
 	    }
 	}
 
@@ -689,7 +686,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		    }
 		  /* We made requests but they turned out no data.
 		     The name is known, though.  */
-		  return (GAIH_OKIFUNSPEC | -EAI_NODATA);
+		  return GAIH_OKIFUNSPEC | -EAI_NODATA;
 		}
 
 	      goto process_list;
@@ -756,7 +753,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		  free (air);
 
 		  if (at->family == AF_UNSPEC)
-		    return (GAIH_OKIFUNSPEC | -EAI_NONAME);
+		    return GAIH_OKIFUNSPEC | -EAI_NONAME;
 
 		  goto process_list;
 		}
@@ -898,13 +895,13 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 	      /* We made requests but they turned out no data.  The name
 		 is known, though.  */
-	      return (GAIH_OKIFUNSPEC | -EAI_NODATA);
+	      return GAIH_OKIFUNSPEC | -EAI_NODATA;
 	    }
 	}
 
     process_list:
       if (at->family == AF_UNSPEC)
-	return (GAIH_OKIFUNSPEC | -EAI_NONAME);
+	return GAIH_OKIFUNSPEC | -EAI_NONAME;
     }
   else
     {
@@ -1098,6 +1095,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   return 0;
 }
 
+#if 0
 static const struct gaih gaih[] =
   {
     { PF_INET6, gaih_inet },
@@ -1107,6 +1105,7 @@ static const struct gaih gaih[] =
 #endif
     { PF_UNSPEC, NULL }
   };
+#endif
 
 struct sort_result
 {
@@ -1114,6 +1113,7 @@ struct sort_result
   struct sockaddr_storage source_addr;
   uint8_t source_addr_len;
   bool got_source_addr;
+  uint8_t source_addr_flags;
 };
 
 
@@ -1204,7 +1204,7 @@ static const struct prefixlist default_precedence[] =
       96, 20 },
     { { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
 				  0x0000, 0xffff, 0x0000, 0x0000 } } },
-      96, 10 },
+      96, 100 },
     { { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
 				  0x0000, 0x0000, 0x0000, 0x0000 } } },
       0, 40 }
@@ -1336,8 +1336,16 @@ rfc3484_sort (const void *p1, const void *p2)
     }
 
 
-  /* Rule 3: Avoid deprecated addresses.
-     That's something only the kernel could decide.  */
+  /* Rule 3: Avoid deprecated addresses.  */
+  if (a1->got_source_addr)
+    {
+      if (!(a1->source_addr_flags & in6ai_deprecated)
+	  && (a2->source_addr_flags & in6ai_deprecated))
+	return -1;
+      if ((a1->source_addr_flags & in6ai_deprecated)
+	  && !(a2->source_addr_flags & in6ai_deprecated))
+	return 1;
+    }
 
   /* Rule 4: Prefer home addresses.
      Another thing only the kernel can decide.  */
@@ -1372,8 +1380,18 @@ rfc3484_sort (const void *p1, const void *p2)
     return 1;
 
 
-  /* Rule 7: Prefer native transport.
-     XXX How to recognize tunnels?  */
+  /* Rule 7: Prefer native transport.  */
+  if (a1->got_source_addr)
+    {
+      if (!(a1->source_addr_flags & in6ai_temporary)
+	  && (a1->source_addr_flags & in6ai_temporary))
+	return -1;
+      if ((a1->source_addr_flags & in6ai_temporary)
+	  && !(a1->source_addr_flags & in6ai_temporary))
+	return -1;
+
+      /* XXX Do we need to check anything beside temporary addresses?  */
+    }
 
 
   /* Rule 8: Prefer smaller scope.  */
@@ -1454,15 +1472,23 @@ rfc3484_sort (const void *p1, const void *p2)
 }
 
 
+static int
+in6aicmp (const void *p1, const void *p2)
+{
+  struct in6addrinfo *a1 = (struct in6addrinfo *) p1;
+  struct in6addrinfo *a2 = (struct in6addrinfo *) p2;
+
+  return memcmp (a1->addr, a2->addr, sizeof (a1->addr));
+}
+
+
 int
 getaddrinfo (const char *name, const char *service,
 	     const struct addrinfo *hints, struct addrinfo **pai)
 {
-  int i = 0, j = 0, last_i = 0;
+  int i = 0, last_i = 0;
   int nresults = 0;
-  struct addrinfo *p = NULL, **end;
-  const struct gaih *g = gaih;
-  const struct gaih *pg = NULL;
+  struct addrinfo *p = NULL;
   struct gaih_service gaih_service, *pservice;
   struct addrinfo local_hints;
 
@@ -1490,15 +1516,23 @@ getaddrinfo (const char *name, const char *service,
   if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
     return EAI_BADFLAGS;
 
+  struct in6addrinfo *in6ai;
+  size_t in6ailen;
+  bool seen_ipv4 = false;
+  bool seen_ipv6 = false;
+  /* We might need information about what kind of interfaces are available.
+     But even if AI_ADDRCONFIG is not used, if the user requested IPv6
+     addresses we have to know whether an address is deprecated or
+     temporary.  */
+  if ((hints->ai_flags & AI_ADDRCONFIG) || hints->ai_family == PF_UNSPEC
+      || hints->ai_family == PF_INET6)
+    /* Determine whether we have IPv4 or IPv6 interfaces or both.  We
+       cannot cache the results since new interfaces could be added at
+       any time.  */
+    __check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen);
+
   if (hints->ai_flags & AI_ADDRCONFIG)
     {
-      /* Determine whether we have IPv4 or IPv6 interfaces or both.
-	 We cannot cache the results since new interfaces could be
-	 added at any time.  */
-      bool seen_ipv4;
-      bool seen_ipv6;
-      __check_pf (&seen_ipv4, &seen_ipv6);
-
       /* Now make a decision on what we return, if anything.  */
       if (hints->ai_family == PF_UNSPEC && (seen_ipv4 || seen_ipv6))
 	{
@@ -1513,8 +1547,11 @@ getaddrinfo (const char *name, const char *service,
 	}
       else if ((hints->ai_family == PF_INET && ! seen_ipv4)
 	       || (hints->ai_family == PF_INET6 && ! seen_ipv6))
-	/* We cannot possibly return a valid answer.  */
-	return EAI_NONAME;
+	{
+	  /* We cannot possibly return a valid answer.  */
+	  free (in6ai);
+	  return EAI_NONAME;
+	}
     }
 
   if (service && service[0])
@@ -1525,7 +1562,10 @@ getaddrinfo (const char *name, const char *service,
       if (*c != '\0')
 	{
 	  if (hints->ai_flags & AI_NUMERICSERV)
-	    return EAI_NONAME;
+	    {
+	      free (in6ai);
+	      return EAI_NONAME;
+	    }
 
 	  gaih_service.num = -1;
 	}
@@ -1535,12 +1575,21 @@ getaddrinfo (const char *name, const char *service,
   else
     pservice = NULL;
 
+  struct addrinfo **end;
   if (pai)
     end = &p;
   else
     end = NULL;
 
   unsigned int naddrs = 0;
+#if 0
+  /* If we would support more protocols than just IPv4 and IPv6 we
+     would iterate over a table with appropriate callback functions.
+     Since we currently only handle IPv4 and IPv6 this is not
+     necessary.  */
+  const struct gaih *g = gaih;
+  const struct gaih *pg = NULL;
+  int j = 0;
   while (g->gaih)
     {
       if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
@@ -1564,6 +1613,7 @@ getaddrinfo (const char *name, const char *service,
 		    }
 
 		  freeaddrinfo (p);
+		  free (in6ai);
 
 		  return -(i & GAIH_EAI);
 		}
@@ -1579,7 +1629,35 @@ getaddrinfo (const char *name, const char *service,
     }
 
   if (j == 0)
-    return EAI_FAMILY;
+    {
+      free (in6ai);
+      return EAI_FAMILY;
+    }
+#else
+  if (hints->ai_family == AF_UNSPEC || hints->ai_family == AF_INET
+      || hints->ai_family == AF_INET6)
+    {
+      last_i = gaih_inet (name, pservice, hints, end, &naddrs);
+      if (last_i != 0)
+	{
+	  freeaddrinfo (p);
+	  free (in6ai);
+
+	  return -(i & GAIH_EAI);
+	}
+      if (end)
+	while (*end)
+	  {
+	    end = &((*end)->ai_next);
+	    ++nresults;
+	  }
+    }
+  else
+    {
+      free (in6ai);
+      return EAI_FAMILY;
+    }
+#endif
 
   if (naddrs > 1)
     {
@@ -1589,6 +1667,11 @@ getaddrinfo (const char *name, const char *service,
       struct addrinfo *last = NULL;
       char *canonname = NULL;
 
+      /* If we have information about deprecated and temporary address
+	 sort the array now.  */
+      if (in6ai != NULL)
+	qsort (in6ai, in6ailen, sizeof (*in6ai), in6aicmp);
+
       for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next)
 	{
 	  results[i].dest_addr = q;
@@ -1603,9 +1686,12 @@ getaddrinfo (const char *name, const char *service,
 		      results[i - 1].source_addr_len);
 	      results[i].source_addr_len = results[i - 1].source_addr_len;
 	      results[i].got_source_addr = results[i - 1].got_source_addr;
+	      results[i].source_addr_flags = results[i - 1].source_addr_flags;
 	    }
 	  else
 	    {
+	      results[i].source_addr_flags = 0;
+
 	      /* We overwrite the type with SOCK_DGRAM since we do not
 		 want connect() to connect to the other side.  If we
 		 cannot determine the source address remember this
@@ -1620,6 +1706,20 @@ getaddrinfo (const char *name, const char *service,
 		{
 		  results[i].source_addr_len = sl;
 		  results[i].got_source_addr = true;
+
+		  if (q->ai_family == PF_INET6 && in6ai != NULL)
+		    {
+		      /* See whether the address is the list of deprecated
+			 or temporary addresses.  */
+		      struct in6addrinfo tmp;
+		      memcpy (tmp.addr, q->ai_addr, IN6ADDRSZ);
+
+		      struct in6addrinfo *found
+			= bsearch (&tmp, in6ai, in6ailen, sizeof (*in6ai),
+				   in6aicmp);
+		      if (found != NULL)
+			results[i].source_addr_flags = found->flags;
+		    }
 		}
 	      else
 		/* Just make sure that if we have to process the same
@@ -1653,6 +1753,8 @@ getaddrinfo (const char *name, const char *service,
       p->ai_canonname = canonname;
     }
 
+  free (in6ai);
+
   if (p)
     {
       *pai = p;