Skip to content

Commit 1aeca6d

Browse files
Lucas Parduen-thumannTrott
authored andcommitted
dgram: add source-specific multicast support
This adds RFC 4607 support for IPv4 and IPv6. Co-Authored-By: Nicolas Thumann <[email protected]> Co-Authored-By: Rich Trott <[email protected]> PR-URL: #15735 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 497b59b commit 1aeca6d

6 files changed

+589
-0
lines changed

doc/api/dgram.md

+33
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,21 @@ if (cluster.isMaster) {
123123
}
124124
```
125125

126+
### `socket.addSourceSpecificMembership(sourceAddress, groupAddress[, multicastInterface])`
127+
<!-- YAML
128+
added: REPLACEME
129+
-->
130+
* `sourceAddress` {string}
131+
* `groupAddress` {string}
132+
* `multicastInterface` {string}
133+
134+
Tells the kernel to join a source-specific multicast channel at the given
135+
`sourceAddress` and `groupAddress`, using the `multicastInterface` with the
136+
`IP_ADD_SOURCE_MEMBERSHIP` socket option. If the `multicastInterface` argument
137+
is not specified, the operating system will choose one interface and will add
138+
membership to it. To add membership to every available interface, call
139+
`socket.addSourceSpecificMembership()` multiple times, once per interface.
140+
126141
### `socket.address()`
127142
<!-- YAML
128143
added: v0.1.99
@@ -297,6 +312,24 @@ never have reason to call this.
297312
If `multicastInterface` is not specified, the operating system will attempt to
298313
drop membership on all valid interfaces.
299314

315+
### `socket.dropSourceSpecificMembership(sourceAddress, groupAddress[, multicastInterface])`
316+
<!-- YAML
317+
added: REPLACEME
318+
-->
319+
320+
* `sourceAddress` {string}
321+
* `groupAddress` {string}
322+
* `multicastInterface` {string}
323+
324+
Instructs the kernel to leave a source-specific multicast channel at the given
325+
`sourceAddress` and `groupAddress` using the `IP_DROP_SOURCE_MEMBERSHIP`
326+
socket option. This method is automatically called by the kernel when the
327+
socket is closed or the process terminates, so most apps will never have
328+
reason to call this.
329+
330+
If `multicastInterface` is not specified, the operating system will attempt to
331+
drop membership on all valid interfaces.
332+
300333
### `socket.getRecvBufferSize()`
301334
<!-- YAML
302335
added: v8.7.0

lib/dgram.js

+49
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,55 @@ Socket.prototype.dropMembership = function(multicastAddress,
845845
}
846846
};
847847

848+
Socket.prototype.addSourceSpecificMembership = function(sourceAddress,
849+
groupAddress,
850+
interfaceAddress) {
851+
healthCheck(this);
852+
853+
if (typeof sourceAddress !== 'string') {
854+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'sourceAddress',
855+
'string');
856+
}
857+
858+
if (typeof groupAddress !== 'string') {
859+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'groupAddress',
860+
'string');
861+
}
862+
863+
const err =
864+
this[kStateSymbol].handle.addSourceSpecificMembership(sourceAddress,
865+
groupAddress,
866+
interfaceAddress);
867+
if (err) {
868+
throw errnoException(err, 'addSourceSpecificMembership');
869+
}
870+
};
871+
872+
873+
Socket.prototype.dropSourceSpecificMembership = function(sourceAddress,
874+
groupAddress,
875+
interfaceAddress) {
876+
healthCheck(this);
877+
878+
if (typeof sourceAddress !== 'string') {
879+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'sourceAddress',
880+
'string');
881+
}
882+
883+
if (typeof groupAddress !== 'string') {
884+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'groupAddress',
885+
'string');
886+
}
887+
888+
const err =
889+
this[kStateSymbol].handle.dropSourceSpecificMembership(sourceAddress,
890+
groupAddress,
891+
interfaceAddress);
892+
if (err) {
893+
throw errnoException(err, 'dropSourceSpecificMembership');
894+
}
895+
};
896+
848897

849898
function healthCheck(socket) {
850899
if (!socket[kStateSymbol].handle) {

src/udp_wrap.cc

+42
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ void UDPWrap::Initialize(Local<Object> target,
128128
GetSockOrPeerName<UDPWrap, uv_udp_getsockname>);
129129
env->SetProtoMethod(t, "addMembership", AddMembership);
130130
env->SetProtoMethod(t, "dropMembership", DropMembership);
131+
env->SetProtoMethod(t, "addSourceSpecificMembership",
132+
AddSourceSpecificMembership);
133+
env->SetProtoMethod(t, "dropSourceSpecificMembership",
134+
DropSourceSpecificMembership);
131135
env->SetProtoMethod(t, "setMulticastInterface", SetMulticastInterface);
132136
env->SetProtoMethod(t, "setMulticastTTL", SetMulticastTTL);
133137
env->SetProtoMethod(t, "setMulticastLoopback", SetMulticastLoopback);
@@ -397,6 +401,44 @@ void UDPWrap::DropMembership(const FunctionCallbackInfo<Value>& args) {
397401
SetMembership(args, UV_LEAVE_GROUP);
398402
}
399403

404+
void UDPWrap::SetSourceMembership(const FunctionCallbackInfo<Value>& args,
405+
uv_membership membership) {
406+
UDPWrap* wrap;
407+
ASSIGN_OR_RETURN_UNWRAP(&wrap,
408+
args.Holder(),
409+
args.GetReturnValue().Set(UV_EBADF));
410+
411+
CHECK_EQ(args.Length(), 3);
412+
413+
node::Utf8Value source_address(args.GetIsolate(), args[0]);
414+
node::Utf8Value group_address(args.GetIsolate(), args[1]);
415+
node::Utf8Value iface(args.GetIsolate(), args[2]);
416+
417+
if (*iface == nullptr) return;
418+
const char* iface_cstr = *iface;
419+
if (args[2]->IsUndefined() || args[2]->IsNull()) {
420+
iface_cstr = nullptr;
421+
}
422+
423+
int err = uv_udp_set_source_membership(&wrap->handle_,
424+
*group_address,
425+
iface_cstr,
426+
*source_address,
427+
membership);
428+
args.GetReturnValue().Set(err);
429+
}
430+
431+
void UDPWrap::AddSourceSpecificMembership(
432+
const FunctionCallbackInfo<Value>& args) {
433+
SetSourceMembership(args, UV_JOIN_GROUP);
434+
}
435+
436+
437+
void UDPWrap::DropSourceSpecificMembership(
438+
const FunctionCallbackInfo<Value>& args) {
439+
SetSourceMembership(args, UV_LEAVE_GROUP);
440+
}
441+
400442

401443
void UDPWrap::DoSend(const FunctionCallbackInfo<Value>& args, int family) {
402444
Environment* env = Environment::GetCurrent(args);

src/udp_wrap.h

+7
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ class UDPWrap: public HandleWrap {
5555
static void RecvStop(const v8::FunctionCallbackInfo<v8::Value>& args);
5656
static void AddMembership(const v8::FunctionCallbackInfo<v8::Value>& args);
5757
static void DropMembership(const v8::FunctionCallbackInfo<v8::Value>& args);
58+
static void AddSourceSpecificMembership(
59+
const v8::FunctionCallbackInfo<v8::Value>& args);
60+
static void DropSourceSpecificMembership(
61+
const v8::FunctionCallbackInfo<v8::Value>& args);
5862
static void SetMulticastInterface(
5963
const v8::FunctionCallbackInfo<v8::Value>& args);
6064
static void SetMulticastTTL(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -88,6 +92,9 @@ class UDPWrap: public HandleWrap {
8892
int family);
8993
static void SetMembership(const v8::FunctionCallbackInfo<v8::Value>& args,
9094
uv_membership membership);
95+
static void SetSourceMembership(
96+
const v8::FunctionCallbackInfo<v8::Value>& args,
97+
uv_membership membership);
9198

9299
static void OnAlloc(uv_handle_t* handle,
93100
size_t suggested_size,

0 commit comments

Comments
 (0)