From 2b6b1ac2170d56b30ea608b6ee51e44a390cc641 Mon Sep 17 00:00:00 2001 From: Lucas Pardue Date: Mon, 2 Oct 2017 13:20:52 +0100 Subject: [PATCH 1/9] dgram: add source-specific multicast support This adds RFC 4607 support for IPv4 and IPv6. --- doc/api/dgram.md | 35 ++- lib/dgram.js | 47 ++++ src/udp_wrap.cc | 41 ++++ src/udp_wrap.h | 7 + .../test-dgram-multicast-ssm-multi-process.js | 230 ++++++++++++++++++ ...est-dgram-multicast-ssmv6-multi-process.js | 230 ++++++++++++++++++ 6 files changed, 589 insertions(+), 1 deletion(-) create mode 100644 test/internet/test-dgram-multicast-ssm-multi-process.js create mode 100644 test/internet/test-dgram-multicast-ssmv6-multi-process.js diff --git a/doc/api/dgram.md b/doc/api/dgram.md index 9efd14c89e00d7..9d7088e7a621f6 100644 --- a/doc/api/dgram.md +++ b/doc/api/dgram.md @@ -123,6 +123,21 @@ if (cluster.isMaster) { } ``` +### socket.addSourceSpecificMembership(sourceAddress, groupAddress[, multicastInterface]) + +* `sourceAddress` {string} +* `groupAddress` {string} +* `multicastInterface` {string} + +Tells the kernel to join a source-specific multicast channel at the given +`sourceAddress` and `groupAddress`, using the `multicastInterface` with the +`IP_ADD_SOURCE_MEMBERSHIP` socket option. If the `multicastInterface` argument +is not specified, the operating system will choose one interface and will add +membership to it. To add membership to every available interface, call +`socket.addSourceSpecificMembership()` multiple times, once per interface. + ### socket.address() + +* `sourceAddress` {string} +* `groupAddress` {string} +* `multicastInterface` {string} + +Instructs the kernel to leave a source-specific multicast channel at the given +`sourceAddress` and `groupAddress` using the `IP_DROP_SOURCE_MEMBERSHIP` +socket option. This method is automatically called by the kernel when the +socket is closed or the process terminates, so most apps will never have +reason to call this. + +If `multicastInterface` is not specified, the operating system will attempt to +drop membership on all valid interfaces. + +### socket.getRecvBufferSize(size) diff --git a/lib/dgram.js b/lib/dgram.js index cc61d4d274eea1..f526a6616cdff8 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -832,6 +832,53 @@ Socket.prototype.dropMembership = function(multicastAddress, } }; +Socket.prototype.addSourceSpecificMembership = function(sourceAddress, + groupAddress, + interfaceAddress) { + this._healthCheck(); + + if (typeof sourceAddress !== 'string') { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'sourceAddress', + 'string'); + } + + if (typeof groupAddress !== 'string') { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'groupAddress', + 'string'); + } + + const err = this._handle.addSourceSpecificMembership(sourceAddress, + groupAddress, + interfaceAddress); + if (err) { + throw errnoException(err, 'addSourceSpecificMembership'); + } +}; + + +Socket.prototype.dropSourceSpecificMembership = function(sourceAddress, + groupAddress, + interfaceAddress) { + this._healthCheck(); + + if (typeof sourceAddress !== 'string') { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'sourceAddress', + 'string'); + } + + if (typeof groupAddress !== 'string') { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'groupAddress', + 'string'); + } + + const err = this._handle.dropSourceSpecificMembership(sourceAddress, + groupAddress, + interfaceAddress); + if (err) { + throw errnoException(err, 'dropSourceSpecificMembership'); + } +}; + function healthCheck(socket) { if (!socket[kStateSymbol].handle) { diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index 64c4c8b304fc5f..46d432e86708f3 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -128,6 +128,10 @@ void UDPWrap::Initialize(Local target, GetSockOrPeerName); env->SetProtoMethod(t, "addMembership", AddMembership); env->SetProtoMethod(t, "dropMembership", DropMembership); + env->SetProtoMethod(t, "addSourceSpecificMembership", + AddSourceSpecificMembership); + env->SetProtoMethod(t, "dropSourceSpecificMembership", + DropSourceSpecificMembership); env->SetProtoMethod(t, "setMulticastInterface", SetMulticastInterface); env->SetProtoMethod(t, "setMulticastTTL", SetMulticastTTL); env->SetProtoMethod(t, "setMulticastLoopback", SetMulticastLoopback); @@ -397,6 +401,43 @@ void UDPWrap::DropMembership(const FunctionCallbackInfo& args) { SetMembership(args, UV_LEAVE_GROUP); } +void UDPWrap::SetSourceMembership(const FunctionCallbackInfo& args, + uv_membership membership) { + UDPWrap* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, + args.Holder(), + args.GetReturnValue().Set(UV_EBADF)); + + CHECK_EQ(args.Length(), 3); + + node::Utf8Value source_address(args.GetIsolate(), args[0]); + node::Utf8Value group_address(args.GetIsolate(), args[1]); + node::Utf8Value iface(args.GetIsolate(), args[2]); + + const char* iface_cstr = *iface; + if (args[1]->IsUndefined() || args[1]->IsNull()) { + iface_cstr = nullptr; + } + + int err = uv_udp_set_source_membership(&wrap->handle_, + *group_address, + iface_cstr, + *source_address, + membership); + args.GetReturnValue().Set(err); +} + +void UDPWrap::AddSourceSpecificMembership( + const FunctionCallbackInfo& args) { + SetSourceMembership(args, UV_JOIN_GROUP); +} + + +void UDPWrap::DropSourceSpecificMembership( + const FunctionCallbackInfo& args) { + SetSourceMembership(args, UV_LEAVE_GROUP); +} + void UDPWrap::DoSend(const FunctionCallbackInfo& args, int family) { Environment* env = Environment::GetCurrent(args); diff --git a/src/udp_wrap.h b/src/udp_wrap.h index fb2a9362cdf2bc..f79fdd9109fd0d 100644 --- a/src/udp_wrap.h +++ b/src/udp_wrap.h @@ -55,6 +55,10 @@ class UDPWrap: public HandleWrap { static void RecvStop(const v8::FunctionCallbackInfo& args); static void AddMembership(const v8::FunctionCallbackInfo& args); static void DropMembership(const v8::FunctionCallbackInfo& args); + static void AddSourceSpecificMembership( + const v8::FunctionCallbackInfo& args); + static void DropSourceSpecificMembership( + const v8::FunctionCallbackInfo& args); static void SetMulticastInterface( const v8::FunctionCallbackInfo& args); static void SetMulticastTTL(const v8::FunctionCallbackInfo& args); @@ -88,6 +92,9 @@ class UDPWrap: public HandleWrap { int family); static void SetMembership(const v8::FunctionCallbackInfo& args, uv_membership membership); + static void SetSourceMembership( + const v8::FunctionCallbackInfo& args, + uv_membership membership); static void OnAlloc(uv_handle_t* handle, size_t suggested_size, diff --git a/test/internet/test-dgram-multicast-ssm-multi-process.js b/test/internet/test-dgram-multicast-ssm-multi-process.js new file mode 100644 index 00000000000000..067584b74c85f6 --- /dev/null +++ b/test/internet/test-dgram-multicast-ssm-multi-process.js @@ -0,0 +1,230 @@ +'use strict'; +const common = require('../common'); +// Skip test in FreeBSD jails. +if (common.inFreeBSDJail) + common.skip('In a FreeBSD jail'); + +const assert = require('assert'); +const dgram = require('dgram'); +const fork = require('child_process').fork; +const networkInterfaces = require('os').networkInterfaces(); +const GROUP_ADDRESS = '232.1.1.1'; +const TIMEOUT = common.platformTimeout(5000); +const messages = [ + Buffer.from('First message to send'), + Buffer.from('Second message to send'), + Buffer.from('Third message to send'), + Buffer.from('Fourth message to send') +]; +const workers = {}; +const listeners = 3; +let listening, sendSocket, done, timer, dead; + +let sourceAddress = null; + +// Take the first non-internal interface as the IPv4 address for binding. +// Ideally, this should favor internal/private interfaces. +get_sourceAddress: for (const name in networkInterfaces) { + const interfaces = networkInterfaces[name]; + for (let i = 0; i < interfaces.length; i++) { + const localInterface = interfaces[i]; + if (!localInterface.internal && localInterface.family === 'IPv4') { + sourceAddress = localInterface.address; + break get_sourceAddress; + } + } +} +assert.ok(sourceAddress); + +function launchChildProcess() { + const worker = fork(__filename, ['child']); + workers[worker.pid] = worker; + + worker.messagesReceived = []; + + // Handle the death of workers. + worker.on('exit', function(code) { + // Don't consider this the true death if the worker has finished + // successfully or if the exit code is 0. + if (worker.isDone || code === 0) { + return; + } + + dead += 1; + console.error('[PARENT] Worker %d died. %d dead of %d', + worker.pid, + dead, + listeners); + + if (dead === listeners) { + console.error('[PARENT] All workers have died.'); + console.error('[PARENT] Fail'); + assert.fail(); + } + }); + + worker.on('message', function(msg) { + if (msg.listening) { + listening += 1; + + if (listening === listeners) { + // All child process are listening, so start sending. + sendSocket.sendNext(); + } + return; + } + if (msg.message) { + worker.messagesReceived.push(msg.message); + + if (worker.messagesReceived.length === messages.length) { + done += 1; + worker.isDone = true; + console.error('[PARENT] %d received %d messages total.', + worker.pid, + worker.messagesReceived.length); + } + + if (done === listeners) { + console.error('[PARENT] All workers have received the ' + + 'required number of messages. Will now compare.'); + + Object.keys(workers).forEach(function(pid) { + const worker = workers[pid]; + + let count = 0; + + worker.messagesReceived.forEach(function(buf) { + for (let i = 0; i < messages.length; ++i) { + if (buf.toString() === messages[i].toString()) { + count++; + break; + } + } + }); + + console.error('[PARENT] %d received %d matching messages.', + worker.pid, count); + + assert.strictEqual(count, messages.length, + 'A worker received an invalid multicast message'); + }); + + clearTimeout(timer); + console.error('[PARENT] Success'); + killChildren(workers); + } + } + }); +} + +function killChildren(children) { + Object.keys(children).forEach(function(key) { + const child = children[key]; + child.kill(); + }); +} + +if (process.argv[2] !== 'child') { + listening = 0; + dead = 0; + let i = 0; + done = 0; + + // Exit the test if it doesn't succeed within TIMEOUT. + timer = setTimeout(function() { + console.error('[PARENT] Responses were not received within %d ms.', + TIMEOUT); + console.error('[PARENT] Fail'); + + killChildren(workers); + + assert.fail(); + }, TIMEOUT); + + // Launch child processes. + for (let x = 0; x < listeners; x++) { + launchChildProcess(x); + } + + sendSocket = dgram.createSocket('udp4'); + + // The socket is actually created async now. + sendSocket.on('listening', function() { + sendSocket.setTTL(1); + sendSocket.setBroadcast(true); + sendSocket.setMulticastTTL(1); + sendSocket.setMulticastLoopback(true); + sendSocket.addSourceSpecificMembership(sourceAddress, GROUP_ADDRESS); + }); + + sendSocket.on('close', function() { + console.error('[PARENT] sendSocket closed'); + }); + + sendSocket.sendNext = function() { + const buf = messages[i++]; + + if (!buf) { + try { sendSocket.close(); } catch (e) {} + return; + } + + sendSocket.send( + buf, + 0, + buf.length, + common.PORT, + GROUP_ADDRESS, + function(err) { + assert.ifError(err); + console.error('[PARENT] sent "%s" to %s:%s', + buf.toString(), + GROUP_ADDRESS, common.PORT); + process.nextTick(sendSocket.sendNext); + } + ); + }; +} + +if (process.argv[2] === 'child') { + const receivedMessages = []; + const listenSocket = dgram.createSocket({ + type: 'udp4', + reuseAddr: true + }); + + listenSocket.on('listening', function() { + listenSocket.setMulticastLoopback(true); + listenSocket.addSourceSpecificMembership(sourceAddress, GROUP_ADDRESS); + + listenSocket.on('message', function(buf, rinfo) { + console.error('[CHILD] %s received "%s" from %j', process.pid, + buf.toString(), rinfo); + + receivedMessages.push(buf); + + process.send({ message: buf.toString() }); + + if (receivedMessages.length === messages.length) { + // .dropSourceSpecificMembership() not strictly needed, + // it is here as a sanity check. + listenSocket.dropSourceSpecificMembership(sourceAddress, GROUP_ADDRESS); + process.nextTick(function() { + listenSocket.close(); + }); + } + }); + + listenSocket.on('close', function() { + // HACK: Wait to exit the process to ensure that the parent + // process has had time to receive all messages via process.send() + // This may be indicative of some other issue. + setTimeout(function() { + process.exit(); + }, common.platformTimeout(1000)); + }); + process.send({ listening: true }); + }); + + listenSocket.bind(common.PORT); +} diff --git a/test/internet/test-dgram-multicast-ssmv6-multi-process.js b/test/internet/test-dgram-multicast-ssmv6-multi-process.js new file mode 100644 index 00000000000000..4319de6ea1e405 --- /dev/null +++ b/test/internet/test-dgram-multicast-ssmv6-multi-process.js @@ -0,0 +1,230 @@ +'use strict'; +const common = require('../common'); +// Skip test in FreeBSD jails. +if (common.inFreeBSDJail) + common.skip('In a FreeBSD jail'); + +const assert = require('assert'); +const dgram = require('dgram'); +const fork = require('child_process').fork; +const networkInterfaces = require('os').networkInterfaces(); +const GROUP_ADDRESS = 'ff3e::1234'; +const TIMEOUT = common.platformTimeout(5000); +const messages = [ + Buffer.from('First message to send'), + Buffer.from('Second message to send'), + Buffer.from('Third message to send'), + Buffer.from('Fourth message to send') +]; +const workers = {}; +const listeners = 3; +let listening, sendSocket, done, timer, dead; + +let sourceAddress = null; + +// Take the first non-internal interface as the IPv6 address for binding. +// Ideally, this should check favor internal/private interfaces. +get_sourceAddress: for (const name in networkInterfaces) { + const interfaces = networkInterfaces[name]; + for (let i = 0; i < interfaces.length; i++) { + const localInterface = interfaces[i]; + if (!localInterface.internal && localInterface.family === 'IPv6') { + sourceAddress = localInterface.address; + break get_sourceAddress; + } + } +} +assert.ok(sourceAddress); + +function launchChildProcess() { + const worker = fork(__filename, ['child']); + workers[worker.pid] = worker; + + worker.messagesReceived = []; + + // Handle the death of workers. + worker.on('exit', function(code) { + // Don't consider this the true death if the worker has finished + // successfully or if the exit code is 0. + if (worker.isDone || code === 0) { + return; + } + + dead += 1; + console.error('[PARENT] Worker %d died. %d dead of %d', + worker.pid, + dead, + listeners); + + if (dead === listeners) { + console.error('[PARENT] All workers have died.'); + console.error('[PARENT] Fail'); + assert.fail(); + } + }); + + worker.on('message', function(msg) { + if (msg.listening) { + listening += 1; + + if (listening === listeners) { + // All child process are listening, so start sending. + sendSocket.sendNext(); + } + return; + } + if (msg.message) { + worker.messagesReceived.push(msg.message); + + if (worker.messagesReceived.length === messages.length) { + done += 1; + worker.isDone = true; + console.error('[PARENT] %d received %d messages total.', + worker.pid, + worker.messagesReceived.length); + } + + if (done === listeners) { + console.error('[PARENT] All workers have received the ' + + 'required number of messages. Will now compare.'); + + Object.keys(workers).forEach(function(pid) { + const worker = workers[pid]; + + let count = 0; + + worker.messagesReceived.forEach(function(buf) { + for (let i = 0; i < messages.length; ++i) { + if (buf.toString() === messages[i].toString()) { + count++; + break; + } + } + }); + + console.error('[PARENT] %d received %d matching messages.', + worker.pid, count); + + assert.strictEqual(count, messages.length, + 'A worker received an invalid multicast message'); + }); + + clearTimeout(timer); + console.error('[PARENT] Success'); + killChildren(workers); + } + } + }); +} + +function killChildren(children) { + Object.keys(children).forEach(function(key) { + const child = children[key]; + child.kill(); + }); +} + +if (process.argv[2] !== 'child') { + listening = 0; + dead = 0; + let i = 0; + done = 0; + + // Exit the test if it doesn't succeed within TIMEOUT. + timer = setTimeout(function() { + console.error('[PARENT] Responses were not received within %d ms.', + TIMEOUT); + console.error('[PARENT] Fail'); + + killChildren(workers); + + assert.fail(); + }, TIMEOUT); + + // Launch child processes. + for (let x = 0; x < listeners; x++) { + launchChildProcess(x); + } + + sendSocket = dgram.createSocket('udp6'); + + // The socket is actually created async now. + sendSocket.on('listening', function() { + sendSocket.setTTL(1); + sendSocket.setBroadcast(true); + sendSocket.setMulticastTTL(1); + sendSocket.setMulticastLoopback(true); + sendSocket.addSourceSpecificMembership(sourceAddress, GROUP_ADDRESS); + }); + + sendSocket.on('close', function() { + console.error('[PARENT] sendSocket closed'); + }); + + sendSocket.sendNext = function() { + const buf = messages[i++]; + + if (!buf) { + try { sendSocket.close(); } catch (e) {} + return; + } + + sendSocket.send( + buf, + 0, + buf.length, + common.PORT, + GROUP_ADDRESS, + function(err) { + assert.ifError(err); + console.error('[PARENT] sent "%s" to %s:%s', + buf.toString(), + GROUP_ADDRESS, common.PORT); + process.nextTick(sendSocket.sendNext); + } + ); + }; +} + +if (process.argv[2] === 'child') { + const receivedMessages = []; + const listenSocket = dgram.createSocket({ + type: 'udp6', + reuseAddr: true + }); + + listenSocket.on('listening', function() { + listenSocket.setMulticastLoopback(true); + listenSocket.addSourceSpecificMembership(sourceAddress, GROUP_ADDRESS); + + listenSocket.on('message', function(buf, rinfo) { + console.error('[CHILD] %s received "%s" from %j', process.pid, + buf.toString(), rinfo); + + receivedMessages.push(buf); + + process.send({ message: buf.toString() }); + + if (receivedMessages.length === messages.length) { + // .dropSourceSpecificMembership() not strictly needed, + // it is here as a sanity check. + listenSocket.dropSourceSpecificMembership(sourceAddress, GROUP_ADDRESS); + process.nextTick(function() { + listenSocket.close(); + }); + } + }); + + listenSocket.on('close', function() { + // HACK: Wait to exit the process to ensure that the parent + // process has had time to receive all messages via process.send() + // This may be indicative of some other issue. + setTimeout(function() { + process.exit(); + }, common.platformTimeout(1000)); + }); + process.send({ listening: true }); + }); + + listenSocket.bind(common.PORT); +} From b36f53c6fccdcdec5f47ab1abc5926a16fd01d95 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Mon, 14 Oct 2019 18:15:12 -0700 Subject: [PATCH 2/9] Update dgram.md --- doc/api/dgram.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/dgram.md b/doc/api/dgram.md index 9d7088e7a621f6..2cda70fa62bc13 100644 --- a/doc/api/dgram.md +++ b/doc/api/dgram.md @@ -330,7 +330,7 @@ reason to call this. If `multicastInterface` is not specified, the operating system will attempt to drop membership on all valid interfaces. -### socket.getRecvBufferSize(size) +### socket.getRecvBufferSize() From 6ea4a7b31875af5170ef0adec607487566c9e74f Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Mon, 14 Oct 2019 19:30:59 -0700 Subject: [PATCH 3/9] Update test-dgram-multicast-ssm-multi-process.js --- test/internet/test-dgram-multicast-ssm-multi-process.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/internet/test-dgram-multicast-ssm-multi-process.js b/test/internet/test-dgram-multicast-ssm-multi-process.js index 067584b74c85f6..1771a4204c137b 100644 --- a/test/internet/test-dgram-multicast-ssm-multi-process.js +++ b/test/internet/test-dgram-multicast-ssm-multi-process.js @@ -105,8 +105,7 @@ function launchChildProcess() { console.error('[PARENT] %d received %d matching messages.', worker.pid, count); - assert.strictEqual(count, messages.length, - 'A worker received an invalid multicast message'); + assert.strictEqual(count, messages.length); }); clearTimeout(timer); @@ -165,7 +164,7 @@ if (process.argv[2] !== 'child') { const buf = messages[i++]; if (!buf) { - try { sendSocket.close(); } catch (e) {} + try { sendSocket.close(); } catch {} return; } From 94f7449a8a6da608d8ecd58509c400103cede8b9 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Mon, 14 Oct 2019 19:59:43 -0700 Subject: [PATCH 4/9] Update test-dgram-multicast-ssmv6-multi-process.js --- test/internet/test-dgram-multicast-ssmv6-multi-process.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/internet/test-dgram-multicast-ssmv6-multi-process.js b/test/internet/test-dgram-multicast-ssmv6-multi-process.js index 4319de6ea1e405..7eccaac0777345 100644 --- a/test/internet/test-dgram-multicast-ssmv6-multi-process.js +++ b/test/internet/test-dgram-multicast-ssmv6-multi-process.js @@ -105,8 +105,7 @@ function launchChildProcess() { console.error('[PARENT] %d received %d matching messages.', worker.pid, count); - assert.strictEqual(count, messages.length, - 'A worker received an invalid multicast message'); + assert.strictEqual(count, messages.length); }); clearTimeout(timer); From f75fd4596feccd17434944140a56cf699ce4ad3f Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Mon, 14 Oct 2019 20:57:04 -0700 Subject: [PATCH 5/9] Update test-dgram-multicast-ssmv6-multi-process.js --- test/internet/test-dgram-multicast-ssmv6-multi-process.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/internet/test-dgram-multicast-ssmv6-multi-process.js b/test/internet/test-dgram-multicast-ssmv6-multi-process.js index 7eccaac0777345..88bb563364bfe3 100644 --- a/test/internet/test-dgram-multicast-ssmv6-multi-process.js +++ b/test/internet/test-dgram-multicast-ssmv6-multi-process.js @@ -164,7 +164,7 @@ if (process.argv[2] !== 'child') { const buf = messages[i++]; if (!buf) { - try { sendSocket.close(); } catch (e) {} + try { sendSocket.close(); } catch {} return; } From f2713577bb2ffc1e84d9f227e36e78cdfb072d33 Mon Sep 17 00:00:00 2001 From: Nicolas Thumann <46975855+n-thumann@users.noreply.github.com> Date: Fri, 11 Oct 2019 23:05:25 +0200 Subject: [PATCH 6/9] Check iface for null, not group_address --- src/udp_wrap.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index 46d432e86708f3..becc94f788a37a 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -415,7 +415,7 @@ void UDPWrap::SetSourceMembership(const FunctionCallbackInfo& args, node::Utf8Value iface(args.GetIsolate(), args[2]); const char* iface_cstr = *iface; - if (args[1]->IsUndefined() || args[1]->IsNull()) { + if (args[2]->IsUndefined() || args[2]->IsNull()) { iface_cstr = nullptr; } From 4392ebf9fd3f3cf0112cc912aa0eb4a271597c8f Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 16 Oct 2019 20:21:20 +0200 Subject: [PATCH 7/9] Check iface against nullptr --- src/udp_wrap.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index becc94f788a37a..4a66ce0a1f1018 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -414,9 +414,10 @@ void UDPWrap::SetSourceMembership(const FunctionCallbackInfo& args, node::Utf8Value group_address(args.GetIsolate(), args[1]); node::Utf8Value iface(args.GetIsolate(), args[2]); + if (*iface == nullptr) return; const char* iface_cstr = *iface; if (args[2]->IsUndefined() || args[2]->IsNull()) { - iface_cstr = nullptr; + iface_cstr = nullptr; } int err = uv_udp_set_source_membership(&wrap->handle_, From b049b3a3845265f43743fcb654e2aaed0ff65ae1 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 16 Oct 2019 20:35:58 +0200 Subject: [PATCH 8/9] Fix optional argument format in docs --- doc/api/dgram.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/dgram.md b/doc/api/dgram.md index 2cda70fa62bc13..894f64f36e135f 100644 --- a/doc/api/dgram.md +++ b/doc/api/dgram.md @@ -123,7 +123,7 @@ if (cluster.isMaster) { } ``` -### socket.addSourceSpecificMembership(sourceAddress, groupAddress[, multicastInterface]) +### socket.addSourceSpecificMembership(sourceAddress, groupAddress\[, multicastInterface\]) @@ -312,7 +312,7 @@ never have reason to call this. If `multicastInterface` is not specified, the operating system will attempt to drop membership on all valid interfaces. -### socket.dropSourceSpecificMembership(sourceAddress, groupAddress[, multicastInterface]) +### socket.dropSourceSpecificMembership(sourceAddress, groupAddress\[, multicastInterface\]) From 888dc05cee96c6ceba9e563d4000a635a36d47c1 Mon Sep 17 00:00:00 2001 From: Nicolas Thumann <46975855+n-thumann@users.noreply.github.com> Date: Wed, 16 Oct 2019 22:16:24 +0200 Subject: [PATCH 9/9] Fixes two deprecation warnings `this._healthCheck()`& `this._handle` caused deprecation warnings. --- lib/dgram.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/dgram.js b/lib/dgram.js index f526a6616cdff8..d29d1e7b190f3a 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -835,7 +835,7 @@ Socket.prototype.dropMembership = function(multicastAddress, Socket.prototype.addSourceSpecificMembership = function(sourceAddress, groupAddress, interfaceAddress) { - this._healthCheck(); + healthCheck(this); if (typeof sourceAddress !== 'string') { throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'sourceAddress', @@ -847,9 +847,10 @@ Socket.prototype.addSourceSpecificMembership = function(sourceAddress, 'string'); } - const err = this._handle.addSourceSpecificMembership(sourceAddress, - groupAddress, - interfaceAddress); + const err = + this[kStateSymbol].handle.addSourceSpecificMembership(sourceAddress, + groupAddress, + interfaceAddress); if (err) { throw errnoException(err, 'addSourceSpecificMembership'); } @@ -859,7 +860,7 @@ Socket.prototype.addSourceSpecificMembership = function(sourceAddress, Socket.prototype.dropSourceSpecificMembership = function(sourceAddress, groupAddress, interfaceAddress) { - this._healthCheck(); + healthCheck(this); if (typeof sourceAddress !== 'string') { throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'sourceAddress', @@ -871,9 +872,10 @@ Socket.prototype.dropSourceSpecificMembership = function(sourceAddress, 'string'); } - const err = this._handle.dropSourceSpecificMembership(sourceAddress, - groupAddress, - interfaceAddress); + const err = + this[kStateSymbol].handle.dropSourceSpecificMembership(sourceAddress, + groupAddress, + interfaceAddress); if (err) { throw errnoException(err, 'dropSourceSpecificMembership'); }