fix: parental control global network block, luci save&apply reload, and fuzzer localization

This commit is contained in:
Dushnilin
2026-09-11 22:35:59 +03:00
parent 77241861af
commit 5933fd2f0c
15 changed files with 2077 additions and 1523 deletions
File diff suppressed because it is too large Load Diff
+72
View File
@@ -9240,3 +9240,75 @@ msgstr ""
#: src/tachyon/tabs/diagnostic/checks/runZapret2Check.ts:115
msgid "Zapret2 sing-box route rules are not configured"
msgstr ""
msgid "+ add"
msgstr ""
msgid "⚠️ Server received fakes"
msgstr ""
msgid "✓ 32KB Streamed"
msgstr ""
msgid "✗ Throttled at 16KB"
msgstr ""
msgid "Analysis inconclusive."
msgstr ""
msgid "Blocked Sites (Only block domains specified below)"
msgstr ""
msgid "DNS Blocking"
msgstr ""
msgid "DNS resolution failed — likely DNS-level blocking or hijacking."
msgstr ""
msgid "DPI bandwidth throttling or delay detected — connection throttled during data transfer."
msgstr ""
msgid "endpoints OK"
msgstr ""
msgid "IP Block (TCP SYN Timeout)"
msgstr ""
msgid "IP-level blocking detected! The resource is blocked at the network level (no TCP SYN response). DPI bypass methods (Zapret / ByeDPI) cannot bypass this address — configure proxy/VPN routing (Sing-box) for this domain!"
msgstr ""
msgid "No Blocking Detected"
msgstr ""
msgid "Target accessible — no DPI blocking detected."
msgstr ""
msgid "TCP connect timed out before TLS handshake — host is blocked at the IP layer. DPI bypass cannot unblock this; route via Sing-box VPN/Proxy outbound instead."
msgstr ""
msgid "TCP Reset Injection"
msgstr ""
msgid "TCP RST received from DPI — active TCP reset injection detected."
msgstr ""
msgid "Testing:"
msgstr ""
msgid "Throttling / Deep Inspection"
msgstr ""
msgid "Unknown DPI Pattern"
msgstr ""
msgid "Zapret v1"
msgstr ""
msgid "Zapret v2"
msgstr ""
msgid "Allow only: "
msgstr ""
msgid "Block: "
msgstr ""
+81 -9
View File
@@ -855,7 +855,7 @@ msgid "Cache file path cannot be empty"
msgstr "Путь к файлу кэша не может быть пустым"
msgid "Camou"
msgstr ""
msgstr "Camou"
msgid "Cancel"
msgstr "Отмена"
@@ -870,7 +870,7 @@ msgid "Cannot save settings"
msgstr "Не удалось сохранить настройки"
msgid "Captive"
msgstr ""
msgstr "Captive"
msgid "Cascade connection"
msgstr "Каскадное подключение"
@@ -993,7 +993,7 @@ msgid "Click to show"
msgstr "Нажмите, чтобы показать"
msgid "Client"
msgstr ""
msgstr "Клиент"
msgid "Client Address"
msgstr "Адрес клиента"
@@ -1263,7 +1263,7 @@ msgid "Dedicated cgroup unavailable"
msgstr "Выделенная cgroup недоступна"
msgid "Default"
msgstr ""
msgstr "По умолчанию"
msgid "Default (auto)"
msgstr "По умолчанию (авто)"
@@ -2130,7 +2130,7 @@ msgid "France (fr)"
msgstr "Франция (fr)"
msgid "Freedom"
msgstr ""
msgstr "Freedom"
msgid "Fri"
msgstr "Пт"
@@ -2787,7 +2787,7 @@ msgid "Japan (jp)"
msgstr "Япония (jp)"
msgid "Jitsi"
msgstr ""
msgstr "Jitsi"
msgid "JSON outbound"
msgstr "JSON outbound"
@@ -5649,10 +5649,10 @@ msgid "When switching to another level, test every server and select the fastest
msgstr "При переключении на другой уровень тестировать каждый сервер и выбирать самый быстрый вместо первого рабочего."
msgid "WHEP"
msgstr ""
msgstr "WHEP"
msgid "WHIP"
msgstr ""
msgstr "WHIP"
msgid "Why Discord fails?"
msgstr "Почему не работает Discord?"
@@ -5670,7 +5670,7 @@ msgid "working strategies"
msgstr "рабочих стратегий"
msgid "WRT"
msgstr ""
msgstr "WRT"
msgid "XHTTP mode"
msgstr "Режим XHTTP"
@@ -6505,3 +6505,75 @@ msgstr "Скачивание и обработка списков"
msgid "Applying rules and subnet lists"
msgstr "Применение правил и списков подсетей"
msgid "+ add"
msgstr "+ добавить"
msgid "⚠️ Server received fakes"
msgstr "⚠️ Сервер получил фейки"
msgid "✓ 32KB Streamed"
msgstr "✓ 32KB Прокачано"
msgid "✗ Throttled at 16KB"
msgstr "✗ Задушено на 16KB"
msgid "Analysis inconclusive."
msgstr "Анализ не дал однозначного результата."
msgid "Blocked Sites (Only block domains specified below)"
msgstr "Заблокированные сайты (Блокировать только указанные домены)"
msgid "DNS Blocking"
msgstr "Блокировка DNS"
msgid "DNS resolution failed — likely DNS-level blocking or hijacking."
msgstr "Сбой разрешения DNS — вероятно, блокировка или перехват на уровне DNS."
msgid "DPI bandwidth throttling or delay detected — connection throttled during data transfer."
msgstr "Обнаружено замедление DPI — соединение заблокировано или задушено при передаче данных."
msgid "endpoints OK"
msgstr "эндпоинтов доступно"
msgid "IP Block (TCP SYN Timeout)"
msgstr "Блокировка по IP (Таймаут TCP SYN)"
msgid "IP-level blocking detected! The resource is blocked at the network level (no TCP SYN response). DPI bypass methods (Zapret / ByeDPI) cannot bypass this address — configure proxy/VPN routing (Sing-box) for this domain!"
msgstr "Обнаружена блокировка по IP! Ресурс блокируется на сетевом уровне (нет ответа на TCP SYN). Методы обхода DPI (Zapret / ByeDPI) бессильны для этого адреса — настройте маршрутизацию через прокси/VPN (Sing-box) для данного домена!"
msgid "No Blocking Detected"
msgstr "Блокировка не обнаружена"
msgid "Target accessible — no DPI blocking detected."
msgstr "Цель доступна — блокировок DPI не обнаружено."
msgid "TCP connect timed out before TLS handshake — host is blocked at the IP layer. DPI bypass cannot unblock this; route via Sing-box VPN/Proxy outbound instead."
msgstr "Таймаут TCP-подключения до рукопожатия TLS — узел заблокирован на уровне IP. Методы обхода DPI не помогут; настройте маршрутизацию через прокси/VPN Sing-box."
msgid "TCP Reset Injection"
msgstr "Внедрение пакетов TCP Reset (RST)"
msgid "TCP RST received from DPI — active TCP reset injection detected."
msgstr "Получен TCP RST от ТСПУ/DPI — зафиксирована активная инъекция сброса соединения."
msgid "Testing:"
msgstr "Тестирование:"
msgid "Throttling / Deep Inspection"
msgstr "Замедление / Глубокий анализ пакетов"
msgid "Unknown DPI Pattern"
msgstr "Неизвестный тип DPI"
msgid "Zapret v1"
msgstr "Zapret v1"
msgid "Zapret v2"
msgstr "Zapret v2"
msgid "Allow only: "
msgstr "Разрешить только: "
msgid "Block: "
msgstr "Блокировать: "
@@ -623,7 +623,7 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
const input = E('input', {
type: 'text',
class: 'cbi-input-text',
placeholder: '+ add',
placeholder: _('+ add'),
style: 'width: 80px; padding: 2px 6px; font-size: 11px;',
});
@@ -888,9 +888,9 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
style: 'flex: 0 1 140px; min-width: 120px;',
},
[
E('option', { value: 'zapret2' }, 'Zapret v2'),
E('option', { value: 'zapret' }, 'Zapret v1'),
E('option', { value: 'byedpi' }, 'ByeDPI'),
E('option', { value: 'zapret2' }, _('Zapret v2')),
E('option', { value: 'zapret' }, _('Zapret v1')),
E('option', { value: 'byedpi' }, _('ByeDPI')),
],
);
const argsInput = E('input', {
@@ -1417,7 +1417,7 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
'Data transfer verified: streamed >= 32KB without throttling or disconnect',
),
},
'✓ 32KB Прокачано',
_('✓ 32KB Streamed'),
),
);
} else if (item.dpi_verdict === 'throttled_16k') {
@@ -1432,7 +1432,7 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
'DPI throttling: stream cut off after ~16KB payload transfer',
),
},
'✗ Задушено на 16KB',
_('✗ Throttled at 16KB'),
),
);
} else if (item.dpi_verdict === 'server_fakes') {
@@ -1447,7 +1447,7 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
'Fake packets reached remote server (HTTP 400 Bad Request)',
),
},
'⚠️ Сервер получил фейки',
_('⚠️ Server received fakes'),
),
);
}
@@ -1459,7 +1459,7 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
E(
'div',
{ style: 'font-size: 10px; opacity: 0.75; margin-top: 2px;' },
`${passedSub}/${totalSub} endpoints OK`,
`${passedSub}/${totalSub} ` + _('endpoints OK'),
),
);
}
@@ -1624,7 +1624,7 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
if (currentStratEl) {
if (state.running && state.current_strategy) {
currentStratEl.innerText = `Testing: [${state.current_strategy.name}] -> ${state.current_strategy.args}`;
currentStratEl.innerText = `${_('Testing:')} [${state.current_strategy.name}] -> ${state.current_strategy.args}`;
} else {
currentStratEl.innerText = '';
}
@@ -1740,12 +1740,40 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
};
const typeLabels: Record<string, string> = {
rst: 'TCP Reset Injection',
throttle: 'Throttling / Deep Inspection',
dns_block: 'DNS Blocking',
ip_block: _('Блокировка по IP (Таймаут TCP SYN)'),
unknown: 'Unknown DPI Pattern',
none: 'No Blocking Detected',
rst: _('TCP Reset Injection'),
throttle: _('Throttling / Deep Inspection'),
dns_block: _('DNS Blocking'),
ip_block: _('IP Block (TCP SYN Timeout)'),
unknown: _('Unknown DPI Pattern'),
none: _('No Blocking Detected'),
};
const getLocalizedDpiDetails = (
det: Tachyon.FuzzerDpiDetection | null | undefined,
) => {
if (!det) return '';
switch (det.type) {
case 'ip_block':
return _(
'TCP connect timed out before TLS handshake — host is blocked at the IP layer. DPI bypass cannot unblock this; route via Sing-box VPN/Proxy outbound instead.',
);
case 'rst':
return _(
'TCP RST received from DPI — active TCP reset injection detected.',
);
case 'dns_block':
return _(
'DNS resolution failed — likely DNS-level blocking or hijacking.',
);
case 'throttle':
return _(
'DPI bandwidth throttling or delay detected — connection throttled during data transfer.',
);
case 'none':
return _('Target accessible — no DPI blocking detected.');
default:
return det.details || _('Analysis inconclusive.');
}
};
const meta = typeMeta[detection.type] || typeMeta.unknown;
@@ -1754,7 +1782,7 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
const ipBlockNotice =
detection.type === 'ip_block'
? `<div class="alert-message danger" style="margin-top: 8px;">
⚠️ ${_('Обнаружена блокировка по IP! Ресурс блокируется на сетевом уровне (нет ответа на TCP SYN). Методы обхода DPI (Zapret / ByeDPI) бессильны для этого адреса — настройте маршрутизацию через прокси/VPN (Sing-box) для данного домена!')}
⚠️ ${_('IP-level blocking detected! The resource is blocked at the network level (no TCP SYN response). DPI bypass methods (Zapret / ByeDPI) cannot bypass this address — configure proxy/VPN routing (Sing-box) for this domain!')}
</div>`
: '';
@@ -1768,7 +1796,7 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
</div>
${detection.recommended_engines.length > 0 ? `<div style="font-size: 11px; opacity: 0.8;">${_('Recommended')}: ${detection.recommended_engines.join(', ')}</div>` : ''}
</div>
<div style="margin-top: 4px; opacity: 0.9; font-size: 11px;">${detection.details}</div>
<div style="margin-top: 4px; opacity: 0.9; font-size: 11px;">${getLocalizedDpiDetails(detection)}</div>
${ipBlockNotice}
`;
};
+1
View File
@@ -15,6 +15,7 @@ export default defineConfig({
},
esbuildOptions(options) {
options.legalComments = 'none';
options.charset = 'utf8';
},
onSuccess: () => {
const outDir =
File diff suppressed because it is too large Load Diff
@@ -1081,7 +1081,8 @@ function createParentalContent(section) {
),
);
o.modalonly = true;
o.default = "all";
o.default = "domains";
o.value("domains", _("Blocked Sites (Only block domains specified below)"));
o.value("all", _("All Internet (Complete internet cutoff)"));
o.value("sections", _("Specific Sections (Select sections below)"));
@@ -433,58 +433,7 @@ const EntryPoint = {
return form.Map.prototype.load.call(self);
});
};
tachyonMap.handleSaveApply = function (ev, mode) {
const refreshUiState = function () {
main.TachyonShellMethods.getUiState()
.then((response) => {
if (
response?.success &&
typeof main.applyUiStateToStore === "function"
) {
main.applyUiStateToStore(response.data);
}
})
.catch(() => null);
};
if (main.store && typeof main.store.set === "function") {
const servicesInfoWidget = main.store.get().servicesInfoWidget;
main.store.set({
servicesInfoWidget: {
...servicesInfoWidget,
data: {
...servicesInfoWidget.data,
tachyonStatus: "reloading",
},
},
});
}
return this.handleSave(ev)
.then(() => ui.changes.apply(mode == "0"))
.then((result) => {
if (
main.TachyonShellMethods &&
typeof main.TachyonShellMethods.serviceActionStart === "function"
) {
return main.TachyonShellMethods.serviceActionStart("reload")
.then(() => {
window.setTimeout(refreshUiState, 500);
return result;
})
.catch(() => {
window.setTimeout(refreshUiState, 500);
return result;
});
}
window.setTimeout(refreshUiState, 500);
return result;
})
.catch((error) => {
refreshUiState();
throw error;
});
};
tachyonMap.handleSaveApply = EntryPoint.handleSaveApply;
const rawTabOrder = uci.get(UCI_PACKAGE, "settings", "tab_order");
const savedOrder = Array.isArray(rawTabOrder)
@@ -758,6 +707,66 @@ const EntryPoint = {
return rendered;
},
handleSaveApply: function (ev, mode) {
const refreshUiState = function () {
if (
main.TachyonShellMethods &&
typeof main.TachyonShellMethods.getUiState === "function"
) {
main.TachyonShellMethods.getUiState()
.then((response) => {
if (
response?.success &&
typeof main.applyUiStateToStore === "function"
) {
main.applyUiStateToStore(response.data);
}
})
.catch(() => null);
}
};
if (main.store && typeof main.store.set === "function") {
const servicesInfoWidget = main.store.get()?.servicesInfoWidget;
if (servicesInfoWidget) {
main.store.set({
servicesInfoWidget: {
...servicesInfoWidget,
data: {
...servicesInfoWidget.data,
tachyonStatus: "reloading",
},
},
});
}
}
return this.handleSave(ev)
.then(() => ui.changes.apply(mode == "0"))
.then((result) => {
if (
main.TachyonShellMethods &&
typeof main.TachyonShellMethods.serviceActionStart === "function"
) {
return main.TachyonShellMethods.serviceActionStart("reload")
.then(() => {
window.setTimeout(refreshUiState, 500);
return result;
})
.catch(() => {
window.setTimeout(refreshUiState, 500);
return result;
});
}
window.setTimeout(refreshUiState, 500);
return result;
})
.catch((error) => {
refreshUiState();
throw error;
});
},
};
return view.extend(EntryPoint);
+81 -9
View File
@@ -855,7 +855,7 @@ msgid "Cache file path cannot be empty"
msgstr "Путь к файлу кэша не может быть пустым"
msgid "Camou"
msgstr ""
msgstr "Camou"
msgid "Cancel"
msgstr "Отмена"
@@ -870,7 +870,7 @@ msgid "Cannot save settings"
msgstr "Не удалось сохранить настройки"
msgid "Captive"
msgstr ""
msgstr "Captive"
msgid "Cascade connection"
msgstr "Каскадное подключение"
@@ -993,7 +993,7 @@ msgid "Click to show"
msgstr "Нажмите, чтобы показать"
msgid "Client"
msgstr ""
msgstr "Клиент"
msgid "Client Address"
msgstr "Адрес клиента"
@@ -1263,7 +1263,7 @@ msgid "Dedicated cgroup unavailable"
msgstr "Выделенная cgroup недоступна"
msgid "Default"
msgstr ""
msgstr "По умолчанию"
msgid "Default (auto)"
msgstr "По умолчанию (авто)"
@@ -2130,7 +2130,7 @@ msgid "France (fr)"
msgstr "Франция (fr)"
msgid "Freedom"
msgstr ""
msgstr "Freedom"
msgid "Fri"
msgstr "Пт"
@@ -2787,7 +2787,7 @@ msgid "Japan (jp)"
msgstr "Япония (jp)"
msgid "Jitsi"
msgstr ""
msgstr "Jitsi"
msgid "JSON outbound"
msgstr "JSON outbound"
@@ -5649,10 +5649,10 @@ msgid "When switching to another level, test every server and select the fastest
msgstr "При переключении на другой уровень тестировать каждый сервер и выбирать самый быстрый вместо первого рабочего."
msgid "WHEP"
msgstr ""
msgstr "WHEP"
msgid "WHIP"
msgstr ""
msgstr "WHIP"
msgid "Why Discord fails?"
msgstr "Почему не работает Discord?"
@@ -5670,7 +5670,7 @@ msgid "working strategies"
msgstr "рабочих стратегий"
msgid "WRT"
msgstr ""
msgstr "WRT"
msgid "XHTTP mode"
msgstr "Режим XHTTP"
@@ -6505,3 +6505,75 @@ msgstr "Скачивание и обработка списков"
msgid "Applying rules and subnet lists"
msgstr "Применение правил и списков подсетей"
msgid "+ add"
msgstr "+ добавить"
msgid "⚠️ Server received fakes"
msgstr "⚠️ Сервер получил фейки"
msgid "✓ 32KB Streamed"
msgstr "✓ 32KB Прокачано"
msgid "✗ Throttled at 16KB"
msgstr "✗ Задушено на 16KB"
msgid "Analysis inconclusive."
msgstr "Анализ не дал однозначного результата."
msgid "Blocked Sites (Only block domains specified below)"
msgstr "Заблокированные сайты (Блокировать только указанные домены)"
msgid "DNS Blocking"
msgstr "Блокировка DNS"
msgid "DNS resolution failed — likely DNS-level blocking or hijacking."
msgstr "Сбой разрешения DNS — вероятно, блокировка или перехват на уровне DNS."
msgid "DPI bandwidth throttling or delay detected — connection throttled during data transfer."
msgstr "Обнаружено замедление DPI — соединение заблокировано или задушено при передаче данных."
msgid "endpoints OK"
msgstr "эндпоинтов доступно"
msgid "IP Block (TCP SYN Timeout)"
msgstr "Блокировка по IP (Таймаут TCP SYN)"
msgid "IP-level blocking detected! The resource is blocked at the network level (no TCP SYN response). DPI bypass methods (Zapret / ByeDPI) cannot bypass this address — configure proxy/VPN routing (Sing-box) for this domain!"
msgstr "Обнаружена блокировка по IP! Ресурс блокируется на сетевом уровне (нет ответа на TCP SYN). Методы обхода DPI (Zapret / ByeDPI) бессильны для этого адреса — настройте маршрутизацию через прокси/VPN (Sing-box) для данного домена!"
msgid "No Blocking Detected"
msgstr "Блокировка не обнаружена"
msgid "Target accessible — no DPI blocking detected."
msgstr "Цель доступна — блокировок DPI не обнаружено."
msgid "TCP connect timed out before TLS handshake — host is blocked at the IP layer. DPI bypass cannot unblock this; route via Sing-box VPN/Proxy outbound instead."
msgstr "Таймаут TCP-подключения до рукопожатия TLS — узел заблокирован на уровне IP. Методы обхода DPI не помогут; настройте маршрутизацию через прокси/VPN Sing-box."
msgid "TCP Reset Injection"
msgstr "Внедрение пакетов TCP Reset (RST)"
msgid "TCP RST received from DPI — active TCP reset injection detected."
msgstr "Получен TCP RST от ТСПУ/DPI — зафиксирована активная инъекция сброса соединения."
msgid "Testing:"
msgstr "Тестирование:"
msgid "Throttling / Deep Inspection"
msgstr "Замедление / Глубокий анализ пакетов"
msgid "Unknown DPI Pattern"
msgstr "Неизвестный тип DPI"
msgid "Zapret v1"
msgstr "Zapret v1"
msgid "Zapret v2"
msgstr "Zapret v2"
msgid "Allow only: "
msgstr "Разрешить только: "
msgid "Block: "
msgstr "Блокировать: "
+72
View File
@@ -9240,3 +9240,75 @@ msgstr ""
#: src/tachyon/tabs/diagnostic/checks/runZapret2Check.ts:115
msgid "Zapret2 sing-box route rules are not configured"
msgstr ""
msgid "+ add"
msgstr ""
msgid "⚠️ Server received fakes"
msgstr ""
msgid "✓ 32KB Streamed"
msgstr ""
msgid "✗ Throttled at 16KB"
msgstr ""
msgid "Analysis inconclusive."
msgstr ""
msgid "Blocked Sites (Only block domains specified below)"
msgstr ""
msgid "DNS Blocking"
msgstr ""
msgid "DNS resolution failed — likely DNS-level blocking or hijacking."
msgstr ""
msgid "DPI bandwidth throttling or delay detected — connection throttled during data transfer."
msgstr ""
msgid "endpoints OK"
msgstr ""
msgid "IP Block (TCP SYN Timeout)"
msgstr ""
msgid "IP-level blocking detected! The resource is blocked at the network level (no TCP SYN response). DPI bypass methods (Zapret / ByeDPI) cannot bypass this address — configure proxy/VPN routing (Sing-box) for this domain!"
msgstr ""
msgid "No Blocking Detected"
msgstr ""
msgid "Target accessible — no DPI blocking detected."
msgstr ""
msgid "TCP connect timed out before TLS handshake — host is blocked at the IP layer. DPI bypass cannot unblock this; route via Sing-box VPN/Proxy outbound instead."
msgstr ""
msgid "TCP Reset Injection"
msgstr ""
msgid "TCP RST received from DPI — active TCP reset injection detected."
msgstr ""
msgid "Testing:"
msgstr ""
msgid "Throttling / Deep Inspection"
msgstr ""
msgid "Unknown DPI Pattern"
msgstr ""
msgid "Zapret v1"
msgstr ""
msgid "Zapret v2"
msgstr ""
msgid "Allow only: "
msgstr ""
msgid "Block: "
msgstr ""
+1
View File
@@ -34,6 +34,7 @@ start_service() {
initd_ucode start-service "$1" "$$"
TACHYON_LAST_START_STATUS="$?"
trap - INT TERM
[ "$TACHYON_LAST_START_STATUS" = "0" ] && sync_service_triggers
return "$TACHYON_LAST_START_STATUS"
}
+1 -1
View File
@@ -1875,7 +1875,7 @@ function validate_schedule(schedule, sections, profiles) {
if (length(sec_names) == 0) {
let single_sec = option(schedule, "sections", "");
if (single_sec != "") sec_names = [ single_sec ];
else if (target != "all" && target != "sections" && target != "") sec_names = [ target ];
else if (target != "all" && target != "sections" && target != "domains" && target != "") sec_names = [ target ];
}
for (let sec_name in sec_names) {
let found = false;
+3 -2
View File
@@ -1055,7 +1055,8 @@ function nft_add_schedule_rules_from_schedules(schedules, sections, table, profi
[ "meta", "hour", sprintf("\"%s\"-\"%s\"", interval[0], interval[1]) ] : null;
let base_match = nft_schedule_rule_base_match(dev_str, is_mac, family, days_args, time_args);
if (target == "all" || (length(target_sec_names) == 0 && target != "sections")) {
let has_sched_domains = length(list_option(schedule, "blocked_domains")) > 0 || option(schedule, "blocked_domains", "") != "";
if ((target == "all" || (length(target_sec_names) == 0 && target != "sections" && target != "domains")) && !has_sched_domains && target != "domains") {
let fwd_rule = [];
append_array(fwd_rule, base_match);
append_array(fwd_rule, [ "counter", verdict ]);
@@ -1067,7 +1068,7 @@ function nft_add_schedule_rules_from_schedules(schedules, sections, table, profi
append_array(ctrl_rule, [ "counter", verdict ]);
if (!nft_add_rule(table, "parental_control", ctrl_rule))
log_debug("nft_add_schedule_rules: failed to add parental_control rule for " + dev_str);
} else {
} else if (length(target_sec_names) > 0) {
for (let sec_name in target_sec_names) {
let target_section = section_by_name(sections, sec_name);
if (!target_section) continue;
+3
View File
@@ -291,7 +291,10 @@ function plan_state_from_file(path) {
zapret2_queue: as_string(state.zapret2_queue_signature),
zapret2_runtime: as_string(state.zapret2_runtime_signature),
byedpi_runtime: as_string(state.byedpi_runtime_signature),
wdtt_runtime: as_string(state.wdtt_runtime_signature),
olcrtc_runtime: as_string(state.olcrtc_runtime_signature),
list: as_string(state.list_signature),
hosts_list: as_string(state.hosts_list_signature),
cron: as_string(state.cron_signature),
urltest_sections_known: state.__has.urltest_enabled_sections === true,
urltest_sections: as_string(state.urltest_enabled_sections),
+34
View File
@@ -287,6 +287,40 @@ parental_jump_line="$(grep -n "mangle jump parental_control" "$NFT_LOG" | cut -d
if [ "$local_return_line" -ge "$parental_jump_line" ]; then
fail "localv4 return (line $local_return_line) must precede jump parental_control (line $parental_jump_line) to protect router panel access"
fi
# ─── Domain-based schedule must NOT add global counter drop rules ────────────
cat >"$WORK_DIR/domain_schedule_fixture.json" <<'JSON'
{
"schedule": [
{
".name": "domain_only_sched",
"enabled": "1",
"device_ip": "192.168.1.180",
"target": "all",
"blocked_domains": [ "tiktok.com", "roblox.com" ],
"action": "block"
},
{
".name": "target_domains_sched",
"enabled": "1",
"device_ip": "192.168.1.181",
"target": "domains",
"action": "block"
}
]
}
JSON
rm -f "$NFT_LOG"
touch "$NFT_LOG"
nft_ucode nft-add-schedule-rules-fixture "$WORK_DIR/domain_schedule_fixture.json" "tachyon"
if grep -Eq "parental_(forward|control).*192\.168\.1\.180.*counter.*drop" "$NFT_LOG"; then
fail "Schedule with blocked_domains must NOT add global counter drop in parental_forward/parental_control"
fi
if grep -Eq "parental_(forward|control).*192\.168\.1\.181.*counter.*drop" "$NFT_LOG"; then
fail "Schedule with target=domains must NOT add global counter drop in parental_forward/parental_control"
fi
printf 'Parental control, profiles and schedule tests passed\n'