🐤 Sync 2025-11-15 00:12:24

This commit is contained in:
actions-user
2025-11-15 00:12:24 +08:00
parent 001b5f14c3
commit 7d627be079
51 changed files with 3355 additions and 616 deletions

View File

@@ -10,7 +10,7 @@ LUCI_DEPENDS:=+luci-base +luci-lib-jsonc +curl +bandix
PKG_MAINTAINER:=timsaya
PKG_VERSION:=0.8.0
PKG_VERSION:=0.8.1
PKG_RELEASE:=1
include $(TOPDIR)/feeds/luci/luci.mk

View File

@@ -570,11 +570,11 @@ function formatDnsServer(query) {
function formatResponseResult(query) {
if (!query) return { display: [], full: [] };
// 显示响应IPresponse_ips它是一个字符串数组
if (query.response_ips && Array.isArray(query.response_ips) && query.response_ips.length > 0) {
// 显示响应记录response_records它是一个字符串数组
if (query.response_records && Array.isArray(query.response_records) && query.response_records.length > 0) {
var maxDisplay = 5; // 最多显示5条
var displayRecords = query.response_ips.slice(0, maxDisplay);
var fullRecords = query.response_ips;
var displayRecords = query.response_records.slice(0, maxDisplay);
var fullRecords = query.response_records;
return {
display: displayRecords,
@@ -1113,20 +1113,25 @@ return view.extend({
]),
E('div', { 'class': 'stats-card' }, [
E('div', { 'class': 'stats-card-title' }, getTranslation('响应时间', language)),
E('div', { 'class': 'stats-card-value', 'id': 'stat-avg-response-time', 'style': 'margin-bottom: 12px;' }, [
E('span', {}, (stats.avg_response_time_ms || 0).toFixed(1)),
E('div', { 'class': 'stats-card-value', 'id': 'stat-latest-response-time', 'style': 'margin-bottom: 12px;' }, [
E('span', {}, (stats.latest_response_time_ms || 0).toFixed(1)),
E('span', { 'class': 'stats-card-unit' }, ' ' + getTranslation('毫秒', language))
]),
E('div', { 'class': 'stats-card-details', 'id': 'stat-response-time-details' }, [
E('div', { 'class': 'stats-detail-row' }, [
E('span', { 'class': 'stats-detail-label' }, getTranslation('平均响应时间', language) + ':'),
E('span', { 'class': 'stats-detail-value', 'id': 'stat-avg-response-time' },
(stats.avg_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language))
]),
E('div', { 'class': 'stats-detail-row' }, [
E('span', { 'class': 'stats-detail-label' }, getTranslation('最快响应时间', language) + ':'),
E('span', { 'class': 'stats-detail-value', 'id': 'stat-min-response-time' },
(stats.min_response_time_ms || 0) + ' ' + getTranslation('毫秒', language))
(stats.min_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language))
]),
E('div', { 'class': 'stats-detail-row' }, [
E('span', { 'class': 'stats-detail-label' }, getTranslation('最慢响应时间', language) + ':'),
E('span', { 'class': 'stats-detail-value', 'id': 'stat-max-response-time' },
(stats.max_response_time_ms || 0) + ' ' + getTranslation('毫秒', language))
(stats.max_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language))
])
])
]),
@@ -1157,19 +1162,24 @@ return view.extend({
totalQueriesEl.textContent = stats.total_queries || 0;
}
var latestResponseTimeEl = document.getElementById('stat-latest-response-time');
if (latestResponseTimeEl && latestResponseTimeEl.firstChild) {
latestResponseTimeEl.firstChild.textContent = (stats.latest_response_time_ms || 0).toFixed(1);
}
var avgResponseTimeEl = document.getElementById('stat-avg-response-time');
if (avgResponseTimeEl && avgResponseTimeEl.firstChild) {
avgResponseTimeEl.firstChild.textContent = (stats.avg_response_time_ms || 0).toFixed(1);
if (avgResponseTimeEl) {
avgResponseTimeEl.textContent = (stats.avg_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language);
}
var minResponseTimeEl = document.getElementById('stat-min-response-time');
if (minResponseTimeEl) {
minResponseTimeEl.textContent = (stats.min_response_time_ms || 0) + ' ' + getTranslation('毫秒', language);
minResponseTimeEl.textContent = (stats.min_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language);
}
var maxResponseTimeEl = document.getElementById('stat-max-response-time');
if (maxResponseTimeEl) {
maxResponseTimeEl.textContent = (stats.max_response_time_ms || 0) + ' ' + getTranslation('毫秒', language);
maxResponseTimeEl.textContent = (stats.max_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language);
}
var successRateEl = document.getElementById('stat-success-rate');

View File

@@ -41,6 +41,8 @@ const translations = {
'明亮模式': '明亮模式',
'暗黑模式': '暗黑模式',
'意见反馈': '意见反馈',
'日志级别': '日志级别',
'设置 Bandix 服务的日志级别': '设置 Bandix 服务的日志级别',
'离线超时时间': '离线超时时间',
'设置设备离线判断的超时时间(秒)': '设置设备离线判断的超时时间(秒)。超过此时间未活动的设备将被标记为离线',
'历史流量周期': '历史流量周期',
@@ -100,6 +102,8 @@ const translations = {
'明亮模式': '明亮模式',
'暗黑模式': '暗黑模式',
'意见反馈': '意見反饋',
'日志级别': '日誌級別',
'设置 Bandix 服务的日志级别': '設置 Bandix 服務的日誌級別',
'离线超时时间': '離線超時時間',
'设置设备离线判断的超时时间(秒)': '設定設備離線判斷的超時時間(秒)。超過此時間未活動的設備將被標記為離線',
'历史流量周期': '歷史流量週期',
@@ -160,6 +164,8 @@ const translations = {
'明亮模式': 'Light Mode',
'暗黑模式': 'Dark Mode',
'意见反馈': 'Feedback',
'日志级别': 'Log Level',
'设置 Bandix 服务的日志级别': 'Set the log level for Bandix service',
'离线超时时间': 'Offline Timeout',
'设置设备离线判断的超时时间(秒)': 'Set the timeout for device offline detection (seconds). Devices inactive for longer than this time will be marked as offline',
'历史流量周期': 'Traffic History Period',
@@ -220,6 +226,8 @@ const translations = {
'明亮模式': 'Mode Clair',
'暗黑模式': 'Mode Sombre',
'意见反馈': 'Commentaires',
'日志级别': 'Niveau de Journal',
'设置 Bandix 服务的日志级别': 'Définir le niveau de journal pour le service Bandix',
'离线超时时间': 'Délai d\'expiration hors ligne',
'设置设备离线判断的超时时间(秒)': 'Définir le délai d\'expiration pour la détection hors ligne des appareils (secondes). Les appareils inactifs plus longtemps que cette durée seront marqués comme hors ligne',
'历史流量周期': 'Période d\'Historique du Trafic',
@@ -280,6 +288,8 @@ const translations = {
'明亮模式': 'ライトモード',
'暗黑模式': 'ダークモード',
'意见反馈': 'フィードバック',
'日志级别': 'ログレベル',
'设置 Bandix 服务的日志级别': 'Bandix サービスのログレベルを設定',
'离线超时时间': 'オフラインタイムアウト',
'设置设备离线判断的超时时间(秒)': 'デバイスのオフライン検出のタイムアウト時間(秒)を設定。この時間を超えて非アクティブなデバイスはオフラインとしてマークされます',
'历史流量周期': 'トラフィック履歴期間',
@@ -340,6 +350,8 @@ const translations = {
'明亮模式': 'Светлый Режим',
'暗黑模式': 'Темный Режим',
'意见反馈': 'Обратная связь',
'日志级别': 'Уровень Журналирования',
'设置 Bandix 服务的日志级别': 'Установить уровень журналирования для службы Bandix',
'离线超时时间': 'Таймаут отключения',
'设置设备离线判断的超时时间(秒)': 'Установить таймаут для обнаружения отключения устройств (секунды). Устройства, неактивные дольше этого времени, будут помечены как отключенные',
'历史流量周期': 'Период Истории Трафика',
@@ -591,13 +603,14 @@ return view.extend({
s.description = getTranslation('配置 Bandix 服务的基本参数', language);
s.addremove = false;
// 添加端口设置选项
o = s.option(form.Value, 'port', getTranslation('端口', language),
getTranslation('Bandix 服务监听的端口', language));
o.default = '8686';
o.datatype = 'port';
o.placeholder = '8686';
o.rmempty = false;
// 添加端口设置选项
o = s.option(form.Value, 'port', getTranslation('端口', language),
getTranslation('Bandix 服务监听的端口', language));
o.default = '8686';
o.datatype = 'port';
o.placeholder = '8686';
o.rmempty = false;
// 添加网卡选择下拉菜单
o = s.option(form.ListValue, 'iface', getTranslation('监控网卡', language),
@@ -631,6 +644,18 @@ return view.extend({
o.default = 'auto';
o.rmempty = false;
// 添加日志级别选择选项
o = s.option(form.ListValue, 'log_level', getTranslation('日志级别', language),
getTranslation('设置 Bandix 服务的日志级别', language));
o.value('trace', 'Trace');
o.value('debug', 'Debug');
o.value('info', 'Info');
o.value('warn', 'Warn');
o.value('error', 'Error');
o.default = 'info';
o.rmempty = false;
// 添加数据目录设置(只读)
o = s.option(form.DummyValue, 'data_dir', getTranslation('数据目录', language));
o.default = '/usr/share/bandix';

View File

@@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=25.11.1
PKG_VERSION:=25.11.14
PKG_RELEASE:=1
PKG_PO_VERSION:=$(PKG_VERSION)

View File

@@ -109,9 +109,11 @@ end
--check excluded domain
local function check_excluded_domain(value)
for k,v in ipairs(excluded_domain) do
if value:find(v) then
return true
value = value and value:lower() or ""
for _, domain in ipairs(excluded_domain) do
local pattern = "^[a-z0-9-]+%.(" .. domain .. ")$"
if value:match(pattern) then
return true
end
end
end

View File

@@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2
PKG_VERSION:=25.11.2
PKG_VERSION:=25.11.14
PKG_RELEASE:=1
PKG_PO_VERSION:=$(PKG_VERSION)

View File

@@ -129,7 +129,7 @@ function hide_menu()
end
function link_add_node()
-- 分片接收以突破uhttpd的限制
-- Fragmented reception to overcome uhttpd limitations
local tmp_file = "/tmp/links.conf"
local chunk = http.formvalue("chunk")
local chunk_index = tonumber(http.formvalue("chunk_index"))
@@ -137,7 +137,7 @@ function link_add_node()
local group = http.formvalue("group") or "default"
if chunk and chunk_index ~= nil and total_chunks ~= nil then
-- 按顺序拼接到文件
-- Assemble the files in order
local mode = "a"
if chunk_index == 0 then
mode = "w"
@@ -147,7 +147,7 @@ function link_add_node()
f:write(chunk)
f:close()
end
-- 如果是最后一片,才执行
-- If it's the last piece, then it will be executed.
if chunk_index + 1 == total_chunks then
luci.sys.call("lua /usr/share/passwall2/subscribe.lua add " .. group)
end
@@ -624,7 +624,7 @@ function restore_backup()
fp:close()
if chunk_index + 1 == total_chunks then
api.sys.call("echo '' > /tmp/log/passwall2.log")
api.log(" * PassWall2 配置文件上传成功")
api.log(string.format(" * PassWall2 %s", i18n.translate("Configuration file uploaded successfully")))
local temp_dir = '/tmp/passwall2_bak'
api.sys.call("mkdir -p " .. temp_dir)
if api.sys.call("tar -xzf " .. file_path .. " -C " .. temp_dir) == 0 then
@@ -634,13 +634,13 @@ function restore_backup()
api.sys.call("cp -f " .. temp_file .. " " .. backup_file)
end
end
api.log(" * PassWall2 配置还原成功")
api.log(" * 重启 PassWall2 服务中…\n")
api.log(string.format(" * PassWall2 %s", i18n.translate("Configuration restored successfully")))
api.log(string.format(" * PassWall2 %s", i18n.translate("Service restarting…")))
luci.sys.call('/etc/init.d/passwall2 restart > /dev/null 2>&1 &')
luci.sys.call('/etc/init.d/passwall2_server restart > /dev/null 2>&1 &')
result = { status = "success", message = "Upload completed", path = file_path }
else
api.log(" * PassWall2 配置文件解压失败,请重试!")
api.log(string.format(" * PassWall2 %s", i18n.translate("Configuration file decompression failed, please try again!")))
result = { status = "error", message = "Decompression failed" }
end
api.sys.call("rm -rf " .. temp_dir)
@@ -746,7 +746,7 @@ function subscribe_manual_all()
end
local section_list = util.split(sections, ",")
local url_list = util.split(urls, ",")
-- 检查是否存在未保存配置
-- Check if there are any unsaved configurations.
for i, section in ipairs(section_list) do
local uci_url = api.sh_uci_get(appname, section, "url")
if not uci_url or uci_url == "" then
@@ -754,7 +754,7 @@ function subscribe_manual_all()
return
end
end
-- 保存有变动的url
-- Save URLs that have changed.
for i, section in ipairs(section_list) do
local current_url = url_list[i] or ""
local uci_url = api.sh_uci_get(appname, section, "url")

View File

@@ -40,7 +40,7 @@ m.uci:foreach(appname, "socks", function(s)
if s.enabled == "1" and s.node then
socks_list[#socks_list + 1] = {
id = "Socks_" .. s[".name"],
remark = translate("Socks Config") .. " [" .. s.port .. "端口]"
remark = translate("Socks Config") .. " [" .. s.port .. translate("Port") .. "]"
}
end
end)
@@ -84,7 +84,7 @@ o.rmempty = false
o = s:taboption("Main", ListValue, "node", "<a style='color: red'>" .. translate("Node") .. "</a>")
o:value("", translate("Close"))
-- 分流
-- Shunt
if (has_singbox or has_xray) and #nodes_table > 0 then
local function get_cfgvalue(shunt_node_id, option)
return function(self, section)
@@ -152,7 +152,7 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
if (has_singbox and has_xray) or (v.type == "sing-box" and not has_singbox) or (v.type == "Xray" and not has_xray) then
type:depends("node", v.id)
else
type:depends({ __hide = true }) --不存在的依赖,即始终隐藏
type:depends({ __hide = true }) -- Always hidden.
end
m.uci:foreach(appname, "shunt_rules", function(e)

View File

@@ -36,6 +36,21 @@ end)
for k, v in pairs(groups) do
o:value(k)
end
o.write = function(self, section, value)
value = api.trim(value)
local lower = value:lower()
if lower == "" or lower == "default" then
return m:del(section, self.option)
end
for _, v in ipairs(self.keylist or {}) do
if v:lower() == lower then
return m:set(section, self.option, v)
end
end
m:set(section, self.option, value)
end
local fs = require "nixio.fs"
local types_dir = "/usr/lib/lua/luci/model/cbi/passwall2/client/type/"

View File

@@ -168,19 +168,33 @@ end
o = s:option(Value, "remark", translate("Remarks"))
o.width = "auto"
o.rmempty = false
o.validate = function(self, value, t)
if value then
local count = 0
m.uci:foreach(appname, "subscribe_list", function(e)
if e[".name"] ~= t and e["remark"] == value then
count = count + 1
o.validate = function(self, value, section)
value = api.trim(value)
if value == "" then
return nil, translate("Remark cannot be empty.")
end
local duplicate = false
m.uci:foreach(appname, "subscribe_list", function(e)
if e[".name"] ~= section and e["remark"] and e["remark"]:lower() == value:lower() then
duplicate = true
return false
end
end)
if duplicate or value:lower() == "default" then
return nil, translate("This remark already exists, please change a new remark.")
end
return value
end
o.write = function(self, section, value)
local old = m:get(section, self.option) or ""
if old:lower() ~= value:lower() then
m.uci:foreach(appname, "nodes", function(e)
if e["group"] and e["group"]:lower() == old:lower() then
m.uci:set(appname, e[".name"], "group", value)
end
end)
if count > 0 then
return nil, translate("This remark already exists, please change a new remark.")
end
return value
end
return Value.write(self, section, value)
end
o = s:option(DummyValue, "_node_count", translate("Subscribe Info"))

View File

@@ -74,13 +74,41 @@ end
o = s:option(Value, "remark", translate("Subscribe Remark"))
o.rmempty = false
o.validate = function(self, value, section)
value = api.trim(value)
if value == "" then
return nil, translate("Remark cannot be empty.")
end
local duplicate = false
m.uci:foreach(appname, "subscribe_list", function(e)
if e[".name"] ~= section and e["remark"] and e["remark"]:lower() == value:lower() then
duplicate = true
return false
end
end)
if duplicate or value:lower() == "default" then
return nil, translate("This remark already exists, please change a new remark.")
end
return value
end
o.write = function(self, section, value)
local old = m:get(section, self.option) or ""
if old:lower() ~= value:lower() then
m.uci:foreach(appname, "nodes", function(e)
if e["group"] and e["group"]:lower() == old:lower() then
m.uci:set(appname, e[".name"], "group", value)
end
end)
end
return Value.write(self, section, value)
end
o = s:option(TextValue, "url", translate("Subscribe URL"))
o.rows = 5
o.rmempty = false
o.validate = function(self, value)
if not value or value == "" then
return nil, translate("URL cannot be empty")
return nil, translate("URL cannot be empty.")
end
return value:gsub("%s+", ""):gsub("%z", "")
end

View File

@@ -119,7 +119,7 @@ if (os.execute("lsmod | grep -i REDIRECT >/dev/null") == 0 and os.execute("lsmod
o:value("tproxy", "TPROXY")
o:depends("ipv6_tproxy", false)
o.remove = function(self, section)
-- 禁止在隐藏时删除
-- Do not delete while hidden
end
o = s:option(ListValue, "_tcp_proxy_way", translate("TCP Proxy Way"))

View File

@@ -27,8 +27,8 @@ div[id^="cbid.passwall2."] .cbi-checkbox {
]]
function clean_text(text)
local nbsp = string.char(0xC2, 0xA0) -- 不间断空格(U+00A0
local fullwidth_space = string.char(0xE3, 0x80, 0x80) -- 全角空格(U+3000
local nbsp = string.char(0xC2, 0xA0) -- Non-breaking space (U+00A0)
local fullwidth_space = string.char(0xE3, 0x80, 0x80) -- Full-width space (U+3000)
return text
:gsub("\t", " ")
:gsub(nbsp, " ")

View File

@@ -95,12 +95,12 @@ m.uci:foreach(appname, "socks", function(s)
if s.enabled == "1" and s.node then
socks_list[#socks_list + 1] = {
id = "Socks_" .. s[".name"],
remark = translate("Socks Config") .. " [" .. s.port .. "端口]"
remark = translate("Socks Config") .. " [" .. s.port .. translate("Port") .. "]"
}
end
end)
-- 负载均衡列表
-- Load balancing node list
o = s:option(DynamicList, _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("protocol")] = "_balancing" })
local valid_ids = {}
@@ -108,7 +108,7 @@ for k, v in pairs(nodes_table) do
o:value(v.id, v.remark)
valid_ids[v.id] = true
end
-- 去重并禁止自定义非法输入
-- Deduplication and disabling of custom and illegal input
function o.custom_write(self, section, value)
local result = {}
if type(value) == "table" then
@@ -145,14 +145,13 @@ local function check_fallback_chain(fb)
end
end
end
-- 检查fallback链去掉会形成闭环的balancer节点
-- Check the fallback chain and remove the balancer node that would form a closed loop.
if is_balancer then
check_fallback_chain(arg[1])
end
for k, v in pairs(fallback_table) do o:value(v.id, v.remark) end
for k, v in pairs(nodes_table) do o:value(v.id, v.remark) 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" })
@@ -167,7 +166,6 @@ o:value("https://connectivitycheck.platform.hicloud.com/generate_204", "HiCloud
o.default = "https://www.google.com/generate_204"
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.default = "1m"
@@ -184,7 +182,7 @@ o.placeholder = "2"
o.description = translate("The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them.")
-- [[ 分流模块 ]]
-- [[ Shunt Start ]]
if #nodes_table > 0 then
o = s:option(Flag, _n("preproxy_enabled"), translate("Preproxy"))
o:depends({ [_n("protocol")] = "_shunt" })
@@ -285,7 +283,7 @@ o:value("hybrid")
o:value("linear")
o:depends({ [_n("protocol")] = "_shunt" })
-- [[ 分流模块 End ]]
-- [[ Shunt End ]]
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)"))
@@ -412,7 +410,7 @@ o:value("half")
o:value("full")
o:depends({ [_n("ech")] = true })
-- [[ REALITY部分 ]] --
-- [[ REALITY ]] --
o = s:option(Value, _n("reality_publicKey"), translate("Public Key"))
o:depends({ [_n("tls")] = true, [_n("reality")] = true })
@@ -493,24 +491,21 @@ o = s:option(Value, _n("wireguard_keepAlive"), translate("Keep Alive"))
o.default = "0"
o:depends({ [_n("protocol")] = "wireguard" })
-- [[ RAW部分 ]]--
-- [[ RAW ]]--
-- TCP伪装
o = s:option(ListValue, _n("tcp_guise"), translate("Camouflage Type"))
o:value("none", "none")
o:value("http", "http")
o:depends({ [_n("transport")] = "raw" })
-- HTTP域名
o = s:option(DynamicList, _n("tcp_guise_http_host"), translate("HTTP Host"))
o:depends({ [_n("tcp_guise")] = "http" })
-- HTTP路径
o = s:option(DynamicList, _n("tcp_guise_http_path"), translate("HTTP Path"))
o.placeholder = "/"
o:depends({ [_n("tcp_guise")] = "http" })
-- [[ mKCP部分 ]]--
-- [[ 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.'))
for a, t in ipairs(header_type_list) do o:value(t) end
@@ -549,7 +544,7 @@ o:depends({ [_n("transport")] = "mkcp" })
o = s:option(Value, _n("mkcp_seed"), translate("KCP Seed"))
o:depends({ [_n("transport")] = "mkcp" })
-- [[ WebSocket部分 ]]--
-- [[ WebSocket ]]--
o = s:option(Value, _n("ws_host"), translate("WebSocket Host"))
o:depends({ [_n("transport")] = "ws" })
@@ -561,7 +556,7 @@ o = s:option(Value, _n("ws_heartbeatPeriod"), translate("HeartbeatPeriod(second)
o.datatype = "integer"
o:depends({ [_n("transport")] = "ws" })
-- [[ gRPC部分 ]]--
-- [[ gRPC ]]--
o = s:option(Value, _n("grpc_serviceName"), "ServiceName")
o:depends({ [_n("transport")] = "grpc" })
@@ -589,7 +584,7 @@ o = s:option(Value, _n("grpc_initial_windows_size"), translate("Initial Windows
o.default = "0"
o:depends({ [_n("transport")] = "grpc" })
-- [[ HttpUpgrade部分 ]]--
-- [[ HttpUpgrade ]]--
o = s:option(Value, _n("httpupgrade_host"), translate("HttpUpgrade Host"))
o:depends({ [_n("transport")] = "httpupgrade" })
@@ -597,7 +592,7 @@ o = s:option(Value, _n("httpupgrade_path"), translate("HttpUpgrade Path"))
o.placeholder = "/"
o:depends({ [_n("transport")] = "httpupgrade" })
-- [[ XHTTP部分 ]]--
-- [[ XHTTP ]]--
o = s:option(ListValue, _n("xhttp_mode"), "XHTTP " .. translate("Mode"))
o:depends({ [_n("transport")] = "xhttp" })
o.default = "auto"

View File

@@ -105,7 +105,7 @@ m.uci:foreach(appname, "socks", function(s)
if s.enabled == "1" and s.node then
socks_list[#socks_list + 1] = {
id = "Socks_" .. s[".name"],
remark = translate("Socks Config") .. " [" .. s.port .. "端口]"
remark = translate("Socks Config") .. " [" .. s.port .. translate("Port") .. "]"
}
end
end)
@@ -118,7 +118,7 @@ for k, v in pairs(nodes_table) do
o:value(v.id, v.remark)
valid_ids[v.id] = true
end
-- 去重并禁止自定义非法输入
-- Deduplication and disabling of custom and illegal input
function o.custom_write(self, section, value)
local result = {}
if type(value) == "table" then
@@ -174,7 +174,7 @@ o:depends({ [_n("protocol")] = "_urltest" })
o.default = "0"
o.description = translate("Interrupt existing connections when the selected outbound has changed.")
-- [[ 分流模块 ]]
-- [[ Shunt Start ]]
if #nodes_table > 0 then
o = s:option(Flag, _n("preproxy_enabled"), translate("Preproxy"))
o:depends({ [_n("protocol")] = "_shunt" })
@@ -259,7 +259,7 @@ if #nodes_table > 0 then
end
end
-- [[ 分流模块 End ]]
-- [[ Shunt End ]]
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)"))
@@ -587,7 +587,7 @@ if singbox_tags:find("with_utls") then
o.default = "chrome"
o:depends({ [_n("utls")] = true })
-- [[ REALITY部分 ]] --
-- [[ REALITY ]] --
o = s:option(Flag, _n("reality"), translate("REALITY"))
o.default = 0
o:depends({ [_n("protocol")] = "vless", [_n("tls")] = true })
@@ -650,7 +650,7 @@ if singbox_tags:find("with_wireguard") then
o:depends({ [_n("protocol")] = "wireguard" })
end
-- [[ TCP部分(模拟) ]]--
-- [[ TCP ]]--
o = s:option(ListValue, _n("tcp_guise"), translate("Camouflage Type"))
o:value("none", "none")
o:value("http", "http")
@@ -663,7 +663,7 @@ o = s:option(DynamicList, _n("tcp_guise_http_path"), translate("HTTP Path"))
o.placeholder = "/"
o:depends({ [_n("tcp_guise")] = "http" })
-- [[ HTTP部分 ]]--
-- [[ HTTP ]]--
o = s:option(DynamicList, _n("http_host"), translate("HTTP Host"))
o:depends({ [_n("transport")] = "http" })
@@ -682,7 +682,7 @@ o = s:option(Value, _n("http_h2_health_check_timeout"), translate("Health check
o.default = "15"
o:depends({ [_n("tls")] = true, [_n("transport")] = "http", [_n("http_h2_health_check")] = true })
-- [[ WebSocket部分 ]]--
-- [[ WebSocket ]]--
o = s:option(Value, _n("ws_host"), translate("WebSocket Host"))
o:depends({ [_n("transport")] = "ws" })
@@ -700,7 +700,7 @@ o:depends({ [_n("ws_enableEarlyData")] = true })
o = s:option(Value, _n("ws_earlyDataHeaderName"), translate("Early data header name"), translate("Recommended value: Sec-WebSocket-Protocol"))
o:depends({ [_n("ws_enableEarlyData")] = true })
-- [[ HTTPUpgrade部分 ]]--
-- [[ HTTPUpgrade ]]--
o = s:option(Value, _n("httpupgrade_host"), translate("HTTPUpgrade Host"))
o:depends({ [_n("transport")] = "httpupgrade" })
@@ -708,7 +708,7 @@ o = s:option(Value, _n("httpupgrade_path"), translate("HTTPUpgrade Path"))
o.placeholder = "/"
o:depends({ [_n("transport")] = "httpupgrade" })
-- [[ gRPC部分 ]]--
-- [[ gRPC ]]--
o = s:option(Value, _n("grpc_serviceName"), "ServiceName")
o:depends({ [_n("transport")] = "grpc" })

View File

@@ -147,7 +147,7 @@ o:depends({ [_n("protocol")] = "socks" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "trojan" })
-- [[ REALITY部分 ]] --
-- [[ REALITY ]] --
o = s:option(Flag, _n("reality"), translate("REALITY"))
o.default = 0
o:depends({ [_n("tls")] = true })
@@ -211,7 +211,7 @@ end
-- o:value("1.3")
--o:depends({ [_n("tls")] = true })
-- [[ TLS部分 ]] --
-- [[ TLS ]] --
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
@@ -268,14 +268,14 @@ o:depends({ [_n("protocol")] = "socks" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "trojan" })
-- [[ WebSocket部分 ]]--
-- [[ WebSocket ]]--
o = s:option(Value, _n("ws_host"), translate("WebSocket Host"))
o:depends({ [_n("transport")] = "ws" })
o = s:option(Value, _n("ws_path"), translate("WebSocket Path"))
o:depends({ [_n("transport")] = "ws" })
-- [[ HttpUpgrade部分 ]]--
-- [[ HttpUpgrade ]]--
o = s:option(Value, _n("httpupgrade_host"), translate("HttpUpgrade Host"))
o:depends({ [_n("transport")] = "httpupgrade" })
@@ -283,7 +283,7 @@ o = s:option(Value, _n("httpupgrade_path"), translate("HttpUpgrade Path"))
o.placeholder = "/"
o:depends({ [_n("transport")] = "httpupgrade" })
-- [[ XHTTP部分 ]]--
-- [[ XHTTP ]]--
o = s:option(Value, _n("xhttp_host"), translate("XHTTP Host"))
o:depends({ [_n("transport")] = "xhttp" })
@@ -307,23 +307,20 @@ o = s:option(Value, _n("splithttp_maxconcurrentuploads"), translate("maxConcurre
o.default = "10"
o:depends({ [_n("transport")] = "splithttp" })
-- [[ TCP部分 ]]--
-- [[ TCP ]]--
-- TCP伪装
o = s:option(ListValue, _n("tcp_guise"), translate("Camouflage Type"))
o:value("none", "none")
o:value("http", "http")
o:depends({ [_n("transport")] = "raw" })
-- HTTP域名
o = s:option(DynamicList, _n("tcp_guise_http_host"), translate("HTTP Host"))
o:depends({ [_n("tcp_guise")] = "http" })
-- HTTP路径
o = s:option(DynamicList, _n("tcp_guise_http_path"), translate("HTTP Path"))
o:depends({ [_n("tcp_guise")] = "http" })
-- [[ mKCP部分 ]]--
-- [[ 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.'))
for a, t in ipairs(header_type_list) do o:value(t) end
@@ -362,7 +359,7 @@ o:depends({ [_n("transport")] = "mkcp" })
o = s:option(Value, _n("mkcp_seed"), translate("KCP Seed"))
o:depends({ [_n("transport")] = "mkcp" })
-- [[ gRPC部分 ]]--
-- [[ gRPC ]]--
o = s:option(Value, _n("grpc_serviceName"), "ServiceName")
o:depends({ [_n("transport")] = "grpc" })
@@ -370,7 +367,7 @@ o = s:option(Flag, _n("acceptProxyProtocol"), translate("acceptProxyProtocol"),
o.default = "0"
o:depends({ [_n("custom")] = false })
-- [[ Fallback部分 ]]--
-- [[ 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" })

View File

@@ -239,7 +239,7 @@ o:depends({ [_n("protocol")] = "anytls" })
-- https://github.com/SagerNet/sing-box/commit/d2a04c4e41e6cef0937331cb6d10211f431caaab
if singbox_tags:find("with_utls") then
-- [[ REALITY部分 ]] --
-- [[ REALITY ]] --
o = s:option(Flag, _n("reality"), translate("REALITY"))
o.default = 0
o:depends({ [_n("protocol")] = "http", [_n("tls")] = true })
@@ -264,7 +264,7 @@ if singbox_tags:find("with_utls") then
o:depends({ [_n("reality")] = true })
end
-- [[ TLS部分 ]] --
-- [[ TLS ]] --
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"
@@ -346,7 +346,7 @@ o:depends({ [_n("protocol")] = "vmess" })
o:depends({ [_n("protocol")] = "vless" })
o:depends({ [_n("protocol")] = "trojan" })
-- [[ HTTP部分 ]]--
-- [[ HTTP ]]--
o = s:option(DynamicList, _n("http_host"), translate("HTTP Host"))
o:depends({ [_n("transport")] = "http" })
@@ -354,7 +354,7 @@ o:depends({ [_n("transport")] = "http" })
o = s:option(Value, _n("http_path"), translate("HTTP Path"))
o:depends({ [_n("transport")] = "http" })
-- [[ WebSocket部分 ]]--
-- [[ WebSocket ]]--
o = s:option(Value, _n("ws_host"), translate("WebSocket Host"))
o:depends({ [_n("transport")] = "ws" })
@@ -362,7 +362,7 @@ o:depends({ [_n("transport")] = "ws" })
o = s:option(Value, _n("ws_path"), translate("WebSocket Path"))
o:depends({ [_n("transport")] = "ws" })
-- [[ HTTPUpgrade部分 ]]--
-- [[ HTTPUpgrade ]]--
o = s:option(Value, _n("httpupgrade_host"), translate("HTTPUpgrade Host"))
o:depends({ [_n("transport")] = "httpupgrade" })
@@ -370,7 +370,7 @@ o:depends({ [_n("transport")] = "httpupgrade" })
o = s:option(Value, _n("httpupgrade_path"), translate("HTTPUpgrade Path"))
o:depends({ [_n("transport")] = "httpupgrade" })
-- [[ gRPC部分 ]]--
-- [[ gRPC ]]--
o = s:option(Value, _n("grpc_serviceName"), "ServiceName")
o:depends({ [_n("transport")] = "grpc" })

View File

@@ -8,6 +8,11 @@ util = require "luci.util"
datatypes = require "luci.cbi.datatypes"
jsonc = require "luci.jsonc"
i18n = require "luci.i18n"
local lang = uci:get("luci", "main", "lang") or "auto"
if lang == "auto" then
lang = i18n.default
end
i18n.setlanguage(lang)
appname = "passwall2"
curl_args = { "-skfL", "--connect-timeout 3", "--retry 3" }
@@ -128,7 +133,7 @@ function base64Encode(text)
return result
end
--提取URL中的域名和端口(no ip)
-- Extract the domain name and port from the URL (no IP address).
function get_domain_port_from_url(url)
local scheme, domain, port = string.match(url, "^(https?)://([%w%.%-]+):?(%d*)")
if not domain then
@@ -141,7 +146,7 @@ function get_domain_port_from_url(url)
return domain, port
end
--解析域名
-- Domain resolution
function domainToIPv4(domain, dns)
local Dns = dns or "223.5.5.5"
local IPs = luci.sys.exec('nslookup %s %s | awk \'/^Name:/{getline; if ($1 == "Address:") print $2}\'' % { domain, Dns })
@@ -161,7 +166,7 @@ function curl_base(url, file, args)
end
function curl_proxy(url, file, args)
--使用代理
-- Use the proxy
local socks_server = get_cache_var("GLOBAL_SOCKS_server")
if socks_server and socks_server ~= "" then
if not args then args = {} end
@@ -181,7 +186,7 @@ function curl_logic(url, file, args)
end
function curl_direct(url, file, args)
--直连访问
-- Direct access
if not args then args = {} end
local tmp_args = clone(args)
local domain, port = get_domain_port_from_url(url)
@@ -223,16 +228,15 @@ function trim(text)
return text:match("^%s*(.-)%s*$")
end
-- 分割字符串
function split(full, sep)
if full then
full = full:gsub("%z", "") -- 这里不是很清楚 有时候结尾带个\0
full = full:gsub("%z", "") -- This is not very clear; sometimes it ends with a `\0`.
local off, result = 1, {}
while true do
local nStart, nEnd = full:find(sep, off)
if not nEnd then
local res = string.sub(full, off, string.len(full))
if #res > 0 then -- 过滤掉 \0
if #res > 0 then -- Filter out `\0`
table.insert(result, res)
end
break
@@ -1145,7 +1149,7 @@ end
function to_check_self()
local url = "https://raw.githubusercontent.com/xiaorouji/openwrt-passwall2/main/luci-app-passwall2/Makefile"
local tmp_file = "/tmp/passwall2_makefile"
local return_code, result = curl_logic(url, tmp_file, curl_args)
local return_code, result = curl_auto(url, tmp_file, curl_args)
result = return_code == 0
if not result then
exec("/bin/rm", {"-f", tmp_file})
@@ -1155,8 +1159,8 @@ function to_check_self()
}
end
local local_version = get_version()
local remote_version = sys.exec("echo -n $(grep 'PKG_VERSION' /tmp/passwall2_makefile|awk -F '=' '{print $2}')")
.. "-" .. sys.exec("echo -n $(grep 'PKG_RELEASE' /tmp/passwall2_makefile|awk -F '=' '{print $2}')")
local remote_version = sys.exec("echo -n $(grep '^PKG_VERSION' /tmp/passwall2_makefile | head -n 1 | awk -F '=' '{print $2}')")
exec("/bin/rm", {"-f", tmp_file})
local has_update = compare_versions(local_version, "<", remote_version)
if not has_update then
@@ -1224,7 +1228,7 @@ function set_apply_on_parse(map)
end
end
map.render = function(self, ...)
getmetatable(self).__index.render(self, ...) -- 保持原渲染流程
getmetatable(self).__index.render(self, ...) -- Maintain the original rendering process
optimize_cbi_ui()
end
end
@@ -1243,7 +1247,7 @@ function luci_types(id, m, s, type_name, option_prefix)
end
s.fields[key].cfgvalue = function(self, section)
-- 添加自定义 custom_cfgvalue 属性,如果有自定义的 custom_cfgvalue 函数,则使用自定义的 cfgvalue 逻辑
-- 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
@@ -1258,7 +1262,7 @@ function luci_types(id, m, s, type_name, option_prefix)
end
s.fields[key].write = function(self, section, value)
if s.fields["type"]:formvalue(id) == type_name then
-- 添加自定义 custom_write 属性,如果有自定义的 custom_write 函数,则使用自定义的 write 逻辑
-- 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
@@ -1274,7 +1278,7 @@ function luci_types(id, m, s, type_name, option_prefix)
end
s.fields[key].remove = function(self, section)
if s.fields["type"]:formvalue(id) == type_name then
-- 添加自定义 custom_remove 属性,如果有自定义的 custom_remove 函数,则使用自定义的 remove 逻辑
-- 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
@@ -1334,14 +1338,14 @@ end
function optimize_cbi_ui()
luci.http.write([[
<script type="text/javascript">
//修正上移、下移按钮名称
//Correct the names of the move up and move down buttons.
document.querySelectorAll("input.btn.cbi-button.cbi-button-up").forEach(function(btn) {
btn.value = "]] .. i18n.translate("Move up") .. [[";
});
document.querySelectorAll("input.btn.cbi-button.cbi-button-down").forEach(function(btn) {
btn.value = "]] .. i18n.translate("Move down") .. [[";
});
//删除控件和说明之间的多余换行
//Remove extra line breaks between controls and descriptions.
document.querySelectorAll("div.cbi-value-description").forEach(function(descDiv) {
var prev = descDiv.previousSibling;
while (prev && prev.nodeType === Node.TEXT_NODE && prev.textContent.trim() === "") {

View File

@@ -184,7 +184,7 @@ local function start()
f:write(jsonc.stringify(config, 1))
f:close()
end
log(string.format("%s 生成配置文件并运行 - %s", remarks, config_file))
log(string.format("%s %s - %s", remarks, api.i18n.translate("Generate configuration file and run"), config_file))
end
if bin then

View File

@@ -33,7 +33,7 @@ end
function gen_config(var)
local node_id = var["-node"]
if not node_id then
print("-node 不能为空")
print("-node Cannot be empty!")
return
end
local node = uci:get_all("passwall2", node_id)

View File

@@ -6,7 +6,7 @@ local jsonc = api.jsonc
function gen_config(var)
local node_id = var["-node"]
if not node_id then
print("-node 不能为空")
print("-node Cannot be empty!")
return
end
local node = uci:get_all("passwall2", node_id)

View File

@@ -33,7 +33,7 @@ local plugin_sh, plugin_bin
function gen_config(var)
local node_id = var["-node"]
if not node_id then
print("-node 不能为空")
print("-node Cannot be empty!")
return
end
local node = uci:get_all("passwall2", node_id)

View File

@@ -97,11 +97,10 @@ function gen_outbound(flag, node, tag, proxy_table)
end
tls = {
enabled = true,
disable_sni = (node.tls_disable_sni == "1") and true or false, --不要在 ClientHello 中发送服务器名称.
server_name = node.tls_serverName, --用于验证返回证书上的主机名,除非设置不安全。它还包含在 ClientHello 中以支持虚拟主机,除非它是 IP 地址。
insecure = (node.tls_allowInsecure == "1") and true or false, --接受任何服务器证书。
alpn = alpn, --支持的应用层协议协商列表,按优先顺序排列。如果两个对等点都支持 ALPN则选择的协议将是此列表中的一个如果没有相互支持的协议则连接将失败。
--min_version = "1.2",
disable_sni = (node.tls_disable_sni == "1") and true or false, -- Do not send the server name in ClientHello.
server_name = node.tls_serverName, -- Used to verify the hostname on the returned certificate, unless the settings are insecure. It is also included in ClientHello to support virtual hosts, unless it is an IP address.
insecure = (node.tls_allowInsecure == "1") and true or false, -- Accepts any server certificate.
alpn = alpn, -- A list of supported application layer protocols, arranged in order of priority. If both peers support ALPN, the protocol selected will be one of these protocols; otherwise, the connection will fail.
--max_version = "1.3",
fragment = fragment,
record_fragment = record_fragment,
@@ -142,7 +141,7 @@ function gen_outbound(flag, node, tag, proxy_table)
local v2ray_transport = nil
if node.transport == "tcp" and node.tcp_guise == "http" and (node.tcp_guise_http_host or "") ~= "" then --模拟xray raw(tcp)传输
if node.transport == "tcp" and node.tcp_guise == "http" and (node.tcp_guise_http_host or "") ~= "" then -- Simulate X-ray Raw (TCP) transmission
v2ray_transport = {
type = "http",
host = node.tcp_guise_http_host,
@@ -153,7 +152,7 @@ function gen_outbound(flag, node, tag, proxy_table)
idle_timeout = (node.http_h2_health_check == "1") and node.http_h2_read_idle_timeout or nil,
ping_timeout = (node.http_h2_health_check == "1") and node.http_h2_health_check_timeout or nil,
}
--不强制执行 TLS。如果未配置 TLS将使用纯 HTTP 1.1。
-- TLS is not enforced. If TLS is not configured, plain HTTP 1.1 will be used.
end
if node.transport == "http" then
@@ -164,7 +163,7 @@ function gen_outbound(flag, node, tag, proxy_table)
idle_timeout = (node.http_h2_health_check == "1") and node.http_h2_read_idle_timeout or nil,
ping_timeout = (node.http_h2_health_check == "1") and node.http_h2_health_check_timeout or nil,
}
--不强制执行 TLS。如果未配置 TLS将使用纯 HTTP 1.1。
-- TLS is not enforced. If TLS is not configured, plain HTTP 1.1 will be used.
end
if node.transport == "ws" then
@@ -173,7 +172,7 @@ function gen_outbound(flag, node, tag, proxy_table)
path = node.ws_path or "/",
headers = (node.ws_host ~= nil) and { Host = node.ws_host } or nil,
max_early_data = tonumber(node.ws_maxEarlyData) or nil,
early_data_header_name = (node.ws_earlyDataHeaderName) and node.ws_earlyDataHeaderName or nil --要与 Xray-core 兼容,请将其设置为 Sec-WebSocket-Protocol。它需要与服务器保持一致。
early_data_header_name = (node.ws_earlyDataHeaderName) and node.ws_earlyDataHeaderName or nil -- For compatibility with Xray-core, set it to Sec-WebSocket-Protocol. It needs to be consistent with the server.
}
end
@@ -189,7 +188,7 @@ function gen_outbound(flag, node, tag, proxy_table)
v2ray_transport = {
type = "quic"
}
--没有额外的加密支持: 它基本上是重复加密。 并且 Xray-core 在这里与 v2ray-core 不兼容。
-- There is no additional encryption support: it's essentially re-encryption. And Xray-core is incompatible with v2ray-core here.
end
if node.transport == "grpc" then
@@ -268,7 +267,7 @@ function gen_outbound(flag, node, tag, proxy_table)
global_padding = (node.global_padding == "1") and true or false,
authenticated_length = (node.authenticated_length == "1") and true or false,
tls = tls,
packet_encoding = "", --UDP 包编码。(空):禁用 packetaddr由 v2ray 5+ 支持 xudp xray 支持
packet_encoding = "", -- UDP packet encoding. (Empty): Disabled. packetaddr: Supported by v2ray 5+. xudp: Supported by xray.
multiplex = mux,
transport = v2ray_transport,
}
@@ -279,7 +278,7 @@ function gen_outbound(flag, node, tag, proxy_table)
uuid = node.uuid,
flow = (node.tls == '1' and node.flow) and node.flow or nil,
tls = tls,
packet_encoding = "xudp", --UDP 包编码。(空):禁用 packetaddr由 v2ray 5+ 支持 xudp xray 支持
packet_encoding = "xudp", -- UDP packet encoding. (Empty): Disabled. packetaddr: Supported by v2ray 5+. xudp: Supported by xray.
multiplex = mux,
transport = v2ray_transport,
}
@@ -529,7 +528,7 @@ function gen_config_server(node)
type = "ws",
path = node.ws_path or "/",
headers = (node.ws_host ~= nil) and { Host = node.ws_host } or nil,
early_data_header_name = (node.ws_earlyDataHeaderName) and node.ws_earlyDataHeaderName or nil --要与 Xray-core 兼容,请将其设置为 Sec-WebSocket-Protocol。它需要与服务器保持一致。
early_data_header_name = (node.ws_earlyDataHeaderName) and node.ws_earlyDataHeaderName or nil -- For compatibility with Xray-core, set it to Sec-WebSocket-Protocol. It needs to be consistent with the server.
}
end
@@ -545,7 +544,7 @@ function gen_config_server(node)
v2ray_transport = {
type = "quic"
}
--没有额外的加密支持: 它基本上是重复加密。 并且 Xray-core 在这里与 v2ray-core 不兼容。
-- There is no additional encryption support: it's essentially re-encryption. And Xray-core is incompatible with v2ray-core here.
end
if node.transport == "grpc" then
@@ -1474,9 +1473,9 @@ function gen_config(var)
servers = {},
rules = {},
disable_cache = (dns_cache and dns_cache == "0") and true or false,
disable_expire = false, --禁用 DNS 缓存过期。
independent_cache = false, --使每个 DNS 服务器的缓存独立,以满足特殊目的。如果启用,将轻微降低性能。
reverse_mapping = true, --在响应 DNS 查询后存储 IP 地址的反向映射以为路由目的提供域名。
disable_expire = false, -- Disable DNS cache expiration.
independent_cache = false, -- Make each DNS server's cache independent for specific purposes. If enabled, it will slightly reduce performance.
reverse_mapping = true, -- After responding to a DNS query, a reverse mapping of the IP address is stored to provide the domain name for routing purposes.
fakeip = nil,
}
@@ -1590,7 +1589,7 @@ function gen_config(var)
end
dns.final = default_dns_flag
--按分流顺序DNS
-- DNS in order of shunt
if dns_domain_rules and #dns_domain_rules > 0 then
for index, value in ipairs(dns_domain_rules) do
if value.outboundTag and (value.domain or value.domain_suffix or value.domain_keyword or value.domain_regex or value.geosite or value.rule_set) then
@@ -1711,15 +1710,10 @@ function gen_config(var)
timestamp = true,
output = logfile,
},
-- DNS
dns = dns,
-- 传入连接
inbounds = inbounds,
-- 传出连接
outbounds = outbounds,
-- 路由
route = route,
--实验性
experimental = experimental,
}
table.insert(outbounds, {
@@ -1919,9 +1913,7 @@ function gen_proto_config(var)
level = "warn",
timestamp = true,
},
-- 传入连接
inbounds = inbounds,
-- 传出连接
outbounds = outbounds,
}
return jsonc.stringify(config, 1)

View File

@@ -6,7 +6,7 @@ local json = api.jsonc
function gen_config(var)
local node_id = var["-node"]
if not node_id then
print("-node 不能为空")
print("-node Cannot be empty!")
return
end
local node = uci:get_all("passwall2", node_id)

View File

@@ -139,7 +139,6 @@ function gen_outbound(flag, node, tag, proxy_table)
concurrency = (node.mux == "1" and ((node.mux_concurrency) and tonumber(node.mux_concurrency) or -1)) or nil,
xudpConcurrency = (node.mux == "1" and ((node.xudp_concurrency) and tonumber(node.xudp_concurrency) or 8)) or nil
} or nil,
-- 底层传输配置
streamSettings = (node.streamSettings or node.protocol == "vmess" or node.protocol == "vless" or node.protocol == "socks" or node.protocol == "shadowsocks" or node.protocol == "trojan") and {
sockopt = {
mark = 255,
@@ -217,7 +216,7 @@ function gen_outbound(flag, node, tag, proxy_table)
mode = node.xhttp_mode or "auto",
path = node.xhttp_path or "/",
host = node.xhttp_host,
-- 如果包含 "extra" 节,取 "extra" 内的内容,否则直接赋值给 extra
-- If the code contains an "extra" section, retrieve the contents of "extra"; otherwise, assign the value directly to "extra".
extra = node.xhttp_extra and (function()
local success, parsed = pcall(jsonc.parse, node.xhttp_extra)
if success then
@@ -445,7 +444,6 @@ function gen_config_server(node)
log = {
loglevel = ("1" == node.log) and node.loglevel or "none"
},
-- 传入连接
inbounds = {
{
listen = (node.bind_local == "1") and "127.0.0.1" or nil,
@@ -519,7 +517,6 @@ function gen_config_server(node)
}
}
},
-- 传出连接
outbounds = outbounds,
routing = routing
}
@@ -1523,18 +1520,12 @@ function gen_config(var)
--dnsLog = true,
loglevel = loglevel
},
-- DNS
dns = dns,
fakedns = fakedns,
-- 传入连接
inbounds = inbounds,
-- 传出连接
outbounds = outbounds,
-- 连接观测
burstObservatory = burstObservatory,
-- 路由
routing = routing,
-- 本地策略
policy = {
levels = {
[0] = {
@@ -1703,8 +1694,7 @@ function gen_proto_config(var)
}
if outbound then table.insert(outbounds, outbound) end
end
-- 额外传出连接
table.insert(outbounds, {
protocol = "freedom", tag = "direct", settings = {keep = ""}
})
@@ -1713,11 +1703,8 @@ function gen_proto_config(var)
log = {
loglevel = "warning"
},
-- 传入连接
inbounds = inbounds,
-- 传出连接
outbounds = outbounds,
-- 路由
routing = routing
}
return jsonc.stringify(config, 1)
@@ -1954,13 +1941,9 @@ function gen_dns_config(var)
--dnsLog = true,
loglevel = loglevel
},
-- DNS
dns = dns,
-- 传入连接
inbounds = inbounds,
-- 传出连接
outbounds = outbounds,
-- 路由
routing = routing
}
return jsonc.stringify(config, 1)

View File

@@ -106,7 +106,7 @@ local api = require "luci.passwall2.api"
})
.then(response => {
if (!response.ok) {
throw new Error("备份失败!");
throw new Error("<%:Backup failed!%>");
}
const filename = response.headers.get("X-Backup-Filename");
if (!filename) {
@@ -175,7 +175,7 @@ local api = require "luci.passwall2.api"
const base64Data = btoa(binaryText);
const targetByteSize = 64 * 1024; // 分片大小 64KB
const targetByteSize = 64 * 1024; // Slice Size 64KB
let chunkSize = Math.floor(targetByteSize * 4 / 3);
chunkSize = chunkSize + (4 - (chunkSize % 4)) % 4;
const totalChunks = Math.ceil(base64Data.length / chunkSize);

View File

@@ -5,7 +5,7 @@ local api = require "luci.passwall2.api"
<script type="text/javascript">
//<![CDATA[
function ajax_add_node(link, group) {
const chunkSize = 1000; // 分片发送以突破uhttpd的限制每块1000字符
const chunkSize = 1000; // Fragmented sending to overcome uhttpd's limitations, with each fragment containing 1000 characters.
const totalChunks = Math.ceil(link.length / chunkSize);
let currentChunk = 0;
@@ -86,7 +86,7 @@ local api = require "luci.passwall2.api"
}
//自定义分组下拉列表事件
// Custom group dropdown list events
document.addEventListener("DOMContentLoaded", function() {
var dropdown = document.getElementById("addlink_group_custom");
if (!dropdown) return;
@@ -127,8 +127,16 @@ local api = require "luci.passwall2.api"
var val = input.value.trim();
if (!val) return;
if (val.toLowerCase() === "default") {
var emptyLi = Array.from(list.querySelectorAll(".dropdown-item"))
.find(function(el){ return !el.dataset.value; });
if (emptyLi) selectItem(emptyLi);
input.value = "";
return;
}
var li = Array.from(list.querySelectorAll(".dropdown-item")).find(function(el){
return el.dataset.value === val;
return el.dataset.value.toLowerCase() === val.toLowerCase();
});
if (!li) {
li = document.createElement("li");
@@ -141,7 +149,7 @@ local api = require "luci.passwall2.api"
input.value = "";
selectItem(li);
});
// 从tab中读取分组名称
// Read group names from tab
var observer = new MutationObserver(function(mutations, obs){
var tabs = document.querySelectorAll(".cbi-tabmenu li");
if(!tabs.length) return;
@@ -163,7 +171,7 @@ local api = require "luci.passwall2.api"
});
observer.observe(document.body, {childList: true, subtree: true});
// 点击外部时自动收起
// Automatically collapses when clicking on the outside.
document.addEventListener("click", function(e) {
if (!dropdown.contains(e.target)) {
list.style.display = "none";

View File

@@ -215,7 +215,7 @@ local hysteria2_type = get_core("hysteria2_type", {{has_hysteria2,"hysteria2"},{
v_port.value + "/?";
var shadow_tls;
//生成SS Shadow-TLS 插件参数
// Generate SS Shadow-TLS plugin parameters
const generateShadowTLSBase64 = function(paramStr) {
try {
let obj = {};
@@ -746,13 +746,13 @@ local hysteria2_type = get_core("hysteria2_type", {{has_hysteria2,"hysteria2"},{
} catch (err) {}
} else {
//alert('<%:Faltal on set option, please help in debug: %>' + opt + ' = ' + val);
// 处理 DynamicList
// Processing DynamicList
var fullName = this.base + '.' + opt;
var lists = document.querySelectorAll('.cbi-dynlist');
for (var i = 0; i < lists.length; i++) {
var parent = lists[i].closest('.cbi-value');
if (!parent) continue;
// 尝试从 label for 属性中提取 fullName
// Try to extract fullName from the label's for attribute.
var label = parent.querySelector('label.cbi-value-title');
var labelFor = label?.getAttribute('for');
if (labelFor === fullName) {
@@ -793,7 +793,7 @@ local hysteria2_type = get_core("hysteria2_type", {{has_hysteria2,"hysteria2"},{
if (ssrurl === null || ssrurl === "") {
return false;
}
ssrurl = ssrurl.replace(/&amp;/gi, '&').replace(/\s*#\s*/, '#').trim(); //一些奇葩的链接用"&amp;"当做"&""#"前后带空格
ssrurl = ssrurl.replace(/&amp;/gi, '&').replace(/\s*#\s*/, '#').trim(); // Some odd links use "&" as "&", and include spaces before and after "#".
s.innerHTML = "";
var ssu = ssrurl.split('://');
var event = document.createEvent("HTMLEvents");
@@ -872,7 +872,7 @@ local hysteria2_type = get_core("hysteria2_type", {{has_hysteria2,"hysteria2"},{
method = userInfo.substr(0, userInfoSplitIndex);
password = userInfo.substr(userInfoSplitIndex + 1);
} else {
password = url0.substr(0, sipIndex); //一些链接用明文uuid做密码
password = url0.substr(0, sipIndex); // Some links use plaintext UUIDs as passwords.
}
} else {
// base64(method:pass@host:port)
@@ -892,7 +892,7 @@ local hysteria2_type = get_core("hysteria2_type", {{has_hysteria2,"hysteria2"},{
}
}
// 判断密码是否经过url编码
// Determine if the password is URL encoded
const isURLEncodedPassword = function(pwd) {
if (!/%[0-9A-Fa-f]{2}/.test(pwd)) return false;
try {
@@ -949,7 +949,7 @@ local hysteria2_type = get_core("hysteria2_type", {{has_hysteria2,"hysteria2"},{
opt.set(dom_prefix + 'plugin_enabled', true);
opt.set(dom_prefix + 'plugin', plugin || "none");
opt.set(dom_prefix + 'plugin_opts', pluginOpts || "");
//obfs-local插件转换成xray支持的格式
// The obfs-local plugin converts data to a format supported by xray.
if (plugin == "obfs-local" && dom_prefix == "xray_") {
var obfs = pluginOpts.match(/obfs=([^;]+)/);
var obfs_host = pluginOpts.match(/obfs-host=([^;]+)/);
@@ -1075,7 +1075,7 @@ local hysteria2_type = get_core("hysteria2_type", {{has_hysteria2,"hysteria2"},{
}
if (queryParam["shadow-tls"]) {
//解析SS Shadow-TLS 插件参数
// Parsing SS Shadow-TLS plugin parameters
const parseShadowTLSParams = function(base64Str, outObj) {
try {
let obj = JSON.parse(b64decsafe(base64Str));

View File

@@ -76,7 +76,7 @@ table td, .table .td {
function cbi_t_switch(section, tab) {
if( cbi_t[section] && cbi_t[section][tab] ) {
//在切换选项卡之前,先取消当前激活选项卡的全选状态
// Before switching tabs, first deselect all currently active tabs.
var btn = document.getElementById("select_all_btn");
if (btn) {
dechecked_all_node(btn);
@@ -646,7 +646,7 @@ table td, .table .td {
}
tab_ul_html += tab_ul_li_html + '</ul>'
tab_content_html += tab_content_html + '</fieldset>'
tab_content_html += '</fieldset>'
var tab_html = tab_ul_html + tab_content_html
document.getElementById("node_list").innerHTML = tab_html

View File

@@ -26,7 +26,7 @@ local api = require "luci.passwall2.api"
var id = onclick_str.substring(onclick_str.lastIndexOf('/') + 1, onclick_str.length - 1);
var td = edit_btn[i].parentNode;
var new_div = "";
//添加"日志"按钮
// Add `log` button
new_div += '<input class="btn cbi-button cbi-button-add" type="button" value="<%:Log%>" onclick="window.open(\'' + '<%=api.url("server_user_log")%>' + '?id=' + id + '\', \'_blank\')"/>&nbsp;&nbsp;';
td.innerHTML = new_div + td.innerHTML;
}

View File

@@ -1,6 +1,3 @@
msgid "PassWall 2"
msgstr "PassWall 2"
msgid "Auto"
msgstr "自动"
@@ -979,6 +976,12 @@ msgstr "手动订阅全部"
msgid "This remark already exists, please change a new remark."
msgstr "此备注已存在,请改一个新的备注。"
msgid "Remark cannot be empty."
msgstr "备注不能为空。"
msgid "URL cannot be empty."
msgstr "网址不能为空。"
msgid "Filter keyword Mode"
msgstr "过滤关键字模式"
@@ -1702,6 +1705,9 @@ msgstr "下载备份"
msgid "RST Backup"
msgstr "恢复备份"
msgid "Backup failed!"
msgstr "备份失败!"
msgid "UL Restore"
msgstr "上传恢复"
@@ -1729,6 +1735,18 @@ msgstr "是否要恢复客户端默认配置?"
msgid "Are you sure you want to restore the client to default settings?"
msgstr "是否真的要恢复客户端默认配置?"
msgid "Configuration file uploaded successfully…"
msgstr "配置文件上传成功…"
msgid "Configuration restored successfully…"
msgstr "配置还原成功…"
msgid "Service restarting…"
msgstr "重启服务中…"
msgid "Configuration file decompression failed, please try again!"
msgstr "配置文件解压失败,请重试!"
msgid "_urltest"
msgstr "URLTest"
@@ -1842,3 +1860,384 @@ msgstr "分组名"
msgid "Using..."
msgstr "使用中。"
msgid "Generate configuration file and run"
msgstr "生成配置文件并运行"
msgid "Start after a delay of %s seconds!"
msgstr "延时 %s 秒后再启动!"
msgid "The script is already running, do not run it again. Exit."
msgstr "脚本已经在运行,不重复运行,退出."
msgid "Stop the script and wait for a timeout, then exit without repeating the process."
msgstr "停止脚本等待超时,不重复运行,退出."
msgid "%s Transparent proxy base dependencies %s Not installed..."
msgstr "%s 透明代理基础依赖 %s 未安装..."
msgid "%s does not have execute permissions and cannot be started: %s %s"
msgstr "%s 没有执行权限,无法启动:%s %s"
msgid "%s not found, unable to start..."
msgstr "找不到 %s无法启动..."
msgid "Socks node: [%s]%s is an invalid server address and cannot be started!"
msgstr "Socks节点[%s]%s 是非法的服务器地址,无法启动!"
msgid "For some reason, the configuration for this Socks service has been lost, and its startup has been aborted!"
msgstr "某种原因,此 Socks 服务的相关配置已失联,启动中止!"
msgid "Socks node: [%s]%s, start failed %s:%s %s"
msgstr "Socks节点[%s]%s启动失败 %s:%s %s"
msgid "Socks node: [%s]%s, starting %s:%s"
msgstr "Socks节点[%s]%s启动 %s:%s"
msgid "To enable experimental IPv6 transparent proxy (TProxy), please ensure your node and type support IPv6!"
msgstr "开启实验性IPv6透明代理(TProxy)请确认您的节点及类型支持IPv6"
msgid "Analyzing the node configuration of the Socks service..."
msgstr "分析 Socks 服务的节点配置..."
msgid "Log file is too long, clear it!"
msgstr "日志文件过长,清空处理!"
msgid "The task is currently running automatically as a scheduled task; no reconfiguration of the scheduled task is required."
msgstr "当前为计划任务自动运行,不重新配置定时任务。"
msgid "Scheduled tasks: Auto stop service."
msgstr "配置定时任务:自动关闭服务。"
msgid "Scheduled tasks: Auto start service."
msgstr "配置定时任务:自动开启服务。"
msgid "Scheduled tasks: Auto restart service."
msgstr "配置定时任务:自动重启服务。"
msgid "Scheduled tasks: Auto update rules."
msgstr "配置定时任务:自动更新规则。"
msgid "Scheduled tasks: Auto update [%s] subscription."
msgstr "配置定时任务:自动更新 【%s】 订阅。"
msgid "Auto updates: Starts a cyclical update process."
msgstr "自动更新:启动循环更新进程。"
msgid "Running in no proxy mode, it only allows scheduled tasks for starting and stopping services."
msgstr "运行于非代理模式,仅允许服务启停的定时任务。"
msgid "Clear scheduled commands."
msgstr "清除定时执行命令。"
msgid "Unable to resolve [%s], route table addition failed!"
msgstr "无法解析[%s],路由表添加失败!"
msgid "[%s] was successfully added to the routing table of interface [%s]!"
msgstr "[%s]添加到接口[%s]路由表成功!"
msgid "Adding [%s] to the [%s] routing table failed! The reason is that the [%s] gateway cannot be found."
msgstr "[%s]添加到接口[%s]路由表失功!原因是找不到[%s]网关。"
msgid "Global nodes are not enabled, skip [%s]."
msgstr "全局节点未启用,跳过【%s】"
msgid "Direct DNS: %s"
msgstr "直连 DNS%s"
msgid "Remote DNS: %s"
msgstr "远程 DNS%s"
msgid "The program has started. Please stop it and then restart it!"
msgstr "程序已启动,先停止再重新启动!"
msgid "The system does not have iptables or ipset installed, or Dnsmasq does not have ipset support enabled, so iptables+ipset transparent proxy cannot be used!"
msgstr "系统未安装iptables或ipset或Dnsmasq没有开启ipset支持无法使用iptables+ipset透明代理"
msgid "fw4 detected, use nftables to transparent proxy."
msgstr "检测到fw4使用nftables进行透明代理。"
msgid "The Dnsmasq package does not meet the requirements for transparent proxy in nftables. If you need to use it, please ensure that the dnsmasq version is 2.87 or higher and that nftset support is enabled."
msgstr "Dnsmasq软件包不满足nftables透明代理要求如需使用请确保dnsmasq版本在2.87以上并开启nftset支持。"
msgid "If your Dnsmasq version is lower than 2.90, it is recommended to upgrade to version 2.90 or higher to avoid Dnsmasq crashing in some cases!"
msgstr "Dnsmasq版本低于2.90建议升级至2.90及以上版本以避免部分情况下Dnsmasq崩溃问题"
msgid "Running complete!"
msgstr "运行完成!"
msgid "Clearing and closing related programs and cache complete."
msgstr "清空并关闭相关程序和缓存完成。"
msgid "Incorrect index listing method (%s), execution terminated!"
msgstr "索引列举方式不正确(%s终止执行"
msgid "parse the traffic splitting rules[%s]-[geoip:%s] add to %s to complete."
msgstr "解析分流规则[%s]-[geoip:%s]加入到 %s 完成"
msgid "Access Control:"
msgstr "访问控制:"
msgid "Add node to the load balancer is directly connected to %s[%s]."
msgstr "加入负载均衡的节点到%s[%s]直连完成。"
msgid "Add all %s nodes to %s[%s] direct connection complete."
msgstr "加入所有%s节点到%s[%s]直连完成。"
msgid "Starting to load %s firewall rules..."
msgstr "开始加载 %s 防火墙规则..."
msgid "local network segments (%s) direct connection: %s"
msgstr "本机 %s 网段直连:%s"
msgid "Add ISP %s DNS to the whitelist: %s"
msgstr "追加ISP %s DNS 到白名单:%s"
msgid "[%s]"
msgstr "【%s】"
msgid "[%s],"
msgstr "【%s】"
msgid "Source iface [%s],"
msgstr "源接口【%s】"
msgid "IP range [%s],"
msgstr "IP 范围【%s】"
msgid "All device,"
msgstr "所有设备,"
msgid "Does not exist, ignore."
msgstr "不存在,忽略。"
msgid "not proxy %s port [%s]"
msgstr "不代理 %s 端口 [%s]"
msgid "not proxy all %s"
msgstr "不代理所有 %s"
msgid "Use the %s node [%s]"
msgstr "使用 %s 节点[%s]"
msgid "Using a node that is different from the global configuration, DNS has been forcibly redirected to a dedicated DNS server."
msgstr "使用与全局配置不相同节点已将DNS强制重定向到专用 DNS 服务器。"
msgid "Add direct DNS to %s: %s"
msgstr "追加直连 DNS 到 %s %s"
msgid "Local"
msgstr "本机"
msgid "[Local],"
msgstr "【本机】,"
msgid "Clear %s."
msgstr "清除 %s。"
msgid "Delete %s rules is complete."
msgstr "删除 %s 规则完成。"
msgid "%s firewall rules load complete!"
msgstr "%s 防火墙规则加载完成!"
msgid "Socks switch detection: Unknown error."
msgstr "Socks切换检测未知错误。"
msgid "Socks switch detection: Unable to connect to the network. Please check if the network is working properly!"
msgstr "Socks切换检测无法连接到网络请检查网络是否正常"
msgid "Socks switch detection: Primary node 【%s: [%s]】 is normal. Switch to the primary node!"
msgstr "Socks切换检测%s 主节点【%s[%s]】正常,切换到主节点!"
msgid "Socks switch detection: %s node switch complete!"
msgstr "Socks切换检测%s 节点切换完毕!"
msgid "Socks switch detection: %s 【%s:[%s]】 normal."
msgstr "Socks切换检测%s 【%s:[%s]】 正常。"
msgid "switch to %s test detect!"
msgstr "切换到 %s 检测!"
msgid "backup node"
msgstr "备用节点"
msgid "next backup node"
msgstr "下一个备用节点"
msgid "main node"
msgstr "主节点"
msgid "Socks switch detection: Unknown error."
msgstr "Socks切换检测未知错误。"
msgid "Socks switch detection: %s 【%s:[%s]】 abnormal, %s"
msgstr "Socks切换检测%s 【%s:[%s]】 异常,%s"
msgid "Socks switch detection: %s 【%s:[%s]】 normal, switch to this node!"
msgstr "Socks切换检测%s 【%s:[%s]】 正常,切换到此节点!"
msgid "Restart dnsmasq service."
msgstr "重启 dnsmasq 服务。"
msgid "Console Port: %s"
msgstr "控制台端口:%s"
msgid "Discard one obviously invalid node."
msgstr "丢弃 1 个明显无效的节点"
msgid "Entrance %s:%s"
msgstr "入口 %s:%s"
msgid "Node: %s:%s, Weight: %s"
msgstr "节点:%s:%s权重%s"
msgid "Start updating the rules..."
msgstr "开始更新规则..."
msgid "%s Start updating..."
msgstr "%s 开始更新..."
msgid "%s version is the same and does not need to be updated."
msgstr "%s 版本一致,无需更新。"
msgid "%s update success."
msgstr "%s 更新成功。"
msgid "%s update failed, please try again later."
msgstr "%s 更新失败,请稍后再试。"
msgid "Restart the service and apply the new rules."
msgstr "重启服务,应用新的规则。"
msgid "The rules have been updated..."
msgstr "规则更新完毕..."
msgid "Socks node list [%s]"
msgstr "Socks 节点列表 [%s]"
msgid "Backup node list"
msgstr "备用节点的列表"
msgid "HAProxy node list [%s]"
msgstr "HAProxy 节点列表 [%s]"
msgid "ACL list [%s]"
msgstr "访问控制列表 [%s]"
msgid "Shunt [%s] node"
msgstr "分流 [%s] 节点"
msgid "Xray Load Balancing node [%s] list"
msgstr "Xray 负载均衡节点 [%s] 列表"
msgid "Xray Load Balancing node [%s] backup node"
msgstr "Xray 负载均衡节点 [%s] 后备节点"
msgid "Sing-Box URLTest node [%s] list"
msgstr "Sing-Box URLTest 节点 [%s] 列表"
msgid "Node [%s] preproxy node"
msgstr "节点 [%s] 前置代理节点"
msgid "Node [%s] landing node"
msgstr "节点 [%s] 落地节点"
msgid "Skipping the %s node is due to incompatibility with the %s core program or incorrect node usage type settings."
msgstr "跳过 %s 节点,因未适配到 %s 核心程序,或未正确设置节点使用类型。"
msgid "Skip node: %s. Because Sing-Box does not support the %s protocol's %s transmission method, Xray needs to be used instead."
msgstr "跳过节点:%s因 Sing-Box 不支持 %s 协议的 %s 传输方式,需更换 Xray。"
msgid "Xray unsupport %s plugin."
msgstr "Xray 不支持 %s 插件。"
msgid "Plugin options Incomplete."
msgstr "插件选项不完整。"
msgid "shadowsocks-libev unsupport 2022 encryption."
msgstr "shadowsocks-libev 不支持2022加密。"
msgid "Please replace Xray or Sing-Box to support more transmission methods in Shadowsocks."
msgstr "请更换 Xray 或 Sing-Box 来支持 Shadowsocks 更多的传输方式。"
msgid "unsupport %s plugin."
msgstr "不支持 %s 插件。"
msgid "Skip the %s node because the %s core program is not installed."
msgstr "跳过 %s 节点,因未安装 %s 核心程序 %s。"
msgid "Skip the %s node, as %s type nodes require Sing-Box version 1.12 or higher."
msgstr "跳过 %s 节点,因 %s 类型的节点需要 Sing-Box 1.12 以上版本支持。"
msgid "%s type node subscriptions are not currently supported, skip this node."
msgstr "暂时不支持 %s 类型的节点订阅,跳过此节点。"
msgid "Update [%s]"
msgstr "'更新【%s】"
msgid "Matching node:"
msgstr "匹配节点:"
msgid "First Matching node:"
msgstr "第一匹配节点:"
msgid "Second Matching node:"
msgstr "第二匹配节点:"
msgid "Third Matching node:"
msgstr "第三匹配节点:"
msgid "Fourth Matching node:"
msgstr "第四匹配节点:"
msgid "Fifth Matching node:"
msgstr "第五匹配节点:"
msgid "Unable to find the best matching node, now replaced with:"
msgstr "无法找到最匹配的节点,当前已更换为:"
msgid "No node information updates are available."
msgstr "没有可用的节点信息更新。"
msgid "Skip unknown types:"
msgstr "跳过未知类型:"
msgid "Discard node: %s, Reason:"
msgstr "丢弃节点: %s ,原因:"
msgid "No usable binary was found."
msgstr "找不到可使用二进制。"
msgid "Discard filter nodes: %s type node %s"
msgstr "丢弃过滤节点: %s 类型节点 %s"
msgid "Parsing error, skip this node."
msgstr "解析错误,跳过此节点。"
msgid "Successfully resolved the [%s] node, number: %s"
msgstr "成功解析【%s】节点数量%s"
msgid "Get subscription content for [%s] is empty. This may be due to an invalid subscription address or a network problem. Please diagnose the issue!"
msgstr "获取到的【%s】订阅内容为空可能是订阅地址无效或是网络问题请诊断"
msgid "Proxy"
msgstr "代理"
msgid "Start subscribing..."
msgstr "开始订阅..."
msgid "Start subscribing: %s"
msgstr "开始订阅:%s"
msgid "Subscription: [%s] No changes, no update required."
msgstr "订阅:【%s】没有变化无需更新。"
msgid "[%s] Subscription failed. This could be due to an invalid subscription address or a network issue. Please diagnose the problem! [%s]"
msgstr "【%s】订阅失败可能是订阅地址无效或是网络问题请诊断[%s]"
msgid "Error, restoring service."
msgstr "发生错误, 正在恢复服务。"
msgid "Subscription complete..."
msgstr "订阅完毕..."

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
zh-tw

View File

@@ -35,7 +35,7 @@ unlock() {
boot_func() {
local delay=$(uci -q get ${CONFIG}.@global_delay[0].start_delay || echo 1)
if [ "$delay" -gt 0 ]; then
$APP_FILE echolog "执行启动延时 $delay 秒后再启动!"
$APP_FILE log_i18n 0 "Start after a delay of %s seconds!" "${delay}"
sleep $delay
fi
restart
@@ -48,7 +48,7 @@ boot() {
start() {
set_lock
[ $? == 1 ] && $APP_FILE echolog "脚本已经在运行,不重复运行,退出." && exit 0
[ $? == 1 ] && $APP_FILE log_i18n 0 "The script is already running, do not run it again. Exit." && exit 0
$APP_FILE start
unset_lock
}
@@ -56,14 +56,14 @@ start() {
stop() {
unlock
set_lock
[ $? == 1 ] && $APP_FILE echolog "停止脚本等待超时,不重复运行,退出." && exit 0
[ $? == 1 ] && $APP_FILE log_i18n 0 "Stop the script and wait for a timeout, then exit without repeating the process." && exit 0
$APP_FILE stop
unset_lock
}
restart() {
set_lock
[ $? == 1 ] && $APP_FILE echolog "脚本已经在运行,不重复运行,退出." && exit 0
[ $? == 1 ] && $APP_FILE log_i18n 0 "The script is already running, do not run it again. Exit." && exit 0
$APP_FILE stop
$APP_FILE start
unset_lock

View File

@@ -87,35 +87,35 @@ config nodes 'myshunt'
config shunt_rules 'DirectGame'
option remarks 'DirectGame'
option network 'tcp,udp'
option domain_list '# steam直连域名获取国内CDN走国内线路下载
option domain_list '# Steam CDN
cm.steampowered.com
steamserver.net
# steam国内CDN华为云
# Steam CN Huawei CDN
steampipe.steamcontent.tnkjmec.com
# steam国内CDN白山云
# Steam CN BaiShan CDN
st.dl.eccdnx.com
st.dl.bscstorage.net
st.dl.pinyuncloud.com
# steam国内CDN新流云(原金山云)(支持ipv6)
# Steam CN XinLiuYun(support ipv6) CDN
dl.steam.clngaa.com
# steam国内CDN网宿
# Steam CN Wangsu CDN
cdn.mileweb.cs.steampowered.com.8686c.com
cdn-ws.content.steamchina.com
# steam国内CDN腾讯云 (蒸汽中国独占)
# Steam CN Tencent CDN
cdn-qc.content.steamchina.com
# steam国内CDN阿里云(支持ipv6)
# Steam CN Aliyun(support ipv6) CDN
cdn-ali.content.steamchina.com
xz.pphimalayanrt.com
lv.queniujq.cn
alibaba.cdn.steampipe.steamcontent.com
# 国内游戏geosite域名
# CN Game geosite domain
geosite:category-games@cn
rule-set:remote:https://raw.githubusercontent.com/lyc8503/sing-box-rules/rule-set-geosite/geosite-category-games%40cn.srs
'
option ip_list '# steam直连IP
option ip_list '# Steam IP
45.121.184.0/24
103.10.124.0/23
103.28.54.0/24

View File

@@ -24,9 +24,38 @@ UTIL_NAIVE=$LUA_UTIL_PATH/util_naiveproxy.lua
UTIL_HYSTERIA2=$LUA_UTIL_PATH/util_hysteria2.lua
UTIL_TUIC=$LUA_UTIL_PATH/util_tuic.lua
i18n() {
echo "$(lua ${APP_PATH}/i18n.lua "$@")"
}
echolog() {
echo -e "$*" >>$LOG_FILE
}
echolog_date() {
local d="$(date "+%Y-%m-%d %H:%M:%S")"
echo -e "$d: $*" >>$LOG_FILE
echolog "$d: $*"
}
log() {
local num="$1"
shift
local content="$@"
local indent=""
if [ "$num" -ge 1 ]; then
for i in $(seq 1 ${num}); do
indent="${indent} "
done
echolog_date "${indent}- ${content}"
else
echolog_date "${content}"
fi
}
log_i18n() {
local num="$1"
shift
log ${num} "$(i18n "$@")"
}
config_get_type() {
@@ -147,7 +176,7 @@ check_host() {
local f=${1}
a=$(echo $f | grep "\/")
[ -n "$a" ] && return 1
# 判断是否包含汉字~
# Determine if it contains Chinese characters.
local tmp=$(echo -n $f | awk '{print gensub(/[!-~]/,"","g",$0)}')
[ -n "$tmp" ] && return 1
return 0
@@ -216,11 +245,11 @@ check_depends() {
[ -d "/lib/apk/packages" ] && file_path="/lib/apk/packages" && file_ext=".list"
if [ "$tables" == "iptables" ]; then
for depends in "iptables-mod-tproxy" "iptables-mod-socket" "iptables-mod-iprange" "iptables-mod-conntrack-extra" "kmod-ipt-nat"; do
[ -s "${file_path}/${depends}${file_ext}" ] || echolog "$tables透明代理基础依赖 $depends 未安装..."
[ -s "${file_path}/${depends}${file_ext}" ] || log_i18n 0 "%s Transparent proxy base dependencies %s Not installed..." "${tables}" "${depends}"
done
else
for depends in "kmod-nft-socket" "kmod-nft-tproxy" "kmod-nft-nat"; do
[ -s "${file_path}/${depends}${file_ext}" ] || echolog "$tables透明代理基础依赖 $depends 未安装..."
[ -s "${file_path}/${depends}${file_ext}" ] || log_i18n 0 "%s Transparent proxy base dependencies %s Not installed..." "${tables}" "${depends}"
done
fi
}
@@ -257,14 +286,14 @@ ln_run() {
ln -s "${file_func}" "${TMP_BIN_PATH}/${ln_name}" >/dev/null 2>&1
file_func="${TMP_BIN_PATH}/${ln_name}"
}
[ -x "${file_func}" ] || echolog " - $(readlink ${file_func}) 没有执行权限,无法启动:${file_func} $*"
[ -x "${file_func}" ] || log 1 "$(i18n "%s does not have execute permissions and cannot be started: %s %s" "$(readlink ${file_func})" "${file_func}" "$*")"
fi
#echo "${file_func} $*" >&2
[ -n "${file_func}" ] || echolog " - 找不到 ${ln_name},无法启动..."
${file_func:-echolog " - ${ln_name}"} "$@" >${output} 2>&1 &
[ -n "${file_func}" ] || log 1 "$(i18n "%s not found, unable to start..." "${ln_name}")"
${file_func:-log 1 "${ln_name}"} "$@" >${output} 2>&1 &
process_count=$(ls $TMP_SCRIPT_FUNC_PATH | grep -v "^_" | wc -l)
process_count=$((process_count + 1))
echo "${file_func:-echolog " - ${ln_name}"} $@ >${output}" > $TMP_SCRIPT_FUNC_PATH/$process_count
echo "${file_func:-log 1 "${ln_name}"} $@ >${output}" > $TMP_SCRIPT_FUNC_PATH/$process_count
}
lua_api() {
@@ -588,12 +617,12 @@ run_socks() {
if [ -n "$server_host" ] && [ -n "$server_port" ]; then
check_host $server_host
[ $? != 0 ] && {
echolog " - Socks节点[$remarks]${server_host} 是非法的服务器地址,无法启动!"
log 1 "$(i18n "Socks node: [%s]%s is an invalid server address and cannot be started!" "${$remarks}" "${server_host}")"
return 1
}
tmp="${server_host}:${server_port}"
else
error_msg="某种原因,此 Socks 服务的相关配置已失联,启动中止!"
error_msg="$(i18n "For some reason, the configuration for this Socks service has been lost, and its startup has been aborted!")"
fi
if [ "$type" == "sing-box" ] || [ "$type" == "xray" ]; then
@@ -604,10 +633,10 @@ run_socks() {
fi
[ -n "${error_msg}" ] && {
[ "$bind" != "127.0.0.1" ] && echolog " - Socks节点[$remarks]${tmp},启动中止 ${bind}:${socks_port} ${error_msg}"
[ "$bind" != "127.0.0.1" ] && log 1 "$(i18n "Socks node: [%s]%s, start failed %s:%s %s" "${remarks}" "${tmp}" "${bind}" "${socks_port}" "${error_msg}")"
return 1
}
[ "$bind" != "127.0.0.1" ] && echolog " - Socks节点[$remarks]${tmp},启动 ${bind}:${socks_port}"
[ "$bind" != "127.0.0.1" ] && log 1 "$(i18n "Socks node: [%s]%s, starting %s:%s" "${remarks}" "${tmp}" "${bind}" "${socks_port}")"
case "$type" in
sing-box)
@@ -707,7 +736,7 @@ socks_node_switch() {
eval_set_val $@
[ -n "$flag" ] && [ -n "$new_node" ] && {
local prefix pf filename
# 结束 SS 插件进程
# Kill the SS plugin process
for prefix in "" "HTTP_"; do
pf="$TMP_PATH/${prefix}SOCKS_${flag}_plugin.pid"
[ -s "$pf" ] && kill -9 "$(head -n1 "$pf")" >/dev/null 2>&1
@@ -747,7 +776,7 @@ run_global() {
mkdir -p ${GLOBAL_ACL_PATH}
if [ $PROXY_IPV6 == "1" ]; then
echolog "开启实验性IPv6透明代理(TProxy),请确认您的节点及类型支持IPv6"
log_i18n 0 "To enable experimental IPv6 transparent proxy (TProxy), please ensure your node and type support IPv6!"
fi
TUN_DNS_PORT=15353
@@ -755,23 +784,23 @@ run_global() {
V2RAY_ARGS="flag=global node=$NODE redir_port=$REDIR_PORT tcp_proxy_way=${TCP_PROXY_WAY}"
V2RAY_ARGS="${V2RAY_ARGS} dns_listen_port=${TUN_DNS_PORT} direct_dns_query_strategy=${DIRECT_DNS_QUERY_STRATEGY} remote_dns_query_strategy=${REMOTE_DNS_QUERY_STRATEGY} dns_cache=${DNS_CACHE}"
local msg="${TUN_DNS} 直连DNS${AUTO_DNS}"
local msg="DNS: ${TUN_DNS} $(i18n "Direct DNS: %s" "${AUTO_DNS}")"
[ -n "$REMOTE_DNS_PROTOCOL" ] && {
V2RAY_ARGS="${V2RAY_ARGS} remote_dns_protocol=${REMOTE_DNS_PROTOCOL} remote_dns_detour=${REMOTE_DNS_DETOUR}"
case "$REMOTE_DNS_PROTOCOL" in
udp*)
V2RAY_ARGS="${V2RAY_ARGS} remote_dns_udp_server=${REMOTE_DNS}"
msg="${msg} 远程DNS${REMOTE_DNS}"
msg="${msg} $(i18n "Remote DNS: %s" "${REMOTE_DNS}")"
;;
tcp)
V2RAY_ARGS="${V2RAY_ARGS} remote_dns_tcp_server=${REMOTE_DNS}"
msg="${msg} 远程DNS${REMOTE_DNS}"
msg="${msg} $(i18n "Remote DNS: %s" "${REMOTE_DNS}")"
;;
doh)
REMOTE_DNS_DOH=$(config_t_get global remote_dns_doh "https://1.1.1.1/dns-query")
V2RAY_ARGS="${V2RAY_ARGS} remote_dns_doh=${REMOTE_DNS_DOH}"
msg="${msg} 远程DNS${REMOTE_DNS_DOH}"
msg="${msg} $(i18n "Remote DNS: %s" "${REMOTE_DNS_DOH}")"
;;
esac
[ "$REMOTE_FAKEDNS" = "1" ] && {
@@ -783,7 +812,7 @@ run_global() {
[ -n "${_remote_dns_client_ip}" ] && V2RAY_ARGS="${V2RAY_ARGS} remote_dns_client_ip=${_remote_dns_client_ip}"
}
msg="${msg}"
echolog ${msg}
log 0 ${msg}
V2RAY_CONFIG=${GLOBAL_ACL_PATH}/global.json
V2RAY_LOG=${GLOBAL_ACL_PATH}/global.log
@@ -855,7 +884,7 @@ start_socks() {
[ "$SOCKS_ENABLED" = "1" ] && {
local ids=$(uci show $CONFIG | grep "=socks" | awk -F '.' '{print $2}' | awk -F '=' '{print $1}')
[ -n "$ids" ] && {
echolog "分析 Socks 服务的节点配置..."
log_i18n 0 "Analyzing the node configuration of the Socks service..."
for id in $ids; do
local enabled=$(config_n_get $id enabled 0)
[ "$enabled" == "0" ] && continue
@@ -874,7 +903,7 @@ start_socks() {
run_socks flag=$id node=$node bind=$bind socks_port=$port config_file=$config_file http_port=$http_port http_config_file=$http_config_file log_file=$log_file
set_cache_var "socks_${id}" "$node"
#自动切换逻辑
# Auto switch logic
local enable_autoswitch=$(config_n_get $id enable_autoswitch 0)
[ "$enable_autoswitch" = "1" ] && $APP_PATH/socks_auto_switch.sh ${id} > /dev/null 2>&1 &
done
@@ -886,7 +915,7 @@ clean_log() {
logsnum=$(cat $LOG_FILE 2>/dev/null | wc -l)
[ "$logsnum" -gt 1000 ] && {
echo "" > $LOG_FILE
echolog "日志文件过长,清空处理!"
log_i18n 0 "Log file is too long, clear it!"
}
}
@@ -910,7 +939,7 @@ start_crontab() {
[ -f "/tmp/lock/${CONFIG}_cron.lock" ] && {
rm -rf "/tmp/lock/${CONFIG}_cron.lock"
echolog "当前为计划任务自动运行,不重新配置定时任务。"
log_i18n 0 "The task is currently running automatically as a scheduled task; no reconfiguration of the scheduled task is required."
return
}
@@ -931,7 +960,7 @@ start_crontab() {
else
echo "$t /etc/init.d/$CONFIG stop > /dev/null 2>&1 &" >>/etc/crontabs/root
fi
echolog "配置定时任务:自动关闭服务。"
log_i18n 0 "Scheduled tasks: Auto stop service."
fi
start_week_mode=$(config_t_get global_delay start_week_mode)
@@ -944,7 +973,7 @@ start_crontab() {
else
echo "$t /etc/init.d/$CONFIG start > /dev/null 2>&1 &" >>/etc/crontabs/root
fi
echolog "配置定时任务:自动开启服务。"
log_i18n 0 "Scheduled tasks: Auto start service."
fi
restart_week_mode=$(config_t_get global_delay restart_week_mode)
@@ -957,7 +986,7 @@ start_crontab() {
else
echo "$t /etc/init.d/$CONFIG restart > /dev/null 2>&1 &" >>/etc/crontabs/root
fi
echolog "配置定时任务:自动重启服务。"
log_i18n 0 "Scheduled tasks: Auto restart service."
fi
autoupdate=$(config_t_get global_rules auto_update)
@@ -971,7 +1000,7 @@ start_crontab() {
else
echo "$t lua $APP_PATH/rule_update.lua log all cron > /dev/null 2>&1 &" >>/etc/crontabs/root
fi
echolog "配置定时任务:自动更新规则。"
log_i18n 0 "Scheduled tasks: Auto update rules."
fi
TMP_SUB_PATH=$TMP_PATH/sub_crontabs
@@ -983,7 +1012,7 @@ start_crontab() {
week_update=$(config_n_get $item week_update)
time_update=$(config_n_get $item time_update)
echo "$cfgid" >> $TMP_SUB_PATH/${week_update}_${time_update}
echolog "配置定时任务:自动更新【$remark】订阅。"
log_i18n 0 "Scheduled tasks: Auto update [%s] subscription." "${remark}"
fi
done
@@ -1006,10 +1035,10 @@ start_crontab() {
if [ "$ENABLED_DEFAULT_ACL" == 1 ] || [ "$ENABLED_ACLS" == 1 ]; then
[ "$update_loop" = "1" ] && {
$APP_PATH/tasks.sh > /dev/null 2>&1 &
echolog "自动更新:启动循环更新进程。"
log_i18n 0 "Auto updates: Starts a cyclical update process."
}
else
echolog "运行于非代理模式,仅允许服务启停的定时任务。"
log_i18n 0 "Running in no proxy mode, it only allows scheduled tasks for starting and stopping services."
fi
/etc/init.d/cron restart
@@ -1019,13 +1048,13 @@ stop_crontab() {
[ -f "/tmp/lock/${CONFIG}_cron.lock" ] && return
clean_crontab
/etc/init.d/cron restart
#echolog "清除定时执行命令。"
#log_i18n 0 "Clear scheduled commands."
}
add_ip2route() {
local ip=$(get_host_ip "ipv4" $1)
[ -z "$ip" ] && {
echolog " - 无法解析[${1}],路由表添加失败!"
log 1 "$(i18n "Unable to resolve [%s], route table addition failed!" "${1}")"
return 1
}
local remarks="${1}"
@@ -1040,9 +1069,9 @@ add_ip2route() {
if [ -n "${gateway}" ]; then
route add -host ${ip} gw ${gateway} dev ${device} >/dev/null 2>&1
echo "$ip" >> $TMP_ROUTE_PATH/${device}
echolog " - [${remarks}]添加到接口[${device}]路由表成功!"
log 1 "$(i18n "[%s] was successfully added to the routing table of interface [%s]!" "${remarks}" "${device}")"
else
echolog " - [${remarks}]添加到接口[${device}]路由表失功!原因是找不到[${device}]网关。"
log 1 "$(i18n "Adding [%s] to the [%s] routing table failed! The reason is that the [%s] gateway cannot be found." "${remarks}" "${device}" "${device}")"
fi
}
@@ -1218,7 +1247,7 @@ acl_app() {
set_cache_var "ACL_${sid}_dns_port" "${GLOBAL_DNSMASQ_PORT}"
set_cache_var "ACL_${sid}_default" "1"
else
echolog " - 全局节点未启用,跳过【${remarks}"
log 1 "$(i18n "Global nodes are not enabled, skip [%s]." "${remarks}")"
fi
else
[ "$(config_get_type $node)" = "nodes" ] && {
@@ -1265,7 +1294,7 @@ acl_app() {
start() {
pgrep -f /tmp/etc/passwall2/bin > /dev/null 2>&1 && {
#echolog "程序已启动,先停止再重新启动!"
#log_i18n 0 "The program has started. Please stop it and then restart it!"
stop
}
mkdir -p /tmp/etc /tmp/log $TMP_PATH $TMP_BIN_PATH $TMP_SCRIPT_FUNC_PATH $TMP_ROUTE_PATH $TMP_ACL_PATH $TMP_PATH2
@@ -1284,9 +1313,9 @@ start() {
if [ -n "$(command -v iptables-legacy || command -v iptables)" ] && [ -n "$(command -v ipset)" ] && [ -n "$(dnsmasq --version | grep 'Compile time options:.* ipset')" ]; then
USE_TABLES="iptables"
else
echolog "系统未安装iptablesipset或Dnsmasq没有开启ipset支持无法使用iptables+ipset透明代理"
log_i18n 0 "The system does not have iptables or ipset installed, or Dnsmasq does not have ipset support enabled, so iptables+ipset transparent proxy cannot be used!"
if [ -n "$(command -v fw4)" ] && [ -n "$(command -v nft)" ] && [ -n "$(dnsmasq --version | grep 'Compile time options:.* nftset')" ]; then
echolog "检测到fw4使用nftables进行透明代理。"
log_i18n 0 "fw4 detected, use nftables to transparent proxy."
USE_TABLES="nftables"
nftflag=1
config_t_set global_forwarding use_nft 1
@@ -1298,7 +1327,7 @@ start() {
USE_TABLES="nftables"
nftflag=1
else
echolog "Dnsmasq软件包不满足nftables透明代理要求如需使用请确保dnsmasq版本在2.87以上并开启nftset支持。"
log_i18n 0 "The Dnsmasq package does not meet the requirements for transparent proxy in nftables. If you need to use it, please ensure that the dnsmasq version is 2.87 or higher and that nftset support is enabled."
fi
fi
@@ -1306,7 +1335,7 @@ start() {
[ "$USE_TABLES" = "nftables" ] && {
dnsmasq_version=$(dnsmasq -v | grep -i "Dnsmasq version " | awk '{print $3}')
[ "$(expr $dnsmasq_version \>= 2.90)" == 0 ] && echolog "Dnsmasq版本低于2.90建议升级至2.90及以上版本以避免部分情况下Dnsmasq崩溃问题"
[ "$(expr $dnsmasq_version \>= 2.90)" == 0 ] && log_i18n 0 "If your Dnsmasq version is lower than 2.90, it is recommended to upgrade to version 2.90 or higher to avoid Dnsmasq crashing in some cases!"
}
if [ "$ENABLED_DEFAULT_ACL" == 1 ] || [ "$ENABLED_ACLS" == 1 ]; then
@@ -1332,7 +1361,8 @@ start() {
}
fi
start_crontab
echolog "运行完成!\n"
log_i18n 0 "Running complete!"
echolog "\n"
}
stop() {
@@ -1340,7 +1370,7 @@ stop() {
eval_cache_var
[ -n "$USE_TABLES" ] && source $APP_PATH/${USE_TABLES}.sh stop
delete_ip2route
# 结束 SS 插件进程
# Kill the SS plugin process
# kill_all xray-plugin v2ray-plugin obfs-local shadow-tls
local pid_file pid
find "$TMP_PATH" -type f -name '*_plugin.pid' 2>/dev/null | while read -r pid_file; do
@@ -1376,7 +1406,7 @@ stop() {
rm -rf $TMP_PATH
rm -rf /tmp/lock/${CONFIG}_socks_auto_switch*
rm -rf /tmp/lock/${CONFIG}_lease2hosts*
echolog "清空并关闭相关程序和缓存完成。"
log_i18n 0 "Clearing and closing related programs and cache complete."
exit 0
}
@@ -1447,8 +1477,14 @@ case $arg1 in
add_ip2route)
add_ip2route $@
;;
echolog)
echolog $@
log)
log $@
;;
log_i18n)
log_i18n "$@"
;;
i18n)
i18n "$@"
;;
get_new_port)
get_new_port $@

View File

@@ -40,8 +40,8 @@ local bind_local = uci:get(appname, "@global_haproxy[0]", "bind_local") or "0"
local bind_address = "0.0.0.0"
if bind_local == "1" then bind_address = "127.0.0.1" end
log("HAPROXY 负载均衡:")
log(string.format(" * 控制台端口:%s", console_port))
log("HAProxy: ")
log(" * " .. api.i18n.translatef("Console Port: %s", console_port))
fs.mkdir(haproxy_path)
local haproxy_file = haproxy_path .. "/" .. haproxy_conf
@@ -150,7 +150,7 @@ uci:foreach(appname, "haproxy_config", function(t)
t.server_port = server_port
table.insert(listens[listen_port], t)
else
log(" - 丢弃1个明显无效的节点")
log(" - " .. api.i18n.translate("Discard one obviously invalid node."))
end
end
end)
@@ -164,7 +164,7 @@ end
table.sort(sortTable, function(a,b) return (a < b) end)
for i, port in pairs(sortTable) do
log(" + 入口 %s:%s" % {bind_address, port})
log(" + " .. api.i18n.translatef("Entrance %s:%s", bind_address, port))
f_out:write("\n" .. string.format([[
listen %s
@@ -183,7 +183,7 @@ listen %s
local count_M, count_B = 1, 1
for i, o in ipairs(listens[port]) do
local remark = o.server_remark or ""
-- 防止重名导致无法运行
-- To prevent duplicate names from causing the program to fail to run.
if tostring(o.backup) ~= "1" then
remark = "M" .. count_M .. "-" .. remark
count_M = count_M + 1
@@ -210,11 +210,11 @@ listen %s
sys.call(string.format("/usr/share/passwall2/app.sh add_ip2route %s %s", o.origin_address, o.export))
end
log(string.format(" | - 出口节点:%s:%s权重%s", o.origin_address, o.origin_port, o.lbweight))
log(string.format(" | - " .. api.i18n.translatef("Node: %s:%s, Weight: %s", o.origin_address, o.origin_port, o.lbweight)))
end
end
--控制台配置
-- Console config
local console_user = uci:get(appname, "@global_haproxy[0]", "console_user")
local console_password = uci:get(appname, "@global_haproxy[0]", "console_password")
local str = [[
@@ -230,7 +230,7 @@ f_out:write("\n" .. string.format(str, console_port, (console_user and console_u
f_out:close()
--内置健康检查URL
-- Built-in health check URL
if health_check_type == "passwall_logic" then
local probeUrl = uci:get(appname, "@global_haproxy[0]", "health_probe_url") or "https://www.google.com/generate_204"
local f_url = io.open(haproxy_path .. "/Probe_URL", "w")

View File

@@ -9,7 +9,6 @@ server_address=$3
server_port=$4
pgrep -af "${CONFIG}/" | awk '/app\.sh.*(start|stop)/ || /nftables\.sh/ || /iptables\.sh/ { found = 1 } END { exit !found }' && {
# 特定任务执行中不检测
exit 0
}

View File

@@ -84,7 +84,7 @@ function restart(var)
local LOG = var["-LOG"]
sys.call("/etc/init.d/dnsmasq restart >/dev/null 2>&1")
if LOG == "1" then
api.log("重启 dnsmasq 服务")
api.log(api.i18n.translate("Restart dnsmasq service."))
end
end
@@ -111,7 +111,7 @@ function logic_restart(var)
sys.call("/etc/init.d/dnsmasq restart >/dev/null 2>&1")
end
if LOG == "1" then
api.log("重启 dnsmasq 服务")
api.log(api.i18n.translate("Restart dnsmasq service."))
end
end
@@ -267,7 +267,7 @@ function add_rule(var)
local fwd_dns
--始终用国内DNS解析节点域名
-- Always use domestic DNS to resolve node domain names
if true then
fwd_dns = LOCAL_DNS
uci:foreach(appname, "nodes", function(t)

View File

@@ -0,0 +1,6 @@
if #arg > 0 then
local api = require "luci.passwall2.api"
local str = arg[1]
table.remove(arg, 1)
print(api.i18n.translatef(str, unpack(arg)))
end

View File

@@ -46,7 +46,7 @@ comment() {
echo "-m comment --comment '$name'"
}
#解决端口超过15个ipt无效支持单端口、端口范围
# Resolves invalid IP addresses for ports exceeding 15; it supports single ports and port ranges.
add_port_rules() {
local ipt_cmd="$1"
local port_list="$2"
@@ -136,7 +136,7 @@ insert_rule_after() {
RULE_LAST_INDEX() {
[ $# -ge 3 ] || {
echolog "索引列举方式不正确(iptables),终止执行!"
log_i18n 1 "Incorrect index listing method (%s), execution terminated!" "iptables"
return 1
}
local ipt_tmp="${1}"; shift
@@ -168,10 +168,6 @@ get_redirect_ip6t() {
echo "$(REDIRECT $2 $3)"
}
get_action_chain_name() {
echo "全局代理"
}
gen_lanlist() {
cat <<-EOF
0.0.0.0/8
@@ -275,7 +271,7 @@ gen_shunt_list() {
get_geoip $_geoip_code ipv4 | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | sed -e "s/^/add $ipset_v4 &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
get_geoip $_geoip_code ipv6 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "s/^/add $ipset_v6 &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
fi
echolog " - [$?]解析分流规则[$shunt_id]-[geoip:${_geoip_code}]加入到 IPSET 完成"
log 1 "$(i18n "parse the traffic splitting rules[%s]-[geoip:%s] add to %s to complete." "${shunt_id}" "${_geoip_code}" "IPSET")"
}
}
}
@@ -321,8 +317,8 @@ add_shunt_t_rule() {
}
load_acl() {
log_i18n 1 "Access Control:"
[ "$ENABLED_ACLS" == 1 ] && {
echolog "访问控制:"
acl_app
for sid in $(ls -F ${TMP_ACL_PATH} | grep '/$' | awk -F '/' '{print $1}' | grep -v 'default'); do
eval $(uci -q show "${CONFIG}.${sid}" | cut -d'.' -sf 3-)
@@ -357,7 +353,7 @@ load_acl() {
ipset -! create $ipset_white nethash maxelem 1048576
ipset -! create $ipset_white6 nethash family inet6 maxelem 1048576
#分流规则的IP列表(使用分流节点时导入)
# Shunt rules IP list (import when use shunt node)
gen_shunt_list "${node}" shunt_list4 shunt_list6 ${write_ipset_direct} ${ipset_white} ${ipset_white6}
fi
}
@@ -374,45 +370,45 @@ load_acl() {
network_get_device device "${interface}"
[ -z "${device}" ] && device="${interface}"
_ipt_source="-i ${device} "
msg="源接口【${device}】,"
msg=$(i18n "Source iface [%s]," "${device}")
else
msg="源接口【所有】,"
msg=$(i18n "Source iface [%s]," $(i18n "All"))
fi
if [ -n "$(echo ${i} | grep '^iprange:')" ]; then
_iprange=$(echo ${i} | sed 's#iprange:##g')
_ipt_source=$(factor ${_iprange} "${_ipt_source}-m iprange --src-range")
msg="${msg}IP range【${_iprange}】,"
msg="${msg}$(i18n "IP range [%s]," "${_iprange}")"
_ipv4="1"
unset _iprange
elif [ -n "$(echo ${i} | grep '^ipset:')" ]; then
_ipset=$(echo ${i} | sed 's#ipset:##g')
msg="${msg}IPset${_ipset}】,"
msg="${msg}IPset$(i18n "[%s]," "${_ipset}")"
ipset -q list ${_ipset} >/dev/null
if [ $? -eq 0 ]; then
_ipt_source="${_ipt_source}-m set --match-set ${_ipset} src"
unset _ipset
else
echolog " - 【$remarks】,${msg}不存在,忽略。"
log 2 "$(i18n "[%s]," "${remarks}")${msg}$(i18n "Does not exist, ignore.")"
unset _ipset
continue
fi
elif [ -n "$(echo ${i} | grep '^ip:')" ]; then
_ip=$(echo ${i} | sed 's#ip:##g')
_ipt_source=$(factor ${_ip} "${_ipt_source}-s")
msg="${msg}IP${_ip}】,"
msg="${msg}IP$(i18n "[%s]," "${_ip}")"
_ipv4="1"
unset _ip
elif [ -n "$(echo ${i} | grep '^mac:')" ]; then
_mac=$(echo ${i} | sed 's#mac:##g')
_ipt_source=$(factor ${_mac} "${_ipt_source}-m mac --mac-source")
msg="${msg}MAC${_mac}】,"
msg="${msg}MAC$(i18n "[%s]," "${_mac}")"
unset _mac
elif [ -n "$(echo ${i} | grep '^any')" ]; then
msg="${msg}所有设备,"
msg="${msg}$(i18n "All device,")"
else
continue
fi
msg="$remarks】,${msg}"
msg="$(i18n "[%s]," "${remarks}")${msg}"
ipt_tmp=$ipt_n
[ -n "${is_tproxy}" ] && ipt_tmp=$ipt_m
@@ -421,11 +417,11 @@ load_acl() {
if ! has_1_65535 "$tcp_no_redir_ports"; then
[ "$_ipv4" != "1" ] && add_port_rules "$ip6t_m -A PSW2 $(comment "$remarks") ${_ipt_source} -p tcp" $tcp_no_redir_ports "-j RETURN" 2>/dev/null
add_port_rules "$ipt_tmp -A PSW2 $(comment "$remarks") ${_ipt_source} -p tcp" $tcp_no_redir_ports "-j RETURN"
echolog " - ${msg}不代理 TCP 端口[${tcp_no_redir_ports}]"
log 2 "${msg}$(i18n "not proxy %s port [%s]" "TCP" "${tcp_no_redir_ports}")"
else
#结束时会return无需加多余的规则。
# It will return when it ends, so no extra rules are needed.
tcp_proxy_mode="disable"
echolog " - ${msg}不代理所有 TCP"
log 2 "${msg}$(i18n "not proxy all %s" "TCP")"
fi
}
@@ -433,11 +429,11 @@ load_acl() {
if ! has_1_65535 "$udp_no_redir_ports"; then
[ "$_ipv4" != "1" ] && add_port_rules "$ip6t_m -A PSW2 $(comment "$remarks") ${_ipt_source} -p udp" $udp_no_redir_ports "-j RETURN" 2>/dev/null
add_port_rules "$ipt_m -A PSW2 $(comment "$remarks") ${_ipt_source} -p udp" $udp_no_redir_ports "-j RETURN"
echolog " - ${msg}不代理 UDP 端口[${udp_no_redir_ports}]"
log 2 "${msg}$(i18n "not proxy %s port [%s]" "UDP" "${udp_no_redir_ports}")"
else
#结束时会return无需加多余的规则。
# It will return when it ends, so no extra rules are needed.
udp_proxy_mode="disable"
echolog " - ${msg}不代理所有 UDP"
log 2 "${msg}$(i18n "not proxy all %s" "UDP")"
fi
}
@@ -446,7 +442,7 @@ load_acl() {
$ip6t_n -A PSW2_DNS $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j REDIRECT --to-ports $dns_redirect_port 2>/dev/null
$ipt_n -A PSW2_DNS $(comment "$remarks") -p tcp ${_ipt_source} --dport 53 -j REDIRECT --to-ports $dns_redirect_port
$ip6t_n -A PSW2_DNS $(comment "$remarks") -p tcp ${_ipt_source} --dport 53 -j REDIRECT --to-ports $dns_redirect_port 2>/dev/null
[ -z "$(get_cache_var "ACL_${sid}_default")" ] && echolog " - ${msg}使用与全局配置不相同节点已将DNS强制重定向到专用 DNS 服务器。"
[ -z "$(get_cache_var "ACL_${sid}_default")" ] && log 2 "${msg}$(i18n "Using a node that is different from the global configuration, DNS has been forcibly redirected to a dedicated DNS server.")"
else
$ipt_n -A PSW2_DNS $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j RETURN
$ip6t_n -A PSW2_DNS $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j RETURN 2>/dev/null
@@ -455,7 +451,7 @@ load_acl() {
fi
[ "$tcp_proxy_mode" != "disable" ] && [ -n "$redir_port" ] && {
msg2="${msg}使用 TCP 节点[$node_remark]"
msg2="${msg}$(i18n "Use the %s node [%s]" "TCP" "${node_remark}")"
if [ -n "${is_tproxy}" ]; then
msg2="${msg2}(TPROXY:${redir_port})"
ipt_j="-j PSW2_RULE"
@@ -487,13 +483,13 @@ load_acl() {
add_port_rules "$ip6t_m -A PSW2 $(comment "$remarks") -p tcp ${_ipt_source}" $tcp_redir_ports "-j PSW2_RULE" 2>/dev/null
$ip6t_m -A PSW2 $(comment "$remarks") -p tcp ${_ipt_source} $(REDIRECT $redir_port TPROXY) 2>/dev/null
}
echolog " - ${msg2}"
log 2 "${msg2}"
}
$ipt_tmp -A PSW2 $(comment "$remarks") ${_ipt_source} -p tcp -j RETURN
[ "$_ipv4" != "1" ] && $ip6t_m -A PSW2 $(comment "$remarks") ${_ipt_source} -p tcp -j RETURN 2>/dev/null
[ "$udp_proxy_mode" != "disable" ] && [ -n "$redir_port" ] && {
msg2="${msg}使用 UDP 节点[$node_remark](TPROXY:${redir_port})"
msg2="${msg}$(i18n "Use the %s node [%s]" "UDP" "${node_remark}")(TPROXY:${redir_port})"
$ipt_m -A PSW2 $(comment "$remarks") -p udp ${_ipt_source} -d $FAKE_IP -j PSW2_RULE
add_shunt_t_rule "${shunt_list4}" "$ipt_m -A PSW2 $(comment "$remarks") -p udp ${_ipt_source}" "-j PSW2_RULE" $udp_redir_ports
@@ -506,7 +502,7 @@ load_acl() {
add_port_rules "$ip6t_m -A PSW2 $(comment "$remarks") -p udp ${_ipt_source}" $udp_redir_ports "-j PSW2_RULE" 2>/dev/null
$ip6t_m -A PSW2 $(comment "$remarks") -p udp ${_ipt_source} $(REDIRECT $redir_port TPROXY) 2>/dev/null
}
echolog " - ${msg2}"
log 2 "${msg2}"
}
$ipt_m -A PSW2 $(comment "$remarks") ${_ipt_source} -p udp -j RETURN
[ "$_ipv4" != "1" ] && $ip6t_m -A PSW2 $(comment "$remarks") ${_ipt_source} -p udp -j RETURN 2>/dev/null
@@ -518,44 +514,44 @@ load_acl() {
}
[ "$ENABLED_DEFAULT_ACL" == 1 ] && [ "$CLIENT_PROXY" == 1 ] && {
# 加载默认代理模式
msg="【默认】,"
local comment_d="$(i18n "Default")"
msg="$(i18n "[%s]," ${comment_d})"
local ipt_tmp=$ipt_n
[ -n "${is_tproxy}" ] && ipt_tmp=$ipt_m
[ "$TCP_NO_REDIR_PORTS" != "disable" ] && {
add_port_rules "$ip6t_m -A PSW2 $(comment "默认") -p tcp" $TCP_NO_REDIR_PORTS "-j RETURN"
add_port_rules "$ipt_tmp -A PSW2 $(comment "默认") -p tcp" $TCP_NO_REDIR_PORTS "-j RETURN"
add_port_rules "$ip6t_m -A PSW2 $(comment "${comment_d}") -p tcp" $TCP_NO_REDIR_PORTS "-j RETURN"
add_port_rules "$ipt_tmp -A PSW2 $(comment "${comment_d}") -p tcp" $TCP_NO_REDIR_PORTS "-j RETURN"
if ! has_1_65535 "$TCP_NO_REDIR_PORTS"; then
echolog " - ${msg}不代理 TCP 端口[${TCP_NO_REDIR_PORTS}]"
log 2 "${msg}$(i18n "not proxy %s port [%s]" "TCP" "${TCP_NO_REDIR_PORTS}")"
else
TCP_PROXY_MODE="disable"
echolog " - ${msg}不代理所有 TCP 端口"
log 2 "${msg}$(i18n "not proxy all %s" "TCP")"
fi
}
[ "$UDP_NO_REDIR_PORTS" != "disable" ] && {
add_port_rules "$ip6t_m -A PSW2 $(comment "默认") -p udp" $UDP_NO_REDIR_PORTS "-j RETURN"
add_port_rules "$ipt_tmp -A PSW2 $(comment "默认") -p udp" $UDP_NO_REDIR_PORTS "-j RETURN"
add_port_rules "$ip6t_m -A PSW2 $(comment "${comment_d}") -p udp" $UDP_NO_REDIR_PORTS "-j RETURN"
add_port_rules "$ipt_tmp -A PSW2 $(comment "${comment_d}") -p udp" $UDP_NO_REDIR_PORTS "-j RETURN"
if ! has_1_65535 "$UDP_NO_REDIR_PORTS"; then
echolog " - ${msg}不代理 UDP 端口[${UDP_NO_REDIR_PORTS}]"
log 2 "${msg}$(i18n "not proxy %s port [%s]" "UDP" "${UDP_NO_REDIR_PORTS}")"
else
UDP_PROXY_MODE="disable"
echolog " - ${msg}不代理所有 UDP 端口"
log 2 "${msg}$(i18n "not proxy all %s" "UDP")"
fi
}
if ([ "$TCP_PROXY_MODE" != "disable" ] || [ "$UDP_PROXY_MODE" != "disable" ]) && [ -n "$NODE" ]; then
[ -n "$DNS_REDIRECT_PORT" ] && {
$ipt_n -A PSW2_DNS $(comment "默认") -p udp --dport 53 -j REDIRECT --to-ports $DNS_REDIRECT_PORT
$ip6t_n -A PSW2_DNS $(comment "默认") -p udp --dport 53 -j REDIRECT --to-ports $DNS_REDIRECT_PORT 2>/dev/null
$ipt_n -A PSW2_DNS $(comment "默认") -p tcp --dport 53 -j REDIRECT --to-ports $DNS_REDIRECT_PORT
$ip6t_n -A PSW2_DNS $(comment "默认") -p tcp --dport 53 -j REDIRECT --to-ports $DNS_REDIRECT_PORT 2>/dev/null
$ipt_n -A PSW2_DNS $(comment "${comment_d}") -p udp --dport 53 -j REDIRECT --to-ports $DNS_REDIRECT_PORT
$ip6t_n -A PSW2_DNS $(comment "${comment_d}") -p udp --dport 53 -j REDIRECT --to-ports $DNS_REDIRECT_PORT 2>/dev/null
$ipt_n -A PSW2_DNS $(comment "${comment_d}") -p tcp --dport 53 -j REDIRECT --to-ports $DNS_REDIRECT_PORT
$ip6t_n -A PSW2_DNS $(comment "${comment_d}") -p tcp --dport 53 -j REDIRECT --to-ports $DNS_REDIRECT_PORT 2>/dev/null
}
fi
if [ "$TCP_PROXY_MODE" != "disable" ] && [ -n "$NODE" ]; then
msg2="${msg}使用 TCP 节点[$(config_n_get $NODE remarks)]"
msg2="${msg}$(i18n "Use the %s node [%s]" "TCP" "$(config_n_get $NODE remarks)")"
if [ -n "${is_tproxy}" ]; then
msg2="${msg2}(TPROXY:${REDIR_PORT})"
ipt_j="-j PSW2_RULE"
@@ -565,48 +561,48 @@ load_acl() {
fi
[ "$accept_icmp" = "1" ] && {
$ipt_n -A PSW2 $(comment "默认") -p icmp -d $FAKE_IP $(REDIRECT)
add_shunt_t_rule "${SHUNT_LIST4}" "$ipt_n -A PSW2 $(comment "默认") -p icmp" "$(REDIRECT)"
$ipt_n -A PSW2 $(comment "默认") -p icmp $(REDIRECT)
$ipt_n -A PSW2 $(comment "${comment_d}") -p icmp -d $FAKE_IP $(REDIRECT)
add_shunt_t_rule "${SHUNT_LIST4}" "$ipt_n -A PSW2 $(comment "${comment_d}") -p icmp" "$(REDIRECT)"
$ipt_n -A PSW2 $(comment "${comment_d}") -p icmp $(REDIRECT)
}
[ "$accept_icmpv6" = "1" ] && [ "$PROXY_IPV6" == "1" ] && {
$ip6t_n -A PSW2 $(comment "默认") -p ipv6-icmp -d $FAKE_IP_6 $(REDIRECT)
add_shunt_t_rule "${SHUNT_LIST6}" "$ip6t_n -A PSW2 $(comment "默认") -p ipv6-icmp" "$(REDIRECT)"
$ip6t_n -A PSW2 $(comment "默认") -p ipv6-icmp $(REDIRECT)
$ip6t_n -A PSW2 $(comment "${comment_d}") -p ipv6-icmp -d $FAKE_IP_6 $(REDIRECT)
add_shunt_t_rule "${SHUNT_LIST6}" "$ip6t_n -A PSW2 $(comment "${comment_d}") -p ipv6-icmp" "$(REDIRECT)"
$ip6t_n -A PSW2 $(comment "${comment_d}") -p ipv6-icmp $(REDIRECT)
}
$ipt_tmp -A PSW2 $(comment "默认") -p tcp -d $FAKE_IP ${ipt_j}
add_shunt_t_rule "${SHUNT_LIST4}" "$ipt_tmp -A PSW2 $(comment "默认") -p tcp" "${ipt_j}" $TCP_REDIR_PORTS
add_port_rules "$ipt_tmp -A PSW2 $(comment "默认") -p tcp" $TCP_REDIR_PORTS "${ipt_j}"
[ -n "${is_tproxy}" ] && $ipt_m -A PSW2 $(comment "默认") -p tcp $(REDIRECT $REDIR_PORT TPROXY)
$ipt_tmp -A PSW2 $(comment "${comment_d}") -p tcp -d $FAKE_IP ${ipt_j}
add_shunt_t_rule "${SHUNT_LIST4}" "$ipt_tmp -A PSW2 $(comment "${comment_d}") -p tcp" "${ipt_j}" $TCP_REDIR_PORTS
add_port_rules "$ipt_tmp -A PSW2 $(comment "${comment_d}") -p tcp" $TCP_REDIR_PORTS "${ipt_j}"
[ -n "${is_tproxy}" ] && $ipt_m -A PSW2 $(comment "${comment_d}") -p tcp $(REDIRECT $REDIR_PORT TPROXY)
[ "$PROXY_IPV6" == "1" ] && {
$ip6t_m -A PSW2 $(comment "默认") -p tcp -d $FAKE_IP_6 -j PSW2_RULE
add_shunt_t_rule "${SHUNT_LIST6}" "$ip6t_m -A PSW2 $(comment "默认") -p tcp" "-j PSW2_RULE" $TCP_REDIR_PORTS
add_port_rules "$ip6t_m -A PSW2 $(comment "默认") -p tcp" $TCP_REDIR_PORTS "-j PSW2_RULE"
$ip6t_m -A PSW2 $(comment "默认") -p tcp $(REDIRECT $REDIR_PORT TPROXY)
$ip6t_m -A PSW2 $(comment "${comment_d}") -p tcp -d $FAKE_IP_6 -j PSW2_RULE
add_shunt_t_rule "${SHUNT_LIST6}" "$ip6t_m -A PSW2 $(comment "${comment_d}") -p tcp" "-j PSW2_RULE" $TCP_REDIR_PORTS
add_port_rules "$ip6t_m -A PSW2 $(comment "${comment_d}") -p tcp" $TCP_REDIR_PORTS "-j PSW2_RULE"
$ip6t_m -A PSW2 $(comment "${comment_d}") -p tcp $(REDIRECT $REDIR_PORT TPROXY)
}
echolog "${msg2}"
log 2 "${msg2}"
fi
if [ "$UDP_PROXY_MODE" != "disable" ] && [ -n "$NODE" ]; then
msg2="${msg}使用 UDP 节点[$(config_n_get $NODE remarks)](TPROXY:${REDIR_PORT})"
msg2="${msg}$(i18n "Use the %s node [%s]" "UDP" "$(config_n_get $NODE remarks)")(TPROXY:${REDIR_PORT})"
$ipt_m -A PSW2 $(comment "默认") -p udp -d $FAKE_IP -j PSW2_RULE
add_shunt_t_rule "${SHUNT_LIST4}" "$ipt_m -A PSW2 $(comment "默认") -p udp" "-j PSW2_RULE" $UDP_REDIR_PORTS
add_port_rules "$ipt_m -A PSW2 $(comment "默认") -p udp" $UDP_REDIR_PORTS "-j PSW2_RULE"
$ipt_m -A PSW2 $(comment "默认") -p udp $(REDIRECT $REDIR_PORT TPROXY)
$ipt_m -A PSW2 $(comment "${comment_d}") -p udp -d $FAKE_IP -j PSW2_RULE
add_shunt_t_rule "${SHUNT_LIST4}" "$ipt_m -A PSW2 $(comment "${comment_d}") -p udp" "-j PSW2_RULE" $UDP_REDIR_PORTS
add_port_rules "$ipt_m -A PSW2 $(comment "${comment_d}") -p udp" $UDP_REDIR_PORTS "-j PSW2_RULE"
$ipt_m -A PSW2 $(comment "${comment_d}") -p udp $(REDIRECT $REDIR_PORT TPROXY)
[ "$PROXY_IPV6" == "1" ] && {
$ip6t_m -A PSW2 $(comment "默认") -p udp -d $FAKE_IP_6 -j PSW2_RULE
add_shunt_t_rule "${SHUNT_LIST6}" "$ip6t_m -A PSW2 $(comment "默认") -p udp" "-j PSW2_RULE" $UDP_REDIR_PORTS
add_port_rules "$ip6t_m -A PSW2 $(comment "默认") -p udp" $UDP_REDIR_PORTS "-j PSW2_RULE"
$ip6t_m -A PSW2 $(comment "默认") -p udp $(REDIRECT $REDIR_PORT TPROXY)
$ip6t_m -A PSW2 $(comment "${comment_d}") -p udp -d $FAKE_IP_6 -j PSW2_RULE
add_shunt_t_rule "${SHUNT_LIST6}" "$ip6t_m -A PSW2 $(comment "${comment_d}") -p udp" "-j PSW2_RULE" $UDP_REDIR_PORTS
add_port_rules "$ip6t_m -A PSW2 $(comment "${comment_d}") -p udp" $UDP_REDIR_PORTS "-j PSW2_RULE"
$ip6t_m -A PSW2 $(comment "${comment_d}") -p udp $(REDIRECT $REDIR_PORT TPROXY)
}
echolog "${msg2}"
log 2 "${msg2}"
fi
}
}
@@ -616,14 +612,14 @@ filter_haproxy() {
local ip=$(get_host_ip ipv4 $(echo $item | awk -F ":" '{print $1}') 1)
[ -n "$ip" ] && ipset -q add $IPSET_VPS $ip
done
echolog "加入负载均衡的节点到ipset[$IPSET_VPS]直连完成"
log_i18n 1 "Add node to the load balancer is directly connected to %s[%s]." "ipset" "${IPSET_VPS}"
}
filter_vpsip() {
uci show $CONFIG | grep -E "(.address=|.download_address=)" | cut -d "'" -f 2 | grep -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}" | grep -v "^127\.0\.0\.1$" | sed -e "/^$/d" | sed -e "s/^/add $IPSET_VPS &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
echolog " - [$?]加入所有IPv4节点到ipset[$IPSET_VPS]直连完成"
#log 1 "$(i18n "Add all %s nodes to %s[%s] direct connection complete." "IPv4" "ipset" "${$IPSET_VPS}")"
uci show $CONFIG | grep -E "(.address=|.download_address=)" | cut -d "'" -f 2 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "/^$/d" | sed -e "s/^/add $IPSET_VPS6 &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
echolog " - [$?]加入所有IPv6节点到ipset[$IPSET_VPS6]直连完成"
#log 1 "$(i18n "Add all %s nodes to %s[%s] direct connection complete." "IPv6" "ipset" "${$IPSET_VPS6}")"
}
filter_server_port() {
@@ -671,7 +667,8 @@ filter_direct_node_list() {
}
add_firewall_rule() {
echolog "开始加载 iptables 防火墙规则..."
log_i18n 0 "Starting to load %s firewall rules..." "iptables"
ipset -! create $IPSET_LOCAL nethash maxelem 1048576
ipset -! create $IPSET_LAN nethash maxelem 1048576
ipset -! create $IPSET_VPS nethash maxelem 1048576
@@ -695,14 +692,14 @@ add_firewall_rule() {
$(gen_lanlist_6 | sed -e "s/^/add $IPSET_LAN6 /")
EOF
# 忽略特殊IP段
# Ignore special IP ranges
local lan_ifname lan_ip
lan_ifname=$(uci -q -p /tmp/state get network.lan.ifname)
[ -n "$lan_ifname" ] && {
lan_ip=$(ip address show $lan_ifname | grep -w "inet" | awk '{print $2}')
lan_ip6=$(ip address show $lan_ifname | grep -w "inet6" | awk '{print $2}')
#echolog "本机IPv4网段互访直连${lan_ip}"
#echolog "本机IPv6网段互访直连${lan_ip6}"
#log_i18n 1 "local network segments (%s) direct connection: %s" "IPv4" "${lan_ip}"
#log_i18n 1 "local network segments (%s) direct connection: %s" "IPv6" "${lan_ip6}"
[ -n "$lan_ip" ] && ipset -! -R <<-EOF
$(echo $lan_ip | sed -e "s/ /\n/g" | sed -e "s/^/add $IPSET_LAN /")
@@ -714,18 +711,16 @@ add_firewall_rule() {
}
[ -n "$ISP_DNS" ] && {
#echolog "处理 ISP DNS 例外..."
for ispip in $ISP_DNS; do
ipset -! add $IPSET_LAN $ispip
echolog " - [$?]追加ISP IPv4 DNS到白名单${ispip}"
log_i18n 1 "$(i18n "Add ISP %s DNS to the whitelist: %s" "IPv4" "${ispip}")"
done
}
[ -n "$ISP_DNS6" ] && {
#echolog "处理 ISP IPv6 DNS 例外..."
for ispip6 in $ISP_DNS6; do
ipset -! add $IPSET_LAN6 $ispip6
echolog " - [$?]追加ISP IPv6 DNS到白名单${ispip6}"
log_i18n 1 "$(i18n "Add ISP %s DNS to the whitelist: %s" "IPv6" "${ispip6}")"
done
}
@@ -734,10 +729,11 @@ add_firewall_rule() {
ipset -! create $ipset_global_white nethash maxelem 1048576 timeout 259200
ipset -! create $ipset_global_white6 nethash family inet6 maxelem 1048576 timeout 259200
#分流规则的IP列表(使用分流节点时导入)
# Shunt rules IP list (import when use shunt node)
gen_shunt_list "${NODE}" SHUNT_LIST4 SHUNT_LIST6 ${WRITE_IPSET_DIRECT} ${ipset_global_white} ${ipset_global_white6}
# 过滤所有节点IP
# Filter all node IPs
filter_vpsip > /dev/null 2>&1 &
filter_haproxy > /dev/null 2>&1 &
@@ -799,7 +795,7 @@ add_firewall_rule() {
local dns_address=$(echo $auto_dns | awk -F '#' '{print $1}')
local dns_port=$(echo $auto_dns | awk -F '#' '{print $2}')
$ipt_m -A PSW2_OUTPUT -p udp -d ${dns_address} --dport ${dns_port:-53} -j RETURN
echolog " - [$?]追加直连DNS到iptables${dns_address}:${dns_port:-53}"
log_i18n 1 "$(i18n "Add direct DNS to %s: %s" "iptables" "${dns_address}:${dns_port:-53}")"
done
}
$ipt_m -A PSW2_OUTPUT -m mark --mark 0xff -j RETURN
@@ -861,15 +857,15 @@ add_firewall_rule() {
TCP_LOCALHOST_PROXY=$LOCALHOST_PROXY
UDP_LOCALHOST_PROXY=$LOCALHOST_PROXY
msg="【路由器本机】,"
msg="$(i18n "[Local],")"
[ "$TCP_NO_REDIR_PORTS" != "disable" ] && {
add_port_rules "$ipt_tmp -A PSW2_OUTPUT -p tcp" $TCP_NO_REDIR_PORTS "-j RETURN"
add_port_rules "$ip6t_m -A PSW2_OUTPUT -p tcp" $TCP_NO_REDIR_PORTS "-j RETURN"
if ! has_1_65535 "$TCP_NO_REDIR_PORTS"; then
echolog " - ${msg}不代理 TCP 端口[${TCP_NO_REDIR_PORTS}]"
log 1 "${msg}$(i18n "not proxy %s port [%s]" "TCP" "${TCP_NO_REDIR_PORTS}")"
else
unset TCP_LOCALHOST_PROXY
echolog " - ${msg}不代理所有 TCP"
log 1 "${msg}$(i18n "not proxy all %s" "TCP")"
fi
}
@@ -877,10 +873,10 @@ add_firewall_rule() {
add_port_rules "$ipt_m -A PSW2_OUTPUT -p udp" $UDP_NO_REDIR_PORTS "-j RETURN"
add_port_rules "$ip6t_m -A PSW2_OUTPUT -p udp" $UDP_NO_REDIR_PORTS "-j RETURN"
if ! has_1_65535 "$UDP_NO_REDIR_PORTS"; then
echolog " - ${msg}不代理 UDP 端口[${UDP_NO_REDIR_PORTS}]"
log 1 "${msg}$(i18n "not proxy %s port [%s]" "UDP" "${UDP_NO_REDIR_PORTS}")"
else
unset UDP_LOCALHOST_PROXY
echolog " - ${msg}不代理所有 UDP"
log 1 "${msg}$(i18n "not proxy all %s" "UDP")"
fi
}
@@ -892,8 +888,10 @@ add_firewall_rule() {
$ip6t_n -A OUTPUT $(comment "PSW2_DNS") -p tcp -o lo --dport 53 -j REDIRECT --to-ports $DNS_REDIRECT_PORT 2>/dev/null
}
fi
# 加载路由器自身代理 TCP
local comment_l="$(i18n "Local")"
# Loading local router proxy TCP
if [ -n "$NODE" ] && [ "$TCP_LOCALHOST_PROXY" = "1" ]; then
[ "$accept_icmp" = "1" ] && {
$ipt_n -A OUTPUT -p icmp -j PSW2_OUTPUT
@@ -920,8 +918,8 @@ add_firewall_rule() {
add_port_rules "$ipt_tmp -A PSW2_OUTPUT -p tcp" $TCP_REDIR_PORTS "${ipt_j}"
[ -z "${is_tproxy}" ] && $ipt_n -A OUTPUT -p tcp -j PSW2_OUTPUT
[ -n "${is_tproxy}" ] && {
$ipt_m -A PSW2 $(comment "本机") -p tcp -i lo $(REDIRECT $REDIR_PORT TPROXY)
$ipt_m -A PSW2 $(comment "本机") -p tcp -i lo -j RETURN
$ipt_m -A PSW2 $(comment "${comment_l}") -p tcp -i lo $(REDIRECT $REDIR_PORT TPROXY)
$ipt_m -A PSW2 $(comment "${comment_l}") -p tcp -i lo -j RETURN
insert_rule_before "$ipt_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -p tcp -j PSW2_OUTPUT"
}
@@ -929,8 +927,8 @@ add_firewall_rule() {
$ip6t_m -A PSW2_OUTPUT -p tcp -d $FAKE_IP_6 -j PSW2_RULE
add_shunt_t_rule "${SHUNT_LIST6}" "$ip6t_m -A PSW2_OUTPUT -p tcp" "-j PSW2_RULE" $TCP_REDIR_PORTS
add_port_rules "$ip6t_m -A PSW2_OUTPUT -p tcp" $TCP_REDIR_PORTS "-j PSW2_RULE"
$ip6t_m -A PSW2 $(comment "本机") -p tcp -i lo $(REDIRECT $REDIR_PORT TPROXY)
$ip6t_m -A PSW2 $(comment "本机") -p tcp -i lo -j RETURN
$ip6t_m -A PSW2 $(comment "${comment_l}") -p tcp -i lo $(REDIRECT $REDIR_PORT TPROXY)
$ip6t_m -A PSW2 $(comment "${comment_l}") -p tcp -i lo -j RETURN
insert_rule_before "$ip6t_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -p tcp -j PSW2_OUTPUT"
}
@@ -942,21 +940,21 @@ add_firewall_rule() {
}
fi
# 加载路由器自身代理 UDP
# Loading local router proxy UDP
if [ -n "$NODE" ] && [ "$UDP_LOCALHOST_PROXY" = "1" ]; then
$ipt_m -A PSW2_OUTPUT -p udp -d $FAKE_IP -j PSW2_RULE
add_shunt_t_rule "${SHUNT_LIST4}" "$ipt_m -A PSW2_OUTPUT -p udp" "-j PSW2_RULE" $UDP_REDIR_PORTS
add_port_rules "$ipt_m -A PSW2_OUTPUT -p udp" $UDP_REDIR_PORTS "-j PSW2_RULE"
$ipt_m -A PSW2 $(comment "本机") -p udp -i lo $(REDIRECT $REDIR_PORT TPROXY)
$ipt_m -A PSW2 $(comment "本机") -p udp -i lo -j RETURN
$ipt_m -A PSW2 $(comment "${comment_l}") -p udp -i lo $(REDIRECT $REDIR_PORT TPROXY)
$ipt_m -A PSW2 $(comment "${comment_l}") -p udp -i lo -j RETURN
insert_rule_before "$ipt_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -p udp -j PSW2_OUTPUT"
[ "$PROXY_IPV6" == "1" ] && {
$ip6t_m -A PSW2_OUTPUT -p udp -d $FAKE_IP_6 -j PSW2_RULE
add_shunt_t_rule "${SHUNT_LIST6}" "$ip6t_m -A PSW2_OUTPUT -p udp" "-j PSW2_RULE" $UDP_REDIR_PORTS
add_port_rules "$ip6t_m -A PSW2_OUTPUT -p udp" $UDP_REDIR_PORTS "-j PSW2_RULE"
$ip6t_m -A PSW2 $(comment "本机") -p udp -i lo $(REDIRECT $REDIR_PORT TPROXY)
$ip6t_m -A PSW2 $(comment "本机") -p udp -i lo -j RETURN
$ip6t_m -A PSW2 $(comment "${comment_l}") -p udp -i lo $(REDIRECT $REDIR_PORT TPROXY)
$ip6t_m -A PSW2 $(comment "${comment_l}") -p udp -i lo -j RETURN
insert_rule_before "$ip6t_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -p udp -j PSW2_OUTPUT"
}
@@ -978,12 +976,11 @@ add_firewall_rule() {
$ip6t_m -A PSW2 -p udp --dport 53 -j RETURN
}
# 加载ACLS
load_acl
filter_direct_node_list > /dev/null 2>&1 &
echolog "防火墙规则加载完成!"
log_i18n 0 "%s firewall rules load complete!" "iptables"
}
del_firewall_rule() {
@@ -1006,11 +1003,11 @@ del_firewall_rule() {
ip -6 rule del fwmark 1 table 100 2>/dev/null
ip -6 route del local ::/0 dev lo table 100 2>/dev/null
$DIR/app.sh echolog "删除 iptables 规则完成。"
$DIR/app.sh log_i18n 0 "Delete %s rules is complete." "iptables"
}
flush_ipset() {
$DIR/app.sh echolog "清空 IPSet"
$DIR/app.sh log_i18n 0 "Clear %s." "IPSet"
for _name in $(ipset list | grep "Name: " | grep "passwall2_" | awk '{print $2}'); do
destroy_ipset ${_name}
done

View File

@@ -36,7 +36,7 @@ while [ "$ENABLED" -eq 1 ]; do
[ -n "$(echo $cmd_check | grep "dns2socks")" ] && cmd_check=$(echo $cmd_check | sed "s#:# #g")
icount=$(pgrep -f "$(echo $cmd_check)" | wc -l)
if [ $icount = 0 ]; then
#echo "${cmd} 进程挂掉,重启" >> /tmp/log/passwall2.log
#echo "${cmd} crashed, restarting." >> /tmp/log/passwall2.log
eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 &
fi
done

View File

@@ -89,7 +89,7 @@ insert_rule_after() {
RULE_LAST_INDEX() {
[ $# -ge 3 ] || {
echolog "索引列举方式不正确(nftables),终止执行!"
log_i18n 1 "Incorrect index listing method (%s), execution terminated!" "nftables"
return 1
}
local table_name="${1}"; shift
@@ -196,10 +196,6 @@ gen_nftset() {
[ -n "${1}" ] && insert_nftset $nftset_name $timeout_argument_element $@
}
get_action_chain_name() {
echo "全局代理"
}
gen_lanlist() {
cat <<-EOF
0.0.0.0/8
@@ -302,7 +298,7 @@ gen_shunt_list() {
insert_nftset $nftset_v4 "0" $(get_geoip $_geoip_code ipv4 | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}")
insert_nftset $nftset_v6 "0" $(get_geoip $_geoip_code ipv6 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}")
fi
echolog " - [$?]解析分流规则[$shunt_id]-[geoip:${_geoip_code}]加入到 NFTSET 完成"
log 1 "$(i18n "parse the traffic splitting rules[%s]-[geoip:%s] add to %s to complete." "${shunt_id}" "${_geoip_code}" "NFTSET")"
}
}
}
@@ -346,7 +342,7 @@ add_shunt_t_rule() {
load_acl() {
[ "$ENABLED_ACLS" == 1 ] && {
echolog "访问控制:"
log_i18n 1 "Access Control:"
acl_app
for sid in $(ls -F ${TMP_ACL_PATH} | grep '/$' | awk -F '/' '{print $1}' | grep -v 'default'); do
eval $(uci -q show "${CONFIG}.${sid}" | cut -d'.' -sf 3-)
@@ -381,7 +377,7 @@ load_acl() {
gen_nftset $nftset_white ipv4_addr 3d 3d
gen_nftset $nftset_white6 ipv6_addr 3d 3d
#分流规则的IP列表(使用分流节点时导入)
# Shunt rules IP list (import when use shunt node)
gen_shunt_list "${node}" shunt_list4 shunt_list6 ${write_ipset_direct} ${nftset_white} ${nftset_white6}
fi
}
@@ -398,48 +394,48 @@ load_acl() {
network_get_device device "${interface}"
[ -z "${device}" ] && device="${interface}"
_ipt_source="iifname ${device} "
msg="源接口【${device}】,"
msg=$(i18n "Source iface [%s]," "${device}")
else
msg="源接口【所有】,"
msg=$(i18n "Source iface [%s]," $(i18n "All"))
fi
if [ -n "$(echo ${i} | grep '^iprange:')" ]; then
_iprange=$(echo ${i} | sed 's#iprange:##g')
_ipt_source=$(factor ${_iprange} "${_ipt_source}ip saddr")
msg="${msg}IP range【${_iprange}】,"
msg="${msg}$(i18n "IP range [%s]," "${_iprange}")"
_ipv4="1"
unset _iprange
elif [ -n "$(echo ${i} | grep '^ipset:')" ]; then
_ipset=$(echo ${i} | sed 's#ipset:##g')
_ipt_source="${_ipt_source}ip daddr @${_ipset}"
msg="${msg}NFTset${_ipset}】,"
msg="${msg}Nftset$(i18n "[%s]," "${_ipset}")"
unset _ipset
elif [ -n "$(echo ${i} | grep '^ip:')" ]; then
_ip=$(echo ${i} | sed 's#ip:##g')
_ipt_source=$(factor ${_ip} "${_ipt_source}ip saddr")
msg="${msg}IP${_ip}】,"
msg="${msg}IP$(i18n "[%s]," "${_ip}")"
_ipv4="1"
unset _ip
elif [ -n "$(echo ${i} | grep '^mac:')" ]; then
_mac=$(echo ${i} | sed 's#mac:##g')
_ipt_source=$(factor ${_mac} "${_ipt_source}ether saddr")
msg="${msg}MAC${_mac}】,"
msg="${msg}MAC$(i18n "[%s]," "${_mac}")"
unset _mac
elif [ -n "$(echo ${i} | grep '^any')" ]; then
msg="${msg}所有设备,"
msg="${msg}$(i18n "All device,")"
else
continue
fi
msg="$remarks】,${msg}"
msg="$(i18n "[%s]," "${remarks}")${msg}"
[ "$tcp_no_redir_ports" != "disable" ] && {
if ! has_1_65535 "$tcp_no_redir_ports"; then
nft "add rule $NFTABLE_NAME $nft_prerouting_chain ${_ipt_source} ip protocol tcp $(factor $tcp_no_redir_ports "tcp dport") counter return comment \"$remarks\""
[ "$_ipv4" != "1" ] && nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 ${_ipt_source} meta l4proto tcp $(factor $tcp_no_redir_ports "tcp dport") counter return comment \"$remarks\""
echolog " - ${msg}不代理 TCP 端口[${tcp_no_redir_ports}]"
log 2 "${msg}$(i18n "not proxy %s port [%s]" "TCP" "${tcp_no_redir_ports}")"
else
#结束时会return无需加多余的规则。
# It will return when it ends, so no extra rules are needed.
tcp_proxy_mode="disable"
echolog " - ${msg}不代理所有 TCP"
log 2 "${msg}$(i18n "not proxy all %s" "TCP")"
fi
}
@@ -447,11 +443,11 @@ load_acl() {
if ! has_1_65535 "$udp_no_redir_ports"; then
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp ${_ipt_source} $(factor $udp_no_redir_ports "udp dport") counter return comment \"$remarks\""
[ "$_ipv4" != "1" ] && nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp ${_ipt_source} $(factor $udp_no_redir_ports "udp dport") counter return comment \"$remarks\"" 2>/dev/null
echolog " - ${msg}不代理 UDP 端口[${udp_no_redir_ports}]"
log 2 "${msg}$(i18n "not proxy %s port [%s]" "UDP" "${udp_no_redir_ports}")"
else
#结束时会return无需加多余的规则。
# It will return when it ends, so no extra rules are needed.
udp_proxy_mode="disable"
echolog " - ${msg}不代理所有 UDP"
log 2 "${msg}$(i18n "not proxy all %s" "UDP")"
fi
}
@@ -461,7 +457,7 @@ load_acl() {
nft "add rule $NFTABLE_NAME PSW2_DNS ip protocol tcp ${_ipt_source} tcp dport 53 counter redirect to :$dns_redirect_port comment \"$remarks\""
nft "add rule $NFTABLE_NAME PSW2_DNS meta l4proto udp ${_ipt_source} udp dport 53 counter redirect to :$dns_redirect_port comment \"$remarks\""
nft "add rule $NFTABLE_NAME PSW2_DNS meta l4proto tcp ${_ipt_source} tcp dport 53 counter redirect to :$dns_redirect_port comment \"$remarks\""
[ -z "$(get_cache_var "ACL_${sid}_default")" ] && echolog " - ${msg}使用与全局配置不相同节点已将DNS强制重定向到专用 DNS 服务器。"
[ -z "$(get_cache_var "ACL_${sid}_default")" ] && log 2 "${msg}$(i18n "Using a node that is different from the global configuration, DNS has been forcibly redirected to a dedicated DNS server.")"
}
else
nft "add rule $NFTABLE_NAME PSW2_DNS ip protocol udp ${_ipt_source} udp dport 53 counter return comment \"$remarks\""
@@ -471,7 +467,7 @@ load_acl() {
fi
[ "$tcp_proxy_mode" != "disable" ] && [ -n "$redir_port" ] && {
msg2="${msg}使用 TCP 节点[$node_remark]"
msg2="${msg}$(i18n "Use the %s node [%s]" "TCP" "${node_remark}")"
if [ -n "${is_tproxy}" ]; then
msg2="${msg2}(TPROXY:${redir_port})"
nft_chain="PSW2_MANGLE"
@@ -507,13 +503,13 @@ load_acl() {
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp ${_ipt_source} $(factor $tcp_redir_ports "tcp dport") counter jump PSW2_RULE comment \"$remarks\"" 2>/dev/null
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp ${_ipt_source} $(REDIRECT $redir_port TPROXY) comment \"$remarks\"" 2>/dev/null
}
echolog " - ${msg2}"
log 2 "${msg2}"
}
nft "add rule $NFTABLE_NAME $nft_prerouting_chain ip protocol tcp ${_ipt_source} counter return comment \"$remarks\""
[ "$_ipv4" != "1" ] && nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp ${_ipt_source} counter return comment \"$remarks\"" 2>/dev/null
[ "$udp_proxy_mode" != "disable" ] && [ -n "$redir_port" ] && {
msg2="${msg}使用 UDP 节点[$node_remark](TPROXY:${redir_port})"
msg2="${msg}$(i18n "Use the %s node [%s]" "UDP" "${node_remark}")(TPROXY:${redir_port})"
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp ${_ipt_source} ip daddr $FAKE_IP counter jump PSW2_RULE comment \"$remarks\""
add_shunt_t_rule "${shunt_list4}" "nft add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp ${_ipt_source} $(factor $udp_redir_ports "udp dport") ip daddr" "counter jump PSW2_RULE" "$remarks"
@@ -526,7 +522,7 @@ load_acl() {
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp ${_ipt_source} $(factor $udp_redir_ports "udp dport") counter jump PSW2_RULE comment \"$remarks\"" 2>/dev/null
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp ${_ipt_source} $(REDIRECT $redir_port TPROXY) comment \"$remarks\"" 2>/dev/null
}
echolog " - ${msg2}"
log 2 "${msg2}"
}
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp ${_ipt_source} counter return comment \"$remarks\""
[ "$_ipv4" != "1" ] && nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp ${_ipt_source} counter return comment \"$remarks\"" 2>/dev/null
@@ -538,42 +534,42 @@ load_acl() {
}
[ "$ENABLED_DEFAULT_ACL" == 1 ] && [ "$CLIENT_PROXY" == 1 ] && {
# 加载默认代理模式
msg="【默认】,"
local comment="$(i18n "Default")"
msg="$(i18n "[%s]," ${comment})"
[ "$TCP_NO_REDIR_PORTS" != "disable" ] && {
nft "add rule $NFTABLE_NAME $nft_prerouting_chain ip protocol tcp $(factor $TCP_NO_REDIR_PORTS "tcp dport") counter return comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp $(factor $TCP_NO_REDIR_PORTS "tcp dport") counter return comment \"默认\""
nft "add rule $NFTABLE_NAME $nft_prerouting_chain ip protocol tcp $(factor $TCP_NO_REDIR_PORTS "tcp dport") counter return comment \"${comment}\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp $(factor $TCP_NO_REDIR_PORTS "tcp dport") counter return comment \"${comment}\""
if ! has_1_65535 "$TCP_NO_REDIR_PORTS"; then
echolog " - ${msg}不代理 TCP 端口[${TCP_NO_REDIR_PORTS}]"
log 2 "${msg}$(i18n "not proxy %s port [%s]" "TCP" "${TCP_NO_REDIR_PORTS}")"
else
TCP_PROXY_MODE="disable"
echolog " - ${msg}不代理所有 TCP 端口"
log 2 "${msg}$(i18n "not proxy all %s" "TCP")"
fi
}
[ "$UDP_NO_REDIR_PORTS" != "disable" ] && {
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 counter meta l4proto udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return comment \"${comment}\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 counter meta l4proto udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return comment \"${comment}\""
if ! has_1_65535 "$UDP_NO_REDIR_PORTS"; then
echolog " - ${msg}不代理 UDP 端口[${UDP_NO_REDIR_PORTS}]"
log 2 "${msg}$(i18n "not proxy %s port [%s]" "UDP" "${UDP_NO_REDIR_PORTS}")"
else
UDP_PROXY_MODE="disable"
echolog " - ${msg}不代理所有 UDP 端口"
log 2 "${msg}$(i18n "not proxy all %s" "UDP")"
fi
}
if ([ "$TCP_PROXY_MODE" != "disable" ] || [ "$UDP_PROXY_MODE" != "disable" ]) && [ -n "$NODE" ]; then
[ -n "$DNS_REDIRECT_PORT" ] && {
nft "add rule $NFTABLE_NAME PSW2_DNS ip protocol udp udp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_DNS ip protocol tcp tcp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_DNS meta l4proto udp udp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_DNS meta l4proto tcp tcp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_DNS ip protocol udp udp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"${comment}\""
nft "add rule $NFTABLE_NAME PSW2_DNS ip protocol tcp tcp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"${comment}\""
nft "add rule $NFTABLE_NAME PSW2_DNS meta l4proto udp udp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"${comment}\""
nft "add rule $NFTABLE_NAME PSW2_DNS meta l4proto tcp tcp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"${comment}\""
}
fi
if [ "$TCP_PROXY_MODE" != "disable" ] && [ -n "$NODE" ]; then
msg2="${msg}使用 TCP 节点[$(config_n_get $NODE remarks)]"
msg2="${msg}$(i18n "Use the %s node [%s]" "TCP" "$(config_n_get $NODE remarks)")"
if [ -n "${is_tproxy}" ]; then
msg2="${msg2}(TPROXY:${REDIR_PORT})"
nft_chain="PSW2_MANGLE"
@@ -585,50 +581,50 @@ load_acl() {
fi
[ "$accept_icmp" = "1" ] && {
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT ip protocol icmp ip daddr $FAKE_IP $(REDIRECT) comment \"默认\""
add_shunt_t_rule "${SHUNT_LIST4}" "nft add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT ip protocol icmp ip daddr" "$(REDIRECT)" "默认"
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT ip protocol icmp $(REDIRECT) comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT ip protocol icmp return comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT ip protocol icmp ip daddr $FAKE_IP $(REDIRECT) comment \"${comment}\""
add_shunt_t_rule "${SHUNT_LIST4}" "nft add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT ip protocol icmp ip daddr" "$(REDIRECT)" "${comment}"
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT ip protocol icmp $(REDIRECT) comment \"${comment}\""
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT ip protocol icmp return comment \"${comment}\""
}
[ "$accept_icmpv6" = "1" ] && [ "$PROXY_IPV6" == "1" ] && {
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT meta l4proto icmpv6 ip6 daddr $FAKE_IP_6 $(REDIRECT) comment \"默认\""
add_shunt_t_rule "${SHUNT_LIST6}" "nft add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT meta l4proto icmpv6 ip6 daddr" "$(REDIRECT)" "默认"
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT meta l4proto icmpv6 $(REDIRECT) comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT meta l4proto icmpv6 return comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT meta l4proto icmpv6 ip6 daddr $FAKE_IP_6 $(REDIRECT) comment \"${comment}\""
add_shunt_t_rule "${SHUNT_LIST6}" "nft add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT meta l4proto icmpv6 ip6 daddr" "$(REDIRECT)" "${comment}"
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT meta l4proto icmpv6 $(REDIRECT) comment \"${comment}\""
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT meta l4proto icmpv6 return comment \"${comment}\""
}
nft "add rule $NFTABLE_NAME $nft_chain ip protocol tcp ip daddr $FAKE_IP ${nft_j} comment \"默认\""
add_shunt_t_rule "${SHUNT_LIST4}" "nft add rule $NFTABLE_NAME $nft_chain ip protocol tcp $(factor $TCP_REDIR_PORTS "tcp dport") ip daddr" "${nft_j}" "默认"
nft "add rule $NFTABLE_NAME $nft_chain ip protocol tcp $(factor $TCP_REDIR_PORTS "tcp dport") ${nft_j} comment \"默认\""
[ -n "${is_tproxy}" ] && nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol tcp $(REDIRECT $REDIR_PORT TPROXY4) comment \"默认\""
nft "add rule $NFTABLE_NAME $nft_chain ip protocol tcp ip daddr $FAKE_IP ${nft_j} comment \"${comment}\""
add_shunt_t_rule "${SHUNT_LIST4}" "nft add rule $NFTABLE_NAME $nft_chain ip protocol tcp $(factor $TCP_REDIR_PORTS "tcp dport") ip daddr" "${nft_j}" "${comment}"
nft "add rule $NFTABLE_NAME $nft_chain ip protocol tcp $(factor $TCP_REDIR_PORTS "tcp dport") ${nft_j} comment \"${comment}\""
[ -n "${is_tproxy}" ] && nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol tcp $(REDIRECT $REDIR_PORT TPROXY4) comment \"${comment}\""
[ "$PROXY_IPV6" == "1" ] && {
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp ip6 daddr $FAKE_IP_6 jump PSW2_RULE comment \"默认\""
add_shunt_t_rule "${SHUNT_LIST6}" "nft add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp $(factor $TCP_REDIR_PORTS "tcp dport") ip6 daddr" "${nft_j}" "默认"
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp $(factor $TCP_REDIR_PORTS "tcp dport") counter jump PSW2_RULE comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp $(REDIRECT $REDIR_PORT TPROXY) comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp ip6 daddr $FAKE_IP_6 jump PSW2_RULE comment \"${comment}\""
add_shunt_t_rule "${SHUNT_LIST6}" "nft add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp $(factor $TCP_REDIR_PORTS "tcp dport") ip6 daddr" "${nft_j}" "${comment}"
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp $(factor $TCP_REDIR_PORTS "tcp dport") counter jump PSW2_RULE comment \"${comment}\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp $(REDIRECT $REDIR_PORT TPROXY) comment \"${comment}\""
}
echolog "${msg2}"
log 2 "${msg2}"
fi
if [ "$UDP_PROXY_MODE" != "disable" ] && [ -n "$NODE" ]; then
msg2="${msg}使用 UDP 节点[$(config_n_get $NODE remarks)](TPROXY:${REDIR_PORT})"
msg2="${msg}$(i18n "Use the %s node [%s]" "UDP" "$(config_n_get $NODE remarks)")(TPROXY:${REDIR_PORT})"
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp ip daddr $FAKE_IP counter jump PSW2_RULE comment \"默认\""
add_shunt_t_rule "${SHUNT_LIST4}" "nft add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp $(factor $UDP_REDIR_PORTS "udp dport") ip daddr" "counter jump PSW2_RULE" "默认"
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp $(factor $UDP_REDIR_PORTS "udp dport") counter jump PSW2_RULE comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp $(REDIRECT $REDIR_PORT TPROXY4) comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp ip daddr $FAKE_IP counter jump PSW2_RULE comment \"${comment}\""
add_shunt_t_rule "${SHUNT_LIST4}" "nft add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp $(factor $UDP_REDIR_PORTS "udp dport") ip daddr" "counter jump PSW2_RULE" "${comment}"
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp $(factor $UDP_REDIR_PORTS "udp dport") counter jump PSW2_RULE comment \"${comment}\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp $(REDIRECT $REDIR_PORT TPROXY4) comment \"${comment}\""
[ "$PROXY_IPV6" == "1" ] && {
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp ip6 daddr $FAKE_IP_6 jump PSW2_RULE comment \"默认\""
add_shunt_t_rule "${SHUNT_LIST6}" "nft add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp $(factor $UDP_REDIR_PORTS "udp dport") ip6 daddr" "counter jump PSW2_RULE" "默认"
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp $(factor $UDP_REDIR_PORTS "udp dport") counter jump PSW2_RULE comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp $(REDIRECT $REDIR_PORT TPROXY) comment \"默认\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp ip6 daddr $FAKE_IP_6 jump PSW2_RULE comment \"${comment}\""
add_shunt_t_rule "${SHUNT_LIST6}" "nft add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp $(factor $UDP_REDIR_PORTS "udp dport") ip6 daddr" "counter jump PSW2_RULE" "${comment}"
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp $(factor $UDP_REDIR_PORTS "udp dport") counter jump PSW2_RULE comment \"${comment}\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp $(REDIRECT $REDIR_PORT TPROXY) comment \"${comment}\""
}
echolog "${msg2}"
log 2 "${msg2}"
udp_flag=1
fi
}
@@ -639,7 +635,7 @@ filter_haproxy() {
local ip=$(get_host_ip ipv4 $(echo $item | awk -F ":" '{print $1}') 1)
[ -n "$ip" ] && insert_nftset $NFTSET_VPS "-1" $ip
done
echolog "加入负载均衡的节点到nftset[$NFTSET_VPS]直连完成"
log_i18n 1 "Add node to the load balancer is directly connected to %s[%s]." "nftset" "${NFTSET_VPS}"
}
filter_vps_addr() {
@@ -653,9 +649,9 @@ filter_vps_addr() {
filter_vpsip() {
insert_nftset $NFTSET_VPS "-1" $(uci show $CONFIG | grep -E "(.address=|.download_address=)" | cut -d "'" -f 2 | grep -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}" | grep -v "^127\.0\.0\.1$" | sed -e "/^$/d")
echolog " - [$?]加入所有IPv4节点到nftset[$NFTSET_VPS]直连完成"
#log 1 "$(i18n "Add all %s nodes to %s[%s] direct connection complete." "IPv4" "nftset" "${$NFTSET_VPS}")"
insert_nftset $NFTSET_VPS6 "-1" $(uci show $CONFIG | grep -E "(.address=|.download_address=)" | cut -d "'" -f 2 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "/^$/d")
echolog " - [$?]加入所有IPv6节点到nftset[$NFTSET_VPS6]直连完成"
#log 1 "$(i18n "Add all %s nodes to %s[%s] direct connection complete." "IPv6" "nftset" "${$NFTSET_VPS6}")"
}
filter_server_port() {
@@ -701,7 +697,7 @@ filter_direct_node_list() {
}
add_firewall_rule() {
echolog "开始加载 nftables 防火墙规则..."
log_i18n 0 "Starting to load %s firewall rules..." "nftables"
gen_nft_tables
gen_nftset $NFTSET_LOCAL ipv4_addr 0 "-1"
gen_nftset $NFTSET_LAN ipv4_addr 0 "-1" $(gen_lanlist)
@@ -714,32 +710,30 @@ add_firewall_rule() {
insert_nftset $NFTSET_LOCAL "-1" $(ip address show | grep -w "inet" | awk '{print $2}' | awk -F '/' '{print $1}' | sed -e "s/ /\n/g")
insert_nftset $NFTSET_LOCAL6 "-1" $(ip address show | grep -w "inet6" | awk '{print $2}' | awk -F '/' '{print $1}' | sed -e "s/ /\n/g")
# 忽略特殊IP段
# Ignore special IP ranges
local lan_ifname lan_ip
lan_ifname=$(uci -q -p /tmp/state get network.lan.ifname)
[ -n "$lan_ifname" ] && {
lan_ip=$(ip address show $lan_ifname | grep -w "inet" | awk '{print $2}')
lan_ip6=$(ip address show $lan_ifname | grep -w "inet6" | awk '{print $2}')
#echolog "本机IPv4网段互访直连${lan_ip}"
#echolog "本机IPv6网段互访直连${lan_ip6}"
#log_i18n 1 "local network segments (%s) direct connection: %s" "IPv4" "${lan_ip}"
#log_i18n 1 "local network segments (%s) direct connection: %s" "IPv6" "${lan_ip6}"
[ -n "$lan_ip" ] && insert_nftset $NFTSET_LAN "-1" $(echo $lan_ip | sed -e "s/ /\n/g")
[ -n "$lan_ip6" ] && insert_nftset $NFTSET_LAN6 "-1" $(echo $lan_ip6 | sed -e "s/ /\n/g")
}
[ -n "$ISP_DNS" ] && {
#echolog "处理 ISP DNS 例外..."
for ispip in $ISP_DNS; do
insert_nftset $NFTSET_LAN "-1" $ispip
echolog " - [$?]追加ISP IPv4 DNS到白名单${ispip}"
log_i18n 1 "$(i18n "Add ISP %s DNS to the whitelist: %s" "IPv4" "${ispip}")"
done
}
[ -n "$ISP_DNS6" ] && {
#echolog "处理 ISP IPv6 DNS 例外..."
for ispip6 in $ISP_DNS6; do
insert_nftset $NFTSET_LAN6 "-1" $ispip6
echolog " - [$?]追加ISP IPv6 DNS到白名单${ispip6}"
log_i18n 1 "$(i18n "Add ISP %s DNS to the whitelist: %s" "IPv6" "${ispip6}")"
done
}
@@ -748,10 +742,10 @@ add_firewall_rule() {
gen_nftset $nftset_global_white ipv4_addr 0 0
gen_nftset $nftset_global_white6 ipv6_addr 0 0
#分流规则的IP列表(使用分流节点时导入)
# Shunt rules IP list (import when use shunt node)
gen_shunt_list "${NODE}" SHUNT_LIST4 SHUNT_LIST6 ${WRITE_IPSET_DIRECT} ${nftset_global_white} ${nftset_global_white6}
# 过滤所有节点IP
# Filter all node IPs
filter_vpsip > /dev/null 2>&1 &
filter_haproxy > /dev/null 2>&1 &
# Prevent some conditions
@@ -807,7 +801,7 @@ add_firewall_rule() {
local dns_address=$(echo $auto_dns | awk -F '#' '{print $1}')
local dns_port=$(echo $auto_dns | awk -F '#' '{print $2}')
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE ip protocol udp ip daddr ${dns_address} $(factor ${dns_port:-53} "udp dport") counter return"
echolog " - [$?]追加直连DNS到nftables${dns_address}:${dns_port:-53}"
log_i18n 1 "$(i18n "Add direct DNS to %s: %s" "nftables" "${dns_address}:${dns_port:-53}")"
done
}
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE meta mark 0xff counter return"
@@ -888,15 +882,15 @@ add_firewall_rule() {
TCP_LOCALHOST_PROXY=$LOCALHOST_PROXY
UDP_LOCALHOST_PROXY=$LOCALHOST_PROXY
msg="【路由器本机】,"
msg="$(i18n "[Local],")"
[ "$TCP_NO_REDIR_PORTS" != "disable" ] && {
nft "add rule $NFTABLE_NAME $nft_output_chain ip protocol tcp $(factor $TCP_NO_REDIR_PORTS "tcp dport") counter return"
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 meta l4proto tcp $(factor $TCP_NO_REDIR_PORTS "tcp dport") counter return"
if ! has_1_65535 "$TCP_NO_REDIR_PORTS"; then
echolog " - ${msg}不代理 TCP 端口[${TCP_NO_REDIR_PORTS}]"
log 1 "${msg}$(i18n "not proxy %s port [%s]" "TCP" "${TCP_NO_REDIR_PORTS}")"
else
unset TCP_LOCALHOST_PROXY
echolog " - ${msg}不代理所有 TCP"
log 1 "${msg}$(i18n "not proxy all %s" "TCP")"
fi
}
@@ -904,10 +898,10 @@ add_firewall_rule() {
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE ip protocol udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return"
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 meta l4proto udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return"
if ! has_1_65535 "$UDP_NO_REDIR_PORTS"; then
echolog " - ${msg}不代理 UDP 端口[${UDP_NO_REDIR_PORTS}]"
log 1 "${msg}$(i18n "not proxy %s port [%s]" "UDP" "${UDP_NO_REDIR_PORTS}")"
else
unset UDP_LOCALHOST_PROXY
echolog " - ${msg}不代理所有 UDP"
log 1 "${msg}$(i18n "not proxy all %s" "UDP")"
fi
}
@@ -920,7 +914,9 @@ add_firewall_rule() {
}
fi
# 加载路由器自身代理 TCP
local comment_l="$(i18n "Local")"
# Loading local router proxy TCP
if [ -n "$NODE" ] && [ "$TCP_LOCALHOST_PROXY" = "1" ]; then
[ "$accept_icmp" = "1" ] && {
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT oif lo ip protocol icmp ip daddr $FAKE_IP counter redirect"
@@ -949,8 +945,8 @@ add_firewall_rule() {
nft "add rule $NFTABLE_NAME $nft_chain ip protocol tcp $(factor $TCP_REDIR_PORTS "tcp dport") ${nft_j}"
[ -z "${is_tproxy}" ] && nft "add rule $NFTABLE_NAME nat_output ip protocol tcp counter jump PSW2_OUTPUT_NAT"
[ -n "${is_tproxy}" ] && {
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol tcp iif lo $(REDIRECT $REDIR_PORT TPROXY4) comment \"本机\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol tcp iif lo counter return comment \"本机\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol tcp iif lo $(REDIRECT $REDIR_PORT TPROXY4) comment \"${comment_l}\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol tcp iif lo counter return comment \"${comment_l}\""
nft "add rule $NFTABLE_NAME mangle_output ip protocol tcp counter jump PSW2_OUTPUT_MANGLE comment \"PSW2_OUTPUT_MANGLE\""
}
@@ -958,8 +954,8 @@ add_firewall_rule() {
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 meta l4proto tcp ip6 daddr $FAKE_IP_6 jump PSW2_RULE"
add_shunt_t_rule "${SHUNT_LIST6}" "nft add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 meta l4proto tcp $(factor $TCP_REDIR_PORTS "tcp dport") ip6 daddr" "counter jump PSW2_RULE"
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 meta l4proto tcp $(factor $TCP_REDIR_PORTS "tcp dport") counter jump PSW2_RULE"
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp iif lo $(REDIRECT $REDIR_PORT TPROXY) comment \"本机\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp iif lo counter return comment \"本机\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp iif lo $(REDIRECT $REDIR_PORT TPROXY) comment \"${comment_l}\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp iif lo counter return comment \"${comment_l}\""
}
[ -d "${TMP_IFACE_PATH}" ] && {
@@ -970,21 +966,21 @@ add_firewall_rule() {
}
fi
# 加载路由器自身代理 UDP
# Loading local router proxy UDP
if [ -n "$NODE" ] && [ "$UDP_LOCALHOST_PROXY" = "1" ]; then
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE ip protocol udp ip daddr $FAKE_IP counter jump PSW2_RULE"
add_shunt_t_rule "${SHUNT_LIST4}" "nft add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE ip protocol udp $(factor $UDP_REDIR_PORTS "udp dport") ip daddr" "counter jump PSW2_RULE"
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE ip protocol udp $(factor $UDP_REDIR_PORTS "udp dport") counter jump PSW2_RULE"
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp iif lo $(REDIRECT $REDIR_PORT TPROXY4) comment \"本机\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp iif lo counter return comment \"本机\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp iif lo $(REDIRECT $REDIR_PORT TPROXY4) comment \"${comment_l}\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp iif lo counter return comment \"${comment_l}\""
nft "add rule $NFTABLE_NAME mangle_output ip protocol udp counter jump PSW2_OUTPUT_MANGLE comment \"PSW2_OUTPUT_MANGLE\""
[ "$PROXY_IPV6" == "1" ] && {
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 meta l4proto udp ip6 daddr $FAKE_IP_6 jump PSW2_RULE"
add_shunt_t_rule "${SHUNT_LIST6}" "nft add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 meta l4proto udp $(factor $UDP_REDIR_PORTS "udp dport") ip6 daddr" "counter jump PSW2_RULE"
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 meta l4proto udp $(factor $UDP_REDIR_PORTS "udp dport") counter jump PSW2_RULE"
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp iif lo $(REDIRECT $REDIR_PORT TPROXY) comment \"本机\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp iif lo counter return comment \"本机\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp iif lo $(REDIRECT $REDIR_PORT TPROXY) comment \"${comment_l}\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp iif lo counter return comment \"${comment_l}\""
}
[ -d "${TMP_IFACE_PATH}" ] && {
@@ -1002,12 +998,11 @@ add_firewall_rule() {
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp udp dport 53 counter return"
}
# 加载ACLS
load_acl
filter_direct_node_list > /dev/null 2>&1 &
echolog "防火墙规则加载完成!"
log_i18n 0 "%s firewall rules load complete!" "nftables"
}
del_firewall_rule() {
@@ -1039,11 +1034,11 @@ del_firewall_rule() {
destroy_nftset $NFTSET_LAN6
destroy_nftset $NFTSET_VPS6
$DIR/app.sh echolog "删除 nftables 规则完成。"
$DIR/app.sh log_i18n 0 "Delete %s rules is complete." "nftables"
}
flush_nftset() {
$DIR/app.sh echolog "清空 NFTSet"
$DIR/app.sh log_i18n 0 "Clear %s." "NFTSet"
for _name in $(nft -a list sets | grep -E "passwall2" | awk -F 'set ' '{print $2}' | awk '{print $1}'); do
destroy_nftset ${_name}
done

View File

@@ -49,9 +49,7 @@ local function curl(url, file)
return tonumber(result)
end
--获取geoip
local function fetch_geoip()
--请求geoip
xpcall(function()
local return_code, content = api.curl_logic(geoip_api)
local json = jsonc.parse(content)
@@ -70,7 +68,7 @@ local function fetch_geoip()
if fs.access(asset_location .. "geoip.dat") then
sys.call(string.format("cp -f %s %s", asset_location .. "geoip.dat", "/tmp/geoip.dat"))
if sys.call('sha256sum -c /tmp/geoip.dat.sha256sum > /dev/null 2>&1') == 0 then
log("geoip 版本一致,无需更新。")
log(api.i18n.translatef("%s version is the same and does not need to be updated.", "geoip"))
return 1
end
end
@@ -80,10 +78,10 @@ local function fetch_geoip()
if sys.call('sha256sum -c /tmp/geoip.dat.sha256sum > /dev/null 2>&1') == 0 then
sys.call(string.format("mkdir -p %s && cp -f %s %s", asset_location, "/tmp/geoip.dat", asset_location .. "geoip.dat"))
reboot = 1
log("geoip 更新成功。")
log(api.i18n.translatef("%s update success.", "geoip"))
return 1
else
log("geoip 更新失败,请稍后再试。")
log(api.i18n.translatef("%s update failed, please try again later.", "geoip"))
end
break
end
@@ -103,9 +101,7 @@ local function fetch_geoip()
return 0
end
--获取geosite
local function fetch_geosite()
--请求geosite
xpcall(function()
local return_code, content = api.curl_logic(geosite_api)
local json = jsonc.parse(content)
@@ -124,7 +120,7 @@ local function fetch_geosite()
if fs.access(asset_location .. "geosite.dat") then
sys.call(string.format("cp -f %s %s", asset_location .. "geosite.dat", "/tmp/geosite.dat"))
if sys.call('sha256sum -c /tmp/geosite.dat.sha256sum > /dev/null 2>&1') == 0 then
log("geosite 版本一致,无需更新。")
log(api.i18n.translatef("%s version is the same and does not need to be updated.", "geosite"))
return 1
end
end
@@ -134,10 +130,10 @@ local function fetch_geosite()
if sys.call('sha256sum -c /tmp/geosite.dat.sha256sum > /dev/null 2>&1') == 0 then
sys.call(string.format("mkdir -p %s && cp -f %s %s", asset_location, "/tmp/geosite.dat", asset_location .. "geosite.dat"))
reboot = 1
log("geosite 更新成功。")
log(api.i18n.translatef("%s update success.", "geosite"))
return 1
else
log("geosite 更新失败,请稍后再试。")
log(api.i18n.translatef("%s update failed, please try again later.", "geosite"))
end
break
end
@@ -174,17 +170,17 @@ if geoip_update == 0 and geosite_update == 0 then
os.exit(0)
end
log("开始更新规则...")
log(api.i18n.translate("Start updating the rules..."))
if tonumber(geoip_update) == 1 then
log("geoip 开始更新...")
log(api.i18n.translatef("%s Start updating...", "geoip"))
local status = fetch_geoip()
os.remove("/tmp/geoip.dat")
os.remove("/tmp/geoip.dat.sha256sum")
end
if tonumber(geosite_update) == 1 then
log("geosite 开始更新...")
log(api.i18n.translatef("%s Start updating...", "geosite"))
local status = fetch_geosite()
os.remove("/tmp/geosite.dat")
os.remove("/tmp/geosite.dat.sha256sum")
@@ -201,8 +197,8 @@ if reboot == 1 then
end
end
log("重启服务,应用新的规则。")
log(api.i18n.translate("Restart the service and apply the new rules."))
uci:set(name, "@global[0]", "flush_set", "1")
api.uci_save(uci, name, true, true)
end
log("规则更新完毕...")
log(api.i18n.translate("The rules have been updated..."))

View File

@@ -1,17 +1,11 @@
#!/bin/sh
CONFIG=passwall2
LOG_FILE=/tmp/log/$CONFIG.log
APP_FILE=/usr/share/${CONFIG}/app.sh
LOCK_FILE_DIR=/tmp/lock
flag=0
echolog() {
local d="$(date "+%Y-%m-%d %H:%M:%S")"
#echo -e "$d: $1"
echo -e "$d: $1" >> $LOG_FILE
}
config_n_get() {
local ret=$(uci -q get "${CONFIG}.${1}.${2}" 2>/dev/null)
echo "${ret:=$3}"
@@ -60,12 +54,12 @@ test_node() {
local node_id=$1
local _type=$(echo $(config_n_get ${node_id} type) | tr 'A-Z' 'a-z')
[ -n "${_type}" ] && {
local _tmp_port=$(/usr/share/${CONFIG}/app.sh get_new_port 61080 tcp,udp)
/usr/share/${CONFIG}/app.sh run_socks flag="test_node_${node_id}" node=${node_id} bind=127.0.0.1 socks_port=${_tmp_port} config_file=test_node_${node_id}.json
local _tmp_port=$($APP_FILE get_new_port 61080 tcp,udp)
$APP_FILE run_socks flag="test_node_${node_id}" node=${node_id} bind=127.0.0.1 socks_port=${_tmp_port} config_file=test_node_${node_id}.json
local curlx="socks5h://127.0.0.1:${_tmp_port}"
sleep 1s
local _proxy_status=$(test_url "${probe_url}" ${retry_num} ${connect_timeout} "-x $curlx")
# 结束 SS 插件进程
# Kill the SS plugin process
local pid_file="/tmp/etc/${CONFIG}/test_node_${node_id}_plugin.pid"
[ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1
pgrep -af "test_node_${node_id}" | awk '! /socks_auto_switch\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
@@ -82,10 +76,10 @@ test_auto_switch() {
local b_nodes=$1
local now_node=$2
[ -z "$now_node" ] && {
if [ -n "$(/usr/share/${CONFIG}/app.sh get_cache_var "socks_${id}")" ]; then
now_node=$(/usr/share/${CONFIG}/app.sh get_cache_var "socks_${id}")
if [ -n "$($APP_FILE get_cache_var "socks_${id}")" ]; then
now_node=$($APP_FILE get_cache_var "socks_${id}")
else
#echolog "Socks切换检测未知错误"
#$APP_FILE log_i18n 0 "Socks switch detection: Unknown error."
return 1
fi
}
@@ -96,46 +90,50 @@ test_auto_switch() {
local status=$(test_proxy)
if [ "$status" = "2" ]; then
echolog "Socks切换检测无法连接到网络请检查网络是否正常"
$APP_FILE log_i18n 0 "Socks switch detection: Unable to connect to the network. Please check if the network is working properly!"
return 2
fi
#检测主节点是否能使用
# Check if the main node is usable
if [ "$restore_switch" = "1" ] && [ -n "$main_node" ] && [ "$now_node" != "$main_node" ]; then
test_node ${main_node}
[ $? -eq 0 ] && {
#主节点正常,切换到主节点
echolog "Socks切换检测:${id}主节点【$(config_n_get $main_node type)[$(config_n_get $main_node remarks)]】正常,切换到主节点!"
/usr/share/${CONFIG}/app.sh socks_node_switch flag=${id} new_node=${main_node}
# The main node is working properly; switch to the main node.
$APP_FILE log_i18n 0 "Socks switch detection: Primary node 【%s: [%s]】 is normal. Switch to the primary node!" "${id}" "$(config_n_get $main_node type)" "$(config_n_get $main_node remarks)"
$APP_FILE socks_node_switch flag=${id} new_node=${main_node}
[ $? -eq 0 ] && {
echolog "Socks切换检测${id}节点切换完毕!"
$APP_FILE log_i18n 0 "Socks switch detection: %s node switch complete!" "${id}"
}
return 0
}
fi
if [ "$status" = "0" ]; then
#echolog "Socks切换检测${id}$(config_n_get $now_node type)[$(config_n_get $now_node remarks)]】正常。"
#$APP_FILE log_i18n 0 "Socks switch detection: %s 【%s:[%s]】 normal." "${id}" "$(config_n_get $now_node type)" "$(config_n_get $now_node remarks)"
return 0
elif [ "$status" = "1" ]; then
local new_node msg
if [ "$backup_node_num" -gt 1 ]; then
# 有多个后备节点时
# When there are multiple backup nodes
local first_node found node
for node in $b_nodes; do
[ -z "$first_node" ] && first_node="$node" # 记录第一个节点
[ "$found" = "1" ] && { new_node="$node"; break; } # 找到当前节点后取下一个
[ "$node" = "$now_node" ] && found=1 # 标记找到当前节点
[ -z "$first_node" ] && first_node="$node" # Record the first node.
[ "$found" = "1" ] && { new_node="$node"; break; } # Find the current node and then retrieve the next one.
[ "$node" = "$now_node" ] && found=1 # Mark the current node found.
done
# 如果没找到当前节点,或者当前节点是最后一个,就取第一个节点
# If the current node is not found, or if the current node is the last node, then take the first node.
[ -z "$new_node" ] && new_node="$first_node"
msg="切换到$([ "$now_node" = "$main_node" ] && echo 备用节点 || echo 下一个备用节点)检测!"
local msg2="$($APP_FILE i18n "next backup node")"
[ "$now_node" = "$main_node" ] && msg2="$($APP_FILE i18n "backup node")"
msg="$($APP_FILE i18n "switch to %s test detect!" "${msg2}")"
else
# 只有一个后备节点时,与主节点轮询
# When there is only one backup node, poll with the primary node.
new_node=$([ "$now_node" = "$main_node" ] && echo "$b_nodes" || echo "$main_node")
msg="切换到$([ "$now_node" = "$main_node" ] && echo 备用节点 || echo 主节点)检测!"
local msg2="$($APP_FILE i18n "main node")"
[ "$now_node" = "$main_node" ] && msg2="$($APP_FILE i18n "backup node")"
msg="$($APP_FILE i18n "switch to %s test detect!" "${msg2}")"
fi
echolog "Socks切换检测:${id}$(config_n_get $now_node type)[$(config_n_get $now_node remarks)]】异常,$msg"
$APP_FILE log_i18n 0 "Socks switch detection: %s 【%s:[%s]】 abnormal, %s" "${id}" "$(config_n_get $now_node type)" "$(config_n_get $now_node remarks)" "${msg}"
test_node ${new_node}
if [ $? -eq 0 ]; then
# [ "$restore_switch" = "0" ] && {
@@ -143,10 +141,10 @@ test_auto_switch() {
# [ -z "$(echo $b_nodes | grep $main_node)" ] && uci add_list $CONFIG.${id}.autoswitch_backup_node=$main_node
# uci commit $CONFIG
# }
echolog "Socks切换检测:${id}$(config_n_get $new_node type)[$(config_n_get $new_node remarks)]】正常,切换到此节点!"
/usr/share/${CONFIG}/app.sh socks_node_switch flag=${id} new_node=${new_node}
$APP_FILE log_i18n 0 "Socks switch detection: %s 【%s:[%s]】 normal, switch to this node!" "${id}" "$(config_n_get $new_node type)" "$(config_n_get $new_node remarks)"
$APP_FILE socks_node_switch flag=${id} new_node=${new_node}
[ $? -eq 0 ] && {
echolog "Socks切换检测${id}节点切换完毕!"
$APP_FILE log_i18n 0 "Socks switch detection: %s node switch complete!" "${id}"
}
return 0
else
@@ -181,7 +179,6 @@ start() {
continue
}
pgrep -af "${CONFIG}/" | awk '/app\.sh.*(start|stop)/ || /nftables\.sh/ || /iptables\.sh/ { found = 1 } END { exit !found }' && {
# 特定任务执行中不检测
sleep 6s
continue
}

View File

@@ -19,6 +19,7 @@ local jsonParse, jsonStringify = luci.jsonc.parse, luci.jsonc.stringify
local base64Decode = api.base64Decode
local uci = api.uci
local fs = api.fs
local i18n = api.i18n
uci:revert(appname)
local has_ss = api.is_finded("ss-redir")
@@ -28,7 +29,7 @@ local has_singbox = api.finded_com("sing-box")
local has_xray = api.finded_com("xray")
local has_hysteria2 = api.finded_com("hysteria")
local allowInsecure_default = true
-- 取节点使用core类型节点订阅页面未设置时自动取默认
-- Nodes should be retrieved using the core type (if not set on the node subscription page, the default type will be used automatically).
local function get_core(field, candidates)
local v = uci:get(appname, "@global_subscribe[0]", field)
if not v or v == "" then
@@ -43,11 +44,11 @@ local trojan_type_default = get_core("trojan_type", {{has_singbox,"sing-box"},{h
local vmess_type_default = get_core("vmess_type", {{has_xray,"xray"},{has_singbox,"sing-box"}})
local vless_type_default = get_core("vless_type", {{has_xray,"xray"},{has_singbox,"sing-box"}})
local hysteria2_type_default = get_core("hysteria2_type", {{has_hysteria2,"hysteria2"},{has_singbox,"sing-box"}})
----
local domain_strategy_default = uci:get(appname, "@global_subscribe[0]", "domain_strategy") or ""
local domain_strategy_node = ""
local preproxy_node_group, to_node_group, chain_node_type = "", "", ""
-- 判断是否过滤节点关键字
-- Determine whether to filter node keywords
local filter_keyword_mode_default = uci:get(appname, "@global_subscribe[0]", "filter_keyword_mode") or "0"
local filter_keyword_discard_list_default = uci:get(appname, "@global_subscribe[0]", "filter_discard_list") or {}
local filter_keyword_keep_list_default = uci:get(appname, "@global_subscribe[0]", "filter_keep_list") or {}
@@ -115,7 +116,7 @@ for k, e in ipairs(api.get_valid_nodes()) do
end
end
-- 获取各项动态配置的当前服务器,可以用 get 和 set get必须要获取到节点表
-- To retrieve the current server's dynamic configurations, you can use `get` and `set`. `get` requires access to the node table.
local CONFIG = {}
do
if true then
@@ -125,7 +126,7 @@ do
local node_id = uci:get(appname, szType, option)
CONFIG[#CONFIG + 1] = {
log = true,
remarks = "节点",
remarks = i18n.translatef("Node"),
currentNode = node_id and uci:get_all(appname, node_id) or nil,
set = function(o, server)
uci:set(appname, szType, option, server)
@@ -144,7 +145,7 @@ do
CONFIG[#CONFIG + 1] = {
log = true,
id = id,
remarks = "Socks节点列表[" .. i .. "]",
remarks = i18n.translatef("Socks node list [%s]", i),
currentNode = node_id and uci:get_all(appname, node_id) or nil,
set = function(o, server)
if not server or server == "" then
@@ -157,7 +158,7 @@ do
end
}
if t.autoswitch_backup_node and #t.autoswitch_backup_node > 0 then
local flag = "Socks节点列表[" .. i .. "]备用节点的列表"
local flag = i18n.translatef("Socks node list [%s]", i) .. " " .. i18n.translatef("Backup node list")
local currentNodes = {}
local newNodes = {}
for k, node_id in ipairs(t.autoswitch_backup_node) do
@@ -206,17 +207,17 @@ do
CONFIG[#CONFIG + 1] = {
log = true,
id = t[".name"],
remarks = "HAProxy负载均衡节点列表[" .. i .. "]",
remarks = i18n.translatef("HAProxy node list [%s]", i),
currentNode = node_id and uci:get_all(appname, node_id) or nil,
set = function(o, server)
-- 如果当前 lbss 值不是 ip:port 格式,才进行修改
-- Modify the LBS value only if it is not in IP:Port format.
if not is_ip_port(t[option]) then
uci:set(appname, t[".name"], option, server)
o.newNodeId = server
end
end,
delete = function(o)
-- 如果当前 lbss 值不是 ip:port 格式,才进行删除
-- Deletion is only performed if the current LBS value is not in IP:port format.
if not is_ip_port(t[option]) then
uci:delete(appname, t[".name"])
end
@@ -234,7 +235,7 @@ do
CONFIG[#CONFIG + 1] = {
log = true,
id = t[".name"],
remarks = "访问控制列表[" .. i .. "]",
remarks = i18n.translatef("ACL list [%s]", i),
currentNode = node_id and uci:get_all(appname, node_id) or nil,
set = function(o, server)
uci:set(appname, t[".name"], option, server)
@@ -255,11 +256,11 @@ do
end)
table.insert(rules, {
[".name"] = "default_node",
remarks = "默认"
remarks = i18n.translatef("Default")
})
table.insert(rules, {
[".name"] = "main_node",
remarks = "默认前置"
remarks = i18n.translatef("Default Preproxy")
})
for k, e in pairs(rules) do
@@ -269,7 +270,7 @@ do
CONFIG[#CONFIG + 1] = {
log = false,
currentNode = _node_id and uci:get_all(appname, _node_id) or nil,
remarks = "分流" .. e.remarks .. "节点",
remarks = i18n.translatef("Shunt [%s] node", e.remarks),
set = function(o, server)
if not server then server = "" end
uci:set(appname, node_id, e[".name"], server)
@@ -280,7 +281,7 @@ do
end
elseif node.protocol and node.protocol == '_balancing' then
local flag = "Xray负载均衡节点[" .. node_id .. "]列表"
local flag = i18n.translatef("Xray Load Balancing node [%s] list", node_id)
local currentNodes = {}
local newNodes = {}
if node.balancing_node then
@@ -310,13 +311,13 @@ do
end
}
--后备节点
-- Backup Node
local currentNode = uci:get_all(appname, node_id) or nil
if currentNode and currentNode.fallback_node then
CONFIG[#CONFIG + 1] = {
log = true,
id = node_id,
remarks = "Xray负载均衡节点[" .. node_id .. "]后备节点",
remarks = i18n.translatef("Xray Load Balancing node [%s] backup node", node_id),
currentNode = uci:get_all(appname, currentNode.fallback_node) or nil,
set = function(o, server)
uci:set(appname, node_id, "fallback_node", server)
@@ -328,7 +329,7 @@ do
}
end
elseif node.protocol and node.protocol == '_urltest' then
local flag = "Sing-Box URLTest节点[" .. node_id .. "]列表"
local flag = i18n.translatef("Sing-Box URLTest node [%s] list", node_id)
local currentNodes = {}
local newNodes = {}
if node.urltest_node then
@@ -358,13 +359,13 @@ do
end
}
else
--前置代理节点
-- Preproxy Node
local currentNode = uci:get_all(appname, node_id) or nil
if currentNode and currentNode.preproxy_node then
CONFIG[#CONFIG + 1] = {
log = true,
id = node_id,
remarks = "节点[" .. node_id .. "]前置代理节点",
remarks = i18n.translatef("Node [%s] preproxy node", node_id),
currentNode = uci:get_all(appname, currentNode.preproxy_node) or nil,
set = function(o, server)
uci:set(appname, node_id, "preproxy_node", server)
@@ -375,13 +376,13 @@ do
end
}
end
--落地节点
-- Landing node
local currentNode = uci:get_all(appname, node_id) or nil
if currentNode and currentNode.to_node then
CONFIG[#CONFIG + 1] = {
log = true,
id = node_id,
remarks = "节点[" .. node_id .. "]落地节点",
remarks = i18n.translatef("Node [%s] landing node", node_id),
currentNode = uci:get_all(appname, currentNode.to_node) or nil,
set = function(o, server)
uci:set(appname, node_id, "to_node", server)
@@ -425,7 +426,7 @@ local function UrlDecode(szText)
end) or nil
end
-- 取机场信息(剩余流量、到期时间)
-- Retrieve subscribe information (remaining data allowance, expiration time).
local subscribe_info = {}
local function get_subscribe_info(cfgid, value)
if type(cfgid) ~= "string" or cfgid == "" or type(value) ~= "string" then
@@ -443,7 +444,7 @@ local function get_subscribe_info(cfgid, value)
end
end
-- 设置 ss 协议实现类型
-- Configure the SS protocol implementation type
local function set_ss_implementation(result)
if ss_type_default == "shadowsocks-libev" and has_ss then
result.type = "SS"
@@ -457,24 +458,24 @@ local function set_ss_implementation(result)
result.type = 'sing-box'
result.protocol = 'shadowsocks'
else
log("跳过 SS 节点,因未适配到 SS 核心程序,或未正确设置节点使用类型。")
log(i18n.translatef("Skipping the %s node is due to incompatibility with the %s core program or incorrect node usage type settings.", "SS", "SS"))
return nil
end
return result
end
-- 处理数据
-- Processing data
local function processData(szType, content, add_mode, group)
--log(content, add_mode, group)
local result = {
timeout = 60,
add_mode = add_mode, --0为手动配置,1为导入,2为订阅
add_mode = add_mode, -- `0` for manual configuration, `1` for import, `2` for subscription
group = group
}
--ssr://base64(host:port:protocol:method:obfs:base64pass/?obfsparam=base64param&protoparam=base64param&remarks=base64remarks&group=base64group&udpport=0&uot=0)
if szType == 'ssr' then
if not has_ssr then
log("跳过 SSR 节点,因未安装 SSR 核心程序 shadowsocksr-libev")
log(i18n.translatef("Skipping the %s node is due to incompatibility with the %s core program or incorrect node usage type settings.", "SSR", "shadowsocksr-libev"))
return nil
end
result.type = "SSR"
@@ -508,7 +509,7 @@ local function processData(szType, content, add_mode, group)
elseif vmess_type_default == "xray" and has_xray then
result.type = "Xray"
else
log("跳过 VMess 节点,因未适配到 VMess 核心程序,或未正确设置节点使用类型。")
log(i18n.translatef("Skipping the %s node is due to incompatibility with the %s core program or incorrect node usage type settings.", "VMess", "VMess"))
return nil
end
result.alter_id = info.aid
@@ -622,7 +623,7 @@ local function processData(szType, content, add_mode, group)
end
if result.type == "sing-box" and (result.transport == "mkcp" or result.transport == "xhttp") then
log("跳过节点:" .. result.remarks .."因Sing-Box不支持" .. szType .. "协议的" .. result.transport .. "传输方式需更换Xray。")
log(i18n.translatef("Skip node: %s. Because Sing-Box does not support the %s protocol's %s transmission method, Xray needs to be used instead.", result.remarks, szType, result.transport))
return nil
end
elseif szType == "ss" then
@@ -700,10 +701,10 @@ local function processData(szType, content, add_mode, group)
method = userinfo:sub(1, userinfo:find(":") - 1)
password = userinfo:sub(userinfo:find(":") + 1, #userinfo)
else
password = hostInfo[1] --一些链接用明文uuid做密码
password = hostInfo[1] -- Some links use plaintext UUIDs as passwords.
end
-- 判断密码是否经过url编码
-- Determine if the password is URL encoded
local function isURLEncodedPassword(pwd)
if not pwd:find("%%[0-9A-Fa-f][0-9A-Fa-f]") then
return false
@@ -734,14 +735,14 @@ local function processData(szType, content, add_mode, group)
if result.plugin then
if result.type == 'Xray' then
-- obfs-local插件转换成xray支持的格式
-- The obfs-local plugin converts data to a format supported by xray.
if result.plugin ~= "obfs-local" then
result.error_msg = "Xray不支持 " .. result.plugin .. " 插件."
result.error_msg = i18n.translatef("Xray unsupport %s plugin.", result.plugin)
else
local obfs = result.plugin_opts:match("obfs=([^;]+)") or ""
local obfs_host = result.plugin_opts:match("obfs%-host=([^;]+)") or ""
if obfs == "" or obfs_host == "" then
result.error_msg = "SS " .. result.plugin .. " 插件选项不完整."
result.error_msg = "SS " .. result.plugin .. " " .. i18n.translatef("Plugin options Incomplete.")
end
if obfs == "http" then
result.transport = "raw"
@@ -770,8 +771,8 @@ local function processData(szType, content, add_mode, group)
end
end
if aead2022 then
-- shadowsocks-libev 不支持2022加密
result.error_msg = "shadowsocks-libev 不支持2022加密."
-- shadowsocks-libev does not support 2022 encryption.
result.error_msg = i18n.translatef("shadowsocks-libev unsupport 2022 encryption.")
end
end
@@ -875,15 +876,15 @@ local function processData(szType, content, add_mode, group)
result.tls_allowInsecure = allowInsecure_default and "1" or "0"
end
else
result.error_msg = "请更换Xray或Sing-Box来支持SS更多的传输方式."
result.error_msg = i18n.translatef("Please replace Xray or Sing-Box to support more transmission methods in Shadowsocks.")
end
end
if params["shadow-tls"] then
if result.type ~= "sing-box" and result.type ~= "SS-Rust" then
result.error_msg = ss_type_default .. " 不支持 shadow-tls 插件."
result.error_msg = ss_type_default .. " " .. i18n.translatef("unsupport %s plugin.", "shadow-tls")
else
-- 解析SS Shadow-TLS 插件参数
-- Parsing SS Shadow-TLS plugin parameters
local function parseShadowTLSParams(b64str, out)
local ok, data = pcall(jsonParse, base64Decode(b64str))
if not ok or type(data) ~= "table" then return "" end
@@ -930,7 +931,7 @@ local function processData(szType, content, add_mode, group)
result.type = 'Xray'
result.protocol = 'trojan'
else
log("跳过 Trojan 节点,因未适配到 Trojan 核心程序,或未正确设置节点使用类型。")
log(i18n.translatef("Skipping the %s node is due to incompatibility with the %s core program or incorrect node usage type settings.", "Trojan", "Trojan"))
return nil
end
@@ -990,7 +991,6 @@ local function processData(szType, content, add_mode, group)
else
result.tls_allowInsecure = string.lower(params.allowinsecure) == "true" and "1" or "0"
end
--log(result.remarks .. ' 使用节点AllowInsecure设定: '.. result.tls_allowInsecure)
else
result.tls_allowInsecure = allowInsecure_default and "1" or "0"
end
@@ -1077,7 +1077,7 @@ local function processData(szType, content, add_mode, group)
result.alpn = params.alpn
if result.type == "sing-box" and (result.transport == "mkcp" or result.transport == "xhttp") then
log("跳过节点:" .. result.remarks .."因Sing-Box不支持" .. szType .. "协议的" .. result.transport .. "传输方式需更换Xray。")
log(i18n.translatef("Skip node: %s. Because Sing-Box does not support the %s protocol's %s transmission method, Xray needs to be used instead.", result.remarks, szType, result.transport))
return nil
end
end
@@ -1098,7 +1098,7 @@ local function processData(szType, content, add_mode, group)
elseif vless_type_default == "xray" and has_xray then
result.type = "Xray"
else
log("跳过 VLESS 节点,因未适配到 VLESS 核心程序,或未正确设置节点使用类型。")
log(i18n.translatef("Skipping the %s node is due to incompatibility with the %s core program or incorrect node usage type settings.", "VLESS", "VLESS"))
return nil
end
result.protocol = "vless"
@@ -1264,7 +1264,7 @@ local function processData(szType, content, add_mode, group)
end
if result.type == "sing-box" and (result.transport == "mkcp" or result.transport == "xhttp") then
log("跳过节点:" .. result.remarks .."因Sing-Box不支持" .. szType .. "协议的" .. result.transport .. "传输方式需更换Xray。")
log(i18n.translatef("Skip node: %s. Because Sing-Box does not support the %s protocol's %s transmission method, Xray needs to be used instead.", result.remarks, szType, result.transport))
return nil
end
end
@@ -1273,7 +1273,7 @@ local function processData(szType, content, add_mode, group)
result.type = 'sing-box'
result.protocol = "hysteria"
else
log("跳过 Hysteria 节点,因未安装 Hysteria 核心程序 Sing-box")
log(i18n.translatef("Skip the %s node because the %s core program is not installed.", "Hysteria", "Hysteria", "Sing-Box"))
return nil
end
@@ -1314,7 +1314,6 @@ local function processData(szType, content, add_mode, group)
params.allowinsecure = params.allowinsecure or params.insecure
if params.allowinsecure and (params.allowinsecure == "1" or params.allowinsecure == "0") then
result.tls_allowInsecure = params.allowinsecure
--log(result.remarks ..' 使用节点AllowInsecure设定: '.. result.tls_allowInsecure)
else
result.tls_allowInsecure = allowInsecure_default and "1" or "0"
end
@@ -1363,7 +1362,6 @@ local function processData(szType, content, add_mode, group)
params.allowinsecure = params.allowinsecure or params.insecure
if params.allowinsecure and (params.allowinsecure == "1" or params.allowinsecure == "0") then
result.tls_allowInsecure = params.allowinsecure
--log(result.remarks ..' 使用节点AllowInsecure设定: '.. result.tls_allowInsecure)
else
result.tls_allowInsecure = allowInsecure_default and "1" or "0"
end
@@ -1383,7 +1381,7 @@ local function processData(szType, content, add_mode, group)
result.hysteria2_obfs = params["obfs-password"] or params["obfs_password"]
end
else
log("跳过 Hysteria2 节点,因未适配到 Hysteria2 核心程序,或未正确设置节点使用类型。")
log(i18n.translatef("Skipping the %s node is due to incompatibility with the %s core program or incorrect node usage type settings.", "Hysteria2", "Hysteria2"))
return nil
end
elseif szType == 'tuic' then
@@ -1391,7 +1389,7 @@ local function processData(szType, content, add_mode, group)
result.type = 'sing-box'
result.protocol = "tuic"
else
log("跳过 Tuic 节点,因未安装 Tuic 核心程序 Sing-box")
log(i18n.translatef("Skip the %s node because the %s core program is not installed.", "Tuic", "Tuic", "Sing-Box"))
return nil
end
@@ -1443,7 +1441,6 @@ local function processData(szType, content, add_mode, group)
else
result.tls_allowInsecure = string.lower(params.allowinsecure) == "true" and "1" or "0"
end
--log(result.remarks .. ' 使用节点AllowInsecure设定: '.. result.tls_allowInsecure)
else
result.tls_allowInsecure = allowInsecure_default and "1" or "0"
end
@@ -1452,7 +1449,7 @@ local function processData(szType, content, add_mode, group)
result.type = 'sing-box'
result.protocol = "anytls"
else
log("跳过 AnyTLS 节点,因未安装 AnyTLS 核心程序 Sing-box 1.12")
log(i18n.translatef("Skip the %s node because the %s core program is not installed.", "AnyTLS", "AnyTLS", "Sing-Box 1.12"))
return nil
end
@@ -1516,12 +1513,12 @@ local function processData(szType, content, add_mode, group)
local singbox_version = api.get_app_version("sing-box")
local version_ge_1_12 = api.compare_versions(singbox_version:match("[^v]+"), ">=", "1.12.0")
if not has_singbox or not version_ge_1_12 then
log("跳过节点:" .. result.remarks ..",因" .. szType .. "类型的节点需要 Sing-Box 1.12 以上版本支持。")
log(i18n.translatef("Skip the %s node, as %s type nodes require Sing-Box version 1.12 or higher.", result.remarks, szType))
return nil
end
end
else
log('暂时不支持' .. szType .. "类型的节点订阅,跳过此节点。")
log(i18n.translatef("%s type node subscriptions are not currently supported, skip this node.", szType))
return nil
end
if not result.remarks or result.remarks == "" then
@@ -1559,7 +1556,7 @@ local function truncate_nodes(group)
local removeNodesSet = {}
for k, v in pairs(config.currentNodes) do
if v.currentNode and v.currentNode.add_mode == "2" then
if (not group) or (group and group == v.currentNode.group) then
if (not group) or (group:lower() == (v.currentNode.group or ""):lower()) then
removeNodesSet[v.currentNode[".name"]] = true
end
end
@@ -1574,7 +1571,7 @@ local function truncate_nodes(group)
end
else
if config.currentNode and config.currentNode.add_mode == "2" then
if (not group) or (group and group == config.currentNode.group) then
if (not group) or (group:lower() == (config.currentNode.group or ""):lower()) then
if config.delete then
config.delete(config)
elseif config.set then
@@ -1586,13 +1583,13 @@ local function truncate_nodes(group)
end
uci:foreach(appname, "nodes", function(node)
if node.add_mode == "2" then
if (not group) or (group and group == node.group) then
if (not group) or (group:lower() == (node.group or ""):lower()) then
uci:delete(appname, node['.name'])
end
end
end)
uci:foreach(appname, "subscribe_list", function(o)
if (not group) or group == o.remark then
if (not group) or (group:lower() == (o.remark or ""):lower()) then
uci:delete(appname, o['.name'], "md5")
end
end)
@@ -1602,26 +1599,26 @@ end
local function select_node(nodes, config, parentConfig)
if config.currentNode then
local server
-- 特别优先级 cfgid
-- Special priority: cfgid
if config.currentNode[".name"] then
for index, node in pairs(nodes) do
if node[".name"] == config.currentNode[".name"] then
if config.log == nil or config.log == true then
log('更新【' .. config.remarks .. '】匹配节点:' .. node.remarks)
log(i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Matching node:") .. " " .. node.remarks)
end
server = node[".name"]
break
end
end
end
-- 第一优先级 类型 + 备注 + IP + 端口
-- First priority: Type + Notes + IP + Port
if not server then
for index, node in pairs(nodes) do
if config.currentNode.type and config.currentNode.remarks and config.currentNode.address and config.currentNode.port then
if node.type and node.remarks and node.address and node.port then
if node.type == config.currentNode.type and node.remarks == config.currentNode.remarks and (node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port) then
if config.log == nil or config.log == true then
log('更新【' .. config.remarks .. '】第一匹配节点:' .. node.remarks)
log(i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("First Matching node:") .. " " .. node.remarks)
end
server = node[".name"]
break
@@ -1630,14 +1627,14 @@ local function select_node(nodes, config, parentConfig)
end
end
end
-- 第二优先级 类型 + IP + 端口
-- Second priority: Type + IP + Port
if not server then
for index, node in pairs(nodes) do
if config.currentNode.type and config.currentNode.address and config.currentNode.port then
if node.type and node.address and node.port then
if node.type == config.currentNode.type and (node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port) then
if config.log == nil or config.log == true then
log('更新【' .. config.remarks .. '】第二匹配节点:' .. node.remarks)
log(i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Second Matching node:") .. " " .. node.remarks)
end
server = node[".name"]
break
@@ -1646,14 +1643,14 @@ local function select_node(nodes, config, parentConfig)
end
end
end
-- 第三优先级 IP + 端口
-- Third priority: IP + Port
if not server then
for index, node in pairs(nodes) do
if config.currentNode.address and config.currentNode.port then
if node.address and node.port then
if node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port then
if config.log == nil or config.log == true then
log('更新【' .. config.remarks .. '】第三匹配节点:' .. node.remarks)
log(i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Third Matching node:") .. " " .. node.remarks)
end
server = node[".name"]
break
@@ -1662,14 +1659,14 @@ local function select_node(nodes, config, parentConfig)
end
end
end
-- 第四优先级 IP
-- Fourth priority: IP
if not server then
for index, node in pairs(nodes) do
if config.currentNode.address then
if node.address then
if node.address == config.currentNode.address then
if config.log == nil or config.log == true then
log('更新【' .. config.remarks .. '】第四匹配节点:' .. node.remarks)
log(i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Fourth Matching node:") .. " " .. node.remarks)
end
server = node[".name"]
break
@@ -1678,14 +1675,14 @@ local function select_node(nodes, config, parentConfig)
end
end
end
-- 第五优先级备注
-- Fifth priority: remarks
if not server then
for index, node in pairs(nodes) do
if config.currentNode.remarks then
if node.remarks then
if node.remarks == config.currentNode.remarks then
if config.log == nil or config.log == true then
log('更新【' .. config.remarks .. '】第五匹配节点:' .. node.remarks)
log(i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Fifth Matching node:") .. " " .. node.remarks)
end
server = node[".name"]
break
@@ -1695,11 +1692,11 @@ local function select_node(nodes, config, parentConfig)
end
end
if not parentConfig then
-- 还不行 随便找一个
-- If that doesn't work, just find one.
if not server then
if #nodes_table > 0 then
if config.log == nil or config.log == true then
log('' .. config.remarks .. '' .. '无法找到最匹配的节点,当前已更换为:' .. nodes_table[1].remarks)
log(i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Unable to find the best matching node, now replaced with:") .. " " .. nodes_table[1].remarks)
end
server = nodes_table[1][".name"]
end
@@ -1721,19 +1718,19 @@ end
local function update_node(manual)
if next(nodeResult) == nil then
log("没有可用的节点信息更新。")
log(i18n.translatef("No node information updates are available."))
return
end
local group = {}
for _, v in ipairs(nodeResult) do
group[v["remark"]] = true
group[v["remark"]:lower()] = true
end
if manual == 0 and next(group) then
uci:foreach(appname, "nodes", function(node)
-- 如果未发现新节点或手动导入的节点就不要删除了...
if node.add_mode == "2" and (node.group and group[node.group] == true) then
-- Do not delete nodes if no new nodes are found or nodes were manually imported...
if node.add_mode == "2" and (node.group and group[node.group:lower()] == true) then
uci:delete(appname, node['.name'])
end
end)
@@ -1750,11 +1747,11 @@ local function update_node(manual)
if kkk ~= "group" or vvv ~= "default" then
uci:set(appname, cfgid, kkk, vvv)
end
-- sing-box 域名解析策略
-- Sing-Box Domain Strategy
if kkk == "type" and vvv == "sing-box" then
uci:set(appname, cfgid, "domain_strategy", domain_strategy_node)
end
-- 订阅组链式代理
-- Subscription Group Chain Agent
if chain_node_type ~= "" and kkk == "type" and vvv == chain_node_type then
if preproxy_node_group ~="" then
uci:set(appname, cfgid, "chain_proxy", "1")
@@ -1768,7 +1765,7 @@ local function update_node(manual)
end
end
end
-- 更新机场信息
-- Update subscription information
for cfgid, info in pairs(subscribe_info) do
for key, value in pairs(info) do
if value ~= "" then
@@ -1816,7 +1813,7 @@ local function parse_link(raw, add_mode, group, cfgid)
if raw and #raw > 0 then
local nodes, szType
local node_list = {}
-- SSD 似乎是这种格式 ssd:// 开头的
-- ssd appear to be in this format, starting with ssd://.
if raw:find('ssd://') then
szType = 'ssd'
local nEnd = select(2, raw:find('ssd://'))
@@ -1829,13 +1826,13 @@ local function parse_link(raw, add_mode, group, cfgid)
password = nodes.password
}
local servers = {}
-- SS里面包着 干脆直接这样
-- SS is wrapped inside, so let's just like this.
for _, server in ipairs(nodes.servers) do
tinsert(servers, setmetatable(server, { __index = extra }))
end
nodes = servers
else
-- ssd 外的格式
-- Formats other than ssd
if add_mode == "1" then
nodes = split(raw, "\n")
else
@@ -1857,22 +1854,22 @@ local function parse_link(raw, add_mode, group, cfgid)
local link = api.trim(dat[2]:gsub("#.*$", ""))
result = processData(dat[1], base64Decode(link), add_mode, group)
else
local link = dat[2]:gsub("&amp;", "&"):gsub("%s*#%s*", "#") -- 一些奇葩的链接用"&amp;"当做"&""#"前后带空格
local link = dat[2]:gsub("&amp;", "&"):gsub("%s*#%s*", "#") -- Some odd links use "&" as "&", and include spaces before and after "#".
result = processData(dat[1], link, add_mode, group)
end
end
else
log('跳过未知类型: ' .. szType)
log(i18n.translatef("Skip unknown types:") .. " " .. szType)
end
-- log(result)
if result then
if result.error_msg then
log('丢弃节点: ' .. result.remarks .. ", 原因:" .. result.error_msg)
log(i18n.translatef("Discard node: %s, Reason:", result.remarks) .. " " .. result.error_msg)
elseif not result.type then
log('丢弃节点: ' .. result.remarks .. ", 找不到可使用二进制.")
log(i18n.translatef("Discard node: %s, Reason:", result.remarks) .. " " .. i18n.translatef("No usable binary was found."))
elseif (add_mode == "2" and is_filter_keyword(result.remarks)) or not result.address or result.remarks == "NULL" or result.address == "127.0.0.1" or
(not datatypes.hostname(result.address) and not (api.is_ip(result.address))) then
log('丢弃过滤节点: ' .. result.type .. ' 节点, ' .. result.remarks)
log(i18n.translatef("Discard filter nodes: %s type node %s", result.type, result.remarks))
else
tinsert(node_list, result)
end
@@ -1882,7 +1879,7 @@ local function parse_link(raw, add_mode, group, cfgid)
end
end, function (err)
--log(err)
log(v, "解析错误,跳过此节点。")
log(v, i18n.translatef("Parsing error, skip this node."))
end
)
end
@@ -1893,10 +1890,10 @@ local function parse_link(raw, add_mode, group, cfgid)
list = node_list
}
end
log('成功解析【' .. group .. '】节点数量: ' .. #node_list)
log(i18n.translatef("Successfully resolved the [%s] node, number: %s", group, #node_list))
else
if add_mode == "2" then
log('获取到的【' .. group .. '】订阅内容为空,可能是订阅地址无效,或是网络问题,请诊断!')
log(i18n.translatef("Get subscription content for [%s] is empty. This may be due to an invalid subscription address or a network problem. Please diagnose the issue!", group))
end
end
end
@@ -1969,7 +1966,7 @@ local execute = function()
domain_strategy_node = domain_strategy_default
end
-- 订阅组链式代理
-- Subscription Group Chain Agent
local function valid_chain_node(node)
if not node then return "" end
local cp = uci:get(appname, node, "chain_proxy") or ""
@@ -1986,8 +1983,8 @@ local execute = function()
local ua = value.user_agent
local access_mode = value.access_mode
local result = (not access_mode) and "自动" or (access_mode == "direct" and "直连访问" or (access_mode == "proxy" and "通过代理" or "自动"))
log('正在订阅:' .. remark .. '' .. url .. ' [' .. result .. ']')
local result = (not access_mode) and i18n.translatef("Auto") or (access_mode == "direct" and i18n.translatef("Direct") or (access_mode == "proxy" and i18n.translatef("Proxy") or i18n.translatef("Auto")))
log(i18n.translatef("Start subscribing: %s", '' .. remark .. '' .. url .. ' [' .. result .. ']'))
local tmp_file = "/tmp/" .. cfgid
value.http_code = curl(url, tmp_file, ua, access_mode)
if value.http_code ~= 200 then
@@ -2001,7 +1998,7 @@ local execute = function()
local old_md5 = value.md5 or ""
local new_md5 = luci.sys.exec("md5sum " .. tmp_file .. " 2>/dev/null | awk '{print $1}'"):gsub("\n", "")
if not manual_sub and old_md5 == new_md5 then
log('订阅:【' .. remark .. '】没有变化,无需更新。')
log(i18n.translatef("Subscription: [%s] No changes, no update required.", remark))
else
parse_link(raw_data, "2", remark, cfgid)
uci:set(appname, cfgid, "md5", new_md5)
@@ -2024,7 +2021,7 @@ local execute = function()
if #fail_list > 0 then
for index, value in ipairs(fail_list) do
log(string.format('【%s】订阅失败可能是订阅地址无效或是网络问题请诊断[%s]', value.remark, tostring(value.http_code)))
log(i18n.translatef("[%s] Subscription failed. This could be due to an invalid subscription address or a network issue. Please diagnose the problem! [%s]", value.remark, tostring(value.http_code)))
end
end
update_node(0)
@@ -2033,13 +2030,13 @@ end
if arg[1] then
if arg[1] == "start" then
log('开始订阅...')
log(i18n.translatef("Start subscribing..."))
xpcall(execute, function(e)
log(e)
log(debug.traceback())
log('发生错误, 正在恢复服务')
log(i18n.translatef("Error, restoring service."))
end)
log('订阅完毕...\n')
log(i18n.translatef("Subscription complete...") .. "\n")
elseif arg[1] == "add" then
local f = assert(io.open("/tmp/links.conf", 'r'))
local raw = f:read('*all')

View File

@@ -1,6 +1,6 @@
#!/bin/sh
## 循环更新脚本
## Loop update script
CONFIG=passwall2
APP_PATH=/usr/share/$CONFIG

View File

@@ -1,13 +1,6 @@
#!/bin/sh
CONFIG=passwall2
LOG_FILE=/tmp/log/$CONFIG.log
echolog() {
local d="$(date "+%Y-%m-%d %H:%M:%S")"
#echo -e "$d: $1"
echo -e "$d: $1" >> $LOG_FILE
}
config_n_get() {
local ret=$(uci -q get "${CONFIG}.${1}.${2}" 2>/dev/null)

View File

@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=bandix
PKG_VERSION:=0.8.1
PKG_VERSION:=0.8.2
PKG_RELEASE:=1
PKG_LICENSE:=Apache-2.0
@@ -13,7 +13,7 @@ include $(INCLUDE_DIR)/package.mk
include $(TOPDIR)/feeds/packages/lang/rust/rust-values.mk
# 二进制文件的文件名和URL
RUST_BANDIX_VERSION:=0.8.1
RUST_BANDIX_VERSION:=0.8.2
RUST_BINARY_FILENAME:=bandix-$(RUST_BANDIX_VERSION)-$(RUSTC_TARGET_ARCH).tar.gz

View File

@@ -7,6 +7,8 @@ config bandix 'general'
option language 'auto'
option theme 'auto'
option log_level 'info'
config bandix 'traffic'
option enabled '0'

View File

@@ -16,6 +16,7 @@ start_service() {
local iface
local port
local data_dir
local log_level
local traffic_enabled
local traffic_retention_seconds
local traffic_flush_interval_seconds
@@ -28,6 +29,7 @@ start_service() {
config_get iface 'general' 'iface'
config_get port 'general' 'port'
config_get data_dir 'general' 'data_dir'
config_get log_level 'general' 'log_level'
config_get_bool traffic_enabled 'traffic' 'enabled'
config_get traffic_retention_seconds 'traffic' 'traffic_retention_seconds'
config_get traffic_flush_interval_seconds 'traffic' 'traffic_flush_interval_seconds'
@@ -40,6 +42,9 @@ start_service() {
# 构建基础命令行参数
local args="--iface $iface --port $port --data-dir $data_dir"
if [ -n "$log_level" ]; then
args="$args --log-level $log_level"
fi
# 添加流量监控相关参数
if [ "$traffic_enabled" -eq 1 ]; then