From ba83f0a487d169240e07a7f1b6b97c6f5ae100ef Mon Sep 17 00:00:00 2001
From: reyk <>
Date: Mon, 9 Feb 2015 09:23:39 +0000
Subject: When parsing the host in tls_connect(), first check if it is a
 numeric IPv4 or IPv6 address before trying to resolve the address with the
 AI_ADDRCONFIG flag set.  This makes sure that attempts to connect to numeric
 IPs or loopback addresses are always possible and not prevented by
 AI_ADDRCONFIG.

OK jsing@ tedu@
---
 src/lib/libtls/tls_client.c | 78 ++++++++++++++++++++++++++++-----------------
 1 file changed, 49 insertions(+), 29 deletions(-)

(limited to 'src')

diff --git a/src/lib/libtls/tls_client.c b/src/lib/libtls/tls_client.c
index 0894ce6333..907c334f15 100644
--- a/src/lib/libtls/tls_client.c
+++ b/src/lib/libtls/tls_client.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls_client.c,v 1.12 2015/02/08 04:12:34 reyk Exp $ */
+/* $OpenBSD: tls_client.c,v 1.13 2015/02/09 09:23:39 reyk Exp $ */
 /*
  * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
  *
@@ -44,10 +44,45 @@ tls_client(void)
 	return (ctx);
 }
 
+static int
+tls_connect_host(struct tls *ctx, const char *host, const char *port,
+    int af, int flag)
+{
+	struct addrinfo hints, *res, *res0;
+	int s = -1;
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = af;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = flag;
+
+	if ((s = getaddrinfo(host, port, &hints, &res0)) != 0) {
+		tls_set_error(ctx, "%s", gai_strerror(s));
+		return (-1);
+	}
+	for (res = res0; res; res = res->ai_next) {
+		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+		if (s == -1) {
+			tls_set_error(ctx, "socket");
+			continue;
+		}
+		if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
+			tls_set_error(ctx, "connect");
+			close(s);
+			s = -1;
+			continue;
+		}
+
+		break;  /* Connected. */
+	}
+	freeaddrinfo(res0);
+
+	return (s);
+}
+
 int
 tls_connect(struct tls *ctx, const char *host, const char *port)
 {
-	struct addrinfo hints, *res, *res0;
 	const char *h = NULL, *p = NULL;
 	char *hs = NULL, *ps = NULL;
 	int rv = -1, s = -1, ret;
@@ -79,33 +114,18 @@ tls_connect(struct tls *ctx, const char *host, const char *port)
 	h = (hs != NULL) ? hs : host;
 	p = (ps != NULL) ? ps : port;
 
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_UNSPEC;
-	hints.ai_socktype = SOCK_STREAM;
-	hints.ai_flags = AI_ADDRCONFIG;
-
-	if ((ret = getaddrinfo(h, p, &hints, &res0)) != 0) {
-		tls_set_error(ctx, "%s", gai_strerror(ret));
-		goto err;
-	}
-	for (res = res0; res; res = res->ai_next) {
-		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
-		if (s == -1) {
-			tls_set_error(ctx, "socket");
-			continue;
-		}
-		if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
-			tls_set_error(ctx, "connect");
-			close(s);
-			s = -1;
-			continue;
-		}
-
-		break;  /* Connected. */
-	}
-	freeaddrinfo(res0);
-
-	if (s == -1)
+	/*
+	 * First check if the host is specified as a numeric IP address,
+	 * either IPv4 or IPv6, before trying to resolve the host.
+	 * The AI_ADDRCONFIG resolver option will not return IPv4 or IPv6
+	 * records if it is not configured on an interface;  not considering
+	 * loopback addresses.  Checking the numeric addresses first makes
+	 * sure that connection attempts to numeric addresses and especially
+	 * 127.0.0.1 or ::1 loopback addresses are always possible.
+	 */
+	if ((s = tls_connect_host(ctx, h, p, AF_INET, AI_NUMERICHOST)) == -1 &&
+	    (s = tls_connect_host(ctx, h, p, AF_INET6, AI_NUMERICHOST)) == -1 &&
+	    (s = tls_connect_host(ctx, h, p, AF_UNSPEC, AI_ADDRCONFIG)) == -1)
 		goto err;
 
 	if (tls_connect_socket(ctx, s, h) != 0) {
-- 
cgit v1.2.3-55-g6feb