From 041fb1c7a85fb2f5180caeb1a81110aecbcc149b Mon Sep 17 00:00:00 2001
From: Daniel Pauli <pauli@indel.ch>
Date: Mon, 20 Aug 2018 13:11:28 +0200
Subject: [PATCH 1/3] mdns: support hostnames with subdomains (foo.bar)

Store hostname as mdns_domain instead of string. This change corrects the length limit for hostnames that contain subdomains (e.g. foo.bar) where each subdomain can be up to 63 bytes. Additionally, it increases performance by eliminating unnecessary string-to-domain conversions.

Co-authored-by: Christian Walther <walther@indel.ch>
---
 src/apps/mdns/mdns.c                | 35 +++++++++++++++--------------------
 src/apps/mdns/mdns_domain.c         | 11 ++++++-----
 src/apps/mdns/mdns_out.c            | 28 ++++++++++------------------
 src/include/lwip/apps/mdns_domain.h |  2 +-
 src/include/lwip/apps/mdns_priv.h   |  2 +-
 test/unit/mdns/test_mdns.c          | 29 +++++++++++++++++++++++++++++
 6 files changed, 62 insertions(+), 45 deletions(-)

diff --git a/src/apps/mdns/mdns.c b/src/apps/mdns/mdns.c
index 5eb79206..4be32f1c 100644
--- a/src/apps/mdns/mdns.c
+++ b/src/apps/mdns/mdns.c
@@ -283,9 +283,8 @@ check_host(struct netif *netif, struct mdns_rr_info *rr, u8_t *reverse_v6_reply)
 #endif
   }
 
-  res = mdns_build_host_domain(&mydomain, NETIF_TO_HOST(netif));
   /* Handle requests for our hostname */
-  if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+  if (mdns_domain_eq(&rr->domain, &(NETIF_TO_HOST(netif))->name)) {
     /* TODO return NSEC if unsupported protocol requested */
 #if LWIP_IPV4
     if (!ip4_addr_isany_val(*netif_ip4_addr(netif))
@@ -1158,11 +1157,10 @@ mdns_parse_pkt_known_answers(struct netif *netif, struct mdns_packet *pkt,
        */
       if (ans.info.type == DNS_RRTYPE_PTR) {
         /* Read domain and compare */
-        struct mdns_domain known_ans, my_ans;
+        struct mdns_domain known_ans;
         u16_t len;
         len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
-        res = mdns_build_host_domain(&my_ans, mdns);
-        if (len != MDNS_READNAME_ERROR && res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
+        if (len != MDNS_READNAME_ERROR && mdns_domain_eq(&known_ans, &mdns->name)) {
 #if LWIP_IPV4
           if (match & REPLY_HOST_PTR_V4) {
             LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v4 PTR\n"));
@@ -1237,7 +1235,7 @@ mdns_parse_pkt_known_answers(struct netif *netif, struct mdns_packet *pkt,
         } else if (match & REPLY_SERVICE_SRV) {
           /* Read and compare to my SRV record */
           u16_t field16, len, read_pos;
-          struct mdns_domain known_ans, my_ans;
+          struct mdns_domain known_ans;
           read_pos = ans.rd_offset;
           do {
             /* Check priority field */
@@ -1260,8 +1258,7 @@ mdns_parse_pkt_known_answers(struct netif *netif, struct mdns_packet *pkt,
             read_pos += len;
             /* Check host field */
             len = mdns_readname(pkt->pbuf, read_pos, &known_ans);
-            mdns_build_host_domain(&my_ans, mdns);
-            if (len == MDNS_READNAME_ERROR || !mdns_domain_eq(&known_ans, &my_ans)) {
+            if (len == MDNS_READNAME_ERROR || !mdns_domain_eq(&known_ans, &mdns->name)) {
               break;
             }
             LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: SRV\n"));
@@ -1929,9 +1926,8 @@ mdns_handle_response(struct mdns_packet *pkt, struct netif *netif)
       struct mdns_domain domain;
       u8_t i;
 
-      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!"));
+      if (mdns_domain_eq(&ans.info.domain, &mdns->name)) {
+        LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches host domain!\n"));
         mdns_probe_conflict(netif, 0);
         break;
       }
@@ -1943,7 +1939,7 @@ mdns_handle_response(struct mdns_packet *pkt, struct netif *netif)
         }
         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!"));
+          LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches service domain!\n"));
           mdns_probe_conflict(netif, i + 1);
           break;
         }
@@ -1966,8 +1962,7 @@ mdns_handle_response(struct mdns_packet *pkt, struct netif *netif)
       u8_t conflict = 0;
 
       /* Evaluate unique hostname records -> A and AAAA */
-      res = mdns_build_host_domain(&domain, mdns);
-      if (res == ERR_OK && mdns_domain_eq(&ans.info.domain, &domain)) {
+      if (mdns_domain_eq(&ans.info.domain, &mdns->name)) {
         LWIP_DEBUGF(MDNS_DEBUG, ("mDNS: response matches host domain, assuming conflict\n"));
         /* This means a conflict has taken place, except when the packet contains
          * exactly the same rdata. */
@@ -2011,7 +2006,7 @@ mdns_handle_response(struct mdns_packet *pkt, struct netif *netif)
           if (ans.info.type == DNS_RRTYPE_SRV) {
             /* Read and compare to with our SRV record */
             u16_t field16, len, read_pos;
-            struct mdns_domain srv_ans, my_ans;
+            struct mdns_domain srv_ans;
             read_pos = ans.rd_offset;
             do {
               /* Check priority field */
@@ -2034,8 +2029,7 @@ mdns_handle_response(struct mdns_packet *pkt, struct netif *netif)
               read_pos += len;
               /* Check host field */
               len = mdns_readname(pkt->pbuf, read_pos, &srv_ans);
-              mdns_build_host_domain(&my_ans, mdns);
-              if (len == MDNS_READNAME_ERROR || !mdns_domain_eq(&srv_ans, &my_ans)) {
+              if (len == MDNS_READNAME_ERROR || !mdns_domain_eq(&srv_ans, &mdns->name)) {
                 break;
               }
               LWIP_DEBUGF(MDNS_DEBUG, ("mDNS: response equals our own SRV record -> no conflict\n"));
@@ -2374,7 +2368,8 @@ mdns_resp_add_netif(struct netif *netif, const char *hostname)
 
   netif_set_client_data(netif, mdns_netif_client_id, mdns);
 
-  MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname)));
+  res = mdns_build_host_domain(&mdns->name, hostname);
+  LWIP_ERROR("mdns_resp_add_netif: Failed to set hostname", (res == ERR_OK), return res);
 
   /* Init delayed message structs with address and port */
 #if LWIP_IPV4
@@ -2475,8 +2470,8 @@ mdns_resp_rename_netif(struct netif *netif, const char *hostname)
   mdns = NETIF_TO_HOST(netif);
   LWIP_ERROR("mdns_resp_rename_netif: Not an mdns netif", (mdns != NULL), return ERR_VAL);
 
-  MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, len));
-  mdns->name[len] = '\0'; /* null termination in case new name is shorter than previous */
+  err_t res = mdns_build_host_domain(&mdns->name, hostname);
+  LWIP_ERROR("mdns_resp_rename_netif: Failed to set hostname", (res == ERR_OK), return res);
 
   mdns_resp_restart_delay(netif, MDNS_PROBE_DELAY_MS);
 
diff --git a/src/apps/mdns/mdns_domain.c b/src/apps/mdns/mdns_domain.c
index 265b5e6a..fe04e11d 100644
--- a/src/apps/mdns/mdns_domain.c
+++ b/src/apps/mdns/mdns_domain.c
@@ -432,18 +432,19 @@ mdns_add_dotlocal(struct mdns_domain *domain)
 /**
  * Build the \<hostname\>.local. domain name
  * @param domain Where to write the domain name
- * @param mdns TMDNS netif descriptor.
+ * @param name \<hostname\>, may consist of multiple '.'-separated labels
  * @return ERR_OK if domain \<hostname\>.local. was written, an err_t otherwise
  */
 err_t
-mdns_build_host_domain(struct mdns_domain *domain, struct mdns_host *mdns)
+mdns_build_host_domain(struct mdns_domain *domain, const char *name)
 {
   err_t res;
   LWIP_UNUSED_ARG(res);
   memset(domain, 0, sizeof(struct mdns_domain));
-  LWIP_ERROR("mdns_build_host_domain: mdns != NULL", (mdns != NULL), return ERR_VAL);
-  res = mdns_domain_add_label(domain, mdns->name, (u8_t)strlen(mdns->name));
-  LWIP_ERROR("mdns_build_host_domain: Failed to add label", (res == ERR_OK), return res);
+  /* an empty label is only allowed at the end of a domain */
+  LWIP_ERROR("mdns_build_host_domain: empty name", (name != NULL && *name != '\0'), return ERR_VAL);
+  res = mdns_domain_add_string(domain, name);
+  LWIP_ERROR("mdns_build_host_domain: Failed to add name", (res == ERR_OK), return res);
   return mdns_add_dotlocal(domain);
 }
 
diff --git a/src/apps/mdns/mdns_out.c b/src/apps/mdns/mdns_out.c
index 5c6d26b7..150ea8ea 100644
--- a/src/apps/mdns/mdns_out.c
+++ b/src/apps/mdns/mdns_out.c
@@ -229,10 +229,8 @@ mdns_add_any_host_question(struct mdns_outpacket *outpkt,
                            struct mdns_host *mdns,
                            u16_t request_unicast_reply)
 {
-  struct mdns_domain host;
-  mdns_build_host_domain(&host, mdns);
   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Adding host question for ANY type\n"));
-  return mdns_add_question(outpkt, &host, DNS_RRTYPE_ANY, DNS_RRCLASS_IN,
+  return mdns_add_question(outpkt, &mdns->name, DNS_RRTYPE_ANY, DNS_RRCLASS_IN,
                            request_unicast_reply);
 }
 
@@ -257,8 +255,6 @@ mdns_add_a_answer(struct mdns_outpacket *reply, struct mdns_outmsg *msg,
 {
   err_t res;
   u32_t ttl = MDNS_TTL_120;
-  struct mdns_domain host;
-  mdns_build_host_domain(&host, netif_mdns_data(netif));
   /* When answering to a legacy querier, we need to repeat the question and
    * limit the ttl to the short legacy ttl */
   if(msg->legacy_query) {
@@ -266,7 +262,7 @@ mdns_add_a_answer(struct mdns_outpacket *reply, struct mdns_outmsg *msg,
      * (max one question), not for the additional records. */
     if(reply->questions < 1) {
       LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Add question for legacy query\n"));
-      res = mdns_add_question(reply, &host, DNS_RRTYPE_A, DNS_RRCLASS_IN, 0);
+      res = mdns_add_question(reply, &netif_mdns_data(netif)->name, DNS_RRTYPE_A, DNS_RRCLASS_IN, 0);
       if (res != ERR_OK) {
         return res;
       }
@@ -276,7 +272,7 @@ mdns_add_a_answer(struct mdns_outpacket *reply, struct mdns_outmsg *msg,
     ttl = MDNS_TTL_10;
   }
   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with A record\n"));
-  return mdns_add_answer(reply, &host, DNS_RRTYPE_A, DNS_RRCLASS_IN, msg->cache_flush,
+  return mdns_add_answer(reply, &netif_mdns_data(netif)->name, DNS_RRTYPE_A, DNS_RRCLASS_IN, msg->cache_flush,
                          ttl, (const u8_t *) netif_ip4_addr(netif),
                          sizeof(ip4_addr_t), NULL);
 }
@@ -288,8 +284,7 @@ mdns_add_hostv4_ptr_answer(struct mdns_outpacket *reply, struct mdns_outmsg *msg
 {
   err_t res;
   u32_t ttl = MDNS_TTL_120;
-  struct mdns_domain host, revhost;
-  mdns_build_host_domain(&host, netif_mdns_data(netif));
+  struct mdns_domain revhost;
   mdns_build_reverse_v4_domain(&revhost, netif_ip4_addr(netif));
   /* When answering to a legacy querier, we need to repeat the question and
    * limit the ttl to the short legacy ttl */
@@ -309,7 +304,7 @@ mdns_add_hostv4_ptr_answer(struct mdns_outpacket *reply, struct mdns_outmsg *msg
   }
   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v4 PTR record\n"));
   return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN,
-                         msg->cache_flush, ttl, NULL, 0, &host);
+                         msg->cache_flush, ttl, NULL, 0, &netif_mdns_data(netif)->name);
 }
 #endif
 
@@ -321,8 +316,6 @@ mdns_add_aaaa_answer(struct mdns_outpacket *reply, struct mdns_outmsg *msg,
 {
   err_t res;
   u32_t ttl = MDNS_TTL_120;
-  struct mdns_domain host;
-  mdns_build_host_domain(&host, netif_mdns_data(netif));
   /* When answering to a legacy querier, we need to repeat the question and
    * limit the ttl to the short legacy ttl */
   if(msg->legacy_query) {
@@ -330,7 +323,7 @@ mdns_add_aaaa_answer(struct mdns_outpacket *reply, struct mdns_outmsg *msg,
      * (max one question), not for the additional records. */
     if(reply->questions < 1) {
       LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Add question for legacy query\n"));
-      res = mdns_add_question(reply, &host, DNS_RRTYPE_AAAA, DNS_RRCLASS_IN, 0);
+      res = mdns_add_question(reply, &netif_mdns_data(netif)->name, DNS_RRTYPE_AAAA, DNS_RRCLASS_IN, 0);
       if (res != ERR_OK) {
         return res;
       }
@@ -340,7 +333,7 @@ mdns_add_aaaa_answer(struct mdns_outpacket *reply, struct mdns_outmsg *msg,
     ttl = MDNS_TTL_10;
   }
   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with AAAA record\n"));
-  return mdns_add_answer(reply, &host, DNS_RRTYPE_AAAA, DNS_RRCLASS_IN, msg->cache_flush,
+  return mdns_add_answer(reply, &netif_mdns_data(netif)->name, DNS_RRTYPE_AAAA, DNS_RRCLASS_IN, msg->cache_flush,
                          ttl, (const u8_t *) netif_ip6_addr(netif, addrindex),
                          sizeof(ip6_addr_p_t), NULL);
 }
@@ -352,8 +345,7 @@ mdns_add_hostv6_ptr_answer(struct mdns_outpacket *reply, struct mdns_outmsg *msg
 {
   err_t res;
   u32_t ttl = MDNS_TTL_120;
-  struct mdns_domain host, revhost;
-  mdns_build_host_domain(&host, netif_mdns_data(netif));
+  struct mdns_domain revhost;
   mdns_build_reverse_v6_domain(&revhost, netif_ip6_addr(netif, addrindex));
   /* When answering to a legacy querier, we need to repeat the question and
    * limit the ttl to the short legacy ttl */
@@ -373,7 +365,7 @@ mdns_add_hostv6_ptr_answer(struct mdns_outpacket *reply, struct mdns_outmsg *msg
   }
   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v6 PTR record\n"));
   return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN,
-                         msg->cache_flush, ttl, NULL, 0, &host);
+                         msg->cache_flush, ttl, NULL, 0, &netif_mdns_data(netif)->name);
 }
 #endif
 
@@ -449,7 +441,7 @@ mdns_add_srv_answer(struct mdns_outpacket *reply, struct mdns_outmsg *msg,
   struct mdns_domain service_instance, srvhost;
   u16_t srvdata[3];
   mdns_build_service_domain(&service_instance, service, 1);
-  mdns_build_host_domain(&srvhost, mdns);
+  srvhost = mdns->name;
   if (msg->legacy_query) {
     /* RFC 6762 section 18.14:
      * In legacy unicast responses generated to answer legacy queries,
diff --git a/src/include/lwip/apps/mdns_domain.h b/src/include/lwip/apps/mdns_domain.h
index 9fa804e6..37ff141f 100644
--- a/src/include/lwip/apps/mdns_domain.h
+++ b/src/include/lwip/apps/mdns_domain.h
@@ -62,7 +62,7 @@ err_t mdns_build_reverse_v4_domain(struct mdns_domain *domain, const ip4_addr_t
 #if LWIP_IPV6
 err_t mdns_build_reverse_v6_domain(struct mdns_domain *domain, const ip6_addr_t *addr);
 #endif
-err_t mdns_build_host_domain(struct mdns_domain *domain, struct mdns_host *mdns);
+err_t mdns_build_host_domain(struct mdns_domain *domain, const char *name);
 err_t mdns_build_dnssd_domain(struct mdns_domain *domain);
 err_t mdns_build_service_domain(struct mdns_domain *domain, struct mdns_service *service, int include_name);
 #if LWIP_MDNS_SEARCH
diff --git a/src/include/lwip/apps/mdns_priv.h b/src/include/lwip/apps/mdns_priv.h
index 5209ba00..6a9d2f0e 100644
--- a/src/include/lwip/apps/mdns_priv.h
+++ b/src/include/lwip/apps/mdns_priv.h
@@ -200,7 +200,7 @@ typedef enum {
 /** Description of a host/netif */
 struct mdns_host {
   /** Hostname */
-  char name[MDNS_LABEL_MAXLEN + 1];
+  struct mdns_domain name;
   /** Pointer to services */
   struct mdns_service *services[MDNS_MAX_SERVICES];
   /** Number of probes/announces sent for the current name */
diff --git a/test/unit/mdns/test_mdns.c b/test/unit/mdns/test_mdns.c
index 01434b5d..a5b1de9d 100644
--- a/test/unit/mdns/test_mdns.c
+++ b/test/unit/mdns/test_mdns.c
@@ -452,6 +452,33 @@ START_TEST(add_label_full)
 }
 END_TEST
 
+START_TEST(build_host_domain_basic)
+{
+  static const u8_t data[] = { 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00 };
+  struct mdns_domain domain;
+  err_t res;
+  LWIP_UNUSED_ARG(_i);
+
+  memset(&domain, 0, sizeof(domain));
+  res = mdns_build_host_domain(&domain, "multi.cast");
+  fail_unless(res == ERR_OK);
+  fail_unless(domain.length == sizeof(data));
+  fail_if(memcmp(&domain.name, data, sizeof(data)));
+}
+END_TEST
+
+START_TEST(build_host_domain_empty)
+{
+  struct mdns_domain domain;
+  err_t res;
+  LWIP_UNUSED_ARG(_i);
+
+  memset(&domain, 0, sizeof(domain));
+  res = mdns_build_host_domain(&domain, "");
+  fail_unless(res == ERR_VAL);
+}
+END_TEST
+
 START_TEST(domain_eq_basic)
 {
   static const u8_t data[] = {
@@ -896,6 +923,8 @@ Suite* mdns_suite(void)
     TESTFUNC(add_label_basic),
     TESTFUNC(add_label_long_label),
     TESTFUNC(add_label_full),
+    TESTFUNC(build_host_domain_basic),
+    TESTFUNC(build_host_domain_empty),
 
     TESTFUNC(domain_eq_basic),
     TESTFUNC(domain_eq_diff),
-- 
2.11.0 (Apple Git-81)

