about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/cache/cache.c2
-rw-r--r--src/cache/deps-exe/shibari-cache1
-rw-r--r--src/cache/dns.c38
-rw-r--r--src/cache/log.c27
-rw-r--r--src/cache/query.c81
-rw-r--r--src/cache/shibari-cache-internal.h51
-rw-r--r--src/cache/shibari-cache.c65
-rw-r--r--src/cache/tcpconnection.c11
-rw-r--r--src/cache/udpqueue.c70
-rw-r--r--src/include/shibari/dcache.h3
-rw-r--r--src/libdcache/dcache_search.c13
11 files changed, 202 insertions, 160 deletions
diff --git a/src/cache/cache.c b/src/cache/cache.c
index e8796f7..1169350 100644
--- a/src/cache/cache.c
+++ b/src/cache/cache.c
@@ -45,7 +45,7 @@ int cache_search (s6dns_domain_t const *name, uint16_t qtype, dcache_key_t *data
   char key[name->len + 1] ;
   uint16_pack_big(key, qtype) ;
   memcpy(key + 2, name->s, name->len - 1) ;
-  node = dcache_search(&cache, key, name->len + 1) ;
+  node = dcache_search_g(&cache, key, name->len + 1) ;
   if (!node) return 0 ;
   data->s = node->key.s + node->key.len ;
   data->len = node->datalen ;
diff --git a/src/cache/deps-exe/shibari-cache b/src/cache/deps-exe/shibari-cache
index 44d74a0..e363f19 100644
--- a/src/cache/deps-exe/shibari-cache
+++ b/src/cache/deps-exe/shibari-cache
@@ -1,7 +1,6 @@
 cache.o
 clientaccess.o
 conf.o
-dns.o
 log.o
 query.o
 tcpconnection.o
diff --git a/src/cache/dns.c b/src/cache/dns.c
deleted file mode 100644
index ce5b611..0000000
--- a/src/cache/dns.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/* ISC license. */
-
-#include <skalibs/uint16.h>
-
-#include <s6-dns/s6dns.h>
-
-#include "shibari-cache-internal.h"
-
-int dns_newquery (uint8_t source, uint16_t i, char const *ip, uint16_t port, char const *buf, uint16_t len)
-{
-  dcache_key_t data ;
-  s6dns_message_header_t hdr ;
-  s6dns_message_counts_t counts ;
-  s6dns_domain_t name ;
-  unsigned int pos ;
-  unsigned int rcode ;
-  uint16_t qtype ;
-  char key[257] ;
-
-  if (!s6dns_message_parse_init(&hdr, &counts, buf, len, &pos)) return 1 ;
-  if (hdr.opcode) { rcode = 4 ; goto err ; }
-  if (!hdr.rd) { rcode = 1 ; goto err ; }
-  if (!s6dns_message_parse_question(&counts, &name, &qtype, buf, len, &pos)
-   || !s6dns_domain_encode(&name))
-  {
-    rcode = errno == ENOTSUP ? 4 : 1 ;
-    goto answer ;
-  }
-  
-  if (cache_search(&name, qtype, &data)) goto got ;
-  return 1 ;
-
- answer:
-  return 1 ;
- err:
- got :
-  return 1 ;
-}
diff --git a/src/cache/log.c b/src/cache/log.c
index bc67384..0e20f91 100644
--- a/src/cache/log.c
+++ b/src/cache/log.c
@@ -6,7 +6,9 @@
 
 #include "shibari-cache-internal.h"
 
-void log_tcptimeout (uint16_t i)
+
+
+void log_udp4bad (char const *ip, uint16_t port)
 {
   if (g->verbosity >= 3)
   {
@@ -20,11 +22,34 @@ void log_newtcp4 (char const *ip, uint16_t port)
   }
 }
 
+void log_tcpbad (uint16_t i)
+{
+  if (g->verbosity >= 3)
+  {
+  }
+}
+
+void log_tcptimeout (uint16_t i)
+{
+  if (g->verbosity >= 3)
+  {
+  }
+}
+
 #ifdef SKALIBS_IPV6_ENABLED
+
+void log_udp6bad (char const *ip, uint16_t port)
+{
+  if (g->verbosity >= 3)
+  {
+  }
+}
+
 void log_newtcp6 (char const *ip, uint16_t port)
 {
   if (g->verbosity >= 3)
   {
   }
 }
+
 #endif
diff --git a/src/cache/query.c b/src/cache/query.c
index 8f5e423..7fb27c4 100644
--- a/src/cache/query.c
+++ b/src/cache/query.c
@@ -2,8 +2,9 @@
 
 #include <stdint.h>
 
-#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns.h>
 
+#include <shibari/constants.h>
 #include "shibari-cache-internal.h"
 
 static uint16_t query_delete (query *q)
@@ -12,6 +13,7 @@ static uint16_t query_delete (query *q)
   QUERY(newi)->next = q->next ;
   QUERY(q->next)->prev = q->prev ;
   q->xindex = UINT16_MAX ;
+  q->qname.len = 0 ;
   return newi ;
 }
 
@@ -38,7 +40,82 @@ uint16_t query_succeed (uint16_t id)
   return query_delete(q) ;
 }
 
-int query_new (uint8_t source, uint16_t i, char const *ip, uint16_t port, char const *buf, uint16_t len)
+int query_end (uint8_t source, uint16_t i, char const *ip, uint16_t port, char const *buf, uint16_t len)
 {
+  return source < 2 ?
+    udpqueue_add(g->udpqueues[source] + i, source, ip, port, buf, len) :
+    tcpconnection_add(g->tcpconnections + i, buf, len) ;
+}
+
+int query_error (uint8_t source, uint16_t i, char const *ip, uint16_t port, s6dns_domain_t *name, uint16_t qtype, uint16_t id, unsigned int rcode)
+{
+  s6dns_message_header_t hdr = S6DNS_MESSAGE_HEADER_ZERO ;
+  unsigned int pos = 12 ;
+  char pkt[name->len + 16] ;
+  hdr.id = id ;
+  hdr.qr = 1 ;
+  hdr.ra = 1 ;
+  hdr.rcode = rcode ;
+  hdr.counts.qd = 1 ;
+  s6dns_message_header_pack(pkt, &hdr) ;
+  memcpy(pkt + pos, name->s, name->len) ; pos += name->len ;
+  uint16_pack_big(pkt + pos, qtype) ; pos += 2 ;
+  uint16_pack_big(pkt + pos, SHIBARI_C_IN) ; pos += 2 ;
+  return query_end(source, i, ip, port, pkt, pos) ;
+}
+
+static void query_init (query *q, uint8_t source, uint16_t i, char const *ip, uint16_t port, s6dns_domain_t const *name, uint16_t qtype)
+{
+  q->source = source ;
+  q->i = i ;
+  if (source < 2)
+  {
+    memcpy(q->ip, ip, source ? 16 : 4) ;
+    q->port = port ;
+  }
+  q->port = port ;
+  if (!stralloc_catb(&q->qname, name->s, name->len)) dienomem() ;
+  q->qtype = qtype ;
+  q->prefixlen = 0 ;
+}
+
+static query *query_new (uint8_t source, uint16_t i, char const *ip, uint16_t port, s6dns_domain_t const *name, uint16_t qtype)
+{
+  uint16_t n = genset_new(&g->queries) ;
+  query *sentinel = QUERY(g->qsentinel) ;
+  query *q = QUERY(n) ;
+  query_init(q, source, i, ip, port, name, type) ;
+  q->prev = g->qsentinel ;
+  q->next = sentinel->next ;
+  QUERY(sentinel->next)->prev = n ;
+  sentinel->next = n ;
+  return q ;
+}
+
+int query_start (uint8_t source, uint16_t i, char const *ip, uint16_t port, char const *buf, uint16_t len)
+{
+  dcache_key_t data ;
+  s6dns_message_header_t hdr ;
+  s6dns_message_counts_t counts ;
+  s6dns_domain_t name ;
+  unsigned int pos ;
+  unsigned int rcode ;
+  uint16_t qtype ;
+
+  if (!s6dns_message_parse_init(&hdr, &counts, buf, len, &pos)
+   || !s6dns_message_parse_question(&counts, &name, &qtype, buf, len, &pos)
+   || !s6dns_domain_encode(&name)) return 0 ;
+  if (hdr.opcode) return query_error(source, i, ip, port, &name, qtype, hdr.id, 4) ;
+  if (!hdr.rd) return query_error(source, i, ip, port, &name, qtype, hdr.id, 9) ;
+
+  if (cache_search(&name, qtype, &data))
+    return query_end(source, i, ip, port, data.s, data.len) ;
+
+  {
+    uint16_t j = genset_new(&g->queries) ;
+    query *q = QUERY(j) ;
+  }
+
   return 1 ;
 }
+
diff --git a/src/cache/shibari-cache-internal.h b/src/cache/shibari-cache-internal.h
index c8819fd..950bfdc 100644
--- a/src/cache/shibari-cache-internal.h
+++ b/src/cache/shibari-cache-internal.h
@@ -9,6 +9,7 @@
 #include <skalibs/uint64.h>
 #include <skalibs/cdb.h>
 #include <skalibs/tai.h>
+#include <skalibs/strerr.h>
 #include <skalibs/stralloc.h>
 #include <skalibs/genalloc.h>
 #include <skalibs/bufalloc.h>
@@ -20,6 +21,8 @@
 
 #include <shibari/dcache.h>
 
+#define dienomem() strerr_diefu1sys(111, "concatenate data") ;
+
 
  /* cache */
 
@@ -47,17 +50,15 @@ extern int conf_get_uint64 (char const *, uint64_t *) ;
 extern char const *conf_get_string (char const *) ;
 
 
- /* dns */
-
-extern int dns_newquery (uint8_t, uint16_t, char const *, uint16_t, char const *, uint16_t) ;
-
-
  /* log */
 
+extern void log_udp4bad (char const *, uint16_t) ;
 extern void log_newtcp4 (char const *, uint16_t) ;
+extern void log_tcpbad (uint16_t) ;
+extern void log_tcptimeout (uint16_t) ;
 #ifdef SKALIBS_IPV6_ENABLED
+extern void log_udp6bad (char const *, uint16_t) ;
 extern void log_newtcp6 (char const *, uint16_t) ;
-extern void log_tcptimeout (uint16_t) ;
 #endif
 
 
@@ -67,15 +68,17 @@ typedef struct query_s query, *query_ref ;
 struct query_s
 {
   s6dns_engine_t dt ;
+  stralloc qname ;
   uint16_t prev ;
   uint16_t next ;
   uint16_t xindex ;
   uint16_t i ;
   uint16_t port ;
+  uint16_t qtype ;
   uint8_t source ;
   char ip[SKALIBS_IP_SIZE] ;
 } ;
-#define QUERY_ZERO { .dt = S6DNS_ENGINE_ZERO, .prev = 0, .next = 0, .xindex = UINT16_MAX, .i = 0, .port = 0, .source = 0, .ip = { 0 } }
+#define QUERY_ZERO { .dt = S6DNS_ENGINE_ZERO, .qname = STRALLOC_ZERO, .prev = 0, .next = 0, .xindex = UINT16_MAX, .i = 0, .port = 0, qtype = 0, name = 0, .source = 0, .ip = { 0 } }
 #define nq (genset_n(&g->queries) - 1)
 #define QUERY(i) genset_p(query, &g->queries, (i))
 #define qstart (QUERY(g->qsentinel)->next)
@@ -83,8 +86,9 @@ struct query_s
 extern uint16_t query_abort (uint16_t) ;
 extern uint16_t query_fail (uint16_t) ;
 extern uint16_t query_succeed (uint16_t) ;
-extern int query_new (uint8_t, uint16_t, char const *, uint16_t, char const *, uint16_t) ;
 
+extern int query_start (uint8_t, uint16_t, char const *, uint16_t, char const *, uint16_t) ;
+extern int query_end (uint8_t, uint16_t, char const *, uint16_t, char const *, uint16_t) ;
 
  /* tcpconnection */
 
@@ -114,32 +118,20 @@ extern void tcpconnection_new (int) ;
 
  /* udpqueue */
 
-typedef struct udp4msg_s udp4msg, *udp4msg_ref ;
-struct udp4msg_s
-{
-  char ip[4] ;
-  uint16_t port ;
-  uint16_t len ;
-} ;
-#define UDP4MSG_ZERO { .ip = { 0 }, .port = 0, .len = 0 }
-
-#ifdef SKALIBS_IPV6_ENABLED
-typedef struct udp6msg_s udp6msg, *udp6msg_ref ;
-struct udp6msg_s
+typedef struct udpaux_s udpaux, *udpaux_ref ;
+struct udpaux_s
 {
-  char ip[16] ;
   uint16_t port ;
   uint16_t len ;
 } ;
-#define UDP6MSG_ZERO { .ip = { 0 }, .port = 0, .len = 0 }
-#endif
+#define UDPAUX_ZERO { .port = 0, .len = 0 }
 
 typedef struct udpqueue_s udpqueue, *udpqueue_ref ;
 struct udpqueue_s
 {
   int fd ;
   stralloc storage ;
-  genalloc messages ; /* udp[46]msg */
+  genalloc messages ; /* udpaux */
   tain deadline ;
   uint16_t xindex ;
 } ;
@@ -147,13 +139,8 @@ struct udpqueue_s
 
 extern void udpqueue_drop (udpqueue *) ;
 
-extern int udpqueue_add4 (udpqueue *, char const *, uint16_t, char const *, uint16_t) ;
-extern int udpqueue_flush4 (udpqueue *) ;
-
-#ifdef SKALIBS_IPV6_ENABLED
-extern int udpqueue_add6 (udpqueue *, char const *, uint16_t, char const *, uint16_t) ;
-extern int udpqueue_flush6 (udpqueue *) ;
-#endif
+extern int udpqueue_add (udpqueue *, uint8_t, char const *, uint16_t, char const *, uint16_t) ;
+extern int udpqueue_flush (udpqueue *, uint8_t) ;
 
 
  /* main */
@@ -166,6 +153,7 @@ struct global_s
   uint16_t verbosity ;
   tain rtto ;
   tain wtto ;
+  udpqueue *udpqueues[2] ;
   genset tcpconnections ;  /* tcpconnection */
   genset queries ;  /* query */
   uint16_t tcpsentinel ;
@@ -177,6 +165,7 @@ struct global_s
   .verbosity = 1, \
   .rtto = TAIN_INFINITE, \
   .wtto = TAIN_INFINITE, \
+  .udpqueues = { 0, 0 }, \
   .tcpconnections = GENSET_ZERO, \
   .queries = GENSET_ZERO, \
   .tcpsentinel = 0, \
diff --git a/src/cache/shibari-cache.c b/src/cache/shibari-cache.c
index ea705bd..d9391f2 100644
--- a/src/cache/shibari-cache.c
+++ b/src/cache/shibari-cache.c
@@ -197,6 +197,8 @@ int main (int argc, char const *const *argv)
 
     memset(udpq4, 0, n4 * sizeof(udpqueue)) ;
     memset(udpq6, 0, n6 * sizeof(udpqueue)) ;
+    g->udpqueues[0] = udpq4 ;
+    g->udpqueues[1] = udpq6 ;
     memset(tcpconnection_storage, 0, (maxtcp + 1) * sizeof(tcpconnection)) ;
     memset(query_storage, 0, (maxqueries + 1) * sizeof(query)) ;
     GENSET_init(&g->tcpconnections, tcpconnection, tcpconnection_storage, tcpconnection_freelist, maxtcp + 1) ;
@@ -391,10 +393,11 @@ int main (int argc, char const *const *argv)
 
       else 
       {
-        for (uint16_t i = 0 ; i < j ; i++) if (x[i].revents & IOPAUSE_EXCEPT) x[i].revents |= x[i].events ;
-
         if (x[0].revents & IOPAUSE_READ) { handle_signals() ; continue ; }
 
+
+       /* write udp */
+
         for (uint16_t i = 0 ; i < n4 ; i++) if (udpq4[i].xindex < UINT16_MAX)
         {
           if (x[udpq4[i].xindex].revents & IOPAUSE_WRITE)
@@ -423,6 +426,9 @@ int main (int argc, char const *const *argv)
         }
 #endif
 
+
+       /* write tcp */
+
         for (uint16_t i = tcpstart ; i != g->tcpsentinel ; i = TCPCONNECTION(i)->next)
         {
           tcpconnection *p = TCPCONNECTION(i) ;
@@ -430,6 +436,9 @@ int main (int argc, char const *const *argv)
             if (tcpconnection_flush(p) == -1) i = tcpconnection_delete(p) ;
         }
 
+
+       /* process in-flight queries */
+
         for (uint16_t i = qstart ; i != g->qsentinel ; i = QUERY(i)->next)
         {
           if (QUERY(i)->xindex == UINT16_MAX) continue ;
@@ -438,6 +447,9 @@ int main (int argc, char const *const *argv)
           else if (r > 0) i = query_succeed(i) ;
         }
 
+
+       /* read udp */
+
         for (uint16_t i = 0 ; i < n4 ; i++)
         {
           if (udpq4[i].xindex < UINT16_MAX && x[udpq4[i].xindex].revents & IOPAUSE_READ)
@@ -456,18 +468,11 @@ int main (int argc, char const *const *argv)
                 strerr_diefu2sys(111, "read from UDP socket bound to ", fmt) ;
               }
               if (!len) break ;
-              if (len < 12 || len > 512) continue ;
-              if (!clientaccess_ip4(ip)) continue ;
-              if (!dns_newquery(0, i, ip, port, buf, len))
+              if (len < 12 || len > 512
+               || !clientaccess_ip4(ip)
+               || !query_start(0, i, ip, port, buf, len))
               {
-                if (g->verbosity)
-                {
-                  char fmtip[IP4_FMT] ;
-                  char fmtport[UINT16_FMT] ;
-                  fmtip[ip4_fmt(fmtip, ip)] = 0 ;
-                  fmtport[uint16_fmt(fmtport, port)] = 0 ;
-                  strerr_warnwu4sys("process new UDP query from ip ", fmtip, " port ", fmtport) ;
-                }
+                if (g->verbosity >= 3) log_udpbad(ip, port) ;
               }
             }
           }
@@ -492,24 +497,20 @@ int main (int argc, char const *const *argv)
                 strerr_diefu2sys(111, "read from UDP socket bound to ", fmt) ;
               }
               if (!len) break ;
-              if (len < 12 || len > 512) continue ;
-              if (!clientaccess_ip6(ip)) continue ;
-              if (!dns_newquery(1, i, ip, port, buf, len))
+              if (len < 12 || len > 512
+               || !clientaccess_ip6(ip)
+               || !query_start(1, i, ip, port, buf, len))
               {
-                if (g->verbosity)
-                {
-                  char fmtip[IP4_FMT] ;
-                  char fmtport[UINT16_FMT] ;
-                  fmtip[ip4_fmt(fmtip, ip)] = 0 ;
-                  fmtport[uint16_fmt(fmtport, port)] = 0 ;
-                  strerr_warnwu4sys("process new UDP query from ip ", fmtip, " port ", fmtport) ;
-                }
+                if (g->verbosity >= 3) log_udpbad(ip, port) ;
               }
             }
           }
         }
 #endif
 
+
+       /* read tcp */
+
         for (uint16_t i = tcpstart ; i != g->tcpsentinel ; i = TCPCONNECTION(i)->next)
         {
           tcpconnection *p = TCPCONNECTION(i) ;
@@ -521,21 +522,21 @@ int main (int argc, char const *const *argv)
               int l = sanitize_read(mininetstring_read(bufalloc_fd(&p->out), &p->in, &p->instate)) ;
               if (l == -1) { i = tcpconnection_delete(p) ; break ; }
               if (!l) break ;
-              if (p->in.len < 12 || p->in.len > 65536) { i = tcpconnection_delete(p) ; break ; }
-              if (!dns_newquery(2, i, 0, 0, p->in.s, p->in.len))
+              if (p->in.len < 12 || p->in.len > 65536
+               || !query_start(2, i, 0, 0, p->in.s, p->in.len))
               {
-                if (g->verbosity)
-                {
-                  char fmt[UINT16_FMT] ;
-                  fmt[uint16_fmt(fmt, i)] = 0 ;
-                  strerr_warnwu2sys("process TCP query on connection ", fmt) ;
-                }
+                if (g->verbosity >= 3) log_tcpbad(i) ;
+                i = tcpconnection_delete(p) ;
+                break ;
               }
               p->in.len = 0 ;
             }
           }
         }
 
+
+       /* new tcp */
+
         for (uint16_t i = 0 ; i < n4 ; i++) if (tcp4xindex[i] < UINT16_MAX)
         {
           if (x[tcp4xindex[i]].revents & IOPAUSE_READ)
diff --git a/src/cache/tcpconnection.c b/src/cache/tcpconnection.c
index 71a4299..40c3035 100644
--- a/src/cache/tcpconnection.c
+++ b/src/cache/tcpconnection.c
@@ -3,6 +3,7 @@
 #include <stdint.h>
 #include <errno.h>
 
+#include <skalibs/uint16.h>
 #include <skalibs/allreadwrite.h>
 #include <skalibs/error.h>
 #include <skalibs/tai.h>
@@ -39,6 +40,16 @@ uint16_t tcpconnection_delete (tcpconnection *p)
   return newi ;
 }
 
+int tcpconnection_add (tcpconnection *p, char const *s, uint16_t len)
+{
+  char pack[2] ;
+  if (!stralloc_readyplus(&p->out.x, 2 + len)) return 0 ;
+  uint16_pack_big(pack, len) ;
+  bufalloc_put(&p->out, pack, 2) ;
+  bufalloc_put(&p->out, s, len) ;
+  return 0 ;
+}
+
 int tcpconnection_flush (tcpconnection *p)
 {
   return bufalloc_flush(&p->out) ? 1 :
diff --git a/src/cache/udpqueue.c b/src/cache/udpqueue.c
index 8750d18..901292e 100644
--- a/src/cache/udpqueue.c
+++ b/src/cache/udpqueue.c
@@ -18,65 +18,35 @@ void udpqueue_drop (udpqueue *q)
   tain_add_g(&q->deadline, &tain_infinite_relative) ;
 }
 
-int udpqueue_add4 (udpqueue *q, char const *ip, uint16_t port, char const *s, uint16_t len)
+int udpqueue_add (udpqueue *q, uint8_t source, char const *ip, uint16_t port, char const *s, uint16_t len)
 {
-  udp4msg msg = { .port = port, .len = len } ;
-  if (!stralloc_readyplus(&q->storage, len)) return 0 ;
-  memcpy(msg.ip, ip, 4) ;
-  if (!genalloc_append(udp4msg, &q->messages, &msg)) return 0 ;
+  size_t iplen = source ? 16 : 4 ;
+  udpaux msg = { .port = port, .len = len } ;
+  if (!stralloc_readyplus(&q->storage, iplen + len)) return 0 ;
+  if (!genalloc_append(udpaux, &q->messages, &msg)) return 0 ;
   if (!q->storage.len) tain_add_g(&q->deadline, &g->wtto) ;
+  stralloc_catb(&q->storage, ip, iplen) ;
   stralloc_catb(&q->storage, s, len) ;
   return 1 ;
 }
 
-int udpqueue_flush4 (udpqueue *q)
+int udpqueue_flush (udpqueue *q, uint8_t is6)
 {
-  size_t n = genalloc_len(udp4msg, &q->messages) ;
+  size_t n = genalloc_len(udpaux, &q->messages) ;
   size_t shead = 0, head = 0 ;
   ssize_t r = 1 ;
   while (head < n)
   {
-    udp4msg const *msg = genalloc_s(udp4msg, &q->messages) + head ;
-    ssize_t r = socket_send4(q->fd, q->storage.s + shead, msg->len, msg->ip, msg->port) ;
-    if (r <= 0) goto adjust ;
-    shead += msg->len ;
-  }
-  udpqueue_drop(q) ;
-  return 1 ;
-
- adjust:
-  memmove(q->storage.s, q->storage.s + shead, q->storage.len - shead) ;
-  q->storage.len -= shead ;
-  memmove(genalloc_s(udp4msg, &q->messages), genalloc_s(udp4msg, &q->messages) + head, (n - head) * sizeof(udp4msg)) ;
-  genalloc_setlen(udp4msg, &q->messages, n - head) ;
-  if (shead) tain_add_g(&q->deadline, &g->wtto) ;
-  return sanitize_read(r) ;
-}
-
-#ifdef SKALIBS_IPv6_ENABLED
-
-int udpqueue_add6 (udpqueue *q, char const *ip, uint16_t port, char const *s, uint16_t len)
-{
-  udp6msg msg = { .port = port, .len = len } ;
-  if (!stralloc_readyplus(&q->storage, len)) return 0 ;
-  memcpy(msg.ip, ip, 16) ;
-  if (!genalloc_append(udp6msg, &q->messages, &msg)) return 0 ;
-  if (!q->storage.len) tain_add_g(&q->deadline, &g->wtto) ;
-  stralloc_catb(&q->storage, s, len) ;
-  return 1 ;
-}
-
-int udpqueue_flush6 (udpqueue *q)
-{
-  size_t n = genalloc_len(udp6msg, &q->messages) ;
-  size_t shead = 0, head = 0 ;
-  ssize_t r = 1 ;
-  while (head < n)
-  {
-    udp6msg const *msg = genalloc_s(udp4msg, &q->messages) + head ;
-    r = socket_send6(q->fd, q->storage.s + shead, msg->len, msg->ip, msg->port) ;
+    udpaux const *msg = genalloc_s(udpaux, &q->messages) + head ;
+    ssize_t r ;
+#if SKALIBS_IPV6_ENABLED
+    if (is6)
+      r = socket_send6(q->fd, q->storage.s + shead + 16, msg->len, q->storage.s + shead, msg->port) ;
+    else
+#endif
+      r = socket_send4(q->fd, q->storage.s + shead + 4, msg->len, q->storage.s + shead, msg->port) ;
     if (r <= 0) goto adjust ;
-    shead += msg->len ;
+    shead += (is6 ? 16 : 4) + msg->len ;
   }
   udpqueue_drop(q) ;
   return 1 ;
@@ -84,10 +54,8 @@ int udpqueue_flush6 (udpqueue *q)
  adjust:
   memmove(q->storage.s, q->storage.s + shead, q->storage.len - shead) ;
   q->storage.len -= shead ;
-  memmove(genalloc_s(udp6msg, &q->messages), genalloc_s(udp6msg, &q->messages) + head, (n - head) * sizeof(udp6msg)) ;
-  genalloc_setlen(udp6msg, &q->messages, n - head) ;
+  memmove(genalloc_s(udpaux, &q->messages), genalloc_s(udpaux, &q->messages) + head, (n - head) * sizeof(udpaux)) ;
+  genalloc_setlen(udpaux, &q->messages, n - head) ;
   if (shead) tain_add_g(&q->deadline, &g->wtto) ;
   return sanitize_read(r) ;
 }
-
-#endif
diff --git a/src/include/shibari/dcache.h b/src/include/shibari/dcache.h
index 50fc44d..1ec9fb6 100644
--- a/src/include/shibari/dcache.h
+++ b/src/include/shibari/dcache.h
@@ -42,7 +42,8 @@ struct dcache_s
 #define DCACHE_ZERO { .storage = GENSETDYN_ZERO, .by_key = AVLTREE_ZERO, .by_entry = AVLTREE_ZERO, .by_expire = AVLTREE_ZERO, .max = 0, .size = 0, .motion = 0 }
 
 extern void dcache_init (dcache_t *, uint64_t) ;
-extern dcache_node_t *dcache_search (dcache_t *, char const *, uint16_t) ;
+extern dcache_node_t *dcache_search (dcache_t *, char const *, uint16_t, tain const *) ;
+#define dcache_search_g(d, key, keylen) dcache_search(d, key, (keylen), &STAMP)
 extern int dcache_add (dcache_t *, char const *, uint16_t, char const *, uint16_t, tain const *, tain const *) ;
 #define dcache_add_g(d, key, keylen, data, datalen, expire) dcache_add(d, key, keylen, data, datalen, (expire), &STAMP)
 extern void dcache_clean_expired (dcache_t *, tain const *) ;
diff --git a/src/libdcache/dcache_search.c b/src/libdcache/dcache_search.c
index 0239b88..3d79dd6 100644
--- a/src/libdcache/dcache_search.c
+++ b/src/libdcache/dcache_search.c
@@ -7,9 +7,18 @@
 #include <shibari/dcache.h>
 #include "dcache-internal.h"
 
-dcache_node_t *dcache_search (dcache_t *z, char const *key, uint16_t keylen)
+dcache_node_t *dcache_search (dcache_t *z, char const *key, uint16_t keylen, tain const *stamp)
 {
   uint32_t i ;
   dcache_key_t k = { .s = (char *)key, .len = keylen } ;
-  return avltree_search(&z->by_key, &k, &i) ? DNODE(z, i) : 0 ;
+  if (avltree_search(&z->by_key, &k, &i))
+  {
+    if (tain_less(&DNODE(z, i)->expire, stamp))
+    {
+      dcache_clean_expired(z, stamp) ;
+      if (!avltree_search(&z->by_key, &k, &i)) return 0 ;
+    }
+    return DNODE(z, i) ;
+  }
+  else return 0 ;
 }