Compare commits

...

15 Commits

Author SHA1 Message Date
itdoginfo
25c887a952 v0.3.27 2025-03-12 17:20:35 +03:00
itdoginfo
e7a3c7adf1 Merge pull request #63 from itdoginfo/chore/fakeip-method
feat: update DNS checks and improve FakeIP status reporting
2025-03-12 17:02:18 +03:00
Ivan K
3e96b9a1af feat: update DNS checks and improve FakeIP status reporting 2025-03-12 16:20:59 +03:00
itdoginfo
251f94cb88 v0.3.26 2025-03-12 14:59:03 +03:00
itdoginfo
44936c698e Merge pull request #62 from itdoginfo/chore/fakeip-method
feat: add CLI check for FakeIP functionality and update status display
2025-03-12 14:57:41 +03:00
Ivan K
0faaca12fc сhore: remove tabs 2025-03-12 14:54:56 +03:00
Ivan K
c6d1f05916 feat: add CLI check for FakeIP functionality and update status display 2025-03-11 19:14:21 +03:00
itdoginfo
57554d518b v0.3.25 2025-03-11 18:39:30 +03:00
itdoginfo
09d761956c Some fixes 2025-03-11 18:39:18 +03:00
itdoginfo
ada807fec3 v0.3.24 2025-03-07 14:46:45 +03:00
itdoginfo
b28a5f1293 New default TTL=60, DOH=8.8.8.8 2025-03-07 14:46:22 +03:00
itdoginfo
2332eae5ff Added dns and github checker. JSON file for custom URL lists 2025-03-07 14:45:36 +03:00
itdoginfo
a755b6661d Merge pull request #59 from itdoginfo/feat/multiple-mixed-inbounds
Add support for multiple mixed inbounds with unique ports
2025-03-07 13:10:32 +03:00
Nikita Skryabin
567ce52253 feat: add support for multiple mixed inbounds with unique ports 2025-03-06 22:54:25 +03:00
Nikita Skryabin
b736360b66 fix: ensure routing rule for mixed-in is always applied 2025-03-06 21:55:40 +03:00
9 changed files with 415 additions and 121 deletions

View File

@@ -74,16 +74,19 @@ Luci: Services/podkop
**Custom subnets enable** - Добавить подсети или IP-адреса. Для подсетей задать маску. **Custom subnets enable** - Добавить подсети или IP-адреса. Для подсетей задать маску.
# Известные баги # Известные баги
- [ ] Не отрабатывает service podkop stop, если podkop запущен и не может, к пример, зарезолвить домен с сломанным DNS - [x] Не отрабатывает service podkop stop, если podkop запущен и не может, к пример, зарезолвить домен с сломанным DNS
- [ ] Update list из remote url domain не удаляет старые домены. А добавляет новые. Для подсетей тоже самое скорее всего. Пересоздавать ruleset? - [x] Update list из remote url domain не удаляет старые домены. А добавляет новые. Для подсетей тоже самое скорее всего. Пересоздавать ruleset?
# ToDo # ToDo
Этот раздел не означает задачи, которые нужно брать и делать. Это общий список хотелок. Если вы хотите помочь, пожалуйста, спросите сначала в телеграмме. Этот раздел не означает задачи, которые нужно брать и делать. Это общий список хотелок. Если вы хотите помочь, пожалуйста, спросите сначала в телеграмме.
- [ ] Проверка, что версия в makefile совпадает с тегом - [ ] Проверка, что версия в makefile совпадает с тегом
- [ ] Диагностика: Proxy check completed successfully предположительно не показывает IP, если вернулся это IPv6.
- [ ] Сделать галку запрещающую подкопу редачить dhcp. Допилить в исключение вместе с пустыми полями proxy и vpn - [ ] Сделать галку запрещающую подкопу редачить dhcp. Допилить в исключение вместе с пустыми полями proxy и vpn
- [ ] Обработка ошибки `sing-box[9345]: FATAL[0000] start service: initialize DNS rule[2]: rule-set not found: main`. Когда не задана строка\интерфейс - [x] Обработка ошибки `sing-box[9345]: FATAL[0000] start service: initialize DNS rule[2]: rule-set not found: main`. Когда не задана строка\интерфейс
- [x] Проверка `/etc/resolv.conf` на наличие DNS-серверов
- [x] Отслеживание интерфейса wan в sing-box
- [ ] Рестарт сервиса без рестарта dnsmasq
- [ ] `ash: can't kill pid 9848: No such process` при обновлении и stop
Низкий приоритет Низкий приоритет
- [ ] Галочка, которая режет доступ к doh серверам - [ ] Галочка, которая режет доступ к doh серверам

View File

@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-podkop PKG_NAME:=luci-app-podkop
PKG_VERSION:=0.3.23 PKG_VERSION:=0.3.27
PKG_RELEASE:=1 PKG_RELEASE:=1
LUCI_TITLE:=LuCI podkop app LUCI_TITLE:=LuCI podkop app

View File

@@ -591,7 +591,7 @@ const createModalContent = (title, content) => {
const showConfigModal = async (command, title) => { const showConfigModal = async (command, title) => {
const res = await safeExec('/usr/bin/podkop', [command]); const res = await safeExec('/usr/bin/podkop', [command]);
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
ui.showModal(_(title), createModalContent(title, formattedOutput)); ui.showModal(_(title), createModalContent(_(title), formattedOutput));
}; };
// Button Factory // Button Factory
@@ -650,7 +650,7 @@ const createStatusPanel = (title, status, buttons) => {
}; };
// Update the status section creation // Update the status section creation
let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, singbox, system, fakeipStatus) { let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, singbox, system, fakeipStatus, fakeipCLIStatus) {
return E('div', { 'class': 'cbi-section' }, [ return E('div', { 'class': 'cbi-section' }, [
E('h3', {}, _('Service Status')), E('h3', {}, _('Service Status')),
E('div', { 'class': 'table', style: 'display: flex; gap: 20px;' }, [ E('div', { 'class': 'table', style: 'display: flex; gap: 20px;' }, [
@@ -712,36 +712,54 @@ let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, s
}) })
]), ]),
// FakeIP Status Panel with dynamic status // FakeIP Status Panel with both browser and router checks
createStatusPanel('FakeIP Status', { createStatusPanel(_('FakeIP Status'), null, [
running: fakeipStatus.state === 'working', E('div', { style: 'margin-bottom: 10px;' }, [
status: fakeipStatus.message E('div', { style: 'margin-bottom: 5px;' }, [
}, [ E('span', { style: `color: ${fakeipStatus.color}` }, [
fakeipStatus.state === 'working' ? '✔' : fakeipStatus.state === 'not_working' ? '✘' : '!',
' ',
fakeipStatus.state === 'working' ? _('works in browser') : _('not works in browser')
])
]),
E('div', {}, [
E('span', { style: `color: ${fakeipCLIStatus.color}` }, [
fakeipCLIStatus.state === 'working' ? '✔' : fakeipCLIStatus.state === 'not_working' ? '✘' : '!',
' ',
fakeipCLIStatus.state === 'working' ? _('works on router') : _('not works on router')
])
])
]),
ButtonFactory.createModalButton({ ButtonFactory.createModalButton({
label: 'Check NFT Rules', label: _('Check NFT Rules'),
command: 'check_nft', command: 'check_nft',
title: 'NFT Rules' title: _('NFT Rules')
}), }),
ButtonFactory.createModalButton({ ButtonFactory.createModalButton({
label: 'Check DNSMasq', label: _('Check DNSMasq'),
command: 'check_dnsmasq', command: 'check_dnsmasq',
title: 'DNSMasq Configuration' title: _('DNSMasq Configuration')
}), }),
ButtonFactory.createModalButton({ ButtonFactory.createModalButton({
label: 'Update Lists', label: _('Update Lists'),
command: 'list_update', command: 'list_update',
title: 'Lists Update Results' title: _('Lists Update Results')
}),
ButtonFactory.createModalButton({
label: _('Check Router FakeIP'),
command: 'check_fakeip',
title: _('FakeIP Router Check')
}) })
]), ]),
// Version Information Panel // Version Information Panel
createStatusPanel('Version Information', null, [ createStatusPanel(_('Version Information'), null, [
E('div', { 'style': 'margin-top: 10px; font-family: monospace; white-space: pre-wrap;' }, [ E('div', { 'style': 'margin-top: 10px; font-family: monospace; white-space: pre-wrap;' }, [
E('strong', {}, 'Podkop: '), podkop.stdout ? podkop.stdout.trim() : _('Unknown'), '\n', E('strong', {}, _('Podkop: ')), podkop.stdout ? podkop.stdout.trim() : _('Unknown'), '\n',
E('strong', {}, 'LuCI App: '), luci.stdout ? luci.stdout.trim() : _('Unknown'), '\n', E('strong', {}, _('LuCI App: ')), luci.stdout ? luci.stdout.trim() : _('Unknown'), '\n',
E('strong', {}, 'Sing-box: '), singbox.stdout ? singbox.stdout.trim() : _('Unknown'), '\n', E('strong', {}, _('Sing-box: ')), singbox.stdout ? singbox.stdout.trim() : _('Unknown'), '\n',
E('strong', {}, 'OpenWrt Version: '), system.stdout ? system.stdout.split('\n')[1].trim() : _('Unknown'), '\n', E('strong', {}, _('OpenWrt Version: ')), system.stdout ? system.stdout.split('\n')[1].trim() : _('Unknown'), '\n',
E('strong', {}, 'Device Model: '), system.stdout ? system.stdout.split('\n')[4].trim() : _('Unknown') E('strong', {}, _('Device Model: ')), system.stdout ? system.stdout.split('\n')[4].trim() : _('Unknown')
]) ])
]) ])
]) ])
@@ -751,9 +769,6 @@ let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, s
return view.extend({ return view.extend({
async render() { async render() {
document.head.insertAdjacentHTML('beforeend', ` document.head.insertAdjacentHTML('beforeend', `
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<style> <style>
.cbi-value { .cbi-value {
margin-bottom: 10px !important; margin-bottom: 10px !important;
@@ -827,7 +842,7 @@ return view.extend({
o.value('dns.adguard-dns.com', 'AdGuard Default (dns.adguard-dns.com)'); o.value('dns.adguard-dns.com', 'AdGuard Default (dns.adguard-dns.com)');
o.value('unfiltered.adguard-dns.com', 'AdGuard Unfiltered (unfiltered.adguard-dns.com)'); o.value('unfiltered.adguard-dns.com', 'AdGuard Unfiltered (unfiltered.adguard-dns.com)');
o.value('family.adguard-dns.com', 'AdGuard Family (family.adguard-dns.com)'); o.value('family.adguard-dns.com', 'AdGuard Family (family.adguard-dns.com)');
o.default = '1.1.1.1'; o.default = '8.8.8.8';
o.rmempty = false; o.rmempty = false;
o.ucisection = 'main'; o.ucisection = 'main';
o.validate = function (section_id, value) { o.validate = function (section_id, value) {
@@ -856,7 +871,7 @@ return view.extend({
}; };
o = mainSection.taboption('additional', form.Value, 'dns_rewrite_ttl', _('DNS Rewrite TTL'), _('Time in seconds for DNS record caching (default: 600)')); o = mainSection.taboption('additional', form.Value, 'dns_rewrite_ttl', _('DNS Rewrite TTL'), _('Time in seconds for DNS record caching (default: 600)'));
o.default = '600'; o.default = '60';
o.rmempty = false; o.rmempty = false;
o.ucisection = 'main'; o.ucisection = 'main';
o.validate = function (section_id, value) { o.validate = function (section_id, value) {
@@ -957,17 +972,15 @@ return view.extend({
const timeoutId = setTimeout(() => controller.abort(), 10000); const timeoutId = setTimeout(() => controller.abort(), 10000);
try { try {
const response = await fetch('http://httpbin.org/ip', { signal: controller.signal }); const response = await fetch('https://fakeip.tech-domain.club/check', { signal: controller.signal });
const text = await response.text(); const data = await response.json();
clearTimeout(timeoutId); clearTimeout(timeoutId);
if (text.includes('Cannot GET /ip')) { if (data.fakeip === true) {
return resolve(createStatus('working', 'working', 'SUCCESS')); return resolve(createStatus('working', 'working', 'SUCCESS'));
} } else {
if (text.includes('"origin":')) {
return resolve(createStatus('not_working', 'not working', 'ERROR')); return resolve(createStatus('not_working', 'not working', 'ERROR'));
} }
return resolve(createStatus('error', 'check error', 'WARNING'));
} catch (fetchError) { } catch (fetchError) {
clearTimeout(timeoutId); clearTimeout(timeoutId);
const message = fetchError.name === 'AbortError' ? 'timeout' : 'check error'; const message = fetchError.name === 'AbortError' ? 'timeout' : 'check error';
@@ -980,6 +993,39 @@ return view.extend({
}); });
} }
function checkFakeIPCLI() {
const createStatus = (state, message, color) => ({
state,
message: _(message),
color: STATUS_COLORS[color]
});
return new Promise(async (resolve) => {
try {
const singboxStatusResult = await safeExec('/usr/bin/podkop', ['get_sing_box_status']);
const singboxStatus = JSON.parse(singboxStatusResult.stdout || '{"running":0,"dns_configured":0}');
if (!singboxStatus.running) {
return resolve(createStatus('not_working', 'sing-box not running', 'ERROR'));
}
if (!singboxStatus.dns_configured) {
return resolve(createStatus('not_working', 'DNS not configured', 'ERROR'));
}
const result = await safeExec('nslookup', ['-timeout=2', 'fakeip.tech-domain.club', '127.0.0.42']);
if (result.stdout && result.stdout.includes('198.18')) {
return resolve(createStatus('working', 'working on router', 'SUCCESS'));
} else {
return resolve(createStatus('not_working', 'not working on router', 'ERROR'));
}
} catch (error) {
console.error('Error in checkFakeIPCLI:', error);
return resolve(createStatus('error', 'CLI check error', 'WARNING'));
}
});
}
async function updateDiagnostics() { async function updateDiagnostics() {
try { try {
const [ const [
@@ -989,7 +1035,8 @@ return view.extend({
luci, luci,
singbox, singbox,
system, system,
fakeipStatus fakeipStatus,
fakeipCLIStatus
] = await Promise.all([ ] = await Promise.all([
safeExec('/usr/bin/podkop', ['get_status']), safeExec('/usr/bin/podkop', ['get_status']),
safeExec('/usr/bin/podkop', ['get_sing_box_status']), safeExec('/usr/bin/podkop', ['get_sing_box_status']),
@@ -997,7 +1044,8 @@ return view.extend({
safeExec('/usr/bin/podkop', ['show_luci_version']), safeExec('/usr/bin/podkop', ['show_luci_version']),
safeExec('/usr/bin/podkop', ['show_sing_box_version']), safeExec('/usr/bin/podkop', ['show_sing_box_version']),
safeExec('/usr/bin/podkop', ['show_system_info']), safeExec('/usr/bin/podkop', ['show_system_info']),
checkFakeIP() checkFakeIP(),
checkFakeIPCLI()
]); ]);
const parsedPodkopStatus = JSON.parse(podkopStatus.stdout || '{"running":0,"enabled":0,"status":"unknown"}'); const parsedPodkopStatus = JSON.parse(podkopStatus.stdout || '{"running":0,"enabled":0,"status":"unknown"}');
@@ -1006,7 +1054,7 @@ return view.extend({
const container = document.getElementById('diagnostics-status'); const container = document.getElementById('diagnostics-status');
if (!container) return; if (!container) return;
const statusSection = createStatusSection(parsedPodkopStatus, parsedSingboxStatus, podkop, luci, singbox, system, fakeipStatus); const statusSection = createStatusSection(parsedPodkopStatus, parsedSingboxStatus, podkop, luci, singbox, system, fakeipStatus, fakeipCLIStatus);
container.innerHTML = ''; container.innerHTML = '';
container.appendChild(statusSection); container.appendChild(statusSection);
@@ -1017,6 +1065,14 @@ return view.extend({
fakeipStatus.message fakeipStatus.message
]).outerHTML; ]).outerHTML;
} }
const fakeipCLIElement = document.getElementById('fakeip-cli-status');
if (fakeipCLIElement) {
fakeipCLIElement.innerHTML = E('span', { 'style': `color: ${fakeipCLIStatus.color}` }, [
fakeipCLIStatus.state === 'working' ? '✔ ' : fakeipCLIStatus.state === 'not_working' ? '✘ ' : '! ',
fakeipCLIStatus.message
]).outerHTML;
}
} catch (e) { } catch (e) {
console.error('Error updating diagnostics:', e); console.error('Error updating diagnostics:', e);
const container = document.getElementById('diagnostics-status'); const container = document.getElementById('diagnostics-status');
@@ -1033,29 +1089,12 @@ return view.extend({
function startPeriodicUpdates(titleDiv) { function startPeriodicUpdates(titleDiv) {
let updateTimer = null; let updateTimer = null;
let isVisible = !document.hidden; let isVisible = !document.hidden;
let versionText = _('Podkop');
let versionReceived = false;
const updateStatus = async () => { const updateStatus = async () => {
try { try {
if (!versionReceived) {
const version = await safeExec('/usr/bin/podkop', ['show_version'], 2000);
if (version.stdout) {
versionText = _('Podkop') + ' v' + version.stdout.trim();
versionReceived = true;
}
}
const singboxStatusResult = await safeExec('/usr/bin/podkop', ['get_sing_box_status']);
const singboxStatus = JSON.parse(singboxStatusResult.stdout || '{"running":0,"dns_configured":0}');
const fakeipStatus = await checkFakeIP();
titleDiv.textContent = versionText + (!singboxStatus.running || !singboxStatus.dns_configured === 'not_working' ? ' (not working)' : '');
await updateDiagnostics(); await updateDiagnostics();
} catch (error) { } catch (error) {
console.warn('Failed to update status:', error); console.warn('Failed to update status:', error);
titleDiv.textContent = versionText + ' (not working)';
} }
}; };

View File

@@ -722,4 +722,28 @@ msgid "stopped but enabled"
msgstr "остановлен, но активирован" msgstr "остановлен, но активирован"
msgid "stopped & disabled" msgid "stopped & disabled"
msgstr "остановлен и деактивирован" msgstr "остановлен и деактивирован"
msgid "works in browser"
msgstr "работает в браузере"
msgid "works on router"
msgstr "работает на роутере"
msgid "Check Router FakeIP"
msgstr "Проверить FakeIP на роутере"
msgid "FakeIP Router Check"
msgstr "Проверка FakeIP на роутере"
msgid "FakeIP CLI Check"
msgstr "Проверка FakeIP через CLI"
msgid "FakeIP CLI Check Results"
msgstr "Результаты проверки FakeIP через CLI"
msgid "not works in browser"
msgstr "не работает в браузере"
msgid "not works on router"
msgstr "не работает на роутере"

View File

@@ -1076,4 +1076,28 @@ msgid "stopped but enabled"
msgstr "" msgstr ""
msgid "stopped & disabled" msgid "stopped & disabled"
msgstr ""
msgid "works in browser"
msgstr ""
msgid "works on router"
msgstr ""
msgid "Check Router FakeIP"
msgstr ""
msgid "FakeIP Router Check"
msgstr ""
msgid "FakeIP CLI Check"
msgstr ""
msgid "FakeIP CLI Check Results"
msgstr ""
msgid "not works in browser"
msgstr ""
msgid "not works on router"
msgstr "" msgstr ""

View File

@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=podkop PKG_NAME:=podkop
PKG_VERSION:=0.3.23 PKG_VERSION:=0.3.27
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_MAINTAINER:=ITDog <podkop@itdog.info> PKG_MAINTAINER:=ITDog <podkop@itdog.info>

View File

@@ -32,6 +32,6 @@ config main 'main'
option dont_touch_dhcp '0' option dont_touch_dhcp '0'
option update_interval '1d' option update_interval '1d'
option dns_type 'doh' option dns_type 'doh'
option dns_server '1.1.1.1' option dns_server '8.8.8.8'
option dns_rewrite_ttl '600' option dns_rewrite_ttl '60'
option cache_file '/tmp/cache.db' option cache_file '/tmp/cache.db'

View File

@@ -6,6 +6,7 @@ USE_PROCD=1
script=$(readlink "$initscript") script=$(readlink "$initscript")
NAME="$(basename ${script:-$initscript})" NAME="$(basename ${script:-$initscript})"
config_load "$NAME" config_load "$NAME"
resolv_conf="/etc/resolv.conf"
start_service() { start_service() {
echo "Start podkop" echo "Start podkop"
@@ -30,6 +31,10 @@ start_service() {
echo "Interface br-lan not found" echo "Interface br-lan not found"
exit 1 exit 1
fi fi
if ! grep -q "search lan" "$resolv_conf" || ! grep -q "nameserver 127.0.0.1" "$resolv_conf"; then
echo "/etc/resolv.conf does not contain 'search lan' or 'nameserver 127.0.0.1' entries"
fi
procd_open_instance procd_open_instance
procd_set_param command /bin/sh -c "/usr/bin/podkop start" procd_set_param command /bin/sh -c "/usr/bin/podkop start"

View File

@@ -19,7 +19,7 @@ SING_BOX_CONFIG="/etc/sing-box/config.json"
FAKEIP="198.18.0.0/15" FAKEIP="198.18.0.0/15"
VALID_SERVICES="russia_inside russia_outside ukraine_inside geoblock block porn news anime youtube discord meta twitter hdrezka tiktok telegram" VALID_SERVICES="russia_inside russia_outside ukraine_inside geoblock block porn news anime youtube discord meta twitter hdrezka tiktok telegram"
DNS_RESOLVERS="1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 9.9.9.9 9.9.9.11 94.140.14.14 94.140.15.15 208.67.220.220 208.67.222.222 77.88.8.1 77.88.8.8" DNS_RESOLVERS="1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 9.9.9.9 9.9.9.11 94.140.14.14 94.140.15.15 208.67.220.220 208.67.222.222 77.88.8.1 77.88.8.8"
TEST_DOMAIN="google.com" TEST_DOMAIN="fakeip.tech-domain.club"
log() { log() {
local message="$1" local message="$1"
@@ -68,15 +68,17 @@ start() {
# sing-box outbounds and rules # sing-box outbounds and rules
config_foreach sing_box_outdound config_foreach sing_box_outdound
config_foreach process_domains_for_section config_foreach process_domains_for_section
config_foreach process_remote_ruleset
config_foreach sing_box_rule_preset config_foreach sing_box_rule_preset
config_foreach process_domains_list_local config_foreach process_domains_list_local
config_foreach process_domains_list_url
config_foreach process_subnet_for_section config_foreach process_subnet_for_section
config_foreach process_subnet_for_section_remote config_foreach process_remote_ruleset_srs
config_foreach process_all_traffic_for_section config_foreach process_all_traffic_for_section
config_foreach add_cron_job config_foreach add_cron_job
config_foreach prepare_custom_ruleset
list_update &
echo $! > /var/run/podkop_list_update.pid
# Future: exclude at the fakeip? # Future: exclude at the fakeip?
config_get_bool exclude_from_ip_enabled "main" "exclude_from_ip_enabled" "0" config_get_bool exclude_from_ip_enabled "main" "exclude_from_ip_enabled" "0"
if [ "$exclude_from_ip_enabled" -eq 1 ]; then if [ "$exclude_from_ip_enabled" -eq 1 ]; then
@@ -123,6 +125,16 @@ start() {
stop() { stop() {
log "Stopping the podkop" log "Stopping the podkop"
if [ -f /var/run/podkop_list_update.pid ]; then
pid=$(cat /var/run/podkop_list_update.pid)
if kill -0 "$pid"; then
kill "$pid"
log "Stopped list_update"
fi
rm -f /var/run/podkop_list_update.pid
fi
remove_cron_job remove_cron_job
config_get_bool dont_touch_dhcp "main" "dont_touch_dhcp" "0" config_get_bool dont_touch_dhcp "main" "dont_touch_dhcp" "0"
@@ -202,6 +214,9 @@ migration() {
log "Found and removed use-application-dns.net in dhcp config" log "Found and removed use-application-dns.net in dhcp config"
sed -i '/use-application-dns/d' "/etc/config/dhcp" sed -i '/use-application-dns/d' "/etc/config/dhcp"
fi fi
# corntab init.d
(crontab -l | grep -v "/etc/init.d/podkop list_update") | crontab -
} }
validate_service() { validate_service() {
@@ -389,19 +404,19 @@ add_cron_job() {
case "$update_interval" in case "$update_interval" in
"1h") "1h")
cron_job="13 * * * * /etc/init.d/podkop list_update" cron_job="13 * * * * /usr/bin/podkop list_update"
;; ;;
"3h") "3h")
cron_job="13 */3 * * * /etc/init.d/podkop list_update" cron_job="13 */3 * * * /usr/bin/podkop list_update"
;; ;;
"12h") "12h")
cron_job="13 */12 * * * /etc/init.d/podkop list_update" cron_job="13 */12 * * * /usr/bin/podkop list_update"
;; ;;
"1d") "1d")
cron_job="13 9 * * * /etc/init.d/podkop list_update" cron_job="13 9 * * * /usr/bin/podkop list_update"
;; ;;
"3d") "3d")
cron_job="13 9 */3 * * /etc/init.d/podkop list_update" cron_job="13 9 */3 * * /usr/bin/podkop list_update"
;; ;;
*) *)
log "Invalid update_interval value: $update_interval" log "Invalid update_interval value: $update_interval"
@@ -421,13 +436,74 @@ add_cron_job() {
} }
remove_cron_job() { remove_cron_job() {
(crontab -l | grep -v "/etc/init.d/podkop list_update") | crontab - (crontab -l | grep -v "/usr/bin/podkop list_update") | crontab -
log "The cron job removed" log "The cron job removed"
} }
prepare_custom_ruleset() {
config_get custom_download_domains_list_enabled "$section" "custom_download_domains_list_enabled"
config_get custom_download_subnets_list_enabled "$section" "custom_download_subnets_list_enabled"
if [ "$custom_download_domains_list_enabled" -eq 1 ] || [ "$custom_download_subnets_list_enabled" -eq 1 ]; then
local file="/tmp/podkop/$section-custom-domains-subnets.json"
local tag="custom-$section"
rm -f $file
jq -n '
{
"version": 3,
"rules": []
}' > $file
jq --arg tag "$tag" \
--arg file "$file" \
'.route.rule_set += [{
"tag": $tag,
"type": "local",
"format": "source",
"path": $file
}]' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
sing_box_rules $tag $section
sing_box_dns_rule_fakeip_section $tag $tag
log "Added 'test' rule_set to sing-box config"
fi
}
list_update() { list_update() {
log "Update remote lists" log "Update remote lists"
config_foreach process_remote_ruleset
local i
for i in $(seq 1 60); do
if nslookup -timeout=1 openwrt.org >/dev/null 2>&1; then
log "DNS is available"
break
fi
log "DNS is unavailable [$i/60]"
sleep 3
done
if [ "$i" -eq 60 ]; then
log "Error: DNS check failed after 10 attempts"
return 1
fi
for i in $(seq 1 60); do
if curl -s -m 3 https://github.com >/dev/null; then
log "GitHub is available"
break
fi
log "GitHub is unavailable [$i/60]"
sleep 3
done
if [ "$i" -eq 60 ]; then
log "Error: Cannot connect to GitHub after 10 attempts"
return 1
fi
config_foreach process_remote_ruleset_subnet
config_foreach process_domains_list_url config_foreach process_domains_list_url
config_foreach process_subnet_for_section_remote config_foreach process_subnet_for_section_remote
} }
@@ -435,12 +511,11 @@ list_update() {
find_working_resolver() { find_working_resolver() {
local resolver_found="" local resolver_found=""
for resolver in $DNS_RESOLVERS; do for resolver in $DNS_RESOLVERS; do
if nslookup $TEST_DOMAIN $resolver >/dev/null 2>&1; then if nslookup -timeout=2 $TEST_DOMAIN $resolver >/dev/null 2>&1; then
echo "$resolver" echo "$resolver"
return 0 return 0
fi fi
done done
echo "8.8.8.8"
return 1 return 1
} }
@@ -454,35 +529,53 @@ sing_box_uci() {
-e "s/option enabled '0'/option enabled '1'/" \ -e "s/option enabled '0'/option enabled '1'/" \
-e "s/option user 'sing-box'/option user 'root'/" $config -e "s/option user 'sing-box'/option user 'root'/" $config
log "Change sing-box UCI config" log "Change sing-box UCI config"
else fi
log "Sing-box UCI config OK"
if grep -q '#\s*list ifaces' "$config"; then
sed -i '/ifaces/s/#//g' $config
log "Uncommented list ifaces"
fi fi
} }
# Future: for every section. +1 port? add_socks5_for_section() {
local section="$1"
local port="$2"
local tag="$section-mixed-in"
log "Adding Socks5 for $section on port $port"
jq \
--arg tag "$tag" \
--arg port "$port" \
--arg section "$section" \
'.inbounds += [{
"tag": $tag,
"type": "mixed",
"listen": "0.0.0.0",
"listen_port": ($port|tonumber),
"set_system_proxy": false
}] |
.route.rules += [{
"inbound": [$tag],
"outbound": $section,
"action": "route"
}]' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
}
process_socks5() { process_socks5() {
config_get_bool socks5 "main" "socks5" "0" config_get_bool main_socks5 "main" "socks5" "0"
if [ "$socks5" -eq 1 ]; then if [ "$main_socks5" -eq 1 ]; then
log "Socks5 local enable port 2080" add_socks5_for_section "main" "2080"
jq '.inbounds += [{
"tag": "mixed-in",
"type": "mixed",
"listen": "0.0.0.0",
"listen_port": 2080,
"set_system_proxy": false
}]' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
#local rule_exists=$(jq -r '.route.rules[] | select(.inbound[] == "mixed-in")' $SING_BOX_CONFIG)
local rule_exists=$(jq -r '.route.rules // [] | map(select(.inbound // [] | index("mixed-in"))) | length' $SING_BOX_CONFIG)
if [ -z "$rule_exists" ]; then
jq '.route.rules += [{
"inbound": ["mixed-in"],
"outbound": "main",
"action": "route"
}]' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
fi
fi fi
local port=2081
for section in $(uci show podkop | awk -F'[.=]' '/=extra/ {print $2}'); do
config_get_bool section_socks5 "$section" "socks5" "0"
if [ "$section_socks5" -eq 1 ]; then
add_socks5_for_section "$section" "$port"
port=$((port + 1))
fi
done
} }
sing_box_inbound_proxy() { sing_box_inbound_proxy() {
@@ -533,7 +626,12 @@ sing_box_dns() {
if [ "$is_ip" = "0" ]; then if [ "$is_ip" = "0" ]; then
log "Finding working DNS resolver" log "Finding working DNS resolver"
local dns_resolver=$(find_working_resolver) local dns_resolver=$(find_working_resolver)
log "Found working resolver: $dns_resolver" if [ -z "$dns_resolver" ]; then
log "No working resolver found, using default DNS server"
dns_resolver="1.1.1.1"
else
log "Found working resolver: $dns_resolver"
fi
fi fi
log "Configure DNS in sing-box" log "Configure DNS in sing-box"
@@ -669,6 +767,12 @@ sing_box_outdound() {
log "VPN mode" log "VPN mode"
log "You are using VPN mode, make sure you have installed all the necessary packages and configured." log "You are using VPN mode, make sure you have installed all the necessary packages and configured."
config_get interface "$section" "interface" config_get interface "$section" "interface"
if [ -z "$interface" ]; then
log "VPN interface is not set. Exit"
exit 1
fi
sing_box_outbound_interface $section $interface sing_box_outbound_interface $section $interface
;; ;;
"proxy") "proxy")
@@ -691,8 +795,8 @@ sing_box_outdound() {
active_proxy_string=$(echo "$proxy_string" | grep -v "^[[:space:]]*\/\/" | head -n 1) active_proxy_string=$(echo "$proxy_string" | grep -v "^[[:space:]]*\/\/" | head -n 1)
if [ -z "$active_proxy_string" ]; then if [ -z "$active_proxy_string" ]; then
log "No active proxy configuration found" log "Proxy string is not set. Exit"
return exit 1
fi fi
if [[ "$active_proxy_string" =~ ^ss:// ]]; then if [[ "$active_proxy_string" =~ ^ss:// ]]; then
@@ -1091,6 +1195,32 @@ sing_box_ruleset_subnets() {
fi fi
} }
sing_box_ruleset_domains_json() {
local domain="$1"
local section="$2"
local file="/tmp/podkop/$section-custom-domains-subnets.json"
jq --arg domain "$domain" '
.rules[0].domain_suffix += if .rules[0].domain_suffix | index($domain) then [] else [$domain] end
' "$file" > "${file}.tmp" && mv "${file}.tmp" "$file"
log "$domain added to $section-custom-domains-subnets.json"
}
sing_box_ruleset_subnets_json() {
local subnet="$1"
local section="$2"
local file="/tmp/podkop/$section-custom-domains-subnets.json"
jq --arg subnet "$subnet" '
.rules[0].ip_cidr += if .rules[0].ip_cidr | index($subnet) then [] else [$subnet] end
' "$file" > "${file}.tmp" && mv "${file}.tmp" "$file"
log "$subnet added to $section-custom-domains-subnets.json"
}
process_domains_for_section() { process_domains_for_section() {
local section="$1" local section="$1"
@@ -1227,11 +1357,18 @@ sing_box_quic_reject() {
fi fi
} }
process_remote_ruleset() { process_remote_ruleset_srs() {
config_get_bool domain_list_enabled "$section" "domain_list_enabled" "0" config_get_bool domain_list_enabled "$section" "domain_list_enabled" "0"
if [ "$domain_list_enabled" -eq 1 ]; then if [ "$domain_list_enabled" -eq 1 ]; then
log "Adding a srs list for $section" log "Adding a srs list for $section"
config_list_foreach "$section" domain_list "sing_box_ruleset_remote" "remote" "1d" config_list_foreach "$section" domain_list "sing_box_ruleset_remote" "remote" "1d"
fi
}
process_remote_ruleset_subnet() {
config_get_bool domain_list_enabled "$section" "domain_list_enabled" "0"
if [ "$domain_list_enabled" -eq 1 ]; then
log "Adding a srs list for $section"
config_list_foreach "$section" domain_list "list_subnets_download" "$section" "$domain_list" config_list_foreach "$section" domain_list "list_subnets_download" "$section" "$domain_list"
fi fi
} }
@@ -1240,17 +1377,15 @@ sing_box_rule_preset() {
config_get custom_domains_list_type "$section" "custom_domains_list_type" config_get custom_domains_list_type "$section" "custom_domains_list_type"
config_get custom_subnets_list_enabled "$section" "custom_subnets_list_enabled" config_get custom_subnets_list_enabled "$section" "custom_subnets_list_enabled"
config_get custom_local_domains_list_enabled "$section" "custom_local_domains_list_enabled" config_get custom_local_domains_list_enabled "$section" "custom_local_domains_list_enabled"
config_get custom_download_domains_list_enabled "$section" "custom_download_domains_list_enabled" # config_get custom_download_domains_list_enabled "$section" "custom_download_domains_list_enabled"
config_get custom_download_subnets_list_enabled "$section" "custom_download_subnets_list_enabled" # config_get custom_download_subnets_list_enabled "$section" "custom_download_subnets_list_enabled"
if [ "$custom_domains_list_type" != "disabled" ] || [ "$custom_subnets_list_enabled" != "disabled" ] || if [ "$custom_domains_list_type" != "disabled" ] || [ "$custom_subnets_list_enabled" != "disabled" ] ||
[ "$custom_local_domains_list_enabled" = "1" ] || [ "$custom_download_domains_list_enabled" = "1" ] || [ "$custom_local_domains_list_enabled" = "1" ]; then
[ "$custom_download_subnets_list_enabled" = "1" ]; then
sing_box_rules "$section" "$section" sing_box_rules "$section" "$section"
fi fi
if [ "$custom_domains_list_type" != "disabled" ] || [ "$custom_local_domains_list_enabled" = "1" ] || if [ "$custom_domains_list_type" != "disabled" ] || [ "$custom_local_domains_list_enabled" = "1" ]; then
[ "$custom_download_domains_list_enabled" = "1" ]; then
sing_box_dns_rule_fakeip_section "$section" "$section" sing_box_dns_rule_fakeip_section "$section" "$section"
fi fi
@@ -1296,8 +1431,8 @@ list_custom_url_domains_create() {
wget -q -O "/tmp/podkop/${filename}" "$URL" wget -q -O "/tmp/podkop/${filename}" "$URL"
while IFS= read -r domain; do while IFS= read -r domain; do
log "From local file: $domain" log "From downloaded file: $domain"
sing_box_ruleset_domains $domain $section sing_box_ruleset_domains_json $domain $section
done <"/tmp/podkop/$filename" done <"/tmp/podkop/$filename"
} }
@@ -1337,7 +1472,8 @@ list_custom_url_subnets_create() {
while IFS= read -r subnet; do while IFS= read -r subnet; do
log "From local file: $subnet" log "From local file: $subnet"
sing_box_ruleset_subnets $subnet $section sing_box_ruleset_subnets_json $subnet $section
nft add element inet PodkopTable podkop_subnets { $subnet }
done <"/tmp/podkop/$filename" done <"/tmp/podkop/$filename"
} }
@@ -1577,6 +1713,71 @@ check_sing_box_logs() {
echo "$logs" echo "$logs"
} }
check_fakeip() {
nolog "Checking fakeip functionality..."
if ! command -v nslookup >/dev/null 2>&1; then
nolog "nslookup is not installed"
return 1
fi
local test_domain="$TEST_DOMAIN"
nolog "Testing DNS resolution with default DNS server"
echo "=== Testing with default DNS server ==="
nslookup -timeout=2 $test_domain
echo ""
nolog "Finding a working DNS resolver..."
local working_resolver=$(find_working_resolver)
if [ -z "$working_resolver" ]; then
nolog "No working resolver found, skipping resolver check"
else
nolog "Using resolver: $working_resolver"
nolog "Testing DNS resolution with working resolver ($working_resolver)"
echo "=== Testing with working resolver ($working_resolver) ==="
nslookup -timeout=2 $test_domain $working_resolver
echo ""
fi
# Main FakeIP check
nolog "Testing DNS resolution for $test_domain using 127.0.0.42"
echo "=== Testing with FakeIP DNS (127.0.0.42) ==="
local result=$(nslookup -timeout=2 $test_domain 127.0.0.42 2>&1)
echo "$result"
if echo "$result" | grep -q "198.18"; then
nolog "✅ FakeIP is working correctly! Domain resolved to FakeIP range (198.18.x.x)"
return 0
else
nolog "❌ FakeIP test failed. Domain did not resolve to FakeIP range"
nolog "Checking if sing-box is running..."
if ! pgrep -f "sing-box" >/dev/null; then
nolog "sing-box is not running"
else
nolog "sing-box is running, but FakeIP might not be configured correctly"
nolog "Checking DNS configuration in sing-box..."
if [ -f "$SING_BOX_CONFIG" ]; then
local fakeip_enabled=$(jq -r '.dns.fakeip.enabled' "$SING_BOX_CONFIG")
local fakeip_range=$(jq -r '.dns.fakeip.inet4_range' "$SING_BOX_CONFIG")
nolog "FakeIP enabled: $fakeip_enabled"
nolog "FakeIP range: $fakeip_range"
local dns_rules=$(jq -r '.dns.rules[] | select(.server == "fakeip-server") | .domain' "$SING_BOX_CONFIG")
nolog "FakeIP domain: $dns_rules"
else
nolog "sing-box config file not found"
fi
fi
return 1
fi
}
check_logs() { check_logs() {
nolog "Showing podkop logs from system journal..." nolog "Showing podkop logs from system journal..."
@@ -1746,19 +1947,14 @@ get_status() {
} }
sing_box_add_secure_dns_probe_domain() { sing_box_add_secure_dns_probe_domain() {
local domain="httpbin.org" local domain="$TEST_DOMAIN"
local override_address="numbersapi.com" local override_port=8443
if [ -z "$override_address" ]; then
log "Error: Could not get br-lan IP address"
return 1
fi
log "Adding DNS probe domain ${domain} to fakeip-server configuration" log "Adding DNS probe domain ${domain} to fakeip-server configuration"
jq \ jq \
--arg domain "$domain" \ --arg domain "$domain" \
--arg override "$override_address" \ --argjson override_port "$override_port" \
'.dns.rules |= map( '.dns.rules |= map(
if .server == "fakeip-server" then if .server == "fakeip-server" then
. + { . + {
@@ -1772,11 +1968,11 @@ sing_box_add_secure_dns_probe_domain() {
{ {
"domain": $domain, "domain": $domain,
"action": "route-options", "action": "route-options",
"override_address": $override "override_port": $override_port
} }
]' "$SING_BOX_CONFIG" >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG" ]' "$SING_BOX_CONFIG" >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG"
log "DNS probe domain ${domain} configured with override to ${override_address}" log "DNS probe domain ${domain} configured with override to port ${override_port}"
} }
case "$1" in case "$1" in
@@ -1814,6 +2010,9 @@ case "$1" in
check_sing_box_logs) check_sing_box_logs)
check_sing_box_logs check_sing_box_logs
;; ;;
check_fakeip)
check_fakeip
;;
check_dnsmasq) check_dnsmasq)
check_dnsmasq check_dnsmasq
;; ;;
@@ -1842,7 +2041,7 @@ case "$1" in
get_sing_box_status get_sing_box_status
;; ;;
*) *)
echo "Usage: $0 {start|stop|restart|reload|enable|disable|main|list_update|check_proxy|check_nft|check_github|check_logs|check_sing_box_connections|check_sing_box_logs|check_dnsmasq|show_config|show_version|show_sing_box_config|show_luci_version|show_sing_box_version|show_system_info|get_status|get_sing_box_status}" echo "Usage: $0 {start|stop|restart|reload|enable|disable|main|list_update|check_proxy|check_nft|check_github|check_logs|check_sing_box_connections|check_sing_box_logs|check_fakeip|check_dnsmasq|show_config|show_version|show_sing_box_config|show_luci_version|show_sing_box_version|show_system_info|get_status|get_sing_box_status}"
exit 1 exit 1
;; ;;
esac esac