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
This commit is contained in:
emanuele-f 2022-12-18 19:19:20 +01:00
parent 85e1ceb88e
commit 68bd85b615
9 changed files with 153 additions and 43 deletions

View File

@ -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; }

View File

@ -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();

View File

@ -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);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*
* 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()));
}
}

View File

@ -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);

View File

@ -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

View File

@ -436,4 +436,10 @@
<string name="items_delete_confirm">Delete the selected items?</string>
<string name="ip_address">IP address</string>
<string name="rule_exists">Rule already defined</string>
<string name="dns_servers">DNS servers</string>
<string name="dns_servers_summary">Configure the DNS servers to use during the capture</string>
<string name="ipv4_dns_server">IPv4 DNS server</string>
<string name="ipv6_dns_server">IPv6 DNS server</string>
<string name="use_system_dns">Use system DNS</string>
<string name="use_system_dns_summary">Use the system DNS servers if possible</string>
</resources>

View File

@ -0,0 +1,25 @@
<PreferenceScreen
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
app:key="system_dns"
android:title="@string/use_system_dns"
app:iconSpaceReserved="false"
app:summary="@string/use_system_dns_summary"
android:defaultValue="true" />
<EditTextPreference
app:key="dns_v4"
app:title="@string/ipv4_dns_server"
app:iconSpaceReserved="false"
app:defaultValue="1.1.1.1"
app:useSimpleSummaryProvider="true" />
<EditTextPreference
app:key="dns_v6"
app:title="@string/ipv6_dns_server"
app:iconSpaceReserved="false"
app:defaultValue="2606:4700:4700::1111"
app:useSimpleSummaryProvider="true" />
</PreferenceScreen>

View File

@ -113,12 +113,6 @@
app:iconSpaceReserved="false"
app:defaultValue="8050"
app:useSimpleSummaryProvider="true" />
<Preference
app:key="port_mapping"
app:title="@string/port_mapping"
app:summary="@string/port_mapping_summary"
app:iconSpaceReserved="false" />
</PreferenceCategory>
<PreferenceCategory app:title="@string/capture_prefs" app:iconSpaceReserved="false" >
@ -142,6 +136,13 @@
app:summary="@string/vpn_exemptions_summary"
app:iconSpaceReserved="false" />
<Preference
android:key="dns_settings"
app:title="@string/dns_servers"
app:summary="@string/dns_servers_summary"
app:iconSpaceReserved="false"
app:fragment="com.emanuelef.remote_capture.fragments.DnsSettings" />
<SwitchPreference
app:key="start_at_boot"
android:title="@string/start_at_boot"
@ -201,6 +202,12 @@
app:defaultValue="system"
app:useSimpleSummaryProvider="true"/>
<Preference
app:key="port_mapping"
app:title="@string/port_mapping"
app:summary="@string/port_mapping_summary"
app:iconSpaceReserved="false" />
<DropDownPreference
app:key="ip_mode"
app:title="@string/ip_mode"
@ -214,7 +221,6 @@
app:key="control_permissions"
app:title="@string/control_permissions"
app:iconSpaceReserved="false"
android:summary="@string/control_permissions_summary">
</Preference>
android:summary="@string/control_permissions_summary" />
</PreferenceCategory>
</PreferenceScreen>