2003-11-17  Ulrich Drepper  <drepper@redhat.com>

	* sysdeps/posix/getaddrinfo.c: Add support for destination address
	selection according to RFC 3484.
This commit is contained in:
Ulrich Drepper 2003-11-18 07:04:13 +00:00
parent 9780c971fb
commit 5ddb5bf5fb
3 changed files with 414 additions and 9 deletions

View File

@ -1,3 +1,8 @@
2003-11-17 Ulrich Drepper <drepper@redhat.com>
* sysdeps/posix/getaddrinfo.c: Add support for destination address
selection according to RFC 3484.
2003-11-15 Ulrich Drepper <drepper@redhat.com>
* posix/regex_internal.h: Add forward declaration of re_dfa_t.

View File

@ -30,7 +30,6 @@ if test "${libc_cv_forced_unwind+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
cat >conftest.$ac_ext <<_ACEOF
#line $LINENO "configure"
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
@ -50,11 +49,21 @@ _Unwind_GetCFA (context)
_ACEOF
rm -f conftest.$ac_objext conftest$ac_exeext
if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
(eval $ac_link) 2>&5
(eval $ac_link) 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -s conftest$ac_exeext'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; } &&
{ ac_try='test -s conftest$ac_exeext'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@ -67,7 +76,8 @@ sed 's/^/| /' conftest.$ac_ext >&5
libc_cv_forced_unwind=no
fi
rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
rm -f conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
echo "$as_me:$LINENO: result: $libc_cv_forced_unwind" >&5
echo "${ECHO_T}$libc_cv_forced_unwind" >&6
@ -84,7 +94,6 @@ if test "${libc_cv_c_cleanup+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
cat >conftest.$ac_ext <<_ACEOF
#line $LINENO "configure"
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
@ -106,11 +115,21 @@ main ()
_ACEOF
rm -f conftest.$ac_objext conftest$ac_exeext
if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
(eval $ac_link) 2>&5
(eval $ac_link) 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -s conftest$ac_exeext'
{ ac_try='test -z "$ac_c_werror_flag"
|| test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; } &&
{ ac_try='test -s conftest$ac_exeext'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
@ -123,7 +142,8 @@ sed 's/^/| /' conftest.$ac_ext >&5
libc_cv_c_cleanup=no
fi
rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
rm -f conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
echo "$as_me:$LINENO: result: $libc_cv_c_cleanup" >&5
echo "${ECHO_T}$libc_cv_c_cleanup" >&6
@ -133,4 +153,8 @@ echo "${ECHO_T}$libc_cv_c_cleanup" >&6
echo "$as_me: error: the compiler must support C cleanup handling" >&2;}
{ (exit 1); exit 1; }; }
fi
else
{ { echo "$as_me:$LINENO: error: forced unwind support is required" >&5
echo "$as_me: error: forced unwind support is required" >&2;}
{ (exit 1); exit 1; }; }
fi

View File

@ -53,6 +53,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sys/utsname.h>
#include <net/if.h>
#include <nsswitch.h>
#include <not-cancel.h>
#define GAIH_OKIFUNSPEC 0x0100
#define GAIH_EAI ~(GAIH_OKIFUNSPEC)
@ -894,11 +895,342 @@ static struct gaih gaih[] =
{ PF_UNSPEC, NULL }
};
struct sort_result
{
struct addrinfo *dest_addr;
struct sockaddr_storage source_addr;
bool got_source_addr;
};
static int
get_scope (const struct sockaddr_storage *ss)
{
int scope;
if (ss->ss_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))
scope = 2;
else if (IN6_IS_ADDR_SITELOCAL (&in6->sin6_addr))
scope = 5;
else
/* XXX Is this the correct default behavior? */
scope = 14;
}
else
scope = in6->sin6_addr.s6_addr[1] & 0xf;
}
else if (ss->ss_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] == 16)
|| (addr[0] == 192 && addr[1] == 168))
scope = 5;
else
scope = 14;
}
else
/* XXX What is a good default? */
scope = 15;
return scope;
}
/* 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 in6_addr prefix;
unsigned int bits;
int val;
} default_labels[] =
{
/* See RFC 3484 for the details. */
{ { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0001 } } },
128, 0 },
{ { .in6_u = { .u6_addr16 = { 0x2002, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000 } } },
16, 2 },
{ { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000 } } },
96, 3 },
{ { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0xffff, 0x0000, 0x0000 } } },
96, 4 },
{ { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000 } } },
0, 1 }
};
static const struct prefixlist default_precedence[] =
{
/* See RFC 3484 for the details. */
{ { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0001 } } },
128, 50 },
{ { .in6_u = { .u6_addr16 = { 0x2002, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000 } } },
16, 30 },
{ { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000 } } },
96, 20 },
{ { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0xffff, 0x0000, 0x0000 } } },
96, 10 },
{ { .in6_u = { .u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000 } } },
0, 40 }
};
static int
match_prefix (const struct sockaddr_storage *ss, const struct prefixlist *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)
{
const struct sockaddr_in *in = (const struct sockaddr_in *) ss;
/* Convert to IPv6 address. */
in6_mem.sin6_family = PF_INET6;
in6_mem.sin6_port = in->sin_port;
in6_mem.sin6_flowinfo = 0;
if (in->sin_addr.s_addr == htonl (0x7f000001))
in6_mem.sin6_addr = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
else
{
/* Construct a V4-to-6 mapped address. */
memset (&in6_mem.sin6_addr, '\0', sizeof (in6_mem.sin6_addr));
in6_mem.sin6_addr.s6_addr16[5] = 0xffff;
in6_mem.sin6_addr.s6_addr32[3] = in->sin_addr.s_addr;
in6_mem.sin6_scope_id = 0;
}
in6 = &in6_mem;
}
else
return default_val;
for (idx = 0; ; ++idx)
{
unsigned int bits = list[idx].bits;
uint8_t *mask = list[idx].prefix.s6_addr;
uint8_t *val = in6->sin6_addr.s6_addr;
while (bits > 8)
{
if (*mask != *val)
break;
++mask;
++val;
bits -= 8;
}
if (bits < 8)
{
if ((*mask & (0xff >> bits)) == (*val & (0xff >> bits)))
/* Match! */
break;
}
}
return list[idx].val;
}
static int
get_label (const struct sockaddr_storage *ss)
{
/* XXX What is a good default value? */
return match_prefix (ss, default_labels, INT_MAX);
}
static int
get_precedence (const struct sockaddr_storage *ss)
{
/* XXX What is a good default value? */
return match_prefix (ss, default_precedence, 0);
}
static int
rfc3484_sort (const void *p1, const void *p2)
{
const struct sort_result *a1 = (const struct sort_result *) p1;
const struct sort_result *a2 = (const struct sort_result *) p2;
/* Rule 1: Avoid unusable destinations.
We have the got_source_addr flag set if the destination is reachable. */
if (a1->got_source_addr && ! a2->got_source_addr)
return -1;
if (! a1->got_source_addr && a2->got_source_addr)
return 1;
/* 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);
int a2_dst_scope
= get_scope ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
if (a1->got_source_addr)
{
int a1_src_scope = get_scope (&a1->source_addr);
int a2_src_scope = get_scope (&a2->source_addr);
if (a1_dst_scope == a1_src_scope && a2_dst_scope != a2_src_scope)
return -1;
if (a1_dst_scope != a1_src_scope && a2_dst_scope == a2_src_scope)
return 1;
}
/* Rule 3: Avoid deprecated addresses.
That's something only the kernel could decide. */
/* Rule 4: Prefer home addresses.
Another thing only the kernel can decide. */
/* Rule 5: Prefer matching label. */
if (a1->got_source_addr)
{
int a1_dst_label
= get_label ((struct sockaddr_storage *) 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);
int a2_src_label = get_label (&a2->source_addr);
if (a1_dst_label == a1_src_label && a2_dst_label != a2_src_label)
return -1;
if (a1_dst_label != a1_src_label && a2_dst_label == a2_src_label)
return 1;
}
/* Rule 6: Prefer higher precedence. */
int a1_prec
= get_precedence ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
int a2_prec
= get_precedence ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
if (a1_prec > a2_prec)
return -1;
if (a1_prec < a2_prec)
return 1;
/* Rule 7: Prefer native transport.
XXX How to recognize tunnels? */
/* Rule 8: Prefer smaller scope. */
if (a1_dst_scope < a2_dst_scope)
return -1;
if (a1_dst_scope > a2_dst_scope)
return 1;
/* Rule 9: Use longest matching prefix. */
if (a1->got_source_addr
&& a1->dest_addr->ai_family == a2->dest_addr->ai_family)
{
int bit1 = 0;
int bit2 = 0;
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 = ffs (in1_dst->sin_addr.s_addr ^ in1_src->sin_addr.s_addr);
bit2 = ffs (in2_dst->sin_addr.s_addr ^ in2_src->sin_addr.s_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);
struct sockaddr_in6 *in1_dst;
struct sockaddr_in6 *in1_src;
struct sockaddr_in6 *in2_dst;
struct sockaddr_in6 *in2_src;
in1_dst = (struct sockaddr_in6 *) a1->dest_addr->ai_addr;
in1_src = (struct sockaddr_in6 *) &a1->source_addr;
in2_dst = (struct sockaddr_in6 *) a2->dest_addr->ai_addr;
in2_src = (struct sockaddr_in6 *) &a2->source_addr;
int i;
for (i = 0; i < 4; ++i)
if (in1_dst->sin6_addr.s6_addr32[i]
!= in1_src->sin6_addr.s6_addr32[i]
|| (in2_dst->sin6_addr.s6_addr32[i]
!= in2_src->sin6_addr.s6_addr32[i]))
break;
if (i < 4)
{
bit1 = ffs (in1_dst->sin6_addr.s6_addr32[i]
^ in1_src->sin6_addr.s6_addr32[i]);
bit2 = ffs (in2_dst->sin6_addr.s6_addr32[i]
^ in2_src->sin6_addr.s6_addr32[i]);
}
}
if (bit1 > bit2)
return -1;
if (bit1 < bit2)
return 1;
}
/* Rule 10: Otherwise, leave the order unchanged. */
return 0;
}
int
getaddrinfo (const char *name, const char *service,
const struct addrinfo *hints, struct addrinfo **pai)
{
int i = 0, j = 0, last_i = 0;
int nresults = 0;
struct addrinfo *p = NULL, **end;
struct gaih *g = gaih, *pg = NULL;
struct gaih_service gaih_service, *pservice;
@ -1000,7 +1332,11 @@ getaddrinfo (const char *name, const char *service,
return -(i & GAIH_EAI);
}
if (end)
while(*end) end = &((*end)->ai_next);
while (*end)
{
end = &((*end)->ai_next);
++nresults;
}
}
}
++g;
@ -1009,6 +1345,46 @@ getaddrinfo (const char *name, const char *service,
if (j == 0)
return EAI_FAMILY;
if (nresults > 1)
{
/* Sort results according to RFC 3484. */
struct sort_result results[nresults];
struct addrinfo *q;
for (i = 0, q = p; q != NULL; ++i, q = q->ai_next)
{
results[i].dest_addr = q;
results[i].got_source_addr = false;
/* 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
fact. */
int fd = __socket (q->ai_family, SOCK_DGRAM, IPPROTO_IP);
if (fd != -1)
{
socklen_t sl = sizeof (results[i].source_addr);
if (__connect (fd, q->ai_addr, q->ai_addrlen) == 0
&& __getsockname (fd,
(struct sockaddr *) &results[i].source_addr,
&sl) == 0)
results[i].got_source_addr = true;
close_not_cancel_no_status (fd);
}
}
/* We got all the source addresses we can get, now sort using
the information. */
qsort (results, nresults, sizeof (results[0]), rfc3484_sort);
/* Queue the results up as they come out of sorting. */
q = p = results[0].dest_addr;
for (i = 1; i < nresults; ++i)
q = q->ai_next = results[i].dest_addr;
q->ai_next = NULL;
}
if (p)
{
*pai = p;