Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c2fea0533 | |||
| 07527fa8a7 | |||
| 9dfb6b410f | |||
| 142340739a | |||
| e4a2410725 | |||
| db953963cd | |||
| 3569850f7a | |||
| 9d8012ccc8 | |||
| 29e1ea8c4c | |||
| 62dadf7eba | |||
| edbaa3160d | |||
|
|
0a5229f4f6 | ||
|
|
5ecb6ef997 | ||
|
|
340c2b3505 | ||
|
|
515c0be38b | ||
|
|
59c59bcb17 | ||
|
|
e5eff41a0f | ||
|
|
bb1c06951c | ||
|
|
4999840340 | ||
|
|
6c5a271105 | ||
|
|
e336bb831c | ||
|
|
00db99723c | ||
|
|
5439504de7 | ||
|
|
c3072162de | ||
|
|
d021636f85 | ||
|
|
a06aac0613 |
@@ -20,7 +20,7 @@ https://podkop.net/
|
|||||||
|
|
||||||
Вкратце, достаточно одного скрипта для установки и обновления:
|
Вкратце, достаточно одного скрипта для установки и обновления:
|
||||||
```
|
```
|
||||||
sh <(wget -O - https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/install.sh)
|
sh <(wget -O - https://git.ownsrv.ru/yanistyle/podkop/raw/branch/main/install.sh))
|
||||||
```
|
```
|
||||||
|
|
||||||
# ToDo
|
# ToDo
|
||||||
|
|||||||
212
install.sh
212
install.sh
@@ -1,6 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
REPO="https://api.github.com/repos/itdoginfo/podkop/releases/latest"
|
# --- Настройки ---
|
||||||
|
REPO_API="https://git.ownsrv.ru/api/v1/repos/yanistyle/podkop/releases"
|
||||||
DOWNLOAD_DIR="/tmp/podkop"
|
DOWNLOAD_DIR="/tmp/podkop"
|
||||||
COUNT=3
|
COUNT=3
|
||||||
|
|
||||||
@@ -11,59 +12,129 @@ msg() {
|
|||||||
printf "\033[32;1m%s\033[0m\n" "$1"
|
printf "\033[32;1m%s\033[0m\n" "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# --- Проверка и установка зависимостей ---
|
||||||
|
install_dependencies() {
|
||||||
|
deps="curl jq wget"
|
||||||
|
for dep in $deps; do
|
||||||
|
if ! command -v $dep >/dev/null 2>&1; then
|
||||||
|
msg "$dep не найден. Пытаемся установить..."
|
||||||
|
opkg update
|
||||||
|
opkg install $dep || { msg "Не удалось установить $dep"; exit 1; }
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Проверка системы ---
|
||||||
|
check_system() {
|
||||||
|
MODEL=$(cat /tmp/sysinfo/model 2>/dev/null || echo "Unknown")
|
||||||
|
msg "Router model: $MODEL"
|
||||||
|
|
||||||
|
openwrt_version=$(cat /etc/openwrt_release 2>/dev/null | grep DISTRIB_RELEASE | cut -d"'" -f2 | cut -d'.' -f1)
|
||||||
|
if [ "$openwrt_version" = "23" ]; then
|
||||||
|
msg "OpenWrt 23.05 не поддерживается начиная с podkop 0.5.0"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
AVAILABLE_SPACE=$(df /overlay | awk 'NR==2 {print $4}')
|
||||||
|
REQUIRED_SPACE=15360
|
||||||
|
if [ "$AVAILABLE_SPACE" -lt "$REQUIRED_SPACE" ]; then
|
||||||
|
msg "Error: Insufficient space in flash"
|
||||||
|
msg "Available: $((AVAILABLE_SPACE/1024))MB, Required: $((REQUIRED_SPACE/1024))MB"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! nslookup google.com >/dev/null 2>&1; then
|
||||||
|
msg "DNS not working"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if opkg list-installed | grep -q https-dns-proxy; then
|
||||||
|
msg "Сonflicting package detected: https-dns-proxy. Remove?"
|
||||||
|
while true; do
|
||||||
|
read -r -p '' DNSPROXY
|
||||||
|
case $DNSPROXY in
|
||||||
|
yes|y|Y)
|
||||||
|
opkg remove --force-depends luci-app-https-dns-proxy https-dns-proxy luci-i18n-https-dns-proxy*
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
msg "Exit"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Проверка и обновление sing-box ---
|
||||||
|
sing_box() {
|
||||||
|
if ! opkg list-installed | grep -q "^sing-box"; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
sing_box_version=$(sing-box version | head -n 1 | awk '{print $3}')
|
||||||
|
required_version="1.12.4"
|
||||||
|
if [ "$(echo -e "$sing_box_version\n$required_version" | sort -V | head -n 1)" != "$required_version" ]; then
|
||||||
|
msg "sing-box version $sing_box_version is older than required $required_version"
|
||||||
|
msg "Removing old version..."
|
||||||
|
opkg remove sing-box
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Основная логика ---
|
||||||
main() {
|
main() {
|
||||||
check_system
|
check_system
|
||||||
sing_box
|
sing_box
|
||||||
|
install_dependencies
|
||||||
|
|
||||||
/usr/sbin/ntpd -q -p 194.190.168.1 -p 216.239.35.0 -p 216.239.35.4 -p 162.159.200.1 -p 162.159.200.123
|
/usr/sbin/ntpd -q -p 194.190.168.1 -p 216.239.35.0 -p 216.239.35.4 -p 162.159.200.1 -p 162.159.200.123
|
||||||
|
|
||||||
opkg update || { echo "opkg update failed"; exit 1; }
|
opkg update || { echo "opkg update failed"; exit 1; }
|
||||||
|
|
||||||
if [ -f "/etc/init.d/podkop" ]; then
|
if [ -f "/etc/init.d/podkop" ]; then
|
||||||
msg "Podkop is already installed. Upgraded..."
|
msg "Podkop is already installed. Upgraded..."
|
||||||
else
|
else
|
||||||
msg "Installed podkop..."
|
msg "Installed podkop..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v curl &> /dev/null; then
|
|
||||||
check_response=$(curl -s "https://api.github.com/repos/itdoginfo/podkop/releases/latest")
|
|
||||||
|
|
||||||
if echo "$check_response" | grep -q 'API rate limit '; then
|
# --- Получаем последний релиз из Gitea ---
|
||||||
msg "You've reached rate limit from GitHub. Repeat in five minutes."
|
msg "Fetching latest release info from Gitea..."
|
||||||
exit 1
|
LATEST_TAG=$(curl -s "$REPO_API" | jq -r '.[0].tag_name')
|
||||||
fi
|
if [ -z "$LATEST_TAG" ] || [ "$LATEST_TAG" = "null" ]; then
|
||||||
|
msg "Не удалось получить последний релиз"
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
msg "Последний релиз: $LATEST_TAG"
|
||||||
|
|
||||||
|
# --- Получаем список всех assets ---
|
||||||
|
ASSETS=$(curl -s "$REPO_API" | jq -r '.[0].assets[].name')
|
||||||
|
|
||||||
download_success=0
|
download_success=0
|
||||||
while read -r url; do
|
for filename in $ASSETS; do
|
||||||
filename=$(basename "$url")
|
|
||||||
filepath="$DOWNLOAD_DIR/$filename"
|
filepath="$DOWNLOAD_DIR/$filename"
|
||||||
|
|
||||||
attempt=0
|
attempt=0
|
||||||
while [ $attempt -lt $COUNT ]; do
|
while [ $attempt -lt $COUNT ]; do
|
||||||
msg "Download $filename (count $((attempt+1)))..."
|
msg "Download $filename (attempt $((attempt+1)))..."
|
||||||
if wget -q -O "$filepath" "$url"; then
|
wget -q -O "$filepath" "https://git.ownsrv.ru/yanistyle/podkop/releases/download/$LATEST_TAG/$filename"
|
||||||
if [ -s "$filepath" ]; then
|
if [ -s "$filepath" ]; then
|
||||||
msg "$filename successfully downloaded"
|
msg "$filename successfully downloaded"
|
||||||
download_success=1
|
download_success=1
|
||||||
break
|
break
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
msg "Download error $filename. Retry..."
|
msg "Download error $filename. Retry..."
|
||||||
rm -f "$filepath"
|
rm -f "$filepath"
|
||||||
attempt=$((attempt+1))
|
attempt=$((attempt+1))
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ $attempt -eq $COUNT ]; then
|
if [ $attempt -eq $COUNT ]; then
|
||||||
msg "Failed to download $filename after $COUNT attempts"
|
msg "Failed to download $filename after $COUNT attempts"
|
||||||
fi
|
fi
|
||||||
done < <(wget -qO- "$REPO" | grep -o 'https://[^"[:space:]]*\.ipk')
|
done
|
||||||
|
|
||||||
if [ $download_success -eq 0 ]; then
|
if [ $download_success -eq 0 ]; then
|
||||||
msg "No packages were downloaded successfully"
|
msg "No packages were downloaded successfully"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# --- Установка пакетов ---
|
||||||
for pkg in podkop luci-app-podkop; do
|
for pkg in podkop luci-app-podkop; do
|
||||||
file=$(ls "$DOWNLOAD_DIR" | grep "^$pkg" | head -n 1)
|
file=$(ls "$DOWNLOAD_DIR" | grep "^$pkg" | head -n 1)
|
||||||
if [ -n "$file" ]; then
|
if [ -n "$file" ]; then
|
||||||
@@ -76,25 +147,25 @@ main() {
|
|||||||
ru=$(ls "$DOWNLOAD_DIR" | grep "luci-i18n-podkop-ru" | head -n 1)
|
ru=$(ls "$DOWNLOAD_DIR" | grep "luci-i18n-podkop-ru" | head -n 1)
|
||||||
if [ -n "$ru" ]; then
|
if [ -n "$ru" ]; then
|
||||||
if opkg list-installed | grep -q luci-i18n-podkop-ru; then
|
if opkg list-installed | grep -q luci-i18n-podkop-ru; then
|
||||||
msg "Upgraded ru translation..."
|
msg "Upgraded ru translation..."
|
||||||
opkg remove luci-i18n-podkop*
|
opkg remove luci-i18n-podkop*
|
||||||
opkg install "$DOWNLOAD_DIR/$ru"
|
opkg install "$DOWNLOAD_DIR/$ru"
|
||||||
else
|
else
|
||||||
msg "Русский язык интерфейса ставим? y/n (Need a Russian translation?)"
|
msg "Русский язык интерфейса ставим? y/n"
|
||||||
while true; do
|
while true; do
|
||||||
read -r -p '' RUS
|
read -r -p '' RUS
|
||||||
case $RUS in
|
case $RUS in
|
||||||
y)
|
y)
|
||||||
opkg remove luci-i18n-podkop*
|
opkg remove luci-i18n-podkop*
|
||||||
opkg install "$DOWNLOAD_DIR/$ru"
|
opkg install "$DOWNLOAD_DIR/$ru"
|
||||||
break
|
break
|
||||||
;;
|
;;
|
||||||
n)
|
n)
|
||||||
break
|
break
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Введите y или n"
|
echo "Введите y или n"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
@@ -103,69 +174,4 @@ main() {
|
|||||||
find "$DOWNLOAD_DIR" -type f -name '*podkop*' -exec rm {} \;
|
find "$DOWNLOAD_DIR" -type f -name '*podkop*' -exec rm {} \;
|
||||||
}
|
}
|
||||||
|
|
||||||
check_system() {
|
|
||||||
# Get router model
|
|
||||||
MODEL=$(cat /tmp/sysinfo/model)
|
|
||||||
msg "Router model: $MODEL"
|
|
||||||
|
|
||||||
# Check OpenWrt version
|
|
||||||
openwrt_version=$(cat /etc/openwrt_release | grep DISTRIB_RELEASE | cut -d"'" -f2 | cut -d'.' -f1)
|
|
||||||
if [ "$openwrt_version" = "23" ]; then
|
|
||||||
msg "OpenWrt 23.05 не поддерживается начиная с podkop 0.5.0"
|
|
||||||
msg "Для OpenWrt 23.05 используйте podkop версии 0.4.11 или устанавливайте зависимости и podkop вручную"
|
|
||||||
msg "Подробности: https://podkop.net/docs/install/#%d1%83%d1%81%d1%82%d0%b0%d0%bd%d0%be%d0%b2%d0%ba%d0%b0-%d0%bd%d0%b0-2305"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check available space
|
|
||||||
AVAILABLE_SPACE=$(df /overlay | awk 'NR==2 {print $4}')
|
|
||||||
REQUIRED_SPACE=15360 # 15MB in KB
|
|
||||||
|
|
||||||
if [ "$AVAILABLE_SPACE" -lt "$REQUIRED_SPACE" ]; then
|
|
||||||
msg "Error: Insufficient space in flash"
|
|
||||||
msg "Available: $((AVAILABLE_SPACE/1024))MB"
|
|
||||||
msg "Required: $((REQUIRED_SPACE/1024))MB"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! nslookup google.com >/dev/null 2>&1; then
|
|
||||||
msg "DNS not working"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if opkg list-installed | grep -q https-dns-proxy; then
|
|
||||||
msg "Сonflicting package detected: https-dns-proxy. Remove?"
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
read -r -p '' DNSPROXY
|
|
||||||
case $DNSPROXY in
|
|
||||||
|
|
||||||
yes|y|Y|yes)
|
|
||||||
opkg remove --force-depends luci-app-https-dns-proxy https-dns-proxy luci-i18n-https-dns-proxy*
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
msg "Exit"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
sing_box() {
|
|
||||||
if ! opkg list-installed | grep -q "^sing-box"; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
sing_box_version=$(sing-box version | head -n 1 | awk '{print $3}')
|
|
||||||
required_version="1.12.4"
|
|
||||||
|
|
||||||
if [ "$(echo -e "$sing_box_version\n$required_version" | sort -V | head -n 1)" != "$required_version" ]; then
|
|
||||||
msg "sing-box version $sing_box_version is older than required $required_version"
|
|
||||||
msg "Removing old version..."
|
|
||||||
opkg remove sing-box
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
main
|
main
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ function createConfigSection(section, map, network) {
|
|||||||
o = s.taboption('basic', form.ListValue, 'proxy_config_type', _('Configuration Type'), _('Select how to configure the proxy'));
|
o = s.taboption('basic', form.ListValue, 'proxy_config_type', _('Configuration Type'), _('Select how to configure the proxy'));
|
||||||
o.value('url', _('Connection URL'));
|
o.value('url', _('Connection URL'));
|
||||||
o.value('outbound', _('Outbound Config'));
|
o.value('outbound', _('Outbound Config'));
|
||||||
|
o.value('urltest', _('URLTest'));
|
||||||
o.default = 'url';
|
o.default = 'url';
|
||||||
o.depends('mode', 'proxy');
|
o.depends('mode', 'proxy');
|
||||||
o.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
@@ -205,6 +206,11 @@ function createConfigSection(section, map, network) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
o = s.taboption('basic', form.DynamicList, 'urltest_proxy_links', _('URLTest Proxy Links'));
|
||||||
|
o.depends('proxy_config_type', 'urltest');
|
||||||
|
o.placeholder = 'vless:// or ss:// link';
|
||||||
|
o.rmempty = false;
|
||||||
|
|
||||||
o = s.taboption('basic', form.Flag, 'ss_uot', _('Shadowsocks UDP over TCP'), _('Apply for SS2022'));
|
o = s.taboption('basic', form.Flag, 'ss_uot', _('Shadowsocks UDP over TCP'), _('Apply for SS2022'));
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.depends('mode', 'proxy');
|
o.depends('mode', 'proxy');
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ start_main() {
|
|||||||
|
|
||||||
# sing-box
|
# sing-box
|
||||||
sing_box_init_config
|
sing_box_init_config
|
||||||
sing_box_config_check
|
|
||||||
config_foreach add_cron_job
|
config_foreach add_cron_job
|
||||||
/etc/init.d/sing-box start
|
/etc/init.d/sing-box start
|
||||||
|
|
||||||
@@ -83,13 +82,14 @@ start_main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
local proxy_string interface outbound_json dont_touch_dhcp
|
local proxy_string interface outbound_json urltest_proxy_links dont_touch_dhcp
|
||||||
config_get proxy_string "main" "proxy_string"
|
config_get proxy_string "main" "proxy_string"
|
||||||
config_get interface "main" "interface"
|
config_get interface "main" "interface"
|
||||||
config_get outbound_json "main" "outbound_json"
|
config_get outbound_json "main" "outbound_json"
|
||||||
|
config_get urltest_proxy_links "main" "urltest_proxy_links"
|
||||||
|
|
||||||
if [ -z "$proxy_string" ] && [ -z "$interface" ] && [ -z "$outbound_json" ]; then
|
if [ -z "$proxy_string" ] && [ -z "$interface" ] && [ -z "$outbound_json" ] && [ -z "$urltest_proxy_links" ]; then
|
||||||
log "Podkop start aborted: required options (proxy_string, interface, outbound_json) are missing in 'main' section"
|
log "Required options (proxy_string, interface, outbound_json, urltest_proxy_links) are missing in 'main' section. Aborted." "fatal"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -238,23 +238,23 @@ migration() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validate_service() {
|
validate_service() {
|
||||||
local domain="$1"
|
local service="$1"
|
||||||
|
|
||||||
for valid_service in $VALID_SERVICES; do
|
for community_service in $COMMUNITY_SERVICES; do
|
||||||
if [ "$domain" = "$valid_service" ]; then
|
if [ "$service" = "$community_service" ]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
log "Invalid service in domain_list: $domain. Exiting. Check config and LuCI cache"
|
log "Invalid service in community lists: $service. Check config and LuCI cache. Aborted." "fatal"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
process_validate_service() {
|
process_validate_service() {
|
||||||
local domain_list_enabled
|
local community_lists_enabled
|
||||||
config_get_bool domain_list_enabled "$section" "domain_list_enabled" 0
|
config_get_bool community_lists_enabled "$section" "community_lists_enabled" 0
|
||||||
if [ "$domain_list_enabled" -eq 1 ]; then
|
if [ "$community_lists_enabled" -eq 1 ]; then
|
||||||
config_list_foreach "$section" domain_list validate_service
|
config_list_foreach "$section" "community_lists" validate_service
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -629,7 +629,7 @@ configure_outbound_handler() {
|
|||||||
|
|
||||||
case "$proxy_config_type" in
|
case "$proxy_config_type" in
|
||||||
url)
|
url)
|
||||||
log "Detected proxy configuration type: url"
|
log "Detected proxy configuration type: url" "debug"
|
||||||
local proxy_string udp_over_tcp
|
local proxy_string udp_over_tcp
|
||||||
config_get proxy_string "$section" "proxy_string"
|
config_get proxy_string "$section" "proxy_string"
|
||||||
config_get udp_over_tcp "$section" "ss_uot"
|
config_get udp_over_tcp "$section" "ss_uot"
|
||||||
@@ -643,11 +643,42 @@ configure_outbound_handler() {
|
|||||||
config=$(sing_box_cf_add_proxy_outbound "$config" "$section" "$active_proxy_string" "$udp_over_tcp")
|
config=$(sing_box_cf_add_proxy_outbound "$config" "$section" "$active_proxy_string" "$udp_over_tcp")
|
||||||
;;
|
;;
|
||||||
outbound)
|
outbound)
|
||||||
log "Detected proxy configuration type: outbound"
|
log "Detected proxy configuration type: outbound" "debug"
|
||||||
local json_outbound
|
local json_outbound
|
||||||
config_get json_outbound "$section" "outbound_json"
|
config_get json_outbound "$section" "outbound_json"
|
||||||
config=$(sing_box_cf_add_json_outbound "$config" "$section" "$json_outbound")
|
config=$(sing_box_cf_add_json_outbound "$config" "$section" "$json_outbound")
|
||||||
;;
|
;;
|
||||||
|
urltest)
|
||||||
|
log "Detected proxy configuration type: urltest" "debug"
|
||||||
|
local urltest_proxy_links udp_over_tcp i urltest_tag selector_tag outbound_tag outbound_tags \
|
||||||
|
urltest_outbounds selector_outbounds
|
||||||
|
config_get urltest_proxy_links "$section" "urltest_proxy_links"
|
||||||
|
config_get udp_over_tcp "$section" "ss_uot"
|
||||||
|
|
||||||
|
if [ -z "$urltest_proxy_links" ]; then
|
||||||
|
log "URLTest proxy links is not set. Aborted." "fatal"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
i=1
|
||||||
|
for link in $urltest_proxy_links; do
|
||||||
|
config="$(sing_box_cf_add_proxy_outbound "$config" "$section-$i" "$link" "$udp_over_tcp")"
|
||||||
|
outbound_tag="$(get_outbound_tag_by_section "$section-$i")"
|
||||||
|
if [ -z "$outbound_tags" ]; then
|
||||||
|
outbound_tags="$outbound_tag"
|
||||||
|
else
|
||||||
|
outbound_tags="$outbound_tags,$outbound_tag"
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
|
||||||
|
urltest_tag="$(get_outbound_tag_by_section "$section-urltest")"
|
||||||
|
selector_tag="$(get_outbound_tag_by_section "$section")"
|
||||||
|
urltest_outbounds="$(comma_string_to_json_array "$outbound_tags")"
|
||||||
|
selector_outbounds="$(comma_string_to_json_array "$outbound_tags,$urltest_tag")"
|
||||||
|
config="$(sing_box_cm_add_urltest_outbound "$config" "$urltest_tag" "$urltest_outbounds")"
|
||||||
|
config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$urltest_tag")"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
log "Unknown proxy configuration type: '$proxy_config_type'. Aborted." "fatal"
|
log "Unknown proxy configuration type: '$proxy_config_type'. Aborted." "fatal"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -939,8 +970,8 @@ configure_community_list_handler() {
|
|||||||
config_get update_interval "main" "update_interval" "1d"
|
config_get update_interval "main" "update_interval" "1d"
|
||||||
|
|
||||||
config=$(sing_box_cm_add_remote_ruleset "$config" "$ruleset_tag" "$format" "$url" "$detour" "$update_interval")
|
config=$(sing_box_cm_add_remote_ruleset "$config" "$ruleset_tag" "$format" "$url" "$detour" "$update_interval")
|
||||||
_add_ruleset_to_dns_rules "$ruleset_tag"
|
|
||||||
config=$(sing_box_cm_patch_route_rule "$config" "$route_rule_tag" "rule_set" "$ruleset_tag")
|
config=$(sing_box_cm_patch_route_rule "$config" "$route_rule_tag" "rule_set" "$ruleset_tag")
|
||||||
|
_add_ruleset_to_dns_rules "$ruleset_tag"
|
||||||
}
|
}
|
||||||
|
|
||||||
configure_user_domain_or_subnets_list() {
|
configure_user_domain_or_subnets_list() {
|
||||||
@@ -1135,6 +1166,8 @@ sing_box_save_config() {
|
|||||||
log "Save sing-box temporary config to $temp_file_path" "debug"
|
log "Save sing-box temporary config to $temp_file_path" "debug"
|
||||||
sing_box_cm_save_config_to_file "$config" "$temp_file_path"
|
sing_box_cm_save_config_to_file "$config" "$temp_file_path"
|
||||||
|
|
||||||
|
sing_box_config_check "$temp_file_path"
|
||||||
|
|
||||||
current_config_hash=$(md5sum "$sing_box_config_path" 2> /dev/null | awk '{print $1}')
|
current_config_hash=$(md5sum "$sing_box_config_path" 2> /dev/null | awk '{print $1}')
|
||||||
temp_config_hash=$(md5sum "$temp_file_path" | awk '{print $1}')
|
temp_config_hash=$(md5sum "$temp_file_path" | awk '{print $1}')
|
||||||
log "Current sing-box config hash: $current_config_hash" "debug"
|
log "Current sing-box config hash: $current_config_hash" "debug"
|
||||||
@@ -1149,10 +1182,10 @@ sing_box_save_config() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sing_box_config_check() {
|
sing_box_config_check() {
|
||||||
local sing_box_config_path
|
local config_path="$1"
|
||||||
config_get sing_box_config_path "main" "config_path"
|
|
||||||
if ! sing-box -c "$sing_box_config_path" check > /dev/null 2>&1; then
|
if ! sing-box -c "$config_path" check > /dev/null 2>&1; then
|
||||||
log "Sing-box configuration is invalid" "fatal"
|
log "Sing-box configuration $config_path is invalid" "fatal"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -1720,15 +1753,14 @@ show_sing_box_config() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
show_config() {
|
show_config() {
|
||||||
if [ ! -f /etc/config/podkop ]; then
|
if [ ! -f "$PODKOP_CONFIG" ]; then
|
||||||
nolog "Configuration file not found"
|
nolog "Configuration file not found"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
tmp_config=$(mktemp)
|
tmp_config=$(mktemp)
|
||||||
|
|
||||||
cat /etc/config/podkop | sed \
|
sed -e 's/\(option proxy_string\).*/\1 '\''MASKED'\''/g' \
|
||||||
-e 's/\(option proxy_string\).*/\1 '\''MASKED'\''/g' \
|
|
||||||
-e 's/\(option outbound_json\).*/\1 '\''MASKED'\''/g' \
|
-e 's/\(option outbound_json\).*/\1 '\''MASKED'\''/g' \
|
||||||
-e 's/\(option second_proxy_string\).*/\1 '\''MASKED'\''/g' \
|
-e 's/\(option second_proxy_string\).*/\1 '\''MASKED'\''/g' \
|
||||||
-e 's/\(option second_outbound_json\).*/\1 '\''MASKED'\''/g' \
|
-e 's/\(option second_outbound_json\).*/\1 '\''MASKED'\''/g' \
|
||||||
@@ -1737,8 +1769,9 @@ show_config() {
|
|||||||
-e 's/\(pbk=[^&]*\)/pbk=MASKED/g' \
|
-e 's/\(pbk=[^&]*\)/pbk=MASKED/g' \
|
||||||
-e 's/\(sid=[^&]*\)/sid=MASKED/g' \
|
-e 's/\(sid=[^&]*\)/sid=MASKED/g' \
|
||||||
-e 's/\(option dns_server '\''[^'\'']*\.dns\.nextdns\.io'\''\)/option dns_server '\''MASKED.dns.nextdns.io'\''/g' \
|
-e 's/\(option dns_server '\''[^'\'']*\.dns\.nextdns\.io'\''\)/option dns_server '\''MASKED.dns.nextdns.io'\''/g' \
|
||||||
-e "s|\(option dns_server 'dns\.nextdns\.io\)/[^']*|\1/MASKED|"
|
-e "s|\(option dns_server 'dns\.nextdns\.io\)/[^']*|\1/MASKED|" \
|
||||||
> "$tmp_config"
|
-e 's/\(list urltest_proxy_links\).*/\1 '\''MASKED'\''/g' \
|
||||||
|
"$PODKOP_CONFIG" > "$tmp_config"
|
||||||
|
|
||||||
cat "$tmp_config"
|
cat "$tmp_config"
|
||||||
rm -f "$tmp_config"
|
rm -f "$tmp_config"
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ SB_MAIN_OUTBOUND_TAG="main-out"
|
|||||||
SB_REJECT_RULE_TAG="reject-rule-tag"
|
SB_REJECT_RULE_TAG="reject-rule-tag"
|
||||||
|
|
||||||
## Lists
|
## Lists
|
||||||
GITHUB_RAW_URL="https://raw.githubusercontent.com/itdoginfo/allow-domains/main"
|
GITHUB_RAW_URL="https://git.ownsrv.ru/yanistyle/allow-domains/src/branch/main"
|
||||||
SRS_MAIN_URL="https://github.com/itdoginfo/allow-domains/releases/latest/download"
|
SRS_MAIN_URL="https://git.ownsrv.ru/yanistyle/allow-domains/releases/latest"
|
||||||
DOMAINS_RU_INSIDE="${GITHUB_RAW_URL}/Russia/inside-dnsmasq-nfset.lst"
|
DOMAINS_RU_INSIDE="${GITHUB_RAW_URL}/Russia/inside-dnsmasq-nfset.lst"
|
||||||
DOMAINS_RU_OUTSIDE="${GITHUB_RAW_URL}/Russia/outside-dnsmasq-nfset.lst"
|
DOMAINS_RU_OUTSIDE="${GITHUB_RAW_URL}/Russia/outside-dnsmasq-nfset.lst"
|
||||||
DOMAINS_UA="${GITHUB_RAW_URL}/Ukraine/inside-dnsmasq-nfset.lst"
|
DOMAINS_UA="${GITHUB_RAW_URL}/Ukraine/inside-dnsmasq-nfset.lst"
|
||||||
@@ -63,4 +63,4 @@ SUBNETS_HETZNER="${GITHUB_RAW_URL}/Subnets/IPv4/hetzner.lst"
|
|||||||
SUBNETS_OVH="${GITHUB_RAW_URL}/Subnets/IPv4/ovh.lst"
|
SUBNETS_OVH="${GITHUB_RAW_URL}/Subnets/IPv4/ovh.lst"
|
||||||
SUBNETS_DIGITALOCEAN="${GITHUB_RAW_URL}/Subnets/IPv4/digitalocean.lst"
|
SUBNETS_DIGITALOCEAN="${GITHUB_RAW_URL}/Subnets/IPv4/digitalocean.lst"
|
||||||
SUBNETS_CLOUDFRONT="${GITHUB_RAW_URL}/Subnets/IPv4/cloudfront.lst"
|
SUBNETS_CLOUDFRONT="${GITHUB_RAW_URL}/Subnets/IPv4/cloudfront.lst"
|
||||||
VALID_SERVICES="russia_inside russia_outside ukraine_inside geoblock block porn news anime youtube discord meta twitter hdrezka tiktok telegram cloudflare google_ai google_play hetzner ovh hodca digitalocean cloudfront"
|
COMMUNITY_SERVICES="russia_inside russia_outside ukraine_inside geoblock block porn news anime youtube hdrezka tiktok google_ai google_play hodca discord meta twitter cloudflare cloudfront digitalocean hetzner ovh telegram"
|
||||||
@@ -40,6 +40,14 @@ is_base64() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Checks if the given string looks like a Shadowsocks userinfo
|
||||||
|
is_shadowsocks_userinfo_format() {
|
||||||
|
local str="$1"
|
||||||
|
local regex='^[^:]+:[^:]+(:[^:]+)?$'
|
||||||
|
|
||||||
|
[[ "$str" =~ $regex ]]
|
||||||
|
}
|
||||||
|
|
||||||
# Checks if the given file exists
|
# Checks if the given file exists
|
||||||
file_exists() {
|
file_exists() {
|
||||||
local filepath="$1"
|
local filepath="$1"
|
||||||
@@ -176,6 +184,13 @@ url_get_file_extension() {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Remove url fragment (everything after the first '#')
|
||||||
|
url_strip_fragment() {
|
||||||
|
local url="$1"
|
||||||
|
|
||||||
|
echo "${url%%#*}"
|
||||||
|
}
|
||||||
|
|
||||||
# Decodes and returns a base64-encoded string
|
# Decodes and returns a base64-encoded string
|
||||||
base64_decode() {
|
base64_decode() {
|
||||||
local str="$1"
|
local str="$1"
|
||||||
@@ -299,33 +314,11 @@ parse_domain_or_subnet_string_to_commas_string() {
|
|||||||
local string="$1"
|
local string="$1"
|
||||||
local type="$2"
|
local type="$2"
|
||||||
|
|
||||||
local result
|
tmpfile=$(mktemp)
|
||||||
for item in $string; do
|
printf "%s\n" "$string" | sed 's/\/\/.*//' | tr ', ' '\n' | grep -v '^$' > "$tmpfile"
|
||||||
case "$type" in
|
|
||||||
domains)
|
|
||||||
if ! is_domain_suffix "$item"; then
|
|
||||||
log "'$item' is not a valid domain" "debug"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
subnets)
|
|
||||||
if ! is_ipv4_ip_or_ipv4_cidr "$item"; then
|
|
||||||
log "'$item' is not IPv4 or IPv4 CIDR" "debug"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
log "Unknown type: $type" "error"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -z "$result" ]; then
|
result="$(parse_domain_or_subnet_file_to_comma_string "$tmpfile" "$type")"
|
||||||
result="$item"
|
rm -f "$tmpfile"
|
||||||
else
|
|
||||||
result="$result,$item"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "$result"
|
echo "$result"
|
||||||
}
|
}
|
||||||
@@ -345,6 +338,8 @@ parse_domain_or_subnet_file_to_comma_string() {
|
|||||||
|
|
||||||
local result
|
local result
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
|
line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||||
|
|
||||||
[ -z "$line" ] && continue
|
[ -z "$line" ] && continue
|
||||||
|
|
||||||
case "$type" in
|
case "$type" in
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ sing_box_cf_add_proxy_outbound() {
|
|||||||
local udp_over_tcp="$4"
|
local udp_over_tcp="$4"
|
||||||
|
|
||||||
url=$(url_decode "$url")
|
url=$(url_decode "$url")
|
||||||
|
url=$(url_strip_fragment "$url")
|
||||||
|
|
||||||
local scheme="${url%%://*}"
|
local scheme="${url%%://*}"
|
||||||
case "$scheme" in
|
case "$scheme" in
|
||||||
@@ -131,8 +132,12 @@ sing_box_cf_add_proxy_outbound() {
|
|||||||
local userinfo tag host port method password udp_over_tcp
|
local userinfo tag host port method password udp_over_tcp
|
||||||
|
|
||||||
userinfo=$(url_get_userinfo "$url")
|
userinfo=$(url_get_userinfo "$url")
|
||||||
if is_base64 "$userinfo"; then
|
if ! is_shadowsocks_userinfo_format "$userinfo"; then
|
||||||
userinfo=$(base64_decode "$userinfo")
|
userinfo=$(base64_decode "$userinfo")
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
log "Cannot decode shadowsocks userinfo or it does not match the expected format. Aborted." "fatal"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
tag=$(get_outbound_tag_by_section "$section")
|
tag=$(get_outbound_tag_by_section "$section")
|
||||||
|
|||||||
@@ -832,6 +832,90 @@ sing_box_cm_add_raw_outbound() {
|
|||||||
)]'
|
)]'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Add a URLTest outbound to the outbounds section of a sing-box JSON configuration.
|
||||||
|
# Arguments:
|
||||||
|
# config: JSON configuration
|
||||||
|
# tag: string, identifier for the URLTest outbound
|
||||||
|
# outbounds: JSON array of outbound tags to test
|
||||||
|
# url: URL to probe (optional)
|
||||||
|
# interval: test interval (e.g., "10s") (optional)
|
||||||
|
# tolerance: max latency difference tolerated (optional)
|
||||||
|
# idle_timeout: idle timeout duration (optional)
|
||||||
|
# interrupt_exist_connections: flag to interrupt existing connections ("true"/"false") (optional)
|
||||||
|
# Outputs:
|
||||||
|
# Writes updated JSON configuration to stdout
|
||||||
|
# Example:
|
||||||
|
# CONFIG=$(sing_box_cm_add_urltest_outbound "$CONFIG" "auto-select" '["proxy1","proxy2"]')
|
||||||
|
#######################################
|
||||||
|
sing_box_cm_add_urltest_outbound() {
|
||||||
|
local config="$1"
|
||||||
|
local tag="$2"
|
||||||
|
local outbounds="$3"
|
||||||
|
local url="$4"
|
||||||
|
local interval="$5"
|
||||||
|
local tolerance="$6"
|
||||||
|
local idle_timeout="$7"
|
||||||
|
local interrupt_exist_connections="$8"
|
||||||
|
|
||||||
|
echo "$config" | jq \
|
||||||
|
--arg tag "$tag" \
|
||||||
|
--argjson outbounds "$outbounds" \
|
||||||
|
--arg url "$url" \
|
||||||
|
--arg interval "$interval" \
|
||||||
|
--arg tolerance "$tolerance" \
|
||||||
|
--arg idle_timeout "$idle_timeout" \
|
||||||
|
--arg interrupt_exist_connections "$interrupt_exist_connections" \
|
||||||
|
'.outbounds += [
|
||||||
|
{
|
||||||
|
type: "urltest",
|
||||||
|
tag: $tag,
|
||||||
|
outbounds: $outbounds
|
||||||
|
}
|
||||||
|
+ (if $url != "" then {url: $url} else {} end)
|
||||||
|
+ (if $interval != "" then {interval: $interval} else {} end)
|
||||||
|
+ (if $tolerance != "" then {tolerance: ($tolerance | tonumber)} else {} end)
|
||||||
|
+ (if $idle_timeout != "" then {idle_timeout: $idle_timeout} else {} end)
|
||||||
|
+ (if $interrupt_exist_connections == "true" then {interrupt_exist_connections: true} else {} end)
|
||||||
|
]'
|
||||||
|
}
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Add a Selector outbound to the outbounds section of a sing-box JSON configuration.
|
||||||
|
# Arguments:
|
||||||
|
# config: JSON configuration
|
||||||
|
# tag: string, identifier for the Selector outbound
|
||||||
|
# outbounds: JSON array of outbound tags to choose from
|
||||||
|
# default: default outbound tag if none selected (optional)
|
||||||
|
# interrupt_exist_connections: flag to interrupt existing connections ("true"/"false") (optional)
|
||||||
|
# Outputs:
|
||||||
|
# Writes updated JSON configuration to stdout
|
||||||
|
# Example:
|
||||||
|
# CONFIG=$(sing_box_cm_add_selector_outbound "$CONFIG" "select-proxy" '["proxy1","proxy2"]')
|
||||||
|
#######################################
|
||||||
|
sing_box_cm_add_selector_outbound() {
|
||||||
|
local config="$1"
|
||||||
|
local tag="$2"
|
||||||
|
local outbounds="$3"
|
||||||
|
local default="$4"
|
||||||
|
local interrupt_exist_connections="$5"
|
||||||
|
|
||||||
|
echo "$config" | jq \
|
||||||
|
--arg tag "$tag" \
|
||||||
|
--argjson outbounds "$outbounds" \
|
||||||
|
--arg default "$default" \
|
||||||
|
--arg interrupt_exist_connections "$interrupt_exist_connections" \
|
||||||
|
'.outbounds += [
|
||||||
|
{
|
||||||
|
type: "selector",
|
||||||
|
tag: $tag,
|
||||||
|
outbounds: $outbounds,
|
||||||
|
default: $default
|
||||||
|
}
|
||||||
|
+ (if $interrupt_exist_connections == "true" then {interrupt_exist_connections: true} else {} end)
|
||||||
|
]'
|
||||||
|
}
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# Configure the route section of a sing-box JSON configuration.
|
# Configure the route section of a sing-box JSON configuration.
|
||||||
# Arguments:
|
# Arguments:
|
||||||
|
|||||||
Reference in New Issue
Block a user