Various bug fixes, completed project

This commit is contained in:
Zi Xing 2021-12-20 15:11:20 -05:00
parent 8aa1a50cce
commit a7ef95c15d
20 changed files with 1195 additions and 65 deletions

View File

@ -2,8 +2,6 @@
namespace khm\Abstracts;
use DynamicalWeb\Abstracts\AbstractSoftware;
abstract class AbstractVersionedSoftware extends AbstractSoftware
{
/**

View File

@ -18,4 +18,14 @@
* Indicates that the IP address is an exit node
*/
const TorExit = 'TOR_EXIT';
/**
* Indicates that the device is a mobile device
*/
const MobileDevice = 'MOBILE_DEVICE';
/**
* Indicates that the browser is a mobile browser
*/
const MobileBrowser = 'MOBILE_BROWSER';
}

View File

@ -2,6 +2,11 @@
namespace khm\Classes\UserAgentParser;
use khm\Abstracts\AbstractParser;
use khm\Classes\RegexLoader;
use khm\Classes\UserAgentParser;
use khm\Objects\UserAgentClient;
class Parser extends AbstractParser
{
/**
@ -21,11 +26,10 @@
/**
* Start up the parser by importing the data file to $this->regexes
* @throws FileNotFoundException
*/
public function __construct()
{
parent::__construct(Utilities::getUserAgentRegexes());
parent::__construct(RegexLoader::getRegex());
$this->deviceParser = new DeviceParser($this->regexes);
$this->operatingSystemParser = new OperatingSystemParser($this->regexes);
$this->userAgentParser = new UserAgentParser($this->regexes);

View File

@ -93,6 +93,7 @@
'fingerprint',
'user_agent',
'os_family',
'os_version',
'device_family',
'device_brand',
'device_model',
@ -160,7 +161,6 @@
'last_seen_timestamp' => $device->LastSeenTimestamp
], 'fingerprint', $this->khm->getDatabase()->real_escape_string($device->Fingerprint));
$QueryResults = $this->khm->getDatabase()->query($Query);
if($QueryResults == false)

View File

@ -49,7 +49,7 @@
'id' => $this->khm->getDatabase()->real_escape_string($knownDevice->ID),
'ip_address' => $this->khm->getDatabase()->real_escape_string($knownDevice->IPAddress),
'device_fingerprint' => $this->khm->getDatabase()->real_escape_string($knownDevice->DeviceFingerprint),
'properties' => $this->khm->getDatabase()->real_escape_string(ZiProto::encode($knownDevice->Properties)),
'properties' => $this->khm->getDatabase()->real_escape_string(ZiProto::encode($knownDevice->Properties->toArray())),
'last_seen_timestamp' => (int)$knownDevice->LastSeenTimestamp,
'created_timestamp' => (int)$knownDevice->CreatedTimestamp
]);
@ -133,7 +133,7 @@
public function updateLastSeen(KnownDevice $device): KnownDevice
{
$device->LastSeenTimestamp = time();
$Query = QueryBuilder::update('known_device', [
$Query = QueryBuilder::update('known_devices', [
'last_seen_timestamp' => $device->LastSeenTimestamp
], 'id', $this->khm->getDatabase()->real_escape_string($device->ID));

View File

@ -27,19 +27,22 @@
/**
* Registers a new known record into the database
*
* @param KnownHost $knownHost
* @param string $ip_address
* @return KnownHost
* @throws DatabaseException
* @noinspection PhpCastIsUnnecessaryInspection
*/
public function registerRecord(KnownHost $knownHost): KnownHost
public function registerRecord(string $ip_address): KnownHost
{
$knownHost = new KnownHost();
$knownHost->IPAddress = $ip_address;
$knownHost->Properties = new KnownHost\Properties();
$knownHost->CreatedTimestamp = time();
$knownHost->LastSeenTimestamp = time();
$Query = QueryBuilder::insert_into('known_hosts', [
'ip_address' => $this->khm->getDatabase()->real_escape_string($knownHost->IPAddress),
'properties' => $this->khm->getDatabase()->real_escape_string(ZiProto::encode($knownHost->Properties)),
'properties' => $this->khm->getDatabase()->real_escape_string(ZiProto::encode($knownHost->Properties->toArray())),
'last_seen_timestamp' => (int)$knownHost->LastSeenTimestamp,
'created_timestamp' => (int)$knownHost->CreatedTimestamp
]);

View File

@ -124,6 +124,10 @@
throw new OnionRecordNotFoundException('The onion record for the IP \'' . $ip_address . '\' was not found');
}
$Row['flags'] = ZiProto::decode($Row['flags']);
$Row['or_addresses'] = ZiProto::decode($Row['or_addresses']);
$Row['exit_addresses'] = ZiProto::decode($Row['exit_addresses']);
return OnionRelay::fromArray($Row);
}

View File

@ -0,0 +1,85 @@
<?php
namespace khm\Objects;
use khm\Abstracts\HostFlags;
use khm\Objects\Client\Abuse;
use khm\Objects\Client\Geo;
class Client
{
/**
* The queried IP address
*
* @var string
*/
public $IPAddress;
/**
* The version of the IP address
*
* @var int
*/
public $IPVersion;
/**
* The ID of the known device if available
*
* @var string|null
*/
public $KnownDeviceID;
/**
* Information about the IP addresses onion relay if available, otherwise null.
*
* @var \khm\Objects\Client\OnionRelay|null
*/
public $Onion;
/**
* Information about the IP addresses geolocation if available, otherwise null.
*
* @var Geo|null
*/
public $Geo;
/**
* Information about the abuse status of the IP address
*
* @var Abuse|null
*/
public $Abuse;
/**
* Information about the users' device if available
*
* @var Device|null
*/
public $Device;
/**
* Flags associated with this IP address query
*
* @var string[]|HostFlags[]
*/
public $Flags;
/**
* Returns an array representation of the object
*
* @return array
*/
public function toArray(): array
{
return [
'ip_address' => $this->IPAddress,
'ip_version' => $this->IPVersion,
'known_device_id' => $this->KnownDeviceID,
'onion' => ($this->Onion == null ? null : $this->Onion->toArray()),
'geo' => ($this->Geo == null ? null : $this->Geo->toArray()),
'abuse' => ($this->Abuse == null ? null : $this->Abuse->toArray()),
'device' => ($this->Device == null ? null : $this->Device->toArray()),
'flags' => ($this->Flags == null ? [] : $this->Flags),
];
}
}

View File

@ -0,0 +1,187 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace khm\Objects\Client;
use khm\Objects\AbuseCheck;
class Abuse
{
/**
* Indicates if the IP address is public or private
*
* @var bool
*/
public $IsPublic;
/**
* 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 [
'is_public' => ($this->IsPublic == null ? false : (bool)$this->IsPublic),
'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 Abuse
* @noinspection PhpTernaryExpressionCanBeReplacedWithConditionInspection
*/
public static function fromArray(array $data): Abuse
{
$AbuseCheckObject = new Abuse();
if(isset($data['is_public']))
$AbuseCheckObject->IsPublic = (bool)$data['is_public'];
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'];
return $AbuseCheckObject;
}
/**
* Constructs object from an AbuseCheck object
*
* @param AbuseCheck $abuseCheck
* @return Abuse
*/
public static function fromAbuseCheck(AbuseCheck $abuseCheck): Abuse
{
return self::fromArray($abuseCheck->toArray());
}
}

View File

@ -0,0 +1,275 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace khm\Objects\Client;
use khm\Objects\GeoLookup;
class Geo
{
/**
* 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 [
'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 Geo
*/
public static function fromArray(array $data, ?string $source=null): Geo
{
$geo_object = new Geo();
if($source !== null && strlen($source) > 0)
$geo_object->Source = $source;
if(isset($data['source']))
$geo_object->Source = $data['source'];
if(isset($data['continent']))
$geo_object->Continent = $data['continent'];
if(isset($data['continent_code']))
$geo_object->ContinentCode = $data['continent_code'];
if(isset($data['country']))
$geo_object->Country = $data['country'];
if(isset($data['country_code']))
$geo_object->CountryCode = $data['country_code'];
if(isset($data['region']))
$geo_object->Region = $data['region'];
if(isset($data['region_code']))
$geo_object->RegionCode = $data['region_code'];
if(isset($data['city']))
$geo_object->City = $data['city'];
if(isset($data['zip_code']))
$geo_object->ZipCode = $data['zip_code'];
if(strlen($geo_object->ZipCode) == 0)
$geo_object->ZipCode = null;
if(isset($data['latitude']))
$geo_object->Latitude = $data['latitude'];
if(isset($data['longitude']))
$geo_object->Longitude = $data['longitude'];
$geo_object->Longitude = (float)$geo_object->Longitude;
$geo_object->Latitude = (float)$geo_object->Latitude;
if(isset($data['timezone']))
$geo_object->Timezone = $data['timezone'];
if(isset($data['offset']))
$geo_object->Offset = (int)$data['offset'];
if(isset($data['currency']))
$geo_object->Currency = $data['currency'];
if(isset($data['isp']))
$geo_object->ISP = (strlen($data['isp']) == null ? null : $data['isp']);
if(isset($data['organization']))
$geo_object->Organization = $data['organization'];
if(isset($data['as']))
$geo_object->AS = $data['as'];
if(isset($data['asname']))
$geo_object->AsName = $data['asname'];
if(isset($data['last_updated_timestamp']))
$geo_object->LastUpdatedTimestamp = (int)$data['last_updated_timestamp'];
if(isset($data['created_timestamp']))
$geo_object->CreatedTimestamp = (int)$data['created_timestamp'];
return $geo_object;
}
/**
* Constructs object from a geo lookup object
*
* @param GeoLookup $geoLookup
* @return Geo
*/
public static function fromGeoLookup(GeoLookup $geoLookup): Geo
{
return Geo::fromArray($geoLookup->toArray());
}
}

View File

@ -0,0 +1,317 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace khm\Objects\Client;
use khm\Abstracts\OnionVersionStatus;
class OnionRelay
{
/**
* Relay nickname consisting of 119 alphanumerical characters.
*
* @var string
*/
public $Nickname;
/**
* Relay fingerprint consisting of 40 upper-case hexadecimal characters.
*
* @var string
*/
public $Fingerprint;
/**
* Array of IPv4 or IPv6 addresses and TCP ports or port lists where the relay accepts onion-routing connections.
* The first address is the primary onion-routing address that the relay used to register in the network,
* subsequent addresses are in arbitrary order. IPv6 hex characters are all lower-case.
*
* @var string[]
*/
public $OrAddresses;
/**
* Array of IPv4 addresses that the relay used to exit to the Internet in the past 24 hours.
* Omitted if array is empty.
*
* @var string[]
*/
public $ExitAddresses;
/**
* IPv4 address where the relay accepts directory connections.
* Omitted if the relay does not accept directory connections.
*
* @var string|null
*/
public $DirAddress;
/**
* TCP port where the relay accepts directory connections.
* Omitted if the relay does not accept directory connections.
*
* @var int|null
*/
public $DirPort;
/**
* Unix Timestamp for when this relay was last seen in a network status consensus.
*
* @var int
*/
public $LastSeenTimestamp;
/**
* Unix Timestamp for when this relay last stopped announcing an IPv4 or IPv6 address or TCP port where it
* previously accepted onion-routing or directory connections. This timestamp can serve as indicator whether
* this relay would be a suitable fallback directory.
*
* @var int
*/
public $LastChangedAddressOrPort;
/**
* Unix Timestamp for when this relay was first seen in a network status consensus.
*
* @var int
*/
public $FirstSeen;
/**
* Array of relay flags that the directory authorities assigned to this relay. May be omitted if empty.
*
* @var array
*/
public $Flags;
/**
* Boolean field saying whether this relay was listed as running in the last relay network status consensus.
*
* @var bool
*/
public $Running;
/**
* Indicates if the exit flag is present
*
* @var bool
*/
public $Exit;
/**
* Indicates if the fast flag is present
*
* @var bool
*/
public $Fast;
/**
* Indicates if the guard flag is present
*
* @var bool
*/
public $Guard;
/**
* Indicates if the stable flag is present
*
* @var bool
*/
public $Stable;
/**
* Indicates if the valid flag is present
*
* @var bool
*/
public $Valid;
/**
* Contact address of the relay operator. Omitted if empty or if descriptor containing this information
* cannot be found.
*
* @var string|null
*/
public $Contact;
/**
* Platform string containing operating system and Tor version details. Omitted if empty or if descriptor
* containing this information cannot be found.
*
* @var string|null
*/
public $Platform;
/**
* Tor software version without leading "Tor" as reported by the directory authorities in the "v" line of the
* consensus. Omitted if either the directory authorities or the relay did not report which version the
* relay runs or if the relay runs an alternative Tor implementation.
*
* @var string|null
*/
public $Version;
/**
* Status of the Tor software version of this relay based on the versions recommended by the directory
* authorities. Possible version statuses are: "recommended" if a version is listed as recommended;
* "experimental" if a version is newer than every recommended version; "obsolete" if a version is older
* than every recommended version; "new in series" if a version has other recommended versions with the
* same first three components, and the version is newer than all such recommended versions, but it is not
* newer than every recommended version; "unrecommended" if none of the above conditions hold. Omitted if
* either the directory authorities did not recommend versions, or the relay did not report which version it runs.
*
* @var string|OnionVersionStatus|null
*/
public $VersionStatus;
/**
* Weight assigned to this relay by the directory authorities that clients use in their path selection
* algorithm. The unit is arbitrary; currently it's kilobytes per second, but that might change in the future.
*
* @var int
*/
public $ConsensusWeight;
/**
* The Unix Timestamp for when this record was last updated
*
* @var int
*/
public $LastUpdatedTimestmap;
/**
* The Unix Timestamp for when this record was first registered into the database
*
* @var int
*/
public $CreatedTimestmap;
/**
* Returns an array representation of the object
*
* @return array
* @noinspection PhpCastIsUnnecessaryInspection
*/
public function toArray(): array
{
return [
'nickname' => $this->Nickname,
'fingerprint' => $this->Fingerprint,
'or_addresses' => $this->OrAddresses,
'exit_addresses' => $this->ExitAddresses,
'dir_address' => $this->DirAddress,
'dir_port' => (int)$this->DirPort,
'last_seen_timestamp' => (int)$this->LastSeenTimestamp,
'last_changed_address_or_port' => (int)$this->LastChangedAddressOrPort,
'first_seen' => (int)$this->FirstSeen,
'flags' => $this->Flags,
'running' => (bool)$this->Running,
'exit' => (bool)$this->Exit,
'fast' => (bool)$this->Fast,
'guard' => (bool)$this->Guard,
'stable' => (bool)$this->Stable,
'valid' => (bool)$this->Valid,
'contact' => $this->Contact,
'platform' => $this->Platform,
'version' => $this->Version,
'version_status' => $this->VersionStatus,
'consensus_weight' => (int)$this->ConsensusWeight,
'last_updated_timestamp' => (int)$this->LastUpdatedTimestmap,
'created_timestamp' => (int)$this->CreatedTimestmap
];
}
/**
* Constructs object from an array representation
*
* @param array $data
* @return OnionRelay
*/
public static function fromArray(array $data): OnionRelay
{
$OnionRelayObject = new OnionRelay();
if(isset($data['nickname']))
$OnionRelayObject->Nickname = $data['nickname'];
if(isset($data['fingerprint']))
$OnionRelayObject->Fingerprint = $data['fingerprint'];
if(isset($data['or_addresses']))
$OnionRelayObject->OrAddresses = $data['or_addresses'];
if(isset($data['exit_addresses']))
$OnionRelayObject->ExitAddresses = $data['exit_addresses'];
if(isset($data['dir_address']))
$OnionRelayObject->DirAddress = $data['dir_address'];
if(isset($data['dir_port']))
$OnionRelayObject->DirPort = (int)$data['dir_port'];
if(isset($data['last_seen_timestamp']))
$OnionRelayObject->LastSeenTimestamp = (int)$data['last_seen_timestamp'];
if(isset($data['last_changed_address_or_port']))
$OnionRelayObject->LastChangedAddressOrPort = (int)$data['last_changed_address_or_port'];
if(isset($data['first_seen']))
$OnionRelayObject->FirstSeen = (int)$data['first_seen'];
if(isset($data['flags']))
$OnionRelayObject->Flags = $data['flags'];
if(isset($data['running']))
$OnionRelayObject->Running = (bool)$data['running'];
if(isset($data['exit']))
$OnionRelayObject->Exit = (bool)$data['exit'];
if(isset($data['fast']))
$OnionRelayObject->Fast = (bool)$data['fast'];
if(isset($data['guard']))
$OnionRelayObject->Guard = (bool)$data['guard'];
if(isset($data['stable']))
$OnionRelayObject->Stable = (bool)$data['stable'];
if(isset($data['valid']))
$OnionRelayObject->Valid = (bool)$data['valid'];
if(isset($data['contact']))
$OnionRelayObject->Contact = $data['contact'];
if(isset($data['platform']))
$OnionRelayObject->Platform = $data['platform'];
if(isset($data['version']))
$OnionRelayObject->Version = $data['version'];
if(isset($data['version_status']))
$OnionRelayObject->VersionStatus = $data['version_status'];
if(isset($data['consensus_weight']))
$OnionRelayObject->ConsensusWeight = $data['consensus_weight'];
if(isset($data['last_updated_timestamp']))
$OnionRelayObject->LastUpdatedTimestmap = (int)$data['last_updated_timestamp'];
if(isset($data['created_timestamp']))
$OnionRelayObject->CreatedTimestmap = (int)$data['created_timestamp'];
return $OnionRelayObject;
}
/**
* Constructs object from a onion relay record
*
* @param \khm\Objects\OnionRelay $relay
* @return OnionRelay
*/
public static function fromOnionRelayObject(\khm\Objects\OnionRelay $relay): OnionRelay
{
return self::fromArray($relay->toArray());
}
}

View File

@ -109,6 +109,8 @@
public function __construct()
{
$this->Properties = new Properties();
$this->MobileDevice = false;
$this->MobileBrowser = false;
}
/**

View File

@ -152,7 +152,7 @@ namespace khm\Objects;
public $LastUpdatedTimestamp;
/**
* The Unix Timestmap for when this record was created
* The Unix Timestamp for when this record was created
*
* @var int
*/
@ -366,6 +366,39 @@ namespace khm\Objects;
$asn = null;
}
if(strlen((string)$geo_lookup_object->Source) == 0)
$geo_lookup_object->Source = null;
if(strlen((string)$geo_lookup_object->Continent) == 0)
$geo_lookup_object->Continent = null;
if(strlen((string)$geo_lookup_object->ContinentCode) == 0)
$geo_lookup_object->ContinentCode = null;
if(strlen((string)$geo_lookup_object->Country) == 0)
$geo_lookup_object->Country = null;
if(strlen((string)$geo_lookup_object->Region) == 0)
$geo_lookup_object->Region = null;
if(strlen((string)$geo_lookup_object->RegionCode) == 0)
$geo_lookup_object->RegionCode = null;
if(strlen((string)$geo_lookup_object->City) == 0)
$geo_lookup_object->City = null;
if(strlen((string)$geo_lookup_object->ZipCode) == 0)
$geo_lookup_object->ZipCode = null;
if(strlen((string)$geo_lookup_object->Latitude) == 0)
$geo_lookup_object->Latitude = null;
if(strlen((string)$geo_lookup_object->Longitude) == 0)
$geo_lookup_object->Longitude = null;
if(strlen((string)$geo_lookup_object->Timezone) == 0)
$geo_lookup_object->Timezone = null;
if(strlen((string)$geo_lookup_object->Offset) == 0)
$geo_lookup_object->Offset = null;
if(strlen((string)$geo_lookup_object->Currency) == 0)
$geo_lookup_object->Currency = null;
if(strlen((string)$geo_lookup_object->Organization) == 0)
$geo_lookup_object->Organization = null;
if(strlen((string)$geo_lookup_object->AS) == 0)
$geo_lookup_object->AS = null;
if(strlen((string)$geo_lookup_object->AsName) == 0)
$geo_lookup_object->AsName = null;
return $geo_lookup_object;
}
}

View File

@ -323,6 +323,9 @@
if(isset($data['created_timestamp']))
$OnionRelayObject->CreatedTimestmap = (int)$data['created_timestamp'];
if($OnionRelayObject->DirAddress == null)
$OnionRelayObject->DirPort = null;
return $OnionRelayObject;
}
}

View File

@ -1,50 +0,0 @@
<?php
namespace khm\Objects;
use khm\Abstracts\HostFlags;
class QueryResults
{
/**
* The queried IP address
*
* @var string
*/
public $IPAddress;
/**
* The version of the IP address
*
* @var int
*/
public $IPVersion;
/**
* Information about the IP addresses onion relay if available, otherwise null.
*
* @var OnionRelay|null
*/
public $Onion;
/**
* Information about the IP addresses geolocation if available, otherwise null.
*
* @var GeoLookup|null
*/
public $Geo;
/**
* Information about the abuse status of the IP address
*
* @var AbuseCheck|null
*/
public $Abuse;
/**
* Flags associated with this IP address query
*
* @var string[]|HostFlags[]
*/
public $Flags;
}

View File

@ -7,6 +7,10 @@
class UserAgentOperatingSystem extends AbstractVersionedSoftware
{
/**
* @var string|null
*/
public $family;
/**
* @var string|null

View File

@ -109,6 +109,63 @@
$onion_relay->LastUpdatedTimestmap = time();
$onion_relay->CreatedTimestmap = time();
$secondary_addresses = [];
if(isset($relay['or_addresses']) && count($relay['or_addresses']) > 0)
{
foreach($relay['or_addresses'] as $address)
{
$first_address = explode(':', $address)[0];
if(in_array($first_address, $secondary_addresses) == false)
{
if (filter_var($first_address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))
{
$second_onion_relay = $onion_relay;
$second_onion_relay->IPAddress = $first_address;
$second_onion_relay->IPVersion = 4;
$results[] = $second_onion_relay;
$secondary_addresses[] = $first_address;
}
elseif (filter_var($first_address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))
{
$second_onion_relay = $onion_relay;
$second_onion_relay->IPAddress = $first_address;
$second_onion_relay->IPVersion = 6;
$results[] = $second_onion_relay;
$secondary_addresses[] = $first_address;
}
}
}
}
if(isset($relay['exit_addresses']) && count($relay['exit_addresses']) > 0)
{
foreach($relay['exit_addresses'] as $address)
{
$first_address = explode(':', $address)[0];
if(in_array($first_address, $secondary_addresses) == false)
{
if (filter_var($first_address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))
{
$second_onion_relay = $onion_relay;
$second_onion_relay->IPAddress = $first_address;
$second_onion_relay->IPVersion = 4;
$results[] = $second_onion_relay;
$secondary_addresses[] = $first_address;
}
elseif (filter_var($first_address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))
{
$second_onion_relay = $onion_relay;
$second_onion_relay->IPAddress = $first_address;
$second_onion_relay->IPVersion = 6;
$results[] = $second_onion_relay;
$secondary_addresses[] = $first_address;
}
}
}
}
$results[] = $onion_relay;
}

View File

@ -9,9 +9,14 @@
use acm2\Objects\Schema;
use Exception;
use khm\Abstracts\GeoLookupSource;
use khm\Abstracts\HostFlags;
use khm\Abstracts\SearchMethods\DeviceSearchMethod;
use khm\Classes\DeviceDetection;
use khm\Classes\Utilities;
use khm\Exceptions\AbuseRecordNotFoundException;
use khm\Exceptions\BadGeoSourceException;
use khm\Exceptions\GeoRecordNotFoundException;
use khm\Exceptions\NoUserAgentProvidedException;
use khm\Managers\AbuseManager;
use khm\Managers\DevicesManager;
use khm\Managers\GeoManager;
@ -19,7 +24,11 @@
use khm\Managers\KnownHostsManager;
use khm\Managers\OnionManager;
use khm\Objects\AbuseCheck;
use khm\Objects\Client;
use khm\Objects\Device;
use khm\Objects\GeoLookup;
use khm\Objects\KnownDevice;
use khm\Objects\KnownHost;
use khm\Objects\OnionRelay;
use khm\ThirdParty\AbuseIPDB;
use khm\ThirdParty\IpAPI;
@ -323,6 +332,74 @@
return $results;
}
/**
* Detects the client's device
*
* @return Device
* @throws Exceptions\DatabaseException
* @throws Exceptions\NoUserAgentProvidedException
*/
public function detectDevice(): Device
{
$device = DeviceDetection::detectDevice();
try
{
$device = $this->DevicesManager->getRecord(DeviceSearchMethod::ByFingerprint, $device->Fingerprint);
return $this->DevicesManager->updateLastSeen($device);
}
catch(Exception $e)
{
return $this->DevicesManager->registerRecord($device);
}
}
/**
* Returns the Known Host record
*
* @return KnownHost
* @throws Exceptions\DatabaseException
*/
public function getKnownHost(): KnownHost
{
$client_ip = DeviceDetection::getClientIP();
try
{
$knownHost = $this->KnownHostsManager->getRecord($client_ip);
}
catch (Exceptions\KnownHostRecordNotFoundException $e)
{
return $this->KnownHostsManager->registerRecord($client_ip);
}
return $this->getKnownHostsManager()->updateLastSeen($knownHost);
}
/**
* Returns a known device record
*
* @param Device $device
* @param KnownHost $knownHost
* @return KnownDevice
* @throws Exceptions\DatabaseException
*/
public function getKnownDevice(Device $device, KnownHost $knownHost): KnownDevice
{
$id = Utilities::generateKnownDeviceId($knownHost, $device);
try
{
$knownDevice = $this->KnownDevicesManager->getRecord($id);
}
catch (Exceptions\KnownDeviceNotFoundException $e)
{
return $this->KnownDevicesManager->registerRecord($device, $knownHost);
}
return $this->getKnownDevicesManager()->updateLastSeen($knownDevice);
}
/**
* Preforms a tor IP lookup against the database if the record exists
*
@ -383,4 +460,104 @@
{
return $this->KnownDevicesManager;
}
/**
* Identifies the current client and returns the results
*
* @return Client
* @throws Exceptions\DatabaseException
*/
public function identify(): Client
{
$client_object = new Client();
// Get the IP address
$client_object->IPAddress = DeviceDetection::getClientIP();
// Detect the version of the IP address
if(filter_var($client_object->IPAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))
{
$client_object->IPVersion = 4;
}
if(filter_var($client_object->IPAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))
{
$client_object->IPVersion = 6;
}
// Detect the device
try
{
$client_object->Device = $this->detectDevice();
}
catch(NoUserAgentProvidedException $exception)
{
unset($exception);
}
// Detect the known host
$known_host = $this->getKnownHost();
// Detect the known device
if($client_object->Device !== null)
{
$known_device = $this->getKnownDevice($client_object->Device, $known_host);
$client_object->KnownDeviceID = $known_device->ID;
}
// Detect the abuse status
try
{
$client_object->Abuse = Client\Abuse::fromAbuseCheck($this->abuseLookup($client_object->IPAddress));
}
catch(Exception $e)
{
unset($e);
}
// Detect the onion status
try
{
$client_object->Onion = Client\OnionRelay::fromOnionRelayObject($this->torLookup($client_object->IPAddress));
}
catch(Exception $e)
{
unset($e);
}
// Detect the geo data
try
{
$client_object->Geo = Client\Geo::fromGeoLookup($this->geoLookup($client_object->IPAddress));
}
catch(Exception $e)
{
unset($e);
}
$client_object->Flags = [];
if($client_object->Onion !== null)
{
$client_object->Flags[] = HostFlags::TorRelay;
if($client_object->Onion->Exit)
$client_object->Flags[] = HostFlags::TorExit;
}
if($client_object->Abuse !== null)
{
if($client_object->Abuse->AbuseConfidenceScore >= 70)
$client_object->Flags[] = HostFlags::BadUser;
}
if($client_object->Device !== null)
{
if($client_object->Device->MobileDevice)
$client_object->Flags[] = HostFlags::MobileDevice;
if($client_object->Device->MobileBrowser)
$client_object->Flags[] = HostFlags::MobileBrowser;
}
return $client_object;
}
}

View File

@ -193,10 +193,6 @@
"required": true,
"file": "Objects/UserAgentClient.php"
},
{
"required": true,
"file": "Objects/QueryResults.php"
},
{
"required": true,
"file": "Objects/Device.php"
@ -213,10 +209,26 @@
"required": true,
"file": "Objects/UserAgentOperatingSystem.php"
},
{
"required": true,
"file": "Objects/Client.php"
},
{
"required": true,
"file": "Objects/KnownHost/Properties.php"
},
{
"required": true,
"file": "Objects/Client/Geo.php"
},
{
"required": true,
"file": "Objects/Client/Abuse.php"
},
{
"required": true,
"file": "Objects/Client/OnionRelay.php"
},
{
"required": true,
"file": "Objects/GeoLookup.php"

9
tests/web_test.php Normal file
View File

@ -0,0 +1,9 @@
<?php
require 'ppm';
require 'net.intellivoid.khm';
$khm = new \khm\khm();
?>
<pre><?PHP print(json_encode($khm->identify(), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); ?></pre>