From cb9a13032285ee9a20507f21c44b67a547a9996c Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <campbell+heimdal@mumble.net>
Date: Wed, 10 Jan 2024 01:25:02 +0000
Subject: [PATCH] auditdns: Cover getnameinfo and gethostbyaddr too.

Fixes the final remaining part of:
https://github.com/heimdal/heimdal/issues/1214
---
 appl/test/auditdns.c | 123 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 123 insertions(+)

diff --git a/appl/test/auditdns.c b/appl/test/auditdns.c
index dfe32e379..4f5b1dde5 100644
--- a/appl/test/auditdns.c
+++ b/appl/test/auditdns.c
@@ -35,8 +35,10 @@
 #include <netinet/in.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include "resolve.h"
+#include "roken.h"
 
 struct rk_dns_reply *
 rk_dns_lookup(const char *domain, const char *type_name)
@@ -66,6 +68,24 @@ gethostbyname2(const char *name, int af)
 
 #endif	/* HAVE_GETHOSTBYNAME2 */
 
+struct hostent *
+gethostbyaddr(const void *addr, socklen_t len, int af)
+{
+    const socklen_t maxlen[] = {
+	[AF_INET] = sizeof(struct in_addr),
+	[AF_INET6] = sizeof(struct in6_addr),
+    };
+    char n[INET6_ADDRSTRLEN + 1];
+
+    if (af < 0 || af >= sizeof(maxlen)/sizeof(maxlen[0]) ||
+	maxlen[af] == 0 || len < maxlen[af] ||
+	inet_ntop(af, addr, n, sizeof n) == NULL)
+	fprintf(stderr, "Reverse DNS leak: %s\n", __func__);
+    else
+	fprintf(stderr, "Reverse DNS leak: %s %s\n", __func__, n);
+    abort();
+}
+
 #ifdef HAVE_GETADDRINFO
 
 void
@@ -294,6 +314,7 @@ getaddrinfo(const char *hostname, const char *servname,
 	 * fall through.
 	 */
 	if (hints->ai_family == AF_UNSPEC || hints->ai_family == AF_INET6) {
+	    /* XXX scope id? */
 	    switch (inet_pton(AF_INET6, hostname, &addr->sin6.sin6_addr)) {
 	    case -1:		/* system error */
 		error = EAI_SYSTEM;
@@ -381,3 +402,105 @@ out:
 }
 
 #endif	/* HAVE_GETADDRINFO */
+
+#ifdef HAVE_GETNAMEINFO
+
+int
+getnameinfo(const struct sockaddr *restrict sa, socklen_t salen,
+    char *restrict node, socklen_t nodelen,
+    char *restrict service, socklen_t servicelen,
+    int flags)
+{
+    char n[INET6_ADDRSTRLEN + 1] = "";
+    char s[5 + 1] = "";		/* ceil(log_10(2^16)) + 1 */
+
+    /*
+     * Call inet_ntop to format the appropriate member of the
+     * sockaddr_*.
+     */
+    switch (sa->sa_family) {
+    case AF_INET: {
+	struct sockaddr_in sin;
+
+	/*
+	 * Verify the socket address length is at least enough for
+	 * sockaddr_in, and make a copy to avoid strict aliasing
+	 * violation.
+	 */
+	if (salen < sizeof sin)
+	    return EAI_FAIL;
+	memcpy(&sin, sa, sizeof sin);
+
+	/*
+	 * Use inet_ntop to format sin_addr as x.y.z.w, and use
+	 * snprintf to format the port number in decimal.
+	 */
+	if (inet_ntop(AF_INET, &sin.sin_addr, n, sizeof n) == NULL)
+	    return EAI_FAIL;
+	snprintf(s, sizeof s, "%d", (int)sin.sin_port);
+	break;
+    }
+    case AF_INET6: {
+	struct sockaddr_in6 sin6;
+
+	/*
+	 * Verify the socket address length is at least enough for
+	 * sockaddr_in6, and make a copy to avoid strict aliasing
+	 * violation.
+	 */
+	if (salen < sizeof sin6)
+	    return EAI_FAIL;
+	memcpy(&sin6, sa, sizeof sin6);
+
+	/*
+	 * Use inet_ntop to format sin6_addr as a:b:c:...:h, and use
+	 * snprintf to format the port number in decimal.
+	 */
+	if (inet_ntop(AF_INET6, &sin6.sin6_addr, n, sizeof n) == NULL)
+	    return EAI_FAIL;
+	/* XXX scope id? */
+	snprintf(s, sizeof s, "%d", (int)sin6.sin6_port);
+	break;
+    }
+    default:
+	return EAI_FAMILY;
+    }
+
+    /*
+     * DNS audit: Abort unless the user specified flags with
+     * NI_NUMERICHOST|NI_NUMERICSERV|NI_NUMERICSCOPE.  We format the
+     * numeric syntax first so it can be included in the error message
+     * to give a clue about what might have DNS leaks.
+     *
+     * The NI_NUMERICSCOPE test is written in a funny way so that on
+     * platforms where it simply doesn't exist (like glibc and
+     * Windows), it doesn't spuriously fail -- scope ids naming is
+     * probably not a source of network leaks.
+     */
+    if ((flags & NI_NUMERICHOST) == 0 ||
+	(flags & NI_NUMERICSERV) == 0 ||
+	(flags & NI_NUMERICSCOPE) != NI_NUMERICSCOPE) {
+	fprintf(stderr, "Reverse DNS leak: %s %s %s\n", __func__, n, s);
+	abort();
+    }
+
+    /*
+     * Verify the (numeric) `names' we determined fit in the buffers
+     * provided, if any.
+     */
+    if ((node && nodelen > 0 && strlen(n) >= nodelen) ||
+	(service && servicelen > 0 && strlen(s) >= servicelen))
+	return EAI_OVERFLOW;
+
+    /*
+     * Copy out the answers that were requested.
+     */
+    if (node)
+	strlcpy(node, n, nodelen);
+    if (service)
+	strlcpy(service, s, servicelen);
+
+    return 0;
+}
+
+#endif	/* HAVE_GETNAMEINFO */