summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2006-05-04 06:38:07 +0000
committerUlrich Drepper <drepper@redhat.com>2006-05-04 06:38:07 +0000
commit77dd4c3be8d3acd04190ad54a4585ef2d4108ba2 (patch)
treeb0be03383534addc39fee4f9eabcee4671643fb5
parent5631e740ff086e9c978e817509bee9d73bceeff3 (diff)
* 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.
-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;