summaryrefslogtreecommitdiff
path: root/libc/sysdeps/posix
diff options
context:
space:
mode:
authorjoseph <joseph@7b3dc134-2b1b-0410-93df-9e9f96275f8d>2008-02-20 12:48:46 +0000
committerjoseph <joseph@7b3dc134-2b1b-0410-93df-9e9f96275f8d>2008-02-20 12:48:46 +0000
commit8c5ab5ff18f334d83e82d40ce5a15294288d82bb (patch)
tree7595ae8eb0d289b345daf994d3a4dc8b265eeba6 /libc/sysdeps/posix
parent1ee006bef1ce679d91782a9beb3c983cec91cbbe (diff)
downloadeglibc2-8c5ab5ff18f334d83e82d40ce5a15294288d82bb.tar.gz
Merge changes between r4014 and r5247 from /fsf/trunk.
git-svn-id: svn://svn.eglibc.org/trunk@5248 7b3dc134-2b1b-0410-93df-9e9f96275f8d
Diffstat (limited to 'libc/sysdeps/posix')
-rw-r--r--libc/sysdeps/posix/getaddrinfo.c644
1 files changed, 430 insertions, 214 deletions
diff --git a/libc/sysdeps/posix/getaddrinfo.c b/libc/sysdeps/posix/getaddrinfo.c
index 8cf9c6bdf..fb18bba47 100644
--- a/libc/sysdeps/posix/getaddrinfo.c
+++ b/libc/sysdeps/posix/getaddrinfo.c
@@ -61,10 +61,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <nscd/nscd-client.h>
#include <nscd/nscd_proto.h>
-#ifdef HAVE_NETLINK_ROUTE
-# include <kernel-features.h>
-#endif
-
#ifdef HAVE_LIBIDN
extern int __idna_to_ascii_lz (const char *input, char **output, int flags);
extern int __idna_to_unicode_lzlz (const char *input, char **output,
@@ -1001,22 +997,63 @@ gaih_inet (const char *name, const struct gaih_service *service,
struct sort_result
{
struct addrinfo *dest_addr;
- struct sockaddr_storage source_addr;
- size_t service_order;
+ /* Using sockaddr_storage is for now overkill. We only support IPv4
+ and IPv6 so far. If this changes at some point we can adjust the
+ type here. */
+ struct sockaddr_in6 source_addr;
uint8_t source_addr_len;
bool got_source_addr;
uint8_t source_addr_flags;
+ uint8_t prefixlen;
+ uint32_t index;
+ int32_t native;
+};
+
+struct sort_result_combo
+{
+ struct sort_result *results;
+ int nresults;
};
+#if __BYTE_ORDER == __BIG_ENDIAN
+# define htonl_c(n) n
+#else
+# define htonl_c(n) __bswap_constant_32 (n)
+#endif
+
+static const struct scopeentry
+{
+ union
+ {
+ char addr[4];
+ uint32_t addr32;
+ };
+ uint32_t netmask;
+ int32_t scope;
+} default_scopes[] =
+ {
+ /* Link-local addresses: scope 2. */
+ { { { 169, 254, 0, 0 } }, htonl_c (0xffff0000), 2 },
+ { { { 127, 0, 0, 0 } }, htonl_c (0xff000000), 2 },
+ /* Site-local addresses: scope 5. */
+ { { { 10, 0, 0, 0 } }, htonl_c (0xff000000), 5 },
+ { { { 172, 16, 0, 0 } }, htonl_c (0xfff00000), 5 },
+ { { { 192, 168, 0, 0 } }, htonl_c (0xffff0000), 5 },
+ /* Default: scope 14. */
+ { { { 0, 0, 0, 0 } }, htonl_c (0x00000000), 14 }
+ };
+
+/* The label table. */
+static const struct scopeentry *scopes;
+
+
static int
-get_scope (const struct sockaddr_storage *ss)
+get_scope (const struct sockaddr_in6 *in6)
{
int scope;
- if (ss->ss_family == PF_INET6)
+ if (in6->sin6_family == PF_INET6)
{
- const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *) ss;
-
if (! IN6_IS_ADDR_MULTICAST (&in6->sin6_addr))
{
if (IN6_IS_ADDR_LINKLOCAL (&in6->sin6_addr))
@@ -1030,20 +1067,20 @@ get_scope (const struct sockaddr_storage *ss)
else
scope = in6->sin6_addr.s6_addr[1] & 0xf;
}
- else if (ss->ss_family == PF_INET)
+ else if (in6->sin6_family == PF_INET)
{
- const struct sockaddr_in *in = (const struct sockaddr_in *) ss;
- const uint8_t *addr = (const uint8_t *) &in->sin_addr;
-
- /* RFC 3484 specifies how to map IPv6 addresses to scopes.
- 169.254/16 and 127/8 are link-local. */
- if ((addr[0] == 169 && addr[1] == 254) || addr[0] == 127)
- scope = 2;
- else if (addr[0] == 10 || (addr[0] == 172 && (addr[1] & 0xf0) == 16)
- || (addr[0] == 192 && addr[1] == 168))
- scope = 5;
- else
- scope = 14;
+ const struct sockaddr_in *in = (const struct sockaddr_in *) in6;
+
+ size_t cnt = 0;
+ while (1)
+ {
+ if ((in->sin_addr.s_addr & scopes[cnt].netmask)
+ == scopes[cnt].addr32)
+ return scopes[cnt].scope;
+
+ ++cnt;
+ }
+ /* NOTREACHED */
}
else
/* XXX What is a good default? */
@@ -1068,22 +1105,22 @@ static const struct prefixentry *labels;
static const struct prefixentry default_labels[] =
{
/* See RFC 3484 for the details. */
- { { .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_addr8 = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 16, 2 },
- { { .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_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } } },
- 96, 4 },
+ { { .__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_addr8 = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+ }, 16, 2 },
+ { { .__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_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } }
+ }, 96, 4 },
/* The next two entries differ from RFC 3484. We need to treat
IPv6 site-local addresses special because they are never NATed,
unlike site-locale IPv4 addresses. If this would not happen, on
@@ -1091,22 +1128,23 @@ static const struct prefixentry default_labels[] =
sorting would prefer the IPv6 site-local addresses, causing
unnecessary delays when trying to connect to a global IPv6 address
through a site-local IPv6 address. */
- { { .in6_u
- = { .u6_addr8 = { 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 10, 5 },
- { { .in6_u
- = { .u6_addr8 = { 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 7, 6 },
- { { .in6_u
- = { .u6_addr8 = { 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 32, 7 },
- { { .in6_u
- = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 0, 1 }
+ { { .__in6_u
+ = { .__u6_addr8 = { 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+ }, 10, 5 },
+ { { .__in6_u
+ = { .__u6_addr8 = { 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+ }, 7, 6 },
+ /* Additional rule for Teredo tunnels. */
+ { { .__in6_u
+ = { .__u6_addr8 = { 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+ }, 32, 7 },
+ { { .__in6_u
+ = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+ }, 0, 1 }
};
@@ -1117,42 +1155,39 @@ static const struct prefixentry *precedence;
static const struct prefixentry default_precedence[] =
{
/* See RFC 3484 for the details. */
- { { .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_addr8 = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 16, 30 },
- { { .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_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } } },
- 96, 10 },
- { { .in6_u
- = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 0, 40 }
+ { { .__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_addr8 = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+ }, 16, 30 },
+ { { .__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_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } }
+ }, 96, 10 },
+ { { .__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,
+match_prefix (const struct sockaddr_in6 *in6,
const struct prefixentry *list, int default_val)
{
int idx;
struct sockaddr_in6 in6_mem;
- const struct sockaddr_in6 *in6;
- if (ss->ss_family == PF_INET6)
- in6 = (const struct sockaddr_in6 *) ss;
- else if (ss->ss_family == PF_INET)
+ if (in6->sin6_family == PF_INET)
{
- const struct sockaddr_in *in = (const struct sockaddr_in *) ss;
+ const struct sockaddr_in *in = (const struct sockaddr_in *) in6;
/* Convert to IPv6 address. */
in6_mem.sin6_family = PF_INET6;
@@ -1171,7 +1206,7 @@ match_prefix (const struct sockaddr_storage *ss,
in6 = &in6_mem;
}
- else
+ else if (in6->sin6_family != PF_INET6)
return default_val;
for (idx = 0; ; ++idx)
@@ -1203,18 +1238,18 @@ match_prefix (const struct sockaddr_storage *ss,
static int
-get_label (const struct sockaddr_storage *ss)
+get_label (const struct sockaddr_in6 *in6)
{
/* XXX What is a good default value? */
- return match_prefix (ss, labels, INT_MAX);
+ return match_prefix (in6, labels, INT_MAX);
}
static int
-get_precedence (const struct sockaddr_storage *ss)
+get_precedence (const struct sockaddr_in6 *in6)
{
/* XXX What is a good default value? */
- return match_prefix (ss, precedence, 0);
+ return match_prefix (in6, precedence, 0);
}
@@ -1223,7 +1258,7 @@ static int
fls (uint32_t a)
{
uint32_t mask;
- int n = 0;
+ int n;
for (n = 0, mask = 1 << 31; n < 32; mask >>= 1, ++n)
if ((a & mask) != 0)
break;
@@ -1232,10 +1267,13 @@ fls (uint32_t a)
static int
-rfc3484_sort (const void *p1, const void *p2)
+rfc3484_sort (const void *p1, const void *p2, void *arg)
{
- const struct sort_result *a1 = (const struct sort_result *) p1;
- const struct sort_result *a2 = (const struct sort_result *) p2;
+ const size_t idx1 = *(const size_t *) p1;
+ const size_t idx2 = *(const size_t *) p2;
+ struct sort_result_combo *src = (struct sort_result_combo *) arg;
+ struct sort_result *a1 = &src->results[idx1];
+ struct sort_result *a2 = &src->results[idx2];
/* Rule 1: Avoid unusable destinations.
We have the got_source_addr flag set if the destination is reachable. */
@@ -1248,10 +1286,10 @@ rfc3484_sort (const void *p1, const void *p2)
/* Rule 2: Prefer matching scope. Only interesting if both
destination addresses are IPv6. */
int a1_dst_scope
- = get_scope ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
+ = get_scope ((struct sockaddr_in6 *) a1->dest_addr->ai_addr);
int a2_dst_scope
- = get_scope ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
+ = get_scope ((struct sockaddr_in6 *) a2->dest_addr->ai_addr);
if (a1->got_source_addr)
{
@@ -1291,11 +1329,11 @@ rfc3484_sort (const void *p1, const void *p2)
if (a1->got_source_addr)
{
int a1_dst_label
- = get_label ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
+ = get_label ((struct sockaddr_in6 *) a1->dest_addr->ai_addr);
int a1_src_label = get_label (&a1->source_addr);
int a2_dst_label
- = get_label ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
+ = get_label ((struct sockaddr_in6 *) a2->dest_addr->ai_addr);
int a2_src_label = get_label (&a2->source_addr);
if (a1_dst_label == a1_src_label && a2_dst_label != a2_src_label)
@@ -1307,9 +1345,9 @@ rfc3484_sort (const void *p1, const void *p2)
/* Rule 6: Prefer higher precedence. */
int a1_prec
- = get_precedence ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
+ = get_precedence ((struct sockaddr_in6 *) a1->dest_addr->ai_addr);
int a2_prec
- = get_precedence ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
+ = get_precedence ((struct sockaddr_in6 *) a2->dest_addr->ai_addr);
if (a1_prec > a2_prec)
return -1;
@@ -1320,14 +1358,61 @@ rfc3484_sort (const void *p1, const void *p2)
/* Rule 7: Prefer native transport. */
if (a1->got_source_addr)
{
- if (!(a1->source_addr_flags & in6ai_temporary)
- && (a2->source_addr_flags & in6ai_temporary))
- return -1;
- if ((a1->source_addr_flags & in6ai_temporary)
- && !(a2->source_addr_flags & in6ai_temporary))
- return 1;
+ /* The same interface index means the same interface which means
+ there is no difference in transport. This should catch many
+ (most?) cases. */
+ if (a1->index != a2->index)
+ {
+ int a1_native = a1->native;
+ int a2_native = a2->native;
+
+ if (a1_native == -1 || a2_native == -1)
+ {
+ uint32_t a1_index;
+ if (a1_native == -1)
+ {
+ /* If we do not have the information use 'native' as
+ the default. */
+ a1_native = 0;
+ a1_index = a1->index;
+ }
+ else
+ a1_index = 0xffffffffu;
+
+ uint32_t a2_index;
+ if (a2_native == -1)
+ {
+ /* If we do not have the information use 'native' as
+ the default. */
+ a2_native = 0;
+ a2_index = a2->index;
+ }
+ else
+ a2_index = 0xffffffffu;
+
+ __check_native (a1_index, &a1_native, a2_index, &a2_native);
- /* XXX Do we need to check anything beside temporary addresses? */
+ /* Fill in the results in all the records. */
+ for (int i = 0; i < src->nresults; ++i)
+ if (src->results[i].index == a1_index)
+ {
+ assert (src->results[i].native == -1
+ || src->results[i].native == a1_native);
+ src->results[i].native = a1_native;
+ }
+ else if (src->results[i].index == a2_index)
+ {
+ assert (src->results[i].native == -1
+ || src->results[i].native == a2_native);
+ src->results[i].native = a2_native;
+ }
+ }
+
+ if (a1_native && !a2_native)
+ return -1;
+ if (!a1_native && a2_native)
+ return 1;
+ }
}
@@ -1347,28 +1432,39 @@ rfc3484_sort (const void *p1, const void *p2)
if (a1->dest_addr->ai_family == PF_INET)
{
- assert (a1->source_addr.ss_family == PF_INET);
- assert (a2->source_addr.ss_family == PF_INET);
-
- struct sockaddr_in *in1_dst;
- struct sockaddr_in *in1_src;
- struct sockaddr_in *in2_dst;
- struct sockaddr_in *in2_src;
-
- in1_dst = (struct sockaddr_in *) a1->dest_addr->ai_addr;
- in1_src = (struct sockaddr_in *) &a1->source_addr;
- in2_dst = (struct sockaddr_in *) a2->dest_addr->ai_addr;
- in2_src = (struct sockaddr_in *) &a2->source_addr;
-
- bit1 = fls (ntohl (in1_dst->sin_addr.s_addr
- ^ in1_src->sin_addr.s_addr));
- bit2 = fls (ntohl (in2_dst->sin_addr.s_addr
- ^ in2_src->sin_addr.s_addr));
+ assert (a1->source_addr.sin6_family == PF_INET);
+ assert (a2->source_addr.sin6_family == PF_INET);
+
+ /* Outside of subnets, as defined by the network masks,
+ common address prefixes for IPv4 addresses make no sense.
+ So, define a non-zero value only if source and
+ destination address are on the same subnet. */
+ struct sockaddr_in *in1_dst
+ = (struct sockaddr_in *) a1->dest_addr->ai_addr;
+ in_addr_t in1_dst_addr = ntohl (in1_dst->sin_addr.s_addr);
+ struct sockaddr_in *in1_src
+ = (struct sockaddr_in *) &a1->source_addr;
+ in_addr_t in1_src_addr = ntohl (in1_src->sin_addr.s_addr);
+ in_addr_t netmask1 = 0xffffffffu << (32 - a1->prefixlen);
+
+ if ((in1_src_addr & netmask1) == (in1_dst_addr & netmask1))
+ bit1 = fls (in1_dst_addr ^ in1_src_addr);
+
+ struct sockaddr_in *in2_dst
+ = (struct sockaddr_in *) a2->dest_addr->ai_addr;
+ in_addr_t in2_dst_addr = ntohl (in2_dst->sin_addr.s_addr);
+ struct sockaddr_in *in2_src
+ = (struct sockaddr_in *) &a2->source_addr;
+ in_addr_t in2_src_addr = ntohl (in2_src->sin_addr.s_addr);
+ in_addr_t netmask2 = 0xffffffffu << (32 - a2->prefixlen);
+
+ if ((in2_src_addr & netmask2) == (in2_dst_addr & netmask2))
+ bit2 = fls (in2_dst_addr ^ in2_src_addr);
}
else if (a1->dest_addr->ai_family == PF_INET6)
{
- assert (a1->source_addr.ss_family == PF_INET6);
- assert (a2->source_addr.ss_family == PF_INET6);
+ assert (a1->source_addr.sin6_family == PF_INET6);
+ assert (a2->source_addr.sin6_family == PF_INET6);
struct sockaddr_in6 *in1_dst;
struct sockaddr_in6 *in1_src;
@@ -1408,7 +1504,7 @@ rfc3484_sort (const void *p1, const void *p2)
compare with the value indicating the order in which the entries
have been received from the services. NB: no two entries can have
the same order so the test will never return zero. */
- return a1->service_order < a2->service_order ? -1 : 1;
+ return idx1 < idx2 ? -1 : 1;
}
@@ -1452,6 +1548,13 @@ libc_freeres_fn(fini)
precedence = default_precedence;
free ((void *) old);
}
+
+ if (scopes != default_scopes)
+ {
+ const struct scopeentry *old = scopes;
+ scopes = default_scopes;
+ free ((void *) old);
+ }
}
@@ -1462,6 +1565,13 @@ struct prefixlist
};
+struct scopelist
+{
+ struct scopeentry entry;
+ struct scopelist *next;
+};
+
+
static void
free_prefixlist (struct prefixlist *list)
{
@@ -1474,6 +1584,18 @@ free_prefixlist (struct prefixlist *list)
}
+static void
+free_scopelist (struct scopelist *list)
+{
+ while (list != NULL)
+ {
+ struct scopelist *oldp = list;
+ list = list->next;
+ free (oldp);
+ }
+}
+
+
static int
prefixcmp (const void *p1, const void *p2)
{
@@ -1488,6 +1610,20 @@ prefixcmp (const void *p1, const void *p2)
}
+static int
+scopecmp (const void *p1, const void *p2)
+{
+ const struct scopeentry *e1 = (const struct scopeentry *) p1;
+ const struct scopeentry *e2 = (const struct scopeentry *) p2;
+
+ if (e1->netmask > e2->netmask)
+ return -1;
+ if (e1->netmask == e2->netmask)
+ return 0;
+ return 1;
+}
+
+
static void
gaiconf_init (void)
{
@@ -1497,6 +1633,9 @@ gaiconf_init (void)
struct prefixlist *precedencelist = NULL;
size_t nprecedencelist = 0;
bool precedencelist_nullbits = false;
+ struct scopelist *scopelist = NULL;
+ size_t nscopelist = 0;
+ bool scopelist_nullbits = false;
FILE *fp = fopen (GAICONF_FNAME, "rc");
if (fp != NULL)
@@ -1587,7 +1726,7 @@ gaiconf_init (void)
|| (bits = strtoul (cp, &endp, 10)) != ULONG_MAX
|| errno != ERANGE)
&& *endp == '\0'
- && bits <= INT_MAX
+ && bits <= 128
&& ((val = strtoul (val2, &endp, 10)) != ULONG_MAX
|| errno != ERANGE)
&& *endp == '\0'
@@ -1621,6 +1760,73 @@ gaiconf_init (void)
}
break;
+ case 7:
+ if (strcmp (cmd, "scopev4") == 0)
+ {
+ struct in6_addr prefix;
+ unsigned long int bits;
+ unsigned long int val;
+ char *endp;
+
+ bits = 32;
+ __set_errno (0);
+ cp = strchr (val1, '/');
+ if (cp != NULL)
+ *cp++ = '\0';
+ if (inet_pton (AF_INET6, val1, &prefix))
+ {
+ if (IN6_IS_ADDR_V4MAPPED (&prefix)
+ && (cp == NULL
+ || (bits = strtoul (cp, &endp, 10)) != ULONG_MAX
+ || errno != ERANGE)
+ && *endp == '\0'
+ && bits >= 96
+ && bits <= 128
+ && ((val = strtoul (val2, &endp, 10)) != ULONG_MAX
+ || errno != ERANGE)
+ && *endp == '\0'
+ && val <= INT_MAX)
+ {
+ struct scopelist *newp;
+ new_scope:
+ newp = malloc (sizeof (*newp));
+ if (newp == NULL)
+ {
+ free (line);
+ fclose (fp);
+ goto no_file;
+ }
+
+ newp->entry.netmask = htonl (bits != 96
+ ? (0xffffffff
+ << (128 - bits))
+ : 0);
+ newp->entry.addr32 = (prefix.s6_addr32[3]
+ & newp->entry.netmask);
+ newp->entry.scope = val;
+ newp->next = scopelist;
+ scopelist = newp;
+ ++nscopelist;
+ scopelist_nullbits |= bits == 96;
+ }
+ }
+ else if (inet_pton (AF_INET, val1, &prefix.s6_addr32[3])
+ && (cp == NULL
+ || (bits = strtoul (cp, &endp, 10)) != ULONG_MAX
+ || errno != ERANGE)
+ && *endp == '\0'
+ && bits <= 32
+ && ((val = strtoul (val2, &endp, 10)) != ULONG_MAX
+ || errno != ERANGE)
+ && *endp == '\0'
+ && val <= INT_MAX)
+ {
+ bits += 96;
+ goto new_scope;
+ }
+ }
+ break;
+
case 10:
if (strcmp (cmd, "precedence") == 0)
{
@@ -1704,12 +1910,52 @@ gaiconf_init (void)
/* Sort the entries so that the most specific ones are at
the beginning. */
- qsort (new_precedence, nprecedencelist, sizeof (*new_labels),
+ qsort (new_precedence, nprecedencelist, sizeof (*new_precedence),
prefixcmp);
}
else
new_precedence = (struct prefixentry *) default_precedence;
+ struct scopeentry *new_scopes;
+ if (nscopelist > 0)
+ {
+ if (!scopelist_nullbits)
+ ++nscopelist;
+ new_scopes = malloc (nscopelist * sizeof (*new_scopes));
+ if (new_scopes == NULL)
+ {
+ if (new_labels != default_labels)
+ free (new_labels);
+ if (new_precedence != default_precedence)
+ free (new_precedence);
+ goto no_file;
+ }
+
+ int i = nscopelist;
+ if (!scopelist_nullbits)
+ {
+ --i;
+ new_scopes[i].addr32 = 0;
+ new_scopes[i].netmask = 0;
+ new_scopes[i].scope = 14;
+ }
+
+ struct scopelist *l = scopelist;
+ while (i-- > 0)
+ {
+ new_scopes[i] = l->entry;
+ l = l->next;
+ }
+ free_scopelist (scopelist);
+
+ /* Sort the entries so that the most specific ones are at
+ the beginning. */
+ qsort (new_scopes, nscopelist, sizeof (*new_scopes),
+ scopecmp);
+ }
+ else
+ new_scopes = (struct scopeentry *) default_scopes;
+
/* Now we are ready to replace the values. */
const struct prefixentry *old = labels;
labels = new_labels;
@@ -1721,6 +1967,11 @@ gaiconf_init (void)
if (old != default_precedence)
free ((void *) old);
+ const struct scopeentry *oldscope = scopes;
+ scopes = new_scopes;
+ if (oldscope != default_scopes)
+ free ((void *) oldscope);
+
gaiconf_mtime = st.st_mtim;
}
else
@@ -1728,6 +1979,7 @@ gaiconf_init (void)
no_file:
free_prefixlist (labellist);
free_prefixlist (precedencelist);
+ free_scopelist (scopelist);
/* If we previously read the file but it is gone now, free the
old data and use the builtin one. Leave the reload flag
@@ -1747,16 +1999,6 @@ gaiconf_reload (void)
}
-#if HAVE_NETLINK_ROUTE
-# if __ASSUME_NETLINK_SUPPORT == 0
-/* Defined in ifaddrs.c. */
-extern int __no_netlink_support attribute_hidden;
-# else
-# define __no_netlink_support 0
-# endif
-#endif
-
-
int
getaddrinfo (const char *name, const char *service,
const struct addrinfo *hints, struct addrinfo **pai)
@@ -1795,71 +2037,12 @@ getaddrinfo (const char *name, const char *service,
size_t in6ailen = 0;
bool seen_ipv4 = false;
bool seen_ipv6 = false;
-#ifdef HAVE_NETLINK_ROUTE
- int sockfd = -1;
- pid_t nl_pid;
-#endif
- /* 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);
-#ifdef HAVE_NETLINK_ROUTE
- if (! __no_netlink_support)
- {
- sockfd = __socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
-
- struct sockaddr_nl nladdr;
- memset (&nladdr, '\0', sizeof (nladdr));
- nladdr.nl_family = AF_NETLINK;
+ /* We might need information about what interfaces are available.
+ Also 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);
- socklen_t addr_len = sizeof (nladdr);
-
- if (sockfd >= 0
- && __bind (fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) == 0
- && __getsockname (sockfd, (struct sockaddr *) &nladdr,
- &addr_len) == 0
- && make_request (sockfd, nladdr.nl_pid, &seen_ipv4, &seen_ipv6,
- in6ai, in6ailen) == 0)
- {
- /* It worked. */
- nl_pid = nladdr.nl_pid;
- goto got_netlink_socket;
- }
-
- if (sockfd >= 0)
- close_not_cancel_no_status (sockfd);
-
-#if __ASSUME_NETLINK_SUPPORT == 0
- /* Remember that there is no netlink support. */
- if (errno != EMFILE && errno != ENFILE)
- __no_netlink_support = 1;
-#else
- else
- {
- if (errno != EMFILE && errno != ENFILE)
- sockfd = -2;
-
- /* We cannot determine what interfaces are available. Be
- pessimistic. */
- seen_ipv4 = true;
- seen_ipv6 = true;
- return;
- }
-#endif
- }
-#endif
- }
-
-#ifdef HAVE_NETLINK_ROUTE
- got_netlink_socket:
-#endif
if (hints->ai_flags & AI_ADDRCONFIG)
{
/* Now make a decision on what we return, if anything. */
@@ -1943,11 +2126,12 @@ getaddrinfo (const char *name, const char *service,
__libc_once (once, gaiconf_init);
/* Sort results according to RFC 3484. */
struct sort_result results[nresults];
+ size_t order[nresults];
struct addrinfo *q;
struct addrinfo *last = NULL;
char *canonname = NULL;
- /* If we have information about deprecated and temporary address
+ /* If we have information about deprecated and temporary addresses
sort the array now. */
if (in6ai != NULL)
qsort (in6ai, in6ailen, sizeof (*in6ai), in6aicmp);
@@ -1958,8 +2142,8 @@ getaddrinfo (const char *name, const char *service,
for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next)
{
results[i].dest_addr = q;
- results[i].got_source_addr = false;
- results[i].service_order = i;
+ results[i].native = -1;
+ order[i] = i;
/* If we just looked up the address for a different
protocol, reuse the result. */
@@ -1971,10 +2155,15 @@ getaddrinfo (const char *name, const char *service,
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;
+ results[i].prefixlen = results[i - 1].prefixlen;
+ results[i].index = results[i - 1].index;
}
else
{
+ results[i].got_source_addr = false;
results[i].source_addr_flags = 0;
+ results[i].prefixlen = 0;
+ results[i].index = 0xffffffffu;
/* We overwrite the type with SOCK_DGRAM since we do not
want connect() to connect to the other side. If we
@@ -2005,22 +2194,40 @@ getaddrinfo (const char *name, const char *service,
results[i].source_addr_len = sl;
results[i].got_source_addr = true;
- if (q->ai_family == AF_INET6 && in6ai != NULL)
+ if (in6ai != NULL)
{
/* See whether the source address is on the list of
deprecated or temporary addresses. */
struct in6addrinfo tmp;
- struct sockaddr_in6 *sin6p
- = (struct sockaddr_in6 *) &results[i].source_addr;
- memcpy (tmp.addr, &sin6p->sin6_addr, IN6ADDRSZ);
+
+ if (q->ai_family == AF_INET && af == AF_INET)
+ {
+ struct sockaddr_in *sinp
+ = (struct sockaddr_in *) &results[i].source_addr;
+ tmp.addr[0] = 0;
+ tmp.addr[1] = 0;
+ tmp.addr[2] = htonl (0xffff);
+ tmp.addr[3] = sinp->sin_addr.s_addr;
+ }
+ else
+ {
+ struct sockaddr_in6 *sin6p
+ = (struct sockaddr_in6 *) &results[i].source_addr;
+ memcpy (tmp.addr, &sin6p->sin6_addr, IN6ADDRSZ);
+ }
struct in6addrinfo *found
= bsearch (&tmp, in6ai, in6ailen, sizeof (*in6ai),
in6aicmp);
if (found != NULL)
- results[i].source_addr_flags = found->flags;
+ {
+ results[i].source_addr_flags = found->flags;
+ results[i].prefixlen = found->prefixlen;
+ results[i].index = found->index;
+ }
}
- else if (q->ai_family == AF_INET && af == AF_INET6)
+
+ if (q->ai_family == AF_INET && af == AF_INET6)
{
/* We have to convert the address. The socket is
IPv6 and the request is for IPv4. */
@@ -2029,10 +2236,17 @@ getaddrinfo (const char *name, const char *service,
struct sockaddr_in *sin
= (struct sockaddr_in *) &results[i].source_addr;
assert (IN6_IS_ADDR_V4MAPPED (sin6->sin6_addr.s6_addr32));
+ sin->sin_family = AF_INET;
+ /* We do not have to initialize sin_port since this
+ fields has the same position and size in the IPv6
+ structure. */
+ assert (offsetof (struct sockaddr_in, sin_port)
+ == offsetof (struct sockaddr_in6, sin6_port));
+ assert (sizeof (sin->sin_port)
+ == sizeof (sin6->sin6_port));
memcpy (&sin->sin_addr,
&sin6->sin6_addr.s6_addr32[3], INADDRSZ);
- results[i].source_addr_len = INADDRSZ;
- sin->sin_family = AF_INET;
+ results[i].source_addr_len = sizeof (struct sockaddr_in);
}
}
else if (errno == EAFNOSUPPORT && af == AF_INET6
@@ -2059,6 +2273,8 @@ getaddrinfo (const char *name, const char *service,
/* We got all the source addresses we can get, now sort using
the information. */
+ struct sort_result_combo src
+ = { .results = results, .nresults = nresults };
if (__builtin_expect (gaiconf_reload_flag_ever_set, 0))
{
__libc_lock_define_initialized (static, lock);
@@ -2066,16 +2282,16 @@ getaddrinfo (const char *name, const char *service,
__libc_lock_lock (lock);
if (old_once && gaiconf_reload_flag)
gaiconf_reload ();
- qsort (results, nresults, sizeof (results[0]), rfc3484_sort);
+ qsort_r (order, nresults, sizeof (order[0]), rfc3484_sort, &src);
__libc_lock_unlock (lock);
}
else
- qsort (results, nresults, sizeof (results[0]), rfc3484_sort);
+ qsort_r (order, nresults, sizeof (order[0]), rfc3484_sort, &src);
/* Queue the results up as they come out of sorting. */
- q = p = results[0].dest_addr;
+ q = p = results[order[0]].dest_addr;
for (i = 1; i < nresults; ++i)
- q = q->ai_next = results[i].dest_addr;
+ q = q->ai_next = results[order[i]].dest_addr;
q->ai_next = NULL;
/* Fill in the canonical name into the new first entry. */