- service.sh: split service_remove into internal (no prompts) and interactive versions to prevent 'eternal wait' when installing new strategy. Install now calls service_remove_internal > /dev/null instead of service_remove. Also filter strategy list to general*.sh only to avoid clutter. Systemd ExecStart now points to run_strategy.sh <name> consistently. - run_strategy.sh: add SIGTERM/SIGINT trap cleanup_strategy() that kills nfqws and cleans up firewall. Prevents stale nfqws/firewall rules after autotest kill. Also handles nfqws exit gracefully with final cleanup. - autotest.sh: rewritten to test strategies by config name (not wrapper filename). Stop now sends SIGTERM to wrapper (which triggers trap cleanup) instead of bare kill. Added extra sleep after stop to let trap fire. Auto-install ExecStart fixed to run_strategy.sh <strategy>. - setup.sh: added libmnl-dev and zlib1g-dev to Ubuntu/Debian dependency install to prevent build failures (missing libmnl/libmnl.h and zlib.h). - general*.sh: removed UTF-8 BOM (0xEF 0xBB 0xBF) that caused 'exec format error' when running scripts on Linux. All 19 wrappers cleaned. - ensure_wrappers.sh: always rewrite wrappers to ensure no stale BOM or paths. Fixes: eternal wait on menu option 1, nfqws build failure, stale processes after test.
580 lines
17 KiB
Bash
Executable File
580 lines
17 KiB
Bash
Executable File
#!/bin/bash
|
|
# service.sh - Zapret Service Manager for Linux
|
|
# Analogous to service.bat on Windows
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "$SCRIPT_DIR/lib/functions.sh"
|
|
|
|
# ============================================
|
|
# External commands (called from general*.sh)
|
|
# ============================================
|
|
if [[ "$1" == "status_zapret" ]]; then
|
|
check_nfqws >/dev/null 2>&1 || true
|
|
tcp_enable_timestamps
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$1" == "check_updates" ]]; then
|
|
if [[ "${2:-}" != "soft" ]]; then
|
|
bash "$0" check_updates soft >&1 &
|
|
exit 0
|
|
else
|
|
check_for_updates soft
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
if [[ "$1" == "load_game_filter" ]]; then
|
|
get_game_filter
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$1" == "load_user_lists" ]]; then
|
|
load_user_lists
|
|
exit 0
|
|
fi
|
|
|
|
# ============================================
|
|
# MENU
|
|
# ============================================
|
|
show_menu() {
|
|
local ipset_status game_filter_status check_updates_status current_strategy
|
|
|
|
ipset_status=$(ipset_switch_status)
|
|
game_filter_status=$(game_switch_status)
|
|
check_updates_status=$(check_updates_switch_status)
|
|
current_strategy=$(get_strategy_name)
|
|
|
|
echo ""
|
|
echo " ZAPRET SERVICE MANAGER v$LOCAL_VERSION"
|
|
echo " $current_strategy"
|
|
echo " ----------------------------------------"
|
|
echo ""
|
|
echo " :: SERVICE"
|
|
echo " 1. Install Service"
|
|
echo " 2. Remove Services"
|
|
echo " 3. Check Status"
|
|
echo ""
|
|
echo " :: SETTINGS"
|
|
echo " 4. Game Filter [$game_filter_status]"
|
|
echo " 5. IPSet Filter [$ipset_status]"
|
|
echo " 6. Auto-Update Check [$check_updates_status]"
|
|
echo ""
|
|
echo " :: UPDATES"
|
|
echo " 7. Update IPSet List"
|
|
echo " 8. Update Hosts File"
|
|
echo " 9. Check for Updates"
|
|
echo ""
|
|
echo " :: TOOLS"
|
|
echo " 10. Run Diagnostics"
|
|
echo " 11. Run Tests"
|
|
echo ""
|
|
echo " ----------------------------------------"
|
|
echo " 0. Exit"
|
|
echo ""
|
|
}
|
|
|
|
# ============================================
|
|
# STATUS HELPERS
|
|
# ============================================
|
|
get_strategy_name() {
|
|
if [[ -f "$SCRIPT_DIR/.service/installed_strategy" ]]; then
|
|
local strat
|
|
strat=$(cat "$SCRIPT_DIR/.service/installed_strategy")
|
|
echo "Current strategy: $strat"
|
|
else
|
|
echo "No service installed"
|
|
fi
|
|
}
|
|
|
|
ipset_switch_status() {
|
|
if [[ -f "$SCRIPT_DIR/.service/ipset_filter_any" ]]; then
|
|
echo "any"
|
|
elif [[ -f "$SCRIPT_DIR/.service/ipset_filter_loaded" ]]; then
|
|
echo "loaded"
|
|
else
|
|
echo "none"
|
|
fi
|
|
}
|
|
|
|
game_switch_status() {
|
|
if [[ -f "$SCRIPT_DIR/.service/game_filter_enabled" ]]; then
|
|
echo "enabled"
|
|
else
|
|
echo "disabled"
|
|
fi
|
|
}
|
|
|
|
check_updates_switch_status() {
|
|
if [[ -f "$SCRIPT_DIR/.service/check_updates_enabled" ]]; then
|
|
echo "enabled"
|
|
else
|
|
echo "disabled"
|
|
fi
|
|
}
|
|
|
|
# ============================================
|
|
# SERVICE INSTALL
|
|
# ============================================
|
|
service_install() {
|
|
echo ""
|
|
echo "[*] Pick one of the strategies:"
|
|
echo ""
|
|
|
|
local files=()
|
|
local i=1
|
|
while IFS= read -r -d '' f; do
|
|
local name
|
|
name=$(basename "$f")
|
|
# Only list strategy wrappers (general*.sh), skip service/utility scripts
|
|
[[ "$name" == general*.sh ]] || continue
|
|
files+=("$f")
|
|
local desc
|
|
desc=$(describe_strategy "$f")
|
|
printf " %2d. %-35s %s\n" "$i" "$name" "$desc"
|
|
((i++))
|
|
done < <(find "$SCRIPT_DIR" -maxdepth 1 -type f -name 'general*.sh' -print0 | sort -z)
|
|
|
|
echo ""
|
|
read -rp " Input file index (number): " choice
|
|
if [[ -z "$choice" ]]; then
|
|
echo "The choice is empty, exiting..."
|
|
return
|
|
fi
|
|
|
|
local selected="${files[$((choice-1))]}"
|
|
if [[ -z "$selected" ]]; then
|
|
echo "Invalid choice, exiting..."
|
|
return
|
|
fi
|
|
|
|
local strategy_name
|
|
strategy_name=$(basename "$selected" .sh)
|
|
|
|
echo ""
|
|
echo "[*] Installing strategy: $strategy_name"
|
|
|
|
# Stop any existing service (internal — no prompts)
|
|
service_remove_internal > /dev/null 2>&1 || true
|
|
|
|
# Write strategy marker
|
|
mkdir -p "$SCRIPT_DIR/.service"
|
|
echo "$strategy_name" > "$SCRIPT_DIR/.service/installed_strategy"
|
|
|
|
# Create systemd service file
|
|
local service_path="$SYSTEMD_DIR/zapret.service"
|
|
cat > "$service_path" <<EOF
|
|
[Unit]
|
|
Description=Zapret DPI bypass (strategy: $strategy_name)
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart=$SCRIPT_DIR/run_strategy.sh $strategy_name
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
# Install and enable
|
|
if command -v systemctl >/dev/null 2>&1; then
|
|
cp "$service_path" /etc/systemd/system/zapret.service
|
|
systemctl daemon-reload
|
|
systemctl enable zapret.service
|
|
systemctl start zapret.service
|
|
echo ""
|
|
print_green "[OK] Service installed and started."
|
|
echo " Strategy: $strategy_name"
|
|
echo " systemd: /etc/systemd/system/zapret.service"
|
|
else
|
|
echo ""
|
|
print_yellow "[!] systemctl not found. Service file created but not installed automatically."
|
|
echo " To start manually: $SCRIPT_DIR/run_strategy.sh $strategy_name"
|
|
fi
|
|
|
|
read -rp "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================
|
|
# SERVICE REMOVE (internal — no prompts)
|
|
# ============================================
|
|
service_remove_internal() {
|
|
# Stop systemd service
|
|
if command -v systemctl >/dev/null 2>&1; then
|
|
systemctl stop zapret.service 2>/dev/null || true
|
|
systemctl disable zapret.service 2>/dev/null || true
|
|
rm -f /etc/systemd/system/zapret.service
|
|
systemctl daemon-reload
|
|
fi
|
|
|
|
# Kill nfqws
|
|
pkill -f "nfqws.*qnum=$NFQUEUE_NUM" 2>/dev/null || true
|
|
sleep 1
|
|
|
|
# Cleanup firewall
|
|
cleanup_firewall
|
|
|
|
# Remove markers
|
|
rm -f "$SCRIPT_DIR/.service/installed_strategy"
|
|
}
|
|
|
|
# ============================================
|
|
# SERVICE REMOVE (interactive)
|
|
# ============================================
|
|
service_remove() {
|
|
echo ""
|
|
echo "[*] Removing services..."
|
|
|
|
service_remove_internal
|
|
|
|
echo ""
|
|
print_green "[OK] Services removed."
|
|
read -rp "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================
|
|
# SERVICE STATUS
|
|
# ============================================
|
|
service_status() {
|
|
echo ""
|
|
echo "[*] Service Status"
|
|
echo ""
|
|
|
|
# Check systemd
|
|
if command -v systemctl >/dev/null 2>&1; then
|
|
if systemctl is-active zapret.service >/dev/null 2>&1; then
|
|
print_green "zapret.service is RUNNING."
|
|
else
|
|
print_red "zapret.service is NOT running."
|
|
fi
|
|
fi
|
|
|
|
# Check nfqws process
|
|
if pgrep -f "nfqws.*qnum=$NFQUEUE_NUM" >/dev/null 2>&1; then
|
|
print_green "nfqws process is RUNNING."
|
|
else
|
|
print_red "nfqws process is NOT running."
|
|
fi
|
|
|
|
# Check iptables/nftables rules
|
|
if nft list table ip $NFT_TABLE >/dev/null 2>&1; then
|
|
print_green "nftables rules are LOADED."
|
|
elif iptables -t mangle -L -n 2>/dev/null | grep -q "NFQUEUE"; then
|
|
print_green "iptables rules are LOADED."
|
|
else
|
|
print_red "No firewall rules found."
|
|
fi
|
|
|
|
# Show strategy
|
|
local strat
|
|
strat=$(get_strategy_name)
|
|
echo ""
|
|
echo "Strategy: $strat"
|
|
echo ""
|
|
|
|
read -rp "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================
|
|
# GAME FILTER SWITCH
|
|
# ============================================
|
|
game_switch() {
|
|
echo ""
|
|
if [[ -f "$SCRIPT_DIR/.service/game_filter_enabled" ]]; then
|
|
rm -f "$SCRIPT_DIR/.service/game_filter_enabled"
|
|
echo "[*] Game Filter: DISABLED"
|
|
echo " (Restart strategy to apply)"
|
|
else
|
|
touch "$SCRIPT_DIR/.service/game_filter_enabled"
|
|
echo "[*] Game Filter: ENABLED"
|
|
echo " (Restart strategy to apply)"
|
|
fi
|
|
read -rp "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================
|
|
# IPSET FILTER SWITCH
|
|
# ============================================
|
|
ipset_switch() {
|
|
echo ""
|
|
echo "[*] IPSet Filter options:"
|
|
echo " 1. none - no IPs checked"
|
|
echo " 2. loaded - check against ipset-all.txt"
|
|
echo " 3. any - any IP goes through filter"
|
|
echo ""
|
|
read -rp " Select option (1-3): " choice
|
|
|
|
rm -f "$SCRIPT_DIR/.service/ipset_filter_none"
|
|
rm -f "$SCRIPT_DIR/.service/ipset_filter_loaded"
|
|
rm -f "$SCRIPT_DIR/.service/ipset_filter_any"
|
|
|
|
case "$choice" in
|
|
1) touch "$SCRIPT_DIR/.service/ipset_filter_none" ;;
|
|
2) touch "$SCRIPT_DIR/.service/ipset_filter_loaded" ;;
|
|
3) touch "$SCRIPT_DIR/.service/ipset_filter_any" ;;
|
|
*) echo "Invalid choice"; return ;;
|
|
esac
|
|
echo "[*] IPSet filter updated. (Restart strategy to apply)"
|
|
read -rp "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================
|
|
# AUTO-UPDATE SWITCH
|
|
# ============================================
|
|
check_updates_switch() {
|
|
echo ""
|
|
if [[ -f "$SCRIPT_DIR/.service/check_updates_enabled" ]]; then
|
|
rm -f "$SCRIPT_DIR/.service/check_updates_enabled"
|
|
echo "[*] Auto-Update Check: DISABLED"
|
|
else
|
|
touch "$SCRIPT_DIR/.service/check_updates_enabled"
|
|
echo "[*] Auto-Update Check: ENABLED"
|
|
fi
|
|
read -rp "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================
|
|
# UPDATE IPSET LIST
|
|
# ============================================
|
|
ipset_update() {
|
|
echo ""
|
|
echo "[*] Updating IPSet list..."
|
|
local url="https://raw.githubusercontent.com/Flowseal/zapret-discord-youtube/main/.service/ipset-all.txt"
|
|
if curl -sfL "$url" -o "$LISTS_DIR/ipset-all.txt.new" 2>/dev/null; then
|
|
mv "$LISTS_DIR/ipset-all.txt.new" "$LISTS_DIR/ipset-all.txt"
|
|
print_green "[OK] ipset-all.txt updated."
|
|
else
|
|
print_red "[!] Failed to update ipset-all.txt"
|
|
echo " (URL may be unavailable)"
|
|
fi
|
|
read -rp "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================
|
|
# UPDATE HOSTS FILE
|
|
# ============================================
|
|
hosts_update() {
|
|
echo ""
|
|
echo "[*] Hosts File Manager"
|
|
echo ""
|
|
|
|
local hosts_file="$SCRIPT_DIR/.service/hosts"
|
|
|
|
if [[ ! -f "$hosts_file" ]]; then
|
|
print_red "[!] Local hosts file not found: $hosts_file"
|
|
read -rp "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
echo "[*] Local hosts entries (for GitHub, Telegram, Discord):"
|
|
echo ""
|
|
grep -v '^#' "$hosts_file" | grep -v '^$' | head -n 20
|
|
local total
|
|
total=$(grep -v '^#' "$hosts_file" | grep -v '^$' | wc -l)
|
|
echo ""
|
|
echo "Total entries: $total"
|
|
echo ""
|
|
|
|
read -rp "Append these entries to /etc/hosts? [y/N]: " ans
|
|
if [[ "$ans" == [yY]* ]]; then
|
|
# Backup /etc/hosts
|
|
sudo cp /etc/hosts "/etc/hosts.backup.$(date +%s)"
|
|
echo "" | sudo tee -a /etc/hosts > /dev/null
|
|
echo "# --- zapret-discord-youtube-linux hosts ---" | sudo tee -a /etc/hosts > /dev/null
|
|
grep -v '^#' "$hosts_file" | grep -v '^$' | sudo tee -a /etc/hosts > /dev/null
|
|
echo "# --- end zapret hosts ---" | sudo tee -a /etc/hosts > /dev/null
|
|
print_green "[OK] Entries added to /etc/hosts"
|
|
echo " Backup saved as /etc/hosts.backup.*"
|
|
else
|
|
echo "[*] Skipped. To add manually:"
|
|
echo " sudo nano /etc/hosts"
|
|
echo " Then copy from: $hosts_file"
|
|
fi
|
|
|
|
read -rp "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================
|
|
# CHECK FOR UPDATES
|
|
# ============================================
|
|
check_for_updates() {
|
|
local mode="${1:-interactive}"
|
|
local github_version_url="https://raw.githubusercontent.com/Flowseal/zapret-discord-youtube/main/.service/version.txt"
|
|
local github_release_url="https://github.com/Flowseal/zapret-discord-youtube/releases/latest"
|
|
|
|
local github_version
|
|
github_version=$(curl -sfL "$github_version_url" 2>/dev/null | head -n1 | tr -d '[:space:]')
|
|
|
|
if [[ -z "$github_version" ]]; then
|
|
echo "Warning: failed to fetch the latest version."
|
|
if [[ "$mode" == "interactive" ]]; then
|
|
read -rp "Press Enter to continue..."
|
|
fi
|
|
return
|
|
fi
|
|
|
|
if [[ "$LOCAL_VERSION" == "$github_version" ]]; then
|
|
echo "Latest version installed: $LOCAL_VERSION"
|
|
if [[ "$mode" == "interactive" ]]; then
|
|
read -rp "Press Enter to continue..."
|
|
fi
|
|
return
|
|
fi
|
|
|
|
echo ""
|
|
echo "New version available: $github_version"
|
|
echo "Your version: $LOCAL_VERSION"
|
|
echo "Release page: $github_release_url"
|
|
echo ""
|
|
|
|
if [[ "$mode" == "interactive" ]]; then
|
|
read -rp "Open download page? [Y/n]: " ans
|
|
if [[ "${ans:-Y}" == [yY]* ]]; then
|
|
if [[ -n "${DISPLAY:-}" ]] && command -v xdg-open >/dev/null 2>&1; then
|
|
xdg-open "$github_release_url"
|
|
elif [[ -n "${DISPLAY:-}" ]] && command -v gnome-open >/dev/null 2>&1; then
|
|
gnome-open "$github_release_url"
|
|
else
|
|
echo "Please open: $github_release_url"
|
|
fi
|
|
fi
|
|
read -rp "Press Enter to continue..."
|
|
fi
|
|
}
|
|
|
|
# ============================================
|
|
# DIAGNOSTICS
|
|
# ============================================
|
|
service_diagnostics() {
|
|
echo ""
|
|
echo "[*] Running Diagnostics"
|
|
echo ""
|
|
|
|
# Check root/sudo
|
|
if [[ "$EUID" -ne 0 ]]; then
|
|
print_yellow "[!] Not running as root. Some checks may fail."
|
|
fi
|
|
|
|
# Check nfqws
|
|
if [[ -x "$BIN_DIR/nfqws" ]]; then
|
|
print_green "[OK] nfqws binary found"
|
|
else
|
|
print_red "[FAIL] nfqws binary NOT found"
|
|
echo " Run: ./install_nfqws.sh"
|
|
fi
|
|
|
|
# Check iptables/nftables
|
|
if command -v iptables >/dev/null 2>&1; then
|
|
print_green "[OK] iptables found"
|
|
else
|
|
print_red "[FAIL] iptables NOT found"
|
|
fi
|
|
|
|
if command -v nft >/dev/null 2>&1; then
|
|
print_green "[OK] nftables (nft) found"
|
|
else
|
|
print_yellow "[WARN] nftables (nft) NOT found"
|
|
fi
|
|
|
|
# Check kernel modules
|
|
if lsmod | grep -q "nfnetlink_queue"; then
|
|
print_green "[OK] nfnetlink_queue module loaded"
|
|
else
|
|
print_yellow "[WARN] nfnetlink_queue module not loaded"
|
|
echo " Try: sudo modprobe nfnetlink_queue"
|
|
fi
|
|
|
|
# Check Secure DNS (systemd-resolved)
|
|
if command -v resolvectl >/dev/null 2>&1; then
|
|
echo ""
|
|
echo "[*] DNS settings:"
|
|
resolvectl status 2>/dev/null | head -n 20 || true
|
|
fi
|
|
|
|
# Check for conflicting processes
|
|
echo ""
|
|
echo "[*] Checking for conflicting processes..."
|
|
if pgrep -f "goodbye-dpi\|byedpi\|zapret-win" >/dev/null 2>&1; then
|
|
print_yellow "[!] Another DPI bypass tool may be running"
|
|
else
|
|
print_green "[OK] No conflicting processes found"
|
|
fi
|
|
|
|
# Offer to clear Discord cache
|
|
echo ""
|
|
read -rp "Clear Discord cache? [y/N]: " ans
|
|
if [[ "$ans" == [yY]* ]]; then
|
|
local discord_cache="$HOME/.config/discord/Cache"
|
|
if [[ -d "$discord_cache" ]]; then
|
|
rm -rf "$discord_cache"/* 2>/dev/null || true
|
|
print_green "[OK] Discord cache cleared."
|
|
else
|
|
print_yellow "[!] Discord cache directory not found."
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
read -rp "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================
|
|
# RUN TESTS
|
|
# ============================================
|
|
run_tests() {
|
|
echo ""
|
|
echo "[*] Running Tests"
|
|
echo ""
|
|
echo " 1. Standard tests (check common sites)"
|
|
echo " 2. DPI checkers (Cloudflare, Amazon, etc.)"
|
|
echo ""
|
|
read -rp " Select option (1-2): " choice
|
|
|
|
case "$choice" in
|
|
1) bash "$UTILS_DIR/test_zapret.sh" standard ;;
|
|
2) bash "$UTILS_DIR/test_zapret.sh" dpi ;;
|
|
*) echo "Invalid choice" ;;
|
|
esac
|
|
|
|
read -rp "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================
|
|
# MAIN LOOP
|
|
# ============================================
|
|
main() {
|
|
# Require root for service management
|
|
if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then
|
|
print_red "[!] This script must be run as root (or with sudo)"
|
|
echo " sudo $0"
|
|
exit 1
|
|
fi
|
|
|
|
while true; do
|
|
clear 2>/dev/null || true
|
|
show_menu
|
|
read -rp " Select option (0-11): " menu_choice
|
|
|
|
case "$menu_choice" in
|
|
1) service_install ;;
|
|
2) service_remove ;;
|
|
3) service_status ;;
|
|
4) game_switch ;;
|
|
5) ipset_switch ;;
|
|
6) check_updates_switch ;;
|
|
7) ipset_update ;;
|
|
8) hosts_update ;;
|
|
9) check_for_updates ;;
|
|
10) service_diagnostics ;;
|
|
11) run_tests ;;
|
|
0) echo "Goodbye!"; exit 0 ;;
|
|
*) echo "Invalid option" ; sleep 1 ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
main
|