From 05be124951d69bca10f52e957afe6c975deb447b Mon Sep 17 00:00:00 2001
From: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date: Wed, 14 Nov 2018 15:38:36 +0100
Subject: [PATCH] mDNS: respond to probe via multicast added

The host only responded to a probe query via unicast because
according to the RFC, a probe should have the QU bit on.
This is a should and not a must so we need to be careful.
We added multicast probe answering support with the needed timouts.
Avahi for example probes with the QM queries. With this commit the
conflict is resolved.
---
 src/apps/mdns/mdns.c              |  90 +++++++++++++---------
 src/apps/mdns/mdns_out.c          | 122 +++++++++++++++++++++++++++---
 src/include/lwip/apps/mdns_out.h  |  12 +++
 src/include/lwip/apps/mdns_priv.h |   6 +-
 4 files changed, 180 insertions(+), 50 deletions(-)

diff --git a/src/apps/mdns/mdns.c b/src/apps/mdns/mdns.c
index 1602ea6bb..83e64f988 100644
--- a/src/apps/mdns/mdns.c
+++ b/src/apps/mdns/mdns.c
@@ -901,10 +901,11 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif)
     reply.cache_flush = 1;
   }
 
-  /* Delaying response.
+  /* Delaying response. (RFC6762 section 6)
    * Always delay the response, unicast or multicast, except when:
-   *  - Answering to a single question with a unique answer (RFC6762 section 6)
-   *  - Answering to a probe query via unicast (RFC6762 section 6)
+   *  - Answering to a single question with a unique answer (not a probe).
+   *  - Answering to a probe query via unicast.
+   *  - Answering to a probe query via multicast if not multicasted within 250ms.
    *
    * unique answer? -> not if it includes service type or name ptr's
    */
@@ -912,9 +913,22 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif)
     shared_answer |= (reply.serv_replies[i] &
                       (REPLY_SERVICE_TYPE_PTR | REPLY_SERVICE_NAME_PTR));
   }
-  if (((pkt->questions == 1) && (!shared_answer)) || reply.probe_query_recv) {
+  if (   ((pkt->questions == 1) && (!shared_answer) && !reply.probe_query_recv)
+      || (reply.probe_query_recv && reply.unicast_reply_requested)) {
     delay_response = 0;
   }
+#if LWIP_IPV6
+  if (IP_IS_V6_VAL(pkt->source_addr) && reply.probe_query_recv
+      && !reply.unicast_reply_requested && !mdns->ipv6.multicast_probe_timeout) {
+    delay_response = 0;
+  }
+#endif
+#if LWIP_IPV4
+  if (IP_IS_V4_VAL(pkt->source_addr) && reply.probe_query_recv
+      && !reply.unicast_reply_requested && !mdns->ipv4.multicast_probe_timeout) {
+    delay_response = 0;
+  }
+#endif
   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: response %s delayed\r\n", (delay_response ? "randomly" : "not")));
 
   /* Unicast / multicast response:
@@ -923,7 +937,7 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif)
    *  a) Unicast reply requested && recently multicasted 1/4ttl (RFC6762 section 5.4)
    *  b) Direct unicast query to port 5353 (RFC6762 section 5.5)
    *  c) Reply to Legacy DNS querier (RFC6762 section 6.7)
-   *  d) A probe message is received (RFC6762 section 6)
+   *  d) A probe message is received requesting unicast (RFC6762 section 6)
    */
 
 #if LWIP_IPV6
@@ -939,7 +953,7 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif)
   if (   (reply.unicast_reply_requested && listen_to_QU_bit)
       || pkt->recv_unicast
       || reply.legacy_query
-      || reply.probe_query_recv ) {
+      || (reply.probe_query_recv && reply.unicast_reply_requested)) {
     send_unicast = 1;
   }
   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: send response via %s\r\n", (send_unicast ? "unicast" : "multicast")));
@@ -984,9 +998,14 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif)
 #if LWIP_IPV6
       /* Add answers to IPv6 waiting list if:
        *  - it's a IPv6 incoming packet
-       *  - and the 1 second timeout is passed (RFC6762 section 6)
+       *  - the 1 second timeout is passed (RFC6762 section 6)
+       *  - and it's not a probe packet
+       * Or if:
+       *  - it's a IPv6 incoming packet
+       *  - and it's a probe packet
        */
-      if (IP_IS_V6_VAL(pkt->source_addr) && !mdns->ipv6.multicast_timeout) {
+      if (IP_IS_V6_VAL(pkt->source_addr) && !mdns->ipv6.multicast_timeout
+          && !reply.probe_query_recv) {
         LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: add answers to multicast IPv6 waiting list\r\n"));
 
         mdns_add_msg_to_delayed(&mdns->ipv6.delayed_msg_multicast, &reply);
@@ -994,13 +1013,25 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif)
         mdns_set_timeout(netif, MDNS_RESPONSE_DELAY, mdns_send_multicast_msg_delayed_ipv6,
                          &mdns->ipv6.multicast_msg_waiting);
       }
+      else if (IP_IS_V6_VAL(pkt->source_addr) && reply.probe_query_recv) {
+        LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: add answers to probe multicast IPv6 waiting list\r\n"));
+
+        mdns_add_msg_to_delayed(&mdns->ipv6.delayed_msg_multicast, &reply);
+
+        mdns->ipv6.multicast_msg_waiting = 1;
+      }
 #endif
 #if LWIP_IPV4
       /* Add answers to IPv4 waiting list if:
        *  - it's a IPv4 incoming packet
-       *  - and the 1 second timeout is passed (RFC6762 section 6)
+       *  - the 1 second timeout is passed (RFC6762 section 6)
+       *  - and it's not a probe packet
+       * Or if:
+       *  - it's a IPv4 incoming packet
+       *  - and it's a probe packet
        */
-      if (IP_IS_V4_VAL(pkt->source_addr) && !mdns->ipv4.multicast_timeout) {
+      if (IP_IS_V4_VAL(pkt->source_addr) && !mdns->ipv4.multicast_timeout
+          && !reply.probe_query_recv) {
         LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: add answers to multicast IPv4 waiting list\r\n"));
 
         mdns_add_msg_to_delayed(&mdns->ipv4.delayed_msg_multicast, &reply);
@@ -1008,6 +1039,13 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif)
         mdns_set_timeout(netif, MDNS_RESPONSE_DELAY, mdns_send_multicast_msg_delayed_ipv4,
                          &mdns->ipv4.multicast_msg_waiting);
       }
+      else if (IP_IS_V4_VAL(pkt->source_addr) && reply.probe_query_recv) {
+        LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: add answers to probe multicast IPv4 waiting list\r\n"));
+
+        mdns_add_msg_to_delayed(&mdns->ipv4.delayed_msg_multicast, &reply);
+
+        mdns->ipv4.multicast_msg_waiting = 1;
+      }
 #endif
     }
   }
@@ -1030,7 +1068,7 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif)
       /* Set IP/port to use when responding multicast */
 #if LWIP_IPV6
       if (IP_IS_V6_VAL(pkt->source_addr)) {
-        if (mdns->ipv6.multicast_timeout) {
+        if (mdns->ipv6.multicast_timeout && !reply.probe_query_recv) {
           LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: we just multicasted, ignore question\r\n"));
           return;
         }
@@ -1039,7 +1077,7 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif)
 #endif
 #if LWIP_IPV4
       if (IP_IS_V4_VAL(pkt->source_addr)) {
-        if (mdns->ipv4.multicast_timeout) {
+        if (mdns->ipv4.multicast_timeout && !reply.probe_query_recv) {
           LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: we just multicasted, ignore question\r\n"));
           return;
         }
@@ -1056,22 +1094,12 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif)
         LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Multicast answer send successfully\r\n"));
 #if LWIP_IPV6
         if (IP_IS_V6_VAL(pkt->source_addr)) {
-          mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT, mdns_multicast_timeout_reset_ipv6,
-                           &mdns->ipv6.multicast_timeout);
-          LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout started - IPv6\n"));
-          mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT_25TTL, mdns_multicast_timeout_25ttl_reset_ipv6,
-                           &mdns->ipv6.multicast_timeout_25TTL);
-          LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout 1/4 of ttl started - IPv6\n"));
+          mdns_start_multicast_timeouts_ipv6(netif);
         }
 #endif
 #if LWIP_IPV4
         if (IP_IS_V4_VAL(pkt->source_addr)) {
-          mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT, mdns_multicast_timeout_reset_ipv4,
-                           &mdns->ipv4.multicast_timeout);
-          LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout started - IPv4\n"));
-          mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT_25TTL, mdns_multicast_timeout_25ttl_reset_ipv4,
-                           &mdns->ipv4.multicast_timeout_25TTL);
-          LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout 1/4 of ttl started - IPv4\n"));
+          mdns_start_multicast_timeouts_ipv4(netif);
         }
 #endif
       }
@@ -1689,22 +1717,12 @@ mdns_resp_announce(struct netif *netif)
     /* Announce on IPv6 and IPv4 */
 #if LWIP_IPV6
     mdns_announce(netif, &v6group);
-    mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT, mdns_multicast_timeout_reset_ipv6,
-                     &mdns->ipv6.multicast_timeout);
-    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout started - IPv6\n"));
-    mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT_25TTL, mdns_multicast_timeout_25ttl_reset_ipv6,
-                     &mdns->ipv6.multicast_timeout_25TTL);
-    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout 1/4 of ttl started - IPv6\n"));
+    mdns_start_multicast_timeouts_ipv6(netif);
 #endif
 #if LWIP_IPV4
     if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
       mdns_announce(netif, &v4group);
-      mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT, mdns_multicast_timeout_reset_ipv4,
-                       &mdns->ipv4.multicast_timeout);
-      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout started - IPv4\n"));
-      mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT_25TTL, mdns_multicast_timeout_25ttl_reset_ipv4,
-                       &mdns->ipv4.multicast_timeout_25TTL);
-      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout 1/4 of ttl started - IPv4\n"));
+      mdns_start_multicast_timeouts_ipv4(netif);
     }
 #endif
   } /* else: ip address changed while probing was ongoing? @todo reset counter to restart? */
diff --git a/src/apps/mdns/mdns_out.c b/src/apps/mdns/mdns_out.c
index ac4d37ace..d13b3f913 100644
--- a/src/apps/mdns/mdns_out.c
+++ b/src/apps/mdns/mdns_out.c
@@ -775,6 +775,37 @@ mdns_multicast_timeout_reset_ipv4(void *arg)
   mdns->ipv4.multicast_timeout = 0;
 }
 
+/**
+ *  Called by timeouts when timer is passed, allows direct multicast IPv4 probe
+ *  response traffic again and sends out probe response if one was pending
+ *
+ *  @param arg  pointer to netif of timeout.
+ */
+void
+mdns_multicast_probe_timeout_reset_ipv4(void *arg)
+{
+  struct netif *netif = (struct netif*)arg;
+  struct mdns_host *mdns = netif_mdns_data(netif);
+  err_t res;
+
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast probe timeout finished - IPv4\n"));
+
+  mdns->ipv4.multicast_probe_timeout = 0;
+
+  if (mdns->ipv4.multicast_msg_waiting) {
+    res = mdns_send_outpacket(&mdns->ipv4.delayed_msg_multicast, netif);
+    if(res != ERR_OK) {
+      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Waiting probe multicast send failed - IPv4\n"));
+    }
+    else {
+      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Waiting probe multicast send successful - IPv4\n"));
+      mdns_clear_outmsg(&mdns->ipv4.delayed_msg_multicast);
+      mdns->ipv4.multicast_msg_waiting = 0;
+      mdns_start_multicast_timeouts_ipv4(netif);
+    }
+  }
+}
+
 /**
  *  Called by timeouts when timer is passed, allows to send an answer on a QU
  *  question via multicast.
@@ -812,12 +843,7 @@ mdns_send_multicast_msg_delayed_ipv4(void *arg)
     LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Delayed multicast send successful - IPv4\n"));
     mdns_clear_outmsg(&mdns->ipv4.delayed_msg_multicast);
     mdns->ipv4.multicast_msg_waiting = 0;
-    mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT, mdns_multicast_timeout_reset_ipv4,
-                     &mdns->ipv4.multicast_timeout);
-    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout started - IPv4\n"));
-    mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT_25TTL, mdns_multicast_timeout_25ttl_reset_ipv4,
-                     &mdns->ipv4.multicast_timeout_25TTL);
-    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout 1/4 of ttl started - IPv4\n"));
+    mdns_start_multicast_timeouts_ipv4(netif);
   }
 }
 
@@ -844,6 +870,29 @@ mdns_send_unicast_msg_delayed_ipv4(void *arg)
   }
 }
 
+/** Start all multicast timeouts for IPv4
+ *  Timeouts started:
+ *    - do not multicast within one second
+ *    - do not multicast a probe response within 250ms
+ *    - send a multicast answer on a QU question if not send recently.
+ *
+ *  @param netif network interface to start timeouts on
+ */
+void
+mdns_start_multicast_timeouts_ipv4(struct netif *netif)
+{
+  struct mdns_host *mdns = netif_mdns_data(netif);
+
+  mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT, mdns_multicast_timeout_reset_ipv4,
+                   &mdns->ipv4.multicast_timeout);
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout started - IPv4\n"));
+  mdns_set_timeout(netif, MDNS_MULTICAST_PROBE_TIMEOUT, mdns_multicast_probe_timeout_reset_ipv4,
+                   &mdns->ipv4.multicast_probe_timeout);
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast probe timeout started - IPv4\n"));
+  mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT_25TTL, mdns_multicast_timeout_25ttl_reset_ipv4,
+                   &mdns->ipv4.multicast_timeout_25TTL);
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout 1/4 of ttl started - IPv4\n"));
+}
 #endif
 
 #if LWIP_IPV6
@@ -863,6 +912,37 @@ mdns_multicast_timeout_reset_ipv6(void *arg)
   mdns->ipv6.multicast_timeout = 0;
 }
 
+/**
+ *  Called by timeouts when timer is passed, allows direct multicast IPv6 probe
+ *  response traffic again and sends out probe response if one was pending
+ *
+ *  @param arg  pointer to netif of timeout.
+ */
+void
+mdns_multicast_probe_timeout_reset_ipv6(void *arg)
+{
+  struct netif *netif = (struct netif*)arg;
+  struct mdns_host *mdns = netif_mdns_data(netif);
+  err_t res;
+
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast probe timeout finished - IPv6\n"));
+
+  mdns->ipv6.multicast_probe_timeout = 0;
+
+  if (mdns->ipv6.multicast_msg_waiting) {
+    res = mdns_send_outpacket(&mdns->ipv6.delayed_msg_multicast, netif);
+    if(res != ERR_OK) {
+      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Waiting probe multicast send failed - IPv6\n"));
+    }
+    else {
+      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Waiting probe multicast send successful - IPv6\n"));
+      mdns_clear_outmsg(&mdns->ipv6.delayed_msg_multicast);
+      mdns->ipv6.multicast_msg_waiting = 0;
+      mdns_start_multicast_timeouts_ipv6(netif);
+    }
+  }
+}
+
 /**
  *  Called by timeouts when timer is passed, allows to send an answer on a QU
  *  question via multicast.
@@ -900,12 +980,7 @@ mdns_send_multicast_msg_delayed_ipv6(void *arg)
     LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Delayed multicast send successful - IPv6\n"));
     mdns_clear_outmsg(&mdns->ipv6.delayed_msg_multicast);
     mdns->ipv6.multicast_msg_waiting = 0;
-    mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT, mdns_multicast_timeout_reset_ipv6,
-                     &mdns->ipv6.multicast_timeout);
-    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout started - IPv6\n"));
-    mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT_25TTL, mdns_multicast_timeout_25ttl_reset_ipv6,
-                     &mdns->ipv6.multicast_timeout_25TTL);
-    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout 1/4 of ttl started - IPv6\n"));
+    mdns_start_multicast_timeouts_ipv6(netif);
   }
 }
 
@@ -932,6 +1007,29 @@ mdns_send_unicast_msg_delayed_ipv6(void *arg)
   }
 }
 
+/** Start all multicast timeouts for IPv6
+ *  Timeouts started:
+ *    - do not multicast within one second
+ *    - do not multicast a probe response within 250ms
+ *    - send a multicast answer on a QU question if not send recently.
+ *
+ *  @param netif network interface to start timeouts on
+ */
+void
+mdns_start_multicast_timeouts_ipv6(struct netif *netif)
+{
+  struct mdns_host *mdns = netif_mdns_data(netif);
+
+  mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT, mdns_multicast_timeout_reset_ipv6,
+                   &mdns->ipv6.multicast_timeout);
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout started - IPv6\n"));
+  mdns_set_timeout(netif, MDNS_MULTICAST_PROBE_TIMEOUT, mdns_multicast_probe_timeout_reset_ipv6,
+                   &mdns->ipv6.multicast_probe_timeout);
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast probe timeout started - IPv6\n"));
+  mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT_25TTL, mdns_multicast_timeout_25ttl_reset_ipv6,
+                   &mdns->ipv6.multicast_timeout_25TTL);
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout 1/4 of ttl started - IPv6\n"));
+}
 #endif
 
 /**
diff --git a/src/include/lwip/apps/mdns_out.h b/src/include/lwip/apps/mdns_out.h
index f36eeeda8..83afab24f 100644
--- a/src/include/lwip/apps/mdns_out.h
+++ b/src/include/lwip/apps/mdns_out.h
@@ -83,6 +83,14 @@ extern "C" {
  */
 #define MDNS_MULTICAST_TIMEOUT    1000
 
+/* RFC6762 section 6:
+ * In this special case only, when responding via multicast to a probe, a
+ * Multicast DNS responder is only required to delay its transmission as
+ * necessary to ensure an interval of at least 250 ms since the last time the
+ * record was multicast on that interface.
+ */
+#define MDNS_MULTICAST_PROBE_TIMEOUT    250
+
 /* RFC6762 section 5.4:
  * When receiving a question with the unicast-response bit set, a responder
  * SHOULD usually respond with a unicast packet directed back to the querier.
@@ -100,15 +108,19 @@ void mdns_set_timeout(struct netif *netif, u32_t msecs,
                         sys_timeout_handler handler, u8_t *busy_flag);
 #if LWIP_IPV4
 void mdns_multicast_timeout_reset_ipv4(void *arg);
+void mdns_multicast_probe_timeout_reset_ipv4(void *arg);
 void mdns_multicast_timeout_25ttl_reset_ipv4(void *arg);
 void mdns_send_multicast_msg_delayed_ipv4(void *arg);
 void mdns_send_unicast_msg_delayed_ipv4(void *arg);
+void mdns_start_multicast_timeouts_ipv4(struct netif *netif);
 #endif
 #if LWIP_IPV6
 void mdns_multicast_timeout_reset_ipv6(void *arg);
+void mdns_multicast_probe_timeout_reset_ipv6(void *arg);
 void mdns_multicast_timeout_25ttl_reset_ipv6(void *arg);
 void mdns_send_multicast_msg_delayed_ipv6(void *arg);
 void mdns_send_unicast_msg_delayed_ipv6(void *arg);
+void mdns_start_multicast_timeouts_ipv6(struct netif *netif);
 #endif
 void mdns_prepare_txtdata(struct mdns_service *service);
 
diff --git a/src/include/lwip/apps/mdns_priv.h b/src/include/lwip/apps/mdns_priv.h
index 903f4cbee..df3a09736 100644
--- a/src/include/lwip/apps/mdns_priv.h
+++ b/src/include/lwip/apps/mdns_priv.h
@@ -145,10 +145,12 @@ struct mdns_outmsg {
 
 /** Delayed msg info */
 struct mdns_delayed_msg {
-  /** Timer state multicast */
+  /** Signals if a multicast msg needs to be send out */
   u8_t multicast_msg_waiting;
-  /** Multicast timeout on */
+  /** Multicast timeout for all multicast traffic except probe answers */
   u8_t multicast_timeout;
+  /** Multicast timeout only for probe answers */
+  u8_t multicast_probe_timeout;
   /** Output msg used for delayed multicast responses */
   struct mdns_outmsg delayed_msg_multicast;
   /** Prefer multicast over unicast timeout -> 25% of TTL = we take 30s as
-- 
2.19.1

