Files
node/src/node_sockaddr.cc
James M Snell 1c14810edc src: allow instances of net.BlockList to be created internally
Initial PR had it so that user code would create BlockList
instances. This sets it up so that instances can be created
internally by Node.js

PR-URL: https://github.com/nodejs/node/pull/34741
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Rich Trott <rtrott@gmail.com>
2020-08-17 11:31:26 -07:00

702 lines
20 KiB
C++

#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) {
SocketAddress addr;
int len = sizeof(sockaddr_storage);
if (fn(&handle, addr.storage(), &len) == 0)
CHECK_EQ(static_cast<size_t>(len), addr.length());
else
addr.storage()->sa_family = 0;
return addr;
}
} // namespace
bool SocketAddress::ToSockAddr(
int32_t family,
const char* host,
uint32_t port,
sockaddr_storage* addr) {
switch (family) {
case AF_INET:
return uv_ip4_addr(
host,
port,
reinterpret_cast<sockaddr_in*>(addr)) == 0;
case AF_INET6:
return uv_ip6_addr(
host,
port,
reinterpret_cast<sockaddr_in6*>(addr)) == 0;
default:
UNREACHABLE();
}
}
bool SocketAddress::New(
const char* host,
uint32_t port,
SocketAddress* addr) {
return New(AF_INET, host, port, addr) || New(AF_INET6, host, port, addr);
}
bool SocketAddress::New(
int32_t family,
const char* host,
uint32_t port,
SocketAddress* addr) {
return ToSockAddr(family, host, port,
reinterpret_cast<sockaddr_storage*>(addr->storage()));
}
size_t SocketAddress::Hash::operator()(const SocketAddress& addr) const {
size_t hash = 0;
switch (addr.family()) {
case AF_INET: {
const sockaddr_in* ipv4 =
reinterpret_cast<const sockaddr_in*>(addr.raw());
hash_combine(&hash, ipv4->sin_port, ipv4->sin_addr.s_addr);
break;
}
case AF_INET6: {
const sockaddr_in6* ipv6 =
reinterpret_cast<const sockaddr_in6*>(addr.raw());
const uint64_t* a =
reinterpret_cast<const uint64_t*>(&ipv6->sin6_addr);
hash_combine(&hash, ipv6->sin6_port, a[0], a[1]);
break;
}
default:
UNREACHABLE();
}
return hash;
}
SocketAddress SocketAddress::FromSockName(const uv_tcp_t& handle) {
return FromUVHandle(uv_tcp_getsockname, handle);
}
SocketAddress SocketAddress::FromSockName(const uv_udp_t& handle) {
return FromUVHandle(uv_udp_getsockname, handle);
}
SocketAddress SocketAddress::FromPeerName(const uv_tcp_t& handle) {
return FromUVHandle(uv_tcp_getpeername, handle);
}
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();
}
BaseObjectPtr<SocketAddressBlockListWrap> SocketAddressBlockListWrap::New(
Environment* env) {
Local<Object> obj;
if (!env->blocklist_instance_template()
->NewInstance(env->context()).ToLocal(&obj)) {
return {};
}
BaseObjectPtr<SocketAddressBlockListWrap> wrap =
MakeDetachedBaseObject<SocketAddressBlockListWrap>(env, obj);
CHECK(wrap);
return wrap;
}
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);
env->set_blocklist_instance_template(t->InstanceTemplate());
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)