@aws-sdk/util-dns 3.271.0 → 3.274.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/DnsCache.js +2 -0
- package/dist-cjs/NodeDnsLookupHostResolver.js +55 -7
- package/dist-cjs/util/HostAddressEntryCollection.js +35 -0
- package/dist-cjs/util/HostEntry.js +120 -0
- package/dist-cjs/util/HostEntryTable.js +27 -0
- package/dist-es/DnsCache.js +1 -0
- package/dist-es/NodeDnsLookupHostResolver.js +55 -7
- package/dist-es/util/HostAddressEntryCollection.js +31 -0
- package/dist-es/util/HostEntry.js +116 -0
- package/dist-es/util/HostEntryTable.js +23 -0
- package/dist-types/DnsCache.d.ts +122 -0
- package/dist-types/NodeDnsLookupHostResolver.d.ts +90 -3
- package/dist-types/ts3.4/DnsCache.d.ts +32 -0
- package/dist-types/ts3.4/NodeDnsLookupHostResolver.d.ts +23 -0
- package/dist-types/ts3.4/util/HostAddressEntryCollection.d.ts +12 -0
- package/dist-types/ts3.4/util/HostEntry.d.ts +19 -0
- package/dist-types/ts3.4/util/HostEntryTable.d.ts +16 -0
- package/dist-types/util/HostAddressEntryCollection.d.ts +29 -0
- package/dist-types/util/HostEntry.d.ts +87 -0
- package/dist-types/util/HostEntryTable.d.ts +41 -0
- package/package.json +2 -2
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.NodeDnsLookupHostResolver = void 0;
|
|
4
4
|
const types_1 = require("@aws-sdk/types");
|
|
5
5
|
const dns_1 = require("dns");
|
|
6
|
+
const HostEntryTable_1 = require("./util/HostEntryTable");
|
|
6
7
|
const NODE_DNS_FAMILY_TO_HOST_ADDRESS_TYPE = {
|
|
7
8
|
4: types_1.HostAddressType.A,
|
|
8
9
|
6: types_1.HostAddressType.AAAA,
|
|
@@ -13,9 +14,59 @@ const DNS_LOOKUP_OPTIONS = {
|
|
|
13
14
|
verbatim: true,
|
|
14
15
|
};
|
|
15
16
|
class NodeDnsLookupHostResolver {
|
|
17
|
+
constructor({ ttlMs = NodeDnsLookupHostResolver.DEFAULT_TTL_MS, cache = NodeDnsLookupHostResolver.createDefaultCacheProvider(), nodeDnsLookup = NodeDnsLookupHostResolver.DEFAULT_NODE_DNS_LOOKUP, } = {}) {
|
|
18
|
+
this.ttlMs = ttlMs;
|
|
19
|
+
this.cache = cache;
|
|
20
|
+
this.nodeDnsLookup = nodeDnsLookup;
|
|
21
|
+
}
|
|
16
22
|
async resolveAddress(args) {
|
|
23
|
+
const possibleHostEntry = this.cache.get(args.hostName);
|
|
24
|
+
const newNextTimestampToUpdateMs = Date.now() + this.ttlMs;
|
|
25
|
+
if (possibleHostEntry === undefined) {
|
|
26
|
+
const addresses = await this.nodeDnsLookupResolveAddress(args);
|
|
27
|
+
this.cache.set(args, addresses, newNextTimestampToUpdateMs);
|
|
28
|
+
}
|
|
29
|
+
const hostEntry = this.cache.get(args.hostName);
|
|
30
|
+
if (possibleHostEntry !== undefined && Date.now() >= hostEntry.nextTimestampToUpdateMs) {
|
|
31
|
+
try {
|
|
32
|
+
const addresses = await this.nodeDnsLookupResolveAddress(args);
|
|
33
|
+
hostEntry.updateRecords(addresses, newNextTimestampToUpdateMs);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.error(`Could not update DNS address cache for "${args.hostName}": ${error}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
hostEntry.processRecords();
|
|
40
|
+
const result = [];
|
|
41
|
+
if (hostEntry.aRecords.length > 0) {
|
|
42
|
+
result.push(hostEntry.aRecords.cycle());
|
|
43
|
+
}
|
|
44
|
+
if (hostEntry.aaaaRecords.length > 0) {
|
|
45
|
+
result.push(hostEntry.aaaaRecords.cycle());
|
|
46
|
+
}
|
|
47
|
+
if (result.length === 0) {
|
|
48
|
+
throw new Error(`Could not resolve addresses for "${args.hostName}"`);
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
reportFailureOnAddress(addr) {
|
|
53
|
+
const hostEntry = this.cache.get(addr.hostName);
|
|
54
|
+
if (hostEntry === undefined) {
|
|
55
|
+
throw new Error(`Could not find cached host name "${addr.hostName}"`);
|
|
56
|
+
}
|
|
57
|
+
hostEntry.failAddressInRecords(addr);
|
|
58
|
+
}
|
|
59
|
+
purgeCache(args) {
|
|
60
|
+
if (args === null || args === void 0 ? void 0 : args.hostName) {
|
|
61
|
+
this.cache.delete(args.hostName);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
this.cache.clear();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async nodeDnsLookupResolveAddress(args) {
|
|
17
68
|
const addresses = [];
|
|
18
|
-
const ipEntries = await
|
|
69
|
+
const ipEntries = await this.nodeDnsLookup(args.hostName, DNS_LOOKUP_OPTIONS);
|
|
19
70
|
for (const { address, family } of ipEntries) {
|
|
20
71
|
const addressType = NODE_DNS_FAMILY_TO_HOST_ADDRESS_TYPE[family];
|
|
21
72
|
if (addressType === undefined) {
|
|
@@ -30,11 +81,8 @@ class NodeDnsLookupHostResolver {
|
|
|
30
81
|
}
|
|
31
82
|
return addresses;
|
|
32
83
|
}
|
|
33
|
-
reportFailureOnAddress(addr) {
|
|
34
|
-
throw new Error("reportFailureOnAddress(addr) is not implemented");
|
|
35
|
-
}
|
|
36
|
-
purgeCache(args) {
|
|
37
|
-
throw new Error("purgeCache(args?) is not implemented");
|
|
38
|
-
}
|
|
39
84
|
}
|
|
40
85
|
exports.NodeDnsLookupHostResolver = NodeDnsLookupHostResolver;
|
|
86
|
+
NodeDnsLookupHostResolver.DEFAULT_TTL_MS = 30000;
|
|
87
|
+
NodeDnsLookupHostResolver.createDefaultCacheProvider = () => new HostEntryTable_1.HostEntryTable();
|
|
88
|
+
NodeDnsLookupHostResolver.DEFAULT_NODE_DNS_LOOKUP = dns_1.promises.lookup;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HostAddressEntryCollection = void 0;
|
|
4
|
+
class HostAddressEntryCollection {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.data = [];
|
|
7
|
+
}
|
|
8
|
+
get length() {
|
|
9
|
+
return this.data.length;
|
|
10
|
+
}
|
|
11
|
+
cycle(collection) {
|
|
12
|
+
if (this.data.length === 0) {
|
|
13
|
+
throw new Error("Cannot cycle an empty collection");
|
|
14
|
+
}
|
|
15
|
+
const entry = this.data.shift();
|
|
16
|
+
(collection || this).append(entry);
|
|
17
|
+
return entry;
|
|
18
|
+
}
|
|
19
|
+
append(entry) {
|
|
20
|
+
this.data.push(entry);
|
|
21
|
+
}
|
|
22
|
+
remove(entry) {
|
|
23
|
+
if (this.length === 0) {
|
|
24
|
+
throw new Error("Cannot remove from an empty collection");
|
|
25
|
+
}
|
|
26
|
+
const index = this.data.findIndex((e) => e.address === entry.address);
|
|
27
|
+
const removedEntry = this.data[index];
|
|
28
|
+
this.data.splice(index, 1);
|
|
29
|
+
return removedEntry;
|
|
30
|
+
}
|
|
31
|
+
[Symbol.iterator]() {
|
|
32
|
+
return this.data[Symbol.iterator]();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.HostAddressEntryCollection = HostAddressEntryCollection;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HostEntry = void 0;
|
|
4
|
+
const types_1 = require("@aws-sdk/types");
|
|
5
|
+
const HostAddressEntryCollection_1 = require("./HostAddressEntryCollection");
|
|
6
|
+
class HostEntry {
|
|
7
|
+
constructor(nextTimestampToProcessMs) {
|
|
8
|
+
this.aaaaRecords = new HostAddressEntryCollection_1.HostAddressEntryCollection();
|
|
9
|
+
this.aRecords = new HostAddressEntryCollection_1.HostAddressEntryCollection();
|
|
10
|
+
this.failedAaaaRecords = new HostAddressEntryCollection_1.HostAddressEntryCollection();
|
|
11
|
+
this.failedARecords = new HostAddressEntryCollection_1.HostAddressEntryCollection();
|
|
12
|
+
this.nextTimestampToUpdateMs = nextTimestampToProcessMs;
|
|
13
|
+
}
|
|
14
|
+
updateRecords(addresses, expirationTtlMs) {
|
|
15
|
+
this.nextTimestampToUpdateMs = expirationTtlMs;
|
|
16
|
+
const addressesToAppend = [];
|
|
17
|
+
for (const freshAddress of addresses) {
|
|
18
|
+
const hostAddressEntry = this.findAddress(freshAddress);
|
|
19
|
+
if (hostAddressEntry !== undefined) {
|
|
20
|
+
hostAddressEntry.expirationTtlMs = expirationTtlMs;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
addressesToAppend.push(freshAddress);
|
|
24
|
+
}
|
|
25
|
+
for (const addressToAppend of addressesToAppend) {
|
|
26
|
+
const hostAddressEntry = this.findAddress(addressToAppend);
|
|
27
|
+
if (hostAddressEntry !== undefined) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const successRecords = this.getGoodRecords(addressToAppend);
|
|
31
|
+
successRecords.append(Object.assign(addressToAppend, {
|
|
32
|
+
expirationTtlMs,
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
processRecords() {
|
|
37
|
+
this.processRecordsAddressType(this.aRecords, this.failedARecords);
|
|
38
|
+
this.processRecordsAddressType(this.aaaaRecords, this.failedAaaaRecords);
|
|
39
|
+
}
|
|
40
|
+
failAddressInRecords(address) {
|
|
41
|
+
const successRecords = this.getGoodRecords(address);
|
|
42
|
+
const failedRecords = this.getFailedRecords(address);
|
|
43
|
+
const recordsToRemove = [];
|
|
44
|
+
for (const hostAddressEntry of successRecords) {
|
|
45
|
+
if (hostAddressEntry.address === address.address) {
|
|
46
|
+
recordsToRemove.push(hostAddressEntry);
|
|
47
|
+
failedRecords.append(hostAddressEntry);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const recordToRemove of recordsToRemove) {
|
|
51
|
+
successRecords.remove(recordToRemove);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
findAddress(address) {
|
|
55
|
+
const successRecords = this.getGoodRecords(address);
|
|
56
|
+
for (const hostAddressEntry of successRecords) {
|
|
57
|
+
if (address.address === hostAddressEntry.address) {
|
|
58
|
+
return hostAddressEntry;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const failedRecords = this.getFailedRecords(address);
|
|
62
|
+
for (const hostAddressEntry of failedRecords) {
|
|
63
|
+
if (address.address === hostAddressEntry.address) {
|
|
64
|
+
return hostAddressEntry;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
processRecordsAddressType(successRecords, failedRecords) {
|
|
70
|
+
const successRecordsToRemove = [];
|
|
71
|
+
let successIndex = 0;
|
|
72
|
+
for (const hostAddressEntry of successRecords) {
|
|
73
|
+
if (successIndex === successRecords.length - 1) {
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
if (Date.now() >= hostAddressEntry.expirationTtlMs) {
|
|
77
|
+
successRecordsToRemove.push(hostAddressEntry);
|
|
78
|
+
}
|
|
79
|
+
successIndex++;
|
|
80
|
+
}
|
|
81
|
+
for (const recordToRemove of successRecordsToRemove) {
|
|
82
|
+
successRecords.remove(recordToRemove);
|
|
83
|
+
}
|
|
84
|
+
const failedRecordsToRemove = [];
|
|
85
|
+
let failedIndex = 0;
|
|
86
|
+
for (const hostAddressEntry of failedRecords) {
|
|
87
|
+
if (failedIndex === failedRecords.length - 1) {
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
if (Date.now() >= hostAddressEntry.expirationTtlMs) {
|
|
91
|
+
failedRecordsToRemove.push(hostAddressEntry);
|
|
92
|
+
}
|
|
93
|
+
failedIndex++;
|
|
94
|
+
}
|
|
95
|
+
for (const recordToRemove of failedRecordsToRemove) {
|
|
96
|
+
failedRecords.remove(recordToRemove);
|
|
97
|
+
}
|
|
98
|
+
if (successRecords.length === 0) {
|
|
99
|
+
let hostAddressEntryToPromote = undefined;
|
|
100
|
+
for (const hostAddressEntry of failedRecords) {
|
|
101
|
+
if (Date.now() >= hostAddressEntry.expirationTtlMs) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
hostAddressEntryToPromote = hostAddressEntry;
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
if (hostAddressEntryToPromote !== undefined) {
|
|
108
|
+
failedRecords.remove(hostAddressEntryToPromote);
|
|
109
|
+
successRecords.append(hostAddressEntryToPromote);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
getGoodRecords(address) {
|
|
114
|
+
return address.addressType === types_1.HostAddressType.AAAA ? this.aaaaRecords : this.aRecords;
|
|
115
|
+
}
|
|
116
|
+
getFailedRecords(address) {
|
|
117
|
+
return address.addressType === types_1.HostAddressType.AAAA ? this.failedAaaaRecords : this.failedARecords;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
exports.HostEntry = HostEntry;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HostEntryTable = void 0;
|
|
4
|
+
const HostEntry_1 = require("./HostEntry");
|
|
5
|
+
class HostEntryTable {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.map = new Map();
|
|
8
|
+
}
|
|
9
|
+
set(args, addresses, nextTimestampToProcessMs) {
|
|
10
|
+
const hostEntry = new HostEntry_1.HostEntry(nextTimestampToProcessMs);
|
|
11
|
+
hostEntry.updateRecords(addresses, nextTimestampToProcessMs);
|
|
12
|
+
this.map.set(args.hostName, hostEntry);
|
|
13
|
+
}
|
|
14
|
+
get(hostName) {
|
|
15
|
+
return this.map.get(hostName);
|
|
16
|
+
}
|
|
17
|
+
delete(hostName) {
|
|
18
|
+
this.map.delete(hostName);
|
|
19
|
+
}
|
|
20
|
+
clear() {
|
|
21
|
+
this.map.clear();
|
|
22
|
+
}
|
|
23
|
+
get size() {
|
|
24
|
+
return this.map.size;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.HostEntryTable = HostEntryTable;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { HostAddressType } from "@aws-sdk/types";
|
|
2
2
|
import { ALL, promises as dnsPromises, V4MAPPED } from "dns";
|
|
3
|
+
import { HostEntryTable } from "./util/HostEntryTable";
|
|
3
4
|
const NODE_DNS_FAMILY_TO_HOST_ADDRESS_TYPE = {
|
|
4
5
|
4: HostAddressType.A,
|
|
5
6
|
6: HostAddressType.AAAA,
|
|
@@ -10,9 +11,59 @@ const DNS_LOOKUP_OPTIONS = {
|
|
|
10
11
|
verbatim: true,
|
|
11
12
|
};
|
|
12
13
|
export class NodeDnsLookupHostResolver {
|
|
14
|
+
constructor({ ttlMs = NodeDnsLookupHostResolver.DEFAULT_TTL_MS, cache = NodeDnsLookupHostResolver.createDefaultCacheProvider(), nodeDnsLookup = NodeDnsLookupHostResolver.DEFAULT_NODE_DNS_LOOKUP, } = {}) {
|
|
15
|
+
this.ttlMs = ttlMs;
|
|
16
|
+
this.cache = cache;
|
|
17
|
+
this.nodeDnsLookup = nodeDnsLookup;
|
|
18
|
+
}
|
|
13
19
|
async resolveAddress(args) {
|
|
20
|
+
const possibleHostEntry = this.cache.get(args.hostName);
|
|
21
|
+
const newNextTimestampToUpdateMs = Date.now() + this.ttlMs;
|
|
22
|
+
if (possibleHostEntry === undefined) {
|
|
23
|
+
const addresses = await this.nodeDnsLookupResolveAddress(args);
|
|
24
|
+
this.cache.set(args, addresses, newNextTimestampToUpdateMs);
|
|
25
|
+
}
|
|
26
|
+
const hostEntry = this.cache.get(args.hostName);
|
|
27
|
+
if (possibleHostEntry !== undefined && Date.now() >= hostEntry.nextTimestampToUpdateMs) {
|
|
28
|
+
try {
|
|
29
|
+
const addresses = await this.nodeDnsLookupResolveAddress(args);
|
|
30
|
+
hostEntry.updateRecords(addresses, newNextTimestampToUpdateMs);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
console.error(`Could not update DNS address cache for "${args.hostName}": ${error}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
hostEntry.processRecords();
|
|
37
|
+
const result = [];
|
|
38
|
+
if (hostEntry.aRecords.length > 0) {
|
|
39
|
+
result.push(hostEntry.aRecords.cycle());
|
|
40
|
+
}
|
|
41
|
+
if (hostEntry.aaaaRecords.length > 0) {
|
|
42
|
+
result.push(hostEntry.aaaaRecords.cycle());
|
|
43
|
+
}
|
|
44
|
+
if (result.length === 0) {
|
|
45
|
+
throw new Error(`Could not resolve addresses for "${args.hostName}"`);
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
reportFailureOnAddress(addr) {
|
|
50
|
+
const hostEntry = this.cache.get(addr.hostName);
|
|
51
|
+
if (hostEntry === undefined) {
|
|
52
|
+
throw new Error(`Could not find cached host name "${addr.hostName}"`);
|
|
53
|
+
}
|
|
54
|
+
hostEntry.failAddressInRecords(addr);
|
|
55
|
+
}
|
|
56
|
+
purgeCache(args) {
|
|
57
|
+
if (args?.hostName) {
|
|
58
|
+
this.cache.delete(args.hostName);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
this.cache.clear();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async nodeDnsLookupResolveAddress(args) {
|
|
14
65
|
const addresses = [];
|
|
15
|
-
const ipEntries = await
|
|
66
|
+
const ipEntries = await this.nodeDnsLookup(args.hostName, DNS_LOOKUP_OPTIONS);
|
|
16
67
|
for (const { address, family } of ipEntries) {
|
|
17
68
|
const addressType = NODE_DNS_FAMILY_TO_HOST_ADDRESS_TYPE[family];
|
|
18
69
|
if (addressType === undefined) {
|
|
@@ -27,10 +78,7 @@ export class NodeDnsLookupHostResolver {
|
|
|
27
78
|
}
|
|
28
79
|
return addresses;
|
|
29
80
|
}
|
|
30
|
-
reportFailureOnAddress(addr) {
|
|
31
|
-
throw new Error("reportFailureOnAddress(addr) is not implemented");
|
|
32
|
-
}
|
|
33
|
-
purgeCache(args) {
|
|
34
|
-
throw new Error("purgeCache(args?) is not implemented");
|
|
35
|
-
}
|
|
36
81
|
}
|
|
82
|
+
NodeDnsLookupHostResolver.DEFAULT_TTL_MS = 30000;
|
|
83
|
+
NodeDnsLookupHostResolver.createDefaultCacheProvider = () => new HostEntryTable();
|
|
84
|
+
NodeDnsLookupHostResolver.DEFAULT_NODE_DNS_LOOKUP = dnsPromises.lookup;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export class HostAddressEntryCollection {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.data = [];
|
|
4
|
+
}
|
|
5
|
+
get length() {
|
|
6
|
+
return this.data.length;
|
|
7
|
+
}
|
|
8
|
+
cycle(collection) {
|
|
9
|
+
if (this.data.length === 0) {
|
|
10
|
+
throw new Error("Cannot cycle an empty collection");
|
|
11
|
+
}
|
|
12
|
+
const entry = this.data.shift();
|
|
13
|
+
(collection || this).append(entry);
|
|
14
|
+
return entry;
|
|
15
|
+
}
|
|
16
|
+
append(entry) {
|
|
17
|
+
this.data.push(entry);
|
|
18
|
+
}
|
|
19
|
+
remove(entry) {
|
|
20
|
+
if (this.length === 0) {
|
|
21
|
+
throw new Error("Cannot remove from an empty collection");
|
|
22
|
+
}
|
|
23
|
+
const index = this.data.findIndex((e) => e.address === entry.address);
|
|
24
|
+
const removedEntry = this.data[index];
|
|
25
|
+
this.data.splice(index, 1);
|
|
26
|
+
return removedEntry;
|
|
27
|
+
}
|
|
28
|
+
[Symbol.iterator]() {
|
|
29
|
+
return this.data[Symbol.iterator]();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { HostAddressType } from "@aws-sdk/types";
|
|
2
|
+
import { HostAddressEntryCollection } from "./HostAddressEntryCollection";
|
|
3
|
+
export class HostEntry {
|
|
4
|
+
constructor(nextTimestampToProcessMs) {
|
|
5
|
+
this.aaaaRecords = new HostAddressEntryCollection();
|
|
6
|
+
this.aRecords = new HostAddressEntryCollection();
|
|
7
|
+
this.failedAaaaRecords = new HostAddressEntryCollection();
|
|
8
|
+
this.failedARecords = new HostAddressEntryCollection();
|
|
9
|
+
this.nextTimestampToUpdateMs = nextTimestampToProcessMs;
|
|
10
|
+
}
|
|
11
|
+
updateRecords(addresses, expirationTtlMs) {
|
|
12
|
+
this.nextTimestampToUpdateMs = expirationTtlMs;
|
|
13
|
+
const addressesToAppend = [];
|
|
14
|
+
for (const freshAddress of addresses) {
|
|
15
|
+
const hostAddressEntry = this.findAddress(freshAddress);
|
|
16
|
+
if (hostAddressEntry !== undefined) {
|
|
17
|
+
hostAddressEntry.expirationTtlMs = expirationTtlMs;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
addressesToAppend.push(freshAddress);
|
|
21
|
+
}
|
|
22
|
+
for (const addressToAppend of addressesToAppend) {
|
|
23
|
+
const hostAddressEntry = this.findAddress(addressToAppend);
|
|
24
|
+
if (hostAddressEntry !== undefined) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const successRecords = this.getGoodRecords(addressToAppend);
|
|
28
|
+
successRecords.append(Object.assign(addressToAppend, {
|
|
29
|
+
expirationTtlMs,
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
processRecords() {
|
|
34
|
+
this.processRecordsAddressType(this.aRecords, this.failedARecords);
|
|
35
|
+
this.processRecordsAddressType(this.aaaaRecords, this.failedAaaaRecords);
|
|
36
|
+
}
|
|
37
|
+
failAddressInRecords(address) {
|
|
38
|
+
const successRecords = this.getGoodRecords(address);
|
|
39
|
+
const failedRecords = this.getFailedRecords(address);
|
|
40
|
+
const recordsToRemove = [];
|
|
41
|
+
for (const hostAddressEntry of successRecords) {
|
|
42
|
+
if (hostAddressEntry.address === address.address) {
|
|
43
|
+
recordsToRemove.push(hostAddressEntry);
|
|
44
|
+
failedRecords.append(hostAddressEntry);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
for (const recordToRemove of recordsToRemove) {
|
|
48
|
+
successRecords.remove(recordToRemove);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
findAddress(address) {
|
|
52
|
+
const successRecords = this.getGoodRecords(address);
|
|
53
|
+
for (const hostAddressEntry of successRecords) {
|
|
54
|
+
if (address.address === hostAddressEntry.address) {
|
|
55
|
+
return hostAddressEntry;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const failedRecords = this.getFailedRecords(address);
|
|
59
|
+
for (const hostAddressEntry of failedRecords) {
|
|
60
|
+
if (address.address === hostAddressEntry.address) {
|
|
61
|
+
return hostAddressEntry;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
processRecordsAddressType(successRecords, failedRecords) {
|
|
67
|
+
const successRecordsToRemove = [];
|
|
68
|
+
let successIndex = 0;
|
|
69
|
+
for (const hostAddressEntry of successRecords) {
|
|
70
|
+
if (successIndex === successRecords.length - 1) {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
if (Date.now() >= hostAddressEntry.expirationTtlMs) {
|
|
74
|
+
successRecordsToRemove.push(hostAddressEntry);
|
|
75
|
+
}
|
|
76
|
+
successIndex++;
|
|
77
|
+
}
|
|
78
|
+
for (const recordToRemove of successRecordsToRemove) {
|
|
79
|
+
successRecords.remove(recordToRemove);
|
|
80
|
+
}
|
|
81
|
+
const failedRecordsToRemove = [];
|
|
82
|
+
let failedIndex = 0;
|
|
83
|
+
for (const hostAddressEntry of failedRecords) {
|
|
84
|
+
if (failedIndex === failedRecords.length - 1) {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
if (Date.now() >= hostAddressEntry.expirationTtlMs) {
|
|
88
|
+
failedRecordsToRemove.push(hostAddressEntry);
|
|
89
|
+
}
|
|
90
|
+
failedIndex++;
|
|
91
|
+
}
|
|
92
|
+
for (const recordToRemove of failedRecordsToRemove) {
|
|
93
|
+
failedRecords.remove(recordToRemove);
|
|
94
|
+
}
|
|
95
|
+
if (successRecords.length === 0) {
|
|
96
|
+
let hostAddressEntryToPromote = undefined;
|
|
97
|
+
for (const hostAddressEntry of failedRecords) {
|
|
98
|
+
if (Date.now() >= hostAddressEntry.expirationTtlMs) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
hostAddressEntryToPromote = hostAddressEntry;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
if (hostAddressEntryToPromote !== undefined) {
|
|
105
|
+
failedRecords.remove(hostAddressEntryToPromote);
|
|
106
|
+
successRecords.append(hostAddressEntryToPromote);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
getGoodRecords(address) {
|
|
111
|
+
return address.addressType === HostAddressType.AAAA ? this.aaaaRecords : this.aRecords;
|
|
112
|
+
}
|
|
113
|
+
getFailedRecords(address) {
|
|
114
|
+
return address.addressType === HostAddressType.AAAA ? this.failedAaaaRecords : this.failedARecords;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { HostEntry } from "./HostEntry";
|
|
2
|
+
export class HostEntryTable {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.map = new Map();
|
|
5
|
+
}
|
|
6
|
+
set(args, addresses, nextTimestampToProcessMs) {
|
|
7
|
+
const hostEntry = new HostEntry(nextTimestampToProcessMs);
|
|
8
|
+
hostEntry.updateRecords(addresses, nextTimestampToProcessMs);
|
|
9
|
+
this.map.set(args.hostName, hostEntry);
|
|
10
|
+
}
|
|
11
|
+
get(hostName) {
|
|
12
|
+
return this.map.get(hostName);
|
|
13
|
+
}
|
|
14
|
+
delete(hostName) {
|
|
15
|
+
this.map.delete(hostName);
|
|
16
|
+
}
|
|
17
|
+
clear() {
|
|
18
|
+
this.map.clear();
|
|
19
|
+
}
|
|
20
|
+
get size() {
|
|
21
|
+
return this.map.size;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { HostAddress, HostResolverArguments } from "@aws-sdk/types";
|
|
2
|
+
/**
|
|
3
|
+
* DNS cache used by a {@link HostResolver} which maps:
|
|
4
|
+
* host name (string) -> {@link DnsCacheEntry}
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export interface DnsCache {
|
|
8
|
+
/**
|
|
9
|
+
* Maps a host name to a {@link DnsCacheEntry} in the cache
|
|
10
|
+
* @param args host resolver arguments which include host name
|
|
11
|
+
* @param addresses addresses to set into the cache entry
|
|
12
|
+
* @param nextTimestampToProcessMs timestamp for both cache entry processed and addresses' expiration
|
|
13
|
+
*/
|
|
14
|
+
set(args: HostResolverArguments, addresses: HostAddress[], nextTimestampToProcessMs: number): void;
|
|
15
|
+
/**
|
|
16
|
+
* Gets the corresponding cache entry for hostName, otherwise undefined
|
|
17
|
+
* @param hostName key to get cache entry
|
|
18
|
+
*/
|
|
19
|
+
get(hostName: string): DnsCacheEntry | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Deletes the corresponding cache entry for hostName
|
|
22
|
+
* @param hostName key to delete cache entry
|
|
23
|
+
*/
|
|
24
|
+
delete(hostName: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* Deletes all cache entries
|
|
27
|
+
*/
|
|
28
|
+
clear(): void;
|
|
29
|
+
/**
|
|
30
|
+
* Gets the size of the cache
|
|
31
|
+
*/
|
|
32
|
+
size: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Entry for a host name, mapped to in {@link DnsCache}
|
|
36
|
+
* @internal
|
|
37
|
+
*/
|
|
38
|
+
export interface DnsCacheEntry {
|
|
39
|
+
/**
|
|
40
|
+
* Collection of good IPv6 records for an address
|
|
41
|
+
*/
|
|
42
|
+
aaaaRecords: DnsCacheEntryCollection;
|
|
43
|
+
/**
|
|
44
|
+
* Collection of good IPv4 records for an address
|
|
45
|
+
*/
|
|
46
|
+
aRecords: DnsCacheEntryCollection;
|
|
47
|
+
/**
|
|
48
|
+
* Collection of failed IPv6 records for an address
|
|
49
|
+
*/
|
|
50
|
+
failedAaaaRecords: DnsCacheEntryCollection;
|
|
51
|
+
/**
|
|
52
|
+
* Collection of failed IPv4 records for an address
|
|
53
|
+
*/
|
|
54
|
+
failedARecords: DnsCacheEntryCollection;
|
|
55
|
+
/**
|
|
56
|
+
* Timestamp for when to next process the entry
|
|
57
|
+
*/
|
|
58
|
+
nextTimestampToUpdateMs: number;
|
|
59
|
+
/**
|
|
60
|
+
* Updates the entry given the new set of addresses and a new expiration
|
|
61
|
+
* timestamp:
|
|
62
|
+
* - If an address is already in the entry, the expiration timestamp is updated.
|
|
63
|
+
* - If an address is not in the entry, then it is added to the good records.
|
|
64
|
+
* @param addresses list of addresses used to update entry records
|
|
65
|
+
* @param expirationTtlMs expiration timestamp for new or updated addresses
|
|
66
|
+
*/
|
|
67
|
+
updateRecords(addresses: HostAddress[], expirationTtlMs: number): void;
|
|
68
|
+
/**
|
|
69
|
+
* Removes expired records from records (except 1 in case of DNS outages).
|
|
70
|
+
* In the case that good records are empty, should attempt to promote 1
|
|
71
|
+
* non-expired failed address to the good records.
|
|
72
|
+
*/
|
|
73
|
+
processRecords(): void;
|
|
74
|
+
/**
|
|
75
|
+
* Moves the address from good records to failed records
|
|
76
|
+
* @param address address to move to failed records
|
|
77
|
+
*/
|
|
78
|
+
failAddressInRecords(address: HostAddress): void;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Collection which is used to manage {@link DnsCacheHostAddressEntry}
|
|
82
|
+
* @internal
|
|
83
|
+
*/
|
|
84
|
+
export interface DnsCacheEntryCollection {
|
|
85
|
+
/**
|
|
86
|
+
* Get length / size of the collection
|
|
87
|
+
*/
|
|
88
|
+
length: number;
|
|
89
|
+
/**
|
|
90
|
+
* Moves a {@link DnsCacheHostAddressEntry} from the beginning of the
|
|
91
|
+
* collection to the end of itself, and returns the host address entry
|
|
92
|
+
* that was cycled.
|
|
93
|
+
* If another collection is provided, it will move the host address entry
|
|
94
|
+
* to the end of that collection.
|
|
95
|
+
* @param collection optional collection to cycle to.
|
|
96
|
+
*/
|
|
97
|
+
cycle(collection?: DnsCacheEntryCollection): DnsCacheHostAddressEntry;
|
|
98
|
+
/**
|
|
99
|
+
* Appends an element to the end of the collection
|
|
100
|
+
* @param element element to append to the collection
|
|
101
|
+
*/
|
|
102
|
+
append(element: DnsCacheHostAddressEntry): void;
|
|
103
|
+
/**
|
|
104
|
+
* Removes an element from the collection, and returns that element.
|
|
105
|
+
* @param element element to remove and return from the collection
|
|
106
|
+
*/
|
|
107
|
+
remove(element: DnsCacheHostAddressEntry): DnsCacheHostAddressEntry;
|
|
108
|
+
/**
|
|
109
|
+
* Implements iterator for the collection
|
|
110
|
+
*/
|
|
111
|
+
[Symbol.iterator](): Iterator<DnsCacheHostAddressEntry>;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Captures cached {@link HostAddress} information
|
|
115
|
+
* @internal
|
|
116
|
+
*/
|
|
117
|
+
export interface DnsCacheHostAddressEntry extends HostAddress {
|
|
118
|
+
/**
|
|
119
|
+
* Timestamp for when the entry expires
|
|
120
|
+
*/
|
|
121
|
+
expirationTtlMs: number;
|
|
122
|
+
}
|
|
@@ -1,16 +1,103 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import { HostAddress, HostResolver as IHostResolver, HostResolverArguments } from "@aws-sdk/types";
|
|
3
|
+
import { LookupAddress, LookupAllOptions } from "dns";
|
|
4
|
+
import { DnsCache } from "./DnsCache";
|
|
5
|
+
/**
|
|
6
|
+
* Node.js dns.lookup() function type used in {@link NodeDnsLookupHostResolver}
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
interface NodeDnsLookupFn {
|
|
10
|
+
(hostname: string, options: LookupAllOptions): Promise<LookupAddress[]>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Configuration options for {@link NodeDnsLookupHostResolver}
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export interface NodeDnsLookupHostResolverConfig {
|
|
17
|
+
/**
|
|
18
|
+
* Cache for holding address entries
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
21
|
+
cache?: DnsCache;
|
|
22
|
+
/**
|
|
23
|
+
* TTL offset in MS when generating expiration TTLs
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
ttlMs?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Pluggable Node.js dns.lookup() function
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
nodeDnsLookup?: NodeDnsLookupFn;
|
|
32
|
+
}
|
|
2
33
|
/**
|
|
3
34
|
* {@link HostResolver} implementation that uses the Node.js dns.lookup() API.
|
|
4
|
-
* TODO(dns): implement cache
|
|
5
35
|
*/
|
|
6
36
|
export declare class NodeDnsLookupHostResolver implements IHostResolver {
|
|
7
37
|
/**
|
|
8
|
-
*
|
|
9
|
-
* @
|
|
38
|
+
* Default TTL in ms
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
private static DEFAULT_TTL_MS;
|
|
42
|
+
/**
|
|
43
|
+
* Provider that returns a default {@link DnsCache}
|
|
44
|
+
* @returns DnsCache implementation
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
47
|
+
private static createDefaultCacheProvider;
|
|
48
|
+
/**
|
|
49
|
+
* Default Node.js dns.lookup() function
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
private static DEFAULT_NODE_DNS_LOOKUP;
|
|
53
|
+
/**
|
|
54
|
+
* TTL in ms
|
|
55
|
+
* @internal
|
|
56
|
+
*/
|
|
57
|
+
private ttlMs;
|
|
58
|
+
/**
|
|
59
|
+
* {@link DnsCache} which maps {@link HostAddress.hostName} to {@link DnsCacheEntry}
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
62
|
+
private cache;
|
|
63
|
+
/**
|
|
64
|
+
* Node.js dns.lookup() API implementation
|
|
65
|
+
* @internal
|
|
66
|
+
*/
|
|
67
|
+
private nodeDnsLookup;
|
|
68
|
+
constructor({ ttlMs, cache, nodeDnsLookup, }?: NodeDnsLookupHostResolverConfig);
|
|
69
|
+
/**
|
|
70
|
+
* Resolves the address(es) for {@link HostResolverArguments} and returns a
|
|
71
|
+
* list of addresses with (most likely) two addresses, one {@link HostAddressType.AAAA}
|
|
72
|
+
* and one {@link HostAddressType.A}. Calls to this function will likely alter
|
|
73
|
+
* the cache so that if there's multiple addresses, a different set will be
|
|
74
|
+
* returned on the next call.
|
|
75
|
+
* In the case of multi-answer, still only a maximum of two records should be
|
|
76
|
+
* returned.
|
|
10
77
|
* @param args arguments with host name query addresses for
|
|
11
78
|
* @returns promise with a list of {@link HostAddress}
|
|
79
|
+
* @see https://github.com/awslabs/aws-c-io/blob/f2ff573c191e1c4ea0248af5c08161356be3bc78/source/host_resolver.c#L964
|
|
12
80
|
*/
|
|
13
81
|
resolveAddress(args: HostResolverArguments): Promise<HostAddress[]>;
|
|
82
|
+
/**
|
|
83
|
+
* Reports a failure on a {@link HostAddress} so that the cache can
|
|
84
|
+
* accomodate the failure and likely not return the address until it recovers.
|
|
85
|
+
* @param addr host address to report a failure on
|
|
86
|
+
*/
|
|
14
87
|
reportFailureOnAddress(addr: HostAddress): void;
|
|
88
|
+
/**
|
|
89
|
+
* Empties the cache for a {@link HostResolverArguments.hostName}.
|
|
90
|
+
* If {@link HostResolverArguments.hostName} is not provided, the cache
|
|
91
|
+
* is emptied for all host names.
|
|
92
|
+
* @param args optional arguments to empty the cache for
|
|
93
|
+
*/
|
|
15
94
|
purgeCache(args?: HostResolverArguments): void;
|
|
95
|
+
/**
|
|
96
|
+
* Node.js resolveAddress() using the dns.lookup() APIs.
|
|
97
|
+
* @see https://nodejs.org/api/dns.html#dnspromiseslookuphostname-options
|
|
98
|
+
* @param args arguments with host name query addresses for
|
|
99
|
+
* @returns promise with a list of {@link HostAddress}
|
|
100
|
+
*/
|
|
101
|
+
private nodeDnsLookupResolveAddress;
|
|
16
102
|
}
|
|
103
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { HostAddress, HostResolverArguments } from "@aws-sdk/types";
|
|
2
|
+
export interface DnsCache {
|
|
3
|
+
set(
|
|
4
|
+
args: HostResolverArguments,
|
|
5
|
+
addresses: HostAddress[],
|
|
6
|
+
nextTimestampToProcessMs: number
|
|
7
|
+
): void;
|
|
8
|
+
get(hostName: string): DnsCacheEntry | undefined;
|
|
9
|
+
delete(hostName: string): void;
|
|
10
|
+
clear(): void;
|
|
11
|
+
size: number;
|
|
12
|
+
}
|
|
13
|
+
export interface DnsCacheEntry {
|
|
14
|
+
aaaaRecords: DnsCacheEntryCollection;
|
|
15
|
+
aRecords: DnsCacheEntryCollection;
|
|
16
|
+
failedAaaaRecords: DnsCacheEntryCollection;
|
|
17
|
+
failedARecords: DnsCacheEntryCollection;
|
|
18
|
+
nextTimestampToUpdateMs: number;
|
|
19
|
+
updateRecords(addresses: HostAddress[], expirationTtlMs: number): void;
|
|
20
|
+
processRecords(): void;
|
|
21
|
+
failAddressInRecords(address: HostAddress): void;
|
|
22
|
+
}
|
|
23
|
+
export interface DnsCacheEntryCollection {
|
|
24
|
+
length: number;
|
|
25
|
+
cycle(collection?: DnsCacheEntryCollection): DnsCacheHostAddressEntry;
|
|
26
|
+
append(element: DnsCacheHostAddressEntry): void;
|
|
27
|
+
remove(element: DnsCacheHostAddressEntry): DnsCacheHostAddressEntry;
|
|
28
|
+
[Symbol.iterator](): Iterator<DnsCacheHostAddressEntry>;
|
|
29
|
+
}
|
|
30
|
+
export interface DnsCacheHostAddressEntry extends HostAddress {
|
|
31
|
+
expirationTtlMs: number;
|
|
32
|
+
}
|
|
@@ -3,8 +3,31 @@ import {
|
|
|
3
3
|
HostResolver as IHostResolver,
|
|
4
4
|
HostResolverArguments,
|
|
5
5
|
} from "@aws-sdk/types";
|
|
6
|
+
import { LookupAddress, LookupAllOptions } from "dns";
|
|
7
|
+
import { DnsCache } from "./DnsCache";
|
|
8
|
+
interface NodeDnsLookupFn {
|
|
9
|
+
(hostname: string, options: LookupAllOptions): Promise<LookupAddress[]>;
|
|
10
|
+
}
|
|
11
|
+
export interface NodeDnsLookupHostResolverConfig {
|
|
12
|
+
cache?: DnsCache;
|
|
13
|
+
ttlMs?: number;
|
|
14
|
+
nodeDnsLookup?: NodeDnsLookupFn;
|
|
15
|
+
}
|
|
6
16
|
export declare class NodeDnsLookupHostResolver implements IHostResolver {
|
|
17
|
+
private static DEFAULT_TTL_MS;
|
|
18
|
+
private static createDefaultCacheProvider;
|
|
19
|
+
private static DEFAULT_NODE_DNS_LOOKUP;
|
|
20
|
+
private ttlMs;
|
|
21
|
+
private cache;
|
|
22
|
+
private nodeDnsLookup;
|
|
23
|
+
constructor({
|
|
24
|
+
ttlMs,
|
|
25
|
+
cache,
|
|
26
|
+
nodeDnsLookup,
|
|
27
|
+
}?: NodeDnsLookupHostResolverConfig);
|
|
7
28
|
resolveAddress(args: HostResolverArguments): Promise<HostAddress[]>;
|
|
8
29
|
reportFailureOnAddress(addr: HostAddress): void;
|
|
9
30
|
purgeCache(args?: HostResolverArguments): void;
|
|
31
|
+
private nodeDnsLookupResolveAddress;
|
|
10
32
|
}
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DnsCacheEntryCollection } from "../DnsCache";
|
|
2
|
+
import { HostAddressEntry } from "./HostEntry";
|
|
3
|
+
export declare class HostAddressEntryCollection
|
|
4
|
+
implements DnsCacheEntryCollection
|
|
5
|
+
{
|
|
6
|
+
data: HostAddressEntry[];
|
|
7
|
+
readonly length: number;
|
|
8
|
+
cycle(collection?: HostAddressEntryCollection): HostAddressEntry;
|
|
9
|
+
append(entry: HostAddressEntry): void;
|
|
10
|
+
remove(entry: HostAddressEntry): HostAddressEntry;
|
|
11
|
+
[Symbol.iterator](): Iterator<HostAddressEntry>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { HostAddress } from "@aws-sdk/types";
|
|
2
|
+
import { DnsCacheEntry, DnsCacheHostAddressEntry } from "../DnsCache";
|
|
3
|
+
import { HostAddressEntryCollection } from "./HostAddressEntryCollection";
|
|
4
|
+
export interface HostAddressEntry extends DnsCacheHostAddressEntry {}
|
|
5
|
+
export declare class HostEntry implements DnsCacheEntry {
|
|
6
|
+
aaaaRecords: HostAddressEntryCollection;
|
|
7
|
+
aRecords: HostAddressEntryCollection;
|
|
8
|
+
failedAaaaRecords: HostAddressEntryCollection;
|
|
9
|
+
failedARecords: HostAddressEntryCollection;
|
|
10
|
+
nextTimestampToUpdateMs: number;
|
|
11
|
+
constructor(nextTimestampToProcessMs: number);
|
|
12
|
+
updateRecords(addresses: HostAddress[], expirationTtlMs: number): void;
|
|
13
|
+
processRecords(): void;
|
|
14
|
+
failAddressInRecords(address: HostAddress): void;
|
|
15
|
+
private findAddress;
|
|
16
|
+
private processRecordsAddressType;
|
|
17
|
+
private getGoodRecords;
|
|
18
|
+
private getFailedRecords;
|
|
19
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { HostAddress, HostResolverArguments } from "@aws-sdk/types";
|
|
2
|
+
import { DnsCache } from "../DnsCache";
|
|
3
|
+
import { HostEntry } from "./HostEntry";
|
|
4
|
+
export declare class HostEntryTable implements DnsCache {
|
|
5
|
+
private map;
|
|
6
|
+
constructor();
|
|
7
|
+
set(
|
|
8
|
+
args: HostResolverArguments,
|
|
9
|
+
addresses: HostAddress[],
|
|
10
|
+
nextTimestampToProcessMs: number
|
|
11
|
+
): void;
|
|
12
|
+
get(hostName: string): HostEntry | undefined;
|
|
13
|
+
delete(hostName: string): void;
|
|
14
|
+
clear(): void;
|
|
15
|
+
readonly size: number;
|
|
16
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { DnsCacheEntryCollection } from "../DnsCache";
|
|
2
|
+
import { HostAddressEntry } from "./HostEntry";
|
|
3
|
+
/**
|
|
4
|
+
* Collection using an Array as the underlying data structure
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export declare class HostAddressEntryCollection implements DnsCacheEntryCollection {
|
|
8
|
+
data: HostAddressEntry[];
|
|
9
|
+
get length(): number;
|
|
10
|
+
/**
|
|
11
|
+
* Removes and appends the first entry to the end, e.g. 2 cycles:
|
|
12
|
+
* [A, B, C] -> [B, C, A] -> [C, A, B]
|
|
13
|
+
* @param collection optional collection to append the cycled entry to
|
|
14
|
+
* @returns entry cycled
|
|
15
|
+
*/
|
|
16
|
+
cycle(collection?: HostAddressEntryCollection): HostAddressEntry;
|
|
17
|
+
/**
|
|
18
|
+
* Adds entry to the end of the collection.
|
|
19
|
+
* @param entry entry to add
|
|
20
|
+
*/
|
|
21
|
+
append(entry: HostAddressEntry): void;
|
|
22
|
+
/**
|
|
23
|
+
* Removes an entry from the collection
|
|
24
|
+
* @param entry entry to remove
|
|
25
|
+
* @returns the removed entry
|
|
26
|
+
*/
|
|
27
|
+
remove(entry: HostAddressEntry): HostAddressEntry;
|
|
28
|
+
[Symbol.iterator](): Iterator<HostAddressEntry>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { HostAddress } from "@aws-sdk/types";
|
|
2
|
+
import { DnsCacheEntry, DnsCacheHostAddressEntry } from "../DnsCache";
|
|
3
|
+
import { HostAddressEntryCollection } from "./HostAddressEntryCollection";
|
|
4
|
+
/**
|
|
5
|
+
* Interface used in {@link HostEntry} records
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export interface HostAddressEntry extends DnsCacheHostAddressEntry {
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* {@link DnsCacheEntry} implementation that uses {@link LinkedList} for
|
|
12
|
+
* {@link DnsCacheEntryCollection}.
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export declare class HostEntry implements DnsCacheEntry {
|
|
16
|
+
/**
|
|
17
|
+
* {@link LinkedList} of good IPv6 records for an address
|
|
18
|
+
*/
|
|
19
|
+
aaaaRecords: HostAddressEntryCollection;
|
|
20
|
+
/**
|
|
21
|
+
* {@link LinkedList} of good IPv4 records for an address
|
|
22
|
+
*/
|
|
23
|
+
aRecords: HostAddressEntryCollection;
|
|
24
|
+
/**
|
|
25
|
+
* {@link LinkedList} of failed IPv6 records for an address
|
|
26
|
+
*/
|
|
27
|
+
failedAaaaRecords: HostAddressEntryCollection;
|
|
28
|
+
/**
|
|
29
|
+
* {@link LinkedList} of failed IPv4 records for an address
|
|
30
|
+
*/
|
|
31
|
+
failedARecords: HostAddressEntryCollection;
|
|
32
|
+
/**
|
|
33
|
+
* Timestamp for when to next process the entry
|
|
34
|
+
*/
|
|
35
|
+
nextTimestampToUpdateMs: number;
|
|
36
|
+
constructor(nextTimestampToProcessMs: number);
|
|
37
|
+
/**
|
|
38
|
+
* Updates the entry given the new set of addresses and a new expiration
|
|
39
|
+
* timestamp:
|
|
40
|
+
* - If an address is already in the entry, the expiration timestamp is updated.
|
|
41
|
+
* - If an address is not in the entry, then it is added to the good records.
|
|
42
|
+
* @param addresses list of addresses used to update entry records
|
|
43
|
+
* @param expirationTtlMs expiration timestamp for new or updated addresses
|
|
44
|
+
* @see https://github.com/awslabs/aws-c-io/blob/f2ff573c191e1c4ea0248af5c08161356be3bc78/source/host_resolver.c#L703
|
|
45
|
+
*/
|
|
46
|
+
updateRecords(addresses: HostAddress[], expirationTtlMs: number): void;
|
|
47
|
+
/**
|
|
48
|
+
* Removes expired records from records (except 1 in case of DNS outages).
|
|
49
|
+
* In the case that good records are empty, should attempt to promote 1
|
|
50
|
+
* non-expired failed address to the good records.
|
|
51
|
+
* Uses {@link processRecordsAddressType} for each {@link HostAddressType}
|
|
52
|
+
* @see https://github.com/awslabs/aws-c-io/blob/f2ff573c191e1c4ea0248af5c08161356be3bc78/source/host_resolver.c#L475
|
|
53
|
+
*/
|
|
54
|
+
processRecords(): void;
|
|
55
|
+
/**
|
|
56
|
+
* Moves the address from good records to failed records
|
|
57
|
+
* @param address address to move to failed records
|
|
58
|
+
*/
|
|
59
|
+
failAddressInRecords(address: HostAddress): void;
|
|
60
|
+
/**
|
|
61
|
+
* Find the corresponding {@link HostAddressEntry} for a {@link HostAddress} in
|
|
62
|
+
* a {@link HostEntry}'s records, or return undefined.
|
|
63
|
+
* @param address address to search for
|
|
64
|
+
* @returns host address entry if found, otherwise undefined
|
|
65
|
+
*/
|
|
66
|
+
private findAddress;
|
|
67
|
+
/**
|
|
68
|
+
* Removes expired records from records (except 1 in case of DNS outages).
|
|
69
|
+
* In the case that good records are empty, should attempt to promote 1
|
|
70
|
+
* non-expired failed address to the good records.
|
|
71
|
+
* @param successRecords good records in cache to update
|
|
72
|
+
* @param failedRecords bad records in cache to update
|
|
73
|
+
*/
|
|
74
|
+
private processRecordsAddressType;
|
|
75
|
+
/**
|
|
76
|
+
* Gets the good records for an address based on {@link HostAddressType}
|
|
77
|
+
* @param address address used to get the {@link HostAddressType}
|
|
78
|
+
* @returns good records for an address
|
|
79
|
+
*/
|
|
80
|
+
private getGoodRecords;
|
|
81
|
+
/**
|
|
82
|
+
* Gets the failed records for an address based on {@link HostAddressType}
|
|
83
|
+
* @param address address used to get the {@link HostAddressType}
|
|
84
|
+
* @returns failed records for an address
|
|
85
|
+
*/
|
|
86
|
+
private getFailedRecords;
|
|
87
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { HostAddress, HostResolverArguments } from "@aws-sdk/types";
|
|
2
|
+
import { DnsCache } from "../DnsCache";
|
|
3
|
+
import { HostEntry } from "./HostEntry";
|
|
4
|
+
/**
|
|
5
|
+
* Host entry table which implements {@link DnsCache}, mapping:
|
|
6
|
+
* host name (string) -> {@link HostEntry}
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export declare class HostEntryTable implements DnsCache {
|
|
10
|
+
/**
|
|
11
|
+
* Internal map as the host name level cache
|
|
12
|
+
*/
|
|
13
|
+
private map;
|
|
14
|
+
constructor();
|
|
15
|
+
/**
|
|
16
|
+
* Maps a host name to a {@link HostEntry} in the cache, and uses
|
|
17
|
+
* {@link HostEntry.updateRecords} to initialize address records.
|
|
18
|
+
* @param args host resolver arguments which include host name
|
|
19
|
+
* @param addresses addresses to set into the cache entry
|
|
20
|
+
* @param nextTimestampToProcessMs timestamp for both cache entry processed and addresses' expiration
|
|
21
|
+
*/
|
|
22
|
+
set(args: HostResolverArguments, addresses: HostAddress[], nextTimestampToProcessMs: number): void;
|
|
23
|
+
/**
|
|
24
|
+
* Gets the corresponding {@link HostEntry} for hostName, otherwise undefined
|
|
25
|
+
* @param hostName key to get cache entry
|
|
26
|
+
*/
|
|
27
|
+
get(hostName: string): HostEntry | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Deletes the corresponding {@link HostEntry} for hostName
|
|
30
|
+
* @param hostName key to delete cache entry
|
|
31
|
+
*/
|
|
32
|
+
delete(hostName: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Deletes all {@link HostEntry} mappings
|
|
35
|
+
*/
|
|
36
|
+
clear(): void;
|
|
37
|
+
/**
|
|
38
|
+
* Gets the size of the cache
|
|
39
|
+
*/
|
|
40
|
+
get size(): number;
|
|
41
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws-sdk/util-dns",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.274.0",
|
|
4
4
|
"description": "Implementations of DNS host resolvers.",
|
|
5
5
|
"main": "./dist-cjs/index.js",
|
|
6
6
|
"module": "./dist-es/index.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"tslib": "^2.3.1"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@aws-sdk/types": "3.
|
|
29
|
+
"@aws-sdk/types": "3.272.0",
|
|
30
30
|
"@tsconfig/recommended": "1.0.1",
|
|
31
31
|
"@types/node": "^14.14.31",
|
|
32
32
|
"concurrently": "7.0.0",
|