LCOV - code coverage report
Current view: top level - lib/src/models - active_host.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 74.4 % 82 61
Test Date: 2026-07-04 10:50:15 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              :     PingResponse? 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              :     PingResponse? 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 PingResponse? _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 :   PingResponse? get pingData => _pingData;
     138            1 :   Duration? get responseTime {
     139            1 :     final event = _pingData;
     140            1 :     if (event is PingResponse) return event.time;
     141              :     return null;
     142              :   }
     143              : 
     144            3 :   String get address => internetAddress.address;
     145              : 
     146            1 :   static PingResponse? getPingData(String host) {
     147              :     const int timeoutInSeconds = 1;
     148              :     PingResponse? tempPingData;
     149              : 
     150            1 :     Ping(
     151              :       host,
     152              :       count: 1,
     153              :       timeout: timeoutInSeconds,
     154            1 :       forceCodepage: Platform.isWindows,
     155            3 :     ).stream.listen((Object? event) {
     156            1 :       final PingResponse? response = _extractPingResponse(event);
     157            1 :       if (response != null && response.time != null) {
     158              :         tempPingData = response;
     159              :       }
     160              :     });
     161              :     return tempPingData;
     162              :   }
     163              : 
     164            1 :   static PingResponse? _extractPingResponse(Object? event) {
     165            1 :     if (event is PingResponse) {
     166              :       return event;
     167              :     }
     168              :     if (event == null) {
     169              :       return null;
     170              :     }
     171              : 
     172              :     // dart_ping changed its stream payload type across versions.
     173              :     // Some versions emit PingSummary or other payloads instead of a response.
     174              :     try {
     175              :       final dynamic dynamicEvent = event;
     176              :       // ignore: avoid_dynamic_calls
     177            1 :       final Object? response = dynamicEvent.response;
     178            0 :       return response is PingResponse ? response : null;
     179              :     } catch (error) {
     180              :       return null;
     181              :     }
     182              :   }
     183              : 
     184              :   /// Try to find the host name of this device, if not exist host name will
     185              :   /// stay null
     186            1 :   Future<String?> setHostInfo() async {
     187              :     // For some reason when internetAddress.host get called before the reverse
     188              :     // there is weired value
     189            3 :     weirdHostName = internetAddress.host;
     190              : 
     191              :     // In the future if mdnsInfo is null it will execute a search
     192              :     // Currently the functionality is missing in dart multicast_dns package
     193              :     // https://github.com/flutter/flutter/issues/96755
     194              : 
     195              :     try {
     196            3 :       internetAddress = await internetAddress.reverse();
     197            2 :       return internetAddress.host;
     198              :     } catch (e) {
     199            0 :       if (e is SocketException &&
     200            0 :           e.osError != null &&
     201            0 :           (e.osError!.message == 'Name or service not known')) {
     202              :         // Some devices does not have host name and the reverse search will just
     203              :         // throw exception.
     204              :         // We don't need to print this crash as it is by design.
     205              :       } else {
     206            0 :         logger.severe('Exception here: $e');
     207              :       }
     208              :     }
     209              :     return null;
     210              :   }
     211              : 
     212            1 :   Future<void> resolveInfo() async {
     213            1 :     await arpData;
     214              :     // await vendor;
     215            1 :     await deviceName;
     216            1 :     await mdnsInfo;
     217            1 :     await hostName;
     218              :   }
     219              : 
     220            1 :   Future<ARPData?> setARPData() {
     221            4 :     return getIt<Repository<ARPData>>().entryFor(address);
     222              :   }
     223              : 
     224            1 :   Future<Vendor?> setVendor() async {
     225            1 :     final String? macAddress = await getMacAddress();
     226              : 
     227              :     return macAddress == null
     228              :         ? null
     229            0 :         : await getIt<Repository<Vendor>>().entryFor(macAddress);
     230              :   }
     231              : 
     232              :   /// Try to find the mdns name of this device, if not exist mdns name will
     233              :   /// be null
     234              :   /// TODO: search mdns name for each device
     235            1 :   Future<MdnsInfo?> setMdnsInfo() async {
     236              :     return null;
     237              :   }
     238              : 
     239              :   /// Set some kind of device name.
     240              :   /// Will try couple of names, if all are null will just return [generic]
     241            1 :   Future<String> setDeviceName() async {
     242            1 :     final String? hostNameTemp = await hostName;
     243              : 
     244              :     if (hostNameTemp != null) {
     245              :       return hostNameTemp;
     246              :     }
     247            0 :     final MdnsInfo? mdnsTemp = await mdnsInfo;
     248              :     if (mdnsTemp != null) {
     249            0 :       return mdnsTemp.getOnlyTheStartOfMdnsName();
     250              :     }
     251              :     return generic;
     252              :   }
     253              : 
     254            1 :   @override
     255            2 :   int get hashCode => address.hashCode;
     256              : 
     257            1 :   @override
     258            4 :   bool operator ==(Object o) => o is ActiveHost && address == o.address;
     259              : 
     260            0 :   int compareTo(ActiveHost other) {
     261            0 :     return hostId.compareTo(other.hostId);
     262              :   }
     263              : 
     264            1 :   @override
     265              :   String toString() {
     266            6 :     return 'Address: $address, HostId: $hostId, Time: ${responseTime?.inMilliseconds}ms, port: ${openPorts.join(",")}';
     267              :   }
     268              : 
     269            0 :   Future<String> toStringFull() async {
     270            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}';
     271              :   }
     272              : }
        

Generated by: LCOV version 2.0-1