commit f66fa6219ff386ed6ed04db08a829733b62a6464
Author: David Girault <dgirault@sevenhugs.com>
Date:   Mon Dec 18 17:30:40 2017 +0100

    mdns: ensure response are sent after a little random timeout

diff --git a/src/apps/mdns/mdns.c b/src/apps/mdns/mdns.c
index cb1ad502..f48cc483 100644
--- a/src/apps/mdns/mdns.c
+++ b/src/apps/mdns/mdns.c
@@ -61,6 +61,7 @@
 #include "lwip/udp.h"
 #include "lwip/ip_addr.h"
 #include "lwip/mem.h"
+#include "lwip/timeouts.h"
 #include "lwip/prot/dns.h"
 
 #include <string.h>
@@ -226,6 +227,8 @@ struct mdns_outpacket {
   u16_t domain_offsets[NUM_DOMAIN_OFFSETS];
   /** If all answers in packet should set cache_flush bit */
   u8_t cache_flush;
+  /** If reply should be sent synchronously (announce) */
+  u8_t no_async;
   /** If reply should be sent unicast */
   u8_t unicast_reply;
   /** If legacy query. (tx_id needed, and write
@@ -239,6 +242,19 @@ struct mdns_outpacket {
   u8_t serv_replies[MDNS_MAX_SERVICES];
 };
 
+/** Information about outgoing packet */
+struct mdns_async_outpacket {
+  /** Netif to send the packet on */
+  struct netif *netif;
+  /** Packet data */
+  struct pbuf *pbuf;
+  /** Destination IP/port if sent unicast */
+  ip_addr_t dest_addr;
+  u16_t dest_port;
+  /** If reply should be sent unicast */
+  u8_t unicast_reply;
+};
+
 struct mdns_question {
   struct mdns_rr_info info;
   /** unicast reply requested */
@@ -1315,6 +1331,40 @@ mdns_init_outpacket(struct mdns_outpacket *out, struct mdns_packet *in)
   }
 }
 
+/**
+ * Send delayed response
+ * @param arg An allocated mdns_async_outpacket structure
+ */
+static void
+mdns_async_send_outpacket(void *arg)
+{
+  const ip_addr_t *mcast_destaddr;
+  struct mdns_async_outpacket *outpkt = (struct mdns_async_outpacket *)arg;
+  struct pbuf *p = outpkt->pbuf;
+  /* Send delayed packet */
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Async sending packet %p, len=%d, unicast=%d, "
+                                 "next=%p, ref=%d\n",
+                                 p, p->tot_len, outpkt->unicast_reply,
+                                 p->next, p->ref));
+  if (IP_IS_V6_VAL(outpkt->dest_addr)) {
+#if LWIP_IPV6
+    mcast_destaddr = &v6group;
+#endif
+  } else {
+#if LWIP_IPV4
+    mcast_destaddr = &v4group;
+#endif
+  }
+  if (outpkt->unicast_reply) {
+    udp_sendto_if(mdns_pcb, p, &outpkt->dest_addr, outpkt->dest_port, outpkt->netif);
+  } else {
+    udp_sendto_if(mdns_pcb, p, mcast_destaddr, MDNS_PORT, outpkt->netif);
+  }
+  pbuf_free(p);
+  outpkt->pbuf = NULL;
+  mem_free(arg);
+}
+
 /**
  * Send chosen answers as a reply
  *
@@ -1502,6 +1552,31 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags)
       mcast_destaddr = &v4group;
 #endif
     }
+
+    /* Delayed answer? See 6.3 in RFC 6762. */
+    if (!outpkt->no_async && outpkt->answers > 1) {
+      struct mdns_async_outpacket *async_outpkt;
+      u32_t msecs = 0;
+      while (msecs < 20)
+        msecs = LWIP_RAND() & 127;
+      async_outpkt = (struct mdns_async_outpacket *)mem_malloc(sizeof(struct mdns_async_outpacket));
+      if (async_outpkt) {
+        struct pbuf *p = outpkt->pbuf;
+        outpkt->pbuf = NULL;
+        async_outpkt->pbuf = p;
+        async_outpkt->netif = outpkt->netif;
+        async_outpkt->dest_port  = outpkt->dest_port;
+        async_outpkt->unicast_reply = outpkt->unicast_reply;
+        memcpy(&async_outpkt->dest_addr, &outpkt->dest_addr, sizeof(outpkt->dest_addr));
+        LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Wait sending packet %p, wait=%d, len=%d, unicast=%d, "
+                                 "next=%p, ref=%d\n",
+                                 p, msecs, p->tot_len, outpkt->unicast_reply,
+                                 p->next, p->ref));
+        sys_timeout(msecs, mdns_async_send_outpacket, async_outpkt);
+        return;
+      }
+    }
+
     /* Send created packet */
     LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Sending packet, len=%d, unicast=%d\n", outpkt->write_offset, outpkt->unicast_reply));
     if (outpkt->unicast_reply) {
@@ -1533,6 +1608,7 @@ mdns_announce(struct netif *netif, const ip_addr_t *destination)
   memset(&announce, 0, sizeof(announce));
   announce.netif = netif;
   announce.cache_flush = 1;
+  announce.no_async = 1;
 #if LWIP_IPV4
   if (!ip4_addr_isany_val(*netif_ip4_addr(netif)))
     announce.host_replies = REPLY_HOST_A | REPLY_HOST_PTR_V4;
@@ -1575,7 +1651,8 @@ mdns_search(struct mdns_request *req, struct netif *netif, const ip_addr_t *dest
   memset(&request, 0, sizeof(request));
   request.netif = netif;
   request.cache_flush = 0;
-  
+  request.no_async = 1;
+
   /* Add question (legacy packet only has 1 question) */
   mdns_build_request_domain(&dom, req, req->name[0]);
   res = mdns_add_question(&request, &dom, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0);
