update 2026-08-15 20:26:22

This commit is contained in:
action committed 2026-08-15 20:26:22 +08:00
1 parent d5fe2d005a
commit a459a74f19
32 files changed
+1991 -1606

No files matched your search

+9
View File
@@ -4,6 +4,15 @@
`DNS-over-TLS`, `DNS-over-HTTPS`, `DNSCrypt`, and `DNS-over-QUIC`. Moreover,
it can work as a `DNS-over-HTTPS`, `DNS-over-TLS` or `DNS-over-QUIC` server.
The LuCI interface also supports reusable DNS profiles. Each profile stores
its own bootstrap, upstream, and fallback server lists and can apply all three
lists at once. Editable example profiles for Cloudflare, Quad9, Google, and a
mixed parallel setup are included to demonstrate the feature.
## Requirements
- OpenWrt >= 24.10
## How to install
1. Go to [here](https://fantastic-packages.github.io/releases/)
@@ -2,13 +2,22 @@
'require form';
'require fs';
'require uci';
'require dom';
'require rpc';
'require poll';
'require ui';
'require view';
'require validation';
'require tools.widgets as widgets';
const conf = 'dnsproxy';
const instance = 'dnsproxy';
const profileListOptions = ['bootstrap', 'upstream', 'fallback'];
const upstream_mode = [
['load_balance', _('Load balance'), _('one upstream per request.')],
['parallel', _('Parallel'), _('first DNS response wins.')],
['fastest_addr', _('Fastest address'), _('tests returned IP addresses.')]
];
const callServiceList = rpc.declare({
object: 'service',
@@ -34,6 +43,91 @@ function getServiceStatus() {
});
}
function profileTitle(profile) {
return profile.label || profile['.name'];
}
function applyProfile(select) {
const profile = uci.sections(conf, 'profile').find((item) => item['.name'] === select.value);
if (!profile)
return ui.addNotification(null, E('p', _('The selected DNS profile no longer exists. Reload the page and try again.')), 'error');
const upstreams = L.toArray(profile.upstream).filter(Boolean);
if (!upstreams.length)
return ui.addNotification(null, E('p', _('The selected DNS profile has no upstream servers.')), 'error');
profileListOptions.forEach((option) => {
const values = L.toArray(profile[option]).filter(Boolean);
uci.set(conf, 'servers', option, values.length ? values : null);
});
if (profile.upstream_mode)
uci.set(conf, 'global', 'upstream_mode', profile.upstream_mode);
return uci.save()
.then(L.bind(this.map.load, this.map))
.then(L.bind(this.map.reset, this.map))
.then(() => {
ui.addNotification(null, E('p', _('DNS profile “%s” has been applied.').format(profileTitle(profile))), 'info');
})
.catch((err) => {
ui.addNotification(null, E('p', _('Failed to apply DNS profile: %s').format(err.message || err)), 'error');
});
}
function validateUniqueValue(section_id, value) {
if (!value)
return _('Expecting: %s').format(_('non-empty value'));
let duplicate = false;
uci.sections(this.config, this.section.sectiontype, (res) => {
if (res['.name'] !== section_id)
if (res[this.option] === value)
duplicate = true;
});
if (duplicate)
return _('Expecting: %s').format(_('unique value'));
return true;
}
function validateServerValue(section_id, value) {
if (!value)
return true;
if (!value.includes('://')) {
let stubValidator = {
factory: validation,
apply(type, value, args) {
if (value != null)
this.value = value;
return validation.types[type].apply(this, args);
},
assert(condition) {
return !!condition;
}
};
if (!stubValidator.apply('host', value) && !stubValidator.apply('hostport', value))
return _('Expecting: %s').format(_('valid host or hostport'));
return true;
}
try {
const url = new URL(value);
if (!url.hostname)
return _('Expecting: %s').format(_('valid URL'));
return true;
}
catch (e) {
return _('Invalid URI');
}
}
return view.extend({
load() {
@@ -116,7 +210,8 @@ return view.extend({
o = s.taboption('main', form.Flag, 'insecure', _('Disable secure TLS cert validation'));
o = s.taboption('main', form.Flag, 'http3', _('DoH uses H3 first'));
o = s.taboption('main', form.Flag, 'http3', _('Enable HTTP/3 for DoH'));
o.description = _('Use HTTP/3 when it is faster. HTTPS fallback remains available.');
o = s.taboption('main', form.Value, 'timeout', _('Timeout for queries to remote upstream (default: 10s)'));
o.datatype = 'string';
@@ -127,14 +222,17 @@ return view.extend({
o = s.taboption('main', form.Value, 'udp_buf_size', _('Size of the UDP buffer in bytes. Set 0 use the system default'));
o.datatype = 'uinteger';
o = s.taboption('main', form.Flag, 'all_servers', _('Parallel queries all upstream'));
o = s.taboption('main', form.Flag, 'fastest_addr', _('Respond to A or AAAA requests only with the fastest IP address'));
o.depends('all_servers', '1');
o = s.taboption('main', form.RichListValue, 'upstream_mode', _('Upstream selection mode'),
_('For the lowest DNS response time choose Parallel.') + '</br>' +
_('Fastest address performs additional IP reachability tests and is a different, slower operation.'));
upstream_mode.forEach((res) => {
o.value.apply(o, res);
})
o.rmempty = false;
s.tab('cache', _('Cache'));
o = s.taboption('cache', form.SectionValue, '_cache', form.NamedSection, 'cache', 'homeproxy');
o = s.taboption('cache', form.SectionValue, '_cache', form.NamedSection, 'cache', 'dnsproxy');
ss = o.subsection;
so = ss.option(form.Flag, 'enabled', _('Enable Cache'));
@@ -161,7 +259,7 @@ return view.extend({
s.tab('dns64', _('DNS64'));
o = s.taboption('dns64', form.SectionValue, '_dns64', form.NamedSection, 'dns64', 'homeproxy');
o = s.taboption('dns64', form.SectionValue, '_dns64', form.NamedSection, 'dns64', 'dnsproxy');
ss = o.subsection;
so = ss.option(form.Flag, 'enabled', _('Enable DNS64'));
@@ -174,7 +272,7 @@ return view.extend({
s.tab('edns', _('EDNS'));
o = s.taboption('edns', form.SectionValue, '_edns', form.NamedSection, 'edns', 'homeproxy');
o = s.taboption('edns', form.SectionValue, '_edns', form.NamedSection, 'edns', 'dnsproxy');
ss = o.subsection;
so = ss.option(form.Flag, 'enabled', _('Enable EDNS'));
@@ -186,7 +284,7 @@ return view.extend({
s.tab('bogus_nxdomain', _('Bogus-NXDOMAIN'));
o = s.taboption('bogus_nxdomain', form.SectionValue, '_bogus_nxdomain', form.NamedSection, 'bogus_nxdomain', 'homeproxy');
o = s.taboption('bogus_nxdomain', form.SectionValue, '_bogus_nxdomain', form.NamedSection, 'bogus_nxdomain', 'dnsproxy');
ss = o.subsection;
so = ss.option(form.DynamicList, 'ip_addr', _('Convert matching single IP responses to NXDOMAIN'));
@@ -194,15 +292,107 @@ return view.extend({
s.tab('servers', _('Upstreams'));
o = s.taboption('servers', form.SectionValue, '_servers', form.NamedSection, 'servers', 'homeproxy');
o = s.taboption('servers', form.DummyValue, '_profile_switcher', _('DNS profiles'),
_('A profile replaces Bootstrap, Upstream and Fallback lists together. Unsaved changes elsewhere on this page are not included.'));
o.renderWidget = function () {
const profiles = uci.sections(conf, 'profile');
const select = E('select', {
'id': 'dnsproxy-profile-select',
'class': 'cbi-input-select',
'disabled': profiles.length ? null : ''
}, profiles.map((profile) => E('option', {
'value': profile['.name']
}, profileTitle(profile))));
return E('div', {}, [
E('div', { 'style': 'display:flex;gap:.75em;align-items:center;flex-wrap:wrap' }, [
select,
E('button', {
'class': 'cbi-button cbi-button-positive important',
'disabled': profiles.length ? null : '',
'click': ui.createHandlerFn(this, applyProfile, select)
}, _('Use selected profile'))
]),
profiles.length ? '' : E('p', {}, _('Create and save a profile in the “DNS profile templates” section below first.'))
]);
};
o = s.taboption('servers', form.SectionValue, '_servers', form.NamedSection, 'servers', 'dnsproxy');
ss = o.subsection;
so = ss.option(form.DynamicList, 'bootstrap', _('Bootstrap DNS Server'));
so.readonly = true
so = ss.option(form.DynamicList, 'upstream', _('Upstream DNS Server'));
so.readonly = true
so.rmempty = false;
so = ss.option(form.DynamicList, 'fallback', _('Fallback DNS Server'));
so.readonly = true
o = s.taboption('servers', form.SectionValue, '_profiles', form.GridSection, 'profile', _('DNS profile templates'),
_('Create reusable templates here. Applying a template does not modify the template itself.'));
ss = o.subsection;
ss.anonymous = true;
ss.addremove = true;
ss.rowcolors = true;
ss.sortable = true;
ss.nodescriptions = true;
ss.addbtntitle = _('Add DNS profile');
ss.renderRowActions = function(section_id, more_label, trEl) {
trEl = form.GridSection.prototype.renderRowActions.apply(this, arguments);
const preset_ids = [
'default'
];
if (preset_ids.includes(section_id))
trEl?.lastChild.querySelector('.cbi-button-remove')?.remove();
return trEl;
}
so = ss.option(form.Value, 'label', _('Profile name'));
so.rmempty = false;
so.validate = validateUniqueValue;
so = ss.option(form.RichListValue, 'upstream_mode', _('Upstream selection mode'));
so.value(' ', _('Keep current mode'));
upstream_mode.forEach((res) => {
so.value.apply(so, res);
})
so.load = function(section_id) {
let value = this.super('load', section_id);
if (!value)
return ' ';
return value;
}
so.write = function(section_id, value) {
if (!value.trim())
return this.super('remove', section_id);
return form.RichListValue.prototype.write.call(this, section_id, value);
}
so.textvalue = function(section_id) {
let cval = this.cfgvalue(section_id);
let i = this.keylist.indexOf(cval);
let val = this.vallist[i];
return dom.elem(val) ? val.firstChild.textContent : val ?? cval;
}
so = ss.option(form.DynamicList, 'bootstrap', _('Bootstrap DNS'));
so.validate = validateServerValue;
so.modalonly = true;
so = ss.option(form.DynamicList, 'upstream', _('Upstream DNS'));
so.rmempty = false;
so.validate = validateServerValue;
so.modalonly = true;
so = ss.option(form.DynamicList, 'fallback', _('Fallback DNS'));
so.validate = validateServerValue;
so.modalonly = true;
return m.render()
.then(L.bind(function(m, nodes) {
+178 -46
View File
@@ -1,155 +1,287 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:187
#: htdocs/luci-static/resources/view/dnsproxy.js:296
msgid ""
"A profile replaces Bootstrap, Upstream and Fallback lists together. Unsaved "
"changes elsewhere on this page are not included."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:341
msgid "Add DNS profile"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:285
msgid "Bogus-NXDOMAIN"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:200
#: htdocs/luci-static/resources/view/dnsproxy.js:384
msgid "Bootstrap DNS"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:323
msgid "Bootstrap DNS Server"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:135
#: htdocs/luci-static/resources/view/dnsproxy.js:233
msgid "Cache"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:146
#: htdocs/luci-static/resources/view/dnsproxy.js:244
msgid "Cache size (in bytes)"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:70
#: htdocs/luci-static/resources/view/dnsproxy.js:164
msgid "Collecting data ..."
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:192
#: htdocs/luci-static/resources/view/dnsproxy.js:290
msgid "Convert matching single IP responses to NXDOMAIN"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:65
#: applications/luci-app-dnsproxy/root/usr/share/luci/menu.d/luci-app-dnsproxy.json:3
#: htdocs/luci-static/resources/view/dnsproxy.js:316
msgid ""
"Create and save a profile in the “DNS profile templates” section below first."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:334
msgid ""
"Create reusable templates here. Applying a template does not modify the "
"template itself."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:159
#: root/usr/share/luci/menu.d/luci-app-dnsproxy.json:3
msgid "DNS Proxy"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:162
#: htdocs/luci-static/resources/view/dnsproxy.js:333
msgid "DNS profile templates"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:72
msgid "DNS profile “%s” has been applied."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:295
msgid "DNS profiles"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:260
msgid "DNS64"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:169
#: htdocs/luci-static/resources/view/dnsproxy.js:267
msgid "DNS64 Prefix"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:113
#: htdocs/luci-static/resources/view/dnsproxy.js:207
msgid "Disable IPv6"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:117
#: htdocs/luci-static/resources/view/dnsproxy.js:211
msgid "Disable secure TLS cert validation"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:119
msgid "DoH uses H3 first"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:175
#: htdocs/luci-static/resources/view/dnsproxy.js:273
msgid "EDNS"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:182
#: htdocs/luci-static/resources/view/dnsproxy.js:280
msgid "EDNS Client Address"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:78
#: htdocs/luci-static/resources/view/dnsproxy.js:172
msgid "Enable"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:140
#: htdocs/luci-static/resources/view/dnsproxy.js:238
msgid "Enable Cache"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:167
#: htdocs/luci-static/resources/view/dnsproxy.js:265
msgid "Enable DNS64"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:180
#: htdocs/luci-static/resources/view/dnsproxy.js:278
msgid "Enable EDNS"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:205
#: htdocs/luci-static/resources/view/dnsproxy.js:213
msgid "Enable HTTP/3 for DoH"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:81
#: htdocs/luci-static/resources/view/dnsproxy.js:90
#: htdocs/luci-static/resources/view/dnsproxy.js:114
#: htdocs/luci-static/resources/view/dnsproxy.js:122
msgid "Expecting: %s"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:75
msgid "Failed to apply DNS profile: %s"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:393
msgid "Fallback DNS"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:330
msgid "Fallback DNS Server"
msgstr ""
#: applications/luci-app-dnsproxy/root/usr/share/rpcd/acl.d/luci-app-dnsproxy.json:3
#: htdocs/luci-static/resources/view/dnsproxy.js:19
msgid "Fastest address"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:227
msgid ""
"Fastest address performs additional IP reachability tests and is a "
"different, slower operation."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:226
msgid "For the lowest DNS response time choose Parallel."
msgstr ""
#: root/usr/share/rpcd/acl.d/luci-app-dnsproxy.json:3
msgid "Grant access to LuCI app dnsproxy"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:86
#: htdocs/luci-static/resources/view/dnsproxy.js:127
msgid "Invalid URI"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:359
msgid "Keep current mode"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:180
msgid "Listen address"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:108
#: htdocs/luci-static/resources/view/dnsproxy.js:202
msgid "Listen ports"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:83
#: htdocs/luci-static/resources/view/dnsproxy.js:17
msgid "Load balance"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:177
msgid "Log file path"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:76
#: htdocs/luci-static/resources/view/dnsproxy.js:170
msgid "Main"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:157
#: htdocs/luci-static/resources/view/dnsproxy.js:255
msgid "Max TTL value for DNS entries, in seconds"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:152
#: htdocs/luci-static/resources/view/dnsproxy.js:250
msgid "Min TTL value for DNS entries, in seconds"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:142
#: htdocs/luci-static/resources/view/dnsproxy.js:240
msgid "Optimistic Cache"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:130
msgid "Parallel queries all upstream"
#: htdocs/luci-static/resources/view/dnsproxy.js:18
msgid "Parallel"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:124
#: htdocs/luci-static/resources/view/dnsproxy.js:354
msgid "Profile name"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:219
msgid "Ratelimit (requests per second)"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:115
#: htdocs/luci-static/resources/view/dnsproxy.js:209
msgid "Refuse <code>ANY</code> requests"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:132
msgid "Respond to A or AAAA requests only with the fastest IP address"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:54
#: htdocs/luci-static/resources/view/dnsproxy.js:148
msgid "SERVER NOT RUNNING"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:52
#: htdocs/luci-static/resources/view/dnsproxy.js:146
msgid "SERVER RUNNING"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:127
#: htdocs/luci-static/resources/view/dnsproxy.js:222
msgid "Size of the UDP buffer in bytes. Set 0 use the system default"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:121
#: htdocs/luci-static/resources/view/dnsproxy.js:58
msgid "The selected DNS profile has no upstream servers."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:54
msgid ""
"The selected DNS profile no longer exists. Reload the page and try again."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:216
msgid "Timeout for queries to remote upstream (default: 10s)"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:202
#: htdocs/luci-static/resources/view/dnsproxy.js:388
msgid "Upstream DNS"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:326
msgid "Upstream DNS Server"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:195
#: htdocs/luci-static/resources/view/dnsproxy.js:225
#: htdocs/luci-static/resources/view/dnsproxy.js:358
msgid "Upstream selection mode"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:293
msgid "Upstreams"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:81
#: htdocs/luci-static/resources/view/dnsproxy.js:214
msgid "Use HTTP/3 when it is faster. HTTPS fallback remains available."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:314
msgid "Use selected profile"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:175
msgid "Verbose"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:18
msgid "first DNS response wins."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:81
msgid "non-empty value"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:17
msgid "one upstream per request."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:19
msgid "tests returned IP addresses."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:90
msgid "unique value"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:122
msgid "valid URL"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:114
msgid "valid host or hostport"
msgstr ""
@@ -0,0 +1,58 @@
#!/bin/sh
add_cloudflare_profile() {
uci set dnsproxy.preset_cloudflare='profile'
uci set dnsproxy.preset_cloudflare.label='Preset: Cloudflare DoH'
uci add_list dnsproxy.preset_cloudflare.bootstrap='1.1.1.1:53'
uci add_list dnsproxy.preset_cloudflare.bootstrap='1.0.0.1:53'
uci add_list dnsproxy.preset_cloudflare.upstream='https://cloudflare-dns.com/dns-query'
uci add_list dnsproxy.preset_cloudflare.fallback='https://dns.quad9.net/dns-query'
uci set dnsproxy.preset_cloudflare.upstream_mode='load_balance'
}
add_quad9_profile() {
uci set dnsproxy.preset_quad9='profile'
uci set dnsproxy.preset_quad9.label='Preset: Quad9 DoH'
uci add_list dnsproxy.preset_quad9.bootstrap='9.9.9.9:53'
uci add_list dnsproxy.preset_quad9.bootstrap='149.112.112.112:53'
uci add_list dnsproxy.preset_quad9.upstream='https://dns.quad9.net/dns-query'
uci add_list dnsproxy.preset_quad9.fallback='https://cloudflare-dns.com/dns-query'
uci set dnsproxy.preset_quad9.upstream_mode='load_balance'
}
add_google_profile() {
uci set dnsproxy.preset_google='profile'
uci set dnsproxy.preset_google.label='Preset: Google DoH'
uci add_list dnsproxy.preset_google.bootstrap='8.8.8.8:53'
uci add_list dnsproxy.preset_google.bootstrap='8.8.4.4:53'
uci add_list dnsproxy.preset_google.upstream='https://dns.google/dns-query'
uci add_list dnsproxy.preset_google.fallback='https://cloudflare-dns.com/dns-query'
uci set dnsproxy.preset_google.upstream_mode='load_balance'
}
add_fast_mixed_profile() {
uci set dnsproxy.preset_fast_mixed='profile'
uci set dnsproxy.preset_fast_mixed.label='Preset: Fast mixed DoH'
uci add_list dnsproxy.preset_fast_mixed.bootstrap='1.1.1.1:53'
uci add_list dnsproxy.preset_fast_mixed.bootstrap='9.9.9.9:53'
uci add_list dnsproxy.preset_fast_mixed.bootstrap='8.8.8.8:53'
uci add_list dnsproxy.preset_fast_mixed.upstream='https://cloudflare-dns.com/dns-query'
uci add_list dnsproxy.preset_fast_mixed.upstream='https://dns.quad9.net/dns-query'
uci add_list dnsproxy.preset_fast_mixed.fallback='https://dns.google/dns-query'
uci set dnsproxy.preset_fast_mixed.upstream_mode='parallel'
}
# preset
if [ -z "$(uci -q get dnsproxy.default)" ]; then
uci -q get dnsproxy.preset_cloudflare >/dev/null || add_cloudflare_profile
uci -q get dnsproxy.preset_quad9 >/dev/null || add_quad9_profile
uci -q get dnsproxy.preset_google >/dev/null || add_google_profile
uci -q get dnsproxy.preset_fast_mixed >/dev/null || add_fast_mixed_profile
uci commit dnsproxy
# default
echo "config profile 'default'" >> /etc/config/dnsproxy
echo " option label 'Default'" >> /etc/config/dnsproxy
sed -n "/config dnsproxy 'servers'/,/^[^\t]/{/^\t/p}" /etc/config/dnsproxy >> /etc/config/dnsproxy
fi
exit 0