From ef58628ea719716b6048fe9ac51cf6f9f23374e3 Mon Sep 17 00:00:00 2001
From: Jens Nielsen <deft@telia.com>
Date: Tue, 16 Jan 2018 22:47:00 +0100
Subject: [PATCH] MDNS send probes to verify domain before use

---
 doc/mdns.txt                 |  13 +-
 src/apps/mdns/mdns.c         | 347 +++++++++++++++++++++++++++++++++++++++----
 src/include/lwip/apps/mdns.h |  11 ++
 3 files changed, 331 insertions(+), 40 deletions(-)

diff --git a/doc/mdns.txt b/doc/mdns.txt
index c5929b76..0c28970d 100644
--- a/doc/mdns.txt
+++ b/doc/mdns.txt
@@ -57,11 +57,11 @@ Answers will use the supplied TTL (in seconds)
 MDNS allows UTF-8 names, but it is recommended to stay within ASCII,
 since the default case-insensitive comparison assumes this.
 
-It is recommended to call this function after an IPv4 address has been set,
-since there is currently no check if the v4 address is valid.
+Call mdns_resp_announce() every time the IP address on the netif has changed.
 
-Call mdns_resp_netif_settings_changed() every time the IP address
-on the netif has changed.
+Call mdns_resp_restart() every time the network interface comes up after being
+down, for example cable connected after being disconnected, administrative 
+interface comes up after being down, or the device wakes up from sleep.
 
 To stop responding on a netif, run
   mdns_resp_remove_netif(struct netif *netif)
@@ -108,6 +108,5 @@ and point them to <hostname>.local:80
 Relevant information will be sent as additional records to reduce number of
 requests required from a client.
 
-Removing services is currently not supported. Services are removed when the
-netif is removed.
-
+To remove a service from a netif, run
+  mdns_resp_del_service(struct netif *netif, s8_t slot)
\ No newline at end of file
diff --git a/src/apps/mdns/mdns.c b/src/apps/mdns/mdns.c
index 12e078dc..8597103b 100644
--- a/src/apps/mdns/mdns.c
+++ b/src/apps/mdns/mdns.c
@@ -13,7 +13,7 @@
  * Things left to implement:
  * -------------------------
  *
- * - Probing/conflict resolution
+ * - Tiebreaking for simultaneous probing
  * - Sending goodbye messages (zero ttl) - shutdown, DHCP lease about to expire, DHCP turned off...
  * - Checking that source address of unicast requests are on the same network
  * - Limiting multicast responses to 1 per second per resource record
@@ -63,6 +63,7 @@
 #include "lwip/mem.h"
 #include "lwip/prot/dns.h"
 #include "lwip/prot/iana.h"
+#include "lwip/timeouts.h"
 
 #include <string.h>
 
@@ -102,6 +103,7 @@ static const ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT;
 static u8_t mdns_netif_client_id;
 static struct udp_pcb *mdns_pcb;
 NETIF_DECLARE_EXT_CALLBACK(netif_callback)
+static mdns_name_result_cb_t mdns_name_result_cb = NULL;
 
 #define NETIF_TO_HOST(netif) (struct mdns_host*)(netif_get_client_data(netif, mdns_netif_client_id))
 
@@ -135,6 +137,12 @@ NETIF_DECLARE_EXT_CALLBACK(netif_callback)
 /* Lookup for text info on service instance */
 #define REPLY_SERVICE_TXT       0x80
 
+typedef enum {
+  MDNS_PROBING_NOT_STARTED,
+  MDNS_PROBING_ONGOING,
+  MDNS_PROBING_COMPLETE,
+} mdns_probing_state;
+
 static const char *dnssd_protos[] = {
   "_udp", /* DNSSD_PROTO_UDP */
   "_tcp", /* DNSSD_PROTO_TCP */
@@ -168,6 +176,10 @@ struct mdns_host {
   struct mdns_service *services[MDNS_MAX_SERVICES];
   /** TTL in seconds of A/AAAA/PTR replies */
   u32_t dns_ttl;
+  /** Number of probes sent for the current name */
+  u8_t probes_sent;
+  /** State in probing sequence */
+  mdns_probing_state probing_state;
 };
 
 /** Information about received packet */
@@ -191,7 +203,7 @@ struct mdns_packet {
   /** Number of unparsed questions */
   u16_t questions_left;
   /** Number of answers in packet,
-   *  (sum of normal, authorative and additional answers)
+   *  (sum of normal, authoritative and additional answers)
    *  read from packet header */
   u16_t answers;
   /** Number of unparsed answers */
@@ -215,6 +227,8 @@ struct mdns_outpacket {
   u16_t questions;
   /** Number of normal answers written */
   u16_t answers;
+  /** Number of authoritative answers written */
+  u16_t authoritative;
   /** Number of additional answers written */
   u16_t additional;
   /** Offsets for written domain names in packet.
@@ -261,6 +275,9 @@ struct mdns_answer {
   u16_t rd_offset;
 };
 
+static err_t mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags);
+static void mdns_probe(void* arg);
+
 static err_t
 mdns_domain_add_label_base(struct mdns_domain *domain, u8_t len)
 {
@@ -1279,13 +1296,14 @@ mdns_init_outpacket(struct mdns_outpacket *out, struct mdns_packet *in)
  * Add additional answers based on the selected answers
  * Send the packet
  */
-static void
-mdns_send_outpacket(struct mdns_outpacket *outpkt)
+static err_t
+mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags)
 {
   struct mdns_service *service;
-  err_t res;
+  err_t res = ERR_ARG;
   int i;
   struct mdns_host *mdns = NETIF_TO_HOST(outpkt->netif);
+  u16_t answers = 0;
 
   /* Write answers to host questions */
 #if LWIP_IPV4
@@ -1294,14 +1312,14 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
     if (res != ERR_OK) {
       goto cleanup;
     }
-    outpkt->answers++;
+    answers++;
   }
   if (outpkt->host_replies & REPLY_HOST_PTR_V4) {
     res = mdns_add_hostv4_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif);
     if (res != ERR_OK) {
       goto cleanup;
     }
-    outpkt->answers++;
+    answers++;
   }
 #endif
 #if LWIP_IPV6
@@ -1313,7 +1331,7 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
         if (res != ERR_OK) {
           goto cleanup;
         }
-        outpkt->answers++;
+        answers++;
       }
     }
   }
@@ -1326,7 +1344,7 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
         if (res != ERR_OK) {
           goto cleanup;
         }
-        outpkt->answers++;
+        answers++;
       }
       addrindex++;
       rev_addrs >>= 1;
@@ -1346,7 +1364,7 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
       if (res != ERR_OK) {
         goto cleanup;
       }
-      outpkt->answers++;
+      answers++;
     }
 
     if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
@@ -1354,7 +1372,7 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
       if (res != ERR_OK) {
         goto cleanup;
       }
-      outpkt->answers++;
+      answers++;
     }
 
     if (outpkt->serv_replies[i] & REPLY_SERVICE_SRV) {
@@ -1362,7 +1380,7 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
       if (res != ERR_OK) {
         goto cleanup;
       }
-      outpkt->answers++;
+      answers++;
     }
 
     if (outpkt->serv_replies[i] & REPLY_SERVICE_TXT) {
@@ -1370,10 +1388,17 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
       if (res != ERR_OK) {
         goto cleanup;
       }
-      outpkt->answers++;
+      answers++;
     }
   }
 
+  /* if this is a response, the data above is anwers, else this is a probe and the answers above goes into auth section */
+  if ( flags & DNS_FLAG1_RESPONSE ) {
+    outpkt->answers += answers;
+  } else {
+    outpkt->authoritative += answers;
+  }
+
   /* All answers written, add additional RRs */
   for (i = 0; i < MDNS_MAX_SERVICES; ++i) {
     service = mdns->services[i];
@@ -1439,13 +1464,12 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
 
     /* Write header */
     memset(&hdr, 0, sizeof(hdr));
-    hdr.flags1 = DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE;
+    hdr.flags1 = flags;
+    hdr.numquestions = lwip_htons(outpkt->questions);
     hdr.numanswers = lwip_htons(outpkt->answers);
+    hdr.numauthrr = lwip_htons(outpkt->authoritative);
     hdr.numextrarr = lwip_htons(outpkt->additional);
-    if (outpkt->legacy_query) {
-      hdr.numquestions = lwip_htons(1);
-      hdr.id = lwip_htons(outpkt->tx_id);
-    }
+    hdr.id = lwip_htons(outpkt->tx_id);
     pbuf_take(outpkt->pbuf, &hdr, sizeof(hdr));
 
     /* Shrink packet */
@@ -1463,9 +1487,9 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
     /* 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) {
-      udp_sendto_if(mdns_pcb, outpkt->pbuf, &outpkt->dest_addr, outpkt->dest_port, outpkt->netif);
+      res = udp_sendto_if(mdns_pcb, outpkt->pbuf, &outpkt->dest_addr, outpkt->dest_port, outpkt->netif);
     } else {
-      udp_sendto_if(mdns_pcb, outpkt->pbuf, mcast_destaddr, LWIP_IANA_PORT_MDNS, outpkt->netif);
+      res = udp_sendto_if(mdns_pcb, outpkt->pbuf, mcast_destaddr, LWIP_IANA_PORT_MDNS, outpkt->netif);
     }
   }
 
@@ -1474,8 +1498,10 @@ cleanup:
     pbuf_free(outpkt->pbuf);
     outpkt->pbuf = NULL;
   }
+  return res;
 }
 
+
 /**
  * Send unsolicited answer containing all our known data
  * @param netif The network interface to send on
@@ -1515,7 +1541,7 @@ mdns_announce(struct netif *netif, const ip_addr_t *destination)
 
   announce.dest_port = LWIP_IANA_PORT_MDNS;
   SMEMCPY(&announce.dest_addr, destination, sizeof(announce.dest_addr));
-  mdns_send_outpacket(&announce);
+  mdns_send_outpacket(&announce, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE);
 }
 
 /**
@@ -1534,6 +1560,12 @@ mdns_handle_question(struct mdns_packet *pkt)
   err_t res;
   struct mdns_host *mdns = NETIF_TO_HOST(pkt->netif);
 
+  if (mdns->probing_state != MDNS_PROBING_COMPLETE) {
+    /* Don't answer questions until we've verified our domains via probing */
+    /* todo we should check incoming questions during probing for tiebreaking */
+    return;
+  }
+
   mdns_init_outpacket(&reply, pkt);
 
   while (pkt->questions_left) {
@@ -1569,6 +1601,7 @@ mdns_handle_question(struct mdns_packet *pkt)
     if (replies && reply.legacy_query) {
       /* Add question to reply packet (legacy packet only has 1 question) */
       res = mdns_add_question(&reply, &q.info.domain, q.info.type, q.info.klass, 0);
+      reply.questions = 1;
       if (res != ERR_OK) {
         goto cleanup;
       }
@@ -1724,7 +1757,7 @@ mdns_handle_question(struct mdns_packet *pkt)
     }
   }
 
-  mdns_send_outpacket(&reply);
+  mdns_send_outpacket(&reply, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE);
 
 cleanup:
   if (reply.pbuf) {
@@ -1741,6 +1774,8 @@ cleanup:
 static void
 mdns_handle_response(struct mdns_packet *pkt)
 {
+  struct mdns_host* mdns = NETIF_TO_HOST(pkt->netif);
+
   /* Ignore all questions */
   while (pkt->questions_left) {
     struct mdns_question q;
@@ -1766,6 +1801,39 @@ mdns_handle_response(struct mdns_packet *pkt)
     LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Answer for domain "));
     mdns_domain_debug_print(&ans.info.domain);
     LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
+
+    /*"Apparently conflicting Multicast DNS responses received *before* the first probe packet is sent MUST
+      be silently ignored" so drop answer if we haven't started probing yet*/
+    if (mdns->probing_state == MDNS_PROBING_ONGOING && mdns->probes_sent > 0) {
+      struct mdns_domain domain;
+      u8_t i;
+      u8_t conflict = 0;
+
+      res = mdns_build_host_domain(&domain, mdns);
+      if (res == ERR_OK && mdns_domain_eq(&ans.info.domain, &domain)) {
+        LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches host domain!"));
+        conflict = 1;
+      }
+
+      for (i = 0; i < MDNS_MAX_SERVICES; ++i) {
+        struct mdns_service* service = mdns->services[i];
+        if (!service) {
+          continue;
+        }
+        res = mdns_build_service_domain(&domain, service, 1);
+        if (res == ERR_OK && mdns_domain_eq(&ans.info.domain, &domain)) {
+          LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches service domain!"));
+          conflict = 1;
+        }
+      }
+
+      if (conflict != 0) {
+        sys_untimeout(mdns_probe, pkt->netif);
+        if (mdns_name_result_cb != NULL) {
+          mdns_name_result_cb(pkt->netif, 0);
+        }
+      }
+    }
   }
 }
 
@@ -1851,13 +1919,13 @@ mdns_netif_ext_status_callback(struct netif *netif, netif_nsc_reason_t reason, c
 
   if (reason & LWIP_NSC_STATUS_CHANGED) {
     if (args->status_changed.state != 0) {
-      mdns_resp_announce(netif);
+      mdns_resp_restart(netif);
     }
     /* TODO: send goodbye message */
   }
   if (reason & LWIP_NSC_LINK_CHANGED) {
     if (args->link_changed.state != 0) {
-      mdns_resp_announce(netif);
+      mdns_resp_restart(netif);
     }
   }
   if (reason & (LWIP_NSC_IPV4_ADDRESS_CHANGED | LWIP_NSC_IPV4_GATEWAY_CHANGED |
@@ -1868,10 +1936,111 @@ mdns_netif_ext_status_callback(struct netif *netif, netif_nsc_reason_t reason, c
 }
 #endif
 
+static err_t
+mdns_send_probe(struct netif* netif, const ip_addr_t *destination)
+{
+  struct mdns_host* mdns;
+  struct mdns_outpacket pkt;
+  struct mdns_domain domain;
+  u8_t i;
+  err_t res;
+
+  mdns = NETIF_TO_HOST(netif);
+
+  memset(&pkt, 0, sizeof(pkt));
+  pkt.netif = netif;
+
+  /* Add unicast questions with rtype ANY for all our desired records */
+  mdns_build_host_domain(&domain, mdns);
+  res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
+  if (res != ERR_OK) {
+    goto cleanup;
+  }
+  pkt.questions++;
+  for (i = 0; i < MDNS_MAX_SERVICES; ++i) {
+    struct mdns_service* service = mdns->services[i];
+    if (!service) {
+      continue;
+    }
+    mdns_build_service_domain(&domain, service, 1);
+    res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
+    if (res != ERR_OK) {
+      goto cleanup;
+    }
+    pkt.questions++;
+  }
+
+  /* Add answers to the questions above into the authority section for tiebreaking */
+#if LWIP_IPV4
+  if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+    pkt.host_replies = REPLY_HOST_A;
+  }
+#endif
+#if LWIP_IPV6
+  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+    if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+      pkt.host_replies |= REPLY_HOST_AAAA;
+    }
+  }
+#endif
+
+  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+    struct mdns_service *serv = mdns->services[i];
+    if (serv) {
+      pkt.serv_replies[i] = REPLY_SERVICE_SRV | REPLY_SERVICE_TXT;
+    }
+  }
+
+  pkt.tx_id = 0;
+  pkt.dest_port = LWIP_IANA_PORT_MDNS;
+  SMEMCPY(&pkt.dest_addr, destination, sizeof(pkt.dest_addr));
+  res = mdns_send_outpacket(&pkt, 0);
+
+cleanup:
+  if (pkt.pbuf) {
+    pbuf_free(pkt.pbuf);
+    pkt.pbuf = NULL;
+  }
+  return res;
+}
+
+/**
+ * Timer callback for probing network.
+ */
+static void
+mdns_probe(void* arg)
+{
+  struct netif *netif = (struct netif *)arg;
+  struct mdns_host* mdns = NETIF_TO_HOST(netif);
+
+  if(mdns->probes_sent >= 3) {
+    /* probing successful, announce the new name */
+    mdns->probing_state = MDNS_PROBING_COMPLETE;
+    mdns_resp_announce(netif);
+    if (mdns_name_result_cb != NULL) {
+      mdns_name_result_cb(netif, 1);
+    }
+  } else {
+#if LWIP_IPV4
+    /*if ipv4 wait with probing until address is set*/
+    if (!ip4_addr_isany_val(*netif_ip4_addr(netif)) &&
+        mdns_send_probe(netif, IP4_ADDR_ANY) == ERR_OK)
+#endif
+    {
+#if LWIP_IPV6
+      if (mdns_send_probe(netif, IP6_ADDR_ANY) == ERR_OK)
+#endif
+      {
+        mdns->probes_sent++;
+      }
+    }
+    sys_timeout(250, mdns_probe, netif);
+  }
+}
+
 /**
  * @ingroup mdns
- * Activate MDNS responder for a network interface and send announce packets.
- * Don't forget to call mdns_resp_announce() after you added all netifs and services.
+ * Activate MDNS responder for a network interface.
  * @param netif The network interface to activate.
  * @param hostname Name to use. Queries for &lt;hostname&gt;.local will be answered
  *                 with the IP addresses of the netif. The hostname will be copied, the
@@ -1897,6 +2066,8 @@ mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl)
 
   MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname)));
   mdns->dns_ttl = dns_ttl;
+  mdns->probes_sent = 0;
+  mdns->probing_state = MDNS_PROBING_NOT_STARTED;
 
   /* Join multicast groups */
 #if LWIP_IPV4
@@ -1912,6 +2083,8 @@ mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl)
   }
 #endif
 
+  mdns_resp_restart(netif);
+
   return ERR_OK;
 
 cleanup:
@@ -1938,6 +2111,10 @@ mdns_resp_remove_netif(struct netif *netif)
   mdns = NETIF_TO_HOST(netif);
   LWIP_ERROR("mdns_resp_remove_netif: Not an active netif", (mdns != NULL), return ERR_VAL);
 
+  if (mdns->probing_state == MDNS_PROBING_ONGOING) {
+    sys_untimeout(mdns_probe, netif);
+  }
+
   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
     struct mdns_service *service = mdns->services[i];
     if (service) {
@@ -1960,8 +2137,37 @@ mdns_resp_remove_netif(struct netif *netif)
 
 /**
  * @ingroup mdns
+ * Update MDNS hostname for a network interface.
+ * @param netif The network interface to activate.
+ * @param hostname Name to use. Queries for &lt;hostname&gt;.local will be answered
+ *                 with the IP addresses of the netif. The hostname will be copied, the
+ *                 given pointer can be on the stack.
+ * @return ERR_OK if name could be set on netif, an err_t otherwise
+ */
+err_t
+mdns_resp_rename_netif(struct netif *netif, const char *hostname)
+{
+  struct mdns_host *mdns;
+  u8_t len;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  len = strlen(hostname);
+  LWIP_ERROR("mdns_resp_rename_netif: netif != NULL", (netif != NULL), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_rename_netif: Hostname too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+  mdns = NETIF_TO_HOST(netif);
+  LWIP_ERROR("mdns_resp_rename_netif: Not an mdns netif", (mdns != NULL), return ERR_VAL);
+
+  MEMCPY(&mdns->name, hostname, len);
+  mdns->name[len] = '\0'; /* null termination in case new name is shorter than previous */
+
+  mdns_resp_restart(netif);
+
+  return ERR_OK;
+}
+
+/**
+ * @ingroup mdns
  * Add a service to the selected network interface.
- * Don't forget to call mdns_resp_announce() after you added all services.
  * @param netif The network interface to publish this service on
  * @param name The name of the service
  * @param service The service type, like "_http"
@@ -2012,6 +2218,8 @@ mdns_resp_add_service(struct netif *netif, const char *name, const char *service
 
   mdns->services[slot] = srv;
 
+  mdns_resp_restart(netif);
+
   return slot;
 }
 
@@ -2041,6 +2249,40 @@ mdns_resp_del_service(struct netif *netif, s8_t slot)
 
 /**
  * @ingroup mdns
+ * Update name for an MDNS service.
+ * @param netif The network interface to activate.
+ * @param slot The service slot number returned by mdns_resp_add_service
+ * @param name The new name for the service
+ * @return ERR_OK if name could be set on service, an err_t otherwise
+ */
+err_t
+mdns_resp_rename_service(struct netif *netif, s8_t slot, const char *name)
+{
+  struct mdns_service *srv;
+  struct mdns_host *mdns;
+  u8_t len;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  len = strlen(name);
+  LWIP_ASSERT("mdns_resp_rename_service: netif != NULL", netif);
+  mdns = NETIF_TO_HOST(netif);
+  LWIP_ERROR("mdns_resp_rename_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_rename_service: Name too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL);
+
+  srv = mdns->services[slot];
+
+  MEMCPY(&srv->name, name, len);
+  srv->name[len] = '\0'; /* null termination in case new name is shorter than previous */
+
+  mdns_resp_restart(netif);
+
+  return ERR_OK;
+}
+
+/**
+ * @ingroup mdns
  * Call this function from inside the service_get_txt_fn_t callback to add text data.
  * Buffer for TXT data is 256 bytes, and each field is prefixed with a length byte.
  * @param service The service provided to the get_txt callback
@@ -2066,22 +2308,61 @@ mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_
 void
 mdns_resp_announce(struct netif *netif)
 {
+  struct mdns_host* mdns;
   LWIP_ASSERT_CORE_LOCKED();
   LWIP_ERROR("mdns_resp_announce: netif != NULL", (netif != NULL), return);
 
-  if (NETIF_TO_HOST(netif) == NULL) {
+  mdns = NETIF_TO_HOST(netif);
+  if (mdns == NULL) {
     return;
   }
 
-  /* Announce on IPv6 and IPv4 */
+  if (mdns->probing_state == MDNS_PROBING_COMPLETE)
+  {
+    /* Announce on IPv6 and IPv4 */
 #if LWIP_IPV6
-  mdns_announce(netif, IP6_ADDR_ANY);
+    mdns_announce(netif, IP6_ADDR_ANY);
 #endif
 #if LWIP_IPV4
-  if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
-    mdns_announce(netif, IP4_ADDR_ANY);
-  }
+    if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+      mdns_announce(netif, IP4_ADDR_ANY);
+    }
 #endif
+  } /* else: ip address changed while probing was ongoing? todo reset counter to restart? */
+}
+
+void
+mdns_resp_register_name_result_cb(mdns_name_result_cb_t cb)
+{
+  mdns_name_result_cb = cb;
+}
+
+/**
+ * @ingroup mdns
+ * Restart mdns responder. Call this when cable is connected after being disconnected or
+ * administrative interface is set up after being down
+ * @param netif The network interface to send on
+ */
+void
+mdns_resp_restart(struct netif *netif)
+{
+  struct mdns_host* mdns;
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ERROR("mdns_resp_restart: netif != NULL", (netif != NULL), return);
+
+  mdns = NETIF_TO_HOST(netif);
+  if (mdns == NULL) {
+    return;
+  }
+
+  if (mdns->probing_state == MDNS_PROBING_ONGOING) {
+    sys_untimeout(mdns_probe, netif);
+  }
+  /*todo first probe timeout SHOULD be random 0-250 ms*/
+  /*todo if we've failed 15 times within a 10 second period we MUST wait 5 seconds (or wait 5 seconds every time except first)*/
+  mdns->probes_sent = 0;
+  mdns->probing_state = MDNS_PROBING_ONGOING;
+  sys_timeout(250, mdns_probe, netif);
 }
 
 /**
diff --git a/src/include/lwip/apps/mdns.h b/src/include/lwip/apps/mdns.h
index 4318eb3b..b36b81f3 100644
--- a/src/include/lwip/apps/mdns.h
+++ b/src/include/lwip/apps/mdns.h
@@ -56,16 +56,27 @@ struct mdns_service;
 /** Callback function to add text to a reply, called when generating the reply */
 typedef void (*service_get_txt_fn_t)(struct mdns_service *service, void *txt_userdata);
 
+/** Callback function to let application know the result of probing network for name
+ *  uniqueness, called with result 1 if no other node claimed use for the name for the
+ *  netif or a service and is safe to use, or 0 if another node is already using it and
+ *  mdns is disabled on this interface */
+typedef void (*mdns_name_result_cb_t)(struct netif* netif, u8_t result);
+
 void mdns_resp_init(void);
 
+void mdns_resp_register_name_result_cb(mdns_name_result_cb_t cb);
+
 err_t mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl);
 err_t mdns_resp_remove_netif(struct netif *netif);
+err_t mdns_resp_rename_netif(struct netif *netif, const char *hostname);
 
 s8_t  mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, u32_t dns_ttl, service_get_txt_fn_t txt_fn, void *txt_userdata);
 err_t mdns_resp_del_service(struct netif *netif, s8_t slot);
+err_t mdns_resp_rename_service(struct netif *netif, s8_t slot, const char *name);
 
 err_t mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len);
 
+void mdns_resp_restart(struct netif *netif);
 void mdns_resp_announce(struct netif *netif);
 
 /**
-- 
2.12.2.windows.2

