Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9151439

Browse filesBrowse files
joyeecheungdanielleadams
authored andcommitted
dns: support dns module in the snapshot
For the initial iteration, only the default resolver can be serialized/deserialized. If `dns.setServers()` has been called, we'll preserve the configured DNS servers in the snapshot. We can consider exposing the serialization method if it becomes necessary for user-land snapshots. PR-URL: #44633 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent f8b2d7a commit 9151439
Copy full SHA for 9151439
Expand file treeCollapse file tree

14 files changed

+474
-7
lines changed
Open diff view settings
Collapse file

‎lib/internal/dns/utils.js‎

Copy file name to clipboardExpand all lines: lib/internal/dns/utils.js
+65-2Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const {
1010
RegExpPrototypeExec,
1111
RegExpPrototypeSymbolReplace,
1212
ObjectCreate,
13+
Symbol,
1314
} = primordials;
1415

1516
const errors = require('internal/errors');
@@ -35,6 +36,12 @@ const {
3536
ERR_INVALID_IP_ADDRESS,
3637
} = errors.codes;
3738

39+
const {
40+
addSerializeCallback,
41+
addDeserializeCallback,
42+
isBuildingSnapshot,
43+
} = require('v8').startupSnapshot;
44+
3845
function validateTimeout(options) {
3946
const { timeout = -1 } = { ...options };
4047
validateInt32(timeout, 'options.timeout', -1);
@@ -47,12 +54,27 @@ function validateTries(options) {
4754
return tries;
4855
}
4956

57+
const kSerializeResolver = Symbol('dns:resolver:serialize');
58+
const kDeserializeResolver = Symbol('dns:resolver:deserialize');
59+
const kSnapshotStates = Symbol('dns:resolver:config');
60+
const kInitializeHandle = Symbol('dns:resolver:initializeHandle');
61+
const kSetServersInteral = Symbol('dns:resolver:setServers');
62+
5063
// Resolver instances correspond 1:1 to c-ares channels.
5164

5265
class ResolverBase {
5366
constructor(options = undefined) {
5467
const timeout = validateTimeout(options);
5568
const tries = validateTries(options);
69+
// If we are building snapshot, save the states of the resolver along
70+
// the way.
71+
if (isBuildingSnapshot()) {
72+
this[kSnapshotStates] = { timeout, tries };
73+
}
74+
this[kInitializeHandle](timeout, tries);
75+
}
76+
77+
[kInitializeHandle](timeout, tries) {
5678
const { ChannelWrap } = lazyBinding();
5779
this._handle = new ChannelWrap(timeout, tries);
5880
}
@@ -77,9 +99,7 @@ class ResolverBase {
7799
// Cache the original servers because in the event of an error while
78100
// setting the servers, c-ares won't have any servers available for
79101
// resolution.
80-
const orig = this._handle.getServers() || [];
81102
const newSet = [];
82-
83103
ArrayPrototypeForEach(servers, (serv, index) => {
84104
validateString(serv, `servers[${index}]`);
85105
let ipVersion = isIP(serv);
@@ -118,6 +138,11 @@ class ResolverBase {
118138
throw new ERR_INVALID_IP_ADDRESS(serv);
119139
});
120140

141+
this[kSetServersInteral](newSet, servers);
142+
}
143+
144+
[kSetServersInteral](newSet, servers) {
145+
const orig = this._handle.getServers() || [];
121146
const errorNumber = this._handle.setServers(newSet);
122147

123148
if (errorNumber !== 0) {
@@ -127,8 +152,13 @@ class ResolverBase {
127152
const err = strerror(errorNumber);
128153
throw new ERR_DNS_SET_SERVERS_FAILED(err, servers);
129154
}
155+
156+
if (isBuildingSnapshot()) {
157+
this[kSnapshotStates].servers = newSet;
158+
}
130159
}
131160

161+
132162
setLocalAddress(ipv4, ipv6) {
133163
validateString(ipv4, 'ipv4');
134164

@@ -137,6 +167,31 @@ class ResolverBase {
137167
}
138168

139169
this._handle.setLocalAddress(ipv4, ipv6);
170+
171+
if (isBuildingSnapshot()) {
172+
this[kSnapshotStates].localAddress = { ipv4, ipv6 };
173+
}
174+
}
175+
176+
// TODO(joyeecheung): consider exposing this if custom DNS resolvers
177+
// end up being useful for snapshot users.
178+
[kSerializeResolver]() {
179+
this._handle = null; // We'll restore it during deserialization.
180+
addDeserializeCallback(function deserializeResolver(resolver) {
181+
resolver[kDeserializeResolver]();
182+
}, this);
183+
}
184+
185+
[kDeserializeResolver]() {
186+
const { timeout, tries, localAddress, servers } = this[kSnapshotStates];
187+
this[kInitializeHandle](timeout, tries);
188+
if (localAddress) {
189+
const { ipv4, ipv6 } = localAddress;
190+
this._handle.setLocalAddress(ipv4, ipv6);
191+
}
192+
if (servers) {
193+
this[kSetServersInteral](servers, servers);
194+
}
140195
}
141196
}
142197

@@ -151,6 +206,14 @@ function initializeDns() {
151206
// Allow the deserialized application to override order from CLI.
152207
dnsOrder = orderFromCLI;
153208
}
209+
210+
if (!isBuildingSnapshot()) {
211+
return;
212+
}
213+
214+
addSerializeCallback(() => {
215+
defaultResolver?.[kSerializeResolver]();
216+
});
154217
}
155218

156219
const resolverKeys = [
Collapse file

‎lib/internal/main/mksnapshot.js‎

Copy file name to clipboardExpand all lines: lib/internal/main/mksnapshot.js
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const supportedModules = new SafeSet(new SafeArrayIterator([
4949
'crypto',
5050
// 'dgram',
5151
// 'diagnostics_channel',
52-
// 'dns',
52+
'dns',
5353
// 'dns/promises',
5454
// 'domain',
5555
'events',
Collapse file

‎src/cares_wrap.cc‎

Copy file name to clipboardExpand all lines: src/cares_wrap.cc
+33-3Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,19 @@
1919
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
2020
// USE OR OTHER DEALINGS IN THE SOFTWARE.
2121

22+
#include "cares_wrap.h"
2223
#include "async_wrap-inl.h"
23-
#include "base_object-inl.h"
2424
#include "base64-inl.h"
25-
#include "cares_wrap.h"
25+
#include "base_object-inl.h"
2626
#include "env-inl.h"
2727
#include "memory_tracker-inl.h"
2828
#include "node.h"
2929
#include "node_errors.h"
30+
#include "node_external_reference.h"
3031
#include "req_wrap-inl.h"
3132
#include "util-inl.h"
32-
#include "v8.h"
3333
#include "uv.h"
34+
#include "v8.h"
3435

3536
#include <cerrno>
3637
#include <cstring>
@@ -1955,7 +1956,36 @@ void Initialize(Local<Object> target,
19551956
SetConstructorFunction(context, target, "ChannelWrap", channel_wrap);
19561957
}
19571958

1959+
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
1960+
registry->Register(GetAddrInfo);
1961+
registry->Register(GetNameInfo);
1962+
registry->Register(CanonicalizeIP);
1963+
registry->Register(StrError);
1964+
registry->Register(ChannelWrap::New);
1965+
1966+
registry->Register(Query<QueryAnyWrap>);
1967+
registry->Register(Query<QueryAWrap>);
1968+
registry->Register(Query<QueryAaaaWrap>);
1969+
registry->Register(Query<QueryCaaWrap>);
1970+
registry->Register(Query<QueryCnameWrap>);
1971+
registry->Register(Query<QueryMxWrap>);
1972+
registry->Register(Query<QueryNsWrap>);
1973+
registry->Register(Query<QueryTxtWrap>);
1974+
registry->Register(Query<QuerySrvWrap>);
1975+
registry->Register(Query<QueryPtrWrap>);
1976+
registry->Register(Query<QueryNaptrWrap>);
1977+
registry->Register(Query<QuerySoaWrap>);
1978+
registry->Register(Query<GetHostByAddrWrap>);
1979+
1980+
registry->Register(GetServers);
1981+
registry->Register(SetServers);
1982+
registry->Register(SetLocalAddress);
1983+
registry->Register(Cancel);
1984+
}
1985+
19581986
} // namespace cares_wrap
19591987
} // namespace node
19601988

19611989
NODE_MODULE_CONTEXT_AWARE_INTERNAL(cares_wrap, node::cares_wrap::Initialize)
1990+
NODE_MODULE_EXTERNAL_REFERENCE(cares_wrap,
1991+
node::cares_wrap::RegisterExternalReferences)
Collapse file

‎src/cares_wrap.h‎

Copy file name to clipboardExpand all lines: src/cares_wrap.h
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
#include "base_object.h"
1010
#include "env.h"
1111
#include "memory_tracker.h"
12-
#include "util.h"
1312
#include "node.h"
13+
#include "node_internals.h"
14+
#include "util.h"
1415

1516
#include "ares.h"
1617
#include "v8.h"
Collapse file

‎src/node_external_reference.h‎

Copy file name to clipboardExpand all lines: src/node_external_reference.h
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class ExternalReferenceRegistry {
6262
V(blob) \
6363
V(buffer) \
6464
V(builtins) \
65+
V(cares_wrap) \
6566
V(contextify) \
6667
V(credentials) \
6768
V(env_var) \
Collapse file

‎test/common/snapshot.js‎

Copy file name to clipboard
+66Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
'use strict';
2+
3+
const tmpdir = require('../common/tmpdir');
4+
const { spawnSync } = require('child_process');
5+
const path = require('path');
6+
const fs = require('fs');
7+
const assert = require('assert');
8+
9+
function buildSnapshot(entry, env) {
10+
const child = spawnSync(process.execPath, [
11+
'--snapshot-blob',
12+
path.join(tmpdir.path, 'snapshot.blob'),
13+
'--build-snapshot',
14+
entry,
15+
], {
16+
cwd: tmpdir.path,
17+
env: {
18+
...process.env,
19+
...env,
20+
},
21+
});
22+
23+
const stderr = child.stderr.toString();
24+
const stdout = child.stdout.toString();
25+
console.log('[stderr]');
26+
console.log(stderr);
27+
console.log('[stdout]');
28+
console.log(stdout);
29+
30+
assert.strictEqual(child.status, 0);
31+
32+
const stats = fs.statSync(path.join(tmpdir.path, 'snapshot.blob'));
33+
assert(stats.isFile());
34+
35+
return { child, stderr, stdout };
36+
}
37+
38+
function runWithSnapshot(entry, env) {
39+
const args = ['--snapshot-blob', path.join(tmpdir.path, 'snapshot.blob')];
40+
if (entry !== undefined) {
41+
args.push(entry);
42+
}
43+
const child = spawnSync(process.execPath, args, {
44+
cwd: tmpdir.path,
45+
env: {
46+
...process.env,
47+
...env,
48+
}
49+
});
50+
51+
const stderr = child.stderr.toString();
52+
const stdout = child.stdout.toString();
53+
console.log('[stderr]');
54+
console.log(stderr);
55+
console.log('[stdout]');
56+
console.log(stdout);
57+
58+
assert.strictEqual(child.status, 0);
59+
60+
return { child, stderr, stdout };
61+
}
62+
63+
module.exports = {
64+
buildSnapshot,
65+
runWithSnapshot,
66+
};
Collapse file
+39Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use strict';
2+
const dns = require('dns');
3+
const assert = require('assert');
4+
5+
assert(process.env.NODE_TEST_HOST);
6+
7+
const {
8+
setDeserializeMainFunction,
9+
} = require('v8').startupSnapshot;
10+
11+
function onError(err) {
12+
console.error('error:', err);
13+
}
14+
15+
function onLookup(address, family) {
16+
console.log(`address: ${JSON.stringify(address)}`);
17+
console.log(`family: ${JSON.stringify(family)}`);
18+
}
19+
20+
function query() {
21+
const host = process.env.NODE_TEST_HOST;
22+
if (process.env.NODE_TEST_PROMISE === 'true') {
23+
dns.promises.lookup(host, { family: 4 }).then(
24+
({address, family}) => onLookup(address, family),
25+
onError);
26+
} else {
27+
dns.lookup(host, { family: 4 }, (err, address, family) => {
28+
if (err) {
29+
onError(err);
30+
} else {
31+
onLookup(address, family);
32+
}
33+
});
34+
}
35+
}
36+
37+
query();
38+
39+
setDeserializeMainFunction(query);
Collapse file
+59Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
'use strict';
2+
const dns = require('dns');
3+
const assert = require('assert');
4+
5+
assert(process.env.NODE_TEST_HOST);
6+
7+
const {
8+
setDeserializeMainFunction,
9+
} = require('v8').startupSnapshot;
10+
11+
function onError(err) {
12+
console.error('error:', err);
13+
}
14+
15+
function onResolve(addresses) {
16+
console.log(`addresses: ${JSON.stringify(addresses)}`);
17+
}
18+
19+
function onReverse(hostnames) {
20+
console.log(`hostnames: ${JSON.stringify(hostnames)}`);
21+
}
22+
23+
function query() {
24+
if (process.env.NODE_TEST_DNS) {
25+
dns.setServers([process.env.NODE_TEST_DNS])
26+
}
27+
28+
const host = process.env.NODE_TEST_HOST;
29+
if (process.env.NODE_TEST_PROMISE === 'true') {
30+
dns.promises.resolve4(host).then(onResolve, onError);
31+
} else {
32+
dns.resolve4(host, (err, addresses) => {
33+
if (err) {
34+
onError(err);
35+
} else {
36+
onResolve(addresses);
37+
}
38+
});
39+
}
40+
41+
const ip = process.env.NODE_TEST_IP;
42+
if (ip) {
43+
if (process.env.NODE_TEST_PROMISE === 'true') {
44+
dns.promises.reverse(ip).then(onReverse, onError);
45+
} else {
46+
dns.reverse(ip, (err, hostnames) => {
47+
if (err) {
48+
onError(err);
49+
} else {
50+
onReverse(hostnames);
51+
}
52+
});
53+
}
54+
}
55+
}
56+
57+
query();
58+
59+
setDeserializeMainFunction(query);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.