Line data Source code
1 : import 'package:dart_ping/dart_ping.dart';
2 : import 'package:network_tools/network_tools.dart';
3 : import 'package:network_tools/src/injection.dart';
4 : import 'package:network_tools/src/network_tools_utils.dart';
5 : import 'package:network_tools/src/repository/repository.dart';
6 : import 'package:universal_io/io.dart';
7 :
8 : /// ActiveHost which implements comparable
9 : /// By default sort by hostId ascending
10 : class ActiveHost {
11 1 : ActiveHost({
12 : required this.internetAddress,
13 : this.openPorts = const [],
14 : String? macAddress,
15 : PingData? pingData,
16 : MdnsInfo? mdnsInfoVar,
17 : }) {
18 1 : _macAddress = macAddress;
19 2 : final String tempAddress = internetAddress.address;
20 :
21 1 : if (tempAddress.contains('.')) {
22 2 : hostId = tempAddress.substring(
23 2 : tempAddress.lastIndexOf('.') + 1,
24 1 : tempAddress.length,
25 : );
26 0 : } else if (tempAddress.contains(':')) {
27 0 : hostId = tempAddress.substring(
28 0 : tempAddress.lastIndexOf(':') + 1,
29 0 : tempAddress.length,
30 : );
31 : } else {
32 0 : hostId = '-1';
33 : }
34 1 : pingData ??= getPingData(tempAddress);
35 1 : _pingData = pingData;
36 :
37 2 : hostName = setHostInfo();
38 :
39 : // For some reason when internetAddress.host get called before the reverse
40 : // there is weired value
41 3 : weirdHostName = internetAddress.host;
42 :
43 : if (mdnsInfoVar != null) {
44 0 : mdnsInfo = Future.value(mdnsInfoVar);
45 : } else {
46 2 : mdnsInfo = setMdnsInfo();
47 : }
48 :
49 2 : deviceName = setDeviceName();
50 :
51 : // fetch entry from in memory arp table
52 2 : arpData = setARPData();
53 :
54 : // fetch vendor from in memory vendor table
55 2 : vendor = setVendor();
56 : }
57 1 : factory ActiveHost.buildWithAddress({
58 : required String address,
59 : String? macAddress,
60 : List<OpenPort> openPorts = const [],
61 : PingData? pingData,
62 : MdnsInfo? mdnsInfo,
63 : }) {
64 1 : final InternetAddress? internetAddressTemp = InternetAddress.tryParse(
65 : address,
66 : );
67 : if (internetAddressTemp == null) {
68 0 : throw 'Cant parse address $address to InternetAddress';
69 : }
70 1 : return ActiveHost(
71 : internetAddress: internetAddressTemp,
72 : macAddress: macAddress,
73 : openPorts: openPorts,
74 : pingData: pingData,
75 : mdnsInfoVar: mdnsInfo,
76 : );
77 : }
78 :
79 1 : factory ActiveHost.fromSendableActiveHost({
80 : required SendableActiveHost sendableActiveHost,
81 : String? macAddress,
82 : MdnsInfo? mdnsInfo,
83 : }) {
84 1 : final InternetAddress? internetAddressTemp = InternetAddress.tryParse(
85 1 : sendableActiveHost.address,
86 : );
87 : if (internetAddressTemp == null) {
88 0 : throw 'Cant parse address ${sendableActiveHost.address} to InternetAddress';
89 : }
90 1 : return ActiveHost(
91 : internetAddress: internetAddressTemp,
92 : macAddress: macAddress,
93 1 : openPorts: sendableActiveHost.openPorts,
94 1 : pingData: sendableActiveHost.pingData,
95 : mdnsInfoVar: mdnsInfo,
96 : );
97 : }
98 :
99 : static const generic = 'Generic Device';
100 :
101 : InternetAddress internetAddress;
102 :
103 : /// The device specific number in the ip address. In IPv4 numbers after the
104 : /// last dot, in IPv6 the numbers after the last colon
105 : late String hostId;
106 :
107 : /// Host name of the device, not to be confused with deviceName which does
108 : /// not follow any internet protocol property
109 : late Future<String?> hostName;
110 : late String weirdHostName;
111 : late final PingData _pingData;
112 :
113 : /// Mdns information of this device
114 : late Future<MdnsInfo?> mdnsInfo;
115 :
116 : /// Resolve ARP data for this host.
117 : /// only supported on Linux, Macos and Windows otherwise null
118 : late Future<ARPData?> arpData;
119 :
120 : /// Only works if arpData is not null and have valid mac address.
121 : late Future<Vendor?> vendor;
122 :
123 : String? _macAddress;
124 1 : Future<String?> getMacAddress() async =>
125 2 : _macAddress ?? (await arpData)?.macAddress;
126 :
127 : /// List of all the open port of this device
128 : List<OpenPort> openPorts;
129 :
130 : /// This device name does not following any guideline and is just some name
131 : /// that we can show for the device.
132 : /// Preferably hostName, if not than mDNS name, if not than will get the
133 : /// value of [generic].
134 : /// This value **can change after the object got created** since getting
135 : /// host name of device is running async function.
136 : late Future<String> deviceName;
137 0 : PingData get pingData => _pingData;
138 3 : Duration? get responseTime => _pingData.response?.time;
139 3 : String get address => internetAddress.address;
140 :
141 1 : static PingData getPingData(String host) {
142 : const int timeoutInSeconds = 1;
143 :
144 : PingData tempPingData = const PingData();
145 :
146 1 : Ping(
147 : host,
148 : count: 1,
149 : timeout: timeoutInSeconds,
150 1 : forceCodepage: Platform.isWindows,
151 3 : ).stream.listen((pingData) {
152 1 : final PingResponse? response = pingData.response;
153 : if (response != null) {
154 1 : final Duration? time = response.time;
155 : if (time != null) {
156 : tempPingData = pingData;
157 : }
158 : }
159 : });
160 : return tempPingData;
161 : }
162 :
163 : /// Try to find the host name of this device, if not exist host name will
164 : /// stay null
165 1 : Future<String?> setHostInfo() async {
166 : // For some reason when internetAddress.host get called before the reverse
167 : // there is weired value
168 3 : weirdHostName = internetAddress.host;
169 :
170 : // In the future if mdnsInfo is null it will execute a search
171 : // Currently the functionality is missing in dart multicast_dns package
172 : // https://github.com/flutter/flutter/issues/96755
173 :
174 : try {
175 3 : internetAddress = await internetAddress.reverse();
176 2 : return internetAddress.host;
177 : } catch (e) {
178 0 : if (e is SocketException &&
179 0 : e.osError != null &&
180 0 : (e.osError!.message == 'Name or service not known')) {
181 : // Some devices does not have host name and the reverse search will just
182 : // throw exception.
183 : // We don't need to print this crash as it is by design.
184 : } else {
185 0 : logger.severe('Exception here: $e');
186 : }
187 : }
188 : return null;
189 : }
190 :
191 1 : Future<void> resolveInfo() async {
192 1 : await arpData;
193 : // await vendor;
194 1 : await deviceName;
195 1 : await mdnsInfo;
196 1 : await hostName;
197 : }
198 :
199 1 : Future<ARPData?> setARPData() {
200 4 : return getIt<Repository<ARPData>>().entryFor(address);
201 : }
202 :
203 1 : Future<Vendor?> setVendor() async {
204 1 : final String? macAddress = await getMacAddress();
205 :
206 : return macAddress == null
207 : ? null
208 0 : : await getIt<Repository<Vendor>>().entryFor(macAddress);
209 : }
210 :
211 : /// Try to find the mdns name of this device, if not exist mdns name will
212 : /// be null
213 : /// TODO: search mdns name for each device
214 1 : Future<MdnsInfo?> setMdnsInfo() async {
215 : return null;
216 : }
217 :
218 : /// Set some kind of device name.
219 : /// Will try couple of names, if all are null will just return [generic]
220 1 : Future<String> setDeviceName() async {
221 1 : final String? hostNameTemp = await hostName;
222 :
223 : if (hostNameTemp != null) {
224 : return hostNameTemp;
225 : }
226 0 : final MdnsInfo? mdnsTemp = await mdnsInfo;
227 : if (mdnsTemp != null) {
228 0 : return mdnsTemp.getOnlyTheStartOfMdnsName();
229 : }
230 : return generic;
231 : }
232 :
233 1 : @override
234 2 : int get hashCode => address.hashCode;
235 :
236 1 : @override
237 4 : bool operator ==(Object o) => o is ActiveHost && address == o.address;
238 :
239 0 : int compareTo(ActiveHost other) {
240 0 : return hostId.compareTo(other.hostId);
241 : }
242 :
243 1 : @override
244 : String toString() {
245 6 : return 'Address: $address, HostId: $hostId, Time: ${responseTime?.inMilliseconds}ms, port: ${openPorts.join(",")}';
246 : }
247 :
248 0 : Future<String> toStringFull() async {
249 0 : return 'Address: $address, MAC: ${(await arpData)?.macAddress}, HostId: $hostId, Vendor: ${(await vendor)?.vendorName} Time: ${responseTime?.inMilliseconds}ms, DeviceName: ${await deviceName}, HostName: ${await hostName}, MdnsInfo: ${await mdnsInfo}';
250 : }
251 : }
|