123 lines
4.3 KiB
PHP
123 lines
4.3 KiB
PHP
<?php
|
|
// Pulls frequent IPs from portspoof API and ingests new entries into the blacklist.
|
|
// "New" means last_seen > last_pulled timestamp.
|
|
// Each run logs itself in the info table using the current timestamp as list value.
|
|
// Safe to run repeatedly (e.g. via cron) — uses ON DUPLICATE KEY UPDATE.
|
|
|
|
set_time_limit(0);
|
|
date_default_timezone_set("America/Montreal");
|
|
include('conn.php');
|
|
include('functions.php');
|
|
|
|
$api_url = "https://www.home.daprogs.net/portspoof/api/frequent_ips.php";
|
|
$api_token = "a2b6ac44bcbf33764c066892f739e546a319e0d68356d08579c2b7e32218f240";
|
|
|
|
$pull_type = 3; // blacklist type ID for portspoof entries
|
|
$enddate_days = 600; // how many days until the ban expires
|
|
$reason = "Portspoof Auto-Ban";
|
|
|
|
$now = date("YmdHis");
|
|
$enddate = date("YmdHis", strtotime("+" . $enddate_days . " days"));
|
|
$info_list = 0; // unique run identifier logged in info table (YYYYMMDDHHmmss)
|
|
|
|
// ------------------------------------------------------------------
|
|
// 1. Get last-pulled timestamp from the most recent previous run
|
|
// (info rows where list > 1 are portspoof pull log entries)
|
|
// ------------------------------------------------------------------
|
|
$SQL = "SELECT MAX(list) AS prev FROM info WHERE list > 1";
|
|
$result = mysqli_query($con, $SQL);
|
|
$row = mysqli_fetch_array($result);
|
|
|
|
if (!empty($row['prev'])) {
|
|
$last_pulled = DateTime::createFromFormat("YmdHis", $row['prev']);
|
|
} else {
|
|
// First run — accept all IPs from the feed
|
|
$last_pulled = new DateTime("@0");
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// 2. Fetch portspoof API
|
|
// ------------------------------------------------------------------
|
|
$ch = curl_init($api_url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . $api_token]);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
|
$response = curl_exec($ch);
|
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($response === false || $http_code !== 200) {
|
|
echo "Error: API fetch failed (HTTP " . $http_code . ")\n";
|
|
$con->close();
|
|
exit;
|
|
}
|
|
|
|
$data = json_decode($response, true);
|
|
if (!isset($data['ips']) || !is_array($data['ips'])) {
|
|
echo "Error: Unexpected API response format\n";
|
|
$con->close();
|
|
exit;
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// 3. Ingest IPs newer than last_pulled (single bulk query)
|
|
// ------------------------------------------------------------------
|
|
$skipped = 0;
|
|
$values = [];
|
|
$reason_safe = mysqli_real_escape_string($con, $reason);
|
|
|
|
foreach ($data['ips'] as $entry) {
|
|
$src_ip = $entry['src_ip'];
|
|
$last_seen_str = $entry['last_seen'];
|
|
|
|
// last_seen format: "2026-03-14 10:14:37.000000" — strip microseconds
|
|
$last_seen = DateTime::createFromFormat("Y-m-d H:i:s", substr($last_seen_str, 0, 19));
|
|
|
|
if ($last_seen === false || $last_seen <= $last_pulled) {
|
|
$skipped++;
|
|
continue;
|
|
}
|
|
|
|
// Normalise to CIDR notation
|
|
if (strpos($src_ip, '/') === false) {
|
|
$src_ip .= '/32';
|
|
}
|
|
|
|
$ip_safe = mysqli_real_escape_string($con, $src_ip);
|
|
$values[] = "('" . $ip_safe . "', " . $pull_type . ", " . $now . ", " . $enddate . ", '" . $reason_safe . "')";
|
|
}
|
|
|
|
$processed = 0;
|
|
$chunk_size = 100;
|
|
|
|
foreach (array_chunk($values, $chunk_size) as $chunk) {
|
|
$SQL = "INSERT INTO blacklist (ip, type, adddate, enddate, reason) VALUES "
|
|
. implode(", ", $chunk)
|
|
. " ON DUPLICATE KEY UPDATE enddate=" . $enddate . ", type=" . $pull_type . ", reason='" . $reason_safe . "'";
|
|
|
|
if ($con->query($SQL) === TRUE) {
|
|
$processed += count($chunk);
|
|
} else {
|
|
echo "Error: " . $con->error . "\n";
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// 4. Update timestamps
|
|
// ------------------------------------------------------------------
|
|
|
|
// Log this run — each run gets a unique row (list = current timestamp)
|
|
$SQL_pull = "INSERT INTO info (list, last) VALUES (" . $info_list . ", " . $now . ")";
|
|
$con->query($SQL_pull);
|
|
|
|
// Update blacklist last-modified only when rows were actually written
|
|
if ($processed > 0) {
|
|
$SQL_bl = "UPDATE info SET last=" . $now . " WHERE list=0";
|
|
$con->query($SQL_bl);
|
|
}
|
|
|
|
echo "Done. Processed: " . $processed . ", Skipped: " . $skipped . "\n";
|
|
|
|
$con->close();
|
|
?>
|