GeezerVison is showing github.com — dns: add setLocalAddress to Resolver · nodejs/node@548f91a
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/dns.md +21Lines changed: 21 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -117,6 +117,27 @@ added: v8.3.0 | 117 | 117 | Cancel all outstanding DNS queries made by this resolver. The corresponding | 118 | 118 | callbacks will be called with an error with code `ECANCELLED`. | 119 | 119 |
| | 120 | +### `resolver.setLocalAddress([ipv4][, ipv6])` | | 121 | +<!-- YAML | | 122 | +added: REPLACEME | | 123 | +--> | | 124 | + | | 125 | +* `ipv4` {string} A string representation of an IPv4 address. | | 126 | + **Default:** `'0.0.0.0'` | | 127 | +* `ipv6` {string} A string representation of an IPv6 address. | | 128 | + **Default:** `'::0'` | | 129 | + | | 130 | +The resolver instance will send its requests from the specified IP address. | | 131 | +This allows programs to specify outbound interfaces when used on multi-homed | | 132 | +systems. | | 133 | + | | 134 | +If a v4 or v6 address is not specified, it is set to the default, and the | | 135 | +operating system will choose a local address automatically. | | 136 | + | | 137 | +The resolver will use the v4 local address when making requests to IPv4 DNS | | 138 | +servers, and the v6 local address when making requests to IPv6 DNS servers. | | 139 | +The `rrtype` of resolution requests has no impact on the local address used. | | 140 | + | 120 | 141 | ## `dns.getServers()` | 121 | 142 | <!-- YAML | 122 | 143 | added: v0.11.3 | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/dns/promises.js +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -217,6 +217,7 @@ class Resolver { | 217 | 217 |
| 218 | 218 | Resolver.prototype.getServers = CallbackResolver.prototype.getServers; | 219 | 219 | Resolver.prototype.setServers = CallbackResolver.prototype.setServers; | | 220 | +Resolver.prototype.setLocalAddress = CallbackResolver.prototype.setLocalAddress; | 220 | 221 | Resolver.prototype.resolveAny = resolveMap.ANY = resolver('queryAny'); | 221 | 222 | Resolver.prototype.resolve4 = resolveMap.A = resolver('queryA'); | 222 | 223 | Resolver.prototype.resolve6 = resolveMap.AAAA = resolver('queryAaaa'); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/dns/utils.js +12Lines changed: 12 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -114,6 +114,18 @@ class Resolver { | 114 | 114 | throw new ERR_DNS_SET_SERVERS_FAILED(err, servers); | 115 | 115 | } | 116 | 116 | } | | 117 | + | | 118 | + setLocalAddress(ipv4, ipv6) { | | 119 | + if (typeof ipv4 !== 'string') { | | 120 | + throw new ERR_INVALID_ARG_TYPE('ipv4', 'String', ipv4); | | 121 | + } | | 122 | + | | 123 | + if (typeof ipv6 !== 'string' && ipv6 !== undefined) { | | 124 | + throw new ERR_INVALID_ARG_TYPE('ipv6', ['String', 'undefined'], ipv6); | | 125 | + } | | 126 | + | | 127 | + this._handle.setLocalAddress(ipv4, ipv6); | | 128 | + } | 117 | 129 | } | 118 | 130 |
| 119 | 131 | let defaultResolver = new Resolver(); | |
Collapse file Copy file name to clipboardExpand all lines: src/cares_wrap.cc +66Lines changed: 66 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 29 | 29 | #include "req_wrap-inl.h" | 30 | 30 | #include "util-inl.h" | 31 | 31 | #include "uv.h" | | 32 | +#include "node_errors.h" | 32 | 33 |
| 33 | 34 | #include <cerrno> | 34 | 35 | #include <cstring> | @@ -2227,6 +2228,70 @@ void SetServers(const FunctionCallbackInfo<Value>& args) { | 2227 | 2228 | args.GetReturnValue().Set(err); | 2228 | 2229 | } | 2229 | 2230 |
| | 2231 | +void SetLocalAddress(const FunctionCallbackInfo<Value>& args) { | | 2232 | + Environment* env = Environment::GetCurrent(args); | | 2233 | + ChannelWrap* channel; | | 2234 | + ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder()); | | 2235 | + | | 2236 | + CHECK_EQ(args.Length(), 2); | | 2237 | + CHECK(args[0]->IsString()); | | 2238 | + | | 2239 | + Isolate* isolate = args.GetIsolate(); | | 2240 | + node::Utf8Value ip0(isolate, args[0]); | | 2241 | + | | 2242 | + unsigned char addr0[sizeof(struct in6_addr)]; | | 2243 | + unsigned char addr1[sizeof(struct in6_addr)]; | | 2244 | + int type0 = 0; | | 2245 | + | | 2246 | + // This function accepts 2 arguments. The first may be either an IPv4 | | 2247 | + // address or an IPv6 address. If present, the second argument must be the | | 2248 | + // other type of address. Otherwise, the unspecified type of IP is set | | 2249 | + // to 0 (any). | | 2250 | + | | 2251 | + if (uv_inet_pton(AF_INET, *ip0, &addr0) == 0) { | | 2252 | + ares_set_local_ip4(channel->cares_channel(), ReadUint32BE(addr0)); | | 2253 | + type0 = 4; | | 2254 | + } else if (uv_inet_pton(AF_INET6, *ip0, &addr0) == 0) { | | 2255 | + ares_set_local_ip6(channel->cares_channel(), addr0); | | 2256 | + type0 = 6; | | 2257 | + } else { | | 2258 | + THROW_ERR_INVALID_ARG_VALUE(env, "Invalid IP address."); | | 2259 | + return; | | 2260 | + } | | 2261 | + | | 2262 | + if (!args[1]->IsUndefined()) { | | 2263 | + CHECK(args[1]->IsString()); | | 2264 | + node::Utf8Value ip1(isolate, args[1]); | | 2265 | + | | 2266 | + if (uv_inet_pton(AF_INET, *ip1, &addr1) == 0) { | | 2267 | + if (type0 == 4) { | | 2268 | + THROW_ERR_INVALID_ARG_VALUE(env, "Cannot specify two IPv4 addresses."); | | 2269 | + return; | | 2270 | + } else { | | 2271 | + ares_set_local_ip4(channel->cares_channel(), ReadUint32BE(addr1)); | | 2272 | + } | | 2273 | + } else if (uv_inet_pton(AF_INET6, *ip1, &addr1) == 0) { | | 2274 | + if (type0 == 6) { | | 2275 | + THROW_ERR_INVALID_ARG_VALUE(env, "Cannot specify two IPv6 addresses."); | | 2276 | + return; | | 2277 | + } else { | | 2278 | + ares_set_local_ip6(channel->cares_channel(), addr1); | | 2279 | + } | | 2280 | + } else { | | 2281 | + THROW_ERR_INVALID_ARG_VALUE(env, "Invalid IP address."); | | 2282 | + return; | | 2283 | + } | | 2284 | + } else { | | 2285 | + // No second arg specifed | | 2286 | + if (type0 == 4) { | | 2287 | + memset(&addr1, 0, sizeof(addr1)); | | 2288 | + ares_set_local_ip6(channel->cares_channel(), addr1); | | 2289 | + } else { | | 2290 | + ares_set_local_ip4(channel->cares_channel(), 0); | | 2291 | + } | | 2292 | + } | | 2293 | +} | | 2294 | + | 2230 | 2295 | void Cancel(const FunctionCallbackInfo<Value>& args) { | 2231 | 2296 | ChannelWrap* channel; | 2232 | 2297 | ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder()); | @@ -2329,6 +2394,7 @@ void Initialize(Local<Object> target, | 2329 | 2394 |
| 2330 | 2395 | env->SetProtoMethodNoSideEffect(channel_wrap, "getServers", GetServers); | 2331 | 2396 | env->SetProtoMethod(channel_wrap, "setServers", SetServers); | | 2397 | + env->SetProtoMethod(channel_wrap, "setLocalAddress", SetLocalAddress); | 2332 | 2398 | env->SetProtoMethod(channel_wrap, "cancel", Cancel); | 2333 | 2399 |
| 2334 | 2400 | Local<String> channelWrapString = | |
Collapse file Copy file name to clipboard +37Lines changed: 37 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +'use strict'; | | 2 | +require('../common'); | | 3 | +const assert = require('assert'); | | 4 | + | | 5 | +const dns = require('dns'); | | 6 | +const resolver = new dns.Resolver(); | | 7 | +const promiseResolver = new dns.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 | +} |
|