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
15
README.md
15
README.md
@ -1,3 +1,5 @@
|
|||||||
|
<!-- markdownlint-disable MD034 -->
|
||||||
|
|
||||||
# Dohna NS
|
# Dohna NS
|
||||||
|
|
||||||
Another DNS over HTTPS recursive resolver.
|
Another DNS over HTTPS recursive resolver.
|
||||||
@ -19,12 +21,13 @@ Read [Dohna NS Documentation](https://dohna.ovh/) to learn how to install Dohna
|
|||||||
|
|
||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|
||||||
| Key | Default | Description |
|
| Key | Default | Description |
|
||||||
| ----------- | ---------------------------- | -------------------------------------------------- |
|
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
|
||||||
| DNS | https://dns.google/dns-query | Specify a DNS over HTTPS 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://dns.google/resolve | Specify a JSON API 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. |
|
| IPV4_PREFIX | 32 | Specify the EDNS client subnet IPv4 prefix length. |
|
||||||
| IPV6_PREFIX | 128 | Specify the EDNS client subnet IPv6 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
|
## Self-hosted
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ export default {
|
|||||||
env.API,
|
env.API,
|
||||||
env.IPV4_PREFIX,
|
env.IPV4_PREFIX,
|
||||||
env.IPV6_PREFIX,
|
env.IPV6_PREFIX,
|
||||||
request.headers.get("cf-connecting-ip")
|
env.CONCURRENT,
|
||||||
|
request.headers.get("cf-connecting-ip"),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|||||||
132
common.js
132
common.js
@ -1,11 +1,47 @@
|
|||||||
export default async function handler(
|
export default async function handler(
|
||||||
request,
|
request,
|
||||||
dns = "https://dns.google/dns-query",
|
dns,
|
||||||
api = "https://dns.google/resolve",
|
api,
|
||||||
ipv4Prefix = 32,
|
ipv4Prefix = 32,
|
||||||
ipv6Prefix = 128,
|
ipv6Prefix = 128,
|
||||||
|
concurrent = false,
|
||||||
rawIP,
|
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 { method, headers, url } = request;
|
||||||
|
|
||||||
const host = headers.get("Host");
|
const host = headers.get("Host");
|
||||||
@ -28,12 +64,40 @@ export default async function handler(
|
|||||||
res = new Response(null, { status: 400 });
|
res = new Response(null, { status: 400 });
|
||||||
|
|
||||||
if (method === "GET" && searchParams.has("name")) {
|
if (method === "GET" && searchParams.has("name")) {
|
||||||
res = fetch(api + search, {
|
if (concurrent) {
|
||||||
method: "GET",
|
res = await Promise.any(
|
||||||
headers: {
|
api.map((server) =>
|
||||||
"User-Agent": "Dohna-NS (https://github.com/LittleChest/Dohna-NS)",
|
fetch(server + search, {
|
||||||
},
|
method: "GET",
|
||||||
});
|
headers: {
|
||||||
|
"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);
|
queryData = new Uint8Array(requestBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queryData !== undefined) {
|
if (queryData) {
|
||||||
res = await queryDns(queryData, ip, dns, ipv4Prefix, ipv6Prefix);
|
res = await queryDns(
|
||||||
|
queryData,
|
||||||
|
ip,
|
||||||
|
dns,
|
||||||
|
ipv4Prefix,
|
||||||
|
ipv6Prefix,
|
||||||
|
concurrent,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function queryDns(queryData, ip, dns, ipv4Prefix, ipv6Prefix) {
|
async function queryDns(
|
||||||
|
queryData,
|
||||||
|
ip,
|
||||||
|
dns,
|
||||||
|
ipv4Prefix,
|
||||||
|
ipv6Prefix,
|
||||||
|
concurrent,
|
||||||
|
) {
|
||||||
const hasOptRecord = checkForOptRecord(queryData);
|
const hasOptRecord = checkForOptRecord(queryData);
|
||||||
let newQueryData = queryData;
|
let newQueryData = queryData;
|
||||||
if (!hasOptRecord && ip) {
|
if (!hasOptRecord && ip) {
|
||||||
@ -107,7 +185,35 @@ async function queryDns(queryData, ip, dns, ipv4Prefix, ipv6Prefix) {
|
|||||||
newQueryData = combineQueryData(headerAndQuestion, optRecord);
|
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",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/dns-message",
|
"Content-Type": "application/dns-message",
|
||||||
@ -117,8 +223,6 @@ async function queryDns(queryData, ip, dns, ipv4Prefix, ipv6Prefix) {
|
|||||||
},
|
},
|
||||||
body: newQueryData,
|
body: newQueryData,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkForOptRecord(data) {
|
function checkForOptRecord(data) {
|
||||||
|
|||||||
@ -6,6 +6,7 @@ export default middleware = async (request) => {
|
|||||||
process.env.DNS,
|
process.env.DNS,
|
||||||
process.env.API,
|
process.env.API,
|
||||||
process.env.IPV4_PREFIX,
|
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("API"),
|
||||||
Netlify.env.get("IPV4_PREFIX"),
|
Netlify.env.get("IPV4_PREFIX"),
|
||||||
Netlify.env.get("IPV6_PREFIX"),
|
Netlify.env.get("IPV6_PREFIX"),
|
||||||
Netlify.context.ip
|
Netlify.env.get("CONCURRENT"),
|
||||||
|
Netlify.context.ip,
|
||||||
);
|
);
|
||||||
export const config = { path: "*" };
|
export const config = { path: "*" };
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user