Merge remote-tracking branch 'goog/mirror-m-wireless-internal-release'

Change-Id: I51337014e2851f47dd5e183c4bfdf39bafa59942
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/client/Android.mk b/client/Android.mk
index 0c5e7ea..d3393d0 100644
--- a/client/Android.mk
+++ b/client/Android.mk
@@ -16,10 +16,11 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_C_INCLUDES := bionic/libc/dns/include external/libcxx/include system/netd/include
+LOCAL_C_INCLUDES := bionic/libc/dns/include system/netd/include
 LOCAL_CLANG := true
 LOCAL_CPPFLAGS := -std=c++11 -Wall -Werror
 LOCAL_MODULE := libnetd_client
 LOCAL_SRC_FILES := FwmarkClient.cpp NetdClient.cpp
 
+include external/libcxx/libcxx.mk
 include $(BUILD_SHARED_LIBRARY)
diff --git a/client/FwmarkClient.cpp b/client/FwmarkClient.cpp
index db2009f..0ac1fbb 100644
--- a/client/FwmarkClient.cpp
+++ b/client/FwmarkClient.cpp
@@ -16,7 +16,9 @@
 
 #include "FwmarkClient.h"
 
+#include <errno.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <unistd.h>
@@ -41,7 +43,7 @@
 }
 
 int FwmarkClient::send(void* data, size_t len, int fd) {
-    mChannel = socket(AF_UNIX, SOCK_STREAM, 0);
+    mChannel = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
     if (mChannel == -1) {
         return -errno;
     }
diff --git a/client/NetdClient.cpp b/client/NetdClient.cpp
index fce362e..3157d3a 100644
--- a/client/NetdClient.cpp
+++ b/client/NetdClient.cpp
@@ -16,15 +16,17 @@
 
 #include "NetdClient.h"
 
+#include <errno.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <atomic>
+
 #include "Fwmark.h"
 #include "FwmarkClient.h"
 #include "FwmarkCommand.h"
 #include "resolv_netid.h"
 
-#include <atomic>
-#include <sys/socket.h>
-#include <unistd.h>
-
 namespace {
 
 std::atomic_uint netIdForProcess(NETID_UNSET);
@@ -116,9 +118,9 @@
     // might itself cause another check with the fwmark server, which would be wasteful.
     int socketFd;
     if (libcSocket) {
-        socketFd = libcSocket(AF_INET6, SOCK_DGRAM, 0);
+        socketFd = libcSocket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
     } else {
-        socketFd = socket(AF_INET6, SOCK_DGRAM, 0);
+        socketFd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
     }
     if (socketFd < 0) {
         return -errno;
diff --git a/server/Android.mk b/server/Android.mk
index 9111d84..ecc8829 100644
--- a/server/Android.mk
+++ b/server/Android.mk
@@ -19,9 +19,7 @@
 LOCAL_C_INCLUDES := \
         $(call include-path-for, libhardware_legacy)/hardware_legacy \
         bionic/libc/dns/include \
-        external/libcxx/include \
         external/mdnsresponder/mDNSShared \
-        external/openssl/include \
         system/netd/include \
 
 LOCAL_CLANG := true
@@ -37,7 +35,13 @@
         liblogwrap \
         libmdnssd \
         libnetutils \
+        libnl \
         libsysutils \
+        libbase \
+        libutils \
+
+LOCAL_STATIC_LIBRARIES := \
+        libpcap \
 
 LOCAL_SRC_FILES := \
         BandwidthController.cpp \
@@ -63,12 +67,14 @@
         ResolverController.cpp \
         RouteController.cpp \
         SoftapController.cpp \
+        StrictController.cpp \
         TetherController.cpp \
         UidRanges.cpp \
         VirtualNetwork.cpp \
         main.cpp \
         oem_iptables_hook.cpp \
 
+include external/libcxx/libcxx.mk
 include $(BUILD_EXECUTABLE)
 
 include $(CLEAR_VARS)
diff --git a/server/BandwidthController.cpp b/server/BandwidthController.cpp
index e1db680..e5cf36c 100644
--- a/server/BandwidthController.cpp
+++ b/server/BandwidthController.cpp
@@ -786,7 +786,7 @@
         return -1;
 
     asprintf(&fname, "/proc/net/xt_quota/%s", costName);
-    fp = fopen(fname, "r");
+    fp = fopen(fname, "re");
     free(fname);
     if (!fp) {
         ALOGE("Reading quota %s failed (%s)", costName, strerror(errno));
@@ -843,7 +843,7 @@
     }
 
     asprintf(&fname, "/proc/net/xt_quota/%s", quotaName);
-    fp = fopen(fname, "w");
+    fp = fopen(fname, "we");
     free(fname);
     if (!fp) {
         ALOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
@@ -1100,12 +1100,12 @@
         return -1;
     }
 
-    asprintf(&alertName, "%sAlert", costName);
     if (!*alertBytes) {
         ALOGE("No prior alert set for %s alert", costName);
         return -1;
     }
 
+    asprintf(&alertName, "%sAlert", costName);
     asprintf(&chainName, "bw_costly_%s", costName);
     asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName);
     res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
diff --git a/server/CommandListener.cpp b/server/CommandListener.cpp
index 69e285f..d795396 100644
--- a/server/CommandListener.cpp
+++ b/server/CommandListener.cpp
@@ -92,6 +92,7 @@
 ResolverController *CommandListener::sResolverCtrl = NULL;
 FirewallController *CommandListener::sFirewallCtrl = NULL;
 ClatdController *CommandListener::sClatdCtrl = NULL;
+StrictController *CommandListener::sStrictCtrl = NULL;
 
 /**
  * List of module chains to be created, along with explicit ordering. ORDERING
@@ -116,6 +117,7 @@
 static const char* FILTER_OUTPUT[] = {
         OEM_IPTABLES_FILTER_OUTPUT,
         FirewallController::LOCAL_OUTPUT,
+        StrictController::LOCAL_OUTPUT,
         BandwidthController::LOCAL_OUTPUT,
         NULL,
 };
@@ -182,6 +184,7 @@
     registerCmd(new FirewallCmd());
     registerCmd(new ClatdCmd());
     registerCmd(new NetworkCommand());
+    registerCmd(new StrictCmd());
 
     if (!sNetCtrl)
         sNetCtrl = new NetworkController();
@@ -205,6 +208,8 @@
         sInterfaceCtrl = new InterfaceController();
     if (!sClatdCtrl)
         sClatdCtrl = new ClatdController(sNetCtrl);
+    if (!sStrictCtrl)
+        sStrictCtrl = new StrictController();
 
     /*
      * This is the only time we touch top-level chains in iptables; controllers
@@ -1397,6 +1402,76 @@
     return 0;
 }
 
+CommandListener::StrictCmd::StrictCmd() :
+    NetdCommand("strict") {
+}
+
+int CommandListener::StrictCmd::sendGenericOkFail(SocketClient *cli, int cond) {
+    if (!cond) {
+        cli->sendMsg(ResponseCode::CommandOkay, "Strict command succeeded", false);
+    } else {
+        cli->sendMsg(ResponseCode::OperationFailed, "Strict command failed", false);
+    }
+    return 0;
+}
+
+StrictPenalty CommandListener::StrictCmd::parsePenalty(const char* arg) {
+    if (!strcmp(arg, "reject")) {
+        return REJECT;
+    } else if (!strcmp(arg, "log")) {
+        return LOG;
+    } else if (!strcmp(arg, "accept")) {
+        return ACCEPT;
+    } else {
+        return INVALID;
+    }
+}
+
+int CommandListener::StrictCmd::runCommand(SocketClient *cli, int argc,
+        char **argv) {
+    if (argc < 2) {
+        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing command", false);
+        return 0;
+    }
+
+    if (!strcmp(argv[1], "enable")) {
+        int res = sStrictCtrl->enableStrict();
+        return sendGenericOkFail(cli, res);
+    }
+    if (!strcmp(argv[1], "disable")) {
+        int res = sStrictCtrl->disableStrict();
+        return sendGenericOkFail(cli, res);
+    }
+
+    if (!strcmp(argv[1], "set_uid_cleartext_policy")) {
+        if (argc != 4) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError,
+                         "Usage: strict set_uid_cleartext_policy <uid> <accept|log|reject>",
+                         false);
+            return 0;
+        }
+
+        errno = 0;
+        unsigned long int uid = strtoul(argv[2], NULL, 0);
+        if (errno || uid > UID_MAX) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError, "Invalid UID", false);
+            return 0;
+        }
+
+        StrictPenalty penalty = parsePenalty(argv[3]);
+        if (penalty == INVALID) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError, "Invalid penalty argument", false);
+            return 0;
+        }
+
+        int res = sStrictCtrl->setUidCleartextPenalty((uid_t) uid, penalty);
+        return sendGenericOkFail(cli, res);
+    }
+
+    cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown command", false);
+    return 0;
+}
+
 CommandListener::NetworkCommand::NetworkCommand() : NetdCommand("network") {
 }
 
diff --git a/server/CommandListener.h b/server/CommandListener.h
index f605647..b71bc45 100644
--- a/server/CommandListener.h
+++ b/server/CommandListener.h
@@ -31,6 +31,7 @@
 #include "ResolverController.h"
 #include "FirewallController.h"
 #include "ClatdController.h"
+#include "StrictController.h"
 
 class CommandListener : public FrameworkListener {
     static TetherController *sTetherCtrl;
@@ -43,6 +44,7 @@
     static ResolverController *sResolverCtrl;
     static FirewallController *sFirewallCtrl;
     static ClatdController *sClatdCtrl;
+    static StrictController *sStrictCtrl;
 
 public:
     static NetworkController *sNetCtrl;
@@ -143,6 +145,16 @@
         int runCommand(SocketClient *c, int argc, char ** argv);
     };
 
+    class StrictCmd : public NetdCommand {
+    public:
+        StrictCmd();
+        virtual ~StrictCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    protected:
+        int sendGenericOkFail(SocketClient *cli, int cond);
+        static StrictPenalty parsePenalty(const char* arg);
+    };
+
     class NetworkCommand : public NetdCommand {
     public:
         NetworkCommand();
diff --git a/server/ConnmarkFlags.h b/server/ConnmarkFlags.h
new file mode 100644
index 0000000..2bbefc0
--- /dev/null
+++ b/server/ConnmarkFlags.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CONNMARK_FLAGS_H
+#define _CONNMARK_FLAGS_H
+
+/*
+ * iptables CONNMARK flag values used by various controllers. These values
+ * need to be stored in one place to avoid clashes.
+ */
+class ConnmarkFlags {
+public:
+    static const unsigned int STRICT_RESOLVED_ACCEPT = 0x01000000;
+    static const unsigned int STRICT_RESOLVED_REJECT = 0x02000000;
+};
+
+#endif
diff --git a/server/DnsProxyListener.cpp b/server/DnsProxyListener.cpp
index 6e27057..a0f0a30 100644
--- a/server/DnsProxyListener.cpp
+++ b/server/DnsProxyListener.cpp
@@ -83,12 +83,15 @@
     return NULL;
 }
 
+static bool sendBE32(SocketClient* c, uint32_t data) {
+    uint32_t be_data = htonl(data);
+    return c->sendData(&be_data, sizeof(be_data)) == 0;
+}
+
 // Sends 4 bytes of big-endian length, followed by the data.
 // Returns true on success.
-static bool sendLenAndData(SocketClient *c, const int len, const void* data) {
-    uint32_t len_be = htonl(len);
-    return c->sendData(&len_be, 4) == 0 &&
-        (len == 0 || c->sendData(data, len) == 0);
+static bool sendLenAndData(SocketClient* c, const int len, const void* data) {
+    return sendBE32(c, len) && (len == 0 || c->sendData(data, len) == 0);
 }
 
 // Returns true on success
@@ -119,6 +122,42 @@
     return success;
 }
 
+static bool sendaddrinfo(SocketClient* c, struct addrinfo* ai) {
+    // struct addrinfo {
+    //      int     ai_flags;       /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
+    //      int     ai_family;      /* PF_xxx */
+    //      int     ai_socktype;    /* SOCK_xxx */
+    //      int     ai_protocol;    /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
+    //      socklen_t ai_addrlen;   /* length of ai_addr */
+    //      char    *ai_canonname;  /* canonical name for hostname */
+    //      struct  sockaddr *ai_addr;      /* binary address */
+    //      struct  addrinfo *ai_next;      /* next structure in linked list */
+    // };
+
+    // Write the struct piece by piece because we might be a 64-bit netd
+    // talking to a 32-bit process.
+    bool success =
+            sendBE32(c, ai->ai_flags) &&
+            sendBE32(c, ai->ai_family) &&
+            sendBE32(c, ai->ai_socktype) &&
+            sendBE32(c, ai->ai_protocol);
+    if (!success) {
+        return false;
+    }
+
+    // ai_addrlen and ai_addr.
+    if (!sendLenAndData(c, ai->ai_addrlen, ai->ai_addr)) {
+        return false;
+    }
+
+    // strlen(ai_canonname) and ai_canonname.
+    if (!sendLenAndData(c, ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0, ai->ai_canonname)) {
+        return false;
+    }
+
+    return true;
+}
+
 void DnsProxyListener::GetAddrInfoHandler::run() {
     if (DBG) {
         ALOGD("GetAddrInfoHandler, now for %s / %s / %u / %u", mHost, mService, mNetId, mMark);
@@ -133,14 +172,10 @@
         bool success = !mClient->sendCode(ResponseCode::DnsProxyQueryResult);
         struct addrinfo* ai = result;
         while (ai && success) {
-            success = sendLenAndData(mClient, sizeof(struct addrinfo), ai)
-                && sendLenAndData(mClient, ai->ai_addrlen, ai->ai_addr)
-                && sendLenAndData(mClient,
-                                  ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0,
-                                  ai->ai_canonname);
+            success = sendBE32(mClient, 1) && sendaddrinfo(mClient, ai);
             ai = ai->ai_next;
         }
-        success = success && sendLenAndData(mClient, 0, "");
+        success = success && sendBE32(mClient, 0);
         if (!success) {
             ALOGW("Error writing DNS result to client");
         }
diff --git a/server/InterfaceController.cpp b/server/InterfaceController.cpp
index b7a4d0b..76f5e05 100644
--- a/server/InterfaceController.cpp
+++ b/server/InterfaceController.cpp
@@ -15,14 +15,22 @@
  */
 
 #include <dirent.h>
+#include <errno.h>
+#include <malloc.h>
 
 #define LOG_TAG "InterfaceController"
+#include <base/file.h>
+#include <base/stringprintf.h>
 #include <cutils/log.h>
 #include <logwrap/logwrap.h>
 
 #include "InterfaceController.h"
 #include "RouteController.h"
 
+using android::base::ReadFileToString;
+using android::base::StringPrintf;
+using android::base::WriteStringToFile;
+
 const char ipv6_proc_path[] = "/proc/sys/net/ipv6/conf";
 
 const char sys_net_path[] = "/sys/class/net";
@@ -47,15 +55,12 @@
 }
 
 int InterfaceController::writeIPv6ProcPath(const char *interface, const char *setting, const char *value) {
-	char *path;
 	if (!isIfaceName(interface)) {
 		errno = ENOENT;
 		return -1;
 	}
-	asprintf(&path, "%s/%s/%s", ipv6_proc_path, interface, setting);
-	int success = writeFile(path, value, strlen(value));
-	free(path);
-	return success;
+	std::string path(StringPrintf("%s/%s/%s", ipv6_proc_path, interface, setting));
+	return WriteStringToFile(value, path);
 }
 
 int InterfaceController::setEnableIPv6(const char *interface, const int on) {
@@ -132,23 +137,18 @@
 //             ID to get the table. If it's set to -1000, routes from interface ID 5 will go into
 //             table 1005, etc.
 void InterfaceController::setAcceptRARouteTable(int tableOrOffset) {
-	char* value;
-	asprintf(&value, "%d", tableOrOffset);
-	setOnAllInterfaces("accept_ra_rt_table", value);
-	free(value);
+	std::string value(StringPrintf("%d", tableOrOffset));
+	setOnAllInterfaces("accept_ra_rt_table", value.c_str());
 }
 
 int InterfaceController::setMtu(const char *interface, const char *mtu)
 {
-	char *path;
 	if (!isIfaceName(interface)) {
 		errno = ENOENT;
 		return -1;
 	}
-	asprintf(&path, "%s/%s/mtu", sys_net_path, interface);
-	int success = writeFile(path, mtu, strlen(mtu));
-	free(path);
-	return success;
+	std::string path(StringPrintf("%s/%s/mtu", sys_net_path, interface));
+	return WriteStringToFile(mtu, path);
 }
 
 void InterfaceController::setIPv6OptimisticMode(const char *value) {
diff --git a/server/NetdConstants.cpp b/server/NetdConstants.cpp
index 4823c91..d2e0bbe 100644
--- a/server/NetdConstants.cpp
+++ b/server/NetdConstants.cpp
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <netdb.h>
+#include <net/if.h>
 #include <netinet/in.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/wait.h>
-#include <ctype.h>
-#include <net/if.h>
 
 #define LOG_TAG "Netd"
 
@@ -112,43 +113,6 @@
     return res;
 }
 
-int writeFile(const char *path, const char *value, int size) {
-    int fd = open(path, O_WRONLY);
-    if (fd < 0) {
-        ALOGE("Failed to open %s: %s", path, strerror(errno));
-        return -1;
-    }
-
-    if (write(fd, value, size) != size) {
-        ALOGE("Failed to write %s: %s", path, strerror(errno));
-        close(fd);
-        return -1;
-    }
-    close(fd);
-    return 0;
-}
-
-int readFile(const char *path, char *buf, int *sizep)
-{
-    int fd = open(path, O_RDONLY);
-    int size;
-
-    if (fd < 0) {
-        ALOGE("Failed to open %s: %s", path, strerror(errno));
-        return -1;
-    }
-
-    size = read(fd, buf, *sizep);
-    if (size < 0) {
-        ALOGE("Failed to write %s: %s", path, strerror(errno));
-        close(fd);
-        return -1;
-    }
-    *sizep = size;
-    close(fd);
-    return 0;
-}
-
 /*
  * Check an interface name for plausibility. This should e.g. help against
  * directory traversal.
diff --git a/server/NetdConstants.h b/server/NetdConstants.h
index 7b894a8..296661d 100644
--- a/server/NetdConstants.h
+++ b/server/NetdConstants.h
@@ -35,8 +35,6 @@
 
 int execIptables(IptablesTarget target, ...);
 int execIptablesSilently(IptablesTarget target, ...);
-int writeFile(const char *path, const char *value, int size);
-int readFile(const char *path, char *buf, int *sizep);
 bool isIfaceName(const char *name);
 int parsePrefix(const char *prefix, uint8_t *family, void *address, int size, uint8_t *prefixlen);
 
diff --git a/server/NetlinkHandler.cpp b/server/NetlinkHandler.cpp
index 6c81c18..5535119 100644
--- a/server/NetlinkHandler.cpp
+++ b/server/NetlinkHandler.cpp
@@ -58,26 +58,26 @@
     }
 
     if (!strcmp(subsys, "net")) {
-        int action = evt->getAction();
+        NetlinkEvent::Action action = evt->getAction();
         const char *iface = evt->findParam("INTERFACE");
 
-        if (action == evt->NlActionAdd) {
+        if (action == NetlinkEvent::Action::kAdd) {
             notifyInterfaceAdded(iface);
-        } else if (action == evt->NlActionRemove) {
+        } else if (action == NetlinkEvent::Action::kRemove) {
             notifyInterfaceRemoved(iface);
-        } else if (action == evt->NlActionChange) {
+        } else if (action == NetlinkEvent::Action::kChange) {
             evt->dump();
             notifyInterfaceChanged("nana", true);
-        } else if (action == evt->NlActionLinkUp) {
+        } else if (action == NetlinkEvent::Action::kLinkUp) {
             notifyInterfaceLinkChanged(iface, true);
-        } else if (action == evt->NlActionLinkDown) {
+        } else if (action == NetlinkEvent::Action::kLinkDown) {
             notifyInterfaceLinkChanged(iface, false);
-        } else if (action == evt->NlActionAddressUpdated ||
-                   action == evt->NlActionAddressRemoved) {
+        } else if (action == NetlinkEvent::Action::kAddressUpdated ||
+                   action == NetlinkEvent::Action::kAddressRemoved) {
             const char *address = evt->findParam("ADDRESS");
             const char *flags = evt->findParam("FLAGS");
             const char *scope = evt->findParam("SCOPE");
-            if (action == evt->NlActionAddressRemoved && iface && address) {
+            if (action == NetlinkEvent::Action::kAddressRemoved && iface && address) {
                 int resetMask = strchr(address, ':') ? RESET_IPV6_ADDRESSES : RESET_IPV4_ADDRESSES;
                 resetMask |= RESET_IGNORE_INTERFACE_ADDRESS;
                 if (int ret = ifc_reset_connections(iface, resetMask)) {
@@ -88,14 +88,14 @@
             if (iface && flags && scope) {
                 notifyAddressChanged(action, address, iface, flags, scope);
             }
-        } else if (action == evt->NlActionRdnss) {
+        } else if (action == NetlinkEvent::Action::kRdnss) {
             const char *lifetime = evt->findParam("LIFETIME");
             const char *servers = evt->findParam("SERVERS");
             if (lifetime && servers) {
                 notifyInterfaceDnsServers(iface, lifetime, servers);
             }
-        } else if (action == evt->NlActionRouteUpdated ||
-                   action == evt->NlActionRouteRemoved) {
+        } else if (action == NetlinkEvent::Action::kRouteUpdated ||
+                   action == NetlinkEvent::Action::kRouteRemoved) {
             const char *route = evt->findParam("ROUTE");
             const char *gateway = evt->findParam("GATEWAY");
             const char *iface = evt->findParam("INTERFACE");
@@ -109,6 +109,11 @@
         const char *iface = evt->findParam("INTERFACE");
         notifyQuotaLimitReached(alertName, iface);
 
+    } else if (!strcmp(subsys, "strict")) {
+        const char *uid = evt->findParam("UID");
+        const char *hex = evt->findParam("HEX");
+        notifyStrictCleartext(uid, hex);
+
     } else if (!strcmp(subsys, "xt_idletimer")) {
         const char *label = evt->findParam("INTERFACE");
         const char *state = evt->findParam("STATE");
@@ -169,12 +174,12 @@
            "IfaceClass %s %s %s", isActive ? "active" : "idle", name, timestamp);
 }
 
-void NetlinkHandler::notifyAddressChanged(int action, const char *addr,
+void NetlinkHandler::notifyAddressChanged(NetlinkEvent::Action action, const char *addr,
                                           const char *iface, const char *flags,
                                           const char *scope) {
     notify(ResponseCode::InterfaceAddressChange,
            "Address %s %s %s %s %s",
-           (action == NetlinkEvent::NlActionAddressUpdated) ? kUpdated : kRemoved,
+           (action == NetlinkEvent::Action::kAddressUpdated) ? kUpdated : kRemoved,
            addr, iface, flags, scope);
 }
 
@@ -185,14 +190,18 @@
            iface, lifetime, servers);
 }
 
-void NetlinkHandler::notifyRouteChange(int action, const char *route,
+void NetlinkHandler::notifyRouteChange(NetlinkEvent::Action action, const char *route,
                                        const char *gateway, const char *iface) {
     notify(ResponseCode::RouteChange,
            "Route %s %s%s%s%s%s",
-           (action == NetlinkEvent::NlActionRouteUpdated) ? kUpdated : kRemoved,
+           (action == NetlinkEvent::Action::kRouteUpdated) ? kUpdated : kRemoved,
            route,
            *gateway ? " via " : "",
            gateway,
            *iface ? " dev " : "",
            iface);
 }
+
+void NetlinkHandler::notifyStrictCleartext(const char* uid, const char* hex) {
+    notify(ResponseCode::StrictCleartext, "%s %s", uid, hex);
+}
diff --git a/server/NetlinkHandler.h b/server/NetlinkHandler.h
index 83baa2b..c70867e 100644
--- a/server/NetlinkHandler.h
+++ b/server/NetlinkHandler.h
@@ -17,6 +17,7 @@
 #ifndef _NETLINKHANDLER_H
 #define _NETLINKHANDLER_H
 
+#include <sysutils/NetlinkEvent.h>
 #include <sysutils/NetlinkListener.h>
 #include "NetlinkManager.h"
 
@@ -41,10 +42,11 @@
     void notifyQuotaLimitReached(const char *name, const char *iface);
     void notifyInterfaceClassActivity(const char *name, bool isActive,
                                       const char *timestamp);
-    void notifyAddressChanged(int action, const char *addr, const char *iface,
+    void notifyAddressChanged(NetlinkEvent::Action action, const char *addr, const char *iface,
                               const char *flags, const char *scope);
     void notifyInterfaceDnsServers(const char *iface, const char *lifetime,
                                    const char *servers);
-    void notifyRouteChange(int action, const char *route, const char *gateway, const char *iface);
+    void notifyRouteChange(NetlinkEvent::Action action, const char *route, const char *gateway, const char *iface);
+    void notifyStrictCleartext(const char* uid, const char* hex);
 };
 #endif
diff --git a/server/NetlinkManager.cpp b/server/NetlinkManager.cpp
index 1d731ac..76af46f 100644
--- a/server/NetlinkManager.cpp
+++ b/server/NetlinkManager.cpp
@@ -30,10 +30,24 @@
 
 #include <cutils/log.h>
 
+#include <netlink/attr.h>
+#include <netlink/genl/genl.h>
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_log.h>
+#include <linux/netfilter/nfnetlink_compat.h>
+
+#include <arpa/inet.h>
+
 #include "NetlinkManager.h"
 #include "NetlinkHandler.h"
 
+#include "pcap-netfilter-linux-android.h"
+
 const int NetlinkManager::NFLOG_QUOTA_GROUP = 1;
+const int NetlinkManager::NETFILTER_STRICT_GROUP = 2;
 
 NetlinkManager *NetlinkManager::sInstance = NULL;
 
@@ -51,7 +65,7 @@
 }
 
 NetlinkHandler *NetlinkManager::setupSocket(int *sock, int netlinkFamily,
-    int groups, int format) {
+    int groups, int format, bool configNflog) {
 
     struct sockaddr_nl nladdr;
     int sz = 64 * 1024;
@@ -62,7 +76,7 @@
     nladdr.nl_pid = getpid();
     nladdr.nl_groups = groups;
 
-    if ((*sock = socket(PF_NETLINK, SOCK_DGRAM, netlinkFamily)) < 0) {
+    if ((*sock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, netlinkFamily)) < 0) {
         ALOGE("Unable to create netlink socket: %s", strerror(errno));
         return NULL;
     }
@@ -85,6 +99,21 @@
         return NULL;
     }
 
+    if (configNflog) {
+        if (android_nflog_send_config_cmd(*sock, 0, NFULNL_CFG_CMD_PF_UNBIND, AF_INET) < 0) {
+            ALOGE("Failed NFULNL_CFG_CMD_PF_UNBIND: %s", strerror(errno));
+            return NULL;
+        }
+        if (android_nflog_send_config_cmd(*sock, 0, NFULNL_CFG_CMD_PF_BIND, AF_INET) < 0) {
+            ALOGE("Failed NFULNL_CFG_CMD_PF_BIND: %s", strerror(errno));
+            return NULL;
+        }
+        if (android_nflog_send_config_cmd(*sock, 0, NFULNL_CFG_CMD_BIND, AF_UNSPEC) < 0) {
+            ALOGE("Failed NFULNL_CFG_CMD_BIND: %s", strerror(errno));
+            return NULL;
+        }
+    }
+
     NetlinkHandler *handler = new NetlinkHandler(this, *sock, format);
     if (handler->start()) {
         ALOGE("Unable to start NetlinkHandler: %s", strerror(errno));
@@ -97,7 +126,7 @@
 
 int NetlinkManager::start() {
     if ((mUeventHandler = setupSocket(&mUeventSock, NETLINK_KOBJECT_UEVENT,
-         0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII)) == NULL) {
+         0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII, false)) == NULL) {
         return -1;
     }
 
@@ -107,13 +136,19 @@
                                      RTMGRP_IPV6_IFADDR |
                                      RTMGRP_IPV6_ROUTE |
                                      (1 << (RTNLGRP_ND_USEROPT - 1)),
-         NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {
+         NetlinkListener::NETLINK_FORMAT_BINARY, false)) == NULL) {
         return -1;
     }
 
     if ((mQuotaHandler = setupSocket(&mQuotaSock, NETLINK_NFLOG,
-        NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {
-        ALOGE("Unable to open quota2 logging socket");
+            NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY, false)) == NULL) {
+        ALOGE("Unable to open quota socket");
+        // TODO: return -1 once the emulator gets a new kernel.
+    }
+
+    if ((mStrictHandler = setupSocket(&mStrictSock, NETLINK_NETFILTER,
+            0, NetlinkListener::NETLINK_FORMAT_BINARY_UNICAST, true)) == NULL) {
+        ALOGE("Unable to open strict socket");
         // TODO: return -1 once the emulator gets a new kernel.
     }
 
@@ -158,5 +193,18 @@
         mQuotaSock = -1;
     }
 
+    if (mStrictHandler) {
+        if (mStrictHandler->stop()) {
+            ALOGE("Unable to stop strict NetlinkHandler: %s", strerror(errno));
+            status = -1;
+        }
+
+        delete mStrictHandler;
+        mStrictHandler = NULL;
+
+        close(mStrictSock);
+        mStrictSock = -1;
+    }
+
     return status;
 }
diff --git a/server/NetlinkManager.h b/server/NetlinkManager.h
index 5187a59..40a5722 100644
--- a/server/NetlinkManager.h
+++ b/server/NetlinkManager.h
@@ -32,9 +32,11 @@
     NetlinkHandler       *mUeventHandler;
     NetlinkHandler       *mRouteHandler;
     NetlinkHandler       *mQuotaHandler;
+    NetlinkHandler       *mStrictHandler;
     int                  mUeventSock;
     int                  mRouteSock;
     int                  mQuotaSock;
+    int                  mStrictSock;
 
 public:
     virtual ~NetlinkManager();
@@ -47,15 +49,14 @@
 
     static NetlinkManager *Instance();
 
-    /* This is the nflog group arg that the xt_quota2 neftiler will use. */
+    /* Group used by xt_quota2 */
     static const int NFLOG_QUOTA_GROUP;
-
-    /* This is the group that the xt_IDLETIMER netfilter will use. */
-    static const int IDLETIMER_GROUP;
+    /* Group used by StrictController rules */
+    static const int NETFILTER_STRICT_GROUP;
 
 private:
     NetlinkManager();
     NetlinkHandler* setupSocket(int *sock, int netlinkFamily, int groups,
-        int format);
+        int format, bool configNflog);
 };
 #endif
diff --git a/server/PppController.cpp b/server/PppController.cpp
index 6b54c31..581b9c6 100644
--- a/server/PppController.cpp
+++ b/server/PppController.cpp
@@ -85,6 +85,8 @@
         char *lr;
 
         asprintf(&lr, "%s:%s", l, r);
+        free(l);
+        free(r);
 
         snprintf(dev, sizeof(dev), "/dev/%s", tty);
 
@@ -94,6 +96,9 @@
                   lr, "ms-dns", d1, "ms-dns", d2, "lcp-max-configure", "99999", (char *) NULL)) {
             ALOGE("execl failed (%s)", strerror(errno));
         }
+        free(lr);
+        free(d1);
+        free(d2);
         ALOGE("Should never get here!");
         return 0;
     } else {
diff --git a/server/ResponseCode.h b/server/ResponseCode.h
index 6a0c22c..19d76c3 100644
--- a/server/ResponseCode.h
+++ b/server/ResponseCode.h
@@ -77,5 +77,6 @@
     static const int InterfaceAddressChange         = 614;
     static const int InterfaceDnsInfo               = 615;
     static const int RouteChange                    = 616;
+    static const int StrictCleartext                = 617;
 };
 #endif
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index cdca2de..889779d 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -16,22 +16,29 @@
 
 #include "RouteController.h"
 
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fib_rules.h>
+#include <net/if.h>
+#include <sys/stat.h>
+
+#include <private/android_filesystem_config.h>
+
+#include <map>
+
 #include "Fwmark.h"
 #include "UidRanges.h"
 #include "DummyNetwork.h"
 
+#include "base/file.h"
 #define LOG_TAG "Netd"
 #include "log/log.h"
 #include "logwrap/logwrap.h"
 #include "netutils/ifc.h"
 #include "resolv_netid.h"
 
-#include <arpa/inet.h>
-#include <fcntl.h>
-#include <linux/fib_rules.h>
-#include <map>
-#include <net/if.h>
-#include <sys/stat.h>
+using android::base::WriteStringToFile;
 
 namespace {
 
@@ -93,7 +100,6 @@
 const bool MODIFY_NON_UID_BASED_RULES = true;
 
 const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables";
-const int RT_TABLES_FLAGS = O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC;
 const mode_t RT_TABLES_MODE = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;  // mode 0644, rw-r--r--
 
 const unsigned ROUTE_FLUSH_ATTEMPTS = 2;
@@ -164,21 +170,10 @@
         addTableName(entry.second, entry.first, &contents);
     }
 
-    int fd = open(RT_TABLES_PATH, RT_TABLES_FLAGS, RT_TABLES_MODE);
-    if (fd == -1) {
-        ALOGE("failed to create %s (%s)", RT_TABLES_PATH, strerror(errno));
+    if (!WriteStringToFile(contents, RT_TABLES_PATH, RT_TABLES_MODE, AID_SYSTEM, AID_WIFI)) {
+        ALOGE("failed to write to %s (%s)", RT_TABLES_PATH, strerror(errno));
         return;
     }
-    // File creation is affected by umask, so make sure the right mode bits are set.
-    if (fchmod(fd, RT_TABLES_MODE) == -1) {
-        ALOGE("failed to set mode 0%o on %s (%s)", RT_TABLES_MODE, RT_TABLES_PATH, strerror(errno));
-    }
-    ssize_t bytesWritten = write(fd, contents.data(), contents.size());
-    if (bytesWritten != static_cast<ssize_t>(contents.size())) {
-        ALOGE("failed to write to %s (%zd vs %zu bytes) (%s)", RT_TABLES_PATH, bytesWritten,
-              contents.size(), strerror(errno));
-    }
-    close(fd);
 }
 
 // Sends a netlink request and expects an ack.
@@ -202,7 +197,7 @@
         nlmsgerr err;
     } response;
 
-    int sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+    int sock = socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
     if (sock != -1 &&
             connect(sock, reinterpret_cast<const sockaddr*>(&NETLINK_ADDRESS),
                     sizeof(NETLINK_ADDRESS)) != -1 &&
diff --git a/server/SoftapController.cpp b/server/SoftapController.cpp
index 853ca8f..d9e40a4 100644
--- a/server/SoftapController.cpp
+++ b/server/SoftapController.cpp
@@ -34,6 +34,8 @@
 #include <openssl/sha.h>
 
 #define LOG_TAG "SoftapController"
+#include <base/file.h>
+#include <base/stringprintf.h>
 #include <cutils/log.h>
 #include <netutils/ifc.h>
 #include <private/android_filesystem_config.h>
@@ -42,6 +44,9 @@
 
 #include "SoftapController.h"
 
+using android::base::StringPrintf;
+using android::base::WriteStringToFile;
+
 static const char HOSTAPD_CONF_FILE[]    = "/data/misc/wifi/hostapd.conf";
 static const char HOSTAPD_BIN_FILE[]    = "/system/bin/hostapd";
 
@@ -116,13 +121,8 @@
  *  argv[7] - Key
  */
 int SoftapController::setSoftap(int argc, char *argv[]) {
-    char psk_str[2*SHA256_DIGEST_LENGTH+1];
-    int ret = ResponseCode::SoftapStatusResult;
-    int fd;
     int hidden = 0;
     int channel = AP_CHANNEL_DEFAULT;
-    char *wbuf = NULL;
-    char *fbuf = NULL;
 
     if (argc < 5) {
         ALOGE("Softap set is missing arguments. Please use:");
@@ -139,62 +139,42 @@
             channel = AP_CHANNEL_DEFAULT;
     }
 
-    asprintf(&wbuf, "interface=%s\ndriver=nl80211\nctrl_interface="
-            "/data/misc/wifi/hostapd\nssid=%s\nchannel=%d\nieee80211n=1\n"
-            "hw_mode=%c\nignore_broadcast_ssid=%d\nwowlan_triggers=any\n",
-            argv[2], argv[3], channel, (channel <= 14) ? 'g' : 'a', hidden);
+    std::string wbuf(StringPrintf("interface=%s\n"
+            "driver=nl80211\n"
+            "ctrl_interface=/data/misc/wifi/hostapd\n"
+            "ssid=%s\n"
+            "channel=%d\n"
+            "ieee80211n=1\n"
+            "hw_mode=%c\n"
+            "ignore_broadcast_ssid=%d\n"
+            "wowlan_triggers=any\n",
+            argv[2], argv[3], channel, (channel <= 14) ? 'g' : 'a', hidden));
 
+    std::string fbuf;
     if (argc > 7) {
+        char psk_str[2*SHA256_DIGEST_LENGTH+1];
         if (!strcmp(argv[6], "wpa-psk")) {
             generatePsk(argv[3], argv[7], psk_str);
-            asprintf(&fbuf, "%swpa=3\nwpa_pairwise=TKIP CCMP\nwpa_psk=%s\n", wbuf, psk_str);
+            fbuf = StringPrintf("%swpa=3\nwpa_pairwise=TKIP CCMP\nwpa_psk=%s\n", wbuf.c_str(), psk_str);
         } else if (!strcmp(argv[6], "wpa2-psk")) {
             generatePsk(argv[3], argv[7], psk_str);
-            asprintf(&fbuf, "%swpa=2\nrsn_pairwise=CCMP\nwpa_psk=%s\n", wbuf, psk_str);
+            fbuf = StringPrintf("%swpa=2\nrsn_pairwise=CCMP\nwpa_psk=%s\n", wbuf.c_str(), psk_str);
         } else if (!strcmp(argv[6], "open")) {
-            asprintf(&fbuf, "%s", wbuf);
+            fbuf = wbuf;
         }
     } else if (argc > 6) {
         if (!strcmp(argv[6], "open")) {
-            asprintf(&fbuf, "%s", wbuf);
+            fbuf = wbuf;
         }
     } else {
-        asprintf(&fbuf, "%s", wbuf);
+        fbuf = wbuf;
     }
 
-    fd = open(HOSTAPD_CONF_FILE, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0660);
-    if (fd < 0) {
-        ALOGE("Cannot update \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
-        free(wbuf);
-        free(fbuf);
-        return ResponseCode::OperationFailed;
-    }
-    if (write(fd, fbuf, strlen(fbuf)) < 0) {
+    if (!WriteStringToFile(fbuf, HOSTAPD_CONF_FILE, 0660, AID_SYSTEM, AID_WIFI)) {
         ALOGE("Cannot write to \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
-        ret = ResponseCode::OperationFailed;
-    }
-    free(wbuf);
-    free(fbuf);
-
-    /* Note: apparently open can fail to set permissions correctly at times */
-    if (fchmod(fd, 0660) < 0) {
-        ALOGE("Error changing permissions of %s to 0660: %s",
-                HOSTAPD_CONF_FILE, strerror(errno));
-        close(fd);
-        unlink(HOSTAPD_CONF_FILE);
         return ResponseCode::OperationFailed;
     }
-
-    if (fchown(fd, AID_SYSTEM, AID_WIFI) < 0) {
-        ALOGE("Error changing group ownership of %s to %d: %s",
-                HOSTAPD_CONF_FILE, AID_WIFI, strerror(errno));
-        close(fd);
-        unlink(HOSTAPD_CONF_FILE);
-        return ResponseCode::OperationFailed;
-    }
-
-    close(fd);
-    return ret;
+    return ResponseCode::SoftapStatusResult;
 }
 
 /*
diff --git a/server/StrictController.cpp b/server/StrictController.cpp
new file mode 100644
index 0000000..a04124d
--- /dev/null
+++ b/server/StrictController.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LOG_TAG "StrictController"
+#define LOG_NDEBUG 0
+
+#include <cutils/log.h>
+
+#include "ConnmarkFlags.h"
+#include "NetdConstants.h"
+#include "StrictController.h"
+
+const char* StrictController::LOCAL_OUTPUT = "st_OUTPUT";
+const char* StrictController::LOCAL_CLEAR_DETECT = "st_clear_detect";
+const char* StrictController::LOCAL_CLEAR_CAUGHT = "st_clear_caught";
+const char* StrictController::LOCAL_PENALTY_LOG = "st_penalty_log";
+const char* StrictController::LOCAL_PENALTY_REJECT = "st_penalty_reject";
+
+StrictController::StrictController(void) {
+}
+
+int StrictController::enableStrict(void) {
+    char connmarkFlagAccept[16];
+    char connmarkFlagReject[16];
+    char connmarkFlagTestAccept[32];
+    char connmarkFlagTestReject[32];
+    sprintf(connmarkFlagAccept, "0x%x", ConnmarkFlags::STRICT_RESOLVED_ACCEPT);
+    sprintf(connmarkFlagReject, "0x%x", ConnmarkFlags::STRICT_RESOLVED_REJECT);
+    sprintf(connmarkFlagTestAccept, "0x%x/0x%x",
+            ConnmarkFlags::STRICT_RESOLVED_ACCEPT,
+            ConnmarkFlags::STRICT_RESOLVED_ACCEPT);
+    sprintf(connmarkFlagTestReject, "0x%x/0x%x",
+            ConnmarkFlags::STRICT_RESOLVED_REJECT,
+            ConnmarkFlags::STRICT_RESOLVED_REJECT);
+
+    int res = 0;
+
+    disableStrict();
+
+    // Chain triggered when cleartext socket detected and penalty is log
+    res |= execIptables(V4V6, "-N", LOCAL_PENALTY_LOG, NULL);
+    res |= execIptables(V4V6, "-A", LOCAL_PENALTY_LOG,
+            "-j", "CONNMARK", "--or-mark", connmarkFlagAccept, NULL);
+    res |= execIptables(V4V6, "-A", LOCAL_PENALTY_LOG,
+            "-j", "NFLOG", "--nflog-group", "0", NULL);
+
+    // Chain triggered when cleartext socket detected and penalty is reject
+    res |= execIptables(V4V6, "-N", LOCAL_PENALTY_REJECT, NULL);
+    res |= execIptables(V4V6, "-A", LOCAL_PENALTY_REJECT,
+            "-j", "CONNMARK", "--or-mark", connmarkFlagReject, NULL);
+    res |= execIptables(V4V6, "-A", LOCAL_PENALTY_REJECT,
+            "-j", "NFLOG", "--nflog-group", "0", NULL);
+    res |= execIptables(V4V6, "-A", LOCAL_PENALTY_REJECT,
+            "-j", "REJECT", NULL);
+
+    // Create chain to detect non-TLS traffic. We use a high-order
+    // mark bit to keep track of connections that we've already resolved.
+    res |= execIptables(V4V6, "-N", LOCAL_CLEAR_DETECT, NULL);
+    res |= execIptables(V4V6, "-N", LOCAL_CLEAR_CAUGHT, NULL);
+
+    // Quickly skip connections that we've already resolved
+    res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT,
+            "-m", "connmark", "--mark", connmarkFlagTestReject,
+            "-j", "REJECT", NULL);
+    res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT,
+            "-m", "connmark", "--mark", connmarkFlagTestAccept,
+            "-j", "RETURN", NULL);
+
+    // Look for IPv4 TCP/UDP connections with TLS/DTLS header
+    res |= execIptables(V4, "-A", LOCAL_CLEAR_DETECT, "-p", "tcp",
+            "-m", "u32", "--u32", "0>>22&0x3C@ 12>>26&0x3C@ 0&0xFFFF0000=0x16030000 &&"
+                                  "0>>22&0x3C@ 12>>26&0x3C@ 4&0x00FF0000=0x00010000",
+            "-j", "CONNMARK", "--or-mark", connmarkFlagAccept, NULL);
+    res |= execIptables(V4, "-A", LOCAL_CLEAR_DETECT, "-p", "udp",
+            "-m", "u32", "--u32", "0>>22&0x3C@ 8&0xFFFF0000=0x16FE0000 &&"
+                                  "0>>22&0x3C@ 20&0x00FF0000=0x00010000",
+            "-j", "CONNMARK", "--or-mark", connmarkFlagAccept, NULL);
+
+    // Look for IPv6 TCP/UDP connections with TLS/DTLS header.  The IPv6 header
+    // doesn't have an IHL field to shift with, so we have to manually add in
+    // the 40-byte offset at every step.
+    res |= execIptables(V6, "-A", LOCAL_CLEAR_DETECT, "-p", "tcp",
+            "-m", "u32", "--u32", "52>>26&0x3C@ 40&0xFFFF0000=0x16030000 &&"
+                                  "52>>26&0x3C@ 44&0x00FF0000=0x00010000",
+            "-j", "CONNMARK", "--or-mark", connmarkFlagAccept, NULL);
+    res |= execIptables(V6, "-A", LOCAL_CLEAR_DETECT, "-p", "udp",
+            "-m", "u32", "--u32", "48&0xFFFF0000=0x16FE0000 &&"
+                                  "60&0x00FF0000=0x00010000",
+            "-j", "CONNMARK", "--or-mark", connmarkFlagAccept, NULL);
+
+    // Skip newly classified connections from above
+    res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT,
+            "-m", "connmark", "--mark", connmarkFlagTestAccept,
+            "-j", "RETURN", NULL);
+
+    // Handle TCP/UDP payloads that didn't match TLS/DTLS filters above,
+    // which means we've probably found cleartext data.  The TCP variant
+    // depends on u32 returning false when we try reading into the message
+    // body to ignore empty ACK packets.
+    res |= execIptables(V4, "-A", LOCAL_CLEAR_DETECT, "-p", "tcp",
+            "-m", "state", "--state", "ESTABLISHED",
+            "-m", "u32", "--u32", "0>>22&0x3C@ 12>>26&0x3C@ 0&0x0=0x0",
+            "-j", LOCAL_CLEAR_CAUGHT, NULL);
+    res |= execIptables(V6, "-A", LOCAL_CLEAR_DETECT, "-p", "tcp",
+            "-m", "state", "--state", "ESTABLISHED",
+            "-m", "u32", "--u32", "52>>26&0x3C@ 40&0x0=0x0",
+            "-j", LOCAL_CLEAR_CAUGHT, NULL);
+
+    res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT, "-p", "udp",
+            "-j", LOCAL_CLEAR_CAUGHT, NULL);
+
+    return res;
+}
+
+int StrictController::disableStrict(void) {
+    int res = 0;
+
+    // Flush any existing rules
+    res |= execIptables(V4V6, "-F", LOCAL_OUTPUT, NULL);
+
+    res |= execIptables(V4V6, "-F", LOCAL_PENALTY_LOG, NULL);
+    res |= execIptables(V4V6, "-F", LOCAL_PENALTY_REJECT, NULL);
+    res |= execIptables(V4V6, "-F", LOCAL_CLEAR_CAUGHT, NULL);
+    res |= execIptables(V4V6, "-F", LOCAL_CLEAR_DETECT, NULL);
+
+    res |= execIptables(V4V6, "-X", LOCAL_PENALTY_LOG, NULL);
+    res |= execIptables(V4V6, "-X", LOCAL_PENALTY_REJECT, NULL);
+    res |= execIptables(V4V6, "-X", LOCAL_CLEAR_CAUGHT, NULL);
+    res |= execIptables(V4V6, "-X", LOCAL_CLEAR_DETECT, NULL);
+
+    return res;
+}
+
+int StrictController::setUidCleartextPenalty(uid_t uid, StrictPenalty penalty) {
+    char uidStr[16];
+    sprintf(uidStr, "%d", uid);
+
+    int res = 0;
+    if (penalty == ACCEPT) {
+        // Clean up any old rules
+        execIptables(V4V6, "-D", LOCAL_OUTPUT,
+                "-m", "owner", "--uid-owner", uidStr,
+                "-j", LOCAL_CLEAR_DETECT, NULL);
+        execIptables(V4V6, "-D", LOCAL_CLEAR_CAUGHT,
+                "-m", "owner", "--uid-owner", uidStr,
+                "-j", LOCAL_PENALTY_LOG, NULL);
+        execIptables(V4V6, "-D", LOCAL_CLEAR_CAUGHT,
+                "-m", "owner", "--uid-owner", uidStr,
+                "-j", LOCAL_PENALTY_REJECT, NULL);
+
+    } else {
+        // Always take a detour to investigate this UID
+        res |= execIptables(V4V6, "-I", LOCAL_OUTPUT,
+                "-m", "owner", "--uid-owner", uidStr,
+                "-j", LOCAL_CLEAR_DETECT, NULL);
+
+        if (penalty == LOG) {
+            res |= execIptables(V4V6, "-I", LOCAL_CLEAR_CAUGHT,
+                    "-m", "owner", "--uid-owner", uidStr,
+                    "-j", LOCAL_PENALTY_LOG, NULL);
+        } else if (penalty == REJECT) {
+            res |= execIptables(V4V6, "-I", LOCAL_CLEAR_CAUGHT,
+                    "-m", "owner", "--uid-owner", uidStr,
+                    "-j", LOCAL_PENALTY_REJECT, NULL);
+        }
+    }
+
+    return res;
+}
diff --git a/server/StrictController.h b/server/StrictController.h
new file mode 100644
index 0000000..52a6779
--- /dev/null
+++ b/server/StrictController.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _STRICT_CONTROLLER_H
+#define _STRICT_CONTROLLER_H
+
+#include <string>
+
+enum StrictPenalty { INVALID, ACCEPT, LOG, REJECT };
+
+/*
+ * Help apps catch unwanted low-level networking behavior, like
+ * connections not wrapped in TLS.
+ */
+class StrictController {
+public:
+    StrictController();
+
+    int enableStrict(void);
+    int disableStrict(void);
+
+    int setUidCleartextPenalty(uid_t, StrictPenalty);
+
+    static const char* LOCAL_OUTPUT;
+    static const char* LOCAL_CLEAR_DETECT;
+    static const char* LOCAL_CLEAR_CAUGHT;
+    static const char* LOCAL_PENALTY_LOG;
+    static const char* LOCAL_PENALTY_REJECT;
+};
+
+#endif
diff --git a/server/TetherController.cpp b/server/TetherController.cpp
index dad03b4..092d456 100644
--- a/server/TetherController.cpp
+++ b/server/TetherController.cpp
@@ -27,6 +27,7 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
+#include <base/file.h>
 #define LOG_TAG "TetherController"
 #include <cutils/log.h>
 #include <cutils/properties.h>
@@ -36,6 +37,9 @@
 #include "Permission.h"
 #include "TetherController.h"
 
+using android::base::ReadFileToString;
+using android::base::WriteStringToFile;
+
 namespace {
 
 static const char BP_TOOLS_MODE[] = "bp-tools";
@@ -43,20 +47,7 @@
 static const char IPV6_FORWARDING_PROC_FILE[] = "/proc/sys/net/ipv6/conf/all/forwarding";
 
 bool writeToFile(const char* filename, const char* value) {
-    int fd = open(filename, O_WRONLY);
-    if (fd < 0) {
-        ALOGE("Failed to open %s: %s", filename, strerror(errno));
-        return false;
-    }
-
-    const ssize_t len = strlen(value);
-    if (write(fd, value, len) != len) {
-        ALOGE("Failed to write %s to %s: %s", value, filename, strerror(errno));
-        close(fd);
-        return false;
-    }
-    close(fd);
-    return true;
+    return WriteStringToFile(value, file);
 }
 
 bool inBpToolsMode() {
@@ -68,7 +59,6 @@
 
 }  // namespace
 
-
 TetherController::TetherController() {
     mInterfaces = new InterfaceCollection();
     mDnsNetId = 0;
@@ -178,6 +168,8 @@
             char *start = strdup(inet_ntoa(addrs[addrIndex++]));
             char *end = strdup(inet_ntoa(addrs[addrIndex++]));
             asprintf(&(args[nextArg++]),"--dhcp-range=%s,%s,1h", start, end);
+            free(start);
+            free(end);
         }
 
         if (execv(args[0], args)) {