From 49e023883818524b392ca820931e7f7022fed00b Mon Sep 17 00:00:00 2001
From: Daniel Pauli <pauli@indel.ch>
Date: Wed, 15 Aug 2018 11:08:18 +0200
Subject: [PATCH 3/3] mdns: add API function mdns_resp_set_alias() that allows
 to set an alias to our hostname.

Queries for <alias>.local will be answered with the IP addresses of the netif and a CNAME resource record containing the actual hostname.
---
 src/apps/mdns/mdns.c              | 92 ++++++++++++++++++++++++++++++++++++++-
 src/apps/mdns/mdns_out.c          | 41 +++++++++++++++--
 src/include/lwip/apps/mdns.h      |  4 ++
 src/include/lwip/apps/mdns_opts.h |  7 +++
 src/include/lwip/apps/mdns_out.h  |  4 ++
 src/include/lwip/apps/mdns_priv.h |  4 ++
 6 files changed, 147 insertions(+), 5 deletions(-)

diff --git a/src/apps/mdns/mdns.c b/src/apps/mdns/mdns.c
index 26f8a27e..7bb2c3ee 100644
--- a/src/apps/mdns/mdns.c
+++ b/src/apps/mdns/mdns.c
@@ -242,6 +242,8 @@ check_host(struct netif *netif, struct mdns_rr_info *rr, u8_t *reverse_v6_reply)
   err_t res;
   int replies = 0;
   struct mdns_domain mydomain;
+  struct mdns_host *host = NETIF_TO_HOST(netif);
+  int match = 0;
 
   LWIP_UNUSED_ARG(reverse_v6_reply); /* if ipv6 is disabled */
 
@@ -277,8 +279,23 @@ check_host(struct netif *netif, struct mdns_rr_info *rr, u8_t *reverse_v6_reply)
 #endif
   }
 
+  /* Check for our hostname */
+  if (mdns_domain_eq(&rr->domain, &host->name)) {
+    match = 1;
+  }
+
+#if LWIP_MDNS_ALIASNAME
+  /* Check for alias to our hostname */
+  if (!match) {
+    if (mdns_domain_eq(&rr->domain, &host->alias)) {
+      match = 1;
+      replies |= REPLY_HOST_CNAME;
+    }
+  }
+#endif
+
   /* Handle requests for our hostname */
-  if (mdns_domain_eq(&rr->domain, &(NETIF_TO_HOST(netif))->name)) {
+  if (match) {
     /* TODO return NSEC if unsupported protocol requested */
 #if LWIP_IPV4
     if (!ip4_addr_isany_val(*netif_ip4_addr(netif))
@@ -537,6 +554,11 @@ mdns_announce(struct netif *netif, const ip_addr_t *destination)
     }
   }
 #endif
+#if LWIP_MDNS_ALIASNAME
+  if (mdns->alias.length) {
+    announce.host_replies |= REPLY_HOST_CNAME;
+  }
+#endif
 
   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
     struct mdns_service *serv = mdns->services[i];
@@ -1173,6 +1195,15 @@ mdns_parse_pkt_known_answers(struct netif *netif, struct mdns_packet *pkt,
           }
 #endif
         }
+      } else if (ans.info.type == DNS_RRTYPE_CNAME) {
+#if LWIP_MDNS_ALIASNAME
+        struct mdns_domain known_ans;
+        u16_t len;
+        len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
+        if (len != MDNS_READNAME_ERROR && mdns_domain_eq(&known_ans, &mdns->name)) {
+          reply->host_replies &= ~REPLY_HOST_CNAME;
+        }
+#endif
       } else if (match & REPLY_HOST_A) {
 #if LWIP_IPV4
         if (ans.rd_length == sizeof(ip4_addr_t) &&
@@ -1767,7 +1798,8 @@ mdns_conflict_save_time(struct netif *netif)
  *  - Let the user know there is a conflict.
  *
  * @param netif network interface on which the conflict occurred.
- * @param slot service index +1 on which the conflict occurred (0 indicate hostname conflict).
+ * @param slot service index +1 on which the conflict occurred (0 indicates
+ *        hostname conflict, -1 alias conflict).
  */
 static void
 mdns_probe_conflict(struct netif *netif, s8_t slot)
@@ -1931,6 +1963,14 @@ mdns_handle_response(struct mdns_packet *pkt, struct netif *netif)
         break;
       }
 
+#if LWIP_MDNS_ALIASNAME
+      if (mdns_domain_eq(&ans.info.domain, &mdns->alias)) {
+        LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches alias domain!\n"));
+        mdns_probe_conflict(netif, -1);
+        break;
+      }
+#endif
+
       for (i = 0; i < MDNS_MAX_SERVICES; i++) {
         struct mdns_service* service = mdns->services[i];
         if (!service) {
@@ -2244,6 +2284,11 @@ mdns_define_probe_rrs_to_send(struct netif *netif, struct mdns_outmsg *outmsg)
     }
   }
 #endif
+#if LWIP_MDNS_ALIASNAME
+  if (mdns->alias.length) {
+    outmsg->host_replies |= REPLY_HOST_CNAME;
+  }
+#endif
 
   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
     struct mdns_service *serv = mdns->services[i];
@@ -2489,6 +2534,49 @@ mdns_resp_netif_active(struct netif *netif)
 	return NETIF_TO_HOST(netif) != NULL;
 }
 
+#if LWIP_MDNS_ALIASNAME
+/**
+ * @ingroup mdns
+ * Set an alias for our hostname. This will result in the responder sending CNAME RRs.
+ * @param netif The network interface to set the alias.
+ * @param alias Alias to use. Queries for &lt;alias&gt;.local will be answered
+ *                 with the IP addresses of the netif and a CNAME resource record
+ *                 containing the actual hostname as specified in mdns_resp_add_netif().
+ *                 The hostname will be copied, the given pointer can be on the stack.
+ *                 NULL or empty string to remove the alias.
+ * @return ERR_OK if alias was set or removed, an err_t otherwise
+ */
+err_t
+mdns_resp_set_alias(struct netif *netif, const char *alias)
+{
+  struct mdns_host *mdns;
+
+  LWIP_ERROR("mdns_resp_set_alias: netif != NULL", (netif != NULL), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_set_alias: Alias too long", (!alias || strlen(alias) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+
+  mdns = NETIF_TO_HOST(netif);
+  LWIP_ERROR("mdns_resp_set_alias: Not an mdns netif", (mdns != NULL), return ERR_VAL);
+
+  struct mdns_domain alias_domain;
+  if (alias && *alias) {
+    err_t res = mdns_build_host_domain(&alias_domain, alias);
+    LWIP_ERROR("mdns_resp_set_alias: Failed to set alias name", (res == ERR_OK), return res);
+  }
+  else {
+	  alias_domain.length = 0;
+  }
+
+  LWIP_ERROR("mdns_resp_set_alias: alias must be different from hostname",
+    (!mdns_domain_eq(&alias_domain, &mdns->name)), return ERR_VAL);
+
+  mdns->alias = alias_domain;
+
+  mdns_resp_restart(netif);
+
+  return ERR_OK;
+}
+#endif
+
 /**
  * @ingroup mdns
  * Add a service to the selected network interface.
diff --git a/src/apps/mdns/mdns_out.c b/src/apps/mdns/mdns_out.c
index c6418819..7e356f9b 100644
--- a/src/apps/mdns/mdns_out.c
+++ b/src/apps/mdns/mdns_out.c
@@ -226,11 +226,11 @@ mdns_add_answer(struct mdns_outpacket *reply, struct mdns_domain *domain,
 /** Write an ANY host question to outpacket */
 static err_t
 mdns_add_any_host_question(struct mdns_outpacket *outpkt,
-                           struct mdns_host *mdns,
+                           struct mdns_domain *host,
                            u16_t request_unicast_reply)
 {
   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Adding host question for ANY type\n"));
-  return mdns_add_question(outpkt, &mdns->name, DNS_RRTYPE_ANY, DNS_RRCLASS_IN,
+  return mdns_add_question(outpkt, host, DNS_RRTYPE_ANY, DNS_RRCLASS_IN,
                            request_unicast_reply);
 }
 
@@ -321,6 +321,23 @@ mdns_add_hostv6_ptr_answer(struct mdns_outpacket *reply, struct mdns_outmsg *msg
 }
 #endif
 
+#if LWIP_MDNS_ALIASNAME
+/** Write a canonical (true) hostname (CNAME) RR to outpacket */
+static err_t
+mdns_add_cname_answer(struct mdns_outpacket *reply, struct mdns_outmsg *msg,
+		              struct netif *netif)
+{
+  err_t res;
+  u32_t ttl = MDNS_TTL_120;
+  if(msg->legacy_query) {
+    /* ttl of legacy answer may not be greater then 10 seconds */
+    ttl = MDNS_TTL_10;
+  }
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with CNAME record\n"));
+  return mdns_add_answer(reply, &netif_mdns_data(netif)->alias, DNS_RRTYPE_CNAME, DNS_RRCLASS_IN, msg->cache_flush, ttl, NULL, 0, &netif_mdns_data(netif)->name);
+}
+#endif
+
 /** Write an all-services -> servicetype PTR RR to outpacket */
 static err_t
 mdns_add_servicetype_ptr_answer(struct mdns_outpacket *reply, struct mdns_outmsg *msg,
@@ -419,11 +436,20 @@ mdns_add_questions_to_outpacket(struct mdns_outpacket *outpkt, struct mdns_outms
 
   /* Write host questions (probing) */
   if(msg->host_questions & QUESTION_PROBE_HOST_ANY) {
-    res = mdns_add_any_host_question(outpkt, mdns, 1);
+    res = mdns_add_any_host_question(outpkt, &mdns->name, 1);
     if (res != ERR_OK) {
       return res;
     }
     outpkt->questions++;
+#if LWIP_MDNS_ALIASNAME
+    if (mdns->alias.length) {
+      res = mdns_add_any_host_question(outpkt, &mdns->alias, 1);
+      if (res != ERR_OK) {
+        return res;
+      }
+      outpkt->questions++;
+    }
+#endif
   }
   /* Write service questions (probing) */
   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
@@ -549,6 +575,15 @@ mdns_create_outpacket(struct netif *netif, struct mdns_outmsg *msg,
     }
   }
 #endif
+#if LWIP_MDNS_ALIASNAME
+  if (msg->host_replies & REPLY_HOST_CNAME) {
+    res = mdns_add_cname_answer(outpkt, msg, netif);
+    if (res != ERR_OK) {
+      return res;
+    }
+    answers++;
+  }
+#endif
 
   /* Write answers to service questions */
   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
diff --git a/src/include/lwip/apps/mdns.h b/src/include/lwip/apps/mdns.h
index 33da4a3e..2128ca23 100644
--- a/src/include/lwip/apps/mdns.h
+++ b/src/include/lwip/apps/mdns.h
@@ -112,6 +112,10 @@ err_t mdns_resp_remove_netif(struct netif *netif);
 err_t mdns_resp_rename_netif(struct netif *netif, const char *hostname);
 int mdns_resp_netif_active(struct netif *netif);
 
+#if LWIP_MDNS_ALIASNAME
+err_t mdns_resp_set_alias(struct netif *netif, const char *alias);
+#endif
+
 s8_t  mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, service_get_txt_fn_t txt_fn, void *txt_userdata);
 err_t mdns_resp_del_service(struct netif *netif, u8_t slot);
 err_t mdns_resp_rename_service(struct netif *netif, u8_t slot, const char *name);
diff --git a/src/include/lwip/apps/mdns_opts.h b/src/include/lwip/apps/mdns_opts.h
index 1eee3e38..349282bd 100644
--- a/src/include/lwip/apps/mdns_opts.h
+++ b/src/include/lwip/apps/mdns_opts.h
@@ -108,6 +108,13 @@
 #endif
 
 /**
+ * LWIP_MDNS_ALIASNAME==1: Advertise a second name (hostname alias) by mDNS.
+ */
+#ifndef LWIP_MDNS_ALIASNAME
+#define LWIP_MDNS_ALIASNAME             0
+#endif
+
+/**
  * MDNS_DEBUG: Enable debugging for multicast DNS.
  */
 #ifndef MDNS_DEBUG
diff --git a/src/include/lwip/apps/mdns_out.h b/src/include/lwip/apps/mdns_out.h
index e6a7e388..2d44eaf2 100644
--- a/src/include/lwip/apps/mdns_out.h
+++ b/src/include/lwip/apps/mdns_out.h
@@ -64,6 +64,10 @@ extern "C" {
 #define REPLY_HOST_AAAA         0x04
 /* Lookup from hostname -> IPv6 */
 #define REPLY_HOST_PTR_V6       0x08
+#if LWIP_MDNS_ALIASNAME
+/* Lookup from alias -> hostname */
+#define REPLY_HOST_CNAME        0x10
+#endif
 
 /* Lookup for service types */
 #define REPLY_SERVICE_TYPE_PTR  0x10
diff --git a/src/include/lwip/apps/mdns_priv.h b/src/include/lwip/apps/mdns_priv.h
index 46ba53fe..0f12538c 100644
--- a/src/include/lwip/apps/mdns_priv.h
+++ b/src/include/lwip/apps/mdns_priv.h
@@ -210,6 +210,10 @@ typedef enum {
 struct mdns_host {
   /** Hostname */
   struct mdns_domain name;
+#if LWIP_MDNS_ALIASNAME
+  /** Alias to hostname */
+  struct mdns_domain alias;
+#endif
   /** Pointer to services */
   struct mdns_service *services[MDNS_MAX_SERVICES];
   /** Number of probes/announces sent for the current name */
-- 
2.11.0 (Apple Git-81)

