am 57abed1d: Merge "netd: SCN must be used instead of PRI for scanf functions"

* commit '57abed1d8463b20a28745894d99942bfa9512e1b':
  netd: SCN must be used instead of PRI for scanf functions
diff --git a/CommandListener.cpp b/CommandListener.cpp
index 7ec3860..9db5b43 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -80,7 +80,6 @@
         OEM_IPTABLES_FILTER_OUTPUT,
         FirewallController::LOCAL_OUTPUT,
         BandwidthController::LOCAL_OUTPUT,
-        SecondaryTableController::LOCAL_FILTER_OUTPUT,
         NULL,
 };
 
@@ -93,11 +92,11 @@
 static const char* MANGLE_POSTROUTING[] = {
         BandwidthController::LOCAL_MANGLE_POSTROUTING,
         IdletimerController::LOCAL_MANGLE_POSTROUTING,
+        SecondaryTableController::LOCAL_MANGLE_POSTROUTING,
         NULL,
 };
 
 static const char* MANGLE_OUTPUT[] = {
-        SecondaryTableController::LOCAL_MANGLE_EXEMPT,
         SecondaryTableController::LOCAL_MANGLE_OUTPUT,
         NULL,
 };
@@ -1018,9 +1017,11 @@
                     "Wrong number of arguments to resolver setifaceforuid", false);
             return 0;
         }
-    } else if (!strcmp(argv[1], "clearifaceforuidrange")) { // resolver clearifaceforuid <l> <h>
-        if (argc == 4) {
-            rc = sResolverCtrl->clearDnsInterfaceForUidRange(atoi(argv[2]), atoi(argv[3]));
+    } else if (!strcmp(argv[1], "clearifaceforuidrange")) {
+        // resolver clearifaceforuid <if> <l> <h>
+        if (argc == 5) {
+            rc = sResolverCtrl->clearDnsInterfaceForUidRange(argv[2], atoi(argv[3]),
+                    atoi(argv[4]));
         } else {
             cli->sendMsg(ResponseCode::CommandSyntaxError,
                     "Wrong number of arguments to resolver clearifaceforuid", false);
diff --git a/DnsProxyListener.cpp b/DnsProxyListener.cpp
index a544259..e0619f7 100644
--- a/DnsProxyListener.cpp
+++ b/DnsProxyListener.cpp
@@ -133,8 +133,7 @@
     if (mIface == NULL) {
         //fall back to the per uid interface if no per pid interface exists
         if(!_resolv_get_pids_associated_interface(mPid, tmp, sizeof(tmp)))
-            if(!_resolv_get_uids_associated_interface(mUid, tmp, sizeof(tmp)))
-                mark = -1; // if we don't have a targeted iface don't use a mark
+            _resolv_get_uids_associated_interface(mUid, tmp, sizeof(tmp));
     }
 
     struct addrinfo* result = NULL;
@@ -473,8 +472,7 @@
     if (mIface == NULL) {
         //fall back to the per uid interface if no per pid interface exists
         if(!_resolv_get_pids_associated_interface(mPid, tmp, sizeof(tmp)))
-            if(!_resolv_get_uids_associated_interface(mUid, tmp, sizeof(tmp)))
-                mark = -1;
+            _resolv_get_uids_associated_interface(mUid, tmp, sizeof(tmp));
     }
     struct hostent* hp;
 
diff --git a/MDnsSdListener.cpp b/MDnsSdListener.cpp
index 4944e18..1430273 100644
--- a/MDnsSdListener.cpp
+++ b/MDnsSdListener.cpp
@@ -415,7 +415,7 @@
     } else if (strcmp(cmd, "stop-discover") == 0) {
         stop(cli, argc, argv, "discover");
     } else if (strcmp(cmd, "register") == 0) {
-        if (argc != 6) {
+        if (argc < 6) {
             cli->sendMsg(ResponseCode::CommandParameterError,
                     "Invalid number of arguments to mdnssd register", false);
             return 0;
@@ -427,11 +427,29 @@
         char *interfaceName = NULL; // will use all
         char *domain = NULL;        // will use default
         char *host = NULL;          // will use default hostname
-        int textLen = 0;
-        void *textRecord = NULL;
-
+        unsigned char txtRecord[2048] = "";
+        unsigned char *ptr = txtRecord;
+        for (int i = 6; i < argc; ++i) {
+          int dataLength = strlen(argv[i]);
+          if (dataLength < 1) {
+            continue;
+          }
+          if (dataLength > 255) {
+            cli->sendMsg(ResponseCode::CommandParameterError,
+                    "TXT record fields must not be longer than 255 characters", false);
+            return 0;
+          }
+          if (ptr + dataLength + 1 > txtRecord + sizeof(txtRecord)) {
+            cli->sendMsg(ResponseCode::CommandParameterError,
+                    "Total length of TXT record must be smaller than 2048 bytes", false);
+            return 0;
+          }
+          *ptr++ = dataLength;
+          strcpy( (char*) ptr, argv[i]);
+          ptr += dataLength;
+        }
         serviceRegister(cli, requestId, interfaceName, serviceName,
-                serviceType, domain, host, port, textLen, textRecord);
+                serviceType, domain, host, port, ptr - txtRecord, txtRecord);
     } else if (strcmp(cmd, "stop-register") == 0) {
         stop(cli, argc, argv, "register");
     } else if (strcmp(cmd, "resolve") == 0) {
diff --git a/NetdConstants.h b/NetdConstants.h
index d686968..2508ea2 100644
--- a/NetdConstants.h
+++ b/NetdConstants.h
@@ -21,6 +21,8 @@
 #include <list>
 #include <stdarg.h>
 
+const int PROTECT_MARK = 0x1;
+
 extern const char * const IPTABLES_PATH;
 extern const char * const IP6TABLES_PATH;
 extern const char * const IP_PATH;
diff --git a/ResolverController.cpp b/ResolverController.cpp
index e61fae7..8a43916 100644
--- a/ResolverController.cpp
+++ b/ResolverController.cpp
@@ -105,12 +105,13 @@
     return _resolv_set_iface_for_uid_range(iface, uid_start, uid_end);
 }
 
-int ResolverController::clearDnsInterfaceForUidRange(int uid_start, int uid_end) {
+int ResolverController::clearDnsInterfaceForUidRange(const char* iface, int uid_start,
+        int uid_end) {
     if (DBG) {
-        ALOGD("clearDnsIfaceForUidRange range = [%d,%d]\n", uid_start, uid_end);
+        ALOGD("clearDnsIfaceForUidRange iface = %s range = [%d,%d]\n", iface, uid_start, uid_end);
     }
 
-    return _resolv_clear_iface_for_uid_range(uid_start, uid_end);
+    return _resolv_clear_iface_for_uid_range(iface, uid_start, uid_end);
 }
 
 int ResolverController::clearDnsInterfaceMappings()
diff --git a/ResolverController.h b/ResolverController.h
index e705c8f..c1d5580 100644
--- a/ResolverController.h
+++ b/ResolverController.h
@@ -34,7 +34,7 @@
     int setDnsInterfaceForPid(const char* iface, int pid);
     int clearDnsInterfaceForPid(int pid);
     int setDnsInterfaceForUidRange(const char* iface, int uid_start, int uid_end);
-    int clearDnsInterfaceForUidRange(int uid_start, int uid_end);
+    int clearDnsInterfaceForUidRange(const char* iface, int uid_start, int uid_end);
     int clearDnsInterfaceMappings();
 };
 
diff --git a/SecondaryTableController.cpp b/SecondaryTableController.cpp
index d12f4c8..5750b41 100644
--- a/SecondaryTableController.cpp
+++ b/SecondaryTableController.cpp
@@ -37,10 +37,8 @@
 #include "SecondaryTableController.h"
 
 const char* SecondaryTableController::LOCAL_MANGLE_OUTPUT = "st_mangle_OUTPUT";
-const char* SecondaryTableController::LOCAL_MANGLE_EXEMPT = "st_mangle_EXEMPT";
-const char* SecondaryTableController::LOCAL_MANGLE_IFACE_FORMAT = "st_mangle_%s_OUTPUT";
+const char* SecondaryTableController::LOCAL_MANGLE_POSTROUTING = "st_mangle_POSTROUTING";
 const char* SecondaryTableController::LOCAL_NAT_POSTROUTING = "st_nat_POSTROUTING";
-const char* SecondaryTableController::LOCAL_FILTER_OUTPUT = "st_filter_OUTPUT";
 
 SecondaryTableController::SecondaryTableController(UidMarkMap *map) : mUidMarkMap(map) {
     int i;
@@ -61,15 +59,7 @@
             "-F",
             LOCAL_MANGLE_OUTPUT,
             NULL);
-    res |= execIptables(V4V6,
-            "-t",
-            "mangle",
-            "-F",
-            LOCAL_MANGLE_EXEMPT,
-            NULL);
-    // rule for skipping anything marked with the PROTECT_MARK
-    char protect_mark_str[11];
-    snprintf(protect_mark_str, sizeof(protect_mark_str), "%d", PROTECT_MARK);
+    // Do not mark sockets that have already been marked elsewhere(for example in DNS or protect).
     res |= execIptables(V4V6,
             "-t",
             "mangle",
@@ -77,8 +67,9 @@
             LOCAL_MANGLE_OUTPUT,
             "-m",
             "mark",
+            "!",
             "--mark",
-            protect_mark_str,
+            "0",
             "-j",
             "RETURN",
             NULL);
@@ -324,7 +315,38 @@
     }
 
     snprintf(mark_str, sizeof(mark_str), "%d", mark);
-    //add the catch all route to the tun. Route rules will make sure the right packets hit the table
+    // Flush any marked routes we added
+    if (!add) {
+        // iproute2 rule del will delete anything that matches, but only one rule at a time.
+        // So clearing the rules requires a bunch of calls.
+        // ip rule del will fail once there are no remaining rules that match.
+        const char *v4_cmd[] = {
+            IP_PATH,
+            "-4",
+            "rule",
+            "del",
+            "fwmark",
+            mark_str,
+            "table",
+            mark_str
+        };
+        while(!runCmd(ARRAY_SIZE(v4_cmd), v4_cmd)) {}
+
+        const char *v6_cmd[] = {
+            IP_PATH,
+            "-6",
+            "rule",
+            "del",
+            "fwmark",
+            mark_str,
+            "table",
+            mark_str
+        };
+        while(!runCmd(ARRAY_SIZE(v6_cmd), v6_cmd)) {}
+    }
+    // Add a route to the table to send all traffic to iface.
+    // We only need a default route because this table is only selected if a packet matches an
+    // IP rule that checks both the route and the mark.
     const char *route_cmd[] = {
         IP_PATH,
         "route",
@@ -336,22 +358,10 @@
         mark_str
     };
     ret = runCmd(ARRAY_SIZE(route_cmd), route_cmd);
+    // The command might fail during delete if the iface is gone
+    if (add && ret) return ret;
 
-    const char *fwmark_cmd[] = {
-        IP_PATH,
-        "rule",
-        add ? "add" : "del",
-        "prio",
-        RULE_PRIO,
-        "fwmark",
-        mark_str,
-        "table",
-        mark_str
-    };
-    ret = runCmd(ARRAY_SIZE(fwmark_cmd), fwmark_cmd);
-    if (ret) return ret;
-
-    //add rules for v6
+    // As above for IPv6
     const char *route6_cmd[] = {
         IP_PATH,
         "-6",
@@ -364,96 +374,25 @@
         mark_str
     };
     ret = runCmd(ARRAY_SIZE(route6_cmd), route6_cmd);
+    // The command might fail during delete if the iface is gone
+    if (add && ret) return ret;
 
-    const char *fwmark6_cmd[] = {
-        IP_PATH,
-        "-6",
-        "rule",
-        add ? "add" : "del",
-        "prio",
-        RULE_PRIO,
-        "fwmark",
-        mark_str,
-        "table",
-        mark_str
-    };
-    ret = runCmd(ARRAY_SIZE(fwmark6_cmd), fwmark6_cmd);
+    /* Best effort, because some kernels might not have the needed TCPMSS */
+    execIptables(V4V6,
+            "-t",
+            "mangle",
+            add ? "-A" : "-D",
+            LOCAL_MANGLE_POSTROUTING,
+            "-p", "tcp", "-o", iface, "--tcp-flags", "SYN,RST", "SYN",
+            "-j",
+            "TCPMSS",
+            "--clamp-mss-to-pmtu",
+            NULL);
 
-
-    if (ret) return ret;
-
-    //create the route rule chain
-    char chain_str[IFNAMSIZ + 18];
-    snprintf(chain_str, sizeof(chain_str), LOCAL_MANGLE_IFACE_FORMAT, iface);
-    //code split due to ordering requirements
-    if (add) {
-        ret = execIptables(V4V6,
-                "-t",
-                "mangle",
-                "-N",
-                chain_str,
-                NULL);
-        //set up the rule for sending premarked packets to the VPN chain
-        //Insert these at the top of the chain so they trigger before any UID rules
-        ret |= execIptables(V4V6,
-                "-t",
-                "mangle",
-                "-I",
-                LOCAL_MANGLE_OUTPUT,
-                "3",
-                "-m",
-                "mark",
-                "--mark",
-                mark_str,
-                "-g",
-                chain_str,
-                NULL);
-        //add a rule to clear the mark in the VPN chain
-        //packets marked with SO_MARK already have the iface's mark set but unless they match a
-        //route they should hit the network instead of the VPN
-        ret |= execIptables(V4V6,
-                "-t",
-                "mangle",
-                "-A",
-                chain_str,
-                "-j",
-                "MARK",
-                "--set-mark",
-                "0",
-                NULL);
-
-    } else {
-        ret = execIptables(V4V6,
-                "-t",
-                "mangle",
-                "-D",
-                LOCAL_MANGLE_OUTPUT,
-                "-m",
-                "mark",
-                "--mark",
-                mark_str,
-                "-g",
-                chain_str,
-                NULL);
-
-        //clear and delete the chain
-        ret |= execIptables(V4V6,
-                "-t",
-                "mangle",
-                "-F",
-                chain_str,
-                NULL);
-
-        ret |= execIptables(V4V6,
-                "-t",
-                "mangle",
-                "-X",
-                chain_str,
-                NULL);
-    }
-
-    //set up the needed source IP rewriting
-    //NOTE: Without ipv6 NAT in the kernel <3.7 only support V4 NAT
+    // Because the mark gets set after the intial routing decision the source IP address is that
+    // of the original out interface. The only way to change the source IP address to that of the
+    // VPN iface is using source NAT.
+    // TODO: Remove this when we get the mark set correctly before the first routing pass.
     ret = execIptables(V4,
             "-t",
             "nat",
@@ -471,7 +410,7 @@
 
     if (ret) return ret;
 
-    //try and set up for ipv6. ipv6 nat came in the kernel only in 3.7, so this can fail
+    // Try and set up NAT for IPv6 as well. This was only added in Linux 3.7 so this may fail.
     ret = execIptables(V6,
             "-t",
             "nat",
@@ -487,21 +426,28 @@
             "MASQUERADE",
             NULL);
     if (ret) {
-        //Without V6 NAT we can't do V6 over VPNs.
-        ret = execIptables(V6,
-                "-t",
-                "filter",
-                add ? "-A" : "-D",
-                LOCAL_FILTER_OUTPUT,
-                "-m",
-                "mark",
-                "--mark",
-                mark_str,
-                "-j",
-                "REJECT",
-                NULL);
+        // Without V6 NAT we can't do V6 over VPNs. If an IPv6 packet matches a VPN rule, then it
+        // will go out on the VPN interface, but without NAT, it will have the wrong source
+        // address. So reject all these packets.
+        // Due to rule application by the time the connection hits the output filter chain the
+        // routing pass based on the new mark has not yet happened. Reject in ip instead.
+        // TODO: Make the VPN code refuse to install IPv6 routes until we don't need IPv6 NAT.
+        const char *reject_cmd[] = {
+            IP_PATH,
+            "-6",
+            "route",
+            add ? "replace" : "del",
+            "unreachable",
+            "default",
+            "table",
+            mark_str
+        };
+        ret = runCmd(ARRAY_SIZE(reject_cmd), reject_cmd);
+        // The command might fail during delete if the iface is gone
+        if (add && ret) return ret;
+
     }
-    return ret;
+    return 0;
 
 }
 
@@ -522,24 +468,25 @@
     }
     int mark = tableIndex + BASE_TABLE_NUMBER;
     char mark_str[11] = {0};
-    char chain_str[IFNAMSIZ + 18];
     char dest_str[44]; // enough to store an IPv6 address + 3 character bitmask
 
     snprintf(mark_str, sizeof(mark_str), "%d", mark);
-    snprintf(chain_str, sizeof(chain_str), LOCAL_MANGLE_IFACE_FORMAT, iface);
     snprintf(dest_str, sizeof(dest_str), "%s/%d", dest, prefix);
-    return execIptables(getIptablesTarget(dest),
-            "-t",
-            "mangle",
-            add ? "-A" : "-D",
-            chain_str,
-            "-d",
-            dest_str,
-            "-j",
-            "MARK",
-            "--set-mark",
-            mark_str,
-            NULL);
+    const char *rule_cmd[] = {
+        IP_PATH,
+        getVersion(dest_str),
+        "rule",
+        add ? "add" : "del",
+        "prio",
+        RULE_PRIO,
+        "to",
+        dest_str,
+        "fwmark",
+        mark_str,
+        "table",
+        mark_str
+    };
+    return runCmd(ARRAY_SIZE(rule_cmd), rule_cmd);
 }
 
 int SecondaryTableController::addUidRule(const char *iface, int uid_start, int uid_end) {
@@ -569,9 +516,9 @@
         }
     }
     char uid_str[24] = {0};
-    char chain_str[IFNAMSIZ + 18];
     snprintf(uid_str, sizeof(uid_str), "%d-%d", uid_start, uid_end);
-    snprintf(chain_str, sizeof(chain_str), LOCAL_MANGLE_IFACE_FORMAT, iface);
+    char mark_str[11] = {0};
+    snprintf(mark_str, sizeof(mark_str), "%d", mark);
     return execIptables(V4V6,
             "-t",
             "mangle",
@@ -581,8 +528,10 @@
             "owner",
             "--uid-owner",
             uid_str,
-            "-g",
-            chain_str,
+            "-j",
+            "MARK",
+            "--set-mark",
+            mark_str,
             NULL);
 }
 
@@ -595,21 +544,6 @@
 }
 
 int SecondaryTableController::setHostExemption(const char *host, bool add) {
-    IptablesTarget target = !strcmp(getVersion(host), "-4") ? V4 : V6;
-    char protect_mark_str[11];
-    snprintf(protect_mark_str, sizeof(protect_mark_str), "%d", PROTECT_MARK);
-    int ret = execIptables(target,
-            "-t",
-            "mangle",
-            add ? "-A" : "-D",
-            LOCAL_MANGLE_EXEMPT,
-            "-d",
-            host,
-            "-j",
-            "MARK",
-            "--set-mark",
-            protect_mark_str,
-            NULL);
     const char *cmd[] = {
         IP_PATH,
         getVersion(host),
@@ -622,8 +556,7 @@
         "table",
         "main"
     };
-    ret |= runCmd(ARRAY_SIZE(cmd), cmd);
-    return ret;
+    return runCmd(ARRAY_SIZE(cmd), cmd);
 }
 
 void SecondaryTableController::getUidMark(SocketClient *cli, int uid) {
diff --git a/SecondaryTableController.h b/SecondaryTableController.h
index 81bb863..e286075 100644
--- a/SecondaryTableController.h
+++ b/SecondaryTableController.h
@@ -30,7 +30,6 @@
 static const int INTERFACES_TRACKED = 10;
 static const int BASE_TABLE_NUMBER = 60;
 static int MAX_TABLE_NUMBER = BASE_TABLE_NUMBER + INTERFACES_TRACKED;
-static const int PROTECT_MARK = 0x1;
 static const char *EXEMPT_PRIO = "99";
 static const char *RULE_PRIO = "100";
 
@@ -59,10 +58,8 @@
     int setupIptablesHooks();
 
     static const char* LOCAL_MANGLE_OUTPUT;
-    static const char* LOCAL_MANGLE_EXEMPT;
-    static const char* LOCAL_MANGLE_IFACE_FORMAT;
+    static const char* LOCAL_MANGLE_POSTROUTING;
     static const char* LOCAL_NAT_POSTROUTING;
-    static const char* LOCAL_FILTER_OUTPUT;
 
 
 private:
diff --git a/UidMarkMap.cpp b/UidMarkMap.cpp
index d30ac53..13630d3 100644
--- a/UidMarkMap.cpp
+++ b/UidMarkMap.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "UidMarkMap.h"
+#include "NetdConstants.h"
 
 UidMarkMap::UidMarkEntry::UidMarkEntry(int start, int end, int new_mark) :
                                             uid_start(start),
@@ -27,16 +28,9 @@
     if (uid_start > uid_end) {
         return false;
     }
-    android::netd::List<UidMarkEntry*>::iterator it;
-    for (it = mMap.begin(); it != mMap.end(); it++) {
-        UidMarkEntry *entry = *it;
-        if (entry->uid_start <= uid_end && uid_start <= entry->uid_end) {
-            return false;
-        }
-    }
 
     UidMarkEntry *e = new UidMarkEntry(uid_start, uid_end, mark);
-    mMap.push_back(e);
+    mMap.push_front(e);
     return true;
 };
 
@@ -63,7 +57,9 @@
             return entry->mark;
         }
     }
-    return -1;
+    // If the uid has no mark specified then it should be protected from any VPN rules that might
+    // be affecting the service acting on its behalf.
+    return PROTECT_MARK;
 };
 
 bool UidMarkMap::anyRulesForMark(int mark) {