sed -n '98,170p' /etc/wireguard/wgctl/modules/rule.module.sh

# ============================================
# Rule Application
# ============================================

function rule::apply() {
  local rule_name="${1:?rule_name required}"
  local client_ip="${2:?client_ip required}"
  local peer_name="${3:-}"

  rule::require_exists "$rule_name" || return 1

  if [[ -z "$peer_name" ]]; then
    peer_name=$(peers::find_by_ip "$client_ip")
  fi

  log::debug "rule::apply: peer_name=$peer_name ip=$client_ip"

  # Check if already applied
  if rule::is_applied "$rule_name" "$client_ip"; then
    log::wg "Rule '${rule_name}' already applied to: ${client_ip}"
    [[ -n "$peer_name" ]] && peers::set_meta "$peer_name" "rule" "$rule_name"
    return 0
  fi

  # Process block_ips
  while IFS= read -r block_ip; do
    [[ -z "$block_ip" ]] && continue
    fw::block_ip "$client_ip" "$block_ip"
  done < <(rule::get "$rule_name" "block_ips")

  # Process block_ports
  while IFS= read -r entry; do
    [[ -z "$entry" ]] && continue
    local target port proto
    IFS=":" read -r target port proto <<< "$entry"
    proto="${proto:-tcp}"
    fw::block_port "$client_ip" "$target" "$port" "$proto"
  done < <(rule::get "$rule_name" "block_ports")

  # Process allow_ips (inserted before blocks)
  while IFS= read -r allow_ip; do
    [[ -z "$allow_ip" ]] && continue
    fw::allow_ip "$client_ip" "$allow_ip"
  done < <(rule::get "$rule_name" "allow_ips")

  # Process allow_ports (highest priority)
  while IFS= read -r entry; do
    [[ -z "$entry" ]] && continue
    local target port proto
    IFS=":" read -r target port proto <<< "$entry"
    proto="${proto:-tcp}"
    fw::allow_port "$client_ip" "$target" "$port" "$proto"
  done < <(rule::get "$rule_name" "allow_ports")

  # Persist rule assignment
  [[ -n "$peer_name" ]] && peers::set_meta "$peer_name" "rule" "$rule_name"

  # DNS redirect
  local dns_redirect
  dns_redirect=$(rule::get "$rule_name" "dns_redirect")
  if [[ "$dns_redirect" == "true" ]]; then
    local peer_subnet
    peer_subnet=$(peers::get_ip "$peer_name" | cut -d'.' -f1-3)
    if ! fw::_nat_exists -i wg0 -s "${peer_subnet}.0/24" \
      -p udp --dport 53 -j DNAT \
      --to-destination "$(config::dns):53" 2>/dev/null; then
      rule::apply_dns_redirect "${peer_subnet}.0/24"
      log::debug "dns_redirect: applied for ${peer_subnet}.0/24"
    else
      log::debug "dns_redirect: already applied for ${peer_subnet}.0/24"
    fi
  fi
root@wireguard:/etc/wireguard/wgctl# 