diff options
Diffstat (limited to 'ext/node/polyfills/internal/blocklist.mjs')
-rw-r--r-- | ext/node/polyfills/internal/blocklist.mjs | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/ext/node/polyfills/internal/blocklist.mjs b/ext/node/polyfills/internal/blocklist.mjs new file mode 100644 index 000000000..a9aba03b6 --- /dev/null +++ b/ext/node/polyfills/internal/blocklist.mjs @@ -0,0 +1,227 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { primordials } from "ext:core/mod.js"; +import { + op_blocklist_add_address, + op_blocklist_add_range, + op_blocklist_add_subnet, + op_blocklist_check, + op_blocklist_new, + op_socket_address_get_serialization, + op_socket_address_parse, +} from "ext:core/ops"; + +import { + validateInt32, + validateObject, + validatePort, + validateString, + validateUint32, +} from "ext:deno_node/internal/validators.mjs"; +import { ERR_INVALID_ARG_VALUE } from "ext:deno_node/internal/errors.ts"; +import { customInspectSymbol } from "ext:deno_node/internal/util.mjs"; +import { inspect } from "ext:deno_node/internal/util/inspect.mjs"; + +const { Symbol } = primordials; + +const internalBlockList = Symbol("blocklist"); + +class BlockList { + constructor() { + this[internalBlockList] = op_blocklist_new(); + } + + [customInspectSymbol](depth, options) { + if (depth < 0) { + return this; + } + + const opts = { + ...options, + depth: options.depth == null ? null : options.depth - 1, + }; + + return `BlockList ${ + inspect({ + rules: [], // TODO(satyarohith): provide the actual rules + }, opts) + }`; + } + + addAddress(address, family = "ipv4") { + if (!SocketAddress.isSocketAddress(address)) { + validateString(address, "address"); + validateString(family, "family"); + new SocketAddress({ + address, + family, + }); + } else { + address = address.address; + } + op_blocklist_add_address(this[internalBlockList], address); + } + + addRange(start, end, family = "ipv4") { + if (!SocketAddress.isSocketAddress(start)) { + validateString(start, "start"); + validateString(family, "family"); + new SocketAddress({ + address: start, + family, + }); + } else { + start = start.address; + } + if (!SocketAddress.isSocketAddress(end)) { + validateString(end, "end"); + validateString(family, "family"); + new SocketAddress({ + address: end, + family, + }); + } else { + end = end.address; + } + const ret = op_blocklist_add_range(this[internalBlockList], start, end); + if (ret === false) { + throw new ERR_INVALID_ARG_VALUE("start", start, "must come before end"); + } + } + + addSubnet(network, prefix, family = "ipv4") { + if (!SocketAddress.isSocketAddress(network)) { + validateString(network, "network"); + validateString(family, "family"); + new SocketAddress({ + address: network, + family, + }); + } else { + network = network.address; + family = network.family; + } + switch (family) { + case "ipv4": + validateInt32(prefix, "prefix", 0, 32); + break; + case "ipv6": + validateInt32(prefix, "prefix", 0, 128); + break; + } + op_blocklist_add_subnet(this[internalBlockList], network, prefix); + } + + check(address, family = "ipv4") { + if (!SocketAddress.isSocketAddress(address)) { + validateString(address, "address"); + validateString(family, "family"); + try { + new SocketAddress({ + address, + family, + }); + } catch { + // Ignore the error. If it's not a valid address, return false. + return false; + } + } else { + family = address.family; + address = address.address; + } + try { + return op_blocklist_check(this[internalBlockList], address, family); + } catch (_) { + // Node API expects false as return value if the address is invalid. + // Example: `blocklist.check("1.1.1.1", "ipv6")` should return false. + return false; + } + } + + get rules() { + // TODO(satyarohith): return the actual rules + return []; + } +} + +const kDetail = Symbol("kDetail"); + +class SocketAddress { + static isSocketAddress(value) { + return value?.[kDetail] !== undefined; + } + + constructor(options = kEmptyObject) { + validateObject(options, "options"); + let { family = "ipv4" } = options; + const { + address = (family === "ipv4" ? "127.0.0.1" : "::"), + port = 0, + flowlabel = 0, + } = options; + + if (typeof family?.toLowerCase === "function") { + // deno-lint-ignore prefer-primordials + family = family.toLowerCase(); + } + switch (family) { + case "ipv4": + break; + case "ipv6": + break; + default: + throw new ERR_INVALID_ARG_VALUE("options.family", options.family); + } + + validateString(address, "options.address"); + validatePort(port, "options.port"); + validateUint32(flowlabel, "options.flowlabel", false); + + this[kDetail] = { + address, + port, + family, + flowlabel, + }; + const useInput = op_socket_address_parse( + address, + port, + family, + ); + if (!useInput) { + const { 0: address_, 1: family_ } = op_socket_address_get_serialization(); + this[kDetail].address = address_; + this[kDetail].family = family_; + } + } + + get address() { + return this[kDetail].address; + } + + get port() { + return this[kDetail].port; + } + + get family() { + return this[kDetail].family; + } + + get flowlabel() { + // TODO(satyarohith): Implement this in Rust. + // The flow label can be changed internally. + return this[kDetail].flowlabel; + } + + toJSON() { + return { + address: this.address, + port: this.port, + family: this.family, + flowlabel: this.flowlabel, + }; + } +} + +export { BlockList, SocketAddress }; |