# h7-desk-ports.lib.sh · shared desk listen classification
# Source from AV/H7 scripts:  # shellcheck source=h7-desk-ports.lib.sh
#   . "$(dirname "$0")/h7-desk-ports.lib.sh"
#
# Law: INSIDE in · OUTSIDE out · but legit Apache HTTPS on OWN IPs is DESK
#      not thrash. Only unexpected public binds are thrash dirt.
#      LE/webroot needs 80/443 on public IP · WireGuard 10.66.66.1:80 is desk.
#
# Exports:
#   desk_own_ips          → space list of host IPv4s (no loopback)
#   is_desk_loopback ADDR
#   is_desk_public_ok ADDR  · Apache 80/443 on own IP / WG = ok
#   is_thrash_public ADDR   · public and NOT desk ok
#   count_thrash_public     · integer
#   list_thrash_public      · one addr per line
#   list_desk_public_ok     · one addr per line

desk_own_ips() {
  ip -4 -o addr show scope global 2>/dev/null \
    | awk '{print $4}' | cut -d/ -f1 | sort -u
}

is_desk_loopback() {
  local addr="${1:-}"
  [[ "$addr" =~ ^127\. ]] && return 0
  [[ "$addr" =~ ^\[::1\] ]] && return 0
  [[ "$addr" == *%lo* ]] && return 0
  return 1
}

# strip optional %iface from ss local address
_desk_addr_plain() {
  local a="${1:-}"
  a="${a%%%*}"
  # [::1]:631 style → keep for other helpers; public IPv6 rare here
  printf '%s' "$a"
}

is_desk_public_ok() {
  local addr plain ip port
  addr="$(_desk_addr_plain "${1:-}")"
  # only HTTP/HTTPS desk surface
  case "$addr" in
    *:80) port=80 ;;
    *:443) port=443 ;;
    *) return 1 ;;
  esac
  ip="${addr%:*}"
  # IPv6 bracket form [::]:80 — treat as thrash unless we own it later
  [[ "$ip" == \[* ]] && return 1
  # classic desk WG + public NIC (dynamic own-IP check)
  if [[ "$ip" == "10.66.66.1" ]]; then
    return 0
  fi
  # any global IPv4 this host owns · Apache ports.conf binds these intentionally
  if desk_own_ips | grep -qx "$ip"; then
    return 0
  fi
  return 1
}

is_thrash_public() {
  local addr="${1:-}"
  is_desk_loopback "$addr" && return 1
  is_desk_public_ok "$addr" && return 1
  return 0
}

list_listen_addrs() {
  ss -ltnH 2>/dev/null | awk '{print $4}' || true
}

list_thrash_public() {
  local a
  while read -r a; do
    [[ -z "$a" ]] && continue
    is_thrash_public "$a" && printf '%s\n' "$a"
  done < <(list_listen_addrs)
}

list_desk_public_ok() {
  local a
  while read -r a; do
    [[ -z "$a" ]] && continue
    is_desk_public_ok "$a" && printf '%s\n' "$a"
  done < <(list_listen_addrs)
}

count_thrash_public() {
  list_thrash_public | wc -l | tr -d ' '
}
