Subject: Re: Extending ipv4/6 support for nsswitch?
To: Bernd Ernesti <netbsd@arresum.inka.de>
From: None <itojun@iijlab.net>
List: tech-net
Date: 02/15/2000 22:36:26
>>the current search path for ipv4/6 is fixed and has some problems
>>when you only want a ipv4 search for dns entries.
>>What about extending the syntax so you can specify that you want
>>a filesIPv4, then a dnsIPv4 and if that all fails a filesIPv6 and
>>dnsIPv6 lookups?
>>And keep the old behaviour when you use files and dns.
>>hosts: files4 dns4 files6 dns4
>>Hmmm, is it possible to get ipv6 entries fron nis?
> The above does not really help. nsdispatch() will be given specific
> address family from gethostbyname2() and obeys that.
> I'm trying to address the issue, by having _dns_gethaddr() variant
> for use from getaddrinfo(). I think we don't need to impose
> nsdispatch() and nsswitch.conf, thanks to very generic code in
> nsdispatch(). I'll try to commit it sooner.
The patch should fix the following problems:
- getaddrinfo(3) now behaves nsswitch.conf search order.
if ai_family = PF_UNSPEC, and order is "files dns", it would do
the following:
lookup /etc/hosts for any of matching entry.
return all entries that match hostname.
if nothing is in /etc/hosts, query DNS with T_ANY
(like "dig hostname any"). return all T_A/T_AAAA entries.
So, if we have the following entries in /etc/hosts, they will all
be returned against "localhost" query.
127.0.0.1 localhost
::1 localhost
- allow extended scoped address syntax (like lo0%fe80::1) in /etc/hosts.
I admit there are too many similar code with gethnamaddr.c.
I hope to clean them up a bit but I don't think I can fully merge them.
(I fear to break gethostby*)
After I fix the following, I think of committing it.
- add NIS case
- sync with extended scoped address syntax change
(really sorry about the mess, this is based on ongoing draft.
now fe80::1%lo0)
itojun
---
Index: getaddrinfo.c
===================================================================
RCS file: /cvsroot/basesrc/lib/libc/net/getaddrinfo.c,v
retrieving revision 1.28
diff -u -r1.28 getaddrinfo.c
--- getaddrinfo.c 2000/02/10 03:06:53 1.28
+++ getaddrinfo.c 2000/02/15 13:30:43
@@ -69,6 +69,16 @@
#include <stdio.h>
#include <errno.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <nsswitch.h>
+
+#ifdef YP
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#endif
+
#define SUCCESS 0
#define ANY 0
#define YES 1
@@ -135,6 +145,9 @@
{ PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
{ PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 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 },
};
@@ -144,6 +157,22 @@
#define PTON_MAX 4
#endif
+static const ns_src default_dns_files[] = {
+ { NSSRC_FILES, NS_SUCCESS },
+ { NSSRC_DNS, NS_SUCCESS },
+ { 0 }
+};
+
+#if PACKETSZ > 1024
+#define MAXPACKET PACKETSZ
+#else
+#define MAXPACKET 1024
+#endif
+
+typedef union {
+ HEADER hdr;
+ u_char buf[MAXPACKET];
+} querybuf;
static int str_isnumber __P((const char *));
static int explore_fqdn __P((const struct addrinfo *, const char *,
@@ -166,6 +195,14 @@
static int ip6_str2scopeid __P((char *, struct sockaddr_in6 *));
#endif
+static struct addrinfo *getanswer __P((const querybuf *, int, const char *, int,
+ const struct addrinfo *, int *));
+static int _dns_getaddrinfo __P((void *, void *, va_list));
+static void _sethtent __P((void));
+static void _endhtent __P((void));
+static struct addrinfo *_gethtent __P((const char *, const struct addrinfo *));
+static int _files_getaddrinfo __P((void *, void *, va_list));
+
static char *ai_errlist[] = {
"Success",
"Address family for hostname not supported", /* EAI_ADDRFAMILY */
@@ -274,10 +311,9 @@
struct addrinfo ai;
struct addrinfo ai0;
struct addrinfo *pai;
- const struct afd *afd;
const struct explore *ex;
- sentinel.ai_next = NULL;
+ memset(&sentinel, 0, sizeof(sentinel));
cur = &sentinel;
pai = &ai;
pai->ai_flags = 0;
@@ -362,6 +398,10 @@
for (ex = explore; ex->e_af >= 0; ex++) {
*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)))
@@ -406,42 +446,32 @@
* we would like to prefer AF_INET6 than AF_INET, so we'll make a
* outer loop by AFs.
*/
- for (afd = afdl; afd->a_af; afd++) {
+ for (ex = explore; ex->e_af >= 0; ex++) {
*pai = ai0;
- if (!MATCH_FAMILY(pai->ai_family, afd->a_af, 1))
+ /* require exact match for family field */
+ if (pai->ai_family != ex->e_af)
continue;
-
- for (ex = explore; ex->e_af >= 0; ex++) {
- *pai = ai0;
-
- if (pai->ai_family == PF_UNSPEC)
- pai->ai_family = afd->a_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))) {
- continue;
- }
- if (!MATCH(pai->ai_protocol, ex->e_protocol,
- WILD_PROTOCOL(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))) {
+ 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;
+ 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);
+ error = explore_fqdn(pai, hostname, servname,
+ &cur->ai_next);
- while (cur && cur->ai_next)
- cur = cur->ai_next;
- }
+ while (cur && cur->ai_next)
+ cur = cur->ai_next;
}
/* XXX */
@@ -476,20 +506,19 @@
const char *servname;
struct addrinfo **res;
{
- struct hostent *hp;
- int h_error;
- int af;
- char **aplist = NULL, *apbuf = NULL;
- char *ap;
- struct addrinfo sentinel, *cur;
- int i;
- int naddrs;
- const struct afd *afd;
+ struct addrinfo *result;
+ struct addrinfo *cur;
int error = 0;
+ static const ns_dtab dtab[] = {
+ NS_FILES_CB(_files_getaddrinfo, NULL)
+ { NSSRC_DNS, _dns_getaddrinfo, NULL }, /* force -DHESIOD */
+#if 0
+ NS_NIS_CB(_yp_gethtbyname, NULL)
+#endif
+ { 0 }
+ };
- *res = NULL;
- sentinel.ai_next = NULL;
- cur = &sentinel;
+ result = NULL;
/*
* If AI_ADDRCONFIG is specified, check if we are expected to
@@ -504,99 +533,22 @@
if (get_portmatch(pai, servname) != 0)
return 0;
- afd = find_afd(pai->ai_family);
-
- hp = gethostbyname2(hostname, pai->ai_family);
- h_error = h_errno;
-
- if (hp == NULL) {
- switch (h_error) {
- case HOST_NOT_FOUND:
- case NO_DATA:
- error = EAI_NODATA;
- break;
- case TRY_AGAIN:
- error = EAI_AGAIN;
- break;
- case NO_RECOVERY:
- case NETDB_INTERNAL:
- default:
- error = EAI_FAIL;
- break;
- }
- } else if ((hp->h_name == NULL) || (hp->h_name[0] == 0)
- || (hp->h_addr_list[0] == NULL)) {
- hp = NULL;
+ if (nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo",
+ default_dns_files, hostname, pai) != NS_SUCCESS)
error = EAI_FAIL;
- }
-
- if (hp == NULL)
- goto free;
-
- /*
- * hp will be overwritten if we use gethostbyname2().
- * always deep copy for simplification.
- */
- for (naddrs = 0; hp->h_addr_list[naddrs] != NULL; naddrs++)
- ;
- naddrs++;
- aplist = (char **)malloc(sizeof(aplist[0]) * naddrs);
- apbuf = (char *)malloc((size_t)hp->h_length * naddrs);
- if (aplist == NULL || apbuf == NULL) {
- error = EAI_MEMORY;
- goto free;
- }
- memset(aplist, 0, sizeof(aplist[0]) * naddrs);
- for (i = 0; i < naddrs; i++) {
- if (hp->h_addr_list[i] == NULL) {
- aplist[i] = NULL;
- continue;
+ else {
+ error = 0;
+ for (cur = result; cur; cur = cur->ai_next) {
+ GET_PORT(cur, servname);
+ /* canonname should be filled already */
}
- memcpy(&apbuf[i * hp->h_length], hp->h_addr_list[i],
- (size_t)hp->h_length);
- aplist[i] = &apbuf[i * hp->h_length];
}
-
- for (i = 0; aplist[i] != NULL; i++) {
- af = hp->h_addrtype;
- ap = aplist[i];
-#ifdef AF_INET6
- if (af == AF_INET6
- && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) {
- af = AF_INET;
- ap = ap + sizeof(struct in6_addr)
- - sizeof(struct in_addr);
- }
-#endif
-
- if (af != pai->ai_family)
- continue;
-
- GET_AI(cur->ai_next, afd, ap);
- GET_PORT(cur->ai_next, servname);
- if ((pai->ai_flags & AI_CANONNAME) != 0) {
- /*
- * RFC2553 says that ai_canonname will be set only for
- * the first element. we do it for all the elements,
- * just for convenience.
- */
- GET_CANONNAME(cur->ai_next, hp->h_name);
- }
- while (cur && cur->ai_next)
- cur = cur->ai_next;
- }
+ *res = result;
- *res = sentinel.ai_next;
return 0;
free:
- if (aplist)
- free(aplist);
- if (apbuf)
- free(apbuf);
- if (sentinel.ai_next)
- freeaddrinfo(sentinel.ai_next);
return error;
}
@@ -1001,3 +953,353 @@
return -1;
}
#endif
+
+/* XXX code duplicate with gethnamaddr.c */
+
+static const char AskedForGot[] =
+ "gethostby*.getanswer: asked for \"%s\", got \"%s\"";
+static FILE *hostf = NULL;
+
+static struct addrinfo *
+getanswer(answer, anslen, qname, qtype, pai, errp)
+ const querybuf *answer;
+ int anslen;
+ const char *qname;
+ int qtype;
+ const struct addrinfo *pai;
+ int *errp;
+{
+ struct addrinfo sentinel, *cur;
+ struct addrinfo ai;
+ const struct afd *afd;
+ char *canonname;
+ const HEADER *hp;
+ const u_char *cp;
+ int n;
+ const u_char *eom;
+ char *bp;
+ int type, class, buflen, ancount, qdcount;
+ int haveanswer, had_error;
+ char tbuf[MAXDNAME];
+ const char *tname;
+ int (*name_ok) __P((const char *));
+ char hostbuf[8*1024];
+
+ memset(&sentinel, 0, sizeof(sentinel));
+ cur = &sentinel;
+
+ tname = qname;
+ canonname = NULL;
+ eom = answer->buf + anslen;
+ switch (qtype) {
+ case T_A:
+ case T_AAAA:
+ case T_ANY: /*use T_ANY only for T_A/T_AAAA lookup*/
+ name_ok = res_hnok;
+ break;
+ default:
+ return (NULL); /* XXX should be abort(); */
+ }
+ /*
+ * find first satisfactory answer
+ */
+ hp = &answer->hdr;
+ ancount = ntohs(hp->ancount);
+ qdcount = ntohs(hp->qdcount);
+ bp = hostbuf;
+ buflen = sizeof hostbuf;
+ cp = answer->buf + HFIXEDSZ;
+ if (qdcount != 1) {
+ *errp = NO_RECOVERY;
+ return (NULL);
+ }
+ n = dn_expand(answer->buf, eom, cp, bp, buflen);
+ if ((n < 0) || !(*name_ok)(bp)) {
+ *errp = NO_RECOVERY;
+ return (NULL);
+ }
+ cp += n + QFIXEDSZ;
+ if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) {
+ /* res_send() has already verified that the query name is the
+ * same as the one we sent; this just gets the expanded name
+ * (i.e., with the succeeding search-domain tacked on).
+ */
+ n = strlen(bp) + 1; /* for the \0 */
+ if (n >= MAXHOSTNAMELEN) {
+ *errp = NO_RECOVERY;
+ return (NULL);
+ }
+ canonname = bp;
+ bp += n;
+ buflen -= n;
+ /* The qname can be abbreviated, but h_name is now absolute. */
+ qname = canonname;
+ }
+ haveanswer = 0;
+ had_error = 0;
+ while (ancount-- > 0 && cp < eom && !had_error) {
+ n = dn_expand(answer->buf, eom, cp, bp, buflen);
+ if ((n < 0) || !(*name_ok)(bp)) {
+ had_error++;
+ continue;
+ }
+ cp += n; /* name */
+ type = _getshort(cp);
+ cp += INT16SZ; /* type */
+ class = _getshort(cp);
+ cp += INT16SZ + INT32SZ; /* class, TTL */
+ n = _getshort(cp);
+ cp += INT16SZ; /* len */
+ if (class != C_IN) {
+ /* XXX - debug? syslog? */
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+ if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) &&
+ type == T_CNAME) {
+ n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
+ if ((n < 0) || !(*name_ok)(tbuf)) {
+ had_error++;
+ continue;
+ }
+ cp += n;
+ /* Get canonical name. */
+ n = strlen(tbuf) + 1; /* for the \0 */
+ if (n > buflen || n >= MAXHOSTNAMELEN) {
+ had_error++;
+ continue;
+ }
+ strcpy(bp, tbuf);
+ canonname = bp;
+ bp += n;
+ buflen -= n;
+ continue;
+ }
+ if (qtype == T_ANY) {
+ if (!(type == T_A || type == T_AAAA)) {
+ cp += n;
+ continue;
+ }
+ } else if (type != qtype) {
+ if (type != T_KEY && type != T_SIG)
+ syslog(LOG_NOTICE|LOG_AUTH,
+ "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"",
+ qname, p_class(C_IN), p_type(qtype),
+ p_type(type));
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+ switch (type) {
+ case T_A:
+ case T_AAAA:
+ if (strcasecmp(canonname, bp) != 0) {
+ syslog(LOG_NOTICE|LOG_AUTH,
+ AskedForGot, canonname, bp);
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+ if (type == T_A && n != INADDRSZ) {
+ cp += n;
+ continue;
+ }
+ if (type == T_AAAA && n != IN6ADDRSZ) {
+ cp += n;
+ continue;
+ }
+ if (!haveanswer) {
+ int nn;
+
+ canonname = bp;
+ nn = strlen(bp) + 1; /* for the \0 */
+ bp += nn;
+ buflen -= nn;
+ }
+
+ /* don't overwrite pai */
+ ai = *pai;
+ ai.ai_family = (type == T_A) ? AF_INET : AF_INET6;
+ afd = find_afd(ai.ai_family);
+ if (afd == NULL) {
+ cp += n;
+ continue;
+ }
+ cur->ai_next = get_ai(&ai, afd, cp);
+ if (cur->ai_next == NULL)
+ had_error++;
+ while (cur && cur->ai_next)
+ cur = cur->ai_next;
+ cp += n;
+ break;
+ default:
+ abort();
+ }
+ if (!had_error)
+ haveanswer++;
+ }
+ if (haveanswer) {
+ if (!canonname)
+ (void)get_canonname(pai, sentinel.ai_next, qname);
+ else
+ (void)get_canonname(pai, sentinel.ai_next, canonname);
+ return sentinel.ai_next;
+ }
+
+ *errp = NO_RECOVERY;
+ return NULL;
+}
+
+/*ARGSUSED*/
+static int
+_dns_getaddrinfo(rv, cb_data, ap)
+ void *rv;
+ void *cb_data;
+ va_list ap;
+{
+ struct addrinfo *ai;
+ querybuf buf;
+ int n, type;
+ const char *name;
+ const struct addrinfo *pai;
+ int error;
+
+ name = va_arg(ap, char *);
+ pai = va_arg(ap, const struct addrinfo *);
+
+ switch (pai->ai_family) {
+ case AF_INET:
+ type = T_A;
+ break;
+ case AF_INET6:
+ type = T_AAAA;
+ break;
+ case AF_UNSPEC:
+ type = T_ANY;
+ break;
+ default:
+ return NS_UNAVAIL;
+ }
+ if ((n = res_search(name, C_IN, type, buf.buf, sizeof(buf))) < 0)
+ return NS_NOTFOUND;
+ ai = getanswer(&buf, n, name, type, pai, &error);
+ if (ai == NULL)
+ switch (error) {
+ case HOST_NOT_FOUND:
+ return NS_NOTFOUND;
+ case TRY_AGAIN:
+ return NS_TRYAGAIN;
+ default:
+ return NS_UNAVAIL;
+ }
+ *((struct addrinfo **)rv) = ai;
+ return NS_SUCCESS;
+}
+
+static void
+_sethtent()
+{
+ if (!hostf)
+ hostf = fopen(_PATH_HOSTS, "r" );
+ else
+ rewind(hostf);
+}
+
+static void
+_endhtent()
+{
+ if (hostf) {
+ (void) fclose(hostf);
+ hostf = NULL;
+ }
+}
+
+static struct addrinfo *
+_gethtent(name, pai)
+ const char *name;
+ const struct addrinfo *pai;
+{
+ char *p;
+ char *cp, *tname;
+ struct addrinfo hints, *res0, *res;
+ int error;
+ const char *addr;
+ char hostbuf[8*1024];
+
+ if (!hostf && !(hostf = fopen(_PATH_HOSTS, "r" )))
+ return (NULL);
+ again:
+ if (!(p = fgets(hostbuf, sizeof hostbuf, hostf)))
+ return (NULL);
+ if (*p == '#')
+ goto again;
+ if (!(cp = strpbrk(p, "#\n")))
+ goto again;
+ *cp = '\0';
+ if (!(cp = strpbrk(p, " \t")))
+ goto again;
+ *cp++ = '\0';
+ addr = p;
+ /* if this is not something we're looking for, skip it. */
+ while (cp && *cp) {
+ if (*cp == ' ' || *cp == '\t') {
+ cp++;
+ continue;
+ }
+ tname = cp;
+ if ((cp = strpbrk(cp, " \t")) != NULL)
+ *cp++ = '\0';
+ if (strcasecmp(name, tname) == 0)
+ goto found;
+ }
+ goto again;
+
+found:
+ hints = *pai;
+ hints.ai_flags = AI_NUMERICHOST;
+ error = getaddrinfo(addr, NULL, &hints, &res0);
+ if (error)
+ goto again;
+ for (res = res0; res; res = res->ai_next) {
+ /* cover it up */
+ res->ai_flags = pai->ai_flags;
+
+ if (pai->ai_flags & AI_CANONNAME) {
+ if (get_canonname(pai, res, name) != 0) {
+ freeaddrinfo(res0);
+ goto again;
+ }
+ }
+ }
+ return res0;
+}
+
+/*ARGSUSED*/
+static int
+_files_getaddrinfo(rv, cb_data, ap)
+ void *rv;
+ void *cb_data;
+ va_list ap;
+{
+ const char *name;
+ const struct addrinfo *pai;
+ struct addrinfo sentinel, *cur;
+ struct addrinfo *p;
+
+ name = va_arg(ap, char *);
+ pai = va_arg(ap, struct addrinfo *);
+
+ memset(&sentinel, 0, sizeof(sentinel));
+ cur = &sentinel;
+
+ _sethtent();
+ while ((p = _gethtent(name, pai)) != NULL) {
+ cur->ai_next = p;
+ while (cur && cur->ai_next)
+ cur = cur->ai_next;
+ }
+ _endhtent();
+
+ *((struct addrinfo **)rv) = sentinel.ai_next;
+ if (sentinel.ai_next == NULL)
+ return NS_NOTFOUND;
+ return NS_SUCCESS;
+}