From c6214313b928cb0e2b2b249ff639eba311e83b00 Mon Sep 17 00:00:00 2001 From: Jan Romann Date: Fri, 27 Oct 2023 23:27:13 +0200 Subject: [PATCH 1/3] feat: implement unicast DNS-SD --- .../core/implementation/thing_discovery.dart | 90 +++++++++++++++++-- pubspec.yaml | 1 + 2 files changed, 83 insertions(+), 8 deletions(-) diff --git a/lib/src/core/implementation/thing_discovery.dart b/lib/src/core/implementation/thing_discovery.dart index 662ef941..ebe1e206 100644 --- a/lib/src/core/implementation/thing_discovery.dart +++ b/lib/src/core/implementation/thing_discovery.dart @@ -6,6 +6,7 @@ import "dart:async"; +import "package:basic_utils/basic_utils.dart"; import "package:coap/coap.dart"; import "package:collection/collection.dart"; import "package:multicast_dns/multicast_dns.dart"; @@ -222,7 +223,7 @@ class ThingDiscovery extends Stream if (dnsName.endsWith("local")) { yield* _discoverUsingMdnssd(dnsName); } else { - throw UnimplementedError("Only mDNS-SD is currently supported!"); + yield* _discoverUsingDnsSd(dnsName); } } @@ -242,6 +243,16 @@ class ThingDiscovery extends Stream ); } + Map _parseTxtRecords(String txtRecords) { + final recordsList = txtRecords + .split("\n") + .map((property) => property.split("=")) + .where((list) => list.length > 1) + .map((list) => MapEntry(list[0], list[1])); + + return Map.fromEntries(recordsList); + } + Future?> _lookupTxtRecords( MDnsClient client, String domainName, @@ -249,17 +260,80 @@ class ThingDiscovery extends Stream final txtRecords = await client .lookup(ResourceRecordQuery.text(domainName)) .toList(); - final recordsList = txtRecords.firstOrNull?.text - .split("\n") - .map((property) => property.split("=")) - .where((list) => list.length > 1) - .map((list) => MapEntry(list[0], list[1])); - if (recordsList == null) { + final firstTxtRecord = txtRecords.firstOrNull?.text; + + if (firstTxtRecord == null) { return null; } - return Map.fromEntries(recordsList); + return _parseTxtRecords(firstTxtRecord); + } + + Stream _discoverUsingDnsSd(String name) async* { + // TODO: Refactor + final ptrRecords = await DnsUtils.lookupRecord(name, RRecordType.PTR); + final defaultScheme = _isUdpDiscovery(name) ? "coap" : "http"; + final discoveredUris = {}; + const defaultType = "Thing"; + + for (final ptrRecord in ptrRecords ?? []) { + final srvRecords = + await DnsUtils.lookupRecord(ptrRecord.name, RRecordType.SRV); + + for (final srvRecord in srvRecords ?? []) { + final srvRecordEntries = srvRecord.data.split(" "); + + final validSrvRecord = srvRecordEntries.length == 7; + + if (!validSrvRecord) { + continue; + } + + final target = srvRecordEntries.last; + final port = + int.tryParse(srvRecordEntries[srvRecordEntries.length - 2]); + + if (port == null) { + continue; + } + + final txtRecords = + await DnsUtils.lookupRecord(srvRecord.name, RRecordType.TXT) ?? []; + + final txtRecord = txtRecords.firstOrNull; + + if (txtRecord == null) { + continue; + } + + final parsedTxtRecord = _parseTxtRecords(txtRecord.data); + + final uri = Uri( + host: target, + port: port, + path: parsedTxtRecord["td"], + scheme: parsedTxtRecord["scheme"] ?? defaultScheme, + ); + + final duplicate = discoveredUris.add(uri); + + if (duplicate) { + continue; + } + + final type = parsedTxtRecord["type"] ?? defaultType; + + print(parsedTxtRecord); + switch (type) { + case "Thing": + yield* _discoverDirectly(uri); + case "Directory": + // TODO(JKRhb): Implement directory discovery. + break; + } + } + } } Stream _discoverUsingMdnssd(String name) async* { diff --git a/pubspec.yaml b/pubspec.yaml index 63f47116..e3612325 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,6 +15,7 @@ dev_dependencies: test: ^1.24.3 dependencies: + basic_utils: ^5.6.1 cbor: ^6.1.0 coap: ^9.0.0 collection: ^1.17.2 From d45a91d5d84b2b837c7818fdd1a88093698334b0 Mon Sep 17 00:00:00 2001 From: Jan Romann Date: Sat, 28 Oct 2023 01:59:46 +0200 Subject: [PATCH 2/3] fixup! feat: implement unicast DNS-SD --- .../core/implementation/thing_discovery.dart | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/src/core/implementation/thing_discovery.dart b/lib/src/core/implementation/thing_discovery.dart index ebe1e206..1d5be51d 100644 --- a/lib/src/core/implementation/thing_discovery.dart +++ b/lib/src/core/implementation/thing_discovery.dart @@ -278,13 +278,17 @@ class ThingDiscovery extends Stream const defaultType = "Thing"; for (final ptrRecord in ptrRecords ?? []) { - final srvRecords = - await DnsUtils.lookupRecord(ptrRecord.name, RRecordType.SRV); + final srvRecords = await DnsUtils.lookupRecord( + ptrRecord.name, + RRecordType.SRV, + provider: DnsApiProvider.CLOUDFLARE, + ); for (final srvRecord in srvRecords ?? []) { + final serviceName = srvRecord.name; final srvRecordEntries = srvRecord.data.split(" "); - final validSrvRecord = srvRecordEntries.length == 7; + final validSrvRecord = srvRecordEntries.length == 4; if (!validSrvRecord) { continue; @@ -298,8 +302,12 @@ class ThingDiscovery extends Stream continue; } - final txtRecords = - await DnsUtils.lookupRecord(srvRecord.name, RRecordType.TXT) ?? []; + final txtRecords = await DnsUtils.lookupRecord( + serviceName, + RRecordType.TXT, + provider: DnsApiProvider.CLOUDFLARE, + ) ?? + []; final txtRecord = txtRecords.firstOrNull; @@ -307,6 +315,7 @@ class ThingDiscovery extends Stream continue; } + // FIXME: Add parsing of multiple TXT records final parsedTxtRecord = _parseTxtRecords(txtRecord.data); final uri = Uri( @@ -316,7 +325,7 @@ class ThingDiscovery extends Stream scheme: parsedTxtRecord["scheme"] ?? defaultScheme, ); - final duplicate = discoveredUris.add(uri); + final duplicate = !discoveredUris.add(uri); if (duplicate) { continue; @@ -324,7 +333,6 @@ class ThingDiscovery extends Stream final type = parsedTxtRecord["type"] ?? defaultType; - print(parsedTxtRecord); switch (type) { case "Thing": yield* _discoverDirectly(uri); From d2d43ab9ba856fbec823259ae04482fcb80e604c Mon Sep 17 00:00:00 2001 From: Jan Romann Date: Mon, 22 Jan 2024 03:16:22 +0100 Subject: [PATCH 3/3] fixup! feat: implement unicast DNS-SD --- .../core/implementation/thing_discovery.dart | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/lib/src/core/implementation/thing_discovery.dart b/lib/src/core/implementation/thing_discovery.dart index 1d5be51d..47e0052d 100644 --- a/lib/src/core/implementation/thing_discovery.dart +++ b/lib/src/core/implementation/thing_discovery.dart @@ -243,7 +243,7 @@ class ThingDiscovery extends Stream ); } - Map _parseTxtRecords(String txtRecords) { + Map _parseMdnsTxtRecords(String txtRecords) { final recordsList = txtRecords .split("\n") .map((property) => property.split("=")) @@ -253,6 +253,29 @@ class ThingDiscovery extends Stream return Map.fromEntries(recordsList); } + static String _trimTxtRecord(String txtRecord) { + final startIndex = txtRecord.startsWith('"') ? 1 : 0; + + final length = txtRecord.length; + final endIndex = txtRecord.endsWith('"') ? length - 1 : length; + + return txtRecord.substring(startIndex, endIndex); + } + + Map _parseTxtRecords(List? txtRecords) { + final entries = txtRecords + ?.map((txtRecord) => txtRecord.data) + .map(_trimTxtRecord) + .map((e) => e.split("=")) + .where((element) => element.length == 2) + .map((txtRecord) { + return MapEntry(txtRecord[0], txtRecord[1]); + }) ?? + []; + + return Map.fromEntries(entries); + } + Future?> _lookupTxtRecords( MDnsClient client, String domainName, @@ -267,7 +290,7 @@ class ThingDiscovery extends Stream return null; } - return _parseTxtRecords(firstTxtRecord); + return _parseMdnsTxtRecords(firstTxtRecord); } Stream _discoverUsingDnsSd(String name) async* { @@ -309,20 +332,13 @@ class ThingDiscovery extends Stream ) ?? []; - final txtRecord = txtRecords.firstOrNull; - - if (txtRecord == null) { - continue; - } - - // FIXME: Add parsing of multiple TXT records - final parsedTxtRecord = _parseTxtRecords(txtRecord.data); + final parsedTxtRecords = _parseTxtRecords(txtRecords); final uri = Uri( host: target, port: port, - path: parsedTxtRecord["td"], - scheme: parsedTxtRecord["scheme"] ?? defaultScheme, + path: parsedTxtRecords["td"], + scheme: parsedTxtRecords["scheme"] ?? defaultScheme, ); final duplicate = !discoveredUris.add(uri); @@ -331,7 +347,7 @@ class ThingDiscovery extends Stream continue; } - final type = parsedTxtRecord["type"] ?? defaultType; + final type = parsedTxtRecords["type"] ?? defaultType; switch (type) { case "Thing":