mivita/app/Services/dbip/dbip-convert.php
2025-10-20 17:42:08 +02:00

211 lines
No EOL
6.1 KiB
PHP

#!/usr/bin/env php
<?php
/**
*
* DB-IP.com CSV database format conversion tool
*
* Copyright (C) 2020 db-ip.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
$outputFormats = [
"range" => function(array $csvData, $of, string $firstIpBin, string $lastIpBin): void {
writeCSV($of, $csvData);
},
"cidr" => function(array $csvData, $of, string $firstIpBin, string $lastIpBin): void {
array_shift($csvData);
foreach (rangeToCIDR($firstIpBin, $lastIpBin) as $cidrNet) {
$csvData[0] = $cidrNet;
writeCSV($of, $csvData);
}
},
"long" => function(array $csvData, $of, string $firstIpBin, string $lastIpBin): void {
if (($firstIpLong = ip2long($csvData[0])) === false) {
throw new Exception("invalid IP address for long conversion : {$csvData[0]}");
} else if (($lastIpLong = ip2long($csvData[1])) === false) {
throw new Exception("invalid IP address for long conversion : {$csvData[1]}");
}
$csvData[0] = $firstIpLong;
$csvData[1] = $lastIpLong;
writeCSV($of, $csvData);
},
];
function writeCSV($of, $csvData) {
fputcsv($of, $csvData);
}
function vEcho(string $s): void {
if (!$GLOBALS["verbose"]) {
return;
}
echo $s;
}
function findLargestNetwork(string $ipStartBin): int {
for ($i = strlen($ipStartBin); $i--; $i >= 0) {
$byte = ord($ipStartBin[$i]);
for ($j = 7; $j >= 0; $j--) {
if ($byte & (0xff >> $j)) {
return ($i << 3) + $j + 1;
}
}
}
return 0;
}
function getLastAddress(string $ipStartBin, int $cidrBits): string {
$ret = $ipStartBin;
for ($i = strlen($ipStartBin); $i--; $i >= 0) {
$firstBit = ($i << 3) + 1;
$lastBit = ($i + 1) << 3;
if ($cidrBits > $lastBit) {
continue;
} else if ($cidrBits < $firstBit) {
$ret[$i] = "\xff";
} else {
$ret[$i] = chr(ord($ret[$i]) | (0xff >> ($cidrBits - $firstBit + 1)));
}
}
return $ret;
}
function getNextAddress(string $ipStartBin): string {
$ret = $ipStartBin;
for ($i = strlen($ipStartBin); $i--; $i >= 0) {
for ($j = 7; $j >= 0; $j--) {
$byte = ord($ret[$i]);
$mask = 1 << (7 - $j);
if ($byte & $mask) {
$ret[$i] = chr($byte & ~$mask);
} else {
$ret[$i] = chr($byte | $mask);
return $ret;
}
}
}
throw new Exception("no next address for " . inet_ntop($ipStartBin));
}
function rangeToCIDR(string $ipStartBin, string $ipEndBin): array {
if (strcmp($ipStartBin, $ipEndBin) > 0) {
throw new Exception("wrong ip address order");
}
$cidrNets = [];
$cidrBits = findLargestNetwork($ipStartBin);
do {
$lastAddrBin = getLastAddress($ipStartBin, $cidrBits);
if (($cmp = strcmp($lastAddrBin, $ipEndBin)) <= 0) {
$cidrNets[] = inet_ntop($ipStartBin) . "/" . $cidrBits;
if ($cmp === 0) {
return $cidrNets;
}
$ipStartBin = getNextAddress($lastAddrBin);
$cidrBits = findLargestNetwork($ipStartBin);
} else {
$cidrBits++;
}
} while (true);
}
try {
$exitCode = 0;
$opts = getopt("i:o:f:qhw46");
$verbose = !isset($opts["q"]);
if (!$opts || isset($opts["h"]) || !isset($opts["i"]) || !isset($opts["o"]) || !isset($opts["f"]) || (isset($opts["4"]) && isset($opts["6"]))) {
echo "usage: {$argv[0]} -i <inputFileName.csv> -o <outputFileName.csv> -f <outputFormat> [-4|-6] [-w] [-q]\n";
echo " -i input CSV file\n";
echo " -o output file name\n";
echo " -f output format (" . implode("|", array_keys($outputFormats)) . ")\n";
echo " -4 output ipv4 networks only\n";
echo " -6 output ipv6 networks only\n";
echo " -w overwrite output file if it already exists\n";
echo " -q be quiet\n";
exit(1);
}
$inputFile = $opts["i"];
if (!file_exists($inputFile)) {
throw new Exception("cannot find input file: {$inputFile}");
} else if (!is_readable($inputFile)) {
throw new Exception("cannot read input file: {$inputFile}");
}
if (substr($inputFile, -3) === ".gz") {
$inputFile = "compress.zlib://" . $inputFile;
}
if (!$if = fopen($inputFile, "r")) {
throw new Exception("cannot open input file: {$inputFile}");
}
$outputFile = $opts["o"];
if (file_exists($outputFile) && !isset($opts["w"])) {
throw new Exception("output file {$outputFile} already exists, use -w to force overwrite");
}
if (substr($outputFile, -3) === ".gz") {
$outputFile = "compress.zlib://" . $outputFile;
}
if (!$of = fopen($outputFile, "w")) {
throw new Exception("cannot open output file: {$outPutFile}");
}
if (!isset($outputFormats[$opts["f"]])) {
throw new Exception("invalid output format {$opts['f']}");
}
$outputFormatter = $outputFormats[$opts["f"]];
$i = 0;
while ($r = fgetcsv($if, 8192)) {
$i++;
[ $firstIp, $lastIp ] = $r;
try {
if (!$firstIpBin = inet_pton($firstIp)) {
throw new Exception("invalid first IP '{$firstIp}' in CSV record");
} else if (!$lastIpBin = inet_pton($lastIp)) {
throw new Exception("invalid last IP '{$lastIp}' in CSV record");
} else if (($addrLen = strlen($firstIpBin)) !== strlen($lastIpBin)) {
throw new Exception("first and last IP address family mismatch ({$firstIp} / {$lastIp})");
}
if (isset($opts["4"]) && ($addrLen !== 4)) {
continue;
} else if (isset($opts["6"]) && ($addrLen !== 16)) {
continue;
}
$outputFormatter($r, $of, $firstIpBin, $lastIpBin);
} catch (Exception $e) {
throw new Exception("line {$i}: {$e->getMessage()}");
}
}
fclose($if);
fclose($of);
vEcho("Wrote {$outputFile}\n");
} catch (Exception $e) {
if ($stdErr = @fopen("php://stderr", "w")) {
fwrite($stdErr, "ERROR: {$e->getMessage()}\n");
fclose($stdErr);
} else {
echo "ERROR: {$e->getMessage()}\n";
}
$exitCode = 1;
}
if ($exitCode) {
exit($exitCode);
}