Skip to content

Commit 0bc14b6

Browse files
bnoordhuisMylesBorins
authored andcommitted
dns: implement {ttl: true} for dns.resolve4()
Add an option to retrieve the Time-To-Live of the A record. PR-URL: #9296 Refs: #5893 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Roman Reiss <me@silverwind.io>
1 parent e72749b commit 0bc14b6

File tree

4 files changed

+57
-19
lines changed

4 files changed

+57
-19
lines changed

doc/api/dns.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ corresponding lookup methods.
195195
On error, `err` is an [`Error`][] object, where `err.code` is
196196
one of the error codes listed [here](#dns_error_codes).
197197

198-
## dns.resolve4(hostname, callback)
198+
## dns.resolve4(hostname[, options], callback)
199199
<!-- YAML
200200
added: v0.1.16
201201
-->
@@ -205,6 +205,13 @@ Uses the DNS protocol to resolve a IPv4 addresses (`A` records) for the
205205
will contain an array of IPv4 addresses (e.g.
206206
`['74.125.79.104', '74.125.79.105', '74.125.79.106']`).
207207

208+
* `hostname` {String} Hostname to resolve.
209+
* `options` {Object}
210+
* `ttl` {Boolean} Retrieve the Time-To-Live value (TTL) of each record.
211+
The callback receives an array of `{ address: '1.2.3.4', ttl: 60 }` objects
212+
rather than an array of strings. The TTL is expressed in seconds.
213+
* `callback` {Function} An `(err, result)` callback function.
214+
208215
## dns.resolve6(hostname, callback)
209216
<!-- YAML
210217
added: v0.1.16

lib/dns.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,10 @@ exports.lookupService = function(host, port, callback) {
211211
};
212212

213213

214-
function onresolve(err, result) {
214+
function onresolve(err, result, ttls) {
215+
if (ttls && this.ttl)
216+
result = result.map((address, index) => ({ address, ttl: ttls[index] }));
217+
215218
if (err)
216219
this.callback(errnoException(err, this.bindingName, this.hostname));
217220
else
@@ -222,7 +225,13 @@ function onresolve(err, result) {
222225
function resolver(bindingName) {
223226
var binding = cares[bindingName];
224227

225-
return function query(name, callback) {
228+
return function query(name, /* options, */ callback) {
229+
var options;
230+
if (arguments.length > 2) {
231+
options = callback;
232+
callback = arguments[2];
233+
}
234+
226235
if (typeof name !== 'string') {
227236
throw new Error('"name" argument must be a string');
228237
} else if (typeof callback !== 'function') {
@@ -235,6 +244,7 @@ function resolver(bindingName) {
235244
req.callback = callback;
236245
req.hostname = name;
237246
req.oncomplete = onresolve;
247+
req.ttl = !!(options && options.ttl);
238248
var err = binding(req, name);
239249
if (err) throw errnoException(err, bindingName);
240250
callback.immediately = true;

src/cares_wrap.cc

+17-16
Original file line numberDiff line numberDiff line change
@@ -337,25 +337,17 @@ class QueryWrap : public AsyncWrap {
337337
delete wrap;
338338
}
339339

340-
void CallOnComplete(Local<Value> answer) {
341-
HandleScope handle_scope(env()->isolate());
342-
Context::Scope context_scope(env()->context());
343-
Local<Value> argv[] = {
344-
Integer::New(env()->isolate(), 0),
345-
answer
346-
};
347-
MakeCallback(env()->oncomplete_string(), arraysize(argv), argv);
348-
}
349-
350-
void CallOnComplete(Local<Value> answer, Local<Value> family) {
340+
void CallOnComplete(Local<Value> answer,
341+
Local<Value> extra = Local<Value>()) {
351342
HandleScope handle_scope(env()->isolate());
352343
Context::Scope context_scope(env()->context());
353344
Local<Value> argv[] = {
354345
Integer::New(env()->isolate(), 0),
355346
answer,
356-
family
347+
extra
357348
};
358-
MakeCallback(env()->oncomplete_string(), arraysize(argv), argv);
349+
const int argc = arraysize(argv) - extra.IsEmpty();
350+
MakeCallback(env()->oncomplete_string(), argc, argv);
359351
}
360352

361353
void ParseError(int status) {
@@ -401,18 +393,27 @@ class QueryAWrap: public QueryWrap {
401393
HandleScope handle_scope(env()->isolate());
402394
Context::Scope context_scope(env()->context());
403395

404-
struct hostent* host;
396+
hostent* host;
397+
ares_addrttl addrttls[256];
398+
int naddrttls = arraysize(addrttls);
405399

406-
int status = ares_parse_a_reply(buf, len, &host, nullptr, nullptr);
400+
int status = ares_parse_a_reply(buf, len, &host, addrttls, &naddrttls);
407401
if (status != ARES_SUCCESS) {
408402
ParseError(status);
409403
return;
410404
}
411405

412406
Local<Array> addresses = HostentToAddresses(env(), host);
407+
Local<Array> ttls = Array::New(env()->isolate(), naddrttls);
408+
409+
auto context = env()->context();
410+
for (int i = 0; i < naddrttls; i += 1) {
411+
auto value = Integer::New(env()->isolate(), addrttls[i].ttl);
412+
ttls->Set(context, i, value).FromJust();
413+
}
413414
ares_free_hostent(host);
414415

415-
this->CallOnComplete(addresses);
416+
CallOnComplete(addresses, ttls);
416417
}
417418
};
418419

test/internet/test-dns.js

+20
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,26 @@ TEST(function test_reverse_bogus(done) {
6060
done();
6161
});
6262

63+
TEST(function test_resolve4_ttl(done) {
64+
const req = dns.resolve4('google.com', { ttl: true }, function(err, result) {
65+
assert.ifError(err);
66+
assert.ok(result.length > 0);
67+
68+
for (let i = 0; i < result.length; i++) {
69+
const item = result[i];
70+
assert.ok(item);
71+
assert.strictEqual(typeof item, 'object');
72+
assert.strictEqual(typeof item.ttl, 'number');
73+
assert.strictEqual(typeof item.address, 'string');
74+
assert.ok(item.ttl > 0);
75+
assert.ok(isIPv4(item.address));
76+
}
77+
78+
done();
79+
});
80+
81+
checkWrap(req);
82+
});
6383

6484
TEST(function test_resolveMx(done) {
6585
const req = dns.resolveMx('gmail.com', function(err, result) {

0 commit comments

Comments
 (0)