Skip to content

About

Networking Tools library which can help you discover open ports, devices on subnet and many other things.

Topics

Resources

Stars

54 stars

Watchers

3 watching

Forks

Repository files navigation

Cross Platform Network Tools

pub package Unified CI/CD Pipeline Code Coverage codecov

Network Tools Supported

  1. Host Scanner

    1. Search all devices on subnet.
    2. Get hostname, MAC address and vendor of devices on Linux, macOS and Windows.
    3. ARP-first discovery on desktop: one numeric neighbor table read instead of a ping process per address, so a /24 scan takes about a second and still finds hosts that ignore ICMP.
    4. Search devices for a specific port open.
    5. Isolate based scanning for large subnets.
  2. Port Scanner

    1. Single Port scan
    2. Range
    3. Custom
    4. Bounded concurrency to scan fast without exhausting file descriptors.

Partly Work:

  1. Mdns Scanner

Network Tools Flutter package

Please check network_tools_flutter package for extensive support to features on different platforms.

Import package in your app

import 'package:network_tools/network_tools.dart';

Configure network tools in main function

For dart native

Future<void> main() async {
  await configureNetworkTools('build', enableDebugging: true);
  runApp(const MyApp());
}

For flutter apps

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // It's necessary to pass correct path to be able to use this library.
  final appDocDirectory = await getApplicationDocumentsDirectory();
  await configureNetworkTools(appDocDirectory.path, enableDebugging: true);
  runApp(const MyApp());
}

Pass buildVendorTable: false from worker isolate entry points, where the table is never read: vendor lookups happen in the isolate that receives the hosts, not the one that starts the scan. Scans started through getAllPingableDevicesAsync already do this. The flag skips the warm-up only — the data ships inside the package, so lookups still resolve in an isolate that never built it.

A MAC is matched against the longest IEEE block that covers it: MA-S and IAB (4.5 octets) first, then MA-M (3.5 octets), then MA-L (3 octets). Sub-blocks are therefore attributed to their own assignee instead of to the MA-L entry that only stands in for them.

The table ships as a packed binary index. vendor_index.data.dart holds the 49,533 assignments gzip-compressed into a Dart string literal, and the first lookup decodes it — about 10 ms and 1.4 MB in an AOT build, then shared by every caller in that isolate. Nothing is generated while you build, so no network access is needed to compile the package and a cold first build resolves vendors.

To refresh the data, replace lib/assets/mac-vendors-export.csv, run dart run tool/build_vendor_index.dart and commit both files. test/vendor_index_parity_test.dart rebuilds the index from the CSV and fails when the committed bytes no longer match it.

Usage

Host Scanner

 String address = '192.168.1.12';
  // or You can also get address using network_info_plus package
  // final String? address = await (NetworkInfo().getWifiIP());
  final String subnet = address.substring(0, address.lastIndexOf('.'));
  final stream = HostScannerService.instance.getAllPingableDevices(subnet, firstHostId: 1, lastHostId: 50,
      progressCallback: (progress) {
    print('Progress for host discovery : $progress');
  });

  stream.listen((host) {
    //Same host can be emitted multiple times
    //Use Set<ActiveHost> instead of List<ActiveHost>
    print('Found device: $host');
  }, onDone: () {
    print('Scan completed');
  }); // Don't forget to cancel the stream when not in use.

Host details (hostname, MAC address and vendor) are resolved lazily on first access, so discovery is not slowed down by additional lookups:

  await for (final ActiveHost host in HostScannerService.instance.getAllPingableDevices(subnet)) {
    final String deviceName = await host.deviceName;
    final String? macAddress = await host.getMacAddress();
    final Vendor? vendor = await host.vendor;
    print('${host.address} $deviceName $macAddress ${vendor?.vendorName}');
  }

For large subnets use getAllPingableDevicesAsync, which runs the whole scan in a separate isolate so the UI stays responsive:

  await for (final ActiveHost host
      in HostScannerService.instance.getAllPingableDevicesAsync('192.168.1')) {
    print('Found device: ${host.address}');
  }

How discovery works

On Linux, macOS and Windows a scan primes the neighbour cache by sending a UDP datagram to every address in the range, then reads the numeric ARP table once (arp -an on macOS, /proc/net/arp on Linux, arp -a on Windows). This replaces one ping process per address, so a /24 scan typically finishes in about a second and still reports hosts that ignore ICMP. Broadcast and multicast MAC rows are discarded. Mobile platforms and non-local subnets fall back to the ICMP sweep.

ARP itself carries no host names, so hostName and deviceName are resolved separately for the hosts that were actually found: a concurrent reverse DNS lookup with a short budget during the scan, and a fresh attempt when the name is first accessed. Networks without reverse DNS (most home routers) simply return no name; deviceName then falls back to mDNS, then the ARP table entry, then Generic Device. MAC vendor lookups still work on those networks.

On Android and iOS names depend on platform multicast and local-network permissions:

  • Android: hold a MulticastLock (CHANGE_WIFI_MULTICAST_STATE) for mDNS, and request ACCESS_FINE_LOCATION for Wi-Fi details.
  • iOS: enable the multicast networking entitlement and request local network permission (NSLocalNetworkUsageDescription).

Port Scanner

  //1. Range
  String target = '192.168.1.1';
  PortScannerService.instance.scanPortsForSingleDevice(target, startPort: 1, endPort: 1024,
      progressCallback: (progress) {
    print('Progress for port discovery : $progress');
  }).listen((ActiveHost event) {
    if (event.openPorts.isNotEmpty) {
      print('Found open ports : ${event.openPorts}');
    }
  }, onDone: () {
    print('Scan completed');
  });
  //2. Single
  final bool isOpen = (await PortScannerService.instance.isOpen(target, 80)) != null;
  //3. Custom
  PortScannerService.instance.customDiscover(target, portList: const [22, 80, 139]);

Scans open at most maxConcurrency sockets at once (128 by default), so large ranges complete quickly without hitting file-descriptor limits. Raise it for more throughput or lower it to be gentler on the network. Pass async: true to run the scan in an isolate.

Results are streamed as each port resolves, so you can show open ports before the scan ends. In the default ordered mode a later result waits until every earlier port has resolved; pass resultsInAddressAscendingOrder: false when you want the earliest possible delivery and do not need a stable order. maxConcurrency also controls how soon results for later ports can appear on high-latency or filtered networks:

  await for (final ActiveHost host in PortScannerService.instance.scanPortsForSingleDevice(
    target,
    startPort: 1,
    endPort: 65535,
    maxConcurrency: 512,
    async: true,
  )) {
    print('Open ports on ${host.address}: ${host.openPorts}');
  }

Mdns Scanner

  for (final ActiveHost activeHost in await MdnsScannerService.instance.searchMdnsDevices()) {
    final MdnsInfo? mdnsInfo = await activeHost.mdnsInfo;
    print('''
    Address: ${activeHost.address}
    Port: ${mdnsInfo!.mdnsPort}
    ServiceType: ${mdnsInfo.mdnsServiceType}
    MdnsName: ${mdnsInfo.getOnlyTheStartOfMdnsName()}
    ''');
  }

Run examples

  1. Run all examples : dart run example/main.dart
  2. Run host scan : dart run example/lib/scan/host_scan.dart
  3. Run port scan : dart run example/lib/scan/port_scan.dart
  4. Run mdns scan : dart run example/lib/scan/mdns_scan.dart

Sample App

Vernet is the open source cross platform network analysis and monitoring app built on top of this library. You can check out the code and implementation for more detailed use case of this package.

Support and Donate

  1. Support this project by becoming a stargazer of this project.

  2. Donate via Liberapay: liberapay.com/OpenSociety/donate

  3. Support Open Society on Ko-fi:

    ko-fi

Inspired from ping_discover_network

About

Networking Tools library which can help you discover open ports, devices on subnet and many other things.

Topics

Resources

Stars

54 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages