LCOV - code coverage report
Current view: top level - lib/src/models - active_host.dart (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 73.7 % 76 56
Test Date: 2025-08-17 13:02:53 Functions: - 0 0

            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              : }
        

Generated by: LCOV version 2.0-1