From dd1a6930a8ee9dd3edcff14d2150a70fc6d51e86 Mon Sep 17 00:00:00 2001
From: itojun <>
Date: Thu, 22 Jun 2000 07:31:18 +0000
Subject: ipv6 support in resolver.  "nameserver" line in /etc/resolv.conf now
 takes ipv6 address.

---
 src/lib/libc/net/res_init.c | 115 ++++++++++++++++++++++++-
 src/lib/libc/net/res_send.c | 202 ++++++++++++++++++++++++++++++++++----------
 2 files changed, 270 insertions(+), 47 deletions(-)

(limited to 'src/lib')

diff --git a/src/lib/libc/net/res_init.c b/src/lib/libc/net/res_init.c
index fd8d279cf0..ac75a4babb 100644
--- a/src/lib/libc/net/res_init.c
+++ b/src/lib/libc/net/res_init.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: res_init.c,v 1.18 1999/09/03 21:14:51 deraadt Exp $	*/
+/*	$OpenBSD: res_init.c,v 1.19 2000/06/22 07:31:18 itojun Exp $	*/
 
 /*
  * ++Copyright++ 1985, 1989, 1993
@@ -55,12 +55,16 @@
  * --Copyright--
  */
 
+#ifndef INET6
+#define INET6
+#endif
+
 #if defined(LIBC_SCCS) && !defined(lint)
 #if 0
 static char sccsid[] = "@(#)res_init.c	8.1 (Berkeley) 6/7/93";
 static char rcsid[] = "$From: res_init.c,v 8.7 1996/09/28 06:51:07 vixie Exp $";
 #else
-static char rcsid[] = "$OpenBSD: res_init.c,v 1.18 1999/09/03 21:14:51 deraadt Exp $";
+static char rcsid[] = "$OpenBSD: res_init.c,v 1.19 2000/06/22 07:31:18 itojun Exp $";
 #endif
 #endif /* LIBC_SCCS and not lint */
 
@@ -78,6 +82,9 @@ static char rcsid[] = "$OpenBSD: res_init.c,v 1.18 1999/09/03 21:14:51 deraadt E
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
+#ifdef INET6
+#include <netdb.h>
+#endif /* INET6 */
 
 /*-------------------------------------- info about "sortlist" --------------
  * Marc Majka		1994/04/16
@@ -119,6 +126,9 @@ struct __res_state _res
 	= { RES_TIMEOUT, }	/* Motorola, et al. */
 # endif
 	;
+#ifdef INET6
+struct __res_state_ext _res_ext;
+#endif /* INET6 */
 
 /*
  * Set up default settings.  If the configuration file exist, the values
@@ -193,6 +203,11 @@ res_init()
 #endif
 	_res.nsaddr.sin_family = AF_INET;
 	_res.nsaddr.sin_port = htons(NAMESERVER_PORT);
+	_res.nsaddr.sin_len = sizeof(struct sockaddr_in);
+#ifdef INET6
+	if (sizeof(_res_ext.nsaddr) >= _res.nsaddr.sin_len)
+		memcpy(&_res_ext.nsaddr, &_res.nsaddr, _res.nsaddr.sin_len);
+#endif
 	_res.nscount = 1;
 	_res.ndots = 1;
 	_res.pfcode = 0;
@@ -336,23 +351,69 @@ res_init()
 		}
 		/* read nameservers to query */
 		if (MATCH(buf, "nameserver") && nserv < MAXNS) {
+#ifdef INET6
+		    char *q;
+		    struct addrinfo hints, *res;
+		    char pbuf[NI_MAXSERV];
+#else
 		    struct in_addr a;
+#endif /* INET6 */
 
 		    cp = buf + sizeof("nameserver") - 1;
 		    while (*cp == ' ' || *cp == '\t')
 			cp++;
+#ifdef INET6
+		    if ((*cp == '\0') || (*cp == '\n'))
+			continue;
+		    for (q = cp; *q; q++) {
+			if (isspace(*q)) {
+			    *q = '\0';
+			    break;
+			}
+		    }
+		    memset(&hints, 0, sizeof(hints));
+		    hints.ai_flags = AI_NUMERICHOST;
+		    hints.ai_socktype = SOCK_DGRAM;
+		    snprintf(pbuf, sizeof(pbuf), "%d", NAMESERVER_PORT);
+		    if (getaddrinfo(cp, pbuf, &hints, &res) == 0 &&
+			    res->ai_next == NULL) {
+			if (res->ai_addrlen <= sizeof(_res_ext.nsaddr_list[nserv])) {
+			    memcpy(&_res_ext.nsaddr_list[nserv], res->ai_addr,
+				res->ai_addrlen);
+			} else {
+			    memset(&_res_ext.nsaddr_list[nserv], 0,
+				sizeof(_res_ext.nsaddr_list[nserv]));
+			}
+			if (res->ai_addrlen <= sizeof(_res.nsaddr_list[nserv])) {
+			    memcpy(&_res.nsaddr_list[nserv], res->ai_addr,
+				res->ai_addrlen);
+			} else {
+			    memset(&_res.nsaddr_list[nserv], 0,
+				sizeof(_res.nsaddr_list[nserv]));
+			}
+			nserv++;
+		    }
+#else /* INET6 */
 		    if ((*cp != '\0') && (*cp != '\n') && inet_aton(cp, &a)) {
 			_res.nsaddr_list[nserv].sin_addr = a;
 			_res.nsaddr_list[nserv].sin_family = AF_INET;
 			_res.nsaddr_list[nserv].sin_port =
 				htons(NAMESERVER_PORT);
+			_res.nsaddr_list[nserv].sin_len =
+				sizeof(struct sockaddr_in);
 			nserv++;
 		    }
+#endif /* INET6 */
 		    continue;
 		}
 #ifdef RESOLVSORT
 		if (MATCH(buf, "sortlist")) {
 		    struct in_addr a;
+#ifdef INET6
+		    struct in6_addr a6;
+		    int m, i;
+		    u_char *u;
+#endif /* INET6 */
 
 		    cp = buf + sizeof("sortlist") - 1;
 		    while (nsort < MAXRESOLVSORT) {
@@ -386,8 +447,58 @@ res_init()
 				_res.sort_list[nsort].mask = 
 				    net_mask(_res.sort_list[nsort].addr);
 			    }
+#ifdef INET6
+			    _res_ext.sort_list[nsort].af = AF_INET;
+			    _res_ext.sort_list[nsort].addr.ina =
+				_res.sort_list[nsort].addr;
+			    _res_ext.sort_list[nsort].mask.ina.s_addr =
+				_res.sort_list[nsort].mask;
+#endif /* INET6 */
+			    nsort++;
+			}
+#ifdef INET6
+			else if (inet_pton(AF_INET6, net, &a6) == 1) {
+			    _res_ext.sort_list[nsort].af = AF_INET6;
+			    _res_ext.sort_list[nsort].addr.in6a = a6;
+			    u = (u_char *)&_res_ext.sort_list[nsort].mask.in6a;
+			    *cp++ = n;
+			    net = cp;
+			    while (*cp && *cp != ';' &&
+				    isascii(*cp) && !isspace(*cp))
+				cp++;
+			    m = n;
+			    n = *cp;
+			    *cp = 0;
+			    switch (m) {
+			    case '/':
+				m = atoi(net);
+				break;
+			    case '&':
+				if (inet_pton(AF_INET6, net, u) == 1) {
+				    m = -1;
+				    break;
+				}
+				/*FALLTHRU*/
+			    default:
+				m = sizeof(struct in6_addr) * NBBY;
+				break;
+			    }
+			    if (m >= 0) {
+				for (i = 0; i < sizeof(struct in6_addr); i++) {
+				    if (m <= 0) {
+					*u = 0;
+				    } else {
+					m -= NBBY;
+					*u = (u_char)~0;
+					if (m < 0)
+					    *u <<= -m;
+				    }
+				    u++;
+				}
+			    }
 			    nsort++;
 			}
+#endif /* INET6 */
 			*cp = n;
 		    }
 		    continue;
diff --git a/src/lib/libc/net/res_send.c b/src/lib/libc/net/res_send.c
index 0cda3510eb..d0f17af8ed 100644
--- a/src/lib/libc/net/res_send.c
+++ b/src/lib/libc/net/res_send.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: res_send.c,v 1.8 1998/03/19 00:30:08 millert Exp $	*/
+/*	$OpenBSD: res_send.c,v 1.9 2000/06/22 07:31:18 itojun Exp $	*/
 
 /*
  * ++Copyright++ 1985, 1989, 1993
@@ -55,12 +55,16 @@
  * --Copyright--
  */
 
+#ifndef INET6
+#define INET6
+#endif
+
 #if defined(LIBC_SCCS) && !defined(lint)
 #if 0
 static char sccsid[] = "@(#)res_send.c	8.1 (Berkeley) 6/4/93";
 static char rcsid[] = "$From: res_send.c,v 8.12 1996/10/08 04:51:06 vixie Exp $";
 #else
-static char rcsid[] = "$OpenBSD: res_send.c,v 1.8 1998/03/19 00:30:08 millert Exp $";
+static char rcsid[] = "$OpenBSD: res_send.c,v 1.9 2000/06/22 07:31:18 itojun Exp $";
 #endif
 #endif /* LIBC_SCCS and not lint */
 
@@ -95,6 +99,7 @@ static char rcsid[] = "$OpenBSD: res_send.c,v 1.8 1998/03/19 00:30:08 millert Ex
 static int s = -1;	/* socket used for communications */
 static int connected = 0;	/* is the socket connected */
 static int vc = 0;	/* is the socket a virtual ciruit? */
+static int af = 0;		/* address family of socket */
 
 #ifndef FD_SET
 /* XXX - should be in portability.h */
@@ -119,21 +124,29 @@ static int vc = 0;	/* is the socket a virtual ciruit? */
 			fprintf args;\
 			__fp_nquery(query, size, stdout);\
 		} else {}
+static char abuf[NI_MAXHOST];
+static char pbuf[NI_MAXSERV];
+static void Aerror __P((FILE *, char *, int, struct sockaddr *));
+static void Perror __P((FILE *, char *, int));
+
     static void
     Aerror(file, string, error, address)
 	FILE *file;
 	char *string;
 	int error;
-	struct sockaddr_in address;
+	struct sockaddr *address;
     {
 	int save = errno;
 
 	if (_res.options & RES_DEBUG) {
-		fprintf(file, "res_send: %s ([%s].%u): %s\n",
-			string,
-			inet_ntoa(address.sin_addr),
-			ntohs(address.sin_port),
-			strerror(error));
+		if (getnameinfo(address, address->sa_len, abuf, sizeof(abuf),
+		    pbuf, sizeof(pbuf),
+		    NI_NUMERICHOST|NI_NUMERICSERV|NI_WITHSCOPEID) != 0) {
+			strncpy(abuf, "?", sizeof(abuf));
+			strncpy(pbuf, "?", sizeof(pbuf));
+		}
+		fprintf(file, "res_send: %s ([%s].%s): %s\n",
+			string, abuf, pbuf, strerror(error));
 	}
 	errno = save;
     }
@@ -172,6 +185,37 @@ res_send_setrhook(hook)
 	Rhook = hook;
 }
 
+#ifdef INET6
+static struct sockaddr * get_nsaddr __P((size_t));
+
+/*
+ * pick appropriate nsaddr_list for use.  see res_init() for initialization.
+ */
+static struct sockaddr *
+get_nsaddr(n)
+	size_t n;
+{
+
+	if (!_res.nsaddr_list[n].sin_family) {
+		/*
+		 * - _res_ext.nsaddr_list[n] holds an address that is larger
+		 *   than struct sockaddr, and
+		 * - user code did not update _res.nsaddr_list[n].
+		 */
+		return (struct sockaddr *)&_res_ext.nsaddr_list[n];
+	} else {
+		/*
+		 * - user code updated _res.nsaddr_list[n], or
+		 * - _res.nsaddr_list[n] has the same content as
+		 *   _res_ext.nsaddr_list[n].
+		 */
+		return (struct sockaddr *)&_res.nsaddr_list[n];
+	}
+}
+#else
+#define get_nsaddr(n)	((struct sockaddr *)&_res.nsaddr_list[(n)])
+#endif
+
 /* int
  * res_isourserver(ina)
  *	looks up "ina" in _res.ns_addr_list[]
@@ -185,21 +229,43 @@ int
 res_isourserver(inp)
 	const struct sockaddr_in *inp;
 {
-	struct sockaddr_in ina;
-	register int ns, ret;
+#ifdef INET6
+	const struct sockaddr_in6 *in6p = (const struct sockaddr_in6 *)inp;
+	const struct sockaddr_in6 *srv6;
+#endif
+	const struct sockaddr_in *srv;
+	int ns, ret;
 
-	ina = *inp;
 	ret = 0;
-	for (ns = 0;  ns < _res.nscount;  ns++) {
-		register const struct sockaddr_in *srv = &_res.nsaddr_list[ns];
-
-		if (srv->sin_family == ina.sin_family &&
-		    srv->sin_port == ina.sin_port &&
-		    (srv->sin_addr.s_addr == INADDR_ANY ||
-		     srv->sin_addr.s_addr == ina.sin_addr.s_addr)) {
-			ret++;
-			break;
+	switch (inp->sin_family) {
+#ifdef INET6
+	case AF_INET6:
+		for (ns = 0; ns < _res.nscount; ns++) {
+			srv6 = (struct sockaddr_in6 *)get_nsaddr(ns);
+			if (srv6->sin6_family == in6p->sin6_family &&
+			    srv6->sin6_port == in6p->sin6_port &&
+			    srv6->sin6_scope_id == in6p->sin6_scope_id &&
+			    (IN6_IS_ADDR_UNSPECIFIED(&srv6->sin6_addr) ||
+			     IN6_ARE_ADDR_EQUAL(&srv6->sin6_addr,
+			         &in6p->sin6_addr))) {
+				ret++;
+				break;
+			}
+		}
+		break;
+#endif
+	case AF_INET:
+		for (ns = 0; ns < _res.nscount; ns++) {
+			srv = (struct sockaddr_in *)get_nsaddr(ns);
+			if (srv->sin_family == inp->sin_family &&
+			    srv->sin_port == inp->sin_port &&
+			    (srv->sin_addr.s_addr == INADDR_ANY ||
+			     srv->sin_addr.s_addr == inp->sin_addr.s_addr)) {
+				ret++;
+				break;
+			}
 		}
+		break;
 	}
 	return (ret);
 }
@@ -308,7 +374,20 @@ res_send(buf, buflen, ans, anssiz)
 	 */
 	for (try = 0; try < _res.retry; try++) {
 	    for (ns = 0; ns < _res.nscount; ns++) {
-		struct sockaddr_in *nsap = &_res.nsaddr_list[ns];
+		struct sockaddr *nsap = get_nsaddr(ns);
+		socklen_t salen;
+
+		if (nsap->sa_len)
+			salen = nsap->sa_len;
+#ifdef INET6
+		else if (nsap->sa_family == AF_INET6)
+			salen = sizeof(struct sockaddr_in6);
+#endif
+		else if (nsap->sa_family == AF_INET)
+			salen = sizeof(struct sockaddr_in);
+		else
+			salen = 0;	/*unknown, die on connect*/
+
     same_ns:
 		if (badns & (1 << ns)) {
 			res_close();
@@ -321,7 +400,8 @@ res_send(buf, buflen, ans, anssiz)
 			do {
 				res_sendhookact act;
 
-				act = (*Qhook)(&nsap, &buf, &buflen,
+				act = (*Qhook)((struct sockaddr_in **)&nsap,
+					       &buf, &buflen,
 					       ans, anssiz, &resplen);
 				switch (act) {
 				case res_goahead:
@@ -345,9 +425,11 @@ res_send(buf, buflen, ans, anssiz)
 			} while (!done);
 		}
 
-		Dprint(_res.options & RES_DEBUG,
+		Dprint((_res.options & RES_DEBUG) &&
+		       getnameinfo(nsap, salen, abuf, sizeof(abuf),
+			   NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID) == 0,
 		       (stdout, ";; Querying server (# %d) address = %s\n",
-			ns + 1, inet_ntoa(nsap->sin_addr)));
+			ns + 1, abuf));
 
 		if (v_circuit) {
 			int truncated;
@@ -361,22 +443,28 @@ res_send(buf, buflen, ans, anssiz)
 			 */
 			try = _res.retry;
 			truncated = 0;
-			if ((s < 0) || (!vc)) {
+			if ((s < 0) || (!vc) || (af != nsap->sa_family)) {
 				if (s >= 0)
 					res_close();
 
-				s = socket(PF_INET, SOCK_STREAM, 0);
+				af = nsap->sa_family;
+				s = socket(af, SOCK_STREAM, 0);
 				if (s < 0) {
 					terrno = errno;
 					Perror(stderr, "socket(vc)", errno);
+#if 0
 					return (-1);
+#else
+					badns |= (1 << ns);
+					res_close();
+					goto next_ns;
+#endif
 				}
 				errno = 0;
-				if (connect(s, (struct sockaddr *)nsap,
-					    sizeof(struct sockaddr)) < 0) {
+				if (connect(s, nsap, salen) < 0) {
 					terrno = errno;
 					Aerror(stderr, "connect/vc",
-					       errno, *nsap);
+					       errno, nsap);
 					badns |= (1 << ns);
 					res_close();
 					goto next_ns;
@@ -490,21 +578,36 @@ read_len:
 			 */
 			struct timeval timeout;
 			fd_set *dsmaskp;
-			struct sockaddr_in from;
+			struct sockaddr_storage from;
 			int fromlen;
 
-			if ((s < 0) || vc) {
+			if ((s < 0) || vc || (af != nsap->sa_family)) {
 				if (vc)
 					res_close();
-				s = socket(PF_INET, SOCK_DGRAM, 0);
+				af = nsap->sa_family;
+				s = socket(af, SOCK_DGRAM, 0);
 				if (s < 0) {
 #if !CAN_RECONNECT
  bad_dg_sock:
 #endif
 					terrno = errno;
 					Perror(stderr, "socket(dg)", errno);
+#if 0
 					return (-1);
+#else
+					badns |= (1 << ns);
+					res_close();
+					goto next_ns;
+#endif
+				}
+#ifdef IPV6_MINMTU
+				if (af == AF_INET6) {
+					const int yes = 1;
+					(void)setsockopt(s, IPPROTO_IPV6,
+					    IPV6_USE_MIN_MTU, &yes,
+					    sizeof(yes));
 				}
+#endif
 				connected = 0;
 			}
 			/*
@@ -528,12 +631,10 @@ read_len:
 				 * receive a response from another server.
 				 */
 				if (!connected) {
-					if (connect(s, (struct sockaddr *)nsap,
-						    sizeof(struct sockaddr)
-						    ) < 0) {
+					if (connect(s, nsap, salen) < 0) {
 						Aerror(stderr,
 						       "connect(dg)",
-						       errno, *nsap);
+						       errno, nsap);
 						badns |= (1 << ns);
 						res_close();
 						goto next_ns;
@@ -553,6 +654,9 @@ read_len:
 				 */
 				if (connected) {
 #if CAN_RECONNECT
+#ifdef INET6
+					/* XXX: any errornous address */
+#endif /* INET6 */
 					struct sockaddr_in no_addr;
 
 					no_addr.sin_family = AF_INET;
@@ -563,22 +667,28 @@ read_len:
 						        &no_addr,
 						       sizeof(no_addr));
 #else
-					int s1 = socket(PF_INET, SOCK_DGRAM,0);
+					int s1 = socket(af, SOCK_DGRAM,0);
 					if (s1 < 0)
 						goto bad_dg_sock;
 					(void) dup2(s1, s);
 					(void) close(s1);
 					Dprint(_res.options & RES_DEBUG,
 					       (stdout, ";; new DG socket\n"))
+#endif
+#ifdef IPV6_MINMTU
+					if (af == AF_INET6) {
+						const int yes = 1;
+						(void)setsockopt(s, IPPROTO_IPV6,
+						    IPV6_USE_MIN_MTU, &yes,
+						    sizeof(yes));
+					}
 #endif
 					connected = 0;
 					errno = 0;
 				}
 				if (sendto(s, (char*)buf, buflen, 0,
-					   (struct sockaddr *)nsap,
-					   sizeof(struct sockaddr))
-				    != buflen) {
-					Aerror(stderr, "sendto", errno, *nsap);
+					   nsap, salen) != buflen) {
+					Aerror(stderr, "sendto", errno, nsap);
 					badns |= (1 << ns);
 					res_close();
 					goto next_ns;
@@ -623,7 +733,7 @@ read_len:
 				goto next_ns;
 			}
 			errno = 0;
-			fromlen = sizeof(struct sockaddr_in);
+			fromlen = sizeof(from);
 			resplen = recvfrom(s, (char*)ans, anssiz, 0,
 					   (struct sockaddr *)&from, &fromlen);
 			if (resplen <= 0) {
@@ -646,7 +756,7 @@ read_len:
 			}
 #if CHECK_SRVR_ADDR
 			if (!(_res.options & RES_INSECURE1) &&
-			    !res_isourserver(&from)) {
+			    !res_isourserver((struct sockaddr_in *)&from)) {
 				/*
 				 * response from wrong server? ignore it.
 				 * XXX - potential security hazard could
@@ -703,7 +813,7 @@ read_len:
 		       (stdout, ";; got answer:\n"));
 		DprintQ((_res.options & RES_DEBUG) ||
 			(_res.pfcode & RES_PRF_REPLY),
-			(stdout, ""),
+			(stdout, "%s", ""),
 			ans, (resplen>anssiz)?anssiz:resplen);
 		/*
 		 * If using virtual circuits, we assume that the first server
@@ -723,7 +833,8 @@ read_len:
 			do {
 				res_sendhookact act;
 
-				act = (*Rhook)(nsap, buf, buflen,
+				act = (*Rhook)((struct sockaddr_in *)nsap,
+					       buf, buflen,
 					       ans, anssiz, &resplen);
 				switch (act) {
 				case res_goahead:
@@ -776,5 +887,6 @@ res_close()
 		s = -1;
 		connected = 0;
 		vc = 0;
+		af = 0;
 	}
 }
-- 
cgit v1.2.3-55-g6feb