Skip to content

Commit

Permalink
Merge pull request #84 from ronkorving/master
Browse files Browse the repository at this point in the history
Get node_mdns up to speed
  • Loading branch information
Ron Korving committed May 29, 2014
2 parents c67c793 + 95205d6 commit 444707a
Show file tree
Hide file tree
Showing 13 changed files with 174 additions and 28 deletions.
29 changes: 29 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
CHANGES

Version 2.1.4 to 2.2.0

- moved the repository back from Wizcorp/node_mdns to agnat/node_mdns

Version 2.1.3 to 2.1.4

- added service to errors from the resolver (bjornstar)

Version 2.1.2 to 2.1.3

- applied the fix from 2.1.0 to remaining parts of the code base (achingbrain)

Version 2.1.1 to 2.1.2

- fixed an exception that was thrown when a service went down along with its network interface (achingbrain)

Version 2.1.0 to 2.1.1

- fixed the interfaceIndex patch by mrose17 as it was breaking on Node 0.8 (ronkorving)

Version 2.0.0-dev to 2.1.0

- errno was broken in Node v0.10 (mrose17)
- allow interfaceIndex to be "really big" (mrose17)

Version 1.1.0 to 2.0.0-dev

- empty version release (agnat)

Version 1.0.0 to 1.1.0

- better handling of network interfaces
Expand Down
4 changes: 2 additions & 2 deletions README.textile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ h1. mdns -- node.js Service Discovery

|_. Package: | mdns |
|_. Description: | multicast DNS service discovery |
|_. Version: | 2.0.0-dev |
|_. Version: | 2.2.0 |
|_. Installation: | @npm install mdns@ (see below) |
|_. Documentation: | "mdns user guide":http://agnat.github.com/node_mdns/user_guide.html |
|_. License: | "MIT":http://github.com/agnat/node_mdns/blob/master/LICENSE |
Expand Down Expand Up @@ -34,7 +34,7 @@ var all_the_types = mdns.browseThemAll(); // all_the_types is just another brows

h2. Installation

On Linux and other systems using the avahi daemon the avahi dns_sd compat library and its header files are required. On debianesque systems the package name is @libavahi-compat-libdnssd-dev@. On other platforms Apples "mDNSResponder":http://opensource.apple.com/tarballs/mDNSResponder/ is recommended. Care should be taken not to install more than one mDNS stack on a system.
On Linux and other systems using the avahi daemon the avahi dns_sd compat library and its header files are required. On debianesque systems the package name is @libavahi-compat-libdnssd-dev@. On other platforms Apples "mDNSResponder":http://opensource.apple.com/tarballs/mDNSResponder/ is recommended. Care should be taken not to install more than one mDNS stack on a system.

On Windows you are going to need Apples "Bonjour SDK for Windows". You can download it either from Apple (registration required) or various unofficial sources. Take your pick. After installing the SDK restart your shell or command prompt and make sure the @BONJOUR_SDK_HOME@ environment variable is set. You'll also need a compiler. Microsoft Visual Studio Express will do. On Windows node >=0.7.9 is required.

Expand Down
31 changes: 31 additions & 0 deletions examples/osc_devices.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env node
var mdns = require('../lib/mdns')
, listOfOscDevices = { /*name: {adresses: ['192.168.0.24', 'fe80::0:18'], port: 10001}}*/ }
;

var mdnsBrowser = mdns.createBrowser(mdns.udp('osc'));

mdnsBrowser.on('serviceUp', function(service) {
// ignore duplicate ups
if(listOfOscDevices[service.name]) return;

listOfOscDevices[service.name] = {'addresses': service.addresses, 'port': service.port};
var cnt = Object.keys(listOfOscDevices).length;

console.log('osc device "'+service.name+' up at '+service.addresses[0]+':'+service.port+', now '+cnt+' devices on the net');
});

mdnsBrowser.on('serviceDown', function(service) {
// ignore duplicate downs
if(!listOfOscDevices[service.name]) return;

var device = listOfOscDevices[service.name];

delete listOfOscDevices[service.name];
var cnt = Object.keys(listOfOscDevices).length;

console.log('osc device "'+service.name+' up at '+device.addresses[0]+':'+device.port+', now '+cnt+' devices on the net');
});

console.log('listening for osc-compatible devices on the net')
mdnsBrowser.start();
2 changes: 1 addition & 1 deletion lib/advertisement.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function Advertisement(serviceType, port, options, callback) {
, domain: domain
, flags: flags
}, context);
}
}
if (error) {
self.emit('error', error);
}
Expand Down
22 changes: 17 additions & 5 deletions lib/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ var Browser = exports.Browser = function Browser(serviceType, options) {
, requested_type = st.makeServiceType( serviceType );
;

var interfaceNames = [];

function on_service_changed(sdRef, flags, ifaceIdx, errorCode, serviceName,
serviceType, replyDomain, context)
{
function on_resolver_done(error, service) {
if (error) {
self.emit('error', error);
self.emit('error', error, service);
} else {
self.emit('serviceChanged', service, context);
self.emit('serviceUp', service, context);
Expand All @@ -42,10 +44,20 @@ var Browser = exports.Browser = function Browser(serviceType, options) {
};
if (serviceName) service.name = serviceName;

if (typeof dns_sd.if_indextoname !== 'undefined' && ifaceIdx > 0) {
service.networkInterface = dns_sd.if_indextoname(ifaceIdx);
} else if (dns_sd.kDNSServiceInterfaceIndexLocalOnly === ifaceIdx) {
if (dns_sd.kDNSServiceInterfaceIndexLocalOnly === ifaceIdx) {
service.networkInterface = nif.loopbackName();
} else if (typeof dns_sd.if_indextoname !== 'undefined' && ifaceIdx > 0) {
try {
service.networkInterface = dns_sd.if_indextoname(ifaceIdx);

interfaceNames[ifaceIdx] = service.networkInterface;
} catch(e) {
if(typeof interfaceNames[ifaceIdx] !== "undefined") {
service.networkInterface = interfaceNames[ifaceIdx];
} else {
throw e;
}
}
}

if (flags & dns_sd.kDNSServiceFlagsAdd) {
Expand Down Expand Up @@ -75,7 +87,7 @@ var resolve = exports.resolve = function resolve(service, sequence, callback) {

function next(error) {
if (error) {
callback(error);
callback(error, service);
return;
}
if (sequence.length === step) {
Expand Down
3 changes: 2 additions & 1 deletion lib/resolver_sequence_tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,10 @@ try {
_getaddrinfo = function getaddrinfo_v06x(host, family, cb) {
var wrap = cares.getaddrinfo(host, family);
if ( ! wrap) {
throw errnoException(errno, 'getaddrinfo');
throw errnoException(process._errno || global.errno, 'getaddrinfo');
}
wrap.oncomplete = function(addresses) {
var errno = process._errno || global.errno;

if (addresses) {
cb(undefined, addresses);
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{ "name": "mdns"
, "version": "2.0.0-dev"
, "version": "2.2.0"
, "description": "multicast DNS service discovery"
, "main": "./lib/mdns.js"
, "scripts":
{ "test": "node utils/testrun"
}
, "keywords": ["zeroconf", "bonjour", "dns_sd", "mDNSResponder"]
, "devDependencies":
, "devDependencies":
{ "ejs": "*"
, "less": "*"
, "mkdirp": "*"
Expand All @@ -15,6 +15,7 @@
, "glob": "*"
, "ncp": "*"
, "minimatch": "*"
, "proxyquire": "~0.5"
}
, "repository":
{ "type": "git"
Expand Down
6 changes: 3 additions & 3 deletions src/dns_service_browse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ OnServiceChanged(DNSServiceRef sdRef, DNSServiceFlags flags,
Local<Value> args[argc];
args[0] = Local<Object>::New(serviceRef->handle_);
args[1] = Integer::New(flags);
args[2] = Integer::New(interfaceIndex);
args[2] = Integer::NewFromUnsigned(interfaceIndex);
args[3] = Integer::New(errorCode);
args[4] = stringOrUndefined(serviceName);
args[5] = stringOrUndefined(serviceType);
Expand Down Expand Up @@ -59,10 +59,10 @@ DNSServiceBrowse(Arguments const& args) {
}
DNSServiceFlags flags = args[1]->ToInteger()->Int32Value();

if ( ! args[2]->IsInt32()) {
if ( ! args[2]->IsUint32() && ! args[2]->IsInt32()) {
return throwTypeError("argument 3 must be an integer (interfaceIndex)");
}
uint32_t interfaceIndex = args[2]->ToInteger()->Int32Value();
uint32_t interfaceIndex = args[2]->ToInteger()->Uint32Value();

if ( ! args[3]->IsString()) {
return throwTypeError("argument 4 must be a string (service type)");
Expand Down
8 changes: 4 additions & 4 deletions src/dns_service_enumerate_domains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ OnEnumeration(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceInde
Local<Value> args[argc];
args[0] = Local<Object>::New(serviceRef->handle_);
args[1] = Integer::New(flags);
args[2] = Integer::New(interfaceIndex);
args[2] = Integer::NewFromUnsigned(interfaceIndex);
args[3] = Integer::New(errorCode);
args[4] = stringOrUndefined(replyDomain);
args[5] = Local<Value>::New(serviceRef->GetContext());
Expand All @@ -49,10 +49,10 @@ DNSServiceEnumerateDomains(Arguments const& args) {
}
DNSServiceFlags flags = args[1]->ToInteger()->Int32Value();

if ( ! args[2]->IsInt32()) {
return throwTypeError("argument 3 must be an integer (interfaceIndex)");
if ( ! args[2]->IsUint32() && ! args[2]->IsInt32()) {
return throwTypeError("argument 3 must be an integer (interfaceIndex)");
}
uint32_t interfaceIndex = args[2]->ToInteger()->Int32Value();
uint32_t interfaceIndex = args[2]->ToInteger()->Uint32Value();

if ( ! args[3]->IsFunction()) {
return throwTypeError("argument 4 must be a function (callBack)");
Expand Down
8 changes: 4 additions & 4 deletions src/dns_service_get_addr_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ OnAddressInfo(DNSServiceRef sdRef, DNSServiceFlags flags,
Local<Value> args[argc];
args[0] = Local<Object>::New(serviceRef->handle_);
args[1] = Integer::New(flags);
args[2] = Integer::New(interfaceIndex);
args[2] = Integer::NewFromUnsigned(interfaceIndex);
args[3] = Integer::New(errorCode);
args[4] = stringOrUndefined(hostname);
args[5] = String::Empty();
Expand Down Expand Up @@ -89,10 +89,10 @@ DNSServiceGetAddrInfo(Arguments const& args) {
}
DNSServiceFlags flags = args[1]->ToInteger()->Int32Value();

if ( ! args[2]->IsInt32()) {
return throwTypeError("argument 3 must be an integer (interfaceIndex)");
if ( ! args[2]->IsUint32() && ! args[2]->IsInt32()) {
return throwTypeError("argument 3 must be an integer (interfaceIndex)");
}
uint32_t interfaceIndex = args[2]->ToInteger()->Int32Value();
uint32_t interfaceIndex = args[2]->ToInteger()->Uint32Value();

if ( ! args[3]->IsInt32()) {
return throwTypeError("argument 4 must be an integer (DNSServiceProtocol)");
Expand Down
6 changes: 3 additions & 3 deletions src/dns_service_register.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ DNSServiceRegister(Arguments const& args) {
if (argumentCountMismatch(args, 11)) {
return throwArgumentCountMismatchException(args, 11);
}

if ( ! ServiceRef::HasInstance(args[0])) {
return throwTypeError("argument 1 must be a DNSServiceRef (sdRef)");
}
Expand All @@ -69,10 +69,10 @@ DNSServiceRegister(Arguments const& args) {
}
DNSServiceFlags flags = args[1]->ToInteger()->Int32Value();

if ( ! args[2]->IsInt32()) {
if ( ! args[2]->IsUint32() && ! args[2]->IsInt32()) {
return throwTypeError("argument 3 must be an integer (interfaceIndex)");
}
uint32_t interfaceIndex = args[2]->ToInteger()->Int32Value();
uint32_t interfaceIndex = args[2]->ToInteger()->Uint32Value();

bool has_name = false;
if ( ! args[3]->IsNull() && ! args[3]->IsUndefined()) {
Expand Down
6 changes: 3 additions & 3 deletions src/dns_service_resolve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ OnResolve(DNSServiceRef sdRef, DNSServiceFlags flags,
Local<Value> args[argc];
args[0] = Local<Object>::New(serviceRef->handle_);
args[1] = Integer::New(flags);
args[2] = Integer::New(interfaceIndex);
args[2] = Integer::NewFromUnsigned(interfaceIndex);
args[3] = Integer::New(errorCode);
args[4] = stringOrUndefined(fullname);
args[5] = stringOrUndefined(hosttarget);
Expand Down Expand Up @@ -72,10 +72,10 @@ DNSServiceResolve(Arguments const& args) {
}
DNSServiceFlags flags = args[1]->ToInteger()->Int32Value();

if ( ! args[2]->IsInt32()) {
if ( ! args[2]->IsUint32() && ! args[2]->IsInt32()) {
return throwTypeError("argument 3 must be an integer (interfaceIndex)");
}
uint32_t interfaceIndex = args[2]->ToInteger()->Int32Value();
uint32_t interfaceIndex = args[2]->ToInteger()->Uint32Value();

if ( ! args[3]->IsString()) {
return throwTypeError("argument 4 must be a string (name)");
Expand Down
72 changes: 72 additions & 0 deletions tests/test_browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
var st = require('../lib/service_type.js')
, dns_sd = require('../lib/dns_sd')
, proxyquire = require('proxyquire')
;

//=== Browser ===========================================================

module.exports["Browser"] = {
"Should retrieve interface name from cache when interface index is no longer valid": function(test) {
var callback = null
, invocations = 0
, threwException = false
, interfaceName = "foo"
, serviceName = "_foo._tcp.";

var mock_dns_sd = {
kDNSServiceErr_NoError: dns_sd.kDNSServiceErr_NoError,
kDNSServiceInterfaceIndexLocalOnly: dns_sd.kDNSServiceInterfaceIndexLocalOnly,
kDNSServiceFlagsAdd: dns_sd.kDNSServiceFlagsAdd,

// we stub the if_indextoname method
if_indextoname: function() {
invocations++;

if(invocations == 1) {
return interfaceName;
}

threwException = true;
throw new Error("Panic!");
},
// and stub DNSServiceBrowse to expose a reference to the passed callback
DNSServiceBrowse: function(sdRef, flags, ifaceIdx, serviceType, domain, on_service_changed, context) {
callback = function() {
// pass 1 as the interface index so we don't try to find the name for loopback
on_service_changed(sdRef, flags, 1, dns_sd.kDNSServiceErr_NoError, serviceName, serviceType, domain, context);
};
}
};

// we're going to expect two invocations, one where the interface is present and one where it's not
var serviceDownInvocations = 0;

// create the browser with our mock of dns_sd
var browser = proxyquire('../lib/browser.js', { './dns_sd': mock_dns_sd }).Browser.create(serviceName);
browser.on("serviceDown", function(service) {
serviceDownInvocations++;

if(serviceDownInvocations == 1) {
// first invocation, should have interface name from dns_sd callback
test.equal(service.networkInterface, interfaceName);

// should not have thrown an exception yet
test.ok( ! threwException);
}

if(serviceDownInvocations == 2) {
// second invocation, should have thrown an exception in the callback
test.ok(threwException);

// should still have the interface name even though we threw an exception
test.equal(service.networkInterface, interfaceName);

test.done();
}
});

// simulate two MDNS events, this will trigger the serviceDown event we listen for above.
callback();
callback();
}
}

0 comments on commit 444707a

Please sign in to comment.