#!/bin/sh
#
# vortex-firewall - DNS-level threat blocking with ×47 impact
#
# Block threats at DNS resolution BEFORE any connection is established.
# Each DNS block prevents ~47 malicious connections (C2 beacon rate × window).
#
# Usage:
#   vortex-firewall intel <command>     Threat intelligence management
#   vortex-firewall stats               Show blocking statistics
#   vortex-firewall sinkhole <command>  Sinkhole server management
#   vortex-firewall mesh <command>      Mesh threat sharing
#   vortex-firewall start|stop|status   Service control
#

VERSION="1.0.0"
NAME="vortex-firewall"

# Directories
VAR_DIR="/var/lib/vortex-firewall"
CACHE_DIR="/tmp/vortex-firewall"
FEEDS_DIR="$VAR_DIR/feeds"
BLOCKLIST_DB="$VAR_DIR/blocklist.db"
BLOCKLIST_HOSTS="$VAR_DIR/sinkhole.hosts"
DNSMASQ_CONF="/etc/dnsmasq.d/vortex-firewall.conf"
STATS_FILE="$VAR_DIR/stats.json"
CONFIG_FILE="/etc/config/vortex-firewall"

# Sinkhole IP (internal, not routed)
SINKHOLE_IP="192.168.255.253"

# Feed URLs
FEED_URLHAUS="https://urlhaus.abuse.ch/downloads/hostfile/"
FEED_FEODO="https://feodotracker.abuse.ch/downloads/ipblocklist.txt"
FEED_PHISHTANK="http://data.phishtank.com/data/online-valid.csv"
FEED_OPENPHISH="https://openphish.com/feed.txt"
FEED_THREATFOX="https://threatfox.abuse.ch/downloads/hostfile/"

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'

log() { echo -e "${GREEN}[VORTEX]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }
info() { echo -e "${CYAN}[INFO]${NC} $1"; }

# ============================================================================
# Initialization
# ============================================================================

init_dirs() {
    mkdir -p "$VAR_DIR" "$CACHE_DIR" "$FEEDS_DIR"
    [ -f "$STATS_FILE" ] || echo '{"blocks":0,"queries":0,"domains":0,"last_update":""}' > "$STATS_FILE"
}

init_db() {
    if [ ! -f "$BLOCKLIST_DB" ]; then
        log "Initializing blocklist database..."
        sqlite3 "$BLOCKLIST_DB" <<EOF
CREATE TABLE IF NOT EXISTS domains (
    domain TEXT PRIMARY KEY,
    threat_type TEXT,
    confidence INTEGER DEFAULT 80,
    source TEXT,
    first_seen TEXT,
    last_seen TEXT,
    hit_count INTEGER DEFAULT 0,
    blocked INTEGER DEFAULT 1
);

CREATE TABLE IF NOT EXISTS feeds (
    name TEXT PRIMARY KEY,
    url TEXT,
    last_update TEXT,
    domain_count INTEGER DEFAULT 0,
    enabled INTEGER DEFAULT 1
);

CREATE TABLE IF NOT EXISTS events (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    timestamp TEXT,
    domain TEXT,
    client_ip TEXT,
    event_type TEXT,
    details TEXT
);

CREATE INDEX IF NOT EXISTS idx_domain ON domains(domain);
CREATE INDEX IF NOT EXISTS idx_threat ON domains(threat_type);
CREATE INDEX IF NOT EXISTS idx_events_ts ON events(timestamp);
EOF
        log "Database initialized: $BLOCKLIST_DB"
    fi
}

# ============================================================================
# Feed Management
# ============================================================================

feed_update_urlhaus() {
    local feed_file="$FEEDS_DIR/urlhaus.txt"
    log "Updating URLhaus feed..."

    if curl -sL --connect-timeout 10 --max-time 60 "$FEED_URLHAUS" -o "$feed_file.tmp" 2>/dev/null; then
        # Extract domains from hosts file format (127.0.0.1 domain)
        grep -v '^#' "$feed_file.tmp" 2>/dev/null | awk '{print $2}' | grep -v '^$' | sort -u > "$feed_file"
        local count=$(wc -l < "$feed_file")
        rm -f "$feed_file.tmp"

        sqlite3 "$BLOCKLIST_DB" "INSERT OR REPLACE INTO feeds VALUES ('urlhaus', '$FEED_URLHAUS', datetime('now'), $count, 1);"
        log "URLhaus: $count domains"
        return 0
    else
        warn "Failed to update URLhaus feed"
        return 1
    fi
}

feed_update_openphish() {
    local feed_file="$FEEDS_DIR/openphish.txt"
    log "Updating OpenPhish feed..."

    if curl -sL --connect-timeout 10 --max-time 30 "$FEED_OPENPHISH" -o "$feed_file.tmp" 2>/dev/null; then
        # Extract domains from URLs
        grep -v '^#' "$feed_file.tmp" 2>/dev/null | sed 's|https\?://||' | cut -d'/' -f1 | sort -u > "$feed_file"
        local count=$(wc -l < "$feed_file")
        rm -f "$feed_file.tmp"

        sqlite3 "$BLOCKLIST_DB" "INSERT OR REPLACE INTO feeds VALUES ('openphish', '$FEED_OPENPHISH', datetime('now'), $count, 1);"
        log "OpenPhish: $count domains"
        return 0
    else
        warn "Failed to update OpenPhish feed"
        return 1
    fi
}

feed_update_threatfox() {
    local feed_file="$FEEDS_DIR/threatfox.txt"
    log "Updating ThreatFox feed..."

    if curl -sL --connect-timeout 10 --max-time 60 "$FEED_THREATFOX" -o "$feed_file.tmp" 2>/dev/null; then
        # Extract domains from hosts file format (127.0.0.1 domain)
        grep -v '^#' "$feed_file.tmp" 2>/dev/null | awk '{print $2}' | grep -v '^$' | sort -u > "$feed_file"
        local count=$(wc -l < "$feed_file")
        rm -f "$feed_file.tmp"

        sqlite3 "$BLOCKLIST_DB" "INSERT OR REPLACE INTO feeds VALUES ('threatfox', '$FEED_THREATFOX', datetime('now'), $count, 1);"
        log "ThreatFox: $count domains"
        return 0
    else
        warn "Failed to update ThreatFox feed"
        return 1
    fi
}

feed_import_dnsguard() {
    local dnsguard_list="/var/lib/dns-guard/threat_domains.txt"
    local feed_file="$FEEDS_DIR/dnsguard.txt"

    if [ -f "$dnsguard_list" ]; then
        log "Importing DNS Guard detections..."
        cp "$dnsguard_list" "$feed_file"
        local count=$(wc -l < "$feed_file")
        sqlite3 "$BLOCKLIST_DB" "INSERT OR REPLACE INTO feeds VALUES ('dnsguard', 'local', datetime('now'), $count, 1);"
        log "DNS Guard: $count domains"
        return 0
    else
        info "No DNS Guard detections found"
        return 0
    fi
}

intel_update() {
    init_dirs
    init_db

    log "Updating threat intelligence feeds..."
    echo ""

    local total=0

    # Update each feed
    feed_update_urlhaus && total=$((total + 1))
    feed_update_openphish && total=$((total + 1))
    feed_update_threatfox && total=$((total + 1))
    feed_import_dnsguard && total=$((total + 1))

    echo ""
    log "Updated $total feeds"

    # Merge feeds into database
    intel_merge

    # Generate dnsmasq blocklist
    generate_blocklist
}

is_valid_domain() {
    local d="$1"
    # Must contain at least one dot
    echo "$d" | grep -q '\.' || return 1
    # Must have valid TLD (at least 2 chars after last dot)
    local tld=$(echo "$d" | sed 's/.*\.//')
    [ ${#tld} -ge 2 ] || return 1
    # Must be reasonable length (3-253 chars)
    [ ${#d} -ge 3 ] && [ ${#d} -le 253 ] || return 1
    # Must not start/end with dot or hyphen
    case "$d" in
        .*|*.|*-|-*) return 1 ;;
    esac
    return 0
}

intel_merge() {
    log "Merging feeds into blocklist..."

    local now=$(date -Iseconds)
    local sql_file="/tmp/vortex-import.sql"
    local imported=0
    local skipped=0

    # Start transaction
    echo "BEGIN TRANSACTION;" > "$sql_file"

    # Import from each feed file
    for feed_file in "$FEEDS_DIR"/*.txt; do
        [ -f "$feed_file" ] || continue
        local feed_name=$(basename "$feed_file" .txt)
        local threat_type="malware"

        case "$feed_name" in
            openphish|phishtank) threat_type="phishing" ;;
            urlhaus|threatfox) threat_type="malware" ;;
            dnsguard) threat_type="ai_detected" ;;
            feodo) threat_type="c2" ;;
        esac

        log "Processing $feed_name..."

        while read -r domain; do
            [ -z "$domain" ] && continue
            [ "${domain:0:1}" = "#" ] && continue

            # Clean domain (inline for speed)
            domain=$(echo "$domain" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9.-')
            [ -z "$domain" ] && continue

            # Quick validation: must have dot and be reasonable length
            case "$domain" in
                *.*) ;;
                *) skipped=$((skipped + 1)); continue ;;
            esac
            [ ${#domain} -lt 4 ] && { skipped=$((skipped + 1)); continue; }
            [ ${#domain} -gt 253 ] && { skipped=$((skipped + 1)); continue; }

            # Escape single quotes for SQL
            domain=$(echo "$domain" | sed "s/'/''/g")

            echo "INSERT OR REPLACE INTO domains (domain, threat_type, source, first_seen, last_seen, blocked) VALUES ('$domain', '$threat_type', '$feed_name', '$now', '$now', 1);" >> "$sql_file"
            imported=$((imported + 1))
        done < "$feed_file"
    done

    echo "COMMIT;" >> "$sql_file"

    # Execute batch import
    log "Executing batch import ($imported entries)..."
    sqlite3 "$BLOCKLIST_DB" < "$sql_file"
    rm -f "$sql_file"

    local total=$(sqlite3 "$BLOCKLIST_DB" "SELECT COUNT(*) FROM domains WHERE blocked=1;")
    log "Imported: $imported domains, Skipped: $skipped invalid entries"
    log "Total blocked domains: $total"
}

generate_blocklist() {
    # Detect DNS server
    local dns_server="dnsmasq"
    if pgrep -f "/usr/sbin/named" >/dev/null 2>&1 || pidof named >/dev/null 2>&1; then
        dns_server="bind"
    fi

    log "Generating blocklist for $dns_server..."

    local count=$(sqlite3 "$BLOCKLIST_DB" "SELECT COUNT(*) FROM domains WHERE blocked=1;")

    if [ "$dns_server" = "bind" ]; then
        # Generate BIND RPZ zone
        generate_bind_rpz "$count"
    else
        # Generate dnsmasq hosts file
        generate_dnsmasq_hosts "$count"
    fi
}

generate_bind_rpz() {
    local count="$1"
    local rpz_zone="/etc/bind/zones/rpz.vortex.zone"
    local rpz_conf="/etc/bind/named.conf.vortex"
    local serial=$(date +%Y%m%d%H)

    log "Generating BIND RPZ zone ($count domains)..."

    # Generate RPZ zone file
    cat > "$rpz_zone" <<EOF
\$TTL 300
@   IN  SOA localhost. root.localhost. (
    $serial ; serial
    3600    ; refresh
    600     ; retry
    86400   ; expire
    300     ; minimum
)
    IN  NS  localhost.

; Vortex DNS Firewall - Response Policy Zone
; Generated: $(date)
; Blocked domains: $count
; Action: NXDOMAIN (block)

EOF

    # Add blocked domains (CNAME . = NXDOMAIN)
    sqlite3 "$BLOCKLIST_DB" "SELECT domain FROM domains WHERE blocked=1;" | while read -r domain; do
        echo "$domain CNAME ." >> "$rpz_zone"
        echo "*.$domain CNAME ." >> "$rpz_zone"
    done

    log "RPZ zone written: $rpz_zone"

    # Generate BIND config include
    cat > "$rpz_conf" <<EOF
// Vortex DNS Firewall - RPZ Configuration
// Generated: $(date)

zone "rpz.vortex" {
    type master;
    file "$rpz_zone";
    allow-query { none; };
};
EOF

    # Check if RPZ is already in named.conf
    if ! grep -q "response-policy" /etc/bind/named.conf 2>/dev/null; then
        log "Adding RPZ policy to BIND config..."
        # Add response-policy to options block
        sed -i '/^options {/,/^};/ {
            /^};/ i\    response-policy { zone "rpz.vortex"; };
        }' /etc/bind/named.conf
    fi

    # Include vortex config if not already
    if ! grep -q "named.conf.vortex" /etc/bind/named.conf 2>/dev/null; then
        echo 'include "/etc/bind/named.conf.vortex";' >> /etc/bind/named.conf
    fi

    log "BIND RPZ config written: $rpz_conf"

    # Reload BIND
    if [ -x /etc/init.d/named ]; then
        /etc/init.d/named reload 2>/dev/null || /etc/init.d/named restart 2>/dev/null
        log "BIND reloaded"
    fi

    # Update stats
    local now=$(date -Iseconds)
    echo "{\"domains\":$count,\"last_update\":\"$now\",\"blocks\":0,\"queries\":0,\"dns_server\":\"bind\"}" > "$STATS_FILE"
}

generate_dnsmasq_hosts() {
    local count="$1"

    log "Generating dnsmasq blocklist..."

    # Generate hosts file for sinkhole
    echo "# Vortex DNS Firewall - Generated $(date)" > "$BLOCKLIST_HOSTS"
    echo "# Sinkhole IP: $SINKHOLE_IP" >> "$BLOCKLIST_HOSTS"
    echo "" >> "$BLOCKLIST_HOSTS"

    sqlite3 -separator ' ' "$BLOCKLIST_DB" \
        "SELECT '$SINKHOLE_IP', domain FROM domains WHERE blocked=1;" >> "$BLOCKLIST_HOSTS"

    log "Generated $count sinkhole entries"

    # Generate dnsmasq config
    cat > "$DNSMASQ_CONF" <<EOF
# Vortex DNS Firewall Configuration
# Generated: $(date)
# Block count: $count

# Load sinkhole hosts
addn-hosts=$BLOCKLIST_HOSTS

# Log queries for analysis
log-queries
log-facility=/var/log/dnsmasq.log
EOF

    log "dnsmasq config written: $DNSMASQ_CONF"

    # Reload dnsmasq
    if [ -x /etc/init.d/dnsmasq ]; then
        /etc/init.d/dnsmasq restart 2>/dev/null
        log "dnsmasq restarted"
    fi

    # Update stats
    local now=$(date -Iseconds)
    echo "{\"domains\":$count,\"last_update\":\"$now\",\"blocks\":0,\"queries\":0}" > "$STATS_FILE"
}

intel_status() {
    init_dirs
    init_db

    echo ""
    echo -e "${BOLD}Vortex DNS Firewall - Threat Intelligence${NC}"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo ""

    echo -e "${BOLD}Feed Status:${NC}"
    sqlite3 -column -header "$BLOCKLIST_DB" \
        "SELECT name, domain_count as domains, last_update, CASE enabled WHEN 1 THEN 'Active' ELSE 'Disabled' END as status FROM feeds;"

    echo ""
    echo -e "${BOLD}Threat Categories:${NC}"
    sqlite3 -column -header "$BLOCKLIST_DB" \
        "SELECT threat_type, COUNT(*) as count FROM domains WHERE blocked=1 GROUP BY threat_type ORDER BY count DESC;"

    echo ""
    local total=$(sqlite3 "$BLOCKLIST_DB" "SELECT COUNT(*) FROM domains WHERE blocked=1;")
    echo -e "${BOLD}Total Blocked Domains:${NC} $total"
    echo ""
}

intel_search() {
    local domain="$1"
    [ -z "$domain" ] && { error "Usage: vortex-firewall intel search <domain>"; return 1; }

    init_db

    local result=$(sqlite3 -column -header "$BLOCKLIST_DB" \
        "SELECT domain, threat_type, confidence, source, first_seen, hit_count FROM domains WHERE domain LIKE '%$domain%' LIMIT 20;")

    if [ -n "$result" ]; then
        echo ""
        echo -e "${RED}BLOCKED${NC} - Domain found in blocklist:"
        echo "$result"
    else
        echo -e "${GREEN}CLEAN${NC} - Domain not in blocklist: $domain"
    fi
}

intel_add() {
    local domain="$1"
    local reason="${2:-manual}"
    [ -z "$domain" ] && { error "Usage: vortex-firewall intel add <domain> [reason]"; return 1; }

    init_db

    domain=$(echo "$domain" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9.-]//g')
    local now=$(date -Iseconds)

    sqlite3 "$BLOCKLIST_DB" \
        "INSERT OR REPLACE INTO domains (domain, threat_type, confidence, source, first_seen, last_seen, blocked)
         VALUES ('$domain', '$reason', 100, 'manual', '$now', '$now', 1);"

    # Add to hosts file immediately
    echo "$SINKHOLE_IP $domain" >> "$BLOCKLIST_HOSTS"

    log "Blocked: $domain (reason: $reason)"

    # Reload dnsmasq
    killall -HUP dnsmasq 2>/dev/null
}

intel_remove() {
    local domain="$1"
    [ -z "$domain" ] && { error "Usage: vortex-firewall intel remove <domain>"; return 1; }

    init_db

    sqlite3 "$BLOCKLIST_DB" "UPDATE domains SET blocked=0 WHERE domain='$domain';"

    # Regenerate blocklist
    generate_blocklist

    log "Unblocked: $domain"
}

# ============================================================================
# Statistics
# ============================================================================

show_stats() {
    init_dirs
    init_db

    echo ""
    echo -e "${BOLD}Vortex DNS Firewall - Statistics${NC}"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo ""

    local total_domains=$(sqlite3 "$BLOCKLIST_DB" "SELECT COUNT(*) FROM domains WHERE blocked=1;" 2>/dev/null || echo 0)
    local total_hits=$(sqlite3 "$BLOCKLIST_DB" "SELECT COALESCE(SUM(hit_count),0) FROM domains;" 2>/dev/null || echo 0)
    local total_events=$(sqlite3 "$BLOCKLIST_DB" "SELECT COUNT(*) FROM events;" 2>/dev/null || echo 0)

    # ×47 Impact calculation
    local x47_impact=$((total_hits * 47))

    echo -e "${BOLD}Blocking Summary:${NC}"
    echo "  Blocked Domains:     $total_domains"
    echo "  Total Hits:          $total_hits"
    echo "  Sinkhole Events:     $total_events"
    echo ""
    echo -e "${BOLD}×47 Impact Score:${NC}"
    echo -e "  ${CYAN}$x47_impact${NC} connections prevented"
    echo "  (Each DNS block prevents ~47 malicious connections)"
    echo ""

    echo -e "${BOLD}Top Blocked Domains:${NC}"
    sqlite3 -column "$BLOCKLIST_DB" \
        "SELECT domain, hit_count, threat_type FROM domains WHERE hit_count > 0 ORDER BY hit_count DESC LIMIT 10;" 2>/dev/null

    echo ""
    echo -e "${BOLD}Threat Distribution:${NC}"
    sqlite3 "$BLOCKLIST_DB" \
        "SELECT threat_type || ': ' || COUNT(*) FROM domains WHERE blocked=1 GROUP BY threat_type ORDER BY COUNT(*) DESC;" 2>/dev/null
    echo ""
}

show_x47() {
    init_db

    local total_hits=$(sqlite3 "$BLOCKLIST_DB" "SELECT COALESCE(SUM(hit_count),0) FROM domains;" 2>/dev/null || echo 0)
    local x47_impact=$((total_hits * 47))
    local total_domains=$(sqlite3 "$BLOCKLIST_DB" "SELECT COUNT(*) FROM domains WHERE blocked=1;" 2>/dev/null || echo 0)

    echo ""
    echo -e "${BOLD}╔══════════════════════════════════════════════════╗${NC}"
    echo -e "${BOLD}║        ×47 VITALITY IMPACT SCORE                 ║${NC}"
    echo -e "${BOLD}╠══════════════════════════════════════════════════╣${NC}"
    echo -e "${BOLD}║${NC}  Blocked Domains:    ${CYAN}$total_domains${NC}"
    echo -e "${BOLD}║${NC}  DNS Blocks:         ${CYAN}$total_hits${NC}"
    echo -e "${BOLD}║${NC}  Connections Prevented: ${GREEN}$x47_impact${NC}"
    echo -e "${BOLD}║${NC}"
    echo -e "${BOLD}║${NC}  ${YELLOW}Each DNS block = 47 connections stopped${NC}"
    echo -e "${BOLD}║${NC}  ${YELLOW}(C2 beacon rate × infection window)${NC}"
    echo -e "${BOLD}╚══════════════════════════════════════════════════╝${NC}"
    echo ""
}

# ============================================================================
# Service Control
# ============================================================================

service_start() {
    log "Starting Vortex DNS Firewall..."

    init_dirs
    init_db

    # Initial feed update if no blocklist exists
    if [ ! -f "$BLOCKLIST_HOSTS" ] || [ $(wc -l < "$BLOCKLIST_HOSTS" 2>/dev/null || echo 0) -lt 10 ]; then
        intel_update
    fi

    log "Vortex DNS Firewall active"
    log "Sinkhole IP: $SINKHOLE_IP"
    log "Blocked domains: $(sqlite3 "$BLOCKLIST_DB" "SELECT COUNT(*) FROM domains WHERE blocked=1;")"
}

service_stop() {
    log "Stopping Vortex DNS Firewall..."

    # Remove dnsmasq config
    rm -f "$DNSMASQ_CONF"

    # Reload dnsmasq
    /etc/init.d/dnsmasq restart 2>/dev/null

    log "Vortex DNS Firewall stopped"
}

service_status() {
    echo ""
    echo -e "${BOLD}Vortex DNS Firewall v$VERSION${NC}"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

    if [ -f "$DNSMASQ_CONF" ]; then
        echo -e "Status:       ${GREEN}Active${NC}"
    else
        echo -e "Status:       ${RED}Inactive${NC}"
    fi

    echo "Sinkhole IP:  $SINKHOLE_IP"

    if [ -f "$BLOCKLIST_DB" ]; then
        local count=$(sqlite3 "$BLOCKLIST_DB" "SELECT COUNT(*) FROM domains WHERE blocked=1;" 2>/dev/null || echo 0)
        echo "Blocked:      $count domains"
    else
        echo "Blocked:      (no database)"
    fi

    if [ -f "$STATS_FILE" ]; then
        local last=$(jsonfilter -i "$STATS_FILE" -e '@.last_update' 2>/dev/null || echo "never")
        echo "Last Update:  $last"
    fi

    echo ""
}

# ============================================================================
# Usage
# ============================================================================

usage() {
    cat <<'EOF'
Vortex DNS Firewall - Block threats at DNS level

Usage: vortex-firewall <command> [options]

Intel Commands:
  intel update              Update all threat feeds
  intel status              Show feed status and stats
  intel search <domain>     Check if domain is blocked
  intel add <domain>        Manually block a domain
  intel remove <domain>     Unblock a domain

Statistics:
  stats                     Show blocking statistics
  stats --x47               Show ×47 impact score
  stats --top-blocked       Top blocked domains

Service:
  start                     Start firewall
  stop                      Stop firewall
  status                    Show service status

The ×47 multiplier: Each DNS block prevents ~47 malicious connections
(based on typical C2 beacon rate × average infection detection window)

Examples:
  vortex-firewall intel update
  vortex-firewall intel search evil.com
  vortex-firewall intel add malware.example.com c2
  vortex-firewall stats --x47
EOF
}

# ============================================================================
# Main
# ============================================================================

case "${1:-}" in
    intel)
        shift
        case "${1:-}" in
            update) intel_update ;;
            status) intel_status ;;
            search) shift; intel_search "$@" ;;
            add) shift; intel_add "$@" ;;
            remove) shift; intel_remove "$@" ;;
            *) error "Unknown intel command. Use: update, status, search, add, remove" ;;
        esac
        ;;

    stats)
        shift
        case "${1:-}" in
            --x47|-x) show_x47 ;;
            --top*) show_stats ;;
            *) show_stats ;;
        esac
        ;;

    start)
        service_start
        ;;

    stop)
        service_stop
        ;;

    status)
        service_status
        ;;

    help|--help|-h)
        usage
        ;;

    "")
        service_status
        ;;

    *)
        error "Unknown command: $1"
        usage >&2
        exit 1
        ;;
esac
