refactor: Move deSEC DNS conversion into DesecDnsRecord model
- Get rid of desired dns records hardcore for deSECpull/392/head
parent
dd9722646e
commit
db1a8b6f67
|
@ -1,4 +1,5 @@
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/json/dns_records.dart';
|
||||||
|
|
||||||
part 'desec_dns_info.g.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
|
/// https://desec.readthedocs.io/en/latest/dns/rrsets.html#retrieving-and-creating-dns-records
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class DesecDnsRecord {
|
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({
|
DesecDnsRecord({
|
||||||
required this.subname,
|
required this.subname,
|
||||||
required this.type,
|
required this.type,
|
||||||
|
|
|
@ -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/desec_dns_info.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/dns_records.dart';
|
import 'package:selfprivacy/logic/models/json/dns_records.dart';
|
||||||
import 'package:selfprivacy/logic/providers/dns_providers/dns_provider.dart';
|
import 'package:selfprivacy/logic/providers/dns_providers/dns_provider.dart';
|
||||||
|
import 'package:selfprivacy/utils/network_utils.dart';
|
||||||
|
|
||||||
class ApiAdapter {
|
class ApiAdapter {
|
||||||
ApiAdapter({final bool isWithToken = true})
|
ApiAdapter({final bool isWithToken = true})
|
||||||
|
@ -75,21 +76,14 @@ class DesecDnsProvider extends DnsProvider {
|
||||||
required final ServerDomain domain,
|
required final ServerDomain domain,
|
||||||
final String? ip4,
|
final String? ip4,
|
||||||
}) async {
|
}) async {
|
||||||
final List<DnsRecord> listDnsRecords = projectDnsRecords(
|
final List<DnsRecord> listDnsRecords = getProjectDnsRecords(
|
||||||
domain.domainName,
|
domain.domainName,
|
||||||
ip4,
|
ip4,
|
||||||
);
|
);
|
||||||
|
|
||||||
final List<DesecDnsRecord> bulkRecords = [];
|
final List<DesecDnsRecord> bulkRecords = [];
|
||||||
for (final DnsRecord record in listDnsRecords) {
|
for (final DnsRecord record in listDnsRecords) {
|
||||||
bulkRecords.add(
|
bulkRecords.add(DesecDnsRecord.fromDnsRecord(record));
|
||||||
DesecDnsRecord(
|
|
||||||
subname: record.name ?? '',
|
|
||||||
type: record.type,
|
|
||||||
ttl: record.ttl,
|
|
||||||
records: [extractContent(record) ?? ''],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return _adapter.api().createMultipleDnsRecords(
|
return _adapter.api().createMultipleDnsRecords(
|
||||||
|
@ -103,18 +97,19 @@ class DesecDnsProvider extends DnsProvider {
|
||||||
required final ServerDomain domain,
|
required final ServerDomain domain,
|
||||||
final String? ip4,
|
final String? ip4,
|
||||||
}) async {
|
}) async {
|
||||||
final List<DnsRecord> listDnsRecords = projectDnsRecords(
|
final List<DnsRecord> listDnsRecords = getProjectDnsRecords(
|
||||||
domain.domainName,
|
domain.domainName,
|
||||||
ip4,
|
ip4,
|
||||||
);
|
);
|
||||||
|
|
||||||
final List<DesecDnsRecord> bulkRecords = [];
|
final List<DesecDnsRecord> bulkRecords = [];
|
||||||
for (final DnsRecord record in listDnsRecords) {
|
for (final DnsRecord record in listDnsRecords) {
|
||||||
|
final desecRecord = DesecDnsRecord.fromDnsRecord(record);
|
||||||
bulkRecords.add(
|
bulkRecords.add(
|
||||||
DesecDnsRecord(
|
DesecDnsRecord(
|
||||||
subname: record.name ?? '',
|
subname: desecRecord.subname,
|
||||||
type: record.type,
|
type: desecRecord.type,
|
||||||
ttl: record.ttl,
|
ttl: desecRecord.ttl,
|
||||||
records: [],
|
records: [],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -181,14 +176,7 @@ class DesecDnsProvider extends DnsProvider {
|
||||||
) async {
|
) async {
|
||||||
final result = await _adapter.api().createMultipleDnsRecords(
|
final result = await _adapter.api().createMultipleDnsRecords(
|
||||||
domainName: domain.domainName,
|
domainName: domain.domainName,
|
||||||
records: [
|
records: [DesecDnsRecord.fromDnsRecord(record)],
|
||||||
DesecDnsRecord(
|
|
||||||
subname: record.name ?? '',
|
|
||||||
type: record.type,
|
|
||||||
ttl: record.ttl,
|
|
||||||
records: [extractContent(record) ?? ''],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return GenericResult(
|
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
|
@override
|
||||||
Future<GenericResult<List<DesiredDnsRecord>>> validateDnsRecords(
|
Future<GenericResult<List<DesiredDnsRecord>>> validateDnsRecords(
|
||||||
final ServerDomain domain,
|
final ServerDomain domain,
|
||||||
final String ip4,
|
final String ip4,
|
||||||
final String dkimPublicKey,
|
final String dkimPublicKey,
|
||||||
|
final List<DnsRecord> pendingDnsRecords,
|
||||||
) async {
|
) async {
|
||||||
final result = await getDnsRecords(domain: domain);
|
final result = await _adapter.api().getDnsRecords(domain.domainName);
|
||||||
if (result.data.isEmpty || !result.success) {
|
if (result.data.isEmpty || !result.success) {
|
||||||
return GenericResult(
|
return GenericResult(
|
||||||
success: result.success,
|
success: result.success,
|
||||||
|
@ -225,45 +205,47 @@ class DesecDnsProvider extends DnsProvider {
|
||||||
final records = result.data;
|
final records = result.data;
|
||||||
final List<DesiredDnsRecord> foundRecords = [];
|
final List<DesiredDnsRecord> foundRecords = [];
|
||||||
try {
|
try {
|
||||||
final List<DesiredDnsRecord> desiredRecords =
|
for (final DnsRecord pendingDnsRecord in pendingDnsRecords) {
|
||||||
getDesiredDnsRecords(domain.domainName, ip4, dkimPublicKey);
|
final record = DesecDnsRecord.fromDnsRecord(pendingDnsRecord);
|
||||||
for (final DesiredDnsRecord record in desiredRecords) {
|
if (record.subname == 'selector._domainkey') {
|
||||||
if (record.description == 'record.dkim') {
|
final DesecDnsRecord foundRecord = records.firstWhere(
|
||||||
final DnsRecord foundRecord = records.firstWhere(
|
|
||||||
(final r) =>
|
(final r) =>
|
||||||
('${r.name}.${domain.domainName}' == record.name) &&
|
('${r.subname}.${domain.domainName}' == record.subname) &&
|
||||||
r.type == record.type,
|
r.type == record.type,
|
||||||
orElse: () => DnsRecord(
|
orElse: () => DesecDnsRecord(
|
||||||
name: record.name,
|
subname: record.subname,
|
||||||
type: record.type,
|
type: record.type,
|
||||||
content: '',
|
records: [],
|
||||||
ttl: 800,
|
ttl: record.ttl,
|
||||||
proxied: false,
|
),
|
||||||
|
);
|
||||||
|
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 {
|
} else {
|
||||||
foundRecords.add(record.copyWith(isSatisfied: false));
|
final foundMatch = records.any(
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (records.any(
|
|
||||||
(final r) =>
|
(final r) =>
|
||||||
('${r.name}.${domain.domainName}' == record.name ||
|
('${r.subname}.${domain.domainName}' == record.subname ||
|
||||||
record.name == '') &&
|
record.subname == '') &&
|
||||||
r.type == record.type &&
|
r.type == record.type &&
|
||||||
r.content == record.content,
|
r.records[0] == record.records[0],
|
||||||
)) {
|
);
|
||||||
foundRecords.add(record.copyWith(isSatisfied: true));
|
foundRecords.add(
|
||||||
} else {
|
DesiredDnsRecord(
|
||||||
foundRecords.add(record.copyWith(isSatisfied: false));
|
name: record.subname,
|
||||||
}
|
content: record.records[0],
|
||||||
|
isSatisfied: foundMatch,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -279,133 +261,4 @@ class DesecDnsProvider extends DnsProvider {
|
||||||
success: true,
|
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,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,14 +63,6 @@ abstract class DnsProvider {
|
||||||
final ServerDomain domain,
|
final ServerDomain domain,
|
||||||
final String ip4,
|
final String ip4,
|
||||||
final String dkimPublicKey,
|
final String dkimPublicKey,
|
||||||
);
|
final List<DnsRecord> pendingDnsRecords,
|
||||||
|
|
||||||
/// 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,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,3 +41,49 @@ void launchURL(final url) async {
|
||||||
print(e);
|
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,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue