mirror of
https://github.com/itdoginfo/podkop.git
synced 2025-12-18 21:48:16 +03:00
feat: add DNS and bypass status checks to diagnostics
This commit is contained in:
@@ -663,7 +663,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, fakeipCLIStatus) {
|
let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, singbox, system, fakeipStatus, fakeipCLIStatus, dnsStatus, bypassStatus) {
|
||||||
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;' }, [
|
||||||
@@ -696,6 +696,21 @@ let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, s
|
|||||||
label: 'View Logs',
|
label: 'View Logs',
|
||||||
command: 'check_logs',
|
command: 'check_logs',
|
||||||
title: 'Podkop Logs'
|
title: 'Podkop Logs'
|
||||||
|
}),
|
||||||
|
ButtonFactory.createModalButton({
|
||||||
|
label: _('Update Lists'),
|
||||||
|
command: 'list_update',
|
||||||
|
title: _('Lists Update Results')
|
||||||
|
}),
|
||||||
|
ButtonFactory.createModalButton({
|
||||||
|
label: _('Check NFT Rules'),
|
||||||
|
command: 'check_nft',
|
||||||
|
title: _('NFT Rules')
|
||||||
|
}),
|
||||||
|
ButtonFactory.createModalButton({
|
||||||
|
label: _('Check DNSMasq'),
|
||||||
|
command: 'check_dnsmasq',
|
||||||
|
title: _('DNSMasq Configuration')
|
||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@@ -736,26 +751,28 @@ let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, s
|
|||||||
])
|
])
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
ButtonFactory.createModalButton({
|
E('div', { style: 'margin-bottom: 10px;' }, [
|
||||||
label: _('Check NFT Rules'),
|
E('div', { style: 'margin-bottom: 5px;' }, [
|
||||||
command: 'check_nft',
|
E('strong', {}, _('DNS Status')),
|
||||||
title: _('NFT Rules')
|
E('br'),
|
||||||
}),
|
E('span', { style: `color: ${dnsStatus.color}` }, [
|
||||||
ButtonFactory.createModalButton({
|
dnsStatus.state === 'available' ? '✔' : dnsStatus.state === 'unavailable' ? '✘' : '!',
|
||||||
label: _('Check DNSMasq'),
|
' ',
|
||||||
command: 'check_dnsmasq',
|
dnsStatus.message
|
||||||
title: _('DNSMasq Configuration')
|
])
|
||||||
}),
|
])
|
||||||
ButtonFactory.createModalButton({
|
]),
|
||||||
label: _('Update Lists'),
|
E('div', { style: 'margin-bottom: 10px;' }, [
|
||||||
command: 'list_update',
|
E('div', { style: 'margin-bottom: 5px;' }, [
|
||||||
title: _('Lists Update Results')
|
E('strong', {}, _('Bypass Status')),
|
||||||
}),
|
E('br'),
|
||||||
ButtonFactory.createModalButton({
|
E('span', { style: `color: ${bypassStatus.color}` }, [
|
||||||
label: _('Check Router FakeIP'),
|
bypassStatus.state === 'working' ? '✔' : bypassStatus.state === 'not_working' ? '✘' : '!',
|
||||||
command: 'check_fakeip',
|
' ',
|
||||||
title: _('FakeIP Router Check')
|
bypassStatus.message
|
||||||
})
|
])
|
||||||
|
])
|
||||||
|
])
|
||||||
]),
|
]),
|
||||||
|
|
||||||
// Version Information Panel
|
// Version Information Panel
|
||||||
@@ -774,12 +791,6 @@ let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, s
|
|||||||
|
|
||||||
return view.extend({
|
return view.extend({
|
||||||
async render() {
|
async render() {
|
||||||
// Always add fresh timestamp to URL to prevent caching
|
|
||||||
const timestamp = new Date().getTime();
|
|
||||||
const url = new URL(window.location.href);
|
|
||||||
url.searchParams.set('_nocache', timestamp);
|
|
||||||
window.history.replaceState({}, document.title, url.toString());
|
|
||||||
|
|
||||||
document.head.insertAdjacentHTML('beforeend', `
|
document.head.insertAdjacentHTML('beforeend', `
|
||||||
<style>
|
<style>
|
||||||
.cbi-value {
|
.cbi-value {
|
||||||
@@ -1077,6 +1088,105 @@ return view.extend({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkBypass() {
|
||||||
|
const createStatus = (state, message, color) => ({
|
||||||
|
state,
|
||||||
|
message: _(message),
|
||||||
|
color: STATUS_COLORS[color]
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
try {
|
||||||
|
let configMode = 'proxy'; // Default fallback
|
||||||
|
try {
|
||||||
|
const formData = document.querySelector('form.map-podkop');
|
||||||
|
if (formData) {
|
||||||
|
const modeSelect = formData.querySelector('select[name="cbid.podkop.main.mode"]');
|
||||||
|
if (modeSelect && modeSelect.value) {
|
||||||
|
configMode = modeSelect.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (formError) {
|
||||||
|
console.error('Error getting mode from form:', formError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if sing-box is running
|
||||||
|
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', `${configMode} not running`, 'ERROR'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch IP from first endpoint
|
||||||
|
let ip1 = null;
|
||||||
|
try {
|
||||||
|
const controller1 = new AbortController();
|
||||||
|
const timeoutId1 = setTimeout(() => controller1.abort(), 10000);
|
||||||
|
|
||||||
|
const response1 = await fetch('https://fakeip.tech-domain.club/check', { signal: controller1.signal });
|
||||||
|
const data1 = await response1.json();
|
||||||
|
clearTimeout(timeoutId1);
|
||||||
|
|
||||||
|
ip1 = data1.IP;
|
||||||
|
} catch (error) {
|
||||||
|
return resolve(createStatus('error', 'First endpoint check failed', 'WARNING'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch IP from second endpoint
|
||||||
|
let ip2 = null;
|
||||||
|
try {
|
||||||
|
const controller2 = new AbortController();
|
||||||
|
const timeoutId2 = setTimeout(() => controller2.abort(), 10000);
|
||||||
|
|
||||||
|
const response2 = await fetch('https://ip.tech-domain.club/check', { signal: controller2.signal });
|
||||||
|
const data2 = await response2.json();
|
||||||
|
clearTimeout(timeoutId2);
|
||||||
|
|
||||||
|
ip2 = data2.IP;
|
||||||
|
} catch (error) {
|
||||||
|
return resolve(createStatus('not_working', `${configMode} not working`, 'ERROR'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare IPs
|
||||||
|
if (ip1 && ip2) {
|
||||||
|
if (ip1 !== ip2) {
|
||||||
|
return resolve(createStatus('working', `${configMode} working correctly`, 'SUCCESS'));
|
||||||
|
} else {
|
||||||
|
return resolve(createStatus('not_working', `${configMode} routing incorrect`, 'ERROR'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return resolve(createStatus('error', 'IP comparison failed', 'WARNING'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return resolve(createStatus('error', 'Bypass check error', 'WARNING'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkDNSAvailability() {
|
||||||
|
const createStatus = (state, message, color) => ({
|
||||||
|
state,
|
||||||
|
message: _(message),
|
||||||
|
color: STATUS_COLORS[color]
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
try {
|
||||||
|
const dnsStatusResult = await safeExec('/usr/bin/podkop', ['check_dns_available']);
|
||||||
|
const dnsStatus = JSON.parse(dnsStatusResult.stdout || '{"dns_type":"unknown","dns_server":"unknown","is_available":0,"status":"unknown"}');
|
||||||
|
|
||||||
|
if (dnsStatus.is_available) {
|
||||||
|
return resolve(createStatus('available', `${dnsStatus.dns_type.toUpperCase()} (${dnsStatus.dns_server}) available`, 'SUCCESS'));
|
||||||
|
} else {
|
||||||
|
return resolve(createStatus('unavailable', `${dnsStatus.dns_type.toUpperCase()} (${dnsStatus.dns_server}) unavailable`, 'ERROR'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return resolve(createStatus('error', 'DNS check error', 'WARNING'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function updateDiagnostics() {
|
async function updateDiagnostics() {
|
||||||
try {
|
try {
|
||||||
const [
|
const [
|
||||||
@@ -1087,7 +1197,9 @@ return view.extend({
|
|||||||
singbox,
|
singbox,
|
||||||
system,
|
system,
|
||||||
fakeipStatus,
|
fakeipStatus,
|
||||||
fakeipCLIStatus
|
fakeipCLIStatus,
|
||||||
|
dnsStatus,
|
||||||
|
bypassStatus
|
||||||
] = 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']),
|
||||||
@@ -1096,7 +1208,9 @@ return view.extend({
|
|||||||
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()
|
checkFakeIPCLI(),
|
||||||
|
checkDNSAvailability(),
|
||||||
|
checkBypass()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const parsedPodkopStatus = JSON.parse(podkopStatus.stdout || '{"running":0,"enabled":0,"status":"unknown"}');
|
const parsedPodkopStatus = JSON.parse(podkopStatus.stdout || '{"running":0,"enabled":0,"status":"unknown"}');
|
||||||
@@ -1105,7 +1219,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, fakeipCLIStatus);
|
const statusSection = createStatusSection(parsedPodkopStatus, parsedSingboxStatus, podkop, luci, singbox, system, fakeipStatus, fakeipCLIStatus, dnsStatus, bypassStatus);
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
container.appendChild(statusSection);
|
container.appendChild(statusSection);
|
||||||
|
|
||||||
|
|||||||
@@ -749,4 +749,43 @@ msgid "not works on router"
|
|||||||
msgstr "не работает на роутере"
|
msgstr "не работает на роутере"
|
||||||
|
|
||||||
msgid "Diagnostics"
|
msgid "Diagnostics"
|
||||||
msgstr "Диагностика"
|
msgstr "Диагностика"
|
||||||
|
|
||||||
|
msgid "DNS Status"
|
||||||
|
msgstr "Статус DNS"
|
||||||
|
|
||||||
|
msgid "Bypass Status"
|
||||||
|
msgstr "Статус обхода"
|
||||||
|
|
||||||
|
msgid "proxy working correctly"
|
||||||
|
msgstr "прокси работает корректно"
|
||||||
|
|
||||||
|
msgid "vpn working correctly"
|
||||||
|
msgstr "vpn работает корректно"
|
||||||
|
|
||||||
|
msgid "proxy not working"
|
||||||
|
msgstr "прокси не работает"
|
||||||
|
|
||||||
|
msgid "vpn not working"
|
||||||
|
msgstr "vpn не работает"
|
||||||
|
|
||||||
|
msgid "proxy not running"
|
||||||
|
msgstr "прокси не запущен"
|
||||||
|
|
||||||
|
msgid "vpn not running"
|
||||||
|
msgstr "vpn не запущен"
|
||||||
|
|
||||||
|
msgid "proxy routing incorrect"
|
||||||
|
msgstr "маршрутизация прокси некорректна"
|
||||||
|
|
||||||
|
msgid "vpn routing incorrect"
|
||||||
|
msgstr "маршрутизация vpn некорректна"
|
||||||
|
|
||||||
|
msgid "First endpoint check failed"
|
||||||
|
msgstr "Проверка первой конечной точки не удалась"
|
||||||
|
|
||||||
|
msgid "IP comparison failed"
|
||||||
|
msgstr "Сравнение IP-адресов не удалось"
|
||||||
|
|
||||||
|
msgid "Bypass check error"
|
||||||
|
msgstr "Ошибка проверки обхода"
|
||||||
@@ -1103,4 +1103,43 @@ msgid "not works on router"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Diagnostics"
|
msgid "Diagnostics"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "DNS Status"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Bypass Status"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "proxy working correctly"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "vpn working correctly"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "proxy not working"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "vpn not working"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "proxy not running"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "vpn not running"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "proxy routing incorrect"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "vpn routing incorrect"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "First endpoint check failed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "IP comparison failed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Bypass check error"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -63,6 +63,7 @@ start() {
|
|||||||
sing_box_dns
|
sing_box_dns
|
||||||
sing_box_dns_rule_fakeip
|
sing_box_dns_rule_fakeip
|
||||||
sing_box_rule_dns
|
sing_box_rule_dns
|
||||||
|
sing_box_create_bypass_ruleset
|
||||||
sing_box_add_secure_dns_probe_domain
|
sing_box_add_secure_dns_probe_domain
|
||||||
sing_box_cache_file
|
sing_box_cache_file
|
||||||
process_socks5
|
process_socks5
|
||||||
@@ -726,6 +727,42 @@ sing_box_dns() {
|
|||||||
}' $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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sing_box_create_bypass_ruleset() {
|
||||||
|
log "Creating bypass ruleset for direct access"
|
||||||
|
|
||||||
|
jq '
|
||||||
|
.route.rule_set += [{
|
||||||
|
"tag": "bypass",
|
||||||
|
"type": "inline",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"domain_suffix": [
|
||||||
|
"ip.tech-domain.club"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
|
||||||
|
|
||||||
|
# Add a rule to route bypass domains to direct-out outbound
|
||||||
|
jq '
|
||||||
|
.route.rules += [{
|
||||||
|
"inbound": ["tproxy-in"],
|
||||||
|
"rule_set": ["bypass"],
|
||||||
|
"outbound": "main",
|
||||||
|
"action": "route"
|
||||||
|
}]' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
|
||||||
|
|
||||||
|
# Make sure the bypass ruleset is in the fakeip DNS rule
|
||||||
|
jq '
|
||||||
|
.dns.rules = (.dns.rules | map(
|
||||||
|
if .server == "fakeip-server" then
|
||||||
|
.rule_set += ["bypass"]
|
||||||
|
else
|
||||||
|
.
|
||||||
|
end
|
||||||
|
))' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
|
||||||
|
}
|
||||||
|
|
||||||
sing_box_dns_rule_fakeip() {
|
sing_box_dns_rule_fakeip() {
|
||||||
local rewrite_ttl
|
local rewrite_ttl
|
||||||
config_get rewrite_ttl "main" "dns_rewrite_ttl" "600"
|
config_get rewrite_ttl "main" "dns_rewrite_ttl" "600"
|
||||||
@@ -1985,6 +2022,45 @@ get_status() {
|
|||||||
echo "{\"running\":$running,\"enabled\":$enabled,\"status\":\"$status\"}"
|
echo "{\"running\":$running,\"enabled\":$enabled,\"status\":\"$status\"}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check_dns_available() {
|
||||||
|
local dns_type=$(uci get podkop.main.dns_type 2>/dev/null)
|
||||||
|
local dns_server=$(uci get podkop.main.dns_server 2>/dev/null)
|
||||||
|
local is_available=0
|
||||||
|
local status="unavailable"
|
||||||
|
|
||||||
|
if [ "$dns_type" = "doh" ]; then
|
||||||
|
# Different DoH providers use different endpoints and formats
|
||||||
|
local result=""
|
||||||
|
|
||||||
|
# Try common DoH endpoints and check for valid responses
|
||||||
|
# First try /dns-query endpoint (Cloudflare, AdGuard DNS, etc.)
|
||||||
|
result=$(curl --connect-timeout 5 -s -H "accept: application/dns-json" "https://$dns_server/dns-query?name=itdog.info&type=A")
|
||||||
|
if [ $? -eq 0 ] && echo "$result" | grep -q "data"; then
|
||||||
|
is_available=1
|
||||||
|
status="available"
|
||||||
|
else
|
||||||
|
# If that fails, try /resolve endpoint (Google DNS)
|
||||||
|
result=$(curl --connect-timeout 5 -s -H "accept: application/dns-json" "https://$dns_server/resolve?name=itdog.info&type=A")
|
||||||
|
if [ $? -eq 0 ] && echo "$result" | grep -q "data"; then
|
||||||
|
is_available=1
|
||||||
|
status="available"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
elif [ "$dns_type" = "dot" ]; then
|
||||||
|
if nc $dns_server 853 </dev/null >/dev/null 2>&1; then
|
||||||
|
is_available=1
|
||||||
|
status="available"
|
||||||
|
fi
|
||||||
|
elif [ "$dns_type" = "udp" ]; then
|
||||||
|
if nslookup -timeout=2 itdog.info $dns_server >/dev/null 2>&1; then
|
||||||
|
is_available=1
|
||||||
|
status="available"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "{\"dns_type\":\"$dns_type\",\"dns_server\":\"$dns_server\",\"is_available\":$is_available,\"status\":\"$status\"}"
|
||||||
|
}
|
||||||
|
|
||||||
sing_box_add_secure_dns_probe_domain() {
|
sing_box_add_secure_dns_probe_domain() {
|
||||||
local domain="$TEST_DOMAIN"
|
local domain="$TEST_DOMAIN"
|
||||||
local override_port=8443
|
local override_port=8443
|
||||||
@@ -2079,8 +2155,11 @@ case "$1" in
|
|||||||
get_sing_box_status)
|
get_sing_box_status)
|
||||||
get_sing_box_status
|
get_sing_box_status
|
||||||
;;
|
;;
|
||||||
|
check_dns_available)
|
||||||
|
check_dns_available
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
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}"
|
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|check_dns_available}"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
Reference in New Issue
Block a user