Skip to content

Commit ed79c98

Browse files
daguejMylesBorins
authored andcommitted
dns: add setLocalAddress to Resolver
Fixes: #34818 PR-URL: #34824 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Roman Reiss <[email protected]>
1 parent f41c28c commit ed79c98

File tree

5 files changed

+137
-0
lines changed

5 files changed

+137
-0
lines changed

‎doc/api/dns.md‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,27 @@ added: v8.3.0
116116
Cancel all outstanding DNS queries made by this resolver. The corresponding
117117
callbacks will be called with an error with code `ECANCELLED`.
118118

119+
### `resolver.setLocalAddress([ipv4][, ipv6])`
120+
<!-- YAML
121+
added: REPLACEME
122+
-->
123+
124+
*`ipv4`{string} A string representation of an IPv4 address.
125+
**Default:**`'0.0.0.0'`
126+
*`ipv6`{string} A string representation of an IPv6 address.
127+
**Default:**`'::0'`
128+
129+
The resolver instance will send its requests from the specified IP address.
130+
This allows programs to specify outbound interfaces when used on multi-homed
131+
systems.
132+
133+
If a v4 or v6 address is not specified, it is set to the default, and the
134+
operating system will choose a local address automatically.
135+
136+
The resolver will use the v4 local address when making requests to IPv4 DNS
137+
servers, and the v6 local address when making requests to IPv6 DNS servers.
138+
The `rrtype` of resolution requests has no impact on the local address used.
139+
119140
## `dns.getServers()`
120141
<!-- YAML
121142
added: v0.11.3

‎lib/internal/dns/promises.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ class Resolver{
217217

218218
Resolver.prototype.getServers=CallbackResolver.prototype.getServers;
219219
Resolver.prototype.setServers=CallbackResolver.prototype.setServers;
220+
Resolver.prototype.setLocalAddress=CallbackResolver.prototype.setLocalAddress;
220221
Resolver.prototype.resolveAny=resolveMap.ANY=resolver('queryAny');
221222
Resolver.prototype.resolve4=resolveMap.A=resolver('queryA');
222223
Resolver.prototype.resolve6=resolveMap.AAAA=resolver('queryAaaa');

‎lib/internal/dns/utils.js‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,18 @@ class Resolver{
114114
thrownewERR_DNS_SET_SERVERS_FAILED(err,servers);
115115
}
116116
}
117+
118+
setLocalAddress(ipv4,ipv6){
119+
if(typeofipv4!=='string'){
120+
thrownewERR_INVALID_ARG_TYPE('ipv4','String',ipv4);
121+
}
122+
123+
if(typeofipv6!=='string'&&ipv6!==undefined){
124+
thrownewERR_INVALID_ARG_TYPE('ipv6',['String','undefined'],ipv6);
125+
}
126+
127+
this._handle.setLocalAddress(ipv4,ipv6);
128+
}
117129
}
118130

119131
letdefaultResolver=newResolver();

‎src/cares_wrap.cc‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include"req_wrap-inl.h"
3030
#include"util-inl.h"
3131
#include"uv.h"
32+
#include"node_errors.h"
3233

3334
#include<cerrno>
3435
#include<cstring>
@@ -2149,6 +2150,70 @@ void SetServers(const FunctionCallbackInfo<Value>& args){
21492150
args.GetReturnValue().Set(err);
21502151
}
21512152

2153+
voidSetLocalAddress(const FunctionCallbackInfo<Value>& args){
2154+
Environment* env = Environment::GetCurrent(args);
2155+
ChannelWrap* channel;
2156+
ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder());
2157+
2158+
CHECK_EQ(args.Length(), 2);
2159+
CHECK(args[0]->IsString());
2160+
2161+
Isolate* isolate = args.GetIsolate();
2162+
node::Utf8Value ip0(isolate, args[0]);
2163+
2164+
unsignedchar addr0[sizeof(structin6_addr)];
2165+
unsignedchar addr1[sizeof(structin6_addr)];
2166+
int type0 = 0;
2167+
2168+
// This function accepts 2 arguments. The first may be either an IPv4
2169+
// address or an IPv6 address. If present, the second argument must be the
2170+
// other type of address. Otherwise, the unspecified type of IP is set
2171+
// to 0 (any).
2172+
2173+
if (uv_inet_pton(AF_INET, *ip0, &addr0) == 0){
2174+
ares_set_local_ip4(channel->cares_channel(), ReadUint32BE(addr0));
2175+
type0 = 4;
2176+
} elseif (uv_inet_pton(AF_INET6, *ip0, &addr0) == 0){
2177+
ares_set_local_ip6(channel->cares_channel(), addr0);
2178+
type0 = 6;
2179+
} else{
2180+
THROW_ERR_INVALID_ARG_VALUE(env, "Invalid IP address.");
2181+
return;
2182+
}
2183+
2184+
if (!args[1]->IsUndefined()){
2185+
CHECK(args[1]->IsString());
2186+
node::Utf8Value ip1(isolate, args[1]);
2187+
2188+
if (uv_inet_pton(AF_INET, *ip1, &addr1) == 0){
2189+
if (type0 == 4){
2190+
THROW_ERR_INVALID_ARG_VALUE(env, "Cannot specify two IPv4 addresses.");
2191+
return;
2192+
} else{
2193+
ares_set_local_ip4(channel->cares_channel(), ReadUint32BE(addr1));
2194+
}
2195+
} elseif (uv_inet_pton(AF_INET6, *ip1, &addr1) == 0){
2196+
if (type0 == 6){
2197+
THROW_ERR_INVALID_ARG_VALUE(env, "Cannot specify two IPv6 addresses.");
2198+
return;
2199+
} else{
2200+
ares_set_local_ip6(channel->cares_channel(), addr1);
2201+
}
2202+
} else{
2203+
THROW_ERR_INVALID_ARG_VALUE(env, "Invalid IP address.");
2204+
return;
2205+
}
2206+
} else{
2207+
// No second arg specifed
2208+
if (type0 == 4){
2209+
memset(&addr1, 0, sizeof(addr1));
2210+
ares_set_local_ip6(channel->cares_channel(), addr1);
2211+
} else{
2212+
ares_set_local_ip4(channel->cares_channel(), 0);
2213+
}
2214+
}
2215+
}
2216+
21522217
voidCancel(const FunctionCallbackInfo<Value>& args){
21532218
ChannelWrap* channel;
21542219
ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder());
@@ -2250,6 +2315,7 @@ void Initialize(Local<Object> target,
22502315

22512316
env->SetProtoMethodNoSideEffect(channel_wrap, "getServers", GetServers);
22522317
env->SetProtoMethod(channel_wrap, "setServers", SetServers);
2318+
env->SetProtoMethod(channel_wrap, "setLocalAddress", SetLocalAddress);
22532319
env->SetProtoMethod(channel_wrap, "cancel", Cancel);
22542320

22552321
Local<String> channelWrapString =
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict';
2+
require('../common');
3+
constassert=require('assert');
4+
5+
constdns=require('dns');
6+
constresolver=newdns.Resolver();
7+
constpromiseResolver=newdns.promises.Resolver();
8+
9+
// Verifies that setLocalAddress succeeds with IPv4 and IPv6 addresses
10+
{
11+
resolver.setLocalAddress('127.0.0.1');
12+
resolver.setLocalAddress('::1');
13+
resolver.setLocalAddress('127.0.0.1','::1');
14+
promiseResolver.setLocalAddress('127.0.0.1','::1');
15+
}
16+
17+
// Verify that setLocalAddress throws if called with an invalid address
18+
{
19+
assert.throws(()=>{
20+
resolver.setLocalAddress('127.0.0.1','127.0.0.1');
21+
},Error);
22+
assert.throws(()=>{
23+
resolver.setLocalAddress('::1','::1');
24+
},Error);
25+
assert.throws(()=>{
26+
resolver.setLocalAddress('bad');
27+
},Error);
28+
assert.throws(()=>{
29+
resolver.setLocalAddress(123);
30+
},Error);
31+
assert.throws(()=>{
32+
resolver.setLocalAddress();
33+
},Error);
34+
assert.throws(()=>{
35+
promiseResolver.setLocalAddress();
36+
},Error);
37+
}

0 commit comments

Comments
(0)