Add URLTest whitelist filtering and fix rule sets validator

This commit is contained in:
ushan0v
2026-05-21 23:14:04 +03:00
parent 7148c7778f
commit 57705e1f88
13 changed files with 1179 additions and 801 deletions
+19 -5
View File
@@ -2,6 +2,7 @@
[![Star](https://img.shields.io/github/stars/ushan0v/podkop-plus?style=social)](https://github.com/ushan0v/podkop-plus/stargazers)
[![Releases](https://img.shields.io/github/v/release/ushan0v/podkop-plus?label=releases)](https://github.com/ushan0v/podkop-plus/releases)
[![Telegram](https://img.shields.io/badge/Telegram-@ushan0v-2CA5E0?logo=telegram&logoColor=white)](https://t.me/ushan0v)
[![Original](https://img.shields.io/badge/original-itdoginfo%2Fpodkop-blue)](https://github.com/itdoginfo/podkop)
[![podkop-evolution](https://img.shields.io/badge/podkop--evolution-yandexru45-blue)](https://github.com/yandexru45/podkop-evolution)
[![zapret-openwrt](https://img.shields.io/badge/zapret--openwrt-remittor-blue)](https://github.com/remittor/zapret-openwrt/releases)
@@ -20,6 +21,10 @@
</tr>
</table>
> **НЕ ПИШИТЕ** с проблемами Podkop Plus в официальный [Telegram-чат](https://t.me/itdogchat). Это независимый форк. Разработчики и участники оригинального чата не имеют отношения к этому проекту.
> * Нашли баг или есть проблема? Создайте [Issue](https://github.com/ushan0v/podkop-plus/issues) в этом репозитории.
> * Не хотите или не умеете создавать Issue? Можете попробовать постучаться в личку [@ushan0v](https://t.me/ushan0v).
### Установка
```sh
@@ -33,13 +38,22 @@ sh <(wget -O - https://raw.githubusercontent.com/ushan0v/podkop-plus/main/instal
- [sing-box-extended](https://github.com/shtorm-7/sing-box-extended/releases)
- [awg-openwrt](https://github.com/Slava-Shchipunov/awg-openwrt/releases)
### Возможности
### Что нового в этом форке
- Поддержка подписок, адаптированная из [podkop-evolution](https://github.com/yandexru45/podkop-evolution).
- Расширена поддержка всех основных форматов подписок: sing-box JSON, URI/base64 списки, Clash/Mihomo. Добавлено чтение метаданных подписки.
- Обновленный LuCI-интерфейс секций. Расширенное управление секциями: изменение приоритета, новые условия и значения.
- Интеграция Zapret и ByeDPI как отдельного действия для секции.
- Добавлена поддержка подписок.
- Интеграция Zapret и ByeDPI как отдельного действия.
- Поддержка sing-box extended и транспорта XHTTP.
- Обновленный LuCI-интерфейс и расширенное управление секциями.
- Возможность исключать серверы из URLTest по стране или названию.
### Подписки
- Работа с подписками адаптирована из [podkop-evolution](https://github.com/yandexru45/podkop-evolution) и значительно расширена.
- Реализована поддержка основных форматов: sing-box json, uri/base64 списки, clash/mihomo
- Добавлено чтение метаданных подписки.
- Подписки поддерживают HWID авторизацию.
- Для расшифровки подписок в формате `happ://crypt` используйте сервис [leeeet.dev/happ-decryptor](https://leeeet.dev/happ-decryptor).
- Парсинг подписок с большим количеством серверов (+100) создает сильную нагрузку, что может привести к высоким задержкам запуска или зависаниям.
### Секции
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+33 -18
View File
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: PODKOP PLUS\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-19 18:51+0300\n"
"PO-Revision-Date: 2026-05-19 18:51+0300\n"
"POT-Creation-Date: 2026-05-21 17:44+0300\n"
"PO-Revision-Date: 2026-05-21 17:44+0300\n"
"Last-Translator: ushan0v\n"
"Language-Team: none\n"
"Language: ru\n"
@@ -17,6 +17,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
msgid "-- Select --"
msgstr "-- Выберите --"
msgid "✔ Running"
msgstr "✔ Работает"
@@ -47,9 +50,6 @@ msgstr "Найдены дополнительные правила маркир
msgid "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."
msgstr "Обеспечивает доступ к YACD из WAN. Убедитесь, что в брандмауэре открыт соответствующий порт."
msgid "An error occurred while saving the form:"
msgstr "При сохранении формы произошла ошибка:"
msgid "Applicable for SOCKS and Shadowsocks links"
msgstr "Применимо для ссылок SOCKS и Shadowsocks"
@@ -74,6 +74,12 @@ msgstr "Backend-проверка завершилась ошибкой: %s"
msgid "Binary file"
msgstr "Бинарный файл"
msgid "Blacklist (Exclude)"
msgstr "Черный список (Исключить)"
msgid "Blacklist: test all servers except selected ones. Whitelist: test only selected servers."
msgstr "Черный список: проверять все серверы, кроме указанных. Белый список: проверять только указанные серверы."
msgid "Bootsrap DNS"
msgstr "Bootstrap DNS"
@@ -227,9 +233,6 @@ msgstr "Отключить QUIC"
msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
msgstr "Отключить QUIC протокол для улучшения совместимости или исправления видео стриминга"
msgid "Dismiss"
msgstr "Закрыть"
msgid "DNS on router"
msgstr "DNS на роутере"
@@ -395,6 +398,12 @@ msgstr "Как часто обновлять этот удалённый наб
msgid "HTTP error"
msgstr "Ошибка HTTP"
msgid "Include server by regular expression in URLTest"
msgstr "Включить сервер по регулярному выражению в URLTest"
msgid "Include server in URLTest"
msgstr "Включить сервер в URLTest"
msgid "Interface Monitoring"
msgstr "Мониторинг интерфейса"
@@ -557,8 +566,8 @@ msgstr "Ключевое слово не должно содержать про
msgid "Latest"
msgstr "Последняя"
msgid "List must be a direct .lst URL or a local .lst path"
msgstr "Список должен быть прямым URL на .lst или локальным путём к .lst"
msgid "List must be an HTTP(S) URL or a local .lst path"
msgstr "Список должен быть HTTP(S) URL или локальным путём .lst"
msgid "List Update Frequency"
msgstr "Частота обновления списков"
@@ -641,6 +650,9 @@ msgstr "Не отвечает"
msgid "Not running"
msgstr "Не запущено"
msgid "Only servers whose names match these regular expressions will be checked through URLTest"
msgstr "Только те серверы, имена которых совпадают с этими регулярными выражениями, будут проверяться через URLTest"
msgid "Open Project Page"
msgstr "Открыть страницу проекта"
@@ -773,8 +785,8 @@ msgstr "Перезапустить Podkop Plus"
msgid "Routing Excluded IPs"
msgstr "Исключённые из маршрутизации IP-адреса"
msgid "Rule set must be a direct .srs / .json URL or a local .srs / .json path"
msgstr "Набор правил должен быть прямым URL на .srs / .json или локальным путём к .srs / .json"
msgid "Rule set must be an HTTP(S) URL or a local .srs / .json path"
msgstr "Набор правил должен быть HTTP(S) URL или локальным путём .srs / .json"
msgid "Rule set URL"
msgstr "URL набора правил"
@@ -809,9 +821,6 @@ msgstr "Запустить диагностику"
msgid "Russia inside restrictions"
msgstr "Ограничения Russia inside"
msgid "Save error"
msgstr "Ошибка сохранения"
msgid "Secret key for authenticating remote access to YACD when WAN access is enabled."
msgstr "Секретный ключ для аутентификации удаленного доступа к YACD при включенном доступе через WAN."
@@ -824,9 +833,6 @@ msgstr "Имя секции"
msgid "Sections"
msgstr "Секции"
msgid "-- Select --"
msgstr "-- Выберите --"
msgid "Select a loaded server or enter an exact server name"
msgstr "Выберите загруженный сервер или введите точное имя сервера"
@@ -848,6 +854,9 @@ msgstr "Выберите или введите путь к файлу кеша s
msgid "Select path for sing-box config file. Change this ONLY if you know what you are doing"
msgstr "Выберите путь к файлу конфигурации sing-box. Изменяйте это, ТОЛЬКО если вы знаете, что делаете"
msgid "Select servers that will participate in URLTest checks. Other servers will be ignored."
msgstr "Выберите серверы, которые будут участвовать в проверке URLTest. Остальные будут проигнорированы."
msgid "Select the log level for sing-box"
msgstr "Выберите уровень логов для sing-box"
@@ -1094,6 +1103,9 @@ msgstr "URL должен использовать один из следующи
msgid "URLTest interval"
msgstr "Интервал URLTest"
msgid "URLTest server filter mode"
msgstr "Режим фильтра серверов URLTest"
msgid "URLTest tolerance"
msgstr "Допуск URLTest"
@@ -1133,6 +1145,9 @@ msgstr "Предупреждение: Russia inside может быть испо
msgid "What Podkop Plus should do when this section matches"
msgstr "Что Podkop Plus должен делать при совпадении этой секции"
msgid "Whitelist (Only include)"
msgstr "Белый список (Только включить)"
msgid "YACD Secret Key"
msgstr "Секретный ключ YACD"
+2
View File
@@ -57,6 +57,8 @@ export const DOMAIN_LIST_OPTIONS = {
google_play: 'Google Play',
hodca: 'H.O.D.C.A',
roblox: 'Roblox',
ads_hagezi_pro: 'Ads (Hagezi Pro)',
supercell: 'Supercell',
hetzner: 'Hetzner ASN',
ovh: 'OVH ASN',
digitalocean: 'Digital Ocean ASN',
+3
View File
@@ -176,9 +176,12 @@ export namespace Podkop {
urltest_enabled?: '0' | '1';
urltest_check_interval_disabled?: '0' | '1';
detect_server_country?: '0' | '1';
urltest_filter_mode?: 'exclude' | 'include';
urltest_exclude_countries?: string[];
urltest_exclude_outbounds?: string[];
urltest_exclude_regex?: string[];
urltest_include_outbounds?: string[];
urltest_include_regex?: string[];
outbound_json?: string;
interface?: string;
yacd_secret_key?: string;
@@ -622,6 +622,8 @@ var DOMAIN_LIST_OPTIONS = {
google_play: "Google Play",
hodca: "H.O.D.C.A",
roblox: "Roblox",
ads_hagezi_pro: "Ads (Hagezi Pro)",
supercell: "Supercell",
hetzner: "Hetzner ASN",
ovh: "OVH ASN",
digitalocean: "Digital Ocean ASN",
@@ -2515,14 +2515,18 @@ function hasAllowedReferenceExtension(value, extensions) {
return extensions.some((extension) => normalized.endsWith(extension));
}
function validateFileReference(value, extensions, errorMessage) {
function validateFileReference(value, extensions, errorMessage, options = {}) {
if (!value || value.length === 0) {
return true;
}
if (value.startsWith("http://") || value.startsWith("https://")) {
const validation = main.validateUrl(value);
if (validation.valid && hasAllowedReferenceExtension(value, extensions)) {
if (
validation.valid &&
(options.allowRemoteWithoutExtension ||
hasAllowedReferenceExtension(value, extensions))
) {
return true;
}
@@ -2545,9 +2549,8 @@ function validateCustomRulesetReference(value) {
return validateFileReference(
value,
[".srs", ".json"],
_(
"Rule set must be a direct .srs / .json URL or a local .srs / .json path",
),
_("Rule set must be an HTTP(S) URL or a local .srs / .json path"),
{ allowRemoteWithoutExtension: true },
);
}
@@ -2555,7 +2558,8 @@ function validatePlainListReference(value) {
return validateFileReference(
value,
[".lst"],
_("List must be a direct .lst URL or a local .lst path"),
_("List must be an HTTP(S) URL or a local .lst path"),
{ allowRemoteWithoutExtension: true },
);
}
@@ -2909,6 +2913,21 @@ function createSectionContent(section) {
return validation.valid ? true : validation.message;
};
o = section.taboption(
"settings",
form.ListValue,
"urltest_filter_mode",
_("URLTest server filtering"),
_("Allows limiting the list of servers for URLTest"),
);
o.value("disabled", _("Disabled"));
o.value("exclude", _("Blacklist"));
o.value("include", _("Whitelist"));
o.default = "disabled";
o.rmempty = false;
o.depends({ action: "proxy", urltest_enabled: "1" });
o.modalonly = true;
o = section.taboption(
"settings",
form.DynamicList,
@@ -2922,6 +2941,7 @@ function createSectionContent(section) {
o.depends({
action: "proxy",
detect_server_country: "1",
urltest_filter_mode: "exclude",
urltest_enabled: "1",
});
o.modalonly = true;
@@ -2936,7 +2956,11 @@ function createSectionContent(section) {
);
o.placeholder = _("-- Select --");
o.rmempty = true;
o.depends({ action: "proxy", urltest_enabled: "1" });
o.depends({
action: "proxy",
urltest_enabled: "1",
urltest_filter_mode: "exclude",
});
o.modalonly = true;
o.renderWidget = function (section_id, _option_index, cfgvalue) {
return createOutboundNameDynamicListWidget(this, section_id, cfgvalue);
@@ -2947,10 +2971,74 @@ function createSectionContent(section) {
form.DynamicList,
"urltest_exclude_regex",
_("Exclude server by regular expression from URLTest"),
_("Servers with names matching these regular expressions will not be tested by URLTest"),
_(
"Servers with names matching these regular expressions will not be tested by URLTest",
),
);
o.rmempty = true;
o.depends({ action: "proxy", urltest_enabled: "1" });
o.depends({
action: "proxy",
urltest_enabled: "1",
urltest_filter_mode: "exclude",
});
o.modalonly = true;
o.validate = validateRegex;
o = section.taboption(
"settings",
form.DynamicList,
"urltest_include_countries",
_("Include country in URLTest"),
_("Only servers from selected countries will be tested by URLTest"),
);
populateCountryOptionValues(o);
o.create = false;
o.rmempty = true;
o.depends({
action: "proxy",
detect_server_country: "1",
urltest_filter_mode: "include",
urltest_enabled: "1",
});
o.modalonly = true;
o.validate = validateCountryCode;
o = section.taboption(
"settings",
form.DynamicList,
"urltest_include_outbounds",
_("Include server in URLTest"),
_(
"Select servers that will participate in URLTest checks. Other servers will be ignored.",
),
);
o.placeholder = _("-- Select --");
o.rmempty = true;
o.depends({
action: "proxy",
urltest_enabled: "1",
urltest_filter_mode: "include",
});
o.modalonly = true;
o.renderWidget = function (section_id, _option_index, cfgvalue) {
return createOutboundNameDynamicListWidget(this, section_id, cfgvalue);
};
o = section.taboption(
"settings",
form.DynamicList,
"urltest_include_regex",
_("Include server by regular expression in URLTest"),
_(
"Only servers whose names match these regular expressions will be checked through URLTest",
),
);
o.rmempty = true;
o.depends({
action: "proxy",
urltest_enabled: "1",
urltest_filter_mode: "include",
});
o.modalonly = true;
o.validate = validateRegex;
+41 -20
View File
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: PODKOP PLUS\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-19 18:51+0300\n"
"PO-Revision-Date: 2026-05-19 18:51+0300\n"
"POT-Creation-Date: 2026-05-21 17:44+0300\n"
"PO-Revision-Date: 2026-05-21 17:44+0300\n"
"Last-Translator: ushan0v\n"
"Language-Team: none\n"
"Language: ru\n"
@@ -17,6 +17,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
msgid "-- Select --"
msgstr "-- Выберите --"
msgid "✔ Running"
msgstr "✔ Работает"
@@ -47,9 +50,6 @@ msgstr "Найдены дополнительные правила маркир
msgid "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."
msgstr "Обеспечивает доступ к YACD из WAN. Убедитесь, что в брандмауэре открыт соответствующий порт."
msgid "An error occurred while saving the form:"
msgstr "При сохранении формы произошла ошибка:"
msgid "Applicable for SOCKS and Shadowsocks links"
msgstr "Применимо для ссылок SOCKS и Shadowsocks"
@@ -74,6 +74,12 @@ msgstr "Backend-проверка завершилась ошибкой: %s"
msgid "Binary file"
msgstr "Бинарный файл"
msgid "Allows limiting the list of servers for URLTest"
msgstr "Позволяет ограничить список серверов для URLTest"
msgid "Blacklist"
msgstr "Черный список"
msgid "Bootsrap DNS"
msgstr "Bootstrap DNS"
@@ -227,8 +233,8 @@ msgstr "Отключить QUIC"
msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
msgstr "Отключить QUIC протокол для улучшения совместимости или исправления видео стриминга"
msgid "Dismiss"
msgstr "Закрыть"
msgid "Disabled"
msgstr "Отключено"
msgid "DNS on router"
msgstr "DNS на роутере"
@@ -395,6 +401,12 @@ msgstr "Как часто обновлять этот удалённый наб
msgid "HTTP error"
msgstr "Ошибка HTTP"
msgid "Include server by regular expression in URLTest"
msgstr "Включить сервер по регулярному выражению в URLTest"
msgid "Include server in URLTest"
msgstr "Включить сервер в URLTest"
msgid "Interface Monitoring"
msgstr "Мониторинг интерфейса"
@@ -557,8 +569,8 @@ msgstr "Ключевое слово не должно содержать про
msgid "Latest"
msgstr "Последняя"
msgid "List must be a direct .lst URL or a local .lst path"
msgstr "Список должен быть прямым URL на .lst или локальным путём к .lst"
msgid "List must be an HTTP(S) URL or a local .lst path"
msgstr "Список должен быть HTTP(S) URL или локальным путём .lst"
msgid "List Update Frequency"
msgstr "Частота обновления списков"
@@ -641,6 +653,9 @@ msgstr "Не отвечает"
msgid "Not running"
msgstr "Не запущено"
msgid "Only servers whose names match these regular expressions will be checked through URLTest"
msgstr "Только те серверы, имена которых совпадают с этими регулярными выражениями, будут проверяться через URLTest"
msgid "Open Project Page"
msgstr "Открыть страницу проекта"
@@ -773,8 +788,8 @@ msgstr "Перезапустить Podkop Plus"
msgid "Routing Excluded IPs"
msgstr "Исключённые из маршрутизации IP-адреса"
msgid "Rule set must be a direct .srs / .json URL or a local .srs / .json path"
msgstr "Набор правил должен быть прямым URL на .srs / .json или локальным путём к .srs / .json"
msgid "Rule set must be an HTTP(S) URL or a local .srs / .json path"
msgstr "Набор правил должен быть HTTP(S) URL или локальным путём .srs / .json"
msgid "Rule set URL"
msgstr "URL набора правил"
@@ -809,9 +824,6 @@ msgstr "Запустить диагностику"
msgid "Russia inside restrictions"
msgstr "Ограничения Russia inside"
msgid "Save error"
msgstr "Ошибка сохранения"
msgid "Secret key for authenticating remote access to YACD when WAN access is enabled."
msgstr "Секретный ключ для аутентификации удаленного доступа к YACD при включенном доступе через WAN."
@@ -824,9 +836,6 @@ msgstr "Имя секции"
msgid "Sections"
msgstr "Секции"
msgid "-- Select --"
msgstr "-- Выберите --"
msgid "Select a loaded server or enter an exact server name"
msgstr "Выберите загруженный сервер или введите точное имя сервера"
@@ -848,6 +857,9 @@ msgstr "Выберите или введите путь к файлу кеша s
msgid "Select path for sing-box config file. Change this ONLY if you know what you are doing"
msgstr "Выберите путь к файлу конфигурации sing-box. Изменяйте это, ТОЛЬКО если вы знаете, что делаете"
msgid "Select servers that will participate in URLTest checks. Other servers will be ignored."
msgstr "Выберите серверы, которые будут участвовать в проверке URLTest. Остальные будут проигнорированы."
msgid "Select the log level for sing-box"
msgstr "Выберите уровень логов для sing-box"
@@ -1094,6 +1106,9 @@ msgstr "URL должен использовать один из следующи
msgid "URLTest interval"
msgstr "Интервал URLTest"
msgid "URLTest server filtering"
msgstr "Фильтрация серверов URLTest"
msgid "URLTest tolerance"
msgstr "Допуск URLTest"
@@ -1133,12 +1148,12 @@ msgstr "Предупреждение: Russia inside может быть испо
msgid "What Podkop Plus should do when this section matches"
msgstr "Что Podkop Plus должен делать при совпадении этой секции"
msgid "Whitelist"
msgstr "Белый список"
msgid "YACD Secret Key"
msgstr "Секретный ключ YACD"
msgid "The section is not available yet. Wait for the settings to finish applying and try again."
msgstr "Секция пока недоступна. Дождитесь завершения применения настроек и попробуйте снова."
msgid "You can select Output Network Interface, by default autodetect"
msgstr "Вы можете выбрать выходной сетевой интерфейс, по умолчанию он определяется автоматически."
@@ -1156,3 +1171,9 @@ msgstr "Бинарник провайдера Zapret доступен"
msgid "Zapret provider binary is not available"
msgstr "Бинарник провайдера Zapret недоступен"
msgid "Include country in URLTest"
msgstr "Включить страну в URLTest"
msgid "Only servers from selected countries will be tested by URLTest"
msgstr "Только серверы из выбранных стран будут проверяться через URLTest"
File diff suppressed because it is too large Load Diff
+3
View File
@@ -34,10 +34,13 @@ config settings 'settings'
# option urltest_check_interval '3m'
# option urltest_tolerance '50'
# option urltest_testing_url 'https://www.gstatic.com/generate_204'
# option urltest_filter_mode 'disabled'
# option detect_server_country '0'
# list urltest_exclude_countries 'US'
# list urltest_exclude_outbounds 'Example server name'
# list urltest_exclude_regex '.*backup.*'
# list urltest_include_outbounds 'Example server name'
# list urltest_include_regex '.*primary.*'
# list community_lists 'russia_inside'
# #list fully_routed_ips '192.168.1.2'
+208 -68
View File
@@ -1216,8 +1216,8 @@ append_sing_box_rule_signature() {
proxy)
local selector_proxy_links subscription_urls udp_over_tcp urltest_enabled \
urltest_check_interval urltest_tolerance urltest_testing_url subscription_update_enabled \
subscription_update_interval detect_server_country urltest_exclude_countries \
urltest_exclude_outbounds urltest_exclude_regex
subscription_update_interval detect_server_country urltest_filter_mode urltest_exclude_countries urltest_include_countries \
urltest_exclude_outbounds urltest_exclude_regex urltest_include_outbounds urltest_include_regex
config_get selector_proxy_links "$section" "selector_proxy_links"
config_get subscription_urls "$section" "subscription_urls"
config_get_bool udp_over_tcp "$section" "enable_udp_over_tcp" 0
@@ -1228,9 +1228,13 @@ append_sing_box_rule_signature() {
config_get urltest_testing_url "$section" "urltest_testing_url" "https://www.gstatic.com/generate_204"
config_get_bool subscription_update_enabled "$section" "subscription_update_enabled" 1
subscription_update_interval="$(get_subscription_update_interval_for_rule "$section")"
config_get urltest_filter_mode "$section" "urltest_filter_mode" "disabled"
config_get urltest_exclude_countries "$section" "urltest_exclude_countries"
config_get urltest_include_countries "$section" "urltest_include_countries"
config_get urltest_exclude_outbounds "$section" "urltest_exclude_outbounds"
config_get urltest_exclude_regex "$section" "urltest_exclude_regex"
config_get urltest_include_outbounds "$section" "urltest_include_outbounds"
config_get urltest_include_regex "$section" "urltest_include_regex"
signature_add "rule.$section.selector_proxy_links" "$selector_proxy_links"
signature_add "rule.$section.subscription_urls" "$subscription_urls"
@@ -1240,9 +1244,13 @@ append_sing_box_rule_signature() {
signature_add "rule.$section.urltest_check_interval" "$urltest_check_interval"
signature_add "rule.$section.urltest_tolerance" "$urltest_tolerance"
signature_add "rule.$section.urltest_testing_url" "$urltest_testing_url"
signature_add "rule.$section.urltest_filter_mode" "$urltest_filter_mode"
signature_add "rule.$section.urltest_exclude_countries" "$urltest_exclude_countries"
signature_add "rule.$section.urltest_include_countries" "$urltest_include_countries"
signature_add "rule.$section.urltest_exclude_outbounds" "$urltest_exclude_outbounds"
signature_add "rule.$section.urltest_exclude_regex" "$urltest_exclude_regex"
signature_add "rule.$section.urltest_include_outbounds" "$urltest_include_outbounds"
signature_add "rule.$section.urltest_include_regex" "$urltest_include_regex"
signature_add "rule.$section.subscription_update_enabled" "$subscription_update_enabled"
signature_add "rule.$section.subscription_update_interval" "$subscription_update_interval"
@@ -1600,13 +1608,9 @@ filter_community_subnet_lists() {
}
reference_is_remote_sing_box_ruleset() {
local extension
case "$1" in
http://* | https://*)
extension="$(url_get_file_extension "$1")"
[ "$extension" = "srs" ] || [ "$extension" = "json" ]
return "$?"
return 0
;;
esac
@@ -2007,6 +2011,19 @@ podkop_uci_set_option_if_missing() {
podkop_uci_set_option "$section" "$option" "$value"
}
podkop_uci_migrate_urltest_filter_mode() {
local section="$1"
podkop_uci_option_exists "$section" "urltest_filter_mode" && return 0
if podkop_uci_option_exists "$section" "urltest_exclude_countries" ||
podkop_uci_option_exists "$section" "urltest_include_countries" ||
podkop_uci_option_exists "$section" "urltest_exclude_outbounds" ||
podkop_uci_option_exists "$section" "urltest_exclude_regex"; then
podkop_uci_set_option "$section" "urltest_filter_mode" "exclude"
fi
}
podkop_uci_delete_option() {
local section="$1"
local option="$2"
@@ -2294,6 +2311,7 @@ migrate_0_7_17_8_rule() {
case "$action" in
proxy)
podkop_uci_migrate_urltest_filter_mode "$section"
migrate_0_7_17_8_proxy_rule "$section" "$proxy_config_type"
if [ "$converted_from_rule" -eq 1 ] && [ -n "$subscription_urls" ]; then
migrate_0_7_17_8_delete_subscription_cache "$section"
@@ -2879,7 +2897,6 @@ validate_service() {
validate_ruleset_reference() {
local reference="$1"
local extension
if [ -z "$reference" ]; then
return 0
@@ -2891,10 +2908,7 @@ validate_ruleset_reference() {
case "$reference" in
http://* | https://*)
extension="$(url_get_file_extension "$reference")"
if [ "$extension" = "srs" ] || [ "$extension" = "json" ]; then
return 0
fi
return 0
;;
/*.srs | /*.json)
return 0
@@ -2907,7 +2921,6 @@ validate_ruleset_reference() {
validate_plain_domain_ip_list_reference() {
local reference="$1"
local extension
if [ -z "$reference" ]; then
return 0
@@ -2915,8 +2928,7 @@ validate_plain_domain_ip_list_reference() {
case "$reference" in
http://* | https://*)
extension="$(url_get_file_extension "$reference")"
[ "$extension" = "lst" ] && return 0
return 0
;;
/*.lst)
return 0
@@ -2978,7 +2990,22 @@ validate_country_code_option() {
exit 1
}
validate_urltest_exclude_regex_option() {
validate_urltest_filter_mode_option() {
local value="$1"
local section="$2"
[ -n "$value" ] || return 0
case "$value" in
disabled | exclude | include)
return 0
;;
esac
log "Invalid URLTest filter mode '$value' in rule '$section'. Aborted." "fatal"
exit 1
}
validate_urltest_regex_option() {
local value="$1"
local section="$2"
local grep_status
@@ -2990,7 +3017,7 @@ validate_urltest_exclude_regex_option() {
return 0
fi
log "Invalid URLTest exclude regular expression '$value' in rule '$section'. Aborted." "fatal"
log "Invalid URLTest regular expression '$value' in rule '$section'. Aborted." "fatal"
exit 1
}
@@ -3097,10 +3124,7 @@ detect_inline_ruleset_reference_kind() {
;;
http://* | https://*)
extension="$(url_get_file_extension "$reference")"
[ "$extension" = "srs" ] || [ "$extension" = "json" ] || {
echo "unknown"
return 0
}
[ "$extension" = "srs" ] || [ "$extension" = "json" ] || [ -z "$extension" ] || log "Unknown rule set URL extension '$extension' for '$reference'; assuming binary format" "debug"
[ "$kind" = "unknown" ] && kind="domains"
echo "$kind"
;;
@@ -3110,6 +3134,25 @@ detect_inline_ruleset_reference_kind() {
esac
}
get_inline_remote_ruleset_format() {
local reference="$1"
local extension
extension="$(url_get_file_extension "$reference")"
case "$extension" in
json)
echo "source"
;;
srs | "")
echo "binary"
;;
*)
log "Unknown rule set URL extension '$extension' for '$reference'; assuming binary format" "debug"
echo "binary"
;;
esac
}
validate_outbound_json_rule() {
local section="$1"
local outbound_json
@@ -3171,13 +3214,24 @@ process_validate_rule() {
fi
if [ "$action" = "proxy" ]; then
local urltest_enabled
local urltest_enabled urltest_filter_mode
config_get_bool urltest_enabled "$section" "urltest_enabled" 0
if [ "$urltest_enabled" -eq 1 ]; then
config_get urltest_filter_mode "$section" "urltest_filter_mode" "disabled"
validate_urltest_filter_mode_option "$urltest_filter_mode" "$section"
urltest_check_interval="$(get_urltest_check_interval_for_rule "$section")"
validate_required_sing_box_duration_option "$urltest_check_interval" "rule.$section.urltest_check_interval"
config_list_foreach "$section" "urltest_exclude_countries" validate_country_code_option "$section"
config_list_foreach "$section" "urltest_exclude_regex" validate_urltest_exclude_regex_option "$section"
case "$urltest_filter_mode" in
include)
config_list_foreach "$section" "urltest_include_regex" validate_urltest_regex_option "$section"
config_list_foreach "$section" "urltest_include_countries" validate_country_code_option "$section"
;;
exclude)
config_list_foreach "$section" "urltest_exclude_countries" validate_country_code_option "$section"
config_list_foreach "$section" "urltest_exclude_outbounds" validate_outbound_name_option "$section"
config_list_foreach "$section" "urltest_exclude_regex" validate_urltest_regex_option "$section"
;;
esac
fi
if rule_has_subscription_urls "$section"; then
@@ -3284,6 +3338,22 @@ register_ruleset_tag() {
ENSURED_RULESET_TAG=""
ENSURED_RULESET_KIND="unknown"
get_community_ruleset_url() {
local name="$1"
case "$name" in
ads_hagezi_pro)
printf '%s\n' "$SRS_ADS_HAGEZI_PRO_URL"
;;
supercell)
printf '%s\n' "$SRS_SUPERCELL_URL"
;;
*)
printf '%s\n' "$SRS_MAIN_URL/$name.srs"
;;
esac
}
ensure_builtin_ruleset() {
local name="$1"
local tag="builtin-$name-ruleset"
@@ -3295,7 +3365,7 @@ ensure_builtin_ruleset() {
local detour update_interval url
detour="$(get_download_detour_tag)"
update_interval="$(get_remote_ruleset_update_interval)"
url="$SRS_MAIN_URL/$name.srs"
url="$(get_community_ruleset_url "$name")"
config=$(sing_box_cm_add_remote_ruleset "$config" "$tag" "binary" "$url" "$detour" "$update_interval")
register_ruleset_tag "$tag"
fi
@@ -3352,7 +3422,7 @@ ensure_custom_ruleset() {
ensure_inline_custom_ruleset() {
local reference="$1"
local tag hash detour update_interval extension
local tag hash detour update_interval extension format
hash="$(printf '%s' "$reference" | md5sum | cut -c1-12)"
tag="inline-custom-$hash-ruleset"
@@ -3380,7 +3450,8 @@ ensure_inline_custom_ruleset() {
config=$(sing_box_cm_add_remote_ruleset "$config" "$tag" "source" "$reference" "$detour" "$update_interval")
;;
*)
return 1
format="$(get_inline_remote_ruleset_format "$reference")"
config=$(sing_box_cm_add_remote_ruleset "$config" "$tag" "$format" "$reference" "$detour" "$update_interval")
;;
esac
;;
@@ -4727,7 +4798,7 @@ config_list_to_json() {
printf '%s\n' "$CONFIG_LIST_VALUES_JSON"
}
get_urltest_regex_excluded_tags_json() {
get_urltest_regex_matching_tags_json() {
local tags_json="$1"
local names_json="$2"
local regex_json="$3"
@@ -4773,6 +4844,10 @@ get_urltest_regex_excluded_tags_json() {
printf '%s\n' "$result_json"
}
get_urltest_regex_excluded_tags_json() {
get_urltest_regex_matching_tags_json "$@"
}
is_ipv6_literal() {
local value="$1"
@@ -5163,55 +5238,114 @@ filter_urltest_outbounds() {
local names_json="$3"
local countries_json="$4"
local country_filter_enabled="$5"
local exclude_names_json exclude_regex_json exclude_regex_tags_json exclude_countries_json filtered_json filtered_count original_count
local filter_mode exclude_names_json exclude_regex_json exclude_regex_tags_json exclude_countries_json \
include_names_json include_regex_json include_regex_tags_json filtered_json filtered_count original_count
exclude_names_json="$(config_list_to_json "$section" "urltest_exclude_outbounds")"
exclude_regex_json="$(config_list_to_json "$section" "urltest_exclude_regex")"
exclude_regex_tags_json="$(get_urltest_regex_excluded_tags_json "$tags_json" "$names_json" "$exclude_regex_json")"
[ -n "$exclude_regex_tags_json" ] || exclude_regex_tags_json="[]"
exclude_countries_json="[]"
if [ "$country_filter_enabled" -eq 1 ]; then
exclude_countries_json="$(config_list_to_json "$section" "urltest_exclude_countries" |
jq -c 'map(tostring | ascii_upcase | select(length == 2))' 2>/dev/null)"
[ -n "$exclude_countries_json" ] || exclude_countries_json="[]"
fi
config_get filter_mode "$section" "urltest_filter_mode" "disabled"
filtered_json="$(jq -cn \
--argjson tags "$tags_json" \
--argjson names "$names_json" \
--argjson countries "$countries_json" \
--argjson exclude_names "$exclude_names_json" \
--argjson exclude_regex_tags "$exclude_regex_tags_json" \
--argjson exclude_countries "$exclude_countries_json" '
reduce ($tags // [])[] as $tag (
[];
($names[$tag] // $tag) as $name
| (($countries[$tag] // "") | ascii_upcase) as $country
| if
(($exclude_names | index($name)) != null) or
(($exclude_names | index($tag)) != null) or
($country != "" and (($exclude_countries | index($country)) != null)) or
(($exclude_regex_tags | index($tag)) != null)
then
.
else
. + [$tag]
end
)
' 2>/dev/null)"
case "$filter_mode" in
disabled)
printf '%s\n' "$tags_json"
return 0
;;
include)
include_names_json="$(config_list_to_json "$section" "urltest_include_outbounds")"
include_regex_json="$(config_list_to_json "$section" "urltest_include_regex")"
include_regex_tags_json="$(get_urltest_regex_matching_tags_json "$tags_json" "$names_json" "$include_regex_json")"
[ -n "$include_regex_tags_json" ] || include_regex_tags_json="[]"
include_countries_json="[]"
if [ "$country_filter_enabled" -eq 1 ]; then
include_countries_json="$(config_list_to_json "$section" "urltest_include_countries" |
jq -c 'map(tostring | ascii_upcase | select(length == 2))' 2>/dev/null)"
[ -n "$include_countries_json" ] || include_countries_json="[]"
fi
filtered_json="$(jq -cn \
--argjson tags "$tags_json" \
--argjson names "$names_json" \
--argjson countries "$countries_json" \
--argjson include_names "$include_names_json" \
--argjson include_regex_tags "$include_regex_tags_json" \
--argjson include_countries "$include_countries_json" '
reduce ($tags // [])[] as $tag (
[];
($names[$tag] // $tag) as $name
| (($countries[$tag] // "") | ascii_upcase) as $country
| if
(($include_names | index($name)) != null) or
(($include_names | index($tag)) != null) or
($country != "" and (($include_countries | index($country)) != null)) or
(($include_regex_tags | index($tag)) != null)
then
. + [$tag]
else
.
end
)
' 2>/dev/null)"
;;
exclude)
exclude_names_json="$(config_list_to_json "$section" "urltest_exclude_outbounds")"
exclude_regex_json="$(config_list_to_json "$section" "urltest_exclude_regex")"
exclude_regex_tags_json="$(get_urltest_regex_matching_tags_json "$tags_json" "$names_json" "$exclude_regex_json")"
[ -n "$exclude_regex_tags_json" ] || exclude_regex_tags_json="[]"
exclude_countries_json="[]"
if [ "$country_filter_enabled" -eq 1 ]; then
exclude_countries_json="$(config_list_to_json "$section" "urltest_exclude_countries" |
jq -c 'map(tostring | ascii_upcase | select(length == 2))' 2>/dev/null)"
[ -n "$exclude_countries_json" ] || exclude_countries_json="[]"
fi
filtered_json="$(jq -cn \
--argjson tags "$tags_json" \
--argjson names "$names_json" \
--argjson countries "$countries_json" \
--argjson exclude_names "$exclude_names_json" \
--argjson exclude_regex_tags "$exclude_regex_tags_json" \
--argjson exclude_countries "$exclude_countries_json" '
reduce ($tags // [])[] as $tag (
[];
($names[$tag] // $tag) as $name
| (($countries[$tag] // "") | ascii_upcase) as $country
| if
(($exclude_names | index($name)) != null) or
(($exclude_names | index($tag)) != null) or
($country != "" and (($exclude_countries | index($country)) != null)) or
(($exclude_regex_tags | index($tag)) != null)
then
.
else
. + [$tag]
end
)
' 2>/dev/null)"
;;
*)
printf '%s\n' "$tags_json"
return 0
;;
esac
[ -n "$filtered_json" ] || filtered_json="$tags_json"
filtered_count="$(printf '%s' "$filtered_json" | jq -r 'length' 2>/dev/null)"
original_count="$(printf '%s' "$tags_json" | jq -r 'length' 2>/dev/null)"
if [ -z "$filtered_count" ] || [ "$filtered_count" -eq 0 ]; then
log "URLTest filters excluded all outbounds for rule '$section'; URLTest will be disabled for this rule" "warn"
if [ "$filter_mode" = "include" ]; then
log "URLTest whitelist matched no outbounds for rule '$section'; URLTest will be disabled for this rule" "warn"
else
log "URLTest filters excluded all outbounds for rule '$section'; URLTest will be disabled for this rule" "warn"
fi
printf '[]\n'
return 0
fi
if [ -n "$original_count" ] && [ "$filtered_count" -lt "$original_count" ]; then
log "URLTest filters excluded $((original_count - filtered_count)) outbound(s) for rule '$section'" "info"
if [ "$filter_mode" = "include" ]; then
log "URLTest whitelist selected $filtered_count of $original_count outbound(s) for rule '$section'" "info"
else
log "URLTest filters excluded $((original_count - filtered_count)) outbound(s) for rule '$section'" "info"
fi
fi
printf '%s\n' "$filtered_json"
@@ -6043,7 +6177,7 @@ configure_community_list_handler() {
local ruleset_tag format url update_interval detour
ruleset_tag="$(get_ruleset_tag "$section" "$tag" "community")"
format="binary"
url="$SRS_MAIN_URL/$tag.srs"
url="$(get_community_ruleset_url "$tag")"
detour="$(get_download_detour_tag)"
update_interval="$(get_remote_ruleset_update_interval)"
@@ -6358,7 +6492,7 @@ import_inline_custom_ruleset_subnets_from_rule() {
import_inline_custom_ruleset_subnets_reference_handler() {
local reference="$1"
local extension
local extension format
case "$reference" in
/*.srs)
@@ -6376,6 +6510,10 @@ import_inline_custom_ruleset_subnets_reference_handler() {
json)
import_custom_ruleset_subnets_from_remote "$reference" "source"
;;
*)
format="$(get_inline_remote_ruleset_format "$reference")"
import_custom_ruleset_subnets_from_remote "$reference" "$format"
;;
esac
;;
esac
@@ -6397,7 +6535,7 @@ import_rule_sets_with_subnets_from_rule() {
import_rule_set_with_subnets_reference_handler() {
local reference="$1"
local section="$2"
local extension
local extension format
log "Importing subnets from rule set reference for '$section': $reference"
@@ -6422,7 +6560,9 @@ import_rule_set_with_subnets_reference_handler() {
log "Failed to import subnets from remote JSON rule set: $reference" "error"
;;
*)
log "Unsupported rule set extension for subnet import: $reference" "error"
format="$(get_inline_remote_ruleset_format "$reference")"
import_custom_ruleset_subnets_from_remote "$reference" "$format" || \
log "Failed to import subnets from remote rule set: $reference" "error"
;;
esac
;;
+3 -1
View File
@@ -67,6 +67,8 @@ SB_CLASH_API_CONTROLLER_PORT=9090
## Lists
GITHUB_RAW_URL="https://raw.githubusercontent.com/itdoginfo/allow-domains/main"
SRS_MAIN_URL="https://github.com/itdoginfo/allow-domains/releases/latest/download"
SRS_ADS_HAGEZI_PRO_URL="https://github.com/zxc-rv/ad-filter/releases/latest/download/adlist.srs"
SRS_SUPERCELL_URL="https://raw.githubusercontent.com/ushan0v/sing-box-supercell-ruleset/main/supercell.srs"
SUBNETS_TWITTER="${GITHUB_RAW_URL}/Subnets/IPv4/twitter.lst"
SUBNETS_META="${GITHUB_RAW_URL}/Subnets/IPv4/meta.lst"
SUBNETS_DISCORD="${GITHUB_RAW_URL}/Subnets/IPv4/discord.lst"
@@ -77,7 +79,7 @@ SUBNETS_HETZNER="${GITHUB_RAW_URL}/Subnets/IPv4/hetzner.lst"
SUBNETS_OVH="${GITHUB_RAW_URL}/Subnets/IPv4/ovh.lst"
SUBNETS_DIGITALOCEAN="${GITHUB_RAW_URL}/Subnets/IPv4/digitalocean.lst"
SUBNETS_CLOUDFRONT="${GITHUB_RAW_URL}/Subnets/IPv4/cloudfront.lst"
COMMUNITY_SERVICES="russia_inside russia_outside ukraine_inside geoblock block porn news anime youtube hdrezka tiktok google_ai google_play hodca discord meta twitter cloudflare cloudfront digitalocean hetzner ovh telegram roblox"
COMMUNITY_SERVICES="russia_inside russia_outside ukraine_inside geoblock block porn news anime youtube hdrezka tiktok google_ai google_play hodca discord meta twitter cloudflare cloudfront digitalocean hetzner ovh telegram roblox ads_hagezi_pro supercell"
## Zapret
ZAPRET_PROVIDER_BASE_DIR="/opt/zapret"