--- clients.h.orig	2009-04-21 23:43:02.000000000 -0400
+++ clients.h
@@ -0,0 +1,7 @@
+#ifndef CLIENTS_H
+#define CLIENTS_H
+
+#define MAXUDP 200
+#define MAXTCP 20
+
+#endif /* CLIENTS_H */
--- dns.h.orig	2001-02-11 16:11:45.000000000 -0500
+++ dns.h
@@ -4,6 +4,7 @@
 #include "stralloc.h"
 #include "iopause.h"
 #include "taia.h"
+#include "clients.h"
 
 #define DNS_C_IN "\0\1"
 #define DNS_C_ANY "\0\377"
@@ -37,8 +38,14 @@ struct dns_transmit {
   const char *servers;
   char localip[4];
   char qtype[2];
+  struct dns_transmit *master;
+  struct dns_transmit *slaves[MAXUDP];
+  int nslaves;
 } ;
 
+extern void dns_enable_merge(void (*logger)(const char *, const char *,
+      const char *));
+
 extern void dns_random_init(const char *);
 extern unsigned int dns_random(unsigned int);
 
--- dns_transmit.c.orig	2001-02-11 16:11:45.000000000 -0500
+++ dns_transmit.c
@@ -7,6 +7,61 @@
 #include "byte.h"
 #include "uint16.h"
 #include "dns.h"
+#include "strerr.h"
+
+static int merge_enable;
+static void (*merge_logger)(const char *, const char *, const char *);
+void dns_enable_merge(void (*f)(const char *, const char *, const char *))
+{
+  merge_enable = 1;
+  merge_logger = f;
+}
+
+static int merge_equal(struct dns_transmit *a, struct dns_transmit *b)
+{
+  const char *ip1 = a->servers + 4 * a->curserver;
+  const char *ip2 = b->servers + 4 * b->curserver;
+  return
+    byte_equal(ip1, 4, ip2) &&
+    byte_equal(a->qtype, 2, b->qtype) &&
+    dns_domain_equal(a->query + 14, b->query + 14);
+}
+
+struct dns_transmit *inprogress[MAXUDP];
+
+static int try_merge(struct dns_transmit *d)
+{
+  int i;
+  for (i = 0; i < MAXUDP; i++) {
+    if (!inprogress[i]) continue;
+    if (!merge_equal(d, inprogress[i])) continue;
+    d->master = inprogress[i];
+    inprogress[i]->slaves[inprogress[i]->nslaves++] = d;
+    return 1;
+  }
+  return 0;
+}
+
+static void register_inprogress(struct dns_transmit *d)
+{
+  int i;
+  for (i = 0; i < MAXUDP; i++) {
+    if (!inprogress[i]) {
+      inprogress[i] = d;
+      return;
+    }
+  }
+  strerr_die1x(100, "BUG: out of inprogress slots");
+}
+
+static void unregister_inprogress(struct dns_transmit *d)
+{
+  int i;
+  for (i = 0; i < MAXUDP; i++) {
+    if (inprogress[i] == d)
+      inprogress[i] = 0;
+  }
+}
 
 static int serverwantstcp(const char *buf,unsigned int len)
 {
@@ -59,8 +114,28 @@ static void packetfree(struct dns_transm
   d->packet = 0;
 }
 
+static void mergefree(struct dns_transmit *d)
+{
+  int i;
+  if (merge_enable)
+    unregister_inprogress(d);
+  /* unregister us from our master */
+  if (d->master) {
+    for (i = 0; i < d->master->nslaves; i++)
+      if (d->master->slaves[i] == d)
+        d->master->slaves[i] = 0;
+  }
+  /* and unregister all of our slaves from us */
+  for (i = 0; i < d->nslaves; i++) {
+    if (d->slaves[i])
+      d->slaves[i]->master = NULL;
+  }
+  d->nslaves = 0;
+}
+
 static void queryfree(struct dns_transmit *d)
 {
+  mergefree(d);
   if (!d->query) return;
   alloc_free(d->query);
   d->query = 0;
@@ -99,11 +174,18 @@ static int thisudp(struct dns_transmit *
   const char *ip;
 
   socketfree(d);
+  mergefree(d);
 
   while (d->udploop < 4) {
     for (;d->curserver < 16;++d->curserver) {
       ip = d->servers + 4 * d->curserver;
       if (byte_diff(ip,4,"\0\0\0\0")) {
+        if (merge_enable && try_merge(d)) {
+          if (merge_logger)
+            merge_logger(ip, d->qtype, d->query + 14);
+          return 0;
+        }
+
 	d->query[2] = dns_random(256);
 	d->query[3] = dns_random(256);
   
@@ -118,6 +200,8 @@ static int thisudp(struct dns_transmit *
             taia_uint(&d->deadline,timeouts[d->udploop]);
             taia_add(&d->deadline,&d->deadline,&now);
             d->tcpstate = 0;
+            if (merge_enable)
+              register_inprogress(d);
             return 0;
           }
   
@@ -226,8 +310,12 @@ void dns_transmit_io(struct dns_transmit
   x->fd = d->s1 - 1;
 
   switch(d->tcpstate) {
-    case 0: case 3: case 4: case 5:
-      x->events = IOPAUSE_READ;
+    case 0:
+      if (d->master) return;
+      if (d->packet) { taia_now(deadline); return; }
+      /* otherwise, fall through */
+    case 3: case 4: case 5:
+        x->events = IOPAUSE_READ;
       break;
     case 1: case 2:
       x->events = IOPAUSE_WRITE;
@@ -244,10 +332,14 @@ int dns_transmit_get(struct dns_transmit
   unsigned char ch;
   int r;
   int fd;
+  int i;
 
   errno = error_io;
   fd = d->s1 - 1;
 
+  if (d->tcpstate == 0 && d->master) return 0;
+  if (d->tcpstate == 0 && d->packet) return 1;
+
   if (!x->revents) {
     if (taia_less(when,&d->deadline)) return 0;
     errno = error_timeout;
@@ -279,6 +371,15 @@ have sent query to curserver on UDP sock
     d->packet = alloc(d->packetlen);
     if (!d->packet) { dns_transmit_free(d); return -1; }
     byte_copy(d->packet,d->packetlen,udpbuf);
+
+    for (i = 0; i < d->nslaves; i++) {
+      if (!d->slaves[i]) continue;
+      d->slaves[i]->packetlen = d->packetlen;
+      d->slaves[i]->packet = alloc(d->packetlen);
+      if (!d->slaves[i]->packet) { dns_transmit_free(d->slaves[i]); continue; }
+      byte_copy(d->slaves[i]->packet,d->packetlen,udpbuf);
+    }
+
     queryfree(d);
     return 1;
   }
--- dnscache.c.orig	2001-02-11 16:11:45.000000000 -0500
+++ dnscache.c
@@ -54,7 +54,6 @@ uint64 numqueries = 0;
 
 static int udp53;
 
-#define MAXUDP 200
 static struct udpclient {
   struct query q;
   struct taia start;
@@ -131,7 +130,6 @@ void u_new(void)
 
 static int tcp53;
 
-#define MAXTCP 20
 struct tcpclient {
   struct query q;
   struct taia start;
@@ -435,6 +433,8 @@ int main()
     response_hidettl();
   if (env_get("FORWARDONLY"))
     query_forwardonly();
+  if (env_get("MERGEQUERIES"))
+    dns_enable_merge(log_merge);
 
   if (!roots_init())
     strerr_die2sys(111,FATAL,"unable to read servers: ");
--- log.c.orig	2001-02-11 16:11:45.000000000 -0500
+++ log.c
@@ -150,6 +150,12 @@ void log_tx(const char *q,const char qty
   line();
 }
 
+void log_merge(const char *addr, const char qtype[2], const char *q)
+{
+  string("merge "); ip(addr); space(); logtype(qtype); space(); name(q);
+  line();
+}
+
 void log_cachedanswer(const char *q,const char type[2])
 {
   string("cached "); logtype(type); space();
--- log.h.orig	2001-02-11 16:11:45.000000000 -0500
+++ log.h
@@ -18,6 +18,7 @@ extern void log_cachednxdomain(const cha
 extern void log_cachedns(const char *,const char *);
 
 extern void log_tx(const char *,const char *,const char *,const char *,unsigned int);
+extern void log_merge(const char *, const char *, const char *);
 
 extern void log_nxdomain(const char *,const char *,unsigned int);
 extern void log_nodata(const char *,const char *,const char *,unsigned int);
