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

net: net.BlockList updates and add net.SocketAddress #37917

Closed
wants to merge 3 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
56 changes: 51 additions & 5 deletions doc/api/net.md
Expand Up @@ -69,7 +69,7 @@ IP subnets.
added: v15.0.0
-->

* `address` {string} An IPv4 or IPv6 address.
* `address` {string|net.SocketAddress} An IPv4 or IPv6 address.
* `type` {string} Either `'ipv4'` or `'ipv6'`. **Default:** `'ipv4'`.

Adds a rule to block the given IP address.
Expand All @@ -79,8 +79,9 @@ Adds a rule to block the given IP address.
added: v15.0.0
-->

* `start` {string} The starting IPv4 or IPv6 address in the range.
* `end` {string} The ending IPv4 or IPv6 address in the range.
* `start` {string|net.SocketAddress} The starting IPv4 or IPv6 address in the
range.
* `end` {string|net.SocketAddress} The ending IPv4 or IPv6 address in the range.
* `type` {string} Either `'ipv4'` or `'ipv6'`. **Default:** `'ipv4'`.

Adds a rule to block a range of IP addresses from `start` (inclusive) to
Expand All @@ -91,7 +92,7 @@ Adds a rule to block a range of IP addresses from `start` (inclusive) to
added: v15.0.0
-->

* `net` {string} The network IPv4 or IPv6 address.
* `net` {string|net.SocketAddress} The network IPv4 or IPv6 address.
* `prefix` {number} The number of CIDR prefix bits. For IPv4, this
must be a value between `0` and `32`. For IPv6, this must be between
`0` and `128`.
Expand All @@ -104,7 +105,7 @@ Adds a rule to block a range of IP addresses specified as a subnet mask.
added: v15.0.0
-->

* `address` {string} The IP address to check
* `address` {string|net.SocketAddress} The IP address to check
* `type` {string} Either `'ipv4'` or `'ipv6'`. **Default:** `'ipv4'`.
* Returns: {boolean}

Expand Down Expand Up @@ -135,6 +136,51 @@ added: v15.0.0

The list of rules added to the blocklist.

## Class: `net.SocketAddress`
<!-- YAML
added: REPLACEME
-->
### `new net.SocketAddress([options])`
<!-- YAML
added: REPLACEME
-->

* `options` {Object}
* `address` {string} The network address as either an IPv4 or IPv6 string.
**Default**: `'127.0.0.1'` if `family` is `'ipv4'`; `'::'` if `family` is
`'ipv6'`.
* `family` {string} One of either `'ipv4'` or 'ipv6'`. **Default**: `'ipv4'`.
* `flowlabel` {number} An IPv6 flow-label used only if `family` is `'ipv6'`.
* `port` {number} An IP port.

### `socketaddress.address`
<!-- YAML
added: REPLACEME
-->

* Type {string}

### `socketaddress.family`
<!-- YAML
added: REPLACEME
-->

* Type {string} Either `'ipv4'` or `'ipv6'`.

### `socketaddress.flowlabel`
<!-- YAML
added: REPLACEME
-->

* Type {number}

### `socketaddress.port`
<!-- YAML
added: REPLACEME
-->

* Type {number}

## Class: `net.Server`
<!-- YAML
added: v0.1.90
Expand Down
5 changes: 5 additions & 0 deletions doc/api/worker_threads.md
Expand Up @@ -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.
Expand Down Expand Up @@ -569,6 +572,8 @@ In particular, the significant differences to `JSON` are:
* {Histogram}s,
* {KeyObject}s,
* {MessagePort}s,
* {net.BlockList}s,
* {net.SocketAddress}es,
* {X509Certificate}s.

```js
Expand Down
142 changes: 98 additions & 44 deletions lib/internal/blocklist.js
Expand Up @@ -2,38 +2,44 @@

const {
Boolean,
ObjectSetPrototypeOf,
Symbol
} = primordials;

const {
BlockList: BlockListHandle,
AF_INET,
AF_INET6,
} = internalBinding('block_list');

const {
customInspectSymbol: kInspect,
} = require('internal/util');

const {
SocketAddress,
kHandle: kSocketAddressHandle,
} = require('internal/socketaddress');

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;
}

Expand All @@ -52,61 +58,109 @@ class BlockList {
}

addAddress(address, family = 'ipv4') {
validateString(address, 'address');
validateString(family, 'family');
family = family.toLowerCase();
if (family !== 'ipv4' && family !== 'ipv6')
throw new ERR_INVALID_ARG_VALUE('family', family);
const type = family === 'ipv4' ? AF_INET : AF_INET6;
this[kHandle].addAddress(address, type);
if (!SocketAddress.isSocketAddress(address)) {
validateString(address, 'address');
validateString(family, 'family');
address = new SocketAddress({
address,
family,
});
}
this[kHandle].addAddress(address[kSocketAddressHandle]);
}

addRange(start, end, family = 'ipv4') {
validateString(start, 'start');
validateString(end, 'end');
validateString(family, 'family');
family = family.toLowerCase();
if (family !== 'ipv4' && family !== 'ipv6')
throw new ERR_INVALID_ARG_VALUE('family', family);
const type = family === 'ipv4' ? AF_INET : AF_INET6;
const ret = this[kHandle].addRange(start, end, type);
if (!SocketAddress.isSocketAddress(start)) {
validateString(start, 'start');
validateString(family, 'family');
start = new SocketAddress({
address: start,
family,
});
}
if (!SocketAddress.isSocketAddress(end)) {
validateString(end, 'end');
validateString(family, 'family');
end = new SocketAddress({
address: end,
family,
});
}
const ret = this[kHandle].addRange(
start[kSocketAddressHandle],
end[kSocketAddressHandle]);
if (ret === false)
throw new ERR_INVALID_ARG_VALUE('start', start, 'must come before end');
}

addSubnet(network, prefix, family = 'ipv4') {
validateString(network, 'network');
validateString(family, 'family');
family = family.toLowerCase();
let type;
switch (family) {
if (!SocketAddress.isSocketAddress(network)) {
validateString(network, 'network');
validateString(family, 'family');
network = new SocketAddress({
address: network,
family,
});
}
switch (network.family) {
case 'ipv4':
type = AF_INET;
validateInt32(prefix, 'prefix', 0, 32);
break;
case 'ipv6':
type = AF_INET6;
validateInt32(prefix, 'prefix', 0, 128);
break;
default:
throw new ERR_INVALID_ARG_VALUE('family', family);
}
this[kHandle].addSubnet(network, type, prefix);
this[kHandle].addSubnet(network[kSocketAddressHandle], prefix);
}

check(address, family = 'ipv4') {
validateString(address, 'address');
validateString(family, 'family');
family = family.toLowerCase();
if (family !== 'ipv4' && family !== 'ipv6')
throw new ERR_INVALID_ARG_VALUE('family', family);
const type = family === 'ipv4' ? AF_INET : AF_INET6;
return Boolean(this[kHandle].check(address, type));
if (!SocketAddress.isSocketAddress(address)) {
validateString(address, 'address');
validateString(family, 'family');
try {
address = new SocketAddress({
address,
family,
});
} catch {
// Ignore the error. If it's not a valid address, return false.
return false;
}
}
return Boolean(this[kHandle].check(address[kSocketAddressHandle]));
}

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,
};