Skip to content

Instantly share code, notes, and snippets.

@OmerFarukOruc
Last active January 22, 2026 12:26
Show Gist options
  • Select an option

  • Save OmerFarukOruc/ab08b4fa0116ab85e79519ce1248af3d to your computer and use it in GitHub Desktop.

Select an option

Save OmerFarukOruc/ab08b4fa0116ab85e79519ce1248af3d to your computer and use it in GitHub Desktop.
Zapret All-in-One Manager - DPI bypass + NextDNS + DNS leak fix for Fedora

Zapret All-in-One Manager

DPI bypass + NextDNS + DNS leak protection for Fedora Linux.

Quick Start

# Interactive mode
sudo ./zapret.sh

# Full auto (no prompts)
sudo NEXTDNS_ID="abc123" ./zapret.sh --auto

# Auto with AI analysis (uses claudez to pick best strategy)
sudo NEXTDNS_ID="abc123" ./zapret.sh --auto --ai

# Force scan mode (most thorough, 20-30 min)
sudo NEXTDNS_ID="abc123" ./zapret.sh --auto --ai --mode force

# Custom domain
sudo NEXTDNS_ID="abc123" ./zapret.sh --auto --domain youtube.com

Command Line Options

Flag Description
--auto, -a Run without prompts
--ai Use LLM to analyze blockcheck results
--ai-cmd <cmd> AI command (default: claudez)
--mode <mode> Scan mode: quick, standard, force
--domain <domain> Test domain (default: discord.com)

Scan Modes

Mode Time Description
quick 5-10 min Fast, stops at first working strategy
standard 10-20 min Tests more combinations
force 20-30 min Tests everything, finds optimal strategy

What It Does

Component Purpose
Zapret Bypasses DPI (Deep Packet Inspection) blocking
NextDNS Encrypted DNS over TLS
Blockcheck Auto-detects working bypass strategy
DNS Leak Fix Prevents ISP DNS from leaking through

Features

  • Auto Mode - Complete installation without prompts
  • AI Analysis - Optional LLM-powered strategy selection
  • Scan Modes - Quick/standard/force for different thoroughness levels
  • Smart Strategy Detection - Parses blockcheck SUMMARY for working strategies
  • DNS Leak Protection - Fixes NetworkManager DNS leaks automatically
  • Menu System - Update config, run blockcheck, view status

Requirements

  • Fedora Linux (tested on 43)
  • Root access
  • NextDNS account (get profile ID from https://my.nextdns.io)
  • (Optional) claudez CLI for AI analysis

Configuration

Set your NextDNS profile ID before running:

# Option 1: Environment variable (recommended)
sudo NEXTDNS_ID="abc123" ./zapret.sh --auto

# Option 2: Edit line 19 in the script
NEXTDNS_PROFILE="your_nextdns_id"

Menu Options

Option Description
1 Update DPI bypass strategy
2 Run blockcheck & auto-apply
3 View current configuration
4 Analyze blockcheck results
5 Full reinstall
6 Restart service
7 View service status

Strategies

The script auto-detects the best strategy. Common ones:

Strategy When Used
--dpi-desync=fake --dpi-desync-ttl=5 Most ISPs
--dpi-desync=multisplit --dpi-desync-split-pos=2 Aggressive DPI
--hostspell=hoSt HTTP-only blocking

Troubleshooting

# Check service status
systemctl status zapret

# View logs
journalctl -u zapret -f

# Verify DNS (should show NextDNS)
resolvectl status

# Test bypass
curl -I https://discord.com

License

MIT

#!/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 "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment