Skip to content

Commit 5a05055

Browse files
committed
dgram: add custom lookup function in sockets
This commit adds support for custom DNS lookup functions in dgram sockets. This is similar to the existing feature in net sockets. Refs: #6189 PR-URL: #14560 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Wyatt Preul <[email protected]> Reviewed-By: Evan Lucas <[email protected]>
1 parent 46c3dd7 commit 5a05055

File tree

3 files changed

+86
-31
lines changed

3 files changed

+86
-31
lines changed

doc/api/dgram.md

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -455,27 +455,30 @@ s.bind(1234, () => {
455455
### dgram.createSocket(options[, callback])
456456
<!-- YAML
457457
added: v0.11.13
458+
changes:
459+
- version: REPLACEME
460+
pr-url: https://github.com/nodejs/node/pull/14560
461+
description: The `lookup` option is supported.
458462
-->
459463

460-
* `options` {Object}
461-
* `callback` {Function} Attached as a listener to `'message'` events.
464+
* `options` {Object} Available options are:
465+
* `type` {string} The family of socket. Must be either `'udp4'` or `'udp6'`.
466+
Required.
467+
* `reuseAddr` {boolean} When `true` [`socket.bind()`][] will reuse the
468+
address, even if another process has already bound a socket on it. Optional.
469+
Defaults to `false`.
470+
* `lookup` {Function} Custom lookup function. Defaults to [`dns.lookup()`][].
471+
Optional.
472+
* `callback` {Function} Attached as a listener for `'message'` events. Optional.
462473
* Returns: {dgram.Socket}
463474

464-
Creates a `dgram.Socket` object. The `options` argument is an object that
465-
should contain a `type` field of either `udp4` or `udp6` and an optional
466-
boolean `reuseAddr` field.
467-
468-
When `reuseAddr` is `true` [`socket.bind()`][] will reuse the address, even if
469-
another process has already bound a socket on it. `reuseAddr` defaults to
470-
`false`. The optional `callback` function is added as a listener for `'message'`
471-
events.
472-
473-
Once the socket is created, calling [`socket.bind()`][] will instruct the
474-
socket to begin listening for datagram messages. When `address` and `port` are
475-
not passed to [`socket.bind()`][] the method will bind the socket to the "all
476-
interfaces" address on a random port (it does the right thing for both `udp4`
477-
and `udp6` sockets). The bound address and port can be retrieved using
478-
[`socket.address().address`][] and [`socket.address().port`][].
475+
Creates a `dgram.Socket` object. Once the socket is created, calling
476+
[`socket.bind()`][] will instruct the socket to begin listening for datagram
477+
messages. When `address` and `port` are not passed to [`socket.bind()`][] the
478+
method will bind the socket to the "all interfaces" address on a random port
479+
(it does the right thing for both `udp4` and `udp6` sockets). The bound address
480+
and port can be retrieved using [`socket.address().address`][] and
481+
[`socket.address().port`][].
479482

480483
### dgram.createSocket(type[, callback])
481484
<!-- YAML
@@ -505,6 +508,7 @@ and `udp6` sockets). The bound address and port can be retrieved using
505508
[`cluster`]: cluster.html
506509
[`dgram.Socket#bind()`]: #dgram_socket_bind_options_callback
507510
[`dgram.createSocket()`]: #dgram_dgram_createsocket_options_callback
511+
[`dns.lookup()`]: dns.html#dns_dns_lookup_hostname_options_callback
508512
[`socket.address().address`]: #dgram_socket_address
509513
[`socket.address().port`]: #dgram_socket_address
510514
[`socket.bind()`]: #dgram_socket_bind_port_address_callback

lib/dgram.js

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,31 +46,32 @@ var cluster = null;
4646
const errnoException = util._errnoException;
4747
const exceptionWithHostPort = util._exceptionWithHostPort;
4848

49-
function lookup(address, family, callback) {
50-
return dns.lookup(address, family, callback);
51-
}
52-
5349

54-
function lookup4(address, callback) {
50+
function lookup4(lookup, address, callback) {
5551
return lookup(address || '127.0.0.1', 4, callback);
5652
}
5753

5854

59-
function lookup6(address, callback) {
55+
function lookup6(lookup, address, callback) {
6056
return lookup(address || '::1', 6, callback);
6157
}
6258

6359

64-
function newHandle(type) {
60+
function newHandle(type, lookup) {
61+
if (lookup === undefined)
62+
lookup = dns.lookup;
63+
else if (typeof lookup !== 'function')
64+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'lookup', 'function');
65+
6566
if (type === 'udp4') {
6667
const handle = new UDP();
67-
handle.lookup = lookup4;
68+
handle.lookup = lookup4.bind(handle, lookup);
6869
return handle;
6970
}
7071

7172
if (type === 'udp6') {
7273
const handle = new UDP();
73-
handle.lookup = lookup6;
74+
handle.lookup = lookup6.bind(handle, lookup);
7475
handle.bind = handle.bind6;
7576
handle.send = handle.send6;
7677
return handle;
@@ -100,13 +101,15 @@ function _createSocketHandle(address, port, addressType, fd, flags) {
100101

101102
function Socket(type, listener) {
102103
EventEmitter.call(this);
104+
var lookup;
103105

104106
if (type !== null && typeof type === 'object') {
105107
var options = type;
106108
type = options.type;
109+
lookup = options.lookup;
107110
}
108111

109-
var handle = newHandle(type);
112+
var handle = newHandle(type, lookup);
110113
handle.owner = this;
111114

112115
this._handle = handle;
@@ -186,10 +189,11 @@ Socket.prototype.bind = function(port_, address_ /*, callback*/) {
186189
}
187190

188191
// defaulting address for bind to all interfaces
189-
if (!address && this._handle.lookup === lookup4) {
190-
address = '0.0.0.0';
191-
} else if (!address && this._handle.lookup === lookup6) {
192-
address = '::';
192+
if (!address) {
193+
if (this.type === 'udp4')
194+
address = '0.0.0.0';
195+
else
196+
address = '::';
193197
}
194198

195199
// resolve address first
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const dgram = require('dgram');
5+
const dns = require('dns');
6+
7+
{
8+
// Verify that the provided lookup function is called.
9+
const lookup = common.mustCall((host, family, callback) => {
10+
dns.lookup(host, family, callback);
11+
});
12+
13+
const socket = dgram.createSocket({ type: 'udp4', lookup });
14+
15+
socket.bind(common.mustCall(() => {
16+
socket.close();
17+
}));
18+
}
19+
20+
{
21+
// Verify that lookup defaults to dns.lookup().
22+
const originalLookup = dns.lookup;
23+
24+
dns.lookup = common.mustCall((host, family, callback) => {
25+
dns.lookup = originalLookup;
26+
originalLookup(host, family, callback);
27+
});
28+
29+
const socket = dgram.createSocket({ type: 'udp4' });
30+
31+
socket.bind(common.mustCall(() => {
32+
socket.close();
33+
}));
34+
}
35+
36+
{
37+
// Verify that non-functions throw.
38+
[null, true, false, 0, 1, NaN, '', 'foo', {}, Symbol()].forEach((value) => {
39+
assert.throws(() => {
40+
dgram.createSocket({ type: 'udp4', lookup: value });
41+
}, common.expectsError({
42+
code: 'ERR_INVALID_ARG_TYPE',
43+
type: TypeError,
44+
message: 'The "lookup" argument must be of type function'
45+
}));
46+
});
47+
}

0 commit comments

Comments
 (0)