Skip to content

Commit

Permalink
Merge pull request #27 from DeBuXer/local_dns
Browse files Browse the repository at this point in the history
Ability to use the local DNS resolver instead of Google DNS
  • Loading branch information
willnode authored Aug 3, 2024
2 parents cc80472 + eee35cc commit ba397dd
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 27 deletions.
1 change: 1 addition & 0 deletions HOSTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ This guide will walk you through the process of setting up your own instance of
`BLACKLIST_HOSTS` | A comma-separated list of root domains to blacklist
`BLACKLIST_REDIRECT` | The URL to redirect to when a blacklisted host is accessed
`HOME_DOMAIN` | The host to enable `/stat` endpoint
`USE_LOCAL_DNS` | Default is `false`, so the Google DNS is used. Set it to `true` if you want to use the DNS resolver of your own host

If `WHITELIST_HOSTS` is set, `BLACKLIST_HOSTS` is ignored. Both is mutually exclusive.

Expand Down
89 changes: 62 additions & 27 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import request from "./certnode/lib/request.js";
import fs from "node:fs";
import { isIPv4, isIPv6 } from "node:net";
import { fileURLToPath } from "node:url";
import dns from 'dns/promises';
const recordParamDestUrl = 'forward-domain';
const recordParamHttpStatus = 'http-status';
const caaRegex = /^0 issue (")?letsencrypt\.org(;validationmethods=http-01)?\1$/;
const useLocalDNS = process.env.USE_LOCAL_DNS || false
/**
* @type {Record<string, boolean>}
*/
Expand Down Expand Up @@ -169,23 +171,42 @@ const parseTxtRecordData = (value) => {
* @return {Promise<string[] | null>}
*/
export async function validateCAARecords(host, mockResolve = undefined) {
/**
* @type {{data: {Answer: {data: string, type: number}[]}}}
*/
const resolve = mockResolve || await request(`https://dns.google/resolve?name=${encodeURIComponent(host)}&type=CAA`);
if (!resolve.data.Answer) {
return null;
}
if (useLocalDNS) {
const records = mockResolve || await dns.resolveCaa(`${encodeURIComponent(host)}`);
if (!records || records.length === 0) {
return null;
}

const issueRecords = resolve.data.Answer.filter((x) =>
x.type == 257 && typeof x.data === 'string' && x.data.startsWith('0 issue ')
).map(x => x.data);
const issueRecords = records
.filter(record => record.type === 257)
.map(record => record.data)
.filter(data => data.startsWith('0 issue '));

// check if any record allows Let'sEncrypt (or no record at all)
if (issueRecords.length == 0 || issueRecords.some(x => caaRegex.test(x))) {
return null;
// Check if any record allows Let's Encrypt (or no record at all)
if (issueRecords.length === 0 || issueRecords.some(record => caaRegex.test(record))) {
return null;
}
return issueRecords;

} else {
/**
* @type {{data: {Answer: {data: string, type: number}[]}}}
*/
const resolve = mockResolve || await request(`https://dns.google/resolve?name=${encodeURIComponent(host)}&type=CAA`);
if (!resolve.data.Answer) {
return null;
}

const issueRecords = resolve.data.Answer.filter((x) =>
x.type == 257 && typeof x.data === 'string' && x.data.startsWith('0 issue ')
).map(x => x.data);

// check if any record allows Let'sEncrypt (or no record at all)
if (issueRecords.length == 0 || issueRecords.some(x => caaRegex.test(x))) {
return null;
}
return issueRecords;
}
return issueRecords;
}

/**
Expand All @@ -194,22 +215,36 @@ export async function validateCAARecords(host, mockResolve = undefined) {
* @return {Promise<{url: string, httpStatus?: string} | null>}
*/
export async function findTxtRecord(host, mockResolve = undefined) {
const resolve = mockResolve || await request(`https://dns.google/resolve?name=_.${encodeURIComponent(host)}&type=TXT`);
if (!resolve.data.Answer) {
if (useLocalDNS) {
const resolve = mockResolve || await dns.resolveTxt(`_.${encodeURIComponent(host)}`);
for (const record of resolve) {
const joinedRecord = record.join('');
const txtData = parseTxtRecordData(joinedRecord);
if (!txtData[recordParamDestUrl]) continue;
return {
url: txtData[recordParamDestUrl],
httpStatus: txtData[recordParamHttpStatus],
};
}
return null;
}
for (const head of resolve.data.Answer) {
if (head.type !== 16) { // RR type of TXT is 16
continue;
} else {
const resolve = mockResolve || await request(`https://dns.google/resolve?name=_.${encodeURIComponent(host)}&type=TXT`);
if (!resolve.data.Answer) {
return null;
}
const txtData = parseTxtRecordData(head.data);
if (!txtData[recordParamDestUrl]) continue;
return {
url: txtData[recordParamDestUrl],
httpStatus: txtData[recordParamHttpStatus],
};
for (const head of resolve.data.Answer) {
if (head.type !== 16) { // RR type of TXT is 16
continue;
}
const txtData = parseTxtRecordData(head.data);
if (!txtData[recordParamDestUrl]) continue;
return {
url: txtData[recordParamDestUrl],
httpStatus: txtData[recordParamHttpStatus],
};
}
return null;
}
return null;
}


Expand Down

0 comments on commit ba397dd

Please sign in to comment.