From a7ef95c15de1c33c7b4b44c081884accc018918d Mon Sep 17 00:00:00 2001 From: Zi Xing Date: Mon, 20 Dec 2021 15:11:20 -0500 Subject: [PATCH] Various bug fixes, completed project --- .../Abstracts/AbstractVersionedSoftware.php | 2 - src/khm/Abstracts/HostFlags.php | 10 + src/khm/Classes/UserAgentParser/Parser.php | 8 +- src/khm/Managers/DevicesManager.php | 2 +- src/khm/Managers/KnownDevicesManager.php | 4 +- src/khm/Managers/KnownHostsManager.php | 9 +- src/khm/Managers/OnionManager.php | 4 + src/khm/Objects/Client.php | 85 +++++ src/khm/Objects/Client/Abuse.php | 187 +++++++++++ src/khm/Objects/Client/Geo.php | 275 +++++++++++++++ src/khm/Objects/Client/OnionRelay.php | 317 ++++++++++++++++++ src/khm/Objects/Device.php | 2 + src/khm/Objects/GeoLookup.php | 35 +- src/khm/Objects/OnionRelay.php | 3 + src/khm/Objects/QueryResults.php | 50 --- src/khm/Objects/UserAgentOperatingSystem.php | 4 + src/khm/ThirdParty/TorProject.php | 57 ++++ src/khm/khm.php | 177 ++++++++++ src/khm/package.json | 20 +- tests/web_test.php | 9 + 20 files changed, 1195 insertions(+), 65 deletions(-) create mode 100644 src/khm/Objects/Client.php create mode 100644 src/khm/Objects/Client/Abuse.php create mode 100644 src/khm/Objects/Client/Geo.php create mode 100644 src/khm/Objects/Client/OnionRelay.php delete mode 100644 src/khm/Objects/QueryResults.php create mode 100644 tests/web_test.php diff --git a/src/khm/Abstracts/AbstractVersionedSoftware.php b/src/khm/Abstracts/AbstractVersionedSoftware.php index d32cc72..f7d5f30 100644 --- a/src/khm/Abstracts/AbstractVersionedSoftware.php +++ b/src/khm/Abstracts/AbstractVersionedSoftware.php @@ -2,8 +2,6 @@ namespace khm\Abstracts; - use DynamicalWeb\Abstracts\AbstractSoftware; - abstract class AbstractVersionedSoftware extends AbstractSoftware { /** diff --git a/src/khm/Abstracts/HostFlags.php b/src/khm/Abstracts/HostFlags.php index 5a09410..8668ea2 100644 --- a/src/khm/Abstracts/HostFlags.php +++ b/src/khm/Abstracts/HostFlags.php @@ -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'; } \ No newline at end of file diff --git a/src/khm/Classes/UserAgentParser/Parser.php b/src/khm/Classes/UserAgentParser/Parser.php index 32148bb..5bd1ab9 100644 --- a/src/khm/Classes/UserAgentParser/Parser.php +++ b/src/khm/Classes/UserAgentParser/Parser.php @@ -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); diff --git a/src/khm/Managers/DevicesManager.php b/src/khm/Managers/DevicesManager.php index 4ce0949..642687f 100644 --- a/src/khm/Managers/DevicesManager.php +++ b/src/khm/Managers/DevicesManager.php @@ -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) diff --git a/src/khm/Managers/KnownDevicesManager.php b/src/khm/Managers/KnownDevicesManager.php index cd520fb..0c7511c 100644 --- a/src/khm/Managers/KnownDevicesManager.php +++ b/src/khm/Managers/KnownDevicesManager.php @@ -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)); diff --git a/src/khm/Managers/KnownHostsManager.php b/src/khm/Managers/KnownHostsManager.php index 25c6c66..264fb64 100644 --- a/src/khm/Managers/KnownHostsManager.php +++ b/src/khm/Managers/KnownHostsManager.php @@ -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 ]); diff --git a/src/khm/Managers/OnionManager.php b/src/khm/Managers/OnionManager.php index 8197e4a..938f841 100644 --- a/src/khm/Managers/OnionManager.php +++ b/src/khm/Managers/OnionManager.php @@ -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); } diff --git a/src/khm/Objects/Client.php b/src/khm/Objects/Client.php new file mode 100644 index 0000000..572164c --- /dev/null +++ b/src/khm/Objects/Client.php @@ -0,0 +1,85 @@ + $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), + ]; + } + } \ No newline at end of file diff --git a/src/khm/Objects/Client/Abuse.php b/src/khm/Objects/Client/Abuse.php new file mode 100644 index 0000000..91a3b6f --- /dev/null +++ b/src/khm/Objects/Client/Abuse.php @@ -0,0 +1,187 @@ + ($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()); + } + } \ No newline at end of file diff --git a/src/khm/Objects/Client/Geo.php b/src/khm/Objects/Client/Geo.php new file mode 100644 index 0000000..8e4291c --- /dev/null +++ b/src/khm/Objects/Client/Geo.php @@ -0,0 +1,275 @@ + $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()); + } + } \ No newline at end of file diff --git a/src/khm/Objects/Client/OnionRelay.php b/src/khm/Objects/Client/OnionRelay.php new file mode 100644 index 0000000..73b7978 --- /dev/null +++ b/src/khm/Objects/Client/OnionRelay.php @@ -0,0 +1,317 @@ + $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()); + } + } \ No newline at end of file diff --git a/src/khm/Objects/Device.php b/src/khm/Objects/Device.php index da746e6..6942e0c 100644 --- a/src/khm/Objects/Device.php +++ b/src/khm/Objects/Device.php @@ -109,6 +109,8 @@ public function __construct() { $this->Properties = new Properties(); + $this->MobileDevice = false; + $this->MobileBrowser = false; } /** diff --git a/src/khm/Objects/GeoLookup.php b/src/khm/Objects/GeoLookup.php index 8e3122f..2e7cda2 100644 --- a/src/khm/Objects/GeoLookup.php +++ b/src/khm/Objects/GeoLookup.php @@ -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; } } \ No newline at end of file diff --git a/src/khm/Objects/OnionRelay.php b/src/khm/Objects/OnionRelay.php index 9da56d8..603bd45 100644 --- a/src/khm/Objects/OnionRelay.php +++ b/src/khm/Objects/OnionRelay.php @@ -323,6 +323,9 @@ if(isset($data['created_timestamp'])) $OnionRelayObject->CreatedTimestmap = (int)$data['created_timestamp']; + if($OnionRelayObject->DirAddress == null) + $OnionRelayObject->DirPort = null; + return $OnionRelayObject; } } \ No newline at end of file diff --git a/src/khm/Objects/QueryResults.php b/src/khm/Objects/QueryResults.php deleted file mode 100644 index 0796181..0000000 --- a/src/khm/Objects/QueryResults.php +++ /dev/null @@ -1,50 +0,0 @@ -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; } diff --git a/src/khm/khm.php b/src/khm/khm.php index c2a7f99..988c58d 100644 --- a/src/khm/khm.php +++ b/src/khm/khm.php @@ -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; + } } \ No newline at end of file diff --git a/src/khm/package.json b/src/khm/package.json index a910286..9a94c5d 100644 --- a/src/khm/package.json +++ b/src/khm/package.json @@ -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" diff --git a/tests/web_test.php b/tests/web_test.php new file mode 100644 index 0000000..7380b20 --- /dev/null +++ b/tests/web_test.php @@ -0,0 +1,9 @@ + + +
identify(), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); ?>
\ No newline at end of file