e9e7d6
From 11ab42e63f9089c4c14a391f30175d4c2d071e99 Mon Sep 17 00:00:00 2001
e9e7d6
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
e9e7d6
Date: Mon, 15 Jul 2019 17:13:12 +0200
e9e7d6
Subject: [PATCH 4/5] Handle listening on duplicate addresses
e9e7d6
e9e7d6
Save listening address into listener. Use it to find existing listeners
e9e7d6
before creating new one. If it exist, increase just used counter.
e9e7d6
Release only listeners not already used.
e9e7d6
e9e7d6
Duplicates family in listener.
e9e7d6
---
e9e7d6
 src/dnsmasq.h |   3 +-
e9e7d6
 src/network.c | 115 ++++++++++++++++++++++++++++++++++++--------------
e9e7d6
 2 files changed, 85 insertions(+), 33 deletions(-)
e9e7d6
e9e7d6
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
e9e7d6
index 89d138a..3b3f6ef 100644
e9e7d6
--- a/src/dnsmasq.h
e9e7d6
+++ b/src/dnsmasq.h
e9e7d6
@@ -526,7 +526,8 @@ struct irec {
e9e7d6
 };
e9e7d6
 
e9e7d6
 struct listener {
e9e7d6
-  int fd, tcpfd, tftpfd, family;
e9e7d6
+  int fd, tcpfd, tftpfd, family, used;
e9e7d6
+  union mysockaddr addr;
e9e7d6
   struct irec *iface; /* only sometimes valid for non-wildcard */
e9e7d6
   struct listener *next;
e9e7d6
 };
e9e7d6
diff --git a/src/network.c b/src/network.c
e9e7d6
index d6d4b01..4bbd810 100644
e9e7d6
--- a/src/network.c
e9e7d6
+++ b/src/network.c
e9e7d6
@@ -577,6 +577,56 @@ static void clean_interfaces()
e9e7d6
   }
e9e7d6
 }
e9e7d6
 
e9e7d6
+/** Release listener if no other interface needs it.
e9e7d6
+ *
e9e7d6
+ * @return 1 if released, 0 if still required
e9e7d6
+ */
e9e7d6
+static int release_listener(struct listener *l)
e9e7d6
+{
e9e7d6
+  if (l->used > 1)
e9e7d6
+    {
e9e7d6
+      struct irec *iface;
e9e7d6
+      for (iface = daemon->interfaces; iface; iface = iface->next)
e9e7d6
+	if (iface->done && sockaddr_isequal(&l->addr, &iface->addr))
e9e7d6
+	  {
e9e7d6
+	    if (iface->found)
e9e7d6
+	      {
e9e7d6
+		/* update listener to point to active interface instead */
e9e7d6
+		if (!l->iface->found)
e9e7d6
+		  l->iface = iface;
e9e7d6
+	      }
e9e7d6
+	    else
e9e7d6
+	      {
e9e7d6
+		l->used--;
e9e7d6
+		iface->done = 0;
e9e7d6
+	      }
e9e7d6
+	  }
e9e7d6
+
e9e7d6
+      /* Someone is still using this listener, skip its deletion */
e9e7d6
+      if (l->used > 0)
e9e7d6
+	return 0;
e9e7d6
+    }
e9e7d6
+
e9e7d6
+  if (l->iface->done)
e9e7d6
+    {
e9e7d6
+      (void)prettyprint_addr(&l->iface->addr, daemon->addrbuff);
e9e7d6
+      my_syslog(LOG_DEBUG, _("stopped listening on %s(#%d): %s"),
e9e7d6
+		l->iface->name, l->iface->index, daemon->addrbuff);
e9e7d6
+      /* In case it ever returns */
e9e7d6
+      l->iface->done = 0;
e9e7d6
+    }
e9e7d6
+
e9e7d6
+  if (l->fd != -1)
e9e7d6
+    close(l->fd);
e9e7d6
+  if (l->tcpfd != -1)
e9e7d6
+    close(l->tcpfd);
e9e7d6
+  if (l->tftpfd != -1)
e9e7d6
+    close(l->tftpfd);
e9e7d6
+
e9e7d6
+  free(l);
e9e7d6
+  return 1;
e9e7d6
+}
e9e7d6
+
e9e7d6
 int enumerate_interfaces(int reset)
e9e7d6
 {
e9e7d6
   static struct addrlist *spare = NULL;
e9e7d6
@@ -684,29 +734,10 @@ int enumerate_interfaces(int reset)
e9e7d6
 	  
e9e7d6
 	  if (!l->iface || l->iface->found)
e9e7d6
 	    up = &l->next;
e9e7d6
-	  else
e9e7d6
+	  else if (release_listener(l))
e9e7d6
 	    {
e9e7d6
-	      *up = l->next;
e9e7d6
-	      if (l->iface->done)
e9e7d6
-	        {
e9e7d6
-	          iface = l->iface;
e9e7d6
-	          (void)prettyprint_addr(&iface->addr, daemon->addrbuff);
e9e7d6
-	          my_syslog(LOG_DEBUG, _("stopped listening on %s(#%d): %s"),
e9e7d6
-	                    iface->name, iface->index, daemon->addrbuff);
e9e7d6
-	        }
e9e7d6
-	      
e9e7d6
-	      /* In case it ever returns */
e9e7d6
-	      l->iface->done = 0;
e9e7d6
-	      
e9e7d6
-	      if (l->fd != -1)
e9e7d6
-		close(l->fd);
e9e7d6
-	      if (l->tcpfd != -1)
e9e7d6
-		close(l->tcpfd);
e9e7d6
-	      if (l->tftpfd != -1)
e9e7d6
-		close(l->tftpfd);
e9e7d6
-	      
e9e7d6
-	      free(l);
e9e7d6
-	      freed = 1;
e9e7d6
+	      *up = tmp;
e9e7d6
+		freed = 1;
e9e7d6
 	    }
e9e7d6
 	}
e9e7d6
 
e9e7d6
@@ -959,7 +990,9 @@ static struct listener *create_listeners(union mysockaddr *addr, int do_tftp, in
e9e7d6
       l->family = addr->sa.sa_family;
e9e7d6
       l->fd = fd;
e9e7d6
       l->tcpfd = tcpfd;
e9e7d6
-      l->tftpfd = tftpfd;	
e9e7d6
+      l->tftpfd = tftpfd;
e9e7d6
+      l->addr = *addr;
e9e7d6
+      l->used = 1;
e9e7d6
       l->iface = NULL;
e9e7d6
     }
e9e7d6
 
e9e7d6
@@ -1000,23 +1033,41 @@ void create_wildcard_listeners(void)
e9e7d6
   daemon->listeners = l;
e9e7d6
 }
e9e7d6
 
e9e7d6
+static struct listener *find_listener(union mysockaddr *addr)
e9e7d6
+{
e9e7d6
+  struct listener *l;
e9e7d6
+  for (l = daemon->listeners; l; l = l->next)
e9e7d6
+    if (sockaddr_isequal(&l->addr, addr))
e9e7d6
+      return l;
e9e7d6
+  return NULL;
e9e7d6
+}
e9e7d6
+
e9e7d6
 void create_bound_listeners(int dienow)
e9e7d6
 {
e9e7d6
   struct listener *new;
e9e7d6
   struct irec *iface;
e9e7d6
   struct iname *if_tmp;
e9e7d6
+  struct listener *existing;
e9e7d6
 
e9e7d6
   for (iface = daemon->interfaces; iface; iface = iface->next)
e9e7d6
-    if (!iface->done && !iface->dad && iface->found &&
e9e7d6
-	(new = create_listeners(&iface->addr, iface->tftp_ok, dienow)))
e9e7d6
+    if (!iface->done && !iface->dad && iface->found)
e9e7d6
       {
e9e7d6
-	new->iface = iface;
e9e7d6
-	new->next = daemon->listeners;
e9e7d6
-	daemon->listeners = new;
e9e7d6
-	iface->done = 1;
e9e7d6
-	(void)prettyprint_addr(&iface->addr, daemon->addrbuff);
e9e7d6
-	my_syslog(LOG_DEBUG, _("listening on %s(#%d): %s"),
e9e7d6
-	          iface->name, iface->index, daemon->addrbuff);
e9e7d6
+	existing = find_listener(&iface->addr);
e9e7d6
+	if (existing)
e9e7d6
+	  {
e9e7d6
+	    iface->done = 1;
e9e7d6
+	    existing->used++; /* increase usage counter */
e9e7d6
+	  }
e9e7d6
+	else if ((new = create_listeners(&iface->addr, iface->tftp_ok, dienow)))
e9e7d6
+	  {
e9e7d6
+	    new->iface = iface;
e9e7d6
+	    new->next = daemon->listeners;
e9e7d6
+	    daemon->listeners = new;
e9e7d6
+	    iface->done = 1;
e9e7d6
+	    (void)prettyprint_addr(&iface->addr, daemon->addrbuff);
e9e7d6
+	    my_syslog(LOG_DEBUG, _("listening on %s(#%d): %s"),
e9e7d6
+		      iface->name, iface->index, daemon->addrbuff);
e9e7d6
+	  }
e9e7d6
       }
e9e7d6
 
e9e7d6
   /* Check for --listen-address options that haven't been used because there's
e9e7d6
-- 
e9e7d6
2.20.1
e9e7d6