feat: DoQ в UI + динамическая фильтрация DNS серверов по протоколу (UDP/DoT/DoH/DoQ)
This commit is contained in:
@@ -42,14 +42,41 @@ export const DOMAIN_LIST_OPTIONS = {
|
||||
cloudfront: 'CloudFront ASN',
|
||||
};
|
||||
|
||||
export const DNS_SERVER_OPTIONS = {
|
||||
'1.1.1.1': '1.1.1.1 (Cloudflare)',
|
||||
'8.8.8.8': '8.8.8.8 (Google)',
|
||||
'9.9.9.9': '9.9.9.9 (Quad9)',
|
||||
'dns.adguard-dns.com': 'dns.adguard-dns.com (AdGuard Default)',
|
||||
'unfiltered.adguard-dns.com':
|
||||
'unfiltered.adguard-dns.com (AdGuard Unfiltered)',
|
||||
'family.adguard-dns.com': 'family.adguard-dns.com (AdGuard Family)',
|
||||
export const DNS_SERVERS_BY_PROTOCOL: Record<string, Record<string, string>> = {
|
||||
udp: {
|
||||
'1.1.1.1': '1.1.1.1 (Cloudflare)',
|
||||
'8.8.8.8': '8.8.8.8 (Google)',
|
||||
'9.9.9.9': '9.9.9.9 (Quad9)',
|
||||
'77.88.8.8': '77.88.8.8 (Yandex)',
|
||||
'94.140.14.14': '94.140.14.14 (AdGuard)',
|
||||
'185.222.222.222': '185.222.222.222 (DNS.SB)',
|
||||
'194.242.2.2': '194.242.2.2 (Mullvad)',
|
||||
},
|
||||
doh: {
|
||||
'https://cloudflare-dns.com/dns-query': 'Cloudflare',
|
||||
'https://dns.google/dns-query': 'Google',
|
||||
'https://dns.quad9.net/dns-query': 'Quad9',
|
||||
'https://dns.adguard.com/dns-query': 'AdGuard',
|
||||
'https://dns.mullvad.net/dns-query': 'Mullvad',
|
||||
'https://dns.nextdns.io/dns-query': 'NextDNS',
|
||||
'https://doh.cleanbrowsing.org/doh/family-filter/': 'CleanBrowsing Family',
|
||||
},
|
||||
dot: {
|
||||
'1.1.1.1': '1.1.1.1 (Cloudflare)',
|
||||
'dns.google': 'Google',
|
||||
'dns.quad9.net': 'Quad9',
|
||||
'dns.adguard.com': 'AdGuard',
|
||||
'dns.mullvad.net': 'Mullvad',
|
||||
'dns.nextdns.io': 'NextDNS',
|
||||
'common.dot.dns.yandex.net': 'Yandex',
|
||||
},
|
||||
doq: {
|
||||
'1.1.1.1:784': 'Cloudflare',
|
||||
'dns.google:784': 'Google',
|
||||
'dns.adguard.com:785': 'AdGuard',
|
||||
'dns.mullvad.net:784': 'Mullvad',
|
||||
'dns.nextdns.io:784': 'NextDNS',
|
||||
},
|
||||
};
|
||||
export const BOOTSTRAP_DNS_SERVER_OPTIONS = {
|
||||
'77.88.8.8': '77.88.8.8 (Yandex DNS)',
|
||||
|
||||
@@ -33,7 +33,7 @@ export { UpdatesTab } from './tachyon/tabs/updates';
|
||||
export {
|
||||
BOOTSTRAP_DNS_SERVER_OPTIONS,
|
||||
DEFAULT_LATENCY_TEST_URL,
|
||||
DNS_SERVER_OPTIONS,
|
||||
DNS_SERVERS_BY_PROTOCOL,
|
||||
DOMAIN_LIST_OPTIONS,
|
||||
LATENCY_TEST_URL_OPTIONS,
|
||||
TACHYON_ACTION_PROVIDERS_AVAILABILITY_EVENT,
|
||||
|
||||
@@ -1118,13 +1118,41 @@ var DOMAIN_LIST_OPTIONS = {
|
||||
digitalocean: "Digital Ocean ASN",
|
||||
cloudfront: "CloudFront ASN"
|
||||
};
|
||||
var DNS_SERVER_OPTIONS = {
|
||||
"1.1.1.1": "1.1.1.1 (Cloudflare)",
|
||||
"8.8.8.8": "8.8.8.8 (Google)",
|
||||
"9.9.9.9": "9.9.9.9 (Quad9)",
|
||||
"dns.adguard-dns.com": "dns.adguard-dns.com (AdGuard Default)",
|
||||
"unfiltered.adguard-dns.com": "unfiltered.adguard-dns.com (AdGuard Unfiltered)",
|
||||
"family.adguard-dns.com": "family.adguard-dns.com (AdGuard Family)"
|
||||
var DNS_SERVERS_BY_PROTOCOL = {
|
||||
udp: {
|
||||
"1.1.1.1": "1.1.1.1 (Cloudflare)",
|
||||
"8.8.8.8": "8.8.8.8 (Google)",
|
||||
"9.9.9.9": "9.9.9.9 (Quad9)",
|
||||
"77.88.8.8": "77.88.8.8 (Yandex)",
|
||||
"94.140.14.14": "94.140.14.14 (AdGuard)",
|
||||
"185.222.222.222": "185.222.222.222 (DNS.SB)",
|
||||
"194.242.2.2": "194.242.2.2 (Mullvad)"
|
||||
},
|
||||
doh: {
|
||||
"https://cloudflare-dns.com/dns-query": "Cloudflare",
|
||||
"https://dns.google/dns-query": "Google",
|
||||
"https://dns.quad9.net/dns-query": "Quad9",
|
||||
"https://dns.adguard.com/dns-query": "AdGuard",
|
||||
"https://dns.mullvad.net/dns-query": "Mullvad",
|
||||
"https://dns.nextdns.io/dns-query": "NextDNS",
|
||||
"https://doh.cleanbrowsing.org/doh/family-filter/": "CleanBrowsing Family"
|
||||
},
|
||||
dot: {
|
||||
"1.1.1.1": "1.1.1.1 (Cloudflare)",
|
||||
"dns.google": "Google",
|
||||
"dns.quad9.net": "Quad9",
|
||||
"dns.adguard.com": "AdGuard",
|
||||
"dns.mullvad.net": "Mullvad",
|
||||
"dns.nextdns.io": "NextDNS",
|
||||
"common.dot.dns.yandex.net": "Yandex"
|
||||
},
|
||||
doq: {
|
||||
"1.1.1.1:784": "Cloudflare",
|
||||
"dns.google:784": "Google",
|
||||
"dns.adguard.com:785": "AdGuard",
|
||||
"dns.mullvad.net:784": "Mullvad",
|
||||
"dns.nextdns.io:784": "NextDNS"
|
||||
}
|
||||
};
|
||||
var BOOTSTRAP_DNS_SERVER_OPTIONS = {
|
||||
"77.88.8.8": "77.88.8.8 (Yandex DNS)",
|
||||
@@ -14933,7 +14961,7 @@ if (typeof structuredClone !== "function")
|
||||
return baseclass.extend({
|
||||
BOOTSTRAP_DNS_SERVER_OPTIONS,
|
||||
DEFAULT_LATENCY_TEST_URL,
|
||||
DNS_SERVER_OPTIONS,
|
||||
DNS_SERVERS_BY_PROTOCOL,
|
||||
DOMAIN_LIST_OPTIONS,
|
||||
DashboardTab,
|
||||
DiagnosticTab,
|
||||
|
||||
@@ -1778,6 +1778,7 @@ function dnsTypeChoices() {
|
||||
return [
|
||||
{ value: "doh", label: _("DNS over HTTPS (DoH)") },
|
||||
{ value: "dot", label: _("DNS over TLS (DoT)") },
|
||||
{ value: "doq", label: _("DNS over QUIC (DoQ)") },
|
||||
{ value: "udp", label: "UDP" },
|
||||
];
|
||||
}
|
||||
@@ -7244,6 +7245,7 @@ function createSectionContent(section) {
|
||||
o.default = "udp";
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
const perRuleDnsTypeOption = o;
|
||||
|
||||
o = section.taboption(
|
||||
"settings",
|
||||
@@ -7254,7 +7256,17 @@ function createSectionContent(section) {
|
||||
);
|
||||
o.depends("action", "dns");
|
||||
o.modalonly = true;
|
||||
configureDnsList(o, main.DNS_SERVER_OPTIONS, "8.8.8.8");
|
||||
configureLiveDynamicListChoices(o, (section_id) => {
|
||||
const dnsType = uci.get(UCI_PACKAGE, section_id, "dns_type") || "udp";
|
||||
const servers = main.DNS_SERVERS_BY_PROTOCOL[dnsType] || main.DNS_SERVERS_BY_PROTOCOL.udp;
|
||||
return Object.entries(servers).map(([value, label]) => ({ value, label: _(label) }));
|
||||
});
|
||||
perRuleDnsTypeOption.onchange = function (_ev, section_id) {
|
||||
const refreshers = dashboardFilterChoiceRefreshers.get(section_id);
|
||||
if (refreshers) {
|
||||
refreshers.forEach((fn) => fn());
|
||||
}
|
||||
};
|
||||
|
||||
o = section.taboption(
|
||||
"settings",
|
||||
|
||||
@@ -138,6 +138,93 @@ function configureDnsFailoverVisibility(option, dnsOption, bootstrapOption) {
|
||||
};
|
||||
}
|
||||
|
||||
function refreshOptionChoices(option, choices) {
|
||||
delete option.keylist;
|
||||
delete option.vallist;
|
||||
(choices || []).forEach((choice) => {
|
||||
if (typeof choice === "object") {
|
||||
option.value(choice.value, choice.label);
|
||||
} else {
|
||||
option.value(choice);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getDnsServerChoices(dnsType) {
|
||||
const servers = main.DNS_SERVERS_BY_PROTOCOL[dnsType] || main.DNS_SERVERS_BY_PROTOCOL.udp;
|
||||
return Object.entries(servers).map(([value, label]) => ({ value, label: _(label) }));
|
||||
}
|
||||
|
||||
function configureDnsDynamicList(option, getChoices, defaultValue) {
|
||||
option.default = [defaultValue];
|
||||
option.rmempty = false;
|
||||
option.validate = function (_section_id, value) {
|
||||
const normalized = `${value || ""}`.trim();
|
||||
if (!normalized) {
|
||||
return optionListValues(option, _section_id).length > 0
|
||||
? true
|
||||
: _("Add at least one DNS server");
|
||||
}
|
||||
const validation = main.validateDNS(normalized);
|
||||
return validation.valid ? true : validation.message;
|
||||
};
|
||||
option.renderWidget = function (section_id, _option_index, cfgvalue) {
|
||||
const values = L.toArray(cfgvalue != null ? cfgvalue : this.default);
|
||||
const choices = getChoices(section_id, values);
|
||||
const labels = {};
|
||||
choices.forEach((choice) => {
|
||||
labels[choice.value] = choice.label;
|
||||
});
|
||||
refreshOptionChoices(this, choices);
|
||||
let choiceSignature = JSON.stringify(
|
||||
choices.map((choice) => [choice.value, choice.label]),
|
||||
);
|
||||
const widget = new ui.DynamicList(values, labels, {
|
||||
id: this.cbid(section_id),
|
||||
sort: this.keylist,
|
||||
optional: this.optional || this.rmempty,
|
||||
datatype: this.datatype,
|
||||
placeholder: this.placeholder,
|
||||
validate: L.bind(this.validate, this, section_id),
|
||||
disabled: this.readonly != null ? this.readonly : this.map.readonly,
|
||||
});
|
||||
const node = widget.render();
|
||||
const refreshChoices = () => {
|
||||
if (!node.isConnected) return false;
|
||||
const currentValues = widget.getValue();
|
||||
const currentChoices = getChoices(section_id, currentValues);
|
||||
const currentLabels = {};
|
||||
currentChoices.forEach((choice) => {
|
||||
currentLabels[choice.value] = choice.label;
|
||||
});
|
||||
const currentSignature = JSON.stringify(
|
||||
currentChoices.map((choice) => [choice.value, choice.label]),
|
||||
);
|
||||
if (currentSignature === choiceSignature) return;
|
||||
choiceSignature = currentSignature;
|
||||
refreshOptionChoices(this, currentChoices);
|
||||
widget.choices = currentLabels;
|
||||
widget.clearChoices();
|
||||
widget.addChoices(
|
||||
currentChoices.map((choice) => choice.value),
|
||||
currentLabels,
|
||||
);
|
||||
};
|
||||
const refreshBeforeOpening = (event) => {
|
||||
if (event.target && event.target.closest(".add-item")) refreshChoices();
|
||||
};
|
||||
node.addEventListener("mousedown", refreshBeforeOpening, true);
|
||||
node.addEventListener("focusin", refreshBeforeOpening, true);
|
||||
if (!settingsDnsDynamicRefreshers.has(section_id)) {
|
||||
settingsDnsDynamicRefreshers.set(section_id, new Set());
|
||||
}
|
||||
settingsDnsDynamicRefreshers.get(section_id).add(refreshChoices);
|
||||
return node;
|
||||
};
|
||||
}
|
||||
|
||||
const settingsDnsDynamicRefreshers = new Map();
|
||||
|
||||
function configureDnsDuration(
|
||||
option,
|
||||
defaultValue,
|
||||
@@ -849,10 +936,13 @@ function createSettingsContent(section, capabilities) {
|
||||
);
|
||||
o.value("doh", _("DNS over HTTPS (DoH)"));
|
||||
o.value("dot", _("DNS over TLS (DoT)"));
|
||||
o.value("doq", _("DNS over QUIC (DoQ)"));
|
||||
o.value("udp", _("UDP (Unprotected DNS)"));
|
||||
o.default = "udp";
|
||||
o.rmempty = false;
|
||||
|
||||
const dnsTypeOption = o;
|
||||
|
||||
const dnsOption = section.option(
|
||||
form.DynamicList,
|
||||
"dns_server",
|
||||
@@ -861,7 +951,10 @@ function createSettingsContent(section, capabilities) {
|
||||
"Main DNS server. If multiple servers are selected, a timeout switches to a backup.",
|
||||
),
|
||||
);
|
||||
configureDnsList(dnsOption, main.DNS_SERVER_OPTIONS, "77.88.8.8");
|
||||
configureDnsDynamicList(dnsOption, (section_id) => {
|
||||
const dnsType = uci.get(UCI_PACKAGE, "settings", "dns_type") || "udp";
|
||||
return getDnsServerChoices(dnsType);
|
||||
}, "77.88.8.8");
|
||||
|
||||
const bootstrapOption = section.option(
|
||||
form.DynamicList,
|
||||
@@ -877,6 +970,13 @@ function createSettingsContent(section, capabilities) {
|
||||
"77.88.8.8",
|
||||
);
|
||||
|
||||
dnsTypeOption.onchange = function (_ev, section_id, value) {
|
||||
const refreshers = settingsDnsDynamicRefreshers.get(section_id);
|
||||
if (refreshers) {
|
||||
refreshers.forEach((fn) => fn());
|
||||
}
|
||||
};
|
||||
|
||||
o = section.option(
|
||||
form.Flag,
|
||||
"fallback_wan_main",
|
||||
|
||||
Reference in New Issue
Block a user