diff options
author | joseph <joseph@7b3dc134-2b1b-0410-93df-9e9f96275f8d> | 2007-09-25 15:22:16 +0000 |
---|---|---|
committer | joseph <joseph@7b3dc134-2b1b-0410-93df-9e9f96275f8d> | 2007-09-25 15:22:16 +0000 |
commit | 39214d562d474c17d8242008f5454e9c9ca710ad (patch) | |
tree | 1c44f1babbdb5d368fc0e1032b4e9d013af2263a /libc/sysdeps/posix | |
parent | 9238710d492f92f0221bba03ee01c08ccbd6387c (diff) | |
download | eglibc2-39214d562d474c17d8242008f5454e9c9ca710ad.tar.gz |
Merge changes between r3467 and r3614 from /fsf/trunk.
git-svn-id: svn://svn.eglibc.org/trunk@3615 7b3dc134-2b1b-0410-93df-9e9f96275f8d
Diffstat (limited to 'libc/sysdeps/posix')
-rw-r--r-- | libc/sysdeps/posix/getaddrinfo.c | 47 |
1 files changed, 41 insertions, 6 deletions
diff --git a/libc/sysdeps/posix/getaddrinfo.c b/libc/sysdeps/posix/getaddrinfo.c index adb3c4f96..221b41dd0 100644 --- a/libc/sysdeps/posix/getaddrinfo.c +++ b/libc/sysdeps/posix/getaddrinfo.c @@ -1944,6 +1944,9 @@ getaddrinfo (const char *name, const char *service, if (in6ai != NULL) qsort (in6ai, in6ailen, sizeof (*in6ai), in6aicmp); + int fd = -1; + int af = AF_UNSPEC; + for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next) { results[i].dest_addr = q; @@ -1968,7 +1971,21 @@ getaddrinfo (const char *name, const char *service, 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 || (af == AF_INET && q->ai_family == AF_INET6)) + { + if (fd != -1) + close_retry: + close_not_cancel_no_status (fd); + af = q->ai_family; + fd = __socket (af, SOCK_DGRAM, IPPROTO_IP); + } + else + { + /* Reset the connection. */ + struct sockaddr sa = { .sa_family = AF_UNSPEC }; + __connect (fd, &sa, sizeof (sa)); + } + socklen_t sl = sizeof (results[i].source_addr); if (fd != -1 && __connect (fd, q->ai_addr, q->ai_addrlen) == 0 @@ -1979,9 +1996,9 @@ 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) + if (q->ai_family == AF_INET6 && in6ai != NULL) { - /* See whether the source address is the list of + /* See whether the source address is on the list of deprecated or temporary addresses. */ struct in6addrinfo tmp; struct sockaddr_in6 *sin6p @@ -1994,14 +2011,29 @@ getaddrinfo (const char *name, const char *service, if (found != NULL) results[i].source_addr_flags = found->flags; } + else 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. */ + struct sockaddr_in6 *sin6 + = (struct sockaddr_in6 *) &results[i].source_addr; + struct sockaddr_in *sin + = (struct sockaddr_in *) &results[i].source_addr; + assert (IN6_IS_ADDR_V4MAPPED (sin6->sin6_addr.s6_addr32)); + memcpy (&sin->sin_addr, + &sin6->sin6_addr.s6_addr32[3], INADDRSZ); + results[i].source_addr_len = INADDRSZ; + sin->sin_family = AF_INET; + } } + else if (errno == EAFNOSUPPORT && af == AF_INET6 + && q->ai_family == AF_INET) + /* This could mean IPv6 sockets are IPv6-only. */ + goto close_retry; else /* Just make sure that if we have to process the same address again we do not copy any memory. */ results[i].source_addr_len = 0; - - if (fd != -1) - close_not_cancel_no_status (fd); } /* Remember the canonical name. */ @@ -2013,6 +2045,9 @@ getaddrinfo (const char *name, const char *service, } } + if (fd != -1) + 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); |