Line data Source code
1 : import 'dart:async';
2 : import 'package:multicast_dns/multicast_dns.dart';
3 : import 'package:network_tools/network_tools.dart';
4 : import 'package:network_tools/src/mdns_scanner/get_srv_list_by_os/srv_list.dart';
5 : import 'package:network_tools/src/network_tools_utils.dart';
6 : import 'package:universal_io/io.dart';
7 :
8 : /// Resolves the bind target for [RawDatagramSocket.bind].
9 : ///
10 : /// On Windows, binding with an [InternetAddress] whose hostname is empty fails
11 : /// with errno 10049. Always use the numeric address string instead.
12 1 : dynamic _mdnsDatagramBindHost(dynamic host) {
13 1 : if (host is InternetAddress) {
14 0 : return host.address;
15 : }
16 : return host;
17 : }
18 :
19 1 : Future<RawDatagramSocket> _mdnsRawDatagramSocketFactory(
20 : dynamic host,
21 : int port, {
22 : bool? reuseAddress,
23 : bool? reusePort,
24 : int? ttl,
25 : }) {
26 1 : return RawDatagramSocket.bind(
27 1 : _mdnsDatagramBindHost(host),
28 : port,
29 : reuseAddress: reuseAddress ?? true,
30 : reusePort:
31 2 : !(Platform.isWindows || Platform.isAndroid) && (reusePort ?? true),
32 : ttl: ttl ?? 255,
33 : );
34 : }
35 :
36 1 : Future<Iterable<NetworkInterface>> _mdnsNetworkInterfacesFactory(
37 : InternetAddressType type,
38 : ) {
39 1 : return NetworkInterface.list(
40 1 : includeLinkLocal: !Platform.isWindows,
41 : type: type,
42 : );
43 : }
44 :
45 : class MdnsScannerServiceImpl extends MdnsScannerService {
46 : /// This method searching for all the mdns devices in the network.
47 : /// TODO: The implementation is **Lacking!** and will not find all the
48 : /// TODO: results that actual exist in the network!, only some of them.
49 : /// TODO: This is because missing functionality in dart
50 : /// TODO: https://github.com/flutter/flutter/issues/97210
51 : /// TODO: In some cases we resolve this missing functionality using
52 : /// TODO: specific os tools.
53 :
54 1 : @override
55 : Future<List<ActiveHost>> searchMdnsDevices({
56 : bool forceUseOfSavedSrvRecordList = false,
57 : }) async {
58 : List<String> srvRecordListToSearchIn;
59 :
60 : if (forceUseOfSavedSrvRecordList) {
61 1 : srvRecordListToSearchIn = tcpSrvRecordsList;
62 2 : srvRecordListToSearchIn.addAll(udpSrvRecordsList);
63 : } else {
64 1 : final List<String>? srvRecordsFromOs = await SrvList.getSrvRecordList();
65 :
66 1 : if (srvRecordsFromOs == null || srvRecordsFromOs.isEmpty) {
67 1 : srvRecordListToSearchIn = tcpSrvRecordsList;
68 2 : srvRecordListToSearchIn.addAll(udpSrvRecordsList);
69 : } else {
70 : srvRecordListToSearchIn = srvRecordsFromOs;
71 : }
72 : }
73 :
74 1 : final List<Future<List<ActiveHost>>> activeHostListsFuture = [];
75 2 : for (final String srvRecord in srvRecordListToSearchIn) {
76 2 : activeHostListsFuture.add(findingMdnsWithAddress(srvRecord));
77 : }
78 :
79 1 : final List<ActiveHost> activeHostList = [];
80 :
81 : for (final Future<List<ActiveHost>> activeHostListFuture
82 2 : in activeHostListsFuture) {
83 1 : activeHostList.addAll(await activeHostListFuture);
84 : }
85 :
86 : return activeHostList;
87 : }
88 :
89 1 : @override
90 : Future<List<ActiveHost>> findingMdnsWithAddress(String serviceType) async {
91 1 : final MDnsClient client = MDnsClient(
92 : rawDatagramSocketFactory: _mdnsRawDatagramSocketFactory,
93 : );
94 :
95 1 : final List<ActiveHost> listOfActiveHost = [];
96 1 : final Completer<void> completer = Completer<void>();
97 :
98 1 : runZonedGuarded(
99 1 : () async {
100 : try {
101 1 : await client.start(
102 1 : listenAddress: InternetAddress.anyIPv4,
103 : interfacesFactory: _mdnsNetworkInterfacesFactory,
104 : );
105 :
106 1 : await for (final PtrResourceRecord ptr
107 1 : in client.lookup<PtrResourceRecord>(
108 1 : ResourceRecordQuery.serverPointer(serviceType),
109 0 : )) {
110 0 : await for (final SrvResourceRecord srv
111 0 : in client.lookup<SrvResourceRecord>(
112 0 : ResourceRecordQuery.service(ptr.domainName),
113 0 : )) {
114 0 : await for (final TxtResourceRecord txtRecords
115 0 : in client.lookup<TxtResourceRecord>(
116 0 : ResourceRecordQuery.text(ptr.domainName),
117 0 : )) {
118 0 : listOfActiveHost.addAll(
119 0 : await findAllActiveHostForSrv(
120 0 : addressType: InternetAddress.anyIPv4,
121 : client: client,
122 : ptr: ptr,
123 : srv: srv,
124 : txt: txtRecords,
125 : ),
126 : );
127 0 : listOfActiveHost.addAll(
128 0 : await findAllActiveHostForSrv(
129 0 : addressType: InternetAddress.anyIPv6,
130 : client: client,
131 : ptr: ptr,
132 : srv: srv,
133 : txt: txtRecords,
134 : ),
135 : );
136 : }
137 : }
138 : }
139 : } catch (e) {
140 0 : logger.severe(
141 0 : 'Error finding mdns devices for serviceType $serviceType: $e',
142 : );
143 : } finally {
144 1 : client.stop();
145 2 : if (!completer.isCompleted) completer.complete();
146 : }
147 : },
148 0 : (Object error, StackTrace stack) {
149 0 : logger.severe(
150 0 : 'Unhandled async error in findingMdnsWithAddress for $serviceType: $error',
151 : );
152 0 : client.stop();
153 0 : if (!completer.isCompleted) completer.complete();
154 : },
155 : );
156 :
157 1 : await completer.future;
158 : return listOfActiveHost;
159 : }
160 :
161 0 : @override
162 : Future<List<ActiveHost>> findAllActiveHostForSrv({
163 : required InternetAddress addressType,
164 : required MDnsClient client,
165 : required PtrResourceRecord ptr,
166 : required SrvResourceRecord srv,
167 : required TxtResourceRecord txt,
168 : }) async {
169 0 : final List<ActiveHost> listOfActiveHost = [];
170 : try {
171 : Stream<IPAddressResourceRecord> iPAddressResourceRecordStream;
172 :
173 0 : if (addressType == InternetAddress.anyIPv4) {
174 0 : iPAddressResourceRecordStream = client.lookup<IPAddressResourceRecord>(
175 0 : ResourceRecordQuery.addressIPv4(srv.target),
176 : );
177 : } else {
178 0 : iPAddressResourceRecordStream = client.lookup<IPAddressResourceRecord>(
179 0 : ResourceRecordQuery.addressIPv6(srv.target),
180 : );
181 : }
182 0 : await for (final IPAddressResourceRecord ip
183 0 : in iPAddressResourceRecordStream) {
184 0 : final ActiveHost activeHost = convertSrvToHostName(
185 0 : internetAddress: InternetAddress.fromRawAddress(
186 0 : ip.address.rawAddress,
187 : ),
188 : ptr: ptr,
189 : srv: srv,
190 : txt: txt,
191 : );
192 :
193 0 : listOfActiveHost.add(activeHost);
194 : }
195 : } catch (e) {
196 0 : logger.severe(
197 0 : 'Error finding ip of mdns record ${ptr.name} srv target ${srv.target}, will add it with ip 0.0.0.0\n$e',
198 : );
199 0 : final ActiveHost activeHost = convertSrvToHostName(
200 0 : internetAddress: InternetAddress('0.0.0.0'),
201 : srv: srv,
202 : ptr: ptr,
203 : txt: txt,
204 : );
205 0 : listOfActiveHost.add(activeHost);
206 : }
207 : return listOfActiveHost;
208 : }
209 :
210 0 : @override
211 : ActiveHost convertSrvToHostName({
212 : required InternetAddress internetAddress,
213 : required PtrResourceRecord ptr,
214 : required SrvResourceRecord srv,
215 : required TxtResourceRecord txt,
216 : }) {
217 0 : final MdnsInfo mdnsInfo = MdnsInfo(
218 : srvResourceRecord: srv,
219 : ptrResourceRecord: ptr,
220 : txtResourceRecord: txt,
221 : );
222 0 : return ActiveHost(internetAddress: internetAddress, mdnsInfoVar: mdnsInfo);
223 : }
224 : }
|