(I sent this diff to Éric Faurot on the 12th, but received no reply.)
Tech,
While everyone's having fun removing code from OpenSSL, I decided to add
some to libasr. I implemented AI_ADDRCONFIG, a getaddrinfo() flag
defined in RFC 2553/3493. Basically, it tells getaddrinfo() to skip IPvX
lookups when there are no IPvX addresses configured on any interface. It
is present on most other OSes. Tons of software out there have to play
Autoconf games to cope with its absence (which, on OpenBSD, predates
libasr if I'm not mistaken).
Noteworthy:
- I call getifaddrs() from getaddrinfo_run_async(). This should not
block, so no need to change the state machine.
- I added AI_ADDRCONFIG to the default hints, like glibc does, and as
was specified in RFC 2553. (RFC 3493 says nothing about default flags.)
ok? comments?
Simon
Index: include/netdb.h
===================================================================
RCS file: /cvs/src/include/netdb.h,v
retrieving revision 1.31
diff -u -p -r1.31 netdb.h
--- include/netdb.h 15 Sep 2012 00:47:08 -0000 1.31
+++ include/netdb.h 12 Apr 2014 17:40:38 -0000
@@ -167,9 +167,11 @@ extern int h_errno;
#define AI_EXT 8 /* enable non-portable extensions */
#define AI_NUMERICSERV 16 /* don't ever try servname lookup */
#define AI_FQDN 32 /* return the FQDN that was resolved */
+#define AI_ADDRCONFIG 64 /* return configured address families only */
/* valid flags for addrinfo */
#define AI_MASK \
- (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | AI_NUMERICSERV |
AI_FQDN)
+ (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | AI_NUMERICSERV |
AI_FQDN | \
+ AI_ADDRCONFIG)
#define NI_NUMERICHOST 1 /* return the host address, not the name */
#define NI_NUMERICSERV 2 /* return the service address, not the name */
Index: lib/libc/asr/getaddrinfo_async.c
===================================================================
RCS file: /cvs/src/lib/libc/asr/getaddrinfo_async.c,v
retrieving revision 1.26
diff -u -p -r1.26 getaddrinfo_async.c
--- lib/libc/asr/getaddrinfo_async.c 26 Mar 2014 18:13:15 -0000 1.26
+++ lib/libc/asr/getaddrinfo_async.c 12 Apr 2014 17:40:39 -0000
@@ -21,6 +21,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
+#include <net/if.h>
#ifdef YP
#include <rpc/rpc.h>
#include <rpcsvc/yp.h>
@@ -32,6 +33,7 @@
#include <asr.h>
#include <err.h>
#include <errno.h>
+#include <ifaddrs.h>
#include <resolv.h> /* for res_hnok */
#include <stdlib.h>
#include <string.h>
@@ -104,6 +106,7 @@ getaddrinfo_async(const char *hostname,
else {
memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints);
as->as.ai.hints.ai_family = PF_UNSPEC;
+ as->as.ai.hints.ai_flags = AI_ADDRCONFIG;
}
asr_ctx_unref(ac);
@@ -127,8 +130,9 @@ getaddrinfo_async_run(struct asr_query *
char fqdn[MAXDNAME];
const char *str;
struct addrinfo *ai;
- int i, family, r;
+ int i, family, r, v4, v6;
FILE *f;
+ struct ifaddrs *ifa, *ifa0;
union {
struct sockaddr sa;
struct sockaddr_in sain;
@@ -193,6 +197,44 @@ getaddrinfo_async_run(struct asr_query *
ar->ar_gai_errno = EAI_SERVICE;
async_set_state(as, ASR_STATE_HALT);
break;
+ }
+
+ /* Restrict result set to configured address families */
+ if (ai->ai_flags & AI_ADDRCONFIG) {
+ if (getifaddrs(&ifa0) != 0) {
+ ar->ar_gai_errno = EAI_FAIL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ v4 = 0;
+ v6 = 0;
+ for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family == PF_INET &&
+ ((struct sockaddr_in *)ifa->ifa_addr)
+ ->sin_addr.s_addr != INADDR_LOOPBACK)
+ v4 = 1;
+ else if (ifa->ifa_addr->sa_family == PF_INET6 &&
+ !IN6_IS_ADDR_LOOPBACK(
+ &((struct sockaddr_in6 *)ifa->ifa_addr)
+ ->sin6_addr) &&
+ !((ifa->ifa_flags & IFF_LOOPBACK) &&
+ IN6_IS_ADDR_LINKLOCAL(
+ &((struct sockaddr_in6 *)ifa->ifa_addr)
+ ->sin6_addr)))
+ v6 = 1;
+ }
+ freeifaddrs(ifa0);
+ if (ai->ai_family == PF_UNSPEC && !v4 && !v6 ||
+ ai->ai_family == PF_INET && !v4 ||
+ ai->ai_family == PF_INET6 && !v6) {
+ ar->ar_gai_errno = EAI_NONAME;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ if (ai->ai_family == PF_UNSPEC && v4 && !v6)
+ ai->ai_family = PF_INET;
+ if (ai->ai_family == PF_UNSPEC && !v4 && v6)
+ ai->ai_family = PF_INET6;
}
/* Make sure there is at least a valid combination */
Index: lib/libc/net/getaddrinfo.3
===================================================================
RCS file: /cvs/src/lib/libc/net/getaddrinfo.3,v
retrieving revision 1.54
diff -u -p -r1.54 getaddrinfo.3
--- lib/libc/net/getaddrinfo.3 21 Jan 2014 03:15:45 -0000 1.54
+++ lib/libc/net/getaddrinfo.3 12 Apr 2014 17:40:39 -0000
@@ -119,6 +119,13 @@ is zero the caller will accept any proto
is formed by
.Tn OR Ns 'ing
the following values:
+.It Dv AI_ADDRCONFIG
+If the
+.Dv AI_ADDRCONFIG
+bit is set, IPv4 addresses will be returned only if an IPv4 address is
+configured on an interface, and IPv6 addresses will be returned only if
an IPv6
+address is configured on an interface. The loopback address is not
considered
+for this case as valid as a configured address.
.Bl -tag -width "AI_CANONNAMEXX"
.It Dv AI_CANONNAME
If the
@@ -219,7 +226,10 @@ behaves as if the caller provided a
with
.Fa ai_family
set to
-.Dv PF_UNSPEC
+.Dv PF_UNSPEC ,
+.Fa ai_flags
+set to
+.Dv AI_ADDRCONFIG ,
and all other elements set to zero or
.Dv NULL .
.Pp