source: npl/internetserver/djbdns/patches/0007-dnscache-merge-similar-outgoing-udp-packets.patch @ 720681d

Last change on this file since 720681d was 37aaf89, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

djbdns patches, so it works correctly with twitter etc

  • Property mode set to 100644
File size: 9.6 KB
RevLine 
[37aaf89]1Description: dnscache: merge similar outgoing udp packets
2 This patch takes a slightly different approach to merging
3 than the previous qmerge patch: rather than merging between
4 the "query" and "dns_transmit" layers, it actually
5 incorporates the merging into the dns_transmit layer.
6 .
7 This makes it a much more invasive and messy patch, but
8 means that it should correctly handle all situations
9 (in particular, ones where sending to two different IPs will
10 gives different results, generally because one server is
11 down or lame).
12 .
13 The general strategy is this:
14 .
15   - when a dns_transmit is about to send() a UDP query, it
16     checks for an equivalent inprogress query.
17 .
18     If it finds one, it marks the inprogress dns_transmit as
19     its "master", and itself as a "slave" of the master.
20 .
21     If it doesn't, it sends the packet and registers itself
22     as inprogress.
23 .
24   - when a dns_transmit recv()s a UDP packet, it creates a
25     copy of the packet for each of its slaves
26 .
27 Two outgoing packets are considered equivalent if:
28 .
29   1. they are going to the same IP
30   2. they have the same qtype
31   3. they have the same qname
32 .
33 Because this change affects the dns library itself, this
34 change can potentially affect not just dnscache, but all of
35 the diagnostic tools. To address this, merging must be
36 enabled explicitly by the caller; dnscache will enable
37 merging if the MERGEQUERIES environment variable is set.
38 .
39 I tried to keep the patch as small and simple as possible so
40 that its correctness could be verified by readers.  There
41 are a few places where performance might benefit from making
42 it more complex:
43 .
44   - dns_transmit now knows the value of MAXUDP, since it is
45     an upper bound on the number of slaves and inprogress
46     queries. As a result:
47 .
48       - each non-merging program which uses the dns library
49         wastes MAXUDP*sizeof(pointer) bytes of static memory
50         for the inprogress list
51 .
52       - each dns_transmit uses an extra MAXUDP*sizeof(pointer)
53         bytes for its slaves list. For dnscache, this
54         translates to 160K total on a 32-bit platform with
55         default MAXUDP.
56 .
57     Both could be avoided by using dynamic allocation.
58 .
59   - We have to do an O(MAXUDP) linear search to find similar
60     inprogress queries (non-merge-enabled users of the
61     library avoid paying this price, though).
62 .
63     This could be avoided by using a data structure with a
64     fast key lookup for inprogress.
65 .
66 This patch is only lightly tested. Use on production servers at your own
67 risk (and please report to the list if you have success using it).
68Author: Jeff King <peff () peff ! net>
69Date: Wed, 1 Apr 2009 14:10:54 +0000
70Debian-Bug: https://bugs.debian.org/516394
71Last-Update: 2020-07-26
72
73diff --git a/clients.h b/clients.h
74new file mode 100644
75index 0000000..983a4ad
76--- /dev/null
77+++ b/clients.h
78@@ -0,0 +1,7 @@
79+#ifndef CLIENTS_H
80+#define CLIENTS_H
81+
82+#define MAXUDP 200
83+#define MAXTCP 20
84+
85+#endif /* CLIENTS_H */
86diff --git a/dns.h b/dns.h
87index 3849f4c..d1e2ffc 100644
88--- a/dns.h
89+++ b/dns.h
90@@ -4,6 +4,7 @@
91 #include "stralloc.h"
92 #include "iopause.h"
93 #include "taia.h"
94+#include "clients.h"
95 
96 #define DNS_C_IN "\0\1"
97 #define DNS_C_ANY "\0\377"
98@@ -38,8 +39,14 @@ struct dns_transmit {
99   const char *servers;
100   char localip[4];
101   char qtype[2];
102+  struct dns_transmit *master;
103+  struct dns_transmit *slaves[MAXUDP];
104+  int nslaves;
105 } ;
106 
107+extern void dns_enable_merge(void (*logger)(const char *, const char *,
108+      const char *));
109+
110 extern void dns_random_init(const char *);
111 extern unsigned int dns_random(unsigned int);
112 
113diff --git a/dns_transmit.c b/dns_transmit.c
114index 4d6e39f..3984776 100644
115--- a/dns_transmit.c
116+++ b/dns_transmit.c
117@@ -7,6 +7,61 @@
118 #include "byte.h"
119 #include "uint16.h"
120 #include "dns.h"
121+#include "strerr.h"
122+
123+static int merge_enable;
124+static void (*merge_logger)(const char *, const char *, const char *);
125+void dns_enable_merge(void (*f)(const char *, const char *, const char *))
126+{
127+  merge_enable = 1;
128+  merge_logger = f;
129+}
130+
131+static int merge_equal(struct dns_transmit *a, struct dns_transmit *b)
132+{
133+  const char *ip1 = a->servers + 4 * a->curserver;
134+  const char *ip2 = b->servers + 4 * b->curserver;
135+  return
136+    byte_equal(ip1, 4, ip2) &&
137+    byte_equal(a->qtype, 2, b->qtype) &&
138+    dns_domain_equal(a->query + 14, b->query + 14);
139+}
140+
141+struct dns_transmit *inprogress[MAXUDP];
142+
143+static int try_merge(struct dns_transmit *d)
144+{
145+  int i;
146+  for (i = 0; i < MAXUDP; i++) {
147+    if (!inprogress[i]) continue;
148+    if (!merge_equal(d, inprogress[i])) continue;
149+    d->master = inprogress[i];
150+    inprogress[i]->slaves[inprogress[i]->nslaves++] = d;
151+    return 1;
152+  }
153+  return 0;
154+}
155+
156+static void register_inprogress(struct dns_transmit *d)
157+{
158+  int i;
159+  for (i = 0; i < MAXUDP; i++) {
160+    if (!inprogress[i]) {
161+      inprogress[i] = d;
162+      return;
163+    }
164+  }
165+  strerr_die1x(100, "BUG: out of inprogress slots");
166+}
167+
168+static void unregister_inprogress(struct dns_transmit *d)
169+{
170+  int i;
171+  for (i = 0; i < MAXUDP; i++) {
172+    if (inprogress[i] == d)
173+      inprogress[i] = 0;
174+  }
175+}
176 
177 static int serverwantstcp(const char *buf,unsigned int len)
178 {
179@@ -59,8 +114,28 @@ static void packetfree(struct dns_transmit *d)
180   d->packet = 0;
181 }
182 
183+static void mergefree(struct dns_transmit *d)
184+{
185+  int i;
186+  if (merge_enable)
187+    unregister_inprogress(d);
188+  /* unregister us from our master */
189+  if (d->master) {
190+    for (i = 0; i < d->master->nslaves; i++)
191+      if (d->master->slaves[i] == d)
192+        d->master->slaves[i] = 0;
193+  }
194+  /* and unregister all of our slaves from us */
195+  for (i = 0; i < d->nslaves; i++) {
196+    if (d->slaves[i])
197+      d->slaves[i]->master = NULL;
198+  }
199+  d->nslaves = 0;
200+}
201+
202 static void queryfree(struct dns_transmit *d)
203 {
204+  mergefree(d);
205   if (!d->query) return;
206   alloc_free(d->query);
207   d->query = 0;
208@@ -99,11 +174,18 @@ static int thisudp(struct dns_transmit *d)
209   const char *ip;
210 
211   socketfree(d);
212+  mergefree(d);
213 
214   while (d->udploop < 4) {
215     for (;d->curserver < 16;++d->curserver) {
216       ip = d->servers + 4 * d->curserver;
217       if (byte_diff(ip,4,"\0\0\0\0")) {
218+        if (merge_enable && try_merge(d)) {
219+          if (merge_logger)
220+            merge_logger(ip, d->qtype, d->query + 14);
221+          return 0;
222+        }
223+
224        d->query[2] = dns_random(256);
225        d->query[3] = dns_random(256);
226   
227@@ -118,6 +200,8 @@ static int thisudp(struct dns_transmit *d)
228             taia_uint(&d->deadline,timeouts[d->udploop]);
229             taia_add(&d->deadline,&d->deadline,&now);
230             d->tcpstate = 0;
231+            if (merge_enable)
232+              register_inprogress(d);
233             return 0;
234           }
235   
236@@ -226,8 +310,12 @@ void dns_transmit_io(struct dns_transmit *d,iopause_fd *x,struct taia *deadline)
237   x->fd = d->s1 - 1;
238 
239   switch(d->tcpstate) {
240-    case 0: case 3: case 4: case 5:
241-      x->events = IOPAUSE_READ;
242+    case 0:
243+      if (d->master) return;
244+      if (d->packet) { taia_now(deadline); return; }
245+      /* otherwise, fall through */
246+    case 3: case 4: case 5:
247+        x->events = IOPAUSE_READ;
248       break;
249     case 1: case 2:
250       x->events = IOPAUSE_WRITE;
251@@ -244,10 +332,14 @@ int dns_transmit_get(struct dns_transmit *d,const iopause_fd *x,const struct tai
252   unsigned char ch;
253   int r;
254   int fd;
255+  int i;
256 
257   errno = error_io;
258   fd = d->s1 - 1;
259 
260+  if (d->tcpstate == 0 && d->master) return 0;
261+  if (d->tcpstate == 0 && d->packet) return 1;
262+
263   if (!x->revents) {
264     if (taia_less(when,&d->deadline)) return 0;
265     errno = error_timeout;
266@@ -279,6 +371,15 @@ have sent query to curserver on UDP socket s
267     d->packet = alloc(d->packetlen);
268     if (!d->packet) { dns_transmit_free(d); return -1; }
269     byte_copy(d->packet,d->packetlen,udpbuf);
270+
271+    for (i = 0; i < d->nslaves; i++) {
272+      if (!d->slaves[i]) continue;
273+      d->slaves[i]->packetlen = d->packetlen;
274+      d->slaves[i]->packet = alloc(d->packetlen);
275+      if (!d->slaves[i]->packet) { dns_transmit_free(d->slaves[i]); continue; }
276+      byte_copy(d->slaves[i]->packet,d->packetlen,udpbuf);
277+    }
278+
279     queryfree(d);
280     return 1;
281   }
282diff --git a/dnscache.c b/dnscache.c
283index 8c899a3..c8db179 100644
284--- a/dnscache.c
285+++ b/dnscache.c
286@@ -54,7 +54,6 @@ uint64 numqueries = 0;
287 
288 static int udp53;
289 
290-#define MAXUDP 200
291 static struct udpclient {
292   struct query q;
293   struct taia start;
294@@ -131,7 +130,6 @@ void u_new(void)
295 
296 static int tcp53;
297 
298-#define MAXTCP 20
299 struct tcpclient {
300   struct query q;
301   struct taia start;
302@@ -435,6 +433,8 @@ int main()
303     response_hidettl();
304   if (env_get("FORWARDONLY"))
305     query_forwardonly();
306+  if (env_get("MERGEQUERIES"))
307+    dns_enable_merge(log_merge);
308 
309   if (!roots_init())
310     strerr_die2sys(111,FATAL,"unable to read servers: ");
311diff --git a/log.c b/log.c
312index c43e8b0..3e1c674 100644
313--- a/log.c
314+++ b/log.c
315@@ -150,6 +150,12 @@ void log_tx(const char *q,const char qtype[2],const char *control,const char ser
316   line();
317 }
318 
319+void log_merge(const char *addr, const char qtype[2], const char *q)
320+{
321+  string("merge "); ip(addr); space(); logtype(qtype); space(); name(q);
322+  line();
323+}
324+
325 void log_cachedanswer(const char *q,const char type[2])
326 {
327   string("cached "); logtype(type); space();
328diff --git a/log.h b/log.h
329index fe62fa3..dd6408a 100644
330--- a/log.h
331+++ b/log.h
332@@ -18,6 +18,7 @@ extern void log_cachednxdomain(const char *);
333 extern void log_cachedns(const char *,const char *);
334 
335 extern void log_tx(const char *,const char *,const char *,const char *,unsigned int);
336+extern void log_merge(const char *, const char *, const char *);
337 
338 extern void log_nxdomain(const char *,const char *,unsigned int);
339 extern void log_nodata(const char *,const char *,const char *,unsigned int);
Note: See TracBrowser for help on using the repository browser.