Skip to content

Commit

Permalink
PIA-625: Introduce DNS option to enable system resolver (#30)
Browse files Browse the repository at this point in the history
* Introduce DNS option to enable system resolver

* update subnet utils

* Update translations with crowdin-provided files

* update dns option selection logic to avoid a potential out of bounds exception

* review: add parenthesis to make the operation more explicit

* PIA-625: Fix edge case where 32 bitmasks were being wrongly processed as being contained in some cidr ranges

---------

Co-authored-by: Aldo Pedromingo <[email protected]>
  • Loading branch information
kp-juan-docal and kp-aldo-pedromingo authored Oct 23, 2023
1 parent 21ac50f commit 4624f9c
Show file tree
Hide file tree
Showing 26 changed files with 580 additions and 255 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@
*/
public class PiaApi {

public static final String GEN4_MACE_ENABLED_DNS = "10.0.0.241";

public static final String ANDROID_HTTP_CLIENT = "privateinternetaccess.com Android Client/" + BuildConfig.VERSION_NAME + "(" + BuildConfig.VERSION_CODE + ")";

private OkHttpClient OKHTTPCLIENT;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ public class PiaPrefHandler {
public static final String CUSTOM_DNS = "CUSTOM_DNS";
public static final String CUSTOM_SECONDARY_DNS = "CUSTOM_SECONDARY_DNS";
public static final String CUSTOM_DNS_SELECTED = "CUSTOM_SELECTED";
public static final String SYSTEM_DNS_RESOLVER_SELECTED = "SYSTEM_DNS_RESOLVER_SELECTED";
public static final String DNS_PREF = "dns_pref";

public static final String CONNECTION_ENDED = "connectionEndedByUser";
Expand Down Expand Up @@ -1260,6 +1261,18 @@ public static void resetCustomDnsSelected(Context context) {
Prefs.with(context).remove(CUSTOM_DNS_SELECTED);
}

public static void setSystemDnsResolverSelected(Context context, boolean value) {
Prefs.with(context).set(SYSTEM_DNS_RESOLVER_SELECTED, value);
}

public static boolean isSystemDnsResolverSelected(Context context) {
return Prefs.with(context).get(SYSTEM_DNS_RESOLVER_SELECTED, false);
}

public static void resetSystemDnsResolverSelected(Context context) {
Prefs.with(context).remove(SYSTEM_DNS_RESOLVER_SELECTED);
}

public static void setPrimaryDns(Context context, String value) {
Prefs.with(context).set(DNS, value);
}
Expand Down Expand Up @@ -1525,6 +1538,8 @@ public static void resetSettings(Context context) {
PiaPrefHandler.resetConnectOnAppUpdate(context);
PiaPrefHandler.resetHapticFeedbackEnabled(context);
PiaPrefHandler.resetShowInAppMessagesEnabled(context);
PiaPrefHandler.resetSystemDnsResolverSelected(context);
PiaPrefHandler.resetCustomDnsSelected(context);

// Per App Settings
prefs.set(PiaPrefHandler.VPN_PER_APP_ARE_ALLOWED, false);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.privateinternetaccess.android.pia.providers

import android.content.Context
import android.net.ConnectivityManager
import android.os.Build
import com.privateinternetaccess.android.pia.handlers.PiaPrefHandler

object DnsProvider {

private const val MACE_DNS = "10.0.0.241"
private const val PIA_DNS = "10.0.0.242"

fun getTargetDns(context: Context, defaultDns: String? = null): Pair<String, String?> {
var primaryDns: String = defaultDns ?: PIA_DNS
var secondaryDns: String? = null

if (PiaPrefHandler.isMaceEnabled(context)) {
primaryDns = MACE_DNS
} else if (PiaPrefHandler.isCustomDnsSelected(context)) {
PiaPrefHandler.getPrimaryDns(context)?.let {
primaryDns = it
}
PiaPrefHandler.getSecondaryDns(context)?.let {
secondaryDns = it
}
} else if (
PiaPrefHandler.isSystemDnsResolverSelected(context) &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
) {
val connectivityManager: ConnectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val network = connectivityManager.activeNetwork
connectivityManager.getLinkProperties(network)?.dnsServers?.let { dnsServers ->
dnsServers.firstOrNull()?.hostAddress?.let {
primaryDns = it
}
if (dnsServers.size > 1) {
dnsServers.lastOrNull()?.hostAddress?.let {
secondaryDns = it
}
}
}
}

return Pair(primaryDns, secondaryDns)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.privateinternetaccess.android.pia.PIAFactory;
import com.privateinternetaccess.android.pia.handlers.PIAServerHandler;
import com.privateinternetaccess.android.pia.handlers.PiaPrefHandler;
import com.privateinternetaccess.android.pia.providers.DnsProvider;
import com.privateinternetaccess.android.pia.providers.VPNFallbackEndpointProvider;
import com.privateinternetaccess.android.pia.utils.DLog;
import com.privateinternetaccess.android.pia.utils.Prefs;
Expand All @@ -47,8 +48,7 @@
import de.blinkt.openvpn.core.ConfigParser;
import de.blinkt.openvpn.core.Connection;
import de.blinkt.openvpn.core.ProfileManager;

import static com.privateinternetaccess.android.pia.api.PiaApi.GEN4_MACE_ENABLED_DNS;
import kotlin.Pair;


public class PiaOvpnConfig {
Expand Down Expand Up @@ -104,20 +104,12 @@ public static VpnProfile generateVpnProfile(Context context, VPNFallbackEndpoint

vp.mDNS1 = "";
vp.mDNS2 = "";
vp.mOverrideDNS = true;

String dns = PiaPrefHandler.getPrimaryDns(context);
if (PiaPrefHandler.isMaceEnabled(context)) {
dns = GEN4_MACE_ENABLED_DNS;
}
DLog.d("PiaOvpnConfig", "Custom DNS: " + dns);
if(!TextUtils.isEmpty(dns)){
vp.mDNS1 = dns;
vp.mOverrideDNS = true;
}

String secondaryDns = PiaPrefHandler.getSecondaryDns(context);
if (!TextUtils.isEmpty(secondaryDns)) {
vp.mDNS2 = secondaryDns;
Pair<String, String> dnsServers = DnsProvider.INSTANCE.getTargetDns(context, null);
vp.mDNS1 = dnsServers.getFirst();
if (!TextUtils.isEmpty(dnsServers.getSecond())) {
vp.mDNS2 = dnsServers.getSecond();
}

if (PiaPrefHandler.getOvpnSmallPacketSizeEnabled(context)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ package com.privateinternetaccess.android.ui.drawer.settings
import android.app.AlertDialog
import android.content.Context
import android.content.DialogInterface
import android.net.InetAddresses
import android.os.Build
import android.os.Bundle
import android.util.Patterns
import android.view.LayoutInflater
Expand Down Expand Up @@ -70,7 +72,7 @@ class SettingsSectionNetworkFragment : Fragment() {
// region private
private fun prepareClickListeners(context: Context) {
binding.dnsSetting.setOnClickListener {
updateDNSSetting(context)
showDNSDialog(context)
}
binding.portForwardingSetting.setOnClickListener {
updatePortForwardingSetting(context)
Expand All @@ -83,10 +85,6 @@ class SettingsSectionNetworkFragment : Fragment() {
}
}

private fun updateDNSSetting(context: Context) {
showDNSDialog(context)
}

private fun updatePortForwardingSetting(context: Context) {
val switch = binding.portForwardingSwitchSetting
PiaPrefHandler.setPortForwardingEnabled(context, !switch.isChecked)
Expand Down Expand Up @@ -125,16 +123,25 @@ class SettingsSectionNetworkFragment : Fragment() {
private fun showDNSDialog(context: Context) {

// Prepare the list of options.
val options = mutableListOf(getString(R.string.pia_dns))
customDnsString(context)?.let { customDnsString ->
val piaDnsString = getString(R.string.pia_dns)
val options = mutableListOf(
piaDnsString,
)
val systemDnsResolverString = getString(R.string.system_resolver_dns)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
options.add(systemDnsResolverString)
}
val customDnsString = customDnsString(context)
if (customDnsString.isNotEmpty()) {
options.add(customDnsString)
}

// We only support one custom dns pair. So, it's either custom or PIA.
val selectedOption = if (PiaPrefHandler.isCustomDnsSelected(context)) {
options.last()
customDnsString
} else if (PiaPrefHandler.isSystemDnsResolverSelected(context)) {
systemDnsResolverString
} else {
options.first()
piaDnsString
}

// Build the dialog
Expand All @@ -148,10 +155,21 @@ class SettingsSectionNetworkFragment : Fragment() {
val index = adapter.selectedIndex
PiaPrefHandler.setDnsChanged(context, true)
when (index) {
0 ->
PiaPrefHandler.setCustomDnsSelected(context, false)
0 -> {
PiaPrefHandler.resetCustomDnsSelected(context)
PiaPrefHandler.resetSystemDnsResolverSelected(context)
}
1 -> {
showDnsWarningDialog(context) {
showDnsSystemResolverAllowLanWarningDialogIfNeeded(context) {
showMaceWarningDialogIfNeeded(context)
applySystemDnsResolverOption(context)
}
}
}
2 -> {
PiaPrefHandler.setCustomDnsSelected(context, true)
PiaPrefHandler.resetSystemDnsResolverSelected(context)
showMaceWarningDialogIfNeeded(context)
}
else ->
Expand All @@ -177,7 +195,9 @@ class SettingsSectionNetworkFragment : Fragment() {
}
} else {
builder.setNeutralButton(R.string.custom_dns) { dialogInterface, _ ->
showDnsWarningDialog(context)
showDnsWarningDialog(context) {
showCustomDnsDialog(context)
}
dialogInterface.dismiss()
}
}
Expand All @@ -186,16 +206,46 @@ class SettingsSectionNetworkFragment : Fragment() {
builder.show()
}

private fun showDnsWarningDialog(context: Context) {
private fun showDnsWarningDialog(
context: Context,
acceptedCallback: () -> Unit
) {
val builder = AlertDialog.Builder(context)
builder.setTitle(R.string.custom_dns)
builder.setCancelable(false)
builder.setMessage(R.string.custom_dns_warning_body)
builder.setPositiveButton(R.string.ok) { dialogInterface, _ ->
showCustomDnsDialog(context)
acceptedCallback()
dialogInterface.dismiss()
}
builder.setNegativeButton(R.string.cancel) { dialogInterface, _ ->
showDNSDialog(context)
dialogInterface.dismiss()
}
builder.show()
}

private fun showDnsSystemResolverAllowLanWarningDialogIfNeeded(
context: Context,
acceptedCallback: () -> Unit
) {
if (PiaPrefHandler.isAllowLocalLanEnabled(context)) {
acceptedCallback()
return
}

val builder = AlertDialog.Builder(context)
builder.setTitle(getString(R.string.system_resolver_dns))
builder.setCancelable(false)
builder.setMessage(getString(R.string.system_resolver_dns_body))
builder.setPositiveButton(R.string.ok) { dialogInterface, _ ->
acceptedCallback()
dialogInterface.dismiss()
}
builder.setNegativeButton(R.string.cancel) { dialogInterface, _ ->
showDNSDialog(context)
dialogInterface.dismiss()
}
builder.setNegativeButton(R.string.cancel, null)
builder.show()
}

Expand Down Expand Up @@ -284,12 +334,24 @@ class SettingsSectionNetworkFragment : Fragment() {
factory.setPositiveButton(getString(R.string.save)) { _ ->
val typedCustomPrimaryDns = primaryInputEditText.text.toString()
val typedCustomSecondaryDns = secondaryInputEditText.text.toString()
if (typedCustomPrimaryDns.isNotEmpty() && !Patterns.IP_ADDRESS.matcher(typedCustomPrimaryDns).matches()) {

val isValidCustomPrimaryDns = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
InetAddresses.isNumericAddress(typedCustomPrimaryDns)
} else {
Patterns.IP_ADDRESS.matcher(typedCustomPrimaryDns).matches()
}
val isValidCustomSecondaryDns = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
InetAddresses.isNumericAddress(typedCustomSecondaryDns)
} else {
Patterns.IP_ADDRESS.matcher(typedCustomSecondaryDns).matches()
}

if (isValidCustomPrimaryDns.not()) {
primaryInputEditText.error = getString(R.string.custom_primary_dns_invalid)
return@setPositiveButton
}

if (typedCustomSecondaryDns.isNotEmpty() && !Patterns.IP_ADDRESS.matcher(typedCustomSecondaryDns).matches()) {
if (isValidCustomSecondaryDns.not()) {
secondaryInputEditText.error = getString(R.string.custom_secondary_dns_invalid)
return@setPositiveButton
}
Expand All @@ -310,13 +372,24 @@ class SettingsSectionNetworkFragment : Fragment() {
dialog.dismiss()
}
}
factory.setNegativeButton(getString(R.string.cancel), null)
factory.setNegativeButton(getString(R.string.cancel)) {
dialog.dismiss()
}
dialog.show()
}

private fun applySystemDnsResolverOption(context: Context) {
PiaPrefHandler.setCustomDnsSelected(context, false)
PiaPrefHandler.setAllowLocalLanEnabled(context, true)
PiaPrefHandler.setSystemDnsResolverSelected(context, true)
applyPersistedStateToUi(context)
}

private fun applyPersistedStateToUi(context: Context) {
binding.dnsSummarySetting.text = if (PiaPrefHandler.isCustomDnsSelected(context)) {
customDnsString(context)
} else if (PiaPrefHandler.isSystemDnsResolverSelected(context)) {
getString(R.string.system_resolver_dns)
} else {
getString(R.string.pia_dns)
}
Expand All @@ -331,8 +404,8 @@ class SettingsSectionNetworkFragment : Fragment() {
}
}

private fun customDnsString(context: Context): String? {
var result: String? = null
private fun customDnsString(context: Context): String {
var result = ""
val primaryDns = PiaPrefHandler.getPrimaryDns(context)
if (!primaryDns.isNullOrEmpty()) {
val secondaryDns = PiaPrefHandler.getSecondaryDns(context)
Expand Down
Loading

0 comments on commit 4624f9c

Please sign in to comment.