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" />