|
#!/bin/bash |
|
|
|
# Zapret All-in-One Manager |
|
# Combined: Installation + Config Update + Analysis |
|
# Version: 5.5 (AI + Scan Mode) |
|
# Usage: sudo ./zapret.sh [--auto] [--ai] [--mode force] [--domain discord.com] |
|
|
|
set -euo pipefail |
|
|
|
# ============================================================================ |
|
# CONFIGURATION |
|
# ============================================================================ |
|
|
|
readonly ZAPRET_DIR="/opt/zapret" |
|
readonly ZAPRET_CONFIG="${ZAPRET_DIR}/config" |
|
readonly BLOCKCHECK_DIR="/tmp/zapret" |
|
|
|
# NextDNS Configuration |
|
readonly NEXTDNS_PROFILE="${NEXTDNS_ID:-YOUR_NEXTDNS_ID}" |
|
|
|
# DPI Bypass Configuration (defaults) |
|
DPI_DESYNC_METHOD="fake" |
|
DPI_TTL="3" |
|
DPI_SPLIT_POS="1" |
|
DPI_FULL_OPTIONS="" |
|
|
|
# Blockcheck Configuration |
|
ENABLE_BLOCKCHECK="" |
|
TEST_DOMAIN="" |
|
BLOCKCHECK_MODE="" |
|
|
|
AUTO_MODE=false |
|
AI_MODE=false |
|
AI_CMD="${AI_CMD:-claudez}" |
|
SCAN_MODE="${SCAN_MODE:-quick}" |
|
|
|
# ============================================================================ |
|
# COLORS AND FORMATTING |
|
# ============================================================================ |
|
|
|
readonly RED='\033[0;31m' |
|
readonly GREEN='\033[0;32m' |
|
readonly YELLOW='\033[1;33m' |
|
readonly BLUE='\033[0;34m' |
|
readonly CYAN='\033[0;36m' |
|
readonly MAGENTA='\033[0;35m' |
|
readonly NC='\033[0m' |
|
|
|
# ============================================================================ |
|
# LOGGING FUNCTIONS |
|
# ============================================================================ |
|
|
|
log() { echo -e "${GREEN}[INFO]${NC} $1"; } |
|
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } |
|
error() { echo -e "${RED}[ERROR]${NC} $1"; } |
|
success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } |
|
|
|
header() { |
|
echo "" |
|
echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}" |
|
printf "${BLUE}║ %-62s ║${NC}\n" "$1" |
|
echo -e "${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}" |
|
echo "" |
|
} |
|
|
|
# ============================================================================ |
|
# UTILITY FUNCTIONS |
|
# ============================================================================ |
|
|
|
check_root() { |
|
if [[ $EUID -ne 0 ]]; then |
|
error "This script must be run as root. Use 'sudo $0'" |
|
exit 1 |
|
fi |
|
} |
|
|
|
is_zapret_installed() { |
|
[[ -d "$ZAPRET_DIR" ]] |
|
} |
|
|
|
is_zapret_running() { |
|
systemctl is-active --quiet zapret 2>/dev/null |
|
} |
|
|
|
wait_for_enter() { |
|
[[ "$AUTO_MODE" == true ]] && return |
|
echo "" |
|
read -r -p "Press Enter to return to menu..." |
|
} |
|
|
|
read_input() { |
|
local prompt="$1" |
|
local default="${2:-}" |
|
|
|
if [[ "$AUTO_MODE" == true ]]; then |
|
printf '%s' "$default" |
|
return |
|
fi |
|
|
|
echo -en "${CYAN}${prompt}${NC} " >&2 |
|
read -r REPLY |
|
printf '%s' "${REPLY:-$default}" |
|
} |
|
|
|
# Validate domain format (basic check) |
|
validate_domain() { |
|
local domain="$1" |
|
[[ "$domain" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$ ]] |
|
} |
|
|
|
show_banner() { |
|
clear |
|
echo -e "${MAGENTA}" |
|
cat << "BANNER" |
|
███████╗ █████╗ ██████╗ ██████╗ ███████╗████████╗ |
|
╚══███╔╝██╔══██╗██╔══██╗██╔══██╗██╔════╝╚══██╔══╝ |
|
███╔╝ ███████║██████╔╝██████╔╝█████╗ ██║ |
|
███╔╝ ██╔══██║██╔═══╝ ██╔══██╗██╔══╝ ██║ |
|
███████╗██║ ██║██║ ██║ ██║███████╗ ██║ |
|
╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ |
|
|
|
Zapret All-in-One Manager v5.1 |
|
NextDNS + Zapret + Blockcheck |
|
BANNER |
|
echo -e "${NC}" |
|
} |
|
|
|
# ============================================================================ |
|
# BACKUP/RESTORE FUNCTIONS |
|
# ============================================================================ |
|
|
|
create_config_backup() { |
|
local backup_file="${ZAPRET_CONFIG}.backup.$(date +%Y%m%d-%H%M%S)" |
|
cp "$ZAPRET_CONFIG" "$backup_file" |
|
echo "$backup_file" |
|
} |
|
|
|
restore_latest_backup() { |
|
local latest_backup |
|
latest_backup=$(ls -t "${ZAPRET_CONFIG}.backup."* 2>/dev/null | head -1) |
|
|
|
if [[ -n "$latest_backup" && -f "$latest_backup" ]]; then |
|
cp "$latest_backup" "$ZAPRET_CONFIG" |
|
return 0 |
|
fi |
|
return 1 |
|
} |
|
|
|
# ============================================================================ |
|
# SERVICE MANAGEMENT |
|
# ============================================================================ |
|
|
|
restart_zapret_service() { |
|
log "Restarting Zapret service..." |
|
systemctl restart zapret |
|
sleep 2 |
|
|
|
if is_zapret_running; then |
|
success "Zapret service restarted successfully" |
|
return 0 |
|
else |
|
error "Service failed to start" |
|
return 1 |
|
fi |
|
} |
|
|
|
# ============================================================================ |
|
# STRATEGY PARSING (shared function) |
|
# ============================================================================ |
|
|
|
parse_blockcheck_strategy() { |
|
local log_file="$1" |
|
local http_strategy="" https_strategy="" |
|
|
|
local summary_section |
|
summary_section=$(sed -n '/^\* SUMMARY/,/^press enter\|^Please note/p' "$log_file" 2>/dev/null || true) |
|
|
|
https_strategy=$(echo "$summary_section" | grep -E "curl_test_https_tls12.*nfqws" | \ |
|
sed 's/.*: nfqws/nfqws/' | head -1 || true) |
|
|
|
http_strategy=$(echo "$summary_section" | grep -E "curl_test_http .*nfqws" | \ |
|
sed 's/.*: nfqws/nfqws/' | head -1 || true) |
|
|
|
if [[ -n "$https_strategy" ]]; then |
|
echo "$https_strategy" |
|
elif [[ -n "$http_strategy" ]]; then |
|
echo "$http_strategy" |
|
else |
|
grep -E "working strategy found.*nfqws" "$log_file" 2>/dev/null | \ |
|
tail -1 | grep -oP 'nfqws [^!]+' | sed 's/ *$//' || true |
|
fi |
|
} |
|
|
|
ai_analyze_blockcheck() { |
|
local log_file="$1" |
|
|
|
if ! command -v "$AI_CMD" &>/dev/null; then |
|
warn "AI command '$AI_CMD' not found. Set AI_CMD env var or install claude/glm." |
|
return 1 |
|
fi |
|
|
|
log "Asking AI to analyze blockcheck results..." |
|
|
|
local summary_section |
|
summary_section=$(sed -n '/^\* SUMMARY/,/^Please note/p' "$log_file" 2>/dev/null | head -50) |
|
|
|
if [[ -z "$summary_section" ]]; then |
|
warn "No SUMMARY section found in log" |
|
return 1 |
|
fi |
|
|
|
local prompt="Analyze this zapret blockcheck SUMMARY and return ONLY the best nfqws strategy. |
|
Prefer HTTPS TLS 1.2 over HTTP. Return ONLY the nfqws command with options, nothing else. |
|
Example output: nfqws --dpi-desync=fake --dpi-desync-ttl=5 |
|
|
|
SUMMARY: |
|
$summary_section" |
|
|
|
local ai_response |
|
ai_response=$("$AI_CMD" -p "$prompt" 2>/dev/null || echo "") |
|
|
|
if [[ -z "$ai_response" ]]; then |
|
warn "AI returned empty response" |
|
return 1 |
|
fi |
|
|
|
local strategy |
|
strategy=$(echo "$ai_response" | grep -oE 'nfqws [^"]+' | head -1 | sed 's/ *$//') |
|
|
|
if [[ -n "$strategy" ]]; then |
|
success "AI recommended: $strategy" |
|
echo "$strategy" |
|
return 0 |
|
fi |
|
|
|
warn "Could not parse AI response" |
|
return 1 |
|
} |
|
|
|
extract_strategy_params() { |
|
local strategy="$1" |
|
local param="$2" |
|
|
|
echo "$strategy" | grep -oP "(?<=--${param}=)[^ ]+" | head -1 || true |
|
} |
|
|
|
build_nfqws_opt() { |
|
local desync="$1" |
|
local ttl="${2:-}" |
|
local fooling="${3:-}" |
|
local split="${4:-}" |
|
|
|
local opt="--filter-tcp=80,443 --dpi-desync=${desync}" |
|
[[ -n "$ttl" ]] && opt="${opt} --dpi-desync-ttl=${ttl}" |
|
[[ -n "$fooling" ]] && opt="${opt} --dpi-desync-fooling=${fooling}" |
|
[[ -n "$split" ]] && opt="${opt} --dpi-desync-split-pos=${split}" |
|
opt="${opt} --uid=0:0" |
|
|
|
echo "$opt" |
|
} |
|
|
|
apply_strategy_from_blockcheck() { |
|
local strategy="$1" |
|
local opts="${strategy#nfqws }" |
|
DPI_FULL_OPTIONS="--filter-tcp=80,443 ${opts} --uid=0:0" |
|
} |
|
|
|
# ============================================================================ |
|
# MAIN MENU |
|
# ============================================================================ |
|
|
|
show_main_menu() { |
|
show_banner |
|
|
|
echo -e "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}" |
|
echo -e "${CYAN}║ Main Menu ║${NC}" |
|
echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}" |
|
echo "" |
|
|
|
if is_zapret_installed; then |
|
if is_zapret_running; then |
|
echo -e "${GREEN}✓${NC} Zapret is installed and running" |
|
else |
|
echo -e "${YELLOW}!${NC} Zapret is installed but NOT running" |
|
fi |
|
echo "" |
|
echo -e "${GREEN}1)${NC} Update Configuration ${YELLOW}(Recommended)${NC}" |
|
echo -e "${GREEN}2)${NC} Run Blockcheck & Auto-Update Config" |
|
echo -e "${GREEN}3)${NC} View Current Configuration" |
|
echo -e "${GREEN}4)${NC} Analyze Blockcheck Results" |
|
echo -e "${GREEN}5)${NC} Reinstall Zapret (Full Installation)" |
|
echo -e "${GREEN}6)${NC} Restart Zapret Service" |
|
echo -e "${GREEN}7)${NC} View Service Status" |
|
echo -e "${GREEN}8)${NC} Exit" |
|
else |
|
echo -e "${YELLOW}!${NC} Zapret is not installed" |
|
echo "" |
|
echo -e "${GREEN}1)${NC} Install Zapret (Full Installation)" |
|
echo -e "${GREEN}2)${NC} Exit" |
|
fi |
|
|
|
echo "" |
|
MENU_CHOICE=$(read_input "Select option:") |
|
} |
|
|
|
# ============================================================================ |
|
# OPTION 1: UPDATE CONFIGURATION |
|
# ============================================================================ |
|
|
|
update_configuration() { |
|
header "Update Configuration" |
|
|
|
if [[ ! -f "$ZAPRET_CONFIG" ]]; then |
|
error "Zapret config not found. Please install Zapret first." |
|
wait_for_enter |
|
return |
|
fi |
|
|
|
echo -e "${CYAN}Current Configuration:${NC}" |
|
local current_opt |
|
current_opt=$(grep "^NFQWS_OPT=" "$ZAPRET_CONFIG" || echo "Not set") |
|
echo -e "${YELLOW}${current_opt}${NC}" |
|
echo "" |
|
|
|
echo -e "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}" |
|
echo -e "${CYAN}║ Available Strategies ║${NC}" |
|
echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}" |
|
echo "" |
|
|
|
echo -e "${GREEN}1)${NC} Fake with TTL=5 ${YELLOW}(RECOMMENDED)${NC}" |
|
echo -e " Simple, reliable, works for most sites" |
|
echo "" |
|
echo -e "${GREEN}2)${NC} Fake with badsum" |
|
echo -e " Uses checksum manipulation, no specific TTL" |
|
echo "" |
|
echo -e "${GREEN}3)${NC} Fake with badseq" |
|
echo -e " Sequence number manipulation" |
|
echo "" |
|
echo -e "${GREEN}4)${NC} FakeDSplit with TTL=5" |
|
echo -e " Advanced: combines fake packets with splitting" |
|
echo "" |
|
echo -e "${GREEN}5)${NC} Cancel" |
|
echo "" |
|
|
|
local strategy_choice new_opt strategy_name |
|
strategy_choice=$(read_input "Select strategy [1-5] (default: 1):" "1") |
|
|
|
case "$strategy_choice" in |
|
1) |
|
new_opt='NFQWS_OPT="--filter-tcp=80,443 --dpi-desync=fake --dpi-desync-ttl=5 --uid=0:0"' |
|
strategy_name="Fake with TTL=5" |
|
;; |
|
2) |
|
new_opt='NFQWS_OPT="--filter-tcp=80,443 --dpi-desync=fake --dpi-desync-fooling=badsum --uid=0:0"' |
|
strategy_name="Fake with badsum" |
|
;; |
|
3) |
|
new_opt='NFQWS_OPT="--filter-tcp=80,443 --dpi-desync=fake --dpi-desync-fooling=badseq --dpi-desync-badseq-increment=0 --uid=0:0"' |
|
strategy_name="Fake with badseq" |
|
;; |
|
4) |
|
new_opt='NFQWS_OPT="--filter-tcp=80,443 --dpi-desync=fakedsplit --dpi-desync-ttl=5 --dpi-desync-split-pos=1 --uid=0:0"' |
|
strategy_name="FakeDSplit with TTL=5" |
|
;; |
|
5) |
|
log "Cancelled" |
|
wait_for_enter |
|
return |
|
;; |
|
*) |
|
error "Invalid choice" |
|
wait_for_enter |
|
return |
|
;; |
|
esac |
|
|
|
log "Creating backup..." |
|
local backup_file |
|
backup_file=$(create_config_backup) |
|
success "Backup created: ${backup_file}" |
|
|
|
log "Updating configuration with: ${strategy_name}" |
|
sed -i "s|^NFQWS_OPT=.*|${new_opt}|" "$ZAPRET_CONFIG" |
|
success "Configuration updated" |
|
|
|
if restart_zapret_service; then |
|
echo "" |
|
echo -e "${GREEN}✓${NC} Strategy: ${strategy_name}" |
|
echo -e "${GREEN}✓${NC} Configuration updated and applied!" |
|
else |
|
error "Service failed to start. Restoring backup..." |
|
if restore_latest_backup; then |
|
systemctl restart zapret 2>/dev/null || true |
|
success "Backup restored" |
|
else |
|
error "Could not restore backup" |
|
fi |
|
fi |
|
|
|
wait_for_enter |
|
} |
|
|
|
# ============================================================================ |
|
# OPTION 2: RUN BLOCKCHECK (shared implementation) |
|
# ============================================================================ |
|
|
|
run_blockcheck_common() { |
|
local domain="$1" |
|
local mode_num="$2" |
|
local mode_name="$3" |
|
|
|
log "Starting blockcheck for ${domain} (${mode_name} mode)..." |
|
log "This may take several minutes..." |
|
echo "" |
|
|
|
local zapret_was_running=false |
|
if is_zapret_running; then |
|
warn "Stopping Zapret for accurate blockcheck..." |
|
systemctl stop zapret |
|
zapret_was_running=true |
|
sleep 1 |
|
fi |
|
|
|
mkdir -p "$BLOCKCHECK_DIR" |
|
local blockcheck_log="${BLOCKCHECK_DIR}/blockcheck-$(date +%Y%m%d-%H%M%S).log" |
|
|
|
cd "$ZAPRET_DIR" |
|
./blockcheck.sh <<BLOCKEOF 2>&1 | tee "$blockcheck_log" |
|
${domain} |
|
4 |
|
Y |
|
Y |
|
N |
|
1 |
|
${mode_num} |
|
BLOCKEOF |
|
|
|
success "Blockcheck completed!" |
|
echo "" |
|
|
|
if [[ "$zapret_was_running" == true ]]; then |
|
log "Restarting Zapret..." |
|
systemctl start zapret |
|
fi |
|
|
|
log "Analyzing results..." |
|
|
|
if grep -q "working without bypass" "$blockcheck_log"; then |
|
echo "" |
|
success "Site is ALREADY ACCESSIBLE without DPI bypass!" |
|
echo -e "${GREEN}No bypass strategy needed for ${domain}${NC}" |
|
echo "Log saved to: $blockcheck_log" |
|
return 1 |
|
fi |
|
|
|
local best_strategy |
|
|
|
if [[ "$AI_MODE" == true ]]; then |
|
best_strategy=$(ai_analyze_blockcheck "$blockcheck_log") || true |
|
fi |
|
|
|
if [[ -z "$best_strategy" ]]; then |
|
best_strategy=$(parse_blockcheck_strategy "$blockcheck_log") |
|
fi |
|
|
|
if [[ -n "$best_strategy" ]]; then |
|
echo "" |
|
echo -e "${GREEN}Best strategy found:${NC}" |
|
echo -e "${YELLOW}${best_strategy}${NC}" |
|
echo "" |
|
|
|
local apply |
|
if [[ "$AUTO_MODE" == true ]]; then |
|
apply="Y" |
|
log "Auto-applying strategy..." |
|
else |
|
apply=$(read_input "Apply this strategy? (Y/n):" "Y") |
|
fi |
|
|
|
if [[ "$apply" != "n" && "$apply" != "N" ]]; then |
|
apply_strategy_from_blockcheck "$best_strategy" |
|
return 0 |
|
fi |
|
else |
|
warn "Could not detect best strategy from results" |
|
echo "Try a different domain or scan mode" |
|
echo "Log saved to: $blockcheck_log" |
|
fi |
|
|
|
return 1 |
|
} |
|
|
|
run_blockcheck_update() { |
|
header "Run Blockcheck & Auto-Update" |
|
|
|
if ! is_zapret_installed; then |
|
error "Zapret not installed. Please install first." |
|
wait_for_enter |
|
return |
|
fi |
|
|
|
local test_domain mode_input mode_num mode_name |
|
|
|
test_domain=$(read_input "Enter domain to test (e.g., discord.com):" "discord.com") |
|
|
|
if ! validate_domain "$test_domain"; then |
|
warn "Domain format may be invalid, proceeding anyway..." |
|
fi |
|
|
|
echo "" |
|
echo -e "${CYAN}Select scan mode:${NC}" |
|
echo -e "${GREEN}1)${NC} Quick (5-10 min)" |
|
echo -e "${GREEN}2)${NC} Standard (10-20 min)" |
|
echo -e "${GREEN}3)${NC} Force (20-30 min)" |
|
echo "" |
|
|
|
mode_input=$(read_input "Choice [1-3] (default: 1):" "1") |
|
|
|
case "$mode_input" in |
|
2) mode_num=2; mode_name="standard" ;; |
|
3) mode_num=3; mode_name="force" ;; |
|
*) mode_num=1; mode_name="quick" ;; |
|
esac |
|
|
|
if run_blockcheck_common "$test_domain" "$mode_num" "$mode_name"; then |
|
log "Updating config..." |
|
create_config_backup >/dev/null |
|
|
|
local new_opt="NFQWS_OPT=\"${DPI_FULL_OPTIONS}\"" |
|
sed -i "s|^NFQWS_OPT=.*|${new_opt}|" "$ZAPRET_CONFIG" |
|
|
|
restart_zapret_service |
|
fi |
|
|
|
wait_for_enter |
|
} |
|
|
|
# ============================================================================ |
|
# OPTION 3: VIEW CONFIGURATION |
|
# ============================================================================ |
|
|
|
view_configuration() { |
|
header "Current Configuration" |
|
|
|
if [[ ! -f "$ZAPRET_CONFIG" ]]; then |
|
error "Config file not found" |
|
wait_for_enter |
|
return |
|
fi |
|
|
|
echo -e "${CYAN}NFQWS Configuration:${NC}" |
|
grep "^NFQWS_OPT=" "$ZAPRET_CONFIG" || echo "Not configured" |
|
echo "" |
|
|
|
echo -e "${CYAN}Service Status:${NC}" |
|
systemctl status zapret --no-pager 2>&1 | head -10 || echo "Service not found" |
|
|
|
wait_for_enter |
|
} |
|
|
|
# ============================================================================ |
|
# OPTION 4: ANALYZE RESULTS |
|
# ============================================================================ |
|
|
|
analyze_results() { |
|
header "Blockcheck Results Analysis" |
|
|
|
echo -e "${GREEN}RECOMMENDED STRATEGIES for Discord:${NC}" |
|
echo "" |
|
echo -e "${YELLOW}1. Fake with TTL=5${NC} (Simplest & Most Reliable)" |
|
echo -e " Works: discord.com, youtube.com, most sites" |
|
echo "" |
|
echo -e "${YELLOW}2. Fake with badsum${NC} (No TTL needed)" |
|
echo -e " Works: When TTL methods fail" |
|
echo "" |
|
echo -e "${YELLOW}3. FakeDSplit${NC} (Advanced splitting)" |
|
echo -e " Works: Stubborn DPI systems" |
|
echo "" |
|
|
|
if [[ -f "$ZAPRET_CONFIG" ]]; then |
|
echo -e "${CYAN}Your Current Config:${NC}" |
|
grep "^NFQWS_OPT=" "$ZAPRET_CONFIG" || echo "Not configured" |
|
fi |
|
|
|
wait_for_enter |
|
} |
|
|
|
# ============================================================================ |
|
# INSTALLATION FUNCTIONS (defined at top level to avoid scope issues) |
|
# ============================================================================ |
|
|
|
validate_nextdns_config() { |
|
if [[ "$NEXTDNS_PROFILE" == "YOUR_NEXTDNS_ID" ]]; then |
|
error "NextDNS Profile ID not configured!" |
|
echo -e "${YELLOW}Please edit the script and set NEXTDNS_PROFILE variable.${NC}" |
|
exit 1 |
|
fi |
|
log "NextDNS Profile ID: ${NEXTDNS_PROFILE}" |
|
} |
|
|
|
ask_blockcheck_config() { |
|
header "Blockcheck Configuration" |
|
|
|
if [[ "$AUTO_MODE" == true ]]; then |
|
ENABLE_BLOCKCHECK="yes" |
|
[[ -z "$TEST_DOMAIN" ]] && TEST_DOMAIN="discord.com" |
|
BLOCKCHECK_MODE="$SCAN_MODE" |
|
success "Blockcheck auto-configured: ${TEST_DOMAIN} (${BLOCKCHECK_MODE})" |
|
return |
|
fi |
|
|
|
local enable_input |
|
enable_input=$(read_input "Run blockcheck to auto-detect best strategy? (Y/n):" "Y") |
|
|
|
if [[ "$enable_input" == "n" || "$enable_input" == "N" ]]; then |
|
ENABLE_BLOCKCHECK="no" |
|
return |
|
fi |
|
|
|
ENABLE_BLOCKCHECK="yes" |
|
|
|
TEST_DOMAIN=$(read_input "Test domain (default: discord.com):" "discord.com") |
|
|
|
echo "" |
|
echo -e "${GREEN}1)${NC} Quick scan (5-10 min)" |
|
echo -e "${GREEN}2)${NC} Standard scan (10-20 min)" |
|
echo -e "${GREEN}3)${NC} Force scan (20-30 min)" |
|
echo "" |
|
|
|
local mode_input |
|
mode_input=$(read_input "Choice [1-3] (default: 1):" "1") |
|
|
|
case "$mode_input" in |
|
2) BLOCKCHECK_MODE="standard" ;; |
|
3) BLOCKCHECK_MODE="force" ;; |
|
*) BLOCKCHECK_MODE="quick" ;; |
|
esac |
|
|
|
success "Blockcheck configured: ${TEST_DOMAIN} (${BLOCKCHECK_MODE})" |
|
sleep 1 |
|
} |
|
|
|
update_hosts_file() { |
|
log "Updating hosts file..." |
|
dnf install -y hostname sed &>/dev/null || true |
|
|
|
local hostname_val |
|
hostname_val=$(hostname) |
|
|
|
if grep -q "^127\.0\.1\.1" /etc/hosts; then |
|
sed -i "s/^127\.0\.1\.1.*/127.0.1.1\t${hostname_val}/" /etc/hosts |
|
else |
|
echo -e "127.0.1.1\t${hostname_val}" >> /etc/hosts |
|
fi |
|
success "Hosts file updated" |
|
} |
|
|
|
cleanup_old_zapret() { |
|
log "Cleaning old installations..." |
|
systemctl stop zapret 2>/dev/null || true |
|
systemctl disable zapret 2>/dev/null || true |
|
rm -rf "$ZAPRET_DIR" |
|
rm -f /etc/systemd/system/zapret.service |
|
systemctl daemon-reload |
|
success "Cleaned" |
|
} |
|
|
|
install_required_tools() { |
|
header "Installing System Tools" |
|
log "Updating packages..." |
|
dnf update -y &>/dev/null |
|
|
|
log "Installing dependencies..." |
|
dnf install -y \ |
|
curl git bind-utils nftables wget systemd-resolved \ |
|
gcc make zlib-devel libnetfilter_queue-devel \ |
|
libmnl-devel libnfnetlink-devel systemd-devel \ |
|
policycoreutils-python-utils &>/dev/null |
|
success "Tools installed" |
|
} |
|
|
|
fix_dns_leaks() { |
|
log "Fixing DNS leaks..." |
|
|
|
mkdir -p /etc/NetworkManager/conf.d |
|
cat > /etc/NetworkManager/conf.d/no-dns.conf <<NMDNS |
|
[main] |
|
dns=none |
|
NMDNS |
|
|
|
local conn_uuid |
|
for conn_uuid in $(nmcli -g UUID connection show --active 2>/dev/null); do |
|
local conn_type |
|
conn_type=$(nmcli -g connection.type connection show "$conn_uuid" 2>/dev/null || true) |
|
|
|
if [[ "$conn_type" == "802-11-wireless" || "$conn_type" == "ethernet" ]]; then |
|
nmcli connection modify "$conn_uuid" ipv4.ignore-auto-dns yes 2>/dev/null || true |
|
nmcli connection modify "$conn_uuid" ipv6.ignore-auto-dns yes 2>/dev/null || true |
|
fi |
|
done |
|
|
|
cat > /etc/NetworkManager/dispatcher.d/99-force-global-dns <<'DISPATCHER' |
|
#!/bin/bash |
|
[[ "$2" != "up" ]] && exit 0 |
|
resolvectl dns "$1" "" 2>/dev/null || true |
|
resolvectl default-route "$1" false 2>/dev/null || true |
|
DISPATCHER |
|
chmod +x /etc/NetworkManager/dispatcher.d/99-force-global-dns |
|
|
|
systemctl restart NetworkManager |
|
sleep 2 |
|
|
|
log "Waiting for DNS..." |
|
local retries=10 |
|
while ! host github.com &>/dev/null && [[ $retries -gt 0 ]]; do |
|
sleep 1 |
|
((retries--)) |
|
done |
|
|
|
success "DNS leak protection enabled" |
|
} |
|
|
|
configure_dns() { |
|
header "Configuring NextDNS" |
|
log "Enabling systemd-resolved..." |
|
systemctl enable --now systemd-resolved |
|
|
|
log "Configuring NextDNS servers..." |
|
cat > /etc/systemd/resolved.conf <<DNSEOF |
|
[Resolve] |
|
DNS=45.90.28.0#${NEXTDNS_PROFILE}.dns.nextdns.io |
|
DNS=2a07:a8c0::#${NEXTDNS_PROFILE}.dns.nextdns.io |
|
DNS=45.90.30.0#${NEXTDNS_PROFILE}.dns.nextdns.io |
|
DNS=2a07:a8c1::#${NEXTDNS_PROFILE}.dns.nextdns.io |
|
DNSOverTLS=yes |
|
DNSSEC=allow-downgrade |
|
Cache=yes |
|
DNSEOF |
|
|
|
ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf |
|
systemctl restart systemd-resolved |
|
success "NextDNS configured" |
|
|
|
fix_dns_leaks |
|
} |
|
|
|
download_zapret() { |
|
header "Downloading Zapret" |
|
cd /opt |
|
|
|
if git clone https://github.com/bol-van/zapret.git; then |
|
success "Cloned" |
|
else |
|
error "Failed to clone" |
|
exit 1 |
|
fi |
|
|
|
chown -R root:root "$ZAPRET_DIR" |
|
chmod -R 755 "$ZAPRET_DIR" |
|
} |
|
|
|
install_zapret_main() { |
|
header "Compiling Zapret" |
|
cd "$ZAPRET_DIR" |
|
|
|
log "Running installer..." |
|
./install_easy.sh <<EOF |
|
Y |
|
2 |
|
Y |
|
N |
|
N |
|
1 |
|
1 |
|
N |
|
1 |
|
1 |
|
EOF |
|
success "Installed" |
|
} |
|
|
|
run_blockcheck_install() { |
|
header "Running Blockcheck - ${TEST_DOMAIN}" |
|
|
|
if [[ "$ENABLE_BLOCKCHECK" != "yes" ]]; then |
|
return |
|
fi |
|
|
|
local mode_num |
|
case "$BLOCKCHECK_MODE" in |
|
standard) mode_num=2 ;; |
|
force) mode_num=3 ;; |
|
*) mode_num=1 ;; |
|
esac |
|
|
|
if run_blockcheck_common "$TEST_DOMAIN" "$mode_num" "$BLOCKCHECK_MODE"; then |
|
success "Will use detected strategy" |
|
fi |
|
|
|
echo "" |
|
if [[ "$AUTO_MODE" != true ]]; then |
|
read -r -p "Press Enter to continue..." |
|
fi |
|
} |
|
|
|
configure_zapret_optimized() { |
|
header "Applying Configuration" |
|
|
|
local nfqws_line |
|
if [[ -n "$DPI_FULL_OPTIONS" ]]; then |
|
nfqws_line="NFQWS_OPT=\"${DPI_FULL_OPTIONS}\"" |
|
else |
|
nfqws_line='NFQWS_OPT="--filter-tcp=80,443 --dpi-desync=fake --dpi-desync-ttl=3 --uid=0:0"' |
|
fi |
|
|
|
cat > "$ZAPRET_CONFIG" <<CONFIGEOF |
|
FWTYPE=nftables |
|
SET_MAXELEM=522288 |
|
IPSET_OPT="hashsize 262144 maxelem \$SET_MAXELEM" |
|
IP2NET_OPT4="--prefix-length=22-30 --v4-threshold=3/4" |
|
IP2NET_OPT6="--prefix-length=56-64 --v6-threshold=5" |
|
AUTOHOSTLIST_RETRANS_THRESHOLD=3 |
|
AUTOHOSTLIST_FAIL_THRESHOLD=3 |
|
AUTOHOSTLIST_FAIL_TIME=60 |
|
AUTOHOSTLIST_DEBUGLOG=0 |
|
MDIG_THREADS=30 |
|
GZIP_LISTS=1 |
|
DESYNC_MARK=0x40000000 |
|
DESYNC_MARK_POSTNAT=0x20000000 |
|
TPWS_ENABLE=0 |
|
TPWS_PORTS=80,443 |
|
TPWS_SOCKS_ENABLE=0 |
|
NFQWS_ENABLE=1 |
|
NFQWS_PORTS_TCP=80,443 |
|
NFQWS_PORTS_UDP=443 |
|
NFQWS_TCP_PKT_OUT=9 |
|
NFQWS_TCP_PKT_IN=3 |
|
NFQWS_UDP_PKT_OUT=9 |
|
NFQWS_UDP_PKT_IN=0 |
|
MODE_FILTER=none |
|
${nfqws_line} |
|
FLOWOFFLOAD=none |
|
INIT_APPLY_FW=1 |
|
DISABLE_IPV4=0 |
|
DISABLE_IPV6=1 |
|
FILTER_TTL_EXPIRED_ICMP=1 |
|
CONFIGEOF |
|
|
|
success "Configuration applied" |
|
} |
|
|
|
start_zapret_service() { |
|
header "Starting Service" |
|
systemctl daemon-reload |
|
systemctl enable zapret |
|
|
|
if restart_zapret_service; then |
|
success "Service running" |
|
else |
|
error "Service failed" |
|
fi |
|
} |
|
|
|
# ============================================================================ |
|
# OPTION 5: FULL INSTALLATION |
|
# ============================================================================ |
|
|
|
full_installation() { |
|
show_banner |
|
check_root |
|
validate_nextdns_config |
|
ask_blockcheck_config |
|
|
|
if [[ "$AUTO_MODE" != true ]]; then |
|
echo "" |
|
read -r -p "Press Enter to start installation..." |
|
fi |
|
|
|
update_hosts_file |
|
install_required_tools |
|
configure_dns |
|
cleanup_old_zapret |
|
download_zapret |
|
install_zapret_main |
|
|
|
if [[ "$ENABLE_BLOCKCHECK" == "yes" ]]; then |
|
run_blockcheck_install |
|
fi |
|
|
|
configure_zapret_optimized |
|
start_zapret_service |
|
|
|
header "Installation Complete!" |
|
echo "" |
|
echo -e "${GREEN}✓${NC} Zapret installed and running" |
|
echo -e "${GREEN}✓${NC} NextDNS configured" |
|
[[ "$ENABLE_BLOCKCHECK" == "yes" ]] && echo -e "${GREEN}✓${NC} Blockcheck completed" |
|
echo "" |
|
|
|
wait_for_enter |
|
} |
|
|
|
# ============================================================================ |
|
# OPTION 6: RESTART SERVICE |
|
# ============================================================================ |
|
|
|
restart_service() { |
|
header "Restart Service" |
|
restart_zapret_service |
|
wait_for_enter |
|
} |
|
|
|
# ============================================================================ |
|
# OPTION 7: VIEW STATUS |
|
# ============================================================================ |
|
|
|
view_status() { |
|
header "Service Status" |
|
systemctl status zapret --no-pager 2>&1 || echo "Service not found" |
|
wait_for_enter |
|
} |
|
|
|
# ============================================================================ |
|
# MAIN LOOP |
|
# ============================================================================ |
|
|
|
main() { |
|
while [[ $# -gt 0 ]]; do |
|
case "$1" in |
|
--auto|-a) |
|
AUTO_MODE=true |
|
shift |
|
;; |
|
--ai) |
|
AI_MODE=true |
|
shift |
|
;; |
|
--ai-cmd) |
|
AI_CMD="$2" |
|
shift 2 |
|
;; |
|
--domain|-d) |
|
TEST_DOMAIN="$2" |
|
shift 2 |
|
;; |
|
--mode|-m) |
|
SCAN_MODE="$2" |
|
shift 2 |
|
;; |
|
*) |
|
shift |
|
;; |
|
esac |
|
done |
|
|
|
check_root |
|
|
|
if [[ "$AUTO_MODE" == true ]]; then |
|
log "Running in AUTO mode - no prompts" |
|
full_installation |
|
exit 0 |
|
fi |
|
|
|
while true; do |
|
show_main_menu |
|
|
|
if is_zapret_installed; then |
|
case "$MENU_CHOICE" in |
|
1) update_configuration ;; |
|
2) run_blockcheck_update ;; |
|
3) view_configuration ;; |
|
4) analyze_results ;; |
|
5) full_installation ;; |
|
6) restart_service ;; |
|
7) view_status ;; |
|
8) |
|
echo "" |
|
log "Goodbye!" |
|
exit 0 |
|
;; |
|
*) |
|
error "Invalid option" |
|
sleep 1 |
|
;; |
|
esac |
|
else |
|
case "$MENU_CHOICE" in |
|
1) full_installation ;; |
|
2) |
|
echo "" |
|
log "Goodbye!" |
|
exit 0 |
|
;; |
|
*) |
|
error "Invalid option" |
|
sleep 1 |
|
;; |
|
esac |
|
fi |
|
done |
|
} |
|
|
|
main "$@" |