From b99e3f6df082112977dc1e5969c3cd3a714b683d Mon Sep 17 00:00:00 2001
From: Ashley Duncan <ashley.duncan@evnex.com>
Date: Mon, 26 Oct 2020 15:00:37 +0100
Subject: [PATCH] Introduce LWIP_DNS_SERVERS_PER_NETIF option

When LWIP_DNS_SERVERS_PER_NETIF is 1, each netif has its own array of
DNS servers. In this case every call to dns_setserver() and
dns_getserver() should specify the netif as the last argument (NULL to
access the global, common array of DNS servers, which is then used as a
fallback). Defult behaviour (0) is to have one common array of DNS
servers, shared among netifs. Generally you should leave this disabled
if your application has just one netif, while you should consider
enabling this feature for an application with multiple netifs which get
their DNS servers automatically (e.g. ETH with DHCP + PPP modem).
---
 src/core/dns.c           | 58 +++++++++++++++++++++++++++-------------
 src/core/ipv4/dhcp.c     |  2 +-
 src/core/ipv6/dhcp6.c    |  2 +-
 src/core/ipv6/nd6.c      |  6 ++---
 src/core/netif.c         | 13 ++++++---
 src/include/lwip/dns.h   | 10 +++++--
 src/include/lwip/netif.h |  3 +++
 src/include/lwip/opt.h   | 14 ++++++++++
 src/netif/ppp/ppp.c      | 22 ++++++++++-----
 9 files changed, 94 insertions(+), 36 deletions(-)

diff --git a/src/core/dns.c b/src/core/dns.c
index f83ea9b4..fde8de69 100644
--- a/src/core/dns.c
+++ b/src/core/dns.c
@@ -318,7 +318,7 @@ dns_init(void)
   /* initialize default DNS server address */
   ip_addr_t dnsserver;
   DNS_SERVER_ADDRESS(&dnsserver);
-  dns_setserver(0, &dnsserver);
+  dns_setserver(0, &dnsserver LWIP_DNS_NETIF_ARG(NULL));
 #endif /* DNS_SERVER_ADDRESS */
 
   LWIP_ASSERT("sanity check SIZEOF_DNS_QUERY",
@@ -356,17 +356,27 @@ dns_init(void)
  *
  * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS
  * @param dnsserver IP address of the DNS server to set
+ * @param netif specific netif to set DNS server on. If NULL, set default DNS server
+ *         (requires LWIP_DNS_SERVERS_PER_NETIF)
  */
 void
-dns_setserver(u8_t numdns, const ip_addr_t *dnsserver)
+dns_setserver(u8_t numdns, const ip_addr_t *dnsserver LWIP_DNS_NETIF_ARG(struct netif *netif))
 {
-  if (numdns < DNS_MAX_SERVERS) {
-    if (dnsserver != NULL) {
-      dns_servers[numdns] = (*dnsserver);
-    } else {
-      dns_servers[numdns] = *IP_ADDR_ANY;
-    }
+  if (numdns >= DNS_MAX_SERVERS) {
+    return;
+  }
+#if LWIP_DNS_SERVERS_PER_NETIF
+  if (netif) {
+    netif->dns_servers[numdns] = *dnsserver;
+  } else {
+    dns_servers[numdns] = *dnsserver;
   }
+  LWIP_DEBUGF(DNS_DEBUG, ("DNS server [%"U16_F"] for %.2s%"U16_F" set to %s\n", (u16_t)numdns,
+              netif ? netif->name : "All", netif ? (u16_t)netif->num : 0, ipaddr_ntoa(dnsserver)));
+#else
+  dns_servers[numdns] = *dnsserver;
+  LWIP_DEBUGF(DNS_DEBUG, ("DNS server [%"U16_F"] set to %s\n", (u16_t)numdns, ipaddr_ntoa(dnsserver)));
+#endif /*LWIP_DNS_SERVERS_PER_NETIF*/
 }
 
 /**
@@ -374,17 +384,27 @@ dns_setserver(u8_t numdns, const ip_addr_t *dnsserver)
  * Obtain one of the currently configured DNS server.
  *
  * @param numdns the index of the DNS server
+ * @param netif specific netif to fetch server from if not NULL, else fetch default DNS server
+ *         (requires LWIP_DNS_SERVERS_PER_NETIF)
  * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
- *         server has not been configured.
+ *         server has not been configured. For the cases where a netif specific DNS server is
+ *         returned, the pointer is to the dns_servers entry in the netif structure, do not store
+ *         the returned pointer persistently in the case where the netif may be destroyed
  */
 const ip_addr_t *
-dns_getserver(u8_t numdns)
+dns_getserver(u8_t numdns LWIP_DNS_NETIF_ARG(struct netif *netif))
 {
-  if (numdns < DNS_MAX_SERVERS) {
-    return &dns_servers[numdns];
-  } else {
+  if (numdns >= DNS_MAX_SERVERS) {
     return IP_ADDR_ANY;
   }
+#if LWIP_DNS_SERVERS_PER_NETIF
+  if (netif) {
+      if (!ip_addr_isany(&netif->dns_servers[numdns])) {
+        return &netif->dns_servers[numdns];
+      }
+  }
+#endif /*LWIP_DNS_SERVERS_PER_NETIF*/
+  return &dns_servers[numdns];
 }
 
 /**
@@ -776,10 +796,10 @@ dns_send(u8_t idx)
   u8_t pcb_idx;
   struct dns_table_entry *entry = &dns_table[idx];
 
-  LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
+  LWIP_DEBUGF(DNS_DEBUG, ("dns_send: server_idx %"U16_F" \"%s\": request\n",
                           (u16_t)(entry->server_idx), entry->name));
   LWIP_ASSERT("dns server out of array", entry->server_idx < DNS_MAX_SERVERS);
-  if (ip_addr_isany_val(dns_servers[entry->server_idx])
+  if (ip_addr_isany(dns_getserver(entry->server_idx LWIP_DNS_NETIF_ARG(netif_default)))
 #if LWIP_DNS_SUPPORT_MDNS_QUERIES
       && !entry->is_mdns
 #endif
@@ -864,7 +884,7 @@ dns_send(u8_t idx)
 #endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
     {
       dst_port = DNS_SERVER_PORT;
-      dst = &dns_servers[entry->server_idx];
+      dst = dns_getserver(entry->server_idx LWIP_DNS_NETIF_ARG(netif_default));
     }
     err = udp_sendto(dns_pcbs[pcb_idx], p, dst, dst_port);
 
@@ -1045,7 +1065,7 @@ dns_backupserver_available(struct dns_table_entry *pentry)
   u8_t ret = 0;
 
   if (pentry) {
-    if ((pentry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany_val(dns_servers[pentry->server_idx + 1])) {
+    if ((pentry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany(dns_getserver(pentry->server_idx + 1 LWIP_DNS_NETIF_ARG(netif_default)))) {
       ret = 1;
     }
   }
@@ -1237,7 +1257,7 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
         {
           /* Check whether response comes from the same network address to which the
              question was sent. (RFC 5452) */
-          if (!ip_addr_eq(addr, &dns_servers[entry->server_idx])) {
+          if (!ip_addr_eq(addr, dns_getserver(entry->server_idx LWIP_DNS_NETIF_ARG(netif_default)))) {
             goto ignore_packet; /* ignore this packet */
           }
         }
@@ -1644,7 +1664,7 @@ dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_call
 #endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
   {
     /* prevent calling found callback if no server is set, return error instead */
-    if (ip_addr_isany_val(dns_servers[0])) {
+    if (ip_addr_isany(dns_getserver(0 LWIP_DNS_NETIF_ARG(netif_default)))) {
       return ERR_VAL;
     }
   }
diff --git a/src/core/ipv4/dhcp.c b/src/core/ipv4/dhcp.c
index 6da9cd6b..4a9e641a 100644
--- a/src/core/ipv4/dhcp.c
+++ b/src/core/ipv4/dhcp.c
@@ -696,7 +696,7 @@ dhcp_handle_ack(struct netif *netif, struct dhcp_msg *msg_in)
   for (n = 0; (n < LWIP_DHCP_PROVIDE_DNS_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n); n++) {
     ip_addr_t dns_addr;
     ip_addr_set_ip4_u32_val(dns_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n)));
-    dns_setserver(n, &dns_addr);
+    dns_setserver(n, &dns_addr LWIP_DNS_NETIF_ARG(netif));
   }
 #endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
 }
diff --git a/src/core/ipv6/dhcp6.c b/src/core/ipv6/dhcp6.c
index ecf569a8..ebb544ac 100644
--- a/src/core/ipv6/dhcp6.c
+++ b/src/core/ipv6/dhcp6.c
@@ -547,7 +547,7 @@ dhcp6_handle_config_reply(struct netif *netif, struct pbuf *p_msg_in)
       }
       ip6_addr_assign_zone(dns_addr6, IP6_UNKNOWN, netif);
       /* @todo: do we need a different offset than DHCP(v4)? */
-      dns_setserver(n, &dns_addr);
+      dns_setserver(n, &dns_addr LWIP_DNS_NETIF_ARG(netif));
     }
   }
   /* @ todo: parse and set Domain Search List */
diff --git a/src/core/ipv6/nd6.c b/src/core/ipv6/nd6.c
index d4673ca6..519a15bb 100644
--- a/src/core/ipv6/nd6.c
+++ b/src/core/ipv6/nd6.c
@@ -791,14 +791,14 @@ nd6_input(struct pbuf *p, struct netif *inp)
 
             if (htonl(rdnss_opt->lifetime) > 0) {
               /* TODO implement Lifetime > 0 */
-              dns_setserver(rdnss_server_idx++, &rdnss_address);
+              dns_setserver(rdnss_server_idx++, &rdnss_address LWIP_DNS_NETIF_ARG(inp));
             } else {
               /* TODO implement DNS removal in dns.c */
               u8_t s;
               for (s = 0; s < DNS_MAX_SERVERS; s++) {
-                const ip_addr_t *addr = dns_getserver(s);
+                const ip_addr_t *addr = dns_getserver(s LWIP_DNS_NETIF_ARG(inp));
                 if(ip_addr_eq(addr, &rdnss_address)) {
-                  dns_setserver(s, NULL);
+                  dns_setserver(s, IP_ADDR_ANY LWIP_DNS_NETIF_ARG(inp));
                 }
               }
             }
diff --git a/src/core/netif.c b/src/core/netif.c
index cf73a209..02ad9b36 100644
--- a/src/core/netif.c
+++ b/src/core/netif.c
@@ -290,7 +290,7 @@ netif_add(struct netif *netif,
 #endif /* LWIP_IPV4 */
           void *state, netif_init_fn init, netif_input_fn input)
 {
-#if LWIP_IPV6
+#if LWIP_IPV6 || (LWIP_DNS && LWIP_DNS_SERVERS_PER_NETIF)
   s8_t i;
 #endif
 
@@ -384,6 +384,13 @@ netif_add(struct netif *netif,
   netif_set_addr(netif, ipaddr, netmask, gw);
 #endif /* LWIP_IPV4 */
 
+#if LWIP_DNS && LWIP_DNS_SERVERS_PER_NETIF
+  for (i = 0; i < DNS_MAX_SERVERS; i++)
+  {
+      ip_addr_set_zero(&netif->dns_servers[i]);
+  }
+#endif /* LWIP_DNS && LWIP_DNS_SERVERS_PER_NETIF */
+
   /* call user specified initialization function for netif */
   if (init(netif) != ERR_OK) {
     return NULL;
@@ -1105,11 +1112,11 @@ netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callb
 /**
  * @ingroup netif
  * Send an IP packet to be received on the same netif (loopif-like).
- * The pbuf is copied and added to an internal queue which is fed to 
+ * The pbuf is copied and added to an internal queue which is fed to
  * netif->input by netif_poll().
  * In multithreaded mode, the call to netif_poll() is queued to be done on the
  * TCP/IP thread.
- * In callback mode, the user has the responsibility to call netif_poll() in 
+ * In callback mode, the user has the responsibility to call netif_poll() in
  * the main loop of their application.
  *
  * @param netif the lwip network interface structure
diff --git a/src/include/lwip/dns.h b/src/include/lwip/dns.h
index 09134154..c63208af 100644
--- a/src/include/lwip/dns.h
+++ b/src/include/lwip/dns.h
@@ -86,6 +86,12 @@ struct local_hostlist_entry {
 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
 #endif /* DNS_LOCAL_HOSTLIST */
 
+#if LWIP_DNS_SERVERS_PER_NETIF
+#define LWIP_DNS_NETIF_ARG(x) , x
+#else
+#define LWIP_DNS_NETIF_ARG(x)
+#endif /* LWIP_DNS_SERVERS_PER_NETIF */
+
 #if LWIP_IPV4
 extern const ip_addr_t dns_mquery_v4group;
 #endif /* LWIP_IPV4 */
@@ -104,8 +110,8 @@ typedef void (*dns_found_callback)(const char *name, const ip_addr_t *ipaddr, vo
 
 void             dns_init(void);
 void             dns_tmr(void);
-void             dns_setserver(u8_t numdns, const ip_addr_t *dnsserver);
-const ip_addr_t* dns_getserver(u8_t numdns);
+void             dns_setserver(u8_t numdns, const ip_addr_t *dnsserver LWIP_DNS_NETIF_ARG(struct netif *netif));
+const ip_addr_t* dns_getserver(u8_t numdns LWIP_DNS_NETIF_ARG(struct netif *netif));
 err_t            dns_gethostbyname(const char *hostname, ip_addr_t *addr,
                                    dns_found_callback found, void *callback_arg);
 err_t            dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr,
diff --git a/src/include/lwip/netif.h b/src/include/lwip/netif.h
index 5ecf8cdc..9afa840d 100644
--- a/src/include/lwip/netif.h
+++ b/src/include/lwip/netif.h
@@ -278,6 +278,9 @@ struct netif {
   ip_addr_t netmask;
   ip_addr_t gw;
 #endif /* LWIP_IPV4 */
+#if LWIP_DNS && LWIP_DNS_SERVERS_PER_NETIF
+  ip_addr_t dns_servers[DNS_MAX_SERVERS];
+#endif /* LWIP_DNS && LWIP_DNS_SERVERS_PER_NETIF */
 #if LWIP_IPV6
   /** Array of IPv6 addresses for this netif. */
   ip_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES];
diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h
index 9fe0bf04..26a0c32c 100644
--- a/src/include/lwip/opt.h
+++ b/src/include/lwip/opt.h
@@ -1191,6 +1191,20 @@
 #if !defined LWIP_DNS_SUPPORT_MDNS_QUERIES || defined __DOXYGEN__
 #define LWIP_DNS_SUPPORT_MDNS_QUERIES   0
 #endif
+
+/**
+ * LWIP_DNS_SERVERS_PER_NETIF==1: each netif has its own array of DNS servers.
+ * In this case every call to dns_setserver() and dns_getserver() should specify
+ * the netif as the last argument (NULL to access the global, common array of
+ * DNS servers, which is then used as a fallback). Defult behaviour (0) is to
+ * have one common array of DNS servers, shared among netifs. Generally you
+ * should leave this disabled if your application has just one netif, while you
+ * should consider enabling this feature for an application with multiple netifs
+ * which get their DNS servers automatically (e.g. ETH with DHCP + PPP modem).
+ */
+#if !defined LWIP_DNS_SERVERS_PER_NETIF || defined __DOXYGEN__
+#define LWIP_DNS_SERVERS_PER_NETIF       0
+#endif
 /**
  * @}
  */
diff --git a/src/netif/ppp/ppp.c b/src/netif/ppp/ppp.c
index 6c070483..38f12c76 100644
--- a/src/netif/ppp/ppp.c
+++ b/src/netif/ppp/ppp.c
@@ -636,7 +636,7 @@ int ppp_init(void)
 
   return 0;
 }
- 
+
 /*
  * Create a new PPP control block.
  *
@@ -1120,12 +1120,16 @@ int cifproxyarp(ppp_pcb *pcb, u32_t his_adr) {
  */
 int sdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2) {
   ip_addr_t ns;
+#if LWIP_DNS_SERVERS_PER_NETIF == 0
   LWIP_UNUSED_ARG(pcb);
+#else
+  LWIP_ASSERT("pcb != NULL", pcb);
+#endif
 
   ip_addr_set_ip4_u32_val(ns, ns1);
-  dns_setserver(0, &ns);
+  dns_setserver(0, &ns LWIP_DNS_NETIF_ARG(pcb->netif));
   ip_addr_set_ip4_u32_val(ns, ns2);
-  dns_setserver(1, &ns);
+  dns_setserver(1, &ns LWIP_DNS_NETIF_ARG(pcb->netif));
   return 1;
 }
 
@@ -1136,17 +1140,21 @@ int sdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2) {
 int cdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2) {
   const ip_addr_t *nsa;
   ip_addr_t nsb;
+#if LWIP_DNS_SERVERS_PER_NETIF == 0
   LWIP_UNUSED_ARG(pcb);
+#else
+  LWIP_ASSERT("pcb != NULL", pcb);
+#endif
 
-  nsa = dns_getserver(0);
+  nsa = dns_getserver(0 LWIP_DNS_NETIF_ARG(pcb->netif));
   ip_addr_set_ip4_u32_val(nsb, ns1);
   if (ip_addr_eq(nsa, &nsb)) {
-    dns_setserver(0, IP_ADDR_ANY);
+    dns_setserver(0, IP_ADDR_ANY LWIP_DNS_NETIF_ARG(pcb->netif));
   }
-  nsa = dns_getserver(1);
+  nsa = dns_getserver(1 LWIP_DNS_NETIF_ARG(pcb->netif));
   ip_addr_set_ip4_u32_val(nsb, ns2);
   if (ip_addr_eq(nsa, &nsb)) {
-    dns_setserver(1, IP_ADDR_ANY);
+    dns_setserver(1, IP_ADDR_ANY LWIP_DNS_NETIF_ARG(pcb->netif));
   }
   return 1;
 }
-- 
2.28.0

