about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog10
-rw-r--r--ChangeLog.56
-rw-r--r--posix/gai.conf40
-rw-r--r--posix/tst-rfc3484-2.c12
-rw-r--r--posix/tst-rfc3484.c12
-rw-r--r--sysdeps/posix/getaddrinfo.c412
6 files changed, 456 insertions, 36 deletions
diff --git a/ChangeLog b/ChangeLog
index 35396998ce..32cbbfef7e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2006-05-03  Ulrich Drepper  <drepper@redhat.com>
+
+	* sysdeps/posix/getaddrinfo.c: Implement configuration file
+	handling.  /etc/gai.conf can contain replacements for the label
+	and precedence table.  Fix byte order of default label and
+	precedence table.
+	* posix/gai.conf: New file.
+	* posix/tst-rfc3484.c: Adjust for changes to getaddrinfo.c.
+	* posix/tst-rfc3484-2.c: Likewise.
+
 2006-05-02  Ulrich Drepper  <drepper@redhat.com>
 
 	[BZ #1201]
diff --git a/ChangeLog.5 b/ChangeLog.5
index 8efe34dcd9..ef38fb2bf2 100644
--- a/ChangeLog.5
+++ b/ChangeLog.5
@@ -6911,9 +6911,9 @@ Sat Sep 30 11:47:05 1995  Roland McGrath  <roland@churchy.gnu.ai.mit.edu>
 
 Fri Sep 29 15:07:10 1995  Ulrich Drepper  <drepper@ipd.info.uni-karlsruhe.de>
 
- 	* sysdeps/unix/sysv/linux/adjtime.c (__adjtime):
- 	Change name of field `mode' in `struct timex' to `modes'.
-	Linux-1.3.28 updates this name according to RFC 1489.
+	* sysdeps/unix/sysv/linux/adjtime.c (__adjtime):
+	Change name of field `mode' in `struct timex' to `modes'.
+	Linux-1.3.28 updates this name according to RFC 1589.
 
 Thu Sep 28 13:05:54 1995  Roland McGrath  <roland@churchy.gnu.ai.mit.edu>
 
diff --git a/posix/gai.conf b/posix/gai.conf
new file mode 100644
index 0000000000..5f063f5c3c
--- /dev/null
+++ b/posix/gai.conf
@@ -0,0 +1,40 @@
+# Configuration for getaddrinfo(3).
+#
+# So far only configuration for the destination address sorting is needed.
+# RFC 3484 governs the sorting.  But the RFC also says that system
+# administrators should be able to overwrite the defaults.  This can be
+# achieved here.
+#
+# All lines have an initial identifier specifying the option followed by
+# up to two values.  Information specified in this file replaces the
+# default information.  Complete absence of data of one kind causes the
+# appropriate default information to be used.  The supported commands include:
+#
+# reload  <yes|no>
+#    If set to yes, each getaddrinfo(3) call will check whether this file
+#    changed and if necessary reload.  This option should not really be
+#    used.  There are possible runtime problems.  The default is no.
+#
+# label   <mask>   <value>
+#    Add another rule to the RFC 3484 label table.  See section 2.1 in
+#    RFC 3484.  The default is:
+#
+#label  ::1/128       0
+#label  ::/0          1
+#label  2002::/16     2
+#label ::/96          3
+#label ::ffff:0:0/96  4
+#
+# precedence  <mask>   <value>
+#    Add another rule the to RFC 3484 precendence table.  See section 2.1
+#    and 10.3 in RFC 3484.  The default is:
+#
+#precendence  ::1/128       50
+#precendence  ::/0          40
+#precendence  2002::/16     30
+#precendence ::/96          20
+#precendence ::ffff:0:0/96  10
+#
+#    For sites which prefer IPv4 connections change the last line to
+#
+#precendence ::ffff:0:0/96  100
diff --git a/posix/tst-rfc3484-2.c b/posix/tst-rfc3484-2.c
index c25b0c2172..2536da8f87 100644
--- a/posix/tst-rfc3484-2.c
+++ b/posix/tst-rfc3484-2.c
@@ -45,9 +45,21 @@ service_user *__nss_hosts_database attribute_hidden;
 #endif
 
 
+ssize_t
+__getline (char **lineptr, size_t *n, FILE *s)
+{
+  *lineptr = NULL;
+  *n = 0;
+  return 0;
+}
+
+
 static int
 do_test (void)
 {
+  labels = default_labels;
+  precedence = default_precedence;
+
   struct sockaddr_in so1;
   so1.sin_family = AF_INET;
   so1.sin_addr.s_addr = h (0xc0a85f19);
diff --git a/posix/tst-rfc3484.c b/posix/tst-rfc3484.c
index 8d273aec84..2e74e9737f 100644
--- a/posix/tst-rfc3484.c
+++ b/posix/tst-rfc3484.c
@@ -64,9 +64,21 @@ static int expected[naddrs] =
   };
 
 
+ssize_t
+__getline (char **lineptr, size_t *n, FILE *s)
+{
+  *lineptr = NULL;
+  *n = 0;
+  return 0;
+}
+
+
 static int
 do_test (void)
 {
+  labels = default_labels;
+  precedence = default_precedence;
+
   struct sockaddr_in so;
   so.sin_family = AF_INET;
   so.sin_addr.s_addr = h (0xc0a85f19);
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index fa3bbe44cf..fc0928676d 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -36,23 +36,27 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /* This software is Copyright 1996 by Craig Metz, All Rights Reserved.  */
 
 #include <assert.h>
+#include <ctype.h>
 #include <errno.h>
 #include <ifaddrs.h>
 #include <netdb.h>
 #include <resolv.h>
 #include <stdbool.h>
 #include <stdio.h>
+#include <stdio_ext.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 #include <arpa/inet.h>
-#include <sys/socket.h>
+#include <net/if.h>
 #include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/un.h>
 #include <sys/utsname.h>
-#include <net/if.h>
+#include <unistd.h>
 #include <nsswitch.h>
+#include <bits/libc-lock.h>
 #include <not-cancel.h>
 #include <nscd/nscd-client.h>
 #include <nscd/nscd_proto.h>
@@ -1161,59 +1165,77 @@ get_scope (const struct sockaddr_storage *ss)
 }
 
 
-/* XXX The system administrator should be able to install other
-   tables.  We need to make this configurable.  The problem is that
-   the kernel is also involved since it needs the same table.  */
-static const struct prefixlist
+struct prefixentry
 {
   struct in6_addr prefix;
   unsigned int bits;
   int val;
-} default_labels[] =
+};
+
+
+/* The label table.  */
+static const struct prefixentry *labels;
+
+/* Default labels.  */
+static const struct prefixentry default_labels[] =
   {
     /* See RFC 3484 for the details.  */
-    { { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
-				  0x0000, 0x0000, 0x0000, 0x0001 } } },
+    { { .in6_u
+	= { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } },
       128, 0 },
-    { { .in6_u = { .u6_addr16 = { 0x2002, 0x0000, 0x0000, 0x0000,
-				  0x0000, 0x0000, 0x0000, 0x0000 } } },
+    { { .in6_u
+	= { .u6_addr8 = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
       16, 2 },
-    { { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
-				  0x0000, 0x0000, 0x0000, 0x0000 } } },
+    { { .in6_u
+	= { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
       96, 3 },
-    { { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
-				  0x0000, 0xffff, 0x0000, 0x0000 } } },
+    { { .in6_u
+	= { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } } },
       96, 4 },
-    { { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
-				  0x0000, 0x0000, 0x0000, 0x0000 } } },
+    { { .in6_u
+	= { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
       0, 1 }
   };
 
 
-static const struct prefixlist default_precedence[] =
+/* The precedence table.  */
+static const struct prefixentry *precedence;
+
+/* The default precedences.  */
+static const struct prefixentry default_precedence[] =
   {
     /* See RFC 3484 for the details.  */
-    { { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
-				  0x0000, 0x0000, 0x0000, 0x0001 } } },
+    { { .in6_u
+	= { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } },
       128, 50 },
-    { { .in6_u = { .u6_addr16 = { 0x2002, 0x0000, 0x0000, 0x0000,
-				  0x0000, 0x0000, 0x0000, 0x0000 } } },
+    { { .in6_u
+	= { .u6_addr8 = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
       16, 30 },
-    { { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
-				  0x0000, 0x0000, 0x0000, 0x0000 } } },
+    { { .in6_u
+	= { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
       96, 20 },
-    { { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
-				  0x0000, 0xffff, 0x0000, 0x0000 } } },
+    { { .in6_u
+	= { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } } },
       96, 100 },
-    { { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
-				  0x0000, 0x0000, 0x0000, 0x0000 } } },
+    { { .in6_u
+	= { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
       0, 40 }
   };
 
 
 static int
-match_prefix (const struct sockaddr_storage *ss, const struct prefixlist *list,
-	      int default_val)
+match_prefix (const struct sockaddr_storage *ss,
+	      const struct prefixentry *list, int default_val)
 {
   int idx;
   struct sockaddr_in6 in6_mem;
@@ -1277,7 +1299,7 @@ static int
 get_label (const struct sockaddr_storage *ss)
 {
   /* XXX What is a good default value?  */
-  return match_prefix (ss, default_labels, INT_MAX);
+  return match_prefix (ss, labels, INT_MAX);
 }
 
 
@@ -1285,7 +1307,7 @@ static int
 get_precedence (const struct sockaddr_storage *ss)
 {
   /* XXX What is a good default value?  */
-  return match_prefix (ss, default_precedence, 0);
+  return match_prefix (ss, precedence, 0);
 }
 
 
@@ -1482,6 +1504,323 @@ in6aicmp (const void *p1, const void *p2)
 }
 
 
+/* Name of the config file for RFC 3484 sorting (for now).  */
+#define GAICONF_FNAME "/etc/gai.conf"
+
+
+/* Nozero if we are supposed to reload the config file automatically
+   whenever it changed.  */
+static int gaiconf_reload_flag;
+
+/* Last modification time.  */
+static struct timespec gaiconf_mtime;
+
+
+libc_freeres_fn(fini)
+{
+  if (labels != default_labels)
+    {
+      const struct prefixentry *old = labels;
+      labels = default_labels;
+      free ((void *) old);
+    }
+
+  if (precedence != default_precedence)
+    {
+      const struct prefixentry *old = precedence;
+      precedence = default_precedence;
+      free ((void *) old);
+    }
+}
+
+
+struct prefixlist
+{
+  struct prefixentry entry;
+  struct prefixlist *next;
+};
+
+
+static void
+free_prefixlist (struct prefixlist *list)
+{
+  while (list != NULL)
+    {
+      struct prefixlist *oldp = list;
+      list = list->next;
+      free (oldp);
+    }
+}
+
+
+static int
+prefixcmp (const void *p1, const void *p2)
+{
+  const struct prefixentry *e1 = (const struct prefixentry *) p1;
+  const struct prefixentry *e2 = (const struct prefixentry *) p2;
+
+  if (e1->bits < e2->bits)
+    return 1;
+  if (e1->bits == e2->bits)
+    return 0;
+  return -1;
+}
+
+
+static void
+gaiconf_init (void)
+{
+  struct prefixlist *labellist = NULL;
+  size_t nlabellist = 0;
+  bool labellist_nullbits = false;
+  struct prefixlist *precedencelist = NULL;
+  size_t nprecedencelist = 0;
+  bool precedencelist_nullbits = false;
+
+  FILE *fp = fopen (GAICONF_FNAME, "rc");
+  if (fp != NULL)
+    {
+      struct stat64 st;
+      if (__fxstat64 (_STAT_VER, fileno (fp), &st) != 0)
+	{
+	  fclose (fp);
+	  goto no_file;
+	}
+
+      char *line = NULL;
+      size_t linelen = 0;
+
+      __fsetlocking (fp, FSETLOCKING_BYCALLER);
+
+      while (!feof_unlocked (fp))
+	{
+	  ssize_t n = __getline (&line, &linelen, fp);
+	  if (n <= 0)
+	    break;
+
+	  /* Handle comments.  No escaping possible so this is easy.  */
+	  char *cp = strchr (line, '#');
+	  if (cp != NULL)
+	    *cp = '\0';
+
+	  cp = line;
+	  while (isspace (*cp))
+	    ++cp;
+
+	  char *cmd = cp;
+	  while (*cp != '\0' && !isspace (*cp))
+	    ++cp;
+	  size_t cmdlen = cp - cmd;
+
+	  if (*cp != '\0')
+	    *cp++ = '\0';
+	  while (isspace (*cp))
+	    ++cp;
+
+	  char *val1 = cp;
+	  while (*cp != '\0' && !isspace (*cp))
+	    ++cp;
+	  size_t val1len = cp - cmd;
+
+	  /* We always need at least two values.  */
+	  if (val1len == 0)
+	    continue;
+
+	  if (*cp != '\0')
+	    *cp++ = '\0';
+	  while (isspace (*cp))
+	    ++cp;
+
+	  char *val2 = cp;
+	  while (*cp != '\0' && !isspace (*cp))
+	    ++cp;
+
+	  /*  Ignore the rest of the line.  */
+	  *cp = '\0';
+
+	  struct prefixlist **listp;
+	  size_t *lenp;
+	  bool *nullbitsp;
+	  switch (cmdlen)
+	    {
+	    case 5:
+	      if (strcmp (cmd, "label") == 0)
+		{
+		  struct in6_addr prefix;
+		  unsigned long int bits = 128;
+		  unsigned long int val;
+		  char *endp;
+
+		  listp = &labellist;
+		  lenp = &nlabellist;
+		  nullbitsp = &labellist_nullbits;
+
+		new_elem:
+		  __set_errno (0);
+		  cp = strchr (val1, '/');
+		  if (cp != NULL)
+		    *cp++ = '\0';
+		  if (inet_pton (AF_INET6, val1, &prefix)
+		      && (cp == NULL
+			  || (bits = strtoul (cp, &endp, 10)) != ULONG_MAX
+			  || errno != ERANGE)
+		      && *endp == '\0'
+		      && bits <= INT_MAX
+		      && ((val = strtoul (val2, &endp, 10)) != ULONG_MAX
+			  || errno != ERANGE)
+		      && *endp == '\0'
+		      && val <= INT_MAX)
+		    {
+		      struct prefixlist *newp = malloc (sizeof (*newp));
+		      if (newp == NULL)
+			{
+			  free (line);
+			  fclose (fp);
+			  goto no_file;
+			}
+
+		      memcpy (&newp->entry.prefix, &prefix, sizeof (prefix));
+		      newp->entry.bits = bits;
+		      newp->entry.val = val;
+		      newp->next = *listp;
+		      *listp = newp;
+		      ++*lenp;
+		      *nullbitsp |= bits == 0;
+		    }
+		}
+	      break;
+
+	    case 6:
+	      if (strcmp (cmd, "reload") == 0)
+		gaiconf_reload_flag = strcmp (val1, "yes") == 0;
+	      break;
+
+	    case 10:
+	      if (strcmp (cmd, "precedence") == 0)
+		{
+		  listp = &precedencelist;
+		  lenp = &nprecedencelist;
+		  nullbitsp = &precedencelist_nullbits;
+		  goto new_elem;
+		}
+	      break;
+	    }
+	}
+
+      free (line);
+
+      fclose (fp);
+
+      /* Create the array for the labels.  */
+      struct prefixentry *new_labels;
+      if (nlabellist > 0)
+	{
+	  if (!labellist_nullbits)
+	    ++nlabellist;
+	  new_labels = malloc (nlabellist * sizeof (*new_labels));
+	  if (new_labels == NULL)
+	    goto no_file;
+
+	  int i = nlabellist;
+	  if (!labellist_nullbits)
+	    {
+	      --i;
+	      memset (&new_labels[i].prefix, '\0', sizeof (struct in6_addr));
+	      new_labels[i].bits = 0;
+	      new_labels[i].val = 1;
+	    }
+
+	  struct prefixlist *l = labellist;
+	  while (i-- > 0)
+	    {
+	      new_labels[i] = l->entry;
+	      l = l->next;
+	    }
+	  free_prefixlist (labellist);
+
+	  /* Sort the entries so that the most specific ones are at
+	     the beginning.  */
+	  qsort (new_labels, nlabellist, sizeof (*new_labels), prefixcmp);
+	}
+      else
+	new_labels = (struct prefixentry *) default_labels;
+
+      struct prefixentry *new_precedence;
+      if (nprecedencelist > 0)
+	{
+	  if (!precedencelist_nullbits)
+	    ++nprecedencelist;
+	  new_precedence = malloc (nprecedencelist * sizeof (*new_precedence));
+	  if (new_precedence == NULL)
+	    {
+	      if (new_labels != default_labels)
+		free (new_labels);
+	      goto no_file;
+	    }
+
+	  int i = nprecedencelist;
+	  if (!precedencelist_nullbits)
+	    {
+	      --i;
+	      memset (&new_precedence[i].prefix, '\0',
+		      sizeof (struct in6_addr));
+	      new_precedence[i].bits = 0;
+	      new_precedence[i].val = 40;
+	    }
+
+	  struct prefixlist *l = precedencelist;
+	  while (i-- > 0)
+	    {
+	      new_precedence[i] = l->entry;
+	      l = l->next;
+	    }
+	  free_prefixlist (precedencelist);
+
+	  /* Sort the entries so that the most specific ones are at
+	     the beginning.  */
+	  qsort (new_precedence, nprecedencelist, sizeof (*new_labels),
+		 prefixcmp);
+	}
+      else
+	new_precedence = (struct prefixentry *) default_precedence;
+
+      /* Now we are ready to replace the values.  */
+      const struct prefixentry *old = labels;
+      labels = new_labels;
+      if (old != default_labels)
+	free ((void *) old);
+
+      old = precedence;
+      precedence = new_precedence;
+      if (old != default_precedence)
+	free ((void *) old);
+
+      gaiconf_mtime = st.st_mtim;
+    }
+  else
+    {
+    no_file:
+      free_prefixlist (labellist);
+      free_prefixlist (precedencelist);
+
+      /* If we previously read the file but it is gone now, free the
+	 old data and use the builtin one.  Leave the reload flag
+	 alone.  */
+      fini ();
+    }
+}
+
+
+static void
+gaiconf_reload (void)
+{
+  struct stat64 st;
+  if (__xstat64 (_STAT_VER, GAICONF_FNAME, &st) != 0
+      || memcmp (&st.st_mtim, &gaiconf_mtime, sizeof (gaiconf_mtime)) != 0)
+    gaiconf_init ();
+}
+
+
 int
 getaddrinfo (const char *name, const char *service,
 	     const struct addrinfo *hints, struct addrinfo **pai)
@@ -1661,6 +2000,13 @@ getaddrinfo (const char *name, const char *service,
 
   if (naddrs > 1)
     {
+      /* Read the config file.  */
+      __libc_once_define (static, once);
+      __typeof (once) old_once = once;
+      __libc_once (once, gaiconf_init);
+      if (old_once && gaiconf_reload_flag)
+	gaiconf_reload ();
+
       /* Sort results according to RFC 3484.  */
       struct sort_result results[nresults];
       struct addrinfo *q;