[tor-commits] [torsocks/master] Add localhost resolve utils function

dgoulet at torproject.org dgoulet at torproject.org
Fri Apr 4 22:40:27 UTC 2014


commit 5ded9b27a083e0f5d14d8141c197a095165d86da
Author: David Goulet <dgoulet at ev0ke.net>
Date:   Sat Feb 8 12:07:44 2014 -0500

    Add localhost resolve utils function
    
    Using a hostname, address family (v4/v6) and a buffer, this function
    tries to match the hostname to the localhost names list local to
    torsocks. If found, the given buffer is populated with the corresponding
    IP address.
    
    This is the first step to remove gethostent() in the code. It's
    important for torsocks to be able to resolve localhost but it should NOT
    try to resolve DNS with a local file (ex: /etc/hosts). One use cases is
    for SSH to be able to bind to localhost for a SOCKS proxy for instance
    using torsocks hence needing to resolve local name.
    
    Signed-off-by: David Goulet <dgoulet at ev0ke.net>
---
 src/common/compat.h     |    5 ++-
 src/common/utils.c      |  103 ++++++++++++++++++++++++++++++++++++++++++++++-
 src/common/utils.h      |    1 +
 tests/unit/test_utils.c |   50 ++++++++++++++++++++++-
 4 files changed, 156 insertions(+), 3 deletions(-)

diff --git a/src/common/compat.h b/src/common/compat.h
index 077bcce..ee8263f 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -100,6 +100,9 @@ void tsocks_mutex_unlock(tsocks_mutex_t *m);
 
 #define TSOCKS_CLASSA_NET   0xff000000
 #define TSOCKS_LOOPBACK_NET 0x7f000000
-#define TSOCKS_IN6_INIT     { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }
+
+/* Loopback address in network byte order. */
+#define TSOCKS_LOOPBACK     0x0100007f
+#define TSOCKS_LOOPBACK6    { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }
 
 #endif /* TORSOCKS_COMPAT_H */
diff --git a/src/common/utils.c b/src/common/utils.c
index 4ecb9f4..e526ce7 100644
--- a/src/common/utils.c
+++ b/src/common/utils.c
@@ -30,6 +30,20 @@
 #include "utils.h"
 
 /*
+ * Hardcoded list of localhost hostname. In order to resolve these for an
+ * application, usually we would need to check in /etc/hosts by using
+ * gethostent() but in order to avoid DNS resolution outside of Tor (even local
+ * file), only the localhost is resolved and the rest is sent through Tor.
+ */
+static const char *localhost_names_v4[] = {
+	"localhost", "ip-localhost", NULL,
+};
+
+static const char *localhost_names_v6[] = {
+	"localhost", "ip6-loopback", "ip6-localhost", NULL,
+};
+
+/*
  * Return 1 if the given IP belongs in the af domain else return a negative
  * value.
  */
@@ -49,6 +63,35 @@ static int check_addr(const char *ip, int af)
 }
 
 /*
+ * Given a name string and a list NULL terminated of strings, this will try to
+ * match the name.
+ *
+ * Return the entry in the list if match else NULL.
+ */
+static const char *match_name(const char *name, const char **list)
+{
+	unsigned int count = 0;
+	const char *entry;
+
+	assert(name);
+	assert(list);
+
+	while ((entry = list[count]) != NULL) {
+		int ret;
+
+		ret = strcmp(entry, name);
+		if (!ret) {
+			/* Match. */
+			goto end;
+		}
+		count++;
+	}
+
+end:
+	return entry;
+}
+
+/*
  * Return 1 if the given IP is an IPv4.
  */
 ATTR_HIDDEN
@@ -195,7 +238,7 @@ int utils_sockaddr_is_localhost(const struct sockaddr *sa)
 				TSOCKS_LOOPBACK_NET);
 	} else if (sa->sa_family == AF_INET6) {
 		const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *) sa;
-		static const uint8_t addr[] = TSOCKS_IN6_INIT;
+		static const uint8_t addr[] = TSOCKS_LOOPBACK6;
 		is_localhost = !memcmp(sin6->sin6_addr.s6_addr, addr,
 				sizeof(sin6->sin6_addr.s6_addr));
 	} else {
@@ -205,3 +248,61 @@ int utils_sockaddr_is_localhost(const struct sockaddr *sa)
 
 	return is_localhost;
 }
+
+/*
+ * Try to match a given name to localhost names (v4 and v6).
+ *
+ * If a match is found, the address in network byte order is copied in the
+ * buffer thus len must match the size of the given address family and 1 is
+ * returned.
+ *
+ * If NO match is found, 0 is return and buf is untouched.
+ *
+ * If len is the wrong size, -EINVAL is returned and buf is untouched.
+ */
+ATTR_HIDDEN
+int utils_localhost_resolve(const char *name, int af, void *buf, size_t len)
+{
+	const char *entry;
+
+	assert(name);
+	assert(buf);
+
+	if (af == AF_INET) {
+		const in_addr_t addr = TSOCKS_LOOPBACK;
+
+		entry = match_name(name, localhost_names_v4);
+		if (entry) {
+			if (len < sizeof(in_addr_t)) {
+				/* Size of buffer is not large enough. */
+				goto error;
+			}
+			memcpy(buf, &addr, sizeof(addr));
+			goto match;
+		}
+	} else if (af == AF_INET6) {
+		const uint8_t addr[] = TSOCKS_LOOPBACK6;
+
+		entry = match_name(name, localhost_names_v6);
+		if (entry) {
+			if (len < sizeof(addr)) {
+				/* Size of buffer is not large enough. */
+				goto error;
+			}
+			memcpy(buf, addr, sizeof(addr));
+			goto match;
+		}
+	} else {
+		/* Unknown family type. */
+		assert(0);
+		goto error;
+	}
+
+	/* No match. */
+	return 0;
+match:
+	/* Match found. */
+	return 1;
+error:
+	return -EINVAL;
+}
diff --git a/src/common/utils.h b/src/common/utils.h
index 7721dc5..b0281eb 100644
--- a/src/common/utils.h
+++ b/src/common/utils.h
@@ -31,5 +31,6 @@ int utils_tokenize_ignore_comments(const char *_line, size_t size, char **tokens
 int utils_is_address_ipv4(const char *ip);
 int utils_is_address_ipv6(const char *ip);
 int utils_sockaddr_is_localhost(const struct sockaddr *sa);
+int utils_localhost_resolve(const char *name, int af, void *buf, size_t len);
 
 #endif /* TORSOCKS_UTILS_H */
diff --git a/tests/unit/test_utils.c b/tests/unit/test_utils.c
index e241901..df8e089 100644
--- a/tests/unit/test_utils.c
+++ b/tests/unit/test_utils.c
@@ -23,7 +23,7 @@
 
 #include <tap/tap.h>
 
-#define NUM_TESTS 10
+#define NUM_TESTS 22
 
 static void test_is_address_ipv4(void)
 {
@@ -60,6 +60,53 @@ static void test_is_address_ipv6(void)
 	ok(ret == -1, "Invalid IPv6 address when IPv4");
 }
 
+static void test_localhost_resolve(void)
+{
+	int ret = 0;
+	in_addr_t ipv4, loopback = TSOCKS_LOOPBACK;
+	struct in6_addr ipv6;
+	const uint8_t loopback6[] = TSOCKS_LOOPBACK6;
+
+	diag("Utils localhost resolve test");
+
+	ret = utils_localhost_resolve("localhost", AF_INET, &ipv4, sizeof(ipv4));
+	ok(ret == 1, "localhost resolved successfully");
+	ok(memcmp(&ipv4, &loopback, sizeof(ipv4)) == 0,
+			"localhost IPv4 address matches");
+
+	ret = utils_localhost_resolve("ip-localhost", AF_INET, &ipv4, sizeof(ipv4));
+	ok(ret == 1, "ip-localhost resolved successfully");
+	ok(memcmp(&ipv4, &loopback, sizeof(ipv4)) == 0,
+			"ip-localhost IPv4 address matches");
+
+	ret = utils_localhost_resolve("nsa.gov", AF_INET, &ipv4, sizeof(ipv4));
+	ok(ret == 0, "nsa.gov did NOT resolved successfully");
+
+	/* Len smaller than buffer size. */
+	ret = utils_localhost_resolve("localhost", AF_INET, &ipv4, 1);
+	ok(ret == -EINVAL, "localhost len of buffer was too small");
+
+	/* IPV6 */
+
+	ret = utils_localhost_resolve("localhost", AF_INET6, &ipv6, sizeof(ipv6));
+	ok(ret == 1, "localhost v6 resolved successfully");
+	ok(memcmp(&ipv6, &loopback6, sizeof(in6addr_loopback)) == 0,
+			"localhost IPv6 address matches");
+
+	ret = utils_localhost_resolve("ip6-localhost", AF_INET6, &ipv6,
+			sizeof(ipv6));
+	ok(ret == 1, "ip6-localhost resolved successfully");
+	ok(memcmp(&ipv6, &loopback6, sizeof(in6addr_loopback)) == 0,
+			"localhost IPv6 address matches");
+
+	ret = utils_localhost_resolve("nsa.gov", AF_INET6, &ipv6, sizeof(ipv6));
+	ok(ret == 0, "nsa.gov did NOT resolved successfully");
+
+	/* Len smaller than buffer size. */
+	ret = utils_localhost_resolve("localhost", AF_INET6, &ipv6, 1);
+	ok(ret == -EINVAL, "localhost v6 len of buffer was too small");
+}
+
 static void helper_reset_tokens(char **tokens)
 {
 	assert(tokens);
@@ -108,6 +155,7 @@ int main(int argc, char **argv)
 
 	test_is_address_ipv4();
 	test_is_address_ipv6();
+	test_localhost_resolve();
 	test_utils_tokenize_ignore_comments();
 
 	return exit_status();





More information about the tor-commits mailing list