Skip to content

Commit cd6397c

Browse files
blainery
authored andcommitted
Add support for MX, TXT, and SRV records in DNS module.
1 parent c420c89 commit cd6397c

5 files changed

Lines changed: 373 additions & 33 deletions

File tree

doc/api.txt

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,22 +1470,50 @@ resolution.addErrback(function (code, msg) {
14701470
});
14711471
-------------------------------------------------------------------------
14721472

1473+
+dns.resolve(domain, rrtype = 'A')+::
14731474

1474-
+dns.resolve4(domain)+::
1475-
1476-
Resolves a domain (e.g. +"google.com"+) into an array of IPv4 addresses (e.g.
1477-
+["74.125.79.104", "74.125.79.105", "74.125.79.106"]+).
1475+
Resolves a domain (e.g. +"google.com"+) into an array of the record types
1476+
specified by rrtype. Valid rrtypes are +A+ (IPV4 addresses), +AAAA+ (IPV6
1477+
addresses), +MX+ (mail exchange records), +TXT+ (text records), +SRV+
1478+
(SRV records), and +PTR+ (used for reverse IP lookups).
14781479
This function returns a promise.
14791480
- on success: returns +addresses, ttl, cname+. +ttl+ (time-to-live) is an integer
14801481
specifying the number of seconds this result is valid for. +cname+ is the
14811482
canonical name for the query.
1483+
The type of each item in +addresses+ is determined by the record type, and
1484+
described in the documentation for the corresponding lookup methods below.
14821485
- on error: returns +code, msg+. +code+ is one of the error codes listed
14831486
below and +msg+ is a string describing the error in English.
14841487

1488+
+dns.resolve4(domain)+::
1489+
1490+
The same as +dns.resolve()+, but only for IPv4 queries (+A+ records).
1491+
+addresses+ is an array of IPv4 addresses (e.g. +["74.125.79.104",
1492+
"74.125.79.105", "74.125.79.106"]+).
1493+
14851494
+dns.resolve6(domain)+::
14861495

14871496
The same as +dns.resolve4()+ except for IPv6 queries (an +AAAA+ query).
14881497

1498+
+dns.resolveMx(domain)+::
1499+
1500+
The same as +dns.resolve()+, but only for mail exchange queries (+MX+ records).
1501+
+addresses+ is an array of MX records, each with a priority and an exchange
1502+
attribute (e.g. +[{"priority": 10, "exchange": "mx.example.com"},...]+).
1503+
1504+
+dns.resolveTxt(domain)+::
1505+
1506+
The same as +dns.resolve()+, but only for text queries (+TXT+ records).
1507+
+addresses+ is an array of the text records available for +domain+ (e.g.,
1508+
+["v=spf1 ip4:0.0.0.0 ~all"]+).
1509+
1510+
+dns.resolveSrv(domain)+::
1511+
1512+
The same as +dns.resolve()+, but only for service records (+SRV+ records).
1513+
+addresses+ is an array of the SRV records available for +domain+. Properties
1514+
of SRV records are priority, weight, port, and name (e.g., +[{"priority": 10,
1515+
{"weight": 5, "port": 21223, "name": "service.example.com"}, ...]+).
1516+
14891517
+dns.reverse(ip)+::
14901518

14911519
Reverse resolves an ip address to an array of domain names.

lib/dns.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@ function callback (promise) {
1010
}
1111
}
1212

13+
exports.resolve = function (domain, type) {
14+
type = (type || 'a').toUpperCase();
15+
16+
var resolveFunc = resolveMap[type];
17+
18+
if (typeof(resolveFunc) == 'function') {
19+
return resolveFunc(domain);
20+
} else {
21+
return undefined;
22+
}
23+
}
24+
1325
exports.resolve4 = function (domain) {
1426
var promise = new events.Promise();
1527
process.dns.resolve4(domain, callback(promise));
@@ -22,6 +34,24 @@ exports.resolve6 = function (domain) {
2234
return promise;
2335
};
2436

37+
exports.resolveMx = function (domain) {
38+
var promise = new process.Promise();
39+
process.dns.resolveMx(domain, callback(promise));
40+
return promise;
41+
};
42+
43+
exports.resolveTxt = function (domain) {
44+
var promise = new process.Promise();
45+
process.dns.resolveTxt(domain, callback(promise));
46+
return promise;
47+
};
48+
49+
exports.resolveSrv = function (domain) {
50+
var promise = new process.Promise();
51+
process.dns.resolveSrv(domain, callback(promise));
52+
return promise;
53+
}
54+
2555
exports.reverse = function (ip) {
2656
var promise = new events.Promise();
2757
process.dns.reverse(ip, callback(promise));
@@ -47,3 +77,12 @@ exports.NOMEM = process.dns.NOMEM;
4777

4878
// the query is malformed.
4979
exports.BADQUERY = process.dns.BADQUERY;
80+
81+
resolveMap = {
82+
'A': exports.resolve4,
83+
'AAAA': exports.resolve6,
84+
'MX': exports.resolveMx,
85+
'TXT': exports.resolveTxt,
86+
'SRV': exports.resolveSrv,
87+
'PTR': exports.reverse,
88+
};

src/node_dns.cc

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ static ev_io io_watcher;
2020
static ev_timer timer_watcher;
2121

2222
static Persistent<String> errno_symbol;
23+
static Persistent<String> exchange_symbol;
24+
static Persistent<String> priority_symbol;
25+
static Persistent<String> weight_symbol;
26+
static Persistent<String> port_symbol;
27+
static Persistent<String> name_symbol;
2328

2429
static inline Persistent<Function>* cb_persist(const Local<Value> &v) {
2530
Persistent<Function> *fn = new Persistent<Function>();
@@ -174,6 +179,145 @@ static void AfterResolveA6(struct dns_ctx *ctx,
174179
cb_destroy(cb);
175180
}
176181

182+
static void AfterResolveMX(struct dns_ctx *ctx,
183+
struct dns_rr_mx *result,
184+
void *data) {
185+
assert(ctx == &dns_defctx);
186+
187+
HandleScope scope;
188+
189+
Persistent<Function> *cb = cb_unwrap(data);
190+
191+
if (result == NULL) {
192+
ResolveError(cb);
193+
cb_destroy(cb);
194+
return;
195+
}
196+
197+
/* canonical name */
198+
Local<String> cname = String::New(result->dnsmx_cname);
199+
200+
/* Time-To-Live (TTL) value */
201+
Local<Integer> ttl = Integer::New(result->dnsmx_ttl);
202+
203+
Local<Array> exchanges = Array::New(result->dnsmx_nrr);
204+
for (int i = 0; i < result->dnsmx_nrr; i++) {
205+
HandleScope loop_scope;
206+
207+
Local<Object> exchange = Object::New();
208+
209+
struct dns_mx *mx = &(result->dnsmx_mx[i]);
210+
exchange->Set(exchange_symbol, String::New(mx->name));
211+
exchange->Set(priority_symbol, Integer::New(mx->priority));
212+
213+
exchanges->Set(Integer::New(i), exchange);
214+
}
215+
216+
Local<Value> argv[3] = { exchanges, ttl, cname };
217+
218+
TryCatch try_catch;
219+
220+
(*cb)->Call(Context::GetCurrent()->Global(), 3, argv);
221+
222+
if (try_catch.HasCaught()) {
223+
FatalException(try_catch);
224+
}
225+
226+
cb_destroy(cb);
227+
}
228+
229+
static void AfterResolveTXT(struct dns_ctx *ctx,
230+
struct dns_rr_txt *result,
231+
void *data) {
232+
assert(ctx == &dns_defctx);
233+
234+
HandleScope scope;
235+
236+
Persistent<Function> *cb = cb_unwrap(data);
237+
238+
if (result == NULL) {
239+
ResolveError(cb);
240+
cb_destroy(cb);
241+
return;
242+
}
243+
244+
/* canonical name */
245+
Local<String> cname = String::New(result->dnstxt_cname);
246+
247+
/* Time-To-Live (TTL) value */
248+
Local<Integer> ttl = Integer::New(result->dnstxt_ttl);
249+
250+
Local<Array> records = Array::New(result->dnstxt_nrr);
251+
for (int i = 0; i < result->dnstxt_nrr; i++) {
252+
HandleScope loop_scope;
253+
254+
struct dns_txt *record = &(result->dnstxt_txt[i]);
255+
const char *txt = (const char *)record->txt;
256+
records->Set(Integer::New(i), String::New(txt));
257+
}
258+
259+
Local<Value> argv[3] = { records, ttl, cname };
260+
261+
TryCatch try_catch;
262+
263+
(*cb)->Call(Context::GetCurrent()->Global(), 3, argv);
264+
265+
if (try_catch.HasCaught()) {
266+
FatalException(try_catch);
267+
}
268+
269+
cb_destroy(cb);
270+
}
271+
272+
static void AfterResolveSRV(struct dns_ctx *ctx,
273+
struct dns_rr_srv *result,
274+
void *data) {
275+
assert(ctx == &dns_defctx);
276+
277+
HandleScope scope;
278+
279+
Persistent<Function> *cb = cb_unwrap(data);
280+
281+
if (result == NULL) {
282+
ResolveError(cb);
283+
cb_destroy(cb);
284+
return;
285+
}
286+
287+
/* canonical name */
288+
Local<String> cname = String::New(result->dnssrv_cname);
289+
290+
/* Time-To-Live (TTL) value */
291+
Local<Integer> ttl = Integer::New(result->dnssrv_ttl);
292+
293+
Local<Array> records = Array::New(result->dnssrv_nrr);
294+
for (int i = 0; i < result->dnssrv_nrr; i++) {
295+
HandleScope loop_scope;
296+
297+
Local<Object> record = Object::New();
298+
299+
struct dns_srv *srv = &(result->dnssrv_srv[i]);
300+
record->Set(priority_symbol, Integer::New(srv->priority));
301+
record->Set(weight_symbol, Integer::New(srv->weight));
302+
record->Set(port_symbol, Integer::New(srv->port));
303+
record->Set(name_symbol, String::New(srv->name));
304+
305+
records->Set(Integer::New(i), record);
306+
}
307+
308+
Local<Value> argv[3] = { records, ttl, cname };
309+
310+
TryCatch try_catch;
311+
312+
(*cb)->Call(Context::GetCurrent()->Global(), 3, argv);
313+
314+
if (try_catch.HasCaught()) {
315+
FatalException(try_catch);
316+
}
317+
318+
cb_destroy(cb);
319+
}
320+
177321
static Handle<Value> ResolveA(int type, const Arguments& args) {
178322
HandleScope scope;
179323

@@ -194,6 +338,18 @@ static Handle<Value> ResolveA(int type, const Arguments& args) {
194338
query = dns_submit_a6(NULL, *name, 0, AfterResolveA6, cb_persist(args[1]));
195339
break;
196340

341+
case DNS_T_MX:
342+
query = dns_submit_mx(NULL, *name, 0, AfterResolveMX, cb_persist(args[1]));
343+
break;
344+
345+
case DNS_T_TXT:
346+
query = dns_submit_txt(NULL, *name, DNS_C_IN, 0, AfterResolveTXT, cb_persist(args[1]));
347+
break;
348+
349+
case DNS_T_SRV:
350+
query = dns_submit_srv(NULL, *name, NULL, NULL, 0, AfterResolveSRV, cb_persist(args[1]));
351+
break;
352+
197353
default:
198354
return ThrowException(Exception::Error(String::New("Unsupported type")));
199355
}
@@ -213,6 +369,18 @@ static Handle<Value> ResolveA6(const Arguments& args) {
213369
return ResolveA(DNS_T_AAAA, args);
214370
}
215371

372+
static Handle<Value> ResolveMX(const Arguments& args) {
373+
return ResolveA(DNS_T_MX, args);
374+
}
375+
376+
static Handle<Value> ResolveTXT(const Arguments& args) {
377+
return ResolveA(DNS_T_TXT, args);
378+
}
379+
380+
static Handle<Value> ResolveSRV(const Arguments& args) {
381+
return ResolveA(DNS_T_SRV, args);
382+
}
383+
216384
static void AfterReverse(struct dns_ctx *ctx,
217385
struct dns_rr_ptr *result,
218386
void *data) {
@@ -312,6 +480,12 @@ void DNS::Initialize(Handle<Object> target) {
312480

313481
errno_symbol = NODE_PSYMBOL("errno");
314482

483+
exchange_symbol = NODE_PSYMBOL("exchange");
484+
priority_symbol = NODE_PSYMBOL("priority");
485+
weight_symbol = NODE_PSYMBOL("weight");
486+
port_symbol = NODE_PSYMBOL("port");
487+
name_symbol = NODE_PSYMBOL("name");
488+
315489
target->Set(String::NewSymbol("TEMPFAIL"), Integer::New(DNS_E_TEMPFAIL));
316490
target->Set(String::NewSymbol("PROTOCOL"), Integer::New(DNS_E_PROTOCOL));
317491
target->Set(String::NewSymbol("NXDOMAIN"), Integer::New(DNS_E_NXDOMAIN));
@@ -325,6 +499,15 @@ void DNS::Initialize(Handle<Object> target) {
325499
Local<FunctionTemplate> resolve6 = FunctionTemplate::New(ResolveA6);
326500
target->Set(String::NewSymbol("resolve6"), resolve6->GetFunction());
327501

502+
Local<FunctionTemplate> resolveMx = FunctionTemplate::New(ResolveMX);
503+
target->Set(String::NewSymbol("resolveMx"), resolveMx->GetFunction());
504+
505+
Local<FunctionTemplate> resolveTxt = FunctionTemplate::New(ResolveTXT);
506+
target->Set(String::NewSymbol("resolveTxt"), resolveTxt->GetFunction());
507+
508+
Local<FunctionTemplate> resolveSrv = FunctionTemplate::New(ResolveSRV);
509+
target->Set(String::NewSymbol("resolveSrv"), resolveSrv->GetFunction());
510+
328511
Local<FunctionTemplate> reverse = FunctionTemplate::New(Reverse);
329512
target->Set(String::NewSymbol("reverse"), reverse->GetFunction());
330513
}

test/mjsunit/disabled/test-dns.js

Lines changed: 0 additions & 29 deletions
This file was deleted.

0 commit comments

Comments
 (0)