refactor: Move deSEC DNS conversion into DesecDnsRecord model

- Get rid of desired dns records hardcore for deSEC
pull/392/head
NaiJi ✨ 2023-11-08 18:31:28 +04:00
parent dd9722646e
commit db1a8b6f67
4 changed files with 111 additions and 200 deletions

View File

@ -1,4 +1,5 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:selfprivacy/logic/models/json/dns_records.dart';
part 'desec_dns_info.g.dart';
@ -28,6 +29,25 @@ class DesecDomain {
/// https://desec.readthedocs.io/en/latest/dns/rrsets.html#retrieving-and-creating-dns-records
@JsonSerializable()
class DesecDnsRecord {
factory DesecDnsRecord.fromDnsRecord(final DnsRecord record) {
final String type = record.type;
String content = record.content ?? '';
String name = record.name ?? '';
if (type == 'MX') {
name = '';
content = '10 $content';
}
if (type == 'TXT' && content.isNotEmpty && !content.startsWith('"')) {
content = '"$content"';
}
return DesecDnsRecord(
subname: name,
type: type,
ttl: record.ttl,
records: [content],
);
}
DesecDnsRecord({
required this.subname,
required this.type,

View File

@ -4,6 +4,7 @@ import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/json/desec_dns_info.dart';
import 'package:selfprivacy/logic/models/json/dns_records.dart';
import 'package:selfprivacy/logic/providers/dns_providers/dns_provider.dart';
import 'package:selfprivacy/utils/network_utils.dart';
class ApiAdapter {
ApiAdapter({final bool isWithToken = true})
@ -75,21 +76,14 @@ class DesecDnsProvider extends DnsProvider {
required final ServerDomain domain,
final String? ip4,
}) async {
final List<DnsRecord> listDnsRecords = projectDnsRecords(
final List<DnsRecord> listDnsRecords = getProjectDnsRecords(
domain.domainName,
ip4,
);
final List<DesecDnsRecord> bulkRecords = [];
for (final DnsRecord record in listDnsRecords) {
bulkRecords.add(
DesecDnsRecord(
subname: record.name ?? '',
type: record.type,
ttl: record.ttl,
records: [extractContent(record) ?? ''],
),
);
bulkRecords.add(DesecDnsRecord.fromDnsRecord(record));
}
return _adapter.api().createMultipleDnsRecords(
@ -103,18 +97,19 @@ class DesecDnsProvider extends DnsProvider {
required final ServerDomain domain,
final String? ip4,
}) async {
final List<DnsRecord> listDnsRecords = projectDnsRecords(
final List<DnsRecord> listDnsRecords = getProjectDnsRecords(
domain.domainName,
ip4,
);
final List<DesecDnsRecord> bulkRecords = [];
for (final DnsRecord record in listDnsRecords) {
final desecRecord = DesecDnsRecord.fromDnsRecord(record);
bulkRecords.add(
DesecDnsRecord(
subname: record.name ?? '',
type: record.type,
ttl: record.ttl,
subname: desecRecord.subname,
type: desecRecord.type,
ttl: desecRecord.ttl,
records: [],
),
);
@ -181,14 +176,7 @@ class DesecDnsProvider extends DnsProvider {
) async {
final result = await _adapter.api().createMultipleDnsRecords(
domainName: domain.domainName,
records: [
DesecDnsRecord(
subname: record.name ?? '',
type: record.type,
ttl: record.ttl,
records: [extractContent(record) ?? ''],
),
],
records: [DesecDnsRecord.fromDnsRecord(record)],
);
return GenericResult(
@ -197,22 +185,14 @@ class DesecDnsProvider extends DnsProvider {
);
}
String? extractContent(final DnsRecord record) {
String? content = record.content;
if (record.type == 'TXT' && content != null && !content.startsWith('"')) {
content = '"$content"';
}
return content;
}
@override
Future<GenericResult<List<DesiredDnsRecord>>> validateDnsRecords(
final ServerDomain domain,
final String ip4,
final String dkimPublicKey,
final List<DnsRecord> pendingDnsRecords,
) async {
final result = await getDnsRecords(domain: domain);
final result = await _adapter.api().getDnsRecords(domain.domainName);
if (result.data.isEmpty || !result.success) {
return GenericResult(
success: result.success,
@ -225,45 +205,47 @@ class DesecDnsProvider extends DnsProvider {
final records = result.data;
final List<DesiredDnsRecord> foundRecords = [];
try {
final List<DesiredDnsRecord> desiredRecords =
getDesiredDnsRecords(domain.domainName, ip4, dkimPublicKey);
for (final DesiredDnsRecord record in desiredRecords) {
if (record.description == 'record.dkim') {
final DnsRecord foundRecord = records.firstWhere(
for (final DnsRecord pendingDnsRecord in pendingDnsRecords) {
final record = DesecDnsRecord.fromDnsRecord(pendingDnsRecord);
if (record.subname == 'selector._domainkey') {
final DesecDnsRecord foundRecord = records.firstWhere(
(final r) =>
('${r.name}.${domain.domainName}' == record.name) &&
('${r.subname}.${domain.domainName}' == record.subname) &&
r.type == record.type,
orElse: () => DnsRecord(
name: record.name,
orElse: () => DesecDnsRecord(
subname: record.subname,
type: record.type,
content: '',
ttl: 800,
proxied: false,
records: [],
ttl: record.ttl,
),
);
final desecRecords = foundRecord.records;
final content = desecRecords.isEmpty ? '' : desecRecords[0];
final String foundContent = content.replaceAll(RegExp(r'\s+'), '');
final String desiredContent =
record.records[0].replaceAll(RegExp(r'\s+'), '');
foundRecords.add(
DesiredDnsRecord(
name: record.subname,
content: record.records[0],
isSatisfied: foundContent == desiredContent,
),
);
// remove all spaces and tabulators from
// the foundRecord.content and the record.content
// to compare them
final String? foundContent =
foundRecord.content?.replaceAll(RegExp(r'\s+'), '');
final String content = record.content.replaceAll(RegExp(r'\s+'), '');
if (foundContent == content) {
foundRecords.add(record.copyWith(isSatisfied: true));
} else {
foundRecords.add(record.copyWith(isSatisfied: false));
}
} else {
if (records.any(
final foundMatch = records.any(
(final r) =>
('${r.name}.${domain.domainName}' == record.name ||
record.name == '') &&
('${r.subname}.${domain.domainName}' == record.subname ||
record.subname == '') &&
r.type == record.type &&
r.content == record.content,
)) {
foundRecords.add(record.copyWith(isSatisfied: true));
} else {
foundRecords.add(record.copyWith(isSatisfied: false));
}
r.records[0] == record.records[0],
);
foundRecords.add(
DesiredDnsRecord(
name: record.subname,
content: record.records[0],
isSatisfied: foundMatch,
),
);
}
}
} catch (e) {
@ -279,133 +261,4 @@ class DesecDnsProvider extends DnsProvider {
success: true,
);
}
List<DnsRecord> projectDnsRecords(
final String? domainName,
final String? ip4,
) {
final DnsRecord domainA = DnsRecord(type: 'A', name: '', content: ip4);
final DnsRecord mx =
DnsRecord(type: 'MX', name: '', content: '10 $domainName.');
final DnsRecord apiA = DnsRecord(type: 'A', name: 'api', content: ip4);
final DnsRecord cloudA = DnsRecord(type: 'A', name: 'cloud', content: ip4);
final DnsRecord gitA = DnsRecord(type: 'A', name: 'git', content: ip4);
final DnsRecord meetA = DnsRecord(type: 'A', name: 'meet', content: ip4);
final DnsRecord passwordA =
DnsRecord(type: 'A', name: 'password', content: ip4);
final DnsRecord socialA =
DnsRecord(type: 'A', name: 'social', content: ip4);
final DnsRecord vpn = DnsRecord(type: 'A', name: 'vpn', content: ip4);
final DnsRecord txt1 = DnsRecord(
type: 'TXT',
name: '_dmarc',
content: '"v=DMARC1; p=none"',
ttl: 18000,
);
final DnsRecord txt2 = DnsRecord(
type: 'TXT',
name: '',
content: '"v=spf1 a mx ip4:$ip4 -all"',
ttl: 18000,
);
return <DnsRecord>[
domainA,
apiA,
cloudA,
gitA,
meetA,
passwordA,
socialA,
mx,
txt1,
txt2,
vpn
];
}
@override
List<DesiredDnsRecord> getDesiredDnsRecords(
final String? domainName,
final String? ip4,
final String? dkimPublicKey,
) {
if (domainName == null || ip4 == null) {
return [];
}
return [
DesiredDnsRecord(
name: '',
content: ip4,
description: 'record.root',
),
DesiredDnsRecord(
name: 'api.$domainName',
content: ip4,
description: 'record.api',
),
DesiredDnsRecord(
name: 'cloud.$domainName',
content: ip4,
description: 'record.cloud',
),
DesiredDnsRecord(
name: 'git.$domainName',
content: ip4,
description: 'record.git',
),
DesiredDnsRecord(
name: 'meet.$domainName',
content: ip4,
description: 'record.meet',
),
DesiredDnsRecord(
name: 'social.$domainName',
content: ip4,
description: 'record.social',
),
DesiredDnsRecord(
name: 'password.$domainName',
content: ip4,
description: 'record.password',
),
DesiredDnsRecord(
name: 'vpn.$domainName',
content: ip4,
description: 'record.vpn',
),
DesiredDnsRecord(
name: '',
content: '10 $domainName.',
description: 'record.mx',
type: 'MX',
category: DnsRecordsCategory.email,
),
DesiredDnsRecord(
name: '_dmarc.$domainName',
content: '"v=DMARC1; p=none"',
description: 'record.dmarc',
type: 'TXT',
category: DnsRecordsCategory.email,
),
DesiredDnsRecord(
name: '',
content: '"v=spf1 a mx ip4:$ip4 -all"',
description: 'record.spf',
type: 'TXT',
category: DnsRecordsCategory.email,
),
if (dkimPublicKey != null)
DesiredDnsRecord(
name: 'selector._domainkey.$domainName',
content: '"$dkimPublicKey"',
description: 'record.dkim',
type: 'TXT',
category: DnsRecordsCategory.email,
),
];
}
}

View File

@ -63,14 +63,6 @@ abstract class DnsProvider {
final ServerDomain domain,
final String ip4,
final String dkimPublicKey,
);
/// Will return list of [DesiredDnsRecord] objects, which represent
/// samples of perfect DNS records we need to know about in order to launch
/// SelfPrivacy application correctly.
List<DesiredDnsRecord> getDesiredDnsRecords(
final String? domainName,
final String? ip4,
final String? dkimPublicKey,
final List<DnsRecord> pendingDnsRecords,
);
}

View File

@ -41,3 +41,49 @@ void launchURL(final url) async {
print(e);
}
}
List<DnsRecord> getProjectDnsRecords(
final String? domainName,
final String? ip4,
) {
final DnsRecord domainA =
DnsRecord(type: 'A', name: domainName, content: ip4);
final DnsRecord mx = DnsRecord(type: 'MX', name: '@', content: domainName);
final DnsRecord apiA = DnsRecord(type: 'A', name: 'api', content: ip4);
final DnsRecord cloudA = DnsRecord(type: 'A', name: 'cloud', content: ip4);
final DnsRecord gitA = DnsRecord(type: 'A', name: 'git', content: ip4);
final DnsRecord meetA = DnsRecord(type: 'A', name: 'meet', content: ip4);
final DnsRecord passwordA =
DnsRecord(type: 'A', name: 'password', content: ip4);
final DnsRecord socialA = DnsRecord(type: 'A', name: 'social', content: ip4);
final DnsRecord vpn = DnsRecord(type: 'A', name: 'vpn', content: ip4);
final DnsRecord txt1 = DnsRecord(
type: 'TXT',
name: '_dmarc',
content: 'v=DMARC1; p=none',
ttl: 18000,
);
final DnsRecord txt2 = DnsRecord(
type: 'TXT',
name: domainName,
content: 'v=spf1 a mx ip4:$ip4 -all',
ttl: 18000,
);
return <DnsRecord>[
domainA,
apiA,
cloudA,
gitA,
meetA,
passwordA,
socialA,
mx,
txt1,
txt2,
vpn,
];
}