#!/usr/bin/env bash # ============================================ # Block file management # ============================================ # ── Core state queries ───────────────────── function block::file() { local name="${1:?name required}" ctx::block::path "${name}.block" } function block::has_file() { local name="${1:?}" [[ -f "$(block::file "$name")" ]] } function block::has_specific_rules() { local name="${1:?}" block::has_file "$name" || return 1 while IFS="|" read -r bname btype target port proto; do [[ -z "$btype" ]] && continue [[ "$btype" != "full" ]] && return 0 done < <(block::get_rules "$name") return 1 } function block::is_blocked() { local name="${1:?}" block::has_file "$name" || return 1 local result result=$(json::block_is_blocked "$(block::file "$name")") [[ "$result" == "true" ]] } function block::is_blocked_direct() { local name="${1:?}" block::has_file "$name" || { echo "false"; return 0; } json::block_get_direct "$(block::file "$name")" } function block::get_groups() { local name="${1:?}" block::has_file "$name" || return 0 json::block_get_groups "$(block::file "$name")" } function block::get_rules() { local name="${1:?}" block::has_file "$name" || return 0 json::block_get_rules "$(block::file "$name")" } # ── State mutations ──────────────────────── function block::set_direct() { local name="${1:?}" client_ip="${2:?}" value="${3:-true}" local file file=$(block::file "$name") json::block_set_direct "$file" "$client_ip" "$value" } function block::add_group() { local name="${1:?}" client_ip="${2:?}" group="${3:?}" local file file=$(block::file "$name") json::block_add_group "$file" "$client_ip" "$group" } function block::remove_group() { local name="${1:?}" client_ip="${2:?}" group="${3:?}" local file file=$(block::file "$name") json::block_remove_group "$file" "$client_ip" "$group" } function block::add_rule() { local name="${1:?}" client_ip="${2:?}" local rule_type="${3:?}" rule_name="${4:-}" local target="${5:-}" port="${6:-}" proto="${7:-}" local file file=$(block::file "$name") json::block_add_rule "$file" "$client_ip" "$rule_type" \ "$rule_name" "$target" "$port" "$proto" } function block::remove_rule() { local name="${1:?}" local rule_type="${2:?}" target="${3:-}" port="${4:-}" proto="${5:-}" local file file=$(block::file "$name") json::block_remove_rule "$file" "$rule_type" "$target" "$port" "$proto" } function block::remove_file() { local name="${1:?}" rm -f "$(block::file "$name")" } function block::rename() { local name="${1:?}" new_name="${2:?}" local old_file new_file old_file=$(block::file "$name") new_file=$(block::file "$new_name") [[ -f "$old_file" ]] && mv "$old_file" "$new_file" return 0 } function block::clear_full_block() { local name="${1:?}" local file file=$(block::file "$name") [[ ! -f "$file" ]] && return 0 json::block_remove_rule "$file" "full" } # ── High level operations ────────────────── function block::apply_full() { local name="${1:?}" client_ip="${2:?}" fw::flush_peer "$client_ip" fw::block_all "$client_ip" "$name" block::add_rule "$name" "$client_ip" "full" "full block" local public_key endpoint public_key=$(keys::public "$name") || return 1 endpoint=$(monitor::endpoint_for_key "$public_key") [[ -n "$endpoint" ]] && monitor::watch "$endpoint" "$name" peers::remove_from_server "$name" peers::reload } function block::restore_peer() { local name="${1:?}" client_ip="${2:?}" fw::unblock_all "$client_ip" fw::flush_peer "$client_ip" monitor::unwatch_client "$name" if ! peers::exists_in_server "$name"; then local public_key public_key=$(keys::public "$name") || return 1 peers::add_to_server "$name" "$public_key" "$client_ip" peers::reload fi return 0 } function block::restore_rules_for() { local name="${1:?}" client_ip="${2:?}" while IFS="|" read -r bname btype target port proto; do [[ -z "$btype" ]] && continue case "$btype" in ip) fw::block_ip "$client_ip" "$target" "append" ;; port) fw::block_port "$client_ip" "$target" "$port" \ "${proto:-tcp}" "append" ;; subnet) fw::block_subnet "$client_ip" "$target" "append" ;; esac done < <(block::get_rules "$name") } function block::restore_all() { while IFS= read -r peer_name; do block::has_file "$peer_name" || continue local client_ip client_ip=$(peers::get_ip "$peer_name") [[ -z "$client_ip" ]] && continue while IFS="|" read -r bname btype target port proto; do [[ -z "$btype" ]] && continue case "$btype" in full) fw::block_all "$client_ip" "$peer_name" ;; ip) fw::block_ip "$client_ip" "$target" ;; port) fw::block_port "$client_ip" "$target" "$port" "${proto:-tcp}" ;; subnet) fw::block_subnet "$client_ip" "$target" ;; esac done < <(block::get_rules "$peer_name") done < <(peers::all) } # ── Display helpers ──────────────────────── function block::format_rules() { local name="${1:?}" block::has_file "$name" || return 0 while IFS="|" read -r bname btype target port proto; do [[ -z "$btype" ]] && continue local display="" ann="" case "$btype" in full) display="all traffic" ;; ip) display="$target" ann=$(net::annotate "$target") ;; port) display="${target}:${port}:${proto}" ann=$(net::annotate "${target}:${port}:${proto}") ;; subnet) display="$target" ann=$(net::annotate "${target%%/*}") ;; esac local label="$bname" # If bname wasn't set (equals type default), clear it case "$label" in full|ip|port|subnet|"") label="" ;; esac # Suppress label if it matches annotation if [[ -n "$ann" && -n "$label" && \ ("$ann" == "$label" || "$ann" == "${label}:"*) ]]; then label="" fi # log::debug "label='$label' ann='$ann' match=$([ "$ann" == "$label" ] && echo yes || echo no)" printf " \033[0;31m-\033[0m %-20s \033[0;37m%s%s\033[0m\n" \ "$display" \ "${label:+${label} }" \ "${ann:+→ ${ann}}" done < <(block::get_rules "$name") return 0 } function block::cleanup() { local name="${1:?}" block::has_file "$name" || return 0 local result result=$(json::block_is_empty "$(block::file "$name")") [[ "$result" == "true" ]] && block::remove_file "$name" return 0 } # function block::format_rules() { # local name="${1:?}" # block::has_file "$name" || return 0 # while IFS="|" read -r bname btype target port proto; do # [[ -z "$btype" ]] && continue # local display # case "$btype" in # full) display="all traffic" ;; # ip) display="$target" ;; # port) display="${target}:${port}:${proto}" ;; # subnet) display="$target" ;; # esac # local label="${bname:-$btype}" # printf " \033[0;31m-\033[0m %-30s \033[0;37m%s\033[0m\n" \ # "$display" "$label" # done < <(block::get_rules "$name") # }