From 322f9019c0ca410c0d72190dd17bc316351d35db Mon Sep 17 00:00:00 2001
From: Martin Brabham <optedoblivion@cyngn.com>
Date: Fri, 9 Jan 2015 16:20:34 -0800
Subject: [PATCH] DNSCrypt: Encrypt dns queries - Add DNS encryption settings
 constants. - Listen for settings changes and handle rerouting dns queries -
 Handle switching servers

Change-Id: Ic97285ac6924aa0c930ff59709da384e197cd3ca
---
 core/java/android/provider/Settings.java           |  22 ++++
 .../com/android/server/ConnectivityService.java    | 116 ++++++++++++++++++---
 2 files changed, 123 insertions(+), 15 deletions(-)

diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f6642d8..9905483 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4058,6 +4058,28 @@ public final class Settings {
     public static final class Secure extends NameValueTable {
         public static final String SYS_PROP_SETTING_VERSION = "sys.settings_secure_version";
 
+        // DNS Encryption
+
+        /**
+         * @hide
+         */
+        public static final String DNS_ENCRYPTION_TOGGLE = "dns_enc_toggle";
+
+        /**
+         * @hide
+         */
+        public static final int DNS_ENCRYPTION_TOGGLE_DEFAULT = 0;
+
+        /**
+         * @hide
+         */
+        public static final String DNS_ENCRYPTION_SERVER = "dns_enc_server";
+
+        /**
+         * @hide
+         */
+        public static final String DNS_ENCRYPTION_SERVER_DEFAULT = "opendns";
+
         /**
          * The content:// style URL for this table
          */
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2a47460..0e6578f 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -91,6 +91,8 @@ import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.provider.Settings.Secure;
+import android.provider.Settings.SettingNotFoundException;
 import android.security.Credentials;
 import android.security.KeyStore;
 import android.telephony.TelephonyManager;
@@ -167,6 +169,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
         implements PendingIntent.OnFinished {
     private static final String TAG = "ConnectivityService";
 
+    private static final String NETID_UPDATE =
+        "org.codeaurora.NETID_UPDATE";
+
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
@@ -229,6 +234,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
     private static final int ENABLED  = 1;
     private static final int DISABLED = 0;
 
+    // DNS Encryption variables
+    private static final String DNSCRYPT_SERVICE = "dnscrypt-proxy";
+    private static final String LOOPBACK_ADDR = "127.0.0.1";
+    private static final String NULL_DOMAIN = "";
+    private boolean mDnsEncryptionEnabled = false;
+    private String mDnsEncryptionServer = Secure.DNS_ENCRYPTION_SERVER_DEFAULT;
+    private Collection<InetAddress> mActualDnses = new ArrayList<InetAddress>();
+    private int mNetId = -1;
+    private String mActualDomains = null;
+
     private enum ReapUnvalidatedNetworks {
         // Tear down networks that have no chance (e.g. even if validated) of becoming
         // the highest scoring network satisfying a NetworkRequest.  This should be passed when
@@ -827,6 +842,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
         mSettingsObserver.observe(
                 Settings.Global.getUriFor(Settings.Global.MOBILE_DATA_ALWAYS_ON),
                 EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
+
+	// Watch for whether or not to use DNScrypt
+        resolver.registerContentObserver(Settings.Secure.getUriFor(
+                Secure.DNS_ENCRYPTION_TOGGLE), false, this);
+        resolver.registerContentObserver(Settings.Secure.getUriFor(Secure
+                .DNS_ENCRYPTION_SERVER), false, this);
+        onPreferencesChanged();
     }
 
     private synchronized int nextNetworkRequestId() {
@@ -3027,7 +3049,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
         }
     }
 
-    private static class SettingsObserver extends ContentObserver {
+    private class SettingsObserver extends ContentObserver {
         final private HashMap<Uri, Integer> mUriEventMap;
         final private Context mContext;
         final private Handler mHandler;
@@ -3058,7 +3080,70 @@ public class ConnectivityService extends IConnectivityManager.Stub
             } else {
                 loge("No matching event to send for URI=" + uri);
             }
+	    onPreferencesChanged();
+        }
+    }
+
+    /**
+     * Called when preference are updated from Settings
+     * {@hide}
+     */
+    private void onPreferencesChanged() {
+       setDnsEncryptionDns();
+    }
+
+    /**
+     * Updates the DNS to either encrypted or regular
+     */
+    private void setDnsEncryptionDns() {
+        try {
+            mDnsEncryptionEnabled = Secure.getIntForUser(mContext.getContentResolver(),
+                    Secure.DNS_ENCRYPTION_TOGGLE, UserHandle.USER_CURRENT) != 0;
+        } catch (SettingNotFoundException e) {
+            loge("Exception getting dns enabled setting: " + e);
+        }
+        String serverName = Secure.getStringForUser(mContext.getContentResolver(),
+                Secure.DNS_ENCRYPTION_SERVER, UserHandle.USER_CURRENT);
+        serverName = (TextUtils.isEmpty(serverName)) ? Secure.DNS_ENCRYPTION_SERVER_DEFAULT :
+                serverName;
+
+        // If they aren't equal, then we have a change!
+        boolean changed = !mDnsEncryptionServer.equals(serverName);
+
+        // Set last server name
+        mDnsEncryptionServer = serverName;
+
+        log("DNS Encryption Enabled: " + mDnsEncryptionEnabled);
+        Collection<InetAddress> dnses = mActualDnses;
+        String domains = mActualDomains;
+        if (mDnsEncryptionEnabled) {
+            if (changed) {
+                // If we had a server change, need to restart service to pick up new server
+                SystemProperties.set("ctl.stop", DNSCRYPT_SERVICE);
+                SystemProperties.set("net.dnscrypt_resolver", mDnsEncryptionServer);
+            }
+            SystemProperties.set("ctl.start", DNSCRYPT_SERVICE);
+            dnses = new ArrayList<InetAddress>();
+            dnses.add(NetworkUtils.numericToInetAddress(LOOPBACK_ADDR));
+            domains = NULL_DOMAIN;
+        } else {
+            SystemProperties.set("ctl.stop", DNSCRYPT_SERVICE);
+        }
+        updateDnsesInternal(dnses, mNetId, domains);
+    }
+
+    private void updateDnsesInternal(Collection<InetAddress> dnses, int netId, String domains) {
+        if (DBG) log("Setting Dns servers for network " + netId + " to " + dnses);
+        try {
+            mNetd.setDnsServersForNetwork(netId, NetworkUtils.makeStrings(dnses), domains);
+        } catch (Exception e) {
+            loge("Exception in setDnsServersForNetwork: " + e);
+        }
+        NetworkAgentInfo defaultNai = mNetworkForRequestId.get(mDefaultRequest.requestId);
+        if (defaultNai != null && defaultNai.network.netId == netId) {
+            setDefaultDnsSystemProperties(dnses);
         }
+        flushVmDnsCache();
     }
 
     private static void log(String s) {
@@ -4122,27 +4207,28 @@ public class ConnectivityService extends IConnectivityManager.Stub
 
     private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId,
                              boolean flush, boolean useDefaultDns) {
-        if (oldLp == null || (newLp.isIdenticalDnses(oldLp) == false)) {
-            Collection<InetAddress> dnses = newLp.getDnsServers();
+        if (oldLp == null || !newLp.isIdenticalDnses(oldLp)) {
+           Collection<InetAddress> dnses = newLp.getDnsServers();
             if (dnses.size() == 0 && mDefaultDns != null && useDefaultDns) {
-                dnses = new ArrayList();
+                dnses = new ArrayList<InetAddress>();
                 dnses.add(mDefaultDns);
                 if (DBG) {
                     loge("no dns provided for netId " + netId + ", so using defaults");
                 }
             }
-            if (DBG) log("Setting Dns servers for network " + netId + " to " + dnses);
-            try {
-                mNetd.setDnsServersForNetwork(netId, NetworkUtils.makeStrings(dnses),
-                    newLp.getDomains());
-            } catch (Exception e) {
-                loge("Exception in setDnsServersForNetwork: " + e);
-            }
-            final NetworkAgentInfo defaultNai = getDefaultNetwork();
-            if (defaultNai != null && defaultNai.network.netId == netId) {
-                setDefaultDnsSystemProperties(dnses);
+
+            // Set tracking fields
+            mNetId = netId;
+            mActualDnses = dnses;
+            mActualDomains = newLp.getDomains();
+
+            String domains = mActualDomains;
+            if (mDnsEncryptionEnabled) {
+                dnses = new ArrayList<InetAddress>();
+                dnses.add(NetworkUtils.numericToInetAddress(LOOPBACK_ADDR));
+                domains = NULL_DOMAIN;
             }
-            flushVmDnsCache();
+            updateDnsesInternal(dnses, netId, domains);
         } else if (flush) {
             try {
                 mNetd.flushNetworkDnsCache(netId);
-- 
2.7.4

