From 2ad17a493474a539b581be8d2e2b480624400b19 Mon Sep 17 00:00:00 2001 From: IP2Location Date: Thu, 2 Sep 2021 09:42:41 +0800 Subject: [PATCH] Added supports for provider field. --- LICENSE.TXT | 2 +- README.md | 3 +- composer.json | 2 +- controllers/IP2Proxy_test.php | 12 +- libraries/IP2Proxy_lib.php | 98 +-- .../{class.IP2Proxy.php => Database.php} | 768 +++++++----------- libraries/ip2proxy/WebService.php | 139 ++++ 7 files changed, 477 insertions(+), 547 deletions(-) rename libraries/ip2proxy/{class.IP2Proxy.php => Database.php} (79%) create mode 100644 libraries/ip2proxy/WebService.php diff --git a/LICENSE.TXT b/LICENSE.TXT index 1f446b9..a54dedb 100644 --- a/LICENSE.TXT +++ b/LICENSE.TXT @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 IP2Location.com +Copyright (c) 2021 IP2Location.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 0fb17f1..d47da9d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ CodeIgniter IP2Proxy Library =============================== -This module allows user to reverse search of IP address to detect VPN servers, open proxies, web proxies, Tor exit nodes, search engine robots, data center ranges and residential proxies. Other information available includes proxy type, country, state, city, ISP, domain name, usage type, AS number, AS name, threats and last seen date. +This module allows user to reverse search of IP address to detect VPN servers, open proxies, web proxies, Tor exit nodes, search engine robots, data center ranges and residential proxies. Other information available includes proxy type, country, state, city, ISP, domain name, usage type, AS number, AS name, threats, last seen date and provider names. ## Installation Upload `controllers` and `libraries` to CodeIgniter `application` folder. @@ -33,6 +33,7 @@ Below are the methods supported for BIN data file lookup. $as = $ipx->getAS($ip); $lastSeen = $ipx->getLastSeen($ip); $threat = $ipx->getThreat($ip); + $provider = $ipx->getProvider($ip); $isProxy = $ipx->isProxy($ip); ### Web Service diff --git a/composer.json b/composer.json index 380b6ae..1d78c78 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "ip2location/codeigniter-ip2proxy", - "description": "IP2Proxy library for CodeIgniter. This module allows user to reverse search of IP address to detect VPN servers, open proxies, web proxies, Tor exit nodes, search engine robots, data center ranges and residential proxies using IP2Proxy BIN database. Other information available includes proxy type, country, state, city, ISP, domain name, usage type, AS number, AS name, threats and last seen date.", + "description": "IP2Proxy library for CodeIgniter. This module allows user to reverse search of IP address to detect VPN servers, open proxies, web proxies, Tor exit nodes, search engine robots, data center ranges and residential proxies using IP2Proxy BIN database. Other information available includes proxy type, country, state, city, ISP, domain name, usage type, AS number, AS name, threats, last seen date and provider names.", "keywords": ["ip2proxy-bin-database", "geolocation-information", "codeigniter-ip2proxy", "ip-geolocation", "geolocation", "ip-lookup", "ip-address-database", "ip-database","web service", "proxy"], "homepage": "https://www.ip2location.com", "license": "MIT", diff --git a/controllers/IP2Proxy_test.php b/controllers/IP2Proxy_test.php index 046105a..4945261 100644 --- a/controllers/IP2Proxy_test.php +++ b/controllers/IP2Proxy_test.php @@ -1,10 +1,12 @@ -load->library('ip2proxy_lib'); + } + public function index() { $ipx = new IP2Proxy_lib(); diff --git a/libraries/IP2Proxy_lib.php b/libraries/IP2Proxy_lib.php index ccf9d4f..645bc83 100644 --- a/libraries/IP2Proxy_lib.php +++ b/libraries/IP2Proxy_lib.php @@ -1,4 +1,5 @@ -open(IP2PROXY_DATABASE, \IP2Proxy\Database::FILE_IO); - $countryShort = self::$ip2proxy->getCountryShort(self::getIP($ip)); - self::$ip2proxy->close(); - return $countryShort; + return self::$ip2proxy->lookup(self::getIP($ip), \IP2Proxy\Database::COUNTRY_CODE); } public function getCountryLong($ip=NULL) { - self::$ip2proxy = new \IP2Proxy\Database(); - self::$ip2proxy->open(IP2PROXY_DATABASE, \IP2Proxy\Database::FILE_IO); - $countryLong = self::$ip2proxy->getCountryLong(self::getIP($ip)); - self::$ip2proxy->close(); - return $countryLong; + return self::$ip2proxy->lookup(self::getIP($ip), \IP2Proxy\Database::COUNTRY_NAME); } public function getRegion($ip=NULL) { - self::$ip2proxy = new \IP2Proxy\Database(); - self::$ip2proxy->open(IP2PROXY_DATABASE, \IP2Proxy\Database::FILE_IO); - $region = self::$ip2proxy->getRegion(self::getIP($ip)); - self::$ip2proxy->close(); - return $region; + return self::$ip2proxy->lookup(self::getIP($ip), \IP2Proxy\Database::REGION_NAME); } public function getCity($ip=NULL) { - self::$ip2proxy = new \IP2Proxy\Database(); - self::$ip2proxy->open(IP2PROXY_DATABASE, \IP2Proxy\Database::FILE_IO); - $city = self::$ip2proxy->getCity(self::getIP($ip)); - self::$ip2proxy->close(); - return $city; + return self::$ip2proxy->lookup(self::getIP($ip), \IP2Proxy\Database::CITY_NAME); } public function getISP($ip=NULL) { - self::$ip2proxy = new \IP2Proxy\Database(); - self::$ip2proxy->open(IP2PROXY_DATABASE, \IP2Proxy\Database::FILE_IO); - $isp = self::$ip2proxy->getISP(self::getIP($ip)); - self::$ip2proxy->close(); - return $isp; + return self::$ip2proxy->lookup(self::getIP($ip), \IP2Proxy\Database::ISP); } public function getDomain($ip=NULL) { - self::$ip2proxy = new \IP2Proxy\Database(); - self::$ip2proxy->open(IP2PROXY_DATABASE, \IP2Proxy\Database::FILE_IO); - $domain = self::$ip2proxy->getDomain(self::getIP($ip)); - self::$ip2proxy->close(); - return $domain; + return self::$ip2proxy->lookup(self::getIP($ip), \IP2Proxy\Database::DOMAIN_NAME); } public function getUsageType($ip=NULL) { - self::$ip2proxy = new \IP2Proxy\Database(); - self::$ip2proxy->open(IP2PROXY_DATABASE, \IP2Proxy\Database::FILE_IO); - $usageType = self::$ip2proxy->getUsageType(self::getIP($ip)); - self::$ip2proxy->close(); - return $usageType; + return self::$ip2proxy->lookup(self::getIP($ip), \IP2Proxy\Database::USAGE_TYPE); } public function getProxyType($ip=NULL) { - self::$ip2proxy = new \IP2Proxy\Database(); - self::$ip2proxy->open(IP2PROXY_DATABASE, \IP2Proxy\Database::FILE_IO); - $proxyType = self::$ip2proxy->getProxyType(self::getIP($ip)); - self::$ip2proxy->close(); - return $proxyType; + return self::$ip2proxy->lookup(self::getIP($ip), \IP2Proxy\Database::PROXY_NAME); } public function getASN($ip=NULL) { - self::$ip2proxy = new \IP2Proxy\Database(); - self::$ip2proxy->open(IP2PROXY_DATABASE, \IP2Proxy\Database::FILE_IO); - $asn = self::$ip2proxy->getASN(self::getIP($ip)); - self::$ip2proxy->close(); - return $asn; + return self::$ip2proxy->lookup(self::getIP($ip), \IP2Proxy\Database::ASN); } public function getAS($ip=NULL) { - self::$ip2proxy = new \IP2Proxy\Database(); - self::$ip2proxy->open(IP2PROXY_DATABASE, \IP2Proxy\Database::FILE_IO); - $as = self::$ip2proxy->getAS(self::getIP($ip)); - self::$ip2proxy->close(); - return $as; + return self::$ip2proxy->lookup(self::getIP($ip), \IP2Proxy\Database::_AS); } public function getLastSeen($ip=NULL) { - self::$ip2proxy = new \IP2Proxy\Database(); - self::$ip2proxy->open(IP2PROXY_DATABASE, \IP2Proxy\Database::FILE_IO); - $lastSeen = self::$ip2proxy->getLastSeen(self::getIP($ip)); - self::$ip2proxy->close(); - return $lastSeen; + return self::$ip2proxy->lookup(self::getIP($ip), \IP2Proxy\Database::LAST_SEEN); } public function getThreat($ip=NULL) { - self::$ip2proxy = new \IP2Proxy\Database(); - self::$ip2proxy->open(IP2PROXY_DATABASE, \IP2Proxy\Database::FILE_IO); - $threat = self::$ip2proxy->getThreat(self::getIP($ip)); - self::$ip2proxy->close(); - return $threat; + return self::$ip2proxy->lookup(self::getIP($ip), \IP2Proxy\Database::THREAT); + } + + public function getProvider($ip=NULL) { + return self::$ip2proxy->lookup(self::getIP($ip), \IP2Proxy\Database::PROVIDER); } public function isProxy($ip=NULL) { - self::$ip2proxy = new \IP2Proxy\Database(); - self::$ip2proxy->open(IP2PROXY_DATABASE, \IP2Proxy\Database::FILE_IO); - $isProxy = self::$ip2proxy->isProxy(self::getIP($ip)); - self::$ip2proxy->close(); - return $isProxy; + return self::$ip2proxy->lookup(self::getIP($ip), \IP2Proxy\Database::IS_PROXY); } public function getAll($ip=NULL) { - self::$ip2proxy = new \IP2Proxy\Database(); - self::$ip2proxy->open(IP2PROXY_DATABASE, \IP2Proxy\Database::FILE_IO); - $all = self::$ip2proxy->getAll(self::getIP($ip)); - self::$ip2proxy->close(); - return $all; + return self::$ip2proxy->lookup(self::getIP($ip), \IP2Proxy\Database::ALL); } public function getWebService($ip=NULL) { diff --git a/libraries/ip2proxy/class.IP2Proxy.php b/libraries/ip2proxy/Database.php similarity index 79% rename from libraries/ip2proxy/class.IP2Proxy.php rename to libraries/ip2proxy/Database.php index edc8f1e..2109dac 100644 --- a/libraries/ip2proxy/class.IP2Proxy.php +++ b/libraries/ip2proxy/Database.php @@ -1,24 +1,5 @@ . - * - */ - namespace IP2Proxy; /** @@ -31,7 +12,7 @@ class Database * * @var string */ - public const VERSION = '3.0.0'; + private const VERSION = '4.1.0'; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Error field constants /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -42,7 +23,7 @@ class Database * * @var string */ - public const FIELD_NOT_SUPPORTED = 'NOT SUPPORTED'; + public const FIELD_NOT_SUPPORTED = 'This parameter is unavailable in selected .BIN data file. Please upgrade data file.'; /** * Unknown field message. @@ -62,6 +43,20 @@ class Database // Field selection constants /////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Maximum IPv4 number. + * + * @var int + */ + public const MAX_IPV4_RANGE = 4294967295; + + /** + * MAximum IPv6 number. + * + * @var int + */ + public const MAX_IPV6_RANGE = 340282366920938463463374607431768211455; + /** * Country code (ISO 3166-1 Alpha 2). * @@ -153,6 +148,13 @@ class Database */ public const THREAT = 13; + /** + * Provider. + * + * @var int + */ + public const PROVIDER = 14; + /** * Country name and code. * @@ -172,21 +174,21 @@ class Database * * @var int */ - public const IP_ADDRESS = 1002; + private const IP_ADDRESS = 1002; /** * Include the IP version of the looked up IP address. * * @var int */ - public const IP_VERSION = 1003; + private const IP_VERSION = 1003; /** * Include the IP number of the looked up IP address. * * @var int */ - public const IP_NUMBER = 1004; + private const IP_NUMBER = 1004; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Exception code constants //////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -197,77 +199,84 @@ class Database * * @var int */ - public const EXCEPTION = 10000; + private const EXCEPTION = 10000; /** * No shmop extension found. * * @var int */ - public const EXCEPTION_NO_SHMOP = 10001; + private const EXCEPTION_NO_SHMOP = 10001; /** * Failed to open shmop memory segment for reading. * * @var int */ - public const EXCEPTION_SHMOP_READING_FAILED = 10002; + private const EXCEPTION_SHMOP_READING_FAILED = 10002; /** * Failed to open shmop memory segment for writing. * * @var int */ - public const EXCEPTION_SHMOP_WRITING_FAILED = 10003; + private const EXCEPTION_SHMOP_WRITING_FAILED = 10003; /** * Failed to create shmop memory segment. * * @var int */ - public const EXCEPTION_SHMOP_CREATE_FAILED = 10004; + private const EXCEPTION_SHMOP_CREATE_FAILED = 10004; /** * The specified database file was not found. * * @var int */ - public const EXCEPTION_DBFILE_NOT_FOUND = 10005; + private const EXCEPTION_DBFILE_NOT_FOUND = 10005; /** * Not enough memory to load database file. * * @var int */ - public const EXCEPTION_NO_MEMORY = 10006; + private const EXCEPTION_NO_MEMORY = 10006; /** * No candidate databse files found. * * @var int */ - public const EXCEPTION_NO_CANDIDATES = 10007; + private const EXCEPTION_NO_CANDIDATES = 10007; /** * Failed to open database file. * * @var int */ - public const EXCEPTION_FILE_OPEN_FAILED = 10008; + private const EXCEPTION_FILE_OPEN_FAILED = 10008; /** * Failed to determine the current path. * * @var int */ - public const EXCEPTION_NO_PATH = 10009; + private const EXCEPTION_NO_PATH = 10009; + + /** + * Invalid BIN database file. + * + * @var int + */ + public const EXCEPTION_INVALID_BIN_DATABASE = 10010; /** * BCMath extension not installed. * * @var int */ - public const EXCEPTION_BCMATH_NOT_INSTALLED = 10010; + private const EXCEPTION_BCMATH_NOT_INSTALLED = 10010; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Caching method constants //////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -303,14 +312,14 @@ class Database * * @var int */ - public const SHM_PERMS = 0600; + private const SHM_PERMS = 0600; /** * Number of bytes to read/write at a time in order to load the shared memory cache (512k). * * @var int */ - public const SHM_CHUNK_SIZE = 524288; + private const SHM_CHUNK_SIZE = 524288; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Static data ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -322,33 +331,30 @@ class Database * Each entry contains an array mapping databse version (0-3) to offset within a record. * A value of 0 means the column is not present in the given database version. * - * @static - * * @var array */ - private static $columns = [ - self::COUNTRY_CODE => [8, 12, 12, 12, 12, 12, 12, 12, 12, 12], - self::COUNTRY_NAME => [8, 12, 12, 12, 12, 12, 12, 12, 12, 12], - self::REGION_NAME => [0, 0, 16, 16, 16, 16, 16, 16, 16, 16], - self::CITY_NAME => [0, 0, 20, 20, 20, 20, 20, 20, 20, 20], - self::ISP => [0, 0, 0, 24, 24, 24, 24, 24, 24, 24], - self::PROXY_TYPE => [0, 8, 8, 8, 8, 8, 8, 8, 8, 8], - self::DOMAIN => [0, 0, 0, 0, 28, 28, 28, 28, 28, 28], - self::USAGE_TYPE => [0, 0, 0, 0, 0, 32, 32, 32, 32, 32], - self::ASN => [0, 0, 0, 0, 0, 0, 36, 36, 36, 36], - self::_AS => [0, 0, 0, 0, 0, 0, 40, 40, 40, 40], - self::LAST_SEEN => [0, 0, 0, 0, 0, 0, 0, 44, 44, 44], - self::THREAT => [0, 0, 0, 0, 0, 0, 0, 0, 48, 48], + private $columns = [ + self::COUNTRY_CODE => [8, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12], + self::COUNTRY_NAME => [8, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12], + self::REGION_NAME => [0, 0, 16, 16, 16, 16, 16, 16, 16, 16, 16], + self::CITY_NAME => [0, 0, 20, 20, 20, 20, 20, 20, 20, 20, 20], + self::ISP => [0, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24], + self::PROXY_TYPE => [0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8], + self::DOMAIN => [0, 0, 0, 0, 28, 28, 28, 28, 28, 28, 28], + self::USAGE_TYPE => [0, 0, 0, 0, 0, 32, 32, 32, 32, 32, 32], + self::ASN => [0, 0, 0, 0, 0, 0, 36, 36, 36, 36, 36], + self::_AS => [0, 0, 0, 0, 0, 0, 40, 40, 40, 40, 40], + self::LAST_SEEN => [0, 0, 0, 0, 0, 0, 0, 44, 44, 44, 44], + self::THREAT => [0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48], + self::PROVIDER => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52], ]; /** * Column name mapping. * - * @static - * * @var array */ - private static $names = [ + private $names = [ self::COUNTRY_CODE => 'countryCode', self::COUNTRY_NAME => 'countryName', self::REGION_NAME => 'regionName', @@ -362,18 +368,20 @@ class Database self::_AS => 'as', self::LAST_SEEN => 'lastSeen', self::THREAT => 'threat', + self::PROVIDER => 'provider', self::IP_ADDRESS => 'ipAddress', self::IP_VERSION => 'ipVersion', self::IP_NUMBER => 'ipNumber', - ]; + ]; /** * Database names, in order of preference for file lookup. * * @var array */ - private static $databases = [ + private $databases = [ // IPv4 databases + 'IP2PROXY-IP-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN-THREAT-RESIDENTIAL-PROVIDER', 'IP2PROXY-IP-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN-THREAT-RESIDENTIAL', 'IP2PROXY-IP-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN-THREAT', 'IP2PROXY-IP-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN', @@ -386,6 +394,7 @@ class Database 'IP2PROXY-IP-COUNTRY', // IPv6 databases + 'IPV6-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN-THREAT-RESIDENTIAL-PROVIDER', 'IPV6-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN-THREAT-RESIDENTIAL', 'IPV6-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN-THREAT', 'IPV6-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN', @@ -396,34 +405,28 @@ class Database 'IPV6-PROXYTYPE-COUNTRY-REGION-CITY', 'IPV6-PROXYTYPE-COUNTRY', 'IPV6-COUNTRY', - ]; + ]; /** * Static memory buffer to use for MEMORY_CACHE mode, the keys will be BIN filenames and the values their contents. * - * @static - * * @var array */ - private static $buffer = []; + private $buffer = []; /** * The machine's float size. * - * @static - * * @var int */ - private static $floatSize = null; + private $floatSize = null; /** * The configured memory limit. * - * @static - * * @var int */ - private static $memoryLimit = null; + private $memoryLimit = null; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Caching backend controls //////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -495,19 +498,29 @@ class Database private $month; private $day; - // This variable will be used to hold the raw row of columns's positions - private $rawPositionsRow; + /** + * Product code. + * + * @var string + */ + private $productCode; - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Default fields ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * License code. + * + * @var string + */ + private $licenseCode; /** - * Default fields to return during lookup. + * Database size. * - * @var array|int + * @var int */ - private $defaultFields = self::ALL; + private $databaseSize; + + // This variable will be used to hold the raw row of columns's positions + private $rawPositionsRow; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Administrative public interface ///////////////////////////////////////////////////////////////////////////////////////////////////// @@ -515,26 +528,13 @@ class Database /** * Constructor. - */ - public function __construct() - { - } - - /** - * Destructor. - */ - public function __destruct() - { - } - - /** - * @param string $file Filename of the BIN database to load - * @param int $mode Caching mode (one of FILE_IO, MEMORY_CACHE, or SHARED_MEMORY) - * @param mixed $defaultFields + * + * @param string $file Filename of the BIN database to load + * @param int $mode Caching mode (one of FILE_IO, MEMORY_CACHE, or SHARED_MEMORY) * * @throws \Exception */ - public function open($file = null, $mode = self::FILE_IO, $defaultFields = self::ALL) + public function __construct($file = null, $mode = self::FILE_IO) { if (!\function_exists('bcadd')) { throw new \Exception(__CLASS__ . ': BCMath extension is not installed.', self::EXCEPTION_BCMATH_NOT_INSTALLED); @@ -604,14 +604,14 @@ public function open($file = null, $mode = self::FILE_IO, $defaultFields = self: case self::MEMORY_CACHE: $this->mode = self::MEMORY_CACHE; $this->resource = $rfile; - if (!\array_key_exists($rfile, self::$buffer)) { + if (!\array_key_exists($rfile, $this->buffer)) { $limit = self::getMemoryLimit(); if ($limit !== false && $size > $limit) { throw new \Exception(__CLASS__ . ": Insufficient memory to load file '{$rfile}'.", self::EXCEPTION_NO_MEMORY); } - self::$buffer[$rfile] = @file_get_contents($rfile); - if (self::$buffer[$rfile] === false) { + $this->buffer[$rfile] = @file_get_contents($rfile); + if ($this->buffer[$rfile] === false) { throw new \Exception(__CLASS__ . ": Unable to open file '{$rfile}'.", self::EXCEPTION_FILE_OPEN_FAILED); } } @@ -626,13 +626,10 @@ public function open($file = null, $mode = self::FILE_IO, $defaultFields = self: // should be used to accomodate different float sizes, but, as the libreary // is written, this is the sanest thing to do anyway // - if (self::$floatSize === null) { - self::$floatSize = \strlen(pack('f', M_PI)); + if ($this->floatSize === null) { + $this->floatSize = \strlen(pack('f', M_PI)); } - // set default fields to retrieve - $this->defaultFields = $defaultFields; - // extract database metadata $this->type = $this->readByte(1) - 1; $this->columnWidth[4] = $this->readByte(2) * 4; @@ -643,12 +640,31 @@ public function open($file = null, $mode = self::FILE_IO, $defaultFields = self: $this->month = $this->readByte(4); $this->day = $this->readByte(5); $this->date = date('Y-m-d', strtotime("{$this->year}-{$this->month}-{$this->day}")); + $this->productCode = $this->readByte(30); + $this->licenseCode = $this->readByte(31); + $this->databaseSize = $this->readByte(32); $this->ipCount[4] = $this->readWord(6); $this->ipBase[4] = $this->readWord(10); $this->ipCount[6] = $this->readWord(14); $this->ipBase[6] = $this->readWord(18); $this->indexBaseAddr[4] = $this->readWord(22); $this->indexBaseAddr[6] = $this->readWord(26); + + if ($this->productCode == 2) { + } else { + if ($this->year <= 20 && $this->productCode == 0) { + } else { + throw new \Exception(__CLASS__ . ': Incorrect IP2Proxy BIN file format. Please make sure that you are using the latest IP2Proxy BIN file.', self::EXCEPTION_INVALID_BIN_DATABASE); + } + } + } + + /** + * Destructor. + */ + public function __destruct() + { + $this->close(); } /** @@ -657,21 +673,21 @@ public function open($file = null, $mode = self::FILE_IO, $defaultFields = self: public function close() { switch ($this->mode) { - case self::FILE_IO: - // free the file pointer - if ($this->resource !== false) { - fclose($this->resource); - $this->resource = false; - } - break; - case self::SHARED_MEMORY: - // detach from the memory segment - if ($this->resource !== false) { - shmop_close($this->resource); - $this->resource = false; + case self::FILE_IO: + // free the file pointer + if ($this->resource !== false) { + fclose($this->resource); + $this->resource = false; + } + break; + case self::SHARED_MEMORY: + // detach from the memory segment + if ($this->resource !== false) { + shmop_close($this->resource); + $this->resource = false; + } + break; } - break; - } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -704,154 +720,6 @@ public function getDatabaseVersion() return $this->year . '.' . $this->month . '.' . $this->day; } - /** - * Return -1, 0, 1, 2. - * - * @param mixed $ip - */ - public function isProxy($ip) - { - return self::lookup($ip, self::IS_PROXY); - } - - // Return string - public function getCountryShort($ip) - { - return self::lookup($ip, self::COUNTRY_CODE); - } - - // Return string - public function getCountryLong($ip) - { - return self::lookup($ip, self::COUNTRY_NAME); - } - - // Return string - public function getRegion($ip) - { - return self::lookup($ip, self::REGION_NAME); - } - - // Return string - public function getCity($ip) - { - return self::lookup($ip, self::CITY_NAME); - } - - // Return string - public function getISP($ip) - { - return self::lookup($ip, self::ISP); - } - - // Return string - public function getProxyType($ip) - { - return self::lookup($ip, self::PROXY_TYPE); - } - - // Return string - public function getDomain($ip) - { - return self::lookup($ip, self::DOMAIN); - } - - // Return string - public function getUsageType($ip) - { - return self::lookup($ip, self::USAGE_TYPE); - } - - // Return string - public function getASN($ip) - { - return self::lookup($ip, self::ASN); - } - - // Return string - public function getAS($ip) - { - return self::lookup($ip, self::_AS); - } - - // Return string - public function getLastSeen($ip) - { - return self::lookup($ip, self::LAST_SEEN); - } - - // Return string - public function getThreat($ip) - { - return self::lookup($ip, self::THREAT); - } - - // Return array - public function getAll($ip) - { - return self::lookup($ip, self::ALL); - } - - /** - * Tear down a shared memory segment created for the given file. - * - * @param string $file Filename of the BIN database whise segment must be deleted - * - * @throws \Exception - */ - protected function shmTeardown($file) - { - // verify the shmop extension is loaded - if (!\extension_loaded('shmop')) { - throw new \Exception(__CLASS__ . ": Please make sure your PHP setup has the 'shmop' extension enabled.", self::EXCEPTION_NO_SHMOP); - } - - // Get actual file path - $rfile = realpath($file); - - // If the file cannot be found, except away - if ($rfile === false) { - throw new \Exception(__CLASS__ . ": Database file '{$file}' does not seem to exist.", self::EXCEPTION_DBFILE_NOT_FOUND); - } - - $shmKey = self::getShmKey($rfile); - - // Try to open the memory segment for writing - $shmId = @shmop_open($shmKey, 'w', 0, 0); - if ($shmId === false) { - throw new \Exception(__CLASS__ . ": Unable to access shared memory block '{$shmKey}' for writing.", self::EXCEPTION_SHMOP_WRITING_FAILED); - } - - // Delete and close the descriptor - shmop_delete($shmId); - shmop_close($shmId); - } - - /** - * Return this database's available fields. - * - * @param bool $asNames Whether to return the mapped names intead of numbered constants - * - * @return array - */ - protected function getFields($asNames = false) - { - $result = array_keys(array_filter(self::$columns, function ($field) { - return $field[$this->type] !== 0; - })); - - if ($asNames) { - $return = []; - foreach ($result as $field) { - $return[] = self::$names[$field]; - } - - return $return; - } - - return $result; - } - /** * This function will look the given IP address up in the database and return the result(s) asked for. * @@ -866,17 +734,17 @@ protected function getFields($asNames = false) * * @return array|bool|mixed */ - protected function lookup($ip, $fields = null, $asNamed = true) + public function lookup($ip, $fields = null, $asNamed = true) { // Extract IP version and number - list($ipVersion, $ipNumber) = self::ipVersionAndNumber($ip); + list($ipVersion, $ipNumber) = $this->ipVersionAndNumber($ip); // Perform the binary search proper (if the IP address was invalid, binSearch will return false) $pointer = $this->binSearch($ipVersion, $ipNumber); // Apply defaults if needed if ($fields === null) { - $fields = $this->defaultFields; + $fields = self::ALL; } // Get the entire row based on the pointer value. @@ -901,6 +769,7 @@ protected function lookup($ip, $fields = null, $asNamed = true) $ifields[] = self::_AS; $ifields[] = self::LAST_SEEN; $ifields[] = self::THREAT; + $ifields[] = self::PROVIDER; $ifields[] = self::COUNTRY; $ifields[] = self::IP_ADDRESS; $ifields[] = self::IP_VERSION; @@ -924,6 +793,7 @@ protected function lookup($ip, $fields = null, $asNamed = true) self::_AS => false, self::LAST_SEEN => false, self::THREAT => false, + self::PROVIDER => false, self::COUNTRY => false, self::IP_ADDRESS => false, self::IP_VERSION => false, @@ -1055,6 +925,13 @@ protected function lookup($ip, $fields = null, $asNamed = true) } break; + case self::PROVIDER: + if (!$done[self::PROVIDER]) { + $results[self::PROVIDER] = $this->readProvider($pointer); + $done[self::PROVIDER] = true; + } + break; + case self::IP_ADDRESS: if (!$done[self::IP_ADDRESS]) { $results[self::IP_ADDRESS] = $ip; @@ -1086,8 +963,8 @@ protected function lookup($ip, $fields = null, $asNamed = true) if ($asNamed) { $return = []; foreach ($results as $key => $val) { - if (\array_key_exists($key, static::$names)) { - $return[static::$names[$key]] = $val; + if (\array_key_exists($key, $this->names)) { + $return[$this->names[$key]] = $val; } else { $return[$key] = $val; } @@ -1102,6 +979,66 @@ protected function lookup($ip, $fields = null, $asNamed = true) return array_values($results)[0]; } + /** + * Tear down a shared memory segment created for the given file. + * + * @param string $file Filename of the BIN database whise segment must be deleted + * + * @throws \Exception + */ + protected function shmTeardown($file) + { + // verify the shmop extension is loaded + if (!\extension_loaded('shmop')) { + throw new \Exception(__CLASS__ . ": Please make sure your PHP setup has the 'shmop' extension enabled.", self::EXCEPTION_NO_SHMOP); + } + + // Get actual file path + $rfile = realpath($file); + + // If the file cannot be found, except away + if ($rfile === false) { + throw new \Exception(__CLASS__ . ": Database file '{$file}' does not seem to exist.", self::EXCEPTION_DBFILE_NOT_FOUND); + } + + $shmKey = self::getShmKey($rfile); + + // Try to open the memory segment for writing + $shmId = @shmop_open($shmKey, 'w', 0, 0); + if ($shmId === false) { + throw new \Exception(__CLASS__ . ": Unable to access shared memory block '{$shmKey}' for writing.", self::EXCEPTION_SHMOP_WRITING_FAILED); + } + + // Delete and close the descriptor + shmop_delete($shmId); + shmop_close($shmId); + } + + /** + * Return this database's available fields. + * + * @param bool $asNames Whether to return the mapped names intead of numbered constants + * + * @return array + */ + protected function getFields($asNames = false) + { + $result = array_keys(array_filter($this->columns, function ($field) { + return $field[$this->type] !== 0; + })); + + if ($asNames) { + $return = []; + foreach ($result as $field) { + $return[] = self::$names[$field]; + } + + return $return; + } + + return $result; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Static tools //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1109,14 +1046,12 @@ protected function lookup($ip, $fields = null, $asNamed = true) /** * Get memory limit from the current PHP settings (return false if no memory limit set). * - * @static - * * @return bool|int */ - private static function getMemoryLimit() + private function getMemoryLimit() { // Get values if no cache - if (self::$memoryLimit === null) { + if ($this->memoryLimit === null) { $limit = ini_get('memory_limit'); // Feal with defaults @@ -1139,10 +1074,10 @@ private static function getMemoryLimit() case 'K': $value *= 1024; } } - self::$memoryLimit = $value; + $this->memoryLimit = $value; } - return self::$memoryLimit; + return $this->memoryLimit; } /** @@ -1154,7 +1089,7 @@ private static function getMemoryLimit() * * @return string */ - private static function findFile($file = null) + private function findFile($file = null) { if ($file !== null) { // Get actual file path @@ -1174,7 +1109,7 @@ private static function findFile($file = null) throw new \Exception(__CLASS__ . ': Cannot determine current path.', self::EXCEPTION_NO_PATH); } // Try each database in turn - foreach (self::$databases as $database) { + foreach ($this->databases as $database) { $rfile = realpath("{$current}/{$database}.BIN"); if ($rfile !== false) { return $rfile; @@ -1187,13 +1122,11 @@ private static function findFile($file = null) /** * Make the given number positive by wrapping it to 8 bit values. * - * @static - * * @param int $x Number to wrap * * @return int */ - private static function wrap8($x) + private function wrap8($x) { return $x + ($x < 0 ? 256 : 0); } @@ -1201,13 +1134,11 @@ private static function wrap8($x) /** * Make the given number positive by wrapping it to 32 bit values. * - * @static - * * @param int $x Number to wrap * * @return int */ - private static function wrap32($x) + private function wrap32($x) { return $x + ($x < 0 ? 4294967296 : 0); } @@ -1215,13 +1146,11 @@ private static function wrap32($x) /** * Generate a unique and repeatable shared memory key for each instance to use. * - * @static - * * @param string $filename Filename of the BIN file * * @return int */ - private static function getShmKey($filename) + private function getShmKey($filename) { // This will create a shared memory key that deterministically depends only on // the current file's path and the BIN file's path @@ -1234,8 +1163,6 @@ private static function getShmKey($filename) * This function will return 0 if the given ip number falls within the given bounds * for the given version, -1 if it falls below, and 1 if it falls above. * - * @static - * * @param int $version IP version to use (either 4 or 6) * @param int|string $ip IP number to check (int for IPv4, string for IPv6) * @param int|string $low Lower bound (int for IPv4, string for IPv6) @@ -1243,7 +1170,7 @@ private static function getShmKey($filename) * * @return int */ - private static function ipBetween($version, $ip, $low, $high) + private function ipBetween($version, $ip, $low, $high) { if ($version === 4) { // Use normal PHP compares @@ -1279,8 +1206,6 @@ private static function ipBetween($version, $ip, $low, $high) * - second: the IP address' number if its version is 4, the number string if * its version is 6, false otherwise. * - * @static - * * @param string $ip IP address to extract the version and number for * * @return array @@ -1288,50 +1213,56 @@ private static function ipBetween($version, $ip, $low, $high) private static function ipVersionAndNumber($ip) { if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { - return [4, sprintf('%u', ip2long($ip))]; + $number = sprintf('%u', ip2long($ip)); + + return [4, ($number == self::MAX_IPV4_RANGE) ? ($number - 1) : $number]; } elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { - // Expand IPv6 address - $ip = implode(':', str_split(unpack('H*0', inet_pton($ip))[0], 4)); + $result = 0; + $ip = self::expand($ip); // 6to4 Address - 2002::/16 if (substr($ip, 0, 4) == '2002') { - return [4, (int) (gmp_import(inet_pton($ip)) >> 80) & 4294967295]; + foreach (str_split(bin2hex(inet_pton($ip)), 8) as $word) { + $result = bcadd(bcmul($result, '4294967296', 0), self::wrap32(hexdec($word)), 0); + } + + return [4, bcmod(bcdiv($result, bcpow(2, 80)), '4294967296')]; } // Teredo Address - 2001:0::/32 if (substr($ip, 0, 9) == '2001:0000') { - return [4, (~hexdec(str_replace(':', '', substr($ip, -9))) & 4294967295)]; + return [4, hexdec(substr($ip, 10, 4) . substr($ip, 15, 4))]; } - // IPv4 Address - if (substr($ip, 0, 9) == '0000:0000') { - return [4, hexdec(substr($ip, -9))]; - } - - // Common IPv6 Address - $result = 0; - foreach (str_split(bin2hex(inet_pton($ip)), 8) as $word) { $result = bcadd(bcmul($result, '4294967296', 0), self::wrap32(hexdec($word)), 0); } + // IPv4 address in IPv6 + if (bccomp($result, '281470681743360') >= 0 && bccomp($result, '281474976710655') <= 0) { + return [4, bcsub($result, '281470681743360')]; + } + return [6, $result]; } - // Invalid IP address, return falses + + // Invalid IP address, return false return [false, false]; } /** * Return the decimal string representing the binary data given. * - * @static - * * @param string $data Binary data to parse * * @return string */ - private static function bcBin2Dec($data) + private function bcBin2Dec($data) { + if (!$data) { + return; + } + $parts = [ unpack('V', substr($data, 12, 4)), unpack('V', substr($data, 8, 4)), @@ -1350,6 +1281,22 @@ private static function bcBin2Dec($data) return $result; } + /** + * Return the decimal string representing the binary data given. + * + * @static + * + * @param mixed $ipv6 + * + * @return string + */ + private static function expand($ipv6) + { + $hex = unpack('H*hex', inet_pton($ipv6)); + + return substr(preg_replace('/([A-f0-9]{4})/', '$1:', $hex['hex']), 0, -1); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Caching backend abstraction ///////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1369,7 +1316,7 @@ private function read($pos, $len) return shmop_read($this->resource, $pos, $len); case self::MEMORY_CACHE: - return $data = substr(self::$buffer[$this->resource], $pos, $len); + return $data = substr($this->buffer[$this->resource], $pos, $len); default: fseek($this->resource, $pos, SEEK_SET); @@ -1409,7 +1356,7 @@ private function readString($pos, $additional = 0) private function readFloat($pos) { // Unpack a float's size worth of data - return unpack('f', $this->read($pos - 1, self::$floatSize))[1]; + return unpack('f', $this->read($pos - 1, $this->floatSize))[1]; } /** @@ -1468,7 +1415,7 @@ private function readCountryNameAndCode($pointer) // Deal with invalid IPs $countryCode = self::INVALID_IP_ADDRESS; $countryName = self::INVALID_IP_ADDRESS; - } elseif (self::$columns[self::COUNTRY_CODE][$this->type] === 0) { + } elseif ($this->columns[self::COUNTRY_CODE][$this->type] === 0) { // If the field is not suported, return accordingly $countryCode = self::FIELD_NOT_SUPPORTED; $countryName = self::FIELD_NOT_SUPPORTED; @@ -1476,8 +1423,8 @@ private function readCountryNameAndCode($pointer) // Read the country code and name (the name shares the country's pointer, // but it must be artificially displaced 3 bytes ahead: 2 for the country code, one // for the country name's length) - $countryCode = $this->readString(self::$columns[self::COUNTRY_CODE][$this->type]); - $countryName = $this->readString(self::$columns[self::COUNTRY_NAME][$this->type], 3); + $countryCode = $this->readString($this->columns[self::COUNTRY_CODE][$this->type]); + $countryName = $this->readString($this->columns[self::COUNTRY_NAME][$this->type], 3); } return [$countryName, $countryCode]; @@ -1495,12 +1442,12 @@ private function readRegionName($pointer) if ($pointer === false) { // Deal with invalid IPs $regionName = self::INVALID_IP_ADDRESS; - } elseif (self::$columns[self::REGION_NAME][$this->type] === 0) { + } elseif ($this->columns[self::REGION_NAME][$this->type] === 0) { // If the field is not suported, return accordingly $regionName = self::FIELD_NOT_SUPPORTED; } else { // Read the region name - $regionName = $this->readString(self::$columns[self::REGION_NAME][$this->type]); + $regionName = $this->readString($this->columns[self::REGION_NAME][$this->type]); } return $regionName; @@ -1518,12 +1465,12 @@ private function readCityName($pointer) if ($pointer === false) { // Deal with invalid IPs $cityName = self::INVALID_IP_ADDRESS; - } elseif (self::$columns[self::CITY_NAME][$this->type] === 0) { + } elseif ($this->columns[self::CITY_NAME][$this->type] === 0) { // If the field is not suported, return accordingly $cityName = self::FIELD_NOT_SUPPORTED; } else { // Read the city name - $cityName = $this->readString(self::$columns[self::CITY_NAME][$this->type]); + $cityName = $this->readString($this->columns[self::CITY_NAME][$this->type]); } return $cityName; @@ -1541,12 +1488,12 @@ private function readIsp($pointer) if ($pointer === false) { // Deal with invalid IPs $isp = self::INVALID_IP_ADDRESS; - } elseif (self::$columns[self::ISP][$this->type] === 0) { + } elseif ($this->columns[self::ISP][$this->type] === 0) { // If the field is not suported, return accordingly $isp = self::FIELD_NOT_SUPPORTED; } else { // Read isp name - $isp = $this->readString(self::$columns[self::ISP][$this->type]); + $isp = $this->readString($this->columns[self::ISP][$this->type]); } return $isp; @@ -1564,12 +1511,12 @@ private function readProxyType($pointer) if ($pointer === false) { // Deal with invalid IPs $proxyType = self::INVALID_IP_ADDRESS; - } elseif (self::$columns[self::PROXY_TYPE][$this->type] === 0) { + } elseif ($this->columns[self::PROXY_TYPE][$this->type] === 0) { // If the field is not suported, return accordingly $proxyType = self::FIELD_NOT_SUPPORTED; } else { // Read proxy type - $proxyType = $this->readString(self::$columns[self::PROXY_TYPE][$this->type]); + $proxyType = $this->readString($this->columns[self::PROXY_TYPE][$this->type]); } return $proxyType; @@ -1587,12 +1534,12 @@ private function readDomain($pointer) if ($pointer === false) { // Deal with invalid IPs $domain = self::INVALID_IP_ADDRESS; - } elseif (self::$columns[self::DOMAIN][$this->type] === 0) { + } elseif ($this->columns[self::DOMAIN][$this->type] === 0) { // If the field is not suported, return accordingly $domain = self::FIELD_NOT_SUPPORTED; } else { // Read the domain - $domain = $this->readString(self::$columns[self::DOMAIN][$this->type]); + $domain = $this->readString($this->columns[self::DOMAIN][$this->type]); } return $domain; @@ -1610,12 +1557,12 @@ private function readUsageType($pointer) if ($pointer === false) { // Deal with invalid IPs $usageType = self::INVALID_IP_ADDRESS; - } elseif (self::$columns[self::USAGE_TYPE][$this->type] === 0) { + } elseif ($this->columns[self::USAGE_TYPE][$this->type] === 0) { // If the field is not suported, return accordingly $usageType = self::FIELD_NOT_SUPPORTED; } else { // Read the domain - $usageType = $this->readString(self::$columns[self::USAGE_TYPE][$this->type]); + $usageType = $this->readString($this->columns[self::USAGE_TYPE][$this->type]); } return $usageType; @@ -1633,12 +1580,12 @@ private function readAsn($pointer) if ($pointer === false) { // Deal with invalid IPs $asn = self::INVALID_IP_ADDRESS; - } elseif (self::$columns[self::ASN][$this->type] === 0) { + } elseif ($this->columns[self::ASN][$this->type] === 0) { // If the field is not suported, return accordingly $asn = self::FIELD_NOT_SUPPORTED; } else { // Read the domain - $asn = $this->readString(self::$columns[self::ASN][$this->type]); + $asn = $this->readString($this->columns[self::ASN][$this->type]); } return $asn; @@ -1656,12 +1603,12 @@ private function readAs($pointer) if ($pointer === false) { // Deal with invalid IPs $as = self::INVALID_IP_ADDRESS; - } elseif (self::$columns[self::_AS][$this->type] === 0) { + } elseif ($this->columns[self::_AS][$this->type] === 0) { // If the field is not suported, return accordingly $as = self::FIELD_NOT_SUPPORTED; } else { // Read the domain - $as = $this->readString(self::$columns[self::_AS][$this->type]); + $as = $this->readString($this->columns[self::_AS][$this->type]); } return $as; @@ -1679,12 +1626,12 @@ private function readLastSeen($pointer) if ($pointer === false) { // Deal with invalid IPs $lastSeen = self::INVALID_IP_ADDRESS; - } elseif (self::$columns[self::LAST_SEEN][$this->type] === 0) { + } elseif ($this->columns[self::LAST_SEEN][$this->type] === 0) { // If the field is not suported, return accordingly $lastSeen = self::FIELD_NOT_SUPPORTED; } else { // Read the domain - $lastSeen = $this->readString(self::$columns[self::LAST_SEEN][$this->type]); + $lastSeen = $this->readString($this->columns[self::LAST_SEEN][$this->type]); } return $lastSeen; @@ -1702,17 +1649,40 @@ private function readThreat($pointer) if ($pointer === false) { // Deal with invalid IPs $threat = self::INVALID_IP_ADDRESS; - } elseif (self::$columns[self::THREAT][$this->type] === 0) { + } elseif ($this->columns[self::THREAT][$this->type] === 0) { // If the field is not suported, return accordingly $threat = self::FIELD_NOT_SUPPORTED; } else { // Read the domain - $threat = $this->readString(self::$columns[self::THREAT][$this->type]); + $threat = $this->readString($this->columns[self::THREAT][$this->type]); } return $threat; } + /** + * High level function to fetch the Provider. + * + * @param int $pointer Position to read from, if false, return self::INVALID_IP_ADDRESS + * + * @return string + */ + private function readProvider($pointer) + { + if ($pointer === false) { + // Deal with invalid IPs + $provider = self::INVALID_IP_ADDRESS; + } elseif ($this->columns[self::PROVIDER][$this->type] === 0) { + // If the field is not suported, return accordingly + $provider = self::FIELD_NOT_SUPPORTED; + } else { + // Read the domain + $provider = $this->readString($this->columns[self::PROVIDER][$this->type]); + } + + return $provider; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Binary search and support functions ///////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1809,139 +1779,3 @@ private function binSearch($version, $ipNumber) return false; } } - -/** - * IP2Proxy web service class. - */ -class WebService -{ - /** - * No cURL extension found. - * - * @var int - */ - public const EXCEPTION_NO_CURL = 10001; - - /** - * Invalid API key format. - * - * @var int - */ - public const EXCEPTION_INVALID_API_KEY = 10002; - - /** - * Web service error. - * - * @var int - */ - public const EXCEPTION_WEB_SERVICE_ERROR = 10003; - - /** - * Constructor. - * - * @param string $apiKey API key of your IP2Proxy web service - * @param string $package Supported IP2Proxy package from PX1 to PX10 - * @param bool $useSsl Enable or disabled HTTPS connection. HTTP is faster but less secure. - * - * @throws \Exception - */ - public function __construct($apiKey, $package = 'PX1', $useSsl = false) - { - if (!\extension_loaded('curl')) { - throw new \Exception(__CLASS__ . ": Please make sure your PHP setup has the 'curl' extension enabled.", self::EXCEPTION_NO_CURL); - } - - if (!preg_match('/^[0-9A-Z]{10}$/', $apiKey) && $apiKey != 'demo') { - throw new \Exception(__CLASS__ . ': Please provide a valid IP2Proxy web service API key.', self::EXCEPTION_INVALID_API_KEY); - } - - if (!preg_match('/^PX[0-9]+$/', $package)) { - $package = 'PX1'; - } - - $this->apiKey = $apiKey; - $this->package = $package; - $this->useSsl = $useSsl; - } - - /** - * This function will look the given IP address up in IP2Proxy web service. - * - * @param string $ip IP address to look up - * - * @throws \Exception - * - * @return array|false - */ - public function lookup($ip) - { - $response = $this->httpRequest('http://api.ip2proxy.com/?' . http_build_query([ - 'key' => $this->apiKey, - 'ip' => $ip, - 'package' => $this->package, - ])); - - if (($data = json_decode($response, true)) === null) { - return false; - } - - if ($data['response'] != 'OK') { - throw new \Exception(__CLASS__ . ': ' . $data['response'], self::EXCEPTION_WEB_SERVICE_ERROR); - } - - return $data; - } - - /** - * Get the remaing credit in your IP2Proxy web service account. - * - * @return int - */ - public function getCredit() - { - $response = $this->httpRequest('http://api.ip2proxy.com/?' . http_build_query([ - 'key' => $this->apiKey, - 'check' => true, - ])); - - if (($data = json_decode($response, true)) === null) { - return 0; - } - - if (!isset($data['response'])) { - return 0; - } - - return $data['response']; - } - - /** - * Open a remote web address. - * - * @param string $url Website URL - * - * @return bool|string - */ - private function httpRequest($url) - { - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_FAILONERROR, 1); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); - curl_setopt($ch, CURLOPT_TIMEOUT, 30); - - $response = curl_exec($ch); - - if (!curl_errno($ch)) { - curl_close($ch); - - return $response; - } - - curl_close($ch); - - return false; - } -} diff --git a/libraries/ip2proxy/WebService.php b/libraries/ip2proxy/WebService.php new file mode 100644 index 0000000..987f2df --- /dev/null +++ b/libraries/ip2proxy/WebService.php @@ -0,0 +1,139 @@ +apiKey = $apiKey; + $this->package = $package; + $this->useSsl = $useSsl; + } + + /** + * This function will look the given IP address up in IP2Proxy web service. + * + * @param string $ip IP address to look up + * + * @throws \Exception + * + * @return array|false + */ + public function lookup($ip) + { + $response = $this->httpRequest('http' . (($this->useSsl) ? 's' : '') . '://api.ip2proxy.com/?' . http_build_query([ + 'key' => $this->apiKey, + 'ip' => $ip, + 'package' => $this->package, + ])); + + if (($data = json_decode($response, true)) === null) { + return false; + } + + if ($data['response'] != 'OK') { + throw new \Exception(__CLASS__ . ': ' . $data['response'], self::EXCEPTION_WEB_SERVICE_ERROR); + } + + return $data; + } + + /** + * Get the remaing credit in your IP2Proxy web service account. + * + * @return int + */ + public function getCredit() + { + $response = $this->httpRequest('http' . (($this->useSsl) ? 's' : '') . '://api.ip2proxy.com/?' . http_build_query([ + 'key' => $this->apiKey, + 'check' => true, + ])); + + if (($data = json_decode($response, true)) === null) { + return 0; + } + + if (!isset($data['response'])) { + return 0; + } + + return $data['response']; + } + + /** + * Open a remote web address. + * + * @param string $url Website URL + * + * @return bool|string + */ + private function httpRequest($url) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_FAILONERROR, 1); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + + $response = curl_exec($ch); + + if (!curl_errno($ch)) { + curl_close($ch); + + return $response; + } + + curl_close($ch); + + return false; + } +}