Compare commits

..

21 Commits

Author SHA1 Message Date
itdoginfo
14e7cbae01 v0.3.44 2025-05-07 17:26:57 +03:00
itdoginfo
c9f610bb1e Change to ip.podkop.net. Fix log. Added restart 2025-05-07 17:24:32 +03:00
itdoginfo
19671c7f67 Stop btn. Change to ip.podkop.net 2025-05-07 17:23:20 +03:00
itdoginfo
6d1e4091e5 Detour 2025-05-07 00:22:04 +03:00
itdoginfo
96d661c49f Fixed default values ttl in comment 2025-05-03 18:52:57 +03:00
itdoginfo
da8dd06b34 Move doc to wiki 2025-05-03 18:12:00 +03:00
itdoginfo
2c1bcffb6d fix iptables 2025-05-03 18:11:49 +03:00
itdoginfo
3040ce7286 v0.3.43 2025-05-02 14:53:11 +03:00
itdoginfo
e025271a14 Added to global check: DNS check and proxy check. From VizzleTF 2025-05-02 14:50:06 +03:00
itdoginfo
2b8208186d Fix global check text 2025-05-02 13:55:07 +03:00
itdoginfo
17fb11baf0 Fixed diagnostics from VizzleTF 2025-05-02 13:34:11 +03:00
itdoginfo
3c1b041b52 Edited text from #96 2025-05-01 22:52:41 +03:00
itdoginfo
38acac1a31 Merge pull request #96 from itdoginfo/chore/sing-box-status
Issue #91 , Issue #94
2025-05-01 22:16:38 +03:00
Ivan K
2939229df3 back to the future 2025-05-01 19:30:05 +03:00
Ivan K
26c3d0bc7e ♻️ refactor(podkop): simplify DoH URL determination logic 2025-05-01 19:26:21 +03:00
Ivan K
b364363b1b feat(dns): add DoH URL resolution function 2025-05-01 19:20:36 +03:00
itdoginfo
d85caf0c0c Fix https-dns-proxy i18 2025-05-01 19:10:18 +03:00
Ivan K
65f72e1e04 ♻️ refactor(podkop): update WARP detection logic 2025-05-01 18:29:42 +03:00
Ivan K
e59ef6dd6f 💄 style(podkop): remove unnecessary sed commands in global_check 2025-05-01 17:57:51 +03:00
Ivan K
05272de650 💄 style(podkop): update formatting and messages 2025-05-01 17:48:25 +03:00
Ivan K
48716e7156 Enhance Podkop functionality with global check feature and improved diagnostics. Added support for FakeIP tests in both browser and router contexts. Updated UI elements for better status reporting and added localization for new messages. 2025-05-01 17:18:07 +03:00
9 changed files with 535 additions and 429 deletions

161
README.md
View File

@@ -11,68 +11,17 @@
- Если у вас не что-то не работает, то следуюет сходить в телеграм чат, прочитать закрепы и выполнить что там написано..
- Если у вас установлен Getdomains, его следует удалить.
# Удаление GetDomains скриптом
```
sh <(wget -O - https://raw.githubusercontent.com/itdoginfo/domain-routing-openwrt/refs/heads/master/getdomains-uninstall.sh)
```
Оставляет туннели, зоны, forwarding. А также stubby и dnscrypt. Они не помешают. Конфиг sing-box будет перезаписан в podkop.
# Документация
https://podkop.net/
# Установка Podkop
Пакет работает на всех архитектурах.
Тестировался на **ванильной** OpenWrt 23.05 и OpenWrt 24.10.
На FriendlyWrt 23.05 присуствуют зависимости от iptables, которые ломают tproxy. Если у вас появляется warning про это в логах, следуйте инструкции по приведённой там ссылке.
Полная информация в [документации](https://podkop.net/docs/install/)
Поддержки APK на данный момент нет. APK будет сделан после того как разгребу основное.
## Автоматическая
Вкратце, достаточно одного скрипта:
```
sh <(wget -O - https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/install.sh)
```
Скрипт также предложит выбрать, какой туннель будет использоваться. Для выбранного туннеля будут установлены нужные пакеты, а для Wireguard и AmneziaWG также будет предложена автоматическая настройка - прямо в консоли скрипт запросит данные конфига. Для AmneziaWG можно также выбрать вариант с использованием конфига обычного Wireguard и автоматической обфускацией до AmneziaWG.
Для AmneziaWG скрипт проверяет наличие пакетов под вашу платформу в [стороннем репозитории](https://github.com/Slava-Shchipunov/awg-openwrt/releases), так как в официальном репозитории OpenWRT они отсутствуют, и автоматически их устанавливает.
## Вручную
Сделать `opkg update`, чтоб установились зависимости.
Скачать пакеты `podkop_*.ipk` и `luci-app-podkop_*.ipk` из релиза. `opkg install` сначала первый, потом второй.
# Обновление
Та же самая команда, что для установки. Но с флагом **upgrade** сразу передёт к обновлению.
```
sh <(wget -qO- https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/install.sh) --upgrade
```
# Удаление
```
opkg remove luci-i18n-podkop-ru luci-app-podkop podkop
```
# Использование
Конфиг: /etc/config/podkop
Luci: Services/podkop
## Режимы
### Proxy
Для VLESS и Shadowsocks. Другие протоколы тоже будут, кидайте в чат примеры строк без чувствительных данных.
В этом режиме просто копируйте строку в **Proxy String** и из неё автоматически настроится sing-box.
### VPN
Здесь у вас должен быть уже настроен WG/OpenVPN/OpenConnect etc, зона Zone и Forwarding не обязательны.
Просто выбрать интерфейс из списка.
## Настройка доменов и подсетей
**Community Lists** - Включить списки комьюнити
**Custom domains enable** - Добавить свои домены
**Custom subnets enable** - Добавить подсети или IP-адреса. Для подсетей задать маску.
# ToDo
Этот раздел не означает задачи, которые нужно брать и делать. Это общий список хотелок. Если вы хотите помочь, пожалуйста, спросите сначала в телеграмме.
@@ -87,105 +36,3 @@ Luci: Services/podkop
- [ ] Handle для dnsmasq
- [ ] Unit тесты (BATS)
- [ ] Интеграционые тесты бекенда (OpenWrt rootfs + BATS)
# Don't touch my dhcp
Нужно в первую очередь, чтоб использовать опцию `server`.
В случае если опция активна, podkop не трогает /etc/config/dhcp. И вам требуется самостоятельно указать следующие значения:
```
option noresolv '1'
option cachesize '0'
list server '127.0.0.42'
```
Без этого podkop работать не будет.
Если нужно до определённых доменов ходить через определённый DNS-сервер, то конфиг выглядит так
```
option noresolv '1'
option cachesize '0'
list server '/itdog.info/1.1.1.1'
list server '127.0.0.42'
```
В этом случае домен и все субдомены ресурса itdog.info будут резолвится через DNS-сервер 1.1.1.1
# Bad WAN
При использовании опции **Interface monitoring** необходимо рестартовать podkop, чтоб init.d подхватил это
```
service podkop restart
```
# Разработка
Есть два варианта:
- Просто поставить пакет на роутер или виртуалку и прям редактировать через SFTP (opkg install openssh-sftp-server)
- SDK, чтоб собирать пакеты
Для сборки пакетов нужен SDK, один из вариантов скачать прям файл и разархивировать
https://downloads.openwrt.org/releases/23.05.5/targets/x86/64/
Нужен файл с SDK в имени
```
wget https://downloads.openwrt.org/releases/23.05.5/targets/x86/64/openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64.tar.xz
tar xf openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64.tar.xz
mv openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64 SDK
```
Последнее для удобства.
Создаём директорию для пакета
```
mkdir package/utilites
```
Симлинк из репозитория
```
ln -s ~/podkop/podkop package/utilites/podkop
ln -s ~/podkop/luci-app-podkop package/luci-app-podkop
```
В первый раз для сборки luci-app необходимо обновить пакеты
```
./scripts/feeds update -a
```
Для make можно добавить флаг -j N, где N - количество ядер для сборки. Первый раз пройдёт быстрее.
При первом make выводится менюшка, можно просто save, exit и всё. Первый раз долго грузит зависимости.
Сборка пакета. Сами пакеты собираются быстро.
```
make package/podkop/{clean,compile} V=s
```
Также для luci
```
make package/luci-app-podkop/{clean,compile} V=s
```
.ipk лежат в `bin/packages/x86_64/base/`
## Примеры строк
https://github.com/itdoginfo/podkop/blob/main/String-example.md
## Ошибки
```
Makefile:17: /SDK/feeds/luci/luci.mk: No such file or directory
make[2]: *** No rule to make target '/SDK/feeds/luci/luci.mk'. Stop.
time: package/luci/luci-app-podkop/clean#0.00#0.00#0.00
ERROR: package/luci/luci-app-podkop failed to build.
make[1]: *** [package/Makefile:129: package/luci/luci-app-podkop/clean] Error 1
make[1]: Leaving directory '/SDK'
make: *** [/SDK/include/toplevel.mk:226: package/luci-app-podkop/clean] Error 2
```
Не загружены пакеты для luci
## make зависимости
https://openwrt.org/docs/guide-developer/toolchain/install-buildsystem
Ubuntu
```
sudo apt update
sudo apt install build-essential clang flex bison g++ gawk \
gcc-multilib g++-multilib gettext git libncurses-dev libssl-dev \
python3-distutils rsync unzip zlib1g-dev file wget
```

View File

@@ -446,7 +446,7 @@ check_system() {
case $DNSPROXY in
yes|y|Y|yes)
opkg remove --force-depends luci-app-https-dns-proxy https-dns-proxy
opkg remove --force-depends luci-app-https-dns-proxy https-dns-proxy luci-i18n-https-dns-proxy*
break
;;
*)
@@ -457,7 +457,7 @@ check_system() {
done
fi
if opkg list-installed | grep -qE "iptables|kmod-iptab"; then
if opkg list-installed | grep -q "iptables-mod-extra"; then
printf "\033[31;1mFound incompatible iptables packages. If you're using FriendlyWrt: https://t.me/itdogchat/44512/181082\033[0m\n"
fi
}

View File

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

View File

@@ -621,9 +621,61 @@ const createModalContent = (title, content) => {
];
};
// Add IP masking function before showConfigModal
const maskIP = (ip) => {
if (!ip) return '';
const parts = ip.split('.');
if (parts.length !== 4) return ip;
return ['XX', 'XX', 'XX', parts[3]].join('.');
};
const showConfigModal = async (command, title) => {
const res = await safeExec('/usr/bin/podkop', [command]);
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
let formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
if (command === 'global_check') {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
const response = await fetch('https://fakeip.podkop.net/check', { signal: controller.signal });
const data = await response.json();
clearTimeout(timeoutId);
if (data.fakeip === true) {
formattedOutput += '\n✅ ' + _('FakeIP is working in browser!') + '\n';
} else {
formattedOutput += '\n❌ ' + _('FakeIP is not working in browser') + '\n';
formattedOutput += _('Check DNS server on current device (PC, phone)') + '\n';
formattedOutput += _('Its must be router!') + '\n';
}
// Bypass check
const bypassResponse = await fetch('https://fakeip.podkop.net/check', { signal: controller.signal });
const bypassData = await bypassResponse.json();
const bypassResponse2 = await fetch('https://ip.podkop.net/check', { signal: controller.signal });
const bypassData2 = await bypassResponse2.json();
formattedOutput += '━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
if (bypassData.IP && bypassData2.IP && bypassData.IP !== bypassData2.IP) {
formattedOutput += '✅ ' + _('Proxy working correctly') + '\n';
formattedOutput += _('Direct IP: ') + maskIP(bypassData.IP) + '\n';
formattedOutput += _('Proxy IP: ') + maskIP(bypassData2.IP) + '\n';
} else if (bypassData.IP === bypassData2.IP) {
formattedOutput += '❌ ' + _('Proxy is not working - same IP for both domains') + '\n';
formattedOutput += _('IP: ') + maskIP(bypassData.IP) + '\n';
} else {
formattedOutput += '❌ ' + _('Proxy check failed') + '\n';
}
} catch (error) {
formattedOutput += '\n❌ ' + _('Check failed: ') + (error.name === 'AbortError' ? _('timeout') : error.message) + '\n';
}
}
ui.showModal(_(title), createModalContent(_(title), formattedOutput));
};
@@ -667,16 +719,23 @@ const ButtonFactory = {
};
// Status Panel Factory
const createStatusPanel = (title, status, buttons) => {
const createStatusPanel = (title, status, buttons, extraData = {}) => {
const headerContent = [
E('strong', {}, _(title)),
status && E('br'),
status && E('span', {
'style': `color: ${status.running ? STATUS_COLORS.SUCCESS : STATUS_COLORS.ERROR}`
'style': `color: ${title === 'Sing-box Status' ?
(status.running && !status.enabled ? STATUS_COLORS.SUCCESS : STATUS_COLORS.ERROR) :
title === 'Podkop Status' ?
(status.enabled ? STATUS_COLORS.SUCCESS : STATUS_COLORS.ERROR) :
(status.running ? STATUS_COLORS.SUCCESS : STATUS_COLORS.ERROR)
}`
}, [
status.running ? '✔' : '✘',
' ',
status.status
title === 'Sing-box Status' ?
(status.running && !status.enabled ? '✔ running' : '✘ ' + status.status) :
title === 'Podkop Status' ?
(status.enabled ? '✔ Autostart enabled' : '✘ Autostart disabled') :
(status.running ? '✔' : '✘') + ' ' + status.status
])
].filter(Boolean);
@@ -688,7 +747,86 @@ const createStatusPanel = (title, status, buttons) => {
E('div', {
'class': 'panel-body',
'style': 'display: flex; flex-direction: column; gap: 8px;'
}, buttons)
}, title === 'Podkop Status' ? [
ButtonFactory.createActionButton({
label: 'Restart Podkop',
type: 'apply',
action: 'restart',
reload: true
}),
ButtonFactory.createActionButton({
label: 'Stop Podkop',
type: 'apply',
action: 'stop',
reload: true
}),
ButtonFactory.createInitActionButton({
label: status.enabled ? 'Disable Autostart' : 'Enable Autostart',
type: status.enabled ? 'remove' : 'apply',
action: status.enabled ? 'disable' : 'enable',
reload: true
}),
ButtonFactory.createModalButton({
label: E('strong', _('Global check')),
command: 'global_check',
title: _('Click here for all the info')
}),
ButtonFactory.createModalButton({
label: 'View Logs',
command: 'check_logs',
title: 'Podkop Logs'
}),
ButtonFactory.createModalButton({
label: _('Update Lists'),
command: 'list_update',
title: _('Lists Update Results')
})
] : title === _('FakeIP Status') ? [
E('div', { style: 'margin-bottom: 10px;' }, [
E('div', { style: 'margin-bottom: 5px;' }, [
E('span', { style: `color: ${extraData.fakeipStatus?.color}` }, [
extraData.fakeipStatus?.state === 'working' ? '✔' : extraData.fakeipStatus?.state === 'not_working' ? '✘' : '!',
' ',
extraData.fakeipStatus?.state === 'working' ? _('works in browser') : _('not works in browser')
])
]),
E('div', {}, [
E('span', { style: `color: ${extraData.fakeipCLIStatus?.color}` }, [
extraData.fakeipCLIStatus?.state === 'working' ? '✔' : extraData.fakeipCLIStatus?.state === 'not_working' ? '✘' : '!',
' ',
extraData.fakeipCLIStatus?.state === 'working' ? _('works on router') : _('not works on router')
])
])
]),
E('div', { style: 'margin-bottom: 10px;' }, [
E('div', { style: 'margin-bottom: 5px;' }, [
E('strong', {}, _('DNS Status')),
E('br'),
E('span', { style: `color: ${extraData.dnsStatus?.remote?.color}` }, [
extraData.dnsStatus?.remote?.state === 'available' ? '✔' : extraData.dnsStatus?.remote?.state === 'unavailable' ? '✘' : '!',
' ',
extraData.dnsStatus?.remote?.message
]),
E('br'),
E('span', { style: `color: ${extraData.dnsStatus?.local?.color}` }, [
extraData.dnsStatus?.local?.state === 'available' ? '✔' : extraData.dnsStatus?.local?.state === 'unavailable' ? '✘' : '!',
' ',
extraData.dnsStatus?.local?.message
])
])
]),
E('div', { style: 'margin-bottom: 10px;' }, [
E('div', { style: 'margin-bottom: 5px;' }, [
E('strong', {}, extraData.configName),
E('br'),
E('span', { style: `color: ${extraData.bypassStatus?.color}` }, [
extraData.bypassStatus?.state === 'working' ? '✔' : extraData.bypassStatus?.state === 'not_working' ? '✘' : '!',
' ',
extraData.bypassStatus?.message
])
])
])
] : buttons)
]);
};
@@ -698,12 +836,6 @@ let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, s
E('div', { 'class': 'table', style: 'display: flex; gap: 20px;' }, [
// Podkop Status Panel
createStatusPanel('Podkop Status', podkopStatus, [
ButtonFactory.createActionButton({
label: podkopStatus.running ? 'Stop Podkop' : 'Start Podkop',
type: podkopStatus.running ? 'remove' : 'apply',
action: podkopStatus.running ? 'stop' : 'start',
reload: true
}),
ButtonFactory.createActionButton({
label: 'Restart Podkop',
type: 'apply',
@@ -717,9 +849,9 @@ let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, s
reload: true
}),
ButtonFactory.createModalButton({
label: 'Show Config',
command: 'show_config',
title: 'Podkop Configuration'
label: _('Global check'),
command: 'global_check',
title: _('Click here for all the info')
}),
ButtonFactory.createModalButton({
label: 'View Logs',
@@ -762,58 +894,14 @@ let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, s
})
]),
// FakeIP Status Panel with both browser and router checks
createStatusPanel(_('FakeIP Status'), null, [
E('div', { style: 'margin-bottom: 10px;' }, [
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')
])
])
]),
E('div', { style: 'margin-bottom: 10px;' }, [
E('div', { style: 'margin-bottom: 5px;' }, [
E('strong', {}, _('DNS Status')),
E('br'),
E('span', { style: `color: ${dnsStatus.remote.color}` }, [
dnsStatus.remote.state === 'available' ? '✔' : dnsStatus.remote.state === 'unavailable' ? '✘' : '!',
' ',
dnsStatus.remote.message
]),
E('br'),
E('span', { style: `color: ${dnsStatus.local.color}` }, [
dnsStatus.local.state === 'available' ? '✔' : dnsStatus.local.state === 'unavailable' ? '✘' : '!',
' ',
dnsStatus.local.message
])
])
]),
E('div', { style: 'margin-bottom: 10px;' }, [
E('div', { style: 'margin-bottom: 5px;' }, [
E('strong', {}, configName),
E('br'),
E('span', { style: `color: ${bypassStatus.color}` }, [
bypassStatus.state === 'working' ? '✔' : bypassStatus.state === 'not_working' ? '✘' : '!',
' ',
bypassStatus.message
])
])
]),
ButtonFactory.createModalButton({
label: _('Global check'),
command: 'global_check',
title: _('Click here for all the info')
})
]),
// FakeIP Status Panel
createStatusPanel(_('FakeIP Status'), null, null, {
fakeipStatus,
fakeipCLIStatus,
dnsStatus,
bypassStatus,
configName
}),
// Version Information Panel
createStatusPanel(_('Version Information'), null, [
@@ -1058,7 +1146,7 @@ return view.extend({
return true;
};
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: 60)'));
o.default = '60';
o.rmempty = false;
o.ucisection = 'main';
@@ -1130,6 +1218,11 @@ return view.extend({
o.rmempty = false;
o.ucisection = 'main';
o = mainSection.taboption('additional', form.Flag, 'detour', _('Proxy download of lists'), _('Downloading all lists via main Proxy/VPN'));
o.default = '0';
o.rmempty = false;
o.ucisection = 'main';
// Extra IPs and exclusions (main section)
o = mainSection.taboption('basic', form.Flag, 'exclude_from_ip_enabled', _('IP for exclusion'), _('Specify local IP addresses that will never use the configured route'));
o.default = '0';
@@ -1220,7 +1313,7 @@ return view.extend({
const timeoutId = setTimeout(() => controller.abort(), 10000);
try {
const response = await fetch('https://fakeip.tech-domain.club/check', { signal: controller.signal });
const response = await fetch('https://fakeip.podkop.net/check', { signal: controller.signal });
const data = await response.json();
clearTimeout(timeoutId);
@@ -1259,7 +1352,7 @@ return view.extend({
return resolve(createStatus('not_working', 'DNS not configured', 'ERROR'));
}
const result = await safeExec('nslookup', ['-timeout=2', 'fakeip.tech-domain.club', '127.0.0.42']);
const result = await safeExec('nslookup', ['-timeout=2', 'fakeip.podkop.net', '127.0.0.42']);
if (result.stdout && result.stdout.includes('198.18')) {
return resolve(createStatus('working', 'working on router', 'SUCCESS'));
@@ -1308,7 +1401,7 @@ return view.extend({
const controller1 = new AbortController();
const timeoutId1 = setTimeout(() => controller1.abort(), 10000);
const response1 = await fetch('https://fakeip.tech-domain.club/check', { signal: controller1.signal });
const response1 = await fetch('https://fakeip.podkop.net/check', { signal: controller1.signal });
const data1 = await response1.json();
clearTimeout(timeoutId1);
@@ -1323,7 +1416,7 @@ return view.extend({
const controller2 = new AbortController();
const timeoutId2 = setTimeout(() => controller2.abort(), 10000);
const response2 = await fetch('https://ip.tech-domain.club/check', { signal: controller2.signal });
const response2 = await fetch('https://ip.podkop.net/check', { signal: controller2.signal });
const data2 = await response2.json();
clearTimeout(timeoutId2);
@@ -1350,32 +1443,67 @@ return view.extend({
async function updateDiagnostics() {
try {
const [
podkopStatus,
singboxStatus,
podkop,
luci,
singbox,
system,
fakeipStatus,
fakeipCLIStatus,
dnsStatus,
bypassStatus
] = await Promise.all([
safeExec('/usr/bin/podkop', ['get_status']),
safeExec('/usr/bin/podkop', ['get_sing_box_status']),
safeExec('/usr/bin/podkop', ['show_version']),
safeExec('/usr/bin/podkop', ['show_luci_version']),
safeExec('/usr/bin/podkop', ['show_sing_box_version']),
safeExec('/usr/bin/podkop', ['show_system_info']),
checkFakeIP(),
checkFakeIPCLI(),
checkDNSAvailability(),
checkBypass()
]);
const results = {
podkopStatus: null,
singboxStatus: null,
podkop: null,
luci: null,
singbox: null,
system: null,
fakeipStatus: null,
fakeipCLIStatus: null,
dnsStatus: null,
bypassStatus: null
};
const parsedPodkopStatus = JSON.parse(podkopStatus.stdout || '{"running":0,"enabled":0,"status":"unknown"}');
const parsedSingboxStatus = JSON.parse(singboxStatus.stdout || '{"running":0,"enabled":0,"status":"unknown"}');
// Perform all checks independently of each other
const checks = [
safeExec('/usr/bin/podkop', ['get_status'])
.then(result => results.podkopStatus = result)
.catch(() => results.podkopStatus = { stdout: '{"enabled":0,"status":"error"}' }),
safeExec('/usr/bin/podkop', ['get_sing_box_status'])
.then(result => results.singboxStatus = result)
.catch(() => results.singboxStatus = { stdout: '{"running":0,"enabled":0,"status":"error"}' }),
safeExec('/usr/bin/podkop', ['show_version'])
.then(result => results.podkop = result)
.catch(() => results.podkop = { stdout: 'error' }),
safeExec('/usr/bin/podkop', ['show_luci_version'])
.then(result => results.luci = result)
.catch(() => results.luci = { stdout: 'error' }),
safeExec('/usr/bin/podkop', ['show_sing_box_version'])
.then(result => results.singbox = result)
.catch(() => results.singbox = { stdout: 'error' }),
safeExec('/usr/bin/podkop', ['show_system_info'])
.then(result => results.system = result)
.catch(() => results.system = { stdout: 'error' }),
checkFakeIP()
.then(result => results.fakeipStatus = result)
.catch(() => results.fakeipStatus = { state: 'error', message: 'check error', color: STATUS_COLORS.WARNING }),
checkFakeIPCLI()
.then(result => results.fakeipCLIStatus = result)
.catch(() => results.fakeipCLIStatus = { state: 'error', message: 'check error', color: STATUS_COLORS.WARNING }),
checkDNSAvailability()
.then(result => results.dnsStatus = result)
.catch(() => results.dnsStatus = {
remote: { state: 'error', message: 'check error', color: STATUS_COLORS.WARNING },
local: { state: 'error', message: 'check error', color: STATUS_COLORS.WARNING }
}),
checkBypass()
.then(result => results.bypassStatus = result)
.catch(() => results.bypassStatus = { state: 'error', message: 'check error', color: STATUS_COLORS.WARNING })
];
// Waiting for all the checks to be completed
await Promise.allSettled(checks);
const container = document.getElementById('diagnostics-status');
if (!container) return;
@@ -1395,11 +1523,7 @@ return view.extend({
const label = activeConfig.split('#').pop();
if (label && label.trim()) {
configName = _('Config: ') + decodeURIComponent(label);
} else {
configName = _('Main config');
}
} else {
configName = _('Main config');
}
}
}
@@ -1407,42 +1531,62 @@ return view.extend({
console.error('Error getting config name from UCI:', e);
}
// Create a modified statusSection function with the configName
const statusSection = createStatusSection(parsedPodkopStatus, parsedSingboxStatus, podkop, luci, singbox, system, fakeipStatus, fakeipCLIStatus, dnsStatus, bypassStatus, configName);
const parsedPodkopStatus = JSON.parse(results.podkopStatus.stdout || '{"enabled":0,"status":"error"}');
const parsedSingboxStatus = JSON.parse(results.singboxStatus.stdout || '{"running":0,"enabled":0,"status":"error"}');
const statusSection = createStatusSection(
parsedPodkopStatus,
parsedSingboxStatus,
results.podkop,
results.luci,
results.singbox,
results.system,
results.fakeipStatus,
results.fakeipCLIStatus,
results.dnsStatus,
results.bypassStatus,
configName
);
container.innerHTML = '';
container.appendChild(statusSection);
const fakeipElement = document.getElementById('fakeip-status');
if (fakeipElement) {
fakeipElement.innerHTML = E('span', { 'style': `color: ${fakeipStatus.color}` }, [
fakeipStatus.state === 'working' ? '✔ ' : fakeipStatus.state === 'not_working' ? '✘ ' : '! ',
fakeipStatus.message
]).outerHTML;
// Updating individual status items
const updateStatusElement = (elementId, status, template) => {
const element = document.getElementById(elementId);
if (element) {
element.innerHTML = template(status);
}
};
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;
}
updateStatusElement('fakeip-status', results.fakeipStatus,
status => E('span', { 'style': `color: ${status.color}` }, [
status.state === 'working' ? '✔ ' : status.state === 'not_working' ? '✘ ' : '! ',
status.message
]).outerHTML
);
const dnsRemoteElement = document.getElementById('dns-remote-status');
if (dnsRemoteElement) {
dnsRemoteElement.innerHTML = E('span', { 'style': `color: ${dnsStatus.remote.color}` }, [
dnsStatus.remote.state === 'available' ? '✔ ' : dnsStatus.remote.state === 'unavailable' ? '✘ ' : '! ',
dnsStatus.remote.message
]).outerHTML;
}
updateStatusElement('fakeip-cli-status', results.fakeipCLIStatus,
status => E('span', { 'style': `color: ${status.color}` }, [
status.state === 'working' ? '✔ ' : status.state === 'not_working' ? '✘ ' : '! ',
status.message
]).outerHTML
);
updateStatusElement('dns-remote-status', results.dnsStatus.remote,
status => E('span', { 'style': `color: ${status.color}` }, [
status.state === 'available' ? '✔ ' : status.state === 'unavailable' ? '✘ ' : '! ',
status.message
]).outerHTML
);
updateStatusElement('dns-local-status', results.dnsStatus.local,
status => E('span', { 'style': `color: ${status.color}` }, [
status.state === 'available' ? '✔ ' : status.state === 'unavailable' ? '✘ ' : '! ',
status.message
]).outerHTML
);
const dnsLocalElement = document.getElementById('dns-local-status');
if (dnsLocalElement) {
dnsLocalElement.innerHTML = E('span', { 'style': `color: ${dnsStatus.local.color}` }, [
dnsStatus.local.state === 'available' ? '✔ ' : dnsStatus.local.state === 'unavailable' ? '✘ ' : '! ',
dnsStatus.local.message
]).outerHTML;
}
} catch (e) {
const container = document.getElementById('diagnostics-status');
if (container) {

View File

@@ -819,3 +819,27 @@ msgstr "недоступен"
msgid "Apply for SS2022"
msgstr "Применить для SS2022"
msgid "PODKOP CONFIGURATION"
msgstr "КОНФИГУРАЦИЯ PODKOP"
msgid "FAKEIP ROUTER TEST"
msgstr "ПРОВЕРКА FAKEIP НА РОУТЕРЕ"
msgid "FAKEIP BROWSER TEST"
msgstr "ПРОВЕРКА FAKEIP В БРАУЗЕРЕ"
msgid "FakeIP is working correctly on router (198.18.x.x)"
msgstr "FakeIP работает корректно на роутере (198.18.x.x)"
msgid "Click here for all the info"
msgstr "Нажмите для просмотра всей информации"
msgid "Check DNS server on current device (PC, phone)"
msgstr "Проверьте DNS сервер на текущем устройстве (ПК, телефон)"
msgid "Its must be router!"
msgstr "Это должен быть роутер!"
msgid "Global check"
msgstr "Глобальная проверка"

View File

@@ -1170,3 +1170,27 @@ msgstr ""
msgid "unavailable"
msgstr ""
msgid "PODKOP CONFIGURATION"
msgstr ""
msgid "FAKEIP ROUTER TEST"
msgstr ""
msgid "FAKEIP BROWSER TEST"
msgstr ""
msgid "FakeIP is working correctly on router (198.18.x.x)"
msgstr ""
msgid "Click here for all the info"
msgstr ""
msgid "Check DNS server on current device (PC, phone)"
msgstr ""
msgid "Its must be router!"
msgstr ""
msgid "Global check"
msgstr ""

View File

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

View File

@@ -39,3 +39,4 @@ config main 'main'
option mon_restart_ifaces '0'
#list restart_ifaces 'wan'
option ss_uot '0'
option detour '0'

View File

@@ -20,19 +20,16 @@ SING_BOX_CONFIG="/etc/sing-box/config.json"
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 cloudflare"
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="fakeip.tech-domain.club"
TEST_DOMAIN="fakeip.podkop.net"
INTERFACES_LIST=""
SRC_INTERFACE=""
RESOLV_CONF="/etc/resolv.conf"
CLOUDFLARE_OCTETS="103.21 103.22 103.31 104.16 104.17 104.18 104.19 104.20 104.21 104.22 104.23 104.24 104.25 104.26 104.27 104.28 108.162 131.0 141.101 162.158 162.159 172.64 172.65 172.66 172.67 172.68 172.69 172.70 172.71 173.245 188.114 190.93 197.234 198.41"
log() {
local message="$1"
local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
local CYAN="\033[0;36m"
local GREEN="\033[0;32m"
local RESET="\033[0m"
echo -e "${CYAN}[$timestamp]${RESET} ${GREEN}$message${RESET}"
logger -t "podkop" "$timestamp $message"
}
@@ -131,6 +128,12 @@ start_main() {
sing_box_quic_reject
fi
config_get_bool detour "main" "detour" "0"
if [ "$detour" -eq 1 ]; then
log "Detour mixed enable"
detour_mixed
fi
sing_box_config_check
/etc/init.d/sing-box start
#/etc/init.d/sing-box enable
@@ -203,6 +206,12 @@ reload() {
start_main
}
restart() {
log "Podkop restart"
stop
start
}
# Migrations and validation funcs
migration() {
# list migrate
@@ -563,10 +572,19 @@ list_update() {
fi
for i in $(seq 1 60); do
config_get_bool detour "main" "detour" "0"
if [ "$detour" -eq 1 ]; then
if http_proxy="http://127.0.0.1:4534" https_proxy="http://127.0.0.1:4534" curl -s -m 3 https://github.com >/dev/null; then
log "GitHub is available"
break
fi
else
if curl -s -m 3 https://github.com >/dev/null; then
log "GitHub is available"
break
fi
fi
log "GitHub is unavailable [$i/60]"
sleep 3
done
@@ -775,7 +793,7 @@ sing_box_create_bypass_ruleset() {
"rules": [
{
"domain_suffix": [
"ip.tech-domain.club"
"ip.podkop.net"
]
}
]
@@ -1011,7 +1029,7 @@ sing_box_config_outbound_json() {
if [ $? -eq 0 ]; then
log "Outbound config updated successfully"
else
log "Error: Invalid JSON config generated"
log "Error: Outbound invalid JSON config generated"
return 1
fi
}
@@ -1072,9 +1090,9 @@ sing_box_config_shadowsocks() {
)' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
if [ $? -eq 0 ]; then
log "Config updated successfully"
log "Config Shadowsocks updated successfully"
else
log "Error: Invalid JSON config generated"
log "Error: Shadowsocks invalid JSON config generated"
return 1
fi
}
@@ -1199,9 +1217,9 @@ sing_box_config_vless() {
if [ $? -eq 0 ]; then
log "Config created successfully"
log "Config VLESS created successfully"
else
log "Error: Invalid JSON config generated"
log "Error: VLESS invalid JSON config generated"
return 1
fi
}
@@ -1355,6 +1373,7 @@ sing_box_ruleset_remote() {
local tag=$1
local type=$2
local update_interval=$3
local detour=$4
url="$SRS_MAIN_URL/$tag.srs"
@@ -1370,15 +1389,19 @@ sing_box_ruleset_remote() {
--arg type "$type" \
--arg url "$url" \
--arg update_interval "$update_interval" \
--arg detour "$detour" \
'
.route.rule_set += [
(
{
"tag": $tag,
"type": $type,
"format": "binary",
"url": $url,
"update_interval": $update_interval
}
} +
(if $detour == "1" then {"download_detour": "main"} else {} end)
)
]' "$SING_BOX_CONFIG" > /tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG"
log "Added new ruleset with tag $tag"
@@ -1413,7 +1436,13 @@ list_subnets_download() {
esac
local filename=$(basename "$URL")
config_get_bool detour "main" "detour" "0"
if [ "$detour" -eq 1 ]; then
http_proxy="http://127.0.0.1:4534" https_proxy="http://127.0.0.1:4534" wget -O "/tmp/podkop/$filename" "$URL"
else
wget -O "/tmp/podkop/$filename" "$URL"
fi
while IFS= read -r subnet; do
if [ "$service" = "discord" ]; then
@@ -1475,8 +1504,9 @@ sing_box_quic_reject() {
process_remote_ruleset_srs() {
config_get_bool domain_list_enabled "$section" "domain_list_enabled" "0"
if [ "$domain_list_enabled" -eq 1 ]; then
config_get_bool detour "main" "detour" "0"
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" "$detour"
fi
}
@@ -1543,7 +1573,12 @@ list_custom_url_domains_create() {
local URL="$1"
local filename=$(basename "$URL")
wget -q -O "/tmp/podkop/${filename}" "$URL"
config_get_bool detour "main" "detour" "0"
if [ "$detour" -eq 1 ]; then
http_proxy="http://127.0.0.1:4534" https_proxy="http://127.0.0.1:4534" wget -O "/tmp/podkop/${filename}" "$URL"
else
wget -O "/tmp/podkop/${filename}" "$URL"
fi
while IFS= read -r domain; do
log "From downloaded file: $domain"
@@ -1583,7 +1618,12 @@ list_custom_url_subnets_create() {
local URL="$1"
local filename=$(basename "$URL")
wget -q -O "/tmp/podkop/${filename}" "$URL"
config_get_bool detour "main" "detour" "0"
if [ "$detour" -eq 1 ]; then
http_proxy="http://127.0.0.1:4534" https_proxy="http://127.0.0.1:4534" wget -O "/tmp/podkop/${filename}" "$URL"
else
wget -O "/tmp/podkop/${filename}" "$URL"
fi
while IFS= read -r subnet; do
log "From local file: $subnet"
@@ -1642,6 +1682,31 @@ sing_box_rules_source_ip_cidr() {
fi
}
detour_mixed() {
local section="main"
local port="4534"
local tag="detour"
log "Adding detour Socks5 for $section on port $port"
jq \
--arg tag "$tag" \
--arg port "$port" \
--arg section "$section" \
'.inbounds += [{
"tag": $tag,
"type": "mixed",
"listen": "127.0.0.1",
"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
}
## nftables
list_all_traffic_from_ip() {
local ip="$1"
@@ -1776,7 +1841,14 @@ check_github() {
for url in "$DOMAINS_RU_INSIDE" "$DOMAINS_RU_OUTSIDE" "$DOMAINS_UA" "$DOMAINS_YOUTUBE" \
"$SUBNETS_TWITTER" "$SUBNETS_META" "$SUBNETS_DISCORD"; do
local list_name=$(basename "$url")
config_get_bool detour "main" "detour" "0"
if [ "$detour" -eq 1 ]; then
http_proxy="http://127.0.0.1:4534" https_proxy="http://127.0.0.1:4534" wget -q -O /dev/null "$url"
else
wget -q -O /dev/null "$url"
fi
if [ $? -eq 0 ]; then
nolog "- $list_name: available"
else
@@ -1954,8 +2026,6 @@ show_sing_box_config() {
}
show_config() {
nolog "📄 Current podkop configuration:"
if [ ! -f /etc/config/podkop ]; then
nolog "Configuration file not found"
return 1
@@ -2046,36 +2116,18 @@ get_sing_box_status() {
}
get_status() {
local running=0
local enabled=0
local status=""
# Check if service is enabled
if [ -x /etc/rc.d/S99podkop ]; then
enabled=1
status="enabled"
else
status="disabled"
fi
# Check if service is running
if pgrep -f "sing-box" >/dev/null; then
running=1
fi
# Format status message
if [ $running -eq 1 ]; then
if [ $enabled -eq 1 ]; then
status="running & enabled"
else
status="running but disabled"
fi
else
if [ $enabled -eq 1 ]; then
status="stopped but enabled"
else
status="stopped & disabled"
fi
fi
echo "{\"running\":$running,\"enabled\":$enabled,\"status\":\"$status\"}"
echo "{\"enabled\":$enabled,\"status\":\"$status\"}"
}
check_dns_available() {
@@ -2171,71 +2223,34 @@ sing_box_add_secure_dns_probe_domain() {
log "DNS probe domain ${domain} configured with override to port ${override_port}"
}
print_global() {
local message="$1"
echo "$message"
}
global_check() {
nolog "📡 Global check run!"
print_global "📡 Global check run!"
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "🛠️ System info"
print_global "🕳️ Podkop: $(opkg list-installed podkop | awk '{print $3}')"
print_global "🕳️ LuCI App: $(opkg list-installed luci-app-podkop | awk '{print $3}')"
print_global "📦 Sing-box: $(sing-box version | head -n 1 | awk '{print $3}')"
print_global "🛜 OpenWrt: $(grep OPENWRT_RELEASE /etc/os-release | cut -d'"' -f2)"
print_global "🛜 Device: $(cat /tmp/sysinfo/model)"
nolog "Podkop $(opkg list-installed podkop | awk '{print $3}')"
nolog "LuCi App $(opkg list-installed luci-app-podkop | awk '{print $3}')"
nolog "Sing-box $(sing-box version | head -n 1 | awk '{print $3}')"
nolog "$(grep OPENWRT_RELEASE /etc/os-release | cut -d'"' -f2)"
nolog "Device: $(cat /tmp/sysinfo/model)"
printf "\n"
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "📄 Podkop config"
show_config
printf "\n"
nolog "Checking fakeip functionality..."
nolog "➡️ DNS resolution: system DNS server"
nslookup -timeout=2 $TEST_DOMAIN
local working_resolver=$(find_working_resolver)
if [ -z "$working_resolver" ]; then
nolog "❌ No working resolver found, skipping resolver check"
else
nolog "➡️ DNS resolution: external resolver ($working_resolver)"
nslookup -timeout=2 $TEST_DOMAIN $working_resolver
fi
# Main FakeIP check
nolog "➡️ DNS resolution: sing-box DNS server (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)"
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
fi
printf "\n"
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "🔧 System check"
if grep -E "^nameserver\s+([0-9]{1,3}\.){3}[0-9]{1,3}" "$RESOLV_CONF" | grep -vqE "127\.0\.0\.1|0\.0\.0\.0"; then
nolog "❌ /etc/resolv.conf contains an external nameserver:"
print_global "❌ /etc/resolv.conf contains external nameserver:"
cat /etc/resolv.conf
echo ""
else
nolog "✅ /etc/resolv.conf OK"
print_global "✅ /etc/resolv.conf"
fi
cachesize="$(uci get dhcp.@dnsmasq[0].cachesize 2>/dev/null)"
@@ -2243,29 +2258,31 @@ global_check() {
server="$(uci get dhcp.@dnsmasq[0].server 2>/dev/null)"
if [ "$cachesize" != "0" ] || [ "$noresolv" != "1" ] || [ "$server" != "127.0.0.42" ]; then
nolog "❌ The configuration differs from the template. 📄 DHCP config:"
print_global "❌ DHCP configuration differs from template. 📄 DHCP config:"
awk '/^config /{p=($2=="dnsmasq")} p' /etc/config/dhcp
elif [ "$(uci get podkop.main.dont_touch_dhcp 2>/dev/null)" = "1" ]; then
nolog "⚠️ Enable dont_touch_dhcp. 📄 DHCP config:"
print_global "⚠️ dont_touch_dhcp is enabled. 📄 DHCP config:"
awk '/^config /{p=($2=="dnsmasq")} p' /etc/config/dhcp
else
nolog "✅ /etc/config/dhcp"
print_global "✅ /etc/config/dhcp"
fi
if ! pgrep -f "sing-box" >/dev/null; then
nolog "❌ sing-box is not running"
print_global "❌ sing-box is not running"
else
nolog "✅ sing-box is running"
print_global "✅ sing-box is running"
fi
nolog "📄 NFT Table Podkop"
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "🧱 NFT table"
if ! nft list table inet PodkopTable >/dev/null 2>&1; then
nolog "PodkopTable not found"
print_global "❌ PodkopTable not found"
else
nft list table inet PodkopTable
fi
nolog "📄 WAN config"
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "📄 WAN config"
if uci show network.wan >/dev/null 2>&1; then
awk '
/^config / {
@@ -2286,26 +2303,72 @@ global_check() {
}
' /etc/config/network
else
nolog "WAN not exists"
print_global "❌ WAN configuration not found"
fi
CLOUDFLARE_OCTETS="103.21 103.22 103.31 104.16 104.17 104.18 104.19 104.20 104.21 104.22 104.23 \
104.24 104.25 104.26 104.27 104.28 108.162 131.0 141.101 162.158 162.159 172.64 172.65 172.66 \
172.67 172.68 172.69 172.70 172.71 173.245 188.114 190.93 197.234 198.41"
if uci show network | grep -q endpoint_host; then
uci show network | grep endpoint_host | cut -d'=' -f2 | tr -d "'\" " | while read -r host; do
if [ "$host" = "engage.cloudflareclient.com" ]; then
nolog "⚠️ WARP detected ($host)"
print_global "⚠️ WARP detected: $host"
continue
fi
ip_prefix=$(echo "$host" | cut -d'.' -f1,2)
if echo "$CLOUDFLARE_OCTETS" | grep -wq "$ip_prefix"; then
nolog "⚠️ WARP detected ($host)"
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "⚠️ WARP detected: $host"
fi
done
fi
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "➡️ DNS status"
dns_info=$(check_dns_available)
dns_type=$(echo "$dns_info" | jq -r '.dns_type')
dns_server=$(echo "$dns_info" | jq -r '.dns_server')
status=$(echo "$dns_info" | jq -r '.status')
print_global "$dns_type ($dns_server) is $status"
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "🔁 FakeIP"
print_global "➡️ DNS resolution: system DNS server"
nslookup -timeout=2 $TEST_DOMAIN
local working_resolver=$(find_working_resolver)
if [ -z "$working_resolver" ]; then
print_global "❌ No working external resolver found"
else
print_global "➡️ DNS resolution: external resolver ($working_resolver)"
nslookup -timeout=2 $TEST_DOMAIN $working_resolver
fi
print_global "➡️ DNS resolution: sing-box DNS server (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
print_global "✅ FakeIP is working correctly on router (198.18.x.x)"
else
print_global "❌ FakeIP test failed: Domain did not resolve to FakeIP range"
if ! pgrep -f "sing-box" >/dev/null; then
print_global " ❌ sing-box is not running"
else
print_global " 🤔 sing-box is running, checking configuration"
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")
local dns_rules=$(jq -r '.dns.rules[] | select(.server == "fakeip-server") | .domain' "$SING_BOX_CONFIG")
print_global " 📦 FakeIP enabled: $fakeip_enabled"
print_global " 📦 FakeIP range: $fakeip_range"
print_global " 📦 FakeIP domain: $dns_rules"
else
print_global " ⛔ sing-box config file not found"
fi
fi
fi
}
case "$1" in
@@ -2318,6 +2381,9 @@ case "$1" in
reload)
reload
;;
restart)
restart
;;
main)
main
;;
@@ -2379,7 +2445,7 @@ case "$1" in
global_check
;;
*)
echo "Usage: $0 {start|stop|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|check_dns_available|global_check}"
echo "Usage: $0 {start|stop|reload|restart|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|check_dns_available|global_check}"
exit 1
;;
esac