net: make net.BlockList cloneable

Signed-off-by: James M Snell <jasnell@gmail.com>

PR-URL: https://github.com/nodejs/node/pull/37917
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
James M Snell 2021-03-25 10:36:30 -07:00 committed by Myles Borins
parent ae70aa3c63
commit a4169ce519
No known key found for this signature in database
GPG Key ID: 933B01F40B5CA946
9 changed files with 247 additions and 55 deletions

View File

@ -527,6 +527,9 @@ are part of the channel.
<!-- YAML
added: v10.5.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/37917
description: Add 'BlockList' to the list of cloneable types.
- version: v15.9.0
pr-url: https://github.com/nodejs/node/pull/37155
description: Add 'Histogram' types to the list of cloneable types.
@ -569,6 +572,7 @@ In particular, the significant differences to `JSON` are:
* {Histogram}s,
* {KeyObject}s,
* {MessagePort}s,
* {net.BlockList}s,
* {X509Certificate}s.
```js

View File

@ -2,6 +2,7 @@
const {
Boolean,
ObjectSetPrototypeOf,
Symbol
} = primordials;
@ -14,26 +15,28 @@ const {
const {
customInspectSymbol: kInspect,
} = require('internal/util');
const {
JSTransferable,
kClone,
kDeserialize,
} = require('internal/worker/js_transferable');
const { inspect } = require('internal/util/inspect');
const kHandle = Symbol('kHandle');
const { owner_symbol } = internalBinding('symbols');
const {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
} = require('internal/errors').codes;
const { validateInt32, validateString } = require('internal/validators');
class BlockList {
constructor(handle = new BlockListHandle()) {
// The handle argument is an intentionally undocumented
// internal API. User code will not be able to create
// a BlockListHandle object directly.
if (!(handle instanceof BlockListHandle))
throw new ERR_INVALID_ARG_TYPE('handle', 'BlockListHandle', handle);
this[kHandle] = handle;
class BlockList extends JSTransferable {
constructor() {
super();
this[kHandle] = new BlockListHandle();
this[kHandle][owner_symbol] = this;
}
@ -107,6 +110,34 @@ class BlockList {
get rules() {
return this[kHandle].getRules();
}
[kClone]() {
const handle = this[kHandle];
return {
data: { handle },
deserializeInfo: 'internal/blocklist:InternalBlockList',
};
}
[kDeserialize]({ handle }) {
this[kHandle] = handle;
this[kHandle][owner_symbol] = this;
}
}
module.exports = BlockList;
class InternalBlockList extends JSTransferable {
constructor(handle) {
super();
this[kHandle] = handle;
if (handle !== undefined)
handle[owner_symbol] = this;
}
}
InternalBlockList.prototype.constructor = BlockList.prototype.constructor;
ObjectSetPrototypeOf(InternalBlockList.prototype, BlockList.prototype);
module.exports = {
BlockList,
InternalBlockList,
};

View File

@ -1748,8 +1748,7 @@ module.exports = {
_normalizeArgs: normalizeArgs,
_setSimultaneousAccepts,
get BlockList() {
if (BlockList === undefined)
BlockList = require('internal/blocklist');
BlockList ??= require('internal/blocklist').BlockList;
return BlockList;
},
connect,

View File

@ -1035,15 +1035,18 @@ inline void Environment::SetInstanceMethod(v8::Local<v8::FunctionTemplate> that,
inline void Environment::SetConstructorFunction(
v8::Local<v8::Object> that,
const char* name,
v8::Local<v8::FunctionTemplate> tmpl) {
SetConstructorFunction(that, OneByteString(isolate(), name), tmpl);
v8::Local<v8::FunctionTemplate> tmpl,
SetConstructorFunctionFlag flag) {
SetConstructorFunction(that, OneByteString(isolate(), name), tmpl, flag);
}
inline void Environment::SetConstructorFunction(
v8::Local<v8::Object> that,
v8::Local<v8::String> name,
v8::Local<v8::FunctionTemplate> tmpl) {
tmpl->SetClassName(name);
v8::Local<v8::FunctionTemplate> tmpl,
SetConstructorFunctionFlag flag) {
if (LIKELY(flag == SetConstructorFunctionFlag::SET_CLASS_NAME))
tmpl->SetClassName(name);
that->Set(
context(),
name,

View File

@ -442,7 +442,7 @@ constexpr size_t kFsStatsBufferLength =
V(base_object_ctor_template, v8::FunctionTemplate) \
V(binding_data_ctor_template, v8::FunctionTemplate) \
V(blob_constructor_template, v8::FunctionTemplate) \
V(blocklist_instance_template, v8::ObjectTemplate) \
V(blocklist_constructor_template, v8::FunctionTemplate) \
V(compiled_fn_entry_template, v8::ObjectTemplate) \
V(dir_instance_template, v8::ObjectTemplate) \
V(fd_constructor_template, v8::ObjectTemplate) \
@ -1231,13 +1231,22 @@ class Environment : public MemoryRetainer {
const char* name,
v8::FunctionCallback callback);
enum class SetConstructorFunctionFlag {
NONE,
SET_CLASS_NAME,
};
inline void SetConstructorFunction(v8::Local<v8::Object> that,
const char* name,
v8::Local<v8::FunctionTemplate> tmpl);
v8::Local<v8::FunctionTemplate> tmpl,
SetConstructorFunctionFlag flag =
SetConstructorFunctionFlag::SET_CLASS_NAME);
inline void SetConstructorFunction(v8::Local<v8::Object> that,
v8::Local<v8::String> name,
v8::Local<v8::FunctionTemplate> tmpl);
v8::Local<v8::FunctionTemplate> tmpl,
SetConstructorFunctionFlag flag =
SetConstructorFunctionFlag::SET_CLASS_NAME);
void AtExit(void (*cb)(void* arg), void* arg);
void RunAtExitCallbacks();

View File

@ -390,6 +390,7 @@ SocketAddressBlockList::SocketAddressBlockList(
void SocketAddressBlockList::AddSocketAddress(
const SocketAddress& address) {
Mutex::ScopedLock lock(mutex_);
std::unique_ptr<Rule> rule =
std::make_unique<SocketAddressRule>(address);
rules_.emplace_front(std::move(rule));
@ -398,6 +399,7 @@ void SocketAddressBlockList::AddSocketAddress(
void SocketAddressBlockList::RemoveSocketAddress(
const SocketAddress& address) {
Mutex::ScopedLock lock(mutex_);
auto it = address_rules_.find(address);
if (it != std::end(address_rules_)) {
rules_.erase(it->second);
@ -408,6 +410,7 @@ void SocketAddressBlockList::RemoveSocketAddress(
void SocketAddressBlockList::AddSocketAddressRange(
const SocketAddress& start,
const SocketAddress& end) {
Mutex::ScopedLock lock(mutex_);
std::unique_ptr<Rule> rule =
std::make_unique<SocketAddressRangeRule>(start, end);
rules_.emplace_front(std::move(rule));
@ -416,12 +419,14 @@ void SocketAddressBlockList::AddSocketAddressRange(
void SocketAddressBlockList::AddSocketAddressMask(
const SocketAddress& network,
int prefix) {
Mutex::ScopedLock lock(mutex_);
std::unique_ptr<Rule> rule =
std::make_unique<SocketAddressMaskRule>(network, prefix);
rules_.emplace_front(std::move(rule));
}
bool SocketAddressBlockList::Apply(const SocketAddress& address) {
Mutex::ScopedLock lock(mutex_);
for (const auto& rule : rules_) {
if (rule->Apply(address))
return true;
@ -488,14 +493,25 @@ std::string SocketAddressBlockList::SocketAddressMaskRule::ToString() {
}
MaybeLocal<Array> SocketAddressBlockList::ListRules(Environment* env) {
Mutex::ScopedLock lock(mutex_);
std::vector<Local<Value>> rules;
if (!ListRules(env, &rules))
return MaybeLocal<Array>();
return Array::New(env->isolate(), rules.data(), rules.size());
}
bool SocketAddressBlockList::ListRules(
Environment* env,
std::vector<v8::Local<v8::Value>>* rules) {
if (parent_ && !parent_->ListRules(env, rules))
return false;
for (const auto& rule : rules_) {
Local<Value> str;
if (!rule->ToV8String(env).ToLocal(&str))
return MaybeLocal<Array>();
rules.push_back(str);
return false;
rules->push_back(str);
}
return Array::New(env->isolate(), rules.data(), rules.size());
return true;
}
void SocketAddressBlockList::MemoryInfo(node::MemoryTracker* tracker) const {
@ -519,20 +535,42 @@ void SocketAddressBlockList::SocketAddressMaskRule::MemoryInfo(
}
SocketAddressBlockListWrap::SocketAddressBlockListWrap(
Environment* env, Local<Object> wrap)
: BaseObject(env, wrap) {
Environment* env,
Local<Object> wrap,
std::shared_ptr<SocketAddressBlockList> blocklist)
: BaseObject(env, wrap),
blocklist_(std::move(blocklist)) {
MakeWeak();
}
BaseObjectPtr<SocketAddressBlockListWrap> SocketAddressBlockListWrap::New(
Environment* env) {
Local<Object> obj;
if (!env->blocklist_instance_template()
if (!env->blocklist_constructor_template()
->InstanceTemplate()
->NewInstance(env->context()).ToLocal(&obj)) {
return {};
return BaseObjectPtr<SocketAddressBlockListWrap>();
}
BaseObjectPtr<SocketAddressBlockListWrap> wrap =
MakeDetachedBaseObject<SocketAddressBlockListWrap>(env, obj);
MakeBaseObject<SocketAddressBlockListWrap>(env, obj);
CHECK(wrap);
return wrap;
}
BaseObjectPtr<SocketAddressBlockListWrap> SocketAddressBlockListWrap::New(
Environment* env,
std::shared_ptr<SocketAddressBlockList> blocklist) {
Local<Object> obj;
if (!env->blocklist_constructor_template()
->InstanceTemplate()
->NewInstance(env->context()).ToLocal(&obj)) {
return BaseObjectPtr<SocketAddressBlockListWrap>();
}
BaseObjectPtr<SocketAddressBlockListWrap> wrap =
MakeBaseObject<SocketAddressBlockListWrap>(
env,
obj,
std::move(blocklist));
CHECK(wrap);
return wrap;
}
@ -562,7 +600,7 @@ void SocketAddressBlockListWrap::AddAddress(
if (!SocketAddress::ToSockAddr(family, *value, 0, &address))
return;
wrap->AddSocketAddress(
wrap->blocklist_->AddSocketAddress(
SocketAddress(reinterpret_cast<const sockaddr*>(&address)));
args.GetReturnValue().Set(true);
@ -597,7 +635,7 @@ void SocketAddressBlockListWrap::AddRange(
if (start_addr > end_addr)
return args.GetReturnValue().Set(false);
wrap->AddSocketAddressRange(start_addr, end_addr);
wrap->blocklist_->AddSocketAddressRange(start_addr, end_addr);
args.GetReturnValue().Set(true);
}
@ -628,7 +666,7 @@ void SocketAddressBlockListWrap::AddSubnet(
CHECK_IMPLIES(family == AF_INET6, prefix <= 128);
CHECK_GE(prefix, 0);
wrap->AddSocketAddressMask(
wrap->blocklist_->AddSocketAddressMask(
SocketAddress(reinterpret_cast<const sockaddr*>(&address)),
prefix);
@ -654,7 +692,8 @@ void SocketAddressBlockListWrap::Check(
return;
args.GetReturnValue().Set(
wrap->Apply(SocketAddress(reinterpret_cast<const sockaddr*>(&address))));
wrap->blocklist_->Apply(
SocketAddress(reinterpret_cast<const sockaddr*>(&address))));
}
void SocketAddressBlockListWrap::GetRules(
@ -663,10 +702,43 @@ void SocketAddressBlockListWrap::GetRules(
SocketAddressBlockListWrap* wrap;
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
Local<Array> rules;
if (wrap->ListRules(env).ToLocal(&rules))
if (wrap->blocklist_->ListRules(env).ToLocal(&rules))
args.GetReturnValue().Set(rules);
}
void SocketAddressBlockListWrap::MemoryInfo(MemoryTracker* tracker) const {
blocklist_->MemoryInfo(tracker);
}
std::unique_ptr<worker::TransferData>
SocketAddressBlockListWrap::CloneForMessaging() const {
return std::make_unique<TransferData>(this);
}
bool SocketAddressBlockListWrap::HasInstance(
Environment* env,
Local<Value> value) {
return GetConstructorTemplate(env)->HasInstance(value);
}
Local<FunctionTemplate> SocketAddressBlockListWrap::GetConstructorTemplate(
Environment* env) {
Local<FunctionTemplate> tmpl = env->blocklist_constructor_template();
if (tmpl.IsEmpty()) {
tmpl = env->NewFunctionTemplate(SocketAddressBlockListWrap::New);
tmpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "BlockList"));
tmpl->Inherit(BaseObject::GetConstructorTemplate(env));
tmpl->InstanceTemplate()->SetInternalFieldCount(kInternalFieldCount);
env->SetProtoMethod(tmpl, "addAddress", AddAddress);
env->SetProtoMethod(tmpl, "addRange", AddRange);
env->SetProtoMethod(tmpl, "addSubnet", AddSubnet);
env->SetProtoMethod(tmpl, "check", Check);
env->SetProtoMethod(tmpl, "getRules", GetRules);
env->set_blocklist_constructor_template(tmpl);
}
return tmpl;
}
void SocketAddressBlockListWrap::Initialize(
Local<Object> target,
Local<Value> unused,
@ -674,23 +746,28 @@ void SocketAddressBlockListWrap::Initialize(
void* priv) {
Environment* env = Environment::GetCurrent(context);
Local<FunctionTemplate> t =
env->NewFunctionTemplate(SocketAddressBlockListWrap::New);
t->InstanceTemplate()->SetInternalFieldCount(BaseObject::kInternalFieldCount);
env->SetProtoMethod(t, "addAddress", SocketAddressBlockListWrap::AddAddress);
env->SetProtoMethod(t, "addRange", SocketAddressBlockListWrap::AddRange);
env->SetProtoMethod(t, "addSubnet", SocketAddressBlockListWrap::AddSubnet);
env->SetProtoMethod(t, "check", SocketAddressBlockListWrap::Check);
env->SetProtoMethod(t, "getRules", SocketAddressBlockListWrap::GetRules);
env->set_blocklist_instance_template(t->InstanceTemplate());
env->SetConstructorFunction(target, "BlockList", t);
env->SetConstructorFunction(
target,
"BlockList",
GetConstructorTemplate(env),
Environment::SetConstructorFunctionFlag::NONE);
NODE_DEFINE_CONSTANT(target, AF_INET);
NODE_DEFINE_CONSTANT(target, AF_INET6);
}
BaseObjectPtr<BaseObject> SocketAddressBlockListWrap::TransferData::Deserialize(
Environment* env,
Local<Context> context,
std::unique_ptr<worker::TransferData> self) {
return New(env, std::move(blocklist_));
}
void SocketAddressBlockListWrap::TransferData::MemoryInfo(
MemoryTracker* tracker) const {
blocklist_->MemoryInfo(tracker);
}
} // namespace node
NODE_MODULE_CONTEXT_AWARE_INTERNAL(

View File

@ -7,6 +7,7 @@
#include "memory_tracker.h"
#include "base_object.h"
#include "node.h"
#include "node_worker.h"
#include "uv.h"
#include "v8.h"
@ -267,21 +268,32 @@ class SocketAddressBlockList : public MemoryRetainer {
SET_SELF_SIZE(SocketAddressBlockList)
private:
bool ListRules(
Environment* env,
std::vector<v8::Local<v8::Value>>* vec);
std::shared_ptr<SocketAddressBlockList> parent_;
std::list<std::unique_ptr<Rule>> rules_;
SocketAddress::Map<std::list<std::unique_ptr<Rule>>::iterator> address_rules_;
Mutex mutex_;
};
class SocketAddressBlockListWrap :
public BaseObject,
public SocketAddressBlockList {
class SocketAddressBlockListWrap : public BaseObject {
public:
static bool HasInstance(Environment* env, v8::Local<v8::Value> value);
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
Environment* env);
static void Initialize(v8::Local<v8::Object> target,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv);
static BaseObjectPtr<SocketAddressBlockListWrap> New(Environment* env);
static BaseObjectPtr<SocketAddressBlockListWrap> New(
Environment* env,
std::shared_ptr<SocketAddressBlockList> blocklist);
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static void AddAddress(const v8::FunctionCallbackInfo<v8::Value>& args);
static void AddRange(const v8::FunctionCallbackInfo<v8::Value>& args);
@ -291,13 +303,43 @@ class SocketAddressBlockListWrap :
SocketAddressBlockListWrap(
Environment* env,
v8::Local<v8::Object> wrap);
v8::Local<v8::Object> wrap,
std::shared_ptr<SocketAddressBlockList> blocklist =
std::make_shared<SocketAddressBlockList>());
void MemoryInfo(node::MemoryTracker* tracker) const override {
SocketAddressBlockList::MemoryInfo(tracker);
}
void MemoryInfo(node::MemoryTracker* tracker) const override;
SET_MEMORY_INFO_NAME(SocketAddressBlockListWrap)
SET_SELF_SIZE(SocketAddressBlockListWrap)
TransferMode GetTransferMode() const override {
return TransferMode::kCloneable;
}
std::unique_ptr<worker::TransferData> CloneForMessaging() const override;
class TransferData : public worker::TransferData {
public:
inline explicit TransferData(const SocketAddressBlockListWrap* wrap)
: blocklist_(wrap->blocklist_) {}
inline explicit TransferData(
std::shared_ptr<SocketAddressBlockList> blocklist)
: blocklist_(std::move(blocklist)) {}
BaseObjectPtr<BaseObject> Deserialize(
Environment* env,
v8::Local<v8::Context> context,
std::unique_ptr<worker::TransferData> self) override;
void MemoryInfo(MemoryTracker* tracker) const override;
SET_MEMORY_INFO_NAME(SocketAddressBlockListWrap::TransferData)
SET_SELF_SIZE(TransferData)
private:
std::shared_ptr<SocketAddressBlockList> blocklist_;
};
private:
std::shared_ptr<SocketAddressBlockList> blocklist_;
};
} // namespace node

View File

@ -0,0 +1,31 @@
'use strict';
const common = require('../common');
const {
BlockList,
} = require('net');
const {
ok,
notStrictEqual,
} = require('assert');
const blocklist = new BlockList();
blocklist.addAddress('123.123.123.123');
const mc = new MessageChannel();
mc.port1.onmessage = common.mustCall(({ data }) => {
notStrictEqual(data, blocklist);
ok(data.check('123.123.123.123'));
ok(!data.check('123.123.123.124'));
data.addAddress('123.123.123.124');
ok(blocklist.check('123.123.123.124'));
ok(data.check('123.123.123.124'));
mc.port1.close();
});
mc.port2.postMessage(blocklist);

View File

@ -137,10 +137,6 @@ const util = require('util');
assert(!blockList.check('8592:757c:efaf:2fff:ffff:ffff:ffff:ffff', 'ipv6'));
}
{
assert.throws(() => new BlockList('NOT BLOCK LIST HANDLE'), /ERR_INVALID_ARG_TYPE/);
}
{
const blockList = new BlockList();
assert.throws(() => blockList.addRange('1.1.1.2', '1.1.1.1'), /ERR_INVALID_ARG_VALUE/);