caa 9.0.3 → 9.2.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.
@@ -0,0 +1,11 @@
1
+ type CaaOpts = {
2
+ ignoreTLDs?: boolean;
3
+ recursions?: number;
4
+ retries?: number;
5
+ port?: number;
6
+ servers?: string[];
7
+ dnsSocket?: any;
8
+ };
9
+ export declare function caa(name: any, opts?: CaaOpts): Promise<any>;
10
+ export declare function caaMatches(name: any, ca: any, opts?: {}): Promise<any>;
11
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,126 @@
1
+ import { getServers } from "node:dns";
2
+ import { promisify } from "node:util";
3
+ import dnsSocket from "dns-socket";
4
+ import tlds from "tlds";
5
+ const defaults = {
6
+ ignoreTLDs: false,
7
+ recursions: 50,
8
+ retries: 12,
9
+ port: 53,
10
+ servers: ["8.8.8.8", "8.8.4.4"],
11
+ // these are only used as fallback to system's servers
12
+ dnsSocket: void 0
13
+ };
14
+ const tldSet = new Set(tlds);
15
+ const isTLD = (name) => tldSet.has(name);
16
+ const isWildcard = (name) => /\*/.test(name);
17
+ const parent = (name) => name.split(".").splice(1).join(".");
18
+ const selectServer = (servers, retries, tries) => servers[(tries - retries) % servers.length];
19
+ function normalizeName(name = "") {
20
+ name = name.toLowerCase();
21
+ return name.endsWith(".") && name.length > 1 ? name.substring(0, name.length - 1) : name;
22
+ }
23
+ const resolve = async ({ name, query, servers, port, recursions, retries, tries, ignoreTLDs }) => {
24
+ var _a, _b, _c, _d, _e;
25
+ name = normalizeName(name);
26
+ if (!name)
27
+ return [];
28
+ if (ignoreTLDs && isTLD(name))
29
+ return [];
30
+ if (recursions <= 0 || retries <= 0)
31
+ return [];
32
+ name = name.replace(/^\*\./, "");
33
+ let res;
34
+ try {
35
+ res = await query({ questions: [{ name, type: "CAA" }] }, port, selectServer(servers, retries, tries));
36
+ } catch {
37
+ if (retries <= 0)
38
+ return [];
39
+ retries -= 1;
40
+ return await resolve({ name, query, servers, port, recursions, retries, tries, ignoreTLDs });
41
+ }
42
+ if (!res || !res.answers && !["NXDOMAIN", "NOERROR"].includes(res.rcode)) {
43
+ if (retries <= 0)
44
+ return [];
45
+ retries -= 1;
46
+ return await resolve({ name, query, servers, port, recursions, retries, tries, ignoreTLDs });
47
+ }
48
+ const records = {};
49
+ if ((_a = res == null ? void 0 : res.answers) == null ? void 0 : _a.length) {
50
+ for (const { name: name2, type, data } of res.answers || {}) {
51
+ if (!name2 || !type || !data)
52
+ continue;
53
+ if (!records[type])
54
+ records[type] = [];
55
+ records[type].push({ name: name2, data });
56
+ }
57
+ }
58
+ if ((_b = records.CAA) == null ? void 0 : _b.length) {
59
+ const caas = records.CAA.filter((record) => record.name === name).map((record) => record.data);
60
+ if (caas.length)
61
+ return caas;
62
+ }
63
+ let alias;
64
+ if ((_c = records.CNAME) == null ? void 0 : _c.length) {
65
+ const dest = records.CNAME.find((record) => record.name === name);
66
+ alias = dest.data;
67
+ } else if ((_d = records.DNAME) == null ? void 0 : _d.length) {
68
+ const dest = records.DNAME.find((record) => record.name === name);
69
+ alias = name.replace(dest.name, dest.data);
70
+ }
71
+ if (alias && ((_e = records.CAA) == null ? void 0 : _e.length)) {
72
+ return records.CAA.filter((record) => record.name === alias && record.data).map((record) => record.data);
73
+ }
74
+ if (!isTLD(name)) {
75
+ recursions -= 1;
76
+ return await resolve({ name: parent(name), query, servers, port, recursions, retries, tries, ignoreTLDs });
77
+ } else {
78
+ return [];
79
+ }
80
+ };
81
+ async function caa(name, opts = {}) {
82
+ if (typeof name !== "string")
83
+ throw new Error(`Expected a string for 'name', got ${name}`);
84
+ name = normalizeName(name);
85
+ if (!opts.servers) {
86
+ const systemServers = getServers();
87
+ opts.servers = (systemServers == null ? void 0 : systemServers.length) ? systemServers : defaults.servers;
88
+ }
89
+ opts = { ...defaults, ...opts };
90
+ const socket = opts.dnsSocket || dnsSocket();
91
+ const query = promisify(socket.query.bind(socket));
92
+ const caa2 = await resolve({
93
+ name,
94
+ query,
95
+ servers: opts.servers,
96
+ port: opts.port,
97
+ recursions: opts.recursions,
98
+ retries: opts.retries,
99
+ tries: opts.retries,
100
+ ignoreTLDs: opts.ignoreTLDs
101
+ });
102
+ if (!opts.dnsSocket)
103
+ socket.destroy();
104
+ return caa2 || [];
105
+ }
106
+ async function caaMatches(name, ca, opts = {}) {
107
+ if (typeof name !== "string")
108
+ throw new Error(`Expected a string for 'name', got ${name}`);
109
+ if (typeof ca !== "string")
110
+ throw new Error(`Expected a string for 'ca', got ${ca}`);
111
+ name = normalizeName(name);
112
+ ca = normalizeName(ca);
113
+ const caas = await caa(name, opts);
114
+ if (!caas.length)
115
+ return true;
116
+ const issueNames = caas.filter((caa2) => (caa2 == null ? void 0 : caa2.tag) === "issue").map((name2) => normalizeName(name2.value.split(";")[0].trim()));
117
+ const issueWildNames = caas.filter((caa2) => (caa2 == null ? void 0 : caa2.tag) === "issuewild").map((name2) => normalizeName(name2.value.split(";")[0].trim()));
118
+ const names = isWildcard(name) ? issueWildNames.length ? issueWildNames : issueNames : issueNames;
119
+ if (names.includes(";"))
120
+ return false;
121
+ return !names.length || names.includes(ca);
122
+ }
123
+ export {
124
+ caa,
125
+ caaMatches
126
+ };
package/package.json CHANGED
@@ -1,28 +1,38 @@
1
1
  {
2
2
  "name": "caa",
3
- "version": "9.0.3",
3
+ "version": "9.2.0",
4
4
  "description": "rfc6844-conform CAA record lookup and validation",
5
5
  "author": "silverwind <me@silverwind.io>",
6
6
  "repository": "silverwind/caa",
7
7
  "license": "BSD-2-Clause",
8
8
  "type": "module",
9
- "exports": "./index.js",
9
+ "sideEffects": false,
10
+ "main": "./dist/index.js",
11
+ "exports": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "files": [
14
+ "dist"
15
+ ],
10
16
  "engines": {
11
17
  "node": ">=18"
12
18
  },
13
- "files": [
14
- "index.js"
15
- ],
16
19
  "dependencies": {
17
20
  "dns-socket": "^4.2.2",
18
21
  "tlds": "^1.252.0"
19
22
  },
20
23
  "devDependencies": {
24
+ "@babel/eslint-parser": "7.24.5",
25
+ "@babel/plugin-syntax-import-assertions": "7.24.1",
26
+ "@types/node": "20.12.12",
21
27
  "eslint": "8.57.0",
22
- "eslint-config-silverwind": "83.0.2",
23
- "updates": "16.0.1",
24
- "versions": "12.0.1",
25
- "vitest": "1.5.0",
26
- "vitest-config-silverwind": "8.0.4"
28
+ "eslint-config-silverwind": "85.1.3",
29
+ "eslint-config-silverwind-typescript": "3.2.5",
30
+ "typescript-config-silverwind": "4.3.1",
31
+ "updates": "16.1.1",
32
+ "versions": "12.0.2",
33
+ "vite": "5.2.11",
34
+ "vite-plugin-dts": "3.9.1",
35
+ "vitest": "1.6.0",
36
+ "vitest-config-silverwind": "9.0.6"
27
37
  }
28
38
  }
package/index.js DELETED
@@ -1,140 +0,0 @@
1
- import {getServers} from "node:dns";
2
- import {promisify} from "node:util";
3
- import dnsSocket from "dns-socket";
4
- import tlds from "tlds";
5
-
6
- const defaults = {
7
- ignoreTLDs: false,
8
- recursions: 50,
9
- retries: 12,
10
- port: 53,
11
- servers: ["8.8.8.8", "8.8.4.4"], // these are only used as fallback to system's servers
12
- dnsSocket: undefined,
13
- };
14
-
15
- const tldSet = new Set(tlds);
16
- const isTLD = name => tldSet.has(name);
17
- const isWildcard = name => /\*/.test(name);
18
- const parent = name => name.split(".").splice(1).join(".");
19
- const selectServer = (servers, retries, tries) => servers[(tries - retries) % servers.length];
20
-
21
- function normalizeName(name = "") {
22
- name = name.toLowerCase();
23
- return (name.endsWith(".") && name.length > 1) ? name.substring(0, name.length - 1) : name;
24
- }
25
-
26
- // resolve a CAA record, possibly via recursion
27
- const resolve = async ({name, query, servers, port, recursions, retries, tries, ignoreTLDs}) => {
28
- name = normalizeName(name);
29
- if (!name) return [];
30
- if (ignoreTLDs && isTLD(name)) return [];
31
- if (recursions <= 0 || retries <= 0) return [];
32
-
33
- // Given a request for a specific domain X, or a request for a wildcard
34
- // domain *.X, the relevant record set R(X) is determined ...
35
- name = name.replace(/^\*\./, "");
36
-
37
- let res;
38
- try {
39
- res = await query({questions: [{name, type: "CAA"}]}, port, selectServer(servers, retries, tries));
40
- } catch {
41
- if (retries <= 0) return [];
42
- retries -= 1;
43
- return await resolve({name, query, servers, port, recursions, retries, tries, ignoreTLDs});
44
- }
45
-
46
- if (!res || (!res.answers && !["NXDOMAIN", "NOERROR"].includes(res.rcode))) {
47
- if (retries <= 0) return [];
48
- retries -= 1;
49
- return await resolve({name, query, servers, port, recursions, retries, tries, ignoreTLDs});
50
- }
51
-
52
- // parse DNS answers to {type: [{name, data}]}
53
- const records = {};
54
- if (res?.answers?.length) {
55
- for (const {name, type, data} of res.answers || {}) {
56
- if (!name || !type || !data) continue;
57
- if (!records[type]) records[type] = [];
58
- records[type].push({name, data});
59
- }
60
- }
61
-
62
- // If CAA(X) is not empty, R(X) = CAA (X)
63
- if (records.CAA?.length) {
64
- const caas = records.CAA.filter(record => record.name === name).map(record => record.data);
65
- if (caas.length) return caas;
66
- }
67
-
68
- let alias;
69
- if (records.CNAME?.length) {
70
- const dest = records.CNAME.find(record => record.name === name);
71
- alias = dest.data;
72
- } else if (records.DNAME?.length) {
73
- const dest = records.DNAME.find(record => record.name === name);
74
- alias = name.replace(dest.name, dest.data);
75
- }
76
-
77
- // If A(X) is not null, and CAA(A(X)) is not empty, then R(X) = CAA(A(X)), otherwise
78
- if (alias && records.CAA?.length) {
79
- return records.CAA.filter(record => record.name === alias && record.data).map(record => record.data);
80
- }
81
-
82
- // If X is not a top-level domain, then R(X) = R(P(X)
83
- if (!isTLD(name)) {
84
- recursions -= 1;
85
- return await resolve({name: parent(name), query, servers, port, recursions, retries, tries, ignoreTLDs});
86
- } else {
87
- return [];
88
- }
89
- };
90
-
91
- export async function caa(name, opts = {}) {
92
- if (typeof name !== "string") throw new Error(`Expected a string for 'name', got ${name}`);
93
- name = normalizeName(name);
94
-
95
- if (!opts.servers) {
96
- const systemServers = getServers();
97
- opts.servers = systemServers?.length ? systemServers : defaults.servers;
98
- }
99
-
100
- opts = {...defaults, ...opts};
101
-
102
- const socket = opts.dnsSocket || dnsSocket();
103
- const query = promisify(socket.query.bind(socket));
104
-
105
- const caa = await resolve({
106
- name, query,
107
- servers: opts.servers,
108
- port: opts.port,
109
- recursions: opts.recursions,
110
- retries: opts.retries,
111
- tries: opts.retries,
112
- ignoreTLDs: opts.ignoreTLDs,
113
- });
114
-
115
- if (!opts.dnsSocket) socket.destroy();
116
- return caa || [];
117
- }
118
-
119
- export async function caaMatches(name, ca, opts = {}) {
120
- if (typeof name !== "string") throw new Error(`Expected a string for 'name', got ${name}`);
121
- if (typeof ca !== "string") throw new Error(`Expected a string for 'ca', got ${ca}`);
122
-
123
- name = normalizeName(name);
124
- ca = normalizeName(ca);
125
-
126
- const caas = await caa(name, opts);
127
- if (!caas.length) return true;
128
-
129
- const issueNames = caas
130
- .filter(caa => caa?.tag === "issue")
131
- .map(name => normalizeName(name.value.split(";")[0].trim()));
132
- const issueWildNames = caas
133
- .filter(caa => caa?.tag === "issuewild")
134
- .map(name => normalizeName(name.value.split(";")[0].trim()));
135
-
136
- const names = isWildcard(name) ? (issueWildNames.length ? issueWildNames : issueNames) : issueNames;
137
-
138
- if (names.includes(";")) return false;
139
- return !names.length || names.includes(ca);
140
- }