Added ipv4 and ipv6 subnet prefix length fields

This commit is contained in:
Juan Gilsanz Polo 2024-02-03 14:38:38 +01:00
parent 5f448f0af0
commit 8df0781da6
5 changed files with 148 additions and 88 deletions

View file

@ -758,5 +758,7 @@
"invalidTime": "Invalid time", "invalidTime": "Invalid time",
"removeDomain": "Remove domain", "removeDomain": "Remove domain",
"addDomain": "Add domain", "addDomain": "Add domain",
"notLess1Hour": "Time cannot be less than 1 hour" "notLess1Hour": "Time cannot be less than 1 hour",
"subnetPrefixLengthIpv4": "Subnet prefix length for IPv4",
"subnetPrefixLengthIpv6": "Subnet prefix length for IPv6"
} }

View file

@ -758,5 +758,7 @@
"invalidTime": "Tiempo no válido", "invalidTime": "Tiempo no válido",
"removeDomain": "Eliminar dominio", "removeDomain": "Eliminar dominio",
"addDomain": "Añadir dominio", "addDomain": "Añadir dominio",
"notLess1Hour": "El tiempo no puede ser inferior a 1 hora" "notLess1Hour": "El tiempo no puede ser inferior a 1 hora",
"subnetPrefixLengthIpv4": "Longitud del prefijo de subred para IPv4",
"subnetPrefixLengthIpv6": "Longitud del prefijo de subred para IPv6"
} }

View file

@ -23,6 +23,8 @@ class DnsInfo {
String blockingIpv6; String blockingIpv6;
List<String> defaultLocalPtrUpstreams; List<String> defaultLocalPtrUpstreams;
int? blockedResponseTtl; int? blockedResponseTtl;
int? ratelimitSubnetLenIpv4;
int? ratelimitSubnetLenIpv6;
DnsInfo({ DnsInfo({
required this.upstreamDns, required this.upstreamDns,
@ -49,6 +51,8 @@ class DnsInfo {
required this.blockingIpv6, required this.blockingIpv6,
required this.defaultLocalPtrUpstreams, required this.defaultLocalPtrUpstreams,
required this.blockedResponseTtl, required this.blockedResponseTtl,
required this.ratelimitSubnetLenIpv4,
required this.ratelimitSubnetLenIpv6,
}); });
factory DnsInfo.fromJson(Map<String, dynamic> json) => DnsInfo( factory DnsInfo.fromJson(Map<String, dynamic> json) => DnsInfo(
@ -75,7 +79,9 @@ class DnsInfo {
blockingIpv4: json["blocking_ipv4"], blockingIpv4: json["blocking_ipv4"],
blockingIpv6: json["blocking_ipv6"], blockingIpv6: json["blocking_ipv6"],
defaultLocalPtrUpstreams: json["default_local_ptr_upstreams"] != null ? List<String>.from(json["default_local_ptr_upstreams"].map((x) => x)) : [], defaultLocalPtrUpstreams: json["default_local_ptr_upstreams"] != null ? List<String>.from(json["default_local_ptr_upstreams"].map((x) => x)) : [],
blockedResponseTtl: json["blocked_response_ttl"] blockedResponseTtl: json["blocked_response_ttl"],
ratelimitSubnetLenIpv4: json["ratelimit_subnet_len_ipv4"],
ratelimitSubnetLenIpv6: json["ratelimit_subnet_len_ipv6"],
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
@ -102,6 +108,8 @@ class DnsInfo {
"blocking_ipv4": blockingIpv4, "blocking_ipv4": blockingIpv4,
"blocking_ipv6": blockingIpv6, "blocking_ipv6": blockingIpv6,
"default_local_ptr_upstreams": List<dynamic>.from(defaultLocalPtrUpstreams.map((x) => x)), "default_local_ptr_upstreams": List<dynamic>.from(defaultLocalPtrUpstreams.map((x) => x)),
"blocked_response_ttl": blockedResponseTtl "blocked_response_ttl": blockedResponseTtl,
"ratelimit_subnet_len_ipv4": ratelimitSubnetLenIpv4,
"ratelimit_subnet_len_ipv6": ratelimitSubnetLenIpv6,
}; };
} }

View file

@ -162,6 +162,8 @@ class DnsProvider with ChangeNotifier {
data.blockingIpv4 = value['blocking_ipv4']; data.blockingIpv4 = value['blocking_ipv4'];
data.blockingIpv6 = value['blocking_ipv6']; data.blockingIpv6 = value['blocking_ipv6'];
data.blockedResponseTtl = value['blocked_response_ttl']; data.blockedResponseTtl = value['blocked_response_ttl'];
data.ratelimitSubnetLenIpv4 = value['ratelimit_subnet_len_ipv4'];
data.ratelimitSubnetLenIpv6 = value['ratelimit_subnet_len_ipv6'];
setDnsInfoData(data); setDnsInfoData(data);
return result; return result;
} }

View file

@ -23,49 +23,49 @@ class DnsServerSettingsScreen extends StatefulWidget {
} }
class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> { class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
final TextEditingController limitRequestsController = TextEditingController(); final _limitRequestsController = TextEditingController();
String? limitRequestsError; final _ipv4PrefixSubnetController = TextEditingController();
String? _ipv4PrefixSubnetError;
final _ipv6PrefixSubnetController = TextEditingController();
String? _ipv6PrefixSubnetError;
String? _limitRequestsError;
final _expandableCustomEdns = ExpandableController(); final _expandableCustomEdns = ExpandableController();
final _expandableEdnsIp = ExpandableController(); final _expandableEdnsIp = ExpandableController();
bool enableEdns = false; bool _enableEdns = false;
bool useCustomIpEdns = false; bool _useCustomIpEdns = false;
final _customIpEdnsController = TextEditingController(); final _customIpEdnsController = TextEditingController();
String? ednsIpError; String? _ednsIpError;
bool enableDnssec = false; bool _enableDnssec = false;
bool disableIpv6Resolving = false; bool _disableIpv6Resolving = false;
String blockingMode = "default"; String blockingMode = "default";
final TextEditingController ipv4controller = TextEditingController(); final _ipv4controller = TextEditingController();
String? ipv4error; String? _ipv4error;
final TextEditingController ipv6controller = TextEditingController(); final _ipv6controller = TextEditingController();
String? ipv6error; String? ipv6error;
final _ttlController = TextEditingController(); final _ttlController = TextEditingController();
String? _ttlError; String? _ttlError;
bool isDataValid = false;
void validateIpv4(String value) { void validateIpv4(String value) {
RegExp ipAddress = RegExp(r'^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$'); RegExp ipAddress = RegExp(r'^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$');
if (ipAddress.hasMatch(value) == true) { if (ipAddress.hasMatch(value) == true) {
setState(() => ipv4error = null); setState(() => _ipv4error = null);
} }
else { else {
setState(() => ipv4error = AppLocalizations.of(context)!.invalidIp); setState(() => _ipv4error = AppLocalizations.of(context)!.invalidIp);
} }
validateData();
} }
void validateEdns(String value) { void validateEdns(String value) {
RegExp ipAddress = RegExp(r'^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$'); RegExp ipAddress = RegExp(r'^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$');
if (ipAddress.hasMatch(value) == true) { if (ipAddress.hasMatch(value) == true) {
setState(() => ednsIpError = null); setState(() => _ednsIpError = null);
} }
else { else {
setState(() => ednsIpError = AppLocalizations.of(context)!.ipNotValid); setState(() => _ednsIpError = AppLocalizations.of(context)!.ipNotValid);
} }
validateData();
} }
void validateIpv6(String value) { void validateIpv6(String value) {
@ -76,63 +76,77 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
else { else {
setState(() => ipv6error = AppLocalizations.of(context)!.invalidIp); setState(() => ipv6error = AppLocalizations.of(context)!.invalidIp);
} }
validateData();
} }
void validateData() { bool validateData() {
if ( if (
limitRequestsController.text != '' && (_limitRequestsController.text == "" || (_limitRequestsController.text != "" && _limitRequestsError == null)) &&
limitRequestsError == null &&
( (
blockingMode != 'custom_ip' || blockingMode != 'custom_ip' ||
( (
blockingMode == 'custom_ip' && blockingMode == 'custom_ip' &&
ipv4controller.text != '' && _ipv4controller.text != '' &&
ipv4error == null && _ipv4error == null &&
ipv6controller.text != '' && _ipv6controller.text != '' &&
ipv6error == null ipv6error == null
) )
) == true && ) == true &&
ednsIpError == null && _ednsIpError == null &&
_ttlError == null _ttlController.text != "" && _ttlError == null &&
(_ipv4PrefixSubnetController.text == "" || (_ipv4PrefixSubnetController.text != "" && _ipv4PrefixSubnetError == null)) &&
(_ipv6PrefixSubnetController.text == "" || (_ipv6PrefixSubnetController.text != "" && _ipv6PrefixSubnetError == null))
) { ) {
setState(() => isDataValid = true); return true;
} }
else { else {
setState(() => isDataValid = false); return false;
} }
} }
void validateNumber(String value) { String? validateTtl(String value) {
final regex = RegExp(r'^(\d)+$'); if (value == "") return AppLocalizations.of(context)!.valueNotNumber;
if (regex.hasMatch(value) == true) { if (int.tryParse(value) != null) {
setState(() => _ttlError = null); return null;
} }
else { else {
setState(() => _ttlError = AppLocalizations.of(context)!.invalidValue); return AppLocalizations.of(context)!.valueNotNumber;
}
}
String? validateNumber(String value) {
if (value == "") return null;
if (int.tryParse(value) != null) {
return null;
}
else {
return AppLocalizations.of(context)!.valueNotNumber;
} }
validateData();
} }
@override @override
void initState() { void initState() {
final dnsProvider = Provider.of<DnsProvider>(context, listen: false); final dnsProvider = Provider.of<DnsProvider>(context, listen: false);
limitRequestsController.text = dnsProvider.dnsInfo!.ratelimit.toString(); _limitRequestsController.text = dnsProvider.dnsInfo!.ratelimit.toString();
enableEdns = dnsProvider.dnsInfo!.ednsCsEnabled; _enableEdns = dnsProvider.dnsInfo!.ednsCsEnabled;
useCustomIpEdns = dnsProvider.dnsInfo!.ednsCsUseCustom ?? false; _useCustomIpEdns = dnsProvider.dnsInfo!.ednsCsUseCustom ?? false;
_customIpEdnsController.text = dnsProvider.dnsInfo!.ednsCsCustomIp ?? ""; _customIpEdnsController.text = dnsProvider.dnsInfo!.ednsCsCustomIp ?? "";
if (dnsProvider.dnsInfo!.ednsCsEnabled == true) _expandableCustomEdns.toggle(); if (dnsProvider.dnsInfo!.ednsCsEnabled == true) _expandableCustomEdns.toggle();
if (dnsProvider.dnsInfo!.ednsCsUseCustom == true) _expandableEdnsIp.toggle(); if (dnsProvider.dnsInfo!.ednsCsUseCustom == true) _expandableEdnsIp.toggle();
enableDnssec = dnsProvider.dnsInfo!.dnssecEnabled; _enableDnssec = dnsProvider.dnsInfo!.dnssecEnabled;
disableIpv6Resolving = dnsProvider.dnsInfo!.disableIpv6; _disableIpv6Resolving = dnsProvider.dnsInfo!.disableIpv6;
blockingMode = dnsProvider.dnsInfo!.blockingMode; blockingMode = dnsProvider.dnsInfo!.blockingMode;
ipv4controller.text = dnsProvider.dnsInfo!.blockingIpv4; _ipv4controller.text = dnsProvider.dnsInfo!.blockingIpv4;
ipv6controller.text = dnsProvider.dnsInfo!.blockingIpv6; _ipv6controller.text = dnsProvider.dnsInfo!.blockingIpv6;
isDataValid = true;
_ttlController.text = dnsProvider.dnsInfo!.blockedResponseTtl != null _ttlController.text = dnsProvider.dnsInfo!.blockedResponseTtl != null
? dnsProvider.dnsInfo!.blockedResponseTtl.toString() ? dnsProvider.dnsInfo!.blockedResponseTtl.toString()
: ""; : "";
_ipv4PrefixSubnetController.text = dnsProvider.dnsInfo!.ratelimitSubnetLenIpv4 != null
? dnsProvider.dnsInfo!.ratelimitSubnetLenIpv4.toString()
: "";
_ipv6PrefixSubnetController.text = dnsProvider.dnsInfo!.ratelimitSubnetLenIpv6 != null
? dnsProvider.dnsInfo!.ratelimitSubnetLenIpv6.toString()
: "";
super.initState(); super.initState();
} }
@ -147,16 +161,18 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
processModal.open(AppLocalizations.of(context)!.savingConfig); processModal.open(AppLocalizations.of(context)!.savingConfig);
final result = await dnsProvider.saveDnsServerConfig({ final result = await dnsProvider.saveDnsServerConfig({
"ratelimit": int.parse(limitRequestsController.text), "ratelimit": int.parse(_limitRequestsController.text),
"edns_cs_enabled": enableEdns, "edns_cs_enabled": _enableEdns,
"edns_cs_use_custom": useCustomIpEdns, "edns_cs_use_custom": _useCustomIpEdns,
"edns_cs_custom_ip": _customIpEdnsController.text, "edns_cs_custom_ip": _customIpEdnsController.text,
"dnssec_enabled": enableDnssec, "dnssec_enabled": _enableDnssec,
"disable_ipv6": disableIpv6Resolving, "disable_ipv6": _disableIpv6Resolving,
"blocking_mode": blockingMode, "blocking_mode": blockingMode,
"blocking_ipv4": ipv4controller.text, "blocking_ipv4": _ipv4controller.text,
"blocking_ipv6": ipv6controller.text, "blocking_ipv6": _ipv6controller.text,
"blocked_response_ttl": int.tryParse(_ttlController.text) "blocked_response_ttl": int.tryParse(_ttlController.text),
"ratelimit_subnet_len_ipv4": int.tryParse(_ipv4PrefixSubnetController.text),
"ratelimit_subnet_len_ipv6": int.tryParse(_ipv6PrefixSubnetController.text),
}); });
processModal.close(); processModal.close();
@ -186,22 +202,24 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
void updateBlockingMode(String mode) { void updateBlockingMode(String mode) {
if (mode != 'custom_ip') { if (mode != 'custom_ip') {
ipv4controller.text = ''; _ipv4controller.text = '';
ipv4error = null; _ipv4error = null;
ipv6controller.text = ''; _ipv6controller.text = '';
ipv6error = null; ipv6error = null;
} }
setState(() => blockingMode = mode); setState(() => blockingMode = mode);
validateData(); validateData();
} }
final dataValid = validateData();
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(AppLocalizations.of(context)!.dnsServerSettings), title: Text(AppLocalizations.of(context)!.dnsServerSettings),
surfaceTintColor: isDesktop(width) ? Colors.transparent : null, surfaceTintColor: isDesktop(width) ? Colors.transparent : null,
actions: [ actions: [
IconButton( IconButton(
onPressed: isDataValid == true onPressed: dataValid == true
? () => saveData() ? () => saveData()
: null, : null,
icon: const Icon(Icons.save_rounded), icon: const Icon(Icons.save_rounded),
@ -217,16 +235,8 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
child: TextFormField( child: TextFormField(
controller: limitRequestsController, controller: _limitRequestsController,
onChanged: (value) { onChanged: (v) => setState(() => _limitRequestsError = validateNumber(v)),
if (int.tryParse(value) != null) {
setState(() => limitRequestsError = null);
}
else {
setState(() => limitRequestsError = AppLocalizations.of(context)!.valueNotNumber);
}
validateData();
},
decoration: InputDecoration( decoration: InputDecoration(
prefixIcon: const Icon(Icons.looks_one_rounded), prefixIcon: const Icon(Icons.looks_one_rounded),
border: const OutlineInputBorder( border: const OutlineInputBorder(
@ -235,22 +245,58 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
) )
), ),
labelText: AppLocalizations.of(context)!.limitRequestsSecond, labelText: AppLocalizations.of(context)!.limitRequestsSecond,
errorText: limitRequestsError errorText: _limitRequestsError
), ),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
), ),
), ),
const SizedBox(height: 10), Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
child: TextFormField(
controller: _ipv4PrefixSubnetController,
onChanged: (v) => setState(() => _ipv4PrefixSubnetError = validateNumber(v)),
decoration: InputDecoration(
prefixIcon: const Icon(Icons.skip_previous_rounded),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(10)
)
),
labelText: AppLocalizations.of(context)!.subnetPrefixLengthIpv4,
errorText: _ipv4PrefixSubnetError
),
keyboardType: TextInputType.number,
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: TextFormField(
controller: _ipv6PrefixSubnetController,
onChanged: (v) => setState(() => _ipv6PrefixSubnetError = validateNumber(v)),
decoration: InputDecoration(
prefixIcon: const Icon(Icons.skip_previous_rounded),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(10)
)
),
labelText: AppLocalizations.of(context)!.subnetPrefixLengthIpv6,
errorText: _ipv6PrefixSubnetError
),
keyboardType: TextInputType.number,
),
),
const SizedBox(height: 16),
CustomSwitchListTile( CustomSwitchListTile(
value: enableEdns, value: _enableEdns,
onChanged: (value) => setState(() { onChanged: (value) => setState(() {
enableEdns = value; _enableEdns = value;
_expandableCustomEdns.toggle(); _expandableCustomEdns.toggle();
if (value == false) { if (value == false) {
useCustomIpEdns = false; _useCustomIpEdns = false;
if (_expandableEdnsIp.expanded == true) _expandableEdnsIp.toggle(); if (_expandableEdnsIp.expanded == true) _expandableEdnsIp.toggle();
_customIpEdnsController.text = ""; _customIpEdnsController.text = "";
ednsIpError = null; _ednsIpError = null;
} }
validateData(); validateData();
}), }),
@ -270,13 +316,13 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
bottom: 12, bottom: 12,
right: 16 right: 16
), ),
value: useCustomIpEdns, value: _useCustomIpEdns,
onChanged: (value) => setState(() { onChanged: (value) => setState(() {
useCustomIpEdns = value; _useCustomIpEdns = value;
_expandableEdnsIp.toggle(); _expandableEdnsIp.toggle();
if (useCustomIpEdns == false) { if (_useCustomIpEdns == false) {
_customIpEdnsController.text = ""; _customIpEdnsController.text = "";
ednsIpError = null; _ednsIpError = null;
} }
validateData(); validateData();
}), }),
@ -304,7 +350,7 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
Radius.circular(10) Radius.circular(10)
) )
), ),
errorText: ednsIpError, errorText: _ednsIpError,
labelText: AppLocalizations.of(context)!.ipAddress, labelText: AppLocalizations.of(context)!.ipAddress,
), ),
), ),
@ -316,14 +362,14 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
) )
), ),
CustomSwitchListTile( CustomSwitchListTile(
value: enableDnssec, value: _enableDnssec,
onChanged: (value) => setState(() => enableDnssec = value), onChanged: (value) => setState(() => _enableDnssec = value),
title: AppLocalizations.of(context)!.enableDnssec, title: AppLocalizations.of(context)!.enableDnssec,
subtitle: AppLocalizations.of(context)!.enableDnssecDescription, subtitle: AppLocalizations.of(context)!.enableDnssecDescription,
), ),
CustomSwitchListTile( CustomSwitchListTile(
value: disableIpv6Resolving, value: _disableIpv6Resolving,
onChanged: (value) => setState(() => disableIpv6Resolving = value), onChanged: (value) => setState(() => _disableIpv6Resolving = value),
title: AppLocalizations.of(context)!.disableResolvingIpv6, title: AppLocalizations.of(context)!.disableResolvingIpv6,
subtitle: AppLocalizations.of(context)!.disableResolvingIpv6Description, subtitle: AppLocalizations.of(context)!.disableResolvingIpv6Description,
), ),
@ -373,7 +419,7 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 24), padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField( child: TextFormField(
controller: ipv4controller, controller: _ipv4controller,
onChanged: validateIpv4, onChanged: validateIpv4,
decoration: InputDecoration( decoration: InputDecoration(
prefixIcon: const Icon(Icons.link_rounded), prefixIcon: const Icon(Icons.link_rounded),
@ -382,7 +428,7 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
Radius.circular(10) Radius.circular(10)
) )
), ),
errorText: ipv4error, errorText: _ipv4error,
helperText: AppLocalizations.of(context)!.blockingIpv4Description, helperText: AppLocalizations.of(context)!.blockingIpv4Description,
helperMaxLines: 10, helperMaxLines: 10,
labelText: AppLocalizations.of(context)!.blockingIpv4, labelText: AppLocalizations.of(context)!.blockingIpv4,
@ -394,7 +440,7 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 24), padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField( child: TextFormField(
controller: ipv6controller, controller: _ipv6controller,
onChanged: validateIpv6, onChanged: validateIpv6,
decoration: InputDecoration( decoration: InputDecoration(
prefixIcon: const Icon(Icons.link_rounded), prefixIcon: const Icon(Icons.link_rounded),
@ -417,7 +463,7 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: TextFormField( child: TextFormField(
controller: _ttlController, controller: _ttlController,
onChanged: validateNumber, onChanged: (v) => setState(() => _ttlError = validateTtl(v)),
decoration: InputDecoration( decoration: InputDecoration(
prefixIcon: const Icon(Icons.timer_rounded), prefixIcon: const Icon(Icons.timer_rounded),
border: const OutlineInputBorder( border: const OutlineInputBorder(