lots of useless recvfrom() calls
Lennert Buytenhek
buytenh at wantstofly.org
Thu Jun 7 10:31:35 MSK 2018
On Tue, May 08, 2018 at 12:12:54PM +0300, Lennert Buytenhek wrote:
> > > Also, looping until EAGAIN makes your application somewhat susceptible
> > > to being DoSed. (The event polling framework I use spends a lot of
> > > effort to make sure that all event sources are handled fairly, and that
> > > no one event source can starve out processing of other event sources.)
> >
> > This is a good point. Maybe limiting number of packets to process to some
> > "reasonable" number, such as 5? :) Still, edge-triggering notification is
> > interesting here.
> >
> > > If udns should work with edge-triggered event notification frameworks,
> > > then perhaps we could add a new function, say, dns_ioevent_single(),
> > > that only processes a single packet for this dns_ctx, and then have
> > > dns_ioevent() just call the _single() version in a loop until EAGAIN,
> > > or something like that?
> >
> > A new function sounds good.
> >
> > Also, if the number of system calls really is a concern, maybe using
> > recvmmsg() instead of recvfrom(), on linux, will be a good idea.
>
> Sounds good! But it's probably worth microbenchmarking to see how
> much slower recvmmsg() is compared to recvfrom() for the case where
> there is one reply, I'll do this if you don't beat me to it.
I (hackily) implemented mmsg support, patches are attached. (I had
to rename sockaddr_ns because recvmmsg() requires -D_GNU_SOURCE, and
enabling _GNU_SOURCE also defines a sockaddr_ns.) With this patchset
there are three ioevent functions:
* dns_ioevent(): loop until EAGAIN
* dns_ioevent_once(): recvfrom() only once from the socket
* dns_ioevent_mmsg(): recvmmsg() N (by default 5) packets at a time,
and loop until a recvmmsg() call returns fewer than N packets
I modified the earlier bench.c and I compared the performance of _once()
versus _mmsg() over several thousand 15-second 1-parallel-query runs,
and that gives me:
===
$ ministat -c 99.5 -w 73 -q earlyout.user mmsg.user
x earlyout.user
+ mmsg.user
N Min Max Median Avg Stddev
x 6228 0.08 1.87 0.53 0.5273571 0.11094749
+ 6184 0.09 1.09 0.54 0.53886158 0.105258
Difference at 99.5% confidence
0.0115045 +/- 0.00599926
2.18154% +/- 1.13761%
(Student's t, pooled s = 0.10815)
$ ministat -c 99.5 -w 73 -q earlyout.system mmsg.system
x earlyout.system
+ mmsg.system
N Min Max Median Avg Stddev
x 6228 0.32 4.72 1.45 1.43092 0.31032514
+ 6184 0.36 3.97 1.53 1.5091882 0.31004362
Difference at 99.5% confidence
0.0782682 +/- 0.0172064
5.46978% +/- 1.20247%
(Student's t, pooled s = 0.310185)
$
===
So user time is up by ~2% and system time is up by ~5.5% for recvmmsg()
versus recvfrom() for the case where all recv*() calls return only 1
packet. recvmmsg() doesn't look interesting from this point of view,
or at least, not for level triggered poll methods, where _once() seems
like the best option.
Maybe the more interesting comparison would be dns_ioevent() versus
dns_ioevent_mmsg(), for the edge triggered poll loop case, because
recvmmsg() does allow avoiding the useless final EAGAIN recvfrom()
for every poll iteration.
-------------- next part --------------
>From c74aceda5e62f84f7030daf784cf9d624012dcad Mon Sep 17 00:00:00 2001
From: Lennert Buytenhek <buytenh at wantstofly.org>
Date: Mon, 4 Jun 2018 12:13:51 +0300
Subject: [PATCH 1/5] Split reply processing code out of dns_ioevent().
---
udns_resolver.c | 160 +++++++++++++++++++++++++-----------------------
1 file changed, 83 insertions(+), 77 deletions(-)
diff --git a/udns_resolver.c b/udns_resolver.c
index b8f899a..fd31aa8 100644
--- a/udns_resolver.c
+++ b/udns_resolver.c
@@ -719,7 +719,7 @@ static void dns_newid(struct dns_ctx *ctx, struct dns_query *q) {
q->dnsq_try = 0;
q->dnsq_servi = 0;
/*XXX probably should keep dnsq_servnEDNS0 bits?
- * See also comments in dns_ioevent() about FORMERR case */
+ * See also comments in dns_handle_reply() about FORMERR case */
q->dnsq_servwait = q->dnsq_servskip = q->dnsq_servnEDNS0 = 0;
}
@@ -853,9 +853,10 @@ dns_send(struct dns_ctx *ctx, struct dns_query *q, time_t now) {
!dns_find_serv(ctx, q)) {
/* no more servers and tries, fail the query */
/* return TEMPFAIL even when searching: no more tries for this
- * searchlist, and no single definitive reply (handled in dns_ioevent()
- * in NOERROR or NXDOMAIN cases) => all nameservers failed to process
- * current search list element, so we don't know whenever the name exists.
+ * searchlist, and no single definitive reply (handled in
+ * dns_handle_reply() in NOERROR or NXDOMAIN cases) => all nameservers
+ * failed to process current search list element, so we don't know
+ * whenever the name exists.
*/
dns_end_query(ctx, q, DNS_E_TEMPFAIL, 0);
return;
@@ -947,77 +948,33 @@ dns_submit_p(struct dns_ctx *ctx,
dns_submit_dn(ctx, ctx->dnsc_pbuf, qcls, qtyp, flags, parse, cbck, data);
}
-/* process readable fd condition.
- * To be usable in edge-triggered environment, the routine
- * should consume all input so it should loop over.
- * Note it isn't really necessary to loop here, because
- * an application may perform the loop just fine by it's own,
- * but in this case we should return some sensitive result,
- * to indicate when to stop calling and error conditions.
- * Note also we may encounter all sorts of recvfrom()
- * errors which aren't fatal, and at the same time we may
- * loop forever if an error IS fatal.
- */
-void dns_ioevent(struct dns_ctx *ctx, time_t now) {
- int r;
- unsigned servi;
- struct dns_query *q;
+static void dns_handle_reply(struct dns_ctx *ctx, time_t now, void *buf,
+ int r, struct sockaddr *src_addr, socklen_t slen)
+{
dnsc_t *pbuf;
dnscc_t *pend, *pcur;
+ union sockaddr_ns *sns;
+ struct dns_query *q;
+ unsigned servi;
void *result;
- union sockaddr_ns sns;
- socklen_t slen;
-
- SETCTX(ctx);
- if (!CTXOPEN(ctx))
- return;
- dns_assert_ctx(ctx);
- pbuf = ctx->dnsc_pbuf;
-
- if (!now) now = time(NULL);
-
-again: /* receive the reply */
-
- slen = sizeof(sns);
- r = recvfrom(ctx->dnsc_udpsock, (void*)pbuf, ctx->dnsc_udpbuf,
- MSG_DONTWAIT, &sns.sa, &slen);
- if (r < 0) {
- /*XXX just ignore recvfrom() errors for now.
- * in the future it may be possible to determine which
- * query failed and requeue it.
- * Note there may be various error conditions, triggered
- * by both local problems and remote problems. It isn't
- * quite trivial to determine whenever an error is local
- * or remote. On local errors, we should stop, while
- * remote errors should be ignored (for now anyway).
- */
-#ifdef WINDOWS
- if (WSAGetLastError() == WSAEWOULDBLOCK)
-#else
- if (errno == EAGAIN)
-#endif
- {
- dns_request_utm(ctx, now);
- return;
- }
- goto again;
- }
+ pbuf = buf;
pend = pbuf + r;
pcur = dns_payload(pbuf);
+ sns = (union sockaddr_ns *)src_addr;
/* check reply header */
if (pcur > pend || dns_numqd(pbuf) > 1 || dns_opcode(pbuf) != 0) {
- DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r);
- goto again;
+ DNS_DBG(ctx, -1/*bad reply*/, &sns->sa, slen, pbuf, r);
+ return;
}
/* find the matching query, by qID */
for (q = ctx->dnsc_qactive.head; ; q = q->dnsq_next) {
if (!q) {
/* no more requests: old reply? */
- DNS_DBG(ctx, -5/*no matching query*/, &sns.sa, slen, pbuf, r);
- goto again;
+ DNS_DBG(ctx, -5/*no matching query*/, &sns->sa, slen, pbuf, r);
+ return;
}
if (pbuf[DNS_H_QID1] == q->dnsq_id[0] &&
pbuf[DNS_H_QID2] == q->dnsq_id[1])
@@ -1030,22 +987,22 @@ again: /* receive the reply */
dnsc_t dn[DNS_MAXDN];
if (dns_getdn(pbuf, &pcur, pend, dn, sizeof(dn)) < 0 ||
pcur + 4 > pend) {
- DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r);
- goto again;
+ DNS_DBG(ctx, -1/*bad reply*/, &sns->sa, slen, pbuf, r);
+ return;
}
if (!dns_dnequal(dn, q->dnsq_dn) ||
memcmp(pcur, q->dnsq_typcls, 4) != 0) {
/* not this query */
- DNS_DBG(ctx, -5/*no matching query*/, &sns.sa, slen, pbuf, r);
- goto again;
+ DNS_DBG(ctx, -5/*no matching query*/, &sns->sa, slen, pbuf, r);
+ return;
}
/* here, query match, and pcur points past qDN in query section in pbuf */
}
/* if no numqd, we only allow FORMERR rcode */
else if (dns_rcode(pbuf) != DNS_R_FORMERR) {
/* treat it as bad reply if !FORMERR */
- DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r);
- goto again;
+ DNS_DBG(ctx, -1/*bad reply*/, &sns->sa, slen, pbuf, r);
+ return;
}
else {
/* else it's FORMERR, handled below */
@@ -1053,16 +1010,16 @@ again: /* receive the reply */
/* find server */
#ifdef HAVE_IPv6
- if (sns.sa.sa_family == AF_INET6 && slen >= sizeof(sns.sin6)) {
+ if (sns->sa.sa_family == AF_INET6 && slen >= sizeof(sns->sin6)) {
for(servi = 0; servi < ctx->dnsc_nserv; ++servi)
- if (sin6_eq(ctx->dnsc_serv[servi].sin6, sns.sin6))
+ if (sin6_eq(ctx->dnsc_serv[servi].sin6, sns->sin6))
break;
}
else
#endif
- if (sns.sa.sa_family == AF_INET && slen >= sizeof(sns.sin)) {
+ if (sns->sa.sa_family == AF_INET && slen >= sizeof(sns->sin)) {
for(servi = 0; servi < ctx->dnsc_nserv; ++servi)
- if (sin_eq(ctx->dnsc_serv[servi].sin, sns.sin))
+ if (sin_eq(ctx->dnsc_serv[servi].sin, sns->sin))
break;
}
else
@@ -1071,13 +1028,13 @@ again: /* receive the reply */
/* check if we expect reply from this server.
* Note we can receive reply from first try if we're already at next */
if (!(q->dnsq_servwait & (1 << servi))) { /* if ever asked this NS */
- DNS_DBG(ctx, -2/*wrong server*/, &sns.sa, slen, pbuf, r);
- goto again;
+ DNS_DBG(ctx, -2/*wrong server*/, &sns->sa, slen, pbuf, r);
+ return;
}
/* we got (some) reply for our query */
- DNS_DBGQ(ctx, q, 0, &sns.sa, slen, pbuf, r);
+ DNS_DBGQ(ctx, q, 0, &sns->sa, slen, pbuf, r);
q->dnsq_servwait &= ~(1 << servi); /* don't expect reply from this serv */
/* process the RCODE */
@@ -1125,7 +1082,7 @@ again: /* receive the reply */
memcpy(result, pbuf, r);
dns_end_query(ctx, q, r, result);
}
- goto again;
+ return;
case DNS_R_NXDOMAIN: /* Non-existing domain. */
if (dns_next_srch(ctx, q))
@@ -1136,7 +1093,7 @@ again: /* receive the reply */
* if we've seen it before, or NXDOMAIN if not. */
dns_end_query(ctx, q,
q->dnsq_flags & DNS_SEEN_NODATA ? DNS_E_NODATA : DNS_E_NXDOMAIN, 0);
- goto again;
+ return;
case DNS_R_FORMERR:
case DNS_R_NOTIMPL:
@@ -1155,7 +1112,7 @@ again: /* receive the reply */
*/
q->dnsq_servnEDNS0 |= 1 << servi;
dns_send_this(ctx, q, servi, now);
- goto again;
+ return;
}
/* else we handle it the same as SERVFAIL etc */
@@ -1195,8 +1152,57 @@ again: /* receive the reply */
else {
/* else don't do anything - not all servers replied yet */
}
- goto again;
+}
+
+/* process readable fd condition.
+ * To be usable in edge-triggered environment, the routine
+ * should consume all input so it should loop over.
+ * Note it isn't really necessary to loop here, because
+ * an application may perform the loop just fine by it's own,
+ * but in this case we should return some sensitive result,
+ * to indicate when to stop calling and error conditions.
+ * Note also we may encounter all sorts of recvfrom()
+ * errors which aren't fatal, and at the same time we may
+ * loop forever if an error IS fatal.
+ */
+void dns_ioevent(struct dns_ctx *ctx, time_t now) {
+ SETCTX(ctx);
+ if (!CTXOPEN(ctx))
+ return;
+ dns_assert_ctx(ctx);
+
+ if (!now) now = time(NULL);
+
+ while (1) {
+ union sockaddr_ns sns;
+ socklen_t slen;
+ int r;
+
+ slen = sizeof(sns);
+ r = recvfrom(ctx->dnsc_udpsock, (void*)ctx->dnsc_pbuf, ctx->dnsc_udpbuf,
+ MSG_DONTWAIT, &sns.sa, &slen);
+ if (r < 0) {
+ /*XXX just ignore recvfrom() errors for now.
+ * in the future it may be possible to determine which
+ * query failed and requeue it.
+ * Note there may be various error conditions, triggered
+ * by both local problems and remote problems. It isn't
+ * quite trivial to determine whenever an error is local
+ * or remote. On local errors, we should stop, while
+ * remote errors should be ignored (for now anyway).
+ */
+#ifdef WINDOWS
+ if (WSAGetLastError() == WSAEWOULDBLOCK)
+#else
+ if (errno == EAGAIN)
+#endif
+ break;
+ continue;
+ }
+ dns_handle_reply(ctx, now, ctx->dnsc_pbuf, r, &sns.sa, slen);
+ }
+ dns_request_utm(ctx, now);
}
/* handle all timeouts */
--
2.17.1
-------------- next part --------------
>From 179ffec488d9949de9cd98bd4c652fc1c6c4e700 Mon Sep 17 00:00:00 2001
From: Lennert Buytenhek <buytenh at wantstofly.org>
Date: Mon, 4 Jun 2018 12:23:28 +0300
Subject: [PATCH 2/5] Get rid of ctx->dnsc_pbuf.
---
udns_resolver.c | 41 ++++++++++++++++++-----------------------
1 file changed, 18 insertions(+), 23 deletions(-)
diff --git a/udns_resolver.c b/udns_resolver.c
index fd31aa8..bb92647 100644
--- a/udns_resolver.c
+++ b/udns_resolver.c
@@ -168,7 +168,6 @@ struct dns_ctx { /* resolver context */
int dnsc_udpsock; /* UDP socket */
struct dns_qlist dnsc_qactive; /* active list sorted by deadline */
int dnsc_nactive; /* number entries in dnsc_qactive */
- dnsc_t *dnsc_pbuf; /* packet buffer (udpbuf size) */
int dnsc_qstatus; /* last query status value */
};
@@ -422,9 +421,6 @@ void dns_close(struct dns_ctx *ctx) {
if (ctx->dnsc_udpsock >= 0)
closesocket(ctx->dnsc_udpsock);
ctx->dnsc_udpsock = -1;
- if (ctx->dnsc_pbuf)
- free(ctx->dnsc_pbuf);
- ctx->dnsc_pbuf = NULL;
q = ctx->dnsc_qactive.head;
while((p = q) != NULL) {
q = q->dnsq_next;
@@ -463,7 +459,6 @@ struct dns_ctx *dns_new(const struct dns_ctx *copy) {
ctx->dnsc_udpsock = -1;
qlist_init(&ctx->dnsc_qactive);
ctx->dnsc_nactive = 0;
- ctx->dnsc_pbuf = NULL;
ctx->dnsc_qstatus = 0;
ctx->dnsc_srchend = ctx->dnsc_srchbuf +
(copy->dnsc_srchend - copy->dnsc_srchbuf);
@@ -567,13 +562,6 @@ int dns_open(struct dns_ctx *ctx) {
return -1;
}
#endif /* WINDOWS */
- /* allocate the packet buffer */
- if ((ctx->dnsc_pbuf = malloc(ctx->dnsc_udpbuf)) == NULL) {
- closesocket(sock);
- ctx->dnsc_qstatus = DNS_E_NOMEM;
- errno = ENOMEM;
- return -1;
- }
ctx->dnsc_udpsock = sock;
dns_request_utm(ctx, 0);
@@ -766,11 +754,14 @@ static int dns_find_serv(const struct dns_ctx *ctx, struct dns_query *q) {
static int
dns_send_this(struct dns_ctx *ctx, struct dns_query *q,
unsigned servi, time_t now) {
+ dnsc_t *pbuf;
unsigned qlen;
unsigned tries;
+ pbuf = alloca(ctx->dnsc_udpbuf);
+
{ /* format the query buffer */
- dnsc_t *p = ctx->dnsc_pbuf;
+ dnsc_t *p = pbuf;
memset(p, 0, DNS_HSIZE);
if (!(q->dnsq_flags & DNS_NORD)) p[DNS_H_F1] |= DNS_HF1_RD;
if (q->dnsq_flags & DNS_AAONLY) p[DNS_H_F1] |= DNS_HF1_AA;
@@ -793,15 +784,15 @@ dns_send_this(struct dns_ctx *ctx, struct dns_query *q,
memset(p, 0, 2+2+2);
if (q->dnsq_flags & DNS_SET_DO) p[2] |= DNS_EF1_DO;
p += 2+2+2;
- ctx->dnsc_pbuf[DNS_H_ARCNT2] = 1;
+ pbuf[DNS_H_ARCNT2] = 1;
}
- qlen = p - ctx->dnsc_pbuf;
+ qlen = p - pbuf;
assert(qlen <= ctx->dnsc_udpbuf);
}
/* send the query */
tries = 10;
- while (sendto(ctx->dnsc_udpsock, (void*)ctx->dnsc_pbuf, qlen, 0,
+ while (sendto(ctx->dnsc_udpsock, (void*)pbuf, qlen, 0,
&ctx->dnsc_serv[servi].sa, ctx->dnsc_salen) < 0) {
/*XXX just ignore the sendto() error for now and try again.
* In the future, it may be possible to retrieve the error code
@@ -814,8 +805,7 @@ dns_send_this(struct dns_ctx *ctx, struct dns_query *q,
return -1;
}
DNS_DBGQ(ctx, q, 1,
- &ctx->dnsc_serv[servi].sa, sizeof(union sockaddr_ns),
- ctx->dnsc_pbuf, qlen);
+ &ctx->dnsc_serv[servi].sa, sizeof(union sockaddr_ns), pbuf, qlen);
q->dnsq_servwait |= 1 << servi; /* expect reply from this ns */
q->dnsq_deadline = now +
@@ -936,16 +926,17 @@ struct dns_query *
dns_submit_p(struct dns_ctx *ctx,
const char *name, int qcls, int qtyp, int flags,
dns_parse_fn *parse, dns_query_fn *cbck, void *data) {
+ dnsc_t *pbuf;
int isabs;
SETCTXOPEN(ctx);
- if (dns_ptodn(name, 0, ctx->dnsc_pbuf, DNS_MAXDN, &isabs) <= 0) {
+ pbuf = alloca(ctx->dnsc_udpbuf);
+ if (dns_ptodn(name, 0, pbuf, DNS_MAXDN, &isabs) <= 0) {
ctx->dnsc_qstatus = DNS_E_BADQUERY;
return NULL;
}
if (isabs)
flags |= DNS_NOSRCH;
- return
- dns_submit_dn(ctx, ctx->dnsc_pbuf, qcls, qtyp, flags, parse, cbck, data);
+ return dns_submit_dn(ctx, pbuf, qcls, qtyp, flags, parse, cbck, data);
}
static void dns_handle_reply(struct dns_ctx *ctx, time_t now, void *buf,
@@ -1166,11 +1157,15 @@ static void dns_handle_reply(struct dns_ctx *ctx, time_t now, void *buf,
* loop forever if an error IS fatal.
*/
void dns_ioevent(struct dns_ctx *ctx, time_t now) {
+ dnsc_t *pbuf;
+
SETCTX(ctx);
if (!CTXOPEN(ctx))
return;
dns_assert_ctx(ctx);
+ pbuf = alloca(ctx->dnsc_udpbuf);
+
if (!now) now = time(NULL);
while (1) {
@@ -1179,7 +1174,7 @@ void dns_ioevent(struct dns_ctx *ctx, time_t now) {
int r;
slen = sizeof(sns);
- r = recvfrom(ctx->dnsc_udpsock, (void*)ctx->dnsc_pbuf, ctx->dnsc_udpbuf,
+ r = recvfrom(ctx->dnsc_udpsock, (void*)pbuf, ctx->dnsc_udpbuf,
MSG_DONTWAIT, &sns.sa, &slen);
if (r < 0) {
/*XXX just ignore recvfrom() errors for now.
@@ -1199,7 +1194,7 @@ void dns_ioevent(struct dns_ctx *ctx, time_t now) {
break;
continue;
}
- dns_handle_reply(ctx, now, ctx->dnsc_pbuf, r, &sns.sa, slen);
+ dns_handle_reply(ctx, now, pbuf, r, &sns.sa, slen);
}
dns_request_utm(ctx, now);
--
2.17.1
-------------- next part --------------
>From eeec6919b328b6beda96dd7491249c2560f9f5a5 Mon Sep 17 00:00:00 2001
From: Lennert Buytenhek <buytenh at wantstofly.org>
Date: Mon, 4 Jun 2018 12:30:53 +0300
Subject: [PATCH 3/5] Add dns_ioevent_once().
---
udns.h | 2 ++
udns_resolver.c | 24 ++++++++++++++++++++++++
2 files changed, 26 insertions(+)
diff --git a/udns.h b/udns.h
index 371e697..632dd48 100644
--- a/udns.h
+++ b/udns.h
@@ -460,6 +460,8 @@ dns_setstatus(struct dns_ctx *ctx, int status);
/* handle I/O event on UDP socket */
UDNS_API void
dns_ioevent(struct dns_ctx *ctx, time_t now);
+UDNS_API void
+dns_ioevent_once(struct dns_ctx *ctx, time_t now);
/* process any timeouts, return time in secounds to the
* next timeout (or -1 if none) but not greather than maxwait */
diff --git a/udns_resolver.c b/udns_resolver.c
index bb92647..a1ed81f 100644
--- a/udns_resolver.c
+++ b/udns_resolver.c
@@ -1200,6 +1200,30 @@ void dns_ioevent(struct dns_ctx *ctx, time_t now) {
dns_request_utm(ctx, now);
}
+void dns_ioevent_once(struct dns_ctx *ctx, time_t now) {
+ dnsc_t *pbuf;
+ union sockaddr_ns sns;
+ socklen_t slen;
+ int r;
+
+ SETCTX(ctx);
+ if (!CTXOPEN(ctx))
+ return;
+ dns_assert_ctx(ctx);
+
+ pbuf = alloca(ctx->dnsc_udpbuf);
+
+ if (!now) now = time(NULL);
+
+ slen = sizeof(sns);
+ r = recvfrom(ctx->dnsc_udpsock, (void*)pbuf, ctx->dnsc_udpbuf,
+ MSG_DONTWAIT, &sns.sa, &slen);
+ if (r >= 0)
+ dns_handle_reply(ctx, now, pbuf, r, &sns.sa, slen);
+
+ dns_request_utm(ctx, now);
+}
+
/* handle all timeouts */
int dns_timeouts(struct dns_ctx *ctx, int maxwait, time_t now) {
/* this is a hot routine */
--
2.17.1
-------------- next part --------------
>From 84af01fd8b4e45e7a5441b4a8e3fc438fed6b1d0 Mon Sep 17 00:00:00 2001
From: Lennert Buytenhek <buytenh at wantstofly.org>
Date: Mon, 4 Jun 2018 12:55:49 +0300
Subject: [PATCH 4/5] s/union sockaddr_ns/union sockaddr_udns/g.
---
udns_resolver.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/udns_resolver.c b/udns_resolver.c
index a1ed81f..f2fdcd9 100644
--- a/udns_resolver.c
+++ b/udns_resolver.c
@@ -126,7 +126,7 @@ qlist_insert_after(struct dns_qlist *list,
qlist_add_head(list, q);
}
-union sockaddr_ns {
+union sockaddr_udns {
struct sockaddr sa;
struct sockaddr_in sin;
#ifdef HAVE_IPv6
@@ -150,7 +150,7 @@ struct dns_ctx { /* resolver context */
unsigned dnsc_port; /* default port (DNS_PORT) */
unsigned dnsc_udpbuf; /* size of UDP buffer */
/* array of nameserver addresses */
- union sockaddr_ns dnsc_serv[DNS_MAXSERV];
+ union sockaddr_udns dnsc_serv[DNS_MAXSERV];
unsigned dnsc_nserv; /* number of nameservers */
unsigned dnsc_salen; /* length of socket addresses */
dnsc_t dnsc_srchbuf[1024]; /* buffer for searchlist */
@@ -229,7 +229,7 @@ enum {
};
int dns_add_serv(struct dns_ctx *ctx, const char *serv) {
- union sockaddr_ns *sns;
+ union sockaddr_udns *sns;
SETCTXFRESH(ctx);
if (!serv)
return (ctx->dnsc_nserv = 0);
@@ -478,7 +478,7 @@ int dns_open(struct dns_ctx *ctx) {
int sock;
unsigned i;
int port;
- union sockaddr_ns *sns;
+ union sockaddr_udns *sns;
#ifdef HAVE_IPv6
unsigned have_inet6 = 0;
#endif
@@ -805,7 +805,7 @@ dns_send_this(struct dns_ctx *ctx, struct dns_query *q,
return -1;
}
DNS_DBGQ(ctx, q, 1,
- &ctx->dnsc_serv[servi].sa, sizeof(union sockaddr_ns), pbuf, qlen);
+ &ctx->dnsc_serv[servi].sa, sizeof(union sockaddr_udns), pbuf, qlen);
q->dnsq_servwait |= 1 << servi; /* expect reply from this ns */
q->dnsq_deadline = now +
@@ -944,7 +944,7 @@ static void dns_handle_reply(struct dns_ctx *ctx, time_t now, void *buf,
{
dnsc_t *pbuf;
dnscc_t *pend, *pcur;
- union sockaddr_ns *sns;
+ union sockaddr_udns *sns;
struct dns_query *q;
unsigned servi;
void *result;
@@ -952,7 +952,7 @@ static void dns_handle_reply(struct dns_ctx *ctx, time_t now, void *buf,
pbuf = buf;
pend = pbuf + r;
pcur = dns_payload(pbuf);
- sns = (union sockaddr_ns *)src_addr;
+ sns = (union sockaddr_udns *)src_addr;
/* check reply header */
if (pcur > pend || dns_numqd(pbuf) > 1 || dns_opcode(pbuf) != 0) {
@@ -1169,7 +1169,7 @@ void dns_ioevent(struct dns_ctx *ctx, time_t now) {
if (!now) now = time(NULL);
while (1) {
- union sockaddr_ns sns;
+ union sockaddr_udns sns;
socklen_t slen;
int r;
@@ -1202,7 +1202,7 @@ void dns_ioevent(struct dns_ctx *ctx, time_t now) {
void dns_ioevent_once(struct dns_ctx *ctx, time_t now) {
dnsc_t *pbuf;
- union sockaddr_ns sns;
+ union sockaddr_udns sns;
socklen_t slen;
int r;
--
2.17.1
-------------- next part --------------
>From 90181f03860a127ea9d3c63694b7c1211fe5e6b4 Mon Sep 17 00:00:00 2001
From: Lennert Buytenhek <buytenh at wantstofly.org>
Date: Mon, 4 Jun 2018 12:56:21 +0300
Subject: [PATCH 5/5] Add dns_ioevent_mmsg().
---
udns.h | 2 ++
udns_resolver.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 68 insertions(+)
diff --git a/udns.h b/udns.h
index 632dd48..1145e66 100644
--- a/udns.h
+++ b/udns.h
@@ -462,6 +462,8 @@ UDNS_API void
dns_ioevent(struct dns_ctx *ctx, time_t now);
UDNS_API void
dns_ioevent_once(struct dns_ctx *ctx, time_t now);
+UDNS_API void
+dns_ioevent_mmsg(struct dns_ctx *ctx, time_t now);
/* process any timeouts, return time in secounds to the
* next timeout (or -1 if none) but not greather than maxwait */
diff --git a/udns_resolver.c b/udns_resolver.c
index f2fdcd9..c42e290 100644
--- a/udns_resolver.c
+++ b/udns_resolver.c
@@ -28,6 +28,7 @@
# include <winsock2.h> /* includes <windows.h> */
# include <ws2tcpip.h> /* needed for struct in6_addr */
#else
+# define _GNU_SOURCE
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
@@ -1224,6 +1225,71 @@ void dns_ioevent_once(struct dns_ctx *ctx, time_t now) {
dns_request_utm(ctx, now);
}
+#define NUMBUF 5
+
+void dns_ioevent_mmsg(struct dns_ctx *ctx, time_t now) {
+ dnsc_t *pbuf;
+
+ SETCTX(ctx);
+ if (!CTXOPEN(ctx))
+ return;
+ dns_assert_ctx(ctx);
+
+ pbuf = alloca(NUMBUF * ctx->dnsc_udpbuf);
+
+ if (!now) now = time(NULL);
+
+ while (1) {
+ struct mmsghdr mh[NUMBUF];
+ union sockaddr_udns sns[NUMBUF];
+ struct iovec iov[NUMBUF];
+ int i;
+ int r;
+
+ for (i = 0; i < NUMBUF; i++) {
+ mh[i].msg_hdr.msg_name = &sns[i];
+ mh[i].msg_hdr.msg_namelen = sizeof(sns[i]);
+ mh[i].msg_hdr.msg_iov = &iov[i];
+ mh[i].msg_hdr.msg_iovlen = 1;
+ mh[i].msg_hdr.msg_control = NULL;
+ mh[i].msg_hdr.msg_controllen = 0;
+
+ iov[i].iov_base = pbuf + (i * ctx->dnsc_udpbuf);
+ iov[i].iov_len = ctx->dnsc_udpbuf;
+ }
+
+ r = recvmmsg(ctx->dnsc_udpsock, mh, NUMBUF, MSG_DONTWAIT, NULL);
+ if (r < 0) {
+ /*XXX just ignore recvfrom() errors for now.
+ * in the future it may be possible to determine which
+ * query failed and requeue it.
+ * Note there may be various error conditions, triggered
+ * by both local problems and remote problems. It isn't
+ * quite trivial to determine whenever an error is local
+ * or remote. On local errors, we should stop, while
+ * remote errors should be ignored (for now anyway).
+ */
+#ifdef WINDOWS
+ if (WSAGetLastError() == WSAEWOULDBLOCK)
+#else
+ if (errno == EAGAIN)
+#endif
+ break;
+ continue;
+ }
+
+ for (i = 0; i < r; i++) {
+ dns_handle_reply(ctx, now, iov[i].iov_base, mh[i].msg_len,
+ &sns[i].sa, mh[i].msg_hdr.msg_namelen);
+ }
+
+ if (r < NUMBUF)
+ break;
+ }
+
+ dns_request_utm(ctx, now);
+}
+
/* handle all timeouts */
int dns_timeouts(struct dns_ctx *ctx, int maxwait, time_t now) {
/* this is a hot routine */
--
2.17.1
More information about the udns
mailing list