Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dgram: add source-specific multicast support #15735

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 33 additions & 0 deletions doc/api/dgram.md
Expand Up @@ -123,6 +123,21 @@ if (cluster.isMaster) {
}
```

### socket.addSourceSpecificMembership(sourceAddress, groupAddress\[, multicastInterface\])
<!-- YAML
added: REPLACEME
-->
* `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()
<!-- YAML
added: v0.1.99
Expand Down Expand Up @@ -297,6 +312,24 @@ 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\])
<!-- YAML
added: REPLACEME
-->

* `sourceAddress` {string}
* `groupAddress` {string}
* `multicastInterface` {string}
addaleax marked this conversation as resolved.
Show resolved Hide resolved

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()
<!-- YAML
added: v8.7.0
Expand Down
49 changes: 49 additions & 0 deletions lib/dgram.js
Expand Up @@ -832,6 +832,55 @@ Socket.prototype.dropMembership = function(multicastAddress,
}
};

Socket.prototype.addSourceSpecificMembership = function(sourceAddress,
groupAddress,
interfaceAddress) {
healthCheck(this);

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[kStateSymbol].handle.addSourceSpecificMembership(sourceAddress,
groupAddress,
interfaceAddress);
if (err) {
throw errnoException(err, 'addSourceSpecificMembership');
}
};


Socket.prototype.dropSourceSpecificMembership = function(sourceAddress,
groupAddress,
interfaceAddress) {
healthCheck(this);

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[kStateSymbol].handle.dropSourceSpecificMembership(sourceAddress,
groupAddress,
interfaceAddress);
if (err) {
throw errnoException(err, 'dropSourceSpecificMembership');
}
};


function healthCheck(socket) {
if (!socket[kStateSymbol].handle) {
Expand Down
42 changes: 42 additions & 0 deletions src/udp_wrap.cc
Expand Up @@ -128,6 +128,10 @@ void UDPWrap::Initialize(Local<Object> target,
GetSockOrPeerName<UDPWrap, uv_udp_getsockname>);
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);
Expand Down Expand Up @@ -397,6 +401,44 @@ void UDPWrap::DropMembership(const FunctionCallbackInfo<Value>& args) {
SetMembership(args, UV_LEAVE_GROUP);
}

void UDPWrap::SetSourceMembership(const FunctionCallbackInfo<Value>& 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]);

if (*iface == nullptr) return;
const char* iface_cstr = *iface;
addaleax marked this conversation as resolved.
Show resolved Hide resolved
if (args[2]->IsUndefined() || args[2]->IsNull()) {
addaleax marked this conversation as resolved.
Show resolved Hide resolved
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<Value>& args) {
SetSourceMembership(args, UV_JOIN_GROUP);
}


void UDPWrap::DropSourceSpecificMembership(
const FunctionCallbackInfo<Value>& args) {
SetSourceMembership(args, UV_LEAVE_GROUP);
}


void UDPWrap::DoSend(const FunctionCallbackInfo<Value>& args, int family) {
Environment* env = Environment::GetCurrent(args);
Expand Down
7 changes: 7 additions & 0 deletions src/udp_wrap.h
Expand Up @@ -55,6 +55,10 @@ class UDPWrap: public HandleWrap {
static void RecvStop(const v8::FunctionCallbackInfo<v8::Value>& args);
static void AddMembership(const v8::FunctionCallbackInfo<v8::Value>& args);
static void DropMembership(const v8::FunctionCallbackInfo<v8::Value>& args);
static void AddSourceSpecificMembership(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void DropSourceSpecificMembership(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetMulticastInterface(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetMulticastTTL(const v8::FunctionCallbackInfo<v8::Value>& args);
Expand Down Expand Up @@ -88,6 +92,9 @@ class UDPWrap: public HandleWrap {
int family);
static void SetMembership(const v8::FunctionCallbackInfo<v8::Value>& args,
uv_membership membership);
static void SetSourceMembership(
const v8::FunctionCallbackInfo<v8::Value>& args,
uv_membership membership);

static void OnAlloc(uv_handle_t* handle,
size_t suggested_size,
Expand Down