npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

node-dns-sd

v1.0.1

Published

The node-dns-sd is a Node.js module which is a pure javascript implementation of mDNS/DNS-SD (Apple Bonjour) browser and packet parser. It allows you to discover IPv4 addresses in the local network specifying a service name such as `_http._tcp.local`. Bes

Downloads

3,913

Readme

node-dns-sd

The node-dns-sd is a Node.js module which is a pure javascript implementation of mDNS/DNS-SD (Apple Bonjour) browser and packet parser. It allows you to discover IPv4 addresses in the local network specifying a service name such as _http._tcp.local. Besides, it allows you to monitor mDNS/DNS-SD packets.

This module focuses on discovering IPv4 addresses from a service name. It is not a full implementation of mDNS/DNS-SD. If you want to announce services or send custom query packets or discover IPv6 addresses, you should choice another one from mDNS/DNS-SD nodejs implementations.

Dependencies

  • Node.js 18 +
    • Though the node-dns-sd works on older version of Node for now, it is strongly recommended to use the latest version of Node.

Installation

$ cd ~
$ npm install node-dns-sd

Table of Contents


Quick Start

Discover devices

The node-dns-sd supports a Promise-based method for discovering devices in the local network. The sample code below shows how to discover devices from a service name. In the sample code, _googlecast._tcp.local is specified for a service name.

const mDnsSd = require('node-dns-sd');

mDnsSd.discover({
  name: '_googlecast._tcp.local'
}).then((device_list) =>{
  console.log(JSON.stringify(device_list, null, '  '));
}).catch((error) => {
  console.error(error);
});

The sample code above will output the result as follows:

[
  {
    "address": "192.168.11.20",
    "fqdn": "BRAVIA-4K-GB-0000043926ff4f7fed3bf248db400000._googlecast._tcp.local",
    "modelName": "BRAVIA 4K GB",
    "familyName": "KJ-43X8300D",
    "service": {
      "port": 8009,
      "protocol": "tcp",
      "type": "googlecast"
    },
    "packet": {...}
  },
  {
    "address": "192.168.11.12",
    "fqdn": "Google-Home-000001eda257d1f8ea765acd79500000._googlecast._tcp.local",
    "modelName": "Google Home",
    "familyName": "Google Home in living room",
    "service": {
      "port": 8009,
      "protocol": "tcp",
      "type": "googlecast"
    },
    "packet": {...}
  }
]

As you can see, you can obtain information of devices which support the service specified to the discover() method.

The value of the packet property in the response is a DnsSdPacket object which represents a DNS-SD response packet. You can find more information in the object.

Monitor packets

The node-dns-sd has a mDNS/DNS-SD packet parser. It can watch mDNS/DNS-SD packets in the local network and reports the packets as human-readable information.

const mDnsSd = require('node-dns-sd');

mDnsSd.ondata = (packet) => {
  console.log(JSON.stringify(packet, null, '  '));
};

mDnsSd.startMonitoring().then(() => {
  console.log('Started.');
}).catch((error) => {
  console.error(error);
});

The sample code above will output the result as follows:

...
{
  "header": {
    "id": 0,
    "qr": 0,
    "op": 0,
    "aa": 0,
    "tc": 0,
    "rd": 0,
    "ra": 0,
    "z": 0,
    "ad": 0,
    "cd": 0,
    "rc": 0,
    "questions": 3,
    "answers": 2,
    "authorities": 0,
    "additionals": 1
  },
  "questions": [
    {
      "name": "_homekit._tcp.local",
      "type": "PTR",
      "class": "IN"
    },
    {
      "name": "A6A1A463-D197-53EA-892B-FFFFFFFFFFFF._homekit._tcp.local",
      "type": "TXT",
      "class": "IN"
    },
    {
      "name": "_sleep-proxy._udp.local",
      "type": "PTR",
      "class": "IN"
    }
  ],
  "answers": [
    {
      "name": "_homekit._tcp.local",
      "type": "PTR",
      "class": "IN",
      "flash": false,
      "ttl": 4500,
      "rdata": "A6A1A463-D197-53EA-892B-FFFFFFFFFFFF._homekit._tcp.local"
    },
    {
      "name": "_sleep-proxy._udp.local",
      "type": "PTR",
      "class": "IN",
      "flash": false,
      "ttl": 4500,
      "rdata": "70-00-00-00.1 Apple TV._sleep-proxy._udp.local"
    }
  ],
  "authorities": [],
  "additionals": [
    {
      "name": "",
      "type": "OPT",
      "class": "",
      "flash": false,
      "ttl": 4500,
      "rdata": "00 04 00 0e 00 24 c2 a5 3e 4c 7c b6 c0 a5 3e 4c 7c b4"
    }
  ],
  "address": "192.168.11.24"
}
...

The object above is a DnsSdPacket object which represents a mDNS/DNS-SD response packet.


DnsSd object

In order to use the node-dns-sd, you have to load the node-dns-sd module as follows:

const DnsSd = require('node-dns-sd');

In the code snippet above, the variable DnsSd is a DnsSd object. The DnsSd object has methods as described in sections below.

discover() method

The discover() method discovers devices supporting the service specified to this method in the local network. This method returns a Promise object.

This method takes a hash object containing the properties as follows:

Property | Type | Required | Description :--------|:--------|:---------|:------------------------- name | String | Required | Service name.(e.g., "_googlecast._tcp.local") type | String | Optional | Query Type (e.g., "PTR"). The default value is "*". key | String | Optional | This value must be "address" (default) or "fqdn". This property indicates how to fold multiple DNS-SD query responses. See the description below for details. wait | Integer | Optional | Duration of monitoring (sec). The default value is 3 sec. quick | Boolean | Optional | If true, this method returns immediately after a device was found ignoring the value of the wait. The default value is false. filter | String | Optional | If a string is specified to the filter, this method discovers only devices which the specified string is found in the fqdn, address, modelName or familyName. filter | Function | Optional | If a function is specified to the filter, this method discovers only devices for which the function returns true. See the sample code below for details.

If you want to discover all services in the local netowrk, you can set the name property to _services._dns-sd._udp.local'.

mDnsSd.discover({
  name: '_services._dns-sd._udp.local',
  type: 'PTR',
  key: 'fqdn'
}).then((device_list) =>{
  console.log(JSON.stringify(device_list, null, '  '));
}).catch((error) => {
  console.error(error);
});

The type property indicates the query type, such as "PTR". This value must be a (Q)TYPE value defined in the RFC 1035 and RFC 2782. If this property is not specified, the wildcard "*" will be applied.

The key property indicates how to fold multiple DNS-SD query responses. If the value is set to "address" or this property is not specified, the last response form an IP address will be reported. If you want to discover IP addresses rather than services, this mode is appropriate.

If the value of the key property is set to "fqdn", responses will be folded by each FQDN. In this mode, multiple responses with the same IP address could be included. If you want to discover services rather than IP address, this mode is appropriate.

Basically you don't need to pass the wait property to this method. In most cases, the default value 3 (sec) works well.

The code blow would find Google devices (Google Home, Google TV, etc.):

mDnsSd.discover({
  name: '_googlecast._tcp.local'
}).then((device_list) =>{
  console.log(JSON.stringify(device_list, null, '  '));
}).catch((error) => {
  console.error(error);
});

The code above will output the result as follows:

[
  {
    "address": "192.168.11.20",
    "fqdn": "BRAVIA-4K-GB-0000043926ff4f7fed3bf248db400000._googlecast._tcp.local",
    "modelName": "BRAVIA 4K GB",
    "familyName": "KJ-43X8300D",
    "service": {
      "port": 8009,
      "protocol": "tcp",
      "type": "googlecast"
    },
    "packet": {...}
  },
  {
    "address": "192.168.11.12",
    "fqdn": "Google-Home-000001eda257d1f8ea765acd79500000._googlecast._tcp.local",
    "modelName": "Google Home",
    "familyName": "Google Home in living room",
    "service": {
      "port": 8009,
      "protocol": "tcp",
      "type": "googlecast"
    },
    "packet": {...}
  }
]

A string is set to the filter parameter, this method limits to devices whose fqdn, address, modelName or familyName includes the string.

mDnsSd.discover({
  name: '_googlecast._tcp.local',
  filter: 'Google Home',
  quick: true
}).then((device_list) =>{
  console.log(JSON.stringify(device_list, null, '  '));
}).catch((error) => {
  console.error(error);
});

A function is set to the filter parameter, this method limits to devices for which the function returns true. The function must return true or false.

mDnsSd.discover({
  name: '_googlecast._tcp.local',
  filter: (devcie) => {
    return (device['modelName'] === 'Google Home' && /Living room/.test(device['familyName']));
  },
  quick: true
}).then((device_list) =>{
  console.log(JSON.stringify(device_list, null, '  '));
}).catch((error) => {
  console.error(error);
});

As you can see from the code above, an object representing a found device is passed to the function. You can evaluate the device information and limit to devices you want.

The discover() method will pass a information list of the found devices to the callback function. Each device information in the list contains the properties as follows:

Property | Type | Description :-------------|:--------|:--------------- address | String | IPv4 address fqdn | String | Fully Qualified Domain Name modelName | String | Model Name familyName | String | Family Name service | Object | +port | Integer | Port number (e.g., 8009) +protocol | String | Protocol (e.g., "tcp") +type | String | Service type (e.g., "googlecast") packet | DnsSdPacket | An object representing the response packet

Note that the values of properties other than the address are not necessarily set in this object. If the values are not obtained from the response packet, they will be set to null.

Here are some examples:

Apple TV

mDnsSd.discover({
  name: '_airplay._tcp.local'
})
[
  {
    "address": "192.168.11.29",
    "fqdn": "Apple TV._airplay._tcp.local",
    "modelName": "Apple TV J42dAP",
    "familyName": null,
    "service": {
      "port": 7000,
      "protocol": "tcp",
      "type": "airplay"
    },
    "packet": {...}
  }
]

Canon Network printer

mDnsSd.discover({
  name: '_printer._tcp.local'
})
[
  {
    "address": "192.168.11.99",
    "fqdn": "Canon MF720C Series._ipp._tcp.local",
    "modelName": "Canon MF720C Series",
    "familyName": null,
    "service": {
      "port": 80,
      "protocol": "tcp",
      "type": "ipp"
    },
    "packet": {...}
  }
]

Philips Hue Bridge

mDnsSd.discover({
  name: '_hap._tcp.local'
})
[
  {
    "address": "192.168.11.18",
    "fqdn": "Philips hue - 123ABC._hap._tcp.local",
    "modelName": "Philips hue BSB002",
    "familyName": null,
    "service": {
      "port": 8080,
      "protocol": "tcp",
      "type": "hap"
    },
    "packet": {...}
  }
]

Raspberry Pi (Raspbian)

mDnsSd.discover({
  name: 'raspberrypi.local'
})
[
  {
    "address": "192.168.11.34",
    "fqdn": null,
    "productName": null,
    "modelName": null,
    "familyName": null,
    "service": null,
    "packet": {...}
  }
]

startMonitoring() method

The startMonitoring() method starts the monitoring mode and listens to mDNS/DNS-SD packets. This method returns a Promise object.

You can catch incoming packets setting a callback function to the ondata event handler.

mDnsSd.ondata = (packet) => {
  console.log(JSON.stringify(packet, null, '  '));
};

mDnsSd.startMonitoring().then(() => {
  console.log('Started.');
}).catch((error) => {
  console.error(error);
});

Whenever a mDNS/DNS-SD packet is received, a DnsSdPacket object will be passed to the callback function. See the section "DnsSdPacket object" for more details.

stopMonitoring() method

The stopMonitoring() method stops the monitoring mode started by the startMonitoring() method. This method returns a Promise object.

mDnsSd.stopMonitoring().then(() => {
  console.log('Stopped.');
}).catch((error) => {
  console.error(error);
});

ondata event handler

The ondata event handler will be called whenever a mDNS/DNS-SD packet is received. Note that this event handler works only if the monitoring mode is active.

See the section "startMonitoring() method" for more details.


DnsSdPacket object

The DnsSdPacket object represents a mDNS/DNS-SD packet. It is a hash object containing the properties as follows:

{
  "header": {
    "id": 0,
    "qr": 1,
    "op": 0,
    "aa": 1,
    "tc": 0,
    "rd": 0,
    "ra": 0,
    "z": 0,
    "ad": 0,
    "cd": 0,
    "rc": 0,
    "questions": 0,
    "answers": 1,
    "authorities": 0,
    "additionals": 3
  },
  "questions": [],
  "answers": [
    {
      "name": "_googlecast._tcp.local",
      "type": "PTR",
      "class": "IN",
      "flash": false,
      "ttl": 120,
      "rdata": "Google-Home-0ae0c1eda257d1f8ea765acd00000000._googlecast._tcp.local"
    }
  ],
  "authorities": [],
  "additionals": [
    {
      "name": "Google-Home-0ae0c1eda257d1f8ea765acd00000000._googlecast._tcp.local",
      "type": "TXT",
      "class": "IN",
      "flash": true,
      "ttl": 4500,
      "rdata": {
        "id": "0ae0c1eda257d1f8ea765acd00000000",
        "cd": "A4030CC6FEF4C94DFCD31B0500000000",
        "rm": "2CE3F99700000000",
        "ve": "05",
        "md": "Google Home",
        "ic": "/setup/icon.png",
        "fn": "Google Home in living room",
        "ca": "2052",
        "st": "0",
        "bs": "FA8F00000000",
        "nf": "1",
        "rs": ""
      },
      "rdata_buffer": {
        "id": {Buffer object},
        "cd": {Buffer object},
        "rm": {Buffer object},
        "ve": {Buffer object},
        "md": {Buffer object},
        "ic": {Buffer object},
        "fn": {Buffer object},
        "ca": {Buffer object},
        "st": {Buffer object},
        "bs": {Buffer object},
        "nf": {Buffer object},
        "rs": {Buffer object}
      }
    },
    {
      "name": "Google-Home-0ae0c1eda257d1f8ea765acd00000000._googlecast._tcp.local",
      "type": "SRV",
      "class": "IN",
      "flash": true,
      "ttl": 120,
      "rdata": {
        "priority": 0,
        "weight": 0,
        "port": 8009,
        "target": "0ae0c1ed-a257-d1f8-ea76-000000000000.local"
      }
    },
    {
      "name": "0ae0c1ed-a257-d1f8-ea76-000000000000.local",
      "type": "A",
      "class": "IN",
      "flash": true,
      "ttl": 120,
      "rdata": "192.168.11.12"
    }
  ],
  "address": "192.168.11.12"
}

Note that the rdata_buffer property is added only if the type is "TXT". Each value in the rdata_buffer is a Buffer object. Some devices set a binary data to each value. You can parse the binary data using this property.

See the section "References" for more details.


Release Note

  • v1.0.1 (2023-04-05)
    • Fixed the constant variable issue (thanks to @cybercode)
  • v1.0.0 (2023-03-11)
    • Rewrote all codes in modern coding style using class, async, await, etc.
    • Supported multi-homed environment.
  • v0.4.2 (2020-09-30)
    • Catch dropMembership error (thanks to @bwp91)
  • v0.4.1 (2020-04-09)
    • Fix of address already in use on udp.addMembership method (thanks to @SlyAndrew)
  • v0.4.0 (2019-02-24)
    • Added the rdata_buffer property in the DnsSdPacket object.
  • v0.3.0 (2018-10-25)
    • Added the key and type parameters to the discover() method.
  • v0.2.1 (2018-10-24)
    • Improved the device discovery. In this version, all available IPv4 network interfaces are joined to a multicast group, so that all devices in the local network are sure to be discovered.
    • Fixed a bug that some event listeners did not be removed when the discovery process is finished.
  • v0.2.0 (2018-08-02)
    • Supported a function-based filtering mechanism in the discover() method. Now you can specify your custom filter as a function to the filter paramter of the discover() method. (thanks to @dayflower)
  • v0.1.2 (2018-01-06)
    • Fixed a bug that an exeption was thrown if the filter was specified to the discover() method.
  • v0.1.0 (2018-01-06)
    • Added the parameter quick and filter to the discover() method.
    • Fixed a bug that a UDP socket was not closed properly.
  • v0.0.1 (2018-01-05)
    • First public release

References


License

The MIT License (MIT)

Copyright (c) 2018-2023 Futomi Hatano

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.