Ground-Zerro / Phobos Public
Code Issues Pull requests Actions Releases View on GitHub ↗
9.0 KB bash
#!/bin/sh
set -e

PHOBOS_DIR=""
ROUTER_PLATFORM=""

. "$(dirname "$0")/lib-client.sh"

remove_obfuscator_instance() {
  local bin="$1"
  local init="$2"
  local service="$3"

  if [ -n "$init" ] && [ -f "/opt/etc/init.d/${init}" ]; then
    "/opt/etc/init.d/${init}" stop >/dev/null 2>&1 || true
    rm -f "/opt/etc/init.d/${init}"
    log "  ✓ Удалён init-скрипт: /opt/etc/init.d/${init}"
  fi

  if [ -n "$service" ] && [ -f "/etc/init.d/${service}" ]; then
    "/etc/init.d/${service}" stop >/dev/null 2>&1 || true
    "/etc/init.d/${service}" disable >/dev/null 2>&1 || true
    rm -f "/etc/init.d/${service}"
    log "  ✓ Удалён procd init: /etc/init.d/${service}"
  fi

  if [ -n "$service" ] && command -v systemctl >/dev/null 2>&1 && [ -f "/etc/systemd/system/${service}.service" ]; then
    systemctl stop "${service}" >/dev/null 2>&1 || true
    systemctl disable "${service}" >/dev/null 2>&1 || true
    rm -f "/etc/systemd/system/${service}.service"
    systemctl daemon-reload >/dev/null 2>&1 || true
    log "  ✓ Удалён systemd сервис: ${service}"
  fi

  local dir
  for dir in /opt/bin /usr/bin /usr/local/bin; do
    if [ -n "$bin" ] && [ -f "${dir}/${bin}" ]; then
      rm -f "${dir}/${bin}"
      log "  ✓ Удалён бинарник: ${dir}/${bin}"
    fi
  done
}

remove_wg_interface_keenetic() {
  local desc="$1"

  if ! command -v curl >/dev/null 2>&1 || ! command -v jq >/dev/null 2>&1; then
    log "  curl/jq недоступны, пропуск удаления интерфейса Keenetic"
    return 0
  fi

  local list
  list=$(curl -s "http://127.0.0.1:79/rci/show/interface" 2>/dev/null || echo "")
  if ! echo "$list" | jq -e . >/dev/null 2>&1; then
    log "  RCI API недоступен, пропуск удаления интерфейса"
    return 0
  fi

  local id
  id=$(echo "$list" | jq -r --arg d "$desc" 'to_entries[] | select((.value.description? // "") == $d) | .key' 2>/dev/null | head -1)
  if [ -z "$id" ]; then
    log "  Интерфейс Keenetic '${desc}' не найден"
    return 0
  fi

  printf '{"interface":{"%s":{"no":true}}}' "$id" | curl -s -X POST \
    -H "Content-Type: application/json" -d @- "http://127.0.0.1:79/rci/" >/dev/null 2>&1 || true
  curl -s -X POST -H "Content-Type: application/json" \
    -d '{"system":{"configuration":{"save":{}}}}' "http://127.0.0.1:79/rci/" >/dev/null 2>&1 || true
  log "  ✓ Интерфейс Keenetic ${id} (${desc}) удалён"
}

remove_wg_interface_openwrt() {
  local iface="$1"

  command -v uci >/dev/null 2>&1 || return 0

  ifdown "$iface" >/dev/null 2>&1 || true

  uci -q delete "network.wgpeer_${iface}" || true
  local section
  for section in $(uci show network 2>/dev/null | grep "=wireguard_${iface}$" | cut -d. -f2 | cut -d= -f1); do
    uci -q delete "network.${section}" || true
  done
  uci -q delete "network.${iface}" || true
  uci commit network

  if uci -q get "firewall.${iface}" >/dev/null 2>&1; then
    uci -q delete "firewall.${iface}" || true
    uci commit firewall
    /etc/init.d/firewall restart >/dev/null 2>&1 || true
  fi

  /etc/init.d/network restart >/dev/null 2>&1 || true
  log "  ✓ Интерфейс OpenWRT ${iface} удалён"
}

remove_socks5_openwrt() {
  [ -f /etc/redsocks.conf ] || [ -f /etc/nftables.d/30-phobos-socks5.nft ] || return 0

  log "  Удаление SOCKS5 (redsocks + nft redirect)..."
  if [ -x /etc/init.d/phobos-redsocks ]; then
    /etc/init.d/phobos-redsocks stop >/dev/null 2>&1 || true
    /etc/init.d/phobos-redsocks disable >/dev/null 2>&1 || true
  fi
  [ -x /etc/init.d/redsocks ] && /etc/init.d/redsocks stop >/dev/null 2>&1 || true
  [ -f /var/run/redsocks.pid ] && kill "$(cat /var/run/redsocks.pid)" 2>/dev/null || true
  pkill -f /usr/sbin/redsocks 2>/dev/null || true
  rm -f /var/run/redsocks.pid /etc/redsocks.conf /etc/nftables.d/30-phobos-socks5.nft /etc/init.d/phobos-redsocks
  nft delete chain inet fw4 phobos_socks5_dstnat 2>/dev/null || true
  nft delete chain inet fw4 phobos_socks5_input 2>/dev/null || true
  fw4 reload >/dev/null 2>&1 || /etc/init.d/firewall reload >/dev/null 2>&1 || true
  log "  ✓ SOCKS5 (redsocks) удалён"
}

remove_wg_interface_linux() {
  local iface="$1"

  command -v systemctl >/dev/null 2>&1 || return 0

  systemctl stop "wg-quick@${iface}" >/dev/null 2>&1 || true
  systemctl disable "wg-quick@${iface}" >/dev/null 2>&1 || true
  rm -rf "/etc/systemd/system/wg-quick@${iface}.service.d"
  rm -f "/etc/wireguard/${iface}.conf"
  systemctl daemon-reload >/dev/null 2>&1 || true
  log "  ✓ Интерфейс Linux ${iface} удалён"
}

remove_client() {
  local client="$1"
  local line
  line=$(registry_line_for_client "$client")

  if [ -z "$line" ]; then
    log "Клиент ${client} не найден в реестре"
    return 1
  fi

  local obf_conf obf_bin init service iface
  obf_conf=$(registry_field "$line" 4)
  obf_bin=$(registry_field "$line" 5)
  init=$(registry_field "$line" 6)
  service=$(registry_field "$line" 7)
  iface=$(registry_field "$line" 8)

  log "Удаление клиента: ${client}"

  remove_obfuscator_instance "$obf_bin" "$init" "$service"

  case "$ROUTER_PLATFORM" in
    keenetic) remove_wg_interface_keenetic "$iface" ;;
    openwrt)  remove_socks5_openwrt; remove_wg_interface_openwrt "$iface" ;;
    linux)    remove_wg_interface_linux "$iface" ;;
  esac

  rm -f "$PHOBOS_DIR/${obf_conf}"
  rm -f "$PHOBOS_DIR/${client}.conf"
  registry_remove_client "$client"

  log "  ✓ Клиент ${client} удалён"
}

remove_phobos_dir() {
  [ -d "$PHOBOS_DIR" ] || return 0
  rm -rf "$PHOBOS_DIR"
  log "✓ Удалена директория: $PHOBOS_DIR"
}

purge_without_registry() {
  log "Реестр не найден — полная очистка артефактов Phobos..."

  local f
  for f in /opt/etc/init.d/S[0-9]*wg-obfuscator /etc/init.d/phobos-obfuscator*; do
    [ -f "$f" ] || continue
    "$f" stop >/dev/null 2>&1 || true
    "$f" disable >/dev/null 2>&1 || true
    rm -f "$f"
    log "  ✓ Удалён init/сервис: $f"
  done

  if command -v systemctl >/dev/null 2>&1; then
    for f in /etc/systemd/system/phobos-obfuscator*.service; do
      [ -f "$f" ] || continue
      local unit
      unit=$(basename "$f")
      systemctl stop "$unit" >/dev/null 2>&1 || true
      systemctl disable "$unit" >/dev/null 2>&1 || true
      rm -f "$f"
      log "  ✓ Удалён systemd сервис: $unit"
    done
    systemctl daemon-reload >/dev/null 2>&1 || true
  fi

  for f in /opt/bin/wg-obfuscator* /usr/bin/wg-obfuscator* /usr/local/bin/wg-obfuscator*; do
    [ -f "$f" ] || continue
    rm -f "$f"
    log "  ✓ Удалён бинарник: $f"
  done

  if command -v pkill >/dev/null 2>&1; then
    pkill -f wg-obfuscator >/dev/null 2>&1 || true
  fi

  remove_phobos_dir
}

remove_all() {
  local client
  for client in $(registry_clients); do
    [ -n "$client" ] && remove_client "$client"
  done
  remove_phobos_dir
}

select_and_remove() {
  local clients count
  clients=$(registry_clients)
  count=$(printf '%s\n' "$clients" | grep -vc '^$')

  if [ "$count" -eq 0 ]; then
    purge_without_registry
    return
  fi

  if [ "$count" -eq 1 ]; then
    remove_client "$clients"
    remove_phobos_dir
    return
  fi

  log ""
  log "Обнаружено несколько клиентов:"
  log ""

  local i=1 line port target
  for client in $clients; do
    line=$(registry_line_for_client "$client")
    port=$(registry_field "$line" 3)
    target=$(registry_field "$line" 9)
    log "  ${i}) ${client}  [порт ${port}, сервер ${target}]"
    i=$((i + 1))
  done

  local all_num=$i
  log "  ${all_num}) Удалить все"
  log ""

  printf "Введите номер для удаления: "
  read -r answer || answer=""

  case "$answer" in
    ''|*[!0-9]*)
      log "ОШИБКА: Неверный ввод"
      exit 1
      ;;
  esac

  if [ "$answer" -eq "$all_num" ]; then
    remove_all
    return
  fi

  if [ "$answer" -lt 1 ] || [ "$answer" -ge "$all_num" ]; then
    log "ОШИБКА: Номер вне диапазона"
    exit 1
  fi

  local selected=""
  i=1
  for client in $clients; do
    if [ "$i" -eq "$answer" ]; then
      selected="$client"
      break
    fi
    i=$((i + 1))
  done

  remove_client "$selected"

  if [ -z "$(registry_clients)" ]; then
    remove_phobos_dir
  fi
}

main() {
  ROUTER_PLATFORM=$(detect_router_platform)
  PHOBOS_DIR=$(detect_phobos_dir "$ROUTER_PLATFORM")

  log "==> Удаление Phobos (платформа: $ROUTER_PLATFORM)"
  log "==> Директория: $PHOBOS_DIR"

  check_root

  select_and_remove

  log ""
  log "╔════════════════════════════════════════════════════════════╗"
  log "║  Удаление завершено                                        ║"
  log "╚════════════════════════════════════════════════════════════╝"
  log ""
}

main "$@"