diff options
author | Ulrich Drepper <drepper@redhat.com> | 2007-09-19 22:12:22 +0000 |
---|---|---|
committer | Ulrich Drepper <drepper@redhat.com> | 2007-09-19 22:12:22 +0000 |
commit | e1db0493fdc908aae2a597c82dcd9fbaff14e9dc (patch) | |
tree | 50be4774a038514edb5b3d678f96417191c3289e /sysdeps/posix | |
parent | c3266dc0d85ed519ff40d1738aadf7b1280af3ba (diff) | |
download | glibc-e1db0493fdc908aae2a597c82dcd9fbaff14e9dc.tar.gz glibc-e1db0493fdc908aae2a597c82dcd9fbaff14e9dc.tar.xz glibc-e1db0493fdc908aae2a597c82dcd9fbaff14e9dc.zip |
* sysdeps/posix/getaddrinfo.c (getaddrinfo): Avoid unnecessary
open/close when determining source addresses.
Diffstat (limited to 'sysdeps/posix')
-rw-r--r-- | sysdeps/posix/getaddrinfo.c | 47 |
1 files changed, 41 insertions, 6 deletions
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c index adb3c4f96a..b668936095 100644 --- a/sysdeps/posix/getaddrinfo.c +++ b/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 (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); |