mirror of
https://github.com/LittleChest/Dohna-NS.git
synced 2026-05-06 22:44:50 +08:00
handler: Add multi-server support
This commit is contained in:
parent
1a10e16745
commit
61e691df70
@ -1,3 +1,5 @@
|
||||
<!-- markdownlint-disable MD034 -->
|
||||
|
||||
# Dohna NS
|
||||
|
||||
Another DNS over HTTPS recursive resolver.
|
||||
@ -20,11 +22,12 @@ Read [Dohna NS Documentation](https://dohna.ovh/) to learn how to install Dohna
|
||||
## Environment Variables
|
||||
|
||||
| Key | Default | Description |
|
||||
| ----------- | ---------------------------- | -------------------------------------------------- |
|
||||
| DNS | https://dns.google/dns-query | Specify a DNS over HTTPS server as the upstream. |
|
||||
| API | https://dns.google/resolve | Specify a JSON API server as the upstream. |
|
||||
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
|
||||
| DNS | ["https://8.8.8.8/dns-query","https://8.8.4.4/dns-query","https://[2001:4860:4860::8888]/dns-query","https://[2001:4860:4860::8888]/dns-query"] | Specify a DNS over HTTPS server as the upstream. |
|
||||
| API | ["https://8.8.8.8/resolve","https://8.8.4.4/resolve","https://[2001:4860:4860::8888]/resolve","https://[2001:4860:4860::8888]/resolve"] | Specify a JSON API server as the upstream. |
|
||||
| IPV4_PREFIX | 32 | Specify the EDNS client subnet IPv4 prefix length. |
|
||||
| IPV6_PREFIX | 128 | Specify the EDNS client subnet IPv6 prefix length. |
|
||||
| CONCURRENT | false | Whether it concurrently queries all servers and returns the fastest result. |
|
||||
|
||||
## Self-hosted
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ export default {
|
||||
env.API,
|
||||
env.IPV4_PREFIX,
|
||||
env.IPV6_PREFIX,
|
||||
request.headers.get("cf-connecting-ip")
|
||||
env.CONCURRENT,
|
||||
request.headers.get("cf-connecting-ip"),
|
||||
),
|
||||
};
|
||||
|
||||
124
common.js
124
common.js
@ -1,11 +1,47 @@
|
||||
export default async function handler(
|
||||
request,
|
||||
dns = "https://dns.google/dns-query",
|
||||
api = "https://dns.google/resolve",
|
||||
dns,
|
||||
api,
|
||||
ipv4Prefix = 32,
|
||||
ipv6Prefix = 128,
|
||||
concurrent = false,
|
||||
rawIP,
|
||||
) {
|
||||
if (!dns || dns.length === 0) {
|
||||
dns = [
|
||||
"https://8.8.8.8/dns-query",
|
||||
"https://8.8.4.4/dns-query",
|
||||
"https://[2001:4860:4860::8888]/dns-query",
|
||||
"https://[2001:4860:4860::8888]/dns-query",
|
||||
];
|
||||
} else {
|
||||
try {
|
||||
dns = JSON.parse(dns.replace(/'/g, '"'));
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
`Unable to parse upstream DNS over HTTPS servers, using ${dns} as the only server.`,
|
||||
);
|
||||
dns = [dns];
|
||||
}
|
||||
}
|
||||
if (!api || api.length === 0) {
|
||||
api = [
|
||||
"https://8.8.8.8/resolve",
|
||||
"https://8.8.4.4/resolve",
|
||||
"https://[2001:4860:4860::8888]/resolve",
|
||||
"https://[2001:4860:4860::8888]/resolve",
|
||||
];
|
||||
} else {
|
||||
try {
|
||||
api = JSON.parse(api.replace(/'/g, '"'));
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
`Unable to parse upstream JSON API servers, using ${api} as the only server.`,
|
||||
);
|
||||
api = [api];
|
||||
}
|
||||
}
|
||||
|
||||
const { method, headers, url } = request;
|
||||
|
||||
const host = headers.get("Host");
|
||||
@ -28,12 +64,40 @@ export default async function handler(
|
||||
res = new Response(null, { status: 400 });
|
||||
|
||||
if (method === "GET" && searchParams.has("name")) {
|
||||
res = fetch(api + search, {
|
||||
if (concurrent) {
|
||||
res = await Promise.any(
|
||||
api.map((server) =>
|
||||
fetch(server + search, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"User-Agent": "Dohna-NS (https://github.com/LittleChest/Dohna-NS)",
|
||||
"User-Agent":
|
||||
"Dohna-NS (https://github.com/LittleChest/Dohna-NS)",
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
const servers = [...api];
|
||||
while (servers.length > 0) {
|
||||
const index = Math.floor(Math.random() * servers.length);
|
||||
const server = servers.splice(index, 1)[0];
|
||||
try {
|
||||
res = await fetch(server + search, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"User-Agent":
|
||||
"Dohna-NS (https://github.com/LittleChest/Dohna-NS)",
|
||||
},
|
||||
});
|
||||
break;
|
||||
} catch (e) {
|
||||
console.warn(`Failed to connect to ${server}: ${e.message}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
console.error("All upstream JSON API servers failed.");
|
||||
res = new Response(null, { status: 500 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,15 +149,29 @@ export default async function handler(
|
||||
queryData = new Uint8Array(requestBody);
|
||||
}
|
||||
|
||||
if (queryData !== undefined) {
|
||||
res = await queryDns(queryData, ip, dns, ipv4Prefix, ipv6Prefix);
|
||||
if (queryData) {
|
||||
res = await queryDns(
|
||||
queryData,
|
||||
ip,
|
||||
dns,
|
||||
ipv4Prefix,
|
||||
ipv6Prefix,
|
||||
concurrent,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
async function queryDns(queryData, ip, dns, ipv4Prefix, ipv6Prefix) {
|
||||
async function queryDns(
|
||||
queryData,
|
||||
ip,
|
||||
dns,
|
||||
ipv4Prefix,
|
||||
ipv6Prefix,
|
||||
concurrent,
|
||||
) {
|
||||
const hasOptRecord = checkForOptRecord(queryData);
|
||||
let newQueryData = queryData;
|
||||
if (!hasOptRecord && ip) {
|
||||
@ -107,7 +185,35 @@ async function queryDns(queryData, ip, dns, ipv4Prefix, ipv6Prefix) {
|
||||
newQueryData = combineQueryData(headerAndQuestion, optRecord);
|
||||
}
|
||||
|
||||
const res = await fetch(dns, {
|
||||
let res = new Response(null, { status: 500 });
|
||||
if (concurrent) {
|
||||
try {
|
||||
res = await Promise.any(
|
||||
dns.map((server) => fetchUpstream(server, ip, newQueryData)),
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("All upstream DNS over HTTPS servers failed: " + e.message);
|
||||
}
|
||||
} else {
|
||||
const servers = [...dns];
|
||||
while (servers.length > 0) {
|
||||
const index = Math.floor(Math.random() * servers.length);
|
||||
const server = servers.splice(index, 1)[0];
|
||||
try {
|
||||
res = await fetchUpstream(server, ip, newQueryData);
|
||||
break;
|
||||
} catch (e) {
|
||||
console.warn(`Failed to connect to ${server}: ${e.message}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!res) console.error("All upstream DNS over HTTPS servers failed.");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function fetchUpstream(dns, ip, newQueryData) {
|
||||
return fetch(dns, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/dns-message",
|
||||
@ -117,8 +223,6 @@ async function queryDns(queryData, ip, dns, ipv4Prefix, ipv6Prefix) {
|
||||
},
|
||||
body: newQueryData,
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function checkForOptRecord(data) {
|
||||
|
||||
@ -6,6 +6,7 @@ export default middleware = async (request) => {
|
||||
process.env.DNS,
|
||||
process.env.API,
|
||||
process.env.IPV4_PREFIX,
|
||||
process.env.IPV6_PREFIX
|
||||
process.env.IPV6_PREFIX,
|
||||
process.env.CONCURRENT,
|
||||
);
|
||||
};
|
||||
|
||||
@ -6,6 +6,7 @@ export default async (request) =>
|
||||
Netlify.env.get("API"),
|
||||
Netlify.env.get("IPV4_PREFIX"),
|
||||
Netlify.env.get("IPV6_PREFIX"),
|
||||
Netlify.context.ip
|
||||
Netlify.env.get("CONCURRENT"),
|
||||
Netlify.context.ip,
|
||||
);
|
||||
export const config = { path: "*" };
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user