Skip to content

Commit a63f7e7

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 1d03df4 commit a63f7e7

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
@@ -832,6 +832,55 @@ Socket.prototype.dropMembership = function(multicastAddress,
832832
}
833833
};
834834

835+
Socket.prototype.addSourceSpecificMembership = function(sourceAddress,
836+
groupAddress,
837+
interfaceAddress) {
838+
healthCheck(this);
839+
840+
if (typeof sourceAddress !== 'string') {
841+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'sourceAddress',
842+
'string');
843+
}
844+
845+
if (typeof groupAddress !== 'string') {
846+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'groupAddress',
847+
'string');
848+
}
849+
850+
const err =
851+
this[kStateSymbol].handle.addSourceSpecificMembership(sourceAddress,
852+
groupAddress,
853+
interfaceAddress);
854+
if (err) {
855+
throw errnoException(err, 'addSourceSpecificMembership');
856+
}
857+
};
858+
859+
860+
Socket.prototype.dropSourceSpecificMembership = function(sourceAddress,
861+
groupAddress,
862+
interfaceAddress) {
863+
healthCheck(this);
864+
865+
if (typeof sourceAddress !== 'string') {
866+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'sourceAddress',
867+
'string');
868+
}
869+
870+
if (typeof groupAddress !== 'string') {
871+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'groupAddress',
872+
'string');
873+
}
874+
875+
const err =
876+
this[kStateSymbol].handle.dropSourceSpecificMembership(sourceAddress,
877+
groupAddress,
878+
interfaceAddress);
879+
if (err) {
880+
throw errnoException(err, 'dropSourceSpecificMembership');
881+
}
882+
};
883+
835884

836885
function healthCheck(socket) {
837886
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)