From: Mark Wooding Date: Sat, 24 May 2014 13:00:03 +0000 (+0100) Subject: src/addrfam.c, src/...: Abstract out address-family-specific details. X-Git-Tag: wip.ipv6.2014-10-13.reentrancy-base~14 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=adns.git;a=commitdiff_plain;h=07554ccd8286775b4f88a608ba3b94ff7b9efaf9 src/addrfam.c, src/...: Abstract out address-family-specific details. Functions which know about the internals of particular address types and socket-address structures live in a new file `addrfam.c'. Some details aren't handled yet: notably, we don't have conversions between internal address structures and external formats such as text strings or reverse-lookup domain names. Signed-off-by: Mark Wooding --- diff --git a/src/addrfam.c b/src/addrfam.c new file mode 100644 index 0000000..f606612 --- /dev/null +++ b/src/addrfam.c @@ -0,0 +1,212 @@ +/* + * 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 "internal.h" + +/* + * General address-family operations. + */ + +#define SIN(sa) ((struct sockaddr_in *)(sa)) +#define CSIN(sa) ((const struct sockaddr_in *)(sa)) + +#define SIN6(sa) ((struct sockaddr_in6 *)(sa)) +#define CSIN6(sa) ((const struct sockaddr_in6 *)(sa)) + +/* This gadget (thanks, Richard Kettlewell) makes sure that we handle the + * same set of address families in each switch. */ +#define AF_CASES(pre) \ + case AF_INET: goto pre##_inet; \ + case AF_INET6: goto pre##_inet6 + +static void unknown_af(int af) { + fprintf(stderr, "ADNS INTERNAL: unknown address family %d\n", af); + abort(); +} + +#define IN6_ADDR_EQUALP(a, b) \ + (!memcmp((a).s6_addr, (b).s6_addr, sizeof((a).s6_addr))) + +int adns__genaddr_equal_p(int af, const union gen_addr *a, + int bf, const void *b) { + const union gen_addr *bb= b; + if (af != bf) return 0; + switch (af) { + AF_CASES(af); + af_inet: return a->v4.s_addr == bb->v4.s_addr; + af_inet6: return IN6_ADDR_EQUALP(a->v6, bb->v6); + default: unknown_af(af); return -1; + } +} + +int adns__sockaddr_equal_p(const struct sockaddr *sa, + const struct sockaddr *sb) { + if (sa->sa_family != sb->sa_family) return 0; + switch (sa->sa_family) { + AF_CASES(af); + af_inet: { + const struct sockaddr_in *sina= CSIN(sa), *sinb= CSIN(sb); + return (sina->sin_addr.s_addr == sinb->sin_addr.s_addr && + sina->sin_port == sinb->sin_port); + } + af_inet6: { + /* Don't check the flowlabel. That's apparently useful for routing + * performance, but doesn't affect the address in any important + * respect. */ + const struct sockaddr_in6 *sin6a= CSIN6(sa), *sin6b= CSIN6(sb); + return (IN6_ADDR_EQUALP(sin6a->sin6_addr, sin6b->sin6_addr) && + sin6a->sin6_port == sin6b->sin6_port && + sin6a->sin6_scope_id == sin6b->sin6_scope_id); + } + default: + unknown_af(sa->sa_family); + return -1; + } +} + +int adns__addr_width(int af) { + switch (af) { + AF_CASES(af); + af_inet: return 32; + af_inet6: return 128; + default: unknown_af(af); return -1; + } +} + +void adns__prefix_mask(int af, int len, union gen_addr *mask_r) { + switch (af) { + AF_CASES(af); + af_inet: + assert(len <= 32); + mask_r->v4.s_addr= htonl(!len ? 0 : 0xffffffff << (32-len)); + break; + af_inet6: { + int i= len/8, j= len%8; + unsigned char *m= mask_r->v6.s6_addr; + assert(len <= 128); + memset(m, 0xff, i); + if (j) m[i++]= (0xff << (8-j)) & 0xff; + memset(m+i, 0, 16-i); + } break; + default: + unknown_af(af); + break; + } +} + +int adns__guess_prefix_length(int af, const union gen_addr *addr) { + switch (af) { + AF_CASES(af); + af_inet: { + 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; + } break; + af_inet6: + return 64; + default: + unknown_af(af); + return -1; + } +} + +int adns__addr_match_p(int addraf, const union gen_addr *addr, + int netaf, const union gen_addr *base, + const union gen_addr *mask) +{ + if (addraf != netaf) return 0; + switch (addraf) { + AF_CASES(af); + af_inet: + return (addr->v4.s_addr & mask->v4.s_addr) == base->v4.s_addr; + af_inet6: { + 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; + } break; + default: + unknown_af(addraf); + return -1; + } +} + +void adns__sockaddr_extract(const struct sockaddr *sa, + union gen_addr *a_r, int *port_r) { + switch (sa->sa_family) { + AF_CASES(af); + af_inet: { + const struct sockaddr_in *sin = CSIN(sa); + if (port_r) *port_r= ntohs(sin->sin_port); + if (a_r) a_r->v4= sin->sin_addr; + break; + } + af_inet6: { + const struct sockaddr_in6 *sin6 = CSIN6(sa); + if (port_r) *port_r= ntohs(sin6->sin6_port); + if (a_r) a_r->v6= sin6->sin6_addr; + break; + } + default: + unknown_af(sa->sa_family); + } +} + +void adns__sockaddr_inject(const union gen_addr *a, int port, + struct sockaddr *sa) { + switch (sa->sa_family) { + AF_CASES(af); + af_inet: { + struct sockaddr_in *sin = SIN(sa); + if (port != -1) sin->sin_port= htons(port); + if (a) sin->sin_addr= a->v4; + break; + } + af_inet6: { + struct sockaddr_in6 *sin6 = SIN6(sa); + if (port != -1) sin6->sin6_port= htons(port); + if (a) sin6->sin6_addr= a->v6; + break; + } + default: + unknown_af(sa->sa_family); + } +} 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 44f52b9..3972ebe 100644 --- a/src/check.c +++ b/src/check.c @@ -81,12 +81,8 @@ static void checkc_global(adns_state ads) { assert(ads->udpsocket >= 0); for (i=0; insortlist; i++) { - assert(ads->sortlist[i].af==AF_INET); - assert(!(ads->sortlist[i].base.v4.s_addr & - ~ads->sortlist[i].mask.v4.s_addr)); sl= &ads->sortlist[i]; - assert(sl->af==AF_INET); - assert(!(sl->base.v4.s_addr & ~sl->mask.v4.s_addr)); + assert(adns__addr_match_p(sl->af,&sl->base, sl->af,&sl->base,&sl->mask)); } assert(ads->tcpserver >= 0 && ads->tcpserver < ads->nservers); diff --git a/src/event.c b/src/event.c index ee4e5f7..433a4d0 100644 --- a/src/event.c +++ b/src/event.c @@ -415,9 +415,8 @@ int adns_processreadable(adns_state ads, int fd, const struct timeval *now) { } for (serv= 0; serv < ads->nservers && - (assert(ads->servers[serv].addr.sa.sa_family==AF_INET), - ads->servers[serv].addr.inet.sin_addr.s_addr != - udpaddr.sin_addr.s_addr); + !adns__sockaddr_equal_p(&ads->servers[serv].addr.sa, + (const struct sockaddr *)&udpaddr); serv++); if (serv >= ads->nservers) { adns__warn(ads,-1,0,"datagram received from unknown nameserver %s", diff --git a/src/internal.h b/src/internal.h index 97af49e..9a55519 100644 --- a/src/internal.h +++ b/src/internal.h @@ -374,6 +374,65 @@ struct adns__state { unsigned short rand48xsubi[3]; }; +/* From addrfam.c: */ + +extern int adns__af_supported_p(int af); +/* Return nonzero if the address family af known to the library and supported + * by the other addrfam operations. Note that the other operations will + * abort on an unrecognized address family rather than returning an error + * code. + */ + +extern int adns__genaddr_equal_p(int af, const union gen_addr *a, + int bf, const void *b); +/* b should point to a `struct in_addr' or equivalent for the address family + * bf. Returns nonzero if the two addresses are equal. + */ + +extern int adns__sockaddr_equal_p(const struct sockaddr *sa, + const struct sockaddr *sb); +/* Return nonzero if the two socket addresses are equal (in all significant + * respects). + */ + +extern int adns__addr_width(int af); +/* Return the width of addresses of family af, in bits. */ + +extern void adns__prefix_mask(int af, int len, union gen_addr *mask_r); +/* Store in mask_r an address mask for address family af, whose first len + * bits are set and the remainder are clear. This is what you want for + * converting a prefix length into a netmask. + */ + +extern int adns__guess_prefix_length(int af, const union gen_addr *addr); +/* Given a network base address, guess the appropriate prefix length based on + * the appropriate rules for the address family (e.g., for IPv4, this uses + * the old address classes). + */ + +extern int adns__addr_match_p(int addraf, const union gen_addr *addr, + int netaf, const union gen_addr *base, + const union gen_addr *mask); +/* Given an address af (with family addraf) and a network (with family netaf, + * base address base, and netmask mask), return nonzero if the address lies + * within the network. + */ + +extern void adns__sockaddr_extract(const struct sockaddr *sa, + union gen_addr *a_r, int *port_r); +/* Extract fields from the socket address, filling in *a_r and *port_r with + * the address and (integer, host byte-order) port number, respectively. + * Either (or, pointlessly, both) of a_r and port_r may be null to mean + * `don't care'. + */ + +extern void adns__sockaddr_inject(const union gen_addr *a, int port, + struct sockaddr *sa); +/* Inject fields into the socket adress sa. If a is not null, copy the + * address in; if port is not -1, then copy the port (converting from host + * byte-order). Assumes that sa->sa_family is already set correctly. + */ + /* From setup.c: */ int adns__setnonblock(adns_state ads, int fd); /* => errno value */ diff --git a/src/setup.c b/src/setup.c index ce44055..03d737e 100644 --- a/src/setup.c +++ b/src/setup.c @@ -46,12 +46,11 @@ static void addserver(adns_state ads, const struct sockaddr *sa, int n) { adns_rr_addr *ss; const struct sockaddr_in *sin; - assert(sa->sa_family==AF_INET); assert(n>=sizeof(*sin)); + assert(sa->sa_family==AF_INET); /* for inet_ntoa */ sin= (const void *)sa; for (i=0; inservers; i++) { - assert(ads->servers[i].addr.sa.sa_family==AF_INET); - if (ads->servers[i].addr.inet.sin_addr.s_addr == sin->sin_addr.s_addr) { + if (adns__sockaddr_equal_p(sa, &ads->servers[i].addr.sa)) { adns__debug(ads,-1,0,"duplicate nameserver %s ignored", inet_ntoa(sin->sin_addr)); return; @@ -165,9 +164,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; + int af= AF_UNSPEC; + int initial= -1; if (!buf) return; @@ -187,55 +187,54 @@ static void ccf_sortlist(adns_state ads, const char *fn, memcpy(tbuf,word,l); tbuf[l]= 0; slash= strchr(tbuf,'/'); if (slash) *slash++= 0; - - if (!inet_aton(tbuf,&base)) { + + sl= &ads->sortlist[ads->nsortlist]; + if (!inet_aton(tbuf, &sl->base.v4)) { configparseerr(ads,fn,lno,"invalid address `%s' in sortlist",tbuf); continue; } + af= AF_INET; if (slash) { - if (strchr(slash,'.')) { + if (slash[strspn(slash, "0123456789")]) { maskwhat = "mask"; - if (!inet_aton(slash,&mask)) { + if (!inet_aton(slash, &sl->mask.v4)) { 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>adns__addr_width(af)) { configparseerr(ads,fn,lno,"mask length `%s' invalid",slash); continue; } - mask.s_addr= htonl((0x0ffffffffUL) << (32-initial)); + adns__prefix_mask(af, 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= adns__guess_prefix_length(af, &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; } + adns__prefix_mask(af, 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 (!adns__addr_match_p(af,&sl->base, af,&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].af= AF_INET; - ads->sortlist[ads->nsortlist].base.v4= base; - ads->sortlist[ads->nsortlist].mask.v4= mask; + sl->af= af; ads->nsortlist++; } } diff --git a/src/types.c b/src/types.c index 431fe19..e0ff9e2 100644 --- a/src/types.c +++ b/src/types.c @@ -258,15 +258,11 @@ static adns_status pa_inaddr(const parseinfo *pai, int cbyte, static int search_sortlist(adns_state ads, int af, const void *ad) { const struct sortlist *slp; - const struct in_addr *a4; int i; - assert(af==AF_INET); - a4= ad; for (i=0, slp=ads->sortlist; insortlist && - (assert(slp->af==AF_INET), - !((a4->s_addr & slp->mask.v4.s_addr) == slp->base.v4.s_addr)); + !adns__addr_match_p(af,ad, slp->af,&slp->base,&slp->mask); i++, slp++); return i; } @@ -313,10 +309,11 @@ static adns_status pa_addr(const parseinfo *pai, int cbyte, } static int search_sortlist_sa(adns_state ads, const struct sockaddr *sa) { - assert(sa->sa_family == AF_INET); - return search_sortlist(ads, sa->sa_family, - &((const struct sockaddr_in *)sa)->sin_addr); + union gen_addr a; + adns__sockaddr_extract(sa, &a, 0); + return search_sortlist(ads, sa->sa_family, &a); } + static int dip_sockaddr(adns_state ads, const struct sockaddr *sa, const struct sockaddr *sb) { @@ -772,10 +769,9 @@ static void icb_ptr(adns_query parent, adns_query child) { } queried= &parent->ctx.tinfo.ptr.addr; - assert(queried->af == AF_INET); assert(cans->type == adns_r_a); for (i=0, found=cans->rrs.bytes; inrrs; i++, found+=cans->rrsz) { - if (queried->addr.v4.s_addr == ((const struct in_addr *)found)->s_addr) { + if (adns__genaddr_equal_p(queried->af,&queried->addr, AF_INET,found)) { if (!parent->children.head) { adns__query_done(parent); return;