* sysdeps/posix/getaddrinfo.c: Implement configuration file
authorUlrich Drepper <drepper@redhat.com>
Thu, 4 May 2006 06:38:07 +0000 (06:38 +0000)
committerUlrich Drepper <drepper@redhat.com>
Thu, 4 May 2006 06:38:07 +0000 (06:38 +0000)
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.

ChangeLog
ChangeLog.5
posix/gai.conf [new file with mode: 0644]
posix/tst-rfc3484-2.c
posix/tst-rfc3484.c
sysdeps/posix/getaddrinfo.c

index 3539699..32cbbfe 100644 (file)
--- 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]
index 8efe34d..ef38fb2 100644 (file)
@@ -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 (file)
index 0000000..5f063f5
--- /dev/null
@@ -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
index c25b0c2..2536da8 100644 (file)
@@ -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);
index 8d273ae..2e74e97 100644 (file)
@@ -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);
index fa3bbe4..fc09286 100644 (file)
@@ -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;