#!/bin/sh
# ==============================================================================
# DAS SYNOLOGY HEIMSERVER-PROJEKT | BAND 4 · KAPITEL 2.4
# Begleitdatei: B4K2.4_olivetin-vpn-client-sync_rev01.sh
# Bereitgestellt: 17. September 2026
#
# FUNKTIONSWEISE & DOKUMENTATION BEIM SKRIPTAUFRUF:
# Dieses Shell-Skript dient als sicheres Synchronisations-Interface für OliveTin, 
# um VPN-Client-Zuordnungen auf dem Gateway kontrolliert zu aktualisieren:
#
# 1. BEFEHLSAUSWAHL & SCHUTZ (status | apply):
#    - Nimmt Befehle direkt oder über SSH ($SSH_ORIGINAL_COMMAND) entgegen.
#    - Blockiert unbekannte Kommandos sofort (Exit-Code 64).
#
# 2. STATUS-ABFRAGE (status):
#    - Gibt den Hostnamen, den Pfad zur vpn-clients.conf, die Anzahl der 
#      registrierten Clients und die SHA256-Prüfsumme der Konfiguration aus.
#
# 3. SICHERE INTEGRITÄTS- & SITZUNGSPRÜFUNG (apply):
#    - Liest die neue Client-Liste über Stdin in eine temporäre Datei (mktemp).
#    - Prüft die Dateigröße (1 Byte bis max. 32 KB).
#    - Syntax-Validierung via AWK: Exakt 3 Felder (Name|IP|Beschreibung),
#      zulässige Zeichen im Gerätenamen, IP-Bereich (192.168.100.x/32).
#    - IP-Sicherheitsfilter: Blockiert reservierte/kritische IPs (NAS, VIPs etc.)
#      sowie doppelte IP-Einträge.
#    - Löschschutz: Verhindert versehentliches Entfernen bestehender Geräte-IDs.
#
# 4. ROLLBACK & VERIFIKATION (Fail-Safe Mechanismen):
#    - Erstellt vor jeder Änderung ein Zeitstempel-Backup (${CONF}.bak-...).
#    - Wendet die Gateway-Regeln via apply-gateway-rules.sh an.
#    - Führt bei Fehlern oder fehlendem Nachweis der IP-Rule (Tabelle 201)
#      ein automatisches Rollback auf den vorherigen Stand durch.
#
# WICHTIGE ANPASSUNGEN VOR DEM EINSATZ:
# - Die Pfade CONF und APPLY an die eigene Gateway-Umgebung anpassen.
# - Ausführrechte setzen: chmod +x B4K2.4_olivetin-vpn-client-sync_rev01.sh
#
# ARBEITSORT: ROUTER / OPENWRT GATEWAY / NAS-HOST (SSH)
# ==============================================================================

set -eu
umask 077

COMMAND="${1:-${SSH_ORIGINAL_COMMAND:-status}}"
CONF="/path/to/vpn-platform/gateway/config/vpn-clients.conf"
APPLY="/path/to/vpn-platform/gateway/apply-gateway-rules.sh"
TMP="$(mktemp /tmp/olivetin-vpn-clients.XXXXXX)"
trap 'rm -f "$TMP"' EXIT

status() {
  echo "HOST=$(hostname)"
  echo "CONFIG=$CONF"
  echo "CLIENTS=$(grep -c '|' "$CONF" 2>/dev/null || true)"
  sha256sum "$CONF"
}

case "$COMMAND" in
  status)
    status
    exit 0
    ;;

  apply)
    cat > "$TMP"

    SIZE="$(wc -c < "$TMP")"
    [ "$SIZE" -gt 0 ] && [ "$SIZE" -le 32768 ] || {
      echo "[STOPP] Uebergabedatei ist leer oder zu gross."
      exit 65
    }

    awk -F'|' '
    function stop(t){print "[STOPP] Zeile " NR ": " t; bad=1}
    NF!=3 {stop("genau drei Felder erforderlich"); next}
    $1 !~ /^[A-Za-z0-9][A-Za-z0-9._-]{0,39}$/ {stop("ungueltiger Geraetename")}
    $2 !~ /^192\.168\.100\.[0-9]+\/32$/ {stop("ungueltige Client-IP (erwartet 192.168.100.x/32)"); next}
    {
      ip=$2
      sub(/^192\.168\.100\./,"",ip)
      sub(/\/32$/,"",ip)
      n=ip+0; if(n<2 || n>254) stop("IP ausserhalb des Clientnetzes")
      if(n==5 || n==10 || n==20 || n==112 || n==250)
        stop("reservierte oder ausgeschlossene IP (NAS, VIP, etc.)")
      if(seen[$2]++) stop("doppelte Client-IP")
      if($3=="") stop("Beschreibung fehlt")
    }
    END {
      if(NR<1) stop("keine Clients")
      if(NR>64) stop("zu viele Clients")
      exit bad ? 1 : 0
    }' "$TMP" || exit 65

    while IFS='|' read -r OLD_NAME OLD_IP OLD_DESC; do
      [ -n "$OLD_IP" ] || continue
      grep -q "^${OLD_NAME}|" "$TMP" || {
        echo "[STOPP] Bestehende geraete_id $OLD_NAME fehlt; Loeschungen sind hier nicht erlaubt."
        exit 65
      }
    done < "$CONF"

    BACKUP="${CONF}.bak-olivetin-sync-$(date +%Y%m%d-%H%M%S)"
    cp -p "$CONF" "$BACKUP"
    cat "$TMP" > "$CONF"

    if ! "$APPLY"; then
      cat "$BACKUP" > "$CONF"
      "$APPLY" >/dev/null 2>&1 || true
      echo "[STOPP] Gateway-Regeln konnten nicht angewendet werden."
      exit 66
    fi

    while IFS='|' read -r NAME CIDR DESC; do
      IP="${CIDR%/32}"
      ip rule show | grep -Fq "from $IP lookup 201" || {
        cat "$BACKUP" > "$CONF"
        "$APPLY" >/dev/null 2>&1 || true
        echo "[STOPP] Regel fuer $IP wurde nicht nachgewiesen."
        exit 67
      }
    done < "$CONF"

    echo "[OK] VPN-Clientliste angewendet und Tabelle 201 geprueft."
    status
    ;;

  *)
    echo "[ABGELEHNT] Nur status oder apply sind freigegeben."
    exit 64
    ;;
esac