From 3f39d2b62b407964911a0e1d045cfbdfe083d101 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 26 Sep 2026 16:35:25 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=B6=20Sync=202026-09-26=2016:35:25?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luci-app-fwlive/Makefile | 4 +- .../luci-static/resources/fwlive/constants.js | 2 +- .../luci-static/resources/fwlive/logging.js | 2 + .../resources/view/status/fwlive.js | 8 +- luci-app-fwlive/po/de/luci-app-fwlive.po | 40 +- luci-app-fwlive/po/ru/luci-app-fwlive.po | 40 +- .../po/templates/luci-app-fwlive.pot | 40 +- luci-app-fwlive/po/zh_Hans/luci-app-fwlive.po | 40 +- .../root/usr/libexec/fwlive-logging.sh | 10 +- luci-app-fwlive/root/usr/libexec/rpcd/fwlive | 185 +- luci-app-passwall/Makefile | 4 +- .../cbi/passwall/client/type/5_hysteria2.lua | 2 +- .../luasrc/passwall/util_hysteria2.lua | 2 +- .../usr/share/passwall/rules/lanlist_ipv6 | 2 +- .../root/usr/share/passwall/rules/proxy_host | 14 + luci-app-ssr-plus/Makefile | 4 +- .../luasrc/model/cbi/shadowsocksr/client.lua | 88 +- .../luasrc/model/cbi/shadowsocksr/control.lua | 53 +- .../luasrc/model/cbi/shadowsocksr/servers.lua | 129 +- .../view/shadowsocksr/clash_yaml_upload.htm | 2 +- luci-app-ssr-plus/po/templates/ssr-plus.pot | 452 ++-- luci-app-ssr-plus/po/zh_Hans/ssr-plus.po | 463 ++-- .../root/etc/init.d/shadowsocksr | 114 +- .../root/etc/ssrplus/fake_ip_filter.list | 78 - .../root/etc/ssrplus/fallback_domain.list | 9 - .../root/etc/ssrplus/fallback_ipcidr.list | 15 - .../root/etc/uci-defaults/luci-ssr-plus | 12 - luci-app-ssr-plus/root/usr/bin/ssr-rules | 357 +-- .../usr/share/shadowsocksr/clash_yaml.lua | 1987 +++++------------ .../usr/share/shadowsocksr/gen_config.lua | 187 +- .../share/shadowsocksr/shadowsocksr.config | 3 - luci-app-wwand/Makefile | 6 +- .../view/dashboard/include/30_wifi.js | 1 + .../luci-static/resources/fs-appearance.js | 5 +- .../htdocs/luci-static/resources/fs-chrome.js | 6 + .../htdocs/luci-static/resources/fs-router.js | 74 +- .../ucode/template/themes/footstrap/header.ut | 40 + mise/Makefile | 4 +- package_kernel_tcp-brutal/Makefile | 4 +- wwand-ipa/Makefile | 102 + wwand-ipad/Makefile | 154 ++ wwand-ipad/patches/100-scard-stdio.patch | 833 +++++++ wwand-qlog/Makefile | 58 + wwand/Makefile | 8 +- 44 files changed, 2741 insertions(+), 2902 deletions(-) delete mode 100644 luci-app-ssr-plus/root/etc/ssrplus/fake_ip_filter.list delete mode 100644 luci-app-ssr-plus/root/etc/ssrplus/fallback_domain.list delete mode 100644 luci-app-ssr-plus/root/etc/ssrplus/fallback_ipcidr.list create mode 100644 wwand-ipa/Makefile create mode 100644 wwand-ipad/Makefile create mode 100644 wwand-ipad/patches/100-scard-stdio.patch create mode 100644 wwand-qlog/Makefile diff --git a/luci-app-fwlive/Makefile b/luci-app-fwlive/Makefile index dc096c61b..4a7eaa368 100644 --- a/luci-app-fwlive/Makefile +++ b/luci-app-fwlive/Makefile @@ -11,8 +11,8 @@ LUCI_DESCRIPTION:=Live firewall log table (firewall4/nft; iptables-tagged lines LUCI_DEPENDS:=+luci-base +logd +jsonfilter LUCI_PKGARCH:=all -PKG_VERSION:=0.1.45 -PKG_RELEASE:=38 +PKG_VERSION:=0.1.46 +PKG_RELEASE:=39 # Reproducible build: honor SOURCE_DATE_EPOCH from the build environment (set by docker-sdk.sh / CI). ifdef SOURCE_DATE_EPOCH diff --git a/luci-app-fwlive/htdocs/luci-static/resources/fwlive/constants.js b/luci-app-fwlive/htdocs/luci-static/resources/fwlive/constants.js index 50a0942f0..e6aaa565f 100644 --- a/luci-app-fwlive/htdocs/luci-static/resources/fwlive/constants.js +++ b/luci-app-fwlive/htdocs/luci-static/resources/fwlive/constants.js @@ -9,7 +9,7 @@ */ return baseclass.extend({ /* Keep in sync with openwrt-feed/luci-app-fwlive/Makefile PKG_VERSION. */ - APP_VERSION: '0.1.45', + APP_VERSION: '0.1.46', ROW_LIMIT_OPTIONS: [25, 50, 100, 250, 500, 1000, 2000], DEFAULT_ROW_LIMIT: 100, FETCH_MODE_OPTIONS: ['auto', 'manual'], diff --git a/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js b/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js index 961ec40b2..17d5441bd 100644 --- a/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js +++ b/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js @@ -262,6 +262,8 @@ function buildEmptyStateNodes(state, callbacks) { ); } + if (!st) return nodes; + if (blocker === 'no_wan_zone') { nodes.push( E( diff --git a/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js b/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js index 7e5e565f7..6f063ff22 100644 --- a/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js +++ b/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js @@ -746,11 +746,11 @@ return view.extend({ this.weakDevice = !!(this.loggingStatus && this.loggingStatus.weak_device === true); } catch (_e) { if (this.viewDisposed) return; - /* Keep last toolbar state. Do not clobber a toggle success/failure notice. */ + /* Keep last-known toolbar; unknown until the first successful fetch. */ if (!this.loggingNotice) - this.loggingNotice = _( - 'Could not refresh logging status; showing the last known state.' - ); + this.loggingNotice = this.loggingStatus + ? _('Could not refresh logging status; showing the last known state.') + : _('Could not load logging status.'); } this.updateBackendUi(); this.updateLoggingToolbarUi(); diff --git a/luci-app-fwlive/po/de/luci-app-fwlive.po b/luci-app-fwlive/po/de/luci-app-fwlive.po index 08eea7363..d253b4f5f 100644 --- a/luci-app-fwlive/po/de/luci-app-fwlive.po +++ b/luci-app-fwlive/po/de/luci-app-fwlive.po @@ -160,7 +160,11 @@ msgstr "Protokollierung konnte nicht deaktiviert werden." msgid "Could not enable logging." msgstr "Protokollierung konnte nicht aktiviert werden." -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:751 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:753 +msgid "Could not load logging status." +msgstr "Protokollstatus konnte nicht geladen werden." + +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:752 msgid "Could not refresh logging status; showing the last known state." msgstr "Protokollstatus konnte nicht aktualisiert werden; zeige den zuletzt bekannten Zustand." @@ -367,7 +371,7 @@ msgstr "" "möglicherweise keine Erfolgs/Fehler-CSS-Variablen; fwlive fällt auf lokale " "Farben zurück (air-gapped, keine Daten verlassen das Gerät)." -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:321 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:323 msgid "" "If the WAN is quiet, wait for probes or use the optional ping check in " "Help / the enabling-logs guide." @@ -393,15 +397,15 @@ msgid "Interface (prefix ! to exclude)" msgstr "Schnittstelle (! voranstellen zum Ausschließen)" #: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:244 -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:359 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:361 msgid "I’ll configure this under Network → Firewall" msgstr "Ich konfiguriere das unter Netzwerk → Firewall" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:284 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:286 msgid "Kernel log modules missing" msgstr "Kernel-Logmodule fehlen" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:287 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:289 msgid "" "Kernel netfilter log modules are missing. Install kmod-nf-log-ipv4 and kmod-" "nf-log-ipv6 (or kmod-nf-log / kmod-nf-log6), then reload the firewall." @@ -426,7 +430,7 @@ msgstr "Eingeschränkte Diagnose — timeout-Befehl fehlt" msgid "Live view may be incomplete — a legacy iptables table is registered" msgstr "Die Live-Ansicht kann unvollständig sein — eine veraltete iptables-Tabelle ist registriert" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:330 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:332 msgid "Logging is off on this router" msgstr "Protokollierung ist auf diesem Router deaktiviert" @@ -447,7 +451,7 @@ msgstr "" "Manuell nutzt weiterhin Serverschutz und Abfrageintervall; es ändert das " "Abrufbudget, nicht das Abfrageintervall." -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:378 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:380 msgid "Manual test (System → Terminal):" msgstr "Manueller Test (System → Terminal):" @@ -470,14 +474,14 @@ msgstr "Weitere Filter" msgid "Network → Firewall" msgstr "Netzwerk → Firewall" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:275 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:277 msgid "" "No WAN firewall zone found in /etc/config/firewall. Configure zones under:" msgstr "" "Keine WAN-Firewallzone in /etc/config/firewall gefunden. Zonen konfigurieren " "unter:" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:270 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:272 msgid "No WAN zone found" msgstr "Keine WAN-Zone gefunden" @@ -492,7 +496,7 @@ msgstr "" msgid "Not now" msgstr "Nicht jetzt" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:353 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:355 msgid "Nothing changes until you click Enable." msgstr "Nichts ändert sich, bis Sie auf Aktivieren klicken." @@ -504,11 +508,11 @@ msgstr "AUSGANG" msgid "One line" msgstr "Einzeilig" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:326 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:328 msgid "Open firewall zone settings" msgstr "Firewall-Zonen-Einstellungen öffnen" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:333 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:335 msgid "" "OpenWrt does not write firewall events to the log until you turn logging on. " "Live View only shows what the firewall already logs — it does not add allow/" @@ -719,11 +723,11 @@ msgstr "" msgid "Theme tint fallback" msgstr "Theme-Farb-Fallback" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:386 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:388 msgid "Then ping the router." msgstr "Dann den Router anpingen." -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:304 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:306 msgid "" "This router reported a logging blocker that Live View does not recognize yet." msgstr "" @@ -752,7 +756,7 @@ msgstr "" "Über das WAN-Protokollierungs-Steuerelement in der Beobachtungsleiste wieder " "deaktivieren." -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:347 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:349 msgid "" "Turns on WAN zone drop/reject logging (same as Network → Firewall → wan → " "Log). Rate-limited by the zone log_limit (OpenWrt default 10/minute). Normal " @@ -786,7 +790,7 @@ msgstr "Ansicht" msgid "WAN drop/reject logging is off." msgstr "WAN-Drop/Reject-Protokollierung ist aus." -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:314 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:316 msgid "" "WAN drop/reject logging is on. Blocked inbound WAN traffic will show up " "here. Normal LAN browsing will not." @@ -815,7 +819,7 @@ msgid "WAN logging on (%s). Click to disable." msgstr "WAN-Protokollierung: EIN (%s). Klicken zum Deaktivieren." #: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:120 -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:301 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:303 msgid "WAN logging unavailable" msgstr "WAN-Protokollierung nicht verfügbar" @@ -827,7 +831,7 @@ msgstr "WAN-Protokollierung nicht verfügbar: fehlende Kernel-Log-Module" msgid "WAN logging unavailable: no WAN zone" msgstr "WAN-Protokollierung nicht verfügbar: keine WAN-Zone" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:311 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:313 msgid "Waiting for firewall events" msgstr "Warte auf Firewall-Ereignisse" diff --git a/luci-app-fwlive/po/ru/luci-app-fwlive.po b/luci-app-fwlive/po/ru/luci-app-fwlive.po index 59f1ee0b3..c419f734a 100644 --- a/luci-app-fwlive/po/ru/luci-app-fwlive.po +++ b/luci-app-fwlive/po/ru/luci-app-fwlive.po @@ -157,7 +157,11 @@ msgstr "Не удалось отключить журналирование." msgid "Could not enable logging." msgstr "Не удалось включить журналирование." -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:751 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:753 +msgid "Could not load logging status." +msgstr "Не удалось загрузить состояние журналирования." + +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:752 msgid "Could not refresh logging status; showing the last known state." msgstr "Не удалось обновить состояние журналирования; показан последний известный статус." @@ -360,7 +364,7 @@ msgstr "" "содержать CSS-переменные success/error или info/warn; fwlive использует " "локальные цвета (работает автономно, данные не покидают устройство)." -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:321 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:323 msgid "" "If the WAN is quiet, wait for probes or use the optional ping check in " "Help / the enabling-logs guide." @@ -386,15 +390,15 @@ msgid "Interface (prefix ! to exclude)" msgstr "Интерфейс (! в начале для исключения)" #: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:244 -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:359 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:361 msgid "I’ll configure this under Network → Firewall" msgstr "Я настрою это в разделе Сеть → Межсетевой экран" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:284 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:286 msgid "Kernel log modules missing" msgstr "Отсутствуют модули журналирования ядра" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:287 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:289 msgid "" "Kernel netfilter log modules are missing. Install kmod-nf-log-ipv4 and kmod-" "nf-log-ipv6 (or kmod-nf-log / kmod-nf-log6), then reload the firewall." @@ -419,7 +423,7 @@ msgstr "Ограниченная диагностика — отсутствуе msgid "Live view may be incomplete — a legacy iptables table is registered" msgstr "Живой просмотр может быть неполным — зарегистрирована устаревшая таблица iptables" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:330 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:332 msgid "Logging is off on this router" msgstr "Журналирование на этом роутере отключено" @@ -440,7 +444,7 @@ msgstr "" "«Вручную» по-прежнему использует защиту сервера и частоту опроса; меняется " "бюджет выборки, а не интервал опроса." -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:378 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:380 msgid "Manual test (System → Terminal):" msgstr "Ручной тест (Система → Терминал):" @@ -463,14 +467,14 @@ msgstr "Больше фильтров" msgid "Network → Firewall" msgstr "Сеть → Межсетевой экран" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:275 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:277 msgid "" "No WAN firewall zone found in /etc/config/firewall. Configure zones under:" msgstr "" "WAN-зона межсетевого экрана не найдена в /etc/config/firewall. Настройте " "зоны в разделе:" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:270 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:272 msgid "No WAN zone found" msgstr "WAN-зона не найдена" @@ -483,7 +487,7 @@ msgstr "WAN-зона не найдена; без неё нельзя перек msgid "Not now" msgstr "Не сейчас" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:353 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:355 msgid "Nothing changes until you click Enable." msgstr "Ничего не изменится, пока вы не нажмёте «Включить»." @@ -495,11 +499,11 @@ msgstr "ВЫХОД" msgid "One line" msgstr "Одна строка" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:326 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:328 msgid "Open firewall zone settings" msgstr "Открыть настройки зон файрвола" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:333 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:335 msgid "" "OpenWrt does not write firewall events to the log until you turn logging on. " "Live View only shows what the firewall already logs — it does not add allow/" @@ -705,11 +709,11 @@ msgstr "" msgid "Theme tint fallback" msgstr "Запасной цвет темы" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:386 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:388 msgid "Then ping the router." msgstr "Затем отправьте ping на роутер." -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:304 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:306 msgid "" "This router reported a logging blocker that Live View does not recognize yet." msgstr "" @@ -737,7 +741,7 @@ msgid "Turn it back off with the WAN logging control on the watch strip." msgstr "" "Отключите её снова элементом управления журналом WAN на панели наблюдения." -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:347 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:349 msgid "" "Turns on WAN zone drop/reject logging (same as Network → Firewall → wan → " "Log). Rate-limited by the zone log_limit (OpenWrt default 10/minute). Normal " @@ -771,7 +775,7 @@ msgstr "Вид" msgid "WAN drop/reject logging is off." msgstr "Журналирование drop/reject для WAN отключено." -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:314 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:316 msgid "" "WAN drop/reject logging is on. Blocked inbound WAN traffic will show up " "here. Normal LAN browsing will not." @@ -801,7 +805,7 @@ msgid "WAN logging on (%s). Click to disable." msgstr "WAN-журнал: ВКЛ (%s). Нажмите, чтобы отключить." #: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:120 -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:301 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:303 msgid "WAN logging unavailable" msgstr "WAN-журналирование недоступно" @@ -813,7 +817,7 @@ msgstr "WAN-журналирование недоступно: отсутств msgid "WAN logging unavailable: no WAN zone" msgstr "WAN-журналирование недоступно: нет зоны WAN" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:311 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:313 msgid "Waiting for firewall events" msgstr "Ожидание событий межсетевого экрана" diff --git a/luci-app-fwlive/po/templates/luci-app-fwlive.pot b/luci-app-fwlive/po/templates/luci-app-fwlive.pot index da9cec4bc..46492d998 100644 --- a/luci-app-fwlive/po/templates/luci-app-fwlive.pot +++ b/luci-app-fwlive/po/templates/luci-app-fwlive.pot @@ -133,7 +133,11 @@ msgstr "" msgid "Could not enable logging." msgstr "" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:751 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:753 +msgid "Could not load logging status." +msgstr "" + +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:752 msgid "Could not refresh logging status; showing the last known state." msgstr "" @@ -317,7 +321,7 @@ msgid "" "data leaves the device)." msgstr "" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:321 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:323 msgid "" "If the WAN is quiet, wait for probes or use the optional ping check in " "Help / the enabling-logs guide." @@ -341,15 +345,15 @@ msgid "Interface (prefix ! to exclude)" msgstr "" #: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:244 -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:359 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:361 msgid "I’ll configure this under Network → Firewall" msgstr "" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:284 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:286 msgid "Kernel log modules missing" msgstr "" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:287 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:289 msgid "" "Kernel netfilter log modules are missing. Install kmod-nf-log-ipv4 and kmod-" "nf-log-ipv6 (or kmod-nf-log / kmod-nf-log6), then reload the firewall." @@ -371,7 +375,7 @@ msgstr "" msgid "Live view may be incomplete — a legacy iptables table is registered" msgstr "" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:330 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:332 msgid "Logging is off on this router" msgstr "" @@ -390,7 +394,7 @@ msgid "" "budget, not the polling interval." msgstr "" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:378 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:380 msgid "Manual test (System → Terminal):" msgstr "" @@ -413,12 +417,12 @@ msgstr "" msgid "Network → Firewall" msgstr "" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:275 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:277 msgid "" "No WAN firewall zone found in /etc/config/firewall. Configure zones under:" msgstr "" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:270 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:272 msgid "No WAN zone found" msgstr "" @@ -431,7 +435,7 @@ msgstr "" msgid "Not now" msgstr "" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:353 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:355 msgid "Nothing changes until you click Enable." msgstr "" @@ -443,11 +447,11 @@ msgstr "" msgid "One line" msgstr "" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:326 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:328 msgid "Open firewall zone settings" msgstr "" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:333 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:335 msgid "" "OpenWrt does not write firewall events to the log until you turn logging on. " "Live View only shows what the firewall already logs — it does not add allow/" @@ -633,11 +637,11 @@ msgstr "" msgid "Theme tint fallback" msgstr "" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:386 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:388 msgid "Then ping the router." msgstr "" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:304 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:306 msgid "" "This router reported a logging blocker that Live View does not recognize yet." msgstr "" @@ -662,7 +666,7 @@ msgstr "" msgid "Turn it back off with the WAN logging control on the watch strip." msgstr "" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:347 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:349 msgid "" "Turns on WAN zone drop/reject logging (same as Network → Firewall → wan → " "Log). Rate-limited by the zone log_limit (OpenWrt default 10/minute). Normal " @@ -690,7 +694,7 @@ msgstr "" msgid "WAN drop/reject logging is off." msgstr "" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:314 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:316 msgid "" "WAN drop/reject logging is on. Blocked inbound WAN traffic will show up " "here. Normal LAN browsing will not." @@ -715,7 +719,7 @@ msgid "WAN logging on (%s). Click to disable." msgstr "" #: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:120 -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:301 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:303 msgid "WAN logging unavailable" msgstr "" @@ -727,7 +731,7 @@ msgstr "" msgid "WAN logging unavailable: no WAN zone" msgstr "" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:311 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:313 msgid "Waiting for firewall events" msgstr "" diff --git a/luci-app-fwlive/po/zh_Hans/luci-app-fwlive.po b/luci-app-fwlive/po/zh_Hans/luci-app-fwlive.po index a8879e933..c261d677a 100644 --- a/luci-app-fwlive/po/zh_Hans/luci-app-fwlive.po +++ b/luci-app-fwlive/po/zh_Hans/luci-app-fwlive.po @@ -148,7 +148,11 @@ msgstr "无法禁用日志记录。" msgid "Could not enable logging." msgstr "无法启用日志记录。" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:751 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:753 +msgid "Could not load logging status." +msgstr "无法加载日志状态。" + +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:752 msgid "Could not refresh logging status; showing the last known state." msgstr "无法刷新日志状态;显示上次已知状态。" @@ -341,7 +345,7 @@ msgstr "" "如果行着色看起来缺失,当前 LuCI 主题可能未提供成功/错误或信息/警告 CSS 变量;" "fwlive 会回退到本地颜色(隔离运行,数据不会离开本设备)。" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:321 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:323 msgid "" "If the WAN is quiet, wait for probes or use the optional ping check in " "Help / the enabling-logs guide." @@ -367,15 +371,15 @@ msgid "Interface (prefix ! to exclude)" msgstr "接口(前缀 ! 表示排除)" #: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:244 -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:359 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:361 msgid "I’ll configure this under Network → Firewall" msgstr "稍后我会在 网络 → 防火墙 中配置" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:284 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:286 msgid "Kernel log modules missing" msgstr "缺少内核日志模块" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:287 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:289 msgid "" "Kernel netfilter log modules are missing. Install kmod-nf-log-ipv4 and kmod-" "nf-log-ipv6 (or kmod-nf-log / kmod-nf-log6), then reload the firewall." @@ -399,7 +403,7 @@ msgstr "诊断功能受限 — 缺少 timeout 命令" msgid "Live view may be incomplete — a legacy iptables table is registered" msgstr "实时视图可能不完整 — 已注册旧版 iptables 表" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:330 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:332 msgid "Logging is off on this router" msgstr "此路由器的日志记录已关闭" @@ -419,7 +423,7 @@ msgid "" msgstr "" "手动模式仍受服务器保护和轮询节奏约束;它改变的是抓取配额,而不是轮询间隔。" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:378 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:380 msgid "Manual test (System → Terminal):" msgstr "手动测试(系统 → 终端):" @@ -442,13 +446,13 @@ msgstr "更多筛选" msgid "Network → Firewall" msgstr "网络 → 防火墙" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:275 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:277 msgid "" "No WAN firewall zone found in /etc/config/firewall. Configure zones under:" msgstr "" "在 /etc/config/firewall 中未找到 WAN 防火墙区域。请在以下位置配置区域:" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:270 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:272 msgid "No WAN zone found" msgstr "未找到 WAN 区域" @@ -461,7 +465,7 @@ msgstr "未找到 WAN 区域;没有它无法切换日志记录。" msgid "Not now" msgstr "暂不" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:353 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:355 msgid "Nothing changes until you click Enable." msgstr "点击「启用」之前不会有任何更改。" @@ -473,11 +477,11 @@ msgstr "出接口" msgid "One line" msgstr "单行" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:326 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:328 msgid "Open firewall zone settings" msgstr "打开防火墙区域设置" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:333 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:335 msgid "" "OpenWrt does not write firewall events to the log until you turn logging on. " "Live View only shows what the firewall already logs — it does not add allow/" @@ -673,11 +677,11 @@ msgstr "防火墙记录流量时表格会自动更新。如果滚动太快,请 msgid "Theme tint fallback" msgstr "主题着色回退" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:386 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:388 msgid "Then ping the router." msgstr "然后 ping 路由器。" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:304 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:306 msgid "" "This router reported a logging blocker that Live View does not recognize yet." msgstr "此路由器报告了 Live View 尚无法识别的日志阻止项。" @@ -702,7 +706,7 @@ msgstr "主要通信方" msgid "Turn it back off with the WAN logging control on the watch strip." msgstr "通过监视栏上的 WAN 日志控件将其重新关闭。" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:347 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:349 msgid "" "Turns on WAN zone drop/reject logging (same as Network → Firewall → wan → " "Log). Rate-limited by the zone log_limit (OpenWrt default 10/minute). Normal " @@ -732,7 +736,7 @@ msgstr "视图" msgid "WAN drop/reject logging is off." msgstr "WAN 丢弃/拒绝日志记录已关闭。" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:314 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:316 msgid "" "WAN drop/reject logging is on. Blocked inbound WAN traffic will show up " "here. Normal LAN browsing will not." @@ -761,7 +765,7 @@ msgid "WAN logging on (%s). Click to disable." msgstr "WAN 日志:开(%s)。点击可禁用。" #: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:120 -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:301 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:303 msgid "WAN logging unavailable" msgstr "WAN 日志不可用" @@ -773,7 +777,7 @@ msgstr "WAN 日志不可用:缺少内核日志模块" msgid "WAN logging unavailable: no WAN zone" msgstr "WAN 日志不可用:无 WAN 区域" -#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:311 +#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:313 msgid "Waiting for firewall events" msgstr "正在等待防火墙事件" diff --git a/luci-app-fwlive/root/usr/libexec/fwlive-logging.sh b/luci-app-fwlive/root/usr/libexec/fwlive-logging.sh index 13d8c6d09..f19c67162 100755 --- a/luci-app-fwlive/root/usr/libexec/fwlive-logging.sh +++ b/luci-app-fwlive/root/usr/libexec/fwlive-logging.sh @@ -200,7 +200,11 @@ find_wan_zone_section_state() { _firewall_show=$(uci -q show firewall 2>/dev/null || true) _zones=$(printf '%s\n' "$_firewall_show" \ | sed -n 's/^firewall\.\([^.=]*\)=zone$/\1/p') - for zone in $_zones; do + # Line-wise here-doc (not an unquoted for-loop): @zone[N] is a glob + # character class and would expand when cwd contains @zoneN. The + # while-read stays in the current shell (here-doc, not a pipeline) + # so WAN_ZONE_* mutations and return reach the caller. + while IFS= read -r zone || [ -n "$zone" ]; do [ -n "$zone" ] || continue _name=$(uci -q get "firewall.${zone}.name" 2>/dev/null || true) if [ -z "$_name" ]; then @@ -239,7 +243,9 @@ find_wan_zone_section_state() { WAN_ZONE_FOUND="$zone" return 0 fi - done + done <comment. Last quoted log prefix / comment on # the line, including \" units (same capture as the former sed). +# Raw tabs in prefix/comment become the two-character sequence \t so the +# separator tab is unique. Backslashes are not re-encoded. nft_dump_fields() { LC_ALL=C awk ' function last_at(s, needle, p, i, found) { @@ -219,17 +228,60 @@ nft_dump_fields() { comment = quoted_at($0, "comment \"") if (prefix == "" && comment == "") next + gsub(/\\/, "\\\\", prefix) + gsub(/\t/, "\\t", prefix) + gsub(/\\/, "\\\\", comment) + gsub(/\t/, "\\t", comment) print prefix "\t" comment } ' || return 1 } +# Restore nft_dump_fields TSV encoding. Walk left to right: \\ is a +# literal backslash, \t is a tab. Other backslash pairs stay as written. +# Do not put backslash sequences in case patterns (they are escapes). +# Fast-path strings with no backslash: dash ${var#?} walks are O(n^2) and +# the map byte-cap tests feed ~60k prefixes with none. +# Result is _tsv_out (no fork). +_nft_tsv_unescape() { + case "$1" in + *\\*) ;; + *) + _tsv_out="$1" + return 0 + ;; + esac + _tab=$(printf '\t') + _in="$1" + _out= + while [ -n "$_in" ]; do + _head=${_in%"${_in#?}"} + _in=${_in#?} + if [ "$_head" = "\\" ]; then + _n=${_in%"${_in#?}"} + _in=${_in#?} + if [ "$_n" = t ]; then + _out="${_out}${_tab}" + elif [ "$_n" = "\\" ]; then + _out="${_out}\\" + else + _out="${_out}\\${_n}" + fi + else + _out="${_out}${_head}" + fi + done + _tsv_out="$_out" +} + # pass=labeled|unlabeled|all — labeled before unlabeled so !fw4: beats # an earlier cosmetic for the same prefix under first-wins. # Reads TSV from nft_dump_fields; stops once the map cap trips. map_from_nft_stream() { pass="${1:-all}" _tab=$(printf '\t') + _esc_t=$(printf '\\t') + _esc_b=$(printf '\\\\') # IFS= keeps a leading tab (empty prefix + comment-only !fw4: line). while IFS= read -r line || [ -n "$line" ]; do [ -n "$_map_overflow" ] && break @@ -239,6 +291,10 @@ map_from_nft_stream() { *"$_tab"*) comment="${line#*"$_tab"}" ;; *) comment="" ;; esac + _nft_tsv_unescape "$prefix" + prefix="$_tsv_out" + _nft_tsv_unescape "$comment" + comment="$_tsv_out" label='' case "$comment" in @@ -666,14 +722,14 @@ resolve_addresses() { return 0 fi - # Wall-clock budget clock starts at the TRUE function entry: the budget - # must bound the whole call — including read_rpc_input (stalled stdin - # must not escape the budget) and json parsing. `date +%s` can jump - # under NTP sync — accepted (worst case the budget over- or under-runs - # by the jump; worker starvation is still prevented). - start=$(date +%s) # Optional JSON on $1 (rpcd may also pass argv $3 at the call site); else stdin. input=$(read_rpc_input "$1") + # Wall-clock budget starts after the input read and bounds the lookup + # loop only. Stalled stdin is not interrupted — production rpcd closes + # a finite payload. `date +%s` can jump under NTP sync — accepted + # (worst case the budget over- or under-runs by the jump; worker + # starvation is still prevented). + start=$(date +%s) lookups=0 truncated=0 OUT='' @@ -719,7 +775,7 @@ resolve_addresses() { truncated=1 break fi - # Abort cleanly once the whole-call wall-clock budget is spent; + # Abort cleanly once the lookup-loop wall-clock budget is spent; # never START a lookup past the budget. now=$(date +%s) if [ $((now - start)) -ge "$RESOLVE_BUDGET" ]; then @@ -793,6 +849,42 @@ run_selftest() { expected=$(printf 'a\\n\\nb') check_eq 'json_escape blank line' "$escaped" "$expected" "expected $expected got $escaped" || return 1 + # Charset applies to every character, not only the first. + if ! is_uci_style_name 'Rule'; then + echo 'is_uci_style_name: expected accept Rule' >&2 + return 1 + fi + if ! is_uci_style_name 'Allow-SSH'; then + echo 'is_uci_style_name: expected accept Allow-SSH' >&2 + return 1 + fi + if ! is_uci_style_name '-lead'; then + echo 'is_uci_style_name: expected accept -lead' >&2 + return 1 + fi + if is_uci_style_name 'My Rule'; then + echo 'is_uci_style_name: expected reject for space' >&2 + return 1 + fi + # First character is legal; later $ / ; / . must still fail. + # shellcheck disable=SC2016 + if is_uci_style_name 'rule$x'; then + echo 'is_uci_style_name: expected reject rule$x (later-char illegal)' >&2 + return 1 + fi + if is_uci_style_name 'rule;id'; then + echo 'is_uci_style_name: expected reject rule;id (later-char illegal)' >&2 + return 1 + fi + if is_uci_style_name 'rule.x'; then + echo 'is_uci_style_name: expected reject rule.x (later-char illegal)' >&2 + return 1 + fi + if is_uci_style_name ''; then + echo 'is_uci_style_name: expected reject empty' >&2 + return 1 + fi + if is_resolvable_address 'not an ip'; then echo 'is_resolvable_address: expected reject for spaces' >&2 return 1 @@ -864,10 +956,17 @@ run_selftest() { # Escaped-quote capture: a log prefix containing \" must not be truncated # at the escaped quote (nft dumps strings with backslash escapes). _tab=$(printf '\t') + _esc_t=$(printf '\\t') + _esc_b=$(printf '\\\\') got=$(printf '%s\n' 'log prefix "My \" Rule"' | nft_dump_fields) got="${got%%${_tab}*}" - if [ "$got" != 'My \" Rule' ]; then - echo "escaped-quote prefix capture: expected 'My \\\" Rule' got '$got'" >&2 + if [ "$got" != 'My \\" Rule' ]; then + echo "escaped-quote prefix capture: expected encoded 'My \\\\\" Rule' got '$got'" >&2 + return 1 + fi + _nft_tsv_unescape "$got" + if [ "$_tsv_out" != 'My \" Rule' ]; then + echo "escaped-quote prefix unescape: expected 'My \\\" Rule' got '$_tsv_out'" >&2 return 1 fi # Plain prefix still captures unchanged. @@ -884,6 +983,70 @@ run_selftest() { echo "empty prefix capture: expected '' got '$got'" >&2 return 1 fi + # Guest nft list emits a raw tab inside the quoted prefix + # (log prefix "fwlivetabbed"). Encode it in the TSV field. + got=$(printf '%s\n' "log prefix \"fwlive${_tab}tabbed\"" | nft_dump_fields) + case "$got" in + *"${_tab}"*"${_tab}"*) + echo "tabbed prefix TSV: expected one separator tab, got extra" >&2 + return 1 + ;; + *"${_tab}"*) ;; + *) + echo "tabbed prefix TSV: missing separator tab, got '$got'" >&2 + return 1 + ;; + esac + got_prefix="${got%%${_tab}*}" + if [ "$got_prefix" != "fwlive${_esc_t}tabbed" ]; then + echo "tabbed prefix TSV: expected fwlive\\ttabbed got '$got_prefix'" >&2 + return 1 + fi + _nft_tsv_unescape "$got_prefix" + if [ "$_tsv_out" != "fwlive${_tab}tabbed" ]; then + echo "tabbed prefix unescape: expected fwlivetabbed" >&2 + return 1 + fi + # Literal backslash-t must round-trip and not become a tab. + got=$(printf '%s\n' "log prefix \"fwlive${_esc_t}lit\"" | nft_dump_fields) + got_prefix="${got%%${_tab}*}" + if [ "$got_prefix" != "fwlive${_esc_b}tlit" ]; then + echo "literal \\\\t TSV: expected fwlive\\\\\\\\tlit got '$got_prefix'" >&2 + return 1 + fi + _nft_tsv_unescape "$got_prefix" + if [ "$_tsv_out" != "fwlive${_esc_t}lit" ]; then + echo "literal \\\\t unescape: expected fwlive\\\\tlit got '$_tsv_out'" >&2 + return 1 + fi + # Tabbed prefix plus !fw4: label: map key is the full prefix. + got=$(printf '%s\n' "log prefix \"fwlive${_tab}tabbed\" comment \"!fw4: TabRule\"" | nft_dump_fields) + OUT='' + _map_keys=0 + _map_overflow='' + map_from_nft_stream all < TabRule, got $OUT" >&2 + return 1 + fi + # Comment-only / !fw4: label path is unchanged when there is no tab. + got=$(printf '%s\n' 'comment "!fw4: GoodName"' | nft_dump_fields) + if [ "$got" != "${_tab}!fw4: GoodName" ]; then + echo "comment-only TSV: expected leading tab + !fw4: GoodName got '$got'" >&2 + return 1 + fi + OUT='' + _map_keys=0 + _map_overflow='' + map_from_nft_stream all <&2 + return 1 + fi got=$(poll_clamp_lines "99999999999999999999") check_eq poll_clamp_lines "$got" "$POLL_LINES_MAX" "over-long expected $POLL_LINES_MAX got $got" || return 1 diff --git a/luci-app-passwall/Makefile b/luci-app-passwall/Makefile index 051ce2eb5..5a7fbd73f 100644 --- a/luci-app-passwall/Makefile +++ b/luci-app-passwall/Makefile @@ -7,8 +7,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall -PKG_VERSION:=26.9.16 -PKG_RELEASE:=292 +PKG_VERSION:=26.9.26 +PKG_RELEASE:=293 PKG_PO_VERSION:=$(PKG_VERSION) PKG_CONFIG_DEPENDS:= \ diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/5_hysteria2.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/5_hysteria2.lua index 1ec435182..bd9a4130b 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/5_hysteria2.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/5_hysteria2.lua @@ -101,7 +101,7 @@ o = s:option(Value, "tls_serverName", translate("Domain")) o = s:option(Flag, "tls_allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped.")) o.default = "0" -o = s:option(Value, "tls_pinSHA256", translate("PinSHA256"),translate("Certificate fingerprint")) +o = s:option(Value, "tls_pinSHA256", translate("TLS Chain Fingerprint (SHA256)")) o = s:option(Value, "up_mbps", translate("Max upload Mbps")) o.rewrite_option = o.option diff --git a/luci-app-passwall/luasrc/passwall/util_hysteria2.lua b/luci-app-passwall/luasrc/passwall/util_hysteria2.lua index 60350d069..21c0954fa 100644 --- a/luci-app-passwall/luasrc/passwall/util_hysteria2.lua +++ b/luci-app-passwall/luasrc/passwall/util_hysteria2.lua @@ -140,7 +140,7 @@ function gen_config(var) tls = { sni = node.tls_serverName, insecure = (node.tls_allowInsecure == "1") and true or false, - pinSHA256 = (node.tls_pinSHA256) and node.tls_pinSHA256 or nil, + pinSHA256 = (node.tls_pinSHA256 and node.tls_pinSHA256 ~= "") and api.sha256_sb_xray(node.tls_pinSHA256) or nil, ech = (node.ech == "1") and node.ech_config or nil }, quic = { diff --git a/luci-app-passwall/root/usr/share/passwall/rules/lanlist_ipv6 b/luci-app-passwall/root/usr/share/passwall/rules/lanlist_ipv6 index 34ec9e31c..de1f11451 100644 --- a/luci-app-passwall/root/usr/share/passwall/rules/lanlist_ipv6 +++ b/luci-app-passwall/root/usr/share/passwall/rules/lanlist_ipv6 @@ -11,6 +11,6 @@ 2002::/16 3fff::/20 5f00::/16 -fc00::/7 +fd00::/8 fe80::/64 ff00::/8 diff --git a/luci-app-passwall/root/usr/share/passwall/rules/proxy_host b/luci-app-passwall/root/usr/share/passwall/rules/proxy_host index 72c498c54..58a53b49c 100644 --- a/luci-app-passwall/root/usr/share/passwall/rules/proxy_host +++ b/luci-app-passwall/root/usr/share/passwall/rules/proxy_host @@ -11,3 +11,17 @@ google.com.tw google.com.hk gstatic.com xn--ngstr-lra8j.com + +#DNS-Leak +dnsleaktest.com +dnsleak.com +expressvpn.com +nordvpn.com +surfshark.com +ipleak.net +perfect-privacy.com +browserleaks.com +browserleaks.org +vpnunlimited.com +whoer.net +whrq.net diff --git a/luci-app-ssr-plus/Makefile b/luci-app-ssr-plus/Makefile index d65ed71d7..7aa417af3 100644 --- a/luci-app-ssr-plus/Makefile +++ b/luci-app-ssr-plus/Makefile @@ -4,7 +4,7 @@ LUCI_TITLE:=luci-app-ssr-plus LUCI_PKGARCH:=all PKG_NAME:=luci-app-ssr-plus PKG_VERSION:=196 -PKG_RELEASE:=76 +PKG_RELEASE:=77 PKG_CONFIG_DEPENDS:= \ CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \ @@ -32,7 +32,7 @@ LUCI_PKGARCH:=all LUCI_DEPENDS:= \ +libuci-lua +lua +luci-compat +coreutils +coreutils-base64 +dns2tcp +dnsmasq-full \ +jq +ip-full +lua-neturl +libuci-lua +microsocks +ipt2socks +lyaml \ - +resolveip +bind-dig +curl +nping +unzip +xz-utils +xz \ + +resolveip +curl +nping +unzip +xz-utils +xz \ +PACKAGE_$(PKG_NAME)_INCLUDE_Xray:xray-core \ +PACKAGE_$(PKG_NAME)_INCLUDE_Xray:coreutils-timeout \ +PACKAGE_$(PKG_NAME)_INCLUDE_Http_Proxy:3proxy \ diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua index aa7b562d8..1aea1be95 100644 --- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua +++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua @@ -5,27 +5,24 @@ local m, s, sec, o local uci = require "luci.model.uci".cursor() local URL = require "url" -local sys = require "luci.sys" -local util = require "luci.util" -local datatypes = require "luci.cbi.datatypes" -- 获取 LAN IP 地址 function lanip() local lan_ip -- 尝试从 UCI 直接读取 - lan_ip = sys.exec("uci -q get network.lan.ipaddr 2>/dev/null | awk -F'/' '{print $1}' | tr -d '\\n'") + lan_ip = luci.sys.exec("uci -q get network.lan.ipaddr 2>/dev/null | awk -F'/' '{print $1}' | tr -d '\\n'") -- 尝试从 LAN 接口信息中读取(优先 ifname,再 fallback 到 device) if not lan_ip or lan_ip == "" then - lan_ip = sys.exec([[ + lan_ip = luci.sys.exec([[ ip -4 addr show $(uci -q -p /tmp/state get network.lan.device || uci -q -p /tmp/state get network.lan.device) 2>/dev/null \ | grep -w 'inet' | awk '{print $2}' | cut -d'/' -f1 | grep -v '^127\.' | head -n1 | tr -d '\n']]) end -- 取任意一个 global IPv4 地址 if not lan_ip or lan_ip == "" then - lan_ip = sys.exec([[ + lan_ip = luci.sys.exec([[ ip -4 addr show scope global 2>/dev/null \ | grep -w 'inet' | awk '{print $2}' | cut -d'/' -f1 | grep -v '^127\.' | head -n1 | tr -d '\n']]) end @@ -34,9 +31,10 @@ ip -4 addr show scope global 2>/dev/null \ end local lan_ip = lanip() +local validation = require "luci.cbi.datatypes" local clash_nodes = {} local function is_finded(e) - return sys.exec(string.format('type -t -p "%s" -p "/usr/libexec/%s" 2>/dev/null', e, e)) ~= "" + return luci.sys.exec(string.format('type -t -p "%s" -p "/usr/libexec/%s" 2>/dev/null', e, e)) ~= "" end local function clash_display_name(s) @@ -50,28 +48,6 @@ local function clash_display_name(s) return "[CLASH]" end -local function is_valid_dns(str) - if datatypes.ip4addrport(str) then - return true - end - - local scheme, target = str:match("^([a-zA-Z0-9%+%-%.]+)://(.+)$") - if scheme and target and #target > 0 then - return true - end - - local host = str:match("^([^:]+):%d+$") or str - if host:match("[a-zA-Z]") - and not host:match("[^%w%.%-]") - and not host:match("^[%.%-]") - and not host:match("[%.%-]$") - and not host:match("%.%.") then - return true - end - - return false -end - m = Map("shadowsocksr", translate("ShadowSocksR Plus+ Settings"), translate("

Support SS/SSR/V2RAY/XRAY/TROJAN/TUIC/HYSTERIA2/NAIVEPROXY/SOCKS5/CLASH etc.

")) m:section(SimpleSection).template = "shadowsocksr/status" @@ -155,23 +131,6 @@ o:value("7", translate("Prefer module built-in DNS")) o:value("0", translate("Use Local DNS Service listen port 5335")) o.default = 1 -o = s:option(Value, "dns2tcp_tunnel_forward", translate("Anti-pollution DNS Server")) -o:value("8.8.4.4:53", translate("Google Public DNS (8.8.4.4)")) -o:value("8.8.8.8:53", translate("Google Public DNS (8.8.8.8)")) -o:value("208.67.222.222:53", translate("OpenDNS (208.67.222.222)")) -o:value("208.67.220.220:53", translate("OpenDNS (208.67.220.220)")) -o:value("209.244.0.3:53", translate("Level 3 Public DNS (209.244.0.3)")) -o:value("209.244.0.4:53", translate("Level 3 Public DNS (209.244.0.4)")) -o:value("4.2.2.1:53", translate("Level 3 Public DNS (4.2.2.1)")) -o:value("4.2.2.2:53", translate("Level 3 Public DNS (4.2.2.2)")) -o:value("4.2.2.3:53", translate("Level 3 Public DNS (4.2.2.3)")) -o:value("4.2.2.4:53", translate("Level 3 Public DNS (4.2.2.4)")) -o:value("1.1.1.1:53", translate("Cloudflare DNS (1.1.1.1)")) -o:depends("pdnsd_enable", "1") -o.description = translate("Custom DNS Server format as IP:PORT (default: 8.8.4.4:53)") -o.datatype = "ip4addrport" -o.default = "8.8.4.4:53" - o = s:option(Value, "tunnel_forward", translate("Anti-pollution DNS Server")) o:value("8.8.4.4:53", translate("Google Public DNS (8.8.4.4)")) o:value("8.8.8.8:53", translate("Google Public DNS (8.8.8.8)")) @@ -184,38 +143,11 @@ o:value("4.2.2.2:53", translate("Level 3 Public DNS (4.2.2.2)")) o:value("4.2.2.3:53", translate("Level 3 Public DNS (4.2.2.3)")) o:value("4.2.2.4:53", translate("Level 3 Public DNS (4.2.2.4)")) o:value("1.1.1.1:53", translate("Cloudflare DNS (1.1.1.1)")) +o:depends("pdnsd_enable", "1") o:depends("pdnsd_enable", "7") -o.datatype = nil -o.description = - "
    " .. - "
  • " .. translate("Custom DNS Server (supports format IP:PORT, domain name, or scheme://...).") .. "
  • " .. - "
  • " .. translate("Muitiple DNS server can saperate with ','") .. "
  • " .. - "
  • " .. translate("Note: IP addresses MUST specify a port.") .. "
  • " .. - "
" +o.description = translate("Custom DNS Server format as IP:PORT (default: 8.8.4.4:53)") +o.datatype = "ip4addrport" o.default = "8.8.4.4:53" -o.validate = function(self, value, section) - if section and value and value ~= "" then - local parts = {} - for part in (value .. ","):gmatch("(.-),") do - part = part:gsub("^%s*", ""):gsub("%s*$", "") - if part ~= "" then - if not is_valid_dns(part) then - return nil, translate("Expecting: %s"):format(translate("IP:PORT, Domain, or Protocol URL (e.g. 8.8.8.8:53, dns.google, https://...).") .. - "
".. translate("Note: Pure IP without port is not allowed.")) - end - table.insert(parts, part) - end - end - - if #parts > 0 then - return table.concat(parts, ",") - end - - return nil, translate("Expecting: %s"):format(translate("valid address:port")) -- 有效的地址:端口 - end - - return value -end o = s:option(Value, "tunnel_forward_mosdns", translate("Anti-pollution DNS Server")) o:value("tcp://8.8.4.4:53,tcp://8.8.8.8:53", translate("Google Public DNS")) @@ -293,7 +225,7 @@ if is_finded("chinadns-ng") then local parts = {} for part in string.gmatch(value, "[^,]+") do part = part:gsub("^%s*", ""):gsub("%s*$", "") - if not datatypes.ip4addrport(part) then + if not validation.ip4addrport(part) then return nil, translate("Expecting: %s"):format(translate("valid address:port (comma separated)")) end table.insert(parts, part) @@ -304,7 +236,7 @@ if is_finded("chinadns-ng") then return table.concat(parts, ",") end - if datatypes.ip4addrport(value) then + if validation.ip4addrport(value) then return value end diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua index 8dc629419..0d92887ff 100644 --- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua +++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua @@ -1,11 +1,8 @@ require "luci.ip" require "nixio.fs" require "luci.sys" -local ucic = luci.model.uci.cursor() local m, s, o -local enable_mihomo = ucic:get_first('shadowsocksr', 'server_subscribe', 'enable_mihomo') or '' - m = Map("shadowsocksr") s = m:section(TypedSection, "access_control") @@ -119,7 +116,7 @@ o.rows = 13 o.wrap = "off" o.rmempty = true o.cfgvalue = function(self, section) - return nixio.fs.readfile(denydomainconf) or "" + return nixio.fs.readfile(denydomainconf) or " " end o.write = function(self, section, value) nixio.fs.writefile(denydomainconf, value:gsub("\r\n", "\n")) @@ -128,54 +125,6 @@ o.remove = function(self, section, value) nixio.fs.writefile(denydomainconf, "") end -if enable_mihomo == "1" then - s:tab("fake_ip_filter", translate("Fake-IP Filter and Fallback Lists")) - local fake_ip_filter_conf = "/etc/ssrplus/fake_ip_filter.list" - o = s:taboption("fake_ip_filter", TextValue, "fake_ip_filter_conf", "", "" .. translate("Specifically for editing the Fake-IP filter list.") .. "") -- 专用于编辑 Fake-IP 过滤规则文件。 - o.rows = 13 - o.wrap = "off" - o.rmempty = true - o.cfgvalue = function(self, section) - return nixio.fs.readfile(fake_ip_filter_conf) or "" - end - o.write = function(self, section, value) - nixio.fs.writefile(fake_ip_filter_conf, value:gsub("\r\n", "\n")) - end - o.remove = function(self, section, value) - nixio.fs.writefile(fake_ip_filter_conf, "") - end - - local fallback_ipcidr_conf = "/etc/ssrplus/fallback_ipcidr.list" - o = s:taboption("fake_ip_filter", TextValue, "fallback_ipcidr_conf", "", "" .. translate("Specifically for editing the Fallback IP-CIDR list.") .. "") -- 专用于编辑 Fallback IP-CIDR 规则文件。 - o.rows = 13 - o.wrap = "off" - o.rmempty = true - o.cfgvalue = function(self, section) - return nixio.fs.readfile(fallback_ipcidr_conf) or "" - end - o.write = function(self, section, value) - nixio.fs.writefile(fallback_ipcidr_conf, value:gsub("\r\n", "\n")) - end - o.remove = function(self, section, value) - nixio.fs.writefile(fallback_ipcidr_conf, "") - end - - local fallback_domain_conf = "/etc/ssrplus/fallback_domain.list" - o = s:taboption("fake_ip_filter", TextValue, "fallback_domain_conf", "", "" .. translate("Specifically for editing the Fallback domain filter list.") .. "") -- 专用于编辑 Fallback 域名过滤规则文件。 - o.rows = 13 - o.wrap = "off" - o.rmempty = true - o.cfgvalue = function(self, section) - return nixio.fs.readfile(fallback_domain_conf) or "" - end - o.write = function(self, section, value) - nixio.fs.writefile(fallback_domain_conf, value:gsub("\r\n", "\n")) - end - o.remove = function(self, section, value) - nixio.fs.writefile(fallback_domain_conf, "") - end -end - if luci.sys.call('[ -f "/www/luci-static/resources/uci.js" ]') == 0 then m.apply_on_parse = true function m.on_apply(self) diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua index e55596d53..e7b13faa7 100644 --- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua +++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua @@ -188,10 +188,9 @@ local function save_uploaded_clash_node(upload_name, final_path) local alias if not sid then - local sid_output = luci.sys.exec("uci add shadowsocksr servers 2>/dev/null") - sid = trim(sid_output) + sid = uci:add("shadowsocksr", "servers") end - if not sid or sid == "" then + if not sid then return nil end @@ -201,50 +200,20 @@ local function save_uploaded_clash_node(upload_name, final_path) alias = upload_alias(upload_name) end - local switch_enable = uci:get("shadowsocksr", sid, "switch_enable") - if not switch_enable or switch_enable == "" then - switch_enable = uci:get_first("shadowsocksr", "server_subscribe", "switch", "1") or "1" - end - - local clash_user_agent = uci:get("shadowsocksr", sid, "clash_user_agent") - if not clash_user_agent or clash_user_agent == "" then - clash_user_agent = "clash" - end - - local safe_sid = luci.util.shellquote(sid) - local safe_alias = luci.util.shellquote(alias) - local safe_path = luci.util.shellquote(final_path) - local safe_upload_name = luci.util.shellquote(upload_name) - local safe_switch_enable = luci.util.shellquote(switch_enable) - local safe_user_agent = luci.util.shellquote(clash_user_agent) - - local cmd = string.format( - "uci set shadowsocksr.%s.type=clash && " .. - "uci set shadowsocksr.%s.alias=%s && " .. - "uci set shadowsocksr.%s.server=127.0.0.1 && " .. - "uci set shadowsocksr.%s.server_port=0 && " .. - "uci delete shadowsocksr.%s.clash_url 2>/dev/null; " .. - "uci set shadowsocksr.%s.clash_path=%s && " .. - "uci set shadowsocksr.%s.clash_user_agent=%s && " .. - "uci set shadowsocksr.%s.switch_enable=%s && " .. - "uci set shadowsocksr.%s.yaml_upload=1 && " .. - "uci set shadowsocksr.%s.yaml_upload_name=%s && " .. - "uci commit shadowsocksr", - safe_sid, - safe_sid, safe_alias, - safe_sid, - safe_sid, - safe_sid, - safe_sid, safe_path, - safe_sid, safe_user_agent, - safe_sid, safe_switch_enable, - safe_sid, - safe_sid, safe_upload_name - ) - - if luci.sys.call(cmd) ~= 0 then - return nil + uci:set("shadowsocksr", sid, "type", "clash") + uci:set("shadowsocksr", sid, "alias", alias) + uci:set("shadowsocksr", sid, "server", "127.0.0.1") + uci:set("shadowsocksr", sid, "server_port", "0") + uci:delete("shadowsocksr", sid, "clash_url") + uci:set("shadowsocksr", sid, "clash_path", final_path) + uci:set("shadowsocksr", sid, "clash_user_agent", uci:get("shadowsocksr", sid, "clash_user_agent") or "clash") + if not uci:get("shadowsocksr", sid, "switch_enable") then + uci:set("shadowsocksr", sid, "switch_enable", uci:get_first("shadowsocksr", "server_subscribe", "switch", "1") or "1") end + uci:set("shadowsocksr", sid, "yaml_upload", "1") + uci:set("shadowsocksr", sid, "yaml_upload_name", upload_name) + uci:save("shadowsocksr") + uci:commit("shadowsocksr") cleanup_old_clash_path(old_path, final_path, sid) luci.sys.call(string.format("/etc/init.d/shadowsocksr clash_cache %s >/dev/null 2>&1 &", luci.util.shellquote(sid))) @@ -298,48 +267,32 @@ if has_mihomo then local tmp_output = string.format("%s/.upload-%d-%d.yaml", CLASH_YAML_DIR, nixio.getpid(), os.time()) local sid local alias - local redirect_message_url nixio.fs.mkdirr(CLASH_YAML_DIR) nixio.fs.remove(tmp_output) if preprocess_clash_yaml(upload_tmp_path, tmp_output) then hash = hash_file(tmp_output) - if not hash or hash == "" then + if hash == "" then + nixio.fs.remove(tmp_output) upload_errmessage = translate("Uploaded YAML validation or preprocessing failed.") else final_path = string.format("%s/%s.yaml", CLASH_YAML_DIR, hash) - local mv_ret = luci.sys.call(string.format("mv -f %s %s", luci.util.shellquote(tmp_output), luci.util.shellquote(final_path))) - if mv_ret == 0 and nixio.fs.access(final_path) then - sid, alias = save_uploaded_clash_node(upload_filename, final_path) - if sid then - local message = string.format(translate("Custom YAML imported successfully: %s"), alias or sid) - local redirect_url = luci.dispatcher.build_url("admin", "services", "shadowsocksr", "servers") - redirect_message_url = redirect_url .. "?upload_message=" .. luci.http.urlencode(message) - else - upload_errmessage = translate("Uploaded YAML validation or preprocessing failed.") - if final_path and nixio.fs.access(final_path) then - nixio.fs.remove(final_path) - end - end + luci.sys.call(string.format("mv -f %s %s", luci.util.shellquote(tmp_output), luci.util.shellquote(final_path))) + sid, alias = save_uploaded_clash_node(upload_filename, final_path) + if sid then + upload_message = string.format(translate("Custom YAML imported successfully: %s"), alias or sid) else - upload_errmessage = translate("Failed to move uploaded YAML to target directory.") + upload_errmessage = translate("Uploaded YAML validation or preprocessing failed.") end end else - upload_errmessage = upload_errmessage or translate("Uploaded YAML validation or preprocessing failed.") - end - - if upload_tmp_path and nixio.fs.access(upload_tmp_path) then - nixio.fs.remove(upload_tmp_path) - end - if tmp_output and nixio.fs.access(tmp_output) then nixio.fs.remove(tmp_output) + upload_errmessage = translate("Uploaded YAML validation or preprocessing failed.") end + end - if redirect_message_url then - luci.http.redirect(redirect_message_url) - return - end + if upload_tmp_path then + nixio.fs.remove(upload_tmp_path) end end end @@ -454,11 +407,8 @@ end m = Map("shadowsocksr", translate("Servers subscription and manage")) if upload_errmessage then m.errmessage = upload_errmessage -else - local message = luci.http.formvalue("upload_message") - if message and message ~= "" then - m.message = message - end +elseif upload_message then + m.message = upload_message end local style_section = m:section(SimpleSection) @@ -616,29 +566,6 @@ if has_mihomo then o.rmempty = true o.description = translate("Disabled means use Xray or ShadowSocks-Rust core.") - o = s:option(Flag, "enable_fake_ip", string.format("%s", translate("Enable Fake-IP Mode"))) - o.default = "0" - o.rmempty = true - o:depends("enable_mihomo", "1") - o.description = translate( - "
    " .. - "
  • " .. translate("Enable Fake-IP mode; disable to use redir-host mode.") .. "
  • " .. - "
  • " .. translate("Enable Fake-IP mode for better performance.") .. "
  • " .. - "
  • " .. translate("If disabled, Redir-Host mode will be used which returns real IP addresses.") .. "
  • " .. - "
  • " .. translate("Tips: Fake-IP Filter and Fallback Lists:") .. " " .. - "" .. - "" .. - "
  • " .. - "
" - ) - o = s:option(Flag, "sub_convert", translate("Subscribe Convert Online")) o.description = translate("Convert subscriptions to Clash/Mihomo YAML with a subscription template URL.") o.default = "0" diff --git a/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_yaml_upload.htm b/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_yaml_upload.htm index b9438b856..830193afb 100644 --- a/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_yaml_upload.htm +++ b/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_yaml_upload.htm @@ -1,4 +1,4 @@ <%+cbi/valueheader%> - + <%+cbi/valuefooter%> diff --git a/luci-app-ssr-plus/po/templates/ssr-plus.pot b/luci-app-ssr-plus/po/templates/ssr-plus.pot index 35eef4811..013227a8a 100644 --- a/luci-app-ssr-plus/po/templates/ssr-plus.pot +++ b/luci-app-ssr-plus/po/templates/ssr-plus.pot @@ -12,11 +12,11 @@ msgstr "" msgid "0" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:118 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:94 msgid "1 Thread" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:125 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:101 msgid "128 Threads" msgstr "" @@ -24,15 +24,15 @@ msgstr "" msgid "16" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:122 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:98 msgid "16 Threads" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:119 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:95 msgid "2 Threads" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:123 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:99 msgid "32 Threads" msgstr "" @@ -40,19 +40,19 @@ msgstr "" msgid "360" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:275 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:207 msgid "360 Security DNS (China Telecom) (101.226.4.6)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:276 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:208 msgid "360 Security DNS (China Unicom) (123.125.81.6)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:120 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:96 msgid "4 Threads" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:124 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:100 msgid "64 Threads" msgstr "" @@ -60,7 +60,7 @@ msgstr "" msgid "8" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:121 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:97 msgid "8 Threads" msgstr "" @@ -74,7 +74,7 @@ msgstr "" msgid "" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:75 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:51 msgid "" "

Support SS/SSR/V2RAY/XRAY/TROJAN/TUIC/HYSTERIA2/NAIVEPROXY/SOCKS5/CLASH " "etc.

" @@ -86,9 +86,8 @@ msgstr "" #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1654 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1667 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1680 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:254 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:281 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:624 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:186 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:213 msgid "
  • " msgstr "" @@ -142,12 +141,12 @@ msgstr "" msgid "Advertising Data" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:273 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:205 msgid "AliYun Public DNS (223.5.5.5)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:684 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:796 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:611 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:723 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/subscribe_actions_footer.htm:187 msgid "Alias" msgstr "" @@ -160,7 +159,7 @@ msgstr "" msgid "All" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:136 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:112 msgid "All Ports" msgstr "" @@ -176,15 +175,15 @@ msgstr "" msgid "All settings saved successfully." msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:38 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:35 msgid "Allow all except listed" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:37 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:34 msgid "Allow listed only" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:536 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:486 msgid "Allow subscribe Insecure nodes By default" msgstr "" @@ -206,10 +205,9 @@ msgstr "" msgid "An FinalMaskObject in JSON format, used for sharing." msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:158 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:175 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:220 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:239 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:134 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:152 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:171 msgid "Anti-pollution DNS Server" msgstr "" @@ -229,12 +227,12 @@ msgstr "" msgid "Apple domains optimization" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:856 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:862 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:783 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:789 msgid "Apply" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:476 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:426 msgid "Appointment Mode" msgstr "" @@ -246,19 +244,19 @@ msgstr "" msgid "Are you sure you want to restore the client to default settings?" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:873 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:800 msgid "Auto Switch" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:117 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:93 msgid "Auto Threads" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:471 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:421 msgid "Auto Update" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:473 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:423 msgid "Auto Update Server subscription, GFW list and CHN route" msgstr "" @@ -285,7 +283,7 @@ msgstr "" msgid "Baidu Connectivity" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:274 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:206 msgid "Baidu Public DNS (180.76.76.76)" msgstr "" @@ -302,7 +300,7 @@ msgstr "" msgid "BitTorrent (uTP)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:99 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:96 msgid "Black Domain List" msgstr "" @@ -310,7 +308,7 @@ msgstr "" msgid "Bloom Filter" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:83 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:80 msgid "Bypass Domain List" msgstr "" @@ -318,7 +316,7 @@ msgstr "" msgid "CLOSE WIN" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:277 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:209 msgid "CNNIC SDNS (1.2.4.8)" msgstr "" @@ -403,7 +401,7 @@ msgstr "" msgid "China IP Data" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:260 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:192 msgid "ChinaDNS-NG query protocol" msgstr "" @@ -423,7 +421,7 @@ msgstr "" msgid "Clash" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:112 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:88 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:893 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:1096 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_panel.htm:3 @@ -458,10 +456,6 @@ msgstr "" msgid "Clear logs" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:633 -msgid "Click here to manage" -msgstr "" - #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua:309 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1162 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1518 @@ -490,13 +484,12 @@ msgstr "" msgid "Close" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:226 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:158 msgid "Cloudflare DNS" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:169 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:186 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:250 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:145 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:182 msgid "Cloudflare DNS (1.1.1.1)" msgstr "" @@ -540,11 +533,11 @@ msgid "" "Controls the policy used when performing DNS queries for ECH configuration." msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:648 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:575 msgid "Convert Address" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:643 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:570 msgid "" "Convert subscriptions to Clash/Mihomo YAML with a subscription template URL." msgstr "" @@ -591,21 +584,16 @@ msgstr "" msgid "Custom Client Proxies Rules" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:191 -msgid "" -"Custom DNS Server (supports format IP:PORT, domain name, or scheme://...)." -msgstr "" - -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:171 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:255 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:148 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:187 msgid "Custom DNS Server format as IP:PORT (default: 8.8.4.4:53)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:282 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:214 msgid "Custom DNS Server format as IP:PORT (default: disabled)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:228 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:160 msgid "" "Custom DNS Server format as tcp://IP:PORT or tls://DOMAIN:PORT " "(tcp://8.8.8.8 or tls://dns.google:853)" @@ -615,11 +603,11 @@ msgstr "" msgid "Custom Plugin Path" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:138 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:114 msgid "Custom Ports" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:315 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:283 msgid "Custom YAML imported successfully: %s" msgstr "" @@ -649,7 +637,7 @@ msgstr "" msgid "DNS Anti-pollution" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:272 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:204 msgid "DNSPod Public DNS (119.29.29.29)" msgstr "" @@ -729,7 +717,7 @@ msgstr "" msgid "Delete" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:569 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:519 msgid "Delete All Subscribe Servers" msgstr "" @@ -737,22 +725,22 @@ msgstr "" msgid "Delete failed:" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:115 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:112 msgid "Deny Domain List" msgstr "" #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:726 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1037 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:105 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:36 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:81 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:33 msgid "Disable" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:268 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:200 msgid "Disable ChinaDNS-NG" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:231 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:163 msgid "Disable IPv6 for Overseas FQDN" msgstr "" @@ -768,7 +756,7 @@ msgstr "" msgid "Disable TCP No_delay" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:617 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:567 msgid "Disabled means use Xray or ShadowSocks-Rust core." msgstr "" @@ -780,7 +768,7 @@ msgstr "" msgid "Do you want to restore the client to default settings?" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:264 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:196 msgid "DoT upstream (Need use wolfssl version)" msgstr "" @@ -792,7 +780,7 @@ msgstr "" msgid "DomainSocket Path" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:267 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:199 msgid "Domestic DNS Server" msgstr "" @@ -839,7 +827,7 @@ msgstr "" #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua:323 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/server-config.lua:54 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/server.lua:101 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:676 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:603 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:1114 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/subscribe_actions_footer.htm:184 msgid "Enable" @@ -862,18 +850,6 @@ msgstr "" msgid "Enable ECH(optional)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:619 -msgid "Enable Fake-IP Mode" -msgstr "" - -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:626 -msgid "Enable Fake-IP mode for better performance." -msgstr "" - -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:625 -msgid "Enable Fake-IP mode; disable to use redir-host mode." -msgstr "" - #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:720 msgid "Enable Lazy Mode" msgstr "" @@ -882,7 +858,7 @@ msgstr "" msgid "Enable ML-DSA-65(optional)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:614 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:564 msgid "Enable Mihomo core" msgstr "" @@ -961,7 +937,7 @@ msgstr "" msgid "Encrypt Method" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:139 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:115 msgid "Enter Custom Ports" msgstr "" @@ -970,35 +946,35 @@ msgstr "" msgid "Error" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:483 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:433 msgid "Every Day" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:488 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:438 msgid "Every Friday" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:484 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:434 msgid "Every Monday" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:489 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:439 msgid "Every Saturday" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:490 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:440 msgid "Every Sunday" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:487 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:437 msgid "Every Thursday" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:485 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:435 msgid "Every Tuesday" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:486 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:436 msgid "Every Wednesday" msgstr "" @@ -1006,11 +982,9 @@ msgstr "" msgid "Example:" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:203 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:214 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:297 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:302 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:311 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:229 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:234 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:243 msgid "Expecting: %s" msgstr "" @@ -1034,7 +1008,7 @@ msgstr "" msgid "Failed to create temp directory" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:275 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:244 msgid "Failed to create temporary YAML upload file." msgstr "" @@ -1043,10 +1017,6 @@ msgstr "" msgid "Failed to fetch release metadata" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:325 -msgid "Failed to move uploaded YAML to target directory." -msgstr "" - #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:112 msgid "Failed to query component information." msgstr "" @@ -1059,10 +1029,6 @@ msgstr "" msgid "Failed to save custom client proxy rules." msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:132 -msgid "Fake-IP Filter and Fallback Lists" -msgstr "" - #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/server_header.htm:21 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/server_header.htm:61 msgid "Fetch Failed" @@ -1077,7 +1043,7 @@ msgstr "" msgid "File Not Exist" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:526 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:476 msgid "Filter Words splited by /" msgstr "" @@ -1138,11 +1104,11 @@ msgstr "" msgid "GFW List Data" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:130 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:106 msgid "GFW List Mode" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:67 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:64 msgid "Game Mode Host List" msgstr "" @@ -1188,7 +1154,7 @@ msgstr "" msgid "Global HTTP/HTTPS Proxy Server" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:132 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:108 msgid "Global Mode" msgstr "" @@ -1209,19 +1175,17 @@ msgstr "" msgid "Google Connectivity" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:221 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:153 msgid "Google Public DNS" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:159 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:176 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:240 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:135 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:172 msgid "Google Public DNS (8.8.4.4)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:160 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:177 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:241 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:136 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:173 msgid "Google Public DNS (8.8.8.8)" msgstr "" @@ -1323,7 +1287,7 @@ msgstr "" msgid "Hysterir QUIC parameters" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:131 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:107 msgid "IP Route Mode" msgstr "" @@ -1331,16 +1295,6 @@ msgstr "" msgid "IP Stack Preference" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:203 -msgid "" -"IP:PORT, Domain, or Protocol URL (e.g. 8.8.8.8:53, dns.google, https://...)." -msgstr "" - -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:627 -msgid "" -"If disabled, Redir-Host mode will be used which returns real IP addresses." -msgstr "" - #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua:148 msgid "If empty, Not change Apple domains parsing DNS (Default is empty)" msgstr "" @@ -1410,11 +1364,11 @@ msgstr "" msgid "Installed package check failed" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:16 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:13 msgid "Interface" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:15 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:12 msgid "Interface control" msgstr "" @@ -1486,23 +1440,23 @@ msgstr "" msgid "KcpTun Version" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:35 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:32 msgid "LAN Access Control" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:51 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:48 msgid "LAN Bypassed Host List" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:59 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:56 msgid "LAN Force Proxy Host List" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:41 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:38 msgid "LAN Host List" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:33 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:30 msgid "LAN IP AC" msgstr "" @@ -1511,51 +1465,45 @@ msgstr "" msgid "Latest Version" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:163 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:180 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:244 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:139 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:176 msgid "Level 3 Public DNS (209.244.0.3)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:164 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:181 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:245 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:140 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:177 msgid "Level 3 Public DNS (209.244.0.4)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:165 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:182 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:246 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:141 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:178 msgid "Level 3 Public DNS (4.2.2.1)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:166 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:183 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:247 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:142 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:179 msgid "Level 3 Public DNS (4.2.2.2)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:167 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:184 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:248 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:143 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:180 msgid "Level 3 Public DNS (4.2.2.3)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:168 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:185 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:249 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:144 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:181 msgid "Level 3 Public DNS (4.2.2.4)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:223 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:155 msgid "Level 3 Public DNS-1 (209.244.0.3-4)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:224 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:156 msgid "Level 3 Public DNS-2 (4.2.2.1-2)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:225 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:157 msgid "Level 3 Public DNS-3 (4.2.2.3-4)" msgstr "" @@ -1568,7 +1516,7 @@ msgid "" "Linux kernel TUN virtual NIC requires system support and root privileges." msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:21 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:18 msgid "Listen only on the given interface or, if unspecified, on all" msgstr "" @@ -1595,7 +1543,7 @@ msgstr "" msgid "Log" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:477 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:427 msgid "Loop Mode" msgstr "" @@ -1619,11 +1567,11 @@ msgstr "" msgid "MTU" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:104 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:80 msgid "Main Server" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:674 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:601 msgid "" "Manage multiple subscribe URLs, including Clash subscriptions. Only enabled " "entries are included when updating all subscriptions." @@ -1704,9 +1652,8 @@ msgstr "" msgid "Move up" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:192 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:256 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:283 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:188 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:215 msgid "Muitiple DNS server can saperate with ','" msgstr "" @@ -1714,7 +1661,7 @@ msgstr "" msgid "Multi" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:116 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:92 msgid "Multi Threads Option" msgstr "" @@ -1746,7 +1693,7 @@ msgstr "" msgid "Name" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:271 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:203 msgid "Nanjing Xinfeng 114DNS (114.114.114.114)" msgstr "" @@ -1786,7 +1733,7 @@ msgstr "" msgid "No available core (Shadowsocks, Mihomo or Xray) to import this node." msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:294 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:263 msgid "No custom YAML file was selected." msgstr "" @@ -1811,7 +1758,7 @@ msgstr "" msgid "No switchable proxy groups found." msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:698 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:625 msgid "" "Node order can be dragged with the mouse and takes effect immediately. The " "automatic switch order of server nodes is consistent with the node order in " @@ -1827,8 +1774,8 @@ msgstr "" #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1269 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1281 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1291 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:793 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:798 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:720 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:725 msgid "None" msgstr "" @@ -1850,14 +1797,6 @@ msgstr "" msgid "Not installed" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:193 -msgid "Note: IP addresses MUST specify a port." -msgstr "" - -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:204 -msgid "Note: Pure IP without port is not allowed." -msgstr "" - #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/log.lua:27 msgid "" "Note: Restoring configurations across different versions may cause " @@ -1908,7 +1847,7 @@ msgstr "" msgid "Once set, connects only when the server’s chain fingerprint matches." msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:137 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:113 msgid "Only Common Ports" msgstr "" @@ -1933,19 +1872,17 @@ msgstr "" msgid "OpenClash GeoSite Database" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:222 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:154 msgid "OpenDNS" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:162 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:179 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:243 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:138 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:175 msgid "OpenDNS (208.67.220.220)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:161 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:178 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:242 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:137 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:174 msgid "OpenDNS (208.67.222.222)" msgstr "" @@ -1996,7 +1933,7 @@ msgstr "" msgid "Permit Without Stream" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:841 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:768 msgid "Ping Latency" msgstr "" @@ -2036,7 +1973,7 @@ msgstr "" msgid "Pre-shared key" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:154 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:130 msgid "Prefer module built-in DNS" msgstr "" @@ -2067,7 +2004,7 @@ msgstr "" msgid "Protocol param (optional)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:135 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:111 msgid "Proxy Ports" msgstr "" @@ -2155,7 +2092,7 @@ msgstr "" msgid "Realm URL" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:860 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:787 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:348 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_panel_button.htm:17 msgid "Reapply" @@ -2189,11 +2126,11 @@ msgstr "" msgid "Refresh..." msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:495 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:445 msgid "Regular update (Hour)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:503 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:453 msgid "Regular update (Min)" msgstr "" @@ -2265,7 +2202,7 @@ msgstr "" msgid "Resetting Defaults..." msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:144 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:120 msgid "Resolve Dns Mode" msgstr "" @@ -2296,7 +2233,7 @@ msgstr "" msgid "Running" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:129 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:105 msgid "Running Mode" msgstr "" @@ -2329,15 +2266,15 @@ msgstr "" msgid "Save" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:557 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:507 msgid "Save Subscribe Settings" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:532 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:482 msgid "Save Words splited by /" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:559 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:509 msgid "Save current subscribe settings" msgstr "" @@ -2363,7 +2300,7 @@ msgstr "" msgid "Server Address" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:571 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:521 msgid "Server Count" msgstr "" @@ -2398,7 +2335,7 @@ msgstr "" msgid "Servers Nodes" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:454 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:407 msgid "Servers subscription and manage" msgstr "" @@ -2436,7 +2373,7 @@ msgstr "" msgid "ShadowSocksR Plus+" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:75 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:51 msgid "ShadowSocksR Plus+ Settings" msgstr "" @@ -2483,7 +2420,7 @@ msgstr "" msgid "Snell Version" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:801 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:728 msgid "Socket Connected" msgstr "" @@ -2516,18 +2453,6 @@ msgstr "" msgid "Socks5 User" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:134 -msgid "Specifically for editing the Fake-IP filter list." -msgstr "" - -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:149 -msgid "Specifically for editing the Fallback IP-CIDR list." -msgstr "" - -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:164 -msgid "Specifically for editing the Fallback domain filter list." -msgstr "" - #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/server_list.htm:129 msgid "Start Detection" msgstr "" @@ -2554,19 +2479,19 @@ msgstr "" msgid "Stop Detection" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:517 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:467 msgid "Subscribe Advanced Settings" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:642 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:569 msgid "Subscribe Convert Online" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:543 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:493 msgid "Subscribe Default Auto-Switch" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:523 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:473 msgid "Subscribe Filter Words" msgstr "" @@ -2574,16 +2499,16 @@ msgstr "" msgid "Subscribe Log" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:530 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:480 msgid "Subscribe Save Words" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:656 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:583 msgid "Subscribe Template URL" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:667 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:691 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:594 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:618 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/subscribe_actions_footer.htm:189 msgid "Subscribe URL" msgstr "" @@ -2592,11 +2517,11 @@ msgstr "" msgid "Subscribe failed, refreshing page..." msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:545 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:495 msgid "Subscribe new add server default Auto-Switch on" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:538 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:488 msgid "Subscribe nodes allows insecure connection as TLS client (insecure)" msgstr "" @@ -2657,7 +2582,7 @@ msgid "" "as bypassing SNI blacklists." msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:262 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:194 msgid "TCP upstream" msgstr "" @@ -2733,10 +2658,11 @@ msgstr "" msgid "The keep-alive period.(Unit:second)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:18 +#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:6 msgid "" -"The online upgrade downloads and installs the matching NaiveProxy package " -"for this system from fw876/helloworld releases." +"The online upgrade downloads the latest luci-app-ssr-plus package from fw876/" +"helloworld releases and installs the matching IPK or APK package for this " +"system." msgstr "" #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:10 @@ -2745,13 +2671,6 @@ msgid "" "this system from fw876/helloworld releases." msgstr "" -#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:6 -msgid "" -"The online upgrade downloads the latest luci-app-ssr-plus package from fw876/" -"helloworld releases and installs the matching IPK or APK package for this " -"system." -msgstr "" - #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:14 msgid "" "The online upgrade only tracks the latest stable Mihomo release and " @@ -2759,6 +2678,12 @@ msgid "" "GitHub release page." msgstr "" +#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:18 +msgid "" +"The online upgrade downloads and installs the matching NaiveProxy package " +"for this system from fw876/helloworld releases." +msgstr "" + #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:816 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:871 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_panel.htm:89 @@ -2776,11 +2701,11 @@ msgstr "" msgid "This section contains no values yet" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:550 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:500 msgid "Through proxy update" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:552 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:502 msgid "Through proxy update list, Not Recommended" msgstr "" @@ -2792,10 +2717,6 @@ msgstr "" msgid "Timeout for establishing a connection to server(second)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:628 -msgid "Tips: Fake-IP Filter and Fallback Lists:" -msgstr "" - #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua:305 msgid "To send noise packets, select \"Noise\" in Xray Settings." msgstr "" @@ -2828,7 +2749,7 @@ msgid "Try Count" msgstr "" #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua:327 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:790 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:717 msgid "Type" msgstr "" @@ -2850,11 +2771,11 @@ msgstr "" msgid "UDP relay mode" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:263 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:195 msgid "UDP upstream" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:261 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:193 msgid "UDP/TCP upstream" msgstr "" @@ -2862,7 +2783,7 @@ msgstr "" msgid "UL Restore" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:589 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:539 msgid "URL Test Address" msgstr "" @@ -2911,19 +2832,19 @@ msgstr "" msgid "Update" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:562 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:512 msgid "Update All Subscribe Servers" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:511 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:461 msgid "Update Interval(min)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:475 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:425 msgid "Update Mode" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:482 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:432 msgid "Update cycle (Day/Week)" msgstr "" @@ -2980,43 +2901,43 @@ msgstr "" msgid "Upload" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:610 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:560 msgid "Upload Custom YAML File" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:612 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:562 msgid "" "Upload a custom Clash/Mihomo YAML file. The file will be preprocessed and " "saved as a local Clash node." msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:308 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:319 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:329 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:277 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:285 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:290 msgid "Uploaded YAML validation or preprocessing failed." msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:152 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:128 msgid "Use ChinaDNS-NG query and cache" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:269 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:201 msgid "Use DNS from WAN" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:270 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:202 msgid "Use DNS from WAN and 114DNS" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:146 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:122 msgid "Use DNS2TCP query" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:155 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:131 msgid "Use Local DNS Service listen port 5335" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:149 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:125 msgid "Use MosDNS query" msgstr "" @@ -3034,7 +2955,7 @@ msgid "User cancelled." msgstr "" #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1373 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:600 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:550 msgid "User-Agent" msgstr "" @@ -3104,15 +3025,15 @@ msgstr "" msgid "Vmess/VLESS ID (UUID)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:29 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:26 msgid "WAN Force Proxy IP" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:24 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:21 msgid "WAN IP AC" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:26 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:23 msgid "WAN White List IP" msgstr "" @@ -3421,13 +3342,12 @@ msgstr "" msgid "v2ray-plugin" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:214 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:302 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:311 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:234 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:243 msgid "valid address:port" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:297 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:229 msgid "valid address:port (comma separated)" msgstr "" diff --git a/luci-app-ssr-plus/po/zh_Hans/ssr-plus.po b/luci-app-ssr-plus/po/zh_Hans/ssr-plus.po index 7558982f9..8cd8c8ea6 100644 --- a/luci-app-ssr-plus/po/zh_Hans/ssr-plus.po +++ b/luci-app-ssr-plus/po/zh_Hans/ssr-plus.po @@ -14,11 +14,11 @@ msgstr "" msgid "0" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:118 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:94 msgid "1 Thread" msgstr "单线程" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:125 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:101 msgid "128 Threads" msgstr "128 线程" @@ -26,15 +26,15 @@ msgstr "128 线程" msgid "16" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:122 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:98 msgid "16 Threads" msgstr "16 线程" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:119 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:95 msgid "2 Threads" msgstr "2 线程" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:123 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:99 msgid "32 Threads" msgstr "32 线程" @@ -42,19 +42,19 @@ msgstr "32 线程" msgid "360" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:275 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:207 msgid "360 Security DNS (China Telecom) (101.226.4.6)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:276 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:208 msgid "360 Security DNS (China Unicom) (123.125.81.6)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:120 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:96 msgid "4 Threads" msgstr "4 线程" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:124 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:100 msgid "64 Threads" msgstr "64 线程" @@ -62,7 +62,7 @@ msgstr "64 线程" msgid "8" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:121 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:97 msgid "8 Threads" msgstr "8 线程" @@ -76,7 +76,7 @@ msgstr "" msgid "" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:75 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:51 msgid "" "

    Support SS/SSR/V2RAY/XRAY/TROJAN/TUIC/HYSTERIA2/NAIVEPROXY/SOCKS5/CLASH " "etc.

    " @@ -90,9 +90,8 @@ msgstr "" #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1654 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1667 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1680 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:254 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:281 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:624 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:186 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:213 msgid "
    • " msgstr "" @@ -146,12 +145,12 @@ msgstr "高级设置" msgid "Advertising Data" msgstr "【广告屏蔽】数据库" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:273 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:205 msgid "AliYun Public DNS (223.5.5.5)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:684 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:796 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:611 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:723 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/subscribe_actions_footer.htm:187 msgid "Alias" msgstr "别名" @@ -164,7 +163,7 @@ msgstr "别名(可选)" msgid "All" msgstr "全部" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:136 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:112 msgid "All Ports" msgstr "所有端口(默认)" @@ -180,15 +179,15 @@ msgstr "所有客户端代理规则已清空。" msgid "All settings saved successfully." msgstr "所有设置保存成功。" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:38 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:35 msgid "Allow all except listed" msgstr "除列表外主机皆允许" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:37 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:34 msgid "Allow listed only" msgstr "仅允许列表内主机" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:536 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:486 msgid "Allow subscribe Insecure nodes By default" msgstr "订阅节点允许不验证 TLS 证书" @@ -210,10 +209,9 @@ msgstr "" msgid "An FinalMaskObject in JSON format, used for sharing." msgstr "JSON 格式的 FinalMaskObject,用来实现分享。" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:158 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:175 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:220 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:239 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:134 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:152 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:171 msgid "Anti-pollution DNS Server" msgstr "访问国外域名 DNS 服务器" @@ -233,12 +231,12 @@ msgstr "Apple 域名更新 URL" msgid "Apple domains optimization" msgstr "Apple 域名解析优化" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:856 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:862 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:783 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:789 msgid "Apply" msgstr "应用" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:476 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:426 msgid "Appointment Mode" msgstr "定时模式" @@ -250,19 +248,19 @@ msgstr "确定要删除此订阅链接:%s 吗?" msgid "Are you sure you want to restore the client to default settings?" msgstr "是否真的要恢复客户端默认配置?" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:873 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:800 msgid "Auto Switch" msgstr "自动切换" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:117 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:93 msgid "Auto Threads" msgstr "自动(CPU 线程数)" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:471 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:421 msgid "Auto Update" msgstr "自动更新" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:473 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:423 msgid "Auto Update Server subscription, GFW list and CHN route" msgstr "自动更新服务器订阅、GFW 列表和中国大陆 IP 段" @@ -289,7 +287,7 @@ msgstr "备份或还原客户端及服务端配置。" msgid "Baidu Connectivity" msgstr "【百度】连通性检查" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:274 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:206 msgid "Baidu Public DNS (180.76.76.76)" msgstr "" @@ -306,7 +304,7 @@ msgstr "仅绑定本地端口" msgid "BitTorrent (uTP)" msgstr "BT 下载(uTP)" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:99 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:96 msgid "Black Domain List" msgstr "强制走代理的域名" @@ -314,7 +312,7 @@ msgstr "强制走代理的域名" msgid "Bloom Filter" msgstr "布隆过滤器" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:83 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:80 msgid "Bypass Domain List" msgstr "不走代理的域名" @@ -322,7 +320,7 @@ msgstr "不走代理的域名" msgid "CLOSE WIN" msgstr "关闭窗口" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:277 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:209 msgid "CNNIC SDNS (1.2.4.8)" msgstr "" @@ -407,7 +405,7 @@ msgstr "检查中..." msgid "China IP Data" msgstr "【中国大陆 IP 段】数据库" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:260 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:192 msgid "ChinaDNS-NG query protocol" msgstr "ChinaDNS-NG 查询协议" @@ -427,7 +425,7 @@ msgstr "" msgid "Clash" msgstr "Clash" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:112 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:88 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:893 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:1096 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_panel.htm:3 @@ -462,10 +460,6 @@ msgstr "清空所有自定义客户端代理规则,并恢复为空白状态吗 msgid "Clear logs" msgstr "清空日志" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:633 -msgid "Click here to manage" -msgstr "点击此处前往管理" - #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua:309 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1162 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1518 @@ -494,13 +488,12 @@ msgstr "客户端支持手动输入 IP 或 IP/CIDR。" msgid "Close" msgstr "关闭" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:226 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:158 msgid "Cloudflare DNS" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:169 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:186 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:250 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:145 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:182 msgid "Cloudflare DNS (1.1.1.1)" msgstr "" @@ -544,11 +537,11 @@ msgid "" "Controls the policy used when performing DNS queries for ECH configuration." msgstr "控制使用 DNS 查询 ECH 配置时的策略。" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:648 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:575 msgid "Convert Address" msgstr "转换订阅模板API服务器" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:643 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:570 msgid "" "Convert subscriptions to Clash/Mihomo YAML with a subscription template URL." msgstr "使用订阅转换模板 URL 将订阅转换为 Clash/Mihomo YAML。" @@ -595,21 +588,16 @@ msgstr "自定义" msgid "Custom Client Proxies Rules" msgstr "自定义客户端代理规则" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:191 -msgid "" -"Custom DNS Server (supports format IP:PORT, domain name, or scheme://...)." -msgstr "支持格式: IP:端口、域名或 scheme://... 等。" - -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:171 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:255 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:148 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:187 msgid "Custom DNS Server format as IP:PORT (default: 8.8.4.4:53)" msgstr "格式为 IP:Port(默认:8.8.4.4:53)" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:282 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:214 msgid "Custom DNS Server format as IP:PORT (default: disabled)" msgstr "格式为 IP:PORT(默认:禁用)" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:228 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:160 msgid "" "Custom DNS Server format as tcp://IP:PORT or tls://DOMAIN:PORT " "(tcp://8.8.8.8 or tls://dns.google:853)" @@ -620,11 +608,11 @@ msgstr "" msgid "Custom Plugin Path" msgstr "自定义插件路径" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:138 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:114 msgid "Custom Ports" msgstr "自定义端口" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:315 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:283 msgid "Custom YAML imported successfully: %s" msgstr "自定义 YAML 导入成功:%s" @@ -654,7 +642,7 @@ msgstr "" msgid "DNS Anti-pollution" msgstr "DNS 防污染服务" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:272 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:204 msgid "DNSPod Public DNS (119.29.29.29)" msgstr "" @@ -738,7 +726,7 @@ msgstr "延迟(ms)" msgid "Delete" msgstr "删除" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:569 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:519 msgid "Delete All Subscribe Servers" msgstr "删除所有订阅节点" @@ -746,22 +734,22 @@ msgstr "删除所有订阅节点" msgid "Delete failed:" msgstr "删除失败:" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:115 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:112 msgid "Deny Domain List" msgstr "禁止连接的域名" #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:726 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1037 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:105 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:36 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:81 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:33 msgid "Disable" msgstr "停用" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:268 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:200 msgid "Disable ChinaDNS-NG" msgstr "直通模式(禁用 ChinaDNS-NG)" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:231 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:163 msgid "Disable IPv6 for Overseas FQDN" msgstr "禁止海外域名返回 IPv6 记录" @@ -777,7 +765,7 @@ msgstr "关闭 SNI 服务器名称指示" msgid "Disable TCP No_delay" msgstr "禁用 TCP 无延迟" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:617 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:567 msgid "Disabled means use Xray or ShadowSocks-Rust core." msgstr "禁用则使用 Xray 或 ShadowSocks-Rust 核心。" @@ -789,7 +777,7 @@ msgstr "执行重置" msgid "Do you want to restore the client to default settings?" msgstr "是否要恢复客户端默认配置?" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:264 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:196 msgid "DoT upstream (Need use wolfssl version)" msgstr "DoT 上游(需使用 wolfssl 版本)" @@ -801,7 +789,7 @@ msgstr "域名解析策略" msgid "DomainSocket Path" msgstr "域 Socket 路径" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:267 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:199 msgid "Domestic DNS Server" msgstr "国内 DNS 服务器" @@ -848,7 +836,7 @@ msgstr "编辑服务器配置" #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua:323 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/server-config.lua:54 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/server.lua:101 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:676 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:603 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:1114 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/subscribe_actions_footer.htm:184 msgid "Enable" @@ -871,18 +859,6 @@ msgstr "启用自动切换" msgid "Enable ECH(optional)" msgstr "启用 ECH (可选)" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:619 -msgid "Enable Fake-IP Mode" -msgstr "开启 Fake-IP 模式" - -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:626 -msgid "Enable Fake-IP mode for better performance." -msgstr "开启 Fake-IP 模式以获取更佳的解析性能。" - -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:625 -msgid "Enable Fake-IP mode; disable to use redir-host mode." -msgstr "启用 Fake-IP 模式;禁用则使用 redir-host 模式。" - #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:720 msgid "Enable Lazy Mode" msgstr "启用懒狗模式" @@ -891,7 +867,7 @@ msgstr "启用懒狗模式" msgid "Enable ML-DSA-65(optional)" msgstr "启用 ML-DSA-65 (可选)" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:614 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:564 msgid "Enable Mihomo core" msgstr "启用 Mihomo 核心" @@ -970,7 +946,7 @@ msgstr "启用 TCP 快速打开需要服务端支持。" msgid "Encrypt Method" msgstr "加密方式" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:139 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:115 msgid "Enter Custom Ports" msgstr "输入自定义端口" @@ -979,35 +955,35 @@ msgstr "输入自定义端口" msgid "Error" msgstr "错误" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:483 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:433 msgid "Every Day" msgstr "每天" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:488 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:438 msgid "Every Friday" msgstr "每周五" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:484 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:434 msgid "Every Monday" msgstr "每周一" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:489 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:439 msgid "Every Saturday" msgstr "每周六" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:490 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:440 msgid "Every Sunday" msgstr "每周日" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:487 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:437 msgid "Every Thursday" msgstr "每周四" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:485 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:435 msgid "Every Tuesday" msgstr "每周二" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:486 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:436 msgid "Every Wednesday" msgstr "每周三" @@ -1015,11 +991,9 @@ msgstr "每周三" msgid "Example:" msgstr "示例:" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:203 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:214 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:297 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:302 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:311 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:229 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:234 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:243 msgid "Expecting: %s" msgstr "应为:%s" @@ -1043,7 +1017,7 @@ msgstr "清空所有客户端代理规则失败。" msgid "Failed to create temp directory" msgstr "创建临时目录失败" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:275 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:244 msgid "Failed to create temporary YAML upload file." msgstr "创建临时 YAML 上传文件失败。" @@ -1052,10 +1026,6 @@ msgstr "创建临时 YAML 上传文件失败。" msgid "Failed to fetch release metadata" msgstr "获取发布信息失败" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:325 -msgid "Failed to move uploaded YAML to target directory." -msgstr "无法将上传的 YAML 文件移动到目标目录。" - #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:112 msgid "Failed to query component information." msgstr "获取组件信息失败。" @@ -1068,10 +1038,6 @@ msgstr "获取 Geo 数据库信息失败。" msgid "Failed to save custom client proxy rules." msgstr "保存自定义客户端代理规则失败。" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:132 -msgid "Fake-IP Filter and Fallback Lists" -msgstr "Fake-IP 过滤器与 Fallback 列表" - #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/server_header.htm:21 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/server_header.htm:61 msgid "Fetch Failed" @@ -1086,7 +1052,7 @@ msgstr "手动获取" msgid "File Not Exist" msgstr "文件不存在" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:526 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:476 msgid "Filter Words splited by /" msgstr "命中关键字的节点将被丢弃。多个关键字用 / 分隔" @@ -1147,11 +1113,11 @@ msgstr "分片包长 (byte)" msgid "GFW List Data" msgstr "【GFW 列表】数据库" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:130 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:106 msgid "GFW List Mode" msgstr "GFW 列表模式" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:67 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:64 msgid "Game Mode Host List" msgstr "增强游戏模式客户端 LAN IP" @@ -1197,7 +1163,7 @@ msgstr "TCP 透明代理" msgid "Global HTTP/HTTPS Proxy Server" msgstr "HTTP/HTTPS 代理服务端(全局)" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:132 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:108 msgid "Global Mode" msgstr "全局模式" @@ -1218,19 +1184,17 @@ msgstr "前往相关配置页面" msgid "Google Connectivity" msgstr "【谷歌】连通性检查" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:221 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:153 msgid "Google Public DNS" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:159 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:176 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:240 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:135 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:172 msgid "Google Public DNS (8.8.4.4)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:160 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:177 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:241 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:136 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:173 msgid "Google Public DNS (8.8.8.8)" msgstr "" @@ -1332,7 +1296,7 @@ msgstr "" msgid "Hysterir QUIC parameters" msgstr "QUIC 参数" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:131 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:107 msgid "IP Route Mode" msgstr "绕过中国大陆 IP 模式" @@ -1340,17 +1304,6 @@ msgstr "绕过中国大陆 IP 模式" msgid "IP Stack Preference" msgstr "IP 栈优先级" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:203 -msgid "" -"IP:PORT, Domain, or Protocol URL (e.g. 8.8.8.8:53, dns.google, https://...)." -msgstr "" -"IP:端口,域名,或者协议 URL (例如:8.8.8.8:53,dns.google,https://...)。" - -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:627 -msgid "" -"If disabled, Redir-Host mode will be used which returns real IP addresses." -msgstr "若禁用,将使用返回真实 IP 地址的 Redir-Host 模式。" - #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua:148 msgid "If empty, Not change Apple domains parsing DNS (Default is empty)" msgstr "如果为空,则不更改 Apple 域名解析 DNS(默认为空)" @@ -1421,11 +1374,11 @@ msgstr "新安装的二进制无法运行" msgid "Installed package check failed" msgstr "安装后软件包检查失败" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:16 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:13 msgid "Interface" msgstr "接口" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:15 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:12 msgid "Interface control" msgstr "接口控制" @@ -1497,23 +1450,23 @@ msgstr "KcpTun 端口" msgid "KcpTun Version" msgstr "KcpTun 版本号" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:35 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:32 msgid "LAN Access Control" msgstr "内网客户端分流代理控制" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:51 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:48 msgid "LAN Bypassed Host List" msgstr "不走代理的局域网 LAN IP" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:59 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:56 msgid "LAN Force Proxy Host List" msgstr "全局代理的 LAN IP" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:41 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:38 msgid "LAN Host List" msgstr "内网主机列表" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:33 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:30 msgid "LAN IP AC" msgstr "LAN IP 访问控制" @@ -1522,51 +1475,45 @@ msgstr "LAN IP 访问控制" msgid "Latest Version" msgstr "最新版本" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:163 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:180 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:244 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:139 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:176 msgid "Level 3 Public DNS (209.244.0.3)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:164 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:181 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:245 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:140 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:177 msgid "Level 3 Public DNS (209.244.0.4)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:165 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:182 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:246 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:141 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:178 msgid "Level 3 Public DNS (4.2.2.1)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:166 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:183 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:247 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:142 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:179 msgid "Level 3 Public DNS (4.2.2.2)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:167 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:184 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:248 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:143 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:180 msgid "Level 3 Public DNS (4.2.2.3)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:168 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:185 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:249 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:144 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:181 msgid "Level 3 Public DNS (4.2.2.4)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:223 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:155 msgid "Level 3 Public DNS-1 (209.244.0.3-4)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:224 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:156 msgid "Level 3 Public DNS-2 (4.2.2.1-2)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:225 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:157 msgid "Level 3 Public DNS-3 (4.2.2.3-4)" msgstr "" @@ -1579,7 +1526,7 @@ msgid "" "Linux kernel TUN virtual NIC requires system support and root privileges." msgstr "Linux 内核 TUN 虚拟网卡需要系统支持和 root 权限。" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:21 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:18 msgid "Listen only on the given interface or, if unspecified, on all" msgstr "仅监听指定的接口,未指定则监听全部。" @@ -1606,7 +1553,7 @@ msgstr "本地地址" msgid "Log" msgstr "日志" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:477 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:427 msgid "Loop Mode" msgstr "循环模式" @@ -1630,11 +1577,11 @@ msgstr "" msgid "MTU" msgstr "最大传输单元" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:104 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:80 msgid "Main Server" msgstr "主服务器" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:674 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:601 msgid "" "Manage multiple subscribe URLs, including Clash subscriptions. Only enabled " "entries are included when updating all subscriptions." @@ -1719,9 +1666,8 @@ msgstr "下移" msgid "Move up" msgstr "上移" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:192 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:256 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:283 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:188 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:215 msgid "Muitiple DNS server can saperate with ','" msgstr "多个上游 DNS 服务器请用 ',' 分隔(注意用英文逗号)" @@ -1729,7 +1675,7 @@ msgstr "多个上游 DNS 服务器请用 ',' 分隔(注意用英文逗号)" msgid "Multi" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:116 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:92 msgid "Multi Threads Option" msgstr "多线程并发转发" @@ -1761,7 +1707,7 @@ msgstr "" msgid "Name" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:271 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:203 msgid "Nanjing Xinfeng 114DNS (114.114.114.114)" msgstr "" @@ -1801,7 +1747,7 @@ msgstr "没有可用的核心(Mihomo 或 Xray)可以导入此节点。" msgid "No available core (Shadowsocks, Mihomo or Xray) to import this node." msgstr "没有可用的核心(Shadowsocks, Mihomo 或 Xray)可以导入此节点。" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:294 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:263 msgid "No custom YAML file was selected." msgstr "没有选择自定义 YAML 文件。" @@ -1826,7 +1772,7 @@ msgstr "没有找到任何订阅项" msgid "No switchable proxy groups found." msgstr "未找到可切换的代理组。" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:698 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:625 msgid "" "Node order can be dragged with the mouse and takes effect immediately. The " "automatic switch order of server nodes is consistent with the node order in " @@ -1843,8 +1789,8 @@ msgstr "噪声" #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1269 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1281 #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1291 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:793 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:798 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:720 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:725 msgid "None" msgstr "" @@ -1866,14 +1812,6 @@ msgstr "未安装可执行文件" msgid "Not installed" msgstr "未安装" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:193 -msgid "Note: IP addresses MUST specify a port." -msgstr "注意:IP 地址必须指定端口。" - -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:204 -msgid "Note: Pure IP without port is not allowed." -msgstr "注意:不允许使用没有端口的纯 IP。" - #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/log.lua:27 msgid "" "Note: Restoring configurations across different versions may cause " @@ -1924,7 +1862,7 @@ msgstr "混淆类型" msgid "Once set, connects only when the server’s chain fingerprint matches." msgstr "设置后,仅在服务器证书链指纹匹配时连接。" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:137 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:113 msgid "Only Common Ports" msgstr "仅常用端口(不走 P2P 流量到代理)" @@ -1949,19 +1887,17 @@ msgstr "仅当 Socks5 认证方式为 Password 时有效,非必填。" msgid "OpenClash GeoSite Database" msgstr "OpenClash GeoSite 数据库" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:222 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:154 msgid "OpenDNS" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:162 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:179 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:243 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:138 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:175 msgid "OpenDNS (208.67.220.220)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:161 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:178 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:242 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:137 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:174 msgid "OpenDNS (208.67.222.222)" msgstr "" @@ -2012,7 +1948,7 @@ msgstr "执行重置" msgid "Permit Without Stream" msgstr "允许无数据流" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:841 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:768 msgid "Ping Latency" msgstr "Ping 延迟" @@ -2052,7 +1988,7 @@ msgstr "预连接" msgid "Pre-shared key" msgstr "预共享密钥" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:154 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:130 msgid "Prefer module built-in DNS" msgstr "优先使用模块内置 DNS" @@ -2083,7 +2019,7 @@ msgstr "传输协议" msgid "Protocol param (optional)" msgstr "传输协议参数(可选)" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:135 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:111 msgid "Proxy Ports" msgstr "需要代理的端口" @@ -2171,7 +2107,7 @@ msgstr "" msgid "Realm URL" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:860 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:787 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:348 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_panel_button.htm:17 msgid "Reapply" @@ -2205,11 +2141,11 @@ msgstr "更新成功!" msgid "Refresh..." msgstr "正在更新,请稍候..." -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:495 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:445 msgid "Regular update (Hour)" msgstr "定时更新(小时)" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:503 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:453 msgid "Regular update (Min)" msgstr "定时更新(分钟)" @@ -2281,7 +2217,7 @@ msgstr "恢复出厂设置" msgid "Resetting Defaults..." msgstr "正在重置默认值..." -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:144 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:120 msgid "Resolve Dns Mode" msgstr "DNS 解析方式" @@ -2312,7 +2248,7 @@ msgstr "恢复默认配置" msgid "Running" msgstr "运行中" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:129 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:105 msgid "Running Mode" msgstr "运行模式" @@ -2345,16 +2281,16 @@ msgstr "服务端" msgid "Save" msgstr "保存" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:557 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:507 msgid "Save Subscribe Settings" msgstr "保存订阅设置" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:532 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:482 msgid "Save Words splited by /" msgstr "" "命中关键字的节点将被保留。多个关键字用 / 分隔。此项为空则不启用保留匹配" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:559 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:509 msgid "Save current subscribe settings" msgstr "保存当前订阅设置" @@ -2380,7 +2316,7 @@ msgstr "自签证书" msgid "Server Address" msgstr "服务器地址" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:571 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:521 msgid "Server Count" msgstr "服务器节点数量" @@ -2415,7 +2351,7 @@ msgstr "服务器节点故障自动切换/广告屏蔽/中国大陆 IP 段数据 msgid "Servers Nodes" msgstr "服务器节点" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:454 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:407 msgid "Servers subscription and manage" msgstr "服务器节点订阅与管理" @@ -2453,7 +2389,7 @@ msgstr "ShadowSocks-rust 版本" msgid "ShadowSocksR Plus+" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:75 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:51 msgid "ShadowSocksR Plus+ Settings" msgstr "ShadowSocksR Plus+ 设置" @@ -2500,7 +2436,7 @@ msgstr "" msgid "Snell Version" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:801 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:728 msgid "Socket Connected" msgstr "连接测试" @@ -2533,18 +2469,6 @@ msgstr "Socks5 密码" msgid "Socks5 User" msgstr "Socks5 用户名" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:134 -msgid "Specifically for editing the Fake-IP filter list." -msgstr "专用于编辑 Fake-IP 过滤规则文件。" - -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:149 -msgid "Specifically for editing the Fallback IP-CIDR list." -msgstr "专用于编辑 Fallback IP-CIDR 规则文件。" - -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:164 -msgid "Specifically for editing the Fallback domain filter list." -msgstr "专用于编辑 Fallback 域名过滤规则文件。" - #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/server_list.htm:129 msgid "Start Detection" msgstr "开始探测" @@ -2571,19 +2495,19 @@ msgstr "状态" msgid "Stop Detection" msgstr "停止探测" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:517 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:467 msgid "Subscribe Advanced Settings" msgstr "订阅高级设置" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:642 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:569 msgid "Subscribe Convert Online" msgstr "在线订阅转换" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:543 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:493 msgid "Subscribe Default Auto-Switch" msgstr "订阅新节点自动切换设置" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:523 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:473 msgid "Subscribe Filter Words" msgstr "订阅节点关键字过滤" @@ -2591,16 +2515,16 @@ msgstr "订阅节点关键字过滤" msgid "Subscribe Log" msgstr "订阅日志" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:530 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:480 msgid "Subscribe Save Words" msgstr "订阅节点关键字保留检查" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:656 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:583 msgid "Subscribe Template URL" msgstr "订阅转换模板 URL" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:667 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:691 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:594 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:618 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/subscribe_actions_footer.htm:189 msgid "Subscribe URL" msgstr "订阅 URL (支持SS/SSR/V2/TROJAN/HY2/TUIC/CLASH等)" @@ -2609,11 +2533,11 @@ msgstr "订阅 URL (支持SS/SSR/V2/TROJAN/HY2/TUIC/CLASH等)" msgid "Subscribe failed, refreshing page..." msgstr "订阅失败,正在刷新页面..." -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:545 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:495 msgid "Subscribe new add server default Auto-Switch on" msgstr "订阅加入的新节点默认开启自动切换" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:538 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:488 msgid "Subscribe nodes allows insecure connection as TLS client (insecure)" msgstr "订阅节点强制开启 不验证TLS客户端证书 (insecure)" @@ -2676,7 +2600,7 @@ msgid "" "as bypassing SNI blacklists." msgstr "TCP 分片,在某些情况下可以欺骗审查系统,比如绕过 SNI 黑名单。" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:262 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:194 msgid "TCP upstream" msgstr "TCP 上游" @@ -2754,22 +2678,6 @@ msgstr "输入的内容不正确!" msgid "The keep-alive period.(Unit:second)" msgstr "心跳包发送间隔(单位:秒)" -#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:18 -msgid "" -"The online upgrade downloads and installs the matching NaiveProxy package " -"for this system from fw876/helloworld releases." -msgstr "" -"在线升级会从 fw876/helloworld 发布页下载并安装适配当前系统的 NaiveProxy 软件" -"包。" - -#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:10 -msgid "" -"The online upgrade downloads and installs the matching Xray-core package for " -"this system from fw876/helloworld releases." -msgstr "" -"在线升级会从 fw876/helloworld 发布页下载并安装适配当前系统的 Xray-core 软件" -"包。" - #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:6 msgid "" "The online upgrade downloads the latest luci-app-ssr-plus package from fw876/" @@ -2779,6 +2687,13 @@ msgstr "" "在线更新会从 fw876/helloworld 发布页下载最新 luci-app-ssr-plus,并根据当前系" "统安装匹配的 IPK 或 APK 包。" +#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:10 +msgid "" +"The online upgrade downloads and installs the matching Xray-core package for " +"this system from fw876/helloworld releases." +msgstr "" +"在线升级会从 fw876/helloworld 发布页下载并安装适配当前系统的 Xray-core 软件包。" + #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:14 msgid "" "The online upgrade only tracks the latest stable Mihomo release and " @@ -2788,6 +2703,13 @@ msgstr "" "在线升级只跟踪 Mihomo 最新稳定版,并根据当前架构从官方 GitHub 发布页下载匹配" "的 Linux 压缩包。" +#: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/component.htm:18 +msgid "" +"The online upgrade downloads and installs the matching NaiveProxy package " +"for this system from fw876/helloworld releases." +msgstr "" +"在线升级会从 fw876/helloworld 发布页下载并安装适配当前系统的 NaiveProxy 软件包。" + #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:816 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_main_panel.htm:871 #: applications/luci-app-ssr-plus/luasrc/view/shadowsocksr/clash_panel.htm:89 @@ -2805,11 +2727,11 @@ msgstr "该面板仅在当前 Clash 总节点已启用时可用。" msgid "This section contains no values yet" msgstr "该部分目前无任何内容。" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:550 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:500 msgid "Through proxy update" msgstr "通过代理更新" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:552 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:502 msgid "Through proxy update list, Not Recommended" msgstr "通过路由器自身代理更新订阅" @@ -2821,10 +2743,6 @@ msgstr "超时" msgid "Timeout for establishing a connection to server(second)" msgstr "连接超时时间(单位:秒)" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:628 -msgid "Tips: Fake-IP Filter and Fallback Lists:" -msgstr "提示:Fake-IP 过滤与 Fallback 规则列表:" - #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua:305 msgid "To send noise packets, select \"Noise\" in Xray Settings." msgstr "在 Xray 设置中勾选 “噪声” 以发送噪声包。" @@ -2857,7 +2775,7 @@ msgid "Try Count" msgstr "重试次数" #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua:327 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:790 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:717 msgid "Type" msgstr "类型" @@ -2879,11 +2797,11 @@ msgstr "" msgid "UDP relay mode" msgstr "UDP 中继模式" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:263 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:195 msgid "UDP upstream" msgstr "UDP 上游" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:261 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:193 msgid "UDP/TCP upstream" msgstr "UDP/TCP 上游" @@ -2891,7 +2809,7 @@ msgstr "UDP/TCP 上游" msgid "UL Restore" msgstr "上传恢复" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:589 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:539 msgid "URL Test Address" msgstr "URL 测试地址" @@ -2940,19 +2858,19 @@ msgstr "不支持的包管理器" msgid "Update" msgstr "更新" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:562 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:512 msgid "Update All Subscribe Servers" msgstr "更新所有订阅节点" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:511 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:461 msgid "Update Interval(min)" msgstr "更新间隔(分钟)" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:475 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:425 msgid "Update Mode" msgstr "更新模式" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:482 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:432 msgid "Update cycle (Day/Week)" msgstr "更新周期(天/周)" @@ -3009,11 +2927,11 @@ msgstr "上行链路容量(默认:Mbps)" msgid "Upload" msgstr "上传" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:610 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:560 msgid "Upload Custom YAML File" msgstr "上传自定义 YAML 文件" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:612 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:562 msgid "" "Upload a custom Clash/Mihomo YAML file. The file will be preprocessed and " "saved as a local Clash node." @@ -3021,33 +2939,33 @@ msgstr "" "上传自定义 Clash/Mihomo YAML 文件。文件会先做预处理,再保存为本地 Clash 节" "点。" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:308 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:319 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:329 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:277 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:285 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:290 msgid "Uploaded YAML validation or preprocessing failed." msgstr "上传的 YAML 校验或预处理失败。" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:152 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:128 msgid "Use ChinaDNS-NG query and cache" msgstr "使用 ChinaDNS-NG 查询并缓存" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:269 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:201 msgid "Use DNS from WAN" msgstr "使用 WAN 下发的 DNS" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:270 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:202 msgid "Use DNS from WAN and 114DNS" msgstr "使用 WAN 下发的 DNS 和 114DNS" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:146 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:122 msgid "Use DNS2TCP query" msgstr "使用 DNS2TCP 查询" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:155 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:131 msgid "Use Local DNS Service listen port 5335" msgstr "使用本机端口为 5335 的 DNS 服务" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:149 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:125 msgid "Use MosDNS query" msgstr "使用 MosDNS 查询" @@ -3065,7 +2983,7 @@ msgid "User cancelled." msgstr "用户已取消。" #: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua:1373 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:600 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua:550 msgid "User-Agent" msgstr "用户代理(User-Agent)" @@ -3135,15 +3053,15 @@ msgstr "" msgid "Vmess/VLESS ID (UUID)" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:29 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:26 msgid "WAN Force Proxy IP" msgstr "强制走代理的 WAN IP" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:24 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:21 msgid "WAN IP AC" msgstr "WAN IP 访问控制" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:26 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua:23 msgid "WAN White List IP" msgstr "不走代理的 WAN IP" @@ -3453,13 +3371,12 @@ msgstr "" msgid "v2ray-plugin" msgstr "" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:214 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:302 -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:311 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:234 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:243 msgid "valid address:port" msgstr "有效的地址:端口" -#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:297 +#: applications/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua:229 msgid "valid address:port (comma separated)" msgstr "有效地址:端口(用逗号分隔)" diff --git a/luci-app-ssr-plus/root/etc/init.d/shadowsocksr b/luci-app-ssr-plus/root/etc/init.d/shadowsocksr index 1b0503278..b030126e2 100755 --- a/luci-app-ssr-plus/root/etc/init.d/shadowsocksr +++ b/luci-app-ssr-plus/root/etc/init.d/shadowsocksr @@ -608,10 +608,6 @@ prepare_clash_runtime_config() { local dns_mode local client_policy_stats local client_rules - local enable_fake_ip - local socks5_auth - local socks5_user - local socks5_pass cache_file="$(get_clash_cache_file "$sid")" workdir="$(get_clash_workdir "$sid")" @@ -627,7 +623,6 @@ prepare_clash_runtime_config() { dns_mode="$(uci_get_by_type global pdnsd_enable 0)" client_policy_stats="" client_rules=0 - enable_fake_ip="$(uci_get_by_type server_subscribe enable_fake_ip "")" [ -s "$cache_file" ] || return 1 @@ -645,35 +640,15 @@ prepare_clash_runtime_config() { routing-mark: $CLASH_OUTBOUND_MARK profile: store-selected: true - store-fake-ip: true tun: enable: false + dns: + enable: $( [ "$dns_mode" = "7" ] && echo "true" || echo "false" ) + enhanced-mode: redir-host + listen: 127.0.0.1:$dns_port + ipv6: $( [ "$dns_ipv4_only" = "1" ] && echo "false" || echo "true" ) EOF - # 根据 enable_fake_ip 决定 enhanced-mode - if [ "$enable_fake_ip" = "1" ]; then - ENHANCED_MODE="fake-ip" - else - ENHANCED_MODE="redir-host" - fi - # 根据 dns_mode 添加不同的 dns 配置 - if [ "$dns_mode" = "7" ]; then - cat >> "$overlay_file" <<-EOF - dns: - enable: true - enhanced-mode: $ENHANCED_MODE - listen: 127.0.0.1:$dns_port - ipv6: $( [ "$dns_ipv4_only" = "1" ] && echo "false" || echo "true" ) - EOF - else - cat >> "$overlay_file" <<-EOF - dns: - enable: true - enhanced-mode: $ENHANCED_MODE - ipv6: $( [ "$dns_ipv4_only" = "1" ] && echo "false" || echo "true" ) - EOF - fi - if [ -n "$socks_port" ] && [ "$socks_port" != "0" ]; then echo "socks-port: $socks_port" >>"$overlay_file" @@ -1273,24 +1248,14 @@ start_dns() { ssrplus_dns="$(uci_get_by_type global pdnsd_enable 0)" global_server_type="$(uci_get_by_name "$GLOBAL_SERVER" type)" builtin_dns_enabled=0 + dnsserver="$(uci_get_by_type global tunnel_forward 8.8.4.4:53)" if [ "$ssrplus_dns" = "5" ]; then ssrplus_dns="$(force_dns2tcp_fallback "旧版 DNSPROXY 模式已移除")" || return 1 uci_set_by_type global pdnsd_enable "$ssrplus_dns" + elif [ "$ssrplus_dns" = "6" ]; then + dnsserver="$(uci_get_by_type global chinadns_ng_tunnel_forward 8.8.4.4:53)" fi - case "$ssrplus_dns" in - 1) - dnsserver="$(uci_get_by_type global dns2tcp_tunnel_forward 8.8.4.4:53)" - ;; - 6) - dnsserver="$(uci_get_by_type global chinadns_ng_tunnel_forward 8.8.4.4:53)" - ;; - 7) - dnsserver="$(uci_get_by_type global tunnel_forward 8.8.4.4:53)" - ;; - *) - ;; - esac run_mode="$(normalize_run_mode)" if [ "$ssrplus_dns" = "4" ] && ! is_finded "mosdns"; then @@ -1556,15 +1521,15 @@ generate_xray_config() { { "log": { "loglevel": "warning" - }, - "inbounds": [$inbound_json], - "outbounds": [ - { - "protocol": "freedom", - "settings": {} - } - ] - } + }, + "inbounds": [$inbound_json], + "outbounds": [ + { + "protocol": "freedom", + "settings": {} + } + ] + } EOF return 0 } @@ -2940,18 +2905,12 @@ start_rules() { local ac_ips local custom_ports local proxyport - local enable_fake_ip server=$(get_host_ip "$GLOBAL_SERVER") local_port=$(get_default_node_local_port) lan_ac_ips=$(uci_get_by_type access_control lan_ac_ips) lan_ac_mode=$(uci_get_by_type access_control lan_ac_mode) - enable_fake_ip=$(uci_get_by_type server_subscribe enable_fake_ip "") - if [ -z "$enable_fake_ip" ]; then - enable_fake_ip="0" - fi - if [ "$kcp_enable_flag" = "0" ] && [ "$redir_udp" = "1" ]; then udp_server=$(get_host_ip "$UDP_RELAY_SERVER") udp_local_port=$tmp_udp_port @@ -2996,27 +2955,26 @@ start_rules() { fi elif [ "$USE_TABLES" = "iptables" ]; then ARG_A="" - fi - /usr/share/shadowsocksr/gfw2ipset.sh - /usr/bin/ssr-rules \ - -s "$server" \ - -l "$local_port" \ - -S "$udp_server" \ - -L "$udp_local_port" \ - -a "$ac_ips" \ - -i "/etc/ssrplus/china_ssr.txt" \ - -b "$(uci_get_by_type access_control wan_bp_ips)" \ - -w "$(uci_get_by_type access_control wan_fw_ips)" \ - -B "$(uci_get_by_type access_control lan_bp_ips)" \ - -p "$(uci_get_by_type access_control lan_fp_ips)" \ - -G "$(uci_get_by_type access_control lan_gm_ips)" \ - -m "$(uci_get_by_type access_control Interface)" \ - -D "$proxyport" \ - -E "$enable_fake_ip" \ - "$(get_arg_out)" "$(gfwmode)" "$ARG_UDP" "$ARG_UDP_RULES" "$ARG_A" + fi + /usr/share/shadowsocksr/gfw2ipset.sh + /usr/bin/ssr-rules \ + -s "$server" \ + -l "$local_port" \ + -S "$udp_server" \ + -L "$udp_local_port" \ + -a "$ac_ips" \ + -i "/etc/ssrplus/china_ssr.txt" \ + -b "$(uci_get_by_type access_control wan_bp_ips)" \ + -w "$(uci_get_by_type access_control wan_fw_ips)" \ + -B "$(uci_get_by_type access_control lan_bp_ips)" \ + -p "$(uci_get_by_type access_control lan_fp_ips)" \ + -G "$(uci_get_by_type access_control lan_gm_ips)" \ + -m "$(uci_get_by_type access_control Interface)" \ + -D "$proxyport" \ + "$(get_arg_out)" "$(gfwmode)" "$ARG_UDP" "$ARG_UDP_RULES" "$ARG_A" - return $? -} + return $? + } start() { local run_mode diff --git a/luci-app-ssr-plus/root/etc/ssrplus/fake_ip_filter.list b/luci-app-ssr-plus/root/etc/ssrplus/fake_ip_filter.list deleted file mode 100644 index 27a84480d..000000000 --- a/luci-app-ssr-plus/root/etc/ssrplus/fake_ip_filter.list +++ /dev/null @@ -1,78 +0,0 @@ -*.lan -*.localdomain -*.example -*.invalid -*.localhost -*.test -*.local -*.home.arpa -time.*.com -time.*.gov -time.*.edu.cn -time.*.apple.com -time1.*.com -time2.*.com -time3.*.com -time4.*.com -time5.*.com -time6.*.com -time7.*.com -ntp.*.com -ntp1.*.com -ntp2.*.com -ntp3.*.com -ntp4.*.com -ntp5.*.com -ntp6.*.com -ntp7.*.com -*.time.edu.cn -*.ntp.org.cn -+.pool.ntp.org -time1.cloud.tencent.com -stun.*.* -stun.*.*.* -swscan.apple.com -mesu.apple.com -music.163.com -*.music.163.com -*.126.net -musicapi.taihe.com -music.taihe.com -songsearch.kugou.com -trackercdn.kugou.com -*.kuwo.cn -api-jooxtt.sanook.com -api.joox.com -y.qq.com -*.y.qq.com -streamoc.music.tc.qq.com -mobileoc.music.tc.qq.com -isure.stream.qqmusic.qq.com -dl.stream.qqmusic.qq.com -aqqmusic.tc.qq.com -amobile.music.tc.qq.com -localhost.ptlogin2.qq.com -*.msftconnecttest.com -*.msftncsi.com -*.xiami.com -*.music.migu.cn -music.migu.cn -+.wotgame.cn -+.wggames.cn -+.wowsgame.cn -+.wargaming.net -*.*.*.srv.nintendo.net -*.*.stun.playstation.net -xbox.*.*.microsoft.com -*.*.xboxlive.com -*.ipv6.microsoft.com -teredo.*.*.* -teredo.*.* -speedtest.cros.wr.pvp.net -+.jjvip8.com -www.douyu.com -activityapi.huya.com -activityapi.huya.com.w.cdngslb.com -www.bilibili.com -api.bilibili.com -a.w.bilicdn1.com \ No newline at end of file diff --git a/luci-app-ssr-plus/root/etc/ssrplus/fallback_domain.list b/luci-app-ssr-plus/root/etc/ssrplus/fallback_domain.list deleted file mode 100644 index 7f66e598b..000000000 --- a/luci-app-ssr-plus/root/etc/ssrplus/fallback_domain.list +++ /dev/null @@ -1,9 +0,0 @@ -+.google.com -+.facebook.com -+.youtube.com -+.twitter.com -+.telegram.org -+.githubusercontent.com -+.googlevideo.com -+.msftconnecttest.com -+.msftncsi.com \ No newline at end of file diff --git a/luci-app-ssr-plus/root/etc/ssrplus/fallback_ipcidr.list b/luci-app-ssr-plus/root/etc/ssrplus/fallback_ipcidr.list deleted file mode 100644 index 5f71fa720..000000000 --- a/luci-app-ssr-plus/root/etc/ssrplus/fallback_ipcidr.list +++ /dev/null @@ -1,15 +0,0 @@ -0.0.0.0/8 -10.0.0.0/8 -100.64.0.0/10 -127.0.0.0/8 -169.254.0.0/16 -172.16.0.0/12 -192.0.0.0/24 -192.0.2.0/24 -192.88.99.0/24 -192.168.0.0/16 -198.51.100.0/24 -203.0.113.0/24 -224.0.0.0/4 -240.0.0.0/4 -255.255.255.255/32 \ No newline at end of file diff --git a/luci-app-ssr-plus/root/etc/uci-defaults/luci-ssr-plus b/luci-app-ssr-plus/root/etc/uci-defaults/luci-ssr-plus index 8da47fbea..54dbf51fd 100644 --- a/luci-app-ssr-plus/root/etc/uci-defaults/luci-ssr-plus +++ b/luci-app-ssr-plus/root/etc/uci-defaults/luci-ssr-plus @@ -47,18 +47,6 @@ if [ -s "/etc/config/shadowsocksr" ]; then uci -q set shadowsocksr.@server_subscribe[0].auto_update_min_time='0' fi - if ! uci -q get shadowsocksr.@server_subscribe[0].enable_mihomo > /dev/null; then - uci -q set shadowsocksr.@server_subscribe[0].enable_mihomo='1' - fi - - if ! uci -q get shadowsocksr.@server_subscribe[0].enable_fake_ip > /dev/null; then - uci -q set shadowsocksr.@server_subscribe[0].enable_fake_ip='1' - fi - - if ! uci -q get shadowsocksr.@server_subscribe[0].sub_convert > /dev/null; then - uci -q set shadowsocksr.@server_subscribe[0].sub_convert='1' - fi - if ! uci -q get shadowsocksr.@server_subscribe[0].config_auto_update_mode > /dev/null; then uci -q set shadowsocksr.@server_subscribe[0].config_auto_update_mode='0' fi diff --git a/luci-app-ssr-plus/root/usr/bin/ssr-rules b/luci-app-ssr-plus/root/usr/bin/ssr-rules index f577281e6..cb7389718 100755 --- a/luci-app-ssr-plus/root/usr/bin/ssr-rules +++ b/luci-app-ssr-plus/root/usr/bin/ssr-rules @@ -46,7 +46,6 @@ CHECK_STATUS=0 RESTORE_RULES=0 FLUSH_RULES=0 CLEANUP_PERSISTENCE=0 -ENABLE_FAKE_IP=0 # ASCII code for SSRP.Use whatever,just not the same. FWMARK="0x53535250" @@ -84,15 +83,14 @@ usage() { -m Interface name -I a file content is bypassed shunt ip list -e extra options for iptables - -E <0|1> enable_fake_ip status -o apply the rules to the OUTPUT chain -O apply the global rules to the OUTPUT chain - -u enable udprelay mode, TPROXY is required - -U enable udprelay mode, using different IP - and ports for TCP and UDP - -y disable all auxiliary UDP rules when UDP - transparent proxy is unavailable - -f flush the rules + -u enable udprelay mode, TPROXY is required + -U enable udprelay mode, using different IP + and ports for TCP and UDP + -y disable all auxiliary UDP rules when UDP + transparent proxy is unavailable + -f flush the rules -g gfwlist mode -r router mode -z all mode @@ -234,12 +232,6 @@ cleanup_persistence_files() { loger 5 "Removed xhttp hash file: /tmp/.last_xhttp_hash" fi - # Remove Fake_IP Mode state file - if [ -f "$ENABLE_FAKE_IP_STATE_FILE" ]; then - rm -f "$ENABLE_FAKE_IP_STATE_FILE" 2>/dev/null - loger 5 "Removed Fake_IP Mode state file: $ENABLE_FAKE_IP_STATE_FILE" - fi - loger 5 "Persistence cleanup completed" return 0 } @@ -323,64 +315,25 @@ flush_nftables() { flush_iptables_legacy() { flush_iptables() { - local table=$1 - local ipt="iptables -t $table" - local DAT - - DAT=$(iptables-save -t $table 2>/dev/null) - - [ -z "$DAT" ] && return 0 - - # 1. 解除所有引用 SS_SPEC* 链的规则 - echo "$DAT" | grep -E "^-A .*SS_SPEC" | while read -r line; do - local chain rule - chain=$(echo "$line" | awk '{print $2}') - rule=$(echo "$line" | sed 's/^-A [^ ]* //') - [ -n "$chain" ] && [ -n "$rule" ] && \ - eval "$ipt -D \"$chain\" $rule 2>/dev/null" + local ipt="iptables -t $1" + local DAT=$(iptables-save -t $1) + eval $(echo "$DAT" | grep "$TAG" | sed -e 's/^-A/$ipt -D/' -e 's/$/;/') + for chain in $(echo "$DAT" | awk '/^:SS_SPEC/{print $1}'); do + $ipt -F ${chain:1} 2>/dev/null && $ipt -X ${chain:1} done - - # 2. 删除带 TAG 或 mark 的残留规则(同样加上 eval) - echo "$DAT" | grep -E "^-A " | grep -E "$TAG|-m mark|--mark" | while read -r line; do - local chain rule - chain=$(echo "$line" | awk '{print $2}') - rule=$(echo "$line" | sed 's/^-A [^ ]* //') - [ -n "$chain" ] && [ -n "$rule" ] && \ - eval "$ipt -D \"$chain\" $rule 2>/dev/null" - done - - # 3. 提取所有以 SS_SPEC 开头的自定义链 - local chains - chains=$(echo "$DAT" | awk '/^:SS_SPEC/{sub(/^:/,"",$1); print $1}') - - # 4. 清空并彻底删除 SS_SPEC* 自定义链 - for chain in $chains; do - $ipt -F "$chain" 2>/dev/null - $ipt -X "$chain" 2>/dev/null - done - - return 0 } - - # 依次对 nat 和 mangle 表执行清理 flush_iptables nat flush_iptables mangle - - # 清理策略路由与路由表 - if ip rule show | grep -Eiq "fwmark ${FWMARK}.*lookup 999"; then + if ip rule show | grep -Eq "fwmark ${FWMARK}.*lookup 999"; then ip rule del fwmark ${FWMARK} table 999 2>/dev/null fi if ip route show table 999 | grep -Eq "^local.*dev lo"; then ip route del local 0.0.0.0/0 dev lo table 999 2>/dev/null fi - - # 清理 IPSET 集合(先 -F 清空,后 -X 销毁) for setname in ss_spec_lan_ac ss_spec_wan_ac ssr_gen_router \ china fplan bplan gmlan whitelist blacklist gfwlist music; do - ipset -F $setname 2>/dev/null ipset -X $setname 2>/dev/null done - [ -n "$FWI" ] && echo '#!/bin/sh' >$FWI return 0 } @@ -571,11 +524,6 @@ fw_rule() { } fw_rule_nft() { - # 优先将 Fake-IP (198.18.0.0/16) 的 TCP 流量重定向至代理端口 - if [ "$ENABLE_FAKE_IP" = "1" ]; then - $NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 198.18.0.0/16 meta l4proto tcp redirect to :$local_port comment "\"$TAG\"" 2>/dev/null - fi - # redirect/translation: when PROXY_PORTS present, redirect those tcp ports to local_port if [ -n "$PROXY_PORTS" ]; then PORTS_ARGS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //') @@ -611,10 +559,6 @@ fw_rule_iptables() { $IPT -A SS_SPEC_WAN_FW -d "$net" -j RETURN done - if [ "$ENABLE_FAKE_IP" = "1" ]; then - $IPT -A SS_SPEC_WAN_FW -d 198.18.0.0/16 -p tcp -j REDIRECT --to-ports "$local_port" -m comment --comment "$TAG" - fi - $IPT -A SS_SPEC_WAN_FW -p tcp $PROXY_PORTS -j REDIRECT --to-ports "$local_port" 2>/dev/null || { loger 3 "Can't redirect TCP, please check the iptables." exit 1 @@ -667,33 +611,6 @@ ac_rule_nft() { fi $NFT flush chain inet ss_spec ss_spec_prerouting 2>/dev/null - # Fake-IP 重定向处理 (引入 Interface 匹配,防止 WAN 口越权) - if [ "$ENABLE_FAKE_IP" = "1" ]; then - if [ -z "$Interface" ]; then - if [ -n "$MATCH_SET" ]; then - $NFT add rule inet ss_spec ss_spec_prerouting ip daddr 198.18.0.0/16 $MATCH_SET meta l4proto tcp redirect to :$local_port comment "\"$TAG\"" 2>/dev/null - $NFT add rule inet ss_spec ss_spec_prerouting ip daddr 198.18.0.0/16 $MATCH_SET meta l4proto udp redirect to :$local_port comment "\"$TAG\"" 2>/dev/null - else - $NFT add rule inet ss_spec ss_spec_prerouting ip daddr 198.18.0.0/16 meta l4proto tcp redirect to :$local_port comment "\"$TAG\"" 2>/dev/null - $NFT add rule inet ss_spec ss_spec_prerouting ip daddr 198.18.0.0/16 meta l4proto udp redirect to :$local_port comment "\"$TAG\"" 2>/dev/null - fi - else - for name in $Interface; do - local IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null) - [ -z "$IFNAME" ] && IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null) - if [ -n "$IFNAME" ]; then - if [ -n "$MATCH_SET" ]; then - $NFT add rule inet ss_spec ss_spec_prerouting meta iifname "$IFNAME" ip daddr 198.18.0.0/16 $MATCH_SET meta l4proto tcp redirect to :$local_port comment "\"$TAG\"" 2>/dev/null - $NFT add rule inet ss_spec ss_spec_prerouting meta iifname "$IFNAME" ip daddr 198.18.0.0/16 $MATCH_SET meta l4proto udp redirect to :$local_port comment "\"$TAG\"" 2>/dev/null - else - $NFT add rule inet ss_spec ss_spec_prerouting meta iifname "$IFNAME" ip daddr 198.18.0.0/16 meta l4proto tcp redirect to :$local_port comment "\"$TAG\"" 2>/dev/null - $NFT add rule inet ss_spec ss_spec_prerouting meta iifname "$IFNAME" ip daddr 198.18.0.0/16 meta l4proto udp redirect to :$local_port comment "\"$TAG\"" 2>/dev/null - fi - fi - done - fi - fi - # Exclude special local addresses if $NFT list chain inet ss_spec ss_spec_prerouting >/dev/null 2>&1; then for net in 0.0.0.0/8 10.0.0.0/8 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.168.0.0/16 224.0.0.0/4 240.0.0.0/4; do @@ -708,6 +625,16 @@ ac_rule_nft() { # done #fi + # Build a rule in the prerouting hook chain that jumps to business chain with conditions + if [ -n "$PROXY_PORTS" ]; then + PORTS_ARGS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //') + if [ -n "$PORTS_ARGS" ]; then + TCP_EXT_ARGS="meta l4proto tcp tcp dport { $PORTS_ARGS }" + fi + else + TCP_EXT_ARGS="meta l4proto tcp" + fi + # Block UDP port 443 when TPROXY not Enable if [ -z "$TPROXY" ] && [ "$DISABLE_UDP_RULES" != "1" ]; then # Add UDP 443 block rule @@ -731,17 +658,6 @@ ac_rule_nft() { done fi fi - - # Build a rule in the prerouting hook chain that jumps to business chain with conditions - if [ -n "$PROXY_PORTS" ]; then - PORTS_ARGS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //') - if [ -n "$PORTS_ARGS" ]; then - TCP_EXT_ARGS="meta l4proto tcp tcp dport { $PORTS_ARGS }" - fi - else - TCP_EXT_ARGS="meta l4proto tcp" - fi - if [ -z "$Interface" ]; then # generic prerouting jump already exists (see ipset_nft), but if we have MATCH_SET_CONDITION we add a more specific rule if [ -n "$MATCH_SET" ]; then @@ -767,16 +683,11 @@ ac_rule_nft() { case "$OUTPUT" in 1) # Create ss_spec_output tcp chain - if ! $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then - $NFT add chain inet ss_spec ss_spec_output '{ type nat hook output priority 0; policy accept; }' - fi - $NFT flush chain inet ss_spec ss_spec_output 2>/dev/null - $NFT add rule inet ss_spec ss_spec_output meta mark 255 return 2>/dev/null - - if [ "$ENABLE_FAKE_IP" = "1" ]; then - $NFT add rule inet ss_spec ss_spec_output ip daddr 198.18.0.0/16 meta l4proto tcp redirect to :$local_port comment "\"$TAG\"" 2>/dev/null - $NFT add rule inet ss_spec ss_spec_output ip daddr 198.18.0.0/16 meta l4proto udp redirect to :$local_port comment "\"$TAG\"" 2>/dev/null - fi + if ! $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then + $NFT add chain inet ss_spec ss_spec_output '{ type nat hook output priority 0; policy accept; }' + fi + $NFT flush chain inet ss_spec ss_spec_output 2>/dev/null + $NFT add rule inet ss_spec ss_spec_output meta mark 255 return 2>/dev/null # Exclude special local addresses if $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then @@ -797,16 +708,11 @@ ac_rule_nft() { ;; 2) # Create ss_spec_output tcp chain - if ! $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then - $NFT add chain inet ss_spec ss_spec_output '{ type nat hook output priority 0; policy accept; }' - fi - $NFT flush chain inet ss_spec ss_spec_output 2>/dev/null - $NFT add rule inet ss_spec ss_spec_output meta mark 255 return 2>/dev/null - - if [ "$ENABLE_FAKE_IP" = "1" ]; then - $NFT add rule inet ss_spec ss_spec_output ip daddr 198.18.0.0/16 meta l4proto tcp redirect to :$local_port comment "\"$TAG\"" 2>/dev/null - $NFT add rule inet ss_spec ss_spec_output ip daddr 198.18.0.0/16 meta l4proto udp redirect to :$local_port comment "\"$TAG\"" 2>/dev/null - fi + if ! $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then + $NFT add chain inet ss_spec ss_spec_output '{ type nat hook output priority 0; policy accept; }' + fi + $NFT flush chain inet ss_spec ss_spec_output 2>/dev/null + $NFT add rule inet ss_spec ss_spec_output meta mark 255 return 2>/dev/null # Exclude special local addresses if $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then @@ -848,38 +754,11 @@ ac_rule_iptables() { return 2 ;; esac - - local ip_list="${LAN_AC_IP#?}" - ipset -! create ss_spec_lan_ac hash:net 2>/dev/null - ipset flush ss_spec_lan_ac 2>/dev/null - if [ -n "$ip_list" ]; then - for ip in $ip_list; do - ipset add ss_spec_lan_ac "$ip" 2>/dev/null - done - fi - fi - - # 2. PREROUTING 链处理 - if [ -z "$Interface" ]; then - # Global rules - if [ -n "$MATCH_SET" ]; then - $IPT -I PREROUTING 1 -p tcp $EXT_ARGS $MATCH_SET -m comment --comment "$TAG" -j SS_SPEC_WAN_AC - else - $IPT -I PREROUTING 1 -p tcp $EXT_ARGS -m comment --comment "$TAG" -j SS_SPEC_WAN_AC - fi - else - for name in $Interface; do - local IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null) - [ -z "$IFNAME" ] && IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null) - if [ -n "$IFNAME" ]; then - if [ -n "$MATCH_SET" ]; then - $IPT -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p tcp $EXT_ARGS $MATCH_SET -m comment --comment "$TAG" -j SS_SPEC_WAN_AC - else - $IPT -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p tcp $EXT_ARGS -m comment --comment "$TAG" -j SS_SPEC_WAN_AC - fi - fi - done fi + ipset -! -R <<-EOF || return 1 + create ss_spec_lan_ac hash:net + $(for ip in ${LAN_AC_IP#?}; do echo "add ss_spec_lan_ac $ip"; done) + EOF # Block UDP port 443 when TPROXY not Enable if [ -z "$TPROXY" ] && [ "$DISABLE_UDP_RULES" != "1" ]; then @@ -905,67 +784,42 @@ ac_rule_iptables() { done fi fi - - # 拦截 Fake-IP 流量 (198.18.0.0/16),受 Interface 和 LAN_AC 控制 - if [ "$ENABLE_FAKE_IP" = "1" ]; then - if [ -z "$Interface" ]; then - if [ -n "$MATCH_SET" ]; then - $IPT -I PREROUTING 1 -p tcp -d 198.18.0.0/16 $MATCH_SET -j REDIRECT --to-ports "$local_port" -m comment --comment "$TAG" - $IPT -I PREROUTING 2 -p udp -d 198.18.0.0/16 $MATCH_SET -j REDIRECT --to-ports "$local_port" -m comment --comment "$TAG" - else - $IPT -I PREROUTING 1 -p tcp -d 198.18.0.0/16 -j REDIRECT --to-ports "$local_port" -m comment --comment "$TAG" - $IPT -I PREROUTING 2 -p udp -d 198.18.0.0/16 -j REDIRECT --to-ports "$local_port" -m comment --comment "$TAG" - fi + if [ -z "$Interface" ]; then + # Global rules + if [ -n "$MATCH_SET" ]; then + $IPT -I PREROUTING 1 -p tcp $EXT_ARGS $MATCH_SET -m comment --comment "$TAG" -j SS_SPEC_WAN_AC else - for name in $Interface; do - local IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null) - [ -z "$IFNAME" ] && IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null) - if [ -n "$IFNAME" ]; then - if [ -n "$MATCH_SET" ]; then - $IPT -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p tcp -d 198.18.0.0/16 $MATCH_SET -j REDIRECT --to-ports "$local_port" -m comment --comment "$TAG" - $IPT -I PREROUTING 2 ${IFNAME:+-i $IFNAME} -p udp -d 198.18.0.0/16 $MATCH_SET -j REDIRECT --to-ports "$local_port" -m comment --comment "$TAG" - else - $IPT -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p tcp -d 198.18.0.0/16 -j REDIRECT --to-ports "$local_port" -m comment --comment "$TAG" - $IPT -I PREROUTING 2 ${IFNAME:+-i $IFNAME} -p udp -d 198.18.0.0/16 -j REDIRECT --to-ports "$local_port" -m comment --comment "$TAG" - fi - fi - done + $IPT -I PREROUTING 1 -p tcp $EXT_ARGS -m comment --comment "$TAG" -j SS_SPEC_WAN_AC fi + else + for name in $Interface; do + local IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null) + [ -z "$IFNAME" ] && IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null) + if [ -n "$IFNAME" ]; then + if [ -n "$MATCH_SET" ]; then + $IPT -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p tcp $EXT_ARGS $MATCH_SET -m comment --comment "$TAG" -j SS_SPEC_WAN_AC + else + $IPT -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p tcp $EXT_ARGS -m comment --comment "$TAG" -j SS_SPEC_WAN_AC + fi + fi + done fi case "$OUTPUT" in 1) - # fake-ip first - if [ "$ENABLE_FAKE_IP" = "1" ]; then - $IPT -I OUTPUT 1 -m mark --mark 255 -j RETURN 2>/dev/null - $IPT -I OUTPUT 2 -p tcp -d 198.18.0.0/16 -j REDIRECT --to-ports "$local_port" -m comment --comment "$TAG" - $IPT -I OUTPUT 3 -p udp -d 198.18.0.0/16 -j REDIRECT --to-ports "$local_port" -m comment --comment "$TAG" - $IPT -I OUTPUT 4 -p tcp $EXT_ARGS -m comment --comment "$TAG" -j SS_SPEC_WAN_AC - else - $IPT -I OUTPUT 1 -m mark --mark 255 -j RETURN 2>/dev/null - $IPT -I OUTPUT 2 -p tcp $EXT_ARGS -m comment --comment "$TAG" -j SS_SPEC_WAN_AC - fi + $IPT -I OUTPUT 1 -p tcp $EXT_ARGS -m comment --comment "$TAG" -j SS_SPEC_WAN_AC ;; 2) ipset -! -R <<-EOF || return 1 create ssr_gen_router hash:net $(gen_spec_iplist | sed -e "s/^/add ssr_gen_router /") EOF - $IPT -N SS_SPEC_ROUTER 2>/dev/null - $IPT -F SS_SPEC_ROUTER 2>/dev/null - # $IPT -A SS_SPEC_ROUTER -m mark --mark 255 -j RETURN - $IPT -A SS_SPEC_ROUTER -m set --match-set ssr_gen_router dst -j RETURN - $IPT -A SS_SPEC_ROUTER -j SS_SPEC_WAN_FW - # fake-ip first - if [ "$ENABLE_FAKE_IP" = "1" ]; then - $IPT -I OUTPUT 1 -m mark --mark 255 -j RETURN 2>/dev/null - $IPT -I OUTPUT 2 -p tcp -d 198.18.0.0/16 -j REDIRECT --to-ports "$local_port" -m comment --comment "$TAG" - $IPT -I OUTPUT 3 -p udp -d 198.18.0.0/16 -j REDIRECT --to-ports "$local_port" -m comment --comment "$TAG" - $IPT -I OUTPUT 4 -p tcp -m comment --comment "$TAG" -j SS_SPEC_ROUTER - else - $IPT -I OUTPUT 1 -m mark --mark 255 -j RETURN 2>/dev/null - $IPT -I OUTPUT 2 -p tcp -m comment --comment "$TAG" -j SS_SPEC_ROUTER - fi + $IPT -N SS_SPEC_ROUTER 2>/dev/null + $IPT -F SS_SPEC_ROUTER 2>/dev/null + $IPT -A SS_SPEC_ROUTER -m mark --mark 255 -j RETURN && \ + $IPT -A SS_SPEC_ROUTER -m set --match-set ssr_gen_router dst -j RETURN && \ + $IPT -A SS_SPEC_ROUTER -j SS_SPEC_WAN_FW + $IPT -I OUTPUT 1 -p tcp -m comment --comment "$TAG" -j SS_SPEC_ROUTER ;; esac return $? @@ -1001,29 +855,29 @@ tp_rule_nft() { fi local MATCH_SET="" - local EXT_ARGS="" if [ -n "$PROXY_PORTS" ]; then - local PORTS_ARGS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //') - [ -n "$PORTS_ARGS" ] && EXT_ARGS="udp dport { $PORTS_ARGS }" + PORTS_ARGS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //') + if [ -n "$PORTS_ARGS" ]; then + EXT_ARGS="udp dport { $PORTS_ARGS }" + else + EXT_ARGS="" + fi fi if [ -n "$LAN_AC_IP" ]; then # Create LAN access control set if needed - local mode="${LAN_AC_IP:0:1}" - local ips="${LAN_AC_IP:1}" - if ! $NFT list set ip ss_spec_mangle ss_spec_lan_ac >/dev/null 2>&1; then $NFT add set ip ss_spec_mangle ss_spec_lan_ac '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null else $NFT flush set ip ss_spec_mangle ss_spec_lan_ac 2>/dev/null fi - for ip in $ips; do + for ip in ${LAN_AC_IP#?}; do [ -n "$ip" ] && $NFT add element ip ss_spec_mangle ss_spec_lan_ac "{ $ip }" 2>/dev/null done - case "$mode" in + case "${LAN_AC_IP%${LAN_AC_IP#?}}" in w | W) MATCH_SET="ip saddr @ss_spec_lan_ac" ;; @@ -1052,10 +906,8 @@ tp_rule_nft() { fi # Bulk import xhttp ip list into nft whitelist (server + shunt) - local xhttp_file="${xhttp_ip:=/etc/ssrplus/xhttp_address.txt}" - if [ -s "$xhttp_file" ]; then - local xhttp_content=$(tr '\n' ',' < "$xhttp_file" | sed 's/,$//') - [ -n "$xhttp_content" ] && $NFT add element ip ss_spec_mangle whitelist "{ $xhttp_content }" 2>/dev/null + if [ -f "${xhttp_ip:=/etc/ssrplus/xhttp_address.txt}" ]; then + $NFT add element ip ss_spec_mangle whitelist "{ $(tr '\n' ',' < "${xhttp_ip}" | sed 's/,$//') }" 2>/dev/null fi # use priority mangle for compatibility with other rules @@ -1065,12 +917,6 @@ tp_rule_nft() { $NFT flush chain ip ss_spec_mangle ss_spec_tproxy 2>/dev/null fi - # 优先对 Fake-IP 进行 TPROXY 重定向 (无需受限于 PROXY_PORTS 限制) - if [ "$ENABLE_FAKE_IP" = "1" ]; then - $NFT add rule ip ss_spec_mangle ss_spec_tproxy ip daddr 198.18.0.0/16 counter tproxy ip to :"$LOCAL_PORT" meta mark set ${FWMARK} 2>/dev/null - fi - - # Exclude special local addresses if $NFT list chain ip ss_spec_mangle ss_spec_tproxy >/dev/null 2>&1; then for net in 0.0.0.0/8 10.0.0.0/8 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.168.0.0/16 224.0.0.0/4 240.0.0.0/4; do $NFT add rule ip ss_spec_mangle ss_spec_tproxy ip daddr $net return 2>/dev/null @@ -1088,9 +934,9 @@ tp_rule_nft() { $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp dport 53 return 2>/dev/null # avoid redirecting to udp server address - # if [ -n "$server" ]; then - # $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp dport != 53 ip daddr "$server" return 2>/dev/null - # fi + if [ -n "$server" ]; then + $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp dport != 53 ip daddr "$server" return 2>/dev/null + fi # if server != SERVER add SERVER to whitelist set (so tproxy won't touch it) if [ -n "$server" ]; then @@ -1100,9 +946,6 @@ tp_rule_nft() { $NFT add element ip ss_spec_mangle whitelist "{ $SERVER }" 2>/dev/null fi - # 显式豁免白名单集合中的 IP - $NFT add rule ip ss_spec_mangle ss_spec_tproxy ip daddr @whitelist return 2>/dev/null - # access control and tproxy rules $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip saddr @bplan return 2>/dev/null $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp $EXT_ARGS ip saddr @fplan counter tproxy ip to :"$LOCAL_PORT" meta mark set ${FWMARK} 2>/dev/null @@ -1122,9 +965,7 @@ tp_rule_nft() { $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip daddr @ss_spec_wan_ac return 2>/dev/null $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip daddr @china return 2>/dev/null - # 80 端口规则:非国内 IP 的 80 端口,走 TPROXY $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp udp dport 80 counter drop comment "\"$TAG\"" 2>/dev/null - # 443 端口规则:非国内 IP 的 443 端口,走 TPROXY $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp udp dport 443 counter drop comment "\"$TAG\"" 2>/dev/null $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip saddr @gmlan ip daddr != @china counter tproxy ip to :"$LOCAL_PORT" meta mark set ${FWMARK} 2>/dev/null $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp $EXT_ARGS ip daddr != @ss_spec_wan_ac counter tproxy ip to :"$LOCAL_PORT" meta mark set ${FWMARK} 2>/dev/null @@ -1134,9 +975,7 @@ tp_rule_nft() { $NFT add set ip ss_spec_mangle gfwlist '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null fi $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip daddr @china return 2>/dev/null - # 80 端口规则:非国内 IP 的 80 端口,走 TPROXY $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp udp dport 80 counter drop comment "\"$TAG\"" 2>/dev/null - # 443 端口规则:非国内 IP 的 443 端口,走 TPROXY $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp udp dport 443 counter drop comment "\"$TAG\"" 2>/dev/null $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp $EXT_ARGS ip daddr @gfwlist counter tproxy ip to :"$LOCAL_PORT" meta mark set ${FWMARK} 2>/dev/null $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip saddr @gmlan ip daddr != @china counter tproxy ip to :"$LOCAL_PORT" meta mark set ${FWMARK} 2>/dev/null @@ -1149,8 +988,6 @@ tp_rule_nft() { # finally, ensure prerouting hook entry to jump to tproxy chain if ! $NFT list chain ip ss_spec_mangle prerouting >/dev/null 2>&1; then $NFT add chain ip ss_spec_mangle prerouting '{ type filter hook prerouting priority mangle; policy accept; }' - else - $NFT flush chain ip ss_spec_mangle prerouting 2>/dev/null fi # add prerouting jump (idempotent) @@ -1219,14 +1056,8 @@ tp_rule_iptables() { do $ipt -A SS_SPEC_TPROXY -p udp -d "$net" -j RETURN done - # fake-ip 规则 - if [ "$ENABLE_FAKE_IP" = "1" ]; then - $ipt -A SS_SPEC_TPROXY -p udp $MATCH_SET -d 198.18.0.0/16 -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK} - fi - [ -n "$SERVER" ] && $ipt -A SS_SPEC_TPROXY -p udp ! --dport 53 -d "$SERVER" -j RETURN [ -n "$SERVER" ] && [ "$server" != "$SERVER" ] && ipset -! add whitelist "$SERVER" - $ipt -A SS_SPEC_TPROXY -p udp -m set --match-set whitelist dst -j RETURN if [ -f "${xhttp_ip:=/etc/ssrplus/xhttp_address.txt}" ]; then while IFS= read -r ip; do [ -n "$ip" ] && ipset add whitelist "$ip" -exist @@ -1234,7 +1065,6 @@ tp_rule_iptables() { fi $ipt -A SS_SPEC_TPROXY -p udp -m set --match-set bplan src -j RETURN $ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set fplan src -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK} - $ipt -A SS_SPEC_TPROXY -p udp -m set --match-set blacklist dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK} case "$RUNMODE" in router) ipset -! -R <<-EOF || return 1 @@ -1243,27 +1073,15 @@ tp_rule_iptables() { EOF $ipt -A SS_SPEC_TPROXY -p udp -m set --match-set ss_spec_wan_ac dst -j RETURN $ipt -A SS_SPEC_TPROXY -p udp -m set --match-set china dst -j RETURN - # 80 端口规则:非国内 IP 的 80 端口,走 TPROXY $ipt -A SS_SPEC_TPROXY -p udp --dport 80 -j DROP - # 443 端口规则:非国内 IP 的 443 端口,走 TPROXY - $ipt -A SS_SPEC_TPROXY -p udp --dport 443 -j DROP - # 保留海外 QUIC 代理 - # $ipt -A SS_SPEC_TPROXY -p udp --dport 80 -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK} - # $ipt -A SS_SPEC_TPROXY -p udp --dport 443 -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK} - # $ipt -A SS_SPEC_TPROXY -p udp -m set ! --match-set china dst --dport 80 -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK} - # $ipt -A SS_SPEC_TPROXY -p udp -m set ! --match-set china dst --dport 443 -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK} + $ipt -A SS_SPEC_TPROXY -p udp --dport 443 -j DROP $ipt -A SS_SPEC_TPROXY -p udp -m set --match-set gmlan src -m set ! --match-set china dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK} $ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set ! --match-set ss_spec_wan_ac dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK} ;; gfw) $ipt -A SS_SPEC_TPROXY -p udp -m set --match-set china dst -j RETURN - # 80 端口规则:非国内 IP 的 80 端口,走 TPROXY $ipt -A SS_SPEC_TPROXY -p udp --dport 80 -j DROP - # 443 端口规则:非国内 IP 的 443 端口,走 TPROXY - $ipt -A SS_SPEC_TPROXY -p udp --dport 443 -j DROP - # 保留海外 QUIC 代理 - # $ipt -A SS_SPEC_TPROXY -p udp --dport 80 -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK} - # $ipt -A SS_SPEC_TPROXY -p udp --dport 443 -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK} + $ipt -A SS_SPEC_TPROXY -p udp --dport 443 -j DROP $ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set gfwlist dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK} $ipt -A SS_SPEC_TPROXY -p udp -m set --match-set gmlan src -m set ! --match-set china dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark ${FWMARK} ;; @@ -1645,7 +1463,7 @@ restore_from_persistence() { fi } -while getopts ":m:s:l:S:L:i:e:E:a:B:b:w:p:G:D:F:N:M:I:oOuUyfgrzAKPCRXh" arg; do +while getopts ":m:s:l:S:L:i:e:a:B:b:w:p:G:D:F:N:M:I:oOuUyfgrzAKPCRXh" arg; do case "$arg" in m) Interface=$OPTARG @@ -1668,9 +1486,6 @@ while getopts ":m:s:l:S:L:i:e:E:a:B:b:w:p:G:D:F:N:M:I:oOuUyfgrzAKPCRXh" arg; do e) EXT_ARGS=$OPTARG ;; - E) - ENABLE_FAKE_IP=$OPTARG - ;; a) LAN_AC_IP=$OPTARG ;; @@ -1874,14 +1689,6 @@ if [ -n "$local_port" ]; then if [ "$USE_NFT" = "1" ]; then # NFTables - # Detect Fake_IP Mode state change (execute first) - ENABLE_FAKE_IP_STATE_FILE="/tmp/.last_enable_fake_ip" - if [ -f "$ENABLE_FAKE_IP_STATE_FILE" ]; then - LAST_ENABLE_FAKE_IP=$(cat "$ENABLE_FAKE_IP_STATE_FILE") - else - LAST_ENABLE_FAKE_IP="" - fi - # Save previous TPROXY state file TPROXY_STATE_FILE="/tmp/.last_tproxy" if [ -f "$TPROXY_STATE_FILE" ]; then @@ -1971,13 +1778,6 @@ if [ -n "$local_port" ]; then LAST_XHTTP_FILE_HASH="" fi - # Check if Fake_IP Mode state has changed - ENABLE_FAKE_IP_CHANGED=0 - if [ "$LAST_ENABLE_FAKE_IP" != "$ENABLE_FAKE_IP" ]; then - loger 6 "Fake_IP Mode state changed: $LAST_ENABLE_FAKE_IP -> $ENABLE_FAKE_IP" - ENABLE_FAKE_IP_CHANGED=1 - fi - # STEP 1: Check if TPROXY has value (1 or 2) if [ "$TPROXY" = "1" ] || [ "$TPROXY" = "2" ]; then TPROXY_HAS_VALUE=1 @@ -2073,9 +1873,7 @@ if [ -n "$local_port" ]; then # 1. TPROXY changes from empty ↔ has value # 2. PROXY_PORTS changes from empty ↔ has value # 3. Any IP list has changed - # 4. ENABLE_FAKE_IP state changed - if [ "$ENABLE_FAKE_IP_CHANGED" = "1" ] || \ - [ "$TPROXY_HAS_VALUE" != "$LAST_HAS_VALUE" ] || \ + if [ "$TPROXY_HAS_VALUE" != "$LAST_HAS_VALUE" ] || \ [ "$PROXY_HAS_VALUE" != "$LAST_PROXY_HAS_VALUE" ] || \ [ "$ANY_IP_LIST_CHANGED" = "1" ]; then FORCE_RECREATE=1 @@ -2093,7 +1891,6 @@ if [ -n "$local_port" ]; then fi # STEP 4: Save current state - echo "$ENABLE_FAKE_IP" > "$ENABLE_FAKE_IP_STATE_FILE" echo "$TPROXY" > "$TPROXY_STATE_FILE" echo "$PROXY_PORTS" > "$PROXY_PORTS_STATE_FILE" echo "$(normalize_ip_list "$WAN_BP_IP")" > "$WAN_BP_IP_STATE_FILE" diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/clash_yaml.lua b/luci-app-ssr-plus/root/usr/share/shadowsocksr/clash_yaml.lua index e1511e2ad..2a4d28aa9 100644 --- a/luci-app-ssr-plus/root/usr/share/shadowsocksr/clash_yaml.lua +++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/clash_yaml.lua @@ -4,9 +4,6 @@ require "nixio" require "nixio.fs" require "luci.model.uci" -local command_cache = {} -local ech_cache = {} - local ok_lyaml, lyaml = pcall(require, "lyaml") if not ok_lyaml then io.stderr:write("lyaml_not_found\n") @@ -16,19 +13,6 @@ end local uci = require "luci.model.uci".cursor() local ok_jsonc, jsonc = pcall(require, "luci.jsonc") -local dns_mode = uci:get_first("shadowsocksr", "global", "pdnsd_enable", "0") -if dns_mode == nil or dns_mode == "" then - dns_mode = "0" -end - -local enable_fake_ip = uci:get_first("shadowsocksr", "server_subscribe", "enable_fake_ip", "") -if enable_fake_ip == nil or enable_fake_ip == "" then - enable_fake_ip = "0" -end - --- ==================== 基础工具函数 ================================= --- ==================== Basic Utility Functions ==================== - local function read_file(path) local data = nixio.fs.readfile(path) if not data or data == "" then @@ -55,32 +39,12 @@ local function load_yaml(path) return parsed end -local function quote_nameserver_policy_keys(rendered) - rendered = rendered:gsub("[^\n]+", function(line) - local indent = line:match("^(%s*)geosite:geolocation%-!cn:%s*$") - if indent then - return indent .. '"geosite:geolocation-!cn":' - end - - indent = line:match("^(%s*)geosite:cn,private,apple:%s*$") - if indent then - return indent .. '"geosite:cn,private,apple":' - end - - return line - end) - - return rendered -end - local function dump_yaml(path, data) local ok, rendered = pcall(lyaml.dump, { data }) if not ok or not rendered then return nil, "dump_failed" end - rendered = quote_nameserver_policy_keys(rendered) - write_file(path, rendered) return true end @@ -127,110 +91,6 @@ local function parse_csv_line(line) return cols end -local function split_csv(value) - local items = {} - for part in tostring(value or ""):gmatch("[^,%s]+") do - items[#items + 1] = part - end - return items -end - -local function split_host_port(value) - local text = tostring(value or "") - if text == "" then - return "", "" - end - local host, port = text:match("^%[(.-)%]:(%d+)$") - if host and port then - return host, port - end - host, port = text:match("^(.-):(%d+)$") - if host and port then - return host, port - end - return text, "" -end - -local function split_alpn(value) - local items = {} - for part in tostring(value or ""):gmatch("[^,;|%s]+") do - items[#items + 1] = part - end - return items -end - -local function split_local_addresses(value) - local items = {} - if type(value) == "table" then - for _, item in ipairs(value) do - if item and item ~= "" then - items[#items + 1] = tostring(item) - end - end - elseif value and value ~= "" then - for item in tostring(value):gmatch("[^,%s]+") do - items[#items + 1] = item - end - end - return items -end - -local function split_wireguard_addresses(value) - local ip, ipv6 - for _, item in ipairs(split_local_addresses(value)) do - if item:find(":", 1, true) then - ipv6 = ipv6 or item - else - ip = ip or item - end - end - return ip, ipv6 -end - -local function first_nonempty(...) - for i = 1, select("#", ...) do - local value = select(i, ...) - if value ~= nil and value ~= "" then - return value - end - end - return nil -end - -local function clone_table(src) - if type(src) ~= "table" then - return nil - end - - local dst = {} - - for k, v in pairs(src) do - if type(v) == "table" then - dst[k] = clone_table(v) - else - dst[k] = v - end - end - - return dst -end - -local function deep_merge(dst, src) - if type(dst) ~= "table" or type(src) ~= "table" then - return src - end - - for k, v in pairs(src) do - if type(v) == "table" and type(dst[k]) == "table" then - dst[k] = deep_merge(dst[k], v) - else - dst[k] = v - end - end - - return dst -end - local function read_clash_client_rules_csv(sid) local rows = {} sid = trim(sid) @@ -268,516 +128,6 @@ local function read_clash_client_rules_csv(sid) return rows end -local function get_server_field(sid, option, default) - local value = uci:get("shadowsocksr", sid, option) - if value == nil or value == "" then - return default - end - return value -end - --- ==================== ECH 配置获取函数 ========================================= --- ==================== ECH Configuration Fetching Function ==================== - --- ============================================================================ --- 检查命令是否存在 --- @param cmd 命令名称 --- @return Boolean 是否存在 --- ============================================================================ -local function command_exists(cmd) - if command_cache[cmd] ~= nil then - return command_cache[cmd] - end - - local safe_cmd = cmd:match("^[%w_%-]+$") - if not safe_cmd then - command_cache[cmd] = false - return false - end - - local handle = io.popen(string.format("command -v %s 2>/dev/null", safe_cmd)) - if not handle then - command_cache[cmd] = false - return false - end - - local result = handle:read("*a") - handle:close() - - command_cache[cmd] = (result and result ~= "") - return command_cache[cmd] -end - --- ============================================================================ --- 获取 ECH 配置 --- @param domain 域名 --- @param dns_server DNS 服务器地址 --- @return String ECH Base64 值 或 nil --- ============================================================================ -local function fetch_ech_config(domain, dns_server) - if not domain or domain == "" or not dns_server or dns_server == "" then - return nil - end - - local cache_key = domain .. "|" .. dns_server - if ech_cache[cache_key] ~= nil then - return ech_cache[cache_key] or nil - end - - if not command_exists("dig") then - ech_cache[cache_key] = false - return nil - end - - local proto = dns_server:match("^([a-zA-Z0-9%+%-%.]+)://") or "" - local port = dns_server:match(":(%d+)") - local server = dns_server - :gsub("^[a-zA-Z0-9%+%-%.]+://", "") - :gsub("/.*$", "") - :gsub(":%d+$", "") - - if server == "" then - ech_cache[cache_key] = false - return nil - end - - local safe_domain = domain:match("^[%w%-%._]+$") - local safe_server = server:match("^[%w%-%._]+$") - local safe_port = port and port:match("^%d+$") or nil - - if not safe_domain or not safe_server then - ech_cache[cache_key] = false - return nil - end - - local dig_opts = "" - proto = proto:lower() - - if proto == "https" or proto == "doh" then - dig_opts = "+https" - if safe_port and safe_port ~= "443" then - dig_opts = dig_opts .. " -p " .. safe_port - end - elseif proto == "tls" or proto == "dot" then - dig_opts = "+tls" - if safe_port and safe_port ~= "853" then - dig_opts = dig_opts .. " -p " .. safe_port - end - else - if safe_port and safe_port ~= "53" then - dig_opts = "-p " .. safe_port - end - end - - local cmd = string.format("dig %s +short TYPE65 %s @%s 2>/dev/null", dig_opts, safe_domain, safe_server) - local handle = io.popen(cmd) - if not handle then - ech_cache[cache_key] = false - return nil - end - - local output = handle:read("*a") - handle:close() - - if not output or output == "" then - ech_cache[cache_key] = false - return nil - end - - local ech = output:match('ech="?([^"%s]+)"?') - if ech and ech ~= "" then - ech_cache[cache_key] = ech - return ech - end - - ech_cache[cache_key] = false - return nil -end - -local function get_filter_aaaa() - local value = uci:get_first("shadowsocksr", "global", "filter_aaaa", "1") - if value == nil or value == "" then - value = uci:get_first("shadowsocksr", "global", "mosdns_ipv6", "1") - end - return value -end - -local function bool_enabled(value) - return value == "1" or value == 1 or value == true or value == "true" -end - -local function bool_default(value, default) - if value == nil or value == "" then - return default - end - return bool_enabled(value) -end - -local function number_or_nil(value) - if value == nil or value == "" then - return nil - end - return tonumber(value) -end - -local function string_or_nil(value) - if value == nil or value == "" then - return nil - end - return tostring(value) -end - -local function get_uptime_ms() - local f = io.open("/proc/uptime", "r") - if f then - local line = f:read("*l") - f:close() - if line then - local sec, frac = line:match("^(%d+)%.(%d+)") - if sec and frac then - frac = (frac .. "00"):sub(1, 3) - return tonumber(sec) * 1000 + tonumber(frac) - end - end - end - return os.time() * 1000 -end - -local function load_list_file(file_path) - local list = {} - local file = io.open(file_path, "r") - if not file then - return nil - end - - for line in file:lines() do - line = line:match("^%s*(.-)%s*$") - if line ~= "" and not line:match("^#") then - table.insert(list, line) - end - end - file:close() - return #list > 0 and list or nil -end - --- ==================== DNS 相关函数 ============================== --- ==================== DNS-related Functions ==================== - -local function format_dns_server(proxy) - if not proxy or proxy == "" then - return proxy - end - - local scheme, rest = proxy:match("^([a-zA-Z0-9%+%-%.]+)://(.+)$") - if scheme then - scheme = scheme:lower() - if scheme == "tls" or scheme == "dot" then - if not rest:match(":%d+$") then - rest = rest .. ":853" - end - return "tls://" .. rest - elseif scheme == "https" or scheme == "doh" or scheme == "dohl" or scheme == "doq" or scheme == "doql" then - local host, path = rest:match("^([^/]+)(.*)$") - host = host or rest - path = path or "" - if path == "" or path == "/" then - path = "/dns-query" - end - return "https://" .. host .. path - elseif scheme == "tcp" or scheme == "udp" then - if not rest:match(":%d+$") then - rest = rest .. ":53" - end - return scheme .. "://" .. rest - else - return proxy - end - end - - local host, port = proxy:match("^([^:]+):(%d+)$") - if host and port then - if port == "853" then - return "tls://" .. host .. ":853" - elseif port == "443" then - return "https://" .. host .. "/dns-query" - elseif port == "53" then - return host - else - return "udp://" .. host .. ":" .. port - end - end - - if proxy:match("[a-zA-Z]") and not proxy:match("[^%w%.%-]") then - local path = "" - if not proxy:match("/") then - path = "/dns-query" - end - return "https://" .. proxy .. path - end - - return proxy -end - -local function get_fastest_dns() - local dns_list = { - "223.5.5.5", "119.29.29.29", - "114.114.114.114", "180.76.76.76", - "114.114.115.115", "223.6.6.6", - "1.2.4.8", "1.12.12.12", - "101.226.4.6", "180.184.1.1", - } - local test_domain = "www.baidu.com" - local valid_dns_results = {} - - for _, dns in ipairs(dns_list) do - local start_time = get_uptime_ms() - - local cmd = string.format( - "timeout 2 nslookup %s %s 2>/dev/null", - test_domain, - dns - ) - - local f = io.popen(cmd) - if f then - local output = f:read("*a") or "" - f:close() - - local elapsed = get_uptime_ms() - start_time - local parsed_ip - for ip in output:gmatch("Address%s*%d*%s*:%s*(%d+%.%d+%.%d+%.%d+)") do - if ip ~= dns then - parsed_ip = ip - break - end - end - local is_success = - parsed_ip - and not output:match("[Cc]an't find") - and not output:match("[Tt]imed out") - and not output:match("[Nn]o servers could be reached") - if is_success and elapsed >= 0 and elapsed < 2500 then - table.insert(valid_dns_results, { - dns = dns, - latency = elapsed, - }) - end - end - end - - table.sort(valid_dns_results, function(a, b) - return a.latency < b.latency - end) - - local result = {} - local used = {} - - for i = 1, math.min(3, #valid_dns_results) do - local dns = valid_dns_results[i].dns - table.insert(result, dns) - used[dns] = true - end - - local fallbacks = { - "223.5.5.5", - "119.29.29.29", - "114.114.114.114", - } - - for _, dns in ipairs(fallbacks) do - if #result >= 3 then - break - end - - if not used[dns] then - table.insert(result, dns) - used[dns] = true - end - end - - return result -end - -local function get_forward_dns_list() - local forward = uci:get_first("shadowsocksr", "global", "tunnel_forward", "8.8.4.4:53") - if not forward or forward == "" then - forward = "8.8.4.4:53" - end - - local forward_list = {} - local used = {} - - for proxy in tostring(forward):gmatch("[^%s,;]+") do - if proxy ~= "" then - local formatted = format_dns_server(proxy) - if not used[formatted] then - table.insert(forward_list, formatted) - used[formatted] = true - end - end - end - - if #forward_list == 0 then - forward_list = { format_dns_server(forward) } - end - - return forward_list -end - -local function build_dns_upstreams() - local fastest_list = get_fastest_dns() - local nameserver_list = {} - local proxy_server_list = {} - --local used_dns = {} - - local proxy_server_list = { - "https://cn.ali-oss.cn:44443/dns-query/6dafe708-d9d6-48cc-a768-e6ed3018a9ec", - "https://hk.ali-oss.cn:44443/dns-query/6dafe708-d9d6-48cc-a768-e6ed3018a9ec" - } - --local used_dns = {} - - for _, dns_ip in ipairs(fastest_list) do - local formatted = format_dns_server(dns_ip) - table.insert(nameserver_list, formatted) - -- - -- if not used_dns[dns_ip] then - -- table.insert(proxy_server_list, dns_ip) - -- used_dns[dns_ip] = true - -- end - end - - return { - ["respect-rules"] = true, - ["nameserver"] = nameserver_list, - ["proxy-server-nameserver"] = proxy_server_list - } -end - -local function build_dns_section(dns_mode, user_dns) - local result - local has_user_dns = type(user_dns) == "table" and next(user_dns) - dns_mode = tostring(dns_mode or "0") - - if not has_user_dns then - result = { - enable = true - } - else - result = clone_table(user_dns) - result.enable = true - end - - if enable_fake_ip == "1" then - result["enhanced-mode"] = "fake-ip" - -- if not result["fake-ip-range"] then - -- result["fake-ip-range"] = "198.18.0.1/16" - -- end - -- Must match the range redirected by ssr-rules; a value supplied by the - -- subscription would leave those redirects pointing at the wrong network. - -- result["fake-ip-range"] = nil - result["fake-ip-range"] = "198.18.0.1/16" - if not result["fake-ip-filter"] then - local filter_file = "/etc/ssrplus/fake_ip_filter.list" - local loaded_filter = load_list_file(filter_file) or { "*.lan", "*.local", "time.*.com", "ntp.*.com" } - if loaded_filter then - result["fake-ip-filter"] = loaded_filter - end - end - if result["use-hosts"] == nil then - result["use-hosts"] = true - end - - -- if not result["fallback"] then - -- result["fallback"] = get_forward_dns_list() - -- end - result["fallback"] = nil - result["fallback"] = get_forward_dns_list() - - if not result["default-nameserver"] then - result["default-nameserver"] = get_fastest_dns() - end - if not result["direct-nameserver"] then - result["direct-nameserver"] = get_fastest_dns() - end - if result["direct-nameserver-follow-policy"] == nil then - result["direct-nameserver-follow-policy"] = false - end - if result["respect-rules"] == nil then - result["respect-rules"] = true - end - if not result["fallback-filter"] then - local domain_file = "/etc/ssrplus/fallback_domain.list" - local domain_list = load_list_file(domain_file) or { "+.google.com", "+.facebook.com", "+.youtube.com" } - local ipcidr_file = "/etc/ssrplus/fallback_ipcidr.list" - local ipcidr_list = load_list_file(ipcidr_file) or { "240.0.0.0/4" } - result["fallback-filter"] = { - domain = domain_list, - ipcidr = ipcidr_list, - geoip = true, - ["geoip-code"] = "CN" - } - else - if result["fallback-filter"].geosite then - result["fallback-filter"].geosite = nil - end - end - - -- if not result["nameserver-policy"] then - -- result["nameserver-policy"] = {} - -- end - -- if not result["nameserver-policy"]["geosite:gfw"] then - -- result["nameserver-policy"]["geosite:gfw"] = result["fallback"] or get_forward_dns_list() - -- end - -- if not result["nameserver-policy"]["geosite:cn,private,apple"] then - -- result["nameserver-policy"]["geosite:cn,private,apple"] = get_fastest_dns() - -- end - result["nameserver-policy"] = nil - result["nameserver-policy"] = { - ["geosite:geolocation-!cn"] = result["fallback"] or get_forward_dns_list(), - ["geosite:cn,private,apple"] = get_fastest_dns() - } - - else - result["enhanced-mode"] = "redir-host" - result["fake-ip-range"] = nil - result["fake-ip-filter"] = nil - result["use-hosts"] = nil - result["fallback"] = nil - result["respect-rules"] = nil - result["fallback-filter"] = nil - result["nameserver-policy"] = nil - result["direct-nameserver"] = nil - result["direct-nameserver-follow-policy"] = nil - end - - local upstreams = build_dns_upstreams() - for k, v in pairs(upstreams) do - if k == "proxy-server-nameserver" then - result[k] = v - elseif result[k] == nil and not (k == "respect-rules" and enable_fake_ip ~= "1") then - result[k] = v - end - end - - if dns_mode == "7" then - -- Must match the address dnsmasq forwards to; a value supplied by the - -- subscription would make every forwarded query hit a closed port. - -- result.listen = nil - result.listen = "127.0.0.1:5335" - else - result.listen = nil - end - - if result.ipv6 == nil then - result.ipv6 = get_filter_aaaa() ~= "1" - end - - return result -end - --- ==================== 配置处理函数 ============================================ --- ==================== Configuration Processing Functions ==================== - local function has_proxy_sections(doc) return type(doc.proxies) == "table" or type(doc["proxy-providers"]) == "table" end @@ -842,6 +192,22 @@ local function filter(path, filter_words) return true end +local function deep_merge(dst, src) + if type(dst) ~= "table" or type(src) ~= "table" then + return src + end + + for k, v in pairs(src) do + if type(v) == "table" and type(dst[k]) == "table" then + dst[k] = deep_merge(dst[k], v) + else + dst[k] = v + end + end + + return dst +end + local function strip_runtime_conflicts(doc) doc.tun = nil doc.listeners = nil @@ -859,61 +225,6 @@ local function strip_runtime_conflicts(doc) end end -local function apply_sniffer_config(doc, enable_fake_ip) - if type(doc) ~= "table" then return end - -- local is_fake_ip = (enable_fake_ip == "1") - local sniffer_config = { - enable = true, - ["override-destination"] = true, - sniff = { - HTTP = { - ports = { 80, 2052, 2082, 2086, 2095, "8080-8880" }, - ["override-destination"] = true - }, - TLS = { - ports = { 443, 2053, 2083, 2087, 2096, 8443 } - }, - QUIC = { - ports = { 443, 8443 } - } - }, - ["skip-domain"] = { "Mijia Cloud", "dlg.io.mi.com" }, - ["force-domain"] = { "+.v2ex.com" }, - -- ["force-dns-mapping"] = not is_fake_ip, -- fake-ip: false, redir-host: true - -- ["parse-pure-ip"] = not is_fake_ip -- fake-ip: false, redir-host: true - ["force-dns-mapping"] = true, - ["parse-pure-ip"] = true - } - - if not doc.sniffer then - doc.sniffer = sniffer_config - else - if doc.sniffer.enable == nil then - doc.sniffer.enable = true - end - if doc.sniffer["override-destination"] == nil then - doc.sniffer["override-destination"] = true - end - if not doc.sniffer.sniff then - doc.sniffer.sniff = sniffer_config.sniff - end - if not doc.sniffer["skip-domain"] then - doc.sniffer["skip-domain"] = sniffer_config["skip-domain"] - end - if not doc.sniffer["force-domain"] then - doc.sniffer["force-domain"] = sniffer_config["force-domain"] - end - if doc.sniffer["force-dns-mapping"] == nil then - -- doc.sniffer["force-dns-mapping"] = not is_fake_ip - doc.sniffer["force-dns-mapping"] = true - end - if doc.sniffer["parse-pure-ip"] == nil then - -- doc.sniffer["parse-pure-ip"] = not is_fake_ip - doc.sniffer["parse-pure-ip"] = true - end - end -end - local function group_requires_candidates(group) local gtype = tostring(group and group.type or ""):lower() return gtype == "select" @@ -970,35 +281,276 @@ local function strip_incompatible_script_rules(doc) return removed end -local function merge_rules_with_direct(existing_rules) - local new_rules = {} - local existing_set = {} - local match_rule = nil +local function prepare(input_path, output_path) + local doc, err = load_yaml(input_path) + if not doc then + io.stderr:write(err or "parse_failed", "\n") + return false + end + if not has_proxy_sections(doc) then + io.stderr:write("missing_proxy_sections\n") + return false + end - if existing_rules then - for _, rule in ipairs(existing_rules) do - local clean_rule = rule:match("^%s*(.-)%s*$") or rule - - if clean_rule:upper():find("^MATCH,") then - if not match_rule then - match_rule = clean_rule - end - else - if not existing_set[clean_rule] and clean_rule ~= "" then - table.insert(new_rules, clean_rule) - existing_set[clean_rule] = true + strip_runtime_conflicts(doc) + local filled_groups = fill_empty_proxy_groups(doc) + local stripped_rules = strip_incompatible_script_rules(doc) + local ok, rendered = pcall(lyaml.dump, { doc }) + if not ok or not rendered then + io.stderr:write("dump_failed\n") + return false + end + + write_file(output_path, rendered) + io.stdout:write(string.format("filled_groups=%d stripped_script_rules=%d\n", filled_groups, stripped_rules)) + return true +end + +local function merge(raw_path, overlay_path, output_path) + local raw_doc, raw_err = load_yaml(raw_path) + if not raw_doc then + io.stderr:write(raw_err or "parse_failed", "\n") + return false + end + + local overlay_doc, overlay_err = load_yaml(overlay_path) + if not overlay_doc then + io.stderr:write(overlay_err or "parse_failed", "\n") + return false + end + + strip_runtime_conflicts(raw_doc) + local filled_groups = fill_empty_proxy_groups(raw_doc) + local stripped_rules = strip_incompatible_script_rules(raw_doc) + local merged = deep_merge(raw_doc, overlay_doc) + local ok, rendered = pcall(lyaml.dump, { merged }) + if not ok or not rendered then + io.stderr:write("dump_failed\n") + return false + end + + write_file(output_path, rendered) + io.stdout:write(string.format("filled_groups=%d stripped_script_rules=%d\n", filled_groups, stripped_rules)) + return true +end + +local function append_client_policy_rules(runtime_path, sid) + local doc, err = load_yaml(runtime_path) + if not doc then + io.stderr:write(err or "parse_failed", "\n") + return false + end + + local valid_policies = {} + for _, proxy in ipairs(doc.proxies or {}) do + if type(proxy) == "table" and proxy.name and proxy.name ~= "" then + valid_policies[tostring(proxy.name)] = true + end + end + for _, group in ipairs(doc["proxy-groups"] or {}) do + if type(group) == "table" and group.name and group.name ~= "" then + valid_policies[tostring(group.name)] = true + end + end + + local custom_rules = {} + for _, section in ipairs(read_clash_client_rules_csv(sid)) do + if tostring(section.enabled or "0") == "1" then + local ip_addr = tostring(section.ip_addr or "") + local policy_group = tostring(section.policy_group or "") + if ip_addr ~= "" and policy_group ~= "" and valid_policies[policy_group] then + if not ip_addr:find("/", 1, true) then + ip_addr = ip_addr .. "/32" end + custom_rules[#custom_rules + 1] = string.format("SRC-IP-CIDR,%s,%s", ip_addr, policy_group) end end end - table.insert(new_rules, match_rule or "MATCH,PROXY") + if #custom_rules == 0 then + io.stdout:write("client_rules=0\n") + return true + end - return new_rules + local existing_rules = {} + for _, rule in ipairs(doc.rules or {}) do + local text = tostring(rule or "") + if not text:match("^SRC%-IP%-CIDR,") then + existing_rules[#existing_rules + 1] = rule + end + end + + doc.rules = {} + for _, rule in ipairs(custom_rules) do + doc.rules[#doc.rules + 1] = rule + end + for _, rule in ipairs(existing_rules) do + doc.rules[#doc.rules + 1] = rule + end + + local ok, rendered = pcall(lyaml.dump, { doc }) + if not ok or not rendered then + io.stderr:write("dump_failed\n") + return false + end + + write_file(runtime_path, rendered) + io.stdout:write(string.format("client_rules=%d\n", #custom_rules)) + return true end --- ==================== Shadowsocks 插件处理函数 ===================================== --- ==================== Shadowsocks Plugin Processing Functions ==================== +local function bool_enabled(value) + return value == "1" or value == 1 or value == true or value == "true" +end + +local function split_csv(value) + local items = {} + for part in tostring(value or ""):gmatch("[^,%s]+") do + items[#items + 1] = part + end + return items +end + +local function get_server_field(sid, option, default) + local value = uci:get("shadowsocksr", sid, option) + if value == nil or value == "" then + return default + end + return value +end + +local function get_filter_aaaa() + local value = uci:get_first("shadowsocksr", "global", "filter_aaaa", "1") + if value == nil or value == "" then + value = uci:get_first("shadowsocksr", "global", "mosdns_ipv6", "1") + end + return value +end + +local function build_tuic_runtime_doc(sid, local_port, socks_port, mode) + local server = get_server_field(sid, "server", "") + local server_port = tonumber(get_server_field(sid, "server_port", "0")) or 0 + local tuic_ip = get_server_field(sid, "tuic_ip", "") + local tls_host = get_server_field(sid, "tls_host", "") + local ipstack_prefer = get_server_field(sid, "ipstack_prefer", "") + local dns_mode = uci:get_first("shadowsocksr", "global", "pdnsd_enable", "0") + + local proxy = { + name = sid, + type = "tuic", + server = server, + port = server_port, + uuid = get_server_field(sid, "tuic_uuid", ""), + password = get_server_field(sid, "tuic_passwd", ""), + ["udp-relay-mode"] = get_server_field(sid, "udp_relay_mode", "native"), + ["congestion-controller"] = get_server_field(sid, "congestion_control", "cubic"), + ["skip-cert-verify"] = bool_enabled(get_server_field(sid, "insecure", "0")), + ["disable-sni"] = bool_enabled(get_server_field(sid, "disable_sni", "0")), + ["reduce-rtt"] = bool_enabled(get_server_field(sid, "zero_rtt_handshake", "0")) + } + + if tuic_ip ~= "" then + proxy.ip = tuic_ip + end + if tls_host ~= "" then + proxy.sni = tls_host + end + + local alpn = split_csv(get_server_field(sid, "tuic_alpn", "")) + if #alpn > 0 then + proxy.alpn = alpn + end + + local heartbeat = tonumber(get_server_field(sid, "heartbeat", "0")) + if heartbeat and heartbeat > 0 then + proxy["heartbeat-interval"] = heartbeat * 1000 + end + + local timeout = tonumber(get_server_field(sid, "timeout", "0")) + if timeout and timeout > 0 then + proxy["request-timeout"] = timeout * 1000 + end + + local max_udp_packet_size = tonumber(get_server_field(sid, "tuic_max_package_size", "0")) + if max_udp_packet_size and max_udp_packet_size > 0 then + proxy["max-udp-relay-packet-size"] = max_udp_packet_size + end + + if ipstack_prefer ~= "" then + proxy["ip-version"] = ipstack_prefer == "v6first" and "ipv6-prefer" or "ipv4-prefer" + end + + local listen_port = tonumber(local_port) + local socks_listen = tonumber(socks_port) + + local doc = { + ["allow-lan"] = true, + ["bind-address"] = "0.0.0.0", + mode = "rule", + ["log-level"] = "silent", + ["find-process-mode"] = "off", + ["unified-delay"] = true, + ["tcp-concurrent"] = true, + ["routing-mark"] = 255, + proxies = { proxy }, + ["proxy-groups"] = { + { + name = "PROXY", + type = "select", + proxies = { sid } + } + }, + rules = { "MATCH,PROXY" }, + tun = { enable = false }, + profile = { ["store-selected"] = true }, + dns = { + enable = dns_mode == "7", + ["enhanced-mode"] = "redir-host", + listen = "127.0.0.1:5335", + ipv6 = get_filter_aaaa() ~= "1" + } + } + + if mode == "socks" then + doc["socks-port"] = listen_port + else + doc["redir-port"] = listen_port + doc["tproxy-port"] = listen_port + if socks_listen and socks_listen > 0 then + doc["socks-port"] = socks_listen + end + end + + if doc["socks-port"] and doc["socks-port"] > 0 then + local socks5_auth = uci:get_first("shadowsocksr", "socks5_proxy", "socks5_auth", "noauth") + if socks5_auth == "password" then + local socks5_user = uci:get_first("shadowsocksr", "socks5_proxy", "socks5_user", "") + local socks5_pass = uci:get_first("shadowsocksr", "socks5_proxy", "socks5_pass", "") + + if socks5_user == "" or socks5_pass == "" then + io.stderr:write("警告:SOCKS5 代理未完整配置用户名或密码,已自动降级为无认证模式 (noauth)!\n") + else + doc["authentication"] = { + string.format("%s:%s", socks5_user, socks5_pass) + } + end + end + end + + + return doc +end + +local function generate_tuic_runtime(sid, output_path, local_port, socks_port, mode) + local doc = build_tuic_runtime_doc(sid, local_port, socks_port, mode) + local ok, rendered = pcall(lyaml.dump, { doc }) + if not ok or not rendered then + io.stderr:write("dump_failed\n") + return false + end + write_file(output_path, rendered) + return true +end local function parse_plugin_opts(value) local result = {} @@ -1011,78 +563,48 @@ local function parse_plugin_opts(value) return result end -local function parse_plugin_headers(plugin_opts) - local headers = {} - local raw_headers = pick_plugin_opt(plugin_opts, "headers", "header") - - if raw_headers and ok_jsonc and jsonc then - local decoded = jsonc.parse(raw_headers) - if type(decoded) == "table" then - for key, value in pairs(decoded) do - headers[tostring(key)] = tostring(value) - end - end +local function split_host_port(value) + local text = tostring(value or "") + if text == "" then + return "", "" end - - if raw_headers and next(headers) == nil then - for part in tostring(raw_headers):gmatch("[^|,]+") do - local key, value = part:match("^%s*([^=:]+)%s*[:=]%s*(.-)%s*$") - if key and key ~= "" and value and value ~= "" then - headers[key] = value - end - end + local host, port = text:match("^%[(.-)%]:(%d+)$") + if host and port then + return host, port end - - for key, value in pairs(plugin_opts) do - local header_name = key:match("^headers[%.:](.+)$") - or key:match("^header[%.:](.+)$") - or key:match("^header_(.+)$") - if header_name and header_name ~= "" and value ~= "" then - headers[header_name] = value - end + host, port = text:match("^(.-):(%d+)$") + if host and port then + return host, port end - - return next(headers) and headers or nil + return text, "" end -local function get_plugin_client_fingerprint(sid, plugin_opts) - return string_or_nil( - pick_plugin_opt( - plugin_opts, - "client-fingerprint", - "client_fingerprint", - "fingerprint" - ) or get_server_field(sid, "fingerprint", "") - ) +local function bool_default(value, default) + if value == nil or value == "" then + return default + end + return bool_enabled(value) end -local function normalize_plugin_name(plugin) - local value = tostring(plugin or ""):lower() - if value == "" or value == "none" then - return "" +local function number_or_nil(value) + if value == nil or value == "" then + return nil end - if value == "simple-obfs" then - return "obfs-local" - end - if value == "obfs" then - return "obfs-local" - end - if value == "shadowtls" then - return "shadow-tls" - end - if value == "gost" then - return "gost-plugin" - end - if value == "kcp-tun" then - return "kcptun" - end - return value + return tonumber(value) end -local function pick_plugin_opt(plugin_opts, ...) +local function string_or_nil(value) + if value == nil or value == "" then + return nil + end + return tostring(value) +end + +local build_shadowsocks_plugin + +local function first_nonempty(...) for i = 1, select("#", ...) do - local key = select(i, ...) - local value = plugin_opts[key] + local value = select(i, ...) if value ~= nil and value ~= "" then return value end @@ -1090,141 +612,14 @@ local function pick_plugin_opt(plugin_opts, ...) return nil end -local function build_shadowsocks_plugin(proxy, sid) - local plugin = normalize_plugin_name(get_server_field(sid, "plugin", "")) - local plugin_opts = parse_plugin_opts(get_server_field(sid, "plugin_opts", "")) - - if plugin == "" then - return - end - - if plugin == "obfs-local" then - proxy.plugin = "obfs" - proxy["plugin-opts"] = { - mode = pick_plugin_opt(plugin_opts, "obfs", "mode") or "http", - host = string_or_nil(pick_plugin_opt(plugin_opts, "obfs-host", "obfs_host", "host")) - } - return - end - - if plugin == "v2ray-plugin" or plugin == "xray-plugin" then - proxy.plugin = "v2ray-plugin" - proxy["plugin-opts"] = { - mode = pick_plugin_opt(plugin_opts, "mode") or "websocket", - tls = bool_default(pick_plugin_opt(plugin_opts, "tls"), false), - fingerprint = string_or_nil(pick_plugin_opt(plugin_opts, "fingerprint")), - ["skip-cert-verify"] = bool_default(pick_plugin_opt(plugin_opts, "skip-cert-verify", "skip_cert_verify", "insecure"), false), - host = string_or_nil(pick_plugin_opt(plugin_opts, "host")), - path = string_or_nil(pick_plugin_opt(plugin_opts, "path")), - mux = bool_default(pick_plugin_opt(plugin_opts, "mux"), false), - headers = parse_plugin_headers(plugin_opts), - ["v2ray-http-upgrade"] = bool_default(pick_plugin_opt(plugin_opts, "v2ray-http-upgrade", "v2ray_http_upgrade"), false) - } - return - end - - if plugin == "gost-plugin" then - proxy.plugin = "gost-plugin" - proxy["plugin-opts"] = { - mode = pick_plugin_opt(plugin_opts, "mode") or "websocket", - tls = bool_default(pick_plugin_opt(plugin_opts, "tls"), false), - fingerprint = string_or_nil(pick_plugin_opt(plugin_opts, "fingerprint")), - ["skip-cert-verify"] = bool_default(pick_plugin_opt(plugin_opts, "skip-cert-verify", "skip_cert_verify", "insecure"), false), - host = string_or_nil(pick_plugin_opt(plugin_opts, "host")), - path = string_or_nil(pick_plugin_opt(plugin_opts, "path")), - mux = bool_default(pick_plugin_opt(plugin_opts, "mux"), false), - headers = parse_plugin_headers(plugin_opts) - } - return - end - - if plugin == "shadow-tls" then - local host, port = split_host_port(pick_plugin_opt(plugin_opts, "host") or "") - local version - if plugin_opts.v3 == "1" or plugin_opts.version == "3" then - version = 3 - elseif plugin_opts.v2 == "1" or plugin_opts.version == "2" then - version = 2 - elseif plugin_opts.v1 == "1" or plugin_opts.version == "1" then - version = 1 - end - proxy.plugin = "shadow-tls" - proxy["client-fingerprint"] = get_plugin_client_fingerprint(sid, plugin_opts) - proxy["plugin-opts"] = { - host = host ~= "" and host or nil, - port = number_or_nil(port), - password = string_or_nil(pick_plugin_opt(plugin_opts, "passwd", "password")), - version = version - } - return - end - - if plugin == "restls" then - proxy.plugin = "restls" - proxy["client-fingerprint"] = get_plugin_client_fingerprint(sid, plugin_opts) - proxy["plugin-opts"] = { - host = string_or_nil(pick_plugin_opt(plugin_opts, "host")), - password = string_or_nil(pick_plugin_opt(plugin_opts, "passwd", "password")), - ["version-hint"] = string_or_nil(pick_plugin_opt(plugin_opts, "version-hint", "version_hint")), - ["restls-script"] = string_or_nil(pick_plugin_opt(plugin_opts, "restls-script", "restls_script")) - } - return - end - - if plugin == "kcptun" then - proxy.plugin = "kcptun" - proxy["plugin-opts"] = { - key = string_or_nil(pick_plugin_opt(plugin_opts, "key", "passwd", "password")), - crypt = string_or_nil(pick_plugin_opt(plugin_opts, "crypt")), - mode = string_or_nil(pick_plugin_opt(plugin_opts, "mode")), - conn = number_or_nil(pick_plugin_opt(plugin_opts, "conn")), - autoexpire = number_or_nil(pick_plugin_opt(plugin_opts, "autoexpire")), - scavengettl = number_or_nil(pick_plugin_opt(plugin_opts, "scavengettl")), - mtu = number_or_nil(pick_plugin_opt(plugin_opts, "mtu")), - ratelimit = number_or_nil(pick_plugin_opt(plugin_opts, "ratelimit")), - sndwnd = number_or_nil(pick_plugin_opt(plugin_opts, "sndwnd")), - rcvwnd = number_or_nil(pick_plugin_opt(plugin_opts, "rcvwnd")), - datashard = number_or_nil(pick_plugin_opt(plugin_opts, "datashard")), - parityshard = number_or_nil(pick_plugin_opt(plugin_opts, "parityshard")), - dscp = number_or_nil(pick_plugin_opt(plugin_opts, "dscp")), - nocomp = bool_default(pick_plugin_opt(plugin_opts, "nocomp"), false), - acknodelay = bool_default(pick_plugin_opt(plugin_opts, "acknodelay"), false), - nodelay = number_or_nil(pick_plugin_opt(plugin_opts, "nodelay")), - interval = number_or_nil(pick_plugin_opt(plugin_opts, "interval")), - resend = number_or_nil(pick_plugin_opt(plugin_opts, "resend")), - sockbuf = number_or_nil(pick_plugin_opt(plugin_opts, "sockbuf")), - smuxver = number_or_nil(pick_plugin_opt(plugin_opts, "smuxver")), - smuxbuf = number_or_nil(pick_plugin_opt(plugin_opts, "smuxbuf")), - framesize = number_or_nil(pick_plugin_opt(plugin_opts, "framesize")), - streambuf = number_or_nil(pick_plugin_opt(plugin_opts, "streambuf")), - keepalive = number_or_nil(pick_plugin_opt(plugin_opts, "keepalive")) - } - return - end - - proxy.plugin = plugin - if next(plugin_opts) then - proxy["plugin-opts"] = plugin_opts +local function split_alpn(value) + local items = {} + for part in tostring(value or ""):gmatch("[^,;|%s]+") do + items[#items + 1] = part end + return items end -local function build_kcptun_plugin(proxy, sid) - if not bool_enabled(get_server_field(sid, "kcp_enable", "0")) then - return - end - - proxy.plugin = "kcptun" - proxy.port = tonumber(get_server_field(sid, "kcp_port", "0")) or proxy.port - proxy["plugin-opts"] = { - key = get_server_field(sid, "kcp_password", ""), - mode = "fast", - mtu = 1350 - } -end - --- ==================== V2Ray 节点构建函数 ===================================== --- ==================== V2Ray Node Construction Functions ==================== - local function parse_wireguard_reserved(sid) local raw = get_server_field(sid, "reserved", nil) local values = {} @@ -1258,6 +653,34 @@ local function parse_wireguard_reserved(sid) return #bytes > 0 and bytes or nil end +local function split_local_addresses(value) + local items = {} + if type(value) == "table" then + for _, item in ipairs(value) do + if item and item ~= "" then + items[#items + 1] = tostring(item) + end + end + elseif value and value ~= "" then + for item in tostring(value):gmatch("[^,%s]+") do + items[#items + 1] = item + end + end + return items +end + +local function split_wireguard_addresses(value) + local ip, ipv6 + for _, item in ipairs(split_local_addresses(value)) do + if item:find(":", 1, true) then + ipv6 = ipv6 or item + else + ip = ip or item + end + end + return ip, ipv6 +end + local function apply_v2ray_tls_options(proxy, sid) local tls = get_server_field(sid, "tls", "0") local reality = get_server_field(sid, "reality", "0") @@ -1284,52 +707,30 @@ local function apply_v2ray_tls_options(proxy, sid) } end - -- ECH 配置处理 if get_server_field(sid, "enable_ech", "0") == "1" then - local raw_ech_cfg = get_server_field(sid, "ech_config", "") - local raw_domain = get_server_field(sid, "ech_domain", "") + proxy["ech-opts"] = { + enable = true, + config = string_or_nil(get_server_field(sid, "ech_config", "")) + } + end +end - local ech_base64_result = nil - local query_server_name = nil +local function apply_trojan_tls_options(proxy, sid) + proxy.sni = string_or_nil(get_server_field(sid, "tls_host", "")) + proxy["client-fingerprint"] = string_or_nil(get_server_field(sid, "fingerprint", "")) + proxy.fingerprint = string_or_nil(get_server_field(sid, "tls_CertSha", "")) + proxy["skip-cert-verify"] = bool_enabled(get_server_field(sid, "insecure", "0")) - local function is_strict_domain(str) - if not str or str == "" then return false end - return str:match("^[%w%-%_]+%.[%w%-%._]+$") ~= nil - end + local alpn = split_alpn(get_server_field(sid, "tls_alpn", "")) + if #alpn > 0 then + proxy.alpn = alpn + end - local function is_dns_input(str) - if not str or str == "" then return false end - return str:match("^https?://") ~= nil - or str:match("^tls://") ~= nil - or str:match("^tcp://") ~= nil - or str:match("^udp://") ~= nil - or str:match("^%d+%.%d+%.%d+%.%d+") ~= nil - end - - local domain_part, dns_part = raw_ech_cfg:match("^([%w%-%_]+%.[%w%-%._]+)%+(.+)$") - if domain_part and dns_part and is_dns_input(dns_part) then - ech_base64_result = string_or_nil(fetch_ech_config(domain_part, dns_part)) - query_server_name = domain_part - elseif is_dns_input(raw_ech_cfg) and is_strict_domain(raw_domain) then - ech_base64_result = string_or_nil(fetch_ech_config(raw_domain, raw_ech_cfg)) - query_server_name = raw_domain - elseif raw_ech_cfg:match("^[A-Za-z0-9%+%/%-%_%=]+$") and #raw_ech_cfg >= 32 then - ech_base64_result = raw_ech_cfg - if is_strict_domain(raw_domain) then - query_server_name = raw_domain - end - end - - if ech_base64_result then - proxy["ech-opts"] = { - enable = true, - config = ech_base64_result - } - - if query_server_name and query_server_name ~= "" then - proxy["ech-opts"]["query-server-name"] = query_server_name - end - end + if get_server_field(sid, "reality", "0") == "1" then + proxy["reality-opts"] = { + ["public-key"] = string_or_nil(get_server_field(sid, "reality_publickey", "")), + ["short-id"] = string_or_nil(get_server_field(sid, "reality_shortid", "")) + } end end @@ -1378,73 +779,6 @@ local function apply_v2ray_transport_options(proxy, sid) end end -local function apply_trojan_tls_options(proxy, sid) - proxy.sni = string_or_nil(get_server_field(sid, "tls_host", "")) - proxy["client-fingerprint"] = string_or_nil(get_server_field(sid, "fingerprint", "")) - proxy.fingerprint = string_or_nil(get_server_field(sid, "tls_CertSha", "")) - proxy["skip-cert-verify"] = bool_enabled(get_server_field(sid, "insecure", "0")) - - local alpn = split_alpn(get_server_field(sid, "tls_alpn", "")) - if #alpn > 0 then - proxy.alpn = alpn - end - - if get_server_field(sid, "reality", "0") == "1" then - proxy["reality-opts"] = { - ["public-key"] = string_or_nil(get_server_field(sid, "reality_publickey", "")), - ["short-id"] = string_or_nil(get_server_field(sid, "reality_shortid", "")) - } - end - - -- ECH 配置处理 - if get_server_field(sid, "enable_ech", "0") == "1" then - local raw_ech_cfg = get_server_field(sid, "ech_config", "") - local raw_domain = get_server_field(sid, "ech_domain", "") - - local ech_base64_result = nil - local query_server_name = nil - - local function is_strict_domain(str) - if not str or str == "" then return false end - return str:match("^[%w%-%_]+%.[%w%-%._]+$") ~= nil - end - - local function is_dns_input(str) - if not str or str == "" then return false end - return str:match("^https?://") ~= nil - or str:match("^tls://") ~= nil - or str:match("^tcp://") ~= nil - or str:match("^udp://") ~= nil - or str:match("^%d+%.%d+%.%d+%.%d+") ~= nil - end - - local domain_part, dns_part = raw_ech_cfg:match("^([%w%-%_]+%.[%w%-%._]+)%+(.+)$") - if domain_part and dns_part and is_dns_input(dns_part) then - ech_base64_result = string_or_nil(fetch_ech_config(domain_part, dns_part)) - query_server_name = domain_part - elseif is_dns_input(raw_ech_cfg) and is_strict_domain(raw_domain) then - ech_base64_result = string_or_nil(fetch_ech_config(raw_domain, raw_ech_cfg)) - query_server_name = raw_domain - elseif raw_ech_cfg:match("^[A-Za-z0-9%+%/%-%_%=]+$") and #raw_ech_cfg >= 32 then - ech_base64_result = raw_ech_cfg - if is_strict_domain(raw_domain) then - query_server_name = raw_domain - end - end - - if ech_base64_result then - proxy["ech-opts"] = { - enable = true, - config = ech_base64_result - } - - if query_server_name and query_server_name ~= "" then - proxy["ech-opts"]["query-server-name"] = query_server_name - end - end - end -end - local function can_mihomo_handle_v2ray_transport(protocol, sid) local transport = get_server_field(sid, "transport", "raw") if transport == "" or transport == "raw" or transport == "tcp" then @@ -1603,13 +937,10 @@ local function build_v2ray_mihomo_proxy(sid) return proxy end --- ==================== 节点运行时文档构建函数 ============================================== --- ==================== Node Runtime Document Construction Functions ==================== - local function build_single_proxy_runtime_doc(proxy, local_port, socks_port, mode) + local dns_mode = uci:get_first("shadowsocksr", "global", "pdnsd_enable", "0") local listen_port = tonumber(local_port) local socks_listen = tonumber(socks_port) - local doc = { ["allow-lan"] = true, ["bind-address"] = "0.0.0.0", @@ -1629,13 +960,14 @@ local function build_single_proxy_runtime_doc(proxy, local_port, socks_port, mod }, rules = { "MATCH,PROXY" }, tun = { enable = false }, - profile = { - ["store-selected"] = true, - ["store-fake-ip"] = true - }, - dns = build_dns_section(dns_mode, nil) + profile = { ["store-selected"] = true }, + dns = { + enable = dns_mode == "7", + ["enhanced-mode"] = "redir-host", + listen = "127.0.0.1:5335", + ipv6 = get_filter_aaaa() ~= "1" + } } - apply_sniffer_config(doc, enable_fake_ip) if mode == "socks" then doc["socks-port"] = listen_port @@ -1646,7 +978,6 @@ local function build_single_proxy_runtime_doc(proxy, local_port, socks_port, mod doc["socks-port"] = socks_listen end end - if doc["socks-port"] and doc["socks-port"] > 0 then local socks5_auth = uci:get_first("shadowsocksr", "socks5_proxy", "socks5_auth", "noauth") if socks5_auth == "password" then @@ -1663,126 +994,227 @@ local function build_single_proxy_runtime_doc(proxy, local_port, socks_port, mod end end + return doc end -local function build_tuic_runtime_doc(sid, local_port, socks_port, mode) - local server = get_server_field(sid, "server", "") - local server_port = tonumber(get_server_field(sid, "server_port", "0")) or 0 - local tuic_ip = get_server_field(sid, "tuic_ip", "") - local tls_host = get_server_field(sid, "tls_host", "") - local ipstack_prefer = get_server_field(sid, "ipstack_prefer", "") - - local proxy = { - name = sid, - type = "tuic", - server = server, - port = server_port, - uuid = get_server_field(sid, "tuic_uuid", ""), - password = get_server_field(sid, "tuic_passwd", ""), - ["udp-relay-mode"] = get_server_field(sid, "udp_relay_mode", "native"), - ["congestion-controller"] = get_server_field(sid, "congestion_control", "cubic"), - ["skip-cert-verify"] = bool_enabled(get_server_field(sid, "insecure", "0")), - ["disable-sni"] = bool_enabled(get_server_field(sid, "disable_sni", "0")), - ["reduce-rtt"] = bool_enabled(get_server_field(sid, "zero_rtt_handshake", "0")) - } - - if tuic_ip ~= "" then - proxy.ip = tuic_ip - end - if tls_host ~= "" then - proxy.sni = tls_host - end - - local alpn = split_csv(get_server_field(sid, "tuic_alpn", "")) - if #alpn > 0 then - proxy.alpn = alpn - end - - local heartbeat = tonumber(get_server_field(sid, "heartbeat", "0")) - if heartbeat and heartbeat > 0 then - proxy["heartbeat-interval"] = heartbeat * 1000 - end - - local timeout = tonumber(get_server_field(sid, "timeout", "0")) - if timeout and timeout > 0 then - proxy["request-timeout"] = timeout * 1000 - end - - local max_udp_packet_size = tonumber(get_server_field(sid, "tuic_max_package_size", "0")) - if max_udp_packet_size and max_udp_packet_size > 0 then - proxy["max-udp-relay-packet-size"] = max_udp_packet_size - end - - if ipstack_prefer ~= "" then - proxy["ip-version"] = ipstack_prefer == "v6first" and "ipv6-prefer" or "ipv4-prefer" - end - - local listen_port = tonumber(local_port) - local socks_listen = tonumber(socks_port) - - local doc = { - ["allow-lan"] = true, - ["bind-address"] = "0.0.0.0", - mode = "rule", - ["log-level"] = "silent", - ["find-process-mode"] = "off", - ["unified-delay"] = true, - ["tcp-concurrent"] = true, - ["routing-mark"] = 255, - proxies = { proxy }, - ["proxy-groups"] = { - { - name = "PROXY", - type = "select", - proxies = { sid } - } - }, - rules = { "MATCH,PROXY" }, - tun = { enable = false }, - profile = { - ["store-selected"] = true, - ["store-fake-ip"] = true - }, - dns = build_dns_section(dns_mode, nil) - } - apply_sniffer_config(doc, enable_fake_ip) - - if mode == "socks" then - doc["socks-port"] = listen_port - else - doc["redir-port"] = listen_port - doc["tproxy-port"] = listen_port - if socks_listen and socks_listen > 0 then - doc["socks-port"] = socks_listen +local function pick_plugin_opt(plugin_opts, ...) + for i = 1, select("#", ...) do + local key = select(i, ...) + local value = plugin_opts[key] + if value ~= nil and value ~= "" then + return value end end + return nil +end - if doc["socks-port"] and doc["socks-port"] > 0 then - local socks5_auth = uci:get_first("shadowsocksr", "socks5_proxy", "socks5_auth", "noauth") - if socks5_auth == "password" then - local socks5_user = uci:get_first("shadowsocksr", "socks5_proxy", "socks5_user", "") - local socks5_pass = uci:get_first("shadowsocksr", "socks5_proxy", "socks5_pass", "") +local function parse_plugin_headers(plugin_opts) + local headers = {} + local raw_headers = pick_plugin_opt(plugin_opts, "headers", "header") - if socks5_user == "" or socks5_pass == "" then - io.stderr:write("警告:SOCKS5 代理未完整配置用户名或密码,已自动降级为无认证模式 (noauth)!\n") - else - doc["authentication"] = { - string.format("%s:%s", socks5_user, socks5_pass) - } + if raw_headers and ok_jsonc and jsonc then + local decoded = jsonc.parse(raw_headers) + if type(decoded) == "table" then + for key, value in pairs(decoded) do + headers[tostring(key)] = tostring(value) end end end - return doc + if raw_headers and next(headers) == nil then + for part in tostring(raw_headers):gmatch("[^|,]+") do + local key, value = part:match("^%s*([^=:]+)%s*[:=]%s*(.-)%s*$") + if key and key ~= "" and value and value ~= "" then + headers[key] = value + end + end + end + + for key, value in pairs(plugin_opts) do + local header_name = key:match("^headers[%.:](.+)$") + or key:match("^header[%.:](.+)$") + or key:match("^header_(.+)$") + if header_name and header_name ~= "" and value ~= "" then + headers[header_name] = value + end + end + + return next(headers) and headers or nil +end + +local function get_plugin_client_fingerprint(sid, plugin_opts) + return string_or_nil( + pick_plugin_opt( + plugin_opts, + "client-fingerprint", + "client_fingerprint", + "fingerprint" + ) or get_server_field(sid, "fingerprint", "") + ) +end + +local function normalize_plugin_name(plugin) + local value = tostring(plugin or ""):lower() + if value == "" or value == "none" then + return "" + end + if value == "simple-obfs" then + return "obfs-local" + end + if value == "obfs" then + return "obfs-local" + end + if value == "shadowtls" then + return "shadow-tls" + end + if value == "gost" then + return "gost-plugin" + end + if value == "kcp-tun" then + return "kcptun" + end + return value +end + +function build_shadowsocks_plugin(proxy, sid) + local plugin = normalize_plugin_name(get_server_field(sid, "plugin", "")) + local plugin_opts = parse_plugin_opts(get_server_field(sid, "plugin_opts", "")) + + if plugin == "" then + return + end + + if plugin == "obfs-local" then + proxy.plugin = "obfs" + proxy["plugin-opts"] = { + mode = pick_plugin_opt(plugin_opts, "obfs", "mode") or "http", + host = string_or_nil(pick_plugin_opt(plugin_opts, "obfs-host", "obfs_host", "host")) + } + return + end + + if plugin == "v2ray-plugin" or plugin == "xray-plugin" then + proxy.plugin = "v2ray-plugin" + proxy["plugin-opts"] = { + mode = pick_plugin_opt(plugin_opts, "mode") or "websocket", + tls = bool_default(pick_plugin_opt(plugin_opts, "tls"), false), + fingerprint = string_or_nil(pick_plugin_opt(plugin_opts, "fingerprint")), + ["skip-cert-verify"] = bool_default(pick_plugin_opt(plugin_opts, "skip-cert-verify", "skip_cert_verify", "insecure"), false), + host = string_or_nil(pick_plugin_opt(plugin_opts, "host")), + path = string_or_nil(pick_plugin_opt(plugin_opts, "path")), + mux = bool_default(pick_plugin_opt(plugin_opts, "mux"), false), + headers = parse_plugin_headers(plugin_opts), + ["v2ray-http-upgrade"] = bool_default(pick_plugin_opt(plugin_opts, "v2ray-http-upgrade", "v2ray_http_upgrade"), false) + } + return + end + + if plugin == "gost-plugin" then + proxy.plugin = "gost-plugin" + proxy["plugin-opts"] = { + mode = pick_plugin_opt(plugin_opts, "mode") or "websocket", + tls = bool_default(pick_plugin_opt(plugin_opts, "tls"), false), + fingerprint = string_or_nil(pick_plugin_opt(plugin_opts, "fingerprint")), + ["skip-cert-verify"] = bool_default(pick_plugin_opt(plugin_opts, "skip-cert-verify", "skip_cert_verify", "insecure"), false), + host = string_or_nil(pick_plugin_opt(plugin_opts, "host")), + path = string_or_nil(pick_plugin_opt(plugin_opts, "path")), + mux = bool_default(pick_plugin_opt(plugin_opts, "mux"), false), + headers = parse_plugin_headers(plugin_opts) + } + return + end + + if plugin == "shadow-tls" then + local host, port = split_host_port(pick_plugin_opt(plugin_opts, "host") or "") + local version + if plugin_opts.v3 == "1" or plugin_opts.version == "3" then + version = 3 + elseif plugin_opts.v2 == "1" or plugin_opts.version == "2" then + version = 2 + elseif plugin_opts.v1 == "1" or plugin_opts.version == "1" then + version = 1 + end + proxy.plugin = "shadow-tls" + proxy["client-fingerprint"] = get_plugin_client_fingerprint(sid, plugin_opts) + proxy["plugin-opts"] = { + host = host ~= "" and host or nil, + port = number_or_nil(port), + password = string_or_nil(pick_plugin_opt(plugin_opts, "passwd", "password")), + version = version + } + return + end + + if plugin == "restls" then + proxy.plugin = "restls" + proxy["client-fingerprint"] = get_plugin_client_fingerprint(sid, plugin_opts) + proxy["plugin-opts"] = { + host = string_or_nil(pick_plugin_opt(plugin_opts, "host")), + password = string_or_nil(pick_plugin_opt(plugin_opts, "passwd", "password")), + ["version-hint"] = string_or_nil(pick_plugin_opt(plugin_opts, "version-hint", "version_hint")), + ["restls-script"] = string_or_nil(pick_plugin_opt(plugin_opts, "restls-script", "restls_script")) + } + return + end + + if plugin == "kcptun" then + proxy.plugin = "kcptun" + proxy["plugin-opts"] = { + key = string_or_nil(pick_plugin_opt(plugin_opts, "key", "passwd", "password")), + crypt = string_or_nil(pick_plugin_opt(plugin_opts, "crypt")), + mode = string_or_nil(pick_plugin_opt(plugin_opts, "mode")), + conn = number_or_nil(pick_plugin_opt(plugin_opts, "conn")), + autoexpire = number_or_nil(pick_plugin_opt(plugin_opts, "autoexpire")), + scavengettl = number_or_nil(pick_plugin_opt(plugin_opts, "scavengettl")), + mtu = number_or_nil(pick_plugin_opt(plugin_opts, "mtu")), + ratelimit = number_or_nil(pick_plugin_opt(plugin_opts, "ratelimit")), + sndwnd = number_or_nil(pick_plugin_opt(plugin_opts, "sndwnd")), + rcvwnd = number_or_nil(pick_plugin_opt(plugin_opts, "rcvwnd")), + datashard = number_or_nil(pick_plugin_opt(plugin_opts, "datashard")), + parityshard = number_or_nil(pick_plugin_opt(plugin_opts, "parityshard")), + dscp = number_or_nil(pick_plugin_opt(plugin_opts, "dscp")), + nocomp = bool_default(pick_plugin_opt(plugin_opts, "nocomp"), false), + acknodelay = bool_default(pick_plugin_opt(plugin_opts, "acknodelay"), false), + nodelay = number_or_nil(pick_plugin_opt(plugin_opts, "nodelay")), + interval = number_or_nil(pick_plugin_opt(plugin_opts, "interval")), + resend = number_or_nil(pick_plugin_opt(plugin_opts, "resend")), + sockbuf = number_or_nil(pick_plugin_opt(plugin_opts, "sockbuf")), + smuxver = number_or_nil(pick_plugin_opt(plugin_opts, "smuxver")), + smuxbuf = number_or_nil(pick_plugin_opt(plugin_opts, "smuxbuf")), + framesize = number_or_nil(pick_plugin_opt(plugin_opts, "framesize")), + streambuf = number_or_nil(pick_plugin_opt(plugin_opts, "streambuf")), + keepalive = number_or_nil(pick_plugin_opt(plugin_opts, "keepalive")) + } + return + end + + proxy.plugin = plugin + if next(plugin_opts) then + proxy["plugin-opts"] = plugin_opts + end +end + +local function build_kcptun_plugin(proxy, sid) + if not bool_enabled(get_server_field(sid, "kcp_enable", "0")) then + return + end + + proxy.plugin = "kcptun" + proxy.port = tonumber(get_server_field(sid, "kcp_port", "0")) or proxy.port + proxy["plugin-opts"] = { + key = get_server_field(sid, "kcp_password", ""), + mode = "fast", + mtu = 1350 + } end local function build_shadowsocks_runtime_doc(sid, local_port, socks_port, mode) + local dns_mode = uci:get_first("shadowsocksr", "global", "pdnsd_enable", "0") local server = get_server_field(sid, "server", "") local server_port = tonumber(get_server_field(sid, "server_port", "0")) or 0 local method = get_server_field(sid, "encrypt_method_ss", "none") local password = get_server_field(sid, "password", "") - local proxy = { name = sid, type = "ss", @@ -1820,13 +1252,14 @@ local function build_shadowsocks_runtime_doc(sid, local_port, socks_port, mode) }, rules = { "MATCH,PROXY" }, tun = { enable = false }, - profile = { - ["store-selected"] = true, - ["store-fake-ip"] = true - }, - dns = build_dns_section(dns_mode, nil) + profile = { ["store-selected"] = true }, + dns = { + enable = dns_mode == "7", + ["enhanced-mode"] = "redir-host", + listen = "127.0.0.1:5335", + ipv6 = get_filter_aaaa() ~= "1" + } } - apply_sniffer_config(doc, enable_fake_ip) local listen_port = tonumber(local_port) local socks_listen = tonumber(socks_port) @@ -1856,11 +1289,36 @@ local function build_shadowsocks_runtime_doc(sid, local_port, socks_port, mode) end end + return doc end --- ==================== 服务端文档构建函数 =========================================== --- ==================== Server Document Construction Functions ==================== +local function generate_shadowsocks_runtime(sid, output_path, local_port, socks_port, mode) + local doc = build_shadowsocks_runtime_doc(sid, local_port, socks_port, mode) + local ok, rendered = pcall(lyaml.dump, { doc }) + if not ok or not rendered then + io.stderr:write("dump_failed\n") + return false + end + write_file(output_path, rendered) + return true +end + +local function generate_v2ray_runtime(sid, output_path, local_port, socks_port, mode) + local proxy = build_v2ray_mihomo_proxy(sid) + if not proxy then + io.stderr:write("unsupported_or_invalid_v2ray_node\n") + return false + end + local doc = build_single_proxy_runtime_doc(proxy, local_port, socks_port, mode) + local ok, rendered = pcall(lyaml.dump, { doc }) + if not ok or not rendered then + io.stderr:write("dump_failed\n") + return false + end + write_file(output_path, rendered) + return true +end local function build_shadowsocks_server_doc(sid) local server_port = tonumber(get_server_field(sid, "server_port", "0")) or 0 @@ -1896,6 +1354,17 @@ local function build_shadowsocks_server_doc(sid) } end +local function generate_shadowsocks_server(sid, output_path) + local doc = build_shadowsocks_server_doc(sid) + local ok, rendered = pcall(lyaml.dump, { doc }) + if not ok or not rendered then + io.stderr:write("dump_failed\n") + return false + end + write_file(output_path, rendered) + return true +end + local function build_mihomo_listener_doc(sid) local ltype = get_server_field(sid, "type", "") local server_port = tonumber(get_server_field(sid, "server_port", "0")) or 0 @@ -1961,237 +1430,21 @@ local function build_mihomo_listener_doc(sid) } end --- ==================== 生成配置函数 ============================================ --- ==================== Configuration Generation Functions ==================== - -local function generate_tuic_runtime(sid, output_path, local_port, socks_port, mode) - local doc = build_tuic_runtime_doc(sid, local_port, socks_port, mode) - if not dump_yaml(output_path, doc) then - io.stderr:write("dump_failed\n") - return false - end - return true -end - -local function generate_shadowsocks_runtime(sid, output_path, local_port, socks_port, mode) - local doc = build_shadowsocks_runtime_doc(sid, local_port, socks_port, mode) - if not dump_yaml(output_path, doc) then - io.stderr:write("dump_failed\n") - return false - end - return true -end - -local function generate_v2ray_runtime(sid, output_path, local_port, socks_port, mode) - local proxy = build_v2ray_mihomo_proxy(sid) - if not proxy then - io.stderr:write("unsupported_or_invalid_v2ray_node\n") - return false - end - local doc = build_single_proxy_runtime_doc(proxy, local_port, socks_port, mode) - if not dump_yaml(output_path, doc) then - io.stderr:write("dump_failed\n") - return false - end - return true -end - -local function generate_shadowsocks_server(sid, output_path) - local doc = build_shadowsocks_server_doc(sid) - if not dump_yaml(output_path, doc) then - io.stderr:write("dump_failed\n") - return false - end - return true -end - local function generate_mihomo_listener(sid, output_path) local doc = build_mihomo_listener_doc(sid) if not doc then io.stderr:write("unsupported_listener\n") return false end - if not dump_yaml(output_path, doc) then + local ok, rendered = pcall(lyaml.dump, { doc }) + if not ok or not rendered then io.stderr:write("dump_failed\n") return false end + write_file(output_path, rendered) return true end --- ==================== 主要功能函数 ====================================== --- ==================== Main Functionality Functions ==================== - -local function prepare(input_path, output_path) - local doc, err = load_yaml(input_path) - if not doc then - io.stderr:write(err or "parse_failed", "\n") - return false - end - if not has_proxy_sections(doc) then - io.stderr:write("missing_proxy_sections\n") - return false - end - - local user_dns = nil - if type(doc.dns) == "table" and next(doc.dns) then - user_dns = clone_table(doc.dns) - end - - strip_runtime_conflicts(doc) - local filled_groups = fill_empty_proxy_groups(doc) - local stripped_rules = strip_incompatible_script_rules(doc) - - doc.dns = build_dns_section(dns_mode, user_dns) - - doc.rules = merge_rules_with_direct(doc.rules) - apply_sniffer_config(doc, enable_fake_ip) - - if doc["unified-delay"] == nil then - doc["unified-delay"] = true - end - if doc["tcp-concurrent"] == nil then - doc["tcp-concurrent"] = true - end - - if doc["find-process-mode"] == nil then - doc["find-process-mode"] = "off" - end - - if not dump_yaml(output_path, doc) then - io.stderr:write("dump_failed\n") - return false - end - io.stdout:write(string.format("filled_groups=%d stripped_script_rules=%d\n", filled_groups, stripped_rules)) - return true -end - -local function merge(raw_path, overlay_path, output_path) - local raw_doc, raw_err = load_yaml(raw_path) - if not raw_doc then - io.stderr:write(raw_err or "parse_failed", "\n") - return false - end - - local overlay_doc, overlay_err = load_yaml(overlay_path) - if not overlay_doc then - io.stderr:write(overlay_err or "parse_failed", "\n") - return false - end - - local user_dns = nil - if type(raw_doc.dns) == "table" and next(raw_doc.dns) then - user_dns = clone_table(raw_doc.dns) - end - - strip_runtime_conflicts(raw_doc) - local filled_groups = fill_empty_proxy_groups(raw_doc) - local stripped_rules = strip_incompatible_script_rules(raw_doc) - local merged = deep_merge(raw_doc, overlay_doc) - - local target_dns = user_dns - if not target_dns and type(merged.dns) == "table" and next(merged.dns) then - target_dns = merged.dns - end - merged.dns = build_dns_section(dns_mode, target_dns) - - merged.rules = merge_rules_with_direct(merged.rules) - apply_sniffer_config(merged, enable_fake_ip) - - if merged["unified-delay"] == nil then - merged["unified-delay"] = true - end - if merged["tcp-concurrent"] == nil then - merged["tcp-concurrent"] = true - end - - if merged["find-process-mode"] == nil then - merged["find-process-mode"] = "off" - end - - if not dump_yaml(output_path, merged) then - io.stderr:write("dump_failed\n") - return false - end - io.stdout:write(string.format("filled_groups=%d stripped_script_rules=%d\n", filled_groups, stripped_rules)) - return true -end - -local function append_client_policy_rules(runtime_path, sid) - local doc, err = load_yaml(runtime_path) - if not doc then - io.stderr:write(err or "parse_failed", "\n") - return false - end - - local valid_policies = {} - for _, proxy in ipairs(doc.proxies or {}) do - if type(proxy) == "table" and proxy.name and proxy.name ~= "" then - valid_policies[tostring(proxy.name)] = true - end - end - for _, group in ipairs(doc["proxy-groups"] or {}) do - if type(group) == "table" and group.name and group.name ~= "" then - valid_policies[tostring(group.name)] = true - end - end - - local custom_rules = {} - for _, section in ipairs(read_clash_client_rules_csv(sid)) do - if tostring(section.enabled or "0") == "1" then - local ip_addr = tostring(section.ip_addr or "") - local policy_group = tostring(section.policy_group or "") - if ip_addr ~= "" and policy_group ~= "" and valid_policies[policy_group] then - if not ip_addr:find("/", 1, true) then - ip_addr = ip_addr .. "/32" - end - custom_rules[#custom_rules + 1] = string.format("SRC-IP-CIDR,%s,%s", ip_addr, policy_group) - end - end - end - - if #custom_rules == 0 then - io.stdout:write("client_rules=0\n") - return true - end - - local existing_rules = {} - for _, rule in ipairs(doc.rules or {}) do - local text = tostring(rule or "") - if not text:match("^SRC%-IP%-CIDR,") then - table.insert(existing_rules, rule) - end - end - - local new_rules = {} - local existing_set = {} - - for _, rule in ipairs(custom_rules) do - if not existing_set[rule] then - table.insert(new_rules, rule) - existing_set[rule] = true - end - end - - for _, rule in ipairs(existing_rules) do - if not existing_set[rule] then - table.insert(new_rules, rule) - existing_set[rule] = true - end - end - - doc.rules = merge_rules_with_direct(new_rules) - - if not dump_yaml(runtime_path, doc) then - io.stderr:write("dump_failed\n") - return false - end - io.stdout:write(string.format("client_rules=%d\n", #custom_rules)) - return true -end - --- ==================== 命令行入口 ==================== --- ==================== Command-line Entry Point ==================== - local action = arg[1] if action == "validate" then os.exit(validate(arg[2]) and 0 or 1) diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua index 4a5af8641..751336403 100755 --- a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua +++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua @@ -3,7 +3,6 @@ require "luci.sys" local ucursor = require "luci.model.uci".cursor() local json = require "luci.jsonc" -local datatypes = require "luci.cbi.datatypes" -- An omitted value reaches us either as a missing argument or as an empty -- string, depending on whether the caller quoted the expansion. Empty strings @@ -182,62 +181,6 @@ local function format_host_port(host, port) return host .. ":" .. tostring(port) end -local function format_dns_server(proxy) - if not proxy or proxy == "" then - return proxy - end - - local scheme, rest = proxy:match("^([a-zA-Z0-9%+%-%.]+)://(.+)$") - if scheme then - scheme = scheme:lower() - if scheme == "tls" or scheme == "dot" then - if not rest:match(":%d+$") then - rest = rest .. ":853" - end - return "tls://" .. rest - elseif scheme == "https" or scheme == "doh" or scheme == "dohl" or scheme == "doq" or scheme == "doql" then - local host, path = rest:match("^([^/]+)(.*)$") - host = host or rest - path = path or "" - if path == "" or path == "/" then - path = "/dns-query" - end - return "https://" .. host .. path - elseif scheme == "tcp" or scheme == "udp" then - if not rest:match(":%d+$") then - rest = rest .. ":53" - end - return scheme .. "://" .. rest - else - return proxy - end - end - - local host, port = proxy:match("^([^:]+):(%d+)$") - if host and port then - if port == "853" then - return "tls://" .. host .. ":853" - elseif port == "443" then - return "https://" .. host .. "/dns-query" - elseif port == "53" then - return host -- 53 端口输出纯 IP / Host (如 8.8.4.4) - else - return "udp://" .. host .. ":" .. port - end - end - - -- 确保包含字母,防止把 IP 当成域名 - if proxy:match("[a-zA-Z]") and not proxy:match("[^%w%.%-]") then - local path = "" - if not proxy:match("/") then - path = "/dns-query" - end - return "https://" .. proxy .. path - end - - return proxy -end - -- 确保正确判断程序是否存在 local function is_finded(e) return luci.sys.exec(string.format('type -t -p "%s" -p "/usr/libexec/%s" 2>/dev/null', e, e)) ~= "" @@ -448,141 +391,31 @@ local Xray = { } if server.type == "v2ray" and dns_mode == "7" and os.getenv("SSR_SWITCH_PROBE") ~= "1" then - local raw_servers_str = builtin_dns_server or "8.8.4.4:53" + local dns_host = builtin_dns_server:match("^([^:]+)") or "8.8.4.4" + local dns_port = tonumber(builtin_dns_server:match(":(%d+)$")) or 53 - local dns_servers = {} - local host_list = {} - local port_list = {} - local first_host = nil - local first_port = nil - - -- 按逗号或空格分割多个 DNS 地址 - for raw_server in string.gmatch(raw_servers_str, "[^,%s\r\n]+") do - if raw_server and raw_server ~= "" then - -- 1. 先进行格式化处理 - local formatted = format_dns_server(raw_server) - - local host = nil - local port = nil - - -- 2. 解析 host 和对应的实际端口 - if formatted:match("^https://") then - local target = formatted:match("^https://([^/]+)") - if target:match(":%d+$") then - host, port = target:match("^([^:]+):(%d+)$") - port = tonumber(port) - else - host = target - port = 443 - end - elseif formatted:match("^tls://") then - local target = formatted:match("^tls://([^/]+)") - if target:match(":%d+$") then - host, port = target:match("^([^:]+):(%d+)$") - port = tonumber(port) - else - host = target - port = 853 - end - elseif formatted:match("^tcp://") or formatted:match("^udp://") then - local target = formatted:match("^[a-zA-Z0-9%+%-%.]+://([^/]+)") - if target:match(":%d+$") then - host, port = target:match("^([^:]+):(%d+)$") - port = tonumber(port) - else - host = target - port = 53 - end - else - if formatted:match(":%d+$") then - host, port = formatted:match("^([^:]+):(%d+)$") - port = tonumber(port) - else - host = formatted - port = 53 - end - end - - -- 保存格式化后的 Xray.dns.servers 条目 - table.insert(dns_servers, formatted) - - -- 分别收集 Host 与 Port - if host and port then - table.insert(host_list, host) - table.insert(port_list, port) - -- 保存第一个 DNS 的 host 和 port - -- if not first_host then --dokodemo-door 入站的 address 单个地址使用。 - -- first_host = host --dokodemo-door 入站的 address 单个地址使用。 - -- first_port = port --dokodemo-door 入站的 address 单个地址使用。 - -- end - end - end - end - - -- 如果没有有效的 DNS 服务器,使用默认值 - if #dns_servers == 0 then - table.insert(dns_servers, "8.8.4.4") - table.insert(host_list, "8.8.4.4") - table.insert(port_list, 53) - -- first_host = "8.8.4.4" --dokodemo-door 入站的 address 单个地址使用。 - -- first_port = 53 --dokodemo-door 入站的 address 单个地址使用。 - end - - -- 构建 dns 出站地址(支持逗号分隔的多个地址) - -- 检查所有 DNS 端口是否一致 - --local outbound_address = "" --dokodemo-door 入站的 address 单个地址使用。 - local all_same_port = true - local common_port = port_list[1] - for i = 2, #port_list do - if port_list[i] ~= common_port then - all_same_port = false - break - end - end - - -- 根据端口一致性计算 address 与 target_port - local address_str = "" - local target_port = 0 - - if all_same_port then - -- outbound_address = table.concat(host_list, ",") --dokodemo-door 入站的 address 单个地址时使用。 - address_str = table.concat(host_list, ",") - target_port = common_port - else - local combined = {} - for i = 1, #host_list do - table.insert(combined, string.format("%s:%d", host_list[i], port_list[i])) - end - -- outbound_address = table.concat(combined, ",") --dokodemo-door 入站的 address 单个地址时使用。 - address_str = table.concat(combined, ",") - target_port = 0 - end - - -- 构建 Xray.dns 配置 Xray.dns = { queryStrategy = (dns_ipv4_only == "1") and "UseIPv4" or "UseIP", - servers = dns_servers + servers = { + string.format("tcp://%s:%d", dns_host, dns_port) + } } - -- 构建 dokodemo-door 入站 table.insert(Xray.inbounds, { listen = "127.0.0.1", port = 5335, protocol = "dokodemo-door", settings = { - address = address_str, - -- address = first_host, -- 单个地址 - port = target_port, - -- port = first_port, -- 对应端口 + address = dns_host, + port = dns_port, network = "tcp,udp" }, tag = "builtin-dns-in" }) - + xray_builtin_dns = { - address = address_str, - -- address = outbound_address, --dokodemo-door 入站的 address 单个地址时使用。 - port = target_port + address = dns_host, + port = dns_port } end -- 传入连接 diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/shadowsocksr.config b/luci-app-ssr-plus/root/usr/share/shadowsocksr/shadowsocksr.config index 67c3af72a..819ce7641 100644 --- a/luci-app-ssr-plus/root/usr/share/shadowsocksr/shadowsocksr.config +++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/shadowsocksr.config @@ -27,9 +27,6 @@ config server_subscribe option auto_update_week_time '*' option auto_update_day_time '2' option auto_update_min_time '0' - option enable_mihomo '1' - option enable_fake_ip '1' - option sub_convert '1' option url_test_url 'https://www.google.com/generate_204' option user_agent 'v2rayN/9.99' option filter_words '过期/重置/套餐/剩余/网址/QQ群/官网/防失联/回国' diff --git a/luci-app-wwand/Makefile b/luci-app-wwand/Makefile index 346da6bae..c41b7e2e7 100644 --- a/luci-app-wwand/Makefile +++ b/luci-app-wwand/Makefile @@ -17,12 +17,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-wwand -PKG_VERSION:=1.6.8_p1 -PKG_RELEASE:=29 +PKG_VERSION:=1.6.8_p4 +PKG_RELEASE:=30 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://github.com/ddimension/luci-app-wwand.git -PKG_SOURCE_VERSION:=f4e1c02e71936eb67981734f3a97a710f0aab264 +PKG_SOURCE_VERSION:=3d7585f3afe7eca05d55a179f4acc56653d8eac3 PKG_MIRROR_HASH:=skip PKG_LICENSE:=GPL-2.0-only diff --git a/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js b/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js index 01e40f851..48bb46dec 100644 --- a/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js +++ b/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js @@ -360,6 +360,7 @@ return baseclass.extend({ visible: true, value: band }, + hostname : { title: _('Hostname'), visible: true, diff --git a/luci-theme-footstrap/htdocs/luci-static/resources/fs-appearance.js b/luci-theme-footstrap/htdocs/luci-static/resources/fs-appearance.js index 9d9dc8a5a..601e28c16 100644 --- a/luci-theme-footstrap/htdocs/luci-static/resources/fs-appearance.js +++ b/luci-theme-footstrap/htdocs/luci-static/resources/fs-appearance.js @@ -56,7 +56,10 @@ function probeColor(expr) { * shared with `luci-app-*`, and an app's unlayered `span { color: … !important }` outranks * a layer and a plain inline style alike. A probe that loses its own colour reports the * app's, which then becomes the admin's saved axis on the next confirm. */ - _probe = E('span', { 'aria-hidden': 'true' }); + /* the id is this probe's escape from fs-router.js's strayBodyNode() (issue #56): it is + * appended once and never removed, so without an fs-* id it would read as body litter and + * force every navigation away from this page into a full load. */ + _probe = E('span', { 'id': 'fs-appearance-probe', 'aria-hidden': 'true' }); _probe.style.cssText = 'position:fixed!important;left:-9999px!important;top:0!important;' + 'width:0!important;height:0!important;overflow:hidden!important;' + 'pointer-events:none!important;'; diff --git a/luci-theme-footstrap/htdocs/luci-static/resources/fs-chrome.js b/luci-theme-footstrap/htdocs/luci-static/resources/fs-chrome.js index 53e580447..f94292f37 100644 --- a/luci-theme-footstrap/htdocs/luci-static/resources/fs-chrome.js +++ b/luci-theme-footstrap/htdocs/luci-static/resources/fs-chrome.js @@ -110,6 +110,12 @@ let _probe = null; function resolveLen(token, dflt) { if (!_probe) { _probe = document.createElement('div'); + /* the id is fs-router.js's strayBodyNode() (issue #56) reading an fs-* id as "ours", which is + * a different question from the chrome mark above: this stays unmarked (no data-fs-chrome, + * so the fence still does not spare it from a hostile !important) but still needs SOME fs-* + * name, or an element appended once and never removed would read as body litter and force + * every navigation off this page into a full load. */ + _probe.id = 'fs-chrome-geom-probe'; _probe.setAttribute('aria-hidden', 'true'); /* out of flow, no box, no ink: it must never affect layout, scroll extent or hit-testing */ _probe.style.cssText = 'position:absolute!important;visibility:hidden!important;' + diff --git a/luci-theme-footstrap/htdocs/luci-static/resources/fs-router.js b/luci-theme-footstrap/htdocs/luci-static/resources/fs-router.js index c68f02e9a..d65f2bcde 100644 --- a/luci-theme-footstrap/htdocs/luci-static/resources/fs-router.js +++ b/luci-theme-footstrap/htdocs/luci-static/resources/fs-router.js @@ -338,6 +338,66 @@ function discard(el) { } } +/* ---- a page that leaves nodes as direct children of hands the next page over by full load, + * the same way an invasive foreign sheet does (docs/third-party-apps.md, Rule 2 extended from CSS to + * the DOM — issue #56) ---- + * + * luci-app-bandix's render() appends 7 tooltip/modal nodes to document.body on every visit, + * unconditionally (reproduced on owrt2512 and owrt2410) — nothing here sweeps them; the reasoning is + * docs/spa-router.md, "Foreign view DOM". The recorder is header.ut's first {# The wallpaper pattern's paint surface: 15-wallpaper.css masks this element so an uploaded SVG takes the theme's ink. A mask applies to an element's whole rendering, diff --git a/mise/Makefile b/mise/Makefile index 876ae1132..6e351759f 100644 --- a/mise/Makefile +++ b/mise/Makefile @@ -14,8 +14,8 @@ MISE_HASH_x86_64:=3832f39c325e343f81fe3d92b2447c5d1a5eea1bc85092bb7b6c2580622264 MISE_HASH_aarch64:=06186cfbfe947049b21d58575fb0ea800cc26ed1375f20f4b678cb3a9d679437 PKG_NAME:=mise -PKG_VERSION:=2026.9.13 -PKG_RELEASE:=20 +PKG_VERSION:=2026.9.14 +PKG_RELEASE:=21 PKG_SOURCE:=$(PKG_NAME)-v$(PKG_VERSION)-linux-$(MISE_ARCH)-musl.tar.gz PKG_SOURCE_URL:=https://github.com/jdx/mise/releases/download/v$(PKG_VERSION)/ PKG_HASH:=skip diff --git a/package_kernel_tcp-brutal/Makefile b/package_kernel_tcp-brutal/Makefile index 27951259f..c50b46cdd 100644 --- a/package_kernel_tcp-brutal/Makefile +++ b/package_kernel_tcp-brutal/Makefile @@ -2,8 +2,8 @@ include $(TOPDIR)/rules.mk include $(INCLUDE_DIR)/kernel.mk PKG_NAME:=tcp-brutal -PKG_VERSION:=2.0.0 -PKG_RELEASE:=2 +PKG_VERSION:=2.0.1 +PKG_RELEASE:=3 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/apernet/tcp-brutal/tar.gz/v$(PKG_VERSION)? diff --git a/wwand-ipa/Makefile b/wwand-ipa/Makefile new file mode 100644 index 000000000..80749f6c5 --- /dev/null +++ b/wwand-ipa/Makefile @@ -0,0 +1,102 @@ +# +# eSIM fleet management for wwand: the router as the SGP.32 IoT Profile +# Assistant of an eIM. A wwand PLUGIN (wwand plugins.uc), kept out of the +# wwand sources on purpose: it is not meant for upstream. +# +# Sources live in https://github.com/ddimension/wwand-ipa, tagged vX.Y.Z. Pin +# a new commit with `scripts/bump-source.sh wwand-ipa `. +# +# Built from a git PKG_SOURCE, so, like luci-app-wwand, installed explicitly +# from $(PKG_BUILD_DIR) and not through luci.mk. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=wwand-ipa +PKG_VERSION:=0.1.0 +PKG_RELEASE:=1 + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/ddimension/wwand-ipa.git +PKG_SOURCE_VERSION:=fc0fad051213f23d10864f26ebc7205ae5c0a451 +PKG_MIRROR_HASH:=skip + +PKG_LICENSE:=GPL-2.0-only +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:= + +include $(INCLUDE_DIR)/package.mk + +UCDIR:=/usr/share/ucode/wwand + +define Package/wwand-ipa + SECTION:=net + CATEGORY:=Network + SUBMENU:=WWAN + TITLE:=eSIM fleet management (SGP.32 eIM) for wwand + # wwand-esim: its bridge carries the assistant's APDUs; wwand-ipad: the + # assistant itself (onomondo-ipa with the stdio card backend) + DEPENDS:=+wwand-esim +wwand-ipad + PKGARCH:=all +endef + +define Package/wwand-ipa/description + Lets an eIM manage the modem's eSIM: wwand polls it (after the connection + comes up, then every ipa_interval seconds) with the onomondo-ipa assistant, + relays the assistant's card traffic over the modem's APDU channel, and after + a profile change resets the SIM (or the modem) and waits for the session on + the new profile before the result is reported. Runs against an SGP.22 + consumer eUICC (the assistant's IoT eUICC emulation); the eIM must accept + that mode. Enable per modem with `wwandctl ipa [modem] eim `. While + enabled, manual profile changes on that card are refused unless forced. +endef + +define Package/luci-app-wwand-ipa + SECTION:=luci + CATEGORY:=LuCI + SUBMENU:=3. Applications + TITLE:=eSIM fleet management page for wwand-ipa + DEPENDS:=+luci-base +wwand-ipa + PKGARCH:=all +endef + +define Package/luci-app-wwand-ipa/description + Network -> eSIM Fleet: per modem, where the eIM polling stands, what the + last run did to the card, and a poll on request. +endef + +define Build/Compile +endef + +# /etc/wwand/ipa holds each card's nvstate (in emulation: its eIM +# configuration and replay counter) and usually the eIM configuration file. +# Losing it in a sysupgrade cuts the card off its eIM, so it is kept through +# keep.d, NOT conffiles: sysupgrade treats conffiles as file+checksum pairs of +# what the package shipped, and these files are created at runtime; keep.d +# entries go through find, which takes the whole directory (base-files +# sbin/sysupgrade list_conffiles / list_static_conffiles, OpenWrt bacda03b76, +# 2026-07-19). +define Package/wwand-ipa/install + $(INSTALL_DIR) $(1)$(UCDIR)/plugins $(1)$(UCDIR)/ctl + $(INSTALL_DATA) $(PKG_BUILD_DIR)/plugins/ipa.uc $(1)$(UCDIR)/plugins/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/ctl/ipa.uc $(1)$(UCDIR)/ctl/ + $(INSTALL_DIR) $(1)/lib/upgrade/keep.d + echo /etc/wwand/ipa/ > $(1)/lib/upgrade/keep.d/wwand-ipa +endef + +# named, not globbed (the rule luci-app-wwand follows) +define Package/luci-app-wwand-ipa/install + $(INSTALL_DIR) $(1)/www/luci-static/resources/view/wwand $(1)/www/luci-static/resources/wwand + $(INSTALL_DATA) $(PKG_BUILD_DIR)/luci/htdocs/luci-static/resources/view/wwand/ipa.js \ + $(1)/www/luci-static/resources/view/wwand/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/luci/htdocs/luci-static/resources/wwand/ipafmt.js \ + $(1)/www/luci-static/resources/wwand/ + $(INSTALL_DIR) $(1)/usr/share/luci/menu.d $(1)/usr/share/rpcd/acl.d + $(INSTALL_DATA) $(PKG_BUILD_DIR)/luci/root/usr/share/luci/menu.d/luci-app-wwand-ipa.json \ + $(1)/usr/share/luci/menu.d/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/luci/root/usr/share/rpcd/acl.d/luci-app-wwand-ipa.json \ + $(1)/usr/share/rpcd/acl.d/ +endef + +$(eval $(call BuildPackage,wwand-ipa)) +$(eval $(call BuildPackage,luci-app-wwand-ipa)) diff --git a/wwand-ipad/Makefile b/wwand-ipad/Makefile new file mode 100644 index 000000000..c7191dc70 --- /dev/null +++ b/wwand-ipad/Makefile @@ -0,0 +1,154 @@ +# onomondo-ipa, the GSMA SGP.32 IoT Profile Assistant, built for wwand-ipa. +# +# The assistant polls an eIM (the fleet server) for eUICC packages and runs +# them on the card. Upstream reaches the card through PC/SC; patch 100 adds a +# card backend that speaks lpac's stdio APDU protocol instead, so wwand's +# esim_bridge relays its APDUs over the modem's own channel (QMI UIM / MBIM +# UICC / AT) exactly as it does for lpac, and adds -H: a profile change is +# handed to the host, which resets the SIM and waits for the new session. The +# patch carries a test for the card backend (tests/scard_stdio). The package +# build does not run it (BUILD_TESTING=OFF, target ipa only); on a host: +# cmake -S . -B build -DIPA_SCARD_STDIO=ON && cmake --build build && ctest --test-dir build +# The -H wiring in main.c has no test of its own: exercising it takes an eIM +# that sends a profile change. +# +# TLS: the same minimal static wolfSSL + libcurl bundle as wwand-lpac, for the +# same reason (OpenWrt's mbedtls lacks curves the GSMA PKI uses, and +# libcurl+OpenSSL is ~6.4 MB). The eIM is reached over HTTPS with the system +# CA bundle unless the eIM configuration says otherwise. +# +# Upstream implements SGP.32 v1.0; wwand always runs it in IoT eUICC emulation +# (-E), i.e. against an SGP.22 consumer eUICC. See wwand's src-ucode/ipa.uc. + +include $(TOPDIR)/rules.mk + +PKG_NAME:=wwand-ipad +PKG_VERSION:=0.0.1_git20260901 +PKG_RELEASE:=1 + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/onomondo/onomondo-ipa.git +PKG_SOURCE_VERSION:=6aaeb38a569e648c8305aa5bd4d300cc1ea6822a +PKG_MIRROR_HASH:=skip + +PKG_LICENSE:=AGPL-3.0-only curl GPL-3.0-or-later +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:= + +# bundled, statically-linked dependencies (keep in step with wwand-lpac) +WOLFSSL_VERSION:=5.9.2-stable +CURL_VERSION:=8.21.0 + +PKG_BUILD_PARALLEL:=1 + +# wolfSSL's SP math carries inline assembly that MIPS16 cannot encode +PKG_BUILD_FLAGS:=no-mips16 + +include $(INCLUDE_DIR)/package.mk + +define Download/wolfssl + FILE:=wolfssl-$(WOLFSSL_VERSION).tar.gz + URL:=https://github.com/wolfSSL/wolfssl/archive/v$(WOLFSSL_VERSION) + HASH:=skip +endef +$(eval $(call Download,wolfssl)) + +define Download/curl + FILE:=curl-$(CURL_VERSION).tar.xz + URL:=https://github.com/curl/curl/releases/download/curl-8_21_0 + HASH:=skip +endef +$(eval $(call Download,curl)) + +define Package/wwand-ipad + SECTION:=net + CATEGORY:=Network + SUBMENU:=WWAN + TITLE:=SGP.32 IoT Profile Assistant (onomondo-ipa) for wwand-ipa + URL:=https://github.com/onomondo/onomondo-ipa + # the eIM is an HTTPS server with an ordinary certificate chain + DEPENDS:=+ca-bundle +endef + +define Package/wwand-ipad/description + onomondo-ipa, the GSMA SGP.32 IoT Profile Assistant, built with a card + backend that reaches the eUICC through wwand (lpac's stdio APDU protocol) + instead of PC/SC, and with a minimal static wolfSSL + libcurl linked in. + Installed as /usr/lib/wwand/ipad; wwand-ipa runs it. Implements SGP.32 v1.0 + and is run in IoT eUICC emulation against an SGP.22 consumer eUICC. +endef + +PREFIX:=$(PKG_BUILD_DIR)/.bundle +WOLFSSL_DIR:=$(PKG_BUILD_DIR)/wolfssl-$(WOLFSSL_VERSION) +CURL_DIR:=$(PKG_BUILD_DIR)/curl-$(CURL_VERSION) +IPA_BUILD:=$(PKG_BUILD_DIR)/.ipa-build + +LTO_CFLAGS:=-flto=auto -ffat-lto-objects +BUNDLE_AR:=AR="$(TARGET_CROSS)gcc-ar" RANLIB="$(TARGET_CROSS)gcc-ranlib" + +# unpack + patch the assistant, then the bundled wolfSSL + curl beside it +define Build/Prepare + $(call Build/Prepare/Default) + $(TAR) -C $(PKG_BUILD_DIR) -xzf $(DL_DIR)/wolfssl-$(WOLFSSL_VERSION).tar.gz + $(TAR) -C $(PKG_BUILD_DIR) -xJf $(DL_DIR)/curl-$(CURL_VERSION).tar.xz +endef + +Build/Configure:= + +define Build/Compile + # 1) minimal static wolfSSL (the wwand-lpac recipe) + ( cd $(WOLFSSL_DIR) && ./autogen.sh ) + ( cd $(WOLFSSL_DIR) && ./configure --host=$(GNU_TARGET_NAME) --prefix=$(PREFIX) \ + --enable-static --disable-shared \ + --enable-curl --enable-tls13 --enable-brainpool --enable-ecccustcurves \ + --enable-curve25519 --disable-dtls --disable-oldtls \ + --disable-examples --disable-crypttests \ + CC="$(TARGET_CC)" $(BUNDLE_AR) \ + CFLAGS="$(TARGET_CFLAGS) $(LTO_CFLAGS) -ffunction-sections -fdata-sections -DWOLFSSL_NO_ASN_STRICT" ) + +$(MAKE) -C $(WOLFSSL_DIR) + $(MAKE) -C $(WOLFSSL_DIR) install + + # 2) minimal static libcurl (HTTP/HTTPS only) against it. HTTP stays: an + # eIM configuration may name a plain-HTTP lab eIM (the assistant's -S). + ( cd $(CURL_DIR) && \ + PKG_CONFIG_PATH="$(PREFIX)/lib/pkgconfig" PKG_CONFIG_LIBDIR="$(PREFIX)/lib/pkgconfig" \ + ./configure --host=$(GNU_TARGET_NAME) --prefix=$(PREFIX) --with-wolfssl=$(PREFIX) \ + --enable-static --disable-shared \ + --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp \ + --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap \ + --disable-smtp --disable-gopher --disable-mqtt --disable-smb --disable-ntlm \ + --disable-manual --disable-unix-sockets --disable-progress-meter \ + --without-libpsl --without-zlib --without-nghttp2 --without-brotli \ + --without-zstd --without-libidn2 --without-librtmp --without-ca-path \ + --with-ca-bundle=/etc/ssl/certs/ca-certificates.crt \ + CC="$(TARGET_CC)" $(BUNDLE_AR) \ + CFLAGS="$(TARGET_CFLAGS) $(LTO_CFLAGS) -ffunction-sections -fdata-sections -DSIZEOF_LONG_LONG=8" \ + CPPFLAGS="-I$(PREFIX)/include" LDFLAGS="-L$(PREFIX)/lib" LIBS="-lm" ) + +$(MAKE) -C $(CURL_DIR) + $(MAKE) -C $(CURL_DIR) install + + # 3) the assistant with the stdio card backend, statically against both. + # AR/RANLIB go in as full paths: CMake took the bare name as a path relative + # to its build directory and wrote `.ipa-build/aarch64-openwrt-linux-musl-gcc-ar` + # into libasn's link.txt, which failed with "Error running link command: no + # such file or directory" (SDK snapshot, aarch64, 2026-09-26). + rm -rf $(IPA_BUILD) + mkdir -p $(IPA_BUILD) + ( cd $(IPA_BUILD) && cmake $(PKG_BUILD_DIR) \ + -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=$(ARCH) \ + -DCMAKE_C_COMPILER="$(TARGET_CROSS)gcc" \ + -DCMAKE_AR="$(TOOLCHAIN_DIR)/bin/$(TARGET_CROSS)gcc-ar" \ + -DCMAKE_RANLIB="$(TOOLCHAIN_DIR)/bin/$(TARGET_CROSS)gcc-ranlib" \ + -DCMAKE_C_FLAGS="$(TARGET_CFLAGS) $(LTO_CFLAGS) -ffunction-sections -fdata-sections -I$(PREFIX)/include" \ + -DCMAKE_EXE_LINKER_FLAGS="$(TARGET_LDFLAGS) $(LTO_CFLAGS) -Wl,--gc-sections -L$(PREFIX)/lib" \ + -DCMAKE_C_STANDARD_LIBRARIES="-L$(PREFIX)/lib -lwolfssl -lm" \ + -DIPA_SCARD_STDIO=ON -DBUILD_TESTING=OFF ) + +$(MAKE) -C $(IPA_BUILD) ipa +endef + +define Package/wwand-ipad/install + $(INSTALL_DIR) $(1)/usr/lib/wwand + $(INSTALL_BIN) $(IPA_BUILD)/src/ipa/ipa $(1)/usr/lib/wwand/ipad +endef + +$(eval $(call BuildPackage,wwand-ipad)) diff --git a/wwand-ipad/patches/100-scard-stdio.patch b/wwand-ipad/patches/100-scard-stdio.patch new file mode 100644 index 000000000..36c2e958d --- /dev/null +++ b/wwand-ipad/patches/100-scard-stdio.patch @@ -0,0 +1,833 @@ +diff --git a/include/onomondo/ipa/scard_stdio.h b/include/onomondo/ipa/scard_stdio.h +new file mode 100644 +index 0000000..fa9ec54 +--- /dev/null ++++ b/include/onomondo/ipa/scard_stdio.h +@@ -0,0 +1,13 @@ ++/* ++ * Copyright (c) 2026 André Valentin ++ * ++ * SPDX-License-Identifier: AGPL-3.0-only ++ */ ++ ++#pragma once ++ ++/*! Hand a profile change to the host and wait until it has restored the data ++ * connection, then re-open the card channel the host's SIM reset closed. ++ * \returns 1 when the host reports the connection back, 0 when it gave up ++ * waiting, negative when the host is gone. */ ++int ipa_scard_stdio_await_online(void); +diff --git a/src/ipa/CMakeLists.txt b/src/ipa/CMakeLists.txt +index 2cf4a2b..b4ee0e5 100644 +--- a/src/ipa/CMakeLists.txt ++++ b/src/ipa/CMakeLists.txt +@@ -5,7 +5,15 @@ add_subdirectory(libasn) + add_subdirectory(libipa) + + add_library(http STATIC http.c) +-add_library(scard STATIC scard.c) ++option(IPA_SCARD_STDIO "Reach the eUICC through a host process (lpac stdio protocol) instead of PC/SC" OFF) ++if (IPA_SCARD_STDIO) ++ add_library(scard STATIC scard_stdio.c) ++ target_compile_definitions(scard PUBLIC IPA_SCARD_STDIO) ++ set(IPA_SCARD_LIBS "") ++else() ++ add_library(scard STATIC scard.c) ++ set(IPA_SCARD_LIBS pcsclite) ++endif() + + target_include_directories(http PUBLIC ${CMAKE_SOURCE_DIR}/include) + target_include_directories(scard PUBLIC ${CMAKE_SOURCE_DIR}/include) +@@ -16,7 +24,7 @@ set_property(TARGET ipa PROPERTY C_STANDARD 99) + target_compile_options(ipa PRIVATE -Wall) + + target_include_directories(ipa PUBLIC ${CMAKE_SOURCE_DIR}/include) +-target_link_libraries(ipa libipa http scard pcsclite curl) ++target_link_libraries(ipa libipa http scard ${IPA_SCARD_LIBS} curl) + + if (M32) + set_target_properties(http PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") +diff --git a/src/ipa/main.c b/src/ipa/main.c +index d4316f2..5dfa703 100644 +--- a/src/ipa/main.c ++++ b/src/ipa/main.c +@@ -17,6 +17,9 @@ + #include + #include + #include ++#ifdef IPA_SCARD_STDIO ++#include ++#endif + + #define DEFAULT_READER_NUMBER 0 + #define DEFAULT_CHANNEL_NUMBER 1 +@@ -58,6 +61,9 @@ static void print_help(void) + printf(" -I .................. disable SSL certificate verification (insecure)\n"); + printf(" -E .................. emulate IoT eUICC (compatibility mode to use consumer eUICCs)\n"); + printf(" -1 .................. force the IPAd to process only one eUICC package (debug, use with caution)\n"); ++#ifdef IPA_SCARD_STDIO ++ printf(" -H .................. hand profile changes to the host and wait for it to restore connectivity\n"); ++#endif + } + + struct ipa_buf *load_ber_from_file(char *dir, char *file) +@@ -134,6 +140,22 @@ static void sig_usr1(int signum) + running = false; + } + ++#ifdef IPA_SCARD_STDIO ++/* The host ends a run it has given up on with SIGTERM. Without a handler that ++ * kills the process before ipa_free_ctx() hands back the nvstate, and in IoT ++ * eUICC emulation the nvstate IS the eIM configuration and the replay counter. ++ * No SA_RESTART: a read blocked on the host must return, so the card layer ++ * fails the current exchange and main() reaches the save below. */ ++static void install_sigterm(void) ++{ ++ struct sigaction sa = { 0 }; ++ ++ sa.sa_handler = sig_usr1; ++ sigemptyset(&sa.sa_mask); ++ sigaction(SIGTERM, &sa, NULL); ++} ++#endif ++ + int main(int argc, char **argv) + { + struct ipa_config cfg = { 0 }; +@@ -146,8 +168,12 @@ int main(int argc, char **argv) + struct ipa_buf *nvstate_load = NULL; + struct ipa_buf *nvstate_save = NULL; + bool getopt_one_euicc_pkg_only = false; ++ bool getopt_host_online = false; + + signal(SIGUSR1, sig_usr1); ++#ifdef IPA_SCARD_STDIO ++ install_sigterm(); ++#endif + + printf("IPAd!\n"); + +@@ -159,7 +185,7 @@ int main(int argc, char **argv) + + /* Overwrite configuration values with user defined parameters */ + while (1) { +- opt = getopt(argc, argv, "ht:e:r:c:f:mn:C:SIEy:a1"); ++ opt = getopt(argc, argv, "ht:e:r:c:f:mn:C:SIEy:a1H"); + if (opt == -1) + break; + +@@ -210,6 +236,9 @@ int main(int argc, char **argv) + case '1': + getopt_one_euicc_pkg_only = true; + break; ++ case 'H': ++ getopt_host_online = true; ++ break; + default: + printf("unhandled option: %c!\n", opt); + break; +@@ -284,6 +313,19 @@ int main(int argc, char **argv) + case IPA_POLL_AGAIN_WHEN_ONLINE: + /* ipa_poll asks us to wait with the next poll cycle until we have a stable IP + * connection. In this example we assume that IP connectivity is always available. */ ++#ifdef IPA_SCARD_STDIO ++ /* On a router it is not: the modem keeps the old profile until the host resets ++ * the SIM, and the data session has to come back on the new one. The process ++ * stays alive meanwhile, because the pending result (and a rollback, should the ++ * eIM stay unreachable) lives in the context, not in the nvstate. Whatever the ++ * host answers, the next ipa_poll is the one that decides: it reports to the ++ * eIM, or rolls the profile back when it cannot. */ ++ if (getopt_host_online && ipa_scard_stdio_await_online() < 0) { ++ IPA_LOGP(SMAIN, LERROR, "host went away during a profile change\n"); ++ rc = -EIO; ++ goto leave; ++ } ++#endif + IPA_LOGP(SMAIN, LINFO, "poll cycle continues normally (profile change)\n"); + rc = 0; + break; +diff --git a/src/ipa/scard_stdio.c b/src/ipa/scard_stdio.c +new file mode 100644 +index 0000000..5b58991 +--- /dev/null ++++ b/src/ipa/scard_stdio.c +@@ -0,0 +1,513 @@ ++/* ++ * Copyright (c) 2026 André Valentin ++ * ++ * SPDX-License-Identifier: AGPL-3.0-only ++ * ++ * Smartcard backend for onomondo-ipa that reaches the eUICC through a HOST ++ * PROCESS instead of PC/SC: every card operation is one JSON line on stdout, ++ * and the answer is one JSON line on stdin. The wire format is lpac's stdio ++ * APDU driver (lpac driver/apdu/stdio.c, v2.3.0) byte for byte, so the host ++ * side that already bridges lpac -- wwand's esim_bridge, which answers from ++ * the modem's own APDU channel (QMI UIM / MBIM UICC / AT) -- bridges this too: ++ * ++ * -> {"type":"apdu","payload":{"func":"logic_channel_open","param":""}} ++ * <- {"type":"apdu","payload":{"ecode":}} ++ * -> {"type":"apdu","payload":{"func":"transmit","param":""}} ++ * <- {"type":"apdu","payload":{"ecode":0,"data":""}} ++ * ++ * The two sides do not speak the same card dialect, and this file is where ++ * that is reconciled: ++ * ++ * - libipa talks raw T=0 (libipa/euicc.c): TERMINAL CAPABILITY on the basic ++ * channel, MANAGE CHANNEL to open one, SELECT ISD-R on it and then STORE ++ * DATA / GET RESPONSE with the channel number in CLA. A modem's APDU ++ * service does not let the host do any of that directly: QMI UIM opens a ++ * logical channel BY AID (OPEN_LOGICAL_CHANNEL, which selects it in the ++ * same step) and addresses the channel by id. So MANAGE CHANNEL is ++ * answered here, the channel is really opened when the SELECT names the ++ * AID, and CLA is rewritten to the channel the modem handed out. ++ * - The modem completes GET RESPONSE itself and returns the whole answer with ++ * 9000 (the QMI UIM long-response path in wwand's sim.uc reassembles even ++ * the chunked ones). libipa insists on seeing 61xx after the last STORE ++ * DATA block and fetching with GET RESPONSE, and it rejects a block whose ++ * length differs from what 61xx announced (euicc.c recv_es10x_block). So a ++ * response with data is held here and handed out again in 61xx/GET ++ * RESPONSE portions of exactly the announced size. ++ * ++ * Logging goes to stderr (libipa's log.c already writes there), so stdout ++ * carries only the protocol. A host may still merge the two, as wwand does ++ * (2>&1, into its syslog): every log line starts with the space-padded ++ * subsystem ("%8s %8s ", log.c:69), never with the '{' a protocol line starts ++ * with, so the two stay apart. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ES10 answers (a profile list, certificates) reach several kB; one line ++ * carries the hex of the largest plus the JSON around it */ ++#define RX_MAX_LEN 32768 ++#define LINE_MAX_LEN (2 * RX_MAX_LEN + 128) ++#define AID_MAX_LEN 16 ++ ++struct scard_ctx { ++ /* the AID libipa selected, kept so the channel can be re-opened after ++ * the host has reset the card (see ipa_scard_stdio_await_online) */ ++ uint8_t aid[AID_MAX_LEN]; ++ size_t aid_len; ++ ++ /* the channel the modem handed out; -1 while none is open */ ++ int channel; ++ ++ /* a card answer being handed out in GET RESPONSE portions */ ++ uint8_t *pending; ++ size_t pending_len; ++ size_t pending_off; ++}; ++ ++/* One process drives one card, and the connectivity hook below is called from ++ * main.c, which only holds libipa's opaque context. */ ++static struct scard_ctx *g_ctx; ++ ++/* Set once a read from the host failed (EOF, or a SIGTERM interrupting it). ++ * Every later exchange fails at once instead of blocking again: the shutdown ++ * path still closes the channel and disconnects, and waiting on a host that ++ * has gone would keep main() from ever saving the nvstate. */ ++static bool host_gone; ++ ++static const char hexdigits[] = "0123456789ABCDEF"; ++ ++static char *to_hex(const uint8_t *data, size_t len) ++{ ++ char *s = malloc(len * 2 + 1); ++ size_t i; ++ ++ assert(s); ++ for (i = 0; i < len; i++) { ++ s[i * 2] = hexdigits[data[i] >> 4]; ++ s[i * 2 + 1] = hexdigits[data[i] & 0x0F]; ++ } ++ s[len * 2] = '\0'; ++ return s; ++} ++ ++static int hexval(char c) ++{ ++ if (c >= '0' && c <= '9') ++ return c - '0'; ++ if (c >= 'a' && c <= 'f') ++ return c - 'a' + 10; ++ if (c >= 'A' && c <= 'F') ++ return c - 'A' + 10; ++ return -1; ++} ++ ++/* Read one line from stdin. Returns false on EOF: the host is gone, and every ++ * caller turns that into a card error rather than waiting forever. */ ++static bool read_line(char *buf, size_t size) ++{ ++ size_t n; ++ ++ if (host_gone) ++ return false; ++ ++ if (!fgets(buf, size, stdin)) { ++ host_gone = true; ++ return false; ++ } ++ ++ n = strlen(buf); ++ ++ /* A line longer than the buffer: the rest would be read as the NEXT ++ * answer and every later exchange would be off by one. Drain it and ++ * report the round as failed instead. */ ++ if (n == size - 1 && buf[n - 1] != '\n') { ++ int c; ++ ++ while ((c = getchar()) != EOF && c != '\n') ++ ; ++ buf[0] = '\0'; ++ IPA_LOGP(SSCARD, LERROR, "stdio: answer longer than %zu bytes dropped\n", size); ++ return true; ++ } ++ while (n && (buf[n - 1] == '\n' || buf[n - 1] == '\r')) ++ buf[--n] = '\0'; ++ ++ return true; ++} ++ ++/* The host's answers are produced by wwand (a fixed sprintf) or lpac-compatible ++ * bridges, so a field lookup is enough; a full JSON parser would be the only ++ * dependency this binary had besides libcurl. */ ++static const char *field(const char *line, const char *name) ++{ ++ char key[32]; ++ const char *p; ++ ++ snprintf(key, sizeof(key), "\"%s\"", name); ++ p = strstr(line, key); ++ if (!p) ++ return NULL; ++ p += strlen(key); ++ while (*p == ' ' || *p == ':') ++ p++; ++ return p; ++} ++ ++/* One request/answer round. `data` receives the decoded "data" field (may be ++ * empty); returns the ecode, or -1 on a protocol failure. */ ++static int roundtrip(const char *func, const uint8_t *param, size_t param_len, struct ipa_buf *data) ++{ ++ static char line[LINE_MAX_LEN]; ++ char *hex = param_len ? to_hex(param, param_len) : NULL; ++ const char *p; ++ int ecode; ++ ++ if (host_gone) { ++ free(hex); ++ return -1; ++ } ++ ++ fflush(stdout); ++ printf("{\"type\":\"apdu\",\"payload\":{\"func\":\"%s\",\"param\":%s%s%s}}\n", ++ func, hex ? "\"" : "", hex ? hex : "null", hex ? "\"" : ""); ++ fflush(stdout); ++ free(hex); ++ ++ if (!read_line(line, sizeof(line))) { ++ IPA_LOGP(SSCARD, LERROR, "stdio: host closed the channel during %s\n", func); ++ return -1; ++ } ++ ++ p = field(line, "ecode"); ++ if (!p) { ++ IPA_LOGP(SSCARD, LERROR, "stdio: malformed answer to %s: %s\n", func, line); ++ return -1; ++ } ++ ecode = atoi(p); ++ ++ if (data) { ++ data->len = 0; ++ p = field(line, "data"); ++ if (p && *p == '"') { ++ p++; ++ while (hexval(p[0]) >= 0 && hexval(p[1]) >= 0) { ++ if (data->len >= data->data_len) { ++ IPA_LOGP(SSCARD, LERROR, "stdio: answer to %s exceeds %zu bytes\n", func, ++ data->data_len); ++ return -1; ++ } ++ data->data[data->len++] = (hexval(p[0]) << 4) | hexval(p[1]); ++ p += 2; ++ } ++ } ++ } ++ ++ return ecode; ++} ++ ++/* CLA with the channel number in it (ISO/IEC 7816-4 5.4.1). Channels 0-3 sit ++ * in bits 1-2 of the first interindustry coding, 4-19 need the further ++ * coding: bit 7 set and channel-4 in bits 1-4. Bit 8 (the proprietary class ++ * STORE DATA uses) is kept either way. libipa sends no secure messaging, so ++ * the SM bits are left clear. lpac writes the low nibble instead ++ * (euicc/interface.c, v2.3.0), which is the same for 0-3 and wrong from 4 on; ++ * a modem is free to hand out a channel above 3. */ ++static uint8_t cla_for_channel(uint8_t cla, int channel) ++{ ++ if (channel <= 3) ++ return (cla & 0x80) | (uint8_t) channel; ++ return (cla & 0x80) | 0x40 | (uint8_t) (channel - 4); ++} ++ ++static void drop_pending(struct scard_ctx *ctx) ++{ ++ free(ctx->pending); ++ ctx->pending = NULL; ++ ctx->pending_len = 0; ++ ctx->pending_off = 0; ++} ++ ++static void put_sw(struct ipa_buf *res, uint16_t sw) ++{ ++ res->data[res->len++] = sw >> 8; ++ res->data[res->len++] = sw & 0xFF; ++} ++ ++/* 61xx for what is still held: xx is the size of the next portion, 00 meaning ++ * 256 (ISO/IEC 7816-4 and ETSI TS 102 221 10.1.6, which libipa follows). */ ++static uint16_t pending_sw(const struct scard_ctx *ctx) ++{ ++ size_t left = ctx->pending_len - ctx->pending_off; ++ ++ if (!left) ++ return 0x9000; ++ return 0x6100 | (left >= 256 ? 0 : (uint8_t) left); ++} ++ ++static void close_channel(struct scard_ctx *ctx) ++{ ++ uint8_t ch; ++ ++ if (ctx->channel < 0) ++ return; ++ ch = (uint8_t) ctx->channel; ++ roundtrip("logic_channel_close", &ch, 1, NULL); ++ ctx->channel = -1; ++} ++ ++static int open_channel(struct scard_ctx *ctx) ++{ ++ int ch = roundtrip("logic_channel_open", ctx->aid, ctx->aid_len, NULL); ++ ++ /* channel 0 is the basic channel, never one the modem hands out for an ++ * AID; lpac treats it as a failure the same way */ ++ if (ch <= 0) { ++ IPA_LOGP(SSCARD, LERROR, "stdio: could not open a logical channel to %s (ecode %d)\n", ++ ipa_hexdump(ctx->aid, ctx->aid_len), ch); ++ ctx->channel = -1; ++ return -EIO; ++ } ++ ++ ctx->channel = ch; ++ IPA_LOGP(SSCARD, LINFO, "stdio: logical channel %d open to %s\n", ch, ipa_hexdump(ctx->aid, ctx->aid_len)); ++ return 0; ++} ++ ++void *ipa_scard_init(unsigned int reader_num) ++{ ++ struct scard_ctx *ctx; ++ ++ (void)reader_num; /* the host decides which modem and slot this is */ ++ ++ if (roundtrip("connect", NULL, 0, NULL) != 0) { ++ IPA_LOGP(SSCARD, LERROR, "stdio: the host refused the connection\n"); ++ return NULL; ++ } ++ ++ ctx = IPA_ALLOC_ZERO(struct scard_ctx); ++ assert(ctx); ++ ctx->channel = -1; ++ g_ctx = ctx; ++ return ctx; ++} ++ ++int ipa_scard_transceive(void *scard_ctx, struct ipa_buf *res, const struct ipa_buf *req) ++{ ++ struct scard_ctx *ctx = scard_ctx; ++ struct ipa_buf *rx = NULL; ++ uint8_t *tx = NULL; ++ uint8_t cla, ins, p1; ++ uint16_t sw; ++ size_t n; ++ int rc = 0; ++ ++ assert(ctx); ++ assert(res); ++ assert(req); ++ res->len = 0; ++ ++ if (req->len < 4) ++ return -EINVAL; ++ ++ cla = req->data[0]; ++ ins = req->data[1]; ++ p1 = req->data[2]; ++ ++ IPA_LOGP(SSCARD, LDEBUG, "stdio TX: %s\n", ipa_buf_hexdump(req)); ++ ++ /* TERMINAL CAPABILITY belongs to the terminal, which is the modem: it ++ * has sent its own to the card at power-up, and it is not something a ++ * host can say on the modem's behalf through a logical channel. lpac ++ * does not send it either. */ ++ if ((cla & 0xF0) == 0x80 && ins == 0xAA) { ++ put_sw(res, 0x9000); ++ goto out; ++ } ++ ++ /* MANAGE CHANNEL: the modem opens channels by AID, so opening waits for ++ * the SELECT that names one; closing closes the real channel. */ ++ if (ins == 0x70) { ++ if (p1 & 0x80) ++ close_channel(ctx); ++ put_sw(res, 0x9000); ++ goto out; ++ } ++ ++ /* SELECT by DF name: this is where the channel is really opened. Any ++ * channel still open is closed first so a re-select does not leak one ++ * per poll. The select response is not handed on: libipa checks only ++ * for 61xx here (euicc.c select_isd_r) and never fetches it. */ ++ if (ins == 0xA4 && p1 == 0x04 && req->len >= 5) { ++ size_t lc = req->data[4]; ++ ++ if (lc == 0 || lc > AID_MAX_LEN || req->len < 5 + lc) { ++ put_sw(res, 0x6A82); ++ goto out; ++ } ++ close_channel(ctx); ++ drop_pending(ctx); ++ memcpy(ctx->aid, req->data + 5, lc); ++ ctx->aid_len = lc; ++ put_sw(res, open_channel(ctx) == 0 ? 0x6100 : 0x6A82); ++ goto out; ++ } ++ ++ /* GET RESPONSE for an answer held here: hand out exactly the announced ++ * portion (Le 00 = 256), then 61xx for the rest or 9000. */ ++ if (ins == 0xC0 && ctx->pending) { ++ size_t le = req->len >= 5 ? req->data[4] : 0; ++ size_t left = ctx->pending_len - ctx->pending_off; ++ ++ if (le == 0) ++ le = 256; ++ n = le < left ? le : left; ++ if (n + 2 > res->data_len) ++ n = res->data_len - 2; ++ memcpy(res->data, ctx->pending + ctx->pending_off, n); ++ res->len = n; ++ ctx->pending_off += n; ++ sw = pending_sw(ctx); ++ if (sw == 0x9000) ++ drop_pending(ctx); ++ put_sw(res, sw); ++ goto out; ++ } ++ ++ if (ctx->channel < 0) { ++ IPA_LOGP(SSCARD, LERROR, "stdio: APDU for a card channel that is not open\n"); ++ return -EIO; ++ } ++ ++ /* the channel number in CLA is the modem's, not the one libipa chose */ ++ tx = malloc(req->len); ++ assert(tx); ++ memcpy(tx, req->data, req->len); ++ tx[0] = cla_for_channel(tx[0], ctx->channel); ++ ++ drop_pending(ctx); ++ rx = ipa_buf_alloc(RX_MAX_LEN); ++ assert(rx); ++ ++ if (roundtrip("transmit", tx, req->len, rx) != 0 || rx->len < 2) { ++ IPA_LOGP(SSCARD, LERROR, "stdio: transmit failed\n"); ++ rc = -EIO; ++ goto free; ++ } ++ ++ sw = (rx->data[rx->len - 2] << 8) | rx->data[rx->len - 1]; ++ n = rx->len - 2; ++ ++ /* An answer with data and a normal ending is held and announced as 61xx. ++ * 91xx is a normal ending too (a proactive command is pending, which the ++ * modem fetches itself); libipa knows only 9000/61xx and would abort. */ ++ if (n && (sw == 0x9000 || (sw & 0xFF00) == 0x9100)) { ++ ctx->pending = malloc(n); ++ assert(ctx->pending); ++ memcpy(ctx->pending, rx->data, n); ++ ctx->pending_len = n; ++ ctx->pending_off = 0; ++ put_sw(res, pending_sw(ctx)); ++ goto free; ++ } ++ ++ if ((sw & 0xFF00) == 0x9100) ++ sw = 0x9000; ++ ++ /* anything else goes to libipa as the card said it, clipped to fit */ ++ if (n + 2 > res->data_len) ++ n = res->data_len - 2; ++ memcpy(res->data, rx->data, n); ++ res->len = n; ++ put_sw(res, sw); ++ ++free: ++ free(tx); ++ ipa_buf_free(rx); ++ if (rc < 0) ++ return rc; ++out: ++ IPA_LOGP(SSCARD, LDEBUG, "stdio RX: %s\n", ipa_buf_hexdump(res)); ++ return 0; ++} ++ ++int ipa_scard_reset(void *scard_ctx) ++{ ++ /* a card reset is the host's to do (it owns the modem and knows what a ++ * reset costs the data session); from here it is a re-open */ ++ struct scard_ctx *ctx = scard_ctx; ++ ++ assert(ctx); ++ drop_pending(ctx); ++ close_channel(ctx); ++ return ctx->aid_len ? open_channel(ctx) : 0; ++} ++ ++int ipa_scard_atr(void *scard_ctx, struct ipa_buf *atr) ++{ ++ /* the modem does not hand the ATR to the host over this channel */ ++ (void)scard_ctx; ++ atr->len = 0; ++ return -EIO; ++} ++ ++int ipa_scard_free(void *scard_ctx) ++{ ++ struct scard_ctx *ctx = scard_ctx; ++ ++ if (!ctx) ++ return 0; ++ ++ drop_pending(ctx); ++ close_channel(ctx); ++ roundtrip("disconnect", NULL, 0, NULL); ++ if (g_ctx == ctx) ++ g_ctx = NULL; ++ IPA_FREE(ctx); ++ return 0; ++} ++ ++int ipa_scard_stdio_await_online(void) ++{ ++ static char line[LINE_MAX_LEN]; ++ const char *p; ++ int online; ++ ++ /* The profile just changed. The host has to make the modem use it (a SIM ++ * power cycle: without REFRESH the modem keeps running the old profile) ++ * and wait until the data session is back; only then can the result reach ++ * the eIM. That power cycle closes every logical channel, so ours is ++ * re-opened here before libipa talks to the card again. */ ++ fflush(stdout); ++ printf("{\"type\":\"event\",\"payload\":{\"event\":\"profile_changed\"}}\n"); ++ fflush(stdout); ++ ++ if (!read_line(line, sizeof(line))) { ++ IPA_LOGP(SSCARD, LERROR, "stdio: host closed the channel while restoring connectivity\n"); ++ return -1; ++ } ++ ++ p = field(line, "online"); ++ online = p && strncmp(p, "true", 4) == 0; ++ IPA_LOGP(SSCARD, LINFO, "stdio: host reports the connection %s\n", online ? "back" : "NOT back"); ++ ++ if (g_ctx) { ++ /* no close first: the card reset already closed it, and the modem ++ * may have handed that id to someone else since */ ++ drop_pending(g_ctx); ++ if (g_ctx->aid_len && open_channel(g_ctx) < 0) ++ return -1; ++ } ++ ++ return online; ++} +diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt +index a3e0ef8..de33415 100644 +--- a/tests/CMakeLists.txt ++++ b/tests/CMakeLists.txt +@@ -6,3 +6,6 @@ enable_testing() + add_subdirectory(activation_code) + add_subdirectory(utils) + add_subdirectory(bpp_segments) ++if (IPA_SCARD_STDIO) ++ add_subdirectory(scard_stdio) ++endif() +diff --git a/tests/scard_stdio/CMakeLists.txt b/tests/scard_stdio/CMakeLists.txt +new file mode 100644 +index 0000000..639a9c7 +--- /dev/null ++++ b/tests/scard_stdio/CMakeLists.txt +@@ -0,0 +1,11 @@ ++# Copyright (c) 2026 André Valentin ++# SPDX-License-Identifier: AGPL-3.0-only ++ ++add_executable(scard_stdio_test scard_stdio_test.c) ++set_property(TARGET scard_stdio_test PROPERTY C_STANDARD 99) ++target_compile_options(scard_stdio_test PRIVATE -Wall) ++target_include_directories(scard_stdio_test PRIVATE ${PROJECT_SOURCE_DIR}/include) ++target_link_libraries(scard_stdio_test scard libipa) ++ ++add_test(NAME scard_stdio_test ++ COMMAND sh -c "$ < ${CMAKE_CURRENT_SOURCE_DIR}/scard_stdio_test.in > scard_stdio_test.out && cmp scard_stdio_test.out ${CMAKE_CURRENT_SOURCE_DIR}/scard_stdio_test.ok") +diff --git a/tests/scard_stdio/scard_stdio_test.c b/tests/scard_stdio/scard_stdio_test.c +new file mode 100644 +index 0000000..831320d +--- /dev/null ++++ b/tests/scard_stdio/scard_stdio_test.c +@@ -0,0 +1,95 @@ ++/* ++ * Copyright (c) 2026 André Valentin ++ * ++ * SPDX-License-Identifier: AGPL-3.0-only ++ * ++ * The stdio card backend against a scripted host: stdin carries the host's ++ * answers (scard_stdio_test.in), stdout the requests, which the test compares ++ * with scard_stdio_test.ok. The card answers the way a modem's APDU service ++ * does -- the whole answer at once with 9000 -- and the checks here are what ++ * libipa relies on seeing instead (libipa/euicc.c). ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static void *card; ++ ++/* send one C-APDU (hex) and return the R-APDU as a buffer */ ++static struct ipa_buf *xfer(const char *capdu_hex) ++{ ++ struct ipa_buf *req = ipa_buf_alloc(300); ++ struct ipa_buf *res = ipa_buf_alloc(258); ++ ++ req->len = ipa_binary_from_hexstr(req->data, req->data_len, capdu_hex); ++ assert(ipa_scard_transceive(card, res, req) == 0); ++ ipa_buf_free(req); ++ return res; ++} ++ ++static unsigned sw_of(const struct ipa_buf *r) ++{ ++ assert(r->len >= 2); ++ return (r->data[r->len - 2] << 8) | r->data[r->len - 1]; ++} ++ ++static void expect_sw(const char *capdu_hex, unsigned sw, size_t data_len) ++{ ++ struct ipa_buf *r = xfer(capdu_hex); ++ ++ if (sw_of(r) != sw || r->len - 2 != data_len) { ++ fprintf(stderr, "%s: got sw %04X with %zu bytes, want %04X with %zu\n", capdu_hex, sw_of(r), ++ r->len - 2, sw, data_len); ++ assert(0); ++ } ++ ipa_buf_free(r); ++} ++ ++int main(void) ++{ ++ card = ipa_scard_init(0); ++ assert(card); ++ ++ /* TERMINAL CAPABILITY and MANAGE CHANNEL never reach the host */ ++ expect_sw("80AA000005A903830107", 0x9000, 0); ++ expect_sw("0070000100", 0x9000, 0); ++ ++ /* SELECT ISD-R opens the real channel (the host hands out 3); libipa ++ * accepts only 61xx here */ ++ expect_sw("01A4040410A0000005591010FFFFFFFF8900000100", 0x6100, 0); ++ ++ /* A 300-byte answer with 9000 is announced as 61 00 and handed out as ++ * 256 + 44, each portion exactly the size announced. CLA goes out with ++ * the host's channel (83), not libipa's (81). */ ++ expect_sw("81E2910003BF2200", 0x6100, 0); ++ expect_sw("01C0000000", 0x612C, 256); ++ expect_sw("01C000002C", 0x9000, 44); ++ ++ /* The host resets the card for a profile change: the channel it closed ++ * is re-opened (the host now hands out 2) and later APDUs use it */ ++ assert(ipa_scard_stdio_await_online() == 1); ++ expect_sw("81E2910003BF2B00", 0x6A88, 0); ++ ++ /* 91xx is a normal ending too: held and announced like 9000 */ ++ expect_sw("81E2910003BF2D00", 0x6102, 0); ++ expect_sw("01C0000002", 0x9000, 2); ++ ++ /* the host reports it could not get the connection back, and re-opens ++ * on channel 4: from there CLA takes the further coding, 0x40 | ch-4 ++ * (ISO/IEC 7816-4 5.4.1), so STORE DATA goes out as C0, not 84 */ ++ assert(ipa_scard_stdio_await_online() == 0); ++ expect_sw("81E2910003BF2B00", 0x9000, 0); ++ ++ assert(ipa_scard_free(card) == 0); ++ ++ /* the host is gone: every exchange fails at once instead of blocking */ ++ card = ipa_scard_init(0); ++ assert(card == NULL); ++ ++ fprintf(stderr, "scard_stdio_test: ok\n"); ++ return 0; ++} +diff --git a/tests/scard_stdio/scard_stdio_test.in b/tests/scard_stdio/scard_stdio_test.in +new file mode 100644 +index 0000000..50d2f24 +--- /dev/null ++++ b/tests/scard_stdio/scard_stdio_test.in +@@ -0,0 +1,12 @@ ++{"type":"apdu","payload":{"ecode":0}} ++{"type":"apdu","payload":{"ecode":3}} ++{"type":"apdu","payload":{"ecode":0,"data":"A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A59000"}} ++{"type":"event","payload":{"online":true}} ++{"type":"apdu","payload":{"ecode":2}} ++{"type":"apdu","payload":{"ecode":0,"data":"6A88"}} ++{"type":"apdu","payload":{"ecode":0,"data":"AABB9110"}} ++{"type":"event","payload":{"online":false}} ++{"type":"apdu","payload":{"ecode":4}} ++{"type":"apdu","payload":{"ecode":0,"data":"9000"}} ++{"type":"apdu","payload":{"ecode":0}} ++{"type":"apdu","payload":{"ecode":0}} +diff --git a/tests/scard_stdio/scard_stdio_test.ok b/tests/scard_stdio/scard_stdio_test.ok +new file mode 100644 +index 0000000..2e653d9 +--- /dev/null ++++ b/tests/scard_stdio/scard_stdio_test.ok +@@ -0,0 +1,13 @@ ++{"type":"apdu","payload":{"func":"connect","param":null}} ++{"type":"apdu","payload":{"func":"logic_channel_open","param":"A0000005591010FFFFFFFF8900000100"}} ++{"type":"apdu","payload":{"func":"transmit","param":"83E2910003BF2200"}} ++{"type":"event","payload":{"event":"profile_changed"}} ++{"type":"apdu","payload":{"func":"logic_channel_open","param":"A0000005591010FFFFFFFF8900000100"}} ++{"type":"apdu","payload":{"func":"transmit","param":"82E2910003BF2B00"}} ++{"type":"apdu","payload":{"func":"transmit","param":"82E2910003BF2D00"}} ++{"type":"event","payload":{"event":"profile_changed"}} ++{"type":"apdu","payload":{"func":"logic_channel_open","param":"A0000005591010FFFFFFFF8900000100"}} ++{"type":"apdu","payload":{"func":"transmit","param":"C0E2910003BF2B00"}} ++{"type":"apdu","payload":{"func":"logic_channel_close","param":"04"}} ++{"type":"apdu","payload":{"func":"disconnect","param":null}} ++{"type":"apdu","payload":{"func":"connect","param":null}} diff --git a/wwand-qlog/Makefile b/wwand-qlog/Makefile new file mode 100644 index 000000000..5d32ed692 --- /dev/null +++ b/wwand-qlog/Makefile @@ -0,0 +1,58 @@ +# +# On-demand Qualcomm diag (QMDL) capture through Quectel QLog, for wwand: +# `wwandctl qlog`. A wwand PLUGIN, kept out of the wwand sources on purpose: +# it is not meant for upstream. wwand itself only resolves and publishes the +# modem's diag node (status.diag_port) and never opens it. +# +# Sources live in https://github.com/ddimension/wwand-qlog, tagged vX.Y.Z. Pin +# a new commit with `scripts/bump-source.sh wwand-qlog `. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=wwand-qlog +PKG_VERSION:=0.1.0 +PKG_RELEASE:=1 + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/ddimension/wwand-qlog.git +PKG_SOURCE_VERSION:=e4ef3350ac11439259f8bdfd7f11baf002ec9659 +PKG_MIRROR_HASH:=skip + +PKG_LICENSE:=GPL-2.0-only +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:= + +include $(INCLUDE_DIR)/package.mk + +UCDIR:=/usr/share/ucode/wwand + +define Package/wwand-qlog + SECTION:=net + CATEGORY:=Network + SUBMENU:=WWAN + TITLE:=Quectel QLog diag capture for wwand (wwandctl qlog) + # qlog: /usr/sbin/QLog and its filter profiles. QLog's device scan covers + # Quectel modules only (its VID/PID allow-list, see the README). + DEPENDS:=+wwand +qlog + PKGARCH:=all +endef + +define Package/wwand-qlog/description + `wwandctl qlog`: start, inspect and stop a Qualcomm diag capture with Quectel + QLog on the modem's DM/DIAG port, which wwand resolves. On demand only; the + storage sink (TCP, TFTP, FTP, a directory) is QLog's own and passed through + verbatim, and a capture without one is refused so a QMDL cannot fill the + router's flash. +endef + +define Build/Compile +endef + +define Package/wwand-qlog/install + $(INSTALL_DIR) $(1)$(UCDIR)/ctl + $(INSTALL_DATA) $(PKG_BUILD_DIR)/qlog.uc $(1)$(UCDIR)/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/ctl/qlog.uc $(1)$(UCDIR)/ctl/ +endef + +$(eval $(call BuildPackage,wwand-qlog)) diff --git a/wwand/Makefile b/wwand/Makefile index 970edb94d..cb3f74138 100644 --- a/wwand/Makefile +++ b/wwand/Makefile @@ -50,12 +50,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=wwand -PKG_VERSION:=1.6.8_p6 -PKG_RELEASE:=56 +PKG_VERSION:=1.6.8_p23 +PKG_RELEASE:=57 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://github.com/ddimension/wwand.git -PKG_SOURCE_VERSION:=2d7803e46b95f60a8a890ccd961f5154ba672424 +PKG_SOURCE_VERSION:=cf8d5cf1cc5767d58824a02c61de4d9d2ff58cf6 PKG_MIRROR_HASH:=skip PKG_LICENSE:=GPL-2.0-only @@ -98,7 +98,7 @@ UCDIR:=/usr/share/ucode/wwand WWAND_BASE_UC:=apndb.uc atcmd.uc atcmd_parse.uc atport.uc backend.uc board.uc \ carrier_config.uc client.uc config.uc config_check.uc context_common.uc \ context_monitor_qmi.uc ctx_settings.uc daemon.uc deps.uc discovery.uc \ - hwops.uc log.uc modem_common.uc modem_datapath_qmi.uc modem_init_qmi.uc \ + hwops.uc log.uc modem_common.uc modem_datapath_qmi.uc modem_init_qmi.uc plugins.uc \ modem_quirks.uc modeswitch.uc ncm_vendors.uc netlink.uc netsel_ops.uc \ protocol_switch.uc reconnect.uc recovery.uc regdetail.uc sim.uc sim_plmn.uc \ simops.uc sms.uc sms_pdu.uc telemetry_mbim.uc telemetry_ncm.uc \