-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
019ca27
commit 0ef52fc
Showing
20 changed files
with
422 additions
and
159 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export {}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { strict as assert } from 'node:assert'; | ||
import { test, suite } from 'node:test'; | ||
import { isIPv4, isIPv6 } from 'node:net'; | ||
import { dnsRecordsCloudflare, dnsRecordsGoogle, dnsRecordsNodeDns } from './dns-resolvers.js'; | ||
suite('Cloudflare DNS resolver', () => { | ||
test('NS records', async () => { | ||
const nsRecords = await dnsRecordsCloudflare('cloudflare.com', 'NS'); | ||
assert.ok(nsRecords.length > 1); | ||
assert.equal(nsRecords[0].name, 'cloudflare.com'); | ||
assert.equal(nsRecords[0].type, 'NS'); | ||
assert.ok(Number.isSafeInteger(nsRecords[0].ttl)); | ||
assert.ok(nsRecords[0].data.length); | ||
}); | ||
test('A records', async () => { | ||
const aRecords = await dnsRecordsCloudflare('cloudflare.com'); | ||
assert.notEqual(aRecords.length, 0); | ||
assert.equal(aRecords[0].name, 'cloudflare.com'); | ||
assert.equal(aRecords[0].type, 'A'); | ||
assert.ok(Number.isSafeInteger(aRecords[0].ttl)); | ||
assert.ok(isIPv4(aRecords[0].data)); | ||
}); | ||
test('AAAA records', async () => { | ||
const aaaaRecords = await dnsRecordsCloudflare('cloudflare.com', 'AAAA'); | ||
assert.notEqual(aaaaRecords.length, 0); | ||
assert.equal(aaaaRecords[0].name, 'cloudflare.com'); | ||
assert.equal(aaaaRecords[0].type, 'AAAA'); | ||
assert.ok(Number.isSafeInteger(aaaaRecords[0].ttl)); | ||
assert.ok(isIPv6(aaaaRecords[0].data)); | ||
}); | ||
test('MX records', async () => { | ||
const mxRecords = await dnsRecordsCloudflare('cloudflare.com', 'MX'); | ||
assert.notEqual(mxRecords.length, 0); | ||
assert.equal(mxRecords[0].name, 'cloudflare.com'); | ||
assert.equal(mxRecords[0].type, 'MX'); | ||
assert.ok(Number.isSafeInteger(mxRecords[0].ttl)); | ||
assert.ok(mxRecords[0].data); | ||
}); | ||
test('TXT records', async () => { | ||
const txtRecords = await dnsRecordsCloudflare('cloudflare.com', 'txt'); | ||
assert.notEqual(txtRecords.length, 0); | ||
assert.equal(txtRecords[0].name, 'cloudflare.com'); | ||
assert.equal(txtRecords[0].type, 'TXT'); | ||
assert.ok(Number.isSafeInteger(txtRecords[0].ttl)); | ||
assert.ok(txtRecords[0].data); | ||
}); | ||
}); | ||
suite('Google DNS resolver', () => { | ||
test('A records', async () => { | ||
const aRecords = await dnsRecordsGoogle('google.com'); | ||
assert.notEqual(aRecords.length, 0); | ||
assert.equal(aRecords[0].name, 'google.com'); | ||
assert.equal(aRecords[0].type, 'A'); | ||
assert.ok(Number.isSafeInteger(aRecords[0].ttl)); | ||
assert.ok(isIPv4(aRecords[0].data)); | ||
}); | ||
test('TXT records', async () => { | ||
const txtRecords = await dnsRecordsGoogle('google.com', 'txt'); | ||
assert.notEqual(txtRecords.length, 0); | ||
assert.equal(txtRecords[0].name, 'google.com'); | ||
assert.equal(txtRecords[0].type, 'TXT'); | ||
assert.ok(Number.isSafeInteger(txtRecords[0].ttl)); | ||
assert.ok(txtRecords[0].data); | ||
}); | ||
}); | ||
suite('Node DNS resolver', () => { | ||
test('NS resolver', async () => { | ||
const nsRecords = await dnsRecordsNodeDns('nodejs.org', 'NS'); | ||
assert.notEqual(nsRecords.length, 0); | ||
assert.equal(nsRecords[0].name, 'nodejs.org'); | ||
assert.equal(nsRecords[0].type, 'NS'); | ||
assert.ok(Number.isSafeInteger(nsRecords[0].ttl)); | ||
assert.ok(nsRecords[0].data.length); | ||
}); | ||
test('A resolver', async () => { | ||
const aRecords = await dnsRecordsNodeDns('nodejs.org'); | ||
assert.notEqual(aRecords.length, 0); | ||
assert.equal(aRecords[0].name, 'nodejs.org'); | ||
assert.equal(aRecords[0].type, 'A'); | ||
assert.ok(Number.isSafeInteger(aRecords[0].ttl)); | ||
assert.ok(isIPv4(aRecords[0].data)); | ||
}); | ||
test('AAAA resolver', async () => { | ||
const aaaaRecords = await dnsRecordsNodeDns('nodejs.org', 'AAAA'); | ||
assert.notEqual(aaaaRecords.length, 0); | ||
assert.equal(aaaaRecords[0].name, 'nodejs.org'); | ||
assert.equal(aaaaRecords[0].type, 'AAAA'); | ||
assert.ok(Number.isSafeInteger(aaaaRecords[0].ttl)); | ||
assert.ok(isIPv6(aaaaRecords[0].data)); | ||
}); | ||
test('TXT resolver', async () => { | ||
const txtRecords = await dnsRecordsNodeDns('nodejs.org', 'txt'); | ||
assert.notEqual(txtRecords.length, 0); | ||
assert.equal(txtRecords[0].name, 'nodejs.org'); | ||
assert.equal(txtRecords[0].type, 'TXT'); | ||
assert.ok(Number.isSafeInteger(txtRecords[0].ttl)); | ||
assert.ok(txtRecords[0].data); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { type DnsRecord } from './index.js'; | ||
/** | ||
* Get DNS records of a given type for a FQDN. | ||
* | ||
* @param name Fully qualified domain name. | ||
* @param type DNS record type: A, AAAA, TXT, CNAME, MX, etc. | ||
* @param resolver Which DNS resolver to use. If not specified, the best DNS resolver for this runtime will be used. | ||
* @returns Array of discovered `DnsRecord` objects. | ||
* | ||
* @example Get TXT records for example.com | ||
* ```js | ||
* import { getDnsRecords } from '@layered/dns-records' | ||
* | ||
* const txtRecords = await getDnsRecords('example.com', 'TXT') | ||
* ``` | ||
* | ||
* @example Get MX records for android.com from Google DNS resolver | ||
* ```js | ||
* import { getDnsRecords } from '@layered/dns-records' | ||
* | ||
* const mxRecords = await getDnsRecords('android.com', 'MX', 'google-dns') | ||
* ``` | ||
*/ | ||
export declare function getDnsRecords(name: string, type?: string, resolver?: string): Promise<DnsRecord[]>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { dnsRecordsCloudflare, dnsRecordsGoogle, dnsRecordsNodeDig, dnsRecordsNodeDns } from './dns-resolvers.js'; | ||
import {} from './index.js'; | ||
import { isDomain } from './utils.js'; | ||
function bestDnsResolverForThisRuntime() { | ||
if (globalThis.process?.release?.name === 'node') { | ||
return 'node-dns'; | ||
} | ||
else if (globalThis.navigator?.userAgent === 'Cloudflare-Workers') { | ||
return 'cloudflare-dns'; | ||
} | ||
else { | ||
return 'google-dns'; | ||
} | ||
} | ||
/** | ||
* Get DNS records of a given type for a FQDN. | ||
* | ||
* @param name Fully qualified domain name. | ||
* @param type DNS record type: A, AAAA, TXT, CNAME, MX, etc. | ||
* @param resolver Which DNS resolver to use. If not specified, the best DNS resolver for this runtime will be used. | ||
* @returns Array of discovered `DnsRecord` objects. | ||
* | ||
* @example Get TXT records for example.com | ||
* ```js | ||
* import { getDnsRecords } from '@layered/dns-records' | ||
* | ||
* const txtRecords = await getDnsRecords('example.com', 'TXT') | ||
* ``` | ||
* | ||
* @example Get MX records for android.com from Google DNS resolver | ||
* ```js | ||
* import { getDnsRecords } from '@layered/dns-records' | ||
* | ||
* const mxRecords = await getDnsRecords('android.com', 'MX', 'google-dns') | ||
* ``` | ||
*/ | ||
export async function getDnsRecords(name, type = 'A', resolver) { | ||
if (!isDomain(name)) { | ||
throw new Error(`"${name}" is not a valid domain name`); | ||
} | ||
if (!resolver) { | ||
resolver = bestDnsResolverForThisRuntime(); | ||
} | ||
if (resolver === 'cloudflare-dns') { | ||
return dnsRecordsCloudflare(name, type); | ||
} | ||
else if (resolver === 'google-dns') { | ||
return dnsRecordsGoogle(name, type); | ||
} | ||
else if (resolver === 'node-dig') { | ||
return dnsRecordsNodeDig(name, type); | ||
} | ||
else if (resolver === 'node-dns') { | ||
return dnsRecordsNodeDns(name, type); | ||
} | ||
else if (resolver === 'deno-dns') { | ||
throw new Error('Deno DNS not yet implemented'); | ||
} | ||
throw new Error(`Invalid DNS resolver: ${resolver}`); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export {}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import { strict as assert } from 'node:assert'; | ||
import { test, suite } from 'node:test'; | ||
import { getDnsRecords } from './get-dns-records.js'; | ||
import { isIPv4 } from 'node:net'; | ||
suite('NS for google.com', async () => { | ||
const expectedNs = ['ns1.google.com', 'ns2.google.com', 'ns3.google.com', 'ns4.google.com']; | ||
const [nsRecordsWithCloudflareDns, nsRecordsWithGoogleDns, nsRecordsWithNodeDns, nsRecordsWithNodeDig] = await Promise.all([ | ||
getDnsRecords('google.com', 'NS', 'cloudflare-dns'), | ||
getDnsRecords('google.com', 'NS', 'google-dns'), | ||
getDnsRecords('google.com', 'NS', 'node-dns'), | ||
getDnsRecords('google.com', 'NS', 'node-dig'), | ||
]); | ||
test('same number of NS from all resolvers', () => { | ||
assert.equal(nsRecordsWithCloudflareDns.length, expectedNs.length, 'Number of NameServers doesn\'t match'); | ||
assert.equal(nsRecordsWithGoogleDns.length, expectedNs.length, 'Number of NameServers doesn\'t match'); | ||
assert.equal(nsRecordsWithNodeDns.length, expectedNs.length, 'Number of NameServers doesn\'t match'); | ||
assert.equal(nsRecordsWithNodeDig.length, expectedNs.length, 'Number of NameServers doesn\'t match'); | ||
}); | ||
test('validate NS from `cloudflare-dns`', () => { | ||
assert.equal(nsRecordsWithCloudflareDns[0].name, 'google.com', 'Returned NS doesn\'t match'); | ||
assert.equal(nsRecordsWithCloudflareDns[0].type, 'NS', 'Returned record type is not NS'); | ||
assert.ok(expectedNs.some(ns => ns === nsRecordsWithCloudflareDns[0].data), 'Returned NS doesn\'t match'); | ||
assert.ok(expectedNs.some(ns => ns === nsRecordsWithCloudflareDns[1].data), 'Returned NS doesn\'t match'); | ||
}); | ||
test('validate NS from `google-dns`', () => { | ||
assert.equal(nsRecordsWithGoogleDns[0].name, 'google.com', 'Returned NS doesn\'t match'); | ||
assert.equal(nsRecordsWithGoogleDns[0].type, 'NS', 'Returned record type is not NS'); | ||
assert.ok(expectedNs.some(ns => ns === nsRecordsWithGoogleDns[0].data), 'Returned NS doesn\'t match'); | ||
assert.ok(expectedNs.some(ns => ns === nsRecordsWithGoogleDns[1].data), 'Returned NS doesn\'t match'); | ||
}); | ||
test('validate NS from `node-dns`', () => { | ||
assert.equal(nsRecordsWithNodeDns[0].name, 'google.com', 'Returned NS doesn\'t match'); | ||
assert.equal(nsRecordsWithNodeDns[0].type, 'NS', 'Returned record type is not NS'); | ||
assert.ok(expectedNs.some(ns => ns === nsRecordsWithNodeDns[0].data), 'Returned NS doesn\'t match'); | ||
assert.ok(expectedNs.some(ns => ns === nsRecordsWithNodeDns[1].data), 'Returned NS doesn\'t match'); | ||
}); | ||
test('validate NS from `node-dig`', () => { | ||
assert.equal(nsRecordsWithNodeDig[0].name, 'google.com', 'Returned NS doesn\'t match'); | ||
assert.equal(nsRecordsWithNodeDig[0].type, 'NS', 'Returned record type is not NS'); | ||
assert.ok(expectedNs.some(ns => ns === nsRecordsWithNodeDig[0].data), 'Returned NS doesn\'t match'); | ||
assert.ok(expectedNs.some(ns => ns === nsRecordsWithNodeDig[1].data), 'Returned NS doesn\'t match'); | ||
}); | ||
}); | ||
suite('A records for "mañana.com" (IDN)', async () => { | ||
const [aRecordsWithCloudflareDns, aRecordsWithGoogleDns, aRecordsWithNodeDns, aRecordsWithNodeDig] = await Promise.all([ | ||
getDnsRecords('mañana.com', 'A', 'cloudflare-dns'), | ||
getDnsRecords('mañana.com', 'A', 'google-dns'), | ||
getDnsRecords('mañana.com', 'A', 'node-dns'), | ||
getDnsRecords('mañana.com', 'A', 'node-dig'), | ||
]); | ||
test('validate length of records', () => { | ||
assert.notEqual(aRecordsWithCloudflareDns.length, 0); | ||
assert.equal(aRecordsWithCloudflareDns.length, aRecordsWithGoogleDns.length); | ||
assert.equal(aRecordsWithGoogleDns.length, aRecordsWithNodeDns.length); | ||
assert.equal(aRecordsWithNodeDns.length, aRecordsWithNodeDig.length); | ||
}); | ||
test('validate returned data', () => { | ||
assert.ok(isIPv4(aRecordsWithCloudflareDns[0].data)); | ||
assert.equal(aRecordsWithCloudflareDns[0].data, aRecordsWithGoogleDns[0].data); | ||
assert.equal(aRecordsWithGoogleDns[0].data, aRecordsWithNodeDns[0].data); | ||
assert.equal(aRecordsWithNodeDns[0].data, aRecordsWithNodeDig[0].data); | ||
}); | ||
}); | ||
suite('TXT records for "cloudflare.com"', async () => { | ||
const [txtRecordsWithCloudflareDns, txtRecordsWithGoogleDns, txtRecordsWithNodeDns, txtRecordsWithNodeDig] = await Promise.all([ | ||
getDnsRecords('cloudflare.com', 'TXT', 'cloudflare-dns'), | ||
getDnsRecords('cloudflare.com', 'TXT', 'google-dns'), | ||
getDnsRecords('cloudflare.com', 'TXT', 'node-dns'), | ||
getDnsRecords('cloudflare.com', 'TXT', 'node-dig'), | ||
]); | ||
test('validate number of records', () => { | ||
assert.notEqual(txtRecordsWithCloudflareDns.length, 0); | ||
assert.equal(txtRecordsWithCloudflareDns.length, txtRecordsWithGoogleDns.length, 'TXT records length between `google-dns` and `cloudflare-dns` doesn\'t match'); | ||
assert.equal(txtRecordsWithGoogleDns.length, txtRecordsWithNodeDns.length, 'TXT records length between `cloudflare-dns` and `node-dns` doesn\'t match'); | ||
assert.equal(txtRecordsWithNodeDns.length, txtRecordsWithNodeDig.length); | ||
}); | ||
test('find spf record (cloudflare.com must have one)', () => { | ||
assert.ok(txtRecordsWithCloudflareDns.some(record => record.data.includes('v=spf1'))); | ||
assert.ok(txtRecordsWithGoogleDns.some(record => record.data.includes('v=spf1'))); | ||
assert.ok(txtRecordsWithNodeDns.some(record => record.data.includes('v=spf1'))); | ||
assert.ok(txtRecordsWithNodeDig.some(record => record.data.includes('v=spf1'))); | ||
}); | ||
}); |
Oops, something went wrong.