diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 1e9271d..15da154 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -697,5 +697,7 @@ "configurationResetSuccessfully": "Configuration resetted successfully", "configurationResetError": "The configuration couldn't be resetted", "testUpstreamDnsServers": "Test upstream DNS servers", - "errorTestUpstreamDns": "Error when testing upstream DNS servers." + "errorTestUpstreamDns": "Error when testing upstream DNS servers.", + "useCustomIpEdns": "Use custom IP for EDNS", + "useCustomIpEdnsDescription": "Allow to use custom IP for EDNS" } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index f649320..c226a91 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -697,5 +697,7 @@ "configurationResetSuccessfully": "Configuración reseteada correctamente", "configurationResetError": "La configuración no ha podido ser reseteada", "testUpstreamDnsServers": "Probar servidores DNS de subida", - "errorTestUpstreamDns": "Error al probar los servidores DNS de subida." + "errorTestUpstreamDns": "Error al probar los servidores DNS de subida.", + "useCustomIpEdns": "Usar IP personalizada para EDNS", + "useCustomIpEdnsDescription": "Permitir usar IP personalizada para EDNS" } \ No newline at end of file diff --git a/lib/models/dns_info.dart b/lib/models/dns_info.dart index 4e85400..0d3aa83 100644 --- a/lib/models/dns_info.dart +++ b/lib/models/dns_info.dart @@ -6,6 +6,8 @@ class DnsInfo { int ratelimit; String blockingMode; bool ednsCsEnabled; + bool? ednsCsUseCustom; + String? ednsCsCustomIp; bool dnssecEnabled; bool disableIpv6; String? upstreamMode; @@ -28,6 +30,8 @@ class DnsInfo { required this.ratelimit, required this.blockingMode, required this.ednsCsEnabled, + required this.ednsCsUseCustom, + required this.ednsCsCustomIp, required this.dnssecEnabled, required this.disableIpv6, required this.upstreamMode, @@ -51,6 +55,8 @@ class DnsInfo { ratelimit: json["ratelimit"], blockingMode: json["blocking_mode"], ednsCsEnabled: json["edns_cs_enabled"], + ednsCsUseCustom: json["edns_cs_use_custom"], + ednsCsCustomIp: json["edns_cs_custom_ip"], dnssecEnabled: json["dnssec_enabled"], disableIpv6: json["disable_ipv6"], upstreamMode: json["upstream_mode"], @@ -74,6 +80,8 @@ class DnsInfo { "ratelimit": ratelimit, "blocking_mode": blockingMode, "edns_cs_enabled": ednsCsEnabled, + "edns_cs_use_custom": ednsCsUseCustom, + "edns_cs_custom_ip": ednsCsCustomIp, "dnssec_enabled": dnssecEnabled, "disable_ipv6": disableIpv6, "upstream_mode": upstreamMode, diff --git a/lib/screens/settings/dns/dns_server_settings.dart b/lib/screens/settings/dns/dns_server_settings.dart index 4cc650a..19f6c09 100644 --- a/lib/screens/settings/dns/dns_server_settings.dart +++ b/lib/screens/settings/dns/dns_server_settings.dart @@ -1,5 +1,6 @@ // ignore_for_file: use_build_context_synchronously +import 'package:expandable/expandable.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -24,7 +25,12 @@ class DnsServerSettingsScreen extends StatefulWidget { class _DnsServerSettingsScreenState extends State { final TextEditingController limitRequestsController = TextEditingController(); String? limitRequestsError; + final _expandableCustomEdns = ExpandableController(); + final _expandableEdnsIp = ExpandableController(); bool enableEdns = false; + bool useCustomIpEdns = false; + final _customIpEdnsController = TextEditingController(); + String? ednsIpError; bool enableDnssec = false; bool disableIpv6Resolving = false; @@ -48,6 +54,17 @@ class _DnsServerSettingsScreenState extends State { validateData(); } + void validateEdns(String value) { + RegExp ipAddress = RegExp(r'^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$'); + if (ipAddress.hasMatch(value) == true) { + setState(() => ednsIpError = null); + } + else { + setState(() => ednsIpError = AppLocalizations.of(context)!.ipNotValid); + } + validateData(); + } + void validateIpv6(String value) { RegExp ipAddress = RegExp(r'(?:^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$)|(?:^(?:(?:[a-fA-F\d]{1,4}:){7}(?:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,2}|:)|(?:[a-fA-F\d]{1,4}:){4}(?:(?::[a-fA-F\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,3}|:)|(?:[a-fA-F\d]{1,4}:){3}(?:(?::[a-fA-F\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,4}|:)|(?:[a-fA-F\d]{1,4}:){2}(?:(?::[a-fA-F\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,5}|:)|(?:[a-fA-F\d]{1,4}:){1}(?:(?::[a-fA-F\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,6}|:)|(?::(?:(?::[a-fA-F\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,7}|:)))(?:%[0-9a-zA-Z]{1,})?$)'); if (ipAddress.hasMatch(value) == true) { @@ -72,7 +89,8 @@ class _DnsServerSettingsScreenState extends State { ipv6controller.text != '' && ipv6error == null ) - ) == true + ) == true && + ednsIpError == null ) { setState(() => isDataValid = true); } @@ -87,6 +105,10 @@ class _DnsServerSettingsScreenState extends State { limitRequestsController.text = dnsProvider.dnsInfo!.ratelimit.toString(); enableEdns = dnsProvider.dnsInfo!.ednsCsEnabled; + useCustomIpEdns = dnsProvider.dnsInfo!.ednsCsUseCustom ?? false; + _customIpEdnsController.text = dnsProvider.dnsInfo!.ednsCsCustomIp ?? ""; + if (dnsProvider.dnsInfo!.ednsCsEnabled == true) _expandableCustomEdns.toggle(); + if (dnsProvider.dnsInfo!.ednsCsUseCustom == true) _expandableEdnsIp.toggle(); enableDnssec = dnsProvider.dnsInfo!.dnssecEnabled; disableIpv6Resolving = dnsProvider.dnsInfo!.disableIpv6; blockingMode = dnsProvider.dnsInfo!.blockingMode; @@ -109,6 +131,8 @@ class _DnsServerSettingsScreenState extends State { final result = await dnsProvider.saveDnsServerConfig({ "ratelimit": int.parse(limitRequestsController.text), "edns_cs_enabled": enableEdns, + "edns_cs_use_custom": useCustomIpEdns, + "edns_cs_custom_ip": _customIpEdnsController.text, "dnssec_enabled": enableDnssec, "disable_ipv6": disableIpv6Resolving, "blocking_mode": blockingMode, @@ -200,10 +224,78 @@ class _DnsServerSettingsScreenState extends State { const SizedBox(height: 10), CustomSwitchListTile( value: enableEdns, - onChanged: (value) => setState(() => enableEdns = value), + onChanged: (value) => setState(() { + enableEdns = value; + _expandableCustomEdns.toggle(); + if (value == false) { + useCustomIpEdns = false; + if (_expandableEdnsIp.expanded == true) _expandableEdnsIp.toggle(); + _customIpEdnsController.text = ""; + ednsIpError = null; + } + validateData(); + }), title: AppLocalizations.of(context)!.enableEdns, subtitle: AppLocalizations.of(context)!.enableEdnsDescription, ), + ExpandableNotifier( + controller: _expandableCustomEdns, + child: Expandable( + collapsed: const SizedBox(), + expanded: Column( + children: [ + CustomSwitchListTile( + padding: const EdgeInsets.only( + left: 50, + top: 12, + bottom: 12, + right: 16 + ), + value: useCustomIpEdns, + onChanged: (value) => setState(() { + useCustomIpEdns = value; + _expandableEdnsIp.toggle(); + if (useCustomIpEdns == false) { + _customIpEdnsController.text = ""; + ednsIpError = null; + } + validateData(); + }), + title: AppLocalizations.of(context)!.useCustomIpEdns, + subtitle: AppLocalizations.of(context)!.useCustomIpEdnsDescription, + ), + ExpandableNotifier( + controller: _expandableEdnsIp, + child: Expandable( + collapsed: const SizedBox(), + expanded: Padding( + padding: const EdgeInsets.only( + top: 16, + bottom: 16, + right: 16, + left: 70 + ), + child: TextFormField( + controller: _customIpEdnsController, + onChanged: validateEdns, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.link_rounded), + border: const OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(10) + ) + ), + errorText: ednsIpError, + labelText: AppLocalizations.of(context)!.ipAddress, + ), + ), + ), + ) + ), + ], + ), + ) + ), CustomSwitchListTile( value: enableDnssec, onChanged: (value) => setState(() => enableDnssec = value),