am 7a12cb36: am 4e0ada97: Properly initialize struct irec pointers after malloc()

* commit '7a12cb364297118947f9ce9786f94a47eb710ce8':
  Properly initialize struct irec pointers after malloc()
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index c4fda28..0b9dbd2 100755
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -321,6 +321,7 @@
   union mysockaddr source_addr;
   char interface[IF_NAMESIZE+1];
   struct serverfd *next;
+  uint32_t mark;
 };
 
 struct randfd {
@@ -335,6 +336,7 @@
   char *domain; /* set if this server only handles a domain. */ 
   int flags, tcpfd;
   unsigned int queries, failed_queries;
+  uint32_t mark;
   struct server *next; 
 };
 
@@ -775,7 +777,7 @@
 
 /* network.c */
 int indextoname(int fd, int index, char *name);
-int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp);
+int local_bind(int fd, union mysockaddr *addr, char *intname, uint32_t mark, int is_tcp);
 int random_sock(int family);
 void pre_allocate_sfds(void);
 int reload_servers(char *fname);
diff --git a/src/forward.c b/src/forward.c
index 40cda1c..a121123 100755
--- a/src/forward.c
+++ b/src/forward.c
@@ -318,8 +318,20 @@
 		      daemon->rfd_save = forward->rfd4;
 		      fd = forward->rfd4->fd;
 		    }
+
+#ifdef ANDROID
+		  // Mark the socket so it goes out on the correct network. Note
+		  // that we never clear the mark, only re-set it the next time we
+		  // allocate a new random fd. This is because we buffer DNS
+		  // queries (in daemon->srv_save, daemon->packet_len) and socket
+		  // file descriptors (in daemon->rfd_save) with the expectation of
+		  // being able to use them again.
+		  //
+		  // Server fds are marked separately in allocate_sfd.
+		  setsockopt(fd, SOL_SOCKET, SO_MARK, &start->mark, sizeof(start->mark));
+#endif
 		}
-	      
+
 	      if (sendto(fd, (char *)header, plen, 0,
 			 &start->addr.sa,
 			 sa_len(&start->addr)) == -1)
@@ -815,7 +827,8 @@
 		  
 		  if ((last_server->tcpfd == -1) &&
 		      (last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 &&
-		      (!local_bind(last_server->tcpfd,  &last_server->source_addr, last_server->interface, 1) ||
+		      (!local_bind(last_server->tcpfd, &last_server->source_addr,
+				   last_server->interface, last_server->mark, 1) ||
 		       connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
 		    {
 		      close(last_server->tcpfd);
diff --git a/src/network.c b/src/network.c
index e24ec3a..c7431df 100755
--- a/src/network.c
+++ b/src/network.c
@@ -481,6 +481,31 @@
 }
 
 /**
+ * If a listener has a struct irec pointer whose address matches the newly
+ * malloc()d struct irec's address, update its pointer to refer to this new
+ * struct irec instance.
+ *
+ * Otherwise, any listeners that are preserved across interface list changes
+ * will point at interface structures that are free()d at the end of
+ * set_interfaces(), and can get overwritten by subsequent memory allocations.
+ *
+ * See b/17475756 for further discussion.
+ */
+void fixup_possible_existing_listener(struct irec *new_iface) {
+  /* find the listener, if present */
+  struct listener *l;
+  for (l = daemon->listeners; l; l = l->next) {
+    struct irec *listener_iface = l->iface;
+    if (listener_iface) {
+      if (sockaddr_isequal(&listener_iface->addr, &new_iface->addr)) {
+        l->iface = new_iface;
+        return;
+      }
+    }
+  }
+}
+
+/**
  * Close the sockets listening on the given interface
  *
  * This new function is needed as we're dynamically changing the interfaces
@@ -682,7 +707,7 @@
 }
   
 
-int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp)
+int local_bind(int fd, union mysockaddr *addr, char *intname, uint32_t mark, int is_tcp)
 {
   union mysockaddr addr_copy = *addr;
 
@@ -706,10 +731,13 @@
     return 0;
 #endif
 
+  if (mark != 0 && setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) == -1)
+    return 0;
+
   return 1;
 }
 
-static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
+static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname, uint32_t mark)
 {
   struct serverfd *sfd;
   int errsave;
@@ -736,6 +764,7 @@
   /* may have a suitable one already */
   for (sfd = daemon->sfds; sfd; sfd = sfd->next )
     if (sockaddr_isequal(&sfd->source_addr, addr) &&
+	mark == sfd->mark &&
 	strcmp(intname, sfd->interface) == 0)
       return sfd;
   
@@ -750,7 +779,7 @@
       return NULL;
     }
   
-  if (!local_bind(sfd->fd, addr, intname, 0) || !fix_fd(sfd->fd))
+  if (!local_bind(sfd->fd, addr, intname, mark, 0) || !fix_fd(sfd->fd))
     { 
       errsave = errno; /* save error from bind. */
       close(sfd->fd);
@@ -761,6 +790,7 @@
     
   strcpy(sfd->interface, intname); 
   sfd->source_addr = *addr;
+  sfd->mark = mark;
   sfd->next = daemon->sfds;
   daemon->sfds = sfd;
   return sfd; 
@@ -782,7 +812,7 @@
 #ifdef HAVE_SOCKADDR_SA_LEN
       addr.in.sin_len = sizeof(struct sockaddr_in);
 #endif
-      allocate_sfd(&addr, "");
+      allocate_sfd(&addr, "", 0);
 #ifdef HAVE_IPV6
       memset(&addr, 0, sizeof(addr));
       addr.in6.sin6_family = AF_INET6;
@@ -791,13 +821,13 @@
 #ifdef HAVE_SOCKADDR_SA_LEN
       addr.in6.sin6_len = sizeof(struct sockaddr_in6);
 #endif
-      allocate_sfd(&addr, "");
+      allocate_sfd(&addr, "", 0);
 #endif
     }
   
   for (srv = daemon->servers; srv; srv = srv->next)
     if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) &&
-	!allocate_sfd(&srv->source_addr, srv->interface) &&
+	!allocate_sfd(&srv->source_addr, srv->interface, srv->mark) &&
 	errno != 0 &&
 	(daemon->options & OPT_NOWILD))
       {
@@ -847,7 +877,7 @@
 	  
 	  /* Do we need a socket set? */
 	  if (!new->sfd && 
-	      !(new->sfd = allocate_sfd(&new->source_addr, new->interface)) &&
+	      !(new->sfd = allocate_sfd(&new->source_addr, new->interface, new->mark)) &&
 	      errno != 0)
 	    {
 	      my_syslog(LOG_WARNING, 
@@ -919,7 +949,7 @@
     strncpy(s, interfaces, sizeof(s));
     while((interface = strsep(&next, ":"))) {
         if_tmp = safe_malloc(sizeof(struct iname));
-        memset(if_tmp, sizeof(struct iname), 0);
+        memset(if_tmp, 0, sizeof(struct iname));
         if ((if_tmp->name = strdup(interface)) == NULL) {
             die(_("malloc failure in set_interfaces: %s"), NULL, EC_BADNET);
         }
@@ -942,11 +972,14 @@
       int found = 0;
       for (new_iface = daemon->interfaces; new_iface; new_iface = new_iface->next) {
         if (sockaddr_isequal(&old_iface->addr, &new_iface->addr)) {
-            found = -1;
+            found = 1;
             break;
         }
       }
-      if (!found) {
+
+      if (found) {
+        fixup_possible_existing_listener(new_iface);
+      } else {
 #ifdef __ANDROID_DEBUG__
         char debug_buff[MAXDNAME];
         prettyprint_addr(&old_iface->addr, debug_buff);
@@ -1001,7 +1034,9 @@
 }
 
 /*
- * Takes a string in the format "1.2.3.4:1.2.3.4:..." - up to 1024 bytes in length
+ * Takes a string in the format "0x100b:1.2.3.4:1.2.3.4:..." - up to 1024 bytes in length
+ *  - The first element is the socket mark to set on sockets that forward DNS queries.
+ *  - The subsequent elements are the DNS servers to forward queries to.
  */
 int set_servers(const char *servers)
 {
@@ -1009,6 +1044,8 @@
   struct server *old_servers = NULL;
   struct server *new_servers = NULL;
   struct server *serv;
+  char *mark_string;
+  uint32_t mark;
 
   strncpy(s, servers, sizeof(s));
 
@@ -1033,10 +1070,14 @@
       serv = tmp;
     }
 
-    char *next = s;
-    char *saddr;
+  char *next = s;
+  char *saddr;
 
- while ((saddr = strsep(&next, ":"))) {
+  /* Parse the mark. */
+  mark_string = strsep(&next, ":");
+  mark = strtoul(mark_string, NULL, 0);
+
+  while ((saddr = strsep(&next, ":"))) {
       union mysockaddr addr, source_addr;
       memset(&addr, 0, sizeof(addr));
       memset(&source_addr, 0, sizeof(source_addr));
@@ -1082,6 +1123,7 @@
       serv->source_addr = source_addr;
       serv->domain = NULL;
       serv->interface[0] = 0;
+      serv->mark = mark;
       serv->sfd = NULL;
       serv->flags = SERV_FROM_RESOLV;
       serv->queries = serv->failed_queries = 0;
@@ -1195,6 +1237,7 @@
       serv->source_addr = source_addr;
       serv->domain = NULL;
       serv->interface[0] = 0;
+      serv->mark = 0;
       serv->sfd = NULL;
       serv->flags = SERV_FROM_RESOLV;
       serv->queries = serv->failed_queries = 0;