* 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.
This commit is contained in:
Ulrich Drepper 2006-05-04 06:38:07 +00:00
parent 5631e740ff
commit 77dd4c3be8
6 changed files with 456 additions and 36 deletions

View file

@ -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]

View 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>

40
posix/gai.conf Normal file
View file

@ -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

View 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);

View 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);

View 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;