From 0825b7e654de008d0c63418233319ac09788b5fb Mon Sep 17 00:00:00 2001 From: git-elliot Date: Tue, 5 Sep 2023 20:38:33 +0530 Subject: [PATCH 1/9] ARP Data for Linux and Macos --- lib/src/host_scanner.dart | 1 + lib/src/models/active_host.dart | 28 +++++++++++++++----- lib/src/models/arp_data.dart | 26 +++++++++++++++++++ lib/src/models/arp_table.dart | 46 +++++++++++++++++++++++++++++++++ test/network_tools_test.dart | 14 +++++----- 5 files changed, 101 insertions(+), 14 deletions(-) create mode 100644 lib/src/models/arp_data.dart create mode 100644 lib/src/models/arp_table.dart 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..7dc2c6d 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..db3687e --- /dev/null +++ b/lib/src/models/arp_data.dart @@ -0,0 +1,26 @@ +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() { + //TODO: add platform specific format + if (Platform.isMacOS) { + return '$host ($iPAddress) at $macAddress on $interfaceName ifscope [$interfaceType]'; + } + return '$host ($iPAddress) at $macAddress on $interfaceName ifscope [$interfaceType]'; + } +} diff --git a/lib/src/models/arp_table.dart b/lib/src/models/arp_table.dart new file mode 100644 index 0000000..039049d --- /dev/null +++ b/lib/src/models/arp_table.dart @@ -0,0 +1,46 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:network_tools/src/models/arp_data.dart'; + +class ARPTable { + 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 { + //todo: implement windows regex + return null; + } + for (final entry in entries) { + final match = pattern.firstMatch(entry); + if (match != null) { + final arpData = ARPData( + host: match.namedGroup("host"), + iPAddress: match.namedGroup("ip"), + macAddress: match.namedGroup("mac"), + interfaceName: match.namedGroup("intf"), + interfaceType: match.namedGroup("typ"), + ); + final key = arpData.iPAddress; + if (key != null) { + arpTable[key] = arpData; + } + } + } + resolved = true; + return arpTable[address]; + } +} diff --git a/test/network_tools_test.dart b/test/network_tools_test.dart index e816bba..c378fd0 100644 --- a/test/network_tools_test.dart +++ b/test/network_tools_test.dart @@ -1,5 +1,5 @@ import 'dart:async'; - +import 'package:intl/intl.dart'; import 'package:logging/logging.dart'; import 'package:network_tools/network_tools.dart'; import 'package:test/test.dart'; @@ -8,12 +8,12 @@ import 'package:universal_io/io.dart'; void main() { final log = Logger("host_scan_test"); - // Logger.root.level = Level.FINE; - // Logger.root.onRecord.listen((record) { - // print( - // '${DateFormat.Hms().format(record.time)}: ${record.level.name}: ${record.loggerName}: ${record.message}', - // ); - // }); + Logger.root.level = Level.FINE; + Logger.root.onRecord.listen((record) { + print( + '${DateFormat.Hms().format(record.time)}: ${record.level.name}: ${record.loggerName}: ${record.message}', + ); + }); int port = 0; int firstHostId = 0; From 0e0b7d69c159115453ea417fd0e327713de551a2 Mon Sep 17 00:00:00 2001 From: git-elliot Date: Tue, 5 Sep 2023 20:44:54 +0530 Subject: [PATCH 2/9] added logs for arp table --- lib/src/models/arp_table.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/src/models/arp_table.dart b/lib/src/models/arp_table.dart index 039049d..079e377 100644 --- a/lib/src/models/arp_table.dart +++ b/lib/src/models/arp_table.dart @@ -1,9 +1,11 @@ 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; @@ -25,6 +27,7 @@ class ARPTable { return null; } for (final entry in entries) { + arpLogger.fine("Found entry: $entry"); final match = pattern.firstMatch(entry); if (match != null) { final arpData = ARPData( @@ -36,6 +39,7 @@ class ARPTable { ); final key = arpData.iPAddress; if (key != null) { + arpLogger.fine("Adding entry to table -> $entry"); arpTable[key] = arpData; } } From b71a16a50645494f28eb3c35754093ca6d50afef Mon Sep 17 00:00:00 2001 From: git-elliot Date: Tue, 5 Sep 2023 20:47:04 +0530 Subject: [PATCH 3/9] ignore print --- test/network_tools_test.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/test/network_tools_test.dart b/test/network_tools_test.dart index c378fd0..19e530f 100644 --- a/test/network_tools_test.dart +++ b/test/network_tools_test.dart @@ -10,6 +10,7 @@ void main() { Logger.root.level = Level.FINE; Logger.root.onRecord.listen((record) { + // ignore: avoid_print print( '${DateFormat.Hms().format(record.time)}: ${record.level.name}: ${record.loggerName}: ${record.message}', ); From ab4d2c517c9bdc89e5cf0588f1ff7131d580a7ee Mon Sep 17 00:00:00 2001 From: git-elliot Date: Tue, 5 Sep 2023 21:01:16 +0530 Subject: [PATCH 4/9] removed null check op --- lib/src/models/active_host.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/models/active_host.dart b/lib/src/models/active_host.dart index 7dc2c6d..d50e881 100644 --- a/lib/src/models/active_host.dart +++ b/lib/src/models/active_host.dart @@ -218,6 +218,6 @@ class ActiveHost extends Comparable { } Future toStringFull() async { - return 'Address: $address, MAC: ${(await arpData)!.macAddress}, 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}'; } } From e7266c7cff4442a8f2a12411533ff2106f54f87f Mon Sep 17 00:00:00 2001 From: git-elliot Date: Tue, 5 Sep 2023 21:03:04 +0530 Subject: [PATCH 5/9] Add log --- lib/src/models/arp_table.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/models/arp_table.dart b/lib/src/models/arp_table.dart index 079e377..42641d8 100644 --- a/lib/src/models/arp_table.dart +++ b/lib/src/models/arp_table.dart @@ -13,6 +13,7 @@ class ARPTable { if (resolved) return arpTable[address]; final result = await Process.run('arp', ['-a']); final entries = const LineSplitter().convert(result.stdout.toString()); + arpLogger.fine(entries); RegExp? pattern; if (Platform.isMacOS) { pattern = RegExp( From ef14e644799392b09fa6418ca77fe43182bbfadc Mon Sep 17 00:00:00 2001 From: git-elliot Date: Tue, 5 Sep 2023 21:30:17 +0530 Subject: [PATCH 6/9] Added arp regex for windows --- lib/src/models/arp_data.dart | 7 +++++-- lib/src/models/arp_table.dart | 16 +++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/src/models/arp_data.dart b/lib/src/models/arp_data.dart index db3687e..85f300d 100644 --- a/lib/src/models/arp_data.dart +++ b/lib/src/models/arp_data.dart @@ -17,10 +17,13 @@ class ARPData { @override String toString() { - //TODO: add platform specific format 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 ifscope [$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 index 42641d8..6f4ae1a 100644 --- a/lib/src/models/arp_table.dart +++ b/lib/src/models/arp_table.dart @@ -13,7 +13,6 @@ class ARPTable { if (resolved) return arpTable[address]; final result = await Process.run('arp', ['-a']); final entries = const LineSplitter().convert(result.stdout.toString()); - arpLogger.fine(entries); RegExp? pattern; if (Platform.isMacOS) { pattern = RegExp( @@ -24,23 +23,26 @@ class ARPTable { r'(?[\w.?]*)\s\((?.*)\)\sat\s(?.*)\s\[(?.*)\]\son\s(?\w+)', ); } else { - //todo: implement windows regex - return null; + pattern = RegExp(r'(?.*)\s(?.*)\s(?.*)'); } + for (final entry in entries) { - arpLogger.fine("Found entry: $entry"); final match = pattern.firstMatch(entry); if (match != null) { final arpData = ARPData( - host: match.namedGroup("host"), + host: match.groupNames.contains('host') + ? match.namedGroup("host") + : null, iPAddress: match.namedGroup("ip"), macAddress: match.namedGroup("mac"), - interfaceName: match.namedGroup("intf"), + 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 -> $entry"); + arpLogger.fine("Adding entry to table -> $arpData"); arpTable[key] = arpData; } } From 5fcce8f4ead48b9b663698976a6ef9f61a576bb9 Mon Sep 17 00:00:00 2001 From: git-elliot Date: Tue, 5 Sep 2023 21:32:58 +0530 Subject: [PATCH 7/9] updated readme --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 From 964bd2f64a3f9cd21da9f81c0e0ebd9d39294172 Mon Sep 17 00:00:00 2001 From: git-elliot Date: Tue, 5 Sep 2023 21:33:37 +0530 Subject: [PATCH 8/9] removed logs --- test/network_tools_test.dart | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/test/network_tools_test.dart b/test/network_tools_test.dart index 19e530f..88c55d3 100644 --- a/test/network_tools_test.dart +++ b/test/network_tools_test.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'package:intl/intl.dart'; import 'package:logging/logging.dart'; import 'package:network_tools/network_tools.dart'; import 'package:test/test.dart'; @@ -8,13 +7,13 @@ import 'package:universal_io/io.dart'; void main() { final log = Logger("host_scan_test"); - Logger.root.level = Level.FINE; - Logger.root.onRecord.listen((record) { - // ignore: avoid_print - print( - '${DateFormat.Hms().format(record.time)}: ${record.level.name}: ${record.loggerName}: ${record.message}', - ); - }); + // Logger.root.level = Level.FINE; + // Logger.root.onRecord.listen((record) { + // // ignore: avoid_print + // // print( + // // '${DateFormat.Hms().format(record.time)}: ${record.level.name}: ${record.loggerName}: ${record.message}', + // // ); + // }); int port = 0; int firstHostId = 0; From 0d2d7f42e26da8dce52f98fdfcb9ff2b78fe70c4 Mon Sep 17 00:00:00 2001 From: git-elliot Date: Tue, 5 Sep 2023 21:37:10 +0530 Subject: [PATCH 9/9] updated version --- CHANGELOG.md | 4 ++++ test/network_tools_test.dart | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) 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/test/network_tools_test.dart b/test/network_tools_test.dart index 88c55d3..3045751 100644 --- a/test/network_tools_test.dart +++ b/test/network_tools_test.dart @@ -9,7 +9,6 @@ void main() { // Logger.root.level = Level.FINE; // Logger.root.onRecord.listen((record) { - // // ignore: avoid_print // // print( // // '${DateFormat.Hms().format(record.time)}: ${record.level.name}: ${record.loggerName}: ${record.message}', // // );