Skip to content

Commit

Permalink
dgram: add custom lookup function in sockets
Browse files Browse the repository at this point in the history
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 <jasnell@gmail.com>
Reviewed-By: Wyatt Preul <wpreul@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
  • Loading branch information
cjihrig committed Aug 3, 2017
1 parent 46c3dd7 commit 5a05055
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 31 deletions.
38 changes: 21 additions & 17 deletions doc/api/dgram.md
Original file line number Diff line number Diff line change
Expand Up @@ -455,27 +455,30 @@ s.bind(1234, () => {
### dgram.createSocket(options[, callback])
<!-- YAML
added: v0.11.13
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/14560
description: The `lookup` option is supported.
-->

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

Creates a `dgram.Socket` object. The `options` argument is an object that
should contain a `type` field of either `udp4` or `udp6` and an optional
boolean `reuseAddr` field.

When `reuseAddr` is `true` [`socket.bind()`][] will reuse the address, even if
another process has already bound a socket on it. `reuseAddr` defaults to
`false`. The optional `callback` function is added as a listener for `'message'`
events.

Once the socket is created, calling [`socket.bind()`][] will instruct the
socket to begin listening for datagram messages. When `address` and `port` are
not passed to [`socket.bind()`][] the method will bind the socket to the "all
interfaces" address on a random port (it does the right thing for both `udp4`
and `udp6` sockets). The bound address and port can be retrieved using
[`socket.address().address`][] and [`socket.address().port`][].
Creates a `dgram.Socket` object. Once the socket is created, calling
[`socket.bind()`][] will instruct the socket to begin listening for datagram
messages. When `address` and `port` are not passed to [`socket.bind()`][] the
method will bind the socket to the "all interfaces" address on a random port
(it does the right thing for both `udp4` and `udp6` sockets). The bound address
and port can be retrieved using [`socket.address().address`][] and
[`socket.address().port`][].

### dgram.createSocket(type[, callback])
<!-- YAML
Expand Down Expand Up @@ -505,6 +508,7 @@ and `udp6` sockets). The bound address and port can be retrieved using
[`cluster`]: cluster.html
[`dgram.Socket#bind()`]: #dgram_socket_bind_options_callback
[`dgram.createSocket()`]: #dgram_dgram_createsocket_options_callback
[`dns.lookup()`]: dns.html#dns_dns_lookup_hostname_options_callback
[`socket.address().address`]: #dgram_socket_address
[`socket.address().port`]: #dgram_socket_address
[`socket.bind()`]: #dgram_socket_bind_port_address_callback
Expand Down
32 changes: 18 additions & 14 deletions lib/dgram.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,31 +46,32 @@ var cluster = null;
const errnoException = util._errnoException;
const exceptionWithHostPort = util._exceptionWithHostPort;

function lookup(address, family, callback) {
return dns.lookup(address, family, callback);
}


function lookup4(address, callback) {
function lookup4(lookup, address, callback) {
return lookup(address || '127.0.0.1', 4, callback);
}


function lookup6(address, callback) {
function lookup6(lookup, address, callback) {
return lookup(address || '::1', 6, callback);
}


function newHandle(type) {
function newHandle(type, lookup) {
if (lookup === undefined)
lookup = dns.lookup;
else if (typeof lookup !== 'function')
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'lookup', 'function');

if (type === 'udp4') {
const handle = new UDP();
handle.lookup = lookup4;
handle.lookup = lookup4.bind(handle, lookup);
return handle;
}

if (type === 'udp6') {
const handle = new UDP();
handle.lookup = lookup6;
handle.lookup = lookup6.bind(handle, lookup);
handle.bind = handle.bind6;
handle.send = handle.send6;
return handle;
Expand Down Expand Up @@ -100,13 +101,15 @@ function _createSocketHandle(address, port, addressType, fd, flags) {

function Socket(type, listener) {
EventEmitter.call(this);
var lookup;

if (type !== null && typeof type === 'object') {
var options = type;
type = options.type;
lookup = options.lookup;
}

var handle = newHandle(type);
var handle = newHandle(type, lookup);
handle.owner = this;

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

// defaulting address for bind to all interfaces
if (!address && this._handle.lookup === lookup4) {
address = '0.0.0.0';
} else if (!address && this._handle.lookup === lookup6) {
address = '::';
if (!address) {
if (this.type === 'udp4')
address = '0.0.0.0';
else
address = '::';
}

// resolve address first
Expand Down
47 changes: 47 additions & 0 deletions test/parallel/test-dgram-custom-lookup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const dgram = require('dgram');
const dns = require('dns');

{
// Verify that the provided lookup function is called.
const lookup = common.mustCall((host, family, callback) => {
dns.lookup(host, family, callback);
});

const socket = dgram.createSocket({ type: 'udp4', lookup });

socket.bind(common.mustCall(() => {
socket.close();
}));
}

{
// Verify that lookup defaults to dns.lookup().
const originalLookup = dns.lookup;

dns.lookup = common.mustCall((host, family, callback) => {
dns.lookup = originalLookup;
originalLookup(host, family, callback);
});

const socket = dgram.createSocket({ type: 'udp4' });

socket.bind(common.mustCall(() => {
socket.close();
}));
}

{
// Verify that non-functions throw.
[null, true, false, 0, 1, NaN, '', 'foo', {}, Symbol()].forEach((value) => {
assert.throws(() => {
dgram.createSocket({ type: 'udp4', lookup: value });
}, common.expectsError({
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "lookup" argument must be of type function'
}));
});
}

0 comments on commit 5a05055

Please sign in to comment.