diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index 5cfae82f47b..900c1a1e66f 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -2332,16 +2332,16 @@ void SetLocalAddress(const FunctionCallbackInfo& args) { // to 0 (any). if (uv_inet_pton(AF_INET, *ip0, &addr0) == 0) { - ares_set_local_ip4(channel->cares_channel(), nbytes::ReadUint32BE(addr0)); type0 = 4; } else if (uv_inet_pton(AF_INET6, *ip0, &addr0) == 0) { - ares_set_local_ip6(channel->cares_channel(), addr0); type0 = 6; } else { THROW_ERR_INVALID_ARG_VALUE(env, "Invalid IP address."); return; } + // Validate the second argument before applying anything, so that a call + // that throws leaves the channel unchanged. if (!args[1]->IsUndefined()) { CHECK(args[1]->IsString()); node::Utf8Value ip1(isolate, args[1]); @@ -2350,16 +2350,11 @@ void SetLocalAddress(const FunctionCallbackInfo& args) { if (type0 == 4) { THROW_ERR_INVALID_ARG_VALUE(env, "Cannot specify two IPv4 addresses."); return; - } else { - ares_set_local_ip4(channel->cares_channel(), - nbytes::ReadUint32BE(addr1)); } } else if (uv_inet_pton(AF_INET6, *ip1, &addr1) == 0) { if (type0 == 6) { THROW_ERR_INVALID_ARG_VALUE(env, "Cannot specify two IPv6 addresses."); return; - } else { - ares_set_local_ip6(channel->cares_channel(), addr1); } } else { THROW_ERR_INVALID_ARG_VALUE(env, "Invalid IP address."); @@ -2367,12 +2362,15 @@ void SetLocalAddress(const FunctionCallbackInfo& args) { } } else { // No second arg specified - if (type0 == 4) { - memset(&addr1, 0, sizeof(addr1)); - ares_set_local_ip6(channel->cares_channel(), addr1); - } else { - ares_set_local_ip4(channel->cares_channel(), 0); - } + memset(&addr1, 0, sizeof(addr1)); + } + + if (type0 == 4) { + ares_set_local_ip4(channel->cares_channel(), nbytes::ReadUint32BE(addr0)); + ares_set_local_ip6(channel->cares_channel(), addr1); + } else { + ares_set_local_ip6(channel->cares_channel(), addr0); + ares_set_local_ip4(channel->cares_channel(), nbytes::ReadUint32BE(addr1)); } } diff --git a/test/parallel/test-dns-setlocaladdress-rejected.js b/test/parallel/test-dns-setlocaladdress-rejected.js new file mode 100644 index 00000000000..e0a316fb66d --- /dev/null +++ b/test/parallel/test-dns-setlocaladdress-rejected.js @@ -0,0 +1,45 @@ +'use strict'; +const common = require('../common'); + +// Verifies that a call to resolver.setLocalAddress() that throws leaves the +// previously configured local address in place. + +// Binding to 127.0.0.0/8 addresses other than 127.0.0.1 works out of the box +// on Linux only. +if (!common.isLinux) + common.skip('requires the whole 127.0.0.0/8 range on the loopback interface'); + +const assert = require('assert'); +const dgram = require('dgram'); +const { Resolver } = require('dns'); + +const resolver = new Resolver({ timeout: 1000, tries: 1 }); +const server = dgram.createSocket('udp4'); + +server.on('message', common.mustCall((msg, rinfo) => { + assert.strictEqual(rinfo.address, '127.0.0.2'); + server.close(); + resolver.cancel(); +})); + +server.bind(0, '127.0.0.1', common.mustCall(() => { + resolver.setServers([`127.0.0.1:${server.address().port}`]); + resolver.setLocalAddress('127.0.0.2'); + + assert.throws(() => { + resolver.setLocalAddress('127.0.0.3', '127.0.0.4'); + }, { + code: 'ERR_INVALID_ARG_VALUE', + message: 'Cannot specify two IPv4 addresses.', + }); + assert.throws(() => { + resolver.setLocalAddress('127.0.0.5', 'bad'); + }, { + code: 'ERR_INVALID_ARG_VALUE', + message: 'Invalid IP address.', + }); + + resolver.resolve4('example.test', common.mustCall((err) => { + assert.strictEqual(err.code, 'ECANCELLED'); + })); +}));