#!/usr/bin/env bash
# Pull the IRDB blocklist and atomic-replace an ipset.
#
# Usage (cron-friendly):
#     IRDB_URL=http://localhost:8081 IRDB_TOKEN=irdb_con_... \
#         IPSET_NAME=irdb-blocked examples/consumers/iptables-restore.sh
#
# Prerequisites:
#     - the ipset is referenced by an iptables rule, e.g.
#         iptables -I INPUT -m set --match-set irdb-blocked src -j DROP
#     - ipset and iptables-restore are installed
#     - the consumer's policy returns IPv4 entries (or a mixed set;
#       this script filters per family)
#
# Atomic-replace pattern: build a swap set in memory, swap, destroy
# old. Even an in-flight rule keeps working through the swap.
set -euo pipefail

: "${IRDB_URL:?must be set}"
: "${IRDB_TOKEN:?must be set}"
IPSET_NAME="${IPSET_NAME:-irdb-blocked}"
TYPE="${IPSET_TYPE:-hash:net}"
TIMEOUT="${IRDB_TIMEOUT:-30}"

LIST=$(curl -fsS --max-time "$TIMEOUT" \
    -H "Authorization: Bearer $IRDB_TOKEN" \
    -H "Accept: text/plain" \
    "$IRDB_URL/api/v1/blocklist")

# Split into v4 / v6 — this script handles v4 only by default. Drop
# v6 entries if your ipset is `hash:net` family inet; mirror this
# script for inet6 if you need both.
SWAP="${IPSET_NAME}-swap"

# Re-create the swap set; ignore "Set cannot be created: set with the
# same name already exists" since we destroy unconditionally below.
ipset create "$SWAP" "$TYPE" -exist
ipset flush  "$SWAP"

while IFS= read -r line; do
    [ -z "$line" ] && continue
    case "$line" in
        *:*) continue ;;            # skip v6
        *)   ipset add "$SWAP" "$line" -exist ;;
    esac
done <<<"$LIST"

# Make the existing target set if absent so the swap target is valid.
ipset create "$IPSET_NAME" "$TYPE" -exist
ipset swap   "$SWAP" "$IPSET_NAME"
ipset destroy "$SWAP"

COUNT=$(ipset list -t "$IPSET_NAME" | awk -F': ' '/^Number of entries:/ {print $2}')
echo "irdb-blocklist updated: $COUNT entries in $IPSET_NAME"
