Skip to content

Commit

Permalink
Merge pull request #139 from osociety/arp-data
Browse files Browse the repository at this point in the history
Arp data for Linux and Macos
  • Loading branch information
git-elliot authored Sep 5, 2023
2 parents a6e2ba0 + 0d2d7f4 commit 2094e8a
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 12 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions lib/src/host_scanner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
28 changes: 21 additions & 7 deletions lib/src/models/active_host.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -46,6 +48,8 @@ class ActiveHost extends Comparable<ActiveHost> {
}

deviceName = setDeviceName();
// fetch entry from in memory arp table
arpData = setARPData();
}
factory ActiveHost.buildWithAddress({
required String address,
Expand Down Expand Up @@ -84,13 +88,8 @@ class ActiveHost extends Comparable<ActiveHost> {
);
}

Future<void> 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
Expand All @@ -106,6 +105,10 @@ class ActiveHost extends Comparable<ActiveHost> {
/// Mdns information of this device
late Future<MdnsInfo?> mdnsInfo;

/// Resolve ARP data for this host.
/// only supported on Linux, Macos and Windows otherwise null
late Future<ARPData?> arpData;

/// List of all the open port of this device
List<OpenPort> openPorts;

Expand Down Expand Up @@ -165,6 +168,17 @@ class ActiveHost extends Comparable<ActiveHost> {
return null;
}

Future<void> resolveInfo() async {
await deviceName;
await mdnsInfo;
await hostName;
await arpData;
}

Future<ARPData?> 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
Expand Down Expand Up @@ -204,6 +218,6 @@ class ActiveHost extends Comparable<ActiveHost> {
}

Future<String> 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}';
}
}
29 changes: 29 additions & 0 deletions lib/src/models/arp_data.dart
Original file line number Diff line number Diff line change
@@ -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]';
}
}
53 changes: 53 additions & 0 deletions lib/src/models/arp_table.dart
Original file line number Diff line number Diff line change
@@ -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 = <String, ARPData>{};
static bool resolved = false;

static Future<ARPData?> 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'(?<host>[\w.?]*)\s\((?<ip>.*)\)\sat\s(?<mac>.*)\son\s(?<intf>\w+)\sifscope\s*(\w*)\s*\[(?<typ>.*)\]',
);
} else if (Platform.isLinux) {
pattern = RegExp(
r'(?<host>[\w.?]*)\s\((?<ip>.*)\)\sat\s(?<mac>.*)\s\[(?<typ>.*)\]\son\s(?<intf>\w+)',
);
} else {
pattern = RegExp(r'(?<ip>.*)\s(?<mac>.*)\s(?<typ>.*)');
}

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];
}
}
7 changes: 3 additions & 4 deletions test/network_tools_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'dart:async';

import 'package:logging/logging.dart';
import 'package:network_tools/network_tools.dart';
import 'package:test/test.dart';
Expand All @@ -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;
Expand Down

0 comments on commit 2094e8a

Please sign in to comment.