* 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.
This commit is contained in:
Ulrich Drepper 2006-04-16 21:34:32 +00:00
parent a238728234
commit 3af48b5b31
6 changed files with 221 additions and 39 deletions

View File

@ -1,3 +1,21 @@
2006-04-16 Ulrich Drepper <drepper@redhat.com>
* 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.
2006-04-15 Ulrich Drepper <drepper@redhat.com>
* sysdeps/posix/getaddrinfo.c: Fix precedence for IP V4-to-V6

10
NEWS
View File

@ -1,9 +1,17 @@
GNU C Library NEWS -- history of user-visible changes. 2006-03-01
GNU C Library NEWS -- history of user-visible changes. 2006-04-16
Copyright (C) 1992-2002,2003,2004,2005,2006 Free Software Foundation, Inc.
See the end for copying conditions.
Please send GNU C library bug reports via <http://sources.redhat.com/bugzilla/>
using `glibc' in the "product" field.
Version 2.5
* For Linux, the sorting of addresses returned by getaddrinfo now also
handles rules 3 and 7 from RFC 3484. Implemented by Ulrich Drepper.
* New Linux interfaces: splice, tee, sync_file_range.
Version 2.4

View File

@ -5,6 +5,17 @@
libc_hidden_proto (getifaddrs)
libc_hidden_proto (freeifaddrs)
extern void __check_pf (bool *seen_ipv4, bool *seen_ipv6) attribute_hidden;
struct in6addrinfo
{
enum {
in6ai_deprecated = 1,
in6ai_temporary = 2
} flags;
uint32_t addr[4];
};
extern void __check_pf (bool *seen_ipv4, bool *seen_ipv6,
struct in6addrinfo **in6ai, size_t *in6ailen)
attribute_hidden;
#endif /* ifaddrs.h */

View File

@ -1,5 +1,5 @@
/* Determine protocol families for which interfaces exist. Generic version.
Copyright (C) 2003 Free Software Foundation, Inc.
Copyright (C) 2003, 2006 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
@ -23,8 +23,14 @@
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)
{
/* By default we have no way to determine information about
deprecated and temporary addresses. */
*in6ai = NULL;
*in6ailen = 0;
/* Get the interface list via getifaddrs. */
struct ifaddrs *ifa = NULL;
if (getifaddrs (&ifa) != 0)

View File

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

View File

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