summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJames M Snell <jasnell@gmail.com>2020-08-04 16:03:30 -0700
committerMyles Borins <mylesborins@github.com>2021-08-31 10:54:59 -0400
commit87c71065ebe05fad9661317d7ff0215585c9889d (patch)
tree89fc5fe898ad089c6499cb8600c36c9d76b7dbac /src
parent1008c80176bd39990ab6e2ccae887833822d8068 (diff)
downloadnode-new-87c71065ebe05fad9661317d7ff0215585c9889d.tar.gz
net: introduce net.BlockList
`net.BlockList` provides an object intended to be used by net APIs to specify rules for disallowing network activity with specific IP addresses. This commit adds the basic mechanism but does not add the specific uses. PR-URL: https://github.com/nodejs/node/pull/34625 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/node_binding.cc1
-rw-r--r--src/node_sockaddr-inl.h28
-rw-r--r--src/node_sockaddr.cc592
-rw-r--r--src/node_sockaddr.h146
4 files changed, 765 insertions, 2 deletions
diff --git a/src/node_binding.cc b/src/node_binding.cc
index 8665b28275..d852a8f67e 100644
--- a/src/node_binding.cc
+++ b/src/node_binding.cc
@@ -37,6 +37,7 @@
// __attribute__((constructor)) like mechanism in GCC.
#define NODE_BUILTIN_STANDARD_MODULES(V) \
V(async_wrap) \
+ V(block_list) \
V(buffer) \
V(cares_wrap) \
V(config) \
diff --git a/src/node_sockaddr-inl.h b/src/node_sockaddr-inl.h
index c8b985aedd..e5d8985771 100644
--- a/src/node_sockaddr-inl.h
+++ b/src/node_sockaddr-inl.h
@@ -4,6 +4,7 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#include "node.h"
+#include "env-inl.h"
#include "node_internals.h"
#include "node_sockaddr.h"
#include "util-inl.h"
@@ -88,11 +89,11 @@ SocketAddress& SocketAddress::operator=(const SocketAddress& addr) {
}
const sockaddr& SocketAddress::operator*() const {
- return *this->data();
+ return *data();
}
const sockaddr* SocketAddress::operator->() const {
- return this->data();
+ return data();
}
size_t SocketAddress::length() const {
@@ -166,6 +167,24 @@ bool SocketAddress::operator!=(const SocketAddress& other) const {
return !(*this == other);
}
+bool SocketAddress::operator<(const SocketAddress& other) const {
+ return compare(other) == CompareResult::LESS_THAN;
+}
+
+bool SocketAddress::operator>(const SocketAddress& other) const {
+ return compare(other) == CompareResult::GREATER_THAN;
+}
+
+bool SocketAddress::operator<=(const SocketAddress& other) const {
+ CompareResult c = compare(other);
+ return c == CompareResult::NOT_COMPARABLE ? false :
+ c <= CompareResult::SAME;
+}
+
+bool SocketAddress::operator>=(const SocketAddress& other) const {
+ return compare(other) >= CompareResult::SAME;
+}
+
template <typename T>
SocketAddressLRU<T>::SocketAddressLRU(
size_t max_size)
@@ -231,6 +250,11 @@ typename T::Type* SocketAddressLRU<T>::Upsert(
return &map_[address]->second;
}
+v8::MaybeLocal<v8::Value> SocketAddressBlockList::Rule::ToV8String(
+ Environment* env) {
+ std::string str = ToString();
+ return ToV8Value(env->context(), str);
+}
} // namespace node
#endif // NODE_WANT_INTERNALS
diff --git a/src/node_sockaddr.cc b/src/node_sockaddr.cc
index 74fe123529..8ba82ff685 100644
--- a/src/node_sockaddr.cc
+++ b/src/node_sockaddr.cc
@@ -1,8 +1,25 @@
#include "node_sockaddr-inl.h" // NOLINT(build/include)
+#include "env-inl.h"
+#include "base_object-inl.h"
+#include "memory_tracker-inl.h"
#include "uv.h"
+#include <memory>
+#include <string>
+#include <vector>
+
namespace node {
+using v8::Array;
+using v8::Context;
+using v8::FunctionCallbackInfo;
+using v8::FunctionTemplate;
+using v8::Local;
+using v8::MaybeLocal;
+using v8::Object;
+using v8::String;
+using v8::Value;
+
namespace {
template <typename T, typename F>
SocketAddress FromUVHandle(F fn, const T& handle) {
@@ -92,4 +109,579 @@ SocketAddress SocketAddress::FromPeerName(const uv_udp_t& handle) {
return FromUVHandle(uv_udp_getpeername, handle);
}
+namespace {
+constexpr uint8_t mask[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
+
+bool is_match_ipv4(
+ const SocketAddress& one,
+ const SocketAddress& two) {
+ const sockaddr_in* one_in =
+ reinterpret_cast<const sockaddr_in*>(one.data());
+ const sockaddr_in* two_in =
+ reinterpret_cast<const sockaddr_in*>(two.data());
+ return memcmp(&one_in->sin_addr, &two_in->sin_addr, sizeof(uint32_t)) == 0;
+}
+
+bool is_match_ipv6(
+ const SocketAddress& one,
+ const SocketAddress& two) {
+ const sockaddr_in6* one_in =
+ reinterpret_cast<const sockaddr_in6*>(one.data());
+ const sockaddr_in6* two_in =
+ reinterpret_cast<const sockaddr_in6*>(two.data());
+ return memcmp(&one_in->sin6_addr, &two_in->sin6_addr, 16) == 0;
+}
+
+bool is_match_ipv4_ipv6(
+ const SocketAddress& ipv4,
+ const SocketAddress& ipv6) {
+ const sockaddr_in* check_ipv4 =
+ reinterpret_cast<const sockaddr_in*>(ipv4.data());
+ const sockaddr_in6* check_ipv6 =
+ reinterpret_cast<const sockaddr_in6*>(ipv6.data());
+
+ const uint8_t* ptr =
+ reinterpret_cast<const uint8_t*>(&check_ipv6->sin6_addr);
+
+ return memcmp(ptr, mask, sizeof(mask)) == 0 &&
+ memcmp(ptr + sizeof(mask),
+ &check_ipv4->sin_addr,
+ sizeof(uint32_t)) == 0;
+}
+
+SocketAddress::CompareResult compare_ipv4(
+ const SocketAddress& one,
+ const SocketAddress& two) {
+ const sockaddr_in* one_in =
+ reinterpret_cast<const sockaddr_in*>(one.data());
+ const sockaddr_in* two_in =
+ reinterpret_cast<const sockaddr_in*>(two.data());
+
+ if (one_in->sin_addr.s_addr < two_in->sin_addr.s_addr)
+ return SocketAddress::CompareResult::LESS_THAN;
+ else if (one_in->sin_addr.s_addr == two_in->sin_addr.s_addr)
+ return SocketAddress::CompareResult::SAME;
+ else
+ return SocketAddress::CompareResult::GREATER_THAN;
+}
+
+SocketAddress::CompareResult compare_ipv6(
+ const SocketAddress& one,
+ const SocketAddress& two) {
+ const sockaddr_in6* one_in =
+ reinterpret_cast<const sockaddr_in6*>(one.data());
+ const sockaddr_in6* two_in =
+ reinterpret_cast<const sockaddr_in6*>(two.data());
+ int ret = memcmp(&one_in->sin6_addr, &two_in->sin6_addr, 16);
+ if (ret < 0)
+ return SocketAddress::CompareResult::LESS_THAN;
+ else if (ret > 0)
+ return SocketAddress::CompareResult::GREATER_THAN;
+ return SocketAddress::CompareResult::SAME;
+}
+
+SocketAddress::CompareResult compare_ipv4_ipv6(
+ const SocketAddress& ipv4,
+ const SocketAddress& ipv6) {
+ const sockaddr_in* ipv4_in =
+ reinterpret_cast<const sockaddr_in*>(ipv4.data());
+ const sockaddr_in6 * ipv6_in =
+ reinterpret_cast<const sockaddr_in6*>(ipv6.data());
+
+ const uint8_t* ptr =
+ reinterpret_cast<const uint8_t*>(&ipv6_in->sin6_addr);
+
+ if (memcmp(ptr, mask, sizeof(mask)) != 0)
+ return SocketAddress::CompareResult::NOT_COMPARABLE;
+
+ int ret = memcmp(
+ &ipv4_in->sin_addr,
+ ptr + sizeof(mask),
+ sizeof(uint32_t));
+
+ if (ret < 0)
+ return SocketAddress::CompareResult::LESS_THAN;
+ else if (ret > 0)
+ return SocketAddress::CompareResult::GREATER_THAN;
+ return SocketAddress::CompareResult::SAME;
+}
+
+bool in_network_ipv4(
+ const SocketAddress& ip,
+ const SocketAddress& net,
+ int prefix) {
+ uint32_t mask = ((1 << prefix) - 1) << (32 - prefix);
+
+ const sockaddr_in* ip_in =
+ reinterpret_cast<const sockaddr_in*>(ip.data());
+ const sockaddr_in* net_in =
+ reinterpret_cast<const sockaddr_in*>(net.data());
+
+ return (htonl(ip_in->sin_addr.s_addr) & mask) ==
+ (htonl(net_in->sin_addr.s_addr) & mask);
+}
+
+bool in_network_ipv6(
+ const SocketAddress& ip,
+ const SocketAddress& net,
+ int prefix) {
+ // Special case, if prefix == 128, then just do a
+ // straight comparison.
+ if (prefix == 128)
+ return compare_ipv6(ip, net) == SocketAddress::CompareResult::SAME;
+
+ uint8_t r = prefix % 8;
+ int len = (prefix - r) / 8;
+ uint8_t mask = ((1 << r) - 1) << (8 - r);
+
+ const sockaddr_in6* ip_in =
+ reinterpret_cast<const sockaddr_in6*>(ip.data());
+ const sockaddr_in6* net_in =
+ reinterpret_cast<const sockaddr_in6*>(net.data());
+
+ if (memcmp(&ip_in->sin6_addr, &net_in->sin6_addr, len) != 0)
+ return false;
+
+ const uint8_t* p1 = reinterpret_cast<const uint8_t*>(
+ ip_in->sin6_addr.s6_addr);
+ const uint8_t* p2 = reinterpret_cast<const uint8_t*>(
+ net_in->sin6_addr.s6_addr);
+
+ return (p1[len] & mask) == (p2[len] & mask);
+}
+
+bool in_network_ipv4_ipv6(
+ const SocketAddress& ip,
+ const SocketAddress& net,
+ int prefix) {
+
+ if (prefix == 128)
+ return compare_ipv4_ipv6(ip, net) == SocketAddress::CompareResult::SAME;
+
+ uint8_t r = prefix % 8;
+ int len = (prefix - r) / 8;
+ uint8_t mask = ((1 << r) - 1) << (8 - r);
+
+ const sockaddr_in* ip_in =
+ reinterpret_cast<const sockaddr_in*>(ip.data());
+ const sockaddr_in6* net_in =
+ reinterpret_cast<const sockaddr_in6*>(net.data());
+
+ uint8_t ip_mask[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 0, 0, 0};
+ uint8_t* ptr = ip_mask;
+ memcpy(ptr + 12, &ip_in->sin_addr, 4);
+
+ if (memcmp(ptr, &net_in->sin6_addr, len) != 0)
+ return false;
+
+ ptr += len;
+ const uint8_t* p2 = reinterpret_cast<const uint8_t*>(
+ net_in->sin6_addr.s6_addr);
+
+ return (ptr[0] & mask) == (p2[len] & mask);
+}
+
+bool in_network_ipv6_ipv4(
+ const SocketAddress& ip,
+ const SocketAddress& net,
+ int prefix) {
+ if (prefix == 32)
+ return compare_ipv4_ipv6(net, ip) == SocketAddress::CompareResult::SAME;
+
+ uint32_t m = ((1 << prefix) - 1) << (32 - prefix);
+
+ const sockaddr_in6* ip_in =
+ reinterpret_cast<const sockaddr_in6*>(ip.data());
+ const sockaddr_in* net_in =
+ reinterpret_cast<const sockaddr_in*>(net.data());
+
+ const uint8_t* ptr =
+ reinterpret_cast<const uint8_t*>(&ip_in->sin6_addr);
+
+ if (memcmp(ptr, mask, sizeof(mask)) != 0)
+ return false;
+
+ ptr += sizeof(mask);
+ uint32_t check = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
+
+ return (check & m) == (htonl(net_in->sin_addr.s_addr) & m);
+}
+} // namespace
+
+// TODO(@jasnell): The implementations of is_match, compare, and
+// is_in_network have not been performance optimized and could
+// likely benefit from work on more performant approaches.
+
+bool SocketAddress::is_match(const SocketAddress& other) const {
+ switch (family()) {
+ case AF_INET:
+ switch (other.family()) {
+ case AF_INET: return is_match_ipv4(*this, other);
+ case AF_INET6: return is_match_ipv4_ipv6(*this, other);
+ }
+ break;
+ case AF_INET6:
+ switch (other.family()) {
+ case AF_INET: return is_match_ipv4_ipv6(other, *this);
+ case AF_INET6: return is_match_ipv6(*this, other);
+ }
+ break;
+ }
+ return false;
+}
+
+SocketAddress::CompareResult SocketAddress::compare(
+ const SocketAddress& other) const {
+ switch (family()) {
+ case AF_INET:
+ switch (other.family()) {
+ case AF_INET: return compare_ipv4(*this, other);
+ case AF_INET6: return compare_ipv4_ipv6(*this, other);
+ }
+ break;
+ case AF_INET6:
+ switch (other.family()) {
+ case AF_INET: {
+ CompareResult c = compare_ipv4_ipv6(other, *this);
+ switch (c) {
+ case SocketAddress::CompareResult::NOT_COMPARABLE:
+ // Fall through
+ case SocketAddress::CompareResult::SAME:
+ return c;
+ case SocketAddress::CompareResult::GREATER_THAN:
+ return SocketAddress::CompareResult::LESS_THAN;
+ case SocketAddress::CompareResult::LESS_THAN:
+ return SocketAddress::CompareResult::GREATER_THAN;
+ }
+ break;
+ }
+ case AF_INET6: return compare_ipv6(*this, other);
+ }
+ break;
+ }
+ return SocketAddress::CompareResult::NOT_COMPARABLE;
+}
+
+bool SocketAddress::is_in_network(
+ const SocketAddress& other,
+ int prefix) const {
+
+ switch (family()) {
+ case AF_INET:
+ switch (other.family()) {
+ case AF_INET: return in_network_ipv4(*this, other, prefix);
+ case AF_INET6: return in_network_ipv4_ipv6(*this, other, prefix);
+ }
+ break;
+ case AF_INET6:
+ switch (other.family()) {
+ case AF_INET: return in_network_ipv6_ipv4(*this, other, prefix);
+ case AF_INET6: return in_network_ipv6(*this, other, prefix);
+ }
+ break;
+ }
+
+ return false;
+}
+
+SocketAddressBlockList::SocketAddressBlockList(
+ std::shared_ptr<SocketAddressBlockList> parent)
+ : parent_(parent) {}
+
+void SocketAddressBlockList::AddSocketAddress(
+ const SocketAddress& address) {
+ std::unique_ptr<Rule> rule =
+ std::make_unique<SocketAddressRule>(address);
+ rules_.emplace_front(std::move(rule));
+ address_rules_[address] = rules_.begin();
+}
+
+void SocketAddressBlockList::RemoveSocketAddress(
+ const SocketAddress& address) {
+ auto it = address_rules_.find(address);
+ if (it != std::end(address_rules_)) {
+ rules_.erase(it->second);
+ address_rules_.erase(it);
+ }
+}
+
+void SocketAddressBlockList::AddSocketAddressRange(
+ const SocketAddress& start,
+ const SocketAddress& end) {
+ std::unique_ptr<Rule> rule =
+ std::make_unique<SocketAddressRangeRule>(start, end);
+ rules_.emplace_front(std::move(rule));
+}
+
+void SocketAddressBlockList::AddSocketAddressMask(
+ const SocketAddress& network,
+ int prefix) {
+ std::unique_ptr<Rule> rule =
+ std::make_unique<SocketAddressMaskRule>(network, prefix);
+ rules_.emplace_front(std::move(rule));
+}
+
+bool SocketAddressBlockList::Apply(const SocketAddress& address) {
+ for (const auto& rule : rules_) {
+ if (rule->Apply(address))
+ return true;
+ }
+ return parent_ ? parent_->Apply(address) : false;
+}
+
+SocketAddressBlockList::SocketAddressRule::SocketAddressRule(
+ const SocketAddress& address_)
+ : address(address_) {}
+
+SocketAddressBlockList::SocketAddressRangeRule::SocketAddressRangeRule(
+ const SocketAddress& start_,
+ const SocketAddress& end_)
+ : start(start_),
+ end(end_) {}
+
+SocketAddressBlockList::SocketAddressMaskRule::SocketAddressMaskRule(
+ const SocketAddress& network_,
+ int prefix_)
+ : network(network_),
+ prefix(prefix_) {}
+
+bool SocketAddressBlockList::SocketAddressRule::Apply(
+ const SocketAddress& address) {
+ return this->address.is_match(address);
+}
+
+std::string SocketAddressBlockList::SocketAddressRule::ToString() {
+ std::string ret = "Address: ";
+ ret += address.family() == AF_INET ? "IPv4" : "IPv6";
+ ret += " ";
+ ret += address.address();
+ return ret;
+}
+
+bool SocketAddressBlockList::SocketAddressRangeRule::Apply(
+ const SocketAddress& address) {
+ return address >= start && address <= end;
+}
+
+std::string SocketAddressBlockList::SocketAddressRangeRule::ToString() {
+ std::string ret = "Range: ";
+ ret += start.family() == AF_INET ? "IPv4" : "IPv6";
+ ret += " ";
+ ret += start.address();
+ ret += "-";
+ ret += end.address();
+ return ret;
+}
+
+bool SocketAddressBlockList::SocketAddressMaskRule::Apply(
+ const SocketAddress& address) {
+ return address.is_in_network(network, prefix);
+}
+
+std::string SocketAddressBlockList::SocketAddressMaskRule::ToString() {
+ std::string ret = "Subnet: ";
+ ret += network.family() == AF_INET ? "IPv4" : "IPv6";
+ ret += " ";
+ ret += network.address();
+ ret += "/" + std::to_string(prefix);
+ return ret;
+}
+
+MaybeLocal<Array> SocketAddressBlockList::ListRules(Environment* env) {
+ std::vector<Local<Value>> rules;
+ for (const auto& rule : rules_) {
+ Local<Value> str;
+ if (!rule->ToV8String(env).ToLocal(&str))
+ return MaybeLocal<Array>();
+ rules.push_back(str);
+ }
+ return Array::New(env->isolate(), rules.data(), rules.size());
+}
+
+void SocketAddressBlockList::MemoryInfo(node::MemoryTracker* tracker) const {
+ tracker->TrackField("rules", rules_);
+}
+
+void SocketAddressBlockList::SocketAddressRule::MemoryInfo(
+ node::MemoryTracker* tracker) const {
+ tracker->TrackField("address", address);
+}
+
+void SocketAddressBlockList::SocketAddressRangeRule::MemoryInfo(
+ node::MemoryTracker* tracker) const {
+ tracker->TrackField("start", start);
+ tracker->TrackField("end", end);
+}
+
+void SocketAddressBlockList::SocketAddressMaskRule::MemoryInfo(
+ node::MemoryTracker* tracker) const {
+ tracker->TrackField("network", network);
+}
+
+SocketAddressBlockListWrap::SocketAddressBlockListWrap(
+ Environment* env, Local<Object> wrap)
+ : BaseObject(env, wrap) {
+ MakeWeak();
+}
+
+void SocketAddressBlockListWrap::New(
+ const FunctionCallbackInfo<Value>& args) {
+ CHECK(args.IsConstructCall());
+ Environment* env = Environment::GetCurrent(args);
+ new SocketAddressBlockListWrap(env, args.This());
+}
+
+void SocketAddressBlockListWrap::AddAddress(
+ const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ SocketAddressBlockListWrap* wrap;
+ ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
+
+ CHECK(args[0]->IsString());
+ CHECK(args[1]->IsInt32());
+
+ sockaddr_storage address;
+ Utf8Value value(args.GetIsolate(), args[0]);
+ int32_t family;
+ if (!args[1]->Int32Value(env->context()).To(&family))
+ return;
+
+ if (!SocketAddress::ToSockAddr(family, *value, 0, &address))
+ return;
+
+ wrap->AddSocketAddress(
+ SocketAddress(reinterpret_cast<const sockaddr*>(&address)));
+
+ args.GetReturnValue().Set(true);
+}
+
+void SocketAddressBlockListWrap::AddRange(
+ const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ SocketAddressBlockListWrap* wrap;
+ ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
+
+ CHECK(args[0]->IsString());
+ CHECK(args[1]->IsString());
+ CHECK(args[2]->IsInt32());
+
+ sockaddr_storage address[2];
+ Utf8Value start(args.GetIsolate(), args[0]);
+ Utf8Value end(args.GetIsolate(), args[1]);
+ int32_t family;
+ if (!args[2]->Int32Value(env->context()).To(&family))
+ return;
+
+ if (!SocketAddress::ToSockAddr(family, *start, 0, &address[0]) ||
+ !SocketAddress::ToSockAddr(family, *end, 0, &address[1])) {
+ return;
+ }
+
+ SocketAddress start_addr(reinterpret_cast<const sockaddr*>(&address[0]));
+ SocketAddress end_addr(reinterpret_cast<const sockaddr*>(&address[1]));
+
+ // Starting address must come before the end address
+ if (start_addr > end_addr)
+ return args.GetReturnValue().Set(false);
+
+ wrap->AddSocketAddressRange(start_addr, end_addr);
+
+ args.GetReturnValue().Set(true);
+}
+
+void SocketAddressBlockListWrap::AddSubnet(
+ const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ SocketAddressBlockListWrap* wrap;
+ ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
+
+ CHECK(args[0]->IsString());
+ CHECK(args[1]->IsInt32());
+ CHECK(args[2]->IsInt32());
+
+ sockaddr_storage address;
+ Utf8Value network(args.GetIsolate(), args[0]);
+ int32_t family;
+ int32_t prefix;
+ if (!args[1]->Int32Value(env->context()).To(&family) ||
+ !args[2]->Int32Value(env->context()).To(&prefix)) {
+ return;
+ }
+
+ if (!SocketAddress::ToSockAddr(family, *network, 0, &address))
+ return;
+
+ CHECK_IMPLIES(family == AF_INET, prefix <= 32);
+ CHECK_IMPLIES(family == AF_INET6, prefix <= 128);
+ CHECK_GE(prefix, 0);
+
+ wrap->AddSocketAddressMask(
+ SocketAddress(reinterpret_cast<const sockaddr*>(&address)),
+ prefix);
+
+ args.GetReturnValue().Set(true);
+}
+
+void SocketAddressBlockListWrap::Check(
+ const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ SocketAddressBlockListWrap* wrap;
+ ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
+
+ CHECK(args[0]->IsString());
+ CHECK(args[1]->IsInt32());
+
+ sockaddr_storage address;
+ Utf8Value value(args.GetIsolate(), args[0]);
+ int32_t family;
+ if (!args[1]->Int32Value(env->context()).To(&family))
+ return;
+
+ if (!SocketAddress::ToSockAddr(family, *value, 0, &address))
+ return;
+
+ args.GetReturnValue().Set(
+ wrap->Apply(SocketAddress(reinterpret_cast<const sockaddr*>(&address))));
+}
+
+void SocketAddressBlockListWrap::GetRules(
+ const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ SocketAddressBlockListWrap* wrap;
+ ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
+ Local<Array> rules;
+ if (wrap->ListRules(env).ToLocal(&rules))
+ args.GetReturnValue().Set(rules);
+}
+
+void SocketAddressBlockListWrap::Initialize(
+ Local<Object> target,
+ Local<Value> unused,
+ Local<Context> context,
+ void* priv) {
+ Environment* env = Environment::GetCurrent(context);
+
+ Local<String> name = FIXED_ONE_BYTE_STRING(env->isolate(), "BlockList");
+ Local<FunctionTemplate> t =
+ env->NewFunctionTemplate(SocketAddressBlockListWrap::New);
+ t->InstanceTemplate()->SetInternalFieldCount(BaseObject::kInternalFieldCount);
+ t->SetClassName(name);
+
+ 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);
+
+ target->Set(env->context(), name,
+ t->GetFunction(env->context()).ToLocalChecked()).FromJust();
+
+ NODE_DEFINE_CONSTANT(target, AF_INET);
+ NODE_DEFINE_CONSTANT(target, AF_INET6);
+}
+
} // namespace node
+
+NODE_MODULE_CONTEXT_AWARE_INTERNAL(
+ block_list,
+ node::SocketAddressBlockListWrap::Initialize)
diff --git a/src/node_sockaddr.h b/src/node_sockaddr.h
index 5d20487f93..f539cf6555 100644
--- a/src/node_sockaddr.h
+++ b/src/node_sockaddr.h
@@ -3,11 +3,14 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+#include "env.h"
#include "memory_tracker.h"
+#include "base_object.h"
#include "node.h"
#include "uv.h"
#include "v8.h"
+#include <memory>
#include <string>
#include <list>
#include <unordered_map>
@@ -18,6 +21,13 @@ class Environment;
class SocketAddress : public MemoryRetainer {
public:
+ enum class CompareResult {
+ NOT_COMPARABLE = -2,
+ LESS_THAN,
+ SAME,
+ GREATER_THAN
+ };
+
struct Hash {
size_t operator()(const SocketAddress& addr) const;
};
@@ -25,6 +35,11 @@ class SocketAddress : public MemoryRetainer {
inline bool operator==(const SocketAddress& other) const;
inline bool operator!=(const SocketAddress& other) const;
+ inline bool operator<(const SocketAddress& other) const;
+ inline bool operator>(const SocketAddress& other) const;
+ inline bool operator<=(const SocketAddress& other) const;
+ inline bool operator>=(const SocketAddress& other) const;
+
inline static bool is_numeric_host(const char* hostname);
inline static bool is_numeric_host(const char* hostname, int family);
@@ -78,6 +93,20 @@ class SocketAddress : public MemoryRetainer {
inline std::string address() const;
inline int port() const;
+ // Returns true if the given other SocketAddress is a match
+ // for this one. The addresses are a match if:
+ // 1. They are the same family and match identically
+ // 2. They are different family but match semantically (
+ // for instance, an IPv4 addres in IPv6 notation)
+ bool is_match(const SocketAddress& other) const;
+
+ // Compares this SocketAddress to the given other SocketAddress.
+ CompareResult compare(const SocketAddress& other) const;
+
+ // Returns true if this SocketAddress is within the subnet
+ // identified by the given network address and CIDR prefix.
+ bool is_in_network(const SocketAddress& network, int prefix) const;
+
// If the SocketAddress is an IPv6 address, returns the
// current value of the IPv6 flow label, if set. Otherwise
// returns 0.
@@ -152,6 +181,123 @@ class SocketAddressLRU : public MemoryRetainer {
size_t max_size_;
};
+// A BlockList is used to evaluate whether a given
+// SocketAddress should be accepted for inbound or
+// outbound network activity.
+class SocketAddressBlockList : public MemoryRetainer {
+ public:
+ explicit SocketAddressBlockList(
+ std::shared_ptr<SocketAddressBlockList> parent = {});
+ ~SocketAddressBlockList() = default;
+
+ void AddSocketAddress(
+ const SocketAddress& address);
+
+ void RemoveSocketAddress(
+ const SocketAddress& address);
+
+ void AddSocketAddressRange(
+ const SocketAddress& start,
+ const SocketAddress& end);
+
+ void AddSocketAddressMask(
+ const SocketAddress& address,
+ int prefix);
+
+ bool Apply(const SocketAddress& address);
+
+ size_t size() const { return rules_.size(); }
+
+ v8::MaybeLocal<v8::Array> ListRules(Environment* env);
+
+ struct Rule : public MemoryRetainer {
+ virtual bool Apply(const SocketAddress& address) = 0;
+ inline v8::MaybeLocal<v8::Value> ToV8String(Environment* env);
+ virtual std::string ToString() = 0;
+ };
+
+ struct SocketAddressRule final : Rule {
+ SocketAddress address;
+
+ explicit SocketAddressRule(const SocketAddress& address);
+
+ bool Apply(const SocketAddress& address) override;
+ std::string ToString() override;
+
+ void MemoryInfo(node::MemoryTracker* tracker) const override;
+ SET_MEMORY_INFO_NAME(SocketAddressRule)
+ SET_SELF_SIZE(SocketAddressRule)
+ };
+
+ struct SocketAddressRangeRule final : Rule {
+ SocketAddress start;
+ SocketAddress end;
+
+ SocketAddressRangeRule(
+ const SocketAddress& start,
+ const SocketAddress& end);
+
+ bool Apply(const SocketAddress& address) override;
+ std::string ToString() override;
+
+ void MemoryInfo(node::MemoryTracker* tracker) const override;
+ SET_MEMORY_INFO_NAME(SocketAddressRangeRule)
+ SET_SELF_SIZE(SocketAddressRangeRule)
+ };
+
+ struct SocketAddressMaskRule final : Rule {
+ SocketAddress network;
+ int prefix;
+
+ SocketAddressMaskRule(
+ const SocketAddress& address,
+ int prefix);
+
+ bool Apply(const SocketAddress& address) override;
+ std::string ToString() override;
+
+ void MemoryInfo(node::MemoryTracker* tracker) const override;
+ SET_MEMORY_INFO_NAME(SocketAddressMaskRule)
+ SET_SELF_SIZE(SocketAddressMaskRule)
+ };
+
+ void MemoryInfo(node::MemoryTracker* tracker) const override;
+ SET_MEMORY_INFO_NAME(SocketAddressBlockList)
+ SET_SELF_SIZE(SocketAddressBlockList)
+
+ private:
+ std::shared_ptr<SocketAddressBlockList> parent_;
+ std::list<std::unique_ptr<Rule>> rules_;
+ SocketAddress::Map<std::list<std::unique_ptr<Rule>>::iterator> address_rules_;
+};
+
+class SocketAddressBlockListWrap :
+ public BaseObject,
+ public SocketAddressBlockList {
+ public:
+ static void Initialize(v8::Local<v8::Object> target,
+ v8::Local<v8::Value> unused,
+ v8::Local<v8::Context> context,
+ void* priv);
+
+ 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);
+ static void AddSubnet(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Check(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetRules(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ SocketAddressBlockListWrap(
+ Environment* env,
+ v8::Local<v8::Object> wrap);
+
+ void MemoryInfo(node::MemoryTracker* tracker) const override {
+ SocketAddressBlockList::MemoryInfo(tracker);
+ }
+ SET_MEMORY_INFO_NAME(SocketAddressBlockListWrap)
+ SET_SELF_SIZE(SocketAddressBlockListWrap)
+};
+
} // namespace node
#endif // NOE_WANT_INTERNALS