about summary refs log tree commit diff
path: root/sysdeps
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2006-04-16 21:34:32 +0000
committerUlrich Drepper <drepper@redhat.com>2006-04-16 21:34:32 +0000
commit3af48b5b3106e1bb1de4efcabb86cd1fafdff8af (patch)
tree680c8164581df5834d65b31796392173461d9325 /sysdeps
parenta238728234a94cb8e8024e4718356ff808487a88 (diff)
downloadglibc-3af48b5b3106e1bb1de4efcabb86cd1fafdff8af.tar.gz
glibc-3af48b5b3106e1bb1de4efcabb86cd1fafdff8af.tar.xz
glibc-3af48b5b3106e1bb1de4efcabb86cd1fafdff8af.zip
* include/ifaddrs.h: Define struct in6addrinfo.
	Add two more parameters to __check_pf.
	* sysdeps/unix/sysv/linux/check_pf.c: When using the netlink
	interface, determine whether IPv6 addresses are deprecated or
	temporary.  Create array of those addresses.
	* inet/check_pf.c: Always tell caller there are no depracated
	and temporary addresses.
	* sysdeps/posix/getaddrinfo.c: Pretty printing.
	(struct sort_result): Add source_addr_flags field.
	(rfc3484_sort): Implement rule 3 and 7.
	(in6aicmp): New function.
	(getaddrinfo): Call __check_pf also when we need info about IPv6
	source addresses.  When creating array for sorting addresses,
	look up deprecated and temporary addresses returned by __check_pf
	and add flag if necessary.
Diffstat (limited to 'sysdeps')
-rw-r--r--sysdeps/posix/getaddrinfo.c127
-rw-r--r--sysdeps/unix/sysv/linux/check_pf.c82
2 files changed, 174 insertions, 35 deletions
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 636ab743d4..5746f4673e 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
@@ -684,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;
@@ -751,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;
 		}
@@ -893,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
     {
@@ -1109,6 +1111,7 @@ struct sort_result
   struct sockaddr_storage source_addr;
   uint8_t source_addr_len;
   bool got_source_addr;
+  uint8_t source_addr_flags;
 };
 
 
@@ -1331,8 +1334,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.  */
@@ -1367,8 +1378,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.  */
@@ -1449,6 +1470,16 @@ 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)
@@ -1485,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))
 	{
@@ -1508,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])
@@ -1520,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;
 	}
@@ -1559,6 +1604,7 @@ getaddrinfo (const char *name, const char *service,
 		    }
 
 		  freeaddrinfo (p);
+		  free (in6ai);
 
 		  return -(i & GAIH_EAI);
 		}
@@ -1574,7 +1620,10 @@ getaddrinfo (const char *name, const char *service,
     }
 
   if (j == 0)
-    return EAI_FAMILY;
+    {
+      free (in6ai);
+      return EAI_FAMILY;
+    }
 
   if (naddrs > 1)
     {
@@ -1584,6 +1633,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;
@@ -1598,9 +1652,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
@@ -1615,6 +1672,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
@@ -1648,6 +1719,8 @@ getaddrinfo (const char *name, const char *service,
       p->ai_canonname = canonname;
     }
 
+  free (in6ai);
+
   if (p)
     {
       *pai = p;
diff --git a/sysdeps/unix/sysv/linux/check_pf.c b/sysdeps/unix/sysv/linux/check_pf.c
index ae6f71d89c..75b7dd0d82 100644
--- a/sysdeps/unix/sysv/linux/check_pf.c
+++ b/sysdeps/unix/sysv/linux/check_pf.c
@@ -29,11 +29,18 @@
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 
+#include <not-cancel.h>
 #include <kernel-features.h>
 
 
+#ifndef IFA_F_TEMPORARY
+# define IFA_F_TEMPORARY IFA_F_SECONDARY
+#endif
+
+
 static int
-make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6)
+make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
+	      struct in6addrinfo **in6ai, size_t *in6ailen)
 {
   struct
   {
@@ -63,6 +70,12 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6)
   bool done = false;
   char buf[4096];
   struct iovec iov = { buf, sizeof (buf) };
+  struct in6ailist
+  {
+    struct in6addrinfo info;
+    struct in6ailist *next;
+  } *in6ailist = NULL;
+  size_t in6ailistlen = 0;
 
   do
     {
@@ -101,6 +114,42 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6)
 		  break;
 		case AF_INET6:
 		  *seen_ipv6 = true;
+
+		  if (ifam->ifa_flags & (IFA_F_DEPRECATED | IFA_F_TEMPORARY))
+		    {
+		      struct rtattr *rta = IFA_RTA (ifam);
+		      size_t len = (nlmh->nlmsg_len
+				    - NLMSG_LENGTH (sizeof (*ifam)));
+		      void *local = NULL;
+		      void *address = NULL;
+		      while (RTA_OK (rta, len))
+			{
+			  switch (rta->rta_type)
+			    {
+			    case IFA_LOCAL:
+			      local = RTA_DATA (rta);
+			      break;
+
+			    case IFA_ADDRESS:
+			      address = RTA_DATA (ta);
+			      break;
+			    }
+
+			  rta = RTA_NEXT (rta, len);
+			}
+
+		      struct in6ailist *newp = alloca (sizeof (*newp));
+		      newp->info.flags = (((ifam->ifa_flags & IFA_F_DEPRECATED)
+					   ? in6ai_deprecated : 0)
+					  | ((ifam->ifa_flags
+					      & IFA_F_TEMPORARY)
+					     ? in6ai_temporary : 0));
+		      memcpy (newp->info.addr, address ?: local,
+			      sizeof (newp->info.addr));
+		      newp->next = in6ailist;
+		      in6ailsit = newp;
+		      ++in6ailistlen;
+		    }
 		  break;
 		default:
 		  /* Ignore.  */
@@ -110,12 +159,27 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6)
 	  else if (nlmh->nlmsg_type == NLMSG_DONE)
 	    /* We found the end, leave the loop.  */
 	    done = true;
-	  else ;
 	}
     }
   while (! done);
 
-  __close (fd);
+  close_not_cancel_no_status (fd);
+
+  if (in6ailist != NULL)
+    {
+      *in6ai = malloc (in6ailistlen * sizeof (**in6ai));
+      if (*in6ai == NULL)
+	return -1;
+
+      *in6ailen = in6ailistlen;
+
+      do
+	{
+	  (*in6ai)[--in6ailistlen] = in6ailist->info;
+	  in6ailist = in6ailist->next;
+	}
+      while (in6ailist != NULL);
+    }
 
   return 0;
 }
@@ -133,8 +197,12 @@ extern int __no_netlink_support attribute_hidden;
 
 void
 attribute_hidden
-__check_pf (bool *seen_ipv4, bool *seen_ipv6)
+__check_pf (bool *seen_ipv4, bool *seen_ipv6,
+	    struct in6addrinfo **in6ai, size_t *in6ailen)
 {
+  *in6ai = NULL;
+  *in6ailen = 0;
+
   if (! __no_netlink_support)
     {
       int fd = __socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
@@ -148,7 +216,8 @@ __check_pf (bool *seen_ipv4, bool *seen_ipv6)
       if (fd >= 0
 	  && __bind (fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) == 0
 	  && __getsockname (fd, (struct sockaddr *) &nladdr, &addr_len) == 0
-	  && make_request (fd, nladdr.nl_pid, seen_ipv4, seen_ipv6) == 0)
+	  && make_request (fd, nladdr.nl_pid, seen_ipv4, seen_ipv6,
+			   in6ai, in6ailen) == 0)
 	/* It worked.  */
 	return;
 
@@ -178,9 +247,6 @@ __check_pf (bool *seen_ipv4, bool *seen_ipv6)
       return;
     }
 
-  *seen_ipv4 = false;
-  *seen_ipv6 = false;
-
   struct ifaddrs *runp;
   for (runp = ifa; runp != NULL; runp = runp->ifa_next)
     if (runp->ifa_addr->sa_family == PF_INET)