diff --git a/CHANGES b/CHANGES index 5b2fbda1..e5635522 100644 --- a/CHANGES +++ b/CHANGES @@ -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 diff --git a/README.textile b/README.textile index 68071dea..6120faf7 100644 --- a/README.textile +++ b/README.textile @@ -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 | @@ -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. diff --git a/examples/osc_devices.js b/examples/osc_devices.js new file mode 100755 index 00000000..664de910 --- /dev/null +++ b/examples/osc_devices.js @@ -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(); diff --git a/lib/advertisement.js b/lib/advertisement.js index 4406ee64..ebc0c60c 100644 --- a/lib/advertisement.js +++ b/lib/advertisement.js @@ -47,7 +47,7 @@ function Advertisement(serviceType, port, options, callback) { , domain: domain , flags: flags }, context); - } + } if (error) { self.emit('error', error); } diff --git a/lib/browser.js b/lib/browser.js index 4a1999e0..db590a4a 100644 --- a/lib/browser.js +++ b/lib/browser.js @@ -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); @@ -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) { @@ -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) { diff --git a/lib/resolver_sequence_tasks.js b/lib/resolver_sequence_tasks.js index 27401fa5..93b1c798 100644 --- a/lib/resolver_sequence_tasks.js +++ b/lib/resolver_sequence_tasks.js @@ -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); diff --git a/package.json b/package.json index d2ba0f3e..3bcb0df3 100644 --- a/package.json +++ b/package.json @@ -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": "*" @@ -15,6 +15,7 @@ , "glob": "*" , "ncp": "*" , "minimatch": "*" + , "proxyquire": "~0.5" } , "repository": { "type": "git" diff --git a/src/dns_service_browse.cpp b/src/dns_service_browse.cpp index 73095ae9..5897f1c3 100644 --- a/src/dns_service_browse.cpp +++ b/src/dns_service_browse.cpp @@ -26,7 +26,7 @@ OnServiceChanged(DNSServiceRef sdRef, DNSServiceFlags flags, Local args[argc]; args[0] = Local::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); @@ -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)"); diff --git a/src/dns_service_enumerate_domains.cpp b/src/dns_service_enumerate_domains.cpp index 44d144bb..fd1c4e4c 100644 --- a/src/dns_service_enumerate_domains.cpp +++ b/src/dns_service_enumerate_domains.cpp @@ -22,7 +22,7 @@ OnEnumeration(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceInde Local args[argc]; args[0] = Local::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::New(serviceRef->GetContext()); @@ -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)"); diff --git a/src/dns_service_get_addr_info.cpp b/src/dns_service_get_addr_info.cpp index 883f860d..3dfde8d1 100644 --- a/src/dns_service_get_addr_info.cpp +++ b/src/dns_service_get_addr_info.cpp @@ -36,7 +36,7 @@ OnAddressInfo(DNSServiceRef sdRef, DNSServiceFlags flags, Local args[argc]; args[0] = Local::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(); @@ -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)"); diff --git a/src/dns_service_register.cpp b/src/dns_service_register.cpp index 70cfadea..26665032 100644 --- a/src/dns_service_register.cpp +++ b/src/dns_service_register.cpp @@ -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)"); } @@ -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()) { diff --git a/src/dns_service_resolve.cpp b/src/dns_service_resolve.cpp index 29df9ce6..3b8f0fab 100644 --- a/src/dns_service_resolve.cpp +++ b/src/dns_service_resolve.cpp @@ -35,7 +35,7 @@ OnResolve(DNSServiceRef sdRef, DNSServiceFlags flags, Local args[argc]; args[0] = Local::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); @@ -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)"); diff --git a/tests/test_browser.js b/tests/test_browser.js new file mode 100644 index 00000000..c1fa4ed2 --- /dev/null +++ b/tests/test_browser.js @@ -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(); + } +}