aboutsummaryrefslogtreecommitdiff
path: root/includes/vendor/maxmind-db/reader/src/MaxMind/Db/Reader.php
diff options
context:
space:
mode:
authors1n <[email protected]>2020-03-28 10:36:41 -0700
committers1n <[email protected]>2020-03-28 10:36:41 -0700
commit25b7d2aab61ae6421398d3abae5da6ffe590333d (patch)
tree611985ec78bb2d94099c9fd5dd687f5c9cee6f3e /includes/vendor/maxmind-db/reader/src/MaxMind/Db/Reader.php
parentInitial commit (diff)
downloadcrack.cf-backup-master.tar.xz
crack.cf-backup-master.zip
3/28/2020, 10:36HEADmaster
Diffstat (limited to 'includes/vendor/maxmind-db/reader/src/MaxMind/Db/Reader.php')
-rw-r--r--includes/vendor/maxmind-db/reader/src/MaxMind/Db/Reader.php309
1 files changed, 309 insertions, 0 deletions
diff --git a/includes/vendor/maxmind-db/reader/src/MaxMind/Db/Reader.php b/includes/vendor/maxmind-db/reader/src/MaxMind/Db/Reader.php
new file mode 100644
index 0000000..745f7bb
--- /dev/null
+++ b/includes/vendor/maxmind-db/reader/src/MaxMind/Db/Reader.php
@@ -0,0 +1,309 @@
+<?php
+
+namespace MaxMind\Db;
+
+use MaxMind\Db\Reader\Decoder;
+use MaxMind\Db\Reader\InvalidDatabaseException;
+use MaxMind\Db\Reader\Metadata;
+use MaxMind\Db\Reader\Util;
+
+/**
+ * Instances of this class provide a reader for the MaxMind DB format. IP
+ * addresses can be looked up using the <code>get</code> method.
+ */
+class Reader
+{
+ private static $DATA_SECTION_SEPARATOR_SIZE = 16;
+ private static $METADATA_START_MARKER = "\xAB\xCD\xEFMaxMind.com";
+ private static $METADATA_START_MARKER_LENGTH = 14;
+ private static $METADATA_MAX_SIZE = 131072; // 128 * 1024 = 128KB
+
+ private $decoder;
+ private $fileHandle;
+ private $fileSize;
+ private $ipV4Start;
+ private $metadata;
+
+ /**
+ * Constructs a Reader for the MaxMind DB format. The file passed to it must
+ * be a valid MaxMind DB file such as a GeoIp2 database file.
+ *
+ * @param string $database
+ * the MaxMind DB file to use
+ *
+ * @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
+ */
+ public function __construct($database)
+ {
+ if (\func_num_args() !== 1) {
+ throw new \InvalidArgumentException(
+ 'The constructor takes exactly one argument.'
+ );
+ }
+
+ if (!is_readable($database)) {
+ 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(
+ "Error opening \"$database\"."
+ );
+ }
+ $this->fileSize = @filesize($database);
+ if ($this->fileSize === false) {
+ throw new \UnexpectedValueException(
+ "Error determining the size of \"$database\"."
+ );
+ }
+
+ $start = $this->findMetadataStart($database);
+ $metadataDecoder = new Decoder($this->fileHandle, $start);
+ list($metadataArray) = $metadataDecoder->decode($start);
+ $this->metadata = new Metadata($metadataArray);
+ $this->decoder = new Decoder(
+ $this->fileHandle,
+ $this->metadata->searchTreeSize + self::$DATA_SECTION_SEPARATOR_SIZE
+ );
+ }
+
+ /**
+ * Looks up the <code>address</code> in the MaxMind DB.
+ *
+ * @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 the record for the IP address
+ */
+ public function get($ipAddress)
+ {
+ if (\func_num_args() !== 1) {
+ throw new \InvalidArgumentException(
+ 'Method takes exactly one argument.'
+ );
+ }
+
+ if (!\is_resource($this->fileHandle)) {
+ throw new \BadMethodCallException(
+ 'Attempt to read from a closed MaxMind DB.'
+ );
+ }
+
+ if (!filter_var($ipAddress, FILTER_VALIDATE_IP)) {
+ 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);
+ if ($pointer === 0) {
+ return null;
+ }
+
+ return $this->resolveDataPointer($pointer);
+ }
+
+ private function findAddressInTree($ipAddress)
+ {
+ // XXX - could simplify. Done as a byte array to ease porting
+ $rawAddress = array_merge(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);
+
+ for ($i = 0; $i < $bitCount; ++$i) {
+ if ($node >= $this->metadata->nodeCount) {
+ break;
+ }
+ $tempBit = 0xFF & $rawAddress[$i >> 3];
+ $bit = 1 & ($tempBit >> 7 - ($i % 8));
+
+ $node = $this->readNode($node, $bit);
+ }
+ if ($node === $this->metadata->nodeCount) {
+ // Record is empty
+ return 0;
+ } elseif ($node > $this->metadata->nodeCount) {
+ // Record is a data pointer
+ return $node;
+ }
+ 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 ($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;
+ }
+
+ private function readNode($nodeNumber, $index)
+ {
+ $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);
+ list(, $node) = unpack('N', "\x00" . $bytes);
+
+ return $node;
+ case 28:
+ $middleByte = Util::read($this->fileHandle, $baseOffset + 3, 1);
+ list(, $middle) = unpack('C', $middleByte);
+ if ($index === 0) {
+ $middle = (0xF0 & $middle) >> 4;
+ } else {
+ $middle = 0x0F & $middle;
+ }
+ $bytes = Util::read($this->fileHandle, $baseOffset + $index * 4, 3);
+ list(, $node) = unpack('N', \chr($middle) . $bytes);
+
+ return $node;
+ case 32:
+ $bytes = Util::read($this->fileHandle, $baseOffset + $index * 4, 4);
+ list(, $node) = unpack('N', $bytes);
+
+ return $node;
+ default:
+ throw new InvalidDatabaseException(
+ 'Unknown record size: '
+ . $this->metadata->recordSize
+ );
+ }
+ }
+
+ private function resolveDataPointer($pointer)
+ {
+ $resolved = $pointer - $this->metadata->nodeCount
+ + $this->metadata->searchTreeSize;
+ if ($resolved > $this->fileSize) {
+ throw new InvalidDatabaseException(
+ "The MaxMind DB file's search tree is corrupt"
+ );
+ }
+
+ list($data) = $this->decoder->decode($resolved);
+
+ return $data;
+ }
+
+ /*
+ * This is an extremely naive but reasonably readable implementation. There
+ * are much faster algorithms (e.g., Boyer-Moore) for this if speed is ever
+ * an issue, but I suspect it won't be.
+ */
+ private function findMetadataStart($filename)
+ {
+ $handle = $this->fileHandle;
+ $fstat = fstat($handle);
+ $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;
+ }
+ }
+
+ return $fileSize - $i;
+ }
+ throw new InvalidDatabaseException(
+ "Error opening database file ($filename). " .
+ 'Is this a valid MaxMind DB file?'
+ );
+ }
+
+ /**
+ * @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(
+ 'Method takes no arguments.'
+ );
+ }
+
+ // 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(
+ 'Attempt to read from a closed MaxMind DB.'
+ );
+ }
+
+ return $this->metadata;
+ }
+
+ /**
+ * Closes the MaxMind DB and returns resources to the system.
+ *
+ * @throws \Exception
+ * if an I/O error occurs
+ */
+ public function close()
+ {
+ if (!\is_resource($this->fileHandle)) {
+ throw new \BadMethodCallException(
+ 'Attempt to close a closed MaxMind DB.'
+ );
+ }
+ fclose($this->fileHandle);
+ }
+}