Low level multicast-dns implementation in pure javascript
npx @tessl/cli install tessl/npm-multicast-dns@7.2.0Multicast DNS provides a low-level multicast DNS (mDNS) implementation in pure JavaScript for Node.js applications. It enables service discovery on local networks by implementing the mDNS protocol (RFC 6762) which allows devices to discover services without requiring a centralized DNS server.
npm install multicast-dnsconst mdns = require('multicast-dns');const mdns = require('multicast-dns')();
// Listen for DNS responses
mdns.on('response', function(response) {
console.log('got a response packet:', response);
});
// Listen for DNS queries
mdns.on('query', function(query) {
console.log('got a query packet:', query);
// Respond to A record queries for 'example.local'
query.questions.forEach(function(q) {
if (q.type === 'A' && q.name === 'example.local') {
mdns.respond({
answers: [{
name: 'example.local',
type: 'A',
ttl: 300,
data: '192.168.1.5'
}]
});
}
});
});
// Query for an A record
mdns.query({
questions: [{
name: 'example.local',
type: 'A'
}]
});The package includes a command-line tool for basic mDNS operations.
Installation for global use:
npm install -g multicast-dnsUsage:
# Query for a hostname (resolve to IP)
multicast-dns example.local
# Output: 192.168.1.5
# Announce a hostname (advertise current machine)
multicast-dns --announce example.localThe CLI tool automatically detects the local IP address for announcements and provides a simple interface for common mDNS operations.
Multicast DNS is built around several key components:
Creates a new multicast DNS instance with configurable network and protocol options.
/**
* Creates a new multicast DNS instance
* @param {Object} [options] - Configuration options
* @param {number} [options.port=5353] - UDP port number
* @param {string} [options.type='udp4'] - Socket type ('udp4' or 'udp6')
* @param {string} [options.ip='224.0.0.251'] - Multicast IP address
* @param {string} [options.host] - Alias for ip option
* @param {string|string[]} [options.interface] - Network interface(s) to bind to
* @param {boolean} [options.multicast=true] - Enable multicast functionality
* @param {number} [options.ttl=255] - Multicast TTL value
* @param {boolean} [options.loopback=true] - Receive own multicast packets
* @param {boolean} [options.reuseAddr=true] - Enable SO_REUSEADDR socket option
* @param {string|boolean} [options.bind] - Bind address or false to disable binding
* @param {dgram.Socket} [options.socket] - Pre-existing socket to use
* @returns {EventEmitter} mDNS instance with query/response capabilities
*/
function multicastdns(options)Send DNS queries to discover services and devices on the local network.
/**
* Send a DNS query packet
* @param {string|Object|Object[]} query - Query specification
* @param {string} [type] - DNS record type when query is a string
* @param {Object} [rinfo] - Target address information
* @param {Function} [callback] - Completion callback
*/
mdns.query(query, type, rinfo, callback)Query Format Options:
// String format with explicit type
mdns.query('example.local', 'A');
// String format with callback (defaults to 'ANY' type)
mdns.query('example.local', callback);
// String format with type and callback
mdns.query('example.local', 'A', callback);
// Array of question objects
mdns.query([{name: 'example.local', type: 'A'}]);
// Full packet object
mdns.query({
questions: [{name: 'example.local', type: 'A'}]
});Send DNS responses to advertise services and answer queries from other devices.
/**
* Send a DNS response packet
* @param {Object[]|Object} response - Response data
* @param {Object} [rinfo] - Target address information
* @param {Function} [callback] - Completion callback
*/
mdns.respond(response, rinfo, callback)
/**
* Alias for respond method - identical signature
* @param {Object[]|Object} response - Response data
* @param {Object} [rinfo] - Target address information
* @param {Function} [callback] - Completion callback
*/
mdns.response(response, rinfo, callback)Response Format Options:
// Array of answer records
mdns.respond([{
name: 'example.local',
type: 'A',
ttl: 300,
data: '192.168.1.5'
}]);
// Full packet object
mdns.respond({
answers: [{name: 'example.local', type: 'A', data: '192.168.1.5'}]
});Low-level packet sending for custom DNS operations.
/**
* Send a raw DNS packet
* @param {Object} packet - DNS packet object
* @param {Object} [rinfo] - Target address information
* @param {Function} [callback] - Completion callback
*/
mdns.send(packet, rinfo, callback)Control the lifecycle and network configuration of mDNS instances.
/**
* Destroy the mDNS instance and close the UDP socket
* @param {Function} [callback] - Completion callback
*/
mdns.destroy(callback)
/**
* Update multicast group memberships for current network interfaces
*/
mdns.update()/**
* Emitted for all incoming DNS packets
* @param {Object} packet - Decoded DNS packet
* @param {Object} rinfo - Remote address information
*/
mdns.on('packet', function(packet, rinfo) {})
/**
* Emitted for incoming DNS query packets
* @param {Object} query - DNS query packet
* @param {Object} rinfo - Remote address information
*/
mdns.on('query', function(query, rinfo) {})
/**
* Emitted for incoming DNS response packets
* @param {Object} response - DNS response packet
* @param {Object} rinfo - Remote address information
*/
mdns.on('response', function(response, rinfo) {})/**
* Emitted when the socket is bound and ready to send/receive
*/
mdns.on('ready', function() {})
/**
* Emitted when network interface memberships are updated
*/
mdns.on('networkInterface', function() {})/**
* Emitted for critical socket errors (EACCES, EADDRINUSE)
* @param {Error} error - Error object
*/
mdns.on('error', function(error) {})
/**
* Emitted for non-critical errors and warnings
* @param {Error} error - Error object
*/
mdns.on('warning', function(error) {})interface DNSPacket {
type: 'query' | 'response';
questions: Question[];
answers: Record[];
authorities: Record[];
additionals: Record[];
flags?: number;
flag_aa?: boolean; // Authoritative Answer flag
}interface Question {
name: string; // Domain name (e.g., 'example.local')
type: string; // Record type ('A', 'AAAA', 'SRV', 'PTR', 'TXT', 'HINFO', 'ANY')
class: string; // Usually 'IN'
}interface Record {
name: string; // Domain name
type: string; // Record type
class: string; // Usually 'IN'
ttl: number; // Time to live in seconds
data: any; // Record-specific data (see Record Data Types)
flush?: boolean; // Cache flush bit for mDNS
}// A Record - IPv4 address
// data: string (e.g., '192.168.1.5')
// AAAA Record - IPv6 address
// data: string (e.g., 'fe80::5ef9:38ff:fe8c:ceaa')
// SRV Record - Service record
// data: {
// port: number, // Service port number
// target: string, // Target hostname
// priority: number, // Priority value (lower = higher priority)
// weight: number // Weight for same priority services
// }
// TXT Record - Text data
// data: Buffer[] | string[] // Array of text strings or buffers
// PTR Record - Pointer record
// data: string // Target domain name
// HINFO Record - Host information
// data: {
// cpu: string, // CPU type
// os: string // Operating system
// }interface RemoteInfo {
address: string; // Remote IP address
port: number; // Remote port number
family: string; // 'IPv4' or 'IPv6'
size: number; // Message size in bytes
}const mdns = require('multicast-dns')();
// Discover all services
mdns.query('_services._dns-sd._udp.local', 'PTR');
mdns.on('response', function(response) {
response.answers.forEach(function(answer) {
if (answer.type === 'PTR') {
console.log('Found service:', answer.data);
}
});
});const mdns = require('multicast-dns')();
mdns.on('query', function(query) {
query.questions.forEach(function(question) {
if (question.name === '_http._tcp.local' && question.type === 'PTR') {
mdns.respond({
answers: [{
name: '_http._tcp.local',
type: 'PTR',
ttl: 120,
data: 'My Web Server._http._tcp.local'
}],
additionals: [{
name: 'My Web Server._http._tcp.local',
type: 'SRV',
ttl: 120,
data: {
port: 8080,
target: 'my-server.local',
priority: 0,
weight: 5
}
}, {
name: 'my-server.local',
type: 'A',
ttl: 120,
data: '192.168.1.100'
}]
});
}
});
});Important: IPv6 multicast requires both ip and interface options to be specified or it will throw an error.
const mdns = require('multicast-dns')({
type: 'udp6',
ip: 'ff02::fb', // Required for IPv6
interface: 'eth0' // Required for IPv6
});
mdns.query('example.local', 'AAAA');
// This will throw an error:
// const mdns = require('multicast-dns')({type: 'udp6'}); // Missing ip and interfaceconst mdns = require('multicast-dns')({
interface: '192.168.1.100', // Bind to specific interface
port: 5353
});
// Listen on multiple interfaces
const mdns2 = require('multicast-dns')({
interface: ['192.168.1.100', '10.0.0.100']
});The library categorizes errors into critical and non-critical types:
const mdns = require('multicast-dns')();
mdns.on('error', function(err) {
console.error('Critical error:', err.message);
// Handle critical errors - may need to recreate instance
});
mdns.on('warning', function(err) {
console.warn('Warning:', err.message);
// Handle warnings - instance continues operating
});