diff --git a/appl/test/auditdns.c b/appl/test/auditdns.c
index ae0e6e63c..d56767952 100644
--- a/appl/test/auditdns.c
+++ b/appl/test/auditdns.c
@@ -28,8 +28,11 @@
 #include <config.h>
 #endif
 
-#include <dlfcn.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <errno.h>
 #include <netdb.h>
+#include <netinet/in.h>
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -65,32 +68,308 @@ gethostbyname2(const char *name, int af)
 
 #ifdef HAVE_GETADDRINFO
 
-typedef int getaddrinfo_fn_t(const char *, const char *,
-    const struct addrinfo *restrict,
-    struct addrinfo **restrict);
-getaddrinfo_fn_t getaddrinfo;
+void
+freeaddrinfo(struct addrinfo *ai)
+{
+
+    free(ai->ai_addr);
+    free(ai);
+}
+
 int
 getaddrinfo(const char *hostname, const char *servname,
     const struct addrinfo *restrict hints,
     struct addrinfo **restrict res)
 {
-    void *sym;
+    char *servend;
+    unsigned long port;
+    union {
+	struct sockaddr		sa;
+	struct sockaddr_in	sin;
+	struct sockaddr_in6	sin6;
+    } *addr = NULL;
+    int af[2] = {AF_INET, AF_INET6};
+    socklen_t addrlen[2] = {sizeof(addr->sin), sizeof(addr->sin6)};
+    int socktype[2] = {SOCK_DGRAM, SOCK_STREAM};
+    int proto[2] = {IPPROTO_UDP, IPPROTO_TCP};
+    size_t i, j, naddr, nproto;
+    struct addrinfo *ai = NULL;
+    int error;
 
+    /*
+     * DNS audit: Abort unless the user specified hints with
+     * AI_NUMERICHOST, AI_NUMERICSERV, and no AI_CANONNAME.
+     */
     if (hints == NULL ||
 	(hints->ai_flags & AI_NUMERICHOST) == 0 ||
+	(hints->ai_flags & AI_NUMERICSERV) == 0 ||
 	(hints->ai_flags & AI_CANONNAME) != 0) {
 	fprintf(stderr, "DNS leak: %s %s:%s\n",
 	    __func__, hostname, servname);
 	abort();
     }
 
-    if ((sym = dlsym(RTLD_NEXT, __func__)) == NULL) {
-	fprintf(stderr, "dlsym(RTLD_NEXT, \"%s\") failed: %s\n",
-	    __func__, dlerror());
-	return EAI_FAIL;
+    /*
+     * Check hints for address family.  If unspecified, use the default
+     * set of address families: {AF_INET, AF_INET6}.
+     */
+    switch (hints->ai_family) {
+    case AF_UNSPEC:
+	naddr = 2;
+	break;
+    case AF_INET:
+	naddr = 1;
+	af[0] = AF_INET;
+	addrlen[0] = sizeof(addr->sin);
+	break;
+    case AF_INET6:
+	naddr = 1;
+	af[0] = AF_INET6;
+	addrlen[0] = sizeof(addr->sin6);
+	break;
+    default:
+	error = EAI_FAMILY;
+	goto out;
     }
 
-    return (*(getaddrinfo_fn_t *)sym)(hostname, servname, hints, res);
+    /*
+     * Check hints for socket type and protocol.  If both are zero, we
+     * use the default set of socktype/proto pairs.  If one is
+     * specified but not the other, use the default.  If both are
+     * specified, make sure they match.
+     */
+    switch (hints->ai_socktype) {
+    case 0:
+	if (hints->ai_protocol == 0)
+	    nproto = sizeof(proto)/sizeof(proto[0]);
+	else
+	    nproto = 1;
+	break;
+    case SOCK_DGRAM:		/* datagram <-> UDP */
+	if (hints->ai_protocol != 0 && hints->ai_protocol != IPPROTO_UDP) {
+	    error = EAI_SOCKTYPE;;
+	    goto out;
+	}
+	socktype[0] = SOCK_DGRAM;
+	proto[0] = IPPROTO_UDP;
+	nproto = 1;
+	break;
+    case SOCK_STREAM:		/* stream <-> TCP */
+	if (hints->ai_protocol != 0 && hints->ai_protocol != IPPROTO_TCP) {
+	    error = EAI_SOCKTYPE;
+	    goto out;
+	}
+	socktype[0] = SOCK_STREAM;
+	proto[0] = IPPROTO_TCP;
+	nproto = 1;
+	break;
+    default:
+	error = EAI_SOCKTYPE;
+	goto out;
+    }
+
+    /*
+     * Check whether a service is specified at all.
+     */
+    if (servname == NULL) {
+	/*
+	 * No service specified.  Use the wildcard port 0.
+	 */
+	port = 0;
+    } else {
+	/*
+	 * Service specified.  Parse it as a nonnegative integer, at
+	 * most 65535.
+	 */
+	errno = 0;
+	port = strtoul(servname, &servend, 10);
+	if (servend == servname ||
+	    *servend != '\0' ||
+	    errno != 0 ||
+	    port > 65535) {
+	    error = EAI_NONAME;
+	    goto out;
+	}
+    }
+
+    /*
+     * Check whether a hostname is specified at all.
+     */
+    if (hostname == NULL) {
+	/*
+	 * No hostname.  This only makes sense if we're going to bind
+	 * to a socket and receive incoming packets or listen and
+	 * accept incoming connections, i.e., only if AI_PASSIVE is
+	 * set.  Otherwise, fail with EAI_NONAME.
+	 */
+	if ((hints->ai_flags & AI_PASSIVE) == 0) {
+	    error = EAI_NONAME;
+	    goto out;
+	}
+
+	/*
+	 * Allocate an array of as many addresses as the hints allow.
+	 */
+	if ((addr = calloc(naddr, sizeof(*addr))) == NULL) {
+	    error = EAI_MEMORY;
+	    goto out;
+	}
+
+	/*
+	 * Fill the addresses with the ANY wildcard address, IPv4
+	 * 0.0.0.0 or IPv6 `::' (i.e., 0000:0000:....:0000).
+	 */
+	switch (hints->ai_family) {
+	case AF_UNSPEC:
+	    assert(naddr == 2);
+	    addr[0].sin.sin_family = AF_INET;
+	    addr[0].sin.sin_port = htons(port);
+	    addr[0].sin.sin_addr.s_addr = htonl(INADDR_ANY);
+	    addr[1].sin6.sin6_family = AF_INET6;
+	    addr[1].sin6.sin6_port = htons(port);
+	    addr[1].sin6.sin6_addr = in6addr_any;
+	    break;
+	case AF_INET:
+	    assert(naddr == 1);
+	    addr[0].sin.sin_family = AF_INET;
+	    addr[0].sin.sin_port = htons(port);
+	    addr[0].sin.sin_addr.s_addr = htonl(INADDR_ANY);
+	    break;
+	case AF_INET6:
+	    assert(naddr == 1);
+	    addr[0].sin6.sin6_family = AF_INET6;
+	    addr[0].sin6.sin6_port = htons(port);
+	    addr[0].sin6.sin6_addr = in6addr_any;
+	    break;
+	default:
+	    error = EAI_FAIL;	/* XXX unreachable */
+	    goto out;
+	}
+	goto have_addr;
+    } else {
+	/*
+	 * Allocate a single socket address record.  Since we have
+	 * AI_NUMERICHOST, the hostname can be parsed as only one
+	 * address and won't be resolved to an array of possibly >1
+	 * addresses.
+	 */
+	naddr = 1;
+	if ((addr = calloc(naddr, sizeof(*addr))) == NULL) {
+	    error = EAI_MEMORY;
+	    goto out;
+	}
+
+	/*
+	 * If the hints specify AF_INET, or don't specify anything, try
+	 * to parse it as an IPv4 address.  If this fails, it will fall
+	 * through.
+	 */
+	if (hints->ai_family == AF_UNSPEC || hints->ai_family == AF_INET) {
+	    switch (inet_pton(AF_INET, hostname, &addr->sin.sin_addr)) {
+	    case -1:		/* system error */
+		error = EAI_SYSTEM;
+		goto out;
+	    case 0:		/* failure */
+		break;
+	    case 1:		/* success */
+		addr->sin.sin_family = AF_INET;
+		addr->sin.sin_port = htons(port);
+		af[0] = AF_INET;
+		addrlen[0] = sizeof(addr->sin);
+		goto have_addr;
+	    }
+	}
+
+	/*
+	 * If the hints specify AF_INET6, or don't specify anything,
+	 * try to parse it as an IPv6 address.  If this fails, it will
+	 * fall through.
+	 */
+	if (hints->ai_family == AF_UNSPEC || hints->ai_family == AF_INET6) {
+	    switch (inet_pton(AF_INET6, hostname, &addr->sin6.sin6_addr)) {
+	    case -1:		/* system error */
+		error = EAI_SYSTEM;
+		goto out;
+	    case 0:		/* failure */
+		break;
+	    case 1:		/* success */
+		addr->sin6.sin6_family = AF_INET6;
+		addr->sin6.sin6_port = htons(port);
+		af[0] = AF_INET6;
+		addrlen[0] = sizeof(addr->sin6);
+		goto have_addr;
+	    }
+	}
+    }
+
+    /*
+     * No hostname, or hostname can't be parsed.
+     */
+    error = EAI_NONAME;
+    goto out;
+
+have_addr:
+    /*
+     * We have an address, or multiple possible addresses.  Allocate an
+     * array of addrinfo records to store the result.
+     */
+    if ((ai = calloc(naddr * nproto, sizeof(*ai))) == NULL) {
+	error = EAI_MEMORY;
+	goto out;
+    }
+
+    /*
+     * Fill in the addrinfo records with the cartesian product of
+     * matching address families and matching socktype/protocol pairs.
+     *
+     * XXX Consider randomizing the output for fun!
+     */
+    for (i = 0; i < naddr; i++) {
+	for (j = 0; j < nproto; j++) {
+	    ai[i*nproto + j] = (struct addrinfo) {
+		.ai_flags = 0, /* input flags, unused on output */
+		.ai_family = af[i],
+		.ai_addrlen = addrlen[i],
+		.ai_addr = &addr[i].sa,
+		.ai_socktype = socktype[j],
+		.ai_protocol = proto[j],
+		.ai_canonname = NULL,
+		.ai_next = &ai[i*nproto + j + 1],
+	    };
+	}
+    }
+    addr = NULL;		/* reference consumed by ai[...].ai_addr */
+
+    /*
+     * Null out the last addrinfo's next pointer.
+     */
+    ai[naddr*nproto - 1].ai_next = NULL;
+
+    /*
+     * Success!
+     */
+    error = 0;
+
+out:
+    /*
+     * In the event of error, free whatever we've allocated so far.
+     * Make sure to save and restore errno in case free touches it,
+     * because EAI_SYSTEM requires errno to report the system error.
+     */
+    if (error) {
+	int errno_save = errno;
+
+	if (addr)
+	    free(addr);
+	addr = NULL;
+	if (ai)
+	    freeaddrinfo(ai);
+	ai = NULL;
+
+	errno = errno_save;
+    }
+    *res = ai;
+    return error;
 }
 
 #endif	/* HAVE_GETADDRINFO */
diff --git a/kadmin/kadm_conn.c b/kadmin/kadm_conn.c
index 37b239f13..ccd89211d 100644
--- a/kadmin/kadm_conn.c
+++ b/kadmin/kadm_conn.c
@@ -65,7 +65,11 @@ add_kadm_port(krb5_context contextp, const char *service, unsigned int port)
 static void
 add_standard_ports (krb5_context contextp)
 {
-    add_kadm_port(contextp, "kerberos-adm", 749);
+    if (krb5_config_get_bool(context, NULL, "libdefaults", "block_dns",
+	    NULL))
+	add_kadm_port(contextp, "749", 749);
+    else
+	add_kadm_port(contextp, "kerberos-adm", 749);
 }
 
 /*
diff --git a/lib/kadm5/init_c.c b/lib/kadm5/init_c.c
index 2aa797523..8cc7cf476 100644
--- a/lib/kadm5/init_c.c
+++ b/lib/kadm5/init_c.c
@@ -574,7 +574,7 @@ kadm_connect(kadm5_client_context *ctx)
     if (krb5_config_get_bool(context, NULL, "libdefaults", "block_dns",
 	    NULL)) {
 	hints.ai_flags &= ~AI_CANONNAME;
-	hints.ai_flags |= AI_NUMERICHOST;
+	hints.ai_flags |= AI_NUMERICHOST|AI_NUMERICSERV;
     }
     error = getaddrinfo(hostname, portstr, &hints, &ai);
     if (error) {
diff --git a/lib/kadm5/ipropd_slave.c b/lib/kadm5/ipropd_slave.c
index d72413fd6..e7c16fe99 100644
--- a/lib/kadm5/ipropd_slave.c
+++ b/lib/kadm5/ipropd_slave.c
@@ -73,7 +73,7 @@ connect_to_master (krb5_context context, const char *master,
     if (krb5_config_get_bool(context, NULL, "libdefaults", "block_dns",
 	    NULL)) {
 	hints.ai_flags &= ~AI_CANONNAME;
-	hints.ai_flags |= AI_NUMERICHOST;
+	hints.ai_flags |= AI_NUMERICHOST|AI_NUMERICSERV;
     }
     error = getaddrinfo(master, port_str, &hints, &ai);
     if (error) {
diff --git a/lib/kadm5/log.c b/lib/kadm5/log.c
index 96d063eb9..cbfde885c 100644
--- a/lib/kadm5/log.c
+++ b/lib/kadm5/log.c
@@ -2720,7 +2720,7 @@ kadm5_log_signal_socket_info(krb5_context context,
 
     memset(&hints, 0, sizeof(hints));
 
-    hints.ai_flags = AI_NUMERICHOST;
+    hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV;
     if (server_end)
 	hints.ai_flags |= AI_PASSIVE;
     hints.ai_family = AF_INET;
diff --git a/lib/krb5/addr_families.c b/lib/krb5/addr_families.c
index 1e54ffdf0..4685d769b 100644
--- a/lib/krb5/addr_families.c
+++ b/lib/krb5/addr_families.c
@@ -1213,7 +1213,7 @@ krb5_parse_address(krb5_context context,
     if (krb5_config_get_bool(context, NULL, "libdefaults", "block_dns",
 	    NULL)) {
 	hint.ai_flags &= ~AI_CANONNAME;
-	hint.ai_flags |= AI_NUMERICHOST;
+	hint.ai_flags |= AI_NUMERICHOST|AI_NUMERICSERV;
     }
     error = getaddrinfo (string, NULL, &hint, &ai);
     if (error) {
diff --git a/lib/krb5/get_for_creds.c b/lib/krb5/get_for_creds.c
index eba58bf06..7524ce8b3 100644
--- a/lib/krb5/get_for_creds.c
+++ b/lib/krb5/get_for_creds.c
@@ -353,7 +353,7 @@ get_addresses(krb5_context      context,
     if (krb5_config_get_bool(context, NULL, "libdefaults", "block_dns",
 	    NULL)) {
 	hints.ai_flags &= ~AI_CANONNAME;
-	hints.ai_flags |= AI_NUMERICHOST;
+	hints.ai_flags |= AI_NUMERICHOST|AI_NUMERICSERV;
     }
     eai = getaddrinfo(hostname, NULL, &hints, &ai);
     if (eai) {
diff --git a/lib/krb5/krbhst.c b/lib/krb5/krbhst.c
index 10143a574..75a3d1f15 100644
--- a/lib/krb5/krbhst.c
+++ b/lib/krb5/krbhst.c
@@ -433,7 +433,7 @@ krb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host,
 	if (krb5_config_get_bool(context, NULL, "libdefaults", "block_dns",
 		NULL)) {
 	    hints.ai_flags &= ~AI_CANONNAME;
-	    hints.ai_flags |= AI_NUMERICHOST;
+	    hints.ai_flags |= AI_NUMERICHOST|AI_NUMERICSERV;
 	}
 	ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai);
 	if (ret) {
@@ -558,7 +558,7 @@ fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
     if (krb5_config_get_bool(context, NULL, "libdefaults", "block_dns",
 	    NULL)) {
 	hints.ai_flags &= ~AI_CANONNAME;
-	hints.ai_flags |= AI_NUMERICHOST;
+	hints.ai_flags |= AI_NUMERICHOST|AI_NUMERICSERV;
     }
     ret = getaddrinfo(host, portstr, &hints, &ai);
     if (ret) {
diff --git a/lib/krb5/send_to_kdc.c b/lib/krb5/send_to_kdc.c
index a22be88e4..8258330c4 100644
--- a/lib/krb5/send_to_kdc.c
+++ b/lib/krb5/send_to_kdc.c
@@ -860,7 +860,7 @@ submit_request(krb5_context context, krb5_sendto_ctx ctx, krb5_krbhst_info *hi)
 	if (krb5_config_get_bool(context, NULL, "libdefaults", "block_dns",
 		NULL)) {
 	    hints.ai_flags &= ~AI_CANONNAME;
-	    hints.ai_flags |= AI_NUMERICHOST;
+	    hints.ai_flags |= AI_NUMERICHOST|AI_NUMERICSERV;
 	}
 	ret = getaddrinfo(proxy, portstr, &hints, &ai);
 	free(proxy2);
diff --git a/lib/krb5/verify_krb5_conf.c b/lib/krb5/verify_krb5_conf.c
index f93136550..ad4a8fb54 100644
--- a/lib/krb5/verify_krb5_conf.c
+++ b/lib/krb5/verify_krb5_conf.c
@@ -205,7 +205,7 @@ check_host(krb5_context context, const char *path, char *data)
     if (krb5_config_get_bool(context, NULL, "libdefaults", "block_dns",
 	    NULL)) {
 	hints.ai_flags &= ~AI_CANONNAME;
-	hints.ai_flags |= AI_NUMERICHOST;
+	hints.ai_flags |= AI_NUMERICHOST|AI_NUMERICSERV;
     }
     ret = getaddrinfo(hostname, service, &hints, &ai);
     if (ret == EAI_SERVICE && !isdigit((unsigned char)service[0])) {
diff --git a/lib/roken/test-mini_inetd.c b/lib/roken/test-mini_inetd.c
index 7ab996ae8..d6333e4b8 100644
--- a/lib/roken/test-mini_inetd.c
+++ b/lib/roken/test-mini_inetd.c
@@ -49,7 +49,7 @@ get_address(int flags, struct addrinfo ** ret)
 
     memset(&ai, 0, sizeof(ai));
 
-    ai.ai_flags = flags | AI_NUMERICHOST;
+    ai.ai_flags = flags | AI_NUMERICHOST | AI_NUMERICSERV;
     ai.ai_family = AF_INET;
     ai.ai_socktype = SOCK_STREAM;
     ai.ai_protocol = PF_UNSPEC;