Allow overlap in resolv uid => DNS iface mapping

When multiple rules exist covering a given uid the one added most
recently will be used.

This allows us to handle the simultaneous tuns case where a new tun is
coming online for an already running VPN.

_resolv_clear_iface_for_uid_range now also takes the iface and removes
only that matching (iface, uid range) entry.

Bug: 12134439
Change-Id: I9b9cfcfae2f38c409022a8c76ccadad7e2babd78
diff --git a/libc/netbsd/resolv/res_cache.c b/libc/netbsd/resolv/res_cache.c
index 8a6dc83..1a0c40c 100644
--- a/libc/netbsd/resolv/res_cache.c
+++ b/libc/netbsd/resolv/res_cache.c
@@ -1852,9 +1852,7 @@
 static struct resolv_pidiface_info* _get_pid_iface_info_locked(int pid);
 
 /* remove a resolv_pidiface_info structure from _res_uidiface_list */
-static int _remove_uidiface_info_locked(int uid_start, int uid_end);
-/* check if a range [low,high] overlaps with any already existing ranges in the uid=>iface map*/
-static int  _resolv_check_uid_range_overlap_locked(int uid_start, int uid_end);
+static int _remove_uidiface_info_locked(const char* iface, int uid_start, int uid_end);
 /* get a resolv_uidiface_info structure from _res_uidiface_list with a certain uid */
 static struct resolv_uidiface_info* _get_uid_iface_info_locked(int uid);
 
@@ -2433,11 +2431,11 @@
 }
 
 static int
-_remove_uidiface_info_locked(int uid_start, int uid_end) {
+_remove_uidiface_info_locked(const char* ifname, int uid_start, int uid_end) {
     struct resolv_uidiface_info* result = _res_uidiface_list.next;
     struct resolv_uidiface_info* prev = &_res_uidiface_list;
-
-    while (result != NULL && result->uid_start != uid_start && result->uid_end != uid_end) {
+    while (result != NULL && !(result->uid_start == uid_start && result->uid_end == uid_end &&
+            !strcmp(result->ifname, ifname))) {
         prev = result;
         result = result->next;
     }
@@ -2461,19 +2459,6 @@
     return result;
 }
 
-static int
-_resolv_check_uid_range_overlap_locked(int uid_start, int uid_end)
-{
-    struct resolv_uidiface_info* cur = _res_uidiface_list.next;
-    while (cur != NULL) {
-        if (cur->uid_start <= uid_end && cur->uid_end >= uid_start) {
-            return -1;
-        }
-        cur = cur->next;
-    }
-    return 0;
-}
-
 void
 _resolv_clear_iface_uid_range_mapping()
 {
@@ -2518,28 +2503,21 @@
         return -1;
     }
     pthread_mutex_lock(&_res_uidiface_list_lock);
-    //check that we aren't adding an overlapping range
-    if (!_resolv_check_uid_range_overlap_locked(uid_start, uid_end)) {
-        uidiface_info = calloc(sizeof(*uidiface_info), 1);
-        if (uidiface_info) {
-            uidiface_info->uid_start = uid_start;
-            uidiface_info->uid_end = uid_end;
-            int len = sizeof(uidiface_info->ifname);
-            strncpy(uidiface_info->ifname, ifname, len - 1);
-            uidiface_info->ifname[len - 1] = '\0';
+    uidiface_info = calloc(sizeof(*uidiface_info), 1);
+    if (uidiface_info) {
+        uidiface_info->uid_start = uid_start;
+        uidiface_info->uid_end = uid_end;
+        int len = sizeof(uidiface_info->ifname);
+        strncpy(uidiface_info->ifname, ifname, len - 1);
+        uidiface_info->ifname[len - 1] = '\0';
 
-            uidiface_info->next = _res_uidiface_list.next;
-            _res_uidiface_list.next = uidiface_info;
+        uidiface_info->next = _res_uidiface_list.next;
+        _res_uidiface_list.next = uidiface_info;
 
-            XLOG("_resolv_set_iface_for_uid_range: [%d,%d], iface %s\n", uid_start, uid_end,
-                    ifname);
-        } else {
-            XLOG("_resolv_set_iface_for_uid_range failing calloc\n");
-            rv = -1;
-            errno = EINVAL;
-        }
+        XLOG("_resolv_set_iface_for_uid_range: [%d,%d], iface %s\n", uid_start, uid_end,
+                ifname);
     } else {
-        XLOG("_resolv_set_iface_for_uid_range range [%d,%d] overlaps\n", uid_start, uid_end);
+        XLOG("_resolv_set_iface_for_uid_range failing calloc\n");
         rv = -1;
         errno = EINVAL;
     }
@@ -2549,14 +2527,14 @@
 }
 
 int
-_resolv_clear_iface_for_uid_range(int uid_start, int uid_end)
+_resolv_clear_iface_for_uid_range(const char* ifname, int uid_start, int uid_end)
 {
     pthread_once(&_res_cache_once, _res_cache_init);
     pthread_mutex_lock(&_res_uidiface_list_lock);
 
-    int rv = _remove_uidiface_info_locked(uid_start, uid_end);
+    int rv = _remove_uidiface_info_locked(ifname, uid_start, uid_end);
 
-    XLOG("_resolv_clear_iface_for_uid_range: [%d,%d]\n", uid_start, uid_end);
+    XLOG("_resolv_clear_iface_for_uid_range: [%d,%d] iface %s\n", uid_start, uid_end, ifname);
 
     pthread_mutex_unlock(&_res_uidiface_list_lock);
 
diff --git a/libc/private/resolv_iface.h b/libc/private/resolv_iface.h
index ad42793..5d24124 100644
--- a/libc/private/resolv_iface.h
+++ b/libc/private/resolv_iface.h
@@ -79,13 +79,12 @@
 extern int _resolv_get_pids_associated_interface(int pid, char* buff, int buffLen);
 
 
-/** set a uid range to use the name servers of the specified interface
- *  If [low,high] overlaps with an already existing rule -1 is returned */
+/** set a uid range to use the name servers of the specified interface */
 extern int _resolv_set_iface_for_uid_range(const char* ifname, int uid_start, int uid_end);
 
-/* clear a uid range from being associated with an interface
- * If the range given is not mapped -1 is returned. */
-extern int _resolv_clear_iface_for_uid_range(int uid_start, int uid_end);
+/** Remove a mapping added by _resolv_set_iface_for_uid_range.
+ *  If no such rule exists -1 is returned. */
+extern int _resolv_clear_iface_for_uid_range(const char* ifname, int uid_start, int uid_end);
 
 /* clear the entire mapping of uid ranges to interfaces. */
 extern void _resolv_clear_iface_uid_range_mapping();
@@ -94,6 +93,7 @@
  *  On error, -1 is returned.
  *  If no interface is found, 0 is returned and buff is set to empty ('\0').
  *  If an interface is found, the name is copied to buff and the length of the name is returned.
+ *  If there are multiple rules covering uid the most recently added rule will be returned.
  *  Arguments:   uid The uid to find an interface for
  *               buff A buffer to copy the result to
  *               buffLen Length of buff. An interface is at most IF_NAMESIZE in length */