commit 2ccad3b139ccd1c77e25e594b812071a092cb64b Author: Miraty Date: Wed Sep 29 14:37:44 2021 +0200 Initial commit diff --git a/db-reader/CHANGELOG.md b/db-reader/CHANGELOG.md new file mode 100644 index 0000000..7b74978 --- /dev/null +++ b/db-reader/CHANGELOG.md @@ -0,0 +1,222 @@ +CHANGELOG +========= + +1.10.1 (2021-04-14) +------------------- + +* Fix a `TypeError` exception in the pure PHP reader when using large + databases on 32-bit PHP builds with the `bcmath` extension. Reported + by dodo1708. GitHub #124. + +1.10.0 (2021-02-09) +------------------- + +* When using the pure PHP reader, unsigned integers up to PHP_MAX_INT + will now be integers in PHP rather than strings. Previously integers + greater than 2^24 on 32-bit platforms and 2^56 on 64-bit platforms + would be strings due to the use of `gmp` or `bcmath` to decode them. + Reported by Alejandro Celaya. GitHub #119. + +1.9.0 (2021-01-07) +------------------ + +* The `maxminddb` extension is now buildable on Windows. Pull request + by Jan Ehrhardt. GitHub #115. + +1.8.0 (2020-10-01) +------------------ + +* Fixes for PHP 8.0. Pull Request by Remi Collet. GitHub #108. + +1.7.0 (2020-08-07) +------------------ + +* IMPORTANT: PHP 7.2 or greater is now required. +* The extension no longer depends on the pure PHP classes in + `maxmind-db/reader`. You can use it independently. +* Type hints have been added to both the pure PHP implementation + and the extension. +* The `metadata` method on the reader now returns a new copy of the + metadata object rather than the actual object used by the reader. +* Work around PHP `is_readable()` bug. Reported by Ben Roberts. GitHub + #92. +* This is the first release of the extension as a PECL package. + GitHub #34. + +1.6.0 (2019-12-19) +------------------ + +* 1.5.0 and 1.5.1 contained a possible memory corruptions when using + `getWithPrefixLen`. This has been fixed. Reported by proton-ab. + GitHub #96. +* The `composer.json` file now conflicts with all versions of the + `maxminddb` C extension less than the Composer version. This is to + reduce the chance of having an older, conflicting version of the + extension installed. You will need to upgrade the extension before + running `composer update`. Pull request by Benoรฎt Burnichon. GitHub + #97. + +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/db-reader/LICENSE b/db-reader/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/db-reader/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/db-reader/README.md b/db-reader/README.md new file mode 100644 index 0000000..49fa280 --- /dev/null +++ b/db-reader/README.md @@ -0,0 +1,185 @@ +# 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/main/README.md#installing-from-a-tarball). +After successfully installing libmaxmindb, you may install the extension +from [pecl](https://pecl.php.net/package/maxminddb): + +``` +pecl install maxminddb +``` + +Alternatively, you may install it from the source. To do so, 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 7.2 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-2020 by MaxMind, Inc. + +This is free software, licensed under the Apache License, Version 2.0. diff --git a/db-reader/autoload.php b/db-reader/autoload.php new file mode 100644 index 0000000..a1119e7 --- /dev/null +++ b/db-reader/autoload.php @@ -0,0 +1,47 @@ +class. + * + * @param string $class + * the name of the class to load + */ +function mmdb_autoload($class): void +{ + /* + * 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/db-reader/composer.json b/db-reader/composer.json new file mode 100644 index 0000000..8d9f9d5 --- /dev/null +++ b/db-reader/composer.json @@ -0,0 +1,44 @@ +{ + "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": ">=7.2" + }, + "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" + }, + "conflict": { + "ext-maxminddb": "<1.10.1,>=2.0.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "*", + "phpunit/phpunit": ">=8.0.0,<10.0.0", + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpcov": ">=6.0.0", + "squizlabs/php_codesniffer": "3.*", + "phpstan/phpstan": "*" + }, + "autoload": { + "psr-4": { + "MaxMind\\Db\\": "src/MaxMind/Db" + } + }, + "autoload-dev": { + "psr-4": { + "MaxMind\\Db\\Test\\Reader\\": "tests/MaxMind/Db/Test/Reader" + } + } +} diff --git a/db-reader/ext/config.m4 b/db-reader/ext/config.m4 new file mode 100644 index 0000000..675e00c --- /dev/null +++ b/db-reader/ext/config.m4 @@ -0,0 +1,40 @@ +PHP_ARG_WITH(maxminddb, + [Whether to enable the MaxMind DB Reader extension], + [ --with-maxminddb Enable MaxMind DB Reader extension support]) + +PHP_ARG_ENABLE(maxminddb-debug, for MaxMind DB debug support, + [ --enable-maxminddb-debug Enable enable MaxMind DB deubg support], no, no) + +if test $PHP_MAXMINDDB != "no"; then + + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + + AC_MSG_CHECKING(for libmaxminddb) + if test -x "$PKG_CONFIG" && $PKG_CONFIG --exists libmaxminddb; then + dnl retrieve build options from pkg-config + if $PKG_CONFIG libmaxminddb --atleast-version 1.0.0; then + LIBMAXMINDDB_INC=`$PKG_CONFIG libmaxminddb --cflags` + LIBMAXMINDDB_LIB=`$PKG_CONFIG libmaxminddb --libs` + LIBMAXMINDDB_VER=`$PKG_CONFIG libmaxminddb --modversion` + AC_MSG_RESULT(found version $LIBMAXMINDDB_VER) + else + AC_MSG_ERROR(system libmaxminddb must be upgraded to version >= 1.0.0) + fi + PHP_EVAL_LIBLINE($LIBMAXMINDDB_LIB, MAXMINDDB_SHARED_LIBADD) + PHP_EVAL_INCLINE($LIBMAXMINDDB_INC) + else + AC_MSG_RESULT(pkg-config information missing) + AC_MSG_WARN(will use libmaxmxinddb from compiler default path) + + PHP_CHECK_LIBRARY(maxminddb, MMDB_open) + PHP_ADD_LIBRARY(maxminddb, 1, MAXMINDDB_SHARED_LIBADD) + fi + + if test $PHP_MAXMINDDB_DEBUG != "no"; then + CFLAGS="$CFLAGS -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Werror" + fi + + PHP_SUBST(MAXMINDDB_SHARED_LIBADD) + + PHP_NEW_EXTENSION(maxminddb, maxminddb.c, $ext_shared) +fi diff --git a/db-reader/ext/config.w32 b/db-reader/ext/config.w32 new file mode 100644 index 0000000..4eb18f8 --- /dev/null +++ b/db-reader/ext/config.w32 @@ -0,0 +1,10 @@ +ARG_WITH("maxminddb", "Enable MaxMind DB Reader extension support", "no"); + +if (PHP_MAXMINDDB == "yes") { + if (CHECK_HEADER_ADD_INCLUDE("maxminddb.h", "CFLAGS_MAXMINDDB", PHP_MAXMINDDB + ";" + PHP_PHP_BUILD + "\\include\\maxminddb") && + CHECK_LIB("libmaxminddb.lib", "maxminddb", PHP_MAXMINDDB)) { + EXTENSION("maxminddb", "maxminddb.c"); + } else { + WARNING('Could not find maxminddb.h or libmaxminddb.lib; skipping'); + } +} diff --git a/db-reader/ext/maxminddb.c b/db-reader/ext/maxminddb.c new file mode 100644 index 0000000..efb2510 --- /dev/null +++ b/db-reader/ext/maxminddb.c @@ -0,0 +1,806 @@ +/* MaxMind, Inc., licenses this file to you 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. + */ + +#include "php_maxminddb.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "Zend/zend_exceptions.h" +#include "Zend/zend_types.h" +#include "ext/spl/spl_exceptions.h" +#include "ext/standard/info.h" +#include + +#ifdef ZTS +#include +#endif + +#define __STDC_FORMAT_MACROS +#include + +#define PHP_MAXMINDDB_NS ZEND_NS_NAME("MaxMind", "Db") +#define PHP_MAXMINDDB_READER_NS ZEND_NS_NAME(PHP_MAXMINDDB_NS, "Reader") +#define PHP_MAXMINDDB_METADATA_NS \ + ZEND_NS_NAME(PHP_MAXMINDDB_READER_NS, "Metadata") +#define PHP_MAXMINDDB_READER_EX_NS \ + ZEND_NS_NAME(PHP_MAXMINDDB_READER_NS, "InvalidDatabaseException") + +#define Z_MAXMINDDB_P(zv) php_maxminddb_fetch_object(Z_OBJ_P(zv)) +typedef size_t strsize_t; +typedef zend_object free_obj_t; + +/* For PHP 8 compatibility */ +#if PHP_VERSION_ID < 80000 + +#define PROP_OBJ(zv) (zv) + +#else + +#define PROP_OBJ(zv) Z_OBJ_P(zv) + +#define TSRMLS_C +#define TSRMLS_CC +#define TSRMLS_DC + +/* End PHP 8 compatibility */ +#endif + +#ifndef ZEND_ACC_CTOR +#define ZEND_ACC_CTOR 0 +#endif + +/* IS_MIXED was added in 2020 */ +#ifndef IS_MIXED +#define IS_MIXED IS_UNDEF +#endif + +typedef struct _maxminddb_obj { + MMDB_s *mmdb; + zend_object std; +} maxminddb_obj; + +PHP_FUNCTION(maxminddb); + +static int +get_record(INTERNAL_FUNCTION_PARAMETERS, zval *record, int *prefix_len); +static const MMDB_entry_data_list_s * +handle_entry_data_list(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC); +static const MMDB_entry_data_list_s * +handle_array(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC); +static const MMDB_entry_data_list_s * +handle_map(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC); +static void handle_uint128(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC); +static void handle_uint64(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC); +static void handle_uint32(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC); + +#define CHECK_ALLOCATED(val) \ + if (!val) { \ + zend_error(E_ERROR, "Out of memory"); \ + return; \ + } + +static zend_object_handlers maxminddb_obj_handlers; +static zend_class_entry *maxminddb_ce, *maxminddb_exception_ce, *metadata_ce; + +static inline maxminddb_obj * +php_maxminddb_fetch_object(zend_object *obj TSRMLS_DC) { + return (maxminddb_obj *)((char *)(obj)-XtOffsetOf(maxminddb_obj, std)); +} + +ZEND_BEGIN_ARG_INFO_EX(arginfo_maxminddbreader_construct, 0, 0, 1) +ZEND_ARG_TYPE_INFO(0, db_file, IS_STRING, 0) +ZEND_END_ARG_INFO() + +PHP_METHOD(MaxMind_Db_Reader, __construct) { + char *db_file = NULL; + strsize_t name_len; + zval *_this_zval = NULL; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + getThis(), + "Os", + &_this_zval, + maxminddb_ce, + &db_file, + &name_len) == FAILURE) { + return; + } + + if (0 != php_check_open_basedir(db_file TSRMLS_CC) || + 0 != access(db_file, R_OK)) { + zend_throw_exception_ex( + spl_ce_InvalidArgumentException, + 0 TSRMLS_CC, + "The file \"%s\" does not exist or is not readable.", + db_file); + return; + } + + MMDB_s *mmdb = (MMDB_s *)ecalloc(1, sizeof(MMDB_s)); + uint16_t status = MMDB_open(db_file, MMDB_MODE_MMAP, mmdb); + + if (MMDB_SUCCESS != status) { + zend_throw_exception_ex( + maxminddb_exception_ce, + 0 TSRMLS_CC, + "Error opening database file (%s). Is this a valid " + "MaxMind DB file?", + db_file); + efree(mmdb); + return; + } + + maxminddb_obj *mmdb_obj = Z_MAXMINDDB_P(getThis()); + mmdb_obj->mmdb = mmdb; +} + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX( + arginfo_maxminddbreader_get, 0, 1, IS_MIXED, 1) +ZEND_ARG_TYPE_INFO(0, ip_address, IS_STRING, 0) +ZEND_END_ARG_INFO() + +PHP_METHOD(MaxMind_Db_Reader, get) { + int prefix_len = 0; + get_record(INTERNAL_FUNCTION_PARAM_PASSTHRU, return_value, &prefix_len); +} + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX( + arginfo_maxminddbreader_getWithPrefixLen, 0, 1, IS_ARRAY, 1) +ZEND_ARG_TYPE_INFO(0, ip_address, IS_STRING, 0) +ZEND_END_ARG_INFO() + +PHP_METHOD(MaxMind_Db_Reader, getWithPrefixLen) { + zval record, z_prefix_len; + + int prefix_len = 0; + if (get_record(INTERNAL_FUNCTION_PARAM_PASSTHRU, &record, &prefix_len) == + FAILURE) { + return; + } + + array_init(return_value); + add_next_index_zval(return_value, &record); + + ZVAL_LONG(&z_prefix_len, prefix_len); + add_next_index_zval(return_value, &z_prefix_len); +} + +static int +get_record(INTERNAL_FUNCTION_PARAMETERS, zval *record, int *prefix_len) { + char *ip_address = NULL; + strsize_t name_len; + zval *this_zval = NULL; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + getThis(), + "Os", + &this_zval, + maxminddb_ce, + &ip_address, + &name_len) == FAILURE) { + return FAILURE; + } + + const maxminddb_obj *mmdb_obj = (maxminddb_obj *)Z_MAXMINDDB_P(getThis()); + + MMDB_s *mmdb = mmdb_obj->mmdb; + + if (NULL == mmdb) { + zend_throw_exception_ex(spl_ce_BadMethodCallException, + 0 TSRMLS_CC, + "Attempt to read from a closed MaxMind DB."); + return FAILURE; + } + + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_flags = AI_NUMERICHOST, + /* We set ai_socktype so that we only get one result back */ + .ai_socktype = SOCK_STREAM}; + + struct addrinfo *addresses = NULL; + int gai_status = getaddrinfo(ip_address, NULL, &hints, &addresses); + if (gai_status) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, + 0 TSRMLS_CC, + "The value \"%s\" is not a valid IP address.", + ip_address); + return FAILURE; + } + if (!addresses || !addresses->ai_addr) { + zend_throw_exception_ex( + spl_ce_InvalidArgumentException, + 0 TSRMLS_CC, + "getaddrinfo was successful but failed to set the addrinfo"); + return FAILURE; + } + + int sa_family = addresses->ai_addr->sa_family; + + int mmdb_error = MMDB_SUCCESS; + MMDB_lookup_result_s result = + MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, &mmdb_error); + + freeaddrinfo(addresses); + + if (MMDB_SUCCESS != mmdb_error) { + zend_class_entry *ex; + if (MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR == mmdb_error) { + ex = spl_ce_InvalidArgumentException; + } else { + ex = maxminddb_exception_ce; + } + zend_throw_exception_ex(ex, + 0 TSRMLS_CC, + "Error looking up %s. %s", + ip_address, + MMDB_strerror(mmdb_error)); + return FAILURE; + } + + *prefix_len = result.netmask; + + if (sa_family == AF_INET && mmdb->metadata.ip_version == 6) { + /* We return the prefix length given the IPv4 address. If there is + no IPv4 subtree, we return a prefix length of 0. */ + *prefix_len = *prefix_len >= 96 ? *prefix_len - 96 : 0; + } + + if (!result.found_entry) { + ZVAL_NULL(record); + return SUCCESS; + } + + MMDB_entry_data_list_s *entry_data_list = NULL; + int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list); + + if (MMDB_SUCCESS != status) { + zend_throw_exception_ex(maxminddb_exception_ce, + 0 TSRMLS_CC, + "Error while looking up data for %s. %s", + ip_address, + MMDB_strerror(status)); + MMDB_free_entry_data_list(entry_data_list); + return FAILURE; + } else if (NULL == entry_data_list) { + zend_throw_exception_ex( + maxminddb_exception_ce, + 0 TSRMLS_CC, + "Error while looking up data for %s. Your database may " + "be corrupt or you have found a bug in libmaxminddb.", + ip_address); + return FAILURE; + } + + const MMDB_entry_data_list_s *rv = + handle_entry_data_list(entry_data_list, record TSRMLS_CC); + if (rv == NULL) { + /* We should have already thrown the exception in handle_entry_data_list + */ + return FAILURE; + } + MMDB_free_entry_data_list(entry_data_list); + return SUCCESS; +} + +ZEND_BEGIN_ARG_INFO_EX(arginfo_maxminddbreader_void, 0, 0, 0) +ZEND_END_ARG_INFO() + +PHP_METHOD(MaxMind_Db_Reader, metadata) { + zval *this_zval = NULL; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + getThis(), + "O", + &this_zval, + maxminddb_ce) == FAILURE) { + return; + } + + const maxminddb_obj *const mmdb_obj = + (maxminddb_obj *)Z_MAXMINDDB_P(this_zval); + + if (NULL == mmdb_obj->mmdb) { + zend_throw_exception_ex(spl_ce_BadMethodCallException, + 0 TSRMLS_CC, + "Attempt to read from a closed MaxMind DB."); + return; + } + + object_init_ex(return_value, metadata_ce); + + MMDB_entry_data_list_s *entry_data_list; + MMDB_get_metadata_as_entry_data_list(mmdb_obj->mmdb, &entry_data_list); + + zval metadata_array; + const MMDB_entry_data_list_s *rv = + handle_entry_data_list(entry_data_list, &metadata_array TSRMLS_CC); + if (rv == NULL) { + return; + } + MMDB_free_entry_data_list(entry_data_list); + zend_call_method_with_1_params(PROP_OBJ(return_value), + metadata_ce, + &metadata_ce->constructor, + ZEND_CONSTRUCTOR_FUNC_NAME, + NULL, + &metadata_array); + zval_ptr_dtor(&metadata_array); +} + +PHP_METHOD(MaxMind_Db_Reader, close) { + zval *this_zval = NULL; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + getThis(), + "O", + &this_zval, + maxminddb_ce) == FAILURE) { + return; + } + + maxminddb_obj *mmdb_obj = (maxminddb_obj *)Z_MAXMINDDB_P(this_zval); + + if (NULL == mmdb_obj->mmdb) { + zend_throw_exception_ex(spl_ce_BadMethodCallException, + 0 TSRMLS_CC, + "Attempt to close a closed MaxMind DB."); + return; + } + MMDB_close(mmdb_obj->mmdb); + efree(mmdb_obj->mmdb); + mmdb_obj->mmdb = NULL; +} + +static const MMDB_entry_data_list_s * +handle_entry_data_list(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC) { + switch (entry_data_list->entry_data.type) { + case MMDB_DATA_TYPE_MAP: + return handle_map(entry_data_list, z_value TSRMLS_CC); + case MMDB_DATA_TYPE_ARRAY: + return handle_array(entry_data_list, z_value TSRMLS_CC); + case MMDB_DATA_TYPE_UTF8_STRING: + ZVAL_STRINGL(z_value, + (char *)entry_data_list->entry_data.utf8_string, + entry_data_list->entry_data.data_size); + break; + case MMDB_DATA_TYPE_BYTES: + ZVAL_STRINGL(z_value, + (char *)entry_data_list->entry_data.bytes, + entry_data_list->entry_data.data_size); + break; + case MMDB_DATA_TYPE_DOUBLE: + ZVAL_DOUBLE(z_value, entry_data_list->entry_data.double_value); + break; + case MMDB_DATA_TYPE_FLOAT: + ZVAL_DOUBLE(z_value, entry_data_list->entry_data.float_value); + break; + case MMDB_DATA_TYPE_UINT16: + ZVAL_LONG(z_value, entry_data_list->entry_data.uint16); + break; + case MMDB_DATA_TYPE_UINT32: + handle_uint32(entry_data_list, z_value TSRMLS_CC); + break; + case MMDB_DATA_TYPE_BOOLEAN: + ZVAL_BOOL(z_value, entry_data_list->entry_data.boolean); + break; + case MMDB_DATA_TYPE_UINT64: + handle_uint64(entry_data_list, z_value TSRMLS_CC); + break; + case MMDB_DATA_TYPE_UINT128: + handle_uint128(entry_data_list, z_value TSRMLS_CC); + break; + case MMDB_DATA_TYPE_INT32: + ZVAL_LONG(z_value, entry_data_list->entry_data.int32); + break; + default: + zend_throw_exception_ex(maxminddb_exception_ce, + 0 TSRMLS_CC, + "Invalid data type arguments: %d", + entry_data_list->entry_data.type); + return NULL; + } + return entry_data_list; +} + +static const MMDB_entry_data_list_s * +handle_map(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC) { + array_init(z_value); + const uint32_t map_size = entry_data_list->entry_data.data_size; + + uint32_t i; + for (i = 0; i < map_size && entry_data_list; i++) { + entry_data_list = entry_data_list->next; + + char *key = estrndup((char *)entry_data_list->entry_data.utf8_string, + entry_data_list->entry_data.data_size); + if (NULL == key) { + zend_throw_exception_ex(maxminddb_exception_ce, + 0 TSRMLS_CC, + "Invalid data type arguments"); + return NULL; + } + + entry_data_list = entry_data_list->next; + zval new_value; + entry_data_list = + handle_entry_data_list(entry_data_list, &new_value TSRMLS_CC); + if (entry_data_list != NULL) { + add_assoc_zval(z_value, key, &new_value); + } + efree(key); + } + return entry_data_list; +} + +static const MMDB_entry_data_list_s * +handle_array(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC) { + const uint32_t size = entry_data_list->entry_data.data_size; + + array_init(z_value); + + uint32_t i; + for (i = 0; i < size && entry_data_list; i++) { + entry_data_list = entry_data_list->next; + zval new_value; + entry_data_list = + handle_entry_data_list(entry_data_list, &new_value TSRMLS_CC); + if (entry_data_list != NULL) { + add_next_index_zval(z_value, &new_value); + } + } + return entry_data_list; +} + +static void handle_uint128(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC) { + uint64_t high = 0; + uint64_t low = 0; +#if MMDB_UINT128_IS_BYTE_ARRAY + int i; + for (i = 0; i < 8; i++) { + high = (high << 8) | entry_data_list->entry_data.uint128[i]; + } + + for (i = 8; i < 16; i++) { + low = (low << 8) | entry_data_list->entry_data.uint128[i]; + } +#else + high = entry_data_list->entry_data.uint128 >> 64; + low = (uint64_t)entry_data_list->entry_data.uint128; +#endif + + char *num_str; + spprintf(&num_str, 0, "0x%016" PRIX64 "%016" PRIX64, high, low); + CHECK_ALLOCATED(num_str); + + ZVAL_STRING(z_value, num_str); + efree(num_str); +} + +static void handle_uint32(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC) { + uint32_t val = entry_data_list->entry_data.uint32; + +#if LONG_MAX >= UINT32_MAX + ZVAL_LONG(z_value, val); + return; +#else + if (val <= LONG_MAX) { + ZVAL_LONG(z_value, val); + return; + } + + char *int_str; + spprintf(&int_str, 0, "%" PRIu32, val); + CHECK_ALLOCATED(int_str); + + ZVAL_STRING(z_value, int_str); + efree(int_str); +#endif +} + +static void handle_uint64(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC) { + uint64_t val = entry_data_list->entry_data.uint64; + +#if LONG_MAX >= UINT64_MAX + ZVAL_LONG(z_value, val); + return; +#else + if (val <= LONG_MAX) { + ZVAL_LONG(z_value, val); + return; + } + + char *int_str; + spprintf(&int_str, 0, "%" PRIu64, val); + CHECK_ALLOCATED(int_str); + + ZVAL_STRING(z_value, int_str); + efree(int_str); +#endif +} + +static void maxminddb_free_storage(free_obj_t *object TSRMLS_DC) { + maxminddb_obj *obj = + php_maxminddb_fetch_object((zend_object *)object TSRMLS_CC); + if (obj->mmdb != NULL) { + MMDB_close(obj->mmdb); + efree(obj->mmdb); + } + + zend_object_std_dtor(&obj->std TSRMLS_CC); +} + +static zend_object *maxminddb_create_handler(zend_class_entry *type TSRMLS_DC) { + maxminddb_obj *obj = (maxminddb_obj *)ecalloc(1, sizeof(maxminddb_obj)); + zend_object_std_init(&obj->std, type TSRMLS_CC); + object_properties_init(&(obj->std), type); + + obj->std.handlers = &maxminddb_obj_handlers; + + return &obj->std; +} + +/* clang-format off */ +static zend_function_entry maxminddb_methods[] = { + PHP_ME(MaxMind_Db_Reader, __construct, arginfo_maxminddbreader_construct, + ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(MaxMind_Db_Reader, close, arginfo_maxminddbreader_void, ZEND_ACC_PUBLIC) + PHP_ME(MaxMind_Db_Reader, get, arginfo_maxminddbreader_get, ZEND_ACC_PUBLIC) + PHP_ME(MaxMind_Db_Reader, getWithPrefixLen, arginfo_maxminddbreader_getWithPrefixLen, ZEND_ACC_PUBLIC) + PHP_ME(MaxMind_Db_Reader, metadata, arginfo_maxminddbreader_void, ZEND_ACC_PUBLIC) + { NULL, NULL, NULL } +}; +/* clang-format on */ + +ZEND_BEGIN_ARG_INFO_EX(arginfo_metadata_construct, 0, 0, 1) +ZEND_ARG_TYPE_INFO(0, metadata, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +PHP_METHOD(MaxMind_Db_Reader_Metadata, __construct) { + zval *object = NULL; + zval *metadata_array = NULL; + zend_long node_count = 0; + zend_long record_size = 0; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + getThis(), + "Oa", + &object, + metadata_ce, + &metadata_array) == FAILURE) { + return; + } + + zval *tmp = NULL; + if ((tmp = zend_hash_str_find(HASH_OF(metadata_array), + "binary_format_major_version", + sizeof("binary_format_major_version") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "binaryFormatMajorVersion", + sizeof("binaryFormatMajorVersion") - 1, + tmp); + } + + if ((tmp = zend_hash_str_find(HASH_OF(metadata_array), + "binary_format_minor_version", + sizeof("binary_format_minor_version") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "binaryFormatMinorVersion", + sizeof("binaryFormatMinorVersion") - 1, + tmp); + } + + if ((tmp = zend_hash_str_find(HASH_OF(metadata_array), + "build_epoch", + sizeof("build_epoch") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "buildEpoch", + sizeof("buildEpoch") - 1, + tmp); + } + + if ((tmp = zend_hash_str_find(HASH_OF(metadata_array), + "database_type", + sizeof("database_type") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "databaseType", + sizeof("databaseType") - 1, + tmp); + } + + if ((tmp = zend_hash_str_find(HASH_OF(metadata_array), + "description", + sizeof("description") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "description", + sizeof("description") - 1, + tmp); + } + + if ((tmp = zend_hash_str_find(HASH_OF(metadata_array), + "ip_version", + sizeof("ip_version") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "ipVersion", + sizeof("ipVersion") - 1, + tmp); + } + + if ((tmp = zend_hash_str_find( + HASH_OF(metadata_array), "languages", sizeof("languages") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "languages", + sizeof("languages") - 1, + tmp); + } + + if ((tmp = zend_hash_str_find(HASH_OF(metadata_array), + "record_size", + sizeof("record_size") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "recordSize", + sizeof("recordSize") - 1, + tmp); + if (Z_TYPE_P(tmp) == IS_LONG) { + record_size = Z_LVAL_P(tmp); + } + } + + if (record_size != 0) { + zend_update_property_long(metadata_ce, + PROP_OBJ(object), + "nodeByteSize", + sizeof("nodeByteSize") - 1, + record_size / 4); + } + + if ((tmp = zend_hash_str_find(HASH_OF(metadata_array), + "node_count", + sizeof("node_count") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "nodeCount", + sizeof("nodeCount") - 1, + tmp); + if (Z_TYPE_P(tmp) == IS_LONG) { + node_count = Z_LVAL_P(tmp); + } + } + + if (record_size != 0) { + zend_update_property_long(metadata_ce, + PROP_OBJ(object), + "searchTreeSize", + sizeof("searchTreeSize") - 1, + record_size * node_count / 4); + } +} + +// clang-format off +static zend_function_entry metadata_methods[] = { + PHP_ME(MaxMind_Db_Reader_Metadata, __construct, arginfo_metadata_construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + {NULL, NULL, NULL} +}; +// clang-format on + +PHP_MINIT_FUNCTION(maxminddb) { + zend_class_entry ce; + + INIT_CLASS_ENTRY(ce, PHP_MAXMINDDB_READER_EX_NS, NULL); + maxminddb_exception_ce = + zend_register_internal_class_ex(&ce, zend_ce_exception); + + INIT_CLASS_ENTRY(ce, PHP_MAXMINDDB_READER_NS, maxminddb_methods); + maxminddb_ce = zend_register_internal_class(&ce TSRMLS_CC); + maxminddb_ce->create_object = maxminddb_create_handler; + + INIT_CLASS_ENTRY(ce, PHP_MAXMINDDB_METADATA_NS, metadata_methods); + metadata_ce = zend_register_internal_class(&ce TSRMLS_CC); + zend_declare_property_null(metadata_ce, + "binaryFormatMajorVersion", + sizeof("binaryFormatMajorVersion") - 1, + ZEND_ACC_PUBLIC); + zend_declare_property_null(metadata_ce, + "binaryFormatMinorVersion", + sizeof("binaryFormatMinorVersion") - 1, + ZEND_ACC_PUBLIC); + zend_declare_property_null( + metadata_ce, "buildEpoch", sizeof("buildEpoch") - 1, ZEND_ACC_PUBLIC); + zend_declare_property_null(metadata_ce, + "databaseType", + sizeof("databaseType") - 1, + ZEND_ACC_PUBLIC); + zend_declare_property_null( + metadata_ce, "description", sizeof("description") - 1, ZEND_ACC_PUBLIC); + zend_declare_property_null( + metadata_ce, "ipVersion", sizeof("ipVersion") - 1, ZEND_ACC_PUBLIC); + zend_declare_property_null( + metadata_ce, "languages", sizeof("languages") - 1, ZEND_ACC_PUBLIC); + zend_declare_property_null(metadata_ce, + "nodeByteSize", + sizeof("nodeByteSize") - 1, + ZEND_ACC_PUBLIC); + zend_declare_property_null( + metadata_ce, "nodeCount", sizeof("nodeCount") - 1, ZEND_ACC_PUBLIC); + zend_declare_property_null( + metadata_ce, "recordSize", sizeof("recordSize") - 1, ZEND_ACC_PUBLIC); + zend_declare_property_null(metadata_ce, + "searchTreeSize", + sizeof("searchTreeSize") - 1, + ZEND_ACC_PUBLIC); + + memcpy(&maxminddb_obj_handlers, + zend_get_std_object_handlers(), + sizeof(zend_object_handlers)); + maxminddb_obj_handlers.clone_obj = NULL; + maxminddb_obj_handlers.offset = XtOffsetOf(maxminddb_obj, std); + maxminddb_obj_handlers.free_obj = maxminddb_free_storage; + zend_declare_class_constant_string(maxminddb_ce, + "MMDB_LIB_VERSION", + sizeof("MMDB_LIB_VERSION") - 1, + MMDB_lib_version() TSRMLS_CC); + + return SUCCESS; +} + +static PHP_MINFO_FUNCTION(maxminddb) { + php_info_print_table_start(); + + php_info_print_table_row(2, "MaxMind DB Reader", "enabled"); + php_info_print_table_row( + 2, "maxminddb extension version", PHP_MAXMINDDB_VERSION); + php_info_print_table_row( + 2, "libmaxminddb library version", MMDB_lib_version()); + + php_info_print_table_end(); +} + +zend_module_entry maxminddb_module_entry = {STANDARD_MODULE_HEADER, + PHP_MAXMINDDB_EXTNAME, + NULL, + PHP_MINIT(maxminddb), + NULL, + NULL, + NULL, + PHP_MINFO(maxminddb), + PHP_MAXMINDDB_VERSION, + STANDARD_MODULE_PROPERTIES}; + +#ifdef COMPILE_DL_MAXMINDDB +ZEND_GET_MODULE(maxminddb) +#endif diff --git a/db-reader/ext/php_maxminddb.h b/db-reader/ext/php_maxminddb.h new file mode 100644 index 0000000..cba9ed5 --- /dev/null +++ b/db-reader/ext/php_maxminddb.h @@ -0,0 +1,24 @@ +/* MaxMind, Inc., licenses this file to you 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. + */ + +#include + +#ifndef PHP_MAXMINDDB_H +#define PHP_MAXMINDDB_H 1 +#define PHP_MAXMINDDB_VERSION "1.10.1" +#define PHP_MAXMINDDB_EXTNAME "maxminddb" + +extern zend_module_entry maxminddb_module_entry; +#define phpext_maxminddb_ptr &maxminddb_module_entry + +#endif diff --git a/db-reader/ext/tests/001-load.phpt b/db-reader/ext/tests/001-load.phpt new file mode 100644 index 0000000..09810ee --- /dev/null +++ b/db-reader/ext/tests/001-load.phpt @@ -0,0 +1,12 @@ +--TEST-- +Check for maxminddb presence +--SKIPIF-- + +--FILE-- + +--EXPECT-- +maxminddb extension is available diff --git a/db-reader/ext/tests/002-final.phpt b/db-reader/ext/tests/002-final.phpt new file mode 100644 index 0000000..d91b7d0 --- /dev/null +++ b/db-reader/ext/tests/002-final.phpt @@ -0,0 +1,13 @@ +--TEST-- +Check that Reader class is not final +--SKIPIF-- + +--FILE-- +isFinal()); +?> +--EXPECT-- +bool(false) diff --git a/db-reader/ext/tests/003-open-basedir.phpt b/db-reader/ext/tests/003-open-basedir.phpt new file mode 100644 index 0000000..26e9781 --- /dev/null +++ b/db-reader/ext/tests/003-open-basedir.phpt @@ -0,0 +1,12 @@ +--TEST-- +openbase_dir is followed +--INI-- +open_basedir=/--dne-- +--FILE-- + +--EXPECTREGEX-- +.*open_basedir restriction in effect.* diff --git a/db-reader/package.xml b/db-reader/package.xml new file mode 100644 index 0000000..b687468 --- /dev/null +++ b/db-reader/package.xml @@ -0,0 +1,63 @@ + + + + maxminddb + pecl.php.net + Reader for the MaxMind DB file format + This is the PHP extension for reading MaxMind DB files. MaxMind DB is a binary file format that stores data indexed by IP address subnets (IPv4 or IPv6). + + Greg Oschwald + oschwald + goschwald@maxmind.com + yes + + 2021-04-14 + + 1.10.1 + 1.10.1 + + + stable + stable + + Apache License 2.0 + * Fix a `TypeError` exception in the pure PHP reader when using large + databases on 32-bit PHP builds with the `bcmath` extension. Reported + by dodo1708. GitHub #124. + + + + + + + + + + + + + + + + + + + + + + + + + 7.2.0 + + + 1.10.0 + + + + maxminddb + + diff --git a/db-reader/phpstan.neon b/db-reader/phpstan.neon new file mode 100644 index 0000000..ee1616d --- /dev/null +++ b/db-reader/phpstan.neon @@ -0,0 +1,7 @@ +parameters: + level: 6 + paths: + - src + - tests + checkMissingIterableValueType: false + diff --git a/db-reader/src/MaxMind/Db/Reader.php b/db-reader/src/MaxMind/Db/Reader.php new file mode 100644 index 0000000..4dfaef4 --- /dev/null +++ b/db-reader/src/MaxMind/Db/Reader.php @@ -0,0 +1,373 @@ +fileHandle = $fileHandle; + + $fileSize = @filesize($database); + if ($fileSize === false) { + throw new UnexpectedValueException( + "Error determining the size of \"$database\"." + ); + } + $this->fileSize = $fileSize; + + $start = $this->findMetadataStart($database); + $metadataDecoder = new Decoder($this->fileHandle, $start); + [$metadataArray] = $metadataDecoder->decode($start); + $this->metadata = new Metadata($metadataArray); + $this->decoder = new Decoder( + $this->fileHandle, + $this->metadata->searchTreeSize + self::$DATA_SECTION_SEPARATOR_SIZE + ); + $this->ipV4Start = $this->ipV4StartNode(); + } + + /** + * 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 InvalidDatabaseException + * if the database is invalid or there is an error reading + * from it + * + * @return mixed the record for the IP address + */ + public function get(string $ipAddress) + { + if (\func_num_args() !== 1) { + throw new ArgumentCountError( + sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args()) + ); + } + [$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(string $ipAddress): array + { + if (\func_num_args() !== 1) { + throw new ArgumentCountError( + sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args()) + ); + } + + if (!\is_resource($this->fileHandle)) { + throw new BadMethodCallException( + 'Attempt to read from a closed MaxMind DB.' + ); + } + + [$pointer, $prefixLen] = $this->findAddressInTree($ipAddress); + if ($pointer === 0) { + return [null, $prefixLen]; + } + + return [$this->resolveDataPointer($pointer), $prefixLen]; + } + + private function findAddressInTree(string $ipAddress): array + { + $packedAddr = @inet_pton($ipAddress); + if ($packedAddr === false) { + throw new InvalidArgumentException( + "The value \"$ipAddress\" is not a valid IP address." + ); + } + + $rawAddress = unpack('C*', $packedAddr); + + $bitCount = \count($rawAddress) * 8; + + // The first node of the tree is always node 0, at the beginning of the + // value + $node = 0; + + $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; + } + } 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 === $nodeCount) { + // Record is empty + return [0, $i]; + } + if ($node > $nodeCount) { + // Record is a data pointer + return [$node, $i]; + } + + throw new InvalidDatabaseException( + 'Invalid or corrupt database. Maximum search depth reached without finding a leaf node' + ); + } + + private function ipV4StartNode(): int + { + // If we have an IPv4 database, the start node is the first node + if ($this->metadata->ipVersion === 4) { + return 0; + } + + $node = 0; + + for ($i = 0; $i < 96 && $node < $this->metadata->nodeCount; ++$i) { + $node = $this->readNode($node, 0); + } + + return $node; + } + + private function readNode(int $nodeNumber, int $index): int + { + $baseOffset = $nodeNumber * $this->metadata->nodeByteSize; + + switch ($this->metadata->recordSize) { + case 24: + $bytes = Util::read($this->fileHandle, $baseOffset + $index * 3, 3); + [, $node] = unpack('N', "\x00" . $bytes); + + return $node; + + case 28: + $bytes = Util::read($this->fileHandle, $baseOffset + 3 * $index, 4); + if ($index === 0) { + $middle = (0xF0 & \ord($bytes[3])) >> 4; + } else { + $middle = 0x0F & \ord($bytes[0]); + } + [, $node] = unpack('N', \chr($middle) . substr($bytes, $index, 3)); + + return $node; + + case 32: + $bytes = Util::read($this->fileHandle, $baseOffset + $index * 4, 4); + [, $node] = unpack('N', $bytes); + + return $node; + + default: + throw new InvalidDatabaseException( + 'Unknown record size: ' + . $this->metadata->recordSize + ); + } + } + + /** + * @return mixed + */ + private function resolveDataPointer(int $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" + ); + } + + [$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(string $filename): int + { + $handle = $this->fileHandle; + $fstat = fstat($handle); + $fileSize = $fstat['size']; + $marker = self::$METADATA_START_MARKER; + $markerLength = self::$METADATA_START_MARKER_LENGTH; + + $minStart = $fileSize - min(self::$METADATA_MAX_SIZE, $fileSize); + + for ($offset = $fileSize - $markerLength; $offset >= $minStart; --$offset) { + if (fseek($handle, $offset) !== 0) { + break; + } + + $value = fread($handle, $markerLength); + if ($value === $marker) { + return $offset + $markerLength; + } + } + + 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(): Metadata + { + if (\func_num_args()) { + throw new ArgumentCountError( + sprintf('%s() expects exactly 0 parameters, %d given', __METHOD__, \func_num_args()) + ); + } + + // 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 clone $this->metadata; + } + + /** + * Closes the MaxMind DB and returns resources to the system. + * + * @throws Exception + * if an I/O error occurs + */ + public function close(): void + { + if (\func_num_args()) { + throw new ArgumentCountError( + sprintf('%s() expects exactly 0 parameters, %d given', __METHOD__, \func_num_args()) + ); + } + + if (!\is_resource($this->fileHandle)) { + throw new BadMethodCallException( + 'Attempt to close a closed MaxMind DB.' + ); + } + fclose($this->fileHandle); + } +} diff --git a/db-reader/src/MaxMind/Db/Reader/Decoder.php b/db-reader/src/MaxMind/Db/Reader/Decoder.php new file mode 100644 index 0000000..4b00456 --- /dev/null +++ b/db-reader/src/MaxMind/Db/Reader/Decoder.php @@ -0,0 +1,381 @@ +fileStream = $fileStream; + $this->pointerBase = $pointerBase; + + $this->pointerBaseByteSize = $pointerBase > 0 ? log($pointerBase, 2) / 8 : 0; + $this->pointerTestHack = $pointerTestHack; + + $this->switchByteOrder = $this->isPlatformLittleEndian(); + } + + public function decode(int $offset): array + { + $ctrlByte = \ord(Util::read($this->fileStream, $offset, 1)); + ++$offset; + + $type = $ctrlByte >> 5; + + // Pointers are a special case, we don't read the next $size bytes, we + // use the size to determine the length of the pointer and then follow + // it. + if ($type === self::_POINTER) { + [$pointer, $offset] = $this->decodePointer($ctrlByte, $offset); + + // for unit testing + if ($this->pointerTestHack) { + return [$pointer]; + } + + [$result] = $this->decode($pointer); + + return [$result, $offset]; + } + + if ($type === self::_EXTENDED) { + $nextByte = \ord(Util::read($this->fileStream, $offset, 1)); + + $type = $nextByte + 7; + + if ($type < 8) { + throw new InvalidDatabaseException( + 'Something went horribly wrong in the decoder. An extended type ' + . 'resolved to a type number < 8 (' + . $type + . ')' + ); + } + + ++$offset; + } + + [$size, $offset] = $this->sizeFromCtrlByte($ctrlByte, $offset); + + return $this->decodeByType($type, $offset, $size); + } + + private function decodeByType(int $type, int $offset, int $size): array + { + switch ($type) { + case self::_MAP: + return $this->decodeMap($size, $offset); + + case self::_ARRAY: + return $this->decodeArray($size, $offset); + + case self::_BOOLEAN: + return [$this->decodeBoolean($size), $offset]; + } + + $newOffset = $offset + $size; + $bytes = Util::read($this->fileStream, $offset, $size); + + switch ($type) { + case self::_BYTES: + case self::_UTF8_STRING: + return [$bytes, $newOffset]; + + case self::_DOUBLE: + $this->verifySize(8, $size); + + return [$this->decodeDouble($bytes), $newOffset]; + + case self::_FLOAT: + $this->verifySize(4, $size); + + return [$this->decodeFloat($bytes), $newOffset]; + + case self::_INT32: + return [$this->decodeInt32($bytes, $size), $newOffset]; + + case self::_UINT16: + case self::_UINT32: + case self::_UINT64: + case self::_UINT128: + return [$this->decodeUint($bytes, $size), $newOffset]; + + default: + throw new InvalidDatabaseException( + 'Unknown or unexpected type: ' . $type + ); + } + } + + private function verifySize(int $expected, int $actual): void + { + if ($expected !== $actual) { + throw new InvalidDatabaseException( + "The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)" + ); + } + } + + private function decodeArray(int $size, int $offset): array + { + $array = []; + + for ($i = 0; $i < $size; ++$i) { + [$value, $offset] = $this->decode($offset); + $array[] = $value; + } + + return [$array, $offset]; + } + + private function decodeBoolean(int $size): bool + { + return $size !== 0; + } + + private function decodeDouble(string $bytes): float + { + // This assumes IEEE 754 doubles, but most (all?) modern platforms + // use them. + [, $double] = unpack('E', $bytes); + + return $double; + } + + private function decodeFloat(string $bytes): float + { + // This assumes IEEE 754 floats, but most (all?) modern platforms + // use them. + [, $float] = unpack('G', $bytes); + + return $float; + } + + private function decodeInt32(string $bytes, int $size): int + { + switch ($size) { + case 0: + return 0; + + case 1: + case 2: + case 3: + $bytes = str_pad($bytes, 4, "\x00", \STR_PAD_LEFT); + + break; + + case 4: + break; + + default: + throw new InvalidDatabaseException( + "The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)" + ); + } + + [, $int] = unpack('l', $this->maybeSwitchByteOrder($bytes)); + + return $int; + } + + private function decodeMap(int $size, int $offset): array + { + $map = []; + + for ($i = 0; $i < $size; ++$i) { + [$key, $offset] = $this->decode($offset); + [$value, $offset] = $this->decode($offset); + $map[$key] = $value; + } + + return [$map, $offset]; + } + + private function decodePointer(int $ctrlByte, int $offset): array + { + $pointerSize = (($ctrlByte >> 3) & 0x3) + 1; + + $buffer = Util::read($this->fileStream, $offset, $pointerSize); + $offset = $offset + $pointerSize; + + switch ($pointerSize) { + case 1: + $packed = \chr($ctrlByte & 0x7) . $buffer; + [, $pointer] = unpack('n', $packed); + $pointer += $this->pointerBase; + + break; + + case 2: + $packed = "\x00" . \chr($ctrlByte & 0x7) . $buffer; + [, $pointer] = unpack('N', $packed); + $pointer += $this->pointerBase + 2048; + + break; + + case 3: + $packed = \chr($ctrlByte & 0x7) . $buffer; + + // It is safe to use 'N' here, even on 32 bit machines as the + // first bit is 0. + [, $pointer] = unpack('N', $packed); + $pointer += $this->pointerBase + 526336; + + break; + + case 4: + // We cannot use unpack here as we might overflow on 32 bit + // machines + $pointerOffset = $this->decodeUint($buffer, $pointerSize); + + $pointerBase = $this->pointerBase; + + if (\PHP_INT_MAX - $pointerBase >= $pointerOffset) { + $pointer = $pointerOffset + $pointerBase; + } else { + throw new RuntimeException( + 'The database offset is too large to be represented on your platform.' + ); + } + + break; + + default: + throw new InvalidDatabaseException( + 'Unexpected pointer size ' . $pointerSize + ); + } + + return [$pointer, $offset]; + } + + // @phpstan-ignore-next-line + private function decodeUint(string $bytes, int $byteLength) + { + if ($byteLength === 0) { + return 0; + } + + $integer = 0; + + // PHP integers are signed. _MM_MAX_INT_BYTES is the number of + // complete bytes that can be converted to an integer. However, + // we can convert another byte if the leading bit is zero. + $useRealInts = $byteLength <= _MM_MAX_INT_BYTES + || ($byteLength === _MM_MAX_INT_BYTES + 1 && (\ord($bytes[0]) & 0x80) === 0); + + for ($i = 0; $i < $byteLength; ++$i) { + $part = \ord($bytes[$i]); + + // We only use gmp or bcmath if the final value is too big + if ($useRealInts) { + $integer = ($integer << 8) + $part; + } elseif (\extension_loaded('gmp')) { + $integer = gmp_strval(gmp_add(gmp_mul((string) $integer, '256'), $part)); + } elseif (\extension_loaded('bcmath')) { + $integer = bcadd(bcmul((string) $integer, '256'), (string) $part); + } else { + throw new RuntimeException( + 'The gmp or bcmath extension must be installed to read this database.' + ); + } + } + + return $integer; + } + + private function sizeFromCtrlByte(int $ctrlByte, int $offset): array + { + $size = $ctrlByte & 0x1f; + + if ($size < 29) { + return [$size, $offset]; + } + + $bytesToRead = $size - 28; + $bytes = Util::read($this->fileStream, $offset, $bytesToRead); + + if ($size === 29) { + $size = 29 + \ord($bytes); + } elseif ($size === 30) { + [, $adjust] = unpack('n', $bytes); + $size = 285 + $adjust; + } else { + [, $adjust] = unpack('N', "\x00" . $bytes); + $size = $adjust + 65821; + } + + return [$size, $offset + $bytesToRead]; + } + + private function maybeSwitchByteOrder(string $bytes): string + { + return $this->switchByteOrder ? strrev($bytes) : $bytes; + } + + private function isPlatformLittleEndian(): bool + { + $testint = 0x00FF; + $packed = pack('S', $testint); + + return $testint === current(unpack('v', $packed)); + } +} diff --git a/db-reader/src/MaxMind/Db/Reader/InvalidDatabaseException.php b/db-reader/src/MaxMind/Db/Reader/InvalidDatabaseException.php new file mode 100644 index 0000000..5323107 --- /dev/null +++ b/db-reader/src/MaxMind/Db/Reader/InvalidDatabaseException.php @@ -0,0 +1,14 @@ +binaryFormatMajorVersion = + $metadata['binary_format_major_version']; + $this->binaryFormatMinorVersion = + $metadata['binary_format_minor_version']; + $this->buildEpoch = $metadata['build_epoch']; + $this->databaseType = $metadata['database_type']; + $this->languages = $metadata['languages']; + $this->description = $metadata['description']; + $this->ipVersion = $metadata['ip_version']; + $this->nodeCount = $metadata['node_count']; + $this->recordSize = $metadata['record_size']; + $this->nodeByteSize = $this->recordSize / 4; + $this->searchTreeSize = $this->nodeCount * $this->nodeByteSize; + } +} diff --git a/db-reader/src/MaxMind/Db/Reader/Util.php b/db-reader/src/MaxMind/Db/Reader/Util.php new file mode 100644 index 0000000..89db715 --- /dev/null +++ b/db-reader/src/MaxMind/Db/Reader/Util.php @@ -0,0 +1,32 @@ + + + + + + +

Unicode emoji list v13.1 without gender and skin tone variations

+
+๐Ÿ˜€ grinning face
+๐Ÿ˜ƒ grinning face with big eyes
+๐Ÿ˜„ grinning face with smiling eyes
+๐Ÿ˜ beaming face with smiling eyes
+๐Ÿ˜† grinning squinting face
+๐Ÿ˜… grinning face with sweat
+๐Ÿคฃ rolling on the floor laughing
+๐Ÿ˜‚ face with tears of joy
+๐Ÿ™‚ slightly smiling face
+๐Ÿ™ƒ upside-down face
+๐Ÿ˜‰ winking face
+๐Ÿ˜Š smiling face with smiling eyes
+๐Ÿ˜‡ smiling face with halo
+๐Ÿฅฐ smiling face with hearts
+๐Ÿ˜ smiling face with heart-eyes
+๐Ÿคฉ star-struck
+๐Ÿ˜˜ face blowing a kiss
+๐Ÿ˜— kissing face
+โ˜บ smiling face
+๐Ÿ˜š kissing face with closed eyes
+๐Ÿ˜™ kissing face with smiling eyes
+๐Ÿฅฒ smiling face with tear
+๐Ÿ˜‹ face savoring food
+๐Ÿ˜› face with tongue
+๐Ÿ˜œ winking face with tongue
+๐Ÿคช zany face
+๐Ÿ˜ squinting face with tongue
+๐Ÿค‘ money-mouth face
+๐Ÿค— hugging face
+๐Ÿคญ face with hand over mouth
+๐Ÿคซ shushing face
+๐Ÿค” thinking face
+๐Ÿค zipper-mouth face
+๐Ÿคจ face with raised eyebrow
+๐Ÿ˜ neutral face
+๐Ÿ˜‘ expressionless face
+๐Ÿ˜ถ face without mouth
+๐Ÿ˜ถโ€๐ŸŒซ๏ธ face in clouds
+๐Ÿ˜ smirking face
+๐Ÿ˜’ unamused face
+๐Ÿ™„ face with rolling eyes
+๐Ÿ˜ฌ grimacing face
+๐Ÿ˜ฎโ€๐Ÿ’จ face exhaling
+๐Ÿคฅ lying face
+๐Ÿ˜Œ relieved face
+๐Ÿ˜” pensive face
+๐Ÿ˜ช sleepy face
+๐Ÿคค drooling face
+๐Ÿ˜ด sleeping face
+๐Ÿ˜ท face with medical mask
+๐Ÿค’ face with thermometer
+๐Ÿค• face with head-bandage
+๐Ÿคข nauseated face
+๐Ÿคฎ face vomiting
+๐Ÿคง sneezing face
+๐Ÿฅต hot face
+๐Ÿฅถ cold face
+๐Ÿฅด woozy face
+๐Ÿ˜ต knocked-out face
+๐Ÿ˜ตโ€๐Ÿ’ซ face with spiral eyes
+๐Ÿคฏ exploding head
+๐Ÿค  cowboy hat face
+๐Ÿฅณ partying face
+๐Ÿฅธ disguised face
+๐Ÿ˜Ž smiling face with sunglasses
+๐Ÿค“ nerd face
+๐Ÿง face with monocle
+๐Ÿ˜• confused face
+๐Ÿ˜Ÿ worried face
+๐Ÿ™ slightly frowning face
+โ˜น frowning face
+๐Ÿ˜ฎ face with open mouth
+๐Ÿ˜ฏ hushed face
+๐Ÿ˜ฒ astonished face
+๐Ÿ˜ณ flushed face
+๐Ÿฅบ pleading face
+๐Ÿ˜ฆ frowning face with open mouth
+๐Ÿ˜ง anguished face
+๐Ÿ˜จ fearful face
+๐Ÿ˜ฐ anxious face with sweat
+๐Ÿ˜ฅ sad but relieved face
+๐Ÿ˜ข crying face
+๐Ÿ˜ญ loudly crying face
+๐Ÿ˜ฑ face screaming in fear
+๐Ÿ˜– confounded face
+๐Ÿ˜ฃ persevering face
+๐Ÿ˜ž disappointed face
+๐Ÿ˜“ downcast face with sweat
+๐Ÿ˜ฉ weary face
+๐Ÿ˜ซ tired face
+๐Ÿฅฑ yawning face
+๐Ÿ˜ค face with steam from nose
+๐Ÿ˜ก pouting face
+๐Ÿ˜  angry face
+๐Ÿคฌ face with symbols on mouth
+๐Ÿ˜ˆ smiling face with horns
+๐Ÿ‘ฟ angry face with horns
+๐Ÿ’€ skull
+โ˜  skull and crossbones
+๐Ÿ’ฉ pile of poo
+๐Ÿคก clown face
+๐Ÿ‘น ogre
+๐Ÿ‘บ goblin
+๐Ÿ‘ป ghost
+๐Ÿ‘ฝ alien
+๐Ÿ‘พ alien monster
+๐Ÿค– robot
+๐Ÿ˜บ grinning cat
+๐Ÿ˜ธ grinning cat with smiling eyes
+๐Ÿ˜น cat with tears of joy
+๐Ÿ˜ป smiling cat with heart-eyes
+๐Ÿ˜ผ cat with wry smile
+๐Ÿ˜ฝ kissing cat
+๐Ÿ™€ weary cat
+๐Ÿ˜ฟ crying cat
+๐Ÿ˜พ pouting cat
+๐Ÿ™ˆ see-no-evil monkey
+๐Ÿ™‰ hear-no-evil monkey
+๐Ÿ™Š speak-no-evil monkey
+๐Ÿ’‹ kiss mark
+๐Ÿ’Œ love letter
+๐Ÿ’˜ heart with arrow
+๐Ÿ’ heart with ribbon
+๐Ÿ’– sparkling heart
+๐Ÿ’— growing heart
+๐Ÿ’“ beating heart
+๐Ÿ’ž revolving hearts
+๐Ÿ’• two hearts
+๐Ÿ’Ÿ heart decoration
+โฃ heart exclamation
+๐Ÿ’” broken heart
+โค๏ธโ€๐Ÿ”ฅ heart on fire
+โค๏ธโ€๐Ÿฉน mending heart
+โค red heart
+๐Ÿงก orange heart
+๐Ÿ’› yellow heart
+๐Ÿ’š green heart
+๐Ÿ’™ blue heart
+๐Ÿ’œ purple heart
+๐ŸคŽ brown heart
+๐Ÿ–ค black heart
+๐Ÿค white heart
+๐Ÿ’ฏ hundred points
+๐Ÿ’ข anger symbol
+๐Ÿ’ฅ collision
+๐Ÿ’ซ dizzy
+๐Ÿ’ฆ sweat droplets
+๐Ÿ’จ dashing away
+๐Ÿ•ณ hole
+๐Ÿ’ฃ bomb
+๐Ÿ’ฌ speech balloon
+๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธ eye in speech bubble
+๐Ÿ—จ left speech bubble
+๐Ÿ—ฏ right anger bubble
+๐Ÿ’ญ thought balloon
+๐Ÿ’ค zzz
+๐Ÿ‘‹ waving hand
+๐Ÿคš raised back of hand
+๐Ÿ– hand with fingers splayed
+โœ‹ raised hand
+๐Ÿ–– vulcan salute
+๐Ÿ‘Œ OK hand
+๐ŸคŒ pinched fingers
+๐Ÿค pinching hand
+โœŒ victory hand
+๐Ÿคž crossed fingers
+๐ŸคŸ love-you gesture
+๐Ÿค˜ sign of the horns
+๐Ÿค™ call me hand
+๐Ÿ‘ˆ backhand index pointing left
+๐Ÿ‘‰ backhand index pointing right
+๐Ÿ‘† backhand index pointing up
+๐Ÿ–• middle finger
+๐Ÿ‘‡ backhand index pointing down
+โ˜ index pointing up
+๐Ÿ‘ thumbs up
+๐Ÿ‘Ž thumbs down
+โœŠ raised fist
+๐Ÿ‘Š oncoming fist
+๐Ÿค› left-facing fist
+๐Ÿคœ right-facing fist
+๐Ÿ‘ clapping hands
+๐Ÿ™Œ raising hands
+๐Ÿ‘ open hands
+๐Ÿคฒ palms up together
+๐Ÿค handshake
+๐Ÿ™ folded hands
+โœ writing hand
+๐Ÿ’… nail polish
+๐Ÿคณ selfie
+๐Ÿ’ช flexed biceps
+๐Ÿฆพ mechanical arm
+๐Ÿฆฟ mechanical leg
+๐Ÿฆต leg
+๐Ÿฆถ foot
+๐Ÿ‘‚ ear
+๐Ÿฆป ear with hearing aid
+๐Ÿ‘ƒ nose
+๐Ÿง  brain
+๐Ÿซ€ anatomical heart
+๐Ÿซ lungs
+๐Ÿฆท tooth
+๐Ÿฆด bone
+๐Ÿ‘€ eyes
+๐Ÿ‘ eye
+๐Ÿ‘… tongue
+๐Ÿ‘„ mouth
+๐Ÿ‘ถ baby
+๐Ÿง’ child
+๐Ÿง‘ person
+๐Ÿ‘ฑ person: blond hair
+๐Ÿง” person: beard
+๐Ÿง‘โ€๐Ÿฆฐ person: red hair
+๐Ÿง‘โ€๐Ÿฆฑ person: curly hair
+๐Ÿง‘โ€๐Ÿฆณ person: white hair
+๐Ÿง‘โ€๐Ÿฆฒ person: bald
+๐Ÿง“ older person
+๐Ÿ™ person frowning
+๐Ÿ™Ž person pouting
+๐Ÿ™… person gesturing NO
+๐Ÿ™† person gesturing OK
+๐Ÿ’ person tipping hand
+๐Ÿ™‹ person raising hand
+๐Ÿง deaf person
+๐Ÿ™‡ person bowing
+๐Ÿคฆ person facepalming
+๐Ÿคท person shrugging
+๐Ÿง‘โ€โš•๏ธ health worker
+๐Ÿง‘โ€๐ŸŽ“ student
+๐Ÿง‘โ€๐Ÿซ teacher
+๐Ÿง‘โ€โš–๏ธ judge
+๐Ÿง‘โ€๐ŸŒพ farmer
+๐Ÿง‘โ€๐Ÿณ cook
+๐Ÿง‘โ€๐Ÿ”ง mechanic
+๐Ÿง‘โ€๐Ÿญ factory worker
+๐Ÿง‘โ€๐Ÿ’ผ office worker
+๐Ÿง‘โ€๐Ÿ”ฌ scientist
+๐Ÿง‘โ€๐Ÿ’ป technologist
+๐Ÿง‘โ€๐ŸŽค singer
+๐Ÿง‘โ€๐ŸŽจ artist
+๐Ÿง‘โ€โœˆ๏ธ pilot
+๐Ÿง‘โ€๐Ÿš€ astronaut
+๐Ÿง‘โ€๐Ÿš’ firefighter
+๐Ÿ‘ฎ police officer
+๐Ÿ•ต detective
+๐Ÿ’‚ guard
+๐Ÿฅท ninja
+๐Ÿ‘ท construction worker
+๐Ÿคด prince
+๐Ÿ‘ธ princess
+๐Ÿ‘ณ person wearing turban
+๐Ÿ‘ฒ person with skullcap
+๐Ÿง• woman with headscarf
+๐Ÿคต person in tuxedo
+๐Ÿ‘ฐ person with veil
+๐Ÿคฐ pregnant woman
+๐Ÿคฑ breast-feeding
+๐Ÿง‘โ€๐Ÿผ person feeding baby
+๐Ÿ‘ผ baby angel
+๐ŸŽ… Santa Claus
+๐Ÿคถ Mrs. Claus
+๐Ÿง‘โ€๐ŸŽ„ mx claus
+๐Ÿฆธ superhero
+๐Ÿฆน supervillain
+๐Ÿง™ mage
+๐Ÿงš fairy
+๐Ÿง› vampire
+๐Ÿงœ merperson
+๐Ÿง elf
+๐Ÿงž genie
+๐ŸงŸ zombie
+๐Ÿ’† person getting massage
+๐Ÿ’‡ person getting haircut
+๐Ÿšถ person walking
+๐Ÿง person standing
+๐ŸงŽ person kneeling
+๐Ÿง‘โ€๐Ÿฆฏ person with white cane
+๐Ÿง‘โ€๐Ÿฆผ person in motorized wheelchair
+๐Ÿง‘โ€๐Ÿฆฝ person in manual wheelchair
+๐Ÿƒ person running
+๐Ÿ’ƒ woman dancing
+๐Ÿ•บ man dancing
+๐Ÿ•ด person in suit levitating
+๐Ÿ‘ฏ people with bunny ears
+๐Ÿง– person in steamy room
+๐Ÿง— person climbing
+๐Ÿคบ person fencing
+๐Ÿ‡ horse racing
+โ›ท skier
+๐Ÿ‚ snowboarder
+๐ŸŒ person golfing
+๐Ÿ„ person surfing
+๐Ÿšฃ person rowing boat
+๐ŸŠ person swimming
+โ›น person bouncing ball
+๐Ÿ‹ person lifting weights
+๐Ÿšด person biking
+๐Ÿšต person mountain biking
+๐Ÿคธ person cartwheeling
+๐Ÿคผ people wrestling
+๐Ÿคฝ person playing water polo
+๐Ÿคพ person playing handball
+๐Ÿคน person juggling
+๐Ÿง˜ person in lotus position
+๐Ÿ›€ person taking bath
+๐Ÿ›Œ person in bed
+๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ people holding hands
+๐Ÿ’ kiss
+๐Ÿ’‘ couple with heart
+๐Ÿ‘ช family
+๐Ÿ—ฃ speaking head
+๐Ÿ‘ค bust in silhouette
+๐Ÿ‘ฅ busts in silhouette
+๐Ÿซ‚ people hugging
+๐Ÿ‘ฃ footprints
+๐Ÿฆฐ red hair
+๐Ÿฆฑ curly hair
+๐Ÿฆณ white hair
+๐Ÿฆฒ bald
+๐Ÿต monkey face
+๐Ÿ’ monkey
+๐Ÿฆ gorilla
+๐Ÿฆง orangutan
+๐Ÿถ dog face
+๐Ÿ• dog
+๐Ÿฆฎ guide dog
+๐Ÿ•โ€๐Ÿฆบ service dog
+๐Ÿฉ poodle
+๐Ÿบ wolf
+๐ŸฆŠ fox
+๐Ÿฆ raccoon
+๐Ÿฑ cat face
+๐Ÿˆ cat
+๐Ÿˆโ€โฌ› black cat
+๐Ÿฆ lion
+๐Ÿฏ tiger face
+๐Ÿ… tiger
+๐Ÿ† leopard
+๐Ÿด horse face
+๐ŸŽ horse
+๐Ÿฆ„ unicorn
+๐Ÿฆ“ zebra
+๐ŸฆŒ deer
+๐Ÿฆฌ bison
+๐Ÿฎ cow face
+๐Ÿ‚ ox
+๐Ÿƒ water buffalo
+๐Ÿ„ cow
+๐Ÿท pig face
+๐Ÿ– pig
+๐Ÿ— boar
+๐Ÿฝ pig nose
+๐Ÿ ram
+๐Ÿ‘ ewe
+๐Ÿ goat
+๐Ÿช camel
+๐Ÿซ two-hump camel
+๐Ÿฆ™ llama
+๐Ÿฆ’ giraffe
+๐Ÿ˜ elephant
+๐Ÿฆฃ mammoth
+๐Ÿฆ rhinoceros
+๐Ÿฆ› hippopotamus
+๐Ÿญ mouse face
+๐Ÿ mouse
+๐Ÿ€ rat
+๐Ÿน hamster
+๐Ÿฐ rabbit face
+๐Ÿ‡ rabbit
+๐Ÿฟ chipmunk
+๐Ÿฆซ beaver
+๐Ÿฆ” hedgehog
+๐Ÿฆ‡ bat
+๐Ÿป bear
+๐Ÿปโ€โ„๏ธ polar bear
+๐Ÿจ koala
+๐Ÿผ panda
+๐Ÿฆฅ sloth
+๐Ÿฆฆ otter
+๐Ÿฆจ skunk
+๐Ÿฆ˜ kangaroo
+๐Ÿฆก badger
+๐Ÿพ paw prints
+๐Ÿฆƒ turkey
+๐Ÿ” chicken
+๐Ÿ“ rooster
+๐Ÿฃ hatching chick
+๐Ÿค baby chick
+๐Ÿฅ front-facing baby chick
+๐Ÿฆ bird
+๐Ÿง penguin
+๐Ÿ•Š dove
+๐Ÿฆ… eagle
+๐Ÿฆ† duck
+๐Ÿฆข swan
+๐Ÿฆ‰ owl
+๐Ÿฆค dodo
+๐Ÿชถ feather
+๐Ÿฆฉ flamingo
+๐Ÿฆš peacock
+๐Ÿฆœ parrot
+๐Ÿธ frog
+๐ŸŠ crocodile
+๐Ÿข turtle
+๐ŸฆŽ lizard
+๐Ÿ snake
+๐Ÿฒ dragon face
+๐Ÿ‰ dragon
+๐Ÿฆ• sauropod
+๐Ÿฆ– T-Rex
+๐Ÿณ spouting whale
+๐Ÿ‹ whale
+๐Ÿฌ dolphin
+๐Ÿฆญ seal
+๐ŸŸ fish
+๐Ÿ  tropical fish
+๐Ÿก blowfish
+๐Ÿฆˆ shark
+๐Ÿ™ octopus
+๐Ÿš spiral shell
+๐ŸŒ snail
+๐Ÿฆ‹ butterfly
+๐Ÿ› bug
+๐Ÿœ ant
+๐Ÿ honeybee
+๐Ÿชฒ beetle
+๐Ÿž lady beetle
+๐Ÿฆ— cricket
+๐Ÿชณ cockroach
+๐Ÿ•ท spider
+๐Ÿ•ธ spider web
+๐Ÿฆ‚ scorpion
+๐ŸฆŸ mosquito
+๐Ÿชฐ fly
+๐Ÿชฑ worm
+๐Ÿฆ  microbe
+๐Ÿ’ bouquet
+๐ŸŒธ cherry blossom
+๐Ÿ’ฎ white flower
+๐Ÿต rosette
+๐ŸŒน rose
+๐Ÿฅ€ wilted flower
+๐ŸŒบ hibiscus
+๐ŸŒป sunflower
+๐ŸŒผ blossom
+๐ŸŒท tulip
+๐ŸŒฑ seedling
+๐Ÿชด potted plant
+๐ŸŒฒ evergreen tree
+๐ŸŒณ deciduous tree
+๐ŸŒด palm tree
+๐ŸŒต cactus
+๐ŸŒพ sheaf of rice
+๐ŸŒฟ herb
+โ˜˜ shamrock
+๐Ÿ€ four leaf clover
+๐Ÿ maple leaf
+๐Ÿ‚ fallen leaf
+๐Ÿƒ leaf fluttering in wind
+๐Ÿ‡ grapes
+๐Ÿˆ melon
+๐Ÿ‰ watermelon
+๐ŸŠ tangerine
+๐Ÿ‹ lemon
+๐ŸŒ banana
+๐Ÿ pineapple
+๐Ÿฅญ mango
+๐ŸŽ red apple
+๐Ÿ green apple
+๐Ÿ pear
+๐Ÿ‘ peach
+๐Ÿ’ cherries
+๐Ÿ“ strawberry
+๐Ÿซ blueberries
+๐Ÿฅ kiwi fruit
+๐Ÿ… tomato
+๐Ÿซ’ olive
+๐Ÿฅฅ coconut
+๐Ÿฅ‘ avocado
+๐Ÿ† eggplant
+๐Ÿฅ” potato
+๐Ÿฅ• carrot
+๐ŸŒฝ ear of corn
+๐ŸŒถ hot pepper
+๐Ÿซ‘ bell pepper
+๐Ÿฅ’ cucumber
+๐Ÿฅฌ leafy green
+๐Ÿฅฆ broccoli
+๐Ÿง„ garlic
+๐Ÿง… onion
+๐Ÿ„ mushroom
+๐Ÿฅœ peanuts
+๐ŸŒฐ chestnut
+๐Ÿž bread
+๐Ÿฅ croissant
+๐Ÿฅ– baguette bread
+๐Ÿซ“ flatbread
+๐Ÿฅจ pretzel
+๐Ÿฅฏ bagel
+๐Ÿฅž pancakes
+๐Ÿง‡ waffle
+๐Ÿง€ cheese wedge
+๐Ÿ– meat on bone
+๐Ÿ— poultry leg
+๐Ÿฅฉ cut of meat
+๐Ÿฅ“ bacon
+๐Ÿ” hamburger
+๐ŸŸ french fries
+๐Ÿ• pizza
+๐ŸŒญ hot dog
+๐Ÿฅช sandwich
+๐ŸŒฎ taco
+๐ŸŒฏ burrito
+๐Ÿซ” tamale
+๐Ÿฅ™ stuffed flatbread
+๐Ÿง† falafel
+๐Ÿฅš egg
+๐Ÿณ cooking
+๐Ÿฅ˜ shallow pan of food
+๐Ÿฒ pot of food
+๐Ÿซ• fondue
+๐Ÿฅฃ bowl with spoon
+๐Ÿฅ— green salad
+๐Ÿฟ popcorn
+๐Ÿงˆ butter
+๐Ÿง‚ salt
+๐Ÿฅซ canned food
+๐Ÿฑ bento box
+๐Ÿ˜ rice cracker
+๐Ÿ™ rice ball
+๐Ÿš cooked rice
+๐Ÿ› curry rice
+๐Ÿœ steaming bowl
+๐Ÿ spaghetti
+๐Ÿ  roasted sweet potato
+๐Ÿข oden
+๐Ÿฃ sushi
+๐Ÿค fried shrimp
+๐Ÿฅ fish cake with swirl
+๐Ÿฅฎ moon cake
+๐Ÿก dango
+๐ŸฅŸ dumpling
+๐Ÿฅ  fortune cookie
+๐Ÿฅก takeout box
+๐Ÿฆ€ crab
+๐Ÿฆž lobster
+๐Ÿฆ shrimp
+๐Ÿฆ‘ squid
+๐Ÿฆช oyster
+๐Ÿฆ soft ice cream
+๐Ÿง shaved ice
+๐Ÿจ ice cream
+๐Ÿฉ doughnut
+๐Ÿช cookie
+๐ŸŽ‚ birthday cake
+๐Ÿฐ shortcake
+๐Ÿง cupcake
+๐Ÿฅง pie
+๐Ÿซ chocolate bar
+๐Ÿฌ candy
+๐Ÿญ lollipop
+๐Ÿฎ custard
+๐Ÿฏ honey pot
+๐Ÿผ baby bottle
+๐Ÿฅ› glass of milk
+โ˜• hot beverage
+๐Ÿซ– teapot
+๐Ÿต teacup without handle
+๐Ÿถ sake
+๐Ÿพ bottle with popping cork
+๐Ÿท wine glass
+๐Ÿธ cocktail glass
+๐Ÿน tropical drink
+๐Ÿบ beer mug
+๐Ÿป clinking beer mugs
+๐Ÿฅ‚ clinking glasses
+๐Ÿฅƒ tumbler glass
+๐Ÿฅค cup with straw
+๐Ÿง‹ bubble tea
+๐Ÿงƒ beverage box
+๐Ÿง‰ mate
+๐ŸงŠ ice
+๐Ÿฅข chopsticks
+๐Ÿฝ fork and knife with plate
+๐Ÿด fork and knife
+๐Ÿฅ„ spoon
+๐Ÿ”ช kitchen knife
+๐Ÿบ amphora
+๐ŸŒ globe showing Europe-Africa
+๐ŸŒŽ globe showing Americas
+๐ŸŒ globe showing Asia-Australia
+๐ŸŒ globe with meridians
+๐Ÿ—บ world map
+๐Ÿ—พ map of Japan
+๐Ÿงญ compass
+๐Ÿ” snow-capped mountain
+โ›ฐ mountain
+๐ŸŒ‹ volcano
+๐Ÿ—ป mount fuji
+๐Ÿ• camping
+๐Ÿ– beach with umbrella
+๐Ÿœ desert
+๐Ÿ desert island
+๐Ÿž national park
+๐ŸŸ stadium
+๐Ÿ› classical building
+๐Ÿ— building construction
+๐Ÿงฑ brick
+๐Ÿชจ rock
+๐Ÿชต wood
+๐Ÿ›– hut
+๐Ÿ˜ houses
+๐Ÿš derelict house
+๐Ÿ  house
+๐Ÿก house with garden
+๐Ÿข office building
+๐Ÿฃ Japanese post office
+๐Ÿค post office
+๐Ÿฅ hospital
+๐Ÿฆ bank
+๐Ÿจ hotel
+๐Ÿฉ love hotel
+๐Ÿช convenience store
+๐Ÿซ school
+๐Ÿฌ department store
+๐Ÿญ factory
+๐Ÿฏ Japanese castle
+๐Ÿฐ castle
+๐Ÿ’’ wedding
+๐Ÿ—ผ Tokyo tower
+๐Ÿ—ฝ Statue of Liberty
+โ›ช church
+๐Ÿ•Œ mosque
+๐Ÿ›• hindu temple
+๐Ÿ• synagogue
+โ›ฉ shinto shrine
+๐Ÿ•‹ kaaba
+โ›ฒ fountain
+โ›บ tent
+๐ŸŒ foggy
+๐ŸŒƒ night with stars
+๐Ÿ™ cityscape
+๐ŸŒ„ sunrise over mountains
+๐ŸŒ… sunrise
+๐ŸŒ† cityscape at dusk
+๐ŸŒ‡ sunset
+๐ŸŒ‰ bridge at night
+โ™จ hot springs
+๐ŸŽ  carousel horse
+๐ŸŽก ferris wheel
+๐ŸŽข roller coaster
+๐Ÿ’ˆ barber pole
+๐ŸŽช circus tent
+๐Ÿš‚ locomotive
+๐Ÿšƒ railway car
+๐Ÿš„ high-speed train
+๐Ÿš… bullet train
+๐Ÿš† train
+๐Ÿš‡ metro
+๐Ÿšˆ light rail
+๐Ÿš‰ station
+๐ŸšŠ tram
+๐Ÿš monorail
+๐Ÿšž mountain railway
+๐Ÿš‹ tram car
+๐ŸšŒ bus
+๐Ÿš oncoming bus
+๐ŸšŽ trolleybus
+๐Ÿš minibus
+๐Ÿš‘ ambulance
+๐Ÿš’ fire engine
+๐Ÿš“ police car
+๐Ÿš” oncoming police car
+๐Ÿš• taxi
+๐Ÿš– oncoming taxi
+๐Ÿš— automobile
+๐Ÿš˜ oncoming automobile
+๐Ÿš™ sport utility vehicle
+๐Ÿ›ป pickup truck
+๐Ÿšš delivery truck
+๐Ÿš› articulated lorry
+๐Ÿšœ tractor
+๐ŸŽ racing car
+๐Ÿ motorcycle
+๐Ÿ›ต motor scooter
+๐Ÿฆฝ manual wheelchair
+๐Ÿฆผ motorized wheelchair
+๐Ÿ›บ auto rickshaw
+๐Ÿšฒ bicycle
+๐Ÿ›ด kick scooter
+๐Ÿ›น skateboard
+๐Ÿ›ผ roller skate
+๐Ÿš bus stop
+๐Ÿ›ฃ motorway
+๐Ÿ›ค railway track
+๐Ÿ›ข oil drum
+โ›ฝ fuel pump
+๐Ÿšจ police car light
+๐Ÿšฅ horizontal traffic light
+๐Ÿšฆ vertical traffic light
+๐Ÿ›‘ stop sign
+๐Ÿšง construction
+โš“ anchor
+โ›ต sailboat
+๐Ÿ›ถ canoe
+๐Ÿšค speedboat
+๐Ÿ›ณ passenger ship
+โ›ด ferry
+๐Ÿ›ฅ motor boat
+๐Ÿšข ship
+โœˆ airplane
+๐Ÿ›ฉ small airplane
+๐Ÿ›ซ airplane departure
+๐Ÿ›ฌ airplane arrival
+๐Ÿช‚ parachute
+๐Ÿ’บ seat
+๐Ÿš helicopter
+๐ŸšŸ suspension railway
+๐Ÿš  mountain cableway
+๐Ÿšก aerial tramway
+๐Ÿ›ฐ satellite
+๐Ÿš€ rocket
+๐Ÿ›ธ flying saucer
+๐Ÿ›Ž bellhop bell
+๐Ÿงณ luggage
+โŒ› hourglass done
+โณ hourglass not done
+โŒš watch
+โฐ alarm clock
+โฑ stopwatch
+โฒ timer clock
+๐Ÿ•ฐ mantelpiece clock
+๐Ÿ•› twelve oโ€™clock
+๐Ÿ•ง twelve-thirty
+๐Ÿ• one oโ€™clock
+๐Ÿ•œ one-thirty
+๐Ÿ•‘ two oโ€™clock
+๐Ÿ• two-thirty
+๐Ÿ•’ three oโ€™clock
+๐Ÿ•ž three-thirty
+๐Ÿ•“ four oโ€™clock
+๐Ÿ•Ÿ four-thirty
+๐Ÿ•” five oโ€™clock
+๐Ÿ•  five-thirty
+๐Ÿ•• six oโ€™clock
+๐Ÿ•ก six-thirty
+๐Ÿ•– seven oโ€™clock
+๐Ÿ•ข seven-thirty
+๐Ÿ•— eight oโ€™clock
+๐Ÿ•ฃ eight-thirty
+๐Ÿ•˜ nine oโ€™clock
+๐Ÿ•ค nine-thirty
+๐Ÿ•™ ten oโ€™clock
+๐Ÿ•ฅ ten-thirty
+๐Ÿ•š eleven oโ€™clock
+๐Ÿ•ฆ eleven-thirty
+๐ŸŒ‘ new moon
+๐ŸŒ’ waxing crescent moon
+๐ŸŒ“ first quarter moon
+๐ŸŒ” waxing gibbous moon
+๐ŸŒ• full moon
+๐ŸŒ– waning gibbous moon
+๐ŸŒ— last quarter moon
+๐ŸŒ˜ waning crescent moon
+๐ŸŒ™ crescent moon
+๐ŸŒš new moon face
+๐ŸŒ› first quarter moon face
+๐ŸŒœ last quarter moon face
+๐ŸŒก thermometer
+โ˜€ sun
+๐ŸŒ full moon face
+๐ŸŒž sun with face
+๐Ÿช ringed planet
+โญ star
+๐ŸŒŸ glowing star
+๐ŸŒ  shooting star
+๐ŸŒŒ milky way
+โ˜ cloud
+โ›… sun behind cloud
+โ›ˆ cloud with lightning and rain
+๐ŸŒค sun behind small cloud
+๐ŸŒฅ sun behind large cloud
+๐ŸŒฆ sun behind rain cloud
+๐ŸŒง cloud with rain
+๐ŸŒจ cloud with snow
+๐ŸŒฉ cloud with lightning
+๐ŸŒช tornado
+๐ŸŒซ fog
+๐ŸŒฌ wind face
+๐ŸŒ€ cyclone
+๐ŸŒˆ rainbow
+๐ŸŒ‚ closed umbrella
+โ˜‚ umbrella
+โ˜” umbrella with rain drops
+โ›ฑ umbrella on ground
+โšก high voltage
+โ„ snowflake
+โ˜ƒ snowman
+โ›„ snowman without snow
+โ˜„ comet
+๐Ÿ”ฅ fire
+๐Ÿ’ง droplet
+๐ŸŒŠ water wave
+๐ŸŽƒ jack-o-lantern
+๐ŸŽ„ Christmas tree
+๐ŸŽ† fireworks
+๐ŸŽ‡ sparkler
+๐Ÿงจ firecracker
+โœจ sparkles
+๐ŸŽˆ balloon
+๐ŸŽ‰ party popper
+๐ŸŽŠ confetti ball
+๐ŸŽ‹ tanabata tree
+๐ŸŽ pine decoration
+๐ŸŽŽ Japanese dolls
+๐ŸŽ carp streamer
+๐ŸŽ wind chime
+๐ŸŽ‘ moon viewing ceremony
+๐Ÿงง red envelope
+๐ŸŽ€ ribbon
+๐ŸŽ wrapped gift
+๐ŸŽ— reminder ribbon
+๐ŸŽŸ admission tickets
+๐ŸŽซ ticket
+๐ŸŽ– military medal
+๐Ÿ† trophy
+๐Ÿ… sports medal
+๐Ÿฅ‡ st place medal
+๐Ÿฅˆ nd place medal
+๐Ÿฅ‰ rd place medal
+โšฝ soccer ball
+โšพ baseball
+๐ŸฅŽ softball
+๐Ÿ€ basketball
+๐Ÿ volleyball
+๐Ÿˆ american football
+๐Ÿ‰ rugby football
+๐ŸŽพ tennis
+๐Ÿฅ flying disc
+๐ŸŽณ bowling
+๐Ÿ cricket game
+๐Ÿ‘ field hockey
+๐Ÿ’ ice hockey
+๐Ÿฅ lacrosse
+๐Ÿ“ ping pong
+๐Ÿธ badminton
+๐ŸฅŠ boxing glove
+๐Ÿฅ‹ martial arts uniform
+๐Ÿฅ… goal net
+โ›ณ flag in hole
+โ›ธ ice skate
+๐ŸŽฃ fishing pole
+๐Ÿคฟ diving mask
+๐ŸŽฝ running shirt
+๐ŸŽฟ skis
+๐Ÿ›ท sled
+๐ŸฅŒ curling stone
+๐ŸŽฏ bullseye
+๐Ÿช€ yo-yo
+๐Ÿช kite
+๐ŸŽฑ pool ball
+๐Ÿ”ฎ crystal ball
+๐Ÿช„ magic wand
+๐Ÿงฟ nazar amulet
+๐ŸŽฎ video game
+๐Ÿ•น joystick
+๐ŸŽฐ slot machine
+๐ŸŽฒ game die
+๐Ÿงฉ puzzle piece
+๐Ÿงธ teddy bear
+๐Ÿช… piรฑata
+๐Ÿช† nesting dolls
+โ™  spade suit
+โ™ฅ heart suit
+โ™ฆ diamond suit
+โ™ฃ club suit
+โ™Ÿ chess pawn
+๐Ÿƒ joker
+๐Ÿ€„ mahjong red dragon
+๐ŸŽด flower playing cards
+๐ŸŽญ performing arts
+๐Ÿ–ผ framed picture
+๐ŸŽจ artist palette
+๐Ÿงต thread
+๐Ÿชก sewing needle
+๐Ÿงถ yarn
+๐Ÿชข knot
+๐Ÿ‘“ glasses
+๐Ÿ•ถ sunglasses
+๐Ÿฅฝ goggles
+๐Ÿฅผ lab coat
+๐Ÿฆบ safety vest
+๐Ÿ‘” necktie
+๐Ÿ‘• t-shirt
+๐Ÿ‘– jeans
+๐Ÿงฃ scarf
+๐Ÿงค gloves
+๐Ÿงฅ coat
+๐Ÿงฆ socks
+๐Ÿ‘— dress
+๐Ÿ‘˜ kimono
+๐Ÿฅป sari
+๐Ÿฉฑ one-piece swimsuit
+๐Ÿฉฒ briefs
+๐Ÿฉณ shorts
+๐Ÿ‘™ bikini
+๐Ÿ‘š womanโ€™s clothes
+๐Ÿ‘› purse
+๐Ÿ‘œ handbag
+๐Ÿ‘ clutch bag
+๐Ÿ› shopping bags
+๐ŸŽ’ backpack
+๐Ÿฉด thong sandal
+๐Ÿ‘ž manโ€™s shoe
+๐Ÿ‘Ÿ running shoe
+๐Ÿฅพ hiking boot
+๐Ÿฅฟ flat shoe
+๐Ÿ‘  high-heeled shoe
+๐Ÿ‘ก womanโ€™s sandal
+๐Ÿฉฐ ballet shoes
+๐Ÿ‘ข womanโ€™s boot
+๐Ÿ‘‘ crown
+๐Ÿ‘’ womanโ€™s hat
+๐ŸŽฉ top hat
+๐ŸŽ“ graduation cap
+๐Ÿงข billed cap
+๐Ÿช– military helmet
+โ›‘ rescue workerโ€™s helmet
+๐Ÿ“ฟ prayer beads
+๐Ÿ’„ lipstick
+๐Ÿ’ ring
+๐Ÿ’Ž gem stone
+๐Ÿ”‡ muted speaker
+๐Ÿ”ˆ speaker low volume
+๐Ÿ”‰ speaker medium volume
+๐Ÿ”Š speaker high volume
+๐Ÿ“ข loudspeaker
+๐Ÿ“ฃ megaphone
+๐Ÿ“ฏ postal horn
+๐Ÿ”” bell
+๐Ÿ”• bell with slash
+๐ŸŽผ musical score
+๐ŸŽต musical note
+๐ŸŽถ musical notes
+๐ŸŽ™ studio microphone
+๐ŸŽš level slider
+๐ŸŽ› control knobs
+๐ŸŽค microphone
+๐ŸŽง headphone
+๐Ÿ“ป radio
+๐ŸŽท saxophone
+๐Ÿช— accordion
+๐ŸŽธ guitar
+๐ŸŽน musical keyboard
+๐ŸŽบ trumpet
+๐ŸŽป violin
+๐Ÿช• banjo
+๐Ÿฅ drum
+๐Ÿช˜ long drum
+๐Ÿ“ฑ mobile phone
+๐Ÿ“ฒ mobile phone with arrow
+โ˜Ž telephone
+๐Ÿ“ž telephone receiver
+๐Ÿ“Ÿ pager
+๐Ÿ“  fax machine
+๐Ÿ”‹ battery
+๐Ÿ”Œ electric plug
+๐Ÿ’ป laptop
+๐Ÿ–ฅ desktop computer
+๐Ÿ–จ printer
+โŒจ keyboard
+๐Ÿ–ฑ computer mouse
+๐Ÿ–ฒ trackball
+๐Ÿ’ฝ computer disk
+๐Ÿ’พ floppy disk
+๐Ÿ’ฟ optical disk
+๐Ÿ“€ dvd
+๐Ÿงฎ abacus
+๐ŸŽฅ movie camera
+๐ŸŽž film frames
+๐Ÿ“ฝ film projector
+๐ŸŽฌ clapper board
+๐Ÿ“บ television
+๐Ÿ“ท camera
+๐Ÿ“ธ camera with flash
+๐Ÿ“น video camera
+๐Ÿ“ผ videocassette
+๐Ÿ” magnifying glass tilted left
+๐Ÿ”Ž magnifying glass tilted right
+๐Ÿ•ฏ candle
+๐Ÿ’ก light bulb
+๐Ÿ”ฆ flashlight
+๐Ÿฎ red paper lantern
+๐Ÿช” diya lamp
+๐Ÿ“” notebook with decorative cover
+๐Ÿ“• closed book
+๐Ÿ“– open book
+๐Ÿ“— green book
+๐Ÿ“˜ blue book
+๐Ÿ“™ orange book
+๐Ÿ“š books
+๐Ÿ““ notebook
+๐Ÿ“’ ledger
+๐Ÿ“ƒ page with curl
+๐Ÿ“œ scroll
+๐Ÿ“„ page facing up
+๐Ÿ“ฐ newspaper
+๐Ÿ—ž rolled-up newspaper
+๐Ÿ“‘ bookmark tabs
+๐Ÿ”– bookmark
+๐Ÿท label
+๐Ÿ’ฐ money bag
+๐Ÿช™ coin
+๐Ÿ’ด yen banknote
+๐Ÿ’ต dollar banknote
+๐Ÿ’ถ euro banknote
+๐Ÿ’ท pound banknote
+๐Ÿ’ธ money with wings
+๐Ÿ’ณ credit card
+๐Ÿงพ receipt
+๐Ÿ’น chart increasing with yen
+โœ‰ envelope
+๐Ÿ“ง e-mail
+๐Ÿ“จ incoming envelope
+๐Ÿ“ฉ envelope with arrow
+๐Ÿ“ค outbox tray
+๐Ÿ“ฅ inbox tray
+๐Ÿ“ฆ package
+๐Ÿ“ซ closed mailbox with raised flag
+๐Ÿ“ช closed mailbox with lowered flag
+๐Ÿ“ฌ open mailbox with raised flag
+๐Ÿ“ญ open mailbox with lowered flag
+๐Ÿ“ฎ postbox
+๐Ÿ—ณ ballot box with ballot
+โœ pencil
+โœ’ black nib
+๐Ÿ–‹ fountain pen
+๐Ÿ–Š pen
+๐Ÿ–Œ paintbrush
+๐Ÿ– crayon
+๐Ÿ“ memo
+๐Ÿ’ผ briefcase
+๐Ÿ“ file folder
+๐Ÿ“‚ open file folder
+๐Ÿ—‚ card index dividers
+๐Ÿ“… calendar
+๐Ÿ“† tear-off calendar
+๐Ÿ—’ spiral notepad
+๐Ÿ—“ spiral calendar
+๐Ÿ“‡ card index
+๐Ÿ“ˆ chart increasing
+๐Ÿ“‰ chart decreasing
+๐Ÿ“Š bar chart
+๐Ÿ“‹ clipboard
+๐Ÿ“Œ pushpin
+๐Ÿ“ round pushpin
+๐Ÿ“Ž paperclip
+๐Ÿ–‡ linked paperclips
+๐Ÿ“ straight ruler
+๐Ÿ“ triangular ruler
+โœ‚ scissors
+๐Ÿ—ƒ card file box
+๐Ÿ—„ file cabinet
+๐Ÿ—‘ wastebasket
+๐Ÿ”’ locked
+๐Ÿ”“ unlocked
+๐Ÿ” locked with pen
+๐Ÿ” locked with key
+๐Ÿ”‘ key
+๐Ÿ— old key
+๐Ÿ”จ hammer
+๐Ÿช“ axe
+โ› pick
+โš’ hammer and pick
+๐Ÿ›  hammer and wrench
+๐Ÿ—ก dagger
+โš” crossed swords
+๐Ÿ”ซ water pistol
+๐Ÿชƒ boomerang
+๐Ÿน bow and arrow
+๐Ÿ›ก shield
+๐Ÿชš carpentry saw
+๐Ÿ”ง wrench
+๐Ÿช› screwdriver
+๐Ÿ”ฉ nut and bolt
+โš™ gear
+๐Ÿ—œ clamp
+โš– balance scale
+๐Ÿฆฏ white cane
+๐Ÿ”— link
+โ›“ chains
+๐Ÿช hook
+๐Ÿงฐ toolbox
+๐Ÿงฒ magnet
+๐Ÿชœ ladder
+โš— alembic
+๐Ÿงช test tube
+๐Ÿงซ petri dish
+๐Ÿงฌ dna
+๐Ÿ”ฌ microscope
+๐Ÿ”ญ telescope
+๐Ÿ“ก satellite antenna
+๐Ÿ’‰ syringe
+๐Ÿฉธ drop of blood
+๐Ÿ’Š pill
+๐Ÿฉน adhesive bandage
+๐Ÿฉบ stethoscope
+๐Ÿšช door
+๐Ÿ›— elevator
+๐Ÿชž mirror
+๐ŸชŸ window
+๐Ÿ› bed
+๐Ÿ›‹ couch and lamp
+๐Ÿช‘ chair
+๐Ÿšฝ toilet
+๐Ÿช  plunger
+๐Ÿšฟ shower
+๐Ÿ› bathtub
+๐Ÿชค mouse trap
+๐Ÿช’ razor
+๐Ÿงด lotion bottle
+๐Ÿงท safety pin
+๐Ÿงน broom
+๐Ÿงบ basket
+๐Ÿงป roll of paper
+๐Ÿชฃ bucket
+๐Ÿงผ soap
+๐Ÿชฅ toothbrush
+๐Ÿงฝ sponge
+๐Ÿงฏ fire extinguisher
+๐Ÿ›’ shopping cart
+๐Ÿšฌ cigarette
+โšฐ coffin
+๐Ÿชฆ headstone
+โšฑ funeral urn
+๐Ÿ—ฟ moai
+๐Ÿชง placard
+๐Ÿง ATM sign
+๐Ÿšฎ litter in bin sign
+๐Ÿšฐ potable water
+โ™ฟ wheelchair symbol
+๐Ÿšน menโ€™s room
+๐Ÿšบ womenโ€™s room
+๐Ÿšป restroom
+๐Ÿšผ baby symbol
+๐Ÿšพ water closet
+๐Ÿ›‚ passport control
+๐Ÿ›ƒ customs
+๐Ÿ›„ baggage claim
+๐Ÿ›… left luggage
+โš  warning
+๐Ÿšธ children crossing
+โ›” no entry
+๐Ÿšซ prohibited
+๐Ÿšณ no bicycles
+๐Ÿšญ no smoking
+๐Ÿšฏ no littering
+๐Ÿšฑ non-potable water
+๐Ÿšท no pedestrians
+๐Ÿ“ต no mobile phones
+๐Ÿ”ž no one under eighteen
+โ˜ข radioactive
+โ˜ฃ biohazard
+โฌ† up arrow
+โ†— up-right arrow
+โžก right arrow
+โ†˜ down-right arrow
+โฌ‡ down arrow
+โ†™ down-left arrow
+โฌ… left arrow
+โ†– up-left arrow
+โ†• up-down arrow
+โ†” left-right arrow
+โ†ฉ right arrow curving left
+โ†ช left arrow curving right
+โคด right arrow curving up
+โคต right arrow curving down
+๐Ÿ”ƒ clockwise vertical arrows
+๐Ÿ”„ counterclockwise arrows button
+๐Ÿ”™ BACK arrow
+๐Ÿ”š END arrow
+๐Ÿ”› ON! arrow
+๐Ÿ”œ SOON arrow
+๐Ÿ” TOP arrow
+๐Ÿ› place of worship
+โš› atom symbol
+๐Ÿ•‰ om
+โœก star of David
+โ˜ธ wheel of dharma
+โ˜ฏ yin yang
+โœ latin cross
+โ˜ฆ orthodox cross
+โ˜ช star and crescent
+โ˜ฎ peace symbol
+๐Ÿ•Ž menorah
+๐Ÿ”ฏ dotted six-pointed star
+โ™ˆ Aries
+โ™‰ Taurus
+โ™Š Gemini
+โ™‹ Cancer
+โ™Œ Leo
+โ™ Virgo
+โ™Ž Libra
+โ™ Scorpio
+โ™ Sagittarius
+โ™‘ Capricorn
+โ™’ Aquarius
+โ™“ Pisces
+โ›Ž Ophiuchus
+๐Ÿ”€ shuffle tracks button
+๐Ÿ” repeat button
+๐Ÿ”‚ repeat single button
+โ–ถ play button
+โฉ fast-forward button
+โญ next track button
+โฏ play or pause button
+โ—€ reverse button
+โช fast reverse button
+โฎ last track button
+๐Ÿ”ผ upwards button
+โซ fast up button
+๐Ÿ”ฝ downwards button
+โฌ fast down button
+โธ pause button
+โน stop button
+โบ record button
+โ eject button
+๐ŸŽฆ cinema
+๐Ÿ”… dim button
+๐Ÿ”† bright button
+๐Ÿ“ถ antenna bars
+๐Ÿ“ณ vibration mode
+๐Ÿ“ด mobile phone off
+โ™€ female sign
+โ™‚ male sign
+โšง transgender symbol
+โœ– multiply
+โž• plus
+โž– minus
+โž— divide
+โ™พ infinity
+โ€ผ double exclamation mark
+โ‰ exclamation question mark
+โ“ red question mark
+โ” white question mark
+โ• white exclamation mark
+โ— red exclamation mark
+ใ€ฐ wavy dash
+๐Ÿ’ฑ currency exchange
+๐Ÿ’ฒ heavy dollar sign
+โš• medical symbol
+โ™ป recycling symbol
+โšœ fleur-de-lis
+๐Ÿ”ฑ trident emblem
+๐Ÿ“› name badge
+๐Ÿ”ฐ Japanese symbol for beginner
+โญ• hollow red circle
+โœ… check mark button
+โ˜‘ check box with check
+โœ” check mark
+โŒ cross mark
+โŽ cross mark button
+โžฐ curly loop
+โžฟ double curly loop
+ใ€ฝ part alternation mark
+โœณ eight-spoked asterisk
+โœด eight-pointed star
+โ‡ sparkle
+ยฉ copyright
+ยฎ registered
+โ„ข trade mark
+#๏ธโƒฃ keycap: #
+*๏ธโƒฃ keycap: *
+0๏ธโƒฃ keycap: 0
+1๏ธโƒฃ keycap: 1
+2๏ธโƒฃ keycap: 2
+3๏ธโƒฃ keycap: 3
+4๏ธโƒฃ keycap: 4
+5๏ธโƒฃ keycap: 5
+6๏ธโƒฃ keycap: 6
+7๏ธโƒฃ keycap: 7
+8๏ธโƒฃ keycap: 8
+9๏ธโƒฃ keycap: 9
+๐Ÿ”Ÿ keycap: 10
+๐Ÿ”  input latin uppercase
+๐Ÿ”ก input latin lowercase
+๐Ÿ”ข input numbers
+๐Ÿ”ฃ input symbols
+๐Ÿ”ค input latin letters
+๐Ÿ…ฐ A button (blood type)
+๐Ÿ†Ž AB button (blood type)
+๐Ÿ…ฑ B button (blood type)
+๐Ÿ†‘ CL button
+๐Ÿ†’ COOL button
+๐Ÿ†“ FREE button
+โ„น information
+๐Ÿ†” ID button
+โ“‚ circled M
+๐Ÿ†• NEW button
+๐Ÿ†– NG button
+๐Ÿ…พ O button (blood type)
+๐Ÿ†— OK button
+๐Ÿ…ฟ P button
+๐Ÿ†˜ SOS button
+๐Ÿ†™ UP! button
+๐Ÿ†š VS button
+๐Ÿˆ Japanese โ€œhereโ€ button
+๐Ÿˆ‚ Japanese โ€œservice chargeโ€ button
+๐Ÿˆท Japanese โ€œmonthly amountโ€ button
+๐Ÿˆถ Japanese โ€œnot free of chargeโ€ button
+๐Ÿˆฏ Japanese โ€œreservedโ€ button
+๐Ÿ‰ Japanese โ€œbargainโ€ button
+๐Ÿˆน Japanese โ€œdiscountโ€ button
+๐Ÿˆš Japanese โ€œfree of chargeโ€ button
+๐Ÿˆฒ Japanese โ€œprohibitedโ€ button
+๐Ÿ‰‘ Japanese โ€œacceptableโ€ button
+๐Ÿˆธ Japanese โ€œapplicationโ€ button
+๐Ÿˆด Japanese โ€œpassing gradeโ€ button
+๐Ÿˆณ Japanese โ€œvacancyโ€ button
+ใŠ— Japanese โ€œcongratulationsโ€ button
+ใŠ™ Japanese โ€œsecretโ€ button
+๐Ÿˆบ Japanese โ€œopen for businessโ€ button
+๐Ÿˆต Japanese โ€œno vacancyโ€ button
+๐Ÿ”ด red circle
+๐ŸŸ  orange circle
+๐ŸŸก yellow circle
+๐ŸŸข green circle
+๐Ÿ”ต blue circle
+๐ŸŸฃ purple circle
+๐ŸŸค brown circle
+โšซ black circle
+โšช white circle
+๐ŸŸฅ red square
+๐ŸŸง orange square
+๐ŸŸจ yellow square
+๐ŸŸฉ green square
+๐ŸŸฆ blue square
+๐ŸŸช purple square
+๐ŸŸซ brown square
+โฌ› black large square
+โฌœ white large square
+โ—ผ black medium square
+โ—ป white medium square
+โ—พ black medium-small square
+โ—ฝ white medium-small square
+โ–ช black small square
+โ–ซ white small square
+๐Ÿ”ถ large orange diamond
+๐Ÿ”ท large blue diamond
+๐Ÿ”ธ small orange diamond
+๐Ÿ”น small blue diamond
+๐Ÿ”บ red triangle pointed up
+๐Ÿ”ป red triangle pointed down
+๐Ÿ’  diamond with a dot
+๐Ÿ”˜ radio button
+๐Ÿ”ณ white square button
+๐Ÿ”ฒ black square button
+๐Ÿ chequered flag
+๐Ÿšฉ triangular flag
+๐ŸŽŒ crossed flags
+๐Ÿด black flag
+๐Ÿณ white flag
+๐Ÿณ๏ธโ€๐ŸŒˆ rainbow flag
+๐Ÿณ๏ธโ€โšง๏ธ transgender flag
+๐Ÿดโ€โ˜ ๏ธ pirate flag
+๐Ÿ‡ฆ๐Ÿ‡จ flag: Ascension Island
+๐Ÿ‡ฆ๐Ÿ‡ฉ flag: Andorra
+๐Ÿ‡ฆ๐Ÿ‡ช flag: United Arab Emirates
+๐Ÿ‡ฆ๐Ÿ‡ซ flag: Afghanistan
+๐Ÿ‡ฆ๐Ÿ‡ฌ flag: Antigua & Barbuda
+๐Ÿ‡ฆ๐Ÿ‡ฎ flag: Anguilla
+๐Ÿ‡ฆ๐Ÿ‡ฑ flag: Albania
+๐Ÿ‡ฆ๐Ÿ‡ฒ flag: Armenia
+๐Ÿ‡ฆ๐Ÿ‡ด flag: Angola
+๐Ÿ‡ฆ๐Ÿ‡ถ flag: Antarctica
+๐Ÿ‡ฆ๐Ÿ‡ท flag: Argentina
+๐Ÿ‡ฆ๐Ÿ‡ธ flag: American Samoa
+๐Ÿ‡ฆ๐Ÿ‡น flag: Austria
+๐Ÿ‡ฆ๐Ÿ‡บ flag: Australia
+๐Ÿ‡ฆ๐Ÿ‡ผ flag: Aruba
+๐Ÿ‡ฆ๐Ÿ‡ฝ flag: ร…land Islands
+๐Ÿ‡ฆ๐Ÿ‡ฟ flag: Azerbaijan
+๐Ÿ‡ง๐Ÿ‡ฆ flag: Bosnia & Herzegovina
+๐Ÿ‡ง๐Ÿ‡ง flag: Barbados
+๐Ÿ‡ง๐Ÿ‡ฉ flag: Bangladesh
+๐Ÿ‡ง๐Ÿ‡ช flag: Belgium
+๐Ÿ‡ง๐Ÿ‡ซ flag: Burkina Faso
+๐Ÿ‡ง๐Ÿ‡ฌ flag: Bulgaria
+๐Ÿ‡ง๐Ÿ‡ญ flag: Bahrain
+๐Ÿ‡ง๐Ÿ‡ฎ flag: Burundi
+๐Ÿ‡ง๐Ÿ‡ฏ flag: Benin
+๐Ÿ‡ง๐Ÿ‡ฑ flag: St. Barthรฉlemy
+๐Ÿ‡ง๐Ÿ‡ฒ flag: Bermuda
+๐Ÿ‡ง๐Ÿ‡ณ flag: Brunei
+๐Ÿ‡ง๐Ÿ‡ด flag: Bolivia
+๐Ÿ‡ง๐Ÿ‡ถ flag: Caribbean Netherlands
+๐Ÿ‡ง๐Ÿ‡ท flag: Brazil
+๐Ÿ‡ง๐Ÿ‡ธ flag: Bahamas
+๐Ÿ‡ง๐Ÿ‡น flag: Bhutan
+๐Ÿ‡ง๐Ÿ‡ป flag: Bouvet Island
+๐Ÿ‡ง๐Ÿ‡ผ flag: Botswana
+๐Ÿ‡ง๐Ÿ‡พ flag: Belarus
+๐Ÿ‡ง๐Ÿ‡ฟ flag: Belize
+๐Ÿ‡จ๐Ÿ‡ฆ flag: Canada
+๐Ÿ‡จ๐Ÿ‡จ flag: Cocos (Keeling) Islands
+๐Ÿ‡จ๐Ÿ‡ฉ flag: Congo - Kinshasa
+๐Ÿ‡จ๐Ÿ‡ซ flag: Central African Republic
+๐Ÿ‡จ๐Ÿ‡ฌ flag: Congo - Brazzaville
+๐Ÿ‡จ๐Ÿ‡ญ flag: Switzerland
+๐Ÿ‡จ๐Ÿ‡ฎ flag: Cรดte dโ€™Ivoire
+๐Ÿ‡จ๐Ÿ‡ฐ flag: Cook Islands
+๐Ÿ‡จ๐Ÿ‡ฑ flag: Chile
+๐Ÿ‡จ๐Ÿ‡ฒ flag: Cameroon
+๐Ÿ‡จ๐Ÿ‡ณ flag: China
+๐Ÿ‡จ๐Ÿ‡ด flag: Colombia
+๐Ÿ‡จ๐Ÿ‡ต flag: Clipperton Island
+๐Ÿ‡จ๐Ÿ‡ท flag: Costa Rica
+๐Ÿ‡จ๐Ÿ‡บ flag: Cuba
+๐Ÿ‡จ๐Ÿ‡ป flag: Cape Verde
+๐Ÿ‡จ๐Ÿ‡ผ flag: Curaรงao
+๐Ÿ‡จ๐Ÿ‡ฝ flag: Christmas Island
+๐Ÿ‡จ๐Ÿ‡พ flag: Cyprus
+๐Ÿ‡จ๐Ÿ‡ฟ flag: Czechia
+๐Ÿ‡ฉ๐Ÿ‡ช flag: Germany
+๐Ÿ‡ฉ๐Ÿ‡ฌ flag: Diego Garcia
+๐Ÿ‡ฉ๐Ÿ‡ฏ flag: Djibouti
+๐Ÿ‡ฉ๐Ÿ‡ฐ flag: Denmark
+๐Ÿ‡ฉ๐Ÿ‡ฒ flag: Dominica
+๐Ÿ‡ฉ๐Ÿ‡ด flag: Dominican Republic
+๐Ÿ‡ฉ๐Ÿ‡ฟ flag: Algeria
+๐Ÿ‡ช๐Ÿ‡ฆ flag: Ceuta & Melilla
+๐Ÿ‡ช๐Ÿ‡จ flag: Ecuador
+๐Ÿ‡ช๐Ÿ‡ช flag: Estonia
+๐Ÿ‡ช๐Ÿ‡ฌ flag: Egypt
+๐Ÿ‡ช๐Ÿ‡ญ flag: Western Sahara
+๐Ÿ‡ช๐Ÿ‡ท flag: Eritrea
+๐Ÿ‡ช๐Ÿ‡ธ flag: Spain
+๐Ÿ‡ช๐Ÿ‡น flag: Ethiopia
+๐Ÿ‡ช๐Ÿ‡บ flag: European Union
+๐Ÿ‡ซ๐Ÿ‡ฎ flag: Finland
+๐Ÿ‡ซ๐Ÿ‡ฏ flag: Fiji
+๐Ÿ‡ซ๐Ÿ‡ฐ flag: Falkland Islands
+๐Ÿ‡ซ๐Ÿ‡ฒ flag: Micronesia
+๐Ÿ‡ซ๐Ÿ‡ด flag: Faroe Islands
+๐Ÿ‡ซ๐Ÿ‡ท flag: France
+๐Ÿ‡ฌ๐Ÿ‡ฆ flag: Gabon
+๐Ÿ‡ฌ๐Ÿ‡ง flag: United Kingdom
+๐Ÿ‡ฌ๐Ÿ‡ฉ flag: Grenada
+๐Ÿ‡ฌ๐Ÿ‡ช flag: Georgia
+๐Ÿ‡ฌ๐Ÿ‡ซ flag: French Guiana
+๐Ÿ‡ฌ๐Ÿ‡ฌ flag: Guernsey
+๐Ÿ‡ฌ๐Ÿ‡ญ flag: Ghana
+๐Ÿ‡ฌ๐Ÿ‡ฎ flag: Gibraltar
+๐Ÿ‡ฌ๐Ÿ‡ฑ flag: Greenland
+๐Ÿ‡ฌ๐Ÿ‡ฒ flag: Gambia
+๐Ÿ‡ฌ๐Ÿ‡ณ flag: Guinea
+๐Ÿ‡ฌ๐Ÿ‡ต flag: Guadeloupe
+๐Ÿ‡ฌ๐Ÿ‡ถ flag: Equatorial Guinea
+๐Ÿ‡ฌ๐Ÿ‡ท flag: Greece
+๐Ÿ‡ฌ๐Ÿ‡ธ flag: South Georgia & South Sandwich Islands
+๐Ÿ‡ฌ๐Ÿ‡น flag: Guatemala
+๐Ÿ‡ฌ๐Ÿ‡บ flag: Guam
+๐Ÿ‡ฌ๐Ÿ‡ผ flag: Guinea-Bissau
+๐Ÿ‡ฌ๐Ÿ‡พ flag: Guyana
+๐Ÿ‡ญ๐Ÿ‡ฐ flag: Hong Kong SAR China
+๐Ÿ‡ญ๐Ÿ‡ฒ flag: Heard & McDonald Islands
+๐Ÿ‡ญ๐Ÿ‡ณ flag: Honduras
+๐Ÿ‡ญ๐Ÿ‡ท flag: Croatia
+๐Ÿ‡ญ๐Ÿ‡น flag: Haiti
+๐Ÿ‡ญ๐Ÿ‡บ flag: Hungary
+๐Ÿ‡ฎ๐Ÿ‡จ flag: Canary Islands
+๐Ÿ‡ฎ๐Ÿ‡ฉ flag: Indonesia
+๐Ÿ‡ฎ๐Ÿ‡ช flag: Ireland
+๐Ÿ‡ฎ๐Ÿ‡ฑ flag: Israel
+๐Ÿ‡ฎ๐Ÿ‡ฒ flag: Isle of Man
+๐Ÿ‡ฎ๐Ÿ‡ณ flag: India
+๐Ÿ‡ฎ๐Ÿ‡ด flag: British Indian Ocean Territory
+๐Ÿ‡ฎ๐Ÿ‡ถ flag: Iraq
+๐Ÿ‡ฎ๐Ÿ‡ท flag: Iran
+๐Ÿ‡ฎ๐Ÿ‡ธ flag: Iceland
+๐Ÿ‡ฎ๐Ÿ‡น flag: Italy
+๐Ÿ‡ฏ๐Ÿ‡ช flag: Jersey
+๐Ÿ‡ฏ๐Ÿ‡ฒ flag: Jamaica
+๐Ÿ‡ฏ๐Ÿ‡ด flag: Jordan
+๐Ÿ‡ฏ๐Ÿ‡ต flag: Japan
+๐Ÿ‡ฐ๐Ÿ‡ช flag: Kenya
+๐Ÿ‡ฐ๐Ÿ‡ฌ flag: Kyrgyzstan
+๐Ÿ‡ฐ๐Ÿ‡ญ flag: Cambodia
+๐Ÿ‡ฐ๐Ÿ‡ฎ flag: Kiribati
+๐Ÿ‡ฐ๐Ÿ‡ฒ flag: Comoros
+๐Ÿ‡ฐ๐Ÿ‡ณ flag: St. Kitts & Nevis
+๐Ÿ‡ฐ๐Ÿ‡ต flag: North Korea
+๐Ÿ‡ฐ๐Ÿ‡ท flag: South Korea
+๐Ÿ‡ฐ๐Ÿ‡ผ flag: Kuwait
+๐Ÿ‡ฐ๐Ÿ‡พ flag: Cayman Islands
+๐Ÿ‡ฐ๐Ÿ‡ฟ flag: Kazakhstan
+๐Ÿ‡ฑ๐Ÿ‡ฆ flag: Laos
+๐Ÿ‡ฑ๐Ÿ‡ง flag: Lebanon
+๐Ÿ‡ฑ๐Ÿ‡จ flag: St. Lucia
+๐Ÿ‡ฑ๐Ÿ‡ฎ flag: Liechtenstein
+๐Ÿ‡ฑ๐Ÿ‡ฐ flag: Sri Lanka
+๐Ÿ‡ฑ๐Ÿ‡ท flag: Liberia
+๐Ÿ‡ฑ๐Ÿ‡ธ flag: Lesotho
+๐Ÿ‡ฑ๐Ÿ‡น flag: Lithuania
+๐Ÿ‡ฑ๐Ÿ‡บ flag: Luxembourg
+๐Ÿ‡ฑ๐Ÿ‡ป flag: Latvia
+๐Ÿ‡ฑ๐Ÿ‡พ flag: Libya
+๐Ÿ‡ฒ๐Ÿ‡ฆ flag: Morocco
+๐Ÿ‡ฒ๐Ÿ‡จ flag: Monaco
+๐Ÿ‡ฒ๐Ÿ‡ฉ flag: Moldova
+๐Ÿ‡ฒ๐Ÿ‡ช flag: Montenegro
+๐Ÿ‡ฒ๐Ÿ‡ซ flag: St. Martin
+๐Ÿ‡ฒ๐Ÿ‡ฌ flag: Madagascar
+๐Ÿ‡ฒ๐Ÿ‡ญ flag: Marshall Islands
+๐Ÿ‡ฒ๐Ÿ‡ฐ flag: North Macedonia
+๐Ÿ‡ฒ๐Ÿ‡ฑ flag: Mali
+๐Ÿ‡ฒ๐Ÿ‡ฒ flag: Myanmar (Burma)
+๐Ÿ‡ฒ๐Ÿ‡ณ flag: Mongolia
+๐Ÿ‡ฒ๐Ÿ‡ด flag: Macao SAR China
+๐Ÿ‡ฒ๐Ÿ‡ต flag: Northern Mariana Islands
+๐Ÿ‡ฒ๐Ÿ‡ถ flag: Martinique
+๐Ÿ‡ฒ๐Ÿ‡ท flag: Mauritania
+๐Ÿ‡ฒ๐Ÿ‡ธ flag: Montserrat
+๐Ÿ‡ฒ๐Ÿ‡น flag: Malta
+๐Ÿ‡ฒ๐Ÿ‡บ flag: Mauritius
+๐Ÿ‡ฒ๐Ÿ‡ป flag: Maldives
+๐Ÿ‡ฒ๐Ÿ‡ผ flag: Malawi
+๐Ÿ‡ฒ๐Ÿ‡ฝ flag: Mexico
+๐Ÿ‡ฒ๐Ÿ‡พ flag: Malaysia
+๐Ÿ‡ฒ๐Ÿ‡ฟ flag: Mozambique
+๐Ÿ‡ณ๐Ÿ‡ฆ flag: Namibia
+๐Ÿ‡ณ๐Ÿ‡จ flag: New Caledonia
+๐Ÿ‡ณ๐Ÿ‡ช flag: Niger
+๐Ÿ‡ณ๐Ÿ‡ซ flag: Norfolk Island
+๐Ÿ‡ณ๐Ÿ‡ฌ flag: Nigeria
+๐Ÿ‡ณ๐Ÿ‡ฎ flag: Nicaragua
+๐Ÿ‡ณ๐Ÿ‡ฑ flag: Netherlands
+๐Ÿ‡ณ๐Ÿ‡ด flag: Norway
+๐Ÿ‡ณ๐Ÿ‡ต flag: Nepal
+๐Ÿ‡ณ๐Ÿ‡ท flag: Nauru
+๐Ÿ‡ณ๐Ÿ‡บ flag: Niue
+๐Ÿ‡ณ๐Ÿ‡ฟ flag: New Zealand
+๐Ÿ‡ด๐Ÿ‡ฒ flag: Oman
+๐Ÿ‡ต๐Ÿ‡ฆ flag: Panama
+๐Ÿ‡ต๐Ÿ‡ช flag: Peru
+๐Ÿ‡ต๐Ÿ‡ซ flag: French Polynesia
+๐Ÿ‡ต๐Ÿ‡ฌ flag: Papua New Guinea
+๐Ÿ‡ต๐Ÿ‡ญ flag: Philippines
+๐Ÿ‡ต๐Ÿ‡ฐ flag: Pakistan
+๐Ÿ‡ต๐Ÿ‡ฑ flag: Poland
+๐Ÿ‡ต๐Ÿ‡ฒ flag: St. Pierre & Miquelon
+๐Ÿ‡ต๐Ÿ‡ณ flag: Pitcairn Islands
+๐Ÿ‡ต๐Ÿ‡ท flag: Puerto Rico
+๐Ÿ‡ต๐Ÿ‡ธ flag: Palestinian Territories
+๐Ÿ‡ต๐Ÿ‡น flag: Portugal
+๐Ÿ‡ต๐Ÿ‡ผ flag: Palau
+๐Ÿ‡ต๐Ÿ‡พ flag: Paraguay
+๐Ÿ‡ถ๐Ÿ‡ฆ flag: Qatar
+๐Ÿ‡ท๐Ÿ‡ช flag: Rรฉunion
+๐Ÿ‡ท๐Ÿ‡ด flag: Romania
+๐Ÿ‡ท๐Ÿ‡ธ flag: Serbia
+๐Ÿ‡ท๐Ÿ‡บ flag: Russia
+๐Ÿ‡ท๐Ÿ‡ผ flag: Rwanda
+๐Ÿ‡ธ๐Ÿ‡ฆ flag: Saudi Arabia
+๐Ÿ‡ธ๐Ÿ‡ง flag: Solomon Islands
+๐Ÿ‡ธ๐Ÿ‡จ flag: Seychelles
+๐Ÿ‡ธ๐Ÿ‡ฉ flag: Sudan
+๐Ÿ‡ธ๐Ÿ‡ช flag: Sweden
+๐Ÿ‡ธ๐Ÿ‡ฌ flag: Singapore
+๐Ÿ‡ธ๐Ÿ‡ญ flag: St. Helena
+๐Ÿ‡ธ๐Ÿ‡ฎ flag: Slovenia
+๐Ÿ‡ธ๐Ÿ‡ฏ flag: Svalbard & Jan Mayen
+๐Ÿ‡ธ๐Ÿ‡ฐ flag: Slovakia
+๐Ÿ‡ธ๐Ÿ‡ฑ flag: Sierra Leone
+๐Ÿ‡ธ๐Ÿ‡ฒ flag: San Marino
+๐Ÿ‡ธ๐Ÿ‡ณ flag: Senegal
+๐Ÿ‡ธ๐Ÿ‡ด flag: Somalia
+๐Ÿ‡ธ๐Ÿ‡ท flag: Suriname
+๐Ÿ‡ธ๐Ÿ‡ธ flag: South Sudan
+๐Ÿ‡ธ๐Ÿ‡น flag: Sรฃo Tomรฉ & Prรญncipe
+๐Ÿ‡ธ๐Ÿ‡ป flag: El Salvador
+๐Ÿ‡ธ๐Ÿ‡ฝ flag: Sint Maarten
+๐Ÿ‡ธ๐Ÿ‡พ flag: Syria
+๐Ÿ‡ธ๐Ÿ‡ฟ flag: Eswatini
+๐Ÿ‡น๐Ÿ‡ฆ flag: Tristan da Cunha
+๐Ÿ‡น๐Ÿ‡จ flag: Turks & Caicos Islands
+๐Ÿ‡น๐Ÿ‡ฉ flag: Chad
+๐Ÿ‡น๐Ÿ‡ซ flag: French Southern Territories
+๐Ÿ‡น๐Ÿ‡ฌ flag: Togo
+๐Ÿ‡น๐Ÿ‡ญ flag: Thailand
+๐Ÿ‡น๐Ÿ‡ฏ flag: Tajikistan
+๐Ÿ‡น๐Ÿ‡ฐ flag: Tokelau
+๐Ÿ‡น๐Ÿ‡ฑ flag: Timor-Leste
+๐Ÿ‡น๐Ÿ‡ฒ flag: Turkmenistan
+๐Ÿ‡น๐Ÿ‡ณ flag: Tunisia
+๐Ÿ‡น๐Ÿ‡ด flag: Tonga
+๐Ÿ‡น๐Ÿ‡ท flag: Turkey
+๐Ÿ‡น๐Ÿ‡น flag: Trinidad & Tobago
+๐Ÿ‡น๐Ÿ‡ป flag: Tuvalu
+๐Ÿ‡น๐Ÿ‡ผ flag: Taiwan
+๐Ÿ‡น๐Ÿ‡ฟ flag: Tanzania
+๐Ÿ‡บ๐Ÿ‡ฆ flag: Ukraine
+๐Ÿ‡บ๐Ÿ‡ฌ flag: Uganda
+๐Ÿ‡บ๐Ÿ‡ฒ flag: U.S. Outlying Islands
+๐Ÿ‡บ๐Ÿ‡ณ flag: United Nations
+๐Ÿ‡บ๐Ÿ‡ธ flag: United States
+๐Ÿ‡บ๐Ÿ‡พ flag: Uruguay
+๐Ÿ‡บ๐Ÿ‡ฟ flag: Uzbekistan
+๐Ÿ‡ป๐Ÿ‡ฆ flag: Vatican City
+๐Ÿ‡ป๐Ÿ‡จ flag: St. Vincent & Grenadines
+๐Ÿ‡ป๐Ÿ‡ช flag: Venezuela
+๐Ÿ‡ป๐Ÿ‡ฌ flag: British Virgin Islands
+๐Ÿ‡ป๐Ÿ‡ฎ flag: U.S. Virgin Islands
+๐Ÿ‡ป๐Ÿ‡ณ flag: Vietnam
+๐Ÿ‡ป๐Ÿ‡บ flag: Vanuatu
+๐Ÿ‡ผ๐Ÿ‡ซ flag: Wallis & Futuna
+๐Ÿ‡ผ๐Ÿ‡ธ flag: Samoa
+๐Ÿ‡ฝ๐Ÿ‡ฐ flag: Kosovo
+๐Ÿ‡พ๐Ÿ‡ช flag: Yemen
+๐Ÿ‡พ๐Ÿ‡น flag: Mayotte
+๐Ÿ‡ฟ๐Ÿ‡ฆ flag: South Africa
+๐Ÿ‡ฟ๐Ÿ‡ฒ flag: Zambia
+๐Ÿ‡ฟ๐Ÿ‡ผ flag: Zimbabwe
+๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ flag: England
+๐Ÿด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ flag: Scotland
+๐Ÿด๓ ง๓ ข๓ ท๓ ฌ๓ ณ๓ ฟ flag: Wales
+
+ + diff --git a/geolite2.php b/geolite2.php new file mode 100644 index 0000000..b36fec8 --- /dev/null +++ b/geolite2.php @@ -0,0 +1,56 @@ +get($ip); +$reader->close(); + +$reader = new Reader("geolite2/GeoLite2-City_" . GEOLITE2_DATE . "/GeoLite2-City.mmdb"); +$cityData = $reader->get($ip); +$reader->close(); + +?> + + + + + Informations about <?= $ipFormat ?> address <?= htmlspecialchars($ip) ?> + + +

Informations about address

+
+ ASN:
+ AS name:
+ Location: , (accuracy radius: km)
+ Continent:
+ Time zone:
+ Country:
+ Subdivision:
+ Postal code:
+ City:
+
+
+ This page queries a local GeoLite2 database dating from . +
+ + + diff --git a/geolite2/GeoLite2-ASN_20210914/COPYRIGHT.txt b/geolite2/GeoLite2-ASN_20210914/COPYRIGHT.txt new file mode 100644 index 0000000..90eb0ee --- /dev/null +++ b/geolite2/GeoLite2-ASN_20210914/COPYRIGHT.txt @@ -0,0 +1 @@ +Database and Contents Copyright (c) 2021 MaxMind, Inc. diff --git a/geolite2/GeoLite2-ASN_20210914/GeoLite2-ASN.mmdb b/geolite2/GeoLite2-ASN_20210914/GeoLite2-ASN.mmdb new file mode 100644 index 0000000..aacf58b Binary files /dev/null and b/geolite2/GeoLite2-ASN_20210914/GeoLite2-ASN.mmdb differ diff --git a/geolite2/GeoLite2-ASN_20210914/LICENSE.txt b/geolite2/GeoLite2-ASN_20210914/LICENSE.txt new file mode 100644 index 0000000..ee7434d --- /dev/null +++ b/geolite2/GeoLite2-ASN_20210914/LICENSE.txt @@ -0,0 +1,3 @@ +Use of this MaxMind product is governed by MaxMind's GeoLite2 End User License Agreement, which can be viewed at https://www.maxmind.com/en/geolite2/eula. + +This database incorporates GeoNames [https://www.geonames.org] geographical data, which is made available under the Creative Commons Attribution 4.0 License. To view a copy of this license, visit https://creativecommons.org/licenses/by/4.0/. diff --git a/geolite2/GeoLite2-City_20210914/COPYRIGHT.txt b/geolite2/GeoLite2-City_20210914/COPYRIGHT.txt new file mode 100644 index 0000000..90eb0ee --- /dev/null +++ b/geolite2/GeoLite2-City_20210914/COPYRIGHT.txt @@ -0,0 +1 @@ +Database and Contents Copyright (c) 2021 MaxMind, Inc. diff --git a/geolite2/GeoLite2-City_20210914/GeoLite2-City.mmdb b/geolite2/GeoLite2-City_20210914/GeoLite2-City.mmdb new file mode 100644 index 0000000..0db8d36 Binary files /dev/null and b/geolite2/GeoLite2-City_20210914/GeoLite2-City.mmdb differ diff --git a/geolite2/GeoLite2-City_20210914/LICENSE.txt b/geolite2/GeoLite2-City_20210914/LICENSE.txt new file mode 100644 index 0000000..ee7434d --- /dev/null +++ b/geolite2/GeoLite2-City_20210914/LICENSE.txt @@ -0,0 +1,3 @@ +Use of this MaxMind product is governed by MaxMind's GeoLite2 End User License Agreement, which can be viewed at https://www.maxmind.com/en/geolite2/eula. + +This database incorporates GeoNames [https://www.geonames.org] geographical data, which is made available under the Creative Commons Attribution 4.0 License. To view a copy of this license, visit https://creativecommons.org/licenses/by/4.0/. diff --git a/geolite2/GeoLite2-City_20210914/README.txt b/geolite2/GeoLite2-City_20210914/README.txt new file mode 100644 index 0000000..16e29ad --- /dev/null +++ b/geolite2/GeoLite2-City_20210914/README.txt @@ -0,0 +1 @@ +Latitude and longitude are not precise and should not be used to identify a particular street address or household. diff --git a/geolite2/GeoLite2-Country_20210914/COPYRIGHT.txt b/geolite2/GeoLite2-Country_20210914/COPYRIGHT.txt new file mode 100644 index 0000000..90eb0ee --- /dev/null +++ b/geolite2/GeoLite2-Country_20210914/COPYRIGHT.txt @@ -0,0 +1 @@ +Database and Contents Copyright (c) 2021 MaxMind, Inc. diff --git a/geolite2/GeoLite2-Country_20210914/GeoLite2-Country.mmdb b/geolite2/GeoLite2-Country_20210914/GeoLite2-Country.mmdb new file mode 100644 index 0000000..d1e6a57 Binary files /dev/null and b/geolite2/GeoLite2-Country_20210914/GeoLite2-Country.mmdb differ diff --git a/geolite2/GeoLite2-Country_20210914/LICENSE.txt b/geolite2/GeoLite2-Country_20210914/LICENSE.txt new file mode 100644 index 0000000..ee7434d --- /dev/null +++ b/geolite2/GeoLite2-Country_20210914/LICENSE.txt @@ -0,0 +1,3 @@ +Use of this MaxMind product is governed by MaxMind's GeoLite2 End User License Agreement, which can be viewed at https://www.maxmind.com/en/geolite2/eula. + +This database incorporates GeoNames [https://www.geonames.org] geographical data, which is made available under the Creative Commons Attribution 4.0 License. To view a copy of this license, visit https://creativecommons.org/licenses/by/4.0/. diff --git a/index.php b/index.php new file mode 100644 index 0000000..a10a912 --- /dev/null +++ b/index.php @@ -0,0 +1,9 @@ + + + + + Meta + + + +
+

IP

+ Client address: (informations from database)
+ Server address: + (informations from database) + - (require IPv6)"; + ?> +
+ +
+ +
+

TCP

+ RTT: milliseconds
+ RTT variance:
+ SND CWND:
+ RCV SPACE:
+
+

Ports

+ + Client port:
+ Server port:
+ +
+ Test outgoing destination ports (IPv6 only) + + https://ports.meta.4.niv.re:/
+ +
+
+
+ +
+

TLS

+ TLS session ID:
+ Offered TLS curves:
+ Offered TLS ciphers:
+ Negotiated TLS cipher:
+ Negotiated TLS version:
+
+ +
+

HTTP

+ Request line:
+ +
+

Headers

+
 $value) {
+          if ($name !== "Content-Length" AND $name !== "Content-Type")
+            echo htmlspecialchars($name) . ": " . htmlspecialchars($value) . "\n";
+        } ?>
+ +
+ Headers definition lists + https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
+ https://developer.mozilla.org/docs/Web/HTTP/Headers
+ https://datatracker.ietf.org/doc/html/rfc2616
+ RFC 7231 Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content > 5. Request Header Fields
+ RFC 7231 Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content > 7. Response Header Fields
+
+
+
+ +
+

Server system informations

+ + Architecture:
+ Distribution:
+ Distribution version:
+ Distribution version codename:
+ Distribution description:
+ Kernel:
+ Kernel version:
+ Nginx version:
+ PHP version:
+ Connection ID since Nginx startup:
+ Requests in this connection:
+ + Unix time: ( UTC)
+ +
+
+ +