*/
#define SIN(sa) ((struct sockaddr_in *)(sa))
+#define CSIN(sa) ((const struct sockaddr_in *)(sa))
static void *inet_sockaddr_to_inaddr(struct sockaddr *sa)
{ return &SIN(sa)->sin_addr; }
+static int inet_sockaddr_equalp(const struct sockaddr *sa,
+ const struct sockaddr *sb)
+{
+ 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);
+}
+
static void inet_prefix_mask(int len, union gen_addr *mask)
{ mask->v4.s_addr = htonl(!len ? 0 : 0xffffffff << (32 - len)); }
(ipv[1]<<8) | (ipv[0]));
}
-static char *inet_rev_mkname(struct sockaddr *sa, char *buf)
+static char *inet_rev_mkname(const struct sockaddr *sa, char *buf)
{
- unsigned long a = ntohl(SIN(sa)->sin_addr.s_addr);
+ unsigned long a = ntohl(CSIN(sa)->sin_addr.s_addr);
int i;
for (i = 0; i < 4; i++) {
const afinfo adns__inet_afinfo = {
AF_INET, 32, '.', 4, 3, adns_r_a,
- inet_sockaddr_to_inaddr, inet_prefix_mask, inet_guess_len, inet_matchp,
+ inet_sockaddr_to_inaddr, inet_sockaddr_equalp,
+ inet_prefix_mask, inet_guess_len, inet_matchp,
inet_rev_parsecomp, inet_rev_mkaddr, inet_rev_mkname
};
*/
#define SIN6(sa) ((struct sockaddr_in6 *)(sa))
+#define CSIN6(sa) ((const struct sockaddr_in6 *)(sa))
static void *inet6_sockaddr_to_inaddr(struct sockaddr *sa)
{ return &SIN6(sa)->sin6_addr; }
+static int inet6_sockaddr_equalp(const struct sockaddr *sa,
+ const struct sockaddr *sb)
+{
+ const struct sockaddr_in6 *sin6a = CSIN6(sa), *sin6b = CSIN6(sb);
+ return (memcmp(sin6a->sin6_addr.s6_addr,
+ sin6b->sin6_addr.s6_addr,
+ sizeof(sin6a->sin6_addr.s6_addr)) == 0 &&
+ sin6a->sin6_port == sin6b->sin6_port &&
+ sin6a->sin6_scope_id == sin6b->sin6_scope_id);
+}
+
static void inet6_prefix_mask(int len, union gen_addr *mask)
{
int i = len/8, j = len%8;
a[i] = (ipv[31-2*i] << 4) | (ipv[30-2*i] << 0);
}
-static char *inet6_rev_mkname(struct sockaddr *sa, char *buf)
+static char *inet6_rev_mkname(const struct sockaddr *sa, char *buf)
{
- unsigned char *a = SIN6(sa)->sin6_addr.s6_addr + 16;
+ const unsigned char *a = CSIN6(sa)->sin6_addr.s6_addr + 16;
unsigned c, y;
int i, j;
const afinfo adns__inet6_afinfo = {
AF_INET6, 128, ':', 32, 1, adns_r_aaaa,
- inet6_sockaddr_to_inaddr, inet6_prefix_mask, inet6_guess_len, inet6_matchp,
+ inet6_sockaddr_to_inaddr, inet6_sockaddr_equalp,
+ inet6_prefix_mask, inet6_guess_len, inet6_matchp,
inet6_rev_parsecomp, inet6_rev_mkaddr, inet6_rev_mkname
};
* In any case this call won't block.
*/
-#define ADNS_POLLFDS_RECOMMENDED 2
+#define ADNS_POLLFDS_RECOMMENDED 3
/* If you allocate an fds buf with at least RECOMMENDED entries then
* you are unlikely to need to enlarge it. You are recommended to do
* so if it's convenient. However, you must be prepared for adns to
void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
int r, fd, tries;
- struct sockaddr_in addr;
+ adns_rr_addr *addr;
struct protoent *proto;
for (tries=0; tries<ads->nservers; tries++) {
adns__diag(ads,-1,0,"unable to find protocol no. for TCP !");
return;
}
- fd= socket(AF_INET,SOCK_STREAM,proto->p_proto);
+ addr = &ads->servers[ads->tcpserver];
+ fd= socket(addr->addr.sa.sa_family, SOCK_STREAM, proto->p_proto);
if (fd<0) {
adns__diag(ads,-1,0,"cannot create TCP socket: %s",strerror(errno));
return;
close(fd);
return;
}
- memset(&addr,0,sizeof(addr));
- addr.sin_family= AF_INET;
- addr.sin_port= htons(DNS_PORT);
- addr.sin_addr= ads->servers[ads->tcpserver].addr;
- r= connect(fd,(const struct sockaddr*)&addr,sizeof(addr));
+ r= connect(fd,&addr->addr.sa,addr->len);
ads->tcpsocket= fd;
ads->tcpstate= server_connecting;
if (r==0) { tcp_connected(ads,now); return; }
int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]) {
/* Returns the number of entries filled in. Always zeroes revents. */
- assert(MAX_POLLFDS==2);
+ int i;
- pollfds_buf[0].fd= ads->udpsocket;
- pollfds_buf[0].events= POLLIN;
- pollfds_buf[0].revents= 0;
+ assert(MAX_POLLFDS == MAXUDP + 1);
+
+ for (i = 0; i < ads->nudp; i++) {
+ pollfds_buf[i].fd= ads->udpsocket[i].fd;
+ pollfds_buf[i].events= POLLIN;
+ pollfds_buf[i].revents= 0;
+ }
switch (ads->tcpstate) {
case server_disconnected:
case server_broken:
return 1;
case server_connecting:
- pollfds_buf[1].events= POLLOUT;
+ pollfds_buf[i].events= POLLOUT;
break;
case server_ok:
- pollfds_buf[1].events=
+ pollfds_buf[i].events=
ads->tcpsend.used ? POLLIN|POLLOUT|POLLPRI : POLLIN|POLLPRI;
break;
default:
abort();
}
- pollfds_buf[1].fd= ads->tcpsocket;
- return 2;
+ pollfds_buf[i++].fd= ads->tcpsocket;
+ return i;
}
int adns_processreadable(adns_state ads, int fd, const struct timeval *now) {
- int want, dgramlen, r, udpaddrlen, serv, old_skip;
+ int want, dgramlen, r, i, udpaddrlen, serv, old_skip;
byte udpbuf[DNS_MAXUDP];
- struct sockaddr_in udpaddr;
+ struct udpsocket *udp;
+ adns_sockaddr udpaddr;
adns__consistency(ads,0,cc_entex);
default:
abort();
}
- if (fd == ads->udpsocket) {
- for (;;) {
- udpaddrlen= sizeof(udpaddr);
- r= recvfrom(ads->udpsocket,udpbuf,sizeof(udpbuf),0,
- (struct sockaddr*)&udpaddr,&udpaddrlen);
- if (r<0) {
- if (errno == EAGAIN || errno == EWOULDBLOCK) { r= 0; goto xit; }
- if (errno == EINTR) continue;
- if (errno_resources(errno)) { r= errno; goto xit; }
- adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno));
- r= 0; goto xit;
- }
- if (udpaddrlen != sizeof(udpaddr)) {
- adns__diag(ads,-1,0,"datagram received with wrong address length %d"
- " (expected %lu)", udpaddrlen,
- (unsigned long)sizeof(udpaddr));
- continue;
- }
- if (udpaddr.sin_family != AF_INET) {
- adns__diag(ads,-1,0,"datagram received with wrong protocol family"
- " %u (expected %u)",udpaddr.sin_family,AF_INET);
- continue;
- }
- if (ntohs(udpaddr.sin_port) != DNS_PORT) {
- adns__diag(ads,-1,0,"datagram received from wrong port"
- " %u (expected %u)", ntohs(udpaddr.sin_port),DNS_PORT);
- continue;
- }
- for (serv= 0;
- serv < ads->nservers &&
- ads->servers[serv].addr.s_addr != udpaddr.sin_addr.s_addr;
- serv++);
- if (serv >= ads->nservers) {
- adns__warn(ads,-1,0,"datagram received from unknown nameserver %s",
- inet_ntoa(udpaddr.sin_addr));
- continue;
+ for (i = 0; i < ads->nudp; i++) {
+ udp = &ads->udpsocket[i];
+ if (fd == udp->fd) {
+ for (;;) {
+ udpaddrlen= sizeof(udpaddr);
+ r= recvfrom(fd,udpbuf,sizeof(udpbuf),0, &udpaddr.sa,&udpaddrlen);
+ if (r<0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) { r= 0; goto xit; }
+ if (errno == EINTR) continue;
+ if (errno_resources(errno)) { r= errno; goto xit; }
+ adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno));
+ r= 0; goto xit;
+ }
+ for (serv= 0;
+ serv < ads->nservers &&
+ !(udpaddr.sa.sa_family == ads->servers[serv].addr.sa.sa_family &&
+ udp->ai->sockaddr_equalp(&udpaddr.sa,
+ &ads->servers[serv].addr.sa));
+ serv++);
+ if (serv >= ads->nservers) {
+ adns__warn(ads,-1,0,"datagram received from unknown nameserver %s",
+ adns__sockaddr_ntoa(&udpaddr.sa, udpaddrlen));
+ continue;
+ }
+ adns__procdgram(ads,udpbuf,r,serv,0,*now);
}
- adns__procdgram(ads,udpbuf,r,serv,0,*now);
+ break;
}
}
r= 0;
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <netdb.h>
#include "internal.h"
/* Core diagnostic functions */
+const char *adns__sockaddr_ntoa(struct sockaddr *sa, size_t n)
+{
+ static char buf[64];
+ int err;
+
+ err = getnameinfo(sa, n, buf, sizeof(buf), 0, 0, NI_NUMERICHOST);
+ assert(!err);
+ return buf;
+}
+
void adns__vlprintf(adns_state ads, const char *fmt, va_list al) {
ads->logfn(ads,ads->logfndata,fmt,al);
}
}
if (serv>=0) {
- adns__lprintf(ads,"%sNS=%s",bef,inet_ntoa(ads->servers[serv].addr));
+ adns__lprintf(ads,"%sNS=%s",bef,
+ adns__sockaddr_ntoa(&ads->servers[serv].addr.sa,
+ ads->servers[serv].len));
bef=", "; aft=")\n";
}
#define DNS_INADDR_ARPA "in-addr", "arpa"
#define DNS_IP6_ARPA "ip6", "arpa"
+#define STRINGIFY(x) REALLY_STRINGIFY(x)
+#define REALLY_STRINGIFY(x) #x
+
#define MAX_POLLFDS ADNS_POLLFDS_RECOMMENDED
typedef enum {
int revcompwd;
adns_rrtype rrtype;
void *(*sockaddr_to_inaddr)(struct sockaddr *sa);
+ int (*sockaddr_equalp)(const struct sockaddr *sa,
+ const struct sockaddr *sb);
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);
int (*rev_parsecomp)(const char *p, size_t n);
void (*rev_mkaddr)(union gen_addr *addr, const byte *ipv);
- char *(*rev_mkname)(struct sockaddr *sa, char *buf);
+ char *(*rev_mkname)(const struct sockaddr *sa, char *buf);
} afinfo;
struct afinfo_addr { const afinfo *ai; union gen_addr addr; };
struct query_queue { adns_query head, tail; };
+#define MAXUDP 2
+
struct adns__state {
adns_initflags iflags;
adns_logcallbackfn *logfn;
int configerrno;
struct query_queue udpw, tcpw, childw, output;
adns_query forallnext;
- int nextid, udpsocket, tcpsocket;
+ int nextid, tcpsocket;
+ struct udpsocket { const afinfo *ai; int fd; } udpsocket[MAXUDP];
+ int nudp;
vbuf tcpsend, tcprecv;
int nservers, nsortlist, nsearchlist, searchndots, tcpserver, tcprecv_skip;
enum adns__tcpstate {
struct sigaction stdsigpipe;
sigset_t stdsigmask;
struct pollfd pollfds_buf[MAX_POLLFDS];
- struct server {
- struct in_addr addr;
- } servers[MAXSERVERS];
+ adns_rr_addr servers[MAXSERVERS];
struct sortlist {
const afinfo *ai;
union gen_addr base, mask;
/* From general.c: */
+const char *adns__sockaddr_ntoa(struct sockaddr *sa, size_t n);
void adns__vlprintf(adns_state ads, const char *fmt, va_list al);
void adns__lprintf(adns_state ads, const char *fmt,
...) PRINTFFORMAT(2,3);
static void readconfig(adns_state ads, const char *filename, int warnmissing);
-static void addserver(adns_state ads, struct in_addr addr) {
+static const afinfo *const transport_aftab[] = {
+ &adns__inet_afinfo,
+ &adns__inet6_afinfo,
+ 0
+};
+
+static const afinfo *find_afinfo(int af)
+{
int i;
- struct server *ss;
+
+ for (i = 0; transport_aftab[i]; i++)
+ if (transport_aftab[i]->af == af) return transport_aftab[i];
+ return 0;
+}
+
+static void addserver(adns_state ads, struct sockaddr *sa, int n) {
+ int i;
+ adns_rr_addr *ss;
+ const afinfo *ai;
+
+ ai = find_afinfo(sa->sa_family);
+ if (!ai) {
+ adns__diag(ads,-1,0,
+ "nameserver %s for unknown address family %d ignored",
+ adns__sockaddr_ntoa(sa, n), sa->sa_family);
+ }
for (i=0; i<ads->nservers; i++) {
- if (ads->servers[i].addr.s_addr == addr.s_addr) {
- adns__debug(ads,-1,0,"duplicate nameserver %s ignored",inet_ntoa(addr));
+ if (ads->servers[i].addr.sa.sa_family == sa->sa_family &&
+ ai->sockaddr_equalp(sa, &ads->servers[i].addr.sa)) {
+ adns__debug(ads,-1,0,"duplicate nameserver %s ignored",
+ adns__sockaddr_ntoa(sa, n));
return;
}
}
if (ads->nservers>=MAXSERVERS) {
- adns__diag(ads,-1,0,"too many nameservers, ignoring %s",inet_ntoa(addr));
+ adns__diag(ads,-1,0,"too many nameservers, ignoring %s",
+ adns__sockaddr_ntoa(sa, n));
return;
}
ss= ads->servers+ads->nservers;
- ss->addr= addr;
+ assert(n <= sizeof(ss->addr));
+ ss->len = n;
+ memcpy(&ss->addr, sa, n);
ads->nservers++;
}
static void ccf_nameserver(adns_state ads, const char *fn,
int lno, const char *buf) {
- struct in_addr ia;
-
- if (!inet_aton(buf,&ia)) {
- configparseerr(ads,fn,lno,"invalid nameserver address `%s'",buf);
+ struct addrinfo *ai, ai_hint = { 0 };
+ int err;
+
+ ai_hint.ai_family = AF_UNSPEC;
+ ai_hint.ai_socktype = SOCK_DGRAM;
+ ai_hint.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+
+ err = getaddrinfo(buf, STRINGIFY(DNS_PORT), &ai_hint, &ai);
+ if (err) {
+ configparseerr(ads,fn,lno,"invalid nameserver address `%s' (%s)",
+ buf, gai_strerror(err));
return;
}
- adns__debug(ads,-1,0,"using nameserver %s",inet_ntoa(ia));
- addserver(ads,ia);
+
+ adns__debug(ads,-1,0,"using nameserver %s",
+ adns__sockaddr_ntoa(ai->ai_addr, ai->ai_addrlen));
+ addserver(ads, ai->ai_addr, ai->ai_addrlen);
+ freeaddrinfo(ai);
}
static void ccf_search(adns_state ads, const char *fn,
LIST_INIT(ads->output);
ads->forallnext= 0;
ads->nextid= 0x311f;
- ads->udpsocket= ads->tcpsocket= -1;
+ ads->nudp= 0;
+ ads->tcpsocket= -1;
adns__vbuf_init(&ads->tcpsend);
adns__vbuf_init(&ads->tcprecv);
ads->tcprecv_skip= 0;
}
static int init_finish(adns_state ads) {
- struct in_addr ia;
+ struct sockaddr_in sin;
struct protoent *proto;
+ struct udpsocket *udp;
+ int i, j;
int r;
if (!ads->nservers) {
if (ads->logfn && ads->iflags & adns_if_debug)
- adns__lprintf(ads,"adns: no nameservers, using localhost\n");
- ia.s_addr= htonl(INADDR_LOOPBACK);
- addserver(ads,ia);
+ adns__lprintf(ads,"adns: no nameservers, using IPv4 localhost\n");
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_port = htons(DNS_PORT);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addserver(ads,(struct sockaddr *)&sin, sizeof(sin));
}
proto= getprotobyname("udp"); if (!proto) { r= ENOPROTOOPT; goto x_free; }
- ads->udpsocket= socket(AF_INET,SOCK_DGRAM,proto->p_proto);
- if (ads->udpsocket<0) { r= errno; goto x_free; }
+ ads->nudp = 0;
+ for (i = 0; i < ads->nservers; i++) {
+ for (j = 0; j < ads->nudp; j++) {
+ if (ads->udpsocket[j].ai->af == ads->servers[i].addr.sa.sa_family)
+ goto afmatch;
+ }
- r= adns__setnonblock(ads,ads->udpsocket);
- if (r) { r= errno; goto x_closeudp; }
+ assert(ads->nudp < MAXUDP);
+ udp = &ads->udpsocket[ads->nudp];
+ udp->ai = find_afinfo(ads->servers[i].addr.sa.sa_family);
+ assert(udp->ai);
+ udp->fd = socket(udp->ai->af,SOCK_DGRAM,proto->p_proto);
+ if (udp->fd < 0) { r= errno; goto x_free; }
+ r= adns__setnonblock(ads,udp->fd);
+ if (r) { r= errno; goto x_closeudp; }
+ ads->nudp++;
+
+ afmatch:;
+ }
return 0;
x_closeudp:
- close(ads->udpsocket);
+ for (j = 0; j < ads->nudp; j++) close(ads->udpsocket[j].fd);
x_free:
free(ads);
return r;
}
void adns_finish(adns_state ads) {
+ int i;
adns__consistency(ads,0,cc_entex);
for (;;) {
if (ads->udpw.head) adns_cancel(ads->udpw.head);
else if (ads->output.head) adns_cancel(ads->output.head);
else break;
}
- close(ads->udpsocket);
+ for (i = 0; i < ads->nudp; i++) close(ads->udpsocket[i].fd);
if (ads->tcpsocket >= 0) close(ads->tcpsocket);
adns__vbuf_free(&ads->tcpsend);
adns__vbuf_free(&ads->tcprecv);
}
void adns__query_send(adns_query qu, struct timeval now) {
- struct sockaddr_in servaddr;
- int serv, r;
+ int serv, r, i;
adns_state ads;
+ int fd = -1;
+ struct udpsocket *udp;
+ adns_rr_addr *addr;
assert(qu->state == query_tosend);
if ((qu->flags & adns_qf_usevc) || (qu->query_dglen > DNS_MAXUDP)) {
return;
}
- serv= qu->udpnextserver;
- memset(&servaddr,0,sizeof(servaddr));
-
ads= qu->ads;
- servaddr.sin_family= AF_INET;
- servaddr.sin_addr= ads->servers[serv].addr;
- servaddr.sin_port= htons(DNS_PORT);
+ serv= qu->udpnextserver;
+ addr= &ads->servers[serv];
+ for (i = 0; i < ads->nudp; i++) {
+ udp = &ads->udpsocket[i];
+ if (udp->ai->af == addr->addr.sa.sa_family) { fd = udp->fd; break; }
+ }
+ assert(fd >= 0);
- r= sendto(ads->udpsocket,qu->query_dgram,qu->query_dglen,0,
- (const struct sockaddr*)&servaddr,sizeof(servaddr));
+ r= sendto(fd,qu->query_dgram,qu->query_dglen,0,
+ &addr->addr.sa,addr->len);
if (r<0 && errno == EMSGSIZE) {
qu->retries= 0;
query_usetcp(qu,now);