From: Mark Wooding Date: Sat, 3 May 2014 10:46:12 +0000 (+0100) Subject: src/: Support IPv6 addresses in `sortlist'. X-Git-Tag: wip.base.getaddrinfo~29 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/adns/commitdiff_plain/9136cf0c3b0a026600a201975f9d21a1b5031763 src/: Support IPv6 addresses in `sortlist'. This involves a fair amount of messing about. There's a new file `addrfam.c' containing a number of address-family-specific facts and utilities, packaged together in a handy structure. The `sortlist' representation now attaches one of these to each entry, and now keeps a union of possible address types. Lookup of addresses in the sortlist is also modified, to check the address family before going any further. Signed-off-by: Mark Wooding --- diff --git a/client/adh-main.c b/client/adh-main.c index b6f3bd4..dc9c46d 100644 --- a/client/adh-main.c +++ b/client/adh-main.c @@ -99,6 +99,7 @@ void of_type(const struct optioninfo *oi, const char *arg, const char *arg2) { /* raw versions */ { adns_r_a, "a" }, + { adns_r_aaaa, "aaaa" }, { adns_r_ns_raw, "ns-" }, { adns_r_soa_raw, "soa-" }, { adns_r_ptr_raw, "ptr-" }, diff --git a/client/adh-opts.c b/client/adh-opts.c index 08310e0..34ce334 100644 --- a/client/adh-opts.c +++ b/client/adh-opts.c @@ -262,10 +262,10 @@ static void printusage(void) { " 11 usage problems\n" "\n" "Query types (see adns.h; default is addr):\n" - " ns soa ptr mx rp srv addr - enhanced versions\n" - " cname hinfo txt - types with only one version\n" - " a ns- soa- ptr- mx- rp- srv- - _raw versions\n" - " type - `unknown' type, RFC3597\n" + " ns soa ptr mx rp srv addr - enhanced versions\n" + " cname hinfo txt - types with only one version\n" + " a aaaa ns- soa- ptr- mx- rp- srv- - _raw versions\n" + " type - `unknown' type, RFC3597\n" "Default is addr, or ptr for -i/--ptr queries\n", stdout); if (ferror(stdout)) sysfail("write usage message",errno); diff --git a/src/addrfam.c b/src/addrfam.c new file mode 100644 index 0000000..40b8f2b --- /dev/null +++ b/src/addrfam.c @@ -0,0 +1,112 @@ +/* + * addrfam.c + * - address-family specific code + */ +/* + * This file is part of adns, which is + * Copyright (C) 1997-2000,2003,2006 Ian Jackson + * Copyright (C) 1999-2000,2003,2006 Tony Finch + * Copyright (C) 1991 Massachusetts Institute of Technology + * (See the file INSTALL for full details.) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "internal.h" + +/* + * IPv4 + */ + +#define SIN(sa) ((struct sockaddr_in *)(sa)) + +static void *inet_sockaddr_to_inaddr(struct sockaddr *sa) + { return &SIN(sa)->sin_addr; } + +static void inet_prefix_mask(int len, union gen_addr *mask) + { mask->v4.s_addr = htonl(!len ? 0 : 0xffffffff << (32 - len)); } + +static int inet_guess_len(const union gen_addr *addr) +{ + unsigned a = (ntohl(addr->v4.s_addr) >> 24) & 0xff; + + if (a < 128) return 8; + else if (a < 192) return 16; + else if (a < 224) return 24; + else return -1; +} + +static int inet_matchp(const union gen_addr *addr, + const union gen_addr *base, + const union gen_addr *mask) + { return (addr->v4.s_addr & mask->v4.s_addr) == base->v4.s_addr; } + +const afinfo adns__inet_afinfo = { + AF_INET, 32, '.', + inet_sockaddr_to_inaddr, inet_prefix_mask, inet_guess_len, inet_matchp +}; + +/* + * IPv6 + */ + +#define SIN6(sa) ((struct sockaddr_in6 *)(sa)) + +static void *inet6_sockaddr_to_inaddr(struct sockaddr *sa) + { return &SIN6(sa)->sin6_addr; } + +static void inet6_prefix_mask(int len, union gen_addr *mask) +{ + int i = len/8, j = len%8; + unsigned char *m = mask->v6.s6_addr; + + assert(len < 128); + memset(m, 0xff, i); + if (j) m[i++] = (0xff << (8-j)) & 0xff; + memset(m + i, 0, 16-i); +} + +static int inet6_guess_len(const union gen_addr *addr) + { return 64; } + +static int inet6_matchp(const union gen_addr *addr, + const union gen_addr *base, + const union gen_addr *mask) +{ + int i; + const char *a = addr->v6.s6_addr; + const char *b = base->v6.s6_addr; + const char *m = mask->v6.s6_addr; + + for (i = 0; i < 16; i++) + if ((a[i] & m[i]) != b[i]) return 0; + return 1; +} + +const afinfo adns__inet6_afinfo = { + AF_INET6, 128, ':', + inet6_sockaddr_to_inaddr, inet6_prefix_mask, inet6_guess_len, inet6_matchp +}; diff --git a/src/adns.h b/src/adns.h index 34f9f49..79f601d 100644 --- a/src/adns.h +++ b/src/adns.h @@ -152,6 +152,8 @@ typedef enum { adns_r_rp_raw= 17, adns_r_rp= adns_r_rp_raw|adns__qtf_mail822, + adns_r_aaaa= 28, + /* For SRV records, query domain without _qf_quoteok_query must look * as expected from SRV RFC with hostname-like Name. _With_ * _quoteok_query, any query domain is allowed. */ @@ -355,6 +357,7 @@ typedef struct { adns_rr_intstr *(*manyistr); /* txt (list strs ends with i=-1, str=0)*/ adns_rr_addr *addr; /* addr */ struct in_addr *inaddr; /* a */ + struct in6_addr *in6addr; /* aaaa */ adns_rr_hostaddr *hostaddr; /* ns */ adns_rr_intstrpair *intstrpair; /* hinfo */ adns_rr_strpair *strpair; /* rp, rp_raw */ diff --git a/src/adns.make b/src/adns.make index b0ba693..1c35eb1 100644 --- a/src/adns.make +++ b/src/adns.make @@ -21,4 +21,4 @@ # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. LIBOBJS= types.o event.o query.o reply.o general.o setup.o transmit.o \ - parse.o poll.o check.o + parse.o poll.o check.o addrfam.o diff --git a/src/check.c b/src/check.c index 41cdde5..6a02ee8 100644 --- a/src/check.c +++ b/src/check.c @@ -75,12 +75,15 @@ static void checkc_notcpbuf(adns_state ads) { } static void checkc_global(adns_state ads) { + const struct sortlist *sl; int i; assert(ads->udpsocket >= 0); - for (i=0; insortlist; i++) - assert(!(ads->sortlist[i].base.s_addr & ~ads->sortlist[i].mask.s_addr)); + for (i=0; insortlist; i++) { + sl = &ads->sortlist[i]; + assert(sl->ai->matchp(&sl->base, &sl->base, &sl->mask)); + } assert(ads->tcpserver >= 0 && ads->tcpserver < ads->nservers); diff --git a/src/internal.h b/src/internal.h index 58cd15d..4f4f87e 100644 --- a/src/internal.h +++ b/src/internal.h @@ -112,6 +112,22 @@ typedef struct { struct timeval now; } parseinfo; +union gen_addr { + struct in_addr v4; + struct in6_addr v6; +}; + +typedef struct { + int af; + int width; + int delim; + void *(*sockaddr_to_inaddr)(struct sockaddr *sa); + void (*prefix_mask)(int len, union gen_addr *a); + int (*guess_len)(const union gen_addr *a); + int (*matchp)(const union gen_addr *addr, + const union gen_addr *base, const union gen_addr *mask); +} afinfo; + typedef struct typeinfo { adns_rrtype typekey; const char *rrtname; @@ -333,12 +349,17 @@ struct adns__state { struct in_addr addr; } servers[MAXSERVERS]; struct sortlist { - struct in_addr base, mask; + const afinfo *ai; + union gen_addr base, mask; } sortlist[MAXSORTLIST]; char **searchlist; unsigned short rand48xsubi[3]; }; +/* From addrfam.c: */ + +extern const afinfo adns__inet_afinfo, adns__inet6_afinfo; + /* From setup.c: */ int adns__setnonblock(adns_state ads, int fd); /* => errno value */ diff --git a/src/setup.c b/src/setup.c index e6f1e55..61f4f67 100644 --- a/src/setup.c +++ b/src/setup.c @@ -153,9 +153,10 @@ static void ccf_sortlist(adns_state ads, const char *fn, const char *word; char tbuf[200], *slash, *ep; const char *maskwhat; - struct in_addr base, mask; + struct sortlist *sl; int l; - unsigned long initial, baselocal; + const afinfo *ai; + int initial = -1; if (!buf) return; @@ -175,54 +176,59 @@ static void ccf_sortlist(adns_state ads, const char *fn, memcpy(tbuf,word,l); tbuf[l]= 0; slash= strchr(tbuf,'/'); if (slash) *slash++= 0; + + sl= &ads->sortlist[ads->nsortlist]; + + if (strchr(tbuf, ':')) + ai= &adns__inet6_afinfo; + else + ai= &adns__inet_afinfo; - if (!inet_aton(tbuf,&base)) { + if (!inet_pton(ai->af, tbuf, &sl->base)) { configparseerr(ads,fn,lno,"invalid address `%s' in sortlist",tbuf); continue; } if (slash) { - if (strchr(slash,'.')) { + if (strchr(slash,ai->delim)) { maskwhat = "mask"; - if (!inet_aton(slash,&mask)) { + if (!inet_pton(ai->af,slash,&sl->mask)) { configparseerr(ads,fn,lno,"invalid mask `%s' in sortlist",slash); continue; } } else { maskwhat = "prefix length"; initial= strtoul(slash,&ep,10); - if (*ep || initial>32) { + if (*ep || initial>ai->width) { configparseerr(ads,fn,lno,"mask length `%s' invalid",slash); continue; } - mask.s_addr= htonl((0x0ffffffffUL) << (32-initial)); + ai->prefix_mask(initial, &sl->mask); } } else { - maskwhat = "implied mask"; - baselocal= ntohl(base.s_addr); - if (!baselocal & 0x080000000UL) /* class A */ - mask.s_addr= htonl(0x0ff000000UL); - else if ((baselocal & 0x0c0000000UL) == 0x080000000UL) - mask.s_addr= htonl(0x0ffff0000UL); /* class B */ - else if ((baselocal & 0x0f0000000UL) == 0x0e0000000UL) - mask.s_addr= htonl(0x0ff000000UL); /* class C */ - else { + maskwhat = "implied prefix length"; + initial = ai->guess_len(&sl->base); + if (initial < 0) { configparseerr(ads,fn,lno, "network address `%s'" " in sortlist is not in classed ranges," " must specify mask explicitly", tbuf); continue; } + ai->prefix_mask(initial, &sl->mask); } - if (base.s_addr & ~mask.s_addr) { - configparseerr(ads,fn,lno, "%s `%s' in sortlist" - " overlaps address `%s'",maskwhat, - slash ? slash : inet_ntoa(mask), tbuf); + if (!ai->matchp(&sl->base, &sl->base, &sl->mask)) { + if (initial >= 0) { + configparseerr(ads,fn,lno, "%s %d in sortlist" + " overlaps address `%s'",maskwhat,initial,tbuf); + } else { + configparseerr(ads,fn,lno, "%s `%s' in sortlist" + " overlaps address `%s'",maskwhat,slash,tbuf); + } continue; } - ads->sortlist[ads->nsortlist].base= base; - ads->sortlist[ads->nsortlist].mask= mask; + sl->ai = ai; ads->nsortlist++; } } diff --git a/src/types.c b/src/types.c index 36ff879..a2ddee3 100644 --- a/src/types.c +++ b/src/types.c @@ -47,8 +47,9 @@ * _intstr (mf,csp,cs) * _manyistr (mf,cs) * _txt (pa) - * _inaddr (pa,dip,di) - * _addr (pa,di,csp,cs) + * _inaddr (pa,cs,di, +search_sortlist, dip_genaddr) + * _in6addr (pa,cs,di) + * _addr (pa,di,csp,cs, +search_sortlist_sa, dip_sockaddr) * _domain (pap) * _host_raw (pa) * _hostaddr (pap,pa,dip,di,mfp,mf,csp,cs +pap_findaddrs) @@ -239,7 +240,7 @@ static adns_status cs_hinfo(vbuf *vb, const void *datap) { } /* - * _inaddr (pa,dip,di) + * _inaddr (pa,di,cs +search_sortlist, dip_genaddr) */ static adns_status pa_inaddr(const parseinfo *pai, int cbyte, @@ -251,32 +252,30 @@ static adns_status pa_inaddr(const parseinfo *pai, int cbyte, return adns_s_ok; } -static int search_sortlist(adns_state ads, struct in_addr ad) { +static int search_sortlist(adns_state ads, int af, const void *ad) { const struct sortlist *slp; int i; for (i=0, slp=ads->sortlist; - insortlist && - !((ad.s_addr & slp->mask.s_addr) == slp->base.s_addr); + insortlist && (af != slp->ai->af || + !slp->ai->matchp(ad, &slp->base, &slp->mask)); i++, slp++); return i; } -static int dip_inaddr(adns_state ads, struct in_addr a, struct in_addr b) { +static int dip_genaddr(adns_state ads, int af, const void *a, const void *b) { int ai, bi; if (!ads->nsortlist) return 0; - ai= search_sortlist(ads,a); - bi= search_sortlist(ads,b); + ai= search_sortlist(ads,af,a); + bi= search_sortlist(ads,af,b); return bis6_addr, pai->dgram + cbyte, 16); + return adns_s_ok; +} + +static int di_in6addr(adns_state ads, + const void *datap_a, const void *datap_b) { + return dip_genaddr(ads,AF_INET6,datap_a,AF_INET6,datap_b); +} + +static adns_status cs_in6addr(vbuf *vb, const void *datap) { + char buf[INET6_ADDRSTRLEN]; + const char *ia; + + ia= inet_ntop(AF_INET6, datap, buf, sizeof(buf)); assert(ia); + CSP_ADDSTR(ia); + return adns_s_ok; +} + +/* + * _addr (pa,di,csp,cs, +search_sortlist_sa, dip_sockaddr) */ static adns_status pa_addr(const parseinfo *pai, int cbyte, @@ -305,11 +331,31 @@ static adns_status pa_addr(const parseinfo *pai, int cbyte, return adns_s_ok; } +static int search_sortlist_sa(adns_state ads, const struct sockaddr *sa) +{ + const struct afinfo *ai = 0; + + switch (sa->sa_family) { + case AF_INET: ai = &adns__inet_afinfo; break; + case AF_INET6: ai = &adns__inet6_afinfo; break; + } + assert(ai); + + return search_sortlist(ads, sa->sa_family, ai->sockaddr_to_inaddr(sa)); +} + +static int dip_sockaddr(adns_state ads, + const struct sockaddr *sa, + const struct sockaddr *sb) +{ + if (!ads->sortlist) return 0; + return search_sortlist_sa(ads, sa) > search_sortlist_sa(ads, sb); +} + static int di_addr(adns_state ads, const void *datap_a, const void *datap_b) { const adns_rr_addr *ap= datap_a, *bp= datap_b; - assert(ap->addr.sa.sa_family == AF_INET); - return dip_inaddr(ads, ap->addr.inet.sin_addr, bp->addr.inet.sin_addr); + return dip_sockaddr(ads, &ap->addr.sa, &bp->addr.sa); } static int div_addr(void *context, const void *datap_a, const void *datap_b) { @@ -539,11 +585,7 @@ static int dip_hostaddr(adns_state ads, if (ap->astatus != bp->astatus) return ap->astatus; if (ap->astatus) return 0; - assert(ap->addrs[0].addr.sa.sa_family == AF_INET); - assert(bp->addrs[0].addr.sa.sa_family == AF_INET); - return dip_inaddr(ads, - ap->addrs[0].addr.inet.sin_addr, - bp->addrs[0].addr.inet.sin_addr); + return dip_sockaddr(ads, &ap->addrs[0].addr.sa, &bp->addrs[0].addr.sa); } static int di_hostaddr(adns_state ads, @@ -1271,6 +1313,7 @@ DEEP_TYPE(hinfo, "HINFO", 0, intstrpair,pa_hinfo, 0, cs_hinfo ), DEEP_TYPE(mx_raw, "MX", "raw",intstr, pa_mx_raw, di_mx_raw,cs_inthost ), DEEP_TYPE(txt, "TXT", 0, manyistr,pa_txt, 0, cs_txt ), DEEP_TYPE(rp_raw, "RP", "raw",strpair, pa_rp, 0, cs_rp ), +FLAT_TYPE(aaaa, "AAAA", 0, in6addr, pa_in6addr, di_in6addr,cs_in6addr ), XTRA_TYPE(srv_raw,"SRV", "raw",srvraw , pa_srvraw, di_srv, cs_srvraw, qdpl_srv, postsort_srv),