summaryrefslogtreecommitdiffstats
path: root/lib/libc/net/getaddrinfo.c
diff options
context:
space:
mode:
authorume <ume@FreeBSD.org>2009-03-29 17:55:11 +0000
committerume <ume@FreeBSD.org>2009-03-29 17:55:11 +0000
commitcb768e2631a7f373d03c4b3c7fb251e76badc6a7 (patch)
tree6a488259d702ef8c4abd5d7e8f27a7a43758a9c3 /lib/libc/net/getaddrinfo.c
parent048abafaf9a435fb875b6c78ab52eb109811be96 (diff)
downloadFreeBSD-src-cb768e2631a7f373d03c4b3c7fb251e76badc6a7.zip
FreeBSD-src-cb768e2631a7f373d03c4b3c7fb251e76badc6a7.tar.gz
Query DNS only once per an address family.
Obtained from: KAME MFC after: 2 weeks
Diffstat (limited to 'lib/libc/net/getaddrinfo.c')
-rw-r--r--lib/libc/net/getaddrinfo.c256
1 files changed, 170 insertions, 86 deletions
diff --git a/lib/libc/net/getaddrinfo.c b/lib/libc/net/getaddrinfo.c
index 121b861..830e464 100644
--- a/lib/libc/net/getaddrinfo.c
+++ b/lib/libc/net/getaddrinfo.c
@@ -102,7 +102,6 @@ __FBSDID("$FreeBSD$");
# define FAITH
#endif
-#define SUCCESS 0
#define ANY 0
#define YES 1
#define NO 0
@@ -179,11 +178,6 @@ static const struct explore explore[] = {
{ 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_STREAM, IPPROTO_SCTP, "sctp", 0x03 },
- { PF_UNSPEC, SOCK_SEQPACKET, IPPROTO_SCTP, "sctp", 0x07 },
- { PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 },
{ -1, 0, 0, NULL, 0 },
};
@@ -233,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 *,
@@ -243,6 +239,7 @@ static int get_canonname(const struct addrinfo *,
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);
@@ -371,12 +368,23 @@ getaddrinfo(const char *hostname, const char *servname,
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;
@@ -416,15 +424,18 @@ getaddrinfo(const char *hostname, const char *servname,
*/
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)
- break;
+
+ /* matched */
+ break;
}
if (ex->e_af < 0)
@@ -460,49 +471,48 @@ getaddrinfo(const char *hostname, const char *servname,
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)
@@ -515,42 +525,55 @@ getaddrinfo(const char *hostname, const char *servname,
/*
* 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
@@ -586,16 +609,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
@@ -1047,6 +1076,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 ::)
@@ -1075,12 +1139,6 @@ explore_null(const struct addrinfo *pai, const char *servname,
} 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;
@@ -1117,12 +1175,6 @@ explore_numeric(const struct addrinfo *pai, const char *hostname,
*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;
@@ -1189,12 +1241,6 @@ explore_numeric_scope(const struct addrinfo *pai, const char *hostname,
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;
@@ -1227,6 +1273,8 @@ explore_numeric_scope(const struct addrinfo *pai, const char *hostname,
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;
@@ -1235,6 +1283,10 @@ explore_numeric_scope(const struct addrinfo *pai, const char *hostname,
free(hostname2);
+ if (error && *res) {
+ freeaddrinfo(*res);
+ *res = NULL;
+ }
return error;
#endif
}
@@ -1318,6 +1370,38 @@ get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr)
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)
{
OpenPOWER on IntegriCloud