From 68bd85b615bd4f2a4b5edfbf8f2ac5f554e32c7f Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Sun, 18 Dec 2022 19:19:20 +0100 Subject: [PATCH] Add ability to choose DNS servers The DNS servers used in VPN mode are now configurable. It's now possible to ignore the system DNS server and use the specified ones. Cloudflare DNS is now the default DNS server, which has a no-logs policy. Closes #275 --- .../remote_capture/CaptureService.java | 34 +++++++------- .../com/emanuelef/remote_capture/Utils.java | 14 ++++++ .../activities/SettingsActivity.java | 43 ++++++++++-------- .../remote_capture/fragments/DnsSettings.java | 44 +++++++++++++++++++ .../emanuelef/remote_capture/model/Prefs.java | 6 +++ app/src/main/jni/core/capture_vpn.c | 2 + app/src/main/res/values/strings.xml | 6 +++ app/src/main/res/xml/dns_preferences.xml | 25 +++++++++++ app/src/main/res/xml/root_preferences.xml | 22 ++++++---- 9 files changed, 153 insertions(+), 43 deletions(-) create mode 100644 app/src/main/java/com/emanuelef/remote_capture/fragments/DnsSettings.java create mode 100644 app/src/main/res/xml/dns_preferences.xml diff --git a/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java b/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java index c97d2244..b9cce905 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java +++ b/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java @@ -160,9 +160,6 @@ public class CaptureService extends VpnService implements Runnable { * Max estimated memory usage: less than 4 MB (+8 MB with payload mode minimal). */ public static final int CONNECTIONS_LOG_SIZE = 8192; - public static final String FALLBACK_DNS_SERVER = "8.8.8.8"; - public static final String IPV6_DNS_SERVER = "2001:4860:4860::8888"; - /* The IP address of the virtual network interface */ public static final String VPN_IP_ADDRESS = "10.215.173.1"; public static final String VPN_IP6_ADDRESS = "fd00:2:fd00:1:fd00:1:fd00:1"; @@ -259,7 +256,8 @@ public class CaptureService extends VpnService implements Runnable { mSettings.root_capture = false; // Retrieve DNS server - dns_server = FALLBACK_DNS_SERVER; + String fallbackDnsV4 = Prefs.getDnsServerV4(mPrefs); + dns_server = fallbackDnsV4; mBlockPrivateDns = false; mStrictDnsNoticeShown = false; mDnsEncrypted = false; @@ -284,13 +282,16 @@ public class CaptureService extends VpnService implements Runnable { if(net != null) { handleLinkProperties(cm.getLinkProperties(net)); - dns_server = Utils.getDnsServer(cm, net); - if(dns_server == null) - dns_server = FALLBACK_DNS_SERVER; - else { - mMonitoredNetwork = net.getNetworkHandle(); - registerNetworkCallbacks(); - } + if(Prefs.useSystemDns(mPrefs) || mSettings.root_capture) { + dns_server = Utils.getDnsServer(cm, net); + if (dns_server == null) + dns_server = fallbackDnsV4; + else { + mMonitoredNetwork = net.getNetworkHandle(); + registerNetworkCallbacks(); + } + } else + dns_server = fallbackDnsV4; } } @@ -405,7 +406,7 @@ public class CaptureService extends VpnService implements Runnable { builder.addRoute("2000::", 3); try { - builder.addDnsServer(InetAddress.getByName(IPV6_DNS_SERVER)); + builder.addDnsServer(InetAddress.getByName(Prefs.getDnsServerV6(mPrefs))); } catch (UnknownHostException e) { Log.w(TAG, "Could not set IPv6 DNS server"); } @@ -688,6 +689,7 @@ public class CaptureService extends VpnService implements Runnable { if(mNetworkCallback != null) return; + String fallbackDns = Prefs.getDnsServerV4(mPrefs); ConnectivityManager cm = (ConnectivityManager) getSystemService(Service.CONNECTIVITY_SERVICE); mNetworkCallback = new ConnectivityManager.NetworkCallback() { @Override @@ -697,8 +699,8 @@ public class CaptureService extends VpnService implements Runnable { // If the network goes offline we roll back to the fallback DNS server to // avoid possibly using a private IP DNS server not reachable anymore if(network.getNetworkHandle() == mMonitoredNetwork) { - Log.i(TAG, "Main network " + network + " lost, using fallback DNS " + FALLBACK_DNS_SERVER); - dns_server = FALLBACK_DNS_SERVER; + Log.i(TAG, "Main network " + network + " lost, using fallback DNS " + fallbackDns); + dns_server = fallbackDns; mMonitoredNetwork = 0; unregisterNetworkCallbacks(); @@ -728,7 +730,7 @@ public class CaptureService extends VpnService implements Runnable { e.printStackTrace(); Log.w(TAG, "registerNetworkCallback failed, DNS server detection disabled"); - dns_server = FALLBACK_DNS_SERVER; + dns_server = fallbackDns; mNetworkCallback = null; } } @@ -1195,7 +1197,7 @@ public class CaptureService extends VpnService implements Runnable { return(dns_server); } - public String getIpv6DnsServer() { return(IPV6_DNS_SERVER); } + public String getIpv6DnsServer() { return(Prefs.getDnsServerV4(mPrefs)); } public int getSocks5Enabled() { return mSocks5Enabled ? 1 : 0; } diff --git a/app/src/main/java/com/emanuelef/remote_capture/Utils.java b/app/src/main/java/com/emanuelef/remote_capture/Utils.java index 230a97ce..a98221c2 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/Utils.java +++ b/app/src/main/java/com/emanuelef/remote_capture/Utils.java @@ -127,6 +127,7 @@ import java.util.List; import java.util.Locale; import java.util.Random; import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -1432,6 +1433,19 @@ public class Utils { } } + // https://mkyong.com/regular-expressions/how-to-validate-ip-address-with-regular-expression/ + private static final Pattern IPV4_PATTERN = Pattern.compile( + "^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.(?!$)|$)){4}$"); + + public static boolean validateIpv4Address(String s) { + Matcher matcher = IPV4_PATTERN.matcher(s); + return matcher.matches(); + } + + public static boolean validateIpv6Address(String s) { + return validateIpAddress(s) && !validateIpv4Address(s); + } + // rough validation public static boolean validateHost(String host) { int len = host.length(); diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/SettingsActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/SettingsActivity.java index c575e1fb..2712c4f7 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/SettingsActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/SettingsActivity.java @@ -44,6 +44,7 @@ import com.emanuelef.remote_capture.Log; import com.emanuelef.remote_capture.PCAPdroid; import com.emanuelef.remote_capture.Utils; import com.emanuelef.remote_capture.MitmAddon; +import com.emanuelef.remote_capture.fragments.DnsSettings; import com.emanuelef.remote_capture.fragments.GeoipSettings; import com.emanuelef.remote_capture.model.Prefs; import com.emanuelef.remote_capture.R; @@ -83,6 +84,9 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment if(prefKey.equals("geolocation")) { targetFragment = new GeoipSettings(); setTitle(R.string.geolocation); + } else if(prefKey.equals("dns_settings")) { + targetFragment = new DnsSettings(); + setTitle(R.string.dns_servers); } if(targetFragment != null) { @@ -131,6 +135,7 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment private DropDownPreference mIpMode; private DropDownPreference mCapInterface; private Preference mVpnExceptions; + private Preference mDnsSettings; private Preference mPortMapping; private Preference mMitmWizard; private SwitchPreference mMalwareDetectionEnabled; @@ -238,21 +243,25 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment private void setupCapturePrefs() { mCapInterface = requirePreference(Prefs.PREF_CAPTURE_INTERFACE); - mVpnExceptions = requirePreference(Prefs.PREF_VPN_EXCEPTIONS); - mPortMapping = requirePreference(Prefs.PREF_PORT_MAPPING); refreshInterfaces(); + mRootCaptureEnabled = requirePreference(Prefs.PREF_ROOT_CAPTURE); + if(Utils.isRootAvailable()) { + mRootCaptureEnabled.setOnPreferenceChangeListener((preference, newValue) -> { + rootCaptureHideShow((Boolean) newValue); + checkDecrpytionWithRoot((Boolean) newValue, mTlsDecryption.isChecked()); + return true; + }); + } else + mRootCaptureEnabled.setVisible(false); + + mDnsSettings = requirePreference("dns_settings");; + mVpnExceptions = requirePreference(Prefs.PREF_VPN_EXCEPTIONS); mVpnExceptions.setOnPreferenceClickListener(preference -> { Intent intent = new Intent(requireContext(), VpnExemptionsActivity.class); startActivity(intent); return true; }); - - mPortMapping.setOnPreferenceClickListener(preference -> { - Intent intent = new Intent(requireContext(), PortMapActivity.class); - startActivity(intent); - return true; - }); } private void setupSecurityPrefs() { @@ -351,23 +360,18 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment }); DropDownPreference appTheme = requirePreference(Prefs.PREF_APP_THEME); - appTheme.setOnPreferenceChangeListener((preference, newValue) -> { Utils.setAppTheme(newValue.toString()); return true; }); - mRootCaptureEnabled = requirePreference(Prefs.PREF_ROOT_CAPTURE); - - if(Utils.isRootAvailable()) { - mRootCaptureEnabled.setOnPreferenceChangeListener((preference, newValue) -> { - rootCaptureHideShow((Boolean) newValue); - checkDecrpytionWithRoot((Boolean) newValue, mTlsDecryption.isChecked()); - return true; - }); - } else - mRootCaptureEnabled.setVisible(false); + mPortMapping = requirePreference(Prefs.PREF_PORT_MAPPING); + mPortMapping.setOnPreferenceClickListener(preference -> { + Intent intent = new Intent(requireContext(), PortMapActivity.class); + startActivity(intent); + return true; + }); mIpMode = requirePreference(Prefs.PREF_IP_MODE); @@ -398,6 +402,7 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment mIpMode.setVisible(!enabled); mCapInterface.setVisible(enabled); mVpnExceptions.setVisible(!enabled); + mDnsSettings.setVisible(!enabled); mPortMapping.setVisible(!enabled); } diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/DnsSettings.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/DnsSettings.java new file mode 100644 index 00000000..8021e4c1 --- /dev/null +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/DnsSettings.java @@ -0,0 +1,44 @@ +/* + * This file is part of PCAPdroid. + * + * PCAPdroid is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PCAPdroid is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with PCAPdroid. If not, see . + * + * Copyright 2020-22 - Emanuele Faranda + */ + +package com.emanuelef.remote_capture.fragments; +import android.os.Bundle; + +import androidx.annotation.Nullable; +import androidx.preference.EditTextPreference; +import androidx.preference.PreferenceFragmentCompat; + +import com.emanuelef.remote_capture.R; +import com.emanuelef.remote_capture.Utils; +import com.emanuelef.remote_capture.model.Prefs; + +import java.util.Objects; + +public class DnsSettings extends PreferenceFragmentCompat { + @Override + public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) { + setPreferencesFromResource(R.xml.dns_preferences, rootKey); + + EditTextPreference p1 = Objects.requireNonNull(findPreference(Prefs.PREF_DNS_SERVER_V4)); + p1.setOnPreferenceChangeListener((preference, newValue) -> Utils.validateIpv4Address(newValue.toString())); + + EditTextPreference p2 = Objects.requireNonNull(findPreference(Prefs.PREF_DNS_SERVER_V6)); + p2.setOnPreferenceChangeListener((preference, newValue) -> Utils.validateIpv6Address(newValue.toString())); + } +} diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java b/app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java index 99e9c022..cce9b765 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java +++ b/app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java @@ -91,6 +91,9 @@ public class Prefs { public static final String PREF_PAYLOAD_NOTICE_ACK = "payload_notice"; public static final String PREF_REMOTE_COLLECTOR_ACK = "remote_collector_notice"; public static final String PREF_MITMPROXY_OPTS = "mitmproxy_opts"; + public static final String PREF_DNS_SERVER_V4 = "dns_v4"; + public static final String PREF_DNS_SERVER_V6 = "dns_v6"; + public static final String PREF_USE_SYSTEM_DNS = "system_dns"; public enum DumpMode { NONE, @@ -192,6 +195,9 @@ public class Prefs { public static boolean isFirewallWhitelistInitialized(SharedPreferences p) { return(p.getInt(PREF_FIREWALL_WHITELIST_INIT_VER, 0) == FIREWALL_WHITELIST_INIT_VER); } public static String getMitmproxyOpts(SharedPreferences p) { return(p.getString(PREF_MITMPROXY_OPTS, "")); } public static boolean isPortMappingEnabled(SharedPreferences p) { return(p.getBoolean(PREF_PORT_MAPPING_ENABLED, true)); } + public static boolean useSystemDns(SharedPreferences p) { return(p.getBoolean(PREF_USE_SYSTEM_DNS, true)); } + public static String getDnsServerV4(SharedPreferences p) { return(p.getString(PREF_DNS_SERVER_V4, "1.1.1.1")); } + public static String getDnsServerV6(SharedPreferences p) { return(p.getString(PREF_DNS_SERVER_V6, "2606:4700:4700::1111")); } public static String asString(Context ctx) { SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(ctx); diff --git a/app/src/main/jni/core/capture_vpn.c b/app/src/main/jni/core/capture_vpn.c index bbc32e2a..df5e7ffe 100644 --- a/app/src/main/jni/core/capture_vpn.c +++ b/app/src/main/jni/core/capture_vpn.c @@ -386,6 +386,8 @@ static void load_dns_servers(pcapdroid_t *pd) { blacklist_add_ipstr(pd->vpn.known_dns_servers, "2001:4860:4860::8844"); blacklist_add_ipstr(pd->vpn.known_dns_servers, "2606:4700:4700::64"); blacklist_add_ipstr(pd->vpn.known_dns_servers, "2606:4700:4700::6400"); + blacklist_add_ipstr(pd->vpn.known_dns_servers, "2606:4700:4700::1111"); + blacklist_add_ipstr(pd->vpn.known_dns_servers, "2606:4700:4700::1001"); // Domains (only private DNS) // https://help.firewalla.com/hc/en-us/articles/360060661873-Dealing-DNS-over-HTTPS-and-DNS-over-TLS-on-your-network diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 41da9527..bf0fe449 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -436,4 +436,10 @@ Delete the selected items? IP address Rule already defined + DNS servers + Configure the DNS servers to use during the capture + IPv4 DNS server + IPv6 DNS server + Use system DNS + Use the system DNS servers if possible diff --git a/app/src/main/res/xml/dns_preferences.xml b/app/src/main/res/xml/dns_preferences.xml new file mode 100644 index 00000000..710ca74d --- /dev/null +++ b/app/src/main/res/xml/dns_preferences.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index a4e3ae53..fa91d404 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -113,12 +113,6 @@ app:iconSpaceReserved="false" app:defaultValue="8050" app:useSimpleSummaryProvider="true" /> - - @@ -142,6 +136,13 @@ app:summary="@string/vpn_exemptions_summary" app:iconSpaceReserved="false" /> + + + + - + android:summary="@string/control_permissions_summary" />