Added Abuse and Geo functions

This commit is contained in:
Zi Xing 2021-12-17 18:05:45 -05:00
commit a836a24826
27 changed files with 108763 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build/

1
.ppm_package Normal file
View File

@ -0,0 +1 @@
src/ProcLib

12
Makefile Normal file
View File

@ -0,0 +1,12 @@
clean:
rm -rf build
update:
ppm --generate-package="src/khm"
build:
mkdir build
ppm --no-intro --compile="src/khm" --directory="build"
install:
ppm --no-intro --no-prompt --fix-conflict --install="build/net.intellivoid.khm.ppm"

23
database/abuse.sql Normal file
View File

@ -0,0 +1,23 @@
create table if not exists abuse
(
ip_address varchar(128) not null comment 'The IP address that this record is for'
primary key,
is_public tinyint(1) null comment 'Indicates if the IP address is public or private',
ip_version int(1) null comment 'The version of the IP address',
is_whitelisted tinyint(1) null comment 'Indicates if the IP address has been whitelisted by the provider',
abuse_confidence_score int(3) null comment 'The abuse confidence score, from 0-100
75 and above meaning it''s potentially harmful, anything lower is considered safe but questionable',
isp varchar(256) null comment 'The Internet Service Provider that owns this IP address',
domain varchar(256) null comment 'The domain name applicable to this IP address if applicable',
hostname varchar(526) null comment 'The primary host name of this IP address',
hostnames blob null comment 'ZiProto encoded array of all the hostnames associated with this IP address',
total_reports int null comment 'The amount of reports that this IP address has gotten',
num_distinct_users int null comment 'The number of distinct users that uses this IP address',
last_reported_at int null comment 'The Unix Timestmap for when this IP address was last reported',
last_updated_timestamp int null comment 'The Unix Timestmap for when this record was last updated',
created_timestamp int null comment 'The Unix Timestmap for when this record was first registered into the database',
constraint abuse_ip_address_uindex
unique (ip_address)
)
comment 'Table for housing abuse checks on hosts from AbuseIPDB';

31
database/geo.sql Normal file
View File

@ -0,0 +1,31 @@
create table if not exists geo
(
ip_address varchar(128) not null comment 'The primary IP address for this record'
primary key,
ip_version int(1) null comment 'The verison of the IP address',
source varchar(255) null comment 'The source of the last lookup',
continent varchar(126) null comment 'Continent name',
continent_code varchar(2) null comment 'Two-letter continent code',
country varchar(126) null comment 'Country name',
country_code varchar(2) null comment 'Two-letter country code ISO 3166-1 alpha-2
https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2',
region varchar(126) null comment 'Region/state',
region_code varchar(126) null comment 'Region/state short code (FIPS or ISO)',
city varchar(126) null comment 'City',
zip_code varchar(64) null comment 'Zip code',
latitude float null comment 'Latitude',
longitude float null comment 'Longitude',
timezone varchar(126) null comment 'Timezone (tz)',
offset int null comment 'Timezone UTC DST offset in seconds',
currency varchar(32) null comment 'National currency',
isp varchar(126) null comment 'ISP name',
organization varchar(126) null comment 'Organization name',
`as` varchar(256) null comment 'AS number and organization, separated by space (RIR). Empty for IP blocks not being announced in BGP tables.',
asname varchar(126) null comment ' AS name (RIR). Empty for IP blocks not being announced in BGP tables.',
last_updated_timestamp int(255) null comment 'The Unix Timestamp for when this record was last updated',
created_timestamp int null comment 'The Unix Timestamp for when this record was created',
constraint geo_ip_address_uindex
unique (ip_address)
)
comment 'Table for housing Geo IP lookup records';

View File

@ -0,0 +1,10 @@
<?php
namespace khm\Abstracts;
class GeoLookupSource
{
const ipApi = 'ip-api.com';
const ipApiCo = 'ipapi.co';
}

96
src/khm/Classes/Curl.php Normal file
View File

@ -0,0 +1,96 @@
<?php
namespace khm\Classes;
use RuntimeException;
class Curl
{
/**
* Sends generic API request
*
* @param string $path
* @param string $api_key
* @param string $method
* @param array $data
* @return string
* @noinspection DuplicatedCode
*/
private static function apiRequest(string $path, string $api_key, string $method, array $data): string
{
$curlErrorNumber = -1; // will be used later to check curl execution
$curlErrorMessage = '';
$url = 'https://api.abuseipdb.com/api/v2/' . $path; // api url
// set the wanted format, JSON (required to prevent having full html page on error)
// and the AbuseIPDB API Key as a header
$headers = [
'Accept: application/json;',
'Key: ' . $api_key,
];
// open curl connection
$ch = curl_init();
// set the method and data to send
if ($method == 'POST')
{
Curl::setCurlOption($ch, CURLOPT_POST, true);
Curl::setCurlOption($ch, CURLOPT_POSTFIELDS, $data);
}
else
{
Curl::setCurlOption($ch, CURLOPT_CUSTOMREQUEST, $method);
$url .= '?' . http_build_query($data);
}
// set url and options
Curl::setCurlOption($ch, CURLOPT_URL, $url);
Curl::setCurlOption($ch, CURLOPT_RETURNTRANSFER, 1);
Curl::setCurlOption($ch, CURLOPT_HTTPHEADER, $headers);
/**
* set timeout
*
* @see https://curl.se/libcurl/c/CURLOPT_TIMEOUT_MS.html
* @see https://curl.se/libcurl/c/CURLOPT_CONNECTTIMEOUT_MS.html
* If libcurl is built to use the standard system name resolver, that portion of the transfer
* will still use full-second resolution for timeouts with a minimum timeout allowed of one second.
* In unix-like systems, this might cause signals to be used unless CURLOPT_NOSIGNAL is set.
*/
Curl::setCurlOption($ch, CURLOPT_NOSIGNAL, 1);
//$this->setCurlOption($ch, CURLOPT_TIMEOUT_MS, $this->timeout);
// execute curl call
$result = curl_exec($ch);
$curlErrorNumber = curl_errno($ch);
$curlErrorMessage = curl_error($ch);
// close connection
curl_close($ch);
if ($curlErrorNumber !== 0)
throw new RuntimeException($curlErrorMessage);
return $result;
}
/**
* Sets a CURL Option, throws error if oopsie woopsie
*
* @param $ch
* @param int $option
* @param $value
* @return void
*/
public static function setCurlOption($ch, int $option, $value): void
{
if(!curl_setopt($ch,$option,$value))
{
throw new RuntimeException('curl_setopt failed! '.curl_error($ch));
}
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace khm\Exceptions;
use Exception;
use Throwable;
class AbuseRecordNotFoundException extends Exception
{
/**
* @param $message
* @param $code
* @param Throwable|null $previous
* @noinspection PhpMissingParamTypeInspection
*/
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
$this->message = $message;
$this->code = $code;
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace khm\Exceptions;
use Exception;
use Throwable;
class BadGeoSourceException extends Exception
{
/**
* @param $message
* @param $code
* @param Throwable|null $previous
* @noinspection PhpMissingParamTypeInspection
*/
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
$this->message = $message;
$this->code = $code;
}
}

View File

@ -0,0 +1,51 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace khm\Exceptions;
use Exception;
use Throwable;
class DatabaseException extends Exception
{
/**
* @var string
*/
private $query;
/**
* @var string
*/
private $database_error;
/**
* @param string $query
* @param string $database_error
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($query="", $database_error="", $code = 0, Throwable $previous = null)
{
parent::__construct($database_error, $code, $previous);
$this->query = $query;
$this->database_error = $database_error;
$this->code = $code;
}
/**
* @return string
*/
public function getQuery(): string
{
return $this->query;
}
/**
* @return string
*/
public function getDatabaseError(): string
{
return $this->database_error;
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace khm\Exceptions;
use Exception;
use Throwable;
class GeoRecordNotFoundException extends Exception
{
/**
* @param $message
* @param $code
* @param Throwable|null $previous
* @noinspection PhpMissingParamTypeInspection
*/
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
$this->message = $message;
$this->code = $code;
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace khm\Exceptions;
use Exception;
use Throwable;
class KhmResolutionException extends Exception
{
/**
* @param $message
* @param $code
* @param Throwable|null $previous
* @noinspection PhpMissingParamTypeInspection
*/
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
$this->message = $message;
$this->code = $code;
}
}

View File

@ -0,0 +1,144 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace khm\Managers;
use khm\Exceptions\AbuseRecordNotFoundException;
use khm\Exceptions\DatabaseException;
use khm\khm;
use khm\Objects\AbuseCheck;
use msqg\QueryBuilder;
use ZiProto\ZiProto;
class AbuseManager
{
private $khm;
/**
* @param khm $khm
*/
public function __construct(khm $khm)
{
$this->khm = $khm;
}
/**
* Registers the record into the database
*
* @param AbuseCheck $abuseCheck
* @return AbuseCheck
* @throws DatabaseException
* @noinspection PhpCastIsUnnecessaryInspection
*/
public function registerRecord(AbuseCheck $abuseCheck): AbuseCheck
{
$abuseCheck = AbuseCheck::fromArray($abuseCheck->toArray());
$abuseCheck->LastUpdatedTimestamp = time();
$abuseCheck->CreatedTimestamp = $abuseCheck->LastUpdatedTimestamp;
$Query = QueryBuilder::insert_into('abuse', [
'ip_address' => $this->khm->getDatabase()->real_escape_string($abuseCheck->IpAddress),
'is_public' => (int)$abuseCheck->IsPublic,
'ip_version' => (int)$abuseCheck->IpVersion,
'is_whitelisted' => (int)$abuseCheck->IsWhitelisted,
'abuse_confidence_score' => (int)$abuseCheck->AbuseConfidenceScore,
'isp' => $this->khm->getDatabase()->real_escape_string($abuseCheck->ISP),
'domain' => $this->khm->getDatabase()->real_escape_string($abuseCheck->Domain),
'hostname' => $this->khm->getDatabase()->real_escape_string($abuseCheck->Hostname),
'hostnames' => $this->khm->getDatabase()->real_escape_string(ZiProto::encode($abuseCheck->Hostname)),
'total_reports' => (int)$abuseCheck->TotalReports,
'num_distinct_users' => (int)$abuseCheck->NumDistinctUsers,
'last_reported_at' => (int)$abuseCheck->LastReportedTimestamp,
'last_updated_timestamp' => (int)$abuseCheck->LastUpdatedTimestamp,
'created_timestamp' => (int)$abuseCheck->CreatedTimestamp
]);
$QueryResults = $this->khm->getDatabase()->query($Query);
if($QueryResults == false)
{
throw new DatabaseException($Query, $this->khm->getDatabase()->error);
}
return $abuseCheck;
}
/**
* Returns an existing record from the database
*
* @param string $ip_address
* @return AbuseCheck
* @throws AbuseRecordNotFoundException
* @throws DatabaseException
*/
public function getRecord(string $ip_address): AbuseCheck
{
$Query = QueryBuilder::select('abuse', [
'ip_address',
'is_public',
'ip_version',
'is_whitelisted',
'abuse_confidence_score',
'isp',
'domain',
'hostname',
'hostnames',
'total_reports',
'num_distinct_users',
'last_reported_at',
'last_updated_timestamp',
'created_timestamp'
], 'ip_address', $this->khm->getDatabase()->real_escape_string($ip_address));
$QueryResults = $this->khm->getDatabase()->query($Query);
if($QueryResults == false)
{
throw new DatabaseException($Query, $this->khm->getDatabase()->error);
}
$Row = $QueryResults->fetch_array(MYSQLI_ASSOC);
if ($Row == False)
{
throw new AbuseRecordNotFoundException('The abuse record for the IP \'' . $ip_address . '\' was not found');
}
$Row['hostnames'] = ZiProto::decode($Row['hostnames']);
return AbuseCheck::fromArray($Row);
}
/**
* Updates an existing record in the database
*
* @param AbuseCheck $abuseCheck
* @return AbuseCheck
* @throws DatabaseException
*/
public function updateRecord(AbuseCheck $abuseCheck): AbuseCheck
{
$abuseCheck->LastUpdatedTimestamp = time();
$Query = QueryBuilder::update('abuse', [
'is_public' => (int)$abuseCheck->IsPublic,
'is_whitelisted' => (int)$abuseCheck->IsWhitelisted,
'abuse_confidence_score' => (int)$abuseCheck->AbuseConfidenceScore,
'isp' => $this->khm->getDatabase()->real_escape_string($abuseCheck->ISP),
'domain' => $this->khm->getDatabase()->real_escape_string($abuseCheck->Domain),
'hostname' => $this->khm->getDatabase()->real_escape_string($abuseCheck->Hostname),
'hostnames' => $this->khm->getDatabase()->real_escape_string(ZiProto::encode($abuseCheck->Hostname)),
'total_reports' => (int)$abuseCheck->TotalReports,
'num_distinct_users' => (int)$abuseCheck->NumDistinctUsers,
'last_reported_at' => (int)$abuseCheck->LastReportedTimestamp,
'last_updated_timestamp' => (int)$abuseCheck->LastUpdatedTimestamp,
], 'ip_address', $this->khm->getDatabase()->real_escape_string($abuseCheck->IpAddress));
$QueryResults = $this->khm->getDatabase()->query($Query);
if($QueryResults == false)
{
throw new DatabaseException($Query, $this->khm->getDatabase()->error);
}
return $abuseCheck;
}
}

View File

@ -0,0 +1,170 @@
<?php
namespace khm\Managers;
use khm\Exceptions\DatabaseException;
use khm\Exceptions\GeoRecordNotFoundException;
use khm\khm;
use khm\Objects\GeoLookup;
use msqg\QueryBuilder;
class GeoManager
{
/**
* @var khm
*/
private $khm;
/**
* @param khm $khm
*/
public function __construct(khm $khm)
{
$this->khm = $khm;
}
/**
* Registers a geo lookup record into the database
*
* @param GeoLookup $geoLookup
* @return GeoLookup
* @throws DatabaseException
* @noinspection PhpCastIsUnnecessaryInspection
*/
public function registerRecord(GeoLookup $geoLookup): GeoLookup
{
$geoLookup->LastUpdatedTimestamp = time();
$geoLookup->CreatedTimestamp = $geoLookup->LastUpdatedTimestamp;
$Query = QueryBuilder::insert_into('geo', [
'ip_address' => $this->khm->getDatabase()->real_escape_string($geoLookup->IPAddress),
'ip_version' => (int)$geoLookup->IPVersion,
'source' => $this->khm->getDatabase()->real_escape_string($geoLookup->Source),
'continent' => $this->khm->getDatabase()->real_escape_string($geoLookup->Continent),
'continent_code' => $this->khm->getDatabase()->real_escape_string($geoLookup->ContinentCode),
'country' => $this->khm->getDatabase()->real_escape_string($geoLookup->Country),
'country_code' => $this->khm->getDatabase()->real_escape_string($geoLookup->CountryCode),
'region' => $this->khm->getDatabase()->real_escape_string($geoLookup->Region),
'region_code' => $this->khm->getDatabase()->real_escape_string($geoLookup->RegionCode),
'city' => $this->khm->getDatabase()->real_escape_string($geoLookup->City),
'zip_code' => ($geoLookup->ZipCode == null ? null : $this->khm->getDatabase()->real_escape_string($geoLookup->ZipCode)),
'latitude' => (float)$geoLookup->Latitude,
'longitude' => (float)$geoLookup->Longitude,
'timezone' => $this->khm->getDatabase()->real_escape_string($geoLookup->Timezone),
'offset' => (int)$geoLookup->Offset,
'currency' => $this->khm->getDatabase()->real_escape_string($geoLookup->Currency),
'isp' => ($geoLookup->ISP == null ? null : $this->khm->getDatabase()->real_escape_string($geoLookup->ISP)),
'organization' => ($geoLookup->Organization == null ? : $this->khm->getDatabase()->real_escape_string($geoLookup->Organization)),
'`as`' => $this->khm->getDatabase()->real_escape_string($geoLookup->AS),
'asname' => $this->khm->getDatabase()->real_escape_string($geoLookup->AsName),
'last_updated_timestamp' => (int)$geoLookup->LastUpdatedTimestamp,
'created_timestamp' => (int)$geoLookup->CreatedTimestamp
]);
$QueryResults = $this->khm->getDatabase()->query($Query);
if($QueryResults == false)
{
throw new DatabaseException($Query, $this->khm->getDatabase()->error);
}
return $geoLookup;
}
/**
* Returns a existing geo record from the database
*
* @param string $ip_address
* @return GeoLookup
* @throws DatabaseException
* @throws GeoRecordNotFoundException
* @noinspection PhpUnused
*/
public function getRecord(string $ip_address): GeoLookup
{
$Query = QueryBuilder::select('geo', [
'ip_address',
'ip_version',
'source',
'continent',
'continent_code',
'country',
'country_code',
'region',
'region_code',
'city',
'zip_code',
'latitude',
'longitude',
'timezone',
'offset',
'currency',
'isp',
'organization',
'`as`',
'asname',
'last_updated_timestamp',
'created_timestamp'
], 'ip_address', $this->khm->getDatabase()->real_escape_string($ip_address));
$QueryResults = $this->khm->getDatabase()->query($Query);
if($QueryResults == false)
{
throw new DatabaseException($Query, $this->khm->getDatabase()->error);
}
$Row = $QueryResults->fetch_array(MYSQLI_ASSOC);
if ($Row == False)
{
throw new GeoRecordNotFoundException('The geo record for the IP \'' . $ip_address . '\' was not found');
}
return GeoLookup::fromArray($Row);
}
/**
* Updates an existing geo record in the database
*
* @param GeoLookup $geoLookup
* @return GeoLookup
* @throws DatabaseException
* @noinspection PhpCastIsUnnecessaryInspection
*/
public function updateRecord(GeoLookup $geoLookup): GeoLookup
{
$geoLookup->LastUpdatedTimestamp = time();
$Query = QueryBuilder::update('geo', [
'source' => $this->khm->getDatabase()->real_escape_string($geoLookup->Source),
'continent' => $this->khm->getDatabase()->real_escape_string($geoLookup->Continent),
'continent_code' => $this->khm->getDatabase()->real_escape_string($geoLookup->ContinentCode),
'country' => $this->khm->getDatabase()->real_escape_string($geoLookup->Country),
'country_code' => $this->khm->getDatabase()->real_escape_string($geoLookup->CountryCode),
'region' => $this->khm->getDatabase()->real_escape_string($geoLookup->Region),
'region_code' => $this->khm->getDatabase()->real_escape_string($geoLookup->RegionCode),
'city' => $this->khm->getDatabase()->real_escape_string($geoLookup->City),
'zip_code' => $this->khm->getDatabase()->real_escape_string($geoLookup->ZipCode),
'latitude' => (float)$geoLookup->Latitude,
'longitude' => (float)$geoLookup->Longitude,
'timezone' => $this->khm->getDatabase()->real_escape_string($geoLookup->Timezone),
'offset' => (int)$geoLookup->Offset,
'currency' => $this->khm->getDatabase()->real_escape_string($geoLookup->Currency),
'isp' => $this->khm->getDatabase()->real_escape_string($geoLookup->ISP),
'organization' => $this->khm->getDatabase()->real_escape_string($geoLookup->Organization),
'`as`' => $this->khm->getDatabase()->real_escape_string($geoLookup->AS),
'asname' => $this->khm->getDatabase()->real_escape_string($geoLookup->AsName),
'last_updated_timestamp' => (int)$geoLookup->LastUpdatedTimestamp,
], 'ip_address', $this->khm->getDatabase()->real_escape_string($geoLookup->IPAddress));
$QueryResults = $this->khm->getDatabase()->query($Query);
if($QueryResults == false)
{
throw new DatabaseException($Query, $this->khm->getDatabase()->error);
}
return $geoLookup;
}
}

View File

@ -0,0 +1,234 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace khm\Objects;
class AbuseCheck
{
/**
* The IP Address
*
* @var string
*/
public $IpAddress;
/**
* Indicates if the IP address is public or private
*
* @var bool
*/
public $IsPublic;
/**
* The version of the IP address
*
* @var int
*/
public $IpVersion;
/**
* Indicates if the IP address has been whitelisted by the provider
*
* @var bool
*/
public $IsWhitelisted;
/**
* The abuse confidence score, from 0-100
* 75 and above meaning it's potentially harmful, anything lower is considered safe but questionable
*
* @var int
*/
public $AbuseConfidenceScore;
/**
* The name of the service provider for this IP
*
* @var string|null
*/
public $ISP;
/**
* The domain address associated with this IP address
*
* @var string|null
*/
public $Domain;
/**
* The primary host name of the IP address
*
* @var string
*/
public $Hostname;
/**
* An array of hostnames associated with this IP address
*
* @var string[]
*/
public $Hostnames;
/**
* The total amount of reports that this IP received
*
* @var int
*/
public $TotalReports;
/**
* The number of distinct users that uses this IP address
*
* @var int
*/
public $NumDistinctUsers;
/**
* Indicates the last time someone reported the IP for abuse
*
* @var int|null
*/
public $LastReportedTimestamp;
/**
* The Unix Timestamp for when this record was last updated
*
* @var int
*/
public $LastUpdatedTimestamp;
/**
* The Unix Timestamp for when this record was created
*
* @var int
*/
public $CreatedTimestamp;
/**
* Returns an array representation of the object
*
* @return array
* @noinspection PhpTernaryExpressionCanBeReplacedWithConditionInspection
* @noinspection PhpCastIsUnnecessaryInspection
*/
public function toArray(): array
{
return [
'ip_address' => $this->IpAddress,
'is_public' => ($this->IsPublic == null ? false : (bool)$this->IsPublic),
'ip_version' => $this->IpVersion,
'is_whitelisted' => ($this->IsWhitelisted == null ? false : (bool)$this->IsWhitelisted),
'abuse_confidence_score' => ($this->AbuseConfidenceScore == null ? 0 : (int)$this->AbuseConfidenceScore),
'isp' => $this->ISP,
'domain' => $this->Domain,
'hostname' => $this->Hostname,
'hostnames' => ($this->Hostnames == null ? [] : $this->Hostnames),
'total_reports' => ($this->TotalReports == null ? 0 : (int)$this->TotalReports),
'num_distinct_users' => ($this->NumDistinctUsers == null ? 0 : (int)$this->NumDistinctUsers),
'last_reported_at' => ($this->LastReportedTimestamp == null ? null : $this->LastReportedTimestamp),
'last_updated_timestamp' => ($this->LastUpdatedTimestamp == null ? 0 : (int)$this->LastUpdatedTimestamp),
'created_timestamp' => ($this->CreatedTimestamp == null ? 0 : (int)$this->CreatedTimestamp)
];
}
/**
* Constructs object from an array representation
*
* @param array $data
* @return AbuseCheck
* @noinspection PhpTernaryExpressionCanBeReplacedWithConditionInspection
*/
public static function fromArray(array $data): AbuseCheck
{
$AbuseCheckObject = new AbuseCheck();
if(isset($data['ip_address']))
$AbuseCheckObject->IpAddress = $data['ip_address'];
if(isset($data['is_public']))
$AbuseCheckObject->IsPublic = (bool)$data['is_public'];
if(isset($data['ip_version']))
$AbuseCheckObject->IpVersion = (int)$data['ip_version'];
if(isset($data['is_whitelisted']))
$AbuseCheckObject->IsWhitelisted = (bool)$data['is_whitelisted'];
if(isset($data['abuse_confidence_score']))
$AbuseCheckObject->AbuseConfidenceScore = (int)$data['abuse_confidence_score'];
if(isset($data['isp']))
$AbuseCheckObject->ISP = $data['isp'];
if(isset($data['domain']))
$AbuseCheckObject->Domain = $data['domain'];
if(isset($data['hostname']))
$AbuseCheckObject->Hostname = $data['hostname'];
if(isset($data['hostnames']))
$AbuseCheckObject->Hostnames = $data['hostnames'];
if(is_array($AbuseCheckObject->Hostnames) == false)
{
$AbuseCheckObject->Hostnames = [$AbuseCheckObject->Hostnames];
}
if(isset($data['total_reports']))
$AbuseCheckObject->TotalReports = (int)$data['total_reports'];
if(isset($data['num_distinct_users']))
$AbuseCheckObject->NumDistinctUsers = (int)$data['num_distinct_users'];
if(isset($data['last_reported_at']))
$AbuseCheckObject->LastReportedTimestamp = (int)$data['last_reported_at'];
if(isset($data['last_updated_timestamp']))
$AbuseCheckObject->LastUpdatedTimestamp = (int)$data['last_updated_timestamp'];
if(isset($data['created_timestamp']))
$AbuseCheckObject->CreatedTimestamp = (int)$data['created_timestamp'];
if(isset($data['data']))
{
if(isset($data['data']['ipAddress']))
$AbuseCheckObject->IpAddress = $data['data']['ipAddress'];
if(isset($data['data']['isPublic']))
$AbuseCheckObject->IsPublic = ($data['data']['isPublic'] == null ? true : (bool)$data['data']['isPublic']);
if(isset($data['data']['ipVersion']))
$AbuseCheckObject->IpVersion = $data['data']['ipVersion'];
if(isset($data['data']['isWhitelisted']))
$AbuseCheckObject->IsWhitelisted = ($data['data']['isWhitelisted'] == null ? false : (bool)$data['data']['isWhitelisted']);
if(isset($data['data']['abuseConfidenceScore']))
$AbuseCheckObject->AbuseConfidenceScore = ($data['data']['abuseConfidenceScore'] == null ? 0 : (int)$data['data']['abuseConfidenceScore']);
if(isset($data['data']['isp']))
$AbuseCheckObject->ISP = $data['data']['isp'];
if(isset($data['data']['domain']))
$AbuseCheckObject->Domain = $data['data']['domain'];
if(isset($data['data']['hostnames']))
$AbuseCheckObject->Hostnames = $data['data']['hostnames'];
if(count($AbuseCheckObject->Hostnames) > 0)
$AbuseCheckObject->Hostname = $AbuseCheckObject->Hostnames[0];
if(isset($data['data']['totalReports']))
$AbuseCheckObject->TotalReports = ($data['data']['totalReports'] == null ? 0 : (int)$data['data']['totalReports']);
if(isset($data['data']['numDistinctUsers']))
$AbuseCheckObject->NumDistinctUsers = ($data['data']['numDistinctUsers'] == null ? 0 : (int)$data['data']['numDistinctUsers']);
if(isset($data['data']['lastReportedAt']))
$AbuseCheckObject->LastReportedTimestamp = ($data['data']['lastReportedAt'] == null ? null : (int)strtotime($data['data']['lastReportedAt']));
}
return $AbuseCheckObject;
}
}

View File

@ -0,0 +1,371 @@
<?php /** @noinspection PhpMissingFieldTypeInspection */
namespace khm\Objects;
class GeoLookup
{
/**
* The primary IP Address for this record
*
* @var string
*/
public $IPAddress;
/**
* The version of the IP address
*
* @var int
*/
public $IPVersion;
/**
* The source of the last lookup
*
* @var string
*/
public $Source;
/**
* The continent of the IP address' location
*
* @var string
*/
public $Continent;
/**
* Two-Letter continent code
*
* @var string
*/
public $ContinentCode;
/**
* Country Name
*
* @var string
*/
public $Country;
/**
* Two-Letter country code ISO 3166-1 alpha-2
*
* @var string
*/
public $CountryCode;
/**
* Region/state
*
* @var string
*/
public $Region;
/**
* Region/state short code (FIPS or ISO)
*
* @var string
*/
public $RegionCode;
/**
* City
*
* @var string
*/
public $City;
/**
* Zip Code
*
* @var string
*/
public $ZipCode;
/**
* Latitude
*
* @var float
*/
public $Latitude;
/**
* Longitude
*
* @var float
*/
public $Longitude;
/**
* Timezone (tz)
*
* @var string
*/
public $Timezone;
/**
* Timezone UTC DST offset in seconds
*
* @var int
*/
public $Offset;
/**
* National Currency
*
* @var string
*/
public $Currency;
/**
* ISP name
*
* @var string|null
*/
public $ISP;
/**
* Organization name
*
* @var string|null
*/
public $Organization;
/**
* AS Number and organization, seperated by space (RIR)
*
* @var string
*/
public $AS;
/**
* AS Name (RIR). Empty for IP blocks not being announced
*
* @var string
*/
public $AsName;
/**
* The unix Timestamp for when this record was last updated
*
* @var int
*/
public $LastUpdatedTimestamp;
/**
* The Unix Timestmap for when this record was created
*
* @var int
*/
public $CreatedTimestamp;
/**
* Returns an array representation of the object
*
* @return array
* @noinspection PhpCastIsUnnecessaryInspection
*/
public function toArray(): array
{
return [
'ip_address' => $this->IPAddress,
'ip_version' => (int)$this->IPVersion,
'source' => $this->Source,
'continent' => $this->Continent,
'continent_code' => $this->ContinentCode,
'country' => $this->Country,
'country_code' => $this->CountryCode,
'region' => $this->Region,
'region_code' => $this->RegionCode,
'city' => $this->City,
'zip_code' => $this->ZipCode,
'latitude' => (float)$this->Latitude,
'longitude' => (float)$this->Longitude,
'timezone' => $this->Timezone,
'offset' => (int)$this->Offset,
'currency' => $this->Currency,
'isp' => (strlen($this->ISP) == 0 ? null : $this->ISP),
'organization' => (strlen($this->Organization) == 0 ? null : $this->Organization),
'as' => $this->AS,
'asname' => $this->AsName,
'last_updated_timestamp' => (int)$this->LastUpdatedTimestamp,
'created_timestamp' => (int)$this->CreatedTimestamp
];
}
/**
* Constructs object from an array representation of the object
*
* @param array $data
* @param string|null $source
* @return GeoLookup
*/
public static function fromArray(array $data, ?string $source=null): GeoLookup
{
$geo_lookup_object = new GeoLookup();
if(isset($data['query']))
$geo_lookup_object->IPAddress = $data['query'];
if(isset($data['ip']))
$geo_lookup_object->IPAddress = $data['ip'];
if(isset($data['ip_address']))
$geo_lookup_object->IPAddress = $data['ip_address'];
if(isset($data['ip_version']))
$geo_lookup_object->IPVersion = $data['ip_version'];
$geo_lookup_object->IPVersion = (int)$geo_lookup_object->IPVersion;
if($source !== null && strlen($source) > 0)
$geo_lookup_object->Source = $source;
if(isset($data['source']))
$geo_lookup_object->Source = $data['source'];
if(isset($data['continent']))
$geo_lookup_object->Continent = $data['continent'];
if(isset($data['continentCode']))
$geo_lookup_object->ContinentCode = $data['continentCode'];
if(isset($data['continent_code']))
$geo_lookup_object->ContinentCode = $data['continent_code'];
if(isset($data['country']))
$geo_lookup_object->Country = $data['country'];
if(isset($data['country_name']))
$geo_lookup_object->Country = $data['country_name'];
if(isset($data['countryCode']))
$geo_lookup_object->CountryCode = $data['countryCode'];
if(isset($data['country_code']))
$geo_lookup_object->CountryCode = $data['country_code'];
if(isset($data['region']))
$geo_lookup_object->Region = $data['region'];
if(isset($data['regionName']))
$geo_lookup_object->Region = $data['regionName'];
if(isset($data['region']))
$geo_lookup_object->RegionCode = $data['region'];
if(isset($data['region_code']))
$geo_lookup_object->RegionCode = $data['region_code'];
if(isset($data['city']))
$geo_lookup_object->City = $data['city'];
if(isset($data['zip']))
$geo_lookup_object->ZipCode = $data['zip'];
if(isset($data['postal']))
$geo_lookup_object->ZipCode = $data['postal'];
if(isset($data['zip_code']))
$geo_lookup_object->ZipCode = $data['zip_code'];
if(strlen($geo_lookup_object->ZipCode) == 0)
$geo_lookup_object->ZipCode = null;
if(isset($data['lat']))
$geo_lookup_object->Latitude = $data['lat'];
if(isset($data['latitude']))
$geo_lookup_object->Latitude = $data['latitude'];
if(isset($data['lon']))
$geo_lookup_object->Longitude = $data['lon'];
if(isset($data['longitude']))
$geo_lookup_object->Longitude = $data['longitude'];
$geo_lookup_object->Longitude = (float)$geo_lookup_object->Longitude;
$geo_lookup_object->Latitude = (float)$geo_lookup_object->Latitude;
if(isset($data['timezone']))
$geo_lookup_object->Timezone = $data['timezone'];
if(isset($data['offset']))
$geo_lookup_object->Offset = (int)$data['offset'];
if(isset($data['utc_offset']))
$geo_lookup_object->Offset = (int)$data['utc_offset'];
if(isset($data['currency']))
$geo_lookup_object->Currency = $data['currency'];
if(isset($data['isp']))
$geo_lookup_object->ISP = (strlen($data['isp']) == null ? null : $data['isp']);
if(isset($data['org']))
$geo_lookup_object->Organization = $data['org'];
if(isset($data['organization']))
$geo_lookup_object->Organization = $data['organization'];
if(isset($data['as']))
$geo_lookup_object->AS = $data['as'];
if(isset($data['asn']))
$geo_lookup_object->AS = $data['asn'];
if(isset($data['asname']))
$geo_lookup_object->AsName = $data['asname'];
if(isset($data['last_updated_timestamp']))
$geo_lookup_object->LastUpdatedTimestamp = (int)$data['last_updated_timestamp'];
if(isset($data['created_timestamp']))
$geo_lookup_object->CreatedTimestamp = (int)$data['created_timestamp'];
// Try to fill in the blanks for missing data
if($geo_lookup_object->IPVersion == null && $geo_lookup_object->IPAddress !== null)
{
if(filter_var($geo_lookup_object->IPAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))
{
$geo_lookup_object->IPVersion = 4;
}
if(filter_var($geo_lookup_object->IPAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))
{
$geo_lookup_object->IPVersion = 6;
}
}
if($geo_lookup_object->Continent == null && $geo_lookup_object->ContinentCode !== null)
{
switch(strtoupper($geo_lookup_object->ContinentCode))
{
case 'AF':
$geo_lookup_object->Continent = 'Africa';
break;
case 'AN':
$geo_lookup_object->Continent = 'Antarctica';
break;
case 'AS':
$geo_lookup_object->Continent = 'Asia';
break;
case 'EU':
$geo_lookup_object->Continent = 'Europe';
break;
case 'NA':
$geo_lookup_object->Continent = 'North America';
break;
case 'OC':
$geo_lookup_object->Continent = 'Oceania';
break;
case 'SA':
$geo_lookup_object->Continent = 'South America';
break;
}
}
if($geo_lookup_object->AsName == null && $geo_lookup_object->AS !== null)
{
$asn = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'asn.json');
$asn = json_decode($asn, true);
if(isset($asn[strtoupper($geo_lookup_object->AS)]))
$geo_lookup_object->AsName = $asn[strtoupper($geo_lookup_object->AS)];
$asn = null;
}
return $geo_lookup_object;
}
}

117
src/khm/ThirdParty/AbuseIPDB.php vendored Normal file
View File

@ -0,0 +1,117 @@
<?php
namespace khm\ThirdParty;
use CurlFile;
use InvalidArgumentException;
use khm\Classes\Curl;
use khm\Objects\AbuseCheck;
use RuntimeException;
class AbuseIPDB
{
/**
* Sends generic API request
*
* @param string $path
* @param string $api_key
* @param string $method
* @param array $data
* @return string
*/
private static function apiRequest(string $path, string $api_key, string $method, array $data): string
{
$curlErrorNumber = -1; // will be used later to check curl execution
$curlErrorMessage = '';
$url = 'https://api.abuseipdb.com/api/v2/' . $path; // api url
// set the wanted format, JSON (required to prevent having full html page on error)
// and the AbuseIPDB API Key as a header
$headers = [
'Accept: application/json;',
'Key: ' . $api_key,
];
// open curl connection
$ch = curl_init();
// set the method and data to send
if ($method == 'POST')
{
Curl::setCurlOption($ch, CURLOPT_POST, true);
Curl::setCurlOption($ch, CURLOPT_POSTFIELDS, $data);
}
else
{
Curl::setCurlOption($ch, CURLOPT_CUSTOMREQUEST, $method);
$url .= '?' . http_build_query($data);
}
// set url and options
Curl::setCurlOption($ch, CURLOPT_URL, $url);
Curl::setCurlOption($ch, CURLOPT_RETURNTRANSFER, 1);
Curl::setCurlOption($ch, CURLOPT_HTTPHEADER, $headers);
/**
* set timeout
*
* @see https://curl.se/libcurl/c/CURLOPT_TIMEOUT_MS.html
* @see https://curl.se/libcurl/c/CURLOPT_CONNECTTIMEOUT_MS.html
* If libcurl is built to use the standard system name resolver, that portion of the transfer
* will still use full-second resolution for timeouts with a minimum timeout allowed of one second.
* In unix-like systems, this might cause signals to be used unless CURLOPT_NOSIGNAL is set.
*/
Curl::setCurlOption($ch, CURLOPT_NOSIGNAL, 1);
//$this->setCurlOption($ch, CURLOPT_TIMEOUT_MS, $this->timeout);
// execute curl call
$result = curl_exec($ch);
$curlErrorNumber = curl_errno($ch);
$curlErrorMessage = curl_error($ch);
// close connection
curl_close($ch);
if ($curlErrorNumber !== 0)
throw new RuntimeException($curlErrorMessage);
return $result;
}
/**
* Does a lookup against AbuseIP's Database
*
* @param string $api_key
* @param string $ip
* @param int $maxAgeInDays
* @param bool $verbose
* @return AbuseCheck
*/
public static function check(string $api_key, string $ip, int $maxAgeInDays = 30, bool $verbose = false): AbuseCheck
{
// max age must be less or equal to 365
if ( $maxAgeInDays > 365 || $maxAgeInDays < 1 ){
throw new InvalidArgumentException('maxAgeInDays must be between 1 and 365.');
}
// ip must be set
if (empty($ip)){
throw new InvalidArgumentException('ip argument must be set (empty value given)');
}
// minimal data
$data = [
'ipAddress' => $ip,
'maxAgeInDays' => $maxAgeInDays,
];
// option
if ($verbose){
$data['verbose'] = true;
}
return AbuseCheck::fromArray(json_decode(self::apiRequest('check', $api_key, 'GET', $data), true));
}
}

249
src/khm/ThirdParty/IpAPI.php vendored Normal file
View File

@ -0,0 +1,249 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace khm\ThirdParty;
use Exception;
use khm\Objects\GeoLookup;
use WpOrg\Requests\Requests;
class IpAPI
{
/**
* Fields to request for each IPs.
*
* @var array
*/
private $fields;
/**
* The language setting for the query.
*
* @var string
*/
private $lang;
/**
* The TTL header.
*
* @var int
*/
private $X_TTL;
/**
* The rate limit header.
*
* @var int
*/
private $X_RL;
/**
* The query limit per request.
*/
private static $limit = 100;
/**
* The API endpoint.
*
* @var string
*/
private static $endpoint = 'http://ip-api.com/batch';
/**
* The request headers.
*
* @var array
*/
private static $headers = [
'User-Agent: Intellivoid-KHM/1.0',
'Content-Type: application/json',
'Accept: application/json'
];
/**
* Set the fields to request.
*
* @param array $fields
* @return IpAPI
*/
public function setFields(array $fields): IpAPI
{
$this->fields = $fields;
return $this;
}
/**
* Append a request field.
*
* @param string $field
* @return IpAPI
*/
public function addField(string $field): IpAPI
{
$this->fields[] = $field;
return $this;
}
/**
* Get the fields to be requested.
*
* @return array
*/
public function getFields(): array
{
return $this->fields;
}
/**
* Get the fields to be requested, as a string.
*
* @return string
*/
public function getFieldString(): string
{
return join(',', $this->fields);
}
/**
* Set the language for this query.
*
* @param string $lang
*/
public function setLanguage($lang): IpAPI
{
$this->lang = $lang;
return $this;
}
/**
* Get the language setting for this query.
*
* @return string
*/
public function getLanguage(): string
{
return $this->lang;
}
/**
* Submit a request and decode the response.
*
* @param string|array $query
* @return GeoLookup
* @throws Exception
*/
public function get(string $query): GeoLookup
{
$payload = $this->buildPayload($query);
return $this->wait()->request($payload);
}
/**
* Submit a request and decode the response.
*
* @param string|array $query
* @return GeoLookup[]
* @throws Exception
*/
public function get_batch(array $query): array
{
$payload = $this->buildPayload($query);
return $this->wait()->request_batch($payload);
}
/**
* Build the payload data for this request. Each IP address submitted must
* individually contain the desired fields and language.
*
* @param string|array $query
* @return array
* @throws Exception
*/
private function buildPayload($query): array
{
$payload = [];
foreach ((array) $query as $ip) {
$i = ['query' => $ip];
if ($this->fields) $i['fields'] = $this->getFieldString();
if ($this->lang) $i['lang'] = $this->lang;
$payload[] = $i;
}
if (count($payload) > static::$limit) {
throw new Exception("Can't request over " . static::$limit . " items.");
}
return $payload;
}
/**
* Wait until it's safe to make requests.
*
* @return self
*/
private function wait(): IpAPI
{
if ($this->X_RL === 0) {
sleep($this->X_TTL + 1);
}
return $this;
}
/**
* Submit a request to the server.
*
* @param array $payload
* @return GeoLookup
* @throws Exception
*/
private function request(array $payload): GeoLookup
{
$response = Requests::post(
static::$endpoint,
static::$headers,
json_encode($payload)
);
if ($response->status_code >= 400)
throw new Exception("API response status: " . $response->status_code);
$this->X_TTL = (int) $response->headers['x-ttl'];
$this->X_RL = (int) $response->headers['x-rl'];
return GeoLookup::fromArray(json_decode($response->body, true)[0], 'ip-api.com');
}
/**
* Submit a request to the server.
*
* @param array $payload
* @return array
* @throws Exception
*/
private function request_batch(array $payload): array
{
$response = Requests::post(
static::$endpoint,
static::$headers,
json_encode($payload)
);
if ($response->status_code >= 400)
throw new Exception("API response status: " . $response->status_code);
$this->X_TTL = (int) $response->headers['x-ttl'];
$this->X_RL = (int) $response->headers['x-rl'];
$results = [];
foreach(json_decode($response->body, true) as $item)
$results[] = GeoLookup::fromArray($item, 'ip-api.com');
return $results;
}
}

53
src/khm/ThirdParty/IpAPIco.php vendored Normal file
View File

@ -0,0 +1,53 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace khm\ThirdParty;
use Exception;
use khm\Objects\GeoLookup;
use WpOrg\Requests\Requests;
class IpAPIco
{
/**
* The API endpoint.
*
* @var string
*/
private static $endpoint = 'https://ipapi.co/';
/**
* The request headers.
*
* @var array
*/
private static $headers = [
'User-Agent: Intellivoid-KHM/1.0',
'Content-Type: application/json',
'Accept: application/json'
];
/**
* Submit a request to the server.
*
* @param string $ip_address
* @return GeoLookup
* @throws Exception
*/
public function get(string $ip_address): GeoLookup
{
$response = Requests::get(
static::$endpoint . urlencode($ip_address) . '/json',
static::$headers
);
if ($response->status_code >= 400)
throw new Exception("API response status: " . $response->status_code);
return GeoLookup::fromArray(json_decode($response->body, true), 'ipapi.co');
}
}

106633
src/khm/data/asn.json Normal file

File diff suppressed because it is too large Load Diff

294
src/khm/khm.php Normal file
View File

@ -0,0 +1,294 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace khm;
use acm2\acm2;
use acm2\Exceptions\ConfigurationNotDefinedException;
use acm2\Objects\Schema;
use Exception;
use khm\Abstracts\GeoLookupSource;
use khm\Exceptions\AbuseRecordNotFoundException;
use khm\Exceptions\BadGeoSourceException;
use khm\Exceptions\GeoRecordNotFoundException;
use khm\Managers\AbuseManager;
use khm\Managers\GeoManager;
use khm\Objects\AbuseCheck;
use khm\Objects\GeoLookup;
use khm\ThirdParty\AbuseIPDB;
use khm\ThirdParty\IpAPI;
use khm\ThirdParty\IpAPIco;
use mysqli;
class khm
{
/**
* @var acm2
*/
private $acm;
/**
* @var mixed
*/
private $DatabaseConfiguration;
/**
* @var mysqli|null
*/
private $database;
/**
* @var int
*/
private $LastConnectedDatabaseTimestamp;
/**
* @var AbuseManager
*/
private $AbuseManager;
/**
* @var mixed
*/
private $AbuseIpDbConfiguration;
/**
* @var GeoManager
*/
private $GeoManager;
/**
* @throws ConfigurationNotDefinedException
*/
public function __construct()
{
// Advanced Configuration Manager
$this->acm = new acm2('khm');
// Database Schema Configuration
$DatabaseSchema = new Schema();
$DatabaseSchema->setName('Database');
$DatabaseSchema->setDefinition('Host', '127.0.0.1');
$DatabaseSchema->setDefinition('Port', '3306');
$DatabaseSchema->setDefinition('Username', 'root');
$DatabaseSchema->setDefinition('Password', 'root');
$DatabaseSchema->setDefinition('Name', 'khm');
$this->acm->defineSchema($DatabaseSchema);
// Database Schema Configuration
$AbuseIpDB = new Schema();
$AbuseIpDB->setName('AbuseIpDB');
$AbuseIpDB->setDefinition('ApiKeys', []);
$this->acm->defineSchema($AbuseIpDB);
// Save any changes
$this->acm->updateConfiguration();
$this->acm->reloadConfiguration();
// Get the configuration
$this->DatabaseConfiguration = $this->acm->getConfiguration('Database');
$this->AbuseIpDbConfiguration = $this->acm->getConfiguration('AbuseIpDB');
$this->AbuseManager = new AbuseManager($this);
$this->GeoManager = new GeoManager($this);
}
/**
* @return mysqli|null
*/
public function getDatabase(): ?mysqli
{
if($this->database == null)
{
$this->connectDatabase();
}
if( (time() - $this->LastConnectedDatabaseTimestamp) > 1800)
$this->connectDatabase();
return $this->database;
}
/**
* Closes the current database connection
*/
public function disconnectDatabase()
{
$this->database->close();
$this->database = null;
$this->LastConnectedDatabaseTimestamp = null;
}
/**
* Creates a new database connection
*/
public function connectDatabase()
{
if($this->database !== null)
{
$this->disconnectDatabase();
}
$this->database = new mysqli(
$this->DatabaseConfiguration['Host'],
$this->DatabaseConfiguration['Username'],
$this->DatabaseConfiguration['Password'],
$this->DatabaseConfiguration['Name'],
$this->DatabaseConfiguration['Port']
);
$this->LastConnectedDatabaseTimestamp = time();
}
/**
* Preforms an IP Abuse lookup, uses local database if the information is not out of date.
*
* @param string $ip_address
* @return AbuseCheck
* @throws Exceptions\DatabaseException
*/
public function abuseLookup(string $ip_address): AbuseCheck
{
$selected_key = $this->AbuseIpDbConfiguration['ApiKeys'][array_rand($this->AbuseIpDbConfiguration['ApiKeys'])];
try
{
$AbuseCheck = $this->AbuseManager->getRecord($ip_address);
}
catch(AbuseRecordNotFoundException $e)
{
$AbuseCheck = $this->AbuseManager->registerRecord(AbuseIPDB::check($selected_key, $ip_address));
}
if((time() - $AbuseCheck->LastUpdatedTimestamp) > 43200)
{
return $this->AbuseManager->updateRecord(AbuseIPDB::check($selected_key, $ip_address));
}
return $AbuseCheck;
}
/**
* Preforms a Geo lookup query, or searches against the database if the record is not out of date
*
* @param string $ip_address
* @param string $source
* @return GeoLookup
* @throws BadGeoSourceException
* @throws Exceptions\DatabaseException
* @throws Exception
*/
public function geoLookup(string $ip_address, string $source=GeoLookupSource::ipApiCo): GeoLookup
{
try
{
$GeoLookup = $this->GeoManager->getRecord($ip_address);
}
catch(GeoRecordNotFoundException $e)
{
$GeoLookup = null;
}
if($GeoLookup == null || (time() - $GeoLookup->LastUpdatedTimestamp) >= 1209600)
{
switch($source)
{
case GeoLookupSource::ipApi:
$api = new IpAPI();
$api->setLanguage('en');
$api->setFields([
'status', 'message', 'continent', 'continentCode', 'country', 'countryCode', 'region',
'regionName', 'city', 'district', 'zip', 'lat', 'lon', 'timezone', 'offset', 'currency',
'isp', 'org', 'as', 'asname', 'reverse', 'mobile', 'proxy', 'hosting', 'query'
]);
if($GeoLookup == null)
{
$GeoLookup = $this->GeoManager->registerRecord($api->get($ip_address));
}
else
{
$GeoLookup = $this->GeoManager->updateRecord($api->get($ip_address));
}
break;
case GeoLookupSource::ipApiCo:
$api = new IpAPIco();
if($GeoLookup == null)
{
$GeoLookup = $this->GeoManager->registerRecord($api->get($ip_address));
}
else
{
$GeoLookup = $this->GeoManager->updateRecord($api->get($ip_address));
}
break;
default:
throw new BadGeoSourceException('The geo lookup source \'' . $source . '\' is not supported by the engine');
}
}
return $GeoLookup;
}
/**
* Preforms a batch query while comparing results from the database
*
* @param array $query
* @return array
* @throws Exceptions\DatabaseException
*/
public function multipleGeoLookup(array $query): array
{
$api = new IpAPI();
$api->setLanguage('en');
$api->setFields([
'status', 'message', 'continent', 'continentCode', 'country', 'countryCode', 'region',
'regionName', 'city', 'district', 'zip', 'lat', 'lon', 'timezone', 'offset', 'currency',
'isp', 'org', 'as', 'asname', 'reverse', 'mobile', 'proxy', 'hosting', 'query'
]);
$new_queries = [];
$update_queries = [];
$results = [];
foreach($query as $item)
{
try
{
$GeoLookup = $this->GeoManager->getRecord($item);
if((time() - $GeoLookup->LastUpdatedTimestamp) >= 1209600)
{
$update_queries[] = $item;
}
else
{
$results[] = $GeoLookup;
}
}
catch(GeoRecordNotFoundException $e)
{
$new_queries[] = $item;
}
}
foreach($api->get_batch(array_merge($new_queries, $update_queries)) as $lookup)
{
if(in_array($lookup->IPAddress, $update_queries))
{
$results[] = $this->GeoManager->updateRecord($lookup);
}
if(in_array($lookup->IPAddress, $new_queries))
{
$results[] = $this->GeoManager->registerRecord($lookup);
}
}
return $results;
}
}

109
src/khm/package.json Normal file
View File

@ -0,0 +1,109 @@
{
"package": {
"package_name": "net.intellivoid.khm",
"name": "Known Hosts Manager",
"version": "1.0.0.0",
"author": "Zi Xing Narrakas",
"organization": "Intellivoid Technologies",
"description": "Manages and checks known hosts to the system to block potential abuse hosts",
"url": "https://github.com/intellivoid/khm",
"dependencies": [
{
"package": "com.rmccue.requests",
"version": "latest",
"source": "rmccue@composer/requests",
"required": true
},
{
"package": "net.intellivoid.acm2",
"version": "latest",
"source": "default@github/intellivoid/acm2",
"required": true
},
{
"package": "net.intellivoid.msqg",
"version": "latest",
"source": "default@github/intellivoid/msqg",
"required": true
},
{
"package": "net.intellivoid.ziproto",
"version": "latest",
"source": "default@github/intellivoid/ziproto",
"required": true
}
],
"configuration": {
"autoload_method": "generated_spl",
"main": null,
"post_installation": [],
"pre_installation": []
}
},
"components": [
{
"required": true,
"file": "Abstracts/GeoLookupSource.php"
},
{
"required": true,
"file": "Classes/Curl.php"
},
{
"required": true,
"file": "khm.php"
},
{
"required": true,
"file": "Exceptions/AbuseRecordNotFoundException.php"
},
{
"required": true,
"file": "Exceptions/DatabaseException.php"
},
{
"required": true,
"file": "Exceptions/GeoRecordNotFoundException.php"
},
{
"required": true,
"file": "Exceptions/KhmResolutionException.php"
},
{
"required": true,
"file": "Exceptions/BadGeoSourceException.php"
},
{
"required": true,
"file": "ThirdParty/IpAPI.php"
},
{
"required": true,
"file": "ThirdParty/AbuseIPDB.php"
},
{
"required": true,
"file": "ThirdParty/IpAPIco.php"
},
{
"required": true,
"file": "Objects/AbuseCheck.php"
},
{
"required": true,
"file": "Objects/GeoLookup.php"
},
{
"required": true,
"file": "Managers/GeoManager.php"
},
{
"required": true,
"file": "Managers/AbuseManager.php"
}
],
"files": [
"package.json",
"data/asn.json"
]
}

View File

@ -0,0 +1,8 @@
<?php
require 'ppm';
require 'net.intellivoid.khm';
$khm = new \khm\khm();
$res = $khm->abuseLookup('5.178.86.77');
var_dump($res);

8
tests/geo_test.php Normal file
View File

@ -0,0 +1,8 @@
<?php
require 'ppm';
require 'net.intellivoid.khm';
$khm = new \khm\khm();
$res = $khm->geoLookup('5.178.86.77');
var_dump($res);

40
tests/ipapi_lookup1.php Normal file
View File

@ -0,0 +1,40 @@
<?php
require 'ppm';
require 'net.intellivoid.khm';
$api = new \khm\ThirdParty\IpAPI();
$api->setLanguage('en');
$api->setFields([
'status',
'message',
'continent',
'continentCode',
'country',
'countryCode',
'region',
'regionName',
'city',
'district',
'zip',
'lat',
'lon',
'timezone',
'offset',
'currency',
'isp',
'org',
'as',
'asname',
'reverse',
'mobile',
'proxy',
'hosting',
'query'
]);
var_dump($api->get('91.198.174.192'));
var_dump($api->get_batch([
'100.142.29.254',
'100.142.39.218'
]));

View File

@ -0,0 +1,8 @@
<?php
require 'ppm';
require 'net.intellivoid.khm';
$api = new \khm\ThirdParty\IpAPIco();
var_dump($api->get('91.198.174.192'));

View File

@ -0,0 +1,11 @@
<?php
require 'ppm';
require 'net.intellivoid.khm';
$khm = new \khm\khm();
$res = $khm->multipleGeoLookup([
'100.142.29.254',
'100.142.39.218'
]);
var_dump($res);