diff --git a/CHANGELOG.md b/CHANGELOG.md index ffb6015..acc7652 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 3.2.5 +1. Added mac address support on Desktop (Linux, macOS, and Windows) + +## 3.2.5 + 1. Bug fix for iOS listing all results in HostScanner ## 3.2.4 diff --git a/README.md b/README.md index 5a9dd1c..9e6c328 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,12 @@ Network Tools Supported 1. Host Scanner + 1. Search all devices on subnet + 2. Get mac address of devices on Linux, macOS and Windows. + 3. Search devices for a specific port open. 2. Port Scanner - 1. Single + 1. Single Port scan 2. Range 3. Custom diff --git a/lib/src/host_scanner.dart b/lib/src/host_scanner.dart index abb3963..85743a1 100644 --- a/lib/src/host_scanner.dart +++ b/lib/src/host_scanner.dart @@ -172,6 +172,7 @@ class HostScanner { final activeHostFound = ActiveHost.fromSendableActiveHost(sendableActiveHost: message); await activeHostFound.resolveInfo(); + log.fine("Found host: ${await activeHostFound.toStringFull()}"); yield activeHostFound; } else if (message is String && message == 'Done') { isolate.kill(); diff --git a/lib/src/models/active_host.dart b/lib/src/models/active_host.dart index 041153e..d50e881 100644 --- a/lib/src/models/active_host.dart +++ b/lib/src/models/active_host.dart @@ -1,4 +1,6 @@ import 'package:dart_ping/dart_ping.dart'; +import 'package:network_tools/src/models/arp_data.dart'; +import 'package:network_tools/src/models/arp_table.dart'; import 'package:network_tools/src/models/mdns_info.dart'; import 'package:network_tools/src/models/open_port.dart'; import 'package:network_tools/src/models/sendable_active_host.dart'; @@ -46,6 +48,8 @@ class ActiveHost extends Comparable { } deviceName = setDeviceName(); + // fetch entry from in memory arp table + arpData = setARPData(); } factory ActiveHost.buildWithAddress({ required String address, @@ -84,13 +88,8 @@ class ActiveHost extends Comparable { ); } - Future resolveInfo() async { - await deviceName; - await mdnsInfo; - await hostName; - } - static const generic = 'Generic Device'; + InternetAddress internetAddress; /// The device specific number in the ip address. In IPv4 numbers after the @@ -106,6 +105,10 @@ class ActiveHost extends Comparable { /// Mdns information of this device late Future mdnsInfo; + /// Resolve ARP data for this host. + /// only supported on Linux, Macos and Windows otherwise null + late Future arpData; + /// List of all the open port of this device List openPorts; @@ -165,6 +168,17 @@ class ActiveHost extends Comparable { return null; } + Future resolveInfo() async { + await deviceName; + await mdnsInfo; + await hostName; + await arpData; + } + + Future setARPData() async { + return ARPTable.entryFor(address); + } + /// Try to find the mdns name of this device, if not exist mdns name will /// be null /// TODO: search mdns name for each device @@ -204,6 +218,6 @@ class ActiveHost extends Comparable { } Future toStringFull() async { - return 'Address: $address, HostId: $hostId Time: ${responseTime?.inMilliseconds}ms, DeviceName: ${await deviceName}, HostName: ${await hostName}, MdnsInfo: ${await mdnsInfo}'; + return 'Address: $address, MAC: ${(await arpData)?.macAddress}, HostId: $hostId Time: ${responseTime?.inMilliseconds}ms, DeviceName: ${await deviceName}, HostName: ${await hostName}, MdnsInfo: ${await mdnsInfo}'; } } diff --git a/lib/src/models/arp_data.dart b/lib/src/models/arp_data.dart new file mode 100644 index 0000000..85f300d --- /dev/null +++ b/lib/src/models/arp_data.dart @@ -0,0 +1,29 @@ +import 'dart:io'; + +class ARPData { + ARPData({ + required this.host, + required this.iPAddress, + required this.macAddress, + required this.interfaceName, + required this.interfaceType, + }); + + final String? host; + final String? iPAddress; + final String? macAddress; + final String? interfaceName; + final String? interfaceType; + + @override + String toString() { + if (Platform.isMacOS) { + return '$host ($iPAddress) at $macAddress on $interfaceName ifscope [$interfaceType]'; + } else if (Platform.isLinux) { + return '$host ($iPAddress) at $macAddress [$interfaceType] on $interfaceName'; + } else if (Platform.isWindows) { + return 'Internet Address: $iPAddress, Physical Address: $macAddress, Type: $interfaceType'; + } + return '$host ($iPAddress) at $macAddress on $interfaceName type [$interfaceType]'; + } +} diff --git a/lib/src/models/arp_table.dart b/lib/src/models/arp_table.dart new file mode 100644 index 0000000..6f4ae1a --- /dev/null +++ b/lib/src/models/arp_table.dart @@ -0,0 +1,53 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:logging/logging.dart'; +import 'package:network_tools/src/models/arp_data.dart'; + +class ARPTable { + static final arpLogger = Logger("arp-table-logger"); + static final arpTable = {}; + static bool resolved = false; + + static Future entryFor(String address) async { + if (resolved) return arpTable[address]; + final result = await Process.run('arp', ['-a']); + final entries = const LineSplitter().convert(result.stdout.toString()); + RegExp? pattern; + if (Platform.isMacOS) { + pattern = RegExp( + r'(?[\w.?]*)\s\((?.*)\)\sat\s(?.*)\son\s(?\w+)\sifscope\s*(\w*)\s*\[(?.*)\]', + ); + } else if (Platform.isLinux) { + pattern = RegExp( + r'(?[\w.?]*)\s\((?.*)\)\sat\s(?.*)\s\[(?.*)\]\son\s(?\w+)', + ); + } else { + pattern = RegExp(r'(?.*)\s(?.*)\s(?.*)'); + } + + for (final entry in entries) { + final match = pattern.firstMatch(entry); + if (match != null) { + final arpData = ARPData( + host: match.groupNames.contains('host') + ? match.namedGroup("host") + : null, + iPAddress: match.namedGroup("ip"), + macAddress: match.namedGroup("mac"), + interfaceName: match.groupNames.contains('intf') + ? match.namedGroup("intf") + : null, + interfaceType: match.namedGroup("typ"), + ); + final key = arpData.iPAddress; + if (key != null) { + arpLogger.fine("Adding entry to table -> $arpData"); + arpTable[key] = arpData; + } + } + } + resolved = true; + return arpTable[address]; + } +} diff --git a/test/network_tools_test.dart b/test/network_tools_test.dart index e816bba..3045751 100644 --- a/test/network_tools_test.dart +++ b/test/network_tools_test.dart @@ -1,5 +1,4 @@ import 'dart:async'; - import 'package:logging/logging.dart'; import 'package:network_tools/network_tools.dart'; import 'package:test/test.dart'; @@ -10,9 +9,9 @@ void main() { // Logger.root.level = Level.FINE; // Logger.root.onRecord.listen((record) { - // print( - // '${DateFormat.Hms().format(record.time)}: ${record.level.name}: ${record.loggerName}: ${record.message}', - // ); + // // print( + // // '${DateFormat.Hms().format(record.time)}: ${record.level.name}: ${record.loggerName}: ${record.message}', + // // ); // }); int port = 0;