svn commit: r190972 - in stable/7/lib/libc: . net string

Hajimu UMEMOTO ume at FreeBSD.org
Sun Apr 12 19:06:42 UTC 2009


Author: ume
Date: Sun Apr 12 19:06:41 2009
New Revision: 190972
URL: http://svn.freebsd.org/changeset/base/190972

Log:
  MFH r190382,190416,190525:
  - getaddrinfo(3) should accept numeric when ai_socktype is not
    specified in hint or hints is NULL.
  - Add support for SCTP to getaddrinfo(3).
    Now, getaddrinfo(3) returns two SOCK_STREAMs, IPPROTO_TCP and
    IPPROTO_SCTP.  It confuses some programs.  If getaddrinfo(3) returns
    IPPROTO_SCTP when SOCK_STREAM is specified by hints.ai_socktype, at
    least Apache doesn't work.  So, I made getaddrinfo(3) to return
    IPPROTO_SCTP with SOCK_STREAM only when IPPROTO_SCTP is specified
    explicitly by hints.ai_protocol.
  - Query DNS only once per an address family.
  
  Approved by:	re (kib)

Modified:
  stable/7/lib/libc/   (props changed)
  stable/7/lib/libc/net/getaddrinfo.c
  stable/7/lib/libc/string/ffsll.c   (props changed)
  stable/7/lib/libc/string/flsll.c   (props changed)

Modified: stable/7/lib/libc/net/getaddrinfo.c
==============================================================================
--- stable/7/lib/libc/net/getaddrinfo.c	Sun Apr 12 19:04:27 2009	(r190971)
+++ stable/7/lib/libc/net/getaddrinfo.c	Sun Apr 12 19:06:41 2009	(r190972)
@@ -102,7 +102,6 @@ __FBSDID("$FreeBSD$");
 # define FAITH
 #endif
 
-#define SUCCESS 0
 #define ANY 0
 #define YES 1
 #define NO  0
@@ -165,19 +164,20 @@ struct explore {
 
 static const struct explore explore[] = {
 #if 0
-	{ PF_LOCAL, 0, ANY, ANY, NULL, 0x01 },
+	{ PF_LOCAL, ANY, ANY, NULL, 0x01 },
 #endif
 #ifdef INET6
 	{ PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
 	{ PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
+	{ PF_INET6, SOCK_STREAM, IPPROTO_SCTP, "sctp", 0x03 },
+	{ PF_INET6, SOCK_SEQPACKET, IPPROTO_SCTP, "sctp", 0x07 },
 	{ PF_INET6, SOCK_RAW, ANY, NULL, 0x05 },
 #endif
 	{ PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
 	{ PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
+	{ PF_INET, SOCK_STREAM, IPPROTO_SCTP, "sctp", 0x03 },
+	{ PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, "sctp", 0x07 },
 	{ PF_INET, SOCK_RAW, ANY, NULL, 0x05 },
-	{ PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
-	{ PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
-	{ PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 },
 	{ -1, 0, 0, NULL, 0 },
 };
 
@@ -227,6 +227,8 @@ typedef union {
 } querybuf;
 
 static int str2number(const char *, int *);
+static int explore_copy(const struct addrinfo *, const struct addrinfo *,
+	struct addrinfo **);
 static int explore_null(const struct addrinfo *,
 	const char *, struct addrinfo **);
 static int explore_numeric(const struct addrinfo *, const char *,
@@ -237,6 +239,7 @@ static int get_canonname(const struct ad
 	struct addrinfo *, const char *);
 static struct addrinfo *get_ai(const struct addrinfo *,
 	const struct afd *, const char *);
+static struct addrinfo *copy_ai(const struct addrinfo *);
 static int get_portmatch(const struct addrinfo *, const char *);
 static int get_port(struct addrinfo *, const char *, int);
 static const struct afd *find_afd(int);
@@ -365,12 +368,23 @@ getaddrinfo(const char *hostname, const 
 	struct addrinfo sentinel;
 	struct addrinfo *cur;
 	int error = 0;
-	struct addrinfo ai;
-	struct addrinfo ai0;
+	struct addrinfo ai, ai0, *afai;
 	struct addrinfo *pai;
+	const struct afd *afd;
 	const struct explore *ex;
+	struct addrinfo *afailist[sizeof(afdl)/sizeof(afdl[0])];
+	struct addrinfo *afai_unspec;
+	int found;
 	int numeric = 0;
 
+	/* ensure we return NULL on errors */
+	*res = NULL;
+
+	memset(&ai, 0, sizeof(ai));
+
+	memset(afailist, 0, sizeof(afailist));
+	afai_unspec = NULL;
+
 	memset(&sentinel, 0, sizeof(sentinel));
 	cur = &sentinel;
 	pai = &ai;
@@ -410,17 +424,22 @@ getaddrinfo(const char *hostname, const 
 		 */
 		if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) {
 			for (ex = explore; ex->e_af >= 0; ex++) {
-				if (pai->ai_family != ex->e_af)
+				if (!MATCH_FAMILY(pai->ai_family, ex->e_af,
+				    WILD_AF(ex)))
 					continue;
-				if (ex->e_socktype == ANY)
+				if (!MATCH(pai->ai_socktype, ex->e_socktype,
+				    WILD_SOCKTYPE(ex)))
 					continue;
-				if (ex->e_protocol == ANY)
+				if (!MATCH(pai->ai_protocol, ex->e_protocol,
+				    WILD_PROTOCOL(ex)))
 					continue;
-				if (pai->ai_socktype == ex->e_socktype &&
-				    pai->ai_protocol != ex->e_protocol) {
-					ERR(EAI_BADHINTS);
-				}
+
+				/* matched */
+				break;
 			}
+
+			if (ex->e_af < 0)
+				ERR(EAI_BADHINTS);
 		}
 	}
 
@@ -473,49 +492,48 @@ getaddrinfo(const char *hostname, const 
 
 	ai0 = *pai;
 
-	/* NULL hostname, or numeric hostname */
-	for (ex = explore; ex->e_af >= 0; ex++) {
+	/*
+	 * NULL hostname, or numeric hostname.
+	 * If numeric representation of AF1 can be interpreted as FQDN
+	 * representation of AF2, we need to think again about the code below.
+	 */
+	found = 0;
+	for (afd = afdl; afd->a_af; afd++) {
 		*pai = ai0;
 
-		/* PF_UNSPEC entries are prepared for DNS queries only */
-		if (ex->e_af == PF_UNSPEC)
-			continue;
-
-		if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex)))
-			continue;
-		if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex)))
-			continue;
-		if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex)))
+		if (!MATCH_FAMILY(pai->ai_family, afd->a_af, 1))
 			continue;
 
 		if (pai->ai_family == PF_UNSPEC)
-			pai->ai_family = ex->e_af;
-		if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
-			pai->ai_socktype = ex->e_socktype;
-		if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
-			pai->ai_protocol = ex->e_protocol;
+			pai->ai_family = afd->a_af;
 
-		if (hostname == NULL)
-			error = explore_null(pai, servname, &cur->ai_next);
-		else
+		if (hostname == NULL) {
+			error = explore_null(pai, servname,
+			    &afailist[afd - afdl]);
+
+			/*
+			 * Errors from explore_null should be unexpected and
+			 * be caught to avoid returning an incomplete result.
+			 */
+			if (error != 0)
+				goto bad;
+		} else {
 			error = explore_numeric_scope(pai, hostname, servname,
-			    &cur->ai_next);
+			    &afailist[afd - afdl]);
 
-		if (error)
-			goto free;
+			/*
+			 * explore_numeric_scope returns an error for address
+			 * families that do not match that of hostname.
+			 * Thus we should not catch the error at this moment. 
+			 */
+		}
 
-		while (cur && cur->ai_next)
-			cur = cur->ai_next;
+		if (!error && afailist[afd - afdl])
+			found++;
 	}
-
-	/*
-	 * XXX
-	 * If numreic representation of AF1 can be interpreted as FQDN
-	 * representation of AF2, we need to think again about the code below.
-	 */
-	if (sentinel.ai_next) {
+	if (found) {
 		numeric = 1;
-		goto good;
+		goto globcopy;
 	}
 
 	if (hostname == NULL)
@@ -528,42 +546,55 @@ getaddrinfo(const char *hostname, const 
 
 	/*
 	 * hostname as alphabetical name.
-	 * we would like to prefer AF_INET6 than AF_INET, so we'll make a
-	 * outer loop by AFs.
 	 */
+	*pai = ai0;
+	error = explore_fqdn(pai, hostname, servname, &afai_unspec);
+
+globcopy:
 	for (ex = explore; ex->e_af >= 0; ex++) {
 		*pai = ai0;
 
-		/* require exact match for family field */
-		if (pai->ai_family != ex->e_af)
+		if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex)))
 			continue;
-
 		if (!MATCH(pai->ai_socktype, ex->e_socktype,
-				WILD_SOCKTYPE(ex))) {
+		    WILD_SOCKTYPE(ex)))
 			continue;
-		}
 		if (!MATCH(pai->ai_protocol, ex->e_protocol,
-				WILD_PROTOCOL(ex))) {
+		    WILD_PROTOCOL(ex)))
 			continue;
-		}
 
+		if (pai->ai_family == PF_UNSPEC)
+			pai->ai_family = ex->e_af;
 		if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
 			pai->ai_socktype = ex->e_socktype;
 		if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
 			pai->ai_protocol = ex->e_protocol;
 
-		error = explore_fqdn(pai, hostname, servname,
-			&cur->ai_next);
+		/*
+		 * if the servname does not match socktype/protocol, ignore it.
+		 */
+		if (get_portmatch(pai, servname) != 0)
+			continue;
+
+		if (afai_unspec)
+			afai = afai_unspec;
+		else {
+			if ((afd = find_afd(pai->ai_family)) == NULL)
+				continue;
+			/* XXX assumes that afd points inside afdl[] */
+			afai = afailist[afd - afdl];
+		}
+		if (!afai)
+			continue;
+
+		error = explore_copy(pai, afai, &cur->ai_next);
+		if (error != 0)
+			goto bad;
 
 		while (cur && cur->ai_next)
 			cur = cur->ai_next;
 	}
 
-	/* XXX inhibit errors if we have the result */
-	if (sentinel.ai_next)
-		error = 0;
-
-good:
 	/*
 	 * ensure we return either:
 	 * - error == 0, non-NULL *res
@@ -599,16 +630,22 @@ good:
 				}
 			}
 			*res = sentinel.ai_next;
-			return SUCCESS;
 		} else
 			error = EAI_FAIL;
 	}
-free:
+
 bad:
-	if (sentinel.ai_next)
-		freeaddrinfo(sentinel.ai_next);
-	*res = NULL;
-	return error;
+	if (afai_unspec)
+		freeaddrinfo(afai_unspec);
+	for (afd = afdl; afd->a_af; afd++) {
+		if (afailist[afd - afdl])
+			freeaddrinfo(afailist[afd - afdl]);
+	}
+	if (!*res)
+		if (sentinel.ai_next)
+			freeaddrinfo(sentinel.ai_next);
+
+	return (error);
 }
 
 static int
@@ -1060,6 +1097,41 @@ gai_addr2scopetype(struct sockaddr *sa)
 	}
 }
 
+static int
+explore_copy(const struct addrinfo *pai, const struct addrinfo *src0,
+    struct addrinfo **res)
+{
+	int error;
+	struct addrinfo sentinel, *cur;
+	const struct addrinfo *src;
+
+	error = 0;
+	sentinel.ai_next = NULL;
+	cur = &sentinel;
+
+	for (src = src0; src != NULL; src = src->ai_next) {
+		if (src->ai_family != pai->ai_family)
+			continue;
+
+		cur->ai_next = copy_ai(src);
+		if (!cur->ai_next) {
+			error = EAI_MEMORY;
+			goto fail;
+		}
+
+		cur->ai_next->ai_socktype = pai->ai_socktype;
+		cur->ai_next->ai_protocol = pai->ai_protocol;
+		cur = cur->ai_next;
+	}
+
+	*res = sentinel.ai_next;
+	return 0;
+
+fail:
+	freeaddrinfo(sentinel.ai_next);
+	return error;
+}
+
 /*
  * hostname == NULL.
  * passive socket -> anyaddr (0.0.0.0 or ::)
@@ -1088,12 +1160,6 @@ explore_null(const struct addrinfo *pai,
 	} else
 		_close(s);
 
-	/*
-	 * if the servname does not match socktype/protocol, ignore it.
-	 */
-	if (get_portmatch(pai, servname) != 0)
-		return 0;
-
 	afd = find_afd(pai->ai_family);
 	if (afd == NULL)
 		return 0;
@@ -1130,12 +1196,6 @@ explore_numeric(const struct addrinfo *p
 	*res = NULL;
 	ai = NULL;
 
-	/*
-	 * if the servname does not match socktype/protocol, ignore it.
-	 */
-	if (get_portmatch(pai, servname) != 0)
-		return 0;
-
 	afd = find_afd(pai->ai_family);
 	if (afd == NULL)
 		return 0;
@@ -1202,12 +1262,6 @@ explore_numeric_scope(const struct addri
 	char *cp, *hostname2 = NULL, *scope, *addr;
 	struct sockaddr_in6 *sin6;
 
-	/*
-	 * if the servname does not match socktype/protocol, ignore it.
-	 */
-	if (get_portmatch(pai, servname) != 0)
-		return 0;
-
 	afd = find_afd(pai->ai_family);
 	if (afd == NULL)
 		return 0;
@@ -1240,6 +1294,8 @@ explore_numeric_scope(const struct addri
 			sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr;
 			if (ip6_str2scopeid(scope, sin6, &scopeid) == -1) {
 				free(hostname2);
+				freeaddrinfo(*res);
+				*res = NULL;
 				return(EAI_NONAME); /* XXX: is return OK? */
 			}
 			sin6->sin6_scope_id = scopeid;
@@ -1248,6 +1304,10 @@ explore_numeric_scope(const struct addri
 
 	free(hostname2);
 
+	if (error && *res) {
+		freeaddrinfo(*res);
+		*res = NULL;
+	}
 	return error;
 #endif
 }
@@ -1331,6 +1391,38 @@ get_ai(const struct addrinfo *pai, const
 	return ai;
 }
 
+/* XXX need to malloc() the same way we do from other functions! */
+static struct addrinfo *
+copy_ai(const struct addrinfo *pai)
+{
+	struct addrinfo *ai;
+	size_t l;
+
+	l = sizeof(*ai) + pai->ai_addrlen;
+	if ((ai = (struct addrinfo *)malloc(l)) == NULL)
+		return NULL;
+	memset(ai, 0, l);
+	memcpy(ai, pai, sizeof(*ai));
+	ai->ai_addr = (struct sockaddr *)(void *)(ai + 1);
+	memcpy(ai->ai_addr, pai->ai_addr, pai->ai_addrlen);
+
+	if (pai->ai_canonname) {
+		l = strlen(pai->ai_canonname) + 1;
+		if ((ai->ai_canonname = malloc(l)) == NULL) {
+			free(ai);
+			return NULL;
+		}
+		strlcpy(ai->ai_canonname, pai->ai_canonname, l);
+	} else {
+		/* just to make sure */
+		ai->ai_canonname = NULL;
+	}
+
+	ai->ai_next = NULL;
+
+	return ai;
+}
+
 static int
 get_portmatch(const struct addrinfo *ai, const char *servname)
 {
@@ -1365,10 +1457,21 @@ get_port(struct addrinfo *ai, const char
 		return EAI_SERVICE;
 	case SOCK_DGRAM:
 	case SOCK_STREAM:
+	case SOCK_SEQPACKET:
 		allownumeric = 1;
 		break;
 	case ANY:
-		allownumeric = 0;
+		switch (ai->ai_family) {
+		case AF_INET:
+#ifdef AF_INET6
+		case AF_INET6:
+#endif
+			allownumeric = 1;
+			break;
+		default:
+			allownumeric = 0;
+			break;
+		}
 		break;
 	default:
 		return EAI_SOCKTYPE;
@@ -1384,13 +1487,17 @@ get_port(struct addrinfo *ai, const char
 	} else {
 		if (ai->ai_flags & AI_NUMERICSERV)
 			return EAI_NONAME;
-		switch (ai->ai_socktype) {
-		case SOCK_DGRAM:
+
+		switch (ai->ai_protocol) {
+		case IPPROTO_UDP:
 			proto = "udp";
 			break;
-		case SOCK_STREAM:
+		case IPPROTO_TCP:
 			proto = "tcp";
 			break;
+		case IPPROTO_SCTP:
+			proto = "sctp";
+			break;
 		default:
 			proto = NULL;
 			break;


More information about the svn-src-all mailing list