cmd/delete-by-addr/main.go

package main

import (
	"database/sql"
	"fmt"
	"log"
	"os"
	"strings"

	_ "github.com/duckdb/duckdb-go/v2"
)

func main() {
	if len(os.Args) != 3 {
		fmt.Fprintf(os.Stderr, "Usage: %s <database_file> <remote_addr_or_cidr>\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "Example: %s honeypot.db 192.168.0.1\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "Example: %s honeypot.db 192.168.0.0/24\n", os.Args[0])
		os.Exit(1)
	}

	databaseFile := os.Args[1]
	target := os.Args[2]

	// Open database connection
	db, err := sql.Open("duckdb", databaseFile)
	if err != nil {
		log.Fatalf("failed to open database: %v", err)
	}
	defer db.Close()

	// First, count how many entries will be deleted
	// Using <<= operator for INET type which handles both single IPs and CIDR ranges
	var eventsCount, blocklistCount, ipsCount int
	eventsWhere := "remote_addr = ?"
	blocklistWhere := "address = ?"
	ipsWhere := "ip = ?"
	if strings.Contains(target, "/") {
		eventsWhere = "remote_addr::INET <<= ?::INET"
		blocklistWhere = "address::INET <<= ?::INET"
		ipsWhere = "ip::INET <<= ?::INET"
	}

	queryEvents := fmt.Sprintf("SELECT COUNT(*) FROM honeypot_events WHERE %s", eventsWhere)
	err = db.QueryRow(queryEvents, target).Scan(&eventsCount)
	if err != nil {
		log.Fatalf("failed to count honeypot_events: %v", err)
	}

	queryBlocklist := fmt.Sprintf("SELECT COUNT(*) FROM blocklist WHERE %s", blocklistWhere)
	err = db.QueryRow(queryBlocklist, target).Scan(&blocklistCount)
	if err != nil {
		log.Fatalf("failed to count blocklist: %v", err)
	}

	queryIps := fmt.Sprintf("SELECT COUNT(*) FROM ips WHERE %s", ipsWhere)
	err = db.QueryRow(queryIps, target).Scan(&ipsCount)
	if err != nil {
		log.Fatalf("failed to count ips: %v", err)
	}

	totalCount := eventsCount + blocklistCount + ipsCount
	if totalCount == 0 {
		fmt.Printf("No entries found matching %s\n", target)
		return
	}

	// Ask for confirmation
	fmt.Printf("Found %d entries matching %s (%d events, %d blocklist, %d ips)\n", totalCount, target, eventsCount, blocklistCount, ipsCount)
	fmt.Print("Are you sure you want to delete these entries? (yes/no): ")
	var confirmation string
	fmt.Scanln(&confirmation)

	if confirmation != "yes" {
		fmt.Println("Deletion cancelled.")
		return
	}

	// Delete entries from honeypot_events
	deleteEventsQuery := fmt.Sprintf("DELETE FROM honeypot_events WHERE %s", eventsWhere)
	resultEvents, err := db.Exec(deleteEventsQuery, target)
	if err != nil {
		log.Fatalf("failed to delete from honeypot_events: %v", err)
	}

	rowsAffectedEvents, _ := resultEvents.RowsAffected()

	// Delete entries from blocklist
	deleteBlocklistQuery := fmt.Sprintf("DELETE FROM blocklist WHERE %s", blocklistWhere)
	resultBlocklist, err := db.Exec(deleteBlocklistQuery, target)
	if err != nil {
		log.Fatalf("failed to delete from blocklist: %v", err)
	}

	rowsAffectedBlocklist, _ := resultBlocklist.RowsAffected()

	// Delete entries from ips
	deleteIpsQuery := fmt.Sprintf("DELETE FROM ips WHERE %s", ipsWhere)
	resultIps, err := db.Exec(deleteIpsQuery, target)
	if err != nil {
		log.Fatalf("failed to delete from ips: %v", err)
	}

	rowsAffectedIps, _ := resultIps.RowsAffected()

	fmt.Printf("Successfully deleted %d events, %d blocklist entries, and %d ips entries matching %s\n", rowsAffectedEvents, rowsAffectedBlocklist, rowsAffectedIps, target)
}