osociety / network_tools

Networking Tools library which can help you discover open ports, devices on subnet and many other things.
https://pub.dev/packages/network_tools
BSD 3-Clause "New" or "Revised" License
45 stars 13 forks source link

Getting ip of mdns #164

Closed guyluz11 closed 11 months ago

guyluz11 commented 11 months ago

Would like to have the option of getting IP of device from it's mdns name.

I have found several options but they are platform specific and not orenaized in a package

Linux - only if avahi-resolve-host-name/avahi-utils is installed

  Future<String?> getIpFromMdnsName(String mdnsName) async {
    try {
      final String fileContent =
          await Process.run('avahi-resolve-host-name', <String>[mdnsName])
              .then((ProcessResult result) {
        return result.stdout.toString();
      });
      return fileContent.substring(fileContent.indexOf('\t') + 1).trim();
    } catch (e) {
      logger.w("Can't get device IP from mdns $mdnsName\n$e");
    }
    return null;
  }

Flutter - using multicast_dns package

import 'package:multicast_dns/multicast_dns.dart';

main() async {
  String serviceType = '_http._tcp';
  final MDnsClient client = MDnsClient();
  await client.start();

  await for (final PtrResourceRecord ptr in client.lookup<PtrResourceRecord>(
      ResourceRecordQuery.serverPointer(serviceType))) {
    print('PTR: ${ptr.toString()}');

    await for (final SrvResourceRecord srv in client.lookup<SrvResourceRecord>(
        ResourceRecordQuery.service(ptr.domainName))) {
      print('SRV target: ${srv.target} port: ${srv.port}');

      await for (final IPAddressResourceRecord ip
          in client.lookup<IPAddressResourceRecord>(
              ResourceRecordQuery.addressIPv4(srv.target))) {
        print('IP: ${ip.address.toString()}');
      }
    }
  }
  client.stop();
}
guyluz11 commented 11 months ago

Found it in the code await InternetAddress.lookup(foundMdns.mdnsSrvTarget); Maybe we want to add a separate function that returns a list of ActiveHost for easy use. Closing the issue

guyluz11 commented 11 months ago

Ok I have found an example that didn't work using InternetAddress.lookup for eWeLink mdns eWeLink_10013abc14.local.

Error: Failed host lookup: 'eWeLink_10013abc14.local' (OS Error: No address associated with hostname, errno = 7).

If I am using Linux command avahi program I can find it

$ avahi-resolve-host-name eWeLink_10013abc14.local
eWeLink_10013abc14.local        192.168.31.220

Also works using the multicast_dns but didn't worked without ResourceRecordQuery.serverPointer

Works:

import 'package:multicast_dns/multicast_dns.dart';

main() async {
  String serviceType = '_ewelink._tcp';
  final MDnsClient client = MDnsClient();
  await client.start();

  await for (final PtrResourceRecord ptr in client.lookup<PtrResourceRecord>(
      ResourceRecordQuery.serverPointer(serviceType))) {
    print('PTR: ${ptr.toString()}');

    await for (final IPAddressResourceRecord ip
        in client.lookup<IPAddressResourceRecord>(
            ResourceRecordQuery.addressIPv4('eWeLink_10013abc14.local'))) {
      print('IP: ${ip.address.toString()}');
    }
  }
  client.stop();
}

Doesn't wok:

import 'package:multicast_dns/multicast_dns.dart';

main() async {
  final MDnsClient client = MDnsClient();
  await client.start();

  await for (final IPAddressResourceRecord ip
  in client.lookup<IPAddressResourceRecord>(
      ResourceRecordQuery.addressIPv4('eWeLink_10013abc14.local'))) {
    print('IP: ${ip.address.toString()}');
  }

  client.stop();
}

I think we should move the search to another function which will try InternetAddress.lookup(); and if it fails fild a way to use the one from multicast_dns. We need to somehow find the corresponding serviceType, maybe searching for the corresponding one in our list_of_srv_records.dart list and using that

guyluz11 commented 11 months ago

That works

import 'dart:io';

import 'package:cbj_integrations_controller/infrastructure/svg/list_of_srv_records.dart';
import 'package:cbj_integrations_controller/utils.dart';
import 'package:collection/collection.dart';
import 'package:multicast_dns/multicast_dns.dart';

main() async {
  getIpFromMdnsName('eWeLink_10013abc14.local');
}

Future<String?> getIpFromMdnsName(String mdnsName) async {
  try {
    bool localDevice = mdnsName.contains('local');
    if (!localDevice) {
      final List<InternetAddress> mdnsLookup =
          await InternetAddress.lookup(mdnsName);
      if (mdnsLookup.isNotEmpty) {
        return mdnsLookup.first.address;
      }
    }

    int endIndex =
        mdnsName.contains('_') ? mdnsName.indexOf('_') : mdnsName.indexOf('.');

    final String generalMdnsServiceType =
        mdnsName.substring(0, endIndex).toLowerCase();
    final String? serviceType = tcpSrvRecordsList.firstWhereOrNull(
        (element) => element.contains(generalMdnsServiceType));
    if (serviceType == null) {
      final List<InternetAddress> mdnsLookup =
          await InternetAddress.lookup(mdnsName);
      if (mdnsLookup.isNotEmpty) {
        return mdnsLookup.first.address;
      }
      return null;
    }

    final MDnsClient client = MDnsClient();
    await client.start();

    await for (final PtrResourceRecord ptr in client.lookup<PtrResourceRecord>(
        ResourceRecordQuery.serverPointer(serviceType))) {
      print('PTR: ${ptr.toString()}');

      await for (final IPAddressResourceRecord ip
          in client.lookup<IPAddressResourceRecord>(
              ResourceRecordQuery.addressIPv4(mdnsName))) {
        print('IP: ${ip.address.toString()}');
        return ip.address.toString();
      }
    }
    client.stop();
  } catch (e) {
    logger.e('Error mdns lookup $e');
  }
  return null;
}
guyluz11 commented 11 months ago

Here are some of my app logs, I think some of them should be address in general, and some points up that my fix will not work 100% for cases that I can't find the serviceType

I/flutter (12406): SocketException: Failed host lookup: 'eWeLink_10013abc14.local' (OS Error: No address associated with hostname, errno = 7)
I/flutter (12406): 2023-11-25 19:13:52.718031: SEVERE: network_tools: Exception here: SocketException: Failed reverse host lookup (OS Error: hostname nor servname provided, or not known, errno = 8), address = 0.0.0.0
I/flutter (12406): 2023-11-25 19:13:52.774003: SEVERE: network_tools: Exception here: SocketException: Failed reverse host lookup (OS Error: hostname nor servname provided, or not known, errno = 8), address = MiWiFi-RM1800-srv.local
I/flutter (12406): 2023-11-25 19:13:57.894549: FINE: network_tools: Problem in fetching arp entry for 10.154.186.127
E/Dart    (12406): Dart Socket ERROR: ../../third_party/dart/runtime/bin/socket_android.cc:147: `reusePort` not supported for Android.
I/flutter (12406): 2023-11-25 19:13:58.380677: SEVERE: network_tools: Exception here: SocketException: Failed reverse host lookup (OS Error: hostname nor servname provided, or not known, errno = 8), address = HPC0180319938E.local
E/Dart    (12406): Dart Socket ERROR: ../../third_party/dart/runtime/bin/socket_android.cc:147: `reusePort` not supported for Android.
guyluz11 commented 11 months ago

Wait, in our case we do know what serviceType we are searching on. So we can use the getIpFromMdnsName function I have added.

guyluz11 commented 11 months ago

I have found a fix for my use case, the future is important but not enough to keep it open.

Closing, if someone needs this feature please create a new issue Thanks