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

+1 -1
View File
@@ -10,7 +10,7 @@ PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=d44529e9525a82ae3f9a42c9cb674256b943f67e
PKG_SOURCE_VERSION:=1f2d6de4a6d1fc9718e7179a124aad0f70002d2d
PKG_SOURCE_URL:=https://github.com/olicesx/dae.git
PKG_MIRROR_HASH:=skip
+1 -1
View File
@@ -16,7 +16,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-amlogic
PKG_VERSION:=3.1.320
PKG_VERSION:=3.1.321
PKG_RELEASE:=2
PKG_LICENSE:=GPL-2.0 License
@@ -68,7 +68,7 @@ get_textoffset() {
temp_dir="$(mktemp -d)"
(
cd ${temp_dir}
tar -xf "${boot_tgz_file}" "${vmlinuz_name}"
tar --no-same-owner -xf "${boot_tgz_file}" "${vmlinuz_name}"
)
# With TEXT_OFFSET patch is [ 0108 ], without TEXT_OFFSET patch is [ 0000 ]
[[ "$(hexdump -n 15 -x "${temp_dir}/${vmlinuz_name}" 2>/dev/null | head -n 1 | awk '{print $7}')" == "0108" ]] && K510="0"
@@ -274,7 +274,7 @@ update_kernel() {
dtb* 2>/dev/null
)
# Extract the new kernel
tar -xf ${P4_PATH}/boot-${kernel_name}.tar.gz -C /boot
tar --no-same-owner -xf ${P4_PATH}/boot-${kernel_name}.tar.gz -C /boot
# Check if the file exists
local valid_files
@@ -319,14 +319,14 @@ update_kernel() {
else
(cd /boot && mkdir -p dtb-${kernel_name}/${PLATFORM} && ln -sf dtb-${kernel_name} dtb)
fi
tar -xf ${P4_PATH}/dtb-${PLATFORM}-${kernel_name}.tar.gz -C /boot/dtb/${PLATFORM}
tar --no-same-owner -xf ${P4_PATH}/dtb-${PLATFORM}-${kernel_name}.tar.gz -C /boot/dtb/${PLATFORM}
[[ "$(ls /boot/dtb/${PLATFORM} -l 2>/dev/null | grep "^-" | wc -l)" -ge "1" ]] || error_msg "/boot/dtb/${PLATFORM} file is missing."
echo -e "(2/3) Unpacking [ dtb-${PLATFORM}-${kernel_name}.tar.gz ] done."
fi
# 03. for /lib/modules/*
rm -rf /lib/modules/*
tar -xf ${P4_PATH}/modules-${kernel_name}.tar.gz -C /lib/modules
tar --no-same-owner -xf ${P4_PATH}/modules-${kernel_name}.tar.gz -C /lib/modules
(
cd /lib/modules/${kernel_name}
rm -f *.ko
@@ -210,6 +210,14 @@ case $MYDEVICE_NAME in
fi
SOC="ht2"
;;
"XYLink XR3528")
if [ -n "${CURRENT_FDTFILE}" ]; then
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
else
MYDTB_FDTFILE="rk3528-xylink-xr3528.dtb"
fi
SOC="xylink-xr3528"
;;
"Radxa E20C")
if [ -n "${CURRENT_FDTFILE}" ]; then
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
+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
@@ -249,7 +249,7 @@ end
function get_redir_log()
local id = http.formvalue("id")
local name = http.formvalue("name")
local file_path = "/tmp/etc/passwall2/acl/" .. id .. "/" .. name .. ".log"
local file_path = api.TMP_PATH .. "/acl/" .. id .. "/" .. name .. ".log"
if nixio.fs.access(file_path) then
local content = luci.sys.exec("tail -n 19999 '" .. file_path .. "'")
content = content:gsub("\n", "<br />")
@@ -261,7 +261,7 @@ end
function get_socks_log()
local name = http.formvalue("name")
local path = "/tmp/etc/passwall2/" .. name .. ".log"
local path = api.TMP_PATH .. "/" .. name .. ".log"
if nixio.fs.access(path) then
local content = luci.sys.exec("tail -n 5000 ".. path)
content = content:gsub("\n", "<br />")
@@ -294,7 +294,7 @@ end
function index_status()
local e = {}
e["global_status"] = luci.sys.call("/bin/busybox top -bn1 | grep -v 'grep' | grep '/tmp/etc/passwall2/bin/' | grep 'default' | grep 'global' >/dev/null") == 0
e["global_status"] = luci.sys.call("/bin/busybox top -bn1 | grep -v 'grep' | grep '%s/bin/' | grep 'default' | grep 'global' >/dev/null" % api.TMP_PATH) == 0
http_write_json(e)
end
@@ -665,8 +665,9 @@ end
function server_log()
local id = http.formvalue("id")
if nixio.fs.access("/tmp/etc/passwall2_server/" .. id .. ".log") then
local content = luci.sys.exec("cat /tmp/etc/passwall2_server/" .. id .. ".log")
local f_file = api.S_TMP_PATH .. "/" .. id .. ".log"
if nixio.fs.access(f_file) then
local content = luci.sys.exec("cat " .. f_file)
content = content:gsub("\n", "<br />")
http.write(content)
else
@@ -2,9 +2,10 @@ local api = require "luci.passwall2.api"
api.set_default_cbi()
m = Map()
m.redirect = api.url("acl")
if not arg[1] or not m:get(arg[1]) then
luci.http.redirect(api.url("acl"))
luci.http.redirect(m.redirect)
end
m:appendTemplate("/cbi/nodes_listvalue_com")
@@ -2,13 +2,12 @@ api = require "luci.passwall2.api"
api.set_default_cbi()
m = Map()
m.redirect = api.url()
m.redirect = api.url("node_list")
if not arg[1] or not m:get(arg[1]) then
luci.http.redirect(api.url("node_list"))
luci.http.redirect(m.redirect)
end
fs = require "nixio.fs"
formvalue_key = "cbid." .. m.config .. "." .. arg[1] .. "."
m:appendTemplate("/node_config/header", {section = arg[1]})
@@ -24,7 +23,7 @@ m:foreach("nodes", function(s)
end
end)
s = m:section(NamedSection, arg[1], "nodes", translate("Node Config"))
local s = m:section(NamedSection, arg[1], "nodes", translate("Node Config"))
s.addremove = false
s.dynamic = false
@@ -74,7 +73,7 @@ end
o = s:option(ListValue, "type", translate("Type"))
local type_table = {}
for filename in fs.dir(types_dir) do
for filename in api.fs.dir(types_dir) do
table.insert(type_table, filename)
end
table.sort(type_table)
@@ -2,9 +2,10 @@ local api = require "luci.passwall2.api"
api.set_default_cbi()
m = Map()
m.redirect = api.url()
if not arg[1] or not m:get(arg[1]) then
luci.http.redirect(api.url())
luci.http.redirect(m.redirect)
end
m:appendTemplate("/cbi/nodes_dynamiclist_com")
@@ -75,6 +76,20 @@ o = s:option(Flag, "log", translate("Enable") .. " " .. translate("Log"))
o.default = 1
o.rmempty = false
o = s:option(DummyValue, "_log", translate("Log File"))
o.rawhtml = true
o.cfgvalue = function(t, n)
local log_path = api.TMP_PATH .. "/" .. arg[1] .. ".log"
local log_url = api.url("get_socks_log") .. "?name=" .. arg[1]
return string.format(
'<code>%s</code>&nbsp;&nbsp;<input class="btn cbi-button cbi-button-apply" type="button" value="%s" onclick="window.open(\'%s\', \'_blank\')" />',
log_path,
translate("View Log"),
log_url
)
end
o:depends("log", true)
o = s:option(Flag, "enable_autoswitch", translate("Auto Switch"))
o.default = 0
o.rmempty = false
@@ -1,51 +1,43 @@
local m, s = ...
if not api.finded_com("hysteria") then
return
end
type_name = "Hysteria2"
-- [[ Hysteria2 ]]
local m, s1 = ...
local type_name = "Hysteria2"
s.fields["type"]:value(type_name, "Hysteria2")
s1.fields["type"]:value(type_name, "Hysteria2")
if s.val["type"] ~= type_name then
if s1.val["type"] ~= type_name then
return
end
local option_prefix = "hysteria2_"
local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "hysteria2_"
local function _n(name)
return option_prefix .. name
end
o = s:option(Value, "address", translate("Address (Support Domain Name)"))
o:depends({ realms = false })
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)"))
o:depends({ [_n("realms")] = false })
o = s:option(Value, _n("port"), translate("Port"))
o = s:option(Value, "port", translate("Port"))
o.datatype = "port"
o:depends({ [_n("realms")] = false })
o:depends({ realms = false })
o = s:option(Value, _n("hop"), translate("Port hopping range"))
o = s:option(Value, "hop", translate("Port hopping range"))
o.description = translate("Format as 1000:2000 or 1000-2000 Multiple groups are separated by commas (,).")
o.rewrite_option = o.option
o:depends({ [_n("realms")] = false })
o:depends({ realms = false })
o = s:option(Value, _n("hop_interval"), translate("Hop Interval(second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5."))
o = s:option(Value, "hop_interval", translate("Hop Interval(second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5."))
o.datatype = "or(uinteger,portrange)"
o.placeholder = "30"
o.default = "30"
o.rewrite_option = o.option
o:depends({ [_n("realms")] = false })
o:depends({ realms = false })
o = s:option(Flag, _n("realms"), translate("Realms"))
o = s:option(Flag, "realms", translate("Realms"))
o.default = "0"
o.rewrite_option = o.option
o = s:option(Value, _n("realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o.rewrite_option = o.option
o:depends({ [_n("realms")] = "1" })
o = s:option(Value, "realm_url", translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ realms = "1" })
o.validate = function(self, value)
value = api.trim(value)
local realm = api.parse_realm_uri(value)
@@ -53,76 +45,62 @@ o.validate = function(self, value)
return nil, translate("Invalid Realm URL.")
end
o = s:option(DynamicList, _n("realm_stun"), translate("Realm STUN"))
o = s:option(DynamicList, "realm_stun", translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o.rewrite_option = o.option
o:depends({ [_n("realms")] = "1" })
o:depends({ realms = "1" })
o = s:option(Value, _n("auth_password"), translate("Auth Password"))
o = s:option(Value, "auth_password", translate("Auth Password"))
o.password = true
o.rewrite_option = o.option
o = s:option(ListValue, _n("obfs_type"), translate("Obfs Type"))
o = s:option(ListValue, "obfs_type", translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:value("gecko")
o.rewrite_option = o.option
o = s:option(Value, _n("obfs_password"), translate("Obfs Password"))
o.rewrite_option = o.option
o:depends({ [_n("obfs_type")] = "salamander" })
o:depends({ [_n("obfs_type")] = "gecko" })
o = s:option(Value, "obfs_password", translate("Obfs Password"))
o:depends({ obfs_type = "salamander" })
o:depends({ obfs_type = "gecko" })
o = s:option(Value, _n("obfs_MinPacketSize"), translate("Gecko Packet Size (min)"))
o = s:option(Value, "obfs_MinPacketSize", translate("Gecko Packet Size (min)"))
o.datatype = "uinteger"
o.placeholder = "512"
o.default = "512"
o:depends({ [_n("obfs_type")] = "gecko" })
o.rewrite_option = o.option
o:depends({ obfs_type = "gecko" })
o = s:option(Value, _n("obfs_MaxPacketSize"), translate("Gecko Packet Size (max)"))
o = s:option(Value, "obfs_MaxPacketSize", translate("Gecko Packet Size (max)"))
o.datatype = "uinteger"
o.placeholder = "1200"
o.default = "1200"
o:depends({ [_n("obfs_type")] = "gecko" })
o.rewrite_option = o.option
o:depends({ obfs_type = "gecko" })
o = s:option(Flag, _n("fast_open"), translate("Fast Open"))
o = s:option(Flag, "fast_open", translate("Fast Open"))
o.default = "0"
o = s:option(Value, _n("tls_serverName"), translate("Domain"))
o = s:option(Value, "tls_serverName", translate("Domain"))
o = s:option(Flag, _n("tls_allowInsecure"), translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
o = s:option(Flag, "tls_allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
o.default = "0"
o = s:option(Value, _n("tls_pinSHA256"), translate("PinSHA256"),translate("Certificate fingerprint"))
o = s:option(Value, "tls_pinSHA256", translate("PinSHA256"),translate("Certificate fingerprint"))
o = s:option(Value, _n("up_mbps"), translate("Max upload Mbps"))
o.rewrite_option = o.option
o = s:option(Value, "up_mbps", translate("Max upload Mbps"))
o = s:option(Value, _n("down_mbps"), translate("Max download Mbps"))
o.rewrite_option = o.option
o = s:option(Value, "down_mbps", translate("Max download Mbps"))
o = s:option(Value, _n("recv_window"), translate("QUIC stream receive window"))
o.rewrite_option = o.option
o = s:option(Value, "recv_window", translate("QUIC stream receive window"))
o = s:option(Value, _n("recv_window_conn"), translate("QUIC connection receive window"))
o.rewrite_option = o.option
o = s:option(Value, "recv_window_conn", translate("QUIC connection receive window"))
o = s:option(Value, _n("idle_timeout"), translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)")
o = s:option(Value, "idle_timeout", translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)")
o.datatype = "range(4,120)"
o.rewrite_option = o.option
o = s:option(Value, _n("keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)")
o = s:option(Value, "keep_alive_period", translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)")
o.datatype = "range(2,60)"
o.rewrite_option = o.option
o = s:option(Flag, _n("disable_mtu_discovery"), translate("Disable MTU detection"))
o = s:option(Flag, "disable_mtu_discovery", translate("Disable MTU detection"))
o.default = "0"
o.rewrite_option = o.option
o = s:option(Flag, _n("lazy_start"), translate("Lazy Start"))
o = s:option(Flag, "lazy_start", translate("Lazy Start"))
o.default = "0"
o.rewrite_option = o.option
api.luci_types(arg[1], m, s, type_name, option_prefix)
api.luci_types(s1, s)
@@ -1,37 +1,33 @@
local m, s = ...
if not api.is_finded("naive") then
return
end
type_name = "Naiveproxy"
-- [[ Naive ]]
local m, s1 = ...
local type_name = "Naiveproxy"
s.fields["type"]:value(type_name, "NaiveProxy")
s1.fields["type"]:value(type_name, "NaiveProxy")
if s.val["type"] ~= type_name then
if s1.val["type"] ~= type_name then
return
end
local option_prefix = "naive_"
local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "naive_"
local function _n(name)
return option_prefix .. name
end
o = s:option(ListValue, _n("protocol"), translate("Protocol"))
o = s:option(ListValue, "protocol", translate("Protocol"))
o:value("https", translate("HTTPS"))
o:value("quic", translate("QUIC"))
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)"))
o = s:option(Value, "address", translate("Address (Support Domain Name)"))
o = s:option(Value, _n("port"), translate("Port"))
o = s:option(Value, "port", translate("Port"))
o.datatype = "port"
o = s:option(Value, _n("username"), translate("Username"))
o = s:option(Value, "username", translate("Username"))
o = s:option(Value, _n("password"), translate("Password"))
o = s:option(Value, "password", translate("Password"))
o.password = true
api.luci_types(arg[1], m, s, type_name, option_prefix)
api.luci_types(s1, s)
@@ -1,33 +1,27 @@
local m, s = ...
if not api.finded_com("xray") then
return
end
local jsonc = api.jsonc
type_name = "Xray"
-- [[ Xray ]]
local m, s1 = ...
local type_name = "Xray"
s.fields["type"]:value(type_name, "Xray")
if not s.fields["type"].default then
s.fields["type"].default = type_name
s1.fields["type"]:value(type_name, "Xray")
if not s1.fields["type"].default then
s1.fields["type"].default = type_name
end
if s.val["type"] ~= type_name then
if s1.val["type"] ~= type_name then
return
end
local option_prefix = "xray_"
local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "xray_"
local function _n(name)
return option_prefix .. name
end
local formvalue_proto = luci.http.formvalue(formvalue_key .. "protocol")
local formvalue_proto = luci.http.formvalue(formvalue_key .. _n("protocol"))
if formvalue_proto then s.val["protocol"] = formvalue_proto end
if formvalue_proto then s1.val["protocol"] = formvalue_proto end
local arg_select_proto = luci.http.formvalue("select_proto") or ""
@@ -43,7 +37,7 @@ local header_type_list = {
local xray_version = api.get_app_version("xray")
o = s:option(ListValue, _n("protocol"), translate("Protocol"))
o = s:option(ListValue, "protocol", translate("Protocol"))
o:value("vmess", translate("Vmess"))
o:value("vless", translate("VLESS"))
o:value("http", translate("HTTP"))
@@ -63,13 +57,13 @@ function o.custom_cfgvalue(self, section)
if arg_select_proto ~= "" then
return arg_select_proto
else
return m:get(section, self.option:sub(1 + #option_prefix))
return m:get(section, self.config_option)
end
end
local load_balancing_options = s.val["protocol"] == "_balancing" or arg_select_proto == "_balancing"
local load_shunt_options = s.val["protocol"] == "_shunt" or arg_select_proto == "_shunt"
local load_iface_options = s.val["protocol"] == "_iface" or arg_select_proto == "_iface"
local load_balancing_options = s1.val["protocol"] == "_balancing" or arg_select_proto == "_balancing"
local load_shunt_options = s1.val["protocol"] == "_shunt" or arg_select_proto == "_shunt"
local load_iface_options = s1.val["protocol"] == "_iface" or arg_select_proto == "_iface"
local load_normal_options = true
if load_balancing_options or load_shunt_options or load_iface_options then
load_normal_options = nil
@@ -96,14 +90,14 @@ for k, e in ipairs(node_list.balancing_list or {}) do
end
if load_balancing_options then -- [[ Load balancing Start ]]
o = s:option(ListValue, _n("node_add_mode"), translate("Node Addition Method"))
o:depends({ [_n("protocol")] = "_balancing" })
o = s:option(ListValue, "node_add_mode", translate("Node Addition Method"))
o:depends({ protocol = "_balancing" })
o.default = "manual"
o:value("manual", translate("Manual"))
o:value("batch", translate("Batch"))
o = s:option(MultiValue, _n("balancing_node"), translate("Load balancing node list"), translate("Load balancing node list, <a target='_blank' href='https://xtls.github.io/config/routing.html#balancerobject'>document</a>"))
o:depends({ [_n("node_add_mode")] = "manual" })
o = s:option(MultiValue, "balancing_node", translate("Load balancing node list"), translate("Load balancing node list, <a target='_blank' href='https://xtls.github.io/config/routing.html#balancerobject'>document</a>"))
o:depends({ node_add_mode = "manual" })
o.widget = "checkbox"
o.template = m:template_path("/cbi/nodes_multivalue")
o.group = {}
@@ -140,16 +134,16 @@ if load_balancing_options then -- [[ Load balancing Start ]]
end
end
o = s:option(MultiValue, _n("node_group"), translate("Select Group"))
o:depends({ [_n("node_add_mode")] = "batch" })
o = s:option(MultiValue, "node_group", translate("Select Group"))
o:depends({ node_add_mode = "batch" })
o.widget = "checkbox"
o:value("default", translate("default"))
for k, v in pairs(groups) do
o:value(api.UrlEncode(k), k)
end
o = s:option(Value, _n("node_match_rule"), translate("Node Matching Rules"))
o:depends({ [_n("node_add_mode")] = "batch" })
o = s:option(Value, "node_match_rule", translate("Node Matching Rules"))
o:depends({ node_add_mode = "batch" })
local descrStr = "Example: <code>^A && B && !C && D$</code><br>"
descrStr = descrStr .. "This means the node remark must start with A (^), include B, exclude C (!), and end with D ($).<br>"
descrStr = descrStr .. "Conditions are joined by <code>&&</code> (AND), and their order does not affect the result.<br>"
@@ -157,8 +151,8 @@ if load_balancing_options then -- [[ Load balancing Start ]]
descrStr = descrStr .. "Example: <code>A && B || C && D</code> means (A AND B) OR (C AND D)."
o.description = translate(descrStr)
o = s:option(ListValue, _n("balancingStrategy"), translate("Balancing Strategy"))
o:depends({ [_n("protocol")] = "_balancing" })
o = s:option(ListValue, "balancingStrategy", translate("Balancing Strategy"))
o:depends({ protocol = "_balancing" })
o:value("random")
o:value("roundRobin")
o:value("leastPing")
@@ -166,11 +160,11 @@ if load_balancing_options then -- [[ Load balancing Start ]]
o.default = "random"
-- Fallback Node
o = s:option(ListValue, _n("fallback_node"), translate("Fallback Node"))
o = s:option(ListValue, "fallback_node", translate("Fallback Node"))
o.group = {"",""}
o:value("", translate("Close(Not use)"))
o:value("_direct", translate("Direct Connection"))
o:depends({ [_n("protocol")] = "_balancing" })
o:depends({ protocol = "_balancing" })
o.template = m:template_path("/cbi/nodes_listvalue")
-- Maximum number of fallback nesting layers
local MAX_FALLBACK_DEPTH = 3
@@ -234,11 +228,11 @@ if load_balancing_options then -- [[ Load balancing Start ]]
end
end
o = s:option(Flag, _n("useCustomProbeUrl"), translate("Use Custom Probe URL"), translate("By default the built-in probe URL will be used, enable this option to use a custom probe URL."))
o:depends({ [_n("protocol")] = "_balancing" })
o = s:option(Flag, "useCustomProbeUrl", translate("Use Custom Probe URL"), translate("By default the built-in probe URL will be used, enable this option to use a custom probe URL."))
o:depends({ protocol = "_balancing" })
o = s:option(Value, _n("probeUrl"), translate("Probe URL"))
o:depends({ [_n("useCustomProbeUrl")] = true })
o = s:option(Value, "probeUrl", translate("Probe URL"))
o:depends({ useCustomProbeUrl = true })
o:value("https://cp.cloudflare.com/", "Cloudflare")
o:value("https://www.gstatic.com/generate_204", "Gstatic")
o:value("https://www.google.com/generate_204", "Google")
@@ -249,23 +243,23 @@ if load_balancing_options then -- [[ Load balancing Start ]]
o.default = o.keylist[3]
o.description = translate("The URL used to detect the connection status.")
o = s:option(Value, _n("probeInterval"), translate("Probe Interval"))
o:depends({ [_n("protocol")] = "_balancing" })
o = s:option(Value, "probeInterval", translate("Probe Interval"))
o:depends({ protocol = "_balancing" })
o.default = "1m"
o.placeholder = "1m"
o.description = translate("The interval between initiating probes.") .. "<br>" ..
translate("The time format is numbers + units, such as '10s', '2h45m', and the supported time units are <code>s</code>, <code>m</code>, <code>h</code>, which correspond to seconds, minutes, and hours, respectively.") .. "<br>" ..
translate("When the unit is not filled in, it defaults to seconds.")
o = s:option(Value, _n("expected"), translate("Preferred Node Count"))
o:depends({ [_n("balancingStrategy")] = "leastLoad" })
o = s:option(Value, "expected", translate("Preferred Node Count"))
o:depends({ balancingStrategy = "leastLoad" })
o.datatype = "uinteger"
o.default = "2"
o.placeholder = "2"
o.description = translate("The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them.")
o = s:option(Value, _n("tolerance"), translate("Failure Tolerance (%)"))
o:depends({ [_n("balancingStrategy")] = "leastLoad" })
o = s:option(Value, "tolerance", translate("Failure Tolerance (%)"))
o:depends({ balancingStrategy = "leastLoad" })
o.datatype = "uinteger"
o.default = "10"
o.placeholder = "10"
@@ -273,8 +267,8 @@ if load_balancing_options then -- [[ Load balancing Start ]]
end -- [[ Load balancing End ]]
if load_iface_options then -- [[ Custom Interface Start ]]
o = s:option(Value, _n("iface"), translate("Interface"))
o:depends({ [_n("protocol")] = "_iface" })
o = s:option(Value, "iface", translate("Interface"))
o:depends({ protocol = "_iface" })
for _, d in ipairs(netdev_list) do
o:value(d.name, d.label)
end
@@ -284,73 +278,72 @@ end -- [[ Custom Interface End ]]
-- [[ Normal single node Start ]]
if load_normal_options then
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)"))
o = s:option(Value, "address", translate("Address (Support Domain Name)"))
o = s:option(Value, _n("port"), translate("Port"))
o = s:option(Value, "port", translate("Port"))
o.datatype = "port"
o = s:option(Value, _n("uuid"), translate("ID"))
o = s:option(Value, "uuid", translate("ID"))
o.password = true
o:depends({ [_n("protocol")] = "vmess" })
o:depends({ [_n("protocol")] = "vless" })
o:depends({ protocol = "vmess" })
o:depends({ protocol = "vless" })
o = s:option(Value, _n("username"), translate("Username"))
o:depends({ [_n("protocol")] = "http" })
o:depends({ [_n("protocol")] = "socks" })
o = s:option(Value, "username", translate("Username"))
o:depends({ protocol = "http" })
o:depends({ protocol = "socks" })
o = s:option(Value, _n("password"), translate("Password"))
o = s:option(Value, "password", translate("Password"))
o.password = true
o:depends({ [_n("protocol")] = "http" })
o:depends({ [_n("protocol")] = "socks" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "trojan" })
o:depends({ protocol = "http" })
o:depends({ protocol = "socks" })
o:depends({ protocol = "shadowsocks" })
o:depends({ protocol = "trojan" })
o = s:option(ListValue, _n("security"), translate("Encrypt Method"))
o = s:option(ListValue, "security", translate("Encrypt Method"))
for a, t in ipairs(security_list) do o:value(t) end
o:depends({ [_n("protocol")] = "vmess" })
o:depends({ protocol = "vmess" })
o = s:option(Value, _n("encryption"), translate("Encrypt Method") .. " (encryption)")
o = s:option(Value, "encryption", translate("Encrypt Method") .. " (encryption)")
o.default = "none"
o.placeholder = "none"
o:depends({ [_n("protocol")] = "vless" })
o:depends({ protocol = "vless" })
o.validate = function(self, value)
value = api.trim(value)
return (value == "" and "none" or value)
end
o = s:option(ListValue, _n("ss_method"), translate("Encrypt Method"))
o.rewrite_option = "method"
o = s:option(ListValue, "ss_method", translate("Encrypt Method"))
for a, t in ipairs(ss_method_list) do o:value(t) end
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ protocol = "shadowsocks" })
o = s:option(ListValue, _n("flow"), translate("flow"))
o = s:option(ListValue, "flow", translate("flow"))
o.default = ""
o:value("", translate("Disable"))
o:value("xtls-rprx-vision")
o:value("xtls-rprx-vision-udp443")
o:depends({ [_n("protocol")] = "vless" })
o:depends({ protocol = "vless" })
---- [[hysteria2]]
o = s:option(Value, _n("hysteria2_hop"), translate("Port hopping range"))
o = s:option(Value, "hysteria2_hop", translate("Port hopping range"))
o.description = translate("Format as 1000:2000 or 1000-2000 Multiple groups are separated by commas (,).")
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o:depends({ protocol = "hysteria2", hysteria2_realms = false })
o = s:option(Value, _n("hysteria2_hop_interval"), translate("Hop Interval(second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5."))
o = s:option(Value, "hysteria2_hop_interval", translate("Hop Interval(second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5."))
o.datatype = "or(uinteger,portrange)"
o.placeholder = "30"
o.default = "30"
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o:depends({ protocol = "hysteria2", hysteria2_realms = false })
o = s:option(Flag, _n("hysteria2_realms"), translate("Realms"))
o = s:option(Flag, "hysteria2_realms", translate("Realms"))
o.default = "0"
if api.compare_versions(xray_version, ">", "26.5.9") then
o:depends({ [_n("protocol")] = "hysteria2"})
o:depends({ protocol = "hysteria2"})
else
o:depends({ [_n("protocol")] = "__hide"})
o:depends({ protocol = "__hide"})
end
o = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ [_n("hysteria2_realms")] = "1" })
o = s:option(Value, "hysteria2_realm_url", translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ hysteria2_realms = "1" })
o.validate = function(self, value)
value = api.trim(value)
local realm = api.parse_realm_uri(value)
@@ -358,73 +351,73 @@ o.validate = function(self, value)
return nil, translate("Invalid Realm URL.")
end
o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN"))
o = s:option(DynamicList, "hysteria2_realm_stun", translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o:depends({ [_n("hysteria2_realms")] = "1" })
o:depends({ hysteria2_realms = "1" })
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o = s:option(Value, "hysteria2_auth_password", translate("Auth Password"))
o.password = true
o:depends({ [_n("protocol")] = "hysteria2"})
o:depends({ protocol = "hysteria2"})
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o = s:option(ListValue, "hysteria2_obfs_type", translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:value("gecko")
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ protocol = "hysteria2" })
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
o = s:option(Value, "hysteria2_obfs_password", translate("Obfs Password"))
o:depends({ hysteria2_obfs_type = "salamander" })
o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Value, _n("hysteria2_obfs_MinPacketSize"), translate("Gecko Packet Size (min)"))
o = s:option(Value, "hysteria2_obfs_MinPacketSize", translate("Gecko Packet Size (min)"))
o.datatype = "uinteger"
o.placeholder = "512"
o.default = "512"
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Value, _n("hysteria2_obfs_MaxPacketSize"), translate("Gecko Packet Size (max)"))
o = s:option(Value, "hysteria2_obfs_MaxPacketSize", translate("Gecko Packet Size (max)"))
o.datatype = "uinteger"
o.placeholder = "1200"
o.default = "1200"
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, "hysteria2_up_mbps", translate("Max upload Mbps"))
o:depends({ protocol = "hysteria2" })
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, "hysteria2_down_mbps", translate("Max download Mbps"))
o:depends({ protocol = "hysteria2" })
o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)")
o = s:option(Value, "hysteria2_idle_timeout", translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)")
o.datatype = "range(4,120)"
o:depends({ [_n("protocol")] = "hysteria2"})
o:depends({ protocol = "hysteria2"})
o = s:option(Value, _n("hysteria2_keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)")
o = s:option(Value, "hysteria2_keep_alive_period", translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)")
o.datatype = "range(2,60)"
o:depends({ [_n("protocol")] = "hysteria2"})
o:depends({ protocol = "hysteria2"})
o = s:option(Flag, _n("hysteria2_disable_mtu_discovery"), translate("Disable MTU detection"))
o = s:option(Flag, "hysteria2_disable_mtu_discovery", translate("Disable MTU detection"))
o.default = "0"
o:depends({ [_n("protocol")] = "hysteria2"})
o:depends({ protocol = "hysteria2"})
---- [[hysteria2 end]]
o = s:option(Flag, _n("tls"), translate("TLS"))
o = s:option(Flag, "tls", translate("TLS"))
o.default = 0
o:depends({ [_n("protocol")] = "vmess" })
o:depends({ [_n("protocol")] = "vless" })
o:depends({ [_n("protocol")] = "http" })
o:depends({ [_n("protocol")] = "socks" })
o:depends({ [_n("protocol")] = "trojan" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ protocol = "vmess" })
o:depends({ protocol = "vless" })
o:depends({ protocol = "http" })
o:depends({ protocol = "socks" })
o:depends({ protocol = "trojan" })
o:depends({ protocol = "shadowsocks" })
o = s:option(Flag, _n("reality"), translate("REALITY"))
o = s:option(Flag, "reality", translate("REALITY"))
o.default = 0
o:depends({ [_n("tls")] = true, [_n("transport")] = "raw" })
o:depends({ [_n("tls")] = true, [_n("transport")] = "ws" })
o:depends({ [_n("tls")] = true, [_n("transport")] = "grpc" })
o:depends({ [_n("tls")] = true, [_n("transport")] = "httpupgrade" })
o:depends({ [_n("tls")] = true, [_n("transport")] = "xhttp" })
o:depends({ tls = true, transport = "raw" })
o:depends({ tls = true, transport = "ws" })
o:depends({ tls = true, transport = "grpc" })
o:depends({ tls = true, transport = "httpupgrade" })
o:depends({ tls = true, transport = "xhttp" })
o = s:option(ListValue, _n("alpn"), translate("alpn"))
o = s:option(ListValue, "alpn", translate("alpn"))
o.default = "default"
o:value("default", translate("Default"))
o:value("h3")
@@ -433,72 +426,72 @@ o:value("h3,h2")
o:value("http/1.1")
o:value("h2,http/1.1")
o:value("h3,h2,http/1.1")
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ tls = true, reality = false })
-- o = s:option(Value, _n("minversion"), translate("minversion"))
-- o = s:option(Value, "minversion", translate("minversion"))
-- o.default = "1.3"
-- o:value("1.3")
-- o:depends({ [_n("tls")] = true })
-- o:depends({ tls = true })
o = s:option(Value, _n("tls_serverName"), "SNI " .. translate("Domain"))
o:depends({ [_n("tls")] = true })
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, "tls_serverName", "SNI " .. translate("Domain"))
o:depends({ tls = true })
o:depends({ protocol = "hysteria2" })
o = s:option(Value, _n("tls_pinSHA256"), translate("TLS Chain Fingerprint (SHA256)"))
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, "tls_pinSHA256", translate("TLS Chain Fingerprint (SHA256)"))
o:depends({ tls = true, reality = false })
o:depends({ protocol = "hysteria2" })
o.description = translate("Once set, connects only when the server’s chain fingerprint matches.") ..
string.format("<a href='javascript:void(0)' onclick='javascript:fetchCertSha256(this)'>%s</a>", "→ " .. translate("Fetch Manually"))
o = s:option(Value, _n("tls_CertByName"), translate("TLS Certificate Name (CertName)"), translate("TLS is used to verify the leaf certificate name."))
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, "tls_CertByName", translate("TLS Certificate Name (CertName)"), translate("TLS is used to verify the leaf certificate name."))
o:depends({ tls = true, reality = false })
o:depends({ protocol = "hysteria2" })
o = s:option(Flag, _n("tls_certificate"), translate("TLS Certificate (PEM)"))
o = s:option(Flag, "tls_certificate", translate("TLS Certificate (PEM)"))
o.default = "0"
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ tls = true, reality = false })
o:depends({ protocol = "hysteria2" })
o = s:option(TextValue, _n("tls_certificate_pem"), " ", translate("Full certificate (chain), PEM format."))
o = s:option(TextValue, "tls_certificate_pem", " ", translate("Full certificate (chain), PEM format."))
o.default = ""
o.rows = 5
o.wrap = "off"
o:depends({ [_n("tls_certificate")] = true })
o:depends({ tls_certificate = true })
o.validate = function(self, value)
value = api.trim(value):gsub("\r\n", "\n"):gsub("[ \t]*\n[ \t]*", "\n"):gsub("\n+", "\n")
return value
end
o = s:option(Flag, _n("ech"), translate("ECH"))
o = s:option(Flag, "ech", translate("ECH"))
o.default = "0"
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o:depends({ tls = true, reality = false })
o:depends({ protocol = "hysteria2", hysteria2_realms = false })
o = s:option(TextValue, _n("ech_config"), translate("ECH Config"))
o = s:option(TextValue, "ech_config", translate("ECH Config"))
o.default = ""
o.rows = 5
o.wrap = "soft"
o:depends({ [_n("ech")] = true })
o:depends({ ech = true })
o.validate = function(self, value)
return api.trim(value:gsub("[\r\n]", ""))
end
-- [[ REALITY ]] --
o = s:option(Value, _n("reality_publicKey"), translate("Public Key"))
o:depends({ [_n("tls")] = true, [_n("reality")] = true })
o = s:option(Value, "reality_publicKey", translate("Public Key"))
o:depends({ tls = true, reality = true })
o = s:option(Value, _n("reality_shortId"), translate("Short Id"))
o:depends({ [_n("tls")] = true, [_n("reality")] = true })
o = s:option(Value, "reality_shortId", translate("Short Id"))
o:depends({ tls = true, reality = true })
o = s:option(Value, _n("reality_spiderX"), translate("Spider X"))
o = s:option(Value, "reality_spiderX", translate("Spider X"))
o.placeholder = "/"
o:depends({ [_n("tls")] = true, [_n("reality")] = true })
o:depends({ tls = true, reality = true })
o = s:option(Flag, _n("utls"), translate("uTLS"))
o = s:option(Flag, "utls", translate("uTLS"))
o.default = "0"
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ tls = true, reality = false })
o = s:option(ListValue, _n("fingerprint"), translate("Finger Print"))
o = s:option(ListValue, "fingerprint", translate("Finger Print"))
o:value("chrome")
o:value("firefox")
o:value("edge")
@@ -511,14 +504,14 @@ o:value("random")
o:value("randomized")
o:value("unsafe")
o.default = "chrome"
o:depends({ [_n("tls")] = true, [_n("utls")] = true })
o:depends({ [_n("tls")] = true, [_n("reality")] = true })
o:depends({ tls = true, utls = true })
o:depends({ tls = true, reality = true })
o = s:option(Flag, _n("use_mldsa65Verify"), translate("ML-DSA-65"))
o = s:option(Flag, "use_mldsa65Verify", translate("ML-DSA-65"))
o.default = "0"
o:depends({ [_n("tls")] = true, [_n("reality")] = true })
o:depends({ tls = true, reality = true })
o = s:option(DynamicList, _n("cipherSuites"), translate("Cipher Suites"), '<a href="https://go.dev/src/crypto/tls/cipher_suites.go#L44" target="_blank">***</a>' .. " " .. translate("Configures the list of supported cipher suites."))
o = s:option(DynamicList, "cipherSuites", translate("Cipher Suites"), '<a href="https://go.dev/src/crypto/tls/cipher_suites.go#L44" target="_blank">***</a>' .. " " .. translate("Configures the list of supported cipher suites."))
o:value("TLS_AES_128_GCM_SHA256")
o:value("TLS_AES_256_GCM_SHA384")
o:value("TLS_CHACHA20_POLY1305_SHA256")
@@ -532,154 +525,154 @@ o:value("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")
o:value("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384")
o:value("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256")
o:value("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256")
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ tls = true, reality = false })
o = s:option(TextValue, _n("reality_mldsa65Verify"), "ML-DSA-65 " .. translate("Public key"))
o = s:option(TextValue, "reality_mldsa65Verify", "ML-DSA-65 " .. translate("Public key"))
o.default = ""
o.rows = 5
o.wrap = "soft"
o:depends({ [_n("use_mldsa65Verify")] = true })
o:depends({ use_mldsa65Verify = true })
o.validate = function(self, value)
return api.trim(value:gsub("[\r\n]", ""))
end
o = s:option(ListValue, _n("transport"), translate("Transport"))
o = s:option(ListValue, "transport", translate("Transport"))
o:value("raw", "RAW (TCP)")
o:value("mkcp", "mKCP")
o:value("ws", "WebSocket")
o:value("grpc", "gRPC")
o:value("httpupgrade", "HttpUpgrade")
o:value("xhttp", "XHTTP")
o:depends({ [_n("protocol")] = "vmess" })
o:depends({ [_n("protocol")] = "vless" })
o:depends({ [_n("protocol")] = "socks" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "trojan" })
o:depends({ protocol = "vmess" })
o:depends({ protocol = "vless" })
o:depends({ protocol = "socks" })
o:depends({ protocol = "shadowsocks" })
o:depends({ protocol = "trojan" })
o = s:option(Value, _n("wireguard_public_key"), translate("Public Key"))
o:depends({ [_n("protocol")] = "wireguard" })
o = s:option(Value, "wireguard_public_key", translate("Public Key"))
o:depends({ protocol = "wireguard" })
o = s:option(Value, _n("wireguard_secret_key"), translate("Private Key"))
o:depends({ [_n("protocol")] = "wireguard" })
o = s:option(Value, "wireguard_secret_key", translate("Private Key"))
o:depends({ protocol = "wireguard" })
o = s:option(Value, _n("wireguard_preSharedKey"), translate("Pre shared key"))
o:depends({ [_n("protocol")] = "wireguard" })
o = s:option(Value, "wireguard_preSharedKey", translate("Pre shared key"))
o:depends({ protocol = "wireguard" })
o = s:option(DynamicList, _n("wireguard_local_address"), translate("Local Address"))
o:depends({ [_n("protocol")] = "wireguard" })
o = s:option(DynamicList, "wireguard_local_address", translate("Local Address"))
o:depends({ protocol = "wireguard" })
o = s:option(Value, _n("wireguard_mtu"), translate("MTU"))
o = s:option(Value, "wireguard_mtu", translate("MTU"))
o.default = "1420"
o:depends({ [_n("protocol")] = "wireguard" })
o:depends({ protocol = "wireguard" })
if api.compare_versions(xray_version, ">=", "1.8.0") then
o = s:option(Value, _n("wireguard_reserved"), translate("Reserved"), translate("Decimal numbers separated by \",\" or Base64-encoded strings."))
o:depends({ [_n("protocol")] = "wireguard" })
o = s:option(Value, "wireguard_reserved", translate("Reserved"), translate("Decimal numbers separated by \",\" or Base64-encoded strings."))
o:depends({ protocol = "wireguard" })
end
o = s:option(Value, _n("wireguard_keepAlive"), translate("Keep Alive"))
o = s:option(Value, "wireguard_keepAlive", translate("Keep Alive"))
o.default = "0"
o:depends({ [_n("protocol")] = "wireguard" })
o:depends({ protocol = "wireguard" })
-- [[ RAW ]]--
o = s:option(ListValue, _n("tcp_guise"), translate("Camouflage Type"))
o = s:option(ListValue, "tcp_guise", translate("Camouflage Type"))
o:value("none", "none")
o:value("http", "http")
o:depends({ [_n("transport")] = "raw" })
o:depends({ transport = "raw" })
o = s:option(DynamicList, _n("tcp_guise_http_host"), translate("HTTP Host"))
o:depends({ [_n("tcp_guise")] = "http" })
o = s:option(DynamicList, "tcp_guise_http_host", translate("HTTP Host"))
o:depends({ tcp_guise = "http" })
o = s:option(DynamicList, _n("tcp_guise_http_path"), translate("HTTP Path"))
o = s:option(DynamicList, "tcp_guise_http_path", translate("HTTP Path"))
o.placeholder = "/"
o:depends({ [_n("tcp_guise")] = "http" })
o:depends({ tcp_guise = "http" })
-- [[ mKCP ]]--
o = s:option(ListValue, _n("mkcp_guise"), translate("Camouflage Type"), translate('<br />none: default, no masquerade, data sent is packets with no characteristics.<br />srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).<br />utp: packets disguised as uTP will be recognized as bittorrent downloaded data.<br />wechat-video: packets disguised as WeChat video calls.<br />dtls: disguised as DTLS 1.2 packet.<br />wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)<br />dns: Disguising traffic as DNS requests.'))
o = s:option(ListValue, "mkcp_guise", translate("Camouflage Type"), translate('<br />none: default, no masquerade, data sent is packets with no characteristics.<br />srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).<br />utp: packets disguised as uTP will be recognized as bittorrent downloaded data.<br />wechat-video: packets disguised as WeChat video calls.<br />dtls: disguised as DTLS 1.2 packet.<br />wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)<br />dns: Disguising traffic as DNS requests.'))
for a, t in ipairs(header_type_list) do o:value(t) end
o:depends({ [_n("transport")] = "mkcp" })
o:depends({ transport = "mkcp" })
o = s:option(Value, _n("mkcp_domain"), translate("Camouflage Domain"), translate("Use it together with the DNS disguised type. You can fill in any domain."))
o:depends({ [_n("mkcp_guise")] = "dns" })
o = s:option(Value, "mkcp_domain", translate("Camouflage Domain"), translate("Use it together with the DNS disguised type. You can fill in any domain."))
o:depends({ mkcp_guise = "dns" })
o = s:option(Value, _n("mkcp_mtu"), translate("KCP MTU"))
o = s:option(Value, "mkcp_mtu", translate("KCP MTU"))
o.datatype = "uinteger"
o.default = 1350
o:depends({ [_n("transport")] = "mkcp" })
o:depends({ transport = "mkcp" })
o = s:option(Value, _n("mkcp_seed"), translate("KCP Seed"))
o:depends({ [_n("transport")] = "mkcp" })
o = s:option(Value, "mkcp_seed", translate("KCP Seed"))
o:depends({ transport = "mkcp" })
-- [[ WebSocket ]]--
o = s:option(Value, _n("ws_host"), translate("WebSocket Host"))
o:depends({ [_n("transport")] = "ws" })
o = s:option(Value, "ws_host", translate("WebSocket Host"))
o:depends({ transport = "ws" })
o = s:option(Value, _n("ws_path"), translate("WebSocket Path"))
o = s:option(Value, "ws_path", translate("WebSocket Path"))
o.placeholder = "/"
o:depends({ [_n("transport")] = "ws" })
o:depends({ transport = "ws" })
o = s:option(Value, _n("ws_heartbeatPeriod"), translate("HeartbeatPeriod(second)"))
o = s:option(Value, "ws_heartbeatPeriod", translate("HeartbeatPeriod(second)"))
o.datatype = "integer"
o:depends({ [_n("transport")] = "ws" })
o:depends({ transport = "ws" })
-- [[ gRPC ]]--
o = s:option(Value, _n("grpc_serviceName"), "ServiceName")
o:depends({ [_n("transport")] = "grpc" })
o = s:option(Value, "grpc_serviceName", "ServiceName")
o:depends({ transport = "grpc" })
o = s:option(ListValue, _n("grpc_mode"), "gRPC " .. translate("Transfer mode"))
o = s:option(ListValue, "grpc_mode", "gRPC " .. translate("Transfer mode"))
o:value("gun")
o:value("multi")
o:depends({ [_n("transport")] = "grpc" })
o:depends({ transport = "grpc" })
o = s:option(Flag, _n("grpc_health_check"), translate("Health check"))
o:depends({ [_n("transport")] = "grpc" })
o = s:option(Flag, "grpc_health_check", translate("Health check"))
o:depends({ transport = "grpc" })
o = s:option(Value, _n("grpc_idle_timeout"), translate("Idle timeout"))
o = s:option(Value, "grpc_idle_timeout", translate("Idle timeout"))
o.default = "10"
o:depends({ [_n("grpc_health_check")] = true })
o:depends({ grpc_health_check = true })
o = s:option(Value, _n("grpc_health_check_timeout"), translate("Health check timeout"))
o = s:option(Value, "grpc_health_check_timeout", translate("Health check timeout"))
o.default = "20"
o:depends({ [_n("grpc_health_check")] = true })
o:depends({ grpc_health_check = true })
o = s:option(Flag, _n("grpc_permit_without_stream"), translate("Permit without stream"))
o = s:option(Flag, "grpc_permit_without_stream", translate("Permit without stream"))
o.default = "0"
o:depends({ [_n("grpc_health_check")] = true })
o:depends({ grpc_health_check = true })
o = s:option(Value, _n("grpc_initial_windows_size"), translate("Initial Windows Size"))
o = s:option(Value, "grpc_initial_windows_size", translate("Initial Windows Size"))
o.default = "0"
o:depends({ [_n("transport")] = "grpc" })
o:depends({ transport = "grpc" })
-- [[ HttpUpgrade ]]--
o = s:option(Value, _n("httpupgrade_host"), translate("HttpUpgrade Host"))
o:depends({ [_n("transport")] = "httpupgrade" })
o = s:option(Value, "httpupgrade_host", translate("HttpUpgrade Host"))
o:depends({ transport = "httpupgrade" })
o = s:option(Value, _n("httpupgrade_path"), translate("HttpUpgrade Path"))
o = s:option(Value, "httpupgrade_path", translate("HttpUpgrade Path"))
o.placeholder = "/"
o:depends({ [_n("transport")] = "httpupgrade" })
o:depends({ transport = "httpupgrade" })
-- [[ XHTTP ]]--
o = s:option(ListValue, _n("xhttp_mode"), "XHTTP " .. translate("Mode"))
o:depends({ [_n("transport")] = "xhttp" })
o = s:option(ListValue, "xhttp_mode", "XHTTP " .. translate("Mode"))
o:depends({ transport = "xhttp" })
o.default = "auto"
o:value("auto")
o:value("packet-up")
o:value("stream-up")
o:value("stream-one")
o = s:option(Value, _n("xhttp_host"), translate("XHTTP Host"))
o:depends({ [_n("transport")] = "xhttp" })
o = s:option(Value, "xhttp_host", translate("XHTTP Host"))
o:depends({ transport = "xhttp" })
o = s:option(Value, _n("xhttp_path"), translate("XHTTP Path"))
o = s:option(Value, "xhttp_path", translate("XHTTP Path"))
o.placeholder = "/"
o:depends({ [_n("transport")] = "xhttp" })
o:depends({ transport = "xhttp" })
o = s:option(Flag, _n("use_xhttp_extra"), translate("XHTTP Extra"))
o = s:option(Flag, "use_xhttp_extra", translate("XHTTP Extra"))
o.default = "0"
o:depends({ [_n("transport")] = "xhttp" })
o:depends({ transport = "xhttp" })
o = s:option(TextValue, _n("xhttp_extra"), " ", translate("An XHttpObject in JSON format, used for sharing."))
o:depends({ [_n("use_xhttp_extra")] = true })
o = s:option(TextValue, "xhttp_extra", " ", translate("An XHttpObject in JSON format, used for sharing."))
o:depends({ use_xhttp_extra = true })
o.rows = 10
o.wrap = "off"
o.datatype = "json"
@@ -698,7 +691,7 @@ o.custom_cfgvalue = function(self, section, value)
end
o.custom_write = function(self, section, value)
m:set(section, "xhttp_extra", api.base64Encode(value) or "")
local success, data = pcall(jsonc.parse, value)
local success, data = pcall(api.jsonc.parse, value)
if success and data then
local address = (data.extra and data.extra.downloadSettings and data.extra.downloadSettings.address)
or (data.downloadSettings and data.downloadSettings.address)
@@ -718,7 +711,7 @@ o.custom_remove = function(self, section, value)
end
-- [[ User-Agent ]]--
o = s:option(Value, _n("user_agent"), translate("User-Agent"))
o = s:option(Value, "user_agent", translate("User-Agent"))
o.default = ""
o:value("", translate("default"))
o:value("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36", "chrome")
@@ -727,44 +720,44 @@ o:value("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (K
o:value("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70", "edge")
o:value("Go-http-client/1.1", "golang")
o:value("curl/7.68.0", "curl")
o:depends({ [_n("tcp_guise")] = "http" })
o:depends({ [_n("transport")] = "ws" })
o:depends({ [_n("transport")] = "httpupgrade" })
o:depends({ [_n("transport")] = "xhttp" })
o:depends({ [_n("transport")] = "grpc" })
o:depends({ tcp_guise = "http" })
o:depends({ transport = "ws" })
o:depends({ transport = "httpupgrade" })
o:depends({ transport = "xhttp" })
o:depends({ transport = "grpc" })
-- [[ Mux.Cool ]]--
o = s:option(Flag, _n("mux"), "Mux", translate("Enable Mux.Cool"))
o:depends({ [_n("protocol")] = "vmess" })
o:depends({ [_n("protocol")] = "vless", [_n("transport")] = "raw" })
o:depends({ [_n("protocol")] = "vless", [_n("transport")] = "ws" })
o:depends({ [_n("protocol")] = "vless", [_n("transport")] = "grpc" })
o:depends({ [_n("protocol")] = "vless", [_n("transport")] = "httpupgrade" })
o:depends({ [_n("protocol")] = "http" })
o:depends({ [_n("protocol")] = "socks" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "trojan" })
o = s:option(Flag, "mux", "Mux", translate("Enable Mux.Cool"))
o:depends({ protocol = "vmess" })
o:depends({ protocol = "vless", transport = "raw" })
o:depends({ protocol = "vless", transport = "ws" })
o:depends({ protocol = "vless", transport = "grpc" })
o:depends({ protocol = "vless", transport = "httpupgrade" })
o:depends({ protocol = "http" })
o:depends({ protocol = "socks" })
o:depends({ protocol = "shadowsocks" })
o:depends({ protocol = "trojan" })
o = s:option(Value, _n("mux_concurrency"), translate("Mux concurrency"))
o = s:option(Value, "mux_concurrency", translate("Mux concurrency"))
o.default = -1
o:depends({ [_n("mux")] = true })
o:depends({ mux = true })
o = s:option(Value, _n("xudp_concurrency"), translate("XUDP Mux concurrency"))
o = s:option(Value, "xudp_concurrency", translate("XUDP Mux concurrency"))
o.default = 8
o:depends({ [_n("mux")] = true })
o:depends({ mux = true })
--[[FinalMask]]
o = s:option(Flag, _n("use_finalmask"), "FinalMask")
o = s:option(Flag, "use_finalmask", "FinalMask")
o.default = "0"
o:depends({ [_n("protocol")] = "vmess" })
o:depends({ [_n("protocol")] = "vless" })
o:depends({ [_n("protocol")] = "trojan" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "wireguard" })
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o:depends({ protocol = "vmess" })
o:depends({ protocol = "vless" })
o:depends({ protocol = "trojan" })
o:depends({ protocol = "shadowsocks" })
o:depends({ protocol = "wireguard" })
o:depends({ protocol = "hysteria2", hysteria2_realms = false })
o = s:option(TextValue, _n("finalmask"), " ")
o:depends({ [_n("use_finalmask")] = true })
o = s:option(TextValue, "finalmask", " ")
o:depends({ use_finalmask = true })
o.rows = 10
o.wrap = "off"
o.description = translate("An FinalMaskObject in JSON format, used for sharing.") .. "<br>" ..
@@ -788,62 +781,62 @@ o.custom_write = function(self, section, value)
end
--[[Fast Open]]
o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"), translate("Need node support required"))
o = s:option(Flag, "tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required"))
o.default = 0
--[[tcpMptcp]]
o = s:option(Flag, _n("tcpMptcp"), "tcpMptcp", translate("Enable Multipath TCP, need to be enabled in both server and client configuration."))
o = s:option(Flag, "tcpMptcp", "tcpMptcp", translate("Enable Multipath TCP, need to be enabled in both server and client configuration."))
o.default = 0
o = s:option(ListValue, _n("domain_resolver"), translate("Domain DNS Resolve"), translate("If the node address is a domain name, this DNS will be used for resolution."))
o = s:option(ListValue, "domain_resolver", translate("Domain DNS Resolve"), translate("If the node address is a domain name, this DNS will be used for resolution."))
o:value("", translate("Auto"))
o:value("tcp", "TCP")
o:value("udp", "UDP")
o:value("https", "HTTPS")
o = s:option(Value, _n("domain_resolver_dns"), "DNS")
o = s:option(Value, "domain_resolver_dns", "DNS")
o.datatype = "or(ipaddr,ipaddrport)"
o:value("114.114.114.114")
o:value("223.5.5.5:53")
o.default = o.keylist[1]
o:depends({ [_n("domain_resolver")] = "tcp" })
o:depends({ [_n("domain_resolver")] = "udp" })
o:depends({ domain_resolver = "tcp" })
o:depends({ domain_resolver = "udp" })
o = s:option(Value, _n("domain_resolver_dns_https"), "DNS")
o = s:option(Value, "domain_resolver_dns_https", "DNS")
o:value("https://120.53.53.53/dns-query", "DNSPod")
o:value("https://223.5.5.5/dns-query", "AliDNS")
o.default = o.keylist[1]
o:depends({ [_n("domain_resolver")] = "https" })
o:depends({ domain_resolver = "https" })
o = s:option(ListValue, _n("domain_strategy"), translate("Domain Strategy"), translate("If is domain name, The requested domain name will be resolved to IP before connect."))
o = s:option(ListValue, "domain_strategy", translate("Domain Strategy"), translate("If is domain name, The requested domain name will be resolved to IP before connect."))
o.default = ""
o:value("", translate("Auto"))
o:value("UseIPv4", translate("IPv4 Only"))
o:value("UseIPv6", translate("IPv6 Only"))
o = s:option(Flag, _n("happy_eyeballs"), translate("Enable Happy Eyeballs"), translate("Attempts IPv4 and IPv6 simultaneously; automatically uses the faster connection."))
o = s:option(Flag, "happy_eyeballs", translate("Enable Happy Eyeballs"), translate("Attempts IPv4 and IPv6 simultaneously; automatically uses the faster connection."))
o.default = 0
local protocols = s.fields[_n("protocol")].keylist
local protocols = s.fields["protocol"].keylist
if #protocols > 0 then
for i, v in ipairs(protocols) do
if not v:find("^_") then
local depends_condition = { [_n("protocol")] = v }
local depends_condition = { protocol = v }
if v == "hysteria2" then
depends_condition[_n("hysteria2_realms")] = false
depends_condition["hysteria2_realms"] = false
end
s.fields[_n("address")]:depends(depends_condition)
s.fields[_n("port")]:depends(depends_condition)
s.fields[_n("domain_resolver")]:depends(depends_condition)
s.fields[_n("happy_eyeballs")]:depends(depends_condition)
s.fields["address"]:depends(depends_condition)
s.fields["port"]:depends(depends_condition)
s.fields["domain_resolver"]:depends(depends_condition)
s.fields["happy_eyeballs"]:depends(depends_condition)
local strategy_depends = api.clone(depends_condition)
strategy_depends[_n("happy_eyeballs")] = false
s.fields[_n("domain_strategy")]:depends(strategy_depends)
strategy_depends["happy_eyeballs"] = false
s.fields["domain_strategy"]:depends(strategy_depends)
if v ~= "hysteria2" then
s.fields[_n("tcp_fast_open")]:depends({ [_n("protocol")] = v })
s.fields[_n("tcpMptcp")]:depends({ [_n("protocol")] = v })
s.fields["tcp_fast_open"]:depends({ protocol = v })
s.fields["tcpMptcp"]:depends({ protocol = v })
end
end
end
@@ -852,7 +845,7 @@ end
-- [[ Normal single node End ]]
if not load_shunt_options then
o = s:option(ListValue, _n("chain_proxy"), translate("Chain Proxy"))
o = s:option(ListValue, "chain_proxy", translate("Chain Proxy"))
o:value("", translate("Close(Not use)"))
if not (load_iface_options or load_balancing_options) then
-- Special node cannot be use pre-proxy.
@@ -861,20 +854,20 @@ if not load_shunt_options then
end
o:value("2", translate("Landing Node"))
o1 = s:option(ListValue, _n("preproxy_node"), translate("Preproxy Node"), translate("Only support a layer of proxy."))
o1:depends({ [_n("chain_proxy")] = "1", [_n("hysteria2_realms")] = false })
o1 = s:option(ListValue, "preproxy_node", translate("Preproxy Node"), translate("Only support a layer of proxy."))
o1:depends({ chain_proxy = "1", hysteria2_realms = false })
o1.template = m:template_path("/cbi/nodes_listvalue")
o1.group = {}
o3 = s:option(Value, _n("outbound_iface"), translate("Outbound Interface"))
o3:depends({ [_n("chain_proxy")] = "3" })
o3 = s:option(Value, "outbound_iface", translate("Outbound Interface"))
o3:depends({ chain_proxy = "3" })
o3:value("", translate("All"))
for _, d in ipairs(netdev_list) do
o3:value(d.name, d.label)
end
o2 = s:option(ListValue, _n("to_node"), translate("Landing Node"), translate("Only support a layer of proxy."))
o2:depends({ [_n("chain_proxy")] = "2", [_n("hysteria2_realms")] = false })
o2 = s:option(ListValue, "to_node", translate("Landing Node"), translate("Only support a layer of proxy."))
o2:depends({ chain_proxy = "2", hysteria2_realms = false })
o2.template = m:template_path("/cbi/nodes_listvalue")
o2.group = {}
@@ -895,12 +888,12 @@ if not load_shunt_options then
end
end
api.luci_types(arg[1], m, s, type_name, option_prefix)
api.luci_types(s1, s)
if load_shunt_options then
local current_node = m:get(arg[1]) or {}
local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall2/client/include/shunt_options.lua")
setfenv(shunt_lua, getfenv(1))(m, s, {
setfenv(shunt_lua, getfenv(1))(m, s1, {
node_id = arg[1],
node = current_node,
node_list = node_list,
File diff suppressed because it is too large. Load diff
@@ -1,24 +1,20 @@
local m, s = ...
if not api.is_finded("sslocal") then
return
end
type_name = "SS-Rust"
-- [[ Shadowsocks Rust ]]
local m, s1 = ...
local type_name = "SS-Rust"
s.fields["type"]:value(type_name, "Shadowsocks Rust")
s1.fields["type"]:value(type_name, "Shadowsocks Rust")
if s.val["type"] ~= type_name then
if s1.val["type"] ~= type_name then
return
end
local option_prefix = "ssrust_"
local function _n(name)
return option_prefix .. name
end
local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "ssrust_"
local ssrust_encrypt_method_list = {
"none", "plain",
@@ -26,35 +22,35 @@ local ssrust_encrypt_method_list = {
"2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha8-poly1305", "2022-blake3-chacha20-poly1305"
}
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)"))
o = s:option(Value, "address", translate("Address (Support Domain Name)"))
o = s:option(Value, _n("port"), translate("Port"))
o = s:option(Value, "port", translate("Port"))
o.datatype = "port"
o = s:option(Value, _n("password"), translate("Password"))
o = s:option(Value, "password", translate("Password"))
o.password = true
o = s:option(Value, _n("method"), translate("Encrypt Method"))
o = s:option(Value, "method", translate("Encrypt Method"))
for a, t in ipairs(ssrust_encrypt_method_list) do o:value(t) end
o = s:option(Value, _n("timeout"), translate("Connection Timeout"))
o = s:option(Value, "timeout", translate("Connection Timeout"))
o.datatype = "uinteger"
o.default = 300
o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"), translate("Need node support required"))
o = s:option(Flag, "tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required"))
o.default = 0
o = s:option(Flag, _n("plugin_enabled"), translate("plugin"))
o = s:option(Flag, "plugin_enabled", translate("plugin"))
o.default = 0
o = s:option(Value, _n("plugin"), "SIP003 " .. translate("plugin"), translate("Supports custom SIP003 plugins, Make sure the plugin is installed."))
o = s:option(Value, "plugin", "SIP003 " .. translate("plugin"), translate("Supports custom SIP003 plugins, Make sure the plugin is installed."))
o.default = "none"
o:value("none", translate("none"))
if api.is_finded("xray-plugin") then o:value("xray-plugin") end
if api.is_finded("v2ray-plugin") then o:value("v2ray-plugin") end
if api.is_finded("obfs-local") then o:value("obfs-local") end
if api.is_finded("shadow-tls") then o:value("shadow-tls") end
o:depends({ [_n("plugin_enabled")] = true })
o:depends({ plugin_enabled = true })
o.validate = function(self, value, t)
if value and value ~= "" and value ~= "none" then
if not api.is_finded(value) then
@@ -66,7 +62,7 @@ o.validate = function(self, value, t)
return nil
end
o = s:option(Value, _n("plugin_opts"), translate("opts"))
o:depends({ [_n("plugin_enabled")] = true })
o = s:option(Value, "plugin_opts", translate("opts"))
o:depends({ plugin_enabled = true })
api.luci_types(arg[1], m, s, type_name, option_prefix)
api.luci_types(s1, s)
@@ -1,24 +1,20 @@
local m, s = ...
if not api.is_finded("ssr-local") then
return
end
type_name = "SSR"
-- [[ ShadowsocksR Libev ]]
local m, s1 = ...
local type_name = "SSR"
s.fields["type"]:value(type_name, "ShadowsocksR Libev")
s1.fields["type"]:value(type_name, "ShadowsocksR Libev")
if s.val["type"] ~= type_name then
if s1.val["type"] ~= type_name then
return
end
local option_prefix = "ssr_"
local function _n(name)
return option_prefix .. name
end
local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "ssr_"
local ssr_encrypt_method_list = {
"none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb",
@@ -39,32 +35,32 @@ local ssr_obfs_list = {
"tls1.0_session_auth", "tls1.2_ticket_auth"
}
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)"))
o = s:option(Value, "address", translate("Address (Support Domain Name)"))
o = s:option(Value, _n("port"), translate("Port"))
o = s:option(Value, "port", translate("Port"))
o.datatype = "port"
o = s:option(Value, _n("password"), translate("Password"))
o = s:option(Value, "password", translate("Password"))
o.password = true
o = s:option(ListValue, _n("method"), translate("Encrypt Method"))
o = s:option(ListValue, "method", translate("Encrypt Method"))
for a, t in ipairs(ssr_encrypt_method_list) do o:value(t) end
o = s:option(ListValue, _n("protocol"), translate("Protocol"))
o = s:option(ListValue, "protocol", translate("Protocol"))
for a, t in ipairs(ssr_protocol_list) do o:value(t) end
o = s:option(Value, _n("protocol_param"), translate("Protocol_param"))
o = s:option(Value, "protocol_param", translate("Protocol_param"))
o = s:option(ListValue, _n("obfs"), translate("Obfs"))
o = s:option(ListValue, "obfs", translate("Obfs"))
for a, t in ipairs(ssr_obfs_list) do o:value(t) end
o = s:option(Value, _n("obfs_param"), translate("Obfs_param"))
o = s:option(Value, "obfs_param", translate("Obfs_param"))
o = s:option(Value, _n("timeout"), translate("Connection Timeout"))
o = s:option(Value, "timeout", translate("Connection Timeout"))
o.datatype = "uinteger"
o.default = 300
o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"), translate("Need node support required"))
o = s:option(Flag, "tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required"))
o.default = 0
api.luci_types(arg[1], m, s, type_name, option_prefix)
api.luci_types(s1, s)
@@ -17,7 +17,7 @@ m:foreach("user", function(s)
user_list[#user_list + 1] = s
end)
s = m:section(NamedSection, arg[1], "server", translate("Server Config"))
local s = m:section(NamedSection, arg[1], "server", translate("Server Config"))
s.addremove = false
s.dynamic = false
@@ -1,135 +1,27 @@
local m, s = ...
if not api.finded_com("hysteria") then
return
end
-- [[ Hysteria2 ]]
local m, s1 = ...
local type_name = "Hysteria2"
-- [[ Hysteria2 ]]
s1.fields["type"]:value(type_name, "Hysteria2")
s.fields["type"]:value(type_name, "Hysteria2")
if s.val["type"] and s.val["type"] ~= type_name then
if s1.val["type"] and s1.val["type"] ~= type_name then
return
end
local option_prefix = "hysteria2_"
local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "hysteria2_"
local function _n(name)
return option_prefix .. name
end
o = s:option(Flag, "custom", translate("Use Custom Config"))
o = s:option(Flag, _n("custom"), translate("Use Custom Config"))
o = s:option(Value, _n("port"), translate("Listen Port"))
o.datatype = "port"
o:depends({ [_n("custom")] = false })
o = s:option(DynamicList, _n("users"), translate("User"))
for i, v in ipairs(user_list) do
o:value(v[".name"], v.username)
end
o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("realms"), translate("Realms"))
o.default = "0"
o.rewrite_option = o.option
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o.rewrite_option = o.option
o:depends({ [_n("realms")] = "1" })
o.validate = function(self, value)
value = api.trim(value)
local realm = api.parse_realm_uri(value)
if realm then return value end
return nil, translate("Invalid Realm URL.")
end
o = s:option(DynamicList, _n("realm_stun"), translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o.rewrite_option = o.option
o:depends({ [_n("realms")] = "1" })
o = s:option(ListValue, _n("obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:value("gecko")
o.rewrite_option = o.option
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("obfs_password"), translate("Obfs Password"))
o.rewrite_option = o.option
o:depends({ [_n("obfs_type")] = "salamander" })
o:depends({ [_n("obfs_type")] = "gecko" })
o = s:option(Value, _n("obfs_MinPacketSize"), translate("Gecko Packet Size (min)"))
o.datatype = "uinteger"
o.placeholder = "512"
o.default = "512"
o:depends({ [_n("obfs_type")] = "gecko" })
o.rewrite_option = o.option
o = s:option(Value, _n("obfs_MaxPacketSize"), translate("Gecko Packet Size (max)"))
o.datatype = "uinteger"
o.placeholder = "1200"
o.default = "1200"
o:depends({ [_n("obfs_type")] = "gecko" })
o.rewrite_option = o.option
o = s:option(Flag, _n("udp"), translate("UDP"))
o.default = "1"
o.rewrite_option = o.option
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("up_mbps"), translate("Max upload Mbps"))
o.rewrite_option = o.option
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("down_mbps"), translate("Max download Mbps"))
o.rewrite_option = o.option
o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("ignoreClientBandwidth"), translate("ignoreClientBandwidth"))
o.default = "0"
o.rewrite_option = o.option
o:depends({ [_n("custom")] = false })
o = s:option(FileUpload, _n("tls_certificateFile"), translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o.validate = function(self, value, t)
if value and value ~= "" then
if not nixio.fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
end
end
return nil
end
o:depends({ [_n("custom")] = false })
o = s:option(FileUpload, _n("tls_keyFile"), translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o.validate = function(self, value, t)
if value and value ~= "" then
if not nixio.fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
end
end
return nil
end
o:depends({ [_n("custom")] = false })
o = s:option(TextValue, _n("custom_config"), translate("Custom Config"))
o = s:option(TextValue, "custom_config", translate("Custom Config"))
o.rows = 10
o.wrap = "off"
o:depends({ [_n("custom")] = true })
o:depends({ custom = true })
o.datatype = "json"
local o_validate = o.validate
o.validate = function(self, value)
@@ -147,8 +39,101 @@ o.custom_write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "")
end
o = s:option(Flag, _n("log"), translate("Log"))
o = s:option(Value, "port", translate("Listen Port"))
o.datatype = "port"
o:depends({ custom = false })
o = s:option(DynamicList, "users", translate("User"))
for i, v in ipairs(user_list) do
o:value(v[".name"], v.username)
end
o:depends({ custom = false })
o = s:option(Flag, "realms", translate("Realms"))
o.default = "0"
o:depends({ custom = false })
o = s:option(Value, "realm_url", translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ realms = true })
o.validate = function(self, value)
value = api.trim(value)
local realm = api.parse_realm_uri(value)
if realm then return value end
return nil, translate("Invalid Realm URL.")
end
o = s:option(DynamicList, "realm_stun", translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o:depends({ realms = true })
o = s:option(ListValue, "obfs_type", translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:value("gecko")
o:depends({ custom = false })
o = s:option(Value, "obfs_password", translate("Obfs Password"))
o:depends({ obfs_type = "salamander" })
o:depends({ obfs_type = "gecko" })
o = s:option(Value, "obfs_MinPacketSize", translate("Gecko Packet Size (min)"))
o.datatype = "uinteger"
o.placeholder = "512"
o.default = "512"
o:depends({ obfs_type = "gecko" })
o = s:option(Value, "obfs_MaxPacketSize", translate("Gecko Packet Size (max)"))
o.datatype = "uinteger"
o.placeholder = "1200"
o.default = "1200"
o:depends({ obfs_type = "gecko" })
o = s:option(Flag, "udp", translate("UDP"))
o.default = "1"
o:depends({ custom = false })
o = s:option(Value, "up_mbps", translate("Max upload Mbps"))
o:depends({ custom = false })
o = s:option(Value, "down_mbps", translate("Max download Mbps"))
o:depends({ custom = false })
o = s:option(Flag, "ignoreClientBandwidth", translate("ignoreClientBandwidth"))
o.default = "0"
o:depends({ custom = false })
o = s:option(FileUpload, "tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o.validate = function(self, value, t)
if value and value ~= "" then
if not api.fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
end
end
return nil
end
o:depends({ custom = false })
o = s:option(FileUpload, "tls_keyFile", translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o.validate = function(self, value, t)
if value and value ~= "" then
if not api.fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
end
end
return nil
end
o:depends({ custom = false })
o = s:option(Flag, "log", translate("Log"))
o.default = "1"
o.rmempty = false
api.luci_types(arg[1], m, s, type_name, option_prefix)
api.luci_types(s1, s)
@@ -1,29 +1,25 @@
local m, s = ...
if not api.finded_com("xray") then
return
end
-- [[ Xray ]]
local m, s1 = ...
local type_name = "Xray"
-- [[ Xray ]]
s.fields["type"]:value(type_name, "Xray")
if not s.fields["type"].default then
s.fields["type"].default = type_name
s1.fields["type"]:value(type_name, "Xray")
if not s1.fields["type"].default then
s1.fields["type"].default = type_name
end
if s.val["type"] and s.val["type"] ~= type_name then
if s1.val["type"] and s1.val["type"] ~= type_name then
return
end
local option_prefix = "xray_"
local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "xray_"
local function _n(name)
return option_prefix .. name
end
local x_ss_method_list = {
local ss_method_list = {
"aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
}
@@ -31,90 +27,91 @@ local header_type_list = {
"none", "srtp", "utp", "wechat-video", "dtls", "wireguard", "dns"
}
o = s:option(Flag, _n("custom"), translate("Use Custom Config"))
o = s:option(Flag, "custom", translate("Use Custom Config"))
o = s:option(ListValue, _n("protocol"), translate("Protocol"))
o = s:option(ListValue, "protocol", translate("Protocol"))
o:value("vmess", "Vmess")
o:value("vless", "VLESS")
o:value("http", "HTTP")
o:value("socks", "Socks")
o:value("shadowsocks", "Shadowsocks")
o:value("trojan", "Trojan")
o:value("hysteria2", translate("Hysteria2"))
o:value("hysteria2", "Hysteria2")
o:value("wireguard", "WireGuard")
o:value("dokodemo-door", "dokodemo-door")
o:depends({ [_n("custom")] = false })
o:depends({ custom = false })
o = s:option(Value, _n("port"), translate("Listen Port"))
o = s:option(Value, "port", translate("Listen Port"))
o.datatype = "port"
o:depends({ [_n("custom")] = false })
o:depends({ custom = false })
o = s:option(DynamicList, _n("users"), translate("User"))
o = s:option(DynamicList, "users", translate("User"))
for i, v in ipairs(user_list) do
o:value(v[".name"], v.username)
end
o:depends({ [_n("protocol")] = "http" })
o:depends({ [_n("protocol")] = "socks" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "vmess" })
o:depends({ [_n("protocol")] = "vless" })
o:depends({ [_n("protocol")] = "trojan" })
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ protocol = "http" })
o:depends({ protocol = "socks" })
o:depends({ protocol = "shadowsocks" })
o:depends({ protocol = "vmess" })
o:depends({ protocol = "vless" })
o:depends({ protocol = "trojan" })
o:depends({ protocol = "hysteria2" })
o:depends({ protocol = "wireguard" })
o = s:option(ListValue, _n("d_protocol"), translate("Destination protocol"))
o = s:option(ListValue, "d_protocol", translate("Destination protocol"))
o:value("tcp", "TCP")
o:value("udp", "UDP")
o:value("tcp,udp", "TCP,UDP")
o:depends({ [_n("protocol")] = "dokodemo-door" })
o:depends({ protocol = "dokodemo-door" })
o = s:option(Value, _n("d_address"), translate("Destination address"))
o:depends({ [_n("protocol")] = "dokodemo-door" })
o = s:option(Value, "d_address", translate("Destination address"))
o:depends({ protocol = "dokodemo-door" })
o = s:option(Value, _n("d_port"), translate("Destination port"))
o = s:option(Value, "d_port", translate("Destination port"))
o.datatype = "port"
o:depends({ [_n("protocol")] = "dokodemo-door" })
o:depends({ protocol = "dokodemo-door" })
o = s:option(Value, _n("decryption"), translate("Encrypt Method") .. " (decryption)")
o = s:option(Value, "decryption", translate("Encrypt Method") .. " (decryption)")
o.default = "none"
o.placeholder = "none"
o:depends({ [_n("protocol")] = "vless" })
o:depends({ protocol = "vless" })
o.validate = function(self, value)
value = api.trim(value)
return (value == "" and "none" or value)
end
o = s:option(ListValue, _n("x_ss_method"), translate("Encrypt Method"))
o.rewrite_option = "method"
for a, t in ipairs(x_ss_method_list) do o:value(t) end
o:depends({ [_n("protocol")] = "shadowsocks" })
o = s:option(ListValue, "ss_method", translate("Encrypt Method"))
for a, t in ipairs(ss_method_list) do o:value(t) end
o:depends({ protocol = "shadowsocks" })
o = s:option(Value, _n("ss_password"), translate("Password"))
o:depends({ [_n("protocol")] = "shadowsocks" })
o = s:option(Value, "ss_password", translate("Password"))
o:depends({ protocol = "shadowsocks" })
o = s:option(ListValue, _n("ss_network"), translate("Transport"))
o = s:option(ListValue, "ss_network", translate("Transport"))
o.default = "tcp,udp"
o:value("tcp", "TCP")
o:value("udp", "UDP")
o:value("tcp,udp", "TCP,UDP")
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ protocol = "shadowsocks" })
o = s:option(Flag, _n("udp_forward"), translate("UDP Forward"))
o = s:option(Flag, "udp_forward", translate("UDP Forward"))
o.default = "1"
o.rmempty = false
o:depends({ [_n("protocol")] = "socks" })
o:depends({ protocol = "socks" })
o = s:option(ListValue, _n("flow"), translate("flow"))
o = s:option(ListValue, "flow", translate("flow"))
o.default = ""
o:value("", translate("Disable"))
o:value("xtls-rprx-vision")
o:depends({ [_n("protocol")] = "vless" })
o:depends({ protocol = "vless" })
---- [[ hysteria2 ]]
o = s:option(Flag, _n("hysteria2_realms"), translate("Realms"))
o = s:option(Flag, "hysteria2_realms", translate("Realms"))
o.default = "0"
o:depends({ [_n("protocol")] = "hysteria2"})
o:depends({ protocol = "hysteria2"})
o = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ [_n("hysteria2_realms")] = "1" })
o = s:option(Value, "hysteria2_realm_url", translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ hysteria2_realms = "1" })
o.validate = function(self, value)
value = api.trim(value)
local realm = api.parse_realm_uri(value)
@@ -122,52 +119,52 @@ o.validate = function(self, value)
return nil, translate("Invalid Realm URL.")
end
o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN"))
o = s:option(DynamicList, "hysteria2_realm_stun", translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o:depends({ [_n("hysteria2_realms")] = "1" })
o:depends({ hysteria2_realms = "1" })
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o = s:option(ListValue, "hysteria2_obfs_type", translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:value("gecko")
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ protocol = "hysteria2" })
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
o = s:option(Value, "hysteria2_obfs_password", translate("Obfs Password"))
o:depends({ hysteria2_obfs_type = "salamander" })
o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Value, _n("hysteria2_obfs_MinPacketSize"), translate("Gecko Packet Size (min)"))
o = s:option(Value, "hysteria2_obfs_MinPacketSize", translate("Gecko Packet Size (min)"))
o.datatype = "uinteger"
o.placeholder = "512"
o.default = "512"
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Value, _n("hysteria2_obfs_MaxPacketSize"), translate("Gecko Packet Size (max)"))
o = s:option(Value, "hysteria2_obfs_MaxPacketSize", translate("Gecko Packet Size (max)"))
o.datatype = "uinteger"
o.placeholder = "1200"
o.default = "1200"
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control"))
o = s:option(Flag, "hysteria2_ignore_client_bandwidth", translate("Client BBR Flow Control"))
o.default = 0
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ protocol = "hysteria2" })
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false })
o = s:option(Value, "hysteria2_up_mbps", translate("Max upload Mbps"))
o:depends({ protocol = "hysteria2", hysteria2_ignore_client_bandwidth = false })
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false })
o = s:option(Value, "hysteria2_down_mbps", translate("Max download Mbps"))
o:depends({ protocol = "hysteria2", hysteria2_ignore_client_bandwidth = false })
---- [[ TLS ]]
o = s:option(Flag, _n("tls"), translate("TLS"))
o = s:option(Flag, "tls", "TLS")
o.default = 0
o.validate = function(self, value, t)
if value then
local reality = s.fields[_n("reality")] and s.fields[_n("reality")]:formvalue(t) or nil
local reality = s.fields["reality"] and s.fields["reality"]:formvalue(t) or nil
if reality and reality == "1" then return value end
if value == "1" then
local ca = s.fields[_n("tls_certificateFile")] and s.fields[_n("tls_certificateFile")]:formvalue(t) or ""
local key = s.fields[_n("tls_keyFile")] and s.fields[_n("tls_keyFile")]:formvalue(t) or ""
local ca = s.fields["tls_certificateFile"] and s.fields["tls_certificateFile"]:formvalue(t) or ""
local key = s.fields["tls_keyFile"] and s.fields["tls_keyFile"]:formvalue(t) or ""
if ca == "" or key == "" then
return nil, translate("Public key and Private key path can not be empty!")
end
@@ -175,32 +172,32 @@ o.validate = function(self, value, t)
return value
end
end
o:depends({ [_n("protocol")] = "vmess" })
o:depends({ [_n("protocol")] = "vless" })
o:depends({ [_n("protocol")] = "http" })
o:depends({ [_n("protocol")] = "socks" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "trojan" })
o:depends({ protocol = "vmess" })
o:depends({ protocol = "vless" })
o:depends({ protocol = "http" })
o:depends({ protocol = "socks" })
o:depends({ protocol = "shadowsocks" })
o:depends({ protocol = "trojan" })
-- [[ REALITY ]] --
o = s:option(Flag, _n("reality"), translate("REALITY"))
o = s:option(Flag, "reality", translate("REALITY"))
o.default = 0
o:depends({ [_n("tls")] = true })
o:depends({ tls = true })
o = s:option(Value, _n("reality_private_key"), translate("Private Key"))
o:depends({ [_n("reality")] = true })
o = s:option(Value, "reality_private_key", translate("Private Key"))
o:depends({ reality = true })
o = s:option(DynamicList, _n("reality_shortId"), translate("Short Id"))
o:depends({ [_n("reality")] = true })
o = s:option(DynamicList, "reality_shortId", translate("Short Id"))
o:depends({ reality = true })
o = s:option(Value, _n("reality_dest"), translate("Dest"))
o = s:option(Value, "reality_dest", translate("Dest"))
o.default = "google.com:443"
o:depends({ [_n("reality")] = true })
o:depends({ reality = true })
o = s:option(DynamicList, _n("reality_serverNames"), translate("serverNames"))
o:depends({ [_n("reality")] = true })
o = s:option(DynamicList, "reality_serverNames", translate("serverNames"))
o:depends({ reality = true })
o = s:option(ListValue, _n("alpn"), translate("alpn"))
o = s:option(ListValue, "alpn", translate("alpn"))
o.default = "default"
o:value("default", translate("Default"))
o:value("h3")
@@ -209,35 +206,35 @@ o:value("h3,h2")
o:value("http/1.1")
o:value("h2,http/1.1")
o:value("h3,h2,http/1.1")
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ tls = true, reality = false })
o = s:option(Flag, _n("use_mldsa65Seed"), translate("ML-DSA-65"))
o = s:option(Flag, "use_mldsa65Seed", translate("ML-DSA-65"))
o.default = "0"
o:depends({ [_n("reality")] = true })
o:depends({ reality = true })
o = s:option(TextValue, _n("reality_mldsa65Seed"), "ML-DSA-65 " .. translate("Private Key"))
o = s:option(TextValue, "reality_mldsa65Seed", "ML-DSA-65 " .. translate("Private Key"))
o.default = ""
o.rows = 5
o.wrap = "soft"
o:depends({ [_n("use_mldsa65Seed")] = true })
o:depends({ use_mldsa65Seed = true })
o.validate = function(self, value)
return api.trim(value:gsub("[\r\n]", ""))
end
-- o = s:option(Value, _n("minversion"), translate("minversion"))
-- o = s:option(Value, "minversion", translate("minversion"))
-- o.default = "1.3"
-- o:value("1.3")
--o:depends({ [_n("tls")] = true })
--o:depends({ tls = true })
-- [[ TLS ]] --
o = s:option(FileUpload, _n("tls_certificateFile"), translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
o = s:option(FileUpload, "tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ [_n("protocol")] = "hysteria2"})
o:depends({ tls = true, reality = false })
o:depends({ protocol = "hysteria2"})
o.validate = function(self, value, t)
if value and value ~= "" then
if not nixio.fs.access(value) then
if not api.fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
@@ -246,14 +243,14 @@ o.validate = function(self, value, t)
return nil
end
o = s:option(FileUpload, _n("tls_keyFile"), translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
o = s:option(FileUpload, "tls_keyFile", translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ [_n("protocol")] = "hysteria2"})
o:depends({ tls = true, reality = false })
o:depends({ protocol = "hysteria2"})
o.validate = function(self, value, t)
if value and value ~= "" then
if not nixio.fs.access(value) then
if not api.fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
@@ -262,117 +259,116 @@ o.validate = function(self, value, t)
return nil
end
o = s:option(Flag, _n("ech"), translate("ECH"))
o = s:option(Flag, "ech", translate("ECH"))
o.default = "0"
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ tls = true, reality = false })
o = s:option(TextValue, _n("ech_key"), translate("ECH Key"))
o = s:option(TextValue, "ech_key", translate("ECH Key"))
o.default = ""
o.rows = 5
o.wrap = "soft"
o:depends({ [_n("ech")] = true })
o:depends({ ech = true })
o.validate = function(self, value)
return api.trim(value:gsub("[\r\n]", ""))
end
o = s:option(ListValue, _n("transport"), translate("Transport"))
o = s:option(ListValue, "transport", translate("Transport"))
o:value("raw", "RAW")
o:value("mkcp", "mKCP")
o:value("ws", "WebSocket")
o:value("grpc", "gRPC")
o:value("httpupgrade", "HttpUpgrade")
o:value("xhttp", "XHTTP")
o:depends({ [_n("protocol")] = "vmess" })
o:depends({ [_n("protocol")] = "vless" })
o:depends({ [_n("protocol")] = "socks" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "trojan" })
o:depends({ protocol = "vmess" })
o:depends({ protocol = "vless" })
o:depends({ protocol = "socks" })
o:depends({ protocol = "shadowsocks" })
o:depends({ protocol = "trojan" })
-- [[ WebSocket ]]--
o = s:option(Value, _n("ws_host"), translate("WebSocket Host"))
o:depends({ [_n("transport")] = "ws" })
o = s:option(Value, "ws_host", translate("WebSocket Host"))
o:depends({ transport = "ws" })
o = s:option(Value, _n("ws_path"), translate("WebSocket Path"))
o:depends({ [_n("transport")] = "ws" })
o = s:option(Value, "ws_path", translate("WebSocket Path"))
o:depends({ transport = "ws" })
-- [[ HttpUpgrade ]]--
o = s:option(Value, _n("httpupgrade_host"), translate("HttpUpgrade Host"))
o:depends({ [_n("transport")] = "httpupgrade" })
o = s:option(Value, "httpupgrade_host", translate("HttpUpgrade Host"))
o:depends({ transport = "httpupgrade" })
o = s:option(Value, _n("httpupgrade_path"), translate("HttpUpgrade Path"))
o = s:option(Value, "httpupgrade_path", translate("HttpUpgrade Path"))
o.placeholder = "/"
o:depends({ [_n("transport")] = "httpupgrade" })
o:depends({ transport = "httpupgrade" })
-- [[ XHTTP ]]--
o = s:option(Value, _n("xhttp_host"), translate("XHTTP Host"))
o:depends({ [_n("transport")] = "xhttp" })
o = s:option(Value, "xhttp_host", translate("XHTTP Host"))
o:depends({ transport = "xhttp" })
o = s:option(Value, _n("xhttp_path"), translate("XHTTP Path"))
o = s:option(Value, "xhttp_path", translate("XHTTP Path"))
o.placeholder = "/"
o:depends({ [_n("transport")] = "xhttp" })
o:depends({ transport = "xhttp" })
o = s:option(Value, _n("xhttp_maxuploadsize"), translate("maxUploadSize"))
o = s:option(Value, "xhttp_maxuploadsize", translate("maxUploadSize"))
o.default = "1000000"
o:depends({ [_n("transport")] = "xhttp" })
o:depends({ transport = "xhttp" })
o = s:option(Value, _n("xhttp_maxconcurrentuploads"), translate("maxConcurrentUploads"))
o = s:option(Value, "xhttp_maxconcurrentuploads", translate("maxConcurrentUploads"))
o.default = "10"
o:depends({ [_n("transport")] = "xhttp" })
o:depends({ transport = "xhttp" })
o = s:option(Value, _n("splithttp_maxuploadsize"), translate("maxUploadSize"))
o = s:option(Value, "splithttp_maxuploadsize", translate("maxUploadSize"))
o.default = "1000000"
o:depends({ [_n("transport")] = "splithttp" })
o:depends({ transport = "splithttp" })
o = s:option(Value, _n("splithttp_maxconcurrentuploads"), translate("maxConcurrentUploads"))
o = s:option(Value, "splithttp_maxconcurrentuploads", translate("maxConcurrentUploads"))
o.default = "10"
o:depends({ [_n("transport")] = "splithttp" })
o:depends({ transport = "splithttp" })
-- [[ TCP ]]--
o = s:option(ListValue, _n("tcp_guise"), translate("Camouflage Type"))
o = s:option(ListValue, "tcp_guise", translate("Camouflage Type"))
o:value("none", "none")
o:value("http", "http")
o:depends({ [_n("transport")] = "raw" })
o:depends({ transport = "raw" })
o = s:option(DynamicList, _n("tcp_guise_http_host"), translate("HTTP Host"))
o:depends({ [_n("tcp_guise")] = "http" })
o = s:option(DynamicList, "tcp_guise_http_host", translate("HTTP Host"))
o:depends({ tcp_guise = "http" })
o = s:option(DynamicList, _n("tcp_guise_http_path"), translate("HTTP Path"))
o:depends({ [_n("tcp_guise")] = "http" })
o = s:option(DynamicList, "tcp_guise_http_path", translate("HTTP Path"))
o:depends({ tcp_guise = "http" })
-- [[ mKCP ]]--
o = s:option(ListValue, _n("mkcp_guise"), translate("Camouflage Type"), translate('<br />none: default, no masquerade, data sent is packets with no characteristics.<br />srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).<br />utp: packets disguised as uTP will be recognized as bittorrent downloaded data.<br />wechat-video: packets disguised as WeChat video calls.<br />dtls: disguised as DTLS 1.2 packet.<br />wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)<br />dns: Disguising traffic as DNS requests.'))
o = s:option(ListValue, "mkcp_guise", translate("Camouflage Type"), translate('<br />none: default, no masquerade, data sent is packets with no characteristics.<br />srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).<br />utp: packets disguised as uTP will be recognized as bittorrent downloaded data.<br />wechat-video: packets disguised as WeChat video calls.<br />dtls: disguised as DTLS 1.2 packet.<br />wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)<br />dns: Disguising traffic as DNS requests.'))
for a, t in ipairs(header_type_list) do o:value(t) end
o:depends({ [_n("transport")] = "mkcp" })
o:depends({ transport = "mkcp" })
o = s:option(Value, _n("mkcp_domain"), translate("Camouflage Domain"), translate("Use it together with the DNS disguised type. You can fill in any domain."))
o:depends({ [_n("mkcp_guise")] = "dns" })
o = s:option(Value, "mkcp_domain", translate("Camouflage Domain"), translate("Use it together with the DNS disguised type. You can fill in any domain."))
o:depends({ mkcp_guise = "dns" })
o = s:option(Value, _n("mkcp_mtu"), translate("KCP MTU"))
o = s:option(Value, "mkcp_mtu", translate("KCP MTU"))
o.datatype = "uinteger"
o.default = 1350
o:depends({ [_n("transport")] = "mkcp" })
o:depends({ transport = "mkcp" })
o = s:option(Value, _n("mkcp_seed"), translate("KCP Seed"))
o:depends({ [_n("transport")] = "mkcp" })
o = s:option(Value, "mkcp_seed", translate("KCP Seed"))
o:depends({ transport = "mkcp" })
-- [[ gRPC ]]--
o = s:option(Value, _n("grpc_serviceName"), "ServiceName")
o:depends({ [_n("transport")] = "grpc" })
o = s:option(Value, "grpc_serviceName", "ServiceName")
o:depends({ transport = "grpc" })
--[[FinalMask]]
o = s:option(Flag, _n("use_finalmask"), "FinalMask")
o = s:option(Flag, "use_finalmask", "FinalMask")
o.default = "0"
o:depends({ [_n("custom")] = false, [_n("protocol")] = "vmess" })
o:depends({ [_n("custom")] = false, [_n("protocol")] = "vless" })
o:depends({ [_n("custom")] = false, [_n("protocol")] = "trojan" })
o:depends({ [_n("custom")] = false, [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("custom")] = false, [_n("protocol")] = "wireguard" })
o:depends({ [_n("custom")] = false, [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o:depends({ custom = false, protocol = "vmess" })
o:depends({ custom = false, protocol = "vless" })
o:depends({ custom = false, protocol = "trojan" })
o:depends({ custom = false, protocol = "shadowsocks" })
o:depends({ custom = false, protocol = "hysteria2", hysteria2_realms = false })
o = s:option(TextValue, _n("finalmask"), "FinalMask JSON")
o:depends({ [_n("use_finalmask")] = true })
o = s:option(TextValue, "finalmask", "FinalMask JSON")
o:depends({ use_finalmask = true })
o.rows = 10
o.wrap = "off"
o.datatype = "json"
@@ -387,48 +383,78 @@ o.custom_write = function(self, section, value)
end
--[[acceptProxyProtocol]]
o = s:option(Flag, _n("acceptProxyProtocol"), translate("acceptProxyProtocol"), translate("Whether to receive PROXY protocol, when this node want to be fallback or forwarded by proxy, it must be enable, otherwise it cannot be used."))
o = s:option(Flag, "acceptProxyProtocol", translate("acceptProxyProtocol"), translate("Whether to receive PROXY protocol, when this node want to be fallback or forwarded by proxy, it must be enable, otherwise it cannot be used."))
o.default = "0"
o:depends({ [_n("custom")] = false })
o:depends({ custom = false })
--[[Fast Open]]
o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"))
o = s:option(Flag, "tcp_fast_open", "TCP " .. translate("Fast Open"))
o.default = "0"
o:depends({ [_n("protocol")] = "vmess", [_n("custom")] = false })
o:depends({ [_n("protocol")] = "vless", [_n("custom")] = false })
o:depends({ [_n("protocol")] = "shadowsocks", [_n("custom")] = false })
o:depends({ [_n("protocol")] = "trojan", [_n("custom")] = false })
o:depends({ protocol = "vmess", custom = false })
o:depends({ protocol = "vless", custom = false })
o:depends({ protocol = "shadowsocks", custom = false })
o:depends({ protocol = "trojan", custom = false })
-- [[ Fallback ]]--
o = s:option(Flag, _n("fallback"), translate("Fallback"))
o:depends({ [_n("protocol")] = "vless", [_n("transport")] = "raw" })
o:depends({ [_n("protocol")] = "trojan", [_n("transport")] = "raw" })
o = s:option(Flag, "fallback", translate("Fallback"))
o:depends({ protocol = "vless", transport = "raw" })
o:depends({ protocol = "trojan", transport = "raw" })
--[[
o = s:option(Value, _n("fallback_alpn"), "Fallback alpn")
o:depends({ [_n("fallback")] = true })
o = s:option(Value, "fallback_alpn", "Fallback alpn")
o:depends({ fallback = true })
o = s:option(Value, _n("fallback_path"), "Fallback path")
o:depends({ [_n("fallback")] = true })
o = s:option(Value, "fallback_path", "Fallback path")
o:depends({ fallback = true })
o = s:option(Value, _n("fallback_dest"), "Fallback dest")
o:depends({ [_n("fallback")] = true })
o = s:option(Value, "fallback_dest", "Fallback dest")
o:depends({ fallback = true })
o = s:option(Value, _n("fallback_xver"), "Fallback xver")
o = s:option(Value, "fallback_xver", "Fallback xver")
o.default = 0
o:depends({ [_n("fallback")] = true })
o:depends({ fallback = true })
]]--
o = s:option(DynamicList, _n("fallback_list"), "Fallback", translate("format: dest,path,xver"))
o:depends({ [_n("fallback")] = true })
o = s:option(DynamicList, "fallback_list", "Fallback", translate("format: dest,path,xver"))
o:depends({ fallback = true })
o = s:option(Flag, _n("bind_local"), translate("Bind Local"), translate("When selected, it can only be accessed localhost."))
o.default = "0"
o:depends({ [_n("custom")] = false })
-- Not supported at present
--[[
o = s:option(Flag, "wireguard_system_interface", translate("System interface"))
o.default = 0
o:depends({ protocol = "wireguard" })
]]--
o = s:option(Flag, _n("accept_lan"), translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!"))
o = s:option(Value, "wireguard_mtu", "MTU")
o.default = "1420"
o:depends({ protocol = "wireguard" })
-- Not supported at present
--[[
o = s:option(DynamicList, "wireguard_local_address", translate("Local Address"))
o:depends({ protocol = "wireguard" })
]]--
o = s:option(Value, "wireguard_private_key", translate("Private Key"))
o.datatype = "base64"
o:depends({ protocol = "wireguard" })
o = s:option(Value, "wireguard_public_key", translate("Public Key"))
o.datatype = "base64"
o:depends({ protocol = "wireguard" })
o = s:option(DummyValue, "gen_wireguard_key")
o.prefix = option_prefix
o.template = m:template_path("/server/server_wireguard")
o:depends({ protocol = "wireguard" })
o = s:option(Flag, "bind_local", translate("Bind Local"), translate("When selected, it can only be accessed localhost."))
o.default = "0"
o:depends({ [_n("custom")] = false })
o:depends({ custom = false })
o = s:option(Flag, "accept_lan", translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!"))
o.default = "0"
o:depends({ custom = false })
local nodes_table = {}
for k, e in ipairs(api.get_valid_nodes()) do
@@ -441,7 +467,7 @@ for k, e in ipairs(api.get_valid_nodes()) do
end
end
o = s:option(ListValue, _n("outbound_node"), translate("outbound node"))
o = s:option(ListValue, "outbound_node", translate("outbound node"))
o:value("", translate("Close"))
o:value("_socks", translate("Custom Socks"))
o:value("_http", translate("Custom HTTP"))
@@ -452,37 +478,37 @@ for k, v in pairs(nodes_table) do
o:value(v.id, v.remarks)
o.group[#o.group+1] = (v.group and v.group ~= "") and v.group or translate("default")
end
o:depends({ [_n("custom")] = false })
o:depends({ custom = false })
o = s:option(Value, _n("outbound_node_address"), translate("Address (Support Domain Name)"))
o:depends({ [_n("outbound_node")] = "_socks"})
o:depends({ [_n("outbound_node")] = "_http"})
o = s:option(Value, "outbound_node_address", translate("Address (Support Domain Name)"))
o:depends({ outbound_node = "_socks"})
o:depends({ outbound_node = "_http"})
o = s:option(Value, _n("outbound_node_port"), translate("Port"))
o = s:option(Value, "outbound_node_port", translate("Port"))
o.datatype = "port"
o:depends({ [_n("outbound_node")] = "_socks"})
o:depends({ [_n("outbound_node")] = "_http"})
o:depends({ outbound_node = "_socks"})
o:depends({ outbound_node = "_http"})
o = s:option(Value, _n("outbound_node_username"), translate("Username"))
o:depends({ [_n("outbound_node")] = "_socks"})
o:depends({ [_n("outbound_node")] = "_http"})
o = s:option(Value, "outbound_node_username", translate("Username"))
o:depends({ outbound_node = "_socks"})
o:depends({ outbound_node = "_http"})
o = s:option(Value, _n("outbound_node_password"), translate("Password"))
o = s:option(Value, "outbound_node_password", translate("Password"))
o.password = true
o:depends({ [_n("outbound_node")] = "_socks"})
o:depends({ [_n("outbound_node")] = "_http"})
o:depends({ outbound_node = "_socks"})
o:depends({ outbound_node = "_http"})
o = s:option(Value, _n("outbound_node_iface"), translate("Interface"))
o:depends({ [_n("outbound_node")] = "_iface"})
o = s:option(Value, "outbound_node_iface", translate("Interface"))
o:depends({ outbound_node = "_iface"})
local netdev_list = api.get_network_devices()
for _, d in ipairs(netdev_list) do
o:value(d.name, d.label)
end
o = s:option(TextValue, _n("custom_config"), translate("Custom Config") .. " (JSON)")
o = s:option(TextValue, "custom_config", translate("Custom Config") .. " (JSON)")
o.rows = 10
o.wrap = "off"
o:depends({ [_n("custom")] = true })
o:depends({ custom = true })
o.datatype = "json"
local o_validate = o.validate
o.validate = function(self, value)
@@ -500,16 +526,16 @@ o.custom_write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "")
end
o = s:option(Flag, _n("log"), translate("Log"))
o = s:option(Flag, "log", translate("Log"))
o.default = "1"
o.rmempty = false
o = s:option(ListValue, _n("loglevel"), translate("Log Level"))
o = s:option(ListValue, "loglevel", translate("Log Level"))
o.default = "warning"
o:value("debug")
o:value("info")
o:value("warning")
o:value("error")
o:depends({ [_n("log")] = true })
o:depends({ log = true })
api.luci_types(arg[1], m, s, type_name, option_prefix)
api.luci_types(s1, s)
@@ -1,29 +1,25 @@
local m, s = ...
local singbox_bin = api.finded_com("sing-box")
if not singbox_bin then
return
end
-- [[ Sing-Box ]]
local m, s1 = ...
local type_name = "sing-box"
-- [[ Sing-Box ]]
s.fields["type"]:value(type_name, "Sing-Box")
if not s.fields["type"].default then
s.fields["type"].default = type_name
s1.fields["type"]:value(type_name, "Sing-Box")
if not s1.fields["type"].default then
s1.fields["type"].default = type_name
end
if s.val["type"] and s.val["type"] ~= type_name then
if s1.val["type"] and s1.val["type"] ~= type_name then
return
end
local option_prefix = "singbox_"
local function _n(name)
return option_prefix .. name
end
local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "singbox_"
local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'")
@@ -32,9 +28,9 @@ local ss_method_list = {
"2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
}
o = s:option(Flag, _n("custom"), translate("Use Custom Config"))
o = s:option(Flag, "custom", translate("Use Custom Config"))
o = s:option(ListValue, _n("protocol"), translate("Protocol"))
o = s:option(ListValue, "protocol", translate("Protocol"))
o:value("mixed", "Mixed")
o:value("socks", "Socks")
o:value("http", "HTTP")
@@ -57,74 +53,74 @@ if singbox_tags:find("with_wireguard") then
o:value("wireguard", "WireGuard")
end
o:value("direct", "Direct")
o:depends({ [_n("custom")] = false })
o:depends({ custom = false })
o = s:option(Value, _n("port"), translate("Listen Port"))
o = s:option(Value, "port", translate("Listen Port"))
o.datatype = "port"
o:depends({ [_n("custom")] = false })
o:depends({ custom = false })
o = s:option(DynamicList, _n("users"), translate("User"))
o = s:option(DynamicList, "users", translate("User"))
for i, v in ipairs(user_list) do
o:value(v[".name"], v.username)
end
o:depends({ [_n("protocol")] = "mixed" })
o:depends({ [_n("protocol")] = "socks" })
o:depends({ [_n("protocol")] = "http" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "vmess" })
o:depends({ [_n("protocol")] = "vless" })
o:depends({ [_n("protocol")] = "trojan" })
o:depends({ [_n("protocol")] = "naive" })
o:depends({ [_n("protocol")] = "hysteria" })
o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ [_n("protocol")] = "anytls" })
o:depends({ [_n("protocol")] = "wireguard" })
o:depends({ protocol = "mixed" })
o:depends({ protocol = "socks" })
o:depends({ protocol = "http" })
o:depends({ protocol = "shadowsocks" })
o:depends({ protocol = "vmess" })
o:depends({ protocol = "vless" })
o:depends({ protocol = "trojan" })
o:depends({ protocol = "naive" })
o:depends({ protocol = "hysteria" })
o:depends({ protocol = "tuic" })
o:depends({ protocol = "hysteria2" })
o:depends({ protocol = "anytls" })
o:depends({ protocol = "wireguard" })
if singbox_tags:find("with_quic") then
o = s:option(Value, _n("hysteria_obfs"), translate("Obfs Password"))
o:depends({ [_n("protocol")] = "hysteria" })
o = s:option(Value, "hysteria_obfs", translate("Obfs Password"))
o:depends({ protocol = "hysteria" })
o = s:option(Value, _n("hysteria_up_mbps"), translate("Max upload Mbps"))
o = s:option(Value, "hysteria_up_mbps", translate("Max upload Mbps"))
o.default = "100"
o:depends({ [_n("protocol")] = "hysteria" })
o:depends({ protocol = "hysteria" })
o = s:option(Value, _n("hysteria_down_mbps"), translate("Max download Mbps"))
o = s:option(Value, "hysteria_down_mbps", translate("Max download Mbps"))
o.default = "100"
o:depends({ [_n("protocol")] = "hysteria" })
o:depends({ protocol = "hysteria" })
o = s:option(Value, _n("hysteria_recv_window_conn"), translate("QUIC stream receive window"))
o:depends({ [_n("protocol")] = "hysteria" })
o = s:option(Value, "hysteria_recv_window_conn", translate("QUIC stream receive window"))
o:depends({ protocol = "hysteria" })
o = s:option(Value, _n("hysteria_recv_window_client"), translate("QUIC connection receive window"))
o:depends({ [_n("protocol")] = "hysteria" })
o = s:option(Value, "hysteria_recv_window_client", translate("QUIC connection receive window"))
o:depends({ protocol = "hysteria" })
o = s:option(Value, _n("hysteria_max_conn_client"), translate("QUIC concurrent bidirectional streams"))
o = s:option(Value, "hysteria_max_conn_client", translate("QUIC concurrent bidirectional streams"))
o.default = "1024"
o:depends({ [_n("protocol")] = "hysteria" })
o:depends({ protocol = "hysteria" })
o = s:option(Flag, _n("hysteria_disable_mtu_discovery"), translate("Disable MTU detection"))
o:depends({ [_n("protocol")] = "hysteria" })
o = s:option(Flag, "hysteria_disable_mtu_discovery", translate("Disable MTU detection"))
o:depends({ protocol = "hysteria" })
end
if singbox_tags:find("with_quic") then
o = s:option(ListValue, _n("tuic_congestion_control"), translate("Congestion control algorithm"))
o = s:option(ListValue, "tuic_congestion_control", translate("Congestion control algorithm"))
o.default = "cubic"
o:value("bbr", translate("BBR"))
o:value("cubic", translate("CUBIC"))
o:value("new_reno", translate("New Reno"))
o:depends({ [_n("protocol")] = "tuic" })
o:depends({ protocol = "tuic" })
o = s:option(Flag, _n("tuic_zero_rtt_handshake"), translate("Enable 0-RTT QUIC handshake"))
o = s:option(Flag, "tuic_zero_rtt_handshake", translate("Enable 0-RTT QUIC handshake"))
o.default = 0
o:depends({ [_n("protocol")] = "tuic" })
o:depends({ protocol = "tuic" })
o = s:option(Value, _n("tuic_heartbeat"), translate("Heartbeat interval(second)"))
o = s:option(Value, "tuic_heartbeat", translate("Heartbeat interval(second)"))
o.datatype = "uinteger"
o.default = "3"
o:depends({ [_n("protocol")] = "tuic" })
o:depends({ protocol = "tuic" })
o = s:option(ListValue, _n("tuic_alpn"), translate("QUIC TLS ALPN"))
o = s:option(ListValue, "tuic_alpn", translate("QUIC TLS ALPN"))
o.default = "default"
o:value("default", translate("Default"))
o:value("h3")
@@ -135,16 +131,16 @@ if singbox_tags:find("with_quic") then
o:value("h3,h2,http/1.1")
o:value("spdy/3.1")
o:value("h3,spdy/3.1")
o:depends({ [_n("protocol")] = "tuic" })
o:depends({ protocol = "tuic" })
end
if singbox_tags:find("with_quic") then
o = s:option(Flag, _n("hysteria2_realms"), translate("Realms"))
o = s:option(Flag, "hysteria2_realms", translate("Realms"))
o.default = "0"
o:depends({ [_n("protocol")] = "hysteria2"})
o:depends({ protocol = "hysteria2"})
o = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ [_n("hysteria2_realms")] = "1" })
o = s:option(Value, "hysteria2_realm_url", translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ hysteria2_realms = "1" })
o.validate = function(self, value)
value = api.trim(value)
local realm = api.parse_realm_uri(value)
@@ -152,83 +148,82 @@ if singbox_tags:find("with_quic") then
return nil, translate("Invalid Realm URL.")
end
o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN"))
o = s:option(DynamicList, "hysteria2_realm_stun", translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o:depends({ [_n("hysteria2_realms")] = "1" })
o:depends({ hysteria2_realms = "1" })
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o = s:option(ListValue, "hysteria2_obfs_type", translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:value("gecko")
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ protocol = "hysteria2" })
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
o = s:option(Value, "hysteria2_obfs_password", translate("Obfs Password"))
o:depends({ hysteria2_obfs_type = "salamander" })
o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Value, _n("hysteria2_obfs_MinPacketSize"), translate("Gecko Packet Size (min)"))
o = s:option(Value, "hysteria2_obfs_MinPacketSize", translate("Gecko Packet Size (min)"))
o.datatype = "uinteger"
o.placeholder = "512"
o.default = "512"
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Value, _n("hysteria2_obfs_MaxPacketSize"), translate("Gecko Packet Size (max)"))
o = s:option(Value, "hysteria2_obfs_MaxPacketSize", translate("Gecko Packet Size (max)"))
o.datatype = "uinteger"
o.placeholder = "1200"
o.default = "1200"
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control"), translate("Commands the client to use the BBR flow control algorithm"))
o = s:option(Flag, "hysteria2_ignore_client_bandwidth", translate("Client BBR Flow Control"), translate("Commands the client to use the BBR flow control algorithm"))
o.default = 0
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ protocol = "hysteria2" })
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false })
o = s:option(Value, "hysteria2_up_mbps", translate("Max upload Mbps"))
o:depends({ protocol = "hysteria2", hysteria2_ignore_client_bandwidth = false })
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false })
o = s:option(Value, "hysteria2_down_mbps", translate("Max download Mbps"))
o:depends({ protocol = "hysteria2", hysteria2_ignore_client_bandwidth = false })
end
o = s:option(ListValue, _n("d_protocol"), translate("Destination protocol"))
o = s:option(ListValue, "d_protocol", translate("Destination protocol"))
o:value("tcp", "TCP")
o:value("udp", "UDP")
o:value("tcp,udp", "TCP,UDP")
o:depends({ [_n("protocol")] = "direct" })
o:depends({ protocol = "direct" })
o = s:option(Value, _n("d_address"), translate("Destination address"))
o:depends({ [_n("protocol")] = "direct" })
o = s:option(Value, "d_address", translate("Destination address"))
o:depends({ protocol = "direct" })
o = s:option(Value, _n("d_port"), translate("Destination port"))
o = s:option(Value, "d_port", translate("Destination port"))
o.datatype = "port"
o:depends({ [_n("protocol")] = "direct" })
o:depends({ protocol = "direct" })
o = s:option(Value, _n("decryption"), translate("Encrypt Method"))
o = s:option(Value, "decryption", translate("Encrypt Method"))
o.default = "none"
o:depends({ [_n("protocol")] = "vless" })
o:depends({ protocol = "vless" })
o = s:option(ListValue, _n("ss_method"), translate("Encrypt Method"))
o.rewrite_option = "method"
o = s:option(ListValue, "ss_method", translate("Encrypt Method"))
for a, t in ipairs(ss_method_list) do o:value(t) end
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ protocol = "shadowsocks" })
o = s:option(Value, _n("ss_password"), translate("Password"))
o:depends({ [_n("protocol")] = "shadowsocks" })
o = s:option(Value, "ss_password", translate("Password"))
o:depends({ protocol = "shadowsocks" })
o = s:option(ListValue, _n("flow"), translate("flow"))
o = s:option(ListValue, "flow", translate("flow"))
o.default = ""
o:value("", translate("Disable"))
o:value("xtls-rprx-vision")
o:depends({ [_n("protocol")] = "vless" , [_n("tls")] = true })
o:depends({ protocol = "vless" , tls = true })
o = s:option(Flag, _n("tls"), translate("TLS"))
o = s:option(Flag, "tls", translate("TLS"))
o.default = 0
o.validate = function(self, value, t)
if value then
local reality = s.fields[_n("reality")] and s.fields[_n("reality")]:formvalue(t) or nil
local reality = s.fields["reality"] and s.fields["reality"]:formvalue(t) or nil
if reality and reality == "1" then return value end
if value == "1" then
local ca = s.fields[_n("tls_certificateFile")] and s.fields[_n("tls_certificateFile")]:formvalue(t) or ""
local key = s.fields[_n("tls_keyFile")] and s.fields[_n("tls_keyFile")]:formvalue(t) or ""
local ca = s.fields["tls_certificateFile"] and s.fields["tls_certificateFile"]:formvalue(t) or ""
local key = s.fields["tls_keyFile"] and s.fields["tls_keyFile"]:formvalue(t) or ""
if ca == "" or key == "" then
return nil, translate("Public key and Private key path can not be empty!")
end
@@ -236,40 +231,40 @@ o.validate = function(self, value, t)
return value
end
end
o:depends({ [_n("protocol")] = "http" })
o:depends({ [_n("protocol")] = "vmess" })
o:depends({ [_n("protocol")] = "vless" })
o:depends({ [_n("protocol")] = "trojan" })
o:depends({ [_n("protocol")] = "anytls" })
o:depends({ protocol = "http" })
o:depends({ protocol = "vmess" })
o:depends({ protocol = "vless" })
o:depends({ protocol = "trojan" })
o:depends({ protocol = "anytls" })
-- https://github.com/SagerNet/sing-box/commit/d2a04c4e41e6cef0937331cb6d10211f431caaab
if singbox_tags:find("with_utls") then
-- [[ REALITY ]] --
o = s:option(Flag, _n("reality"), translate("REALITY"))
o = s:option(Flag, "reality", translate("REALITY"))
o.default = 0
o:depends({ [_n("protocol")] = "http", [_n("tls")] = true })
o:depends({ [_n("protocol")] = "vmess", [_n("tls")] = true })
o:depends({ [_n("protocol")] = "vless", [_n("tls")] = true })
o:depends({ [_n("protocol")] = "trojan", [_n("tls")] = true })
o:depends({ [_n("protocol")] = "anytls", [_n("tls")] = true })
o:depends({ protocol = "http", tls = true })
o:depends({ protocol = "vmess", tls = true })
o:depends({ protocol = "vless", tls = true })
o:depends({ protocol = "trojan", tls = true })
o:depends({ protocol = "anytls", tls = true })
o = s:option(Value, _n("reality_private_key"), translate("Private Key"))
o:depends({ [_n("reality")] = true })
o = s:option(Value, "reality_private_key", translate("Private Key"))
o:depends({ reality = true })
o = s:option(Value, _n("reality_shortId"), translate("Short Id"))
o:depends({ [_n("reality")] = true })
o = s:option(Value, "reality_shortId", translate("Short Id"))
o:depends({ reality = true })
o = s:option(Value, _n("reality_handshake_server"), translate("Handshake Server"))
o = s:option(Value, "reality_handshake_server", translate("Handshake Server"))
o.default = "google.com"
o:depends({ [_n("reality")] = true })
o:depends({ reality = true })
o = s:option(Value, _n("reality_handshake_server_port"), translate("Handshake Server Port"))
o = s:option(Value, "reality_handshake_server_port", translate("Handshake Server Port"))
o.datatype = "port"
o.default = "443"
o:depends({ [_n("reality")] = true })
o:depends({ reality = true })
end
o = s:option(ListValue, _n("alpn"), translate("ALPN"))
o = s:option(ListValue, "alpn", translate("ALPN"))
o.default = "default"
o:value("default", translate("Default"))
o:value("h3")
@@ -278,22 +273,22 @@ o:value("h3,h2")
o:value("http/1.1")
o:value("h2,http/1.1")
o:value("h3,h2,http/1.1")
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ [_n("protocol")] = "hysteria" })
o:depends({ tls = true, reality = false })
o:depends({ protocol = "hysteria" })
-- [[ TLS ]] --
o = s:option(FileUpload, _n("tls_certificateFile"), translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
o = s:option(FileUpload, "tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ [_n("protocol")] = "naive" })
o:depends({ [_n("protocol")] = "hysteria" })
o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ tls = true, reality = false })
o:depends({ protocol = "naive" })
o:depends({ protocol = "hysteria" })
o:depends({ protocol = "tuic" })
o:depends({ protocol = "hysteria2" })
o.validate = function(self, value, t)
if value and value ~= "" then
if not fs.access(value) then
if not api.fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
@@ -302,17 +297,17 @@ o.validate = function(self, value, t)
return nil
end
o = s:option(FileUpload, _n("tls_keyFile"), translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
o = s:option(FileUpload, "tls_keyFile", translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ [_n("protocol")] = "naive" })
o:depends({ [_n("protocol")] = "hysteria" })
o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ tls = true, reality = false })
o:depends({ protocol = "naive" })
o:depends({ protocol = "hysteria" })
o:depends({ protocol = "tuic" })
o:depends({ protocol = "hysteria2" })
o.validate = function(self, value, t)
if value and value ~= "" then
if not fs.access(value) then
if not api.fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
@@ -321,19 +316,19 @@ o.validate = function(self, value, t)
return nil
end
o = s:option(Flag, _n("ech"), translate("ECH"))
o = s:option(Flag, "ech", translate("ECH"))
o.default = "0"
o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false })
o:depends({ [_n("protocol")] = "naive" })
o:depends({ [_n("protocol")] = "hysteria" })
o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o:depends({ tls = true, flow = "", reality = false })
o:depends({ protocol = "naive" })
o:depends({ protocol = "hysteria" })
o:depends({ protocol = "tuic" })
o:depends({ protocol = "hysteria2", hysteria2_realms = false })
o = s:option(TextValue, _n("ech_key"), translate("ECH Key"))
o = s:option(TextValue, "ech_key", translate("ECH Key"))
o.default = ""
o.rows = 5
o.wrap = "off"
o:depends({ [_n("ech")] = true })
o:depends({ ech = true })
o.validate = function(self, value)
value = value:gsub("^%s+", ""):gsub("%s+$","\n"):gsub("\r\n","\n"):gsub("[ \t]*\n[ \t]*", "\n")
value = value:gsub("^%s*\n", "")
@@ -343,100 +338,100 @@ o.validate = function(self, value)
return value
end
o = s:option(ListValue, _n("transport"), translate("Transport"))
o = s:option(ListValue, "transport", translate("Transport"))
o:value("tcp", "TCP")
o:value("http", "HTTP")
o:value("ws", "WebSocket")
o:value("httpupgrade", "HTTPUpgrade")
o:value("quic", "QUIC")
o:value("grpc", "gRPC")
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "vmess" })
o:depends({ [_n("protocol")] = "vless" })
o:depends({ [_n("protocol")] = "trojan" })
o:depends({ protocol = "shadowsocks" })
o:depends({ protocol = "vmess" })
o:depends({ protocol = "vless" })
o:depends({ protocol = "trojan" })
-- [[ HTTP ]]--
o = s:option(DynamicList, _n("http_host"), translate("HTTP Host"))
o:depends({ [_n("transport")] = "http" })
o = s:option(DynamicList, "http_host", translate("HTTP Host"))
o:depends({ transport = "http" })
o = s:option(Value, _n("http_path"), translate("HTTP Path"))
o:depends({ [_n("transport")] = "http" })
o = s:option(Value, "http_path", translate("HTTP Path"))
o:depends({ transport = "http" })
-- [[ WebSocket ]]--
o = s:option(Value, _n("ws_host"), translate("WebSocket Host"))
o:depends({ [_n("transport")] = "ws" })
o = s:option(Value, "ws_host", translate("WebSocket Host"))
o:depends({ transport = "ws" })
o = s:option(Value, _n("ws_path"), translate("WebSocket Path"))
o:depends({ [_n("transport")] = "ws" })
o = s:option(Value, "ws_path", translate("WebSocket Path"))
o:depends({ transport = "ws" })
-- [[ HTTPUpgrade ]]--
o = s:option(Value, _n("httpupgrade_host"), translate("HTTPUpgrade Host"))
o:depends({ [_n("transport")] = "httpupgrade" })
o = s:option(Value, "httpupgrade_host", translate("HTTPUpgrade Host"))
o:depends({ transport = "httpupgrade" })
o = s:option(Value, _n("httpupgrade_path"), translate("HTTPUpgrade Path"))
o:depends({ [_n("transport")] = "httpupgrade" })
o = s:option(Value, "httpupgrade_path", translate("HTTPUpgrade Path"))
o:depends({ transport = "httpupgrade" })
-- [[ gRPC ]]--
o = s:option(Value, _n("grpc_serviceName"), "ServiceName")
o:depends({ [_n("transport")] = "grpc" })
o = s:option(Value, "grpc_serviceName", "ServiceName")
o:depends({ transport = "grpc" })
-- [[ Mux ]]--
o = s:option(Flag, _n("mux"), translate("Mux"))
o = s:option(Flag, "mux", translate("Mux"))
o.rmempty = false
o:depends({ [_n("protocol")] = "vmess" })
o:depends({ [_n("protocol")] = "vless", [_n("flow")] = "" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "trojan" })
o:depends({ protocol = "vmess" })
o:depends({ protocol = "vless", flow = "" })
o:depends({ protocol = "shadowsocks" })
o:depends({ protocol = "trojan" })
-- [[ TCP Brutal ]]--
o = s:option(Flag, _n("tcpbrutal"), translate("TCP Brutal"))
o = s:option(Flag, "tcpbrutal", translate("TCP Brutal"))
o.default = 0
o:depends({ [_n("mux")] = true })
o:depends({ mux = true })
o = s:option(Value, _n("tcpbrutal_up_mbps"), translate("Max upload Mbps"))
o = s:option(Value, "tcpbrutal_up_mbps", translate("Max upload Mbps"))
o.default = "10"
o:depends({ [_n("tcpbrutal")] = true })
o:depends({ tcpbrutal = true })
o = s:option(Value, _n("tcpbrutal_down_mbps"), translate("Max download Mbps"))
o = s:option(Value, "tcpbrutal_down_mbps", translate("Max download Mbps"))
o.default = "50"
o:depends({ [_n("tcpbrutal")] = true })
o:depends({ tcpbrutal = true })
if singbox_tags:find("with_wireguard") then
o = s:option(Flag, _n("wireguard_system_interface"), translate("System interface"))
o = s:option(Flag, "wireguard_system_interface", translate("System interface"))
o.default = 0
o:depends({ [_n("protocol")] = "wireguard" })
o:depends({ protocol = "wireguard" })
o = s:option(Value, _n("wireguard_mtu"), "MTU")
o = s:option(Value, "wireguard_mtu", "MTU")
o.default = "1408"
o:depends({ [_n("protocol")] = "wireguard" })
o:depends({ protocol = "wireguard" })
o = s:option(DynamicList, _n("wireguard_local_address"), translate("Local Address"))
o:depends({ [_n("protocol")] = "wireguard" })
o = s:option(DynamicList, "wireguard_local_address", translate("Local Address"))
o:depends({ protocol = "wireguard" })
o = s:option(Value, _n("wireguard_private_key"), translate("Private Key"))
o = s:option(Value, "wireguard_private_key", translate("Private Key"))
o.datatype = "base64"
o:depends({ [_n("protocol")] = "wireguard" })
o:depends({ protocol = "wireguard" })
o = s:option(Value, _n("wireguard_public_key"), translate("Public Key"))
o = s:option(Value, "wireguard_public_key", translate("Public Key"))
o.datatype = "base64"
o:depends({ [_n("protocol")] = "wireguard" })
o:depends({ protocol = "wireguard" })
o = s:option(DummyValue, _n("gen_wireguard_key"))
o = s:option(DummyValue, "gen_wireguard_key")
o.prefix = option_prefix
o.template = m:template_path("/server/server_wireguard")
o:depends({ [_n("protocol")] = "wireguard" })
o:depends({ protocol = "wireguard" })
end
o = s:option(Flag, _n("bind_local"), translate("Bind Local"), translate("When selected, it can only be accessed localhost."))
o = s:option(Flag, "bind_local", translate("Bind Local"), translate("When selected, it can only be accessed localhost."))
o.default = "0"
o:depends({ [_n("custom")] = false })
o:depends({ custom = false })
o = s:option(Flag, _n("accept_lan"), translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!"))
o = s:option(Flag, "accept_lan", translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!"))
o.default = "0"
o:depends({ [_n("custom")] = false })
o:depends({ custom = false })
local nodes_table = {}
for k, e in ipairs(api.get_valid_nodes()) do
@@ -449,7 +444,7 @@ for k, e in ipairs(api.get_valid_nodes()) do
end
end
o = s:option(ListValue, _n("outbound_node"), translate("outbound node"))
o = s:option(ListValue, "outbound_node", translate("outbound node"))
o:value("", translate("Close"))
o:value("_socks", translate("Custom Socks"))
o:value("_http", translate("Custom HTTP"))
@@ -460,37 +455,37 @@ for k, v in pairs(nodes_table) do
o:value(v.id, v.remarks)
o.group[#o.group+1] = (v.group and v.group ~= "") and v.group or translate("default")
end
o:depends({ [_n("custom")] = false })
o:depends({ custom = false })
o = s:option(Value, _n("outbound_node_address"), translate("Address (Support Domain Name)"))
o:depends({ [_n("outbound_node")] = "_socks" })
o:depends({ [_n("outbound_node")] = "_http" })
o = s:option(Value, "outbound_node_address", translate("Address (Support Domain Name)"))
o:depends({ outbound_node = "_socks" })
o:depends({ outbound_node = "_http" })
o = s:option(Value, _n("outbound_node_port"), translate("Port"))
o = s:option(Value, "outbound_node_port", translate("Port"))
o.datatype = "port"
o:depends({ [_n("outbound_node")] = "_socks" })
o:depends({ [_n("outbound_node")] = "_http" })
o:depends({ outbound_node = "_socks" })
o:depends({ outbound_node = "_http" })
o = s:option(Value, _n("outbound_node_username"), translate("Username"))
o:depends({ [_n("outbound_node")] = "_socks" })
o:depends({ [_n("outbound_node")] = "_http" })
o = s:option(Value, "outbound_node_username", translate("Username"))
o:depends({ outbound_node = "_socks" })
o:depends({ outbound_node = "_http" })
o = s:option(Value, _n("outbound_node_password"), translate("Password"))
o = s:option(Value, "outbound_node_password", translate("Password"))
o.password = true
o:depends({ [_n("outbound_node")] = "_socks" })
o:depends({ [_n("outbound_node")] = "_http" })
o:depends({ outbound_node = "_socks" })
o:depends({ outbound_node = "_http" })
o = s:option(Value, _n("outbound_node_iface"), translate("Interface"))
o:depends({ [_n("outbound_node")] = "_iface" })
o = s:option(Value, "outbound_node_iface", translate("Interface"))
o:depends({ outbound_node = "_iface" })
local netdev_list = api.get_network_devices()
for _, d in ipairs(netdev_list) do
o:value(d.name, d.label)
end
o = s:option(TextValue, _n("custom_config"), translate("Custom Config") .. " (JSON)")
o = s:option(TextValue, "custom_config", translate("Custom Config") .. " (JSON)")
o.rows = 10
o.wrap = "off"
o:depends({ [_n("custom")] = true })
o:depends({ custom = true })
o.datatype = "json"
local o_validate = o.validate
o.validate = function(self, value)
@@ -508,16 +503,16 @@ o.custom_write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "")
end
o = s:option(Flag, _n("log"), translate("Log"))
o = s:option(Flag, "log", translate("Log"))
o.default = "1"
o.rmempty = false
o = s:option(ListValue, _n("loglevel"), translate("Log Level"))
o = s:option(ListValue, "loglevel", translate("Log Level"))
o.default = "info"
o:value("debug")
o:value("info")
o:value("warn")
o:value("error")
o:depends({ [_n("log")] = true })
o:depends({ log = true })
api.luci_types(arg[1], m, s, type_name, option_prefix)
api.luci_types(s1, s)
@@ -1,24 +1,20 @@
local m, s = ...
if not api.is_finded("ssserver") then
return
end
-- [[ Shadowsocks Rust ]]
local m, s1 = ...
local type_name = "SS-Rust"
-- [[ Shadowsocks Rust ]]
s1.fields["type"]:value(type_name, translate("Shadowsocks Rust"))
s.fields["type"]:value(type_name, translate("Shadowsocks Rust"))
if s.val["type"] and s.val["type"] ~= type_name then
if s1.val["type"] and s1.val["type"] ~= type_name then
return
end
local option_prefix = "ssrust_"
local function _n(name)
return option_prefix .. name
end
local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "ssrust_"
local ssrust_encrypt_method_list = {
"plain", "none",
@@ -26,35 +22,12 @@ local ssrust_encrypt_method_list = {
"2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha8-poly1305", "2022-blake3-chacha20-poly1305"
}
o = s:option(Flag, _n("custom"), translate("Use Custom Config"))
o = s:option(Flag, "custom", translate("Use Custom Config"))
o = s:option(Value, _n("port"), translate("Listen Port"))
o.datatype = "port"
o:depends({ [_n("custom")] = false })
o = s:option(ListValue, _n("user"), translate("User"))
for i, v in ipairs(user_list) do
o:value(v[".name"], v.username)
end
o:depends({ [_n("custom")] = false })
o = s:option(ListValue, _n("method"), translate("Encrypt Method"))
for a, t in ipairs(ssrust_encrypt_method_list) do o:value(t) end
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("timeout"), translate("Connection Timeout"))
o.datatype = "uinteger"
o.default = 300
o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"))
o.default = "0"
o:depends({ [_n("custom")] = false })
o = s:option(TextValue, _n("custom_config"), translate("Custom Config") .. " (JSON)")
o = s:option(TextValue, "custom_config", translate("Custom Config") .. " (JSON)")
o.rows = 10
o.wrap = "off"
o:depends({ [_n("custom")] = true })
o:depends({ custom = true })
o.datatype = "json"
local o_validate = o.validate
o.validate = function(self, value)
@@ -72,8 +45,31 @@ o.custom_write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "")
end
o = s:option(Flag, _n("log"), translate("Log"))
o = s:option(Value, "port", translate("Listen Port"))
o.datatype = "port"
o:depends({ custom = false })
o = s:option(ListValue, "user", translate("User"))
for i, v in ipairs(user_list) do
o:value(v[".name"], v.username)
end
o:depends({ custom = false })
o = s:option(ListValue, "method", translate("Encrypt Method"))
for a, t in ipairs(ssrust_encrypt_method_list) do o:value(t) end
o:depends({ custom = false })
o = s:option(Value, "timeout", translate("Connection Timeout"))
o.datatype = "uinteger"
o.default = 300
o:depends({ custom = false })
o = s:option(Flag, "tcp_fast_open", "TCP " .. translate("Fast Open"))
o.default = "0"
o:depends({ custom = false })
o = s:option(Flag, "log", translate("Log"))
o.default = "1"
o.rmempty = false
api.luci_types(arg[1], m, s, type_name, option_prefix)
api.luci_types(s1, s)
@@ -1,24 +1,20 @@
local m, s = ...
if not api.is_finded("ssr-server") then
return
end
-- [[ ShadowsocksR ]]
local m, s1 = ...
local type_name = "SSR"
-- [[ ShadowsocksR ]]
s1.fields["type"]:value(type_name, translate("ShadowsocksR"))
s.fields["type"]:value(type_name, translate("ShadowsocksR"))
if s.val["type"] and s.val["type"] ~= type_name then
if s1.val["type"] and s1.val["type"] ~= type_name then
return
end
local option_prefix = "ssr_"
local function _n(name)
return option_prefix .. name
end
local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "ssr_"
local ssr_encrypt_method_list = {
"none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb",
@@ -39,49 +35,12 @@ local ssr_obfs_list = {
"tls1.0_session_auth", "tls1.2_ticket_auth"
}
o = s:option(Flag, _n("custom"), translate("Use Custom Config"))
o = s:option(Flag, "custom", translate("Use Custom Config"))
o = s:option(Value, _n("port"), translate("Listen Port"))
o.datatype = "port"
o:depends({ [_n("custom")] = false })
o = s:option(ListValue, _n("user"), translate("User"))
for i, v in ipairs(user_list) do
o:value(v[".name"], v.username)
end
o:depends({ [_n("custom")] = false })
o = s:option(ListValue, _n("method"), translate("Encrypt Method"))
for a, t in ipairs(ssr_encrypt_method_list) do o:value(t) end
o:depends({ [_n("custom")] = false })
o = s:option(ListValue, _n("protocol"), translate("Protocol"))
for a, t in ipairs(ssr_protocol_list) do o:value(t) end
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("protocol_param"), translate("Protocol_param"))
o:depends({ [_n("custom")] = false })
o = s:option(ListValue, _n("obfs"), translate("Obfs"))
for a, t in ipairs(ssr_obfs_list) do o:value(t) end
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("obfs_param"), translate("Obfs_param"))
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("timeout"), translate("Connection Timeout"))
o.datatype = "uinteger"
o.default = 300
o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"))
o.default = "0"
o:depends({ [_n("custom")] = false })
o = s:option(TextValue, _n("custom_config"), translate("Custom Config") .. " (JSON)")
o = s:option(TextValue, "custom_config", translate("Custom Config") .. " (JSON)")
o.rows = 10
o.wrap = "off"
o:depends({ [_n("custom")] = true })
o:depends({ custom = true })
o.datatype = "json"
local o_validate = o.validate
o.validate = function(self, value)
@@ -99,12 +58,49 @@ o.custom_write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "")
end
o = s:option(Flag, _n("udp_forward"), translate("UDP Forward"))
o = s:option(Value, "port", translate("Listen Port"))
o.datatype = "port"
o:depends({ custom = false })
o = s:option(ListValue, "user", translate("User"))
for i, v in ipairs(user_list) do
o:value(v[".name"], v.username)
end
o:depends({ custom = false })
o = s:option(ListValue, "method", translate("Encrypt Method"))
for a, t in ipairs(ssr_encrypt_method_list) do o:value(t) end
o:depends({ custom = false })
o = s:option(ListValue, "protocol", translate("Protocol"))
for a, t in ipairs(ssr_protocol_list) do o:value(t) end
o:depends({ custom = false })
o = s:option(Value, "protocol_param", translate("Protocol_param"))
o:depends({ custom = false })
o = s:option(ListValue, "obfs", translate("Obfs"))
for a, t in ipairs(ssr_obfs_list) do o:value(t) end
o:depends({ custom = false })
o = s:option(Value, "obfs_param", translate("Obfs_param"))
o:depends({ custom = false })
o = s:option(Value, "timeout", translate("Connection Timeout"))
o.datatype = "uinteger"
o.default = 300
o:depends({ custom = false })
o = s:option(Flag, "tcp_fast_open", "TCP " .. translate("Fast Open"))
o.default = "0"
o:depends({ custom = false })
o = s:option(Flag, "udp_forward", translate("UDP Forward"))
o.default = "1"
o.rmempty = false
o = s:option(Flag, _n("log"), translate("Log"))
o = s:option(Flag, "log", translate("Log"))
o.default = "1"
o.rmempty = false
api.luci_types(arg[1], m, s, type_name, option_prefix)
api.luci_types(s1, s)
+114 -107
View File
@@ -1,3 +1,6 @@
-- Copyright (C) 2022-2025 xiaorouji
-- Copyright (C) 2026 Openwrt-Passwall Organization
module("luci.passwall2.api", package.seeall)
appname = "passwall2"
c_config = "passwall2"
@@ -17,10 +20,11 @@ command_timeout = 300
OPENWRT_ARCH = nil
DISTRIB_ARCH = nil
LOCK_PREFIX = "/tmp/lock/passwall2"
LOG_FILE = "/tmp/log/passwall2.log"
TMP_PATH = "/tmp/etc/passwall2"
LOCK_PREFIX = "/tmp/lock/" .. c_config
LOG_FILE = "/tmp/log/" .. c_config .. ".log"
TMP_PATH = "/tmp/etc/" .. c_config
CACHE_PATH = TMP_PATH .. "_tmp"
S_TMP_PATH = "/tmp/etc/" .. s_config
TMP_IFACE_PATH = TMP_PATH .. "/iface"
local lang = uci:get("luci", "main", "lang") or "auto"
@@ -814,16 +818,16 @@ function clone(org)
end
local function get_bin_version_cache(file, cmd)
sys.call("mkdir -p /tmp/etc/passwall2_tmp")
sys.call("mkdir -p " .. CACHE_PATH)
if fs.access(file) then
chmod_755(file)
local md5 = sys.exec("echo -n $(md5sum " .. file .. " | awk '{print $1}')")
if fs.access("/tmp/etc/passwall2_tmp/" .. md5) then
return sys.exec("echo -n $(cat /tmp/etc/passwall2_tmp/%s)" % md5)
if fs.access(CACHE_PATH .. "/" .. md5) then
return sys.exec("echo -n $(cat %s)" % { CACHE_PATH .. "/" .. md5 })
else
local version = sys.exec(string.format("echo -n $(%s %s)", file, cmd))
if version and version ~= "" then
sys.call("echo '" .. version .. "' > " .. "/tmp/etc/passwall2_tmp/" .. md5)
sys.call("echo '%s' > %s" % { version, CACHE_PATH .. "/" .. md5})
return version
end
end
@@ -1415,12 +1419,24 @@ function set_default_cbi()
if true then
--Map
local Map = cbi.Map
local original_init = Map.__init__
local default_init = Map.__init__
function Map.__init__(self, config, ...)
if not config then config = c_config end
original_init(self, config, ...)
default_init(self, config, ...)
self.api = require "luci.passwall2.api"
end
if is_js_luci() == true then
local default_parse = Map.parse
function Map.parse(self, ...)
apply_redirect(self)
local old = self.on_after_save
self.on_after_save = function(self)
if old then old(self) end
self:set("@global[0]", "timestamp", os.time())
end
return default_parse(self, ...)
end
end
function Map.foreach(self, stype, func)
self.uci:foreach(self.config, stype, func)
end
@@ -1459,25 +1475,6 @@ function set_default_cbi()
sh_uci_set(c_config, "@global[0]", "auto_lang", lang, true)
end
end
if is_js_luci() == true then
local hide_popup_box = nil
if hide_popup_box == true then
Map.apply_on_parse = false
Map.on_after_apply = function(self)
if self.redirect then
os.execute("sleep 1")
luci.http.redirect(self.redirect)
end
end
else
apply_redirect(Map)
local old = Map.on_after_save
Map.on_after_save = function(self)
if old then old(self) end
self:set("@global[0]", "timestamp", os.time())
end
end
end
end
if true then
--AbstractSection
@@ -1498,9 +1495,9 @@ function set_default_cbi()
if true then
--TextValue
local TextValue = cbi.TextValue
local original_init = TextValue.__init__
local default_init = TextValue.__init__
function TextValue.__init__(self, ...)
original_init(self, ...)
default_init(self, ...)
self.template = appname .. "/cbi/tvalue"
end
end
@@ -1543,102 +1540,112 @@ function return_map(map)
return map
end
function luci_types(id, m, s, type_name, option_prefix)
function luci_types(s, s2)
local cbi = require "luci.cbi"
local m = s.map
local id = s2.section
local type_name = s2.type_name
local option_prefix = s2.option_prefix
local fv_type
local field_type = s.fields["type"]
if field_type then
fv_type = field_type:formvalue(id)
end
local rewrite_option_table = {}
for key, value in pairs(s.fields) do
if key:find(option_prefix) == 1 then
if not s.fields[key].not_rewrite then
if s.fields[key].rewrite_option then
if not rewrite_option_table[s.fields[key].rewrite_option] then
rewrite_option_table[s.fields[key].rewrite_option] = 1
for i, v in ipairs(s2.children) do
local o = s2.children[i]
o.config_option = o.option
o.option = option_prefix .. o.option
if not o.not_rewrite then
o.cfgvalue = function(self, section)
-- Add a custom `custom_cfgvalue` attribute. If a custom `custom_cfgvalue` function exists, the custom `cfgvalue` logic will be used.
if self.custom_cfgvalue then
return self:custom_cfgvalue(section)
else
if self.rewrite_option then
return m:get(section, self.rewrite_option)
else
rewrite_option_table[s.fields[key].rewrite_option] = rewrite_option_table[s.fields[key].rewrite_option] + 1
return m:get(section, self.config_option)
end
end
s.fields[key].cfgvalue = function(self, section)
-- Add a custom `custom_cfgvalue` attribute. If a custom `custom_cfgvalue` function exists, the custom `cfgvalue` logic will be used.
if self.custom_cfgvalue then
return self:custom_cfgvalue(section)
end
o.write = function(self, section, value)
if s.fields["type"]:formvalue(id) == type_name then
-- Add a custom `custom_write` attribute; if a custom `custom_write` function exists, then use the custom write logic.
if self.custom_write then
self:custom_write(section, value)
else
local new_val = value
if util.instanceof(self, cbi.DynamicList) then
local new_t = {}
if type(value) == "table" then
new_t = table_remove_duplicates(value)
else
new_t = { value }
end
if self.cast == "string" then
new_val = table.concat(new_t, " ")
else
new_val = new_t
end
end
if self.rewrite_option then
m:set(section, self.rewrite_option, new_val)
else
m:set(section, self.config_option, new_val)
end
end
end
end
o.remove = function(self, section)
if s.fields["type"]:formvalue(id) == type_name then
-- Add a custom `custom_remove` attribute; if a custom `custom_remove` function exists, use the custom remove logic.
if self.custom_remove then
self:custom_remove(section)
else
if self.rewrite_option then
return m:get(section, self.rewrite_option)
m:del(section, self.rewrite_option)
else
if self.option:find(option_prefix) == 1 then
return m:get(section, self.option:sub(1 + #option_prefix))
end
m:del(section, self.config_option)
end
end
end
s.fields[key].write = function(self, section, value)
if s.fields["type"]:formvalue(id) == type_name then
-- Add a custom `custom_write` attribute; if a custom `custom_write` function exists, then use the custom write logic.
if self.custom_write then
self:custom_write(section, value)
else
local new_val = value
if util.instanceof(self, cbi.DynamicList) then
local new_t = {}
if type(value) == "table" then
new_t = table_remove_duplicates(value)
else
new_t = { value }
end
if self.cast == "string" then
new_val = table.concat(new_t, " ")
else
new_val = new_t
end
end
if self.rewrite_option then
m:set(section, self.rewrite_option, new_val)
else
if self.option:find(option_prefix) == 1 then
m:set(section, self.option:sub(1 + #option_prefix), new_val)
end
end
end
end
end
s.fields[key].remove = function(self, section)
if s.fields["type"]:formvalue(id) == type_name then
-- Add a custom `custom_remove` attribute; if a custom `custom_remove` function exists, use the custom remove logic.
if self.custom_remove then
self:custom_remove(section)
else
if self.rewrite_option and rewrite_option_table[self.rewrite_option] == 1 then
m:del(section, self.rewrite_option)
else
if self.option:find(option_prefix) == 1 then
m:del(section, self.option:sub(1 + #option_prefix))
end
end
end
end
end
end
local deps = s.fields[key].deps
if #deps > 0 then
for index, value in ipairs(deps) do
deps[index]["type"] = type_name
end
else
s.fields[key]:depends({ type = type_name })
end
if fv_type and fv_type ~= type_name then
s.fields[key].rmempty = true
end
end
local deps = o.deps
if #deps > 0 then
local function process_deps(dep)
local rewrite_deps = {}
for k, v in pairs(dep) do
if k:find("!") then
rewrite_deps[k] = v
else
rewrite_deps[option_prefix .. k] = v
end
end
if not rewrite_deps['!reverse'] then
rewrite_deps["type"] = type_name
end
return rewrite_deps
end
for index, value in ipairs(deps) do
local rewrite_deps = process_deps(value)
if rewrite_deps then
deps[index] = rewrite_deps
end
end
else
o:depends({ type = type_name })
end
if fv_type and fv_type ~= type_name then
o.rmempty = true
end
s:append(o)
end
end
function format_go_time(input, default)
input = input and trim(input)
local N = 0
@@ -7,7 +7,7 @@ local uci = api.uci
local jsonc = api.jsonc
local CONFIG = api.s_config
local CONFIG_PATH = "/tmp/etc/" .. CONFIG
local CONFIG_PATH = api.S_TMP_PATH
local NFT_INCLUDE_FILE = CONFIG_PATH .. "/" .. CONFIG .. ".nft"
local LOG_APP_FILE = "/tmp/log/" .. CONFIG .. ".log"
local TMP_BIN_PATH = CONFIG_PATH .. "/bin"
@@ -53,7 +53,7 @@ local function ln_run(s, d, command, output)
end
local function gen_include()
cmd(string.format("echo '#!/bin/sh' > /tmp/etc/%s.include", CONFIG))
cmd(string.format("echo '#!/bin/sh' > %s.include", CONFIG_PATH))
local function extract_rules(n, a)
local _ipt = ipt_bin
if n == "6" then
@@ -64,7 +64,7 @@ local function gen_include()
result = result .. "COMMIT"
return result
end
local f, err = io.open("/tmp/etc/" .. CONFIG .. ".include", "a")
local f, err = io.open(CONFIG_PATH .. ".include", "a")
if f and err == nil then
if nft_flag == "0" then
f:write(ipt_bin .. '-save -c | grep -v "PSW2-SERVER" | ' .. ipt_bin .. '-restore -c' .. "\n")
@@ -232,7 +232,7 @@ local function stop()
cmd("nft flush chain inet fw4 PSW2-SERVER 2>/dev/null")
cmd("nft delete chain inet fw4 PSW2-SERVER 2>/dev/null")
end
cmd(string.format("rm -rf %s %s /tmp/etc/%s.include", CONFIG_PATH, LOG_APP_FILE, CONFIG))
cmd(string.format("rm -rf %s %s %s", CONFIG_PATH, LOG_APP_FILE, CONFIG_PATH .. ".include"))
end
if action then
@@ -18,8 +18,8 @@ function gen_config_server(node)
end
local config = {
listen = (function()
if node.hysteria2_realms and node.hysteria2_realm_url then
local url = node.hysteria2_realm_url:gsub("/+$", "")
if node.realms and node.realm_url then
local url = node.realm_url:gsub("/+$", "")
if node.port then
url = url .. (url:find("?") and "&lport=" or "?lport=") .. node.port
end
@@ -31,30 +31,30 @@ function gen_config_server(node)
cert = node.tls_certificateFile,
key = node.tls_keyFile,
},
obfs = (node.hysteria2_obfs_type and node.hysteria2_obfs_password) and {
type = node.hysteria2_obfs_type,
[node.hysteria2_obfs_type] = {
password = node.hysteria2_obfs_password
obfs = (node.obfs_type and node.obfs_password) and {
type = node.obfs_type,
[node.obfs_type] = {
password = node.obfs_password
}
} or nil,
auth = users and {
type = "userpass",
userpass = users
} or nil,
bandwidth = (node.hysteria2_up_mbps or node.hysteria2_down_mbps) and {
up = node.hysteria2_up_mbps and node.hysteria2_up_mbps .. " mbps" or nil,
down = node.hysteria2_down_mbps and node.hysteria2_down_mbps .. " mbps" or nil
bandwidth = (node.up_mbps or node.down_mbps) and {
up = node.up_mbps and node.up_mbps .. " mbps" or nil,
down = node.down_mbps and node.down_mbps .. " mbps" or nil
} or nil,
ignoreClientBandwidth = (node.hysteria2_ignoreClientBandwidth == "1") and true or false,
disableUDP = (node.hysteria2_udp == "0") and true or false,
realm = (node.hysteria2_realms and node.hysteria2_realm_stun) and {
stunServers = node.hysteria2_realm_stun
ignoreClientBandwidth = (node.ignoreClientBandwidth == "1") and true or false,
disableUDP = (node.udp == "0") and true or false,
realm = (node.realms and node.realm_stun) and {
stunServers = node.realm_stun
} or nil
}
if config.obfs and config.obfs.gecko then
local min = tonumber(node.hysteria2_obfs_MinPacketSize) or 512
local max = tonumber(node.hysteria2_obfs_MaxPacketSize) or 1200
local min = tonumber(node.obfs_MinPacketSize) or 512
local max = tonumber(node.obfs_MaxPacketSize) or 1200
if min <= 0 or min > max or max > 2048 then
min = 512
max = 1200
@@ -402,7 +402,7 @@ function gen_outbound(flag, node, tag, proxy_table)
if node.protocol == "shadowsocks" then
protocol_table = {
method = node.method or nil,
method = node.ss_method or nil,
password = node.password or "",
plugin = (node.plugin_enabled and node.plugin) or nil,
plugin_opts = (node.plugin_enabled and node.plugin_opts) or nil,
@@ -416,7 +416,7 @@ function gen_outbound(flag, node, tag, proxy_table)
if node.protocol == "shadowsocksr" then
protocol_table = {
method = node.method or nil,
method = node.ssr_method or nil,
password = node.password or "",
obfs = node.ssr_obfs,
obfs_param = node.ssr_obfs_param,
@@ -865,7 +865,7 @@ function gen_config_server(node)
if node.protocol == "shadowsocks" then
protocol_table = {
method = node.method,
method = node.ss_method,
password = node.ss_password,
users = users,
multiplex = mux,
@@ -388,8 +388,8 @@ function gen_outbound(flag, node, tag, proxy_table)
user = (node.protocol == "socks" or node.protocol == "http") and node.username or nil,
pass = (node.protocol == "socks" or node.protocol == "http") and node.password or nil,
password = (node.protocol == "shadowsocks" or node.protocol == "trojan") and node.password or nil,
method = (node.protocol == "shadowsocks") and ((node.method == "chacha20-ietf-poly1305" and "chacha20-poly1305") or
(node.method == "xchacha20-ietf-poly1305" and "xchacha20-poly1305") or (node.method ~= "" and node.method) or nil) or nil,
method = (node.protocol == "shadowsocks") and ((node.ss_method == "chacha20-ietf-poly1305" and "chacha20-poly1305") or
(node.ss_method == "xchacha20-ietf-poly1305" and "xchacha20-poly1305") or (node.ss_method ~= "" and node.ss_method) or nil) or nil,
secretKey = (node.protocol == "wireguard") and node.wireguard_secret_key or nil,
peers = (node.protocol == "wireguard") and {
{
@@ -523,6 +523,12 @@ function gen_config_server(node)
u.email = user.username
u.auth = user.password
end
if node.protocol == "wireguard" then
u.publicKey = user.wireguard_public_key
u.preSharedKey = user.wireguard_pre_shared_key
u.keepAlive = 0
u.allowedIPs = user.allowed_ips
end
users[#users + 1] = u
end
end
@@ -553,7 +559,7 @@ function gen_config_server(node)
node.tcp_guise = "none"
elseif node.protocol == "shadowsocks" then
settings = {
method = node.method,
method = node.ss_method,
password = node.ss_password,
users = users,
network = node.ss_network or "tcp,udp"
@@ -575,6 +581,14 @@ function gen_config_server(node)
address = node.d_address,
port = tonumber(node.d_port)
}
elseif node.protocol == "wireguard" then
settings = {
secretKey = node.wireguard_private_key,
--address = node.wireguard_local_address,
--noKernelTun = node.wireguard_system_interface ~= "1" and true or false,
mtu = tonumber(node.wireguard_mtu or 1420),
peers = users
}
end
if node.fallback and node.fallback == "1" then
@@ -1120,7 +1120,7 @@ acl_app() {
}
start() {
busybox pgrep -f /tmp/etc/passwall2/bin > /dev/null 2>&1 && {
busybox pgrep -f ${TMP_PATH}/bin > /dev/null 2>&1 && {
logger -t PW2-RESTART "Upgrade or overload residue is detected, and the subprocess is being called to perform complete cleaning..."
(stop)
sleep 2
@@ -81,10 +81,10 @@ test_node() {
local curlx="socks5h://127.0.0.1:${_tmp_port}"
local _proxy_status=$(test_url "${probe_url}" ${retry_num} ${connect_timeout} "-x $curlx" 1)
# Kill the SS plugin process
local pid_file="/tmp/etc/${CONFIG}/test_node_${node_id}_plugin.pid"
local pid_file="${TMP_PATH}/test_node_${node_id}_plugin.pid"
[ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1
busybox pgrep -af "test_node_${node_id}" | awk '! /socks_auto_switch\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
rm -rf /tmp/etc/${CONFIG}/test_node_${node_id}*.*
rm -rf ${TMP_PATH}/test_node_${node_id}*.*
if [ "${_proxy_status}" -eq 200 ]; then
return 0
fi
@@ -53,10 +53,10 @@ url_test_node() {
local probeUrl=$(config_t_get global_other url_test_url https://www.google.com/generate_204)
result=$(curl --connect-timeout 3 --max-time 5 -o /dev/null -I -skL -w "%{http_code}:%{time_pretransfer}" -x ${curlx} "${probeUrl}")
# End the SS plugin process
local pid_file="/tmp/etc/${CONFIG}/url_test_${node_id}_plugin.pid"
local pid_file="${TMP_PATH}/url_test_${node_id}_plugin.pid"
[ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1
busybox pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
rm -rf /tmp/etc/${CONFIG}/*url_test_${node_id}*.*
rm -rf ${TMP_PATH}/*url_test_${node_id}*.*
}
echo $result
}