From 04485c2404651a834ab52ea5ec7939249eb857df Mon Sep 17 00:00:00 2001 From: Amaia Anabitarte Date: Fri, 13 Dec 2019 12:22:40 +0100 Subject: [PATCH] MDL-67380 lib: Upgrade MixMind DB reader lib to 1.5.1 --- lib/maxmind/MaxMind/CHANGELOG.md | 167 +++++++++++++++ lib/maxmind/MaxMind/Db/Reader.php | 174 ++++++++------- lib/maxmind/MaxMind/Db/Reader/Decoder.php | 47 ++-- .../Db/Reader/InvalidDatabaseException.php | 4 +- lib/maxmind/MaxMind/Db/Reader/Metadata.php | 69 +++--- lib/maxmind/MaxMind/LICENSE | 202 ++++++++++++++++++ lib/maxmind/MaxMind/README.md | 178 +++++++++++++++ lib/maxmind/MaxMind/autoload.php | 45 ++++ lib/maxmind/MaxMind/composer.json | 40 ++++ 9 files changed, 806 insertions(+), 120 deletions(-) create mode 100644 lib/maxmind/MaxMind/CHANGELOG.md create mode 100644 lib/maxmind/MaxMind/LICENSE create mode 100644 lib/maxmind/MaxMind/README.md create mode 100644 lib/maxmind/MaxMind/autoload.php create mode 100644 lib/maxmind/MaxMind/composer.json diff --git a/lib/maxmind/MaxMind/CHANGELOG.md b/lib/maxmind/MaxMind/CHANGELOG.md new file mode 100644 index 00000000000..9ed49bd416d --- /dev/null +++ b/lib/maxmind/MaxMind/CHANGELOG.md @@ -0,0 +1,167 @@ +CHANGELOG +========= + +1.5.1 (2019-12-12) +------------------ + +* Minor performance improvements. +* Make tests pass with older versions of libmaxminddb. PR by Remi + Collet. GitHub #90. +* Test enhancements. PR by Chun-Sheng, Li. GitHub #91. + +1.5.0 (2019-09-30) +------------------ + +* PHP 5.6 or greater is now required. +* The C extension now supports PHP 8. Pull request by John Boehr. + GitHub #87. +* A new method, `getWithPrefixLen`, was added to the `Reader` class. + This method returns an array containing the record and the prefix + length for that record. GitHub #89. + +1.4.1 (2019-01-04) +------------------ + +* The `maxminddb` extension now returns a string when a `uint32` + value is greater than `LONG_MAX`. Previously, the value would + overflow. This generally only affects 32-bit machines. Reported + by Remi Collet. GitHub #79. +* For `uint64` values, the `maxminddb` extension now returns an + integer rather than a string when the value is less than or equal + to `LONG_MAX`. This more closely matches the behavior of the pure + PHP reader. + +1.4.0 (2018-11-20) +------------------ + +* The `maxminddb` extension now has the arginfo when using reflection. + PR by Remi Collet. GitHub #75. +* The `maxminddb` extension now provides `MINFO()` function that + displays the extension version and the libmaxminddb version. PR by + Remi Collet. GitHub #74. +* The `maxminddb` `configure` script now uses `pkg-config` when + available to get libmaxmindb build info. PR by Remi Collet. + GitHub #73. +* The pure PHP reader now correctly decodes integers on 32-bit platforms. + Previously, large integers would overflow. Reported by Remi Collet. + GitHub #77. +* There are small performance improvements for the pure PHP reader. + +1.3.0 (2018-02-21) +------------------ + +* IMPORTANT: The `maxminddb` extension now obeys `open_basedir`. If + `open_basedir` is set, you _must_ store the database within the + specified directory. Placing the file outside of this directory + will result in an exception. Please test your integration before + upgrading the extension. This does not affect the pure PHP + implementation, which has always had this restriction. Reported + by BenoƮt Burnichon. GitHub #61. +* A custom `autoload.php` file is provided for installations without + Composer. GitHub #56. + +1.2.0 (2017-10-27) +------------------ + +* PHP 5.4 or greater is now required. +* The `Reader` class for the `maxminddb` extension is no longer final. + This was change to match the behavior of the pure PHP class. + Reported and fixed by venyii. GitHub #52 & #54. + +1.1.3 (2017-01-19) +------------------ + +* Fix incorrect version in `ext/php_maxminddb.h`. GitHub #48. + +1.1.2 (2016-11-22) +------------------ + +* Searching for database metadata only occurs within the last 128KB + (128 * 1024 bytes) of the file, speeding detection of corrupt + datafiles. Reported by Eric Teubert. GitHub #42. +* Suggest relevant extensions when installing with Composer. GitHub #37. + +1.1.1 (2016-09-15) +------------------ + +* Development files were added to the `.gitattributes` as `export-ignore` so + that they are not part of the Composer release. Pull request by Michele + Locati. GitHub #39. + +1.1.0 (2016-01-04) +------------------ + +* The MaxMind DB extension now supports PHP 7. Pull request by John Boehr. + GitHub #27. + +1.0.3 (2015-03-13) +------------------ + +* All uses of `strlen` were removed. This should prevent issues in situations + where the function is overloaded or otherwise broken. + +1.0.2 (2015-01-19) +------------------ + +* Previously the MaxMind DB extension would cause a segfault if the Reader + object's destructor was called without first having called the constructor. + (Reported by Matthias Saou & Juan Peri. GitHub #20.) + +1.0.1 (2015-01-12) +------------------ + +* In the last several releases, the version number in the extension was + incorrect. This release is being done to correct it. No other code changes + are included. + +1.0.0 (2014-09-22) +------------------ + +* First production release. +* In the pure PHP reader, a string length test after `fread()` was replaced + with the difference between the start pointer and the end pointer. This + provided a 15% speed increase. + +0.3.3 (2014-09-15) +------------------ + +* Clarified behavior of 128-bit type in documentation. +* Updated phpunit and fixed some test breakage from the newer version. + +0.3.2 (2014-09-10) +------------------ + +* Fixed invalid reference to global class RuntimeException from namespaced + code. Fixed by Steven Don. GitHub issue #15. +* Additional documentation of `Metadata` class as well as misc. documentation + cleanup. + +0.3.1 (2014-05-01) +------------------ + +* The API now works when `mbstring.func_overload` is set. +* BCMath is no longer required. If the decoder encounters a big integer, + it will try to use GMP and then BCMath. If both of those fail, it will + throw an exception. No databases released by MaxMind currently use big + integers. +* The API now officially supports HHVM when using the pure PHP reader. + +0.3.0 (2014-02-19) +------------------ + +* This API is now licensed under the Apache License, Version 2.0. +* The code for the C extension was cleaned up, fixing several potential + issues. + +0.2.0 (2013-10-21) +------------------ + +* Added optional C extension for using libmaxminddb in place of the pure PHP + reader. +* Significantly improved error handling in pure PHP reader. +* Improved performance for IPv4 lookups in an IPv6 database. + +0.1.0 (2013-07-16) +------------------ + +* Initial release diff --git a/lib/maxmind/MaxMind/Db/Reader.php b/lib/maxmind/MaxMind/Db/Reader.php index 745f7bb3ae0..3d5a8291d95 100644 --- a/lib/maxmind/MaxMind/Db/Reader.php +++ b/lib/maxmind/MaxMind/Db/Reader.php @@ -2,14 +2,18 @@ namespace MaxMind\Db; +use BadMethodCallException; +use Exception; +use InvalidArgumentException; use MaxMind\Db\Reader\Decoder; use MaxMind\Db\Reader\InvalidDatabaseException; use MaxMind\Db\Reader\Metadata; use MaxMind\Db\Reader\Util; +use UnexpectedValueException; /** * Instances of this class provide a reader for the MaxMind DB format. IP - * addresses can be looked up using the get method. + * addresses can be looked up using the get method. */ class Reader { @@ -31,7 +35,7 @@ class Reader * @param string $database * the MaxMind DB file to use * - * @throws \InvalidArgumentException for invalid database path or unknown arguments + * @throws InvalidArgumentException for invalid database path or unknown arguments * @throws \MaxMind\Db\Reader\InvalidDatabaseException * if the database is invalid or there is an error reading * from it @@ -39,25 +43,25 @@ class Reader public function __construct($database) { if (\func_num_args() !== 1) { - throw new \InvalidArgumentException( + throw new InvalidArgumentException( 'The constructor takes exactly one argument.' ); } if (!is_readable($database)) { - throw new \InvalidArgumentException( + throw new InvalidArgumentException( "The file \"$database\" does not exist or is not readable." ); } $this->fileHandle = @fopen($database, 'rb'); if ($this->fileHandle === false) { - throw new \InvalidArgumentException( + throw new InvalidArgumentException( "Error opening \"$database\"." ); } $this->fileSize = @filesize($database); if ($this->fileSize === false) { - throw new \UnexpectedValueException( + throw new UnexpectedValueException( "Error determining the size of \"$database\"." ); } @@ -70,115 +74,133 @@ class Reader $this->fileHandle, $this->metadata->searchTreeSize + self::$DATA_SECTION_SEPARATOR_SIZE ); + $this->ipV4Start = $this->ipV4StartNode(); } /** - * Looks up the address in the MaxMind DB. + * Retrieves the record for the IP address. * * @param string $ipAddress * the IP address to look up * - * @throws \BadMethodCallException if this method is called on a closed database - * @throws \InvalidArgumentException if something other than a single IP address is passed to the method + * @throws BadMethodCallException if this method is called on a closed database + * @throws InvalidArgumentException if something other than a single IP address is passed to the method * @throws InvalidDatabaseException - * if the database is invalid or there is an error reading - * from it + * if the database is invalid or there is an error reading + * from it * - * @return array the record for the IP address + * @return mixed the record for the IP address */ public function get($ipAddress) { if (\func_num_args() !== 1) { - throw new \InvalidArgumentException( + throw new InvalidArgumentException( + 'Method takes exactly one argument.' + ); + } + list($record) = $this->getWithPrefixLen($ipAddress); + + return $record; + } + + /** + * Retrieves the record for the IP address and its associated network prefix length. + * + * @param string $ipAddress + * the IP address to look up + * + * @throws BadMethodCallException if this method is called on a closed database + * @throws InvalidArgumentException if something other than a single IP address is passed to the method + * @throws InvalidDatabaseException + * if the database is invalid or there is an error reading + * from it + * + * @return array an array where the first element is the record and the + * second the network prefix length for the record + */ + public function getWithPrefixLen($ipAddress) + { + if (\func_num_args() !== 1) { + throw new InvalidArgumentException( 'Method takes exactly one argument.' ); } if (!\is_resource($this->fileHandle)) { - throw new \BadMethodCallException( + throw new BadMethodCallException( 'Attempt to read from a closed MaxMind DB.' ); } if (!filter_var($ipAddress, FILTER_VALIDATE_IP)) { - throw new \InvalidArgumentException( + throw new InvalidArgumentException( "The value \"$ipAddress\" is not a valid IP address." ); } - if ($this->metadata->ipVersion === 4 && strrpos($ipAddress, ':')) { - throw new \InvalidArgumentException( - "Error looking up $ipAddress. You attempted to look up an" - . ' IPv6 address in an IPv4-only database.' - ); - } - $pointer = $this->findAddressInTree($ipAddress); + list($pointer, $prefixLen) = $this->findAddressInTree($ipAddress); if ($pointer === 0) { - return null; + return [null, $prefixLen]; } - return $this->resolveDataPointer($pointer); + return [$this->resolveDataPointer($pointer), $prefixLen]; } private function findAddressInTree($ipAddress) { - // XXX - could simplify. Done as a byte array to ease porting - $rawAddress = array_merge(unpack('C*', inet_pton($ipAddress))); + $rawAddress = unpack('C*', inet_pton($ipAddress)); $bitCount = \count($rawAddress) * 8; // The first node of the tree is always node 0, at the beginning of the // value - $node = $this->startNode($bitCount); + $node = 0; - for ($i = 0; $i < $bitCount; ++$i) { - if ($node >= $this->metadata->nodeCount) { - break; + $metadata = $this->metadata; + + // Check if we are looking up an IPv4 address in an IPv6 tree. If this + // is the case, we can skip over the first 96 nodes. + if ($metadata->ipVersion === 6) { + if ($bitCount === 32) { + $node = $this->ipV4Start; } - $tempBit = 0xFF & $rawAddress[$i >> 3]; + } elseif ($metadata->ipVersion === 4 && $bitCount === 128) { + throw new InvalidArgumentException( + "Error looking up $ipAddress. You attempted to look up an" + . ' IPv6 address in an IPv4-only database.' + ); + } + + $nodeCount = $metadata->nodeCount; + + for ($i = 0; $i < $bitCount && $node < $nodeCount; ++$i) { + $tempBit = 0xFF & $rawAddress[($i >> 3) + 1]; $bit = 1 & ($tempBit >> 7 - ($i % 8)); $node = $this->readNode($node, $bit); } - if ($node === $this->metadata->nodeCount) { + if ($node === $nodeCount) { // Record is empty - return 0; - } elseif ($node > $this->metadata->nodeCount) { + return [0, $i]; + } elseif ($node > $nodeCount) { // Record is a data pointer - return $node; + return [$node, $i]; } throw new InvalidDatabaseException('Something bad happened'); } - private function startNode($length) - { - // Check if we are looking up an IPv4 address in an IPv6 tree. If this - // is the case, we can skip over the first 96 nodes. - if ($this->metadata->ipVersion === 6 && $length === 32) { - return $this->ipV4StartNode(); - } - // The first node of the tree is always node 0, at the beginning of the - // value - return 0; - } - private function ipV4StartNode() { - // This is a defensive check. There is no reason to call this when you - // have an IPv4 tree. + // If we have an IPv4 database, the start node is the first node if ($this->metadata->ipVersion === 4) { return 0; } - if ($this->ipV4Start) { - return $this->ipV4Start; - } $node = 0; for ($i = 0; $i < 96 && $node < $this->metadata->nodeCount; ++$i) { $node = $this->readNode($node, 0); } - $this->ipV4Start = $node; return $node; } @@ -187,7 +209,6 @@ class Reader { $baseOffset = $nodeNumber * $this->metadata->nodeByteSize; - // XXX - probably could condense this. switch ($this->metadata->recordSize) { case 24: $bytes = Util::read($this->fileHandle, $baseOffset + $index * 3, 3); @@ -195,15 +216,13 @@ class Reader return $node; case 28: - $middleByte = Util::read($this->fileHandle, $baseOffset + 3, 1); - list(, $middle) = unpack('C', $middleByte); + $bytes = Util::read($this->fileHandle, $baseOffset + 3 * $index, 4); if ($index === 0) { - $middle = (0xF0 & $middle) >> 4; + $middle = (0xF0 & \ord($bytes[3])) >> 4; } else { - $middle = 0x0F & $middle; + $middle = 0x0F & \ord($bytes[0]); } - $bytes = Util::read($this->fileHandle, $baseOffset + $index * 4, 3); - list(, $node) = unpack('N', \chr($middle) . $bytes); + list(, $node) = unpack('N', \chr($middle) . substr($bytes, $index, 3)); return $node; case 32: @@ -223,7 +242,7 @@ class Reader { $resolved = $pointer - $this->metadata->nodeCount + $this->metadata->searchTreeSize; - if ($resolved > $this->fileSize) { + if ($resolved >= $this->fileSize) { throw new InvalidDatabaseException( "The MaxMind DB file's search tree is corrupt" ); @@ -246,19 +265,18 @@ class Reader $fileSize = $fstat['size']; $marker = self::$METADATA_START_MARKER; $markerLength = self::$METADATA_START_MARKER_LENGTH; - $metadataMaxLengthExcludingMarker - = min(self::$METADATA_MAX_SIZE, $fileSize) - $markerLength; - for ($i = 0; $i <= $metadataMaxLengthExcludingMarker; ++$i) { - for ($j = 0; $j < $markerLength; ++$j) { - fseek($handle, $fileSize - $i - $j - 1); - $matchBit = fgetc($handle); - if ($matchBit !== $marker[$markerLength - $j - 1]) { - continue 2; - } + $minStart = $fileSize - min(self::$METADATA_MAX_SIZE, $fileSize); + + for ($offset = $fileSize - $markerLength; $offset >= $minStart; --$offset) { + if (fseek($handle, $offset) !== 0) { + break; } - return $fileSize - $i; + $value = fread($handle, $markerLength); + if ($value === $marker) { + return $offset + $markerLength; + } } throw new InvalidDatabaseException( "Error opening database file ($filename). " . @@ -267,15 +285,15 @@ class Reader } /** - * @throws \InvalidArgumentException if arguments are passed to the method - * @throws \BadMethodCallException if the database has been closed + * @throws InvalidArgumentException if arguments are passed to the method + * @throws BadMethodCallException if the database has been closed * * @return Metadata object for the database */ public function metadata() { if (\func_num_args()) { - throw new \InvalidArgumentException( + throw new InvalidArgumentException( 'Method takes no arguments.' ); } @@ -283,7 +301,7 @@ class Reader // Not technically required, but this makes it consistent with // C extension and it allows us to change our implementation later. if (!\is_resource($this->fileHandle)) { - throw new \BadMethodCallException( + throw new BadMethodCallException( 'Attempt to read from a closed MaxMind DB.' ); } @@ -294,13 +312,13 @@ class Reader /** * Closes the MaxMind DB and returns resources to the system. * - * @throws \Exception - * if an I/O error occurs + * @throws Exception + * if an I/O error occurs */ public function close() { if (!\is_resource($this->fileHandle)) { - throw new \BadMethodCallException( + throw new BadMethodCallException( 'Attempt to close a closed MaxMind DB.' ); } diff --git a/lib/maxmind/MaxMind/Db/Reader/Decoder.php b/lib/maxmind/MaxMind/Db/Reader/Decoder.php index a71b3de8ec5..132dae82c22 100644 --- a/lib/maxmind/MaxMind/Db/Reader/Decoder.php +++ b/lib/maxmind/MaxMind/Db/Reader/Decoder.php @@ -3,7 +3,13 @@ namespace MaxMind\Db\Reader; // @codingStandardsIgnoreLine -// We subtract 1 from the log to protect against precision loss. +use RuntimeException; + +/** + * @ignore + * + * We subtract 1 from the log to protect against precision loss. + */ \define(__NAMESPACE__ . '\_MM_MAX_INT_BYTES', (log(PHP_INT_MAX, 2) - 1) / 8); class Decoder @@ -15,21 +21,37 @@ class Decoder private $pointerTestHack; private $switchByteOrder; + /** @ignore */ const _EXTENDED = 0; + /** @ignore */ const _POINTER = 1; + /** @ignore */ const _UTF8_STRING = 2; + /** @ignore */ const _DOUBLE = 3; + /** @ignore */ const _BYTES = 4; + /** @ignore */ const _UINT16 = 5; + /** @ignore */ const _UINT32 = 6; + /** @ignore */ const _MAP = 7; + /** @ignore */ const _INT32 = 8; + /** @ignore */ const _UINT64 = 9; + /** @ignore */ const _UINT128 = 10; + /** @ignore */ const _ARRAY = 11; + /** @ignore */ const _CONTAINER = 12; + /** @ignore */ const _END_MARKER = 13; + /** @ignore */ const _BOOLEAN = 14; + /** @ignore */ const _FLOAT = 15; public function __construct( @@ -48,10 +70,7 @@ class Decoder public function decode($offset) { - list(, $ctrlByte) = unpack( - 'C', - Util::read($this->fileStream, $offset, 1) - ); + $ctrlByte = \ord(Util::read($this->fileStream, $offset, 1)); ++$offset; $type = $ctrlByte >> 5; @@ -73,10 +92,7 @@ class Decoder } if ($type === self::_EXTENDED) { - list(, $nextByte) = unpack( - 'C', - Util::read($this->fileStream, $offset, 1) - ); + $nextByte = \ord(Util::read($this->fileStream, $offset, 1)); $type = $nextByte + 7; @@ -233,17 +249,17 @@ class Decoder switch ($pointerSize) { case 1: - $packed = (pack('C', $ctrlByte & 0x7)) . $buffer; + $packed = \chr($ctrlByte & 0x7) . $buffer; list(, $pointer) = unpack('n', $packed); $pointer += $this->pointerBase; break; case 2: - $packed = "\x00" . (pack('C', $ctrlByte & 0x7)) . $buffer; + $packed = "\x00" . \chr($ctrlByte & 0x7) . $buffer; list(, $pointer) = unpack('N', $packed); $pointer += $this->pointerBase + 2048; break; case 3: - $packed = (pack('C', $ctrlByte & 0x7)) . $buffer; + $packed = \chr($ctrlByte & 0x7) . $buffer; // It is safe to use 'N' here, even on 32 bit machines as the // first bit is 0. @@ -264,7 +280,7 @@ class Decoder } elseif (\extension_loaded('bcmath')) { $pointer = bcadd($pointerOffset, $this->pointerBase); } else { - throw new \RuntimeException( + throw new RuntimeException( 'The gmp or bcmath extension must be installed to read this database.' ); } @@ -292,7 +308,7 @@ class Decoder } elseif (\extension_loaded('bcmath')) { $integer = bcadd(bcmul($integer, 256), $part); } else { - throw new \RuntimeException( + throw new RuntimeException( 'The gmp or bcmath extension must be installed to read this database.' ); } @@ -319,8 +335,7 @@ class Decoder $size = 285 + $adjust; } elseif ($size > 30) { list(, $adjust) = unpack('N', "\x00" . $bytes); - $size = ($adjust & (0x0FFFFFFF >> (32 - (8 * $bytesToRead)))) - + 65821; + $size = $adjust + 65821; } return [$size, $offset + $bytesToRead]; diff --git a/lib/maxmind/MaxMind/Db/Reader/InvalidDatabaseException.php b/lib/maxmind/MaxMind/Db/Reader/InvalidDatabaseException.php index d2a9a775f28..478a22c5563 100644 --- a/lib/maxmind/MaxMind/Db/Reader/InvalidDatabaseException.php +++ b/lib/maxmind/MaxMind/Db/Reader/InvalidDatabaseException.php @@ -2,9 +2,11 @@ namespace MaxMind\Db\Reader; +use Exception; + /** * This class should be thrown when unexpected data is found in the database. */ -class InvalidDatabaseException extends \Exception +class InvalidDatabaseException extends Exception { } diff --git a/lib/maxmind/MaxMind/Db/Reader/Metadata.php b/lib/maxmind/MaxMind/Db/Reader/Metadata.php index 4efdd3dedac..0d67c6e0420 100644 --- a/lib/maxmind/MaxMind/Db/Reader/Metadata.php +++ b/lib/maxmind/MaxMind/Db/Reader/Metadata.php @@ -5,31 +5,50 @@ namespace MaxMind\Db\Reader; /** * This class provides the metadata for the MaxMind DB file. * - * @property int nodeCount This is an unsigned 32-bit integer indicating - * the number of nodes in the search tree. - * @property int recordSize This is an unsigned 16-bit integer. It - * indicates the number of bits in a record in the search tree. Note that each - * node consists of two records. - * @property int ipVersion This is an unsigned 16-bit integer which is - * always 4 or 6. It indicates whether the database contains IPv4 or IPv6 - * address data. - * @property string databaseType This is a string that indicates the structure - * of each data record associated with an IP address. The actual definition of - * these structures is left up to the database creator. - * @property array languages An array of strings, each of which is a language - * code. A given record may contain data items that have been localized to - * some or all of these languages. This may be undefined. - * @property int binaryFormatMajorVersion This is an unsigned 16-bit - * integer indicating the major version number for the database's binary - * format. - * @property int binaryFormatMinorVersion This is an unsigned 16-bit - * integer indicating the minor version number for the database's binary format. - * @property int buildEpoch This is an unsigned 64-bit integer that - * contains the database build timestamp as a Unix epoch value. - * @property array description This key will always point to a map - * (associative array). The keys of that map will be language codes, and the - * values will be a description in that language as a UTF-8 string. May be - * undefined for some databases. + * @property int $nodeCount This is an unsigned 32-bit + * integer indicating the number of + * nodes in the search tree. + * @property int $recordSize This is an unsigned 16-bit + * integer. It indicates the number + * of bits in a record in the search + * tree. Note that each node + * consists of two records. + * @property int $ipVersion This is an unsigned 16-bit + * integer which is always 4 or 6. + * It indicates whether the database + * contains IPv4 or IPv6 address + * data. + * @property string $databaseType This is a string that indicates + * the structure of each data record + * associated with an IP address. + * The actual definition of these + * structures is left up to the + * database creator. + * @property array $languages An array of strings, each of + * which is a language code. A given + * record may contain data items + * that have been localized to some + * or all of these languages. This + * may be undefined. + * @property int $binaryFormatMajorVersion This is an unsigned 16-bit + * integer indicating the major + * version number for the database's + * binary format. + * @property int $binaryFormatMinorVersion This is an unsigned 16-bit + * integer indicating the minor + * version number for the database's + * binary format. + * @property int $buildEpoch This is an unsigned 64-bit + * integer that contains the + * database build timestamp as a + * Unix epoch value. + * @property array $description This key will always point to a + * map (associative array). The keys + * of that map will be language + * codes, and the values will be a + * description in that language as a + * UTF-8 string. May be undefined + * for some databases. */ class Metadata { diff --git a/lib/maxmind/MaxMind/LICENSE b/lib/maxmind/MaxMind/LICENSE new file mode 100644 index 00000000000..d6456956733 --- /dev/null +++ b/lib/maxmind/MaxMind/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lib/maxmind/MaxMind/README.md b/lib/maxmind/MaxMind/README.md new file mode 100644 index 00000000000..d0600e871b1 --- /dev/null +++ b/lib/maxmind/MaxMind/README.md @@ -0,0 +1,178 @@ +# MaxMind DB Reader PHP API # + +## Description ## + +This is the PHP API for reading MaxMind DB files. MaxMind DB is a binary file +format that stores data indexed by IP address subnets (IPv4 or IPv6). + +## Installation (Composer) ## + +We recommend installing this package with [Composer](https://getcomposer.org/). + +### Download Composer ### + +To download Composer, run in the root directory of your project: + +```bash +curl -sS https://getcomposer.org/installer | php +``` + +You should now have the file `composer.phar` in your project directory. + +### Install Dependencies ### + +Run in your project root: + +``` +php composer.phar require maxmind-db/reader:~1.0 +``` + +You should now have the files `composer.json` and `composer.lock` as well as +the directory `vendor` in your project directory. If you use a version control +system, `composer.json` should be added to it. + +### Require Autoloader ### + +After installing the dependencies, you need to require the Composer autoloader +from your code: + +```php +require 'vendor/autoload.php'; +``` + +## Installation (Standalone) ## + +If you don't want to use Composer for some reason, a custom +`autoload.php` is provided for you in the project root. To use the +library, simply include that file, + +```php +require('/path/to/MaxMind-DB-Reader-php/autoload.php'); +``` + +and then instantiate the reader class normally: + +```php +use MaxMind\Db\Reader; +$reader = new Reader('example.mmdb'); +``` + +## Installation (RPM) + +RPMs are available in the [official Fedora repository](https://apps.fedoraproject.org/packages/php-maxminddb). + +To install on Fedora, run: + +```bash +dnf install php-maxminddb +``` + +To install on CentOS or RHEL 7, first [enable the EPEL repository](https://fedoraproject.org/wiki/EPEL) +and then run: + +```bash +yum install php-maxminddb +``` + +Please note that these packages are *not* maintained by MaxMind. + +## Usage ## + +## Example ## + +```php +get($ipAddress)); + +// getWithPrefixLen returns an array containing the record and the +// associated prefix length for that record. +print_r($reader->getWithPrefixLen($ipAddress)); + +$reader->close(); +``` + +## Optional PHP C Extension ## + +MaxMind provides an optional C extension that is a drop-in replacement for +`MaxMind\Db\Reader`. In order to use this extension, you must install the +Reader API as described above and install the extension as described below. If +you are using an autoloader, no changes to your code should be necessary. + +### Installing Extension ### + +First install [libmaxminddb](https://github.com/maxmind/libmaxminddb) as +described in its [README.md +file](https://github.com/maxmind/libmaxminddb/blob/master/README.md#installing-from-a-tarball). +After successfully installing libmaxmindb, run the following commands from the +top-level directory of this distribution: + +``` +cd ext +phpize +./configure +make +make test +sudo make install +``` + +You then must load your extension. The recommend method is to add the +following to your `php.ini` file: + +``` +extension=maxminddb.so +``` + +Note: You may need to install the PHP development package on your OS such as +php5-dev for Debian-based systems or php-devel for RedHat/Fedora-based ones. + +## 128-bit Integer Support ## + +The MaxMind DB format includes 128-bit unsigned integer as a type. Although +no MaxMind-distributed database currently makes use of this type, both the +pure PHP reader and the C extension support this type. The pure PHP reader +requires gmp or bcmath to read databases with 128-bit unsigned integers. + +The integer is currently returned as a hexadecimal string (prefixed with "0x") +by the C extension and a decimal string (no prefix) by the pure PHP reader. +Any change to make the reader implementations always return either a +hexadecimal or decimal representation of the integer will NOT be considered a +breaking change. + +## Support ## + +Please report all issues with this code using the [GitHub issue tracker](https://github.com/maxmind/MaxMind-DB-Reader-php/issues). + +If you are having an issue with a MaxMind service that is not specific to the +client API, please see [our support page](https://www.maxmind.com/en/support). + +## Requirements ## + +This library requires PHP 5.6 or greater. + +The GMP or BCMath extension may be required to read some databases +using the pure PHP API. + +## Contributing ## + +Patches and pull requests are encouraged. All code should follow the PSR-1 and +PSR-2 style guidelines. Please include unit tests whenever possible. + +## Versioning ## + +The MaxMind DB Reader PHP API uses [Semantic Versioning](https://semver.org/). + +## Copyright and License ## + +This software is Copyright (c) 2014-2019 by MaxMind, Inc. + +This is free software, licensed under the Apache License, Version 2.0. diff --git a/lib/maxmind/MaxMind/autoload.php b/lib/maxmind/MaxMind/autoload.php new file mode 100644 index 00000000000..1314b698e64 --- /dev/null +++ b/lib/maxmind/MaxMind/autoload.php @@ -0,0 +1,45 @@ +class. + * + * @param string $class + * the name of the class to load + */ +function mmdb_autoload($class) +{ + /* + * A project-specific mapping between the namespaces and where + * they're located. By convention, we include the trailing + * slashes. The one-element array here simply makes things easy + * to extend in the future if (for example) the test classes + * begin to use one another. + */ + $namespace_map = ['MaxMind\\Db\\' => __DIR__ . '/src/MaxMind/Db/']; + + foreach ($namespace_map as $prefix => $dir) { + /* First swap out the namespace prefix with a directory... */ + $path = str_replace($prefix, $dir, $class); + + /* replace the namespace separator with a directory separator... */ + $path = str_replace('\\', '/', $path); + + /* and finally, add the PHP file extension to the result. */ + $path = $path . '.php'; + + /* $path should now contain the path to a PHP file defining $class */ + if (file_exists($path)) { + include $path; + } + } +} + +spl_autoload_register('mmdb_autoload'); diff --git a/lib/maxmind/MaxMind/composer.json b/lib/maxmind/MaxMind/composer.json new file mode 100644 index 00000000000..4ddcc362d96 --- /dev/null +++ b/lib/maxmind/MaxMind/composer.json @@ -0,0 +1,40 @@ +{ + "name": "maxmind-db/reader", + "description": "MaxMind DB Reader API", + "keywords": ["database", "geoip", "geoip2", "geolocation", "maxmind"], + "homepage": "https://github.com/maxmind/MaxMind-DB-Reader-php", + "type": "library", + "license": "Apache-2.0", + "authors": [ + { + "name": "Gregory J. Oschwald", + "email": "goschwald@maxmind.com", + "homepage": "https://www.maxmind.com/" + } + ], + "require": { + "php": ">=5.6" + }, + "suggest": { + "ext-bcmath": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder", + "ext-gmp": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder", + "ext-maxminddb": "A C-based database decoder that provides significantly faster lookups" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "2.*", + "phpunit/phpunit": "5.*", + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpcov": "^3.0", + "squizlabs/php_codesniffer": "3.*" + }, + "autoload": { + "psr-4": { + "MaxMind\\Db\\": "src/MaxMind/Db" + } + }, + "autoload-dev": { + "psr-4": { + "MaxMind\\Db\\Test\\Reader\\": "tests/MaxMind/Db/Test/Reader" + } + } +}